<commit_before>;(function () {
  module.exports = parseGame
  var tenthPattern = new RegExp('^[XF0-9\-]{1}[XF0-9\-\/]?[XF0-9\-\/]?$', 'i')
  var basicPattern = new RegExp('^[XF0-9\-]{1}[F0-9\-\/]?$', 'i')
  var zeroEquiv = new RegExp('[F\-]', 'g')

  function validateFrame (frame, isTenthFrame) {
    if (frame.length === 0) {
      throw new Error('frame must not be empty: ' + frame)
    } else if (frame.length > 3) {
      throw new Error('frame must be less than 4 chars: ' + frame)
    } else if (frame.length === 3 && !isTenthFrame) {
      throw new Error('frame length too long for frames 1-9: ' + frame)
    } else if (isTenthFrame && !tenthPattern.test(frame)) {
      throw new Error('failed tenth frame validation' + frame)
    } else if (!isTenthFrame && !basicPattern.test(frame)) {
      throw new Error('failed normal frame validation' + frame)
    }
  }

  function parseFrame (result) {
    if (typeof result !== 'string') result = result + ''

    result = result.replace(/0/g, '-')
    result = result.toUpperCase()
    return result
  }

  function isStrike (frame) { return frame === 'X' }

  function isSpare (frame) {
    return frame.length === 2 && frame.match(/\//)
  }

  function scoreFrame (frame) {
    var score

    if (isStrike(frame) || isSpare(frame)) {
      score = null
    } else {
      var addable = frame.replace(zeroEquiv, '0')
      score = 0
      for (var c = 0; c < addable.length; c++) {
        score += parseInt(addable[c], 10)
        if (score > 9) {
          throw new Error('invalid frame score: ' + frame + ' = ' + score)
        }
      }
    }
    return score
  }

  function getRollScores (frames) {
    if (frames === undefined) frames = []

    var rollScores = []
    var rolls = frames.join('').replace(zeroEquiv, '0')

    for (var t = 0; t < rolls.length; t++) {
      var score

      if (rolls[t] === 'X') {
        score = 10
      } else if (rolls[t] === '/') {
        score = 10 - parseInt(rolls[t - 1], 10)
      } else {
        score = parseInt(rolls[t], 10)
      }

      rollScores.push(score)
    }

    return rollScores
  }

  function updateCumulatives (scoresheet) {
    var cumulative = 0
    for (var f = 0; f < scoresheet.length; f++) {
      if (scoresheet[f].score === null) break

      cumulative += scoresheet[f].score
      scoresheet[f].cumulative = cumulative
    }
    return scoresheet
  }

  function scoreTenthFrame (frame) {
    var score = 0
    var isFinal = false

    if (frame.length === 3) {
      isFinal = true
    } else if (frame.length === 2 && frame[1] !== 'X' && frame[1] !== '/') {
      isFinal = true
    }

    if (!isFinal) return null

    while (frame.length > 0) {
      if (isStrike(frame[0])) {
        score += 10
        frame = frame.slice(1)
      } else if (isSpare(frame.substr(0, 2))) {
        score += 10
        frame = frame.slice(2)
      } else {
        score += scoreFrame(frame[0])
        frame = frame.slice(1)
      }
    }
    return score
  }

  function attemptFrameFinalize (frame, leadingFrames) {
    var prevIsPending = frame.score === null
    var nextScores = getRollScores(leadingFrames)
    var bonus = 0

    if (prevIsPending) {
      if (isStrike(frame.outcome)) {
        if (nextScores.length >= 2) {
          bonus += nextScores[0]
          bonus += nextScores[1]
          frame.score = 10 + bonus
        }
      } else if (isSpare(frame.outcome)) {
        if (nextScores.length >= 1) {
          bonus += nextScores[0]
          frame.score = 10 + bonus
        }
      }
    }
  }

  function parseGame (game) {
    var scoresheet = []

    if (game.length > 10) throw new Error('too many frames')

    for (var i = 0; i < game.length; i++) {
      var isTenthFrame = i === 9
      var notFirstFrame = i > 0
      var notFirstTwoFrames = i > 1
      var cleanOutcome = parseFrame(game[i], isTenthFrame)

      validateFrame(cleanOutcome, isTenthFrame)

      scoresheet[i] = {
        outcome: cleanOutcome,
        cumulative: null,
        score: scoreFrame(cleanOutcome)
      }

      var prevFrame, leadingFrames

      if (notFirstFrame) {
        prevFrame = scoresheet[i - 1]
        leadingFrames = [scoresheet[i].outcome]

        attemptFrameFinalize(prevFrame, leadingFrames)
      }

      if (notFirstTwoFrames) {
        prevFrame = scoresheet[i - 2]
        leadingFrames = [scoresheet[i - 1].outcome, scoresheet[i].outcome]

        attemptFrameFinalize(prevFrame, leadingFrames)
      }

      if (isTenthFrame) {
        scoresheet[i].score = scoreTenthFrame(scoresheet[i].outcome)
      }
    }

    scoresheet = updateCumulatives(scoresheet)

    return scoresheet
  }
})(this)
<commit_msg>removed unnecessary escape chars for standard upgrade<commit_after>;(function () {
  module.exports = parseGame
  var tenthPattern = new RegExp('^[XF0-9-]{1}[XF0-9-/]?[XF0-9-/]?$', 'i')
  var basicPattern = new RegExp('^[XF0-9-]{1}[F0-9-/]?$', 'i')
  var zeroEquiv = new RegExp('[F-]', 'g')

  function validateFrame (frame, isTenthFrame) {
    if (frame.length === 0) {
      throw new Error('frame must not be empty: ' + frame)
    } else if (frame.length > 3) {
      throw new Error('frame must be less than 4 chars: ' + frame)
    } else if (frame.length === 3 && !isTenthFrame) {
      throw new Error('frame length too long for frames 1-9: ' + frame)
    } else if (isTenthFrame && !tenthPattern.test(frame)) {
      throw new Error('failed tenth frame validation' + frame)
    } else if (!isTenthFrame && !basicPattern.test(frame)) {
      throw new Error('failed normal frame validation' + frame)
    }
  }

  function parseFrame (result) {
    if (typeof result !== 'string') result = result + ''

    result = result.replace(/0/g, '-')
    result = result.toUpperCase()
    return result
  }

  function isStrike (frame) { return frame === 'X' }

  function isSpare (frame) {
    return frame.length === 2 && frame.match(/\//)
  }

  function scoreFrame (frame) {
    var score

    if (isStrike(frame) || isSpare(frame)) {
      score = null
    } else {
      var addable = frame.replace(zeroEquiv, '0')
      score = 0
      for (var c = 0; c < addable.length; c++) {
        score += parseInt(addable[c], 10)
        if (score > 9) {
          throw new Error('invalid frame score: ' + frame + ' = ' + score)
        }
      }
    }
    return score
  }

  function getRollScores (frames) {
    if (frames === undefined) frames = []

    var rollScores = []
    var rolls = frames.join('').replace(zeroEquiv, '0')

    for (var t = 0; t < rolls.length; t++) {
      var score

      if (rolls[t] === 'X') {
        score = 10
      } else if (rolls[t] === '/') {
        score = 10 - parseInt(rolls[t - 1], 10)
      } else {
        score = parseInt(rolls[t], 10)
      }

      rollScores.push(score)
    }

    return rollScores
  }

  function updateCumulatives (scoresheet) {
    var cumulative = 0
    for (var f = 0; f < scoresheet.length; f++) {
      if (scoresheet[f].score === null) break

      cumulative += scoresheet[f].score
      scoresheet[f].cumulative = cumulative
    }
    return scoresheet
  }

  function scoreTenthFrame (frame) {
    var score = 0
    var isFinal = false

    if (frame.length === 3) {
      isFinal = true
    } else if (frame.length === 2 && frame[1] !== 'X' && frame[1] !== '/') {
      isFinal = true
    }

    if (!isFinal) return null

    while (frame.length > 0) {
      if (isStrike(frame[0])) {
        score += 10
        frame = frame.slice(1)
      } else if (isSpare(frame.substr(0, 2))) {
        score += 10
        frame = frame.slice(2)
      } else {
        score += scoreFrame(frame[0])
        frame = frame.slice(1)
      }
    }
    return score
  }

  function attemptFrameFinalize (frame, leadingFrames) {
    var prevIsPending = frame.score === null
    var nextScores = getRollScores(leadingFrames)
    var bonus = 0

    if (prevIsPending) {
      if (isStrike(frame.outcome)) {
        if (nextScores.length >= 2) {
          bonus += nextScores[0]
          bonus += nextScores[1]
          frame.score = 10 + bonus
        }
      } else if (isSpare(frame.outcome)) {
        if (nextScores.length >= 1) {
          bonus += nextScores[0]
          frame.score = 10 + bonus
        }
      }
    }
  }

  function parseGame (game) {
    var scoresheet = []

    if (game.length > 10) throw new Error('too many frames')

    for (var i = 0; i < game.length; i++) {
      var isTenthFrame = i === 9
      var notFirstFrame = i > 0
      var notFirstTwoFrames = i > 1
      var cleanOutcome = parseFrame(game[i], isTenthFrame)

      validateFrame(cleanOutcome, isTenthFrame)

      scoresheet[i] = {
        outcome: cleanOutcome,
        cumulative: null,
        score: scoreFrame(cleanOutcome)
      }

      var prevFrame, leadingFrames

      if (notFirstFrame) {
        prevFrame = scoresheet[i - 1]
        leadingFrames = [scoresheet[i].outcome]

        attemptFrameFinalize(prevFrame, leadingFrames)
      }

      if (notFirstTwoFrames) {
        prevFrame = scoresheet[i - 2]
        leadingFrames = [scoresheet[i - 1].outcome, scoresheet[i].outcome]

        attemptFrameFinalize(prevFrame, leadingFrames)
      }

      if (isTenthFrame) {
        scoresheet[i].score = scoreTenthFrame(scoresheet[i].outcome)
      }
    }

    scoresheet = updateCumulatives(scoresheet)

    return scoresheet
  }
})(this)
<|endoftext|>


<commit_before>const { CLIEngine, Linter } = require('eslint');
const path = require('path');
const {
  filter,
  orderBy,
  forEach
} = require('lodash');

function sortRules(rules) {
  const results = [];

  rules.forEach((value, key) => {
    results.push({
      name: key,
      ...value
    });
  });

  return orderBy(results, ['category', 'deprecated', 'name']);
}

class RuleFinder {
  constructor(configFile) {
    const cf = path.join(process.cwd(), configFile);

    const cli = new CLIEngine({
      useEslintrc: false,
      configFile: cf
    });

    const linter = new Linter();

    this.config = cli.getConfigForFile();
    this.eslintRules = linter.getRules();
    this.getPluginRules();
  }

  getPluginRules() {
    const { plugins } = this.config;

    forEach(plugins, (plugin) => {
      // eslint-disable-next-line
      const pluginConfig = require(`eslint-plugin-${plugin}`);

      forEach(pluginConfig.rules, (value, key) => {
        let category = plugin;

        if (value.meta.docs && value.meta.docs.category) {
          category += `/${value.meta.docs.category}`;
        }

        this.eslintRules.set(`${plugin}/${key}`, {
          deprecated: value.meta.deprecated || false,
          category,
          ...value
        });
      });
    });
  }

  diffRules() {
    const { eslintRules, config } = this;

    const diff = new Map();

    eslintRules.forEach((value, key) => {
      if (!config.rules.hasOwnProperty(key)) {
        diff.set(key, {
          deprecated: value.meta.deprecated || false,
          category: value.meta.docs.category,
          ...value
        });
      }
    });

    return diff;
  }

  getCurrent() {
    const { rules } = this.config;
    return sortRules(rules);
  }

  getUnused(includeDeprecated = false) {
    console.log(includeDeprecated);
    let rules = this.diffRules();
    rules = sortRules(rules);

    if (!includeDeprecated) {
      rules = filter(rules, rule => !rule.deprecated);
    }

    return rules;
  }
}

module.exports = RuleFinder;
<commit_msg>fix(category): fix error caused by plugin syntax<commit_after>const { CLIEngine, Linter } = require('eslint');
const path = require('path');
const {
  filter,
  orderBy,
  forEach
} = require('lodash');

function sortRules(rules) {
  const results = [];

  rules.forEach((value, key) => {
    results.push({
      name: key,
      ...value
    });
  });

  return orderBy(results, ['category', 'deprecated', 'name']);
}

class RuleFinder {
  constructor(configFile) {
    const cf = path.join(process.cwd(), configFile);

    const cli = new CLIEngine({
      useEslintrc: false,
      configFile: cf
    });

    const linter = new Linter();

    this.config = cli.getConfigForFile();
    this.eslintRules = linter.getRules();
    this.getPluginRules();
  }

  getPluginRules() {
    const { plugins } = this.config;

    forEach(plugins, (plugin) => {
      // eslint-disable-next-line
      const pluginConfig = require(`eslint-plugin-${plugin}`);

      forEach(pluginConfig.rules, (value, key) => {
        let category = plugin;

        if (value.meta.docs && value.meta.docs.category) {
          category += `/${value.meta.docs.category}`;
        }

        this.eslintRules.set(`${plugin}/${key}`, {
          ...value,
          meta: {
            ...value.meta,
            docs: {
              ...value.meta,
              category
            }
          }
        });
      });
    });
  }

  diffRules() {
    const { eslintRules, config } = this;

    const diff = new Map();

    eslintRules.forEach((value, key) => {
      console.log(key, value.meta);
      if (!config.rules.hasOwnProperty(key)) {
        diff.set(key, {
          deprecated: value.meta.deprecated || false,
          category: value.meta.docs.category,
          ...value
        });
      }
    });

    return diff;
  }

  getCurrent() {
    const { rules } = this.config;
    return sortRules(rules);
  }

  getUnused(includeDeprecated = false) {
    let rules = this.diffRules();
    rules = sortRules(rules);

    if (!includeDeprecated) {
      rules = filter(rules, rule => !rule.deprecated);
    }

    return rules;
  }
}

module.exports = RuleFinder;
<|endoftext|>


<commit_before>'use strict';
module.exports = function (str, opts) {
	if (typeof str !== 'string') {
		throw new TypeError('Expected a string');
	}

	opts = opts || {};

	return str + ' & ' + (opts.postfix || 'rainbows');
};
<commit_msg>Functional milestone. Export webpack config<commit_after>var debug = require('./lib/debug.js')('index');

var devConfig = require('./lib/config.dev.js');
var prodConfig = require('./lib/config.prod.js');
var removeKeys = require('./lib/removeKeys.js');

// Custom arguments that should be removed from the final config we export to
// webpack
var customArgs = [
  'in',
  'out',
  'cleanOnBuild',
  'isDev',
];

function validateConfig(config) {
  if (!config || typeof config !== 'object') {
    debug('Non-object was passed as config');
    throw new TypeError('Expected a configuration object');
  }

  debug('Validating required config params');
  ['in', 'out'].forEach(function(opt) {
    if (!config[opt]) {
      debug('`' + opt + '` was not defined on configuration object');
      throw new Error('Configuration object must have `in` and `out` properties.');
    }
  });
}

module.exports = function(config) {
  debug('Validating user defined config:', config);
  validateConfig(config);
  debug('Config valid. Exporting based on NODE_EN: ' + process.env.NODE_ENV);

  var finalConfig = removeKeys(
    config.isDev === false ? prodConfig(config) : devConfig(config),
    customArgs
  );

  debug('Exporting final config:', finalConfig);
  return finalConfig;
};
<|endoftext|>


<commit_before>var child = require('child_process');
var chokidar = require('chokidar');
var fs = require('fs');
var http = require('http');
var mime = require('mime');
var path = require('path');
var rdbg = require('rdbg');
var util = require('util');
var temp = require('temp');

function serve(options, callback) {
  var server = http.createServer(function(request, response) {
    if (request.url == '/') {
      response.setHeader('content-type', 'text/html');
      response.write('<!doctype html><meta charset="utf-8">');

      for (var key in options.scripts) {
        response.write('');
      }

      response.end('');
    } else {
      var filename = options.scripts[path.basename(request.url)];
      if (!filename) {
        var filename = path.join(options.cwd, request.url);
      }

      fs.exists(filename, function(exists) {
        if (exists) {
          response.setHeader('content-type', mime.lookup(filename));
          response.writeHead(200);

          var file = fs.createReadStream(filename);
          file.pipe(response);
        } else {
          response.writeHead(404);
          response.end('404');
        }
      });
    }
  });

  server.listen(options.port, options.host, callback);
  return server;
}

function compile(options, callback) {
  switch (options.compiler) {
    case 'browserify':
      var command = 'watchify';
      var args = [
        '-o',
        options.output,
      ];
      break;

    case 'webpack':
      var command = 'webpack';
      var args = [
        '--watch',
        '--output-file',
        options.output,
      ];
      break;

    case 'typescript':
      var command = 'tsc';
      var args = [
        '--watch',
        '--out',
        options.output,
      ];
      break;

    case 'coffeescript':
      var command = 'coffee';
      var args = [
        '--watch',
        '--out',
        options.output,
      ];
      break;

    default:
      var args = options.compiler.match(/'[^"]*'|"[^"]*"|\S+/g);
      var command = args.shift();
      break;
  }

  args = args.concat(options.args);

  var compiler = child.spawn(command, args);
  process.nextTick(function() {
    callback(null, compiler.stdout, compiler.stderr);
  });

  return compiler;
}

function watch(options, callback) {
  var files = Object.keys(options.scripts).map(function(key) {
    return path.dirname(options.scripts[key]);
  });

  var watcher = chokidar.watch(files, {
    persistent: true
  });

  if (callback) {
    watcher.once('ready', callback);
  }

  return watcher;
}

function debug(options, callback) {
  var bugger = rdbg.connect(options.debuggerPort, options.debuggerHost, function(target) {
    callback(null, target);
  });

  bugger.targets(function(targets) {
    var target = targets.filter(function(target) {
      return target.url.indexOf(options.host) > -1 && target.webSocketDebuggerUrl;
    })[0];

    bugger.attach(target);
  });

  return bugger;
}

function open(options, callback) {
  switch (options.client) {
    case 'chrome':
      var command = (function() {
        if (process.platform == 'win32') {
          var suffix = '\\Google\\Chrome\\Application\\chrome.exe';
          var prefixes = [
            process.env['LOCALAPPDATA'],
            process.env['PROGRAMFILES'],
            process.env['PROGRAMFILES(X86)'],
          ];

          var executables = prefixes.map(function(prefix) {
            return prefix + suffix;
          }).filter(function(path) {
            return fs.existsSync(path);
          });

          return executables[0];
        } else if (process.platform == 'darwin') {
          return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
        } else {
          return 'google-chrome';
        }
      }());

      var args = [
        '--remote-debugging-port=' + options.debuggerPort,
        '--no-first-run',
        '--no-default-browser-check',
        '--disable-translate',
        '--disable-default-apps',
        '--disable-popup-blocking',
        '--disable-zero-browsers-open-for-tests',
        '--user-data-dir=' + temp.mkdirSync('amok-chrome'),
      ];
      break;

    default:
      var args = options.client.match(/'[^"]*'|"[^"]*"|\S+/g);
      var command = args.shift();
      break;
  }

  var url = util.format('http://%s:%d', options.host, options.port);
  args.push(url);

  var client = child.spawn(command, args);

  process.nextTick(function() {
    callback(null, client.stdout, client.stderr);
  });

  return client;
}

module.exports.serve = serve;
module.exports.watch = watch;
module.exports.compile = compile;
module.exports.debug = debug;
module.exports.open = open;
<commit_msg>Add support for babel compiler<commit_after>var child = require('child_process');
var chokidar = require('chokidar');
var fs = require('fs');
var http = require('http');
var mime = require('mime');
var path = require('path');
var rdbg = require('rdbg');
var util = require('util');
var temp = require('temp');

function serve(options, callback) {
  var server = http.createServer(function(request, response) {
    if (request.url == '/') {
      response.setHeader('content-type', 'text/html');
      response.write('<!doctype html><meta charset="utf-8">');

      for (var key in options.scripts) {
        response.write('');
      }

      response.end('');
    } else {
      var filename = options.scripts[path.basename(request.url)];
      if (!filename) {
        var filename = path.join(options.cwd, request.url);
      }

      fs.exists(filename, function(exists) {
        if (exists) {
          response.setHeader('content-type', mime.lookup(filename));
          response.writeHead(200);

          var file = fs.createReadStream(filename);
          file.pipe(response);
        } else {
          response.writeHead(404);
          response.end('404');
        }
      });
    }
  });

  server.listen(options.port, options.host, callback);
  return server;
}

function compile(options, callback) {
  switch (options.compiler) {
    case 'browserify':
      var command = 'watchify';
      var args = [
        '-o',
        options.output,
      ];
      break;

    case 'webpack':
      var command = 'webpack';
      var args = [
        '--watch',
        '--output-file',
        options.output,
      ];
      break;

    case 'typescript':
      var command = 'tsc';
      var args = [
        '--watch',
        '--out',
        options.output,
      ];
      break;

    case 'coffeescript':
      var command = 'coffee';
      var args = [
        '--watch',
        '--out',
        options.output,
      ];
      break;

    case 'babel':
      var command = 'babel';
      var args = [
        '--watch',
        '--out-file',
        options.output,
      ];
      break;

    default:
      var args = options.compiler.match(/'[^"]*'|"[^"]*"|\S+/g);
      var command = args.shift();
      break;
  }

  args = args.concat(options.args);

  var compiler = child.spawn(command, args);
  process.nextTick(function() {
    callback(null, compiler.stdout, compiler.stderr);
  });

  return compiler;
}

function watch(options, callback) {
  var files = Object.keys(options.scripts).map(function(key) {
    return path.dirname(options.scripts[key]);
  });

  var watcher = chokidar.watch(files, {
    persistent: true
  });

  if (callback) {
    watcher.once('ready', callback);
  }

  return watcher;
}

function debug(options, callback) {
  var bugger = rdbg.connect(options.debuggerPort, options.debuggerHost, function(target) {
    callback(null, target);
  });

  bugger.targets(function(targets) {
    var target = targets.filter(function(target) {
      return target.url.indexOf(options.host) > -1 && target.webSocketDebuggerUrl;
    })[0];

    bugger.attach(target);
  });

  return bugger;
}

function open(options, callback) {
  switch (options.client) {
    case 'chrome':
      var command = (function() {
        if (process.platform == 'win32') {
          var suffix = '\\Google\\Chrome\\Application\\chrome.exe';
          var prefixes = [
            process.env['LOCALAPPDATA'],
            process.env['PROGRAMFILES'],
            process.env['PROGRAMFILES(X86)'],
          ];

          var executables = prefixes.map(function(prefix) {
            return prefix + suffix;
          }).filter(function(path) {
            return fs.existsSync(path);
          });

          return executables[0];
        } else if (process.platform == 'darwin') {
          return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
        } else {
          return 'google-chrome';
        }
      }());

      var args = [
        '--remote-debugging-port=' + options.debuggerPort,
        '--no-first-run',
        '--no-default-browser-check',
        '--disable-translate',
        '--disable-default-apps',
        '--disable-popup-blocking',
        '--disable-zero-browsers-open-for-tests',
        '--user-data-dir=' + temp.mkdirSync('amok-chrome'),
      ];
      break;

    default:
      var args = options.client.match(/'[^"]*'|"[^"]*"|\S+/g);
      var command = args.shift();
      break;
  }

  var url = util.format('http://%s:%d', options.host, options.port);
  args.push(url);

  var client = child.spawn(command, args);

  process.nextTick(function() {
    callback(null, client.stdout, client.stderr);
  });

  return client;
}

module.exports.serve = serve;
module.exports.watch = watch;
module.exports.compile = compile;
module.exports.debug = debug;
module.exports.open = open;
<|endoftext|>


<commit_before>var request = require("request");
var express = require("express");
var app = express();

var gif_response = "";

app.get('/:gif_type', function(req, response) {
    response.send("Hello World");
    console.log("I see...");

  //  request("http://api.giphy.com/v1/gifs/search?q=" + req.params["gif_type"] + "&limit=100&api_key=dc6zaTOxFJmzC", function(error, response, body) {
      request("http://api.giphy.com/v1/gifs/recent?api_key=dc6zaTOxFJmzC", function(error, response, body ) {
        if(error) {
            console.log("failed to query api");
        }
        else {
            var json_response = JSON.parse(body);
            console.log(json_response);
        }
    });
});

var port = process.env.PORT || 5000;
app.listen(port, function() {
    console.log("Listening on " + port);
});
<commit_msg>print the giphy stuff<commit_after>var request = require("request");
var express = require("express");
var app = express();

var gif_response = "";

app.get('/:gif_type', function(req, response) {
    console.log("I see...");

  //  request("http://api.giphy.com/v1/gifs/search?q=" + req.params["gif_type"] + "&limit=100&api_key=dc6zaTOxFJmzC", function(error, response, body) {
      request("http://api.giphy.com/v1/gifs/recent?api_key=dc6zaTOxFJmzC", function(error, response, body ) {
        if(error) {
            console.log("failed to query api");
        }
        else {
            var json_response = JSON.parse(body);
            console.log(json_response);
            gif_response = json_response;
        }
    });

    response.send(json_response);

});

var port = process.env.PORT || 5000;
app.listen(port, function() {
    console.log("Listening on " + port);
});
<|endoftext|>


<commit_before>// Based on Split by Name Webpack Plugin – https://github.com/soundcloud/split-by-name-webpack-plugin

function regExpQuote(str) {
  return (str + '').replace(/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g, '\\$&');
}

function SplitByPathPlugin(buckets, config) {
  config = config || {};
  config.ignore = config.ignore || [];

  if (!Array.isArray(config.ignore)) {
    config.ignore = [ config.ignore ];
  }

  config.ignore = config.ignore.map(function (item) {
    if (item instanceof RegExp) {
      return item;
    }

    return new RegExp('^' + regExpQuote(item));
  });

  this.ignore = config.ignore;
  this.buckets = buckets.slice(0).map(function (bucket) {
    if (!Array.isArray(bucket.path)) {
      bucket.path = [ bucket.path ];
    }

    bucket.path = bucket.path.map(function (path) {
      if (path instanceof RegExp) {
        return path;
      }

      return new RegExp('^' + regExpQuote(path));
    });

    return bucket;
  });
}

SplitByPathPlugin.prototype.apply = function(compiler) {
  var buckets = this.buckets;
  var ignore = this.ignore;

  function findMatchingBucket(chunk) {
    var match = null;

    if (!chunk.userRequest) {
      return match;
    }

    var userRequest = chunk.userRequest;
    var lastIndex = userRequest.lastIndexOf('!');
    if (lastIndex !== -1) {
      userRequest = userRequest.substring(lastIndex + 1);
    }

    for (var i in ignore) {
      if (ignore[i].test(userRequest)) {
        return match;
      }
    }

    buckets.some(function (bucket) {
      return bucket.path.some(function (path) {
        if (path.test(userRequest)) {
          match = bucket;
          return true;
        }
      });
    });

    return match;
  }

  compiler.plugin('compilation', function (compilation) {
    var extraChunks = {};

    // Find the chunk which was already created by this bucket.
    // This is also the grossest function name I've written today.
    function bucketToChunk(bucket) {
      return extraChunks[bucket.name];
    }

    compilation.plugin('optimize-chunks', function (chunks) {
      var addChunk = this.addChunk.bind(this);
      chunks
        // only parse the entry chunk
        .filter(function (chunk) {
          return chunk.entry;
        })
        .forEach(function (chunk) {
          chunk.modules.slice().forEach(function (mod) {
            var bucket = findMatchingBucket(mod),
                newChunk;
            if (!bucket) {
              // it stays in the original bucket
              return;
            }
            if (!(newChunk = bucketToChunk(bucket))) {
              newChunk = extraChunks[bucket.name] = addChunk(bucket.name);
            }
            // add the module to the new chunk
            newChunk.addModule(mod);
            mod.addChunk(newChunk);
            // remove it from the existing chunk
            mod.removeChunk(chunk);
          });

          buckets
            .map(bucketToChunk)
            .filter(Boolean)
            .concat(chunk)
            .forEach(function (bucket, index, allChunks) { // allChunks = [bucket0, bucket1, .. bucketN, orig]
              if (index) { // not the first one, they get the first chunk as a parent
                bucket.parents = [allChunks[0]];
              } else { // the first chunk, it gets the others as 'sub' chunks
                bucket.chunks = allChunks.slice(1);
              }

              bucket.initial = true;
              bucket.entry = !index;
            });
        });
    });
  });
};

module.exports = SplitByPathPlugin;
<commit_msg>Fixed webpack extract text issue without ignore<commit_after>// Based on Split by Name Webpack Plugin – https://github.com/soundcloud/split-by-name-webpack-plugin

function regExpQuote(str) {
  return (str + '').replace(/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g, '\\$&');
}

function SplitByPathPlugin(buckets, config) {
  config = config || {};
  config.ignore = config.ignore || [];

  if (!Array.isArray(config.ignore)) {
    config.ignore = [ config.ignore ];
  }

  config.ignore = config.ignore.map(function (item) {
    if (item instanceof RegExp) {
      return item;
    }

    return new RegExp('^' + regExpQuote(item));
  });

  this.ignore = config.ignore;
  this.buckets = buckets.slice(0).map(function (bucket) {
    if (!Array.isArray(bucket.path)) {
      bucket.path = [ bucket.path ];
    }

    bucket.path = bucket.path.map(function (path) {
      if (path instanceof RegExp) {
        return path;
      }

      return new RegExp('^' + regExpQuote(path));
    });

    return bucket;
  });
}

SplitByPathPlugin.prototype.apply = function(compiler) {
  var buckets = this.buckets;
  var ignore = this.ignore;

  function findMatchingBucket(chunk) {
    var match = null;

    if (!chunk.userRequest) {
      return match;
    }

    var userRequest = chunk.userRequest;
    var lastIndex = userRequest.lastIndexOf('!');
    if (lastIndex !== -1) {
      userRequest = userRequest.substring(lastIndex + 1);
    }

    for (var i in ignore) {
      if (ignore[i].test(userRequest)) {
        return match;
      }
    }

    buckets.some(function (bucket) {
      return bucket.path.some(function (path) {
        if (path.test(userRequest)) {
          match = bucket;
          return true;
        }
      });
    });

    return match;
  }

  compiler.plugin('compilation', function (compilation) {
    var extraChunks = {};

    // Find the chunk which was already created by this bucket.
    // This is also the grossest function name I've written today.
    function bucketToChunk(bucket) {
      return extraChunks[bucket.name];
    }

    compilation.plugin('optimize-chunks', function (chunks) {
      var addChunk = this.addChunk.bind(this);
      chunks
        // only parse the entry chunk
        .filter(function (chunk) {
          return chunk.entry && chunk.name;
        })
        .forEach(function (chunk) {
          chunk.modules.slice().forEach(function (mod) {
            var bucket = findMatchingBucket(mod),
                newChunk;
            if (!bucket) {
              // it stays in the original bucket
              return;
            }
            if (!(newChunk = bucketToChunk(bucket))) {
              newChunk = extraChunks[bucket.name] = addChunk(bucket.name);
            }
            // add the module to the new chunk
            newChunk.addModule(mod);
            mod.addChunk(newChunk);
            // remove it from the existing chunk
            mod.removeChunk(chunk);
          });

          buckets
            .map(bucketToChunk)
            .filter(Boolean)
            .concat(chunk)
            .forEach(function (bucket, index, allChunks) { // allChunks = [bucket0, bucket1, .. bucketN, orig]
              if (index) { // not the first one, they get the first chunk as a parent
                bucket.parents = [allChunks[0]];
              } else { // the first chunk, it gets the others as 'sub' chunks
                bucket.chunks = allChunks.slice(1);
              }

              bucket.initial = true;
              bucket.entry = !index;
            });
        });
    });
  });
};

module.exports = SplitByPathPlugin;
<|endoftext|>


<commit_before>var request = require('request');
var apiUrl = 'https://geo.api.gouv.fr/';

function Geo() {
  this.apiUrl = apiUrl;
  this.request = req;
}

function req(method, apiUrl, data, callback) {
  var options = {};
  options.method = method;
  options.url = apiUrl;
  options.headers = { 'Content-Type': 'application/json' };
  request(options, function (error, response, body) {
    if (error) return callback(null, JSON.parse(error));
    switch (response.statusCode) {
      case 200:
        return callback(null, JSON.parse(body));
        break;
      case 400:
        return callback('BAD_REQUEST');
        break;
      case 404:
        return callback('NOT_FOUND');
        break;
    }
  });
}

Geo.prototype.getTowns = function(params, callback) {
  if (!params) return callback('MISSING_PARAMETERS');
  var url = apiUrl + 'communes/?';
  if (params.codePostal) url = (url.substr(url.length-1, 1) === '?' ? url + 'codePostal=' + params.codePostal : url + '&codePostal=' + params.codePostal);
  if (params.lat) url = (url.substr(url.length-1, 1) === '?' ? url + 'lat=' + params.lat : url + '&lat=' + params.lat);
  if (params.lon) url = (url.substr(url.length-1, 1) === '?' ? url + 'lon=' + params.lon : url + '&lon=' + params.lon);
  if (params.nom) url = (url.substr(url.length-1, 1) === '?' ? url + 'nom=' + params.nom : url + '&nom=' + params.nom);
  if (params.boost) url = (url.substr(url.length-1, 1) === '?' ? url + 'boost=' + params.boost : url + '&boost=' + params.boost);
  if (params.code) url = (url.substr(url.length-1, 1) === '?' ? url + 'code=' + params.code : url + '&code=' + params.code);
  if (params.codeDepartement) url = (url.substr(url.length-1, 1) === '?' ? url + 'codeDepartement=' + params.codeDepartement : url + '&codeDepartement=' + params.codeDepartement);
  if (params.codeRegion) url = (url.substr(url.length-1, 1) === '?' ? url + 'codeRegion=' + params.codeRegion : url + '&codeRegion=' + params.codeRegion);
  if (params.fields) url = (url.substr(url.length-1, 1) === '?' ? url + 'fields=' + params.fields : url + '&fields=' + params.fields);
  if (params.format) url = (url.substr(url.length-1, 1) === '?' ? url + 'format=' + params.format : url + '&format=' + params.format);
  if (params.geometry) url = (url.substr(url.length-1, 1) === '?' ? url + 'geometry=' + params.geometry : url + '&geometry=' + params.geometry);
  req('GET', url, null, function(error, result) {
    if (error) return callback(error);
    else return callback(null, result);
  });
}

Geo.prototype.getTown = function(params, callback) {
  if (!params) return callback('MISSING_PARAMETERS');
  if (!params.code) return callback('MISSING_CODE');
  var url = apiUrl + 'communes/' + params.code + '?';
  if (params.code) url = (url.substr(url.length-1, 1) === '?' ? url + 'code=' + params.code : url + '&code=' + params.code);
  if (params.fields) url = (url.substr(url.length-1, 1) === '?' ? url + 'fields=' + params.fields : url + '&fields=' + params.fields);
  if (params.format) url = (url.substr(url.length-1, 1) === '?' ? url + 'format=' + params.format : url + '&format=' + params.format);
  if (params.geometry) url = (url.substr(url.length-1, 1) === '?' ? url + 'geometry=' + params.geometry : url + '&geometry=' + params.geometry);
  req('GET', url, null, function(error, result) {
    if (error) return callback(error);
    else return callback(null, result);
  });
}

module.exports = Geo;
<commit_msg>Add getDepartments method<commit_after>var request = require('request');
var apiUrl = 'https://geo.api.gouv.fr/';

function Geo() {
  this.apiUrl = apiUrl;
  this.request = req;
}

function req(method, apiUrl, data, callback) {
  var options = {};
  options.method = method;
  options.url = apiUrl;
  options.headers = { 'Content-Type': 'application/json' };
  request(options, function (error, response, body) {
    if (error) return callback(null, JSON.parse(error));
    switch (response.statusCode) {
      case 200:
        return callback(null, JSON.parse(body));
        break;
      case 400:
        return callback('BAD_REQUEST');
        break;
      case 404:
        return callback('NOT_FOUND');
        break;
    }
  });
}

Geo.prototype.getTowns = function(params, callback) {
  if (!params) return callback('MISSING_PARAMETERS');
  var url = apiUrl + 'communes/?';
  if (params.codePostal) url = (url.substr(url.length-1, 1) === '?' ? url + 'codePostal=' + params.codePostal : url + '&codePostal=' + params.codePostal);
  if (params.lat) url = (url.substr(url.length-1, 1) === '?' ? url + 'lat=' + params.lat : url + '&lat=' + params.lat);
  if (params.lon) url = (url.substr(url.length-1, 1) === '?' ? url + 'lon=' + params.lon : url + '&lon=' + params.lon);
  if (params.nom) url = (url.substr(url.length-1, 1) === '?' ? url + 'nom=' + params.nom : url + '&nom=' + params.nom);
  if (params.boost) url = (url.substr(url.length-1, 1) === '?' ? url + 'boost=' + params.boost : url + '&boost=' + params.boost);
  if (params.code) url = (url.substr(url.length-1, 1) === '?' ? url + 'code=' + params.code : url + '&code=' + params.code);
  if (params.codeDepartement) url = (url.substr(url.length-1, 1) === '?' ? url + 'codeDepartement=' + params.codeDepartement : url + '&codeDepartement=' + params.codeDepartement);
  if (params.codeRegion) url = (url.substr(url.length-1, 1) === '?' ? url + 'codeRegion=' + params.codeRegion : url + '&codeRegion=' + params.codeRegion);
  if (params.fields) url = (url.substr(url.length-1, 1) === '?' ? url + 'fields=' + params.fields : url + '&fields=' + params.fields);
  if (params.format) url = (url.substr(url.length-1, 1) === '?' ? url + 'format=' + params.format : url + '&format=' + params.format);
  if (params.geometry) url = (url.substr(url.length-1, 1) === '?' ? url + 'geometry=' + params.geometry : url + '&geometry=' + params.geometry);
  req('GET', url, null, function(error, result) {
    if (error) return callback(error);
    else return callback(null, result);
  });
}

Geo.prototype.getTown = function(params, callback) {
  if (!params) return callback('MISSING_PARAMETERS');
  if (!params.code) return callback('MISSING_CODE');
  var url = apiUrl + 'communes/' + params.code + '?';
  if (params.code) url = (url.substr(url.length-1, 1) === '?' ? url + 'code=' + params.code : url + '&code=' + params.code);
  if (params.fields) url = (url.substr(url.length-1, 1) === '?' ? url + 'fields=' + params.fields : url + '&fields=' + params.fields);
  if (params.format) url = (url.substr(url.length-1, 1) === '?' ? url + 'format=' + params.format : url + '&format=' + params.format);
  if (params.geometry) url = (url.substr(url.length-1, 1) === '?' ? url + 'geometry=' + params.geometry : url + '&geometry=' + params.geometry);
  req('GET', url, null, function(error, result) {
    if (error) return callback(error);
    else return callback(null, result);
  });
}

Geo.prototype.getDepartments = function(params, callback) {
  var url = apiUrl + 'departements/?';
  if (params.code) url = (url.substr(url.length-1, 1) === '?' ? url + 'code=' + params.code : url + '&code=' + params.code);
  if (params.codeRegion) url = (url.substr(url.length-1, 1) === '?' ? url + 'codeRegion=' + params.codeRegion : url + '&codeRegion=' + params.codeRegion);
  if (params.nom) url = (url.substr(url.length-1, 1) === '?' ? url + 'nom=' + params.nom : url + '&nom=' + params.nom);
  if (params.fields) url = (url.substr(url.length-1, 1) === '?' ? url + 'fields=' + params.fields : url + '&fields=' + params.fields);
  req('GET', url, null, function(error, result) {
    if (error) return callback(error);
    else return callback(null, result);
  });
}

module.exports = Geo;
<|endoftext|>


<commit_before>var dgram = require('dgram')

function Messenger (node, port, address) {
//object to deal with networking.
//will contain a dgram socket.
//each node will own one messenger object.
    this.node = node
    this.port = port
    this.address = address
    this.socket = dgram.createSocket("udp4")
    this.socket.bind(port, address)
    this.close = function () {
        this.socket.close()
    }
    this.createMessage = function (obj) {
        return new Buffer(JSON.stringify(obj))
    }
    this.sendAcceptRequest = function () {
        var acceptReq = this.createMessage({
            type: "accept",
            proposalId: this.node.proposalId,
            proposal: this.node.proposal,
            address: this.address,
            port: this.port
        })
        for (var acceptor in this.node.acceptors) {
            this.socket.send(acceptReq, 0, acceptReq.length, this.node.acceptors[acceptor][0][0], this.node.acceptors[acceptor][0][1])
        }
    }
    this.sendPromise = function (port, address) {
        var promise = this.createMessage({
            type: "promise",
            proposalId: this.node.promisedId,
            lastValue: this.node.lastAccepted,
            lastAcceptedId: this.node.lastAcceptedId,
            address: this.address,
            port: this.port,
            id: this.node.id
        })
        this.socket.send(promise, 0, promise.length, port, address)
    }
    this.sendPrepare = function () {
        var proposal = this.createMessage({
            type: "prepare",
            address: this.node.address,
            port: this.node.port,
            nodeId: this.node.id,
            proposalId: this.node.proposalId
        })
        this.sendToAcceptors(proposal)
    }
    this.sendNACK = function(address, port, promise) {
        var nack = this.createMessage({
            type: "NACK",
            address: this.address,
            port: this.port,
            highestProposalNum: promise
        })
        this.socket.send(nack, 0, nack.length, port, address)
    }
    this.sendIdentRequest = function(from) {
        var identReq = this.createMessage({
            type: "identify",
            nodeInfo: from,
            address: this.address,
            port: this.port
        })
        this.sendToAcceptors(identReq)
        node.waiting.push(from)
    }

    this.sendPrevious = function (port, address, proposalId, proposal) {
        var prevAccepted = this.createMessage({
            type: proposal ? "accepted" : "promised",
            address: this.address,
            port: this.port,
            proposalId: proposalId,
            proposal: proposal
        })
        this.socket.send(prevAccepted, 0, prevAccepted.length, port, address)
    }

    this.notifyProposers = function (proposers, messageType, info) {
        var message = this.createMessage({
            type: messageType,
            info: info
        })

        for (var proposer in proposers) {
            this.socket.send(message, 0, message.length, proposers[proposer][0], proposers[proposer][1])
        }
    }

    this.sendToAcceptors = function (message) {
        if (JSON.stringify(this.node.acceptors) == "{}") {
            this.node.pendingMessage = message
            return
        }
        for (var acceptor in this.node.acceptors) {
            this.socket.send(message, 0, message.length, this.node.acceptors[acceptor][0][0], this.node.acceptors[acceptor][0][1])
        }
    }
    this.sendToLearners = function (message) {
        for (var learner in this.node.learners) {
            this.socket.send(message, 0, message.length, this.node.learners[learner][0], this.node.learners[learner][1])
        }
    }
    this.send = function (message, address, port) {
        this.socket.send(message, 0, message.length, port, address)
    }

    this.setMessageHandlers = function (node, role) {
      if (role == "Proposer") {
        this.socket.on("message", function (message, rinfo) {
            message = JSON.parse(message.toString())
            if (message.type == "promise") {
                node.receivePromise(message.id, message.address, message.proposalId, message.lastValue, message.lastAcceptedId)
            } else if (message.type == "proposal") {
                node.setProposal(message.proposal)
            } else if (message.type == "accepted") {
                node.receiveAccept(message.from, message.proposalId, message.value)
            } else if (message.type == "known") {
                node.acceptors[message.nodeId] = [[message.nodePort, message.nodeAddress], null]
                var index = this.node.waiting.indexOf(message.nodeId)
                if (index > -1) {
                    node.promises.push(message.nodeId)
                    node.waiting.splice(index, 1)
                }
            } else if (message.type == "NACK") {
                node.prepare(true, message.highestProposalNum)
            } else if (message.type == "new acceptor") {
                node.acceptors[message.nodeId] = [message.info, null]
            }
        })
      } else if (role == "Acceptor") {
        this.socket.on("message", function (message, rinfo) {
            message = JSON.parse(message.toString())
            if (message.type == "prepare") {
                node.receivePrepare(message.port, message.address, message.proposalId)
            } else if (message.type == "accept") {
                node.receiveAcceptRequest(message.address, message.port, message.proposalId, message.proposal)
            } else if (message.type == "identify") {
                if (node.knownNode(message.nodeInfo)) {
                    // send back 'known' if address belongs to known acceptor
                }
            }
        })
      } else if (role == "Learner") {
        this.socket.on("message", function (message, rinfo) {
            message = JSON.parse(message.toString())
            if (message.type == "accepted") {
                node.receiveAccept(message.from, message.proposalId, message.proposal)
            }
        })
      }
    }
}

function Node (id, address, port, generateProposalId, currentRound) { // :: Int -> Int -> Int -> Socket -> (Int) -> Node
    this.id = id
    this.address = address
    this.port = port
    this.acceptors = {} // ID -> [[port, address], last proposal]
    this.proposal = null
    this.value = null
    this.roles = []
    this.quorum = null
    this.generateProposalId = generateProposalId
    this.messenger = new Messenger(this, port, address)
    if (currentRound) {
        this.currentRound = currentRound
    } else {
        this.currentRound = 1
    }
    this.end = function () {
        this.messenger.close()
        // should probably persist stateLog here
    }
}

function Cluster (nodes) { // :: [Node] -> Cluster
    this.learners = {}
    this.acceptors = {}
    this.proposers = {}

    this.setQuorum = function () {
        if (this.acceptors.length % 2 == 0) {
            this.quorum = this.acceptors.length / 2 + 1
        } else {
            this.quorum = Math.ceil(Object.keys(this.acceptors).length / 2)
        }
        if (nodes) {
          for (var node in nodes) {
              node.quorum = this.quorum
          }
        }
    }

    this.addNode = function (node) {
        if (node.roles) {
            if (node.roles.indexOf('Learner') > -1) {
             this.learners[node.id] = [node.port, node.address]
            }
            if (node.roles.indexOf('Acceptor') > -1) {
                this.acceptors[node.id] = [node.port, node.address]
                node.messenger.notifyProposers(this.proposers, "new acceptor", {nodeId: node.id, info: this.acceptors[node.id]})
            }
            if (node.roles.indexOf('Proposer') > -1) {
                this.proposers[node.id] = [node.port, node.address]
            }
            for (var id in this.acceptors) {
                node.acceptors[id] = [this.acceptors[id], null]
            }
        }
    }
    if (nodes) {
        for (var node in nodes) {
            this.addNode(node)
        }
    }
}

function initializeProposer (node, cluster) { // :: Node -> Cluster -> a ->
    node.roles.push('Proposer')
    node.proposalId = null
    node.lastAcceptedId = null
    node.promises = []
    node.nextProposalNum = 1
    node.waiting = []
    node.messenger.setMessageHandlers(node, 'Proposer')

    node.startProposal = function (proposal, callback) {
        node.promises = []
        node.proposal = proposal
        if (callback) {
            node.callback = callback
        }
        node.prepare(false)
    }

    node.prepare = function (nack, seed) {
        node.proposalId = seed ? node.generateProposalId(seed) : node.generateProposalId()
        if (nack) {
            node.callback({
                event: "NACK",
                newProposalId: node.proposalId
            })
        }
        node.messenger.sendPrepare()
    }

    node.receivePromise = function (fromId, fromAddress, proposalId, lastValue, lastAcceptedId) { // :: Int -> Int -> Int -> a ->
        if (proposalId != node.proposalId) {
            return
        }

        if (node.acceptors[fromId] == null) {
            node.messenger.sendIdentRequest([fromId, fromAddress])
            return
        }

        if (node.promises.indexOf(fromId) < 0) {
            node.promises.push(fromId)
        } else { return } // we have already received a promise. Something is probably wrong.

        if (lastAcceptedId > node.lastAcceptedId) {
            node.lastAcceptedId = lastAcceptedId
            if (lastValue) { node.proposal = lastValue }
        }

        if (node.promises.length >= node.quorum) {
            if (node.proposal) {
                node.messenger.sendAcceptRequest()
            }
        }
    }

    node.receiveAccept = function (from, proposalId, proposal) {
        if (node.callback) {
            console.log('reached callback')
            node.callback(proposal)
        }
        node.messenger.sendToLearners(node.messenger.createMessage({
            type: "accepted",
            proposalId: proposalId,
            value: proposal,
            from: from
        }))
    }

    node.receivePrevious = function (from, proposalId) {
    }

    if (cluster) {
      cluster.addNode(node)
      cluster.setQuorum()
    }
}

function initializeAcceptor (node, cluster, callback) { // :: Node -> Cluster ->
    node.roles.push('Acceptor')
    node.stateLog = {}
    // Sync stateLog with acceptors in cluster
    node.promisedId = null
    node.acceptedId = null
    node.lastAccepted = null
    node.learners = cluster.learners
    node.messenger.setMessageHandlers(node, 'Acceptor')
    if (callback) { node.callback = callback }

    node.receivePrepare = function (port, address, proposalId) {
        if (proposalId == node.promisedId) {
            return
        } else if (proposalId > node.promisedId) {
            node.promisedId = proposalId
            node.messenger.sendPromise(port, address)
        } else {
            node.messenger.sendPrevious(port, address, proposalId)
        }
    }

    node.receiveAcceptRequest = function (address, port, proposalId, proposal) { // :: Int -> Int -> a ->
        console.log('accepted')
        if (proposalId == node.promisedId) {
            node.promisedId = proposalId
            node.acceptedId = proposalId
            node.value = proposal
            var message = node.messenger.createMessage({
                type: "accepted",
                value: proposal,
                address: address,
                port: port,
                proposalId: proposalId
            })
            node.messenger.sendToAcceptors(message)
            node.messenger.sendToLearners(message)
            node.messenger.send(message, address, port)
            node.stateLog[Date.now()] = {round: node.currentRound, value: proposal, leader: {address: address, port: port}}
            if (node.callback) {
                node.callback()
            }
        } else if (proposalId < node.promisedId) {
            node.messenger.sendPrevious(port, address, proposalId, proposal)
        } else {
            node.messenger.sendNACK(address, port, node.promisedId)
        }
    }

    node.knownNode = function (info) {
        return (node.acceptors[from[0]][0] == from[1])
    }

    if (cluster) {
      cluster.addNode(node)
      cluster.setQuorum()
    }
}

function initializeLearner (node, cluster, callback) { // :: Node -> Cluster ->
    node.roles.push('Learner')
    node.finalValue = null
    node.stateLog = {}
    node.finalProposalId = null
    if (callback) { node.callback = callback }

    node.proposals = {} // proposal ID -> [accept count, retain count, value]

    node.receiveAccept = function (from, proposalId, acceptedValue) { // :: Int -> Int -> a ->
        if (node.finalValue != null) {
            return
        }

        var last = node.acceptors[from][1]
        if (last) {
            if (last > proposalId) { return }
            node.acceptors[from][1] = proposalId

            oldProposal = node.proposals[last]
            oldProposal[1] -= 1
            if (oldProposal[1] == 0) { delete node.proposals[last] }
        }

        if (node.proposals[proposalId] == null) {
            node.proposals[proposalId] = [1, 1, acceptedValue]
        }

        if (node.proposals[proposalId][0] == node.quorum) { // round over
            node.finalValue = acceptedValue
            node.finalProposalId = proposalId
            console.log(node.finalValue)
        }
    }
    if (cluster) {
      cluster.addNode(node)
      cluster.setQuorum()
    }
}

exports.Messenger = Messenger
exports.Node = Node
exports.initializeProposer = initializeProposer
exports.initializeAcceptor = initializeAcceptor
exports.initializeLearner = initializeLearner
exports.Cluster = Cluster
<commit_msg>Fix callbacks.<commit_after>var dgram = require('dgram')

function Messenger (node, port, address) {
//object to deal with networking.
//will contain a dgram socket.
//each node will own one messenger object.
    this.node = node
    this.port = port
    this.address = address
    this.socket = dgram.createSocket("udp4")
    this.socket.bind(port, address)
    this.close = function () {
        this.socket.close()
    }
    this.createMessage = function (obj) {
        return new Buffer(JSON.stringify(obj))
    }
    this.sendAcceptRequest = function () {
        var acceptReq = this.createMessage({
            type: "accept",
            proposalId: this.node.proposalId,
            proposal: this.node.proposal,
            address: this.address,
            port: this.port
        })
        for (var acceptor in this.node.acceptors) {
            this.socket.send(acceptReq, 0, acceptReq.length, this.node.acceptors[acceptor][0][0], this.node.acceptors[acceptor][0][1])
        }
    }
    this.sendPromise = function (port, address) {
        var promise = this.createMessage({
            type: "promise",
            proposalId: this.node.promisedId,
            lastValue: this.node.lastAccepted,
            lastAcceptedId: this.node.lastAcceptedId,
            address: this.address,
            port: this.port,
            id: this.node.id
        })
        this.socket.send(promise, 0, promise.length, port, address)
    }
    this.sendPrepare = function () {
        var proposal = this.createMessage({
            type: "prepare",
            address: this.node.address,
            port: this.node.port,
            nodeId: this.node.id,
            proposalId: this.node.proposalId
        })
        this.sendToAcceptors(proposal)
    }
    this.sendNACK = function(address, port, promise) {
        var nack = this.createMessage({
            type: "NACK",
            address: this.address,
            port: this.port,
            highestProposalNum: promise
        })
        this.socket.send(nack, 0, nack.length, port, address)
    }
    this.sendIdentRequest = function(from) {
        var identReq = this.createMessage({
            type: "identify",
            nodeInfo: from,
            address: this.address,
            port: this.port
        })
        this.sendToAcceptors(identReq)
        node.waiting.push(from)
    }

    this.sendPrevious = function (port, address, proposalId, proposal) {
        var prevAccepted = this.createMessage({
            type: proposal ? "accepted" : "promised",
            address: this.address,
            port: this.port,
            proposalId: proposalId,
            proposal: proposal
        })
        this.socket.send(prevAccepted, 0, prevAccepted.length, port, address)
    }

    this.notifyProposers = function (proposers, messageType, info) {
        var message = this.createMessage({
            type: messageType,
            info: info
        })

        for (var proposer in proposers) {
            this.socket.send(message, 0, message.length, proposers[proposer][0], proposers[proposer][1])
        }
    }

    this.sendToAcceptors = function (message) {
        if (JSON.stringify(this.node.acceptors) == "{}") {
            this.node.pendingMessage = message
            return
        }
        for (var acceptor in this.node.acceptors) {
            this.socket.send(message, 0, message.length, this.node.acceptors[acceptor][0][0], this.node.acceptors[acceptor][0][1])
        }
    }
    this.sendToLearners = function (message) {
        for (var learner in this.node.learners) {
            this.socket.send(message, 0, message.length, this.node.learners[learner][0], this.node.learners[learner][1])
        }
    }
    this.send = function (message, address, port) {
        this.socket.send(message, 0, message.length, port, address)
    }

    this.setMessageHandlers = function (node, role) {
      if (role == "Proposer") {
        this.socket.on("message", function (message, rinfo) {
            message = JSON.parse(message.toString())
            if (message.type == "promise") {
                node.receivePromise(message.id, message.address, message.proposalId, message.lastValue, message.lastAcceptedId)
            } else if (message.type == "proposal") {
                node.setProposal(message.proposal)
            } else if (message.type == "accepted") {
                node.receiveAccept(message.from, message.proposalId, message.value)
            } else if (message.type == "known") {
                node.acceptors[message.nodeId] = [[message.nodePort, message.nodeAddress], null]
                var index = this.node.waiting.indexOf(message.nodeId)
                if (index > -1) {
                    node.promises.push(message.nodeId)
                    node.waiting.splice(index, 1)
                }
            } else if (message.type == "NACK") {
                node.prepare(true, message.highestProposalNum)
            } else if (message.type == "new acceptor") {
                node.acceptors[message.nodeId] = [message.info, null]
            }
        })
      } else if (role == "Acceptor") {
        this.socket.on("message", function (message, rinfo) {
            message = JSON.parse(message.toString())
            if (message.type == "prepare") {
                node.receivePrepare(message.port, message.address, message.proposalId)
            } else if (message.type == "accept") {
                node.receiveAcceptRequest(message.address, message.port, message.proposalId, message.proposal)
            } else if (message.type == "identify") {
                if (node.knownNode(message.nodeInfo)) {
                    // send back 'known' if address belongs to known acceptor
                }
            }
        })
      } else if (role == "Learner") {
        this.socket.on("message", function (message, rinfo) {
            message = JSON.parse(message.toString())
            if (message.type == "accepted") {
                node.receiveAccept(message.from, message.proposalId, message.proposal)
            }
        })
      }
    }
}

function Node (id, address, port, generateProposalId, currentRound) { // :: Int -> Int -> Int -> Socket -> (Int) -> Node
    this.id = id
    this.address = address
    this.port = port
    this.acceptors = {} // ID -> [[port, address], last proposal]
    this.proposal = null
    this.value = null
    this.roles = []
    this.quorum = null
    this.generateProposalId = generateProposalId
    this.messenger = new Messenger(this, port, address)
    if (currentRound) {
        this.currentRound = currentRound
    } else {
        this.currentRound = 1
    }
    this.end = function () {
        this.messenger.close()
        // should probably persist stateLog here
    }
}

function Cluster (nodes) { // :: [Node] -> Cluster
    this.learners = {}
    this.acceptors = {}
    this.proposers = {}

    this.setQuorum = function () {
        if (this.acceptors.length % 2 == 0) {
            this.quorum = this.acceptors.length / 2 + 1
        } else {
            this.quorum = Math.ceil(Object.keys(this.acceptors).length / 2)
        }
        if (nodes) {
          for (var node in nodes) {
              node.quorum = this.quorum
          }
        }
    }

    this.addNode = function (node) {
        if (node.roles) {
            if (node.roles.indexOf('Learner') > -1) {
             this.learners[node.id] = [node.port, node.address]
            }
            if (node.roles.indexOf('Acceptor') > -1) {
                this.acceptors[node.id] = [node.port, node.address]
                node.messenger.notifyProposers(this.proposers, "new acceptor", {nodeId: node.id, info: this.acceptors[node.id]})
            }
            if (node.roles.indexOf('Proposer') > -1) {
                this.proposers[node.id] = [node.port, node.address]
            }
            for (var id in this.acceptors) {
                node.acceptors[id] = [this.acceptors[id], null]
            }
        }
    }
    if (nodes) {
        for (var node in nodes) {
            this.addNode(node)
        }
    }
}

function initializeProposer (node, cluster) { // :: Node -> Cluster -> a ->
    node.roles.push('Proposer')
    node.proposalId = null
    node.lastAcceptedId = null
    node.promises = []
    node.nextProposalNum = 1
    node.waiting = []
    node.messenger.setMessageHandlers(node, 'Proposer')

    node.startProposal = function (proposal, callback) {
        node.promises = []
        node.proposal = proposal
        if (callback) {
            node.callback = callback
        } else {
            node.callback = function (body) { console.log(body) }
        }
        node.prepare(false)
    }

    node.prepare = function (nack, seed) {
        node.proposalId = seed ? node.generateProposalId(seed) : node.generateProposalId()
        if (nack) {
            node.callback({
                eventType: "NACK",
                newProposalId: node.proposalId
            })
        }
        node.messenger.sendPrepare()
    }

    node.receivePromise = function (fromId, fromAddress, proposalId, lastValue, lastAcceptedId) { // :: Int -> Int -> Int -> a ->
        if (proposalId != node.proposalId) {
            return
        }

        if (node.acceptors[fromId] == null) {
            node.messenger.sendIdentRequest([fromId, fromAddress])
            return
        }

        if (node.promises.indexOf(fromId) < 0) {
            node.promises.push(fromId)
        } else { return } // we have already received a promise. Something is probably wrong.

        if (lastAcceptedId > node.lastAcceptedId) {
            node.lastAcceptedId = lastAcceptedId
            if (lastValue) { node.proposal = lastValue }
        }

        if (node.promises.length >= node.quorum) {
            if (node.proposal) {
                node.messenger.sendAcceptRequest()
            }
        }
    }

    node.receiveAccept = function (from, proposalId, proposal) {
        node.callback({
            eventType: "accept",
            proposal: proposal,
            proposalId: proposalId
        })
        node.messenger.sendToLearners(node.messenger.createMessage({
            type: "accepted",
            proposalId: proposalId,
            value: proposal,
            from: from
        }))
    }

    node.receivePrevious = function (from, proposalId) {
    }

    if (cluster) {
      cluster.addNode(node)
      cluster.setQuorum()
    }
}

function initializeAcceptor (node, cluster, callback) { // :: Node -> Cluster ->
    node.roles.push('Acceptor')
    node.stateLog = {}
    // Sync stateLog with acceptors in cluster
    node.promisedId = null
    node.acceptedId = null
    node.lastAccepted = null
    node.learners = cluster.learners
    node.messenger.setMessageHandlers(node, 'Acceptor')
    if (callback) {
        node.callback = callback
    } else {
        node.callback = function (body) { console.log(body) }
    }

    node.receivePrepare = function (port, address, proposalId) {
        if (proposalId == node.promisedId) {
            return
        } else if (proposalId > node.promisedId) {
            node.promisedId = proposalId
            node.messenger.sendPromise(port, address)
        } else {
            node.messenger.sendPrevious(port, address, proposalId)
        }
    }

    node.receiveAcceptRequest = function (address, port, proposalId, proposal) { // :: Int -> Int -> a ->
        if (proposalId == node.promisedId) {
            node.promisedId = proposalId
            node.acceptedId = proposalId
            node.value = proposal
            var message = node.messenger.createMessage({
                type: "accepted",
                value: proposal,
                address: address,
                port: port,
                proposalId: proposalId
            })
            node.messenger.sendToAcceptors(message)
            node.messenger.sendToLearners(message)
            node.messenger.send(message, address, port)
            node.stateLog[Date.now()] = {round: node.currentRound, value: proposal, leader: {address: address, port: port}}
            node.callback({
                eventType: "accepted",
                proposal: proposal,
                proposalId: proposalId
            })
        } else if (proposalId < node.promisedId) {
            node.messenger.sendPrevious(port, address, proposalId, proposal)
        } else {
            node.messenger.sendNACK(address, port, node.promisedId)
        }
    }

    node.knownNode = function (info) {
        return (node.acceptors[from[0]][0] == from[1])
    }

    if (cluster) {
      cluster.addNode(node)
      cluster.setQuorum()
    }
}

function initializeLearner (node, cluster, callback) { // :: Node -> Cluster ->
    node.roles.push('Learner')
    node.finalValue = null
    node.stateLog = {}
    node.finalProposalId = null
    if (callback) {
    node.callback = callback
    } else {
        node.callback = function (body) { console.log(body) }
    }

    node.proposals = {} // proposal ID -> [accept count, retain count, value]

    node.receiveAccept = function (from, proposalId, acceptedValue) { // :: Int -> Int -> a ->
        if (node.finalValue != null) {
            return
        }

        var last = node.acceptors[from][1]
        if (last) {
            if (last > proposalId) { return }
            node.acceptors[from][1] = proposalId

            oldProposal = node.proposals[last]
            oldProposal[1] -= 1
            if (oldProposal[1] == 0) { delete node.proposals[last] }
        }

        if (node.proposals[proposalId] == null) {
            node.proposals[proposalId] = [1, 1, acceptedValue]
        }

        if (node.proposals[proposalId][0] == node.quorum) { // round over
            node.finalValue = acceptedValue
            node.finalProposalId = proposalId
            console.log(node.finalValue)
        }
    }
    if (cluster) {
      cluster.addNode(node)
      cluster.setQuorum()
    }
}

exports.Messenger = Messenger
exports.Node = Node
exports.initializeProposer = initializeProposer
exports.initializeAcceptor = initializeAcceptor
exports.initializeLearner = initializeLearner
exports.Cluster = Cluster
<|endoftext|>


<commit_before>// Const
var TRANSFORMED_TYPE_KEY    = '@t';
var CIRCULAR_REF_KEY        = '@r';
var KEY_REQUIRE_ESCAPING_RE = /^#*@(t|r)$/;

var GLOBAL = (function getGlobal () {
    // NOTE: see http://www.ecma-international.org/ecma-262/6.0/index.html#sec-performeval step 10
    var savedEval = eval;

    return savedEval('this');
})();

var ARRAY_BUFFER_SUPPORTED = typeof ArrayBuffer === 'function';
var MAP_SUPPORTED          = typeof Map === 'function';
var SET_SUPPORTED          = typeof Set === 'function';

var TYPED_ARRAY_CTORS = [
    'Int8Array',
    'Uint8Array',
    'Uint8ClampedArray',
    'Int16Array',
    'Uint16Array',
    'Int32Array',
    'Uint32Array',
    'Float32Array',
    'Float64Array'
];

// Saved proto functions
var arrSlice = Array.prototype.slice;

// Default serializer
var JSONSerializer = {
    serialize: function (val) {
        return JSON.stringify(val);
    },

    deserialize: function (val) {
        return JSON.parse(val);
    }
};

// EncodingTransformer
var EncodingTransformer = function (val, transforms) {
    this.references               = val;
    this.transforms               = transforms;
    this.circularCandidates       = [];
    this.circularCandidatesDescrs = [];
    this.circularRefCount         = 0;
};

EncodingTransformer._createRefMark = function (idx) {
    var obj = Object.create(null);

    obj[CIRCULAR_REF_KEY] = idx;

    return obj;
};

EncodingTransformer.prototype._createCircularCandidate = function (val, parent, key) {
    this.circularCandidates.push(val);
    this.circularCandidatesDescrs.push({ parent: parent, key: key, refIdx: -1 });
};

EncodingTransformer.prototype._applyTransform = function (val, parent, key, transform) {
    var result          = Object.create(null);
    var serializableVal = transform.toSerializable(val);

    if (typeof serializableVal === 'object')
        this._createCircularCandidate(val, parent, key);

    result[TRANSFORMED_TYPE_KEY] = transform.type;
    result.data                  = this._handleValue(serializableVal, parent, key);

    return result;
};

EncodingTransformer.prototype._handleArray = function (arr) {
    var result = [];

    for (var i = 0; i < arr.length; i++)
        result[i] = this._handleValue(arr[i], result, i);

    return result;
};

EncodingTransformer.prototype._handlePlainObject = function (obj) {
    var result = Object.create(null);

    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            var resultKey = KEY_REQUIRE_ESCAPING_RE.test(key) ? '#' + key : key;

            result[resultKey] = this._handleValue(obj[key], result, resultKey);
        }
    }

    return result;
};

EncodingTransformer.prototype._handleObject = function (obj, parent, key) {
    this._createCircularCandidate(obj, parent, key);

    return Array.isArray(obj) ? this._handleArray(obj) : this._handlePlainObject(obj);
};

EncodingTransformer.prototype._ensureCircularReference = function (obj) {
    var circularCandidateIdx = this.circularCandidates.indexOf(obj);

    if (circularCandidateIdx > -1) {
        var descr = this.circularCandidatesDescrs[circularCandidateIdx];

        if (descr.refIdx === -1)
            descr.refIdx = descr.parent ? ++this.circularRefCount : 0;

        return EncodingTransformer._createRefMark(descr.refIdx);
    }

    return null;
};

EncodingTransformer.prototype._handleValue = function (val, parent, key) {
    var type     = typeof val;
    var isObject = type === 'object' && val !== null;

    if (isObject) {
        var refMark = this._ensureCircularReference(val);

        if (refMark)
            return refMark;
    }

    for (var i = 0; i < this.transforms.length; i++) {
        var transform = this.transforms[i];

        if (transform.shouldTransform(type, val))
            return this._applyTransform(val, parent, key, transform);
    }

    if (isObject)
        return this._handleObject(val, parent, key);

    return val;
};

EncodingTransformer.prototype.transform = function () {
    var references = [this._handleValue(this.references, null, null)];

    for (var i = 0; i < this.circularCandidatesDescrs.length; i++) {
        var descr = this.circularCandidatesDescrs[i];

        if (descr.refIdx > 0) {
            references[descr.refIdx] = descr.parent[descr.key];
            descr.parent[descr.key]  = EncodingTransformer._createRefMark(descr.refIdx);
        }
    }

    return references;
};

// DecodingTransform
var DecodingTransformer = function (references, transformsMap) {
    this.references            = references;
    this.transformMap          = transformsMap;
    this.activeTransformsStack = [];
    this.visitedRefs           = Object.create(null);
};

DecodingTransformer.prototype._handlePlainObject = function (obj) {
    var unescaped = Object.create(null);

    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            this._handleValue(obj[key], obj, key);

            if (KEY_REQUIRE_ESCAPING_RE.test(key)) {
                // NOTE: use intermediate object to avoid unescaped and escaped keys interference
                // E.g. unescaped "##@t" will be "#@t" which can overwrite escaped "#@t".
                unescaped[key.substring(1)] = obj[key];
                delete obj[key];
            }
        }
    }

    for (var unsecapedKey in unescaped)
        obj[unsecapedKey] = unescaped[unsecapedKey];
};

DecodingTransformer.prototype._handleTransformedObject = function (obj, parent, key) {
    var transformType = obj[TRANSFORMED_TYPE_KEY];
    var transform     = this.transformMap[transformType];

    if (!transform)
        throw new Error('Can\'t find transform for "' + transformType + '" type.');

    this.activeTransformsStack.push(obj);
    this._handleValue(obj.data, obj, 'data');
    this.activeTransformsStack.pop();

    parent[key] = transform.fromSerializable(obj.data);
};

DecodingTransformer.prototype._handleCircularSelfRefDuringTransform = function (refIdx, parent, key) {
    // NOTE: we've hit a hard case: object reference itself during transformation.
    // We can't dereference it since we don't have resulting object yet. And we'll
    // not be able to restore reference lately because we will need to traverse
    // transformed object again and reference might be unreachable or new object contain
    // new circular references. As a workaround we create getter, so once transformation
    // complete, dereferenced property will point to correct transformed object.
    var references = this.references;
    var val = void 0;

    Object.defineProperty(parent, key, {
        configurable: true,
        enumerable:   true,

        get: function () {
            if (val === void 0)
                val = references[refIdx];

            return val;
        },

        set: function (value) {
            val = value;
        }
    });
};

DecodingTransformer.prototype._handleCircularRef = function (refIdx, parent, key) {
    if (this.activeTransformsStack.indexOf(this.references[refIdx]) > -1)
        this._handleCircularSelfRefDuringTransform(refIdx, parent, key);

    else {
        if (!this.visitedRefs[refIdx]) {
            this.visitedRefs[refIdx] = true;
            this._handleValue(this.references[refIdx], this.references, refIdx);
        }

        parent[key] = this.references[refIdx];
    }
};

DecodingTransformer.prototype._handleValue = function (val, parent, key) {
    if (typeof val !== 'object' || val === null)
        return;

    var refIdx = val[CIRCULAR_REF_KEY];

    if (refIdx !== void 0)
        this._handleCircularRef(refIdx, parent, key);

    else if (val[TRANSFORMED_TYPE_KEY])
        this._handleTransformedObject(val, parent, key);

    else if (Array.isArray(val)) {
        for (var i = 0; i < val.length; i++)
            this._handleValue(val[i], val, i);
    }

    else
        this._handlePlainObject(val);
};

DecodingTransformer.prototype.transform = function () {
    this.visitedRefs[0] = true;
    this._handleValue(this.references[0], this.references, 0);

    return this.references[0];
};

// Transforms
var builtInTransforms = [
    {
        type: '[[NaN]]',

        shouldTransform: function (type, val) {
            return type === 'number' && isNaN(val);
        },

        toSerializable: function () {
            return '';
        },

        fromSerializable: function () {
            return NaN;
        }
    },

    {
        type: '[[undefined]]',

        shouldTransform: function (type) {
            return type === 'undefined';
        },

        toSerializable: function () {
            return '';
        },

        fromSerializable: function () {
            return void 0;
        }
    },
    {
        type: '[[Date]]',

        shouldTransform: function (type, val) {
            return val instanceof Date;
        },

        toSerializable: function (date) {
            return date.getTime();
        },

        fromSerializable: function (val) {
            var date = new Date();

            date.setTime(val);
            return date;
        }
    },
    {
        type: '[[RegExp]]',

        shouldTransform: function (type, val) {
            return val instanceof RegExp;
        },

        toSerializable: function (re) {
            var result = {
                src:   re.source,
                flags: ''
            };

            if (re.global)
                result.flags += 'g';

            if (re.ignoreCase)
                result.flags += 'i';

            if (re.multiline)
                result.flags += 'm';

            return result;
        },

        fromSerializable: function (val) {
            return new RegExp(val.src, val.flags);
        }
    },

    {
        type: '[[Error]]',

        shouldTransform: function (type, val) {
            return val instanceof Error;
        },

        toSerializable: function (err) {
            return {
                name:    err.name,
                message: err.message,
                stack:   err.stack
            };
        },

        fromSerializable: function (val) {
            var Ctor = GLOBAL[val.name] || Error;
            var err  = new Ctor(val.message);

            err.stack = val.stack;
            return err;
        }
    },

    {
        type: '[[ArrayBuffer]]',

        shouldTransform: function (type, val) {
            return ARRAY_BUFFER_SUPPORTED && val instanceof ArrayBuffer;
        },

        toSerializable: function (buffer) {
            var view = new Int8Array(buffer);

            return arrSlice.call(view);
        },

        fromSerializable: function (val) {
            if (ARRAY_BUFFER_SUPPORTED) {
                var buffer = new ArrayBuffer(val.length);
                var view   = new Int8Array(buffer);

                view.set(val);

                return buffer;
            }

            return val;
        }
    },

    {
        type: '[[TypedArray]]',

        shouldTransform: function (type, val) {
            for (var i = 0; i < TYPED_ARRAY_CTORS.length; i++) {
                var ctorName = TYPED_ARRAY_CTORS[i];

                if (typeof GLOBAL[ctorName] === 'function' && val instanceof GLOBAL[ctorName])
                    return true;
            }

            return false;
        },

        toSerializable: function (arr) {
            return {
                ctorName: arr.constructor.name,
                arr:      arrSlice.call(arr)
            };
        },

        fromSerializable: function (val) {
            return typeof GLOBAL[val.ctorName] === 'function' ? new GLOBAL[val.ctorName](val.arr) : val.arr;
        }
    },

    {
        type: '[[Map]]',

        shouldTransform: function (type, val) {
            return MAP_SUPPORTED && val instanceof Map;
        },

        toSerializable: function (map) {
            var flattenedKVArr = [];

            map.forEach(function (val, key) {
                flattenedKVArr.push(key);
                flattenedKVArr.push(val);
            });

            return flattenedKVArr;
        },

        fromSerializable: function (val) {
            if (MAP_SUPPORTED) {
                // NOTE: new Map(iterable) is not supported by all browsers
                var map = new Map();

                for (var i = 0; i < val.length; i += 2)
                    map.set(val[i], val[i + 1]);

                return map;
            }

            var kvArr = [];

            for (var j = 0; j < val.length; j += 2)
                kvArr.push([val[i], val[i + 1]]);

            return kvArr;
        }
    },

    {
        type: '[[Set]]',

        shouldTransform: function (type, val) {
            return SET_SUPPORTED && val instanceof Set;
        },

        toSerializable: function (set) {
            var arr = [];

            set.forEach(function (val) {
                arr.push(val);
            });

            return arr;
        },

        fromSerializable: function (val) {
            if (SET_SUPPORTED) {
                // NOTE: new Set(iterable) is not supported by all browsers
                var set = new Set();

                for (var i = 0; i < val.length; i++)
                    set.add(val[i]);

                return set;
            }

            return val;
        }
    }
];

// Replicator
var Replicator = module.exports = function (serializer) {
    this.transforms    = [];
    this.transformsMap = Object.create(null);
    this.serializer    = serializer || JSONSerializer;

    this.addTransforms(builtInTransforms);
};

// Manage transforms
Replicator.prototype.addTransforms = function (transforms) {
    transforms = Array.isArray(transforms) ? transforms : [transforms];

    for (var i = 0; i < transforms.length; i++) {
        var transform = transforms[i];

        if (this.transformsMap[transform.type])
            throw new Error('Transform with type "' + transform.type + '" was already added.');

        this.transforms.push(transform);
        this.transformsMap[transform.type] = transform;
    }

    return this;
};

Replicator.prototype.removeTransforms = function (transforms) {
    transforms = Array.isArray(transforms) ? transforms : [transforms];

    for (var i = 0; i < transforms.length; i++) {
        var transform = transforms[i];
        var idx       = this.transforms.indexOf(transform);

        if (idx > -1)
            this.transforms.splice(idx, 1);

        delete this.transformsMap[transform.type];
    }

    return this;
};

Replicator.prototype.encode = function (val) {
    var transformer = new EncodingTransformer(val, this.transforms);
    var references  = transformer.transform();

    return this.serializer.serialize(references);
};

Replicator.prototype.decode = function (val) {
    var references  = this.serializer.deserialize(val);
    var transformer = new DecodingTransformer(references, this.transformsMap);

    return transformer.transform();
};
<commit_msg>Fix setter to return the assigned value.<commit_after>// Const
var TRANSFORMED_TYPE_KEY    = '@t';
var CIRCULAR_REF_KEY        = '@r';
var KEY_REQUIRE_ESCAPING_RE = /^#*@(t|r)$/;

var GLOBAL = (function getGlobal () {
    // NOTE: see http://www.ecma-international.org/ecma-262/6.0/index.html#sec-performeval step 10
    var savedEval = eval;

    return savedEval('this');
})();

var ARRAY_BUFFER_SUPPORTED = typeof ArrayBuffer === 'function';
var MAP_SUPPORTED          = typeof Map === 'function';
var SET_SUPPORTED          = typeof Set === 'function';

var TYPED_ARRAY_CTORS = [
    'Int8Array',
    'Uint8Array',
    'Uint8ClampedArray',
    'Int16Array',
    'Uint16Array',
    'Int32Array',
    'Uint32Array',
    'Float32Array',
    'Float64Array'
];

// Saved proto functions
var arrSlice = Array.prototype.slice;

// Default serializer
var JSONSerializer = {
    serialize: function (val) {
        return JSON.stringify(val);
    },

    deserialize: function (val) {
        return JSON.parse(val);
    }
};

// EncodingTransformer
var EncodingTransformer = function (val, transforms) {
    this.references               = val;
    this.transforms               = transforms;
    this.circularCandidates       = [];
    this.circularCandidatesDescrs = [];
    this.circularRefCount         = 0;
};

EncodingTransformer._createRefMark = function (idx) {
    var obj = Object.create(null);

    obj[CIRCULAR_REF_KEY] = idx;

    return obj;
};

EncodingTransformer.prototype._createCircularCandidate = function (val, parent, key) {
    this.circularCandidates.push(val);
    this.circularCandidatesDescrs.push({ parent: parent, key: key, refIdx: -1 });
};

EncodingTransformer.prototype._applyTransform = function (val, parent, key, transform) {
    var result          = Object.create(null);
    var serializableVal = transform.toSerializable(val);

    if (typeof serializableVal === 'object')
        this._createCircularCandidate(val, parent, key);

    result[TRANSFORMED_TYPE_KEY] = transform.type;
    result.data                  = this._handleValue(serializableVal, parent, key);

    return result;
};

EncodingTransformer.prototype._handleArray = function (arr) {
    var result = [];

    for (var i = 0; i < arr.length; i++)
        result[i] = this._handleValue(arr[i], result, i);

    return result;
};

EncodingTransformer.prototype._handlePlainObject = function (obj) {
    var result = Object.create(null);

    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            var resultKey = KEY_REQUIRE_ESCAPING_RE.test(key) ? '#' + key : key;

            result[resultKey] = this._handleValue(obj[key], result, resultKey);
        }
    }

    return result;
};

EncodingTransformer.prototype._handleObject = function (obj, parent, key) {
    this._createCircularCandidate(obj, parent, key);

    return Array.isArray(obj) ? this._handleArray(obj) : this._handlePlainObject(obj);
};

EncodingTransformer.prototype._ensureCircularReference = function (obj) {
    var circularCandidateIdx = this.circularCandidates.indexOf(obj);

    if (circularCandidateIdx > -1) {
        var descr = this.circularCandidatesDescrs[circularCandidateIdx];

        if (descr.refIdx === -1)
            descr.refIdx = descr.parent ? ++this.circularRefCount : 0;

        return EncodingTransformer._createRefMark(descr.refIdx);
    }

    return null;
};

EncodingTransformer.prototype._handleValue = function (val, parent, key) {
    var type     = typeof val;
    var isObject = type === 'object' && val !== null;

    if (isObject) {
        var refMark = this._ensureCircularReference(val);

        if (refMark)
            return refMark;
    }

    for (var i = 0; i < this.transforms.length; i++) {
        var transform = this.transforms[i];

        if (transform.shouldTransform(type, val))
            return this._applyTransform(val, parent, key, transform);
    }

    if (isObject)
        return this._handleObject(val, parent, key);

    return val;
};

EncodingTransformer.prototype.transform = function () {
    var references = [this._handleValue(this.references, null, null)];

    for (var i = 0; i < this.circularCandidatesDescrs.length; i++) {
        var descr = this.circularCandidatesDescrs[i];

        if (descr.refIdx > 0) {
            references[descr.refIdx] = descr.parent[descr.key];
            descr.parent[descr.key]  = EncodingTransformer._createRefMark(descr.refIdx);
        }
    }

    return references;
};

// DecodingTransform
var DecodingTransformer = function (references, transformsMap) {
    this.references            = references;
    this.transformMap          = transformsMap;
    this.activeTransformsStack = [];
    this.visitedRefs           = Object.create(null);
};

DecodingTransformer.prototype._handlePlainObject = function (obj) {
    var unescaped = Object.create(null);

    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            this._handleValue(obj[key], obj, key);

            if (KEY_REQUIRE_ESCAPING_RE.test(key)) {
                // NOTE: use intermediate object to avoid unescaped and escaped keys interference
                // E.g. unescaped "##@t" will be "#@t" which can overwrite escaped "#@t".
                unescaped[key.substring(1)] = obj[key];
                delete obj[key];
            }
        }
    }

    for (var unsecapedKey in unescaped)
        obj[unsecapedKey] = unescaped[unsecapedKey];
};

DecodingTransformer.prototype._handleTransformedObject = function (obj, parent, key) {
    var transformType = obj[TRANSFORMED_TYPE_KEY];
    var transform     = this.transformMap[transformType];

    if (!transform)
        throw new Error('Can\'t find transform for "' + transformType + '" type.');

    this.activeTransformsStack.push(obj);
    this._handleValue(obj.data, obj, 'data');
    this.activeTransformsStack.pop();

    parent[key] = transform.fromSerializable(obj.data);
};

DecodingTransformer.prototype._handleCircularSelfRefDuringTransform = function (refIdx, parent, key) {
    // NOTE: we've hit a hard case: object reference itself during transformation.
    // We can't dereference it since we don't have resulting object yet. And we'll
    // not be able to restore reference lately because we will need to traverse
    // transformed object again and reference might be unreachable or new object contain
    // new circular references. As a workaround we create getter, so once transformation
    // complete, dereferenced property will point to correct transformed object.
    var references = this.references;
    var val = void 0;

    Object.defineProperty(parent, key, {
        configurable: true,
        enumerable:   true,

        get: function () {
            if (val === void 0)
                val = references[refIdx];

            return val;
        },

        set: function (value) {
            val = value;
            return val;
        }
    });
};

DecodingTransformer.prototype._handleCircularRef = function (refIdx, parent, key) {
    if (this.activeTransformsStack.indexOf(this.references[refIdx]) > -1)
        this._handleCircularSelfRefDuringTransform(refIdx, parent, key);

    else {
        if (!this.visitedRefs[refIdx]) {
            this.visitedRefs[refIdx] = true;
            this._handleValue(this.references[refIdx], this.references, refIdx);
        }

        parent[key] = this.references[refIdx];
    }
};

DecodingTransformer.prototype._handleValue = function (val, parent, key) {
    if (typeof val !== 'object' || val === null)
        return;

    var refIdx = val[CIRCULAR_REF_KEY];

    if (refIdx !== void 0)
        this._handleCircularRef(refIdx, parent, key);

    else if (val[TRANSFORMED_TYPE_KEY])
        this._handleTransformedObject(val, parent, key);

    else if (Array.isArray(val)) {
        for (var i = 0; i < val.length; i++)
            this._handleValue(val[i], val, i);
    }

    else
        this._handlePlainObject(val);
};

DecodingTransformer.prototype.transform = function () {
    this.visitedRefs[0] = true;
    this._handleValue(this.references[0], this.references, 0);

    return this.references[0];
};

// Transforms
var builtInTransforms = [
    {
        type: '[[NaN]]',

        shouldTransform: function (type, val) {
            return type === 'number' && isNaN(val);
        },

        toSerializable: function () {
            return '';
        },

        fromSerializable: function () {
            return NaN;
        }
    },

    {
        type: '[[undefined]]',

        shouldTransform: function (type) {
            return type === 'undefined';
        },

        toSerializable: function () {
            return '';
        },

        fromSerializable: function () {
            return void 0;
        }
    },
    {
        type: '[[Date]]',

        shouldTransform: function (type, val) {
            return val instanceof Date;
        },

        toSerializable: function (date) {
            return date.getTime();
        },

        fromSerializable: function (val) {
            var date = new Date();

            date.setTime(val);
            return date;
        }
    },
    {
        type: '[[RegExp]]',

        shouldTransform: function (type, val) {
            return val instanceof RegExp;
        },

        toSerializable: function (re) {
            var result = {
                src:   re.source,
                flags: ''
            };

            if (re.global)
                result.flags += 'g';

            if (re.ignoreCase)
                result.flags += 'i';

            if (re.multiline)
                result.flags += 'm';

            return result;
        },

        fromSerializable: function (val) {
            return new RegExp(val.src, val.flags);
        }
    },

    {
        type: '[[Error]]',

        shouldTransform: function (type, val) {
            return val instanceof Error;
        },

        toSerializable: function (err) {
            return {
                name:    err.name,
                message: err.message,
                stack:   err.stack
            };
        },

        fromSerializable: function (val) {
            var Ctor = GLOBAL[val.name] || Error;
            var err  = new Ctor(val.message);

            err.stack = val.stack;
            return err;
        }
    },

    {
        type: '[[ArrayBuffer]]',

        shouldTransform: function (type, val) {
            return ARRAY_BUFFER_SUPPORTED && val instanceof ArrayBuffer;
        },

        toSerializable: function (buffer) {
            var view = new Int8Array(buffer);

            return arrSlice.call(view);
        },

        fromSerializable: function (val) {
            if (ARRAY_BUFFER_SUPPORTED) {
                var buffer = new ArrayBuffer(val.length);
                var view   = new Int8Array(buffer);

                view.set(val);

                return buffer;
            }

            return val;
        }
    },

    {
        type: '[[TypedArray]]',

        shouldTransform: function (type, val) {
            for (var i = 0; i < TYPED_ARRAY_CTORS.length; i++) {
                var ctorName = TYPED_ARRAY_CTORS[i];

                if (typeof GLOBAL[ctorName] === 'function' && val instanceof GLOBAL[ctorName])
                    return true;
            }

            return false;
        },

        toSerializable: function (arr) {
            return {
                ctorName: arr.constructor.name,
                arr:      arrSlice.call(arr)
            };
        },

        fromSerializable: function (val) {
            return typeof GLOBAL[val.ctorName] === 'function' ? new GLOBAL[val.ctorName](val.arr) : val.arr;
        }
    },

    {
        type: '[[Map]]',

        shouldTransform: function (type, val) {
            return MAP_SUPPORTED && val instanceof Map;
        },

        toSerializable: function (map) {
            var flattenedKVArr = [];

            map.forEach(function (val, key) {
                flattenedKVArr.push(key);
                flattenedKVArr.push(val);
            });

            return flattenedKVArr;
        },

        fromSerializable: function (val) {
            if (MAP_SUPPORTED) {
                // NOTE: new Map(iterable) is not supported by all browsers
                var map = new Map();

                for (var i = 0; i < val.length; i += 2)
                    map.set(val[i], val[i + 1]);

                return map;
            }

            var kvArr = [];

            for (var j = 0; j < val.length; j += 2)
                kvArr.push([val[i], val[i + 1]]);

            return kvArr;
        }
    },

    {
        type: '[[Set]]',

        shouldTransform: function (type, val) {
            return SET_SUPPORTED && val instanceof Set;
        },

        toSerializable: function (set) {
            var arr = [];

            set.forEach(function (val) {
                arr.push(val);
            });

            return arr;
        },

        fromSerializable: function (val) {
            if (SET_SUPPORTED) {
                // NOTE: new Set(iterable) is not supported by all browsers
                var set = new Set();

                for (var i = 0; i < val.length; i++)
                    set.add(val[i]);

                return set;
            }

            return val;
        }
    }
];

// Replicator
var Replicator = module.exports = function (serializer) {
    this.transforms    = [];
    this.transformsMap = Object.create(null);
    this.serializer    = serializer || JSONSerializer;

    this.addTransforms(builtInTransforms);
};

// Manage transforms
Replicator.prototype.addTransforms = function (transforms) {
    transforms = Array.isArray(transforms) ? transforms : [transforms];

    for (var i = 0; i < transforms.length; i++) {
        var transform = transforms[i];

        if (this.transformsMap[transform.type])
            throw new Error('Transform with type "' + transform.type + '" was already added.');

        this.transforms.push(transform);
        this.transformsMap[transform.type] = transform;
    }

    return this;
};

Replicator.prototype.removeTransforms = function (transforms) {
    transforms = Array.isArray(transforms) ? transforms : [transforms];

    for (var i = 0; i < transforms.length; i++) {
        var transform = transforms[i];
        var idx       = this.transforms.indexOf(transform);

        if (idx > -1)
            this.transforms.splice(idx, 1);

        delete this.transformsMap[transform.type];
    }

    return this;
};

Replicator.prototype.encode = function (val) {
    var transformer = new EncodingTransformer(val, this.transforms);
    var references  = transformer.transform();

    return this.serializer.serialize(references);
};

Replicator.prototype.decode = function (val) {
    var references  = this.serializer.deserialize(val);
    var transformer = new DecodingTransformer(references, this.transformsMap);

    return transformer.transform();
};
<|endoftext|>