<commit_before>var thetamin = 0,
  thetamax = 6 * Math.PI,
  period = 5,
  linespacing = 1 / 30,
  linelength = linespacing / 2,
  yscreenoffset = 300,
  xscreenoffset = 260,
  xscreenscale = 360,
  yscreenscale = 360,
  ycamera = 2,
  zcamera = -3,

  rate = 1 / (2 * Math.PI), // every rotation y gets one bigger
  factor = rate / 3;

function run() {
  var ctx = document.getElementById('scene').getContext('2d'),
    redSpiral = new Spiral({
      foreground: "#ff0000",
      angleoffset: Math.PI,
      factor: factor
    }),
    redSpiralShadow = new Spiral({
      foreground: "#660000",
      angleoffset: Math.PI * 0.95,
      factor: 0.93 * factor
    }),
    redSpiralShadow2 = new Spiral({
      foreground: "#220000",
      angleoffset: Math.PI * 0.92,
      factor: 0.90 * factor
    }),
    cyanSpiral = new Spiral({
      foreground: "#00ffcc",
      angleoffset: 0,
      factor: factor
    }),
    cyanSpiralShadow = new Spiral({
      foreground: "#003322",
      angleoffset: -Math.PI * 0.05,
      factor: 0.93 * factor
    }),
    cyanSpiralShadow2 = new Spiral({
      foreground: "#002211",
      angleoffset: -Math.PI * 0.08,
      factor: 0.90 * factor
    });

  render();

  function render() {
    requestAnimationFrame(render);
    renderFrame();
  }

  function renderFrame() {
    ctx.clearRect(0, 0, 500, 500);
    ctx.beginPath();

    redSpiralShadow2.draw(ctx);
    cyanSpiralShadow2.draw(ctx);

    redSpiralShadow.draw(ctx);
    cyanSpiralShadow.draw(ctx);

    redSpiral.draw(ctx);
    cyanSpiral.draw(ctx);
  }

  function Spiral(config) {
    this.period = config.period || period;
    this.spacing = config.spacing || linespacing;
    this.offset = 0;
    this.rate = config.rate || rate;
    this.linelength = config.linelength || linelength;

    var angleoffset = config.angleoffset || 0;
    var lineSegments = computeLineSegments.call(this);

    this.draw = function(ctx) {
      this.offset -= 1;
      if (this.offset <= -this.period) {
        this.offset += this.period;
      }

      lineSegments[this.offset].forEach(drawLineSegment);
    };

    function drawLineSegment(segment) {
      stroke(config.foreground, segment.start.alpha);
      ctx.moveTo(segment.start.x, segment.start.y);
      ctx.lineTo(segment.end.x, segment.end.y);
    }

    function computeLineSegments() {
      var lineSegments = {};
      var factor = config.factor;
      var thetanew, pointsAtOffset, thetaold, rate = this.rate;
      for (var offset = 0; offset > -this.period; offset--) {
        lineSegments[offset] = lines = [];
        for (var theta = thetamin + getdtheta(thetamin, offset * this.spacing / this.period, rate, factor); theta < thetamax; theta += getdtheta(theta, this.spacing, rate, factor)) {
          thetaold = (theta >= thetamin) ? theta : thetamin;
          thetanew = theta + getdtheta(theta, this.linelength, rate, factor);

          if (thetanew <= thetamin) {
            continue;
          }

          lines.push({
            start: getPointByAngle(thetaold, factor, angleoffset, rate),
            end: getPointByAngle(thetanew, factor, angleoffset, rate)
          });
        }
      }

      return lineSegments;
    }
  }

  function stroke(color, alpha) {
    ctx.closePath();
    ctx.stroke();
    ctx.strokeStyle = color;
    ctx.globalAlpha = alpha;
    ctx.beginPath();
  }

  function getPointByAngle(theta, factor, angleoffset, rate) {
    var x = theta * factor *  Math.cos(theta + angleoffset);
    var z = - theta * factor * Math.sin(theta + angleoffset);
    var y = rate * theta;
    // now that we have 3d coorinates, project them into 2d space:
    var point = projectTo2d(x, y, z);
    // calculate point's color alpha level:
    point.alpha = Math.atan((y * factor / rate * 0.1 + 0.02 - z) * 40) * 0.35 + 0.65;

    return point;
  }

  function getdtheta(theta, length, rate, factor) {
    return length / Math.sqrt(rate * rate + factor * factor * theta * theta);
  }

  function projectTo2d(x, y, z) {
    return {
      x: xscreenoffset + xscreenscale * (x / (z - zcamera)),
      y: yscreenoffset + yscreenscale * ((y - ycamera) / (z - zcamera))
    };
  }

  // I actually want it to be slower then 60fps
  function requestAnimationFrame(callback) {
    window.setTimeout(callback, 1000 / 24);
  }
}
<commit_msg>Using global config<commit_after>var thetamin = 0,
  thetamax = 6 * Math.PI,
  period = 5,
  linespacing = 1 / 30,
  linelength = linespacing / 2,
  yscreenoffset = 300,
  xscreenoffset = 260,
  xscreenscale = 360,
  yscreenscale = 360,
  ycamera = 2,
  zcamera = -3,

  rate = 1 / (2 * Math.PI), // every rotation y gets one bigger
  factor = rate / 3;

function run() {
  var ctx = document.getElementById('scene').getContext('2d'),
    redSpiral = new Spiral({
      foreground: "#ff0000",
      angleoffset: Math.PI,
      factor: factor
    }),
    redSpiralShadow = new Spiral({
      foreground: "#660000",
      angleoffset: Math.PI * 0.95,
      factor: 0.93 * factor
    }),
    redSpiralShadow2 = new Spiral({
      foreground: "#220000",
      angleoffset: Math.PI * 0.92,
      factor: 0.90 * factor
    }),
    cyanSpiral = new Spiral({
      foreground: "#00ffcc",
      angleoffset: 0,
      factor: factor
    }),
    cyanSpiralShadow = new Spiral({
      foreground: "#003322",
      angleoffset: -Math.PI * 0.05,
      factor: 0.93 * factor
    }),
    cyanSpiralShadow2 = new Spiral({
      foreground: "#002211",
      angleoffset: -Math.PI * 0.08,
      factor: 0.90 * factor
    });

  render();

  function render() {
    requestAnimationFrame(render);
    renderFrame();
  }

  function renderFrame() {
    ctx.clearRect(0, 0, 500, 500);
    ctx.beginPath();

    redSpiralShadow2.draw(ctx);
    cyanSpiralShadow2.draw(ctx);

    redSpiralShadow.draw(ctx);
    cyanSpiralShadow.draw(ctx);

    redSpiral.draw(ctx);
    cyanSpiral.draw(ctx);
  }

  function Spiral(config) {
    this.offset = 0;

    var angleoffset = config.angleoffset;
    var lineSegments = computeLineSegments(this);

    this.draw = function(ctx) {
      this.offset -= 1;
      if (this.offset <= -period) {
        this.offset += period;
      }

      lineSegments[this.offset].forEach(drawLineSegment);
    };

    function drawLineSegment(segment) {
      stroke(config.foreground, segment.start.alpha);
      ctx.moveTo(segment.start.x, segment.start.y);
      ctx.lineTo(segment.end.x, segment.end.y);
    }

    function computeLineSegments() {
      var lineSegments = {};
      var factor = config.factor;
      var thetanew, pointsAtOffset, thetaold;
      for (var offset = 0; offset > -period; offset--) {
        lineSegments[offset] = lines = [];
        for (var theta = thetamin + getdtheta(thetamin, offset * linespacing / period, rate, factor); theta < thetamax; theta += getdtheta(theta, linespacing, rate, factor)) {
          thetaold = (theta >= thetamin) ? theta : thetamin;
          thetanew = theta + getdtheta(theta, linelength, rate, factor);

          if (thetanew <= thetamin) {
            continue;
          }

          lines.push({
            start: getPointByAngle(thetaold, factor, angleoffset, rate),
            end: getPointByAngle(thetanew, factor, angleoffset, rate)
          });
        }
      }

      return lineSegments;
    }
  }

  function stroke(color, alpha) {
    ctx.closePath();
    ctx.stroke();
    ctx.strokeStyle = color;
    ctx.globalAlpha = alpha;
    ctx.beginPath();
  }

  function getPointByAngle(theta, factor, angleoffset, rate) {
    var x = theta * factor *  Math.cos(theta + angleoffset);
    var z = - theta * factor * Math.sin(theta + angleoffset);
    var y = rate * theta;
    // now that we have 3d coorinates, project them into 2d space:
    var point = projectTo2d(x, y, z);
    // calculate point's color alpha level:
    point.alpha = Math.atan((y * factor / rate * 0.1 + 0.02 - z) * 40) * 0.35 + 0.65;

    return point;
  }

  function getdtheta(theta, lineLength, rate, factor) {
    return lineLength / Math.sqrt(rate * rate + factor * factor * theta * theta);
  }

  function projectTo2d(x, y, z) {
    return {
      x: xscreenoffset + xscreenscale * (x / (z - zcamera)),
      y: yscreenoffset + yscreenscale * ((y - ycamera) / (z - zcamera))
    };
  }

  // I actually want it to be slower then 60fps
  function requestAnimationFrame(callback) {
    window.setTimeout(callback, 1000 / 24);
  }
}
<|endoftext|>


<commit_before>'use strict';
var _ = require('lodash');
var Subscriber = require('emissary').Subscriber;
var jshint = require('jshint').JSHINT;
var jsxhint = require('jshint-jsx').JSXHINT;
var loadConfig = require('./load-config');
var plugin = module.exports;

var markersByEditorId = {};

Subscriber.extend(plugin);

function getMarkersForEditor() {
	var editor = atom.workspace.getActiveEditor();

	if (editor && markersByEditorId[editor.id]) {
		return markersByEditorId[editor.id];
	}

	return {};
}

function clearOldMarkers(errors) {
	var rows = _.map(errors, function (error) {
		return getRowForError(error);
	});

	var oldMarkers = getMarkersForEditor();
	_.each(_.keys(oldMarkers), function (row) {
		if (!_.contains(rows, row)) {
			destroyMarkerAtRow(row);
		}
	});
}

function destroyMarkerAtRow(row) {
	var editor = atom.workspace.getActiveEditor();
	if (markersByEditorId[editor.id] && markersByEditorId[editor.id][row]) {
		markersByEditorId[editor.id][row].destroy();
		delete markersByEditorId[editor.id][row];
	}
}

function saveMarker(marker, row) {
	var editor = atom.workspace.getActiveEditor();

	if (!markersByEditorId[editor.id]) {
		markersByEditorId[editor.id] = {};
	}

	markersByEditorId[editor.id][row] = marker;
}

function getMarkerAtRow(row) {
	var editor = atom.workspace.getActiveEditor();

	if (!markersByEditorId[editor.id]) {
		return null;
	}

	return markersByEditorId[editor.id][row];
}

function updateStatusbar(error) {
	if (!error) {
		return;
	}

	error = error[0];

	if (atom.workspaceView.statusBar) {
		atom.workspaceView.statusBar.appendLeft('JSHint ' + error.line + ':' + error.character + ' ' + error.reason + '');
	}
}

function getRowForError(error) {
	var line = error[0].line || 1; // JSHint reports `line: 0` when config error
	var row = line - 1;
	return row;
}

function displayError(error) {
	var row = getRowForError(error);

	if (getMarkerAtRow(row)) {
		return;
	}

	var editor = atom.workspace.getActiveEditor();
	var marker = editor.markBufferRange([[row, 0], [row, 1]]);
	editor.decorateMarker(marker, {type: 'line', class: 'jshint-line'});
	editor.decorateMarker(marker, {type: 'gutter', class: 'jshint-line-number'});
	saveMarker(marker, row);
	addReasons(marker, error);
}

function getReasonsForError(error) {
	return _.map(error, function (el) {
		return el.character + ': ' + el.reason;
	});
}

function addReasons(marker, error) {
	var row = getRowForError(error);
	var editorView = atom.workspaceView.getActiveView();
	var gutter = editorView.gutter;
	var reasons = '
' + getReasonsForError(error).join('
') + '
';
	var gutterRow = gutter.find(gutter.getLineNumberElement(row));

	gutterRow.destroyTooltip();
	gutterRow.setTooltip({title: reasons, placement: 'bottom', delay: {show: 200}});
	marker.on('changed destroyed', function () {
		gutterRow.destroyTooltip();
	});
}

function lint() {
	var editor = atom.workspace.getActiveEditor();

	if (!editor) {
		return;
	}

	if (['JavaScript', 'JavaScript (JSX)'].indexOf(editor.getGrammar().name) === -1) {
		return;
	}

	var file = editor.getUri();
	var config = file ? loadConfig(file) : {};

	if (atom.workspaceView.statusBar) {
		atom.workspaceView.statusBar.find('#jshint-statusbar').remove();
	}

	var linter = (atom.config.get('jshint.supportLintingJsx') || atom.config.get('jshint.transformJsx')) ? jsxhint : jshint;
	linter(editor.getText(), config, config.globals);

	// workaround the errors array sometimes containing `null`
	var errors = _.compact(linter.errors);

	if (errors.length > 0) {
		// aggregate same-line errors
		var ret = [];
		_.each(errors, function (el) {
			var l = el.line;

			if (Array.isArray(ret[l])) {
				ret[l].push(el);

				ret[l] = _.sortBy(ret[l], function (el) {
					return el.character;
				});
			} else {
				ret[l] = [el];
			}
		});
		errors = _.compact(ret);
	}

	displayErrors(errors);
}

function displayErrors(errors) {
	clearOldMarkers(errors);
	updateStatusbar(_.first(errors));
	_.each(errors, displayError);
}

function removeMarkersForEditorId(id) {
	if (markersByEditorId[id]) {
		delete markersByEditorId[id];
	}
}

function registerEvents() {
	lint();

	atom.workspace.eachEditor(function (editor) {
		var buffer = editor.getBuffer();
		var events = 'saved contents-modified';

		editor.off('scroll-top-changed');
		plugin.unsubscribe(buffer);

		if (atom.config.get('jshint.validateOnlyOnSave')) {
			events = 'saved';
		} else {
			// TODO: find a less noisy event for this
			editor.on('scroll-top-changed', _.debounce(lint, 200));
		}

		plugin.subscribe(buffer, events, _.debounce(lint, 50));
	});

	atom.workspaceView.on('editor:will-be-removed', function (e, editorView) {
		if (editorView && editorView.editor) {
			removeMarkersForEditorId(editorView.editor.id);
		}
	});
}

plugin.configDefaults = {
	validateOnlyOnSave: false,
	supportLintingJsx: false
};

plugin.activate = function () {
	registerEvents();
	plugin.subscribe(atom.config.observe('jshint.validateOnlyOnSave', registerEvents));
};
<commit_msg>Update index.js<commit_after>'use strict';
var _ = require('lodash');
var Subscriber = require('emissary').Subscriber;
var jshint = require('jshint').JSHINT;
var jsxhint = require('jshint-jsx').JSXHINT;
var loadConfig = require('./load-config');
var plugin = module.exports;

var markersByEditorId = {};

Subscriber.extend(plugin);

function getMarkersForEditor() {
	var editor = atom.workspace.getActiveEditor();

	if (editor && markersByEditorId[editor.id]) {
		return markersByEditorId[editor.id];
	}

	return {};
}

function clearOldMarkers(errors) {
	var rows = _.map(errors, function (error) {
		return getRowForError(error);
	});

	var oldMarkers = getMarkersForEditor();
	_.each(_.keys(oldMarkers), function (row) {
		if (!_.contains(rows, row)) {
			destroyMarkerAtRow(row);
		}
	});
}

function destroyMarkerAtRow(row) {
	var editor = atom.workspace.getActiveEditor();
	if (markersByEditorId[editor.id] && markersByEditorId[editor.id][row]) {
		markersByEditorId[editor.id][row].destroy();
		delete markersByEditorId[editor.id][row];
	}
}

function saveMarker(marker, row) {
	var editor = atom.workspace.getActiveEditor();

	if (!markersByEditorId[editor.id]) {
		markersByEditorId[editor.id] = {};
	}

	markersByEditorId[editor.id][row] = marker;
}

function getMarkerAtRow(row) {
	var editor = atom.workspace.getActiveEditor();

	if (!markersByEditorId[editor.id]) {
		return null;
	}

	return markersByEditorId[editor.id][row];
}

function updateStatusbar(error) {
	if (!error) {
		return;
	}

	error = error[0];

	if (atom.workspaceView.statusBar) {
		atom.workspaceView.statusBar.appendLeft('JSHint ' + error.line + ':' + error.character + ' ' + error.reason + '');
	}
}

function getRowForError(error) {
	var line = error[0].line || 1; // JSHint reports `line: 0` when config error
	var row = line - 1;
	return row;
}

function displayError(error) {
	var row = getRowForError(error);

	if (getMarkerAtRow(row)) {
		return;
	}

	var editor = atom.workspace.getActiveEditor();
	var marker = editor.markBufferRange([[row, 0], [row, 1]]);
	editor.decorateMarker(marker, {type: 'line', class: 'jshint-line'});
	editor.decorateMarker(marker, {type: 'gutter', class: 'jshint-line-number'});
	saveMarker(marker, row);
	addReasons(marker, error);
}

function getReasonsForError(error) {
	return _.map(error, function (el) {
		return el.character + ': ' + el.reason;
	});
}

function addReasons(marker, error) {
	var row = getRowForError(error);
	var editorView = atom.workspaceView.getActiveView();
	var gutter = editorView.gutter;
	var reasons = '
' + getReasonsForError(error).join('
') + '
';
	var gutterRow = gutter.find(gutter.getLineNumberElement(row));

	gutterRow.destroyTooltip();
	gutterRow.setTooltip({title: reasons, placement: 'bottom', delay: {show: 200}});
	marker.on('changed destroyed', function () {
		gutterRow.destroyTooltip();
	});
}

function lint() {
	var editor = atom.workspace.getActiveEditor();

	if (!editor) {
		return;
	}

	if (['JavaScript', 'JavaScript (JSX)'].indexOf(editor.getGrammar().name) === -1) {
		return;
	}

	var file = editor.getUri();
	var config = file ? loadConfig(file) : {};

	if (atom.workspaceView.statusBar) {
		atom.workspaceView.statusBar.find('#jshint-statusbar').remove();
	}

	var linter = (atom.config.get('jshint.supportLintingJsx') || atom.config.get('jshint.transformJsx')) ? jsxhint : jshint;
	linter(editor.getText(), config, config.globals);

	// workaround the errors array sometimes containing `null`
	var errors = _.compact(linter.errors);

	if (errors.length > 0) {
		// aggregate same-line errors
		var ret = [];
		_.each(errors, function (el) {
			var l = el.line;

			if (Array.isArray(ret[l])) {
				ret[l].push(el);

				ret[l] = _.sortBy(ret[l], function (el) {
					return el.character;
				});
			} else {
				ret[l] = [el];
			}
		});
		errors = _.compact(ret);
	}

	displayErrors(errors);
}

function displayErrors(errors) {
	clearOldMarkers(errors);
	updateStatusbar(_.first(errors));
	_.each(errors, displayError);
}

function removeMarkersForEditorId(id) {
	if (markersByEditorId[id]) {
		delete markersByEditorId[id];
	}
}

function registerEvents() {
	lint();

	atom.workspace.eachEditor(function (editor) {
		var buffer = editor.getBuffer();
		var events = 'saved contents-modified';

		editor.off('scroll-top-changed');
		plugin.unsubscribe(buffer);

		if (atom.config.get('jshint.validateOnlyOnSave')) {
			events = 'saved';
		} else {
			// TODO: find a less noisy event for this
			editor.on('scroll-top-changed', _.debounce(lint, 200));
		}

		plugin.subscribe(buffer, events, _.debounce(lint, 50));
	});

	atom.workspaceView.on('editor:will-be-removed', function (e, editorView) {
		if (editorView && editorView.editor) {
			removeMarkersForEditorId(editorView.editor.id);
		}
	});
}

plugin.configDefaults = {
	validateOnlyOnSave: false,
	supportLintingJsx: false
};

plugin.activate = function () {
	registerEvents();
	plugin.subscribe(atom.config.observe('jshint.validateOnlyOnSave', registerEvents));
	atom.workspaceView.command('jshint:lint', lint);
};
<|endoftext|>


<commit_before>// 'use strict';

var rework = require('broccoli-rework');
var Funnel = require('broccoli-funnel');

/**
 * Preprocessor
 */

// Preprocessor constructor
function Preprocessor(options) {
  this.name = 'ember-cli-rework';
  this.options = options || {};
}

// Preprocessor.toTree method
Preprocessor.prototype.toTree = function(tree, inputPath, outputPath, inputOptions) {
  // Apply rework plugins to css
  var reworkTree = function reworkTree(tree) {
    var plugins = this.options.plugins;

    if (plugins) {
      tree = rework(tree, { use: function(css) {
        plugins.forEach(function(plugin) {
          css.use(plugin);
        });
      }});
    }

    // Return the modified css
    return tree;
  };

  // Passes rework's ouput to broccoli-funnel
  return new Funnel(reworkTree(tree), {
    // Broccoli-funnel options
    srcDir: inputPath,
    destDir: outputPath
  });
};

/**
 * Export
 */

// Export rework preprocessor
module.exports = {
  name: 'ember-cli-rework',

  included: function(app) {
    this._super.included.apply(this, arguments);
    app.registry.add('css', new Preprocessor(app.options && app.options.rework));
  }
};
<commit_msg>Fix reworkTree<commit_after>'use strict';

var rework = require('broccoli-rework');
var Funnel = require('broccoli-funnel');

/**
 * Preprocessor
 */

// Preprocessor constructor
function Preprocessor(options) {
  this.name = 'ember-cli-rework';
  this.options = options || {};
}

// Preprocessor.toTree method
Preprocessor.prototype.toTree = function(tree, inputPath, outputPath, inputOptions) {
  var self = this;
  // All css in directories below "app/styles" is filtered out by default
  var excludedCss = this.options.exclude || ['app/styles/*/**/*.css'];

  // Filter css in subdirectories from tree
  var filteredTree = new Funnel(tree, {
    exclude: excludedCss
  });

  function reworkTree(tree) {
    var plugins = self.options.plugins;
    if (plugins) {
      tree = rework(tree, {
        use: function(css) {
          // Use all plugins defined in Brocfile.js config
          plugins.forEach(function(plugin) {
            css.use(plugin);
          });
        }
      });
    }
    // Return the reworked css
    return tree;
  };

  // Funnel tree with reworked css to output path and return
  return new Funnel(reworkTree(filteredTree), {
    srcDir: inputPath,
    destDir: outputPath
  });
};

/**
 * Export
 */

// Export rework preprocessor
module.exports = {
  name: 'ember-cli-rework',

  included: function(app) {
    this._super.included.apply(this, arguments);
    app.registry.add('css', new Preprocessor(app.options && app.options.rework));
  }
};
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var crypto = require('crypto');
var Stopwatch = require('timer-stopwatch');

app.set('port', (process.env.PORT || 5000));
app.set('view engine', 'jade');
app.use('/public', express.static('public'));
app.use('/node_modules', express.static('node_modules'));

app.use(require('connect-livereload')({
  port: 35729
}));

app.get('/', (req, res) => {
  res.render(`${__dirname}/public/index`);
});

app.get('/views/:name', (req, res) => {
  res.render(`${__dirname}/public/views/${req.params.name}`);
});

const ROOM_ID_SIZE = 5;
const DEFAULT_GAME_TIME = 8 * 60000; // 8 minutes in ms
var rooms = {};
var nextUsername = 1;
var users = {};

// TODO: We should probably figure out a better way to store the various
//       locations and roles rather than directly in the code.
var locations = {
  airplane: {
    roles: ['1st Class Passenger',
            'Air Marshal',
            'Mechanic',
            'Coach Passenger',
            'Flight Attendant',
            'Co-Pilot',
            'Captain']
  }
};

io.use((socket, next) => {
  var userSid = socket.handshake.query.userSid;
  if (!userSid || !findUserBySid(userSid)) {
    var newUser = {
      userSid: crypto.createHash('md5').update(new Date().toString()).digest('hex'),
      username: getNextUsername(),
      active: true
    };
    users[socket.id] = newUser;
  }
  else {
    var oldUser = findUserBySid(userSid);
    delete users[oldUser.userSid];
    oldUser.userObj.active = true;
    users[socket.id] = oldUser.userObj;
  }
  next();
});

io.on('connection', socket => {
  socket.clientIp = socket.request.connection.remoteAddress;

  socket.broadcast.emit('user:connect', {
    user: users[socket.id].username
  });

  socket.on('conn', (data, callback) => {
    callback({
      userSid: users[socket.id].userSid,
      username: users[socket.id].username
    });
  });

  socket.on('home', (data, callback) => {
    callback({
      users: Object.keys(users).filter(x => users[x].active).map(x => users[x].username),
      rooms
    });
  });

  socket.on('join', (data, callback) => {
    if (!rooms[data.roomId]) {
      callback(null, 'Room doesn\'t exist');
      return;
    }
    socket.join(data.roomId);

    var isHost = (socket.id === rooms[data.roomId].host);
    rooms[data.roomId].users[users[socket.id].username] = isHost;

    io.in(data.roomId).clients((error, clients) => {
      callback({ isHost: isHost,
                 host: users[rooms[data.roomId].host].username,
                 users: rooms[data.roomId].users
               });
    });

    socket.broadcast.to(data.roomId).emit('user:join', {
      user: users[socket.id].username
    });
  });

  socket.on('msg', (data, callback) => {
    io.in(data.roomId).emit('user:msg', { chatMsg: data.chatMsg });
    callback();
  });

  socket.on('disconnect', () => {
    socket.broadcast.emit('user:disconnect', {
        user: users[socket.id].username
    });

    var username = users[socket.id].username;
    Object.keys(rooms).forEach(x => {
      var cur = rooms[x].users;
      if (cur[username] !== undefined)
        delete cur[username];
      });

    users[socket.id].active = false;
  });

  socket.on('create-room', (data, callback) => {
    var roomId = _createRoomId();
    rooms[roomId] = {
      host: socket.id,
      users: {
        [users[socket.id].username]: false
      }
    };
    callback({ roomId });
  });

  socket.on('startgame', (data, callback) => {
    if (socket.id !== rooms[data.roomId].host) {
      callback(null, 'Only the host can start the game');
      return;
    }

    for (var u in rooms[data.roomId].users) {
      if (!rooms[data.roomId].users[u]) {
        callback(u, 'Not all users are ready');
        return;
      }
    }

    io.in(data.roomId).clients((error, clients) => {
      var locationKeys = Object.keys(locations);
      var location = locationKeys[locationKeys.length * Math.random() << 0];
      var roles = JSON.parse(JSON.stringify(locations[location].roles));
      var role = '';
      rooms[data.roomId].location = location;

      var clientCount = clients.length;
      for (var i = 0; i < clientCount; i++) {
        var clientId = clients.splice(clients.length * Math.random() << 0, 1)[0];
        if (i === 0) {
          role = 'Spy';
          io.in(clientId).emit('user:role', {role: role,
                                             location: 'Unknown'});
        } else {
          role = roles.splice(roles.length * Math.random() << 0, 1)[0];
          io.in(clientId).emit('user:role', {role: role,
                                             location: location});
        }
        users[clientId].role = role;
      }
    });

    rooms[data.roomId].startTime = new Date();
    rooms[data.roomId].endTime = new Date(rooms[data.roomId].startTime.getTime() + 8*60000);
    var timer = new Stopwatch(DEFAULT_GAME_TIME);
    timer.onDone( () => {
      // Game Over
    });
    timer.start();
    rooms[data.roomId].timer = timer;
    rooms[data.roomId].state = 'main';

    io.to(data.roomId).emit('user:startgame', { startTime: rooms[data.roomId].startTime,
                                                endTime: rooms[data.roomId].endTime
                                              });
  });

  socket.on('pause', (data, callback) => {
    var room = rooms[data.roomId];

    if (room.state !== 'main') {
      callback(null, 'Cannot pause at this time');
      return;
    }

    if (users[socket.id].role === 'Spy' && !data.intent) {
      callback({ isSpy: true });
      return;
    }
    room.timer.stop();
    room.state = 'paused';

    if (data.intent === 'guessLocation') {
      if (users[socket.id].role !== 'Spy') {
        callback(null, 'Only spies can attempt to guess the location');
        return;
      }
      socket.to(data.roomId).broadcast.emit('user:waitforlocation', null);
      io.to(socket.id).emit('spy:guesslocation', null);
    } else {
      socket.to(data.roomId).broadcast.emit('user:waitforaccusation', null);
      io.to(socket.id).emit('user:accuse', null);
    }
  });

  socket.on('toggleready', data => {
    var username = users[socket.id].username;
    var isReady = rooms[data.roomId].users[username];
    isReady = !isReady;
    rooms[data.roomId].users[users[socket.id].username] = isReady;
    io.in(data.roomId).emit('user:toggleready', {
      user: username,
      isReady: isReady
    });
  });

  socket.on('change-username', (data, callback) => {
    var oldUsername = users[socket.id].username;
    var newUsername = data.newUsername;
    if (_isValidUsername(newUsername)) {
      users[socket.id].username = newUsername;
      callback({ newUsername });
      io.emit('user:change-username', {
        oldUsername,
        newUsername
      });
    }
  });
});

http.listen(app.get('port'), () => {
  console.log(`listening on *:${app.get('port')}`);
});

function _createRoomId() {
  return crypto.createHash('md5').update(new Date().toString()).digest('hex').substring(0, ROOM_ID_SIZE);
}

function findUserBySid(userSid) {
  var socketIds = Object.keys(users);
  for (var i = 0; i < socketIds.length; i++) {
    var id = socketIds[i];
    if (users[id].userSid === userSid)
      return { userSid: id, userObj: users[id] };
  }
  return null;
}

function getNextUsername() {
  return 'Guest ' + nextUsername++ % 100;
}

function _isValidUsername(username) {
  return true;
}
<commit_msg>Server now emits which user has paused.<commit_after>var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var crypto = require('crypto');
var Stopwatch = require('timer-stopwatch');

app.set('port', (process.env.PORT || 5000));
app.set('view engine', 'jade');
app.use('/public', express.static('public'));
app.use('/node_modules', express.static('node_modules'));

app.use(require('connect-livereload')({
  port: 35729
}));

app.get('/', (req, res) => {
  res.render(`${__dirname}/public/index`);
});

app.get('/views/:name', (req, res) => {
  res.render(`${__dirname}/public/views/${req.params.name}`);
});

const ROOM_ID_SIZE = 5;
const DEFAULT_GAME_TIME = 8 * 60000; // 8 minutes in ms
var rooms = {};
var nextUsername = 1;
var users = {};

// TODO: We should probably figure out a better way to store the various
//       locations and roles rather than directly in the code.
var locations = {
  airplane: {
    roles: ['1st Class Passenger',
            'Air Marshal',
            'Mechanic',
            'Coach Passenger',
            'Flight Attendant',
            'Co-Pilot',
            'Captain']
  }
};

io.use((socket, next) => {
  var userSid = socket.handshake.query.userSid;
  if (!userSid || !findUserBySid(userSid)) {
    var newUser = {
      userSid: crypto.createHash('md5').update(new Date().toString()).digest('hex'),
      username: getNextUsername(),
      active: true
    };
    users[socket.id] = newUser;
  }
  else {
    var oldUser = findUserBySid(userSid);
    delete users[oldUser.userSid];
    oldUser.userObj.active = true;
    users[socket.id] = oldUser.userObj;
  }
  next();
});

io.on('connection', socket => {
  socket.clientIp = socket.request.connection.remoteAddress;

  socket.broadcast.emit('user:connect', {
    user: users[socket.id].username
  });

  socket.on('conn', (data, callback) => {
    callback({
      userSid: users[socket.id].userSid,
      username: users[socket.id].username
    });
  });

  socket.on('home', (data, callback) => {
    callback({
      users: Object.keys(users).filter(x => users[x].active).map(x => users[x].username),
      rooms
    });
  });

  socket.on('join', (data, callback) => {
    if (!rooms[data.roomId]) {
      callback(null, 'Room doesn\'t exist');
      return;
    }
    socket.join(data.roomId);

    var isHost = (socket.id === rooms[data.roomId].host);
    rooms[data.roomId].users[users[socket.id].username] = isHost;

    io.in(data.roomId).clients((error, clients) => {
      callback({ isHost: isHost,
                 host: users[rooms[data.roomId].host].username,
                 users: rooms[data.roomId].users
               });
    });

    socket.broadcast.to(data.roomId).emit('user:join', {
      user: users[socket.id].username
    });
  });

  socket.on('msg', (data, callback) => {
    io.in(data.roomId).emit('user:msg', { chatMsg: data.chatMsg });
    callback();
  });

  socket.on('disconnect', () => {
    socket.broadcast.emit('user:disconnect', {
        user: users[socket.id].username
    });

    var username = users[socket.id].username;
    Object.keys(rooms).forEach(x => {
      var cur = rooms[x].users;
      if (cur[username] !== undefined)
        delete cur[username];
      });

    users[socket.id].active = false;
  });

  socket.on('create-room', (data, callback) => {
    var roomId = _createRoomId();
    rooms[roomId] = {
      host: socket.id,
      users: {
        [users[socket.id].username]: false
      }
    };
    callback({ roomId });
  });

  socket.on('startgame', (data, callback) => {
    if (socket.id !== rooms[data.roomId].host) {
      callback(null, 'Only the host can start the game');
      return;
    }

    for (var u in rooms[data.roomId].users) {
      if (!rooms[data.roomId].users[u]) {
        callback(u, 'Not all users are ready');
        return;
      }
    }

    io.in(data.roomId).clients((error, clients) => {
      var locationKeys = Object.keys(locations);
      var location = locationKeys[locationKeys.length * Math.random() << 0];
      var roles = JSON.parse(JSON.stringify(locations[location].roles));
      var role = '';
      rooms[data.roomId].location = location;

      var clientCount = clients.length;
      for (var i = 0; i < clientCount; i++) {
        var clientId = clients.splice(clients.length * Math.random() << 0, 1)[0];
        if (i === 0) {
          role = 'Spy';
          io.in(clientId).emit('user:role', {role: role,
                                             location: 'Unknown'});
        } else {
          role = roles.splice(roles.length * Math.random() << 0, 1)[0];
          io.in(clientId).emit('user:role', {role: role,
                                             location: location});
        }
        users[clientId].role = role;
      }
    });

    rooms[data.roomId].startTime = new Date();
    rooms[data.roomId].endTime = new Date(rooms[data.roomId].startTime.getTime() + 8*60000);
    var timer = new Stopwatch(DEFAULT_GAME_TIME);
    timer.onDone( () => {
      // Game Over
    });
    timer.start();
    rooms[data.roomId].timer = timer;
    rooms[data.roomId].state = 'main';

    io.to(data.roomId).emit('user:startgame', { startTime: rooms[data.roomId].startTime,
                                                endTime: rooms[data.roomId].endTime
                                              });
  });

  socket.on('pause', (data, callback) => {
    var room = rooms[data.roomId];

    if (room.state !== 'main') {
      callback(null, 'Cannot pause at this time');
      return;
    }

    if (users[socket.id].role === 'Spy' && !data.intent) {
      callback({ isSpy: true });
      return;
    }
    room.timer.stop();
    room.state = 'paused';

    if (data.intent === 'guessLocation') {
      if (users[socket.id].role !== 'Spy') {
        callback(null, 'Only spies can attempt to guess the location');
        return;
      }
      socket.to(data.roomId).broadcast.emit('user:waitforlocation', {user: users[socket.id]});
      io.to(socket.id).emit('spy:guesslocation', null);
    } else {
      socket.to(data.roomId).broadcast.emit('user:waitforaccusation', {user: users[socket.id]});
      io.to(socket.id).emit('user:accuse', null);
    }
  });

  socket.on('toggleready', data => {
    var username = users[socket.id].username;
    var isReady = rooms[data.roomId].users[username];
    isReady = !isReady;
    rooms[data.roomId].users[users[socket.id].username] = isReady;
    io.in(data.roomId).emit('user:toggleready', {
      user: username,
      isReady: isReady
    });
  });

  socket.on('change-username', (data, callback) => {
    var oldUsername = users[socket.id].username;
    var newUsername = data.newUsername;
    if (_isValidUsername(newUsername)) {
      users[socket.id].username = newUsername;
      callback({ newUsername });
      io.emit('user:change-username', {
        oldUsername,
        newUsername
      });
    }
  });
});

http.listen(app.get('port'), () => {
  console.log(`listening on *:${app.get('port')}`);
});

function _createRoomId() {
  return crypto.createHash('md5').update(new Date().toString()).digest('hex').substring(0, ROOM_ID_SIZE);
}

function findUserBySid(userSid) {
  var socketIds = Object.keys(users);
  for (var i = 0; i < socketIds.length; i++) {
    var id = socketIds[i];
    if (users[id].userSid === userSid)
      return { userSid: id, userObj: users[id] };
  }
  return null;
}

function getNextUsername() {
  return 'Guest ' + nextUsername++ % 100;
}

function _isValidUsername(username) {
  return true;
}
<|endoftext|>


<commit_before>const _ = require('lodash');
const co = require('co');

const MAX_TIMEOUT_VALUE = 2147483647;

module.exports = function(underlying, interval = 0, {
    leading = null,
    trailing = null,
    maxWait = null,
    delayBetweenExecutions = null,
    condenseExecutions = false,
    allowConcurrentExecutions = false
} = {}) {
    leading = !!leading;
    trailing = !!trailing;

    // function must either execute on leading side or on trailing side, not neither
    if (!leading && !trailing) {
        trailing = true;
    }

    condenseExecutions = !!condenseExecutions;
    allowConcurrentExecutions = !!allowConcurrentExecutions;

    interval = _.clamp(interval, 0, MAX_TIMEOUT_VALUE);

    let applyMaxWait = !_.isNil(maxWait);
    maxWait = _.clamp(maxWait, interval, MAX_TIMEOUT_VALUE);

    let applyDelayBetweenExecutions = !_.isNil(delayBetweenExecutions);
    delayBetweenExecutions = _.clamp(delayBetweenExecutions, 0, MAX_TIMEOUT_VALUE);

    let executions = [];

    let waitTimer = null;
    let waitTimerIsImmediate = false;

    function cancelWait() {
        // properly cancel either immediate or timeout
        if (waitTimer) {
            if (waitTimerIsImmediate) {
                clearImmediate(waitTimer);
            }
            else {
                clearTimeout(waitTimer);
            }
        }

        waitTimer = null;
    }

    function scheduleWait(func, time) {
        cancelWait();

        // clamp to acceptable timeout values
        time = _.clamp(time, 0, MAX_TIMEOUT_VALUE);

        // use either immediates or timeouts
        if (time === 0) {
            waitTimer = setImmediate(func);
            waitTimerIsImmediate = true;
        }
        else {
            waitTimer = setTimeout(func, time);
            waitTimerIsImmediate = false;
        }
    }

    function dispatchExecution(execution) {
        return co(function*() {
            execution.startTime = Date.now();

            try {
                let result = yield underlying.apply(execution.this, execution.arguments);

                execution.resolve(result);
            }
            catch (err) {
                execution.reject(err);
            }

            execution.finishTime = Date.now();

            maintainExecutions(); // eslint-disable-line no-use-before-define
        })
    }

    function maintainExecutions() {
        let currentTime = Date.now();

        cancelWait();

        let nextCheckWait;

        for (let i = 0; i < executions.length; i++) {
            let execution = executions[i];

            if (execution.startTime) {
                // execution started

                if (execution.finishTime) {
                    // execution finished

                    if (applyDelayBetweenExecutions && execution.finishTime + delayBetweenExecutions > currentTime) {
                        // keep execution in array to constrain next execution
                        continue;
                    }
                    else if (execution.lastCallTime + interval > currentTime) {
                        // still within debounce interval, allow calls to continue being attached
                        continue;
                    }
                    else {
                        // remove execution from array and move on
                        executions.splice(i, 1);
                        i--;
                        continue;
                    }
                }
                else {
                    // no need to operate on an unfinished execution
                    continue;
                }
            }

            let expectedExecutionTime = execution.isLeading ? execution.firstCallTime : execution.lastCallTime + interval;

            if (i > 0) {
                // execution before this, check if wait required

                let lastExecution = executions[i - 1];

                if (!allowConcurrentExecutions) {
                    // no concurrent executions allowed

                    if (!lastExecution.startTime || !lastExecution.finishTime) {
                        // last execution is running or hasn't even started, so can't start this one
                        expectedExecutionTime = null;
                    }
                    else {
                        if (applyDelayBetweenExecutions) {
                            // need to make sure execution is sufficiently delayed
                            expectedExecutionTime = Math.max(lastExecution.finishTime + delayBetweenExecutions, expectedExecutionTime);
                        }
                    }
                }
                else {
                    if (applyDelayBetweenExecutions) {
                        // need to make sure execution is sufficiently delayed

                        if (!lastExecution.startTime) {
                            // last execution hasn't even started, so can't start this one
                            expectedExecutionTime = null;
                        }
                        else {
                            // need to make sure execution is sufficiently delayed
                            expectedExecutionTime = Math.max(lastExecution.startTime + delayBetweenExecutions, expectedExecutionTime);
                        }
                    }
                }
            }

            if (expectedExecutionTime) {
                if (expectedExecutionTime <= currentTime) {
                    // need to run the execution now, dispatch and continue operating on executions
                    dispatchExecution(execution);
                    continue;
                }
                else {
                    // defined amount of time before execution can begin
                    nextCheckWait = expectedExecutionTime - currentTime;
                }
            }

            break;
        }

        if (nextCheckWait) {
            scheduleWait(maintainExecutions, nextCheckWait);
        }
    }

    function createNewExecution({
        firstCallTime,
        isLeading,
        executionArguments,
        executionThis
    }) {
        let execution = {
            firstCallTime,
            lastCallTime: firstCallTime,
            isLeading,
            arguments: executionArguments,
            this: executionThis,
            startTime: null,
            finishTime: null
        };
        execution.promise = new Promise(function(resolve, reject) {
            execution.resolve = resolve;
            execution.reject = reject;
        });

        executions.push(execution);

        return execution;
    }

    function getApplicableExecution(currentTime, callArguments, callThis) {
        let lastExecution = _.last(executions);

        if (!lastExecution) {
            // there are no active executions, create a new one
            return createNewExecution({
                firstCallTime: currentTime,
                isLeading: leading,
                executionArguments: callArguments,
                executionThis: callThis
            });
        }

        if (condenseExecutions && !lastExecution.startTime) {
            // condense into non-executed execution
            return lastExecution;
        }

        if (lastExecution.lastCallTime + interval > currentTime) {
            // call falls within debounce interval of last execution

            if (lastExecution.isLeading && trailing) {
                // need to create a trailing execution
                return createNewExecution({
                    firstCallTime: lastExecution.firstCallTime,
                    isLeading: false,
                    executionArguments: callArguments,
                    executionThis: callThis
                });
            }

            if (applyMaxWait && lastExecution.firstCallTime + maxWait <= currentTime) {
                // max wait has been exceeded, create new execution
                return createNewExecution({
                    firstCallTime: currentTime,
                    isLeading: leading,
                    executionArguments: callArguments,
                    executionThis: callThis
                });
            }

            // attach to last execution
            return lastExecution;
        }

        // need to create a new execution
        return createNewExecution({
            firstCallTime: currentTime,
            isLeading: leading,
            executionArguments: callArguments,
            executionThis: callThis
        });
    }

    let debounced = function debouncer() {
        let currentTime = Date.now();
        let execution = getApplicableExecution(currentTime, arguments, this);

        // update last call time
        execution.lastCallTime = currentTime;

        if (!execution.isLeading) {
            // update arguments and this for trailing execution
            execution.arguments = arguments;
            execution.this = this;
        }

        maintainExecutions();

        return execution.promise;
    }

    debounced.flush = function flush() {
        _.forEach(executions, function(execution) { // eslint-disable-line lodash/prefer-filter
            if (!execution.startTime) {
                // execution hasn't started, dispatch
                dispatchExecution(execution);
            }
        });

        maintainExecutions();
    }

    debounced.cancel = function cancel() {
        executions = _.reject(executions, function(execution) {
            if (execution.startTime) {
                // execution has started, leave alone
                return false;
            }
            else {
                // execution hasn't started, cancel
                execution.reject(new Error('canceled'));
                return true;
            }
        });

        maintainExecutions();
    }

    return debounced;
}
<commit_msg>Change parameter coercion a bit.<commit_after>const _ = require('lodash');
const co = require('co');

const MAX_TIMEOUT_VALUE = 2147483647;

module.exports = function(underlying, interval = 0, {
    leading = null,
    trailing = null,
    maxWait = null,
    delayBetweenExecutions = null,
    condenseExecutions = false,
    allowConcurrentExecutions = false
} = {}) {
    leading = !!leading;
    trailing = !!trailing;

    // function must either execute on leading side or on trailing side, not neither
    if (!leading && !trailing) {
        trailing = true;
    }

    condenseExecutions = !!condenseExecutions;
    allowConcurrentExecutions = !!allowConcurrentExecutions;

    interval = _.clamp(_.toFinite(interval), 0, MAX_TIMEOUT_VALUE);

    let applyMaxWait = _.isFinite(_.toNumber(maxWait));
    maxWait = _.clamp(maxWait, interval, MAX_TIMEOUT_VALUE);

    let applyDelayBetweenExecutions = _.isFinite(_.toNumber(delayBetweenExecutions));
    delayBetweenExecutions = _.clamp(delayBetweenExecutions, 0, MAX_TIMEOUT_VALUE);

    let executions = [];

    let waitTimer = null;
    let waitTimerIsImmediate = false;

    function cancelWait() {
        // properly cancel either immediate or timeout
        if (waitTimer) {
            if (waitTimerIsImmediate) {
                clearImmediate(waitTimer);
            }
            else {
                clearTimeout(waitTimer);
            }
        }

        waitTimer = null;
    }

    function scheduleWait(func, time) {
        cancelWait();

        // clamp to acceptable timeout values
        time = _.clamp(time, 0, MAX_TIMEOUT_VALUE);

        // use either immediates or timeouts
        if (time === 0) {
            waitTimer = setImmediate(func);
            waitTimerIsImmediate = true;
        }
        else {
            waitTimer = setTimeout(func, time);
            waitTimerIsImmediate = false;
        }
    }

    function dispatchExecution(execution) {
        return co(function*() {
            execution.startTime = Date.now();

            try {
                let result = yield underlying.apply(execution.this, execution.arguments);

                execution.resolve(result);
            }
            catch (err) {
                execution.reject(err);
            }

            execution.finishTime = Date.now();

            maintainExecutions(); // eslint-disable-line no-use-before-define
        })
    }

    function maintainExecutions() {
        let currentTime = Date.now();

        cancelWait();

        let nextCheckWait;

        for (let i = 0; i < executions.length; i++) {
            let execution = executions[i];

            if (execution.startTime) {
                // execution started

                if (execution.finishTime) {
                    // execution finished

                    if (applyDelayBetweenExecutions && execution.finishTime + delayBetweenExecutions > currentTime) {
                        // keep execution in array to constrain next execution
                        continue;
                    }
                    else if (execution.lastCallTime + interval > currentTime) {
                        // still within debounce interval, allow calls to continue being attached
                        continue;
                    }
                    else {
                        // remove execution from array and move on
                        executions.splice(i, 1);
                        i--;
                        continue;
                    }
                }
                else {
                    // no need to operate on an unfinished execution
                    continue;
                }
            }

            let expectedExecutionTime = execution.isLeading ? execution.firstCallTime : execution.lastCallTime + interval;

            if (i > 0) {
                // execution before this, check if wait required

                let lastExecution = executions[i - 1];

                if (!allowConcurrentExecutions) {
                    // no concurrent executions allowed

                    if (!lastExecution.startTime || !lastExecution.finishTime) {
                        // last execution is running or hasn't even started, so can't start this one
                        expectedExecutionTime = null;
                    }
                    else {
                        if (applyDelayBetweenExecutions) {
                            // need to make sure execution is sufficiently delayed
                            expectedExecutionTime = Math.max(lastExecution.finishTime + delayBetweenExecutions, expectedExecutionTime);
                        }
                    }
                }
                else {
                    if (applyDelayBetweenExecutions) {
                        // need to make sure execution is sufficiently delayed

                        if (!lastExecution.startTime) {
                            // last execution hasn't even started, so can't start this one
                            expectedExecutionTime = null;
                        }
                        else {
                            // need to make sure execution is sufficiently delayed
                            expectedExecutionTime = Math.max(lastExecution.startTime + delayBetweenExecutions, expectedExecutionTime);
                        }
                    }
                }
            }

            if (expectedExecutionTime) {
                if (expectedExecutionTime <= currentTime) {
                    // need to run the execution now, dispatch and continue operating on executions
                    dispatchExecution(execution);
                    continue;
                }
                else {
                    // defined amount of time before execution can begin
                    nextCheckWait = expectedExecutionTime - currentTime;
                }
            }

            break;
        }

        if (nextCheckWait) {
            scheduleWait(maintainExecutions, nextCheckWait);
        }
    }

    function createNewExecution({
        firstCallTime,
        isLeading,
        executionArguments,
        executionThis
    }) {
        let execution = {
            firstCallTime,
            lastCallTime: firstCallTime,
            isLeading,
            arguments: executionArguments,
            this: executionThis,
            startTime: null,
            finishTime: null
        };
        execution.promise = new Promise(function(resolve, reject) {
            execution.resolve = resolve;
            execution.reject = reject;
        });

        executions.push(execution);

        return execution;
    }

    function getApplicableExecution(currentTime, callArguments, callThis) {
        let lastExecution = _.last(executions);

        if (!lastExecution) {
            // there are no active executions, create a new one
            return createNewExecution({
                firstCallTime: currentTime,
                isLeading: leading,
                executionArguments: callArguments,
                executionThis: callThis
            });
        }

        if (condenseExecutions && !lastExecution.startTime) {
            // condense into non-executed execution
            return lastExecution;
        }

        if (lastExecution.lastCallTime + interval > currentTime) {
            // call falls within debounce interval of last execution

            if (lastExecution.isLeading && trailing) {
                // need to create a trailing execution
                return createNewExecution({
                    firstCallTime: lastExecution.firstCallTime,
                    isLeading: false,
                    executionArguments: callArguments,
                    executionThis: callThis
                });
            }

            if (applyMaxWait && lastExecution.firstCallTime + maxWait <= currentTime) {
                // max wait has been exceeded, create new execution
                return createNewExecution({
                    firstCallTime: currentTime,
                    isLeading: leading,
                    executionArguments: callArguments,
                    executionThis: callThis
                });
            }

            // attach to last execution
            return lastExecution;
        }

        // need to create a new execution
        return createNewExecution({
            firstCallTime: currentTime,
            isLeading: leading,
            executionArguments: callArguments,
            executionThis: callThis
        });
    }

    let debounced = function debouncer() {
        let currentTime = Date.now();
        let execution = getApplicableExecution(currentTime, arguments, this);

        // update last call time
        execution.lastCallTime = currentTime;

        if (!execution.isLeading) {
            // update arguments and this for trailing execution
            execution.arguments = arguments;
            execution.this = this;
        }

        maintainExecutions();

        return execution.promise;
    }

    debounced.flush = function flush() {
        _.forEach(executions, function(execution) { // eslint-disable-line lodash/prefer-filter
            if (!execution.startTime) {
                // execution hasn't started, dispatch
                dispatchExecution(execution);
            }
        });

        maintainExecutions();
    }

    debounced.cancel = function cancel() {
        executions = _.reject(executions, function(execution) {
            if (execution.startTime) {
                // execution has started, leave alone
                return false;
            }
            else {
                // execution hasn't started, cancel
                execution.reject(new Error('canceled'));
                return true;
            }
        });

        maintainExecutions();
    }

    return debounced;
}
<|endoftext|>


<commit_before>function Container() {
    this.componentDescriptions = {};
    this.builders = {};

    this.bind = function (contract) {
        var name = this.getName(contract);
        if (name === "") throw 'invalid contract';
        var componentDescription = new ComponentDescription(name);
        this.componentDescriptions[name] = componentDescription;
        return new ConcreteSpecification(componentDescription);
    };

    this.resolve = function (contract, identityMap) {
        if (contract instanceof Value) {
            return contract.value;
        }
        var name = this.getName(contract);
        if (identityMap) {
            return undefined === identityMap[name] ? this.getBuilder(name).build(this, this.componentDescriptions[name], identityMap)
                : identityMap[name]
        }
        return this.getBuilder(name).build(this, this.componentDescriptions[name]);
    };

    this.value = function (value) {
        return new Value(value);
    };

    this.getBuilder = function (name) {
        if (undefined === this.builders[name]) {
            if (undefined === this.componentDescriptions[name]) throw 'component not found';
            this.builders[name] = this.componentDescriptions[name].builder;
        }
        return this.builders[name];
    };

    this.getName = function (contract) {
        return typeof contract === 'function' ? contract.name : contract;
    }
};

function Value(value) {
    this.value = value;
};

function ConcreteSpecification(description) {
    this.to = function (concrete) {
        if (undefined === concrete || null === concrete) throw 'concrete not defined';
        if (typeof concrete === 'function') {
            if (concrete.name !== "") {
                description.builder = new CtorBuilder(concrete);
            } else {
                description.builder = new FuncBuilder(concrete);
            }
        } else {
            description.builder = new WrapperBuilder(concrete);
        }
        return new DependencySpecification(description);
    }
};

function DependencySpecification(description) {
    this.use = function () {
        description.dependencies = [].slice.call(arguments);
        return this;
    };

    this.set = function (properties) {
        description.injectProperties = properties;
        return this;
    };

    this.asSingleton = function () {
        description.builder = new SingletonBuilder(description.builder);
        return this;
    }
};

function ComponentDescription(name) {
    this.name = name;
    this.builder = undefined;
    this.dependencies = undefined;
    this.injectProperties = undefined;
};

function ComponentBuilder() {

    this.build = function (container, componentDescription, identityMap) {
        var component = this.createComponent(this.buildDependencies(container, componentDescription.dependencies, identityMap));
        if (identityMap)
            identityMap[componentDescription.name] = component;
        this.injectProperties(container, component, componentDescription.injectProperties, identityMap);
        return component;
    };

    this.buildDependencies = function (container, dependencies, identityMap) {
        var resolved = [];
        if (undefined !== dependencies) {
            dependencies.forEach(function (dependency) {
                resolved.push(container.resolve(dependency, identityMap));
            });
        }
        return resolved;
    };

    this.injectProperties = function (container, component, properties, identityMap) {
        if (undefined === properties) return;
        for (var property in properties) {
            if (properties.hasOwnProperty(property))
                component[property] = container.resolve(properties[property], identityMap);
        };
    }

    this.createComponent = function (dependencies) {
        throw 'must be implemented in subclass';
    }
}

function WrapperBuilder(instance) {
    ComponentBuilder.call(this);

    this.build = function (container, componentDescription, identityMap) {
        return instance;
    }
};
WrapperBuilder.prototype = new ComponentBuilder();

function CtorBuilder(ctor) {
    ComponentBuilder.call(this);

    this.createComponent = function (dependencies) {
        var boundConstructor = ctor.bind.apply(ctor, [null].concat(dependencies));
        return new boundConstructor();
    }
};
CtorBuilder.prototype = new ComponentBuilder();

function FuncBuilder(fn) {
    ComponentBuilder.call(this);

    this.createComponent = function (dependencies) {
        return fn.apply(null, dependencies);
    }
};
FuncBuilder.prototype = new ComponentBuilder();

function SingletonBuilder(inner) {
    ComponentBuilder.call(this);
    this.instance = undefined;

    this.build = function (container, componentDescription, identityMap) {
        if (undefined === this.instance) {
            this.instance = inner.build(container, componentDescription, identityMap);
        }
        return this.instance;
    }
};
SingletonBuilder.prototype = new ComponentBuilder();

var roboContainer = function (contract, identityMap) {
    return new roboContainer.fn.r(contract, identityMap);
};
roboContainer.fn = roboContainer.prototype = new Container();
roboContainer.bind = function(contract) {
    return roboContainer.fn.bind(contract);
};
roboContainer.resolve = function(contract, identityMap) {
    return roboContainer.fn.resolve(contract, identityMap);
};
roboContainer.value = function(value) {
    return roboContainer.fn.value(value);
}
roboContainer.fn.r = function(contract, identityMap) {
    if (!contract) return this;
    return roboContainer.resolve(contract, identityMap);
};

module.exports = roboContainer;<commit_msg>added use strict directive<commit_after>"use strict";

function Container() {
    this.componentDescriptions = {};
    this.builders = {};

    this.bind = function (contract) {
        var name = this.getName(contract);
        if (name === "") throw 'invalid contract';
        var componentDescription = new ComponentDescription(name);
        this.componentDescriptions[name] = componentDescription;
        return new ConcreteSpecification(componentDescription);
    };

    this.resolve = function (contract, identityMap) {
        if (contract instanceof Value) {
            return contract.value;
        }
        var name = this.getName(contract);
        if (identityMap) {
            return undefined === identityMap[name] ? this.getBuilder(name).build(this, this.componentDescriptions[name], identityMap)
                : identityMap[name]
        }
        return this.getBuilder(name).build(this, this.componentDescriptions[name]);
    };

    this.value = function (value) {
        return new Value(value);
    };

    this.getBuilder = function (name) {
        if (undefined === this.builders[name]) {
            if (undefined === this.componentDescriptions[name]) throw 'component not found';
            this.builders[name] = this.componentDescriptions[name].builder;
        }
        return this.builders[name];
    };

    this.getName = function (contract) {
        return typeof contract === 'function' ? contract.name : contract;
    }
};

function Value(value) {
    this.value = value;
};

function ConcreteSpecification(description) {
    this.to = function (concrete) {
        if (undefined === concrete || null === concrete) throw 'concrete not defined';
        if (typeof concrete === 'function') {
            if (concrete.name !== "") {
                description.builder = new CtorBuilder(concrete);
            } else {
                description.builder = new FuncBuilder(concrete);
            }
        } else {
            description.builder = new WrapperBuilder(concrete);
        }
        return new DependencySpecification(description);
    }
};

function DependencySpecification(description) {
    this.use = function () {
        description.dependencies = [].slice.call(arguments);
        return this;
    };

    this.set = function (properties) {
        description.injectProperties = properties;
        return this;
    };

    this.asSingleton = function () {
        description.builder = new SingletonBuilder(description.builder);
        return this;
    }
};

function ComponentDescription(name) {
    this.name = name;
    this.builder = undefined;
    this.dependencies = undefined;
    this.injectProperties = undefined;
};

function ComponentBuilder() {

    this.build = function (container, componentDescription, identityMap) {
        var component = this.createComponent(this.buildDependencies(container, componentDescription.dependencies, identityMap));
        if (identityMap)
            identityMap[componentDescription.name] = component;
        this.injectProperties(container, component, componentDescription.injectProperties, identityMap);
        return component;
    };

    this.buildDependencies = function (container, dependencies, identityMap) {
        var resolved = [];
        if (undefined !== dependencies) {
            dependencies.forEach(function (dependency) {
                resolved.push(container.resolve(dependency, identityMap));
            });
        }
        return resolved;
    };

    this.injectProperties = function (container, component, properties, identityMap) {
        if (undefined === properties) return;
        for (var property in properties) {
            if (properties.hasOwnProperty(property))
                component[property] = container.resolve(properties[property], identityMap);
        };
    }

    this.createComponent = function (dependencies) {
        throw 'must be implemented in subclass';
    }
}

function WrapperBuilder(instance) {
    ComponentBuilder.call(this);

    this.build = function (container, componentDescription, identityMap) {
        return instance;
    }
};
WrapperBuilder.prototype = new ComponentBuilder();

function CtorBuilder(ctor) {
    ComponentBuilder.call(this);

    this.createComponent = function (dependencies) {
        var boundConstructor = ctor.bind.apply(ctor, [null].concat(dependencies));
        return new boundConstructor();
    }
};
CtorBuilder.prototype = new ComponentBuilder();

function FuncBuilder(fn) {
    ComponentBuilder.call(this);

    this.createComponent = function (dependencies) {
        return fn.apply(null, dependencies);
    }
};
FuncBuilder.prototype = new ComponentBuilder();

function SingletonBuilder(inner) {
    ComponentBuilder.call(this);
    this.instance = undefined;

    this.build = function (container, componentDescription, identityMap) {
        if (undefined === this.instance) {
            this.instance = inner.build(container, componentDescription, identityMap);
        }
        return this.instance;
    }
};
SingletonBuilder.prototype = new ComponentBuilder();

var roboContainer = function (contract, identityMap) {
    return new roboContainer.fn.r(contract, identityMap);
};
roboContainer.fn = roboContainer.prototype = new Container();
roboContainer.bind = function(contract) {
    return roboContainer.fn.bind(contract);
};
roboContainer.resolve = function(contract, identityMap) {
    return roboContainer.fn.resolve(contract, identityMap);
};
roboContainer.value = function(value) {
    return roboContainer.fn.value(value);
}
roboContainer.fn.r = function(contract, identityMap) {
    if (!contract) return this;
    return roboContainer.resolve(contract, identityMap);
};

module.exports = roboContainer;<|endoftext|>


<commit_before>/*jshint multistr: true ,node: true*/
"use strict";

var

    OS                  = require('os'),
    UTIL                = require('util'),
    /* NPM Third Party */
    _                   = require('lodash'),
    V                   = require('validator'),

    // NPMLOG              = require('npmlog'),
    MOMENT              = require('moment'),
    // ANSI                = require('ansi'),

    /* NPM Paytm */

    /* Project Files */

    /* Global */
    ADDITIONAL_VARS = {
        'ram'       : {},
        'ts'        : {},
        'uptime'    : {},
        'pid'       : {},
        'count'     : {},
        '__FUNC__'  : {},
        '__FILE__'  : {},
        '__LINE__'  : {},
        '__COLM__'  : {},
    },

    DEFAULT_LOGFORMAT   = '<%= prefix %> <%= ts %> [<%= uptime %>] [<%= count %>] <%= msg %>',
    DEFAULT_STREAM      = process.stdout,
    DEFAULT_WEIGHT      = 1000,
    DEFAULT_PREFIX      = 'INFO',
    DEFAULT_STYLE       = {},
    DEFAULT_TS_FORMAT   = 'YYYY-MM-DD HH:mm:ss';

function LGR(opts) {
    var self = this;

    this.count = 0; // Global count , sort of Id for log

    // will be later to set to levels object
    this.levels = {};

    // currnetly set level
    this.currentLevel = null;

    this.basicSettings();
}

/*
   Always nice to have __FUNC__, __FILE__, and __LINE__.
   Referred from http://stackoverflow.com/questions/11386492/accessing-line-number-in-v8-javascript-chrome-node-js
   Also read https://github.com/v8/v8/wiki/Stack-Trace-API
*/

function captureStack(){
    // Hijack the Error.prepareStackTrace() function, which can be used to format the captured structuredStack.
    var
        orig = Error.prepareStackTrace,
        err,
        stack;

    Error.prepareStackTrace = function(_, structuredStack){
        return structuredStack;
    };
    err = new Error();

    // Naming the anonymous function allows us to skip the top of the stack till customLGRLevel
    Error.captureStackTrace(err, LGR.customLGRLevel);
    stack = err.stack;

    // Don't forget to restore the hijacked function.
    Error.prepareStackTrace = orig;
    return stack;
}

/* returns the object according to demanded data */
LGR.prototype._getInfoObj = function(level){
    var
        logFormatObject = {
            "ram"       : UTIL.format(process.memoryUsage()),
            "ts"        : MOMENT().format(level.tsFormat),
            "uptime"    : process.uptime(),
            "pid"       : process.pid,
            "count"     : this.count,
            "hostname"  : OS.hostname(),
        },
        callSiteObj;

    if(level.stackTrace) {
        callSiteObj = captureStack()[3];
        _.set(logFormatObject,"__FUNC__",callSiteObj.getFunctionName() || '(anon)');
        _.set(logFormatObject,"__FILE__",callSiteObj.getFileName());
        _.set(logFormatObject,"__LINE__",callSiteObj.getLineNumber());
        _.set(logFormatObject,"__COLM__",callSiteObj.getColumnNumber());
    }

    return logFormatObject;
};

LGR.prototype.setLevel = function(level) {
    if(this.levels[level] === undefined) throw new Error('unknown level ' + level);
    this.currentLevel = this.levels[level];
};

LGR.prototype.getLevel = function() { return this.currentLevel.name; };
LGR.prototype.getLevels = function() {
    var
        self    = this,
        retObj  = {};

    Object.keys(self.levels).forEach(function(key){
        var lvl = self.levels[key];
        retObj[lvl.name] = lvl.weight;
    });

    return retObj;
};

LGR.prototype._checkStackTraceReqd = function(logFormat){
    if(
        logFormat.indexOf('__FUNC__') > 0 ||
        logFormat.indexOf('__FILE__') > 0 ||
        logFormat.indexOf('__LINE__') > 0 ||
        logFormat.indexOf('__COLM__') >= 0
    ) return true;
    else return false;
};

/* Levels */
LGR.prototype.addLevel = function(levelName, weight, style, dispPrefix, logFormat, stream, tsFormat){
    /*
        1. Lets just register the level
        We cant create streams or ansi cursors here
    */

    var
        self        = this,
        stackTrace  = false;

    if (!levelName ) throw new Error('name missing');
    if (!weight ) throw new Error('weight missing');

    // Default fallback values
    style       = style || DEFAULT_STYLE;
    dispPrefix  = dispPrefix || DEFAULT_PREFIX;
    logFormat   = logFormat || DEFAULT_LOGFORMAT;
    stream      = stream || DEFAULT_STREAM; // If stream unspecified , then it is process.stdout
    tsFormat    = tsFormat || DEFAULT_TS_FORMAT;

    // Lets parse logformat and see if we need capture stack which is the heavy part
    stackTrace = self._checkStackTraceReqd(logFormat);

    self.levels[levelName] = {
        'name'          : levelName,
        'weight'        : weight,
        'style'         : style,
        'dispPrefix'    : dispPrefix,
        'stream'        : stream,
        'logFormat'     : logFormat,
        'logTemplate'   : _.template(logFormat),
        'stackTrace'    : stackTrace,
        'tsFormat'      : tsFormat,
    };

    // Bind the function
    self[levelName] = function () {
        // insert level as first argument
        var a = new Array(arguments.length + 1);
        a[0] = levelName;
        for (var i = 0; i < arguments.length; i ++) a[i + 1] = arguments[i];
        return this._writeLog.apply(this, arguments);
    }.bind(this, levelName);
};

// Change a property of level
LGR.prototype.editLevel = function(levelName, prop, newVal) {
    /* weight, style, dispPrefix, logFormat, stream */
    var
        self = this,
        opts = ['weight', 'style', 'dispPrefix', 'logFormat', 'stream', 'tsFormat'];

    if(self.levels[levelName] === undefined) throw new Error('wrong level, see getlevels');
    if(opts.indexOf(prop) <=-1) throw new Error('wrong property');

    //set property
    self.levels[levelName][prop] = newVal;

    // Lets parse logformat and see if we need capture stack which is the heavy part
    if(prop === 'logFormat') {
        self.levels[levelName].stackTrace = self._checkStackTraceReqd(newVal);
        self.levels[levelName].logTemplate = _.template(newVal);
    }
};

/*
    Gets linear string for an argument
    if error and has stack then we take stack
    otherwise if it is an object or array we try to stringify it
    if it is a function we do toString
*/
LGR.prototype._getlinearMsg = function(arg) {

    var t = typeof arg;

    if(t === 'string')   return UTIL.format(arg);
    else if(t === 'function') {

        // tostring only gives back 'function' at times
        // using util.format here
        // return t.toString();
        return UTIL.format(arg);
    }
    else if(t === 'number') {
        return UTIL.format(arg);
    }
    else if(t === 'undefined') {
        return UTIL.format(arg);
    }

    /*
        Bug is json.stringify puts "" around each string

        Using infinite util.inspect instead of e levels, as per http://stackoverflow.com/questions/10729276/how-can-i-get-the-full-object-in-node-js-console-log-rather-than-object
    */
    else if (t === 'object' && (arg instanceof Error) && arg.stack) {
        return UTIL.inspect(arg.stack, {showHidden: false, depth: null, maxArrayLength: null});
        // return UTIL.format(arg.stack);
    }
    else if (t === 'object') {
        return UTIL.inspect(arg, {showHidden: false, depth: null, maxArrayLength: null});
        // return JSON.stringify(arg.stack);
        // return UTIL.format(arg.stack);
    }
    else if (t === 'boolean') {
        return UTIL.format(arg);
    }
    // no idea what is here
    else {
        try {
            return UTIL.inspect(arg, {showHidden: false, depth: null, maxArrayLength: null});
            // return UTIL.format(arg);
        } catch(ex) { return 'cannot parse ' + V.toString(arg); }
    }
};

// main log writing code
LGR.prototype._writeLog = function (lvl) {
    var
        self        = this,
        logline     = '',
        formatObj   = null,
        finalLog    = null,
        level       = self.levels[lvl];

    // increment count
    self.count++;

    // Don't do anything unless the log is less than the general log setting .
    if (level.weight < self.currentLevel.weight) return;

    // first lets concat all user sent args in a single line
    for (var i = 1; i < arguments.length; i ++)
        logline = logline + self._getlinearMsg(arguments[i]) + ' ';

    // remove last Space if any
    logline = logline.slice(0, -1);

    // format the log according to the format
    formatObj = self._getInfoObj(level);
    formatObj.msg = logline;
    formatObj.prefix = level.dispPrefix;

    // final line that goes to the stream
    finalLog = level.logTemplate(formatObj);

    // Add \n in the end after the formatting
    finalLog += '\n';

    level.stream.write(finalLog);
};

// update timestamp for all levels
LGR.prototype.updateTsFormat = function(tsFormat) {
    this._updatePropertyAllLevels('tsFormat', tsFormat);
};

LGR.prototype._updatePropertyAllLevels = function(prop, newVal) {
    var
        self = this;

    Object.keys(self.getLevels()).forEach(function(k){
        self.editLevel(k, prop, newVal);
    });
};

// initiate basic levels
LGR.prototype.basicSettings = function() {
    var self = this;

    /* Add log levels */
    // log.prefixStyle = { fg: 'magenta' }
    // log.headingStyle = { fg: 'white', bg: 'black' }

    self.addLevel('silly', -Infinity, { inverse: true }, 'SILL');
    self.addLevel('verbose', 1000, { fg: 'blue', bg: 'black' }, 'VERB');
    self.addLevel('info', 2000, { fg: 'green' }, 'INFO');
    self.addLevel('log', 2000, { fg: 'green' }, 'INFO');
    self.addLevel('http', 3000, { fg: 'green', bg: 'black' });
    self.addLevel('warn', 4000, { fg: 'black', bg: 'yellow' }, 'WARN');

    self.addLevel('error', 5000, { fg: 'red', bg: 'black' }, 'ERR!', '<%= prefix %> <%=hostname%> <%= ts %> [<%= uptime %>] [<%= count %>] <%= __FILE__ %>:<%= __FUNC__ %>:<%= __LINE__ %>:<%= __COLM__ %> <%= msg %>', process.stderr);
    self.addLevel('critical', 6000, {  fg : 'red', 'bg' : 'yellow'  }, 'CRIT!', '<%= prefix %> <%= ts %> <%= ram %> [<%= uptime %>] [<%= count %>] <%= __FILE__ %>:<%= __FUNC__ %>:<%= __LINE__ %>:<%= __COLM__ %> <%= msg %>', process.stderr);

    self.addLevel('silent', Infinity);

    // Set default level info
    self.setLevel('info');
};

module.exports = new LGR();
<commit_msg>putting hack aroung util maxlength check ; #27 . Test case to follow<commit_after>/*jshint multistr: true ,node: true*/
"use strict";

var

    OS                  = require('os'),
    UTIL                = require('util'),
    /* NPM Third Party */
    _                   = require('lodash'),
    V                   = require('validator'),

    // NPMLOG              = require('npmlog'),
    MOMENT              = require('moment'),
    // ANSI                = require('ansi'),

    /* NPM Paytm */

    /* Project Files */

    /* Global */
    ADDITIONAL_VARS = {
        'ram'       : {},
        'ts'        : {},
        'uptime'    : {},
        'pid'       : {},
        'count'     : {},
        '__FUNC__'  : {},
        '__FILE__'  : {},
        '__LINE__'  : {},
        '__COLM__'  : {},
    },

    DEFAULT_LOGFORMAT   = '<%= prefix %> <%= ts %> [<%= uptime %>] [<%= count %>] <%= msg %>',
    DEFAULT_STREAM      = process.stdout,
    DEFAULT_WEIGHT      = 1000,
    DEFAULT_PREFIX      = 'INFO',
    DEFAULT_STYLE       = {},
    DEFAULT_TS_FORMAT   = 'YYYY-MM-DD HH:mm:ss';

function LGR(opts) {
    var self = this;

    this.count = 0; // Global count , sort of Id for log

    // will be later to set to levels object
    this.levels = {};

    // currnetly set level
    this.currentLevel = null;

    this.basicSettings();
}

/*
   Always nice to have __FUNC__, __FILE__, and __LINE__.
   Referred from http://stackoverflow.com/questions/11386492/accessing-line-number-in-v8-javascript-chrome-node-js
   Also read https://github.com/v8/v8/wiki/Stack-Trace-API
*/

function captureStack(){
    // Hijack the Error.prepareStackTrace() function, which can be used to format the captured structuredStack.
    var
        orig = Error.prepareStackTrace,
        err,
        stack;

    Error.prepareStackTrace = function(_, structuredStack){
        return structuredStack;
    };
    err = new Error();

    // Naming the anonymous function allows us to skip the top of the stack till customLGRLevel
    Error.captureStackTrace(err, LGR.customLGRLevel);
    stack = err.stack;

    // Don't forget to restore the hijacked function.
    Error.prepareStackTrace = orig;
    return stack;
}

/* returns the object according to demanded data */
LGR.prototype._getInfoObj = function(level){
    var
        logFormatObject = {
            "ram"       : UTIL.format(process.memoryUsage()),
            "ts"        : MOMENT().format(level.tsFormat),
            "uptime"    : process.uptime(),
            "pid"       : process.pid,
            "count"     : this.count,
            "hostname"  : OS.hostname(),
        },
        callSiteObj;

    if(level.stackTrace) {
        callSiteObj = captureStack()[3];
        _.set(logFormatObject,"__FUNC__",callSiteObj.getFunctionName() || '(anon)');
        _.set(logFormatObject,"__FILE__",callSiteObj.getFileName());
        _.set(logFormatObject,"__LINE__",callSiteObj.getLineNumber());
        _.set(logFormatObject,"__COLM__",callSiteObj.getColumnNumber());
    }

    return logFormatObject;
};

LGR.prototype.setLevel = function(level) {
    if(this.levels[level] === undefined) throw new Error('unknown level ' + level);
    this.currentLevel = this.levels[level];
};

LGR.prototype.getLevel = function() { return this.currentLevel.name; };
LGR.prototype.getLevels = function() {
    var
        self    = this,
        retObj  = {};

    Object.keys(self.levels).forEach(function(key){
        var lvl = self.levels[key];
        retObj[lvl.name] = lvl.weight;
    });

    return retObj;
};

LGR.prototype._checkStackTraceReqd = function(logFormat){
    if(
        logFormat.indexOf('__FUNC__') > 0 ||
        logFormat.indexOf('__FILE__') > 0 ||
        logFormat.indexOf('__LINE__') > 0 ||
        logFormat.indexOf('__COLM__') >= 0
    ) return true;
    else return false;
};

/* Levels */
LGR.prototype.addLevel = function(levelName, weight, style, dispPrefix, logFormat, stream, tsFormat){
    /*
        1. Lets just register the level
        We cant create streams or ansi cursors here
    */

    var
        self        = this,
        stackTrace  = false;

    if (!levelName ) throw new Error('name missing');
    if (!weight ) throw new Error('weight missing');

    // Default fallback values
    style       = style || DEFAULT_STYLE;
    dispPrefix  = dispPrefix || DEFAULT_PREFIX;
    logFormat   = logFormat || DEFAULT_LOGFORMAT;
    stream      = stream || DEFAULT_STREAM; // If stream unspecified , then it is process.stdout
    tsFormat    = tsFormat || DEFAULT_TS_FORMAT;

    // Lets parse logformat and see if we need capture stack which is the heavy part
    stackTrace = self._checkStackTraceReqd(logFormat);

    self.levels[levelName] = {
        'name'          : levelName,
        'weight'        : weight,
        'style'         : style,
        'dispPrefix'    : dispPrefix,
        'stream'        : stream,
        'logFormat'     : logFormat,
        'logTemplate'   : _.template(logFormat),
        'stackTrace'    : stackTrace,
        'tsFormat'      : tsFormat,
    };

    // Bind the function
    self[levelName] = function () {
        // insert level as first argument
        var a = new Array(arguments.length + 1);
        a[0] = levelName;
        for (var i = 0; i < arguments.length; i ++) a[i + 1] = arguments[i];
        return this._writeLog.apply(this, arguments);
    }.bind(this, levelName);
};

// Change a property of level
LGR.prototype.editLevel = function(levelName, prop, newVal) {
    /* weight, style, dispPrefix, logFormat, stream */
    var
        self = this,
        opts = ['weight', 'style', 'dispPrefix', 'logFormat', 'stream', 'tsFormat'];

    if(self.levels[levelName] === undefined) throw new Error('wrong level, see getlevels');
    if(opts.indexOf(prop) <=-1) throw new Error('wrong property');

    //set property
    self.levels[levelName][prop] = newVal;

    // Lets parse logformat and see if we need capture stack which is the heavy part
    if(prop === 'logFormat') {
        self.levels[levelName].stackTrace = self._checkStackTraceReqd(newVal);
        self.levels[levelName].logTemplate = _.template(newVal);
    }
};

/*

    - If error object is there we take argument.stack

    - JSON.stringify doesnt handle Special things like NaN, circular dependencies very well
    - JSON.stringify puts "" around each string
    - util.ispect gives multiline output for objects whose print length > 60 chars

    - We take this for granted then that huge objects take time to print and are problematic on production
    - For multiline objects with \n , we again replace \n with empty string

    - for various outputs we use Validator.toString() but utl.format is anyway better than that

    - using infinite levels, depth and arraylength
    Reference : http://stackoverflow.com/questions/10729276/how-can-i-get-the-full-object-in-node-js-console-log-rather-than-object
*/
LGR.prototype._getlinearMsg = function(arg) {
    var
        t   = typeof arg,
        res =   '';

    if(
        t === 'string' ||
        t === 'function' ||
        t === 'number' ||
        t === 'undefined' ||
        t === 'boolean'
    )
        res = UTIL.format(arg);

    else if (t === 'object' && (arg instanceof Error) && arg.stack) {
        res = UTIL.inspect(arg.stack, {showHidden: false, depth: null, maxArrayLength: null});
        // return UTIL.format(arg.stack);
    }
    else if (t === 'object') {
        res = UTIL.inspect(arg, {showHidden: false, depth: null, maxArrayLength: null});
    }

    // no idea what is here
    else {
        try {
            res = UTIL.inspect(arg, {showHidden: false, depth: null, maxArrayLength: null});
        } catch(ex) { res = 'cannot parse '; }
    }

    // remove newlines and other wierd chars
    res = res.replace(/(\n|\t|\r)/gi,'');
    return res;
};

// main log writing code
LGR.prototype._writeLog = function (lvl) {
    var
        self        = this,
        logline     = '',
        formatObj   = null,
        finalLog    = null,
        level       = self.levels[lvl];

    // increment count
    self.count++;

    // Don't do anything unless the log is less than the general log setting .
    if (level.weight < self.currentLevel.weight) return;

    // first lets concat all user sent args in a single line
    for (var i = 1; i < arguments.length; i ++)
        logline = logline + self._getlinearMsg(arguments[i]) + ' ';

    // remove last Space if any
    logline = logline.slice(0, -1);

    // format the log according to the format
    formatObj = self._getInfoObj(level);
    formatObj.msg = logline;
    formatObj.prefix = level.dispPrefix;

    // final line that goes to the stream
    finalLog = level.logTemplate(formatObj);

    // Add \n in the end after the formatting
    finalLog += '\n';

    level.stream.write(finalLog);
};

// update timestamp for all levels
LGR.prototype.updateTsFormat = function(tsFormat) {
    this._updatePropertyAllLevels('tsFormat', tsFormat);
};

LGR.prototype._updatePropertyAllLevels = function(prop, newVal) {
    var
        self = this;

    Object.keys(self.getLevels()).forEach(function(k){
        self.editLevel(k, prop, newVal);
    });
};

// initiate basic levels
LGR.prototype.basicSettings = function() {
    var self = this;

    /* Add log levels */
    // log.prefixStyle = { fg: 'magenta' }
    // log.headingStyle = { fg: 'white', bg: 'black' }

    self.addLevel('silly', -Infinity, { inverse: true }, 'SILL');
    self.addLevel('verbose', 1000, { fg: 'blue', bg: 'black' }, 'VERB');
    self.addLevel('info', 2000, { fg: 'green' }, 'INFO');
    self.addLevel('log', 2000, { fg: 'green' }, 'INFO');
    self.addLevel('http', 3000, { fg: 'green', bg: 'black' });
    self.addLevel('warn', 4000, { fg: 'black', bg: 'yellow' }, 'WARN');

    self.addLevel('error', 5000, { fg: 'red', bg: 'black' }, 'ERR!', '<%= prefix %> <%=hostname%> <%= ts %> [<%= uptime %>] [<%= count %>] <%= __FILE__ %>:<%= __FUNC__ %>:<%= __LINE__ %>:<%= __COLM__ %> <%= msg %>', process.stderr);
    self.addLevel('critical', 6000, {  fg : 'red', 'bg' : 'yellow'  }, 'CRIT!', '<%= prefix %> <%= ts %> <%= ram %> [<%= uptime %>] [<%= count %>] <%= __FILE__ %>:<%= __FUNC__ %>:<%= __LINE__ %>:<%= __COLM__ %> <%= msg %>', process.stderr);

    self.addLevel('silent', Infinity);

    // Set default level info
    self.setLevel('info');
};

module.exports = new LGR();
<|endoftext|>