<commit_before>/*jshint node:true, laxcomma:true */

var util    = require('util')
  , net    = require('net')
  , config = require('./lib/config')
  , helpers = require('./lib/helpers')
  , fs     = require('fs')
  , events = require('events')
  , logger = require('./lib/logger')
  , set = require('./lib/set')
  , pm = require('./lib/process_metrics')
  , process_mgmt = require('./lib/process_mgmt')
  , mgmt = require('./lib/mgmt_console');

// initialize data structures with defaults for statsd stats
var keyCounter = {};
var counters = {};
var timers = {};
var timer_counters = {};
var gauges = {};
var sets = {};
var counter_rates = {};
var timer_data = {};
var pctThreshold = null;
var flushInterval, keyFlushInt, serverLoaded, mgmtServer;
var startup_time = Math.round(new Date().getTime() / 1000);
var backendEvents = new events.EventEmitter();
var healthStatus = config.healthStatus || 'up';
<<<<<<< HEAD
var old_timestamp = 0;
var timestamp_lag_namespace;
=======
var keyNameSanitize = true;
>>>>>>> pr/155

// Load and init the backend from the backends/ directory.
function loadBackend(config, name) {
  var backendmod = require(name);

  if (config.debug) {
    l.log("Loading backend: " + name, 'DEBUG');
  }

  var ret = backendmod.init(startup_time, config, backendEvents, l);
  if (!ret) {
    l.log("Failed to load backend: " + name);
    process.exit(1);
  }
}

// Load and init the server from the servers/ directory.
// The callback mimics the dgram 'message' event parameters (msg, rinfo)
//   msg: the message received by the server. may contain more than one metric
//   rinfo: contains remote address information and message length
//      (attributes are .address, .port, .family, .size - you're welcome)
function startServer(config, name, callback) {
  var servermod = require(name);

  if (config.debug) {
    l.log("Loading server: " + name, 'DEBUG');
  }

  var ret = servermod.start(config, callback);
  if (!ret) {
    l.log("Failed to load server: " + name);
    process.exit(1);
  }
}

// global for conf
var conf;

// Flush metrics to each backend.
function flushMetrics() {
  var time_stamp = Math.round(new Date().getTime() / 1000);
  if (old_timestamp > 0) {
    gauges[timestamp_lag_namespace] = (time_stamp - old_timestamp - (Number(conf.flushInterval)/1000));
  }
  old_timestamp = time_stamp;

  var metrics_hash = {
    counters: counters,
    gauges: gauges,
    timers: timers,
    timer_counters: timer_counters,
    sets: sets,
    counter_rates: counter_rates,
    timer_data: timer_data,
    pctThreshold: pctThreshold,
    histogram: conf.histogram
  };

  // After all listeners, reset the stats
  backendEvents.once('flush', function clear_metrics(ts, metrics) {
    // TODO: a lot of this should be moved up into an init/constructor so we don't have to do it every
    // single flushInterval....
    // allows us to flag all of these on with a single config but still override them individually
    conf.deleteIdleStats = conf.deleteIdleStats !== undefined ? conf.deleteIdleStats : false;
    if (conf.deleteIdleStats) {
      conf.deleteCounters = conf.deleteCounters !== undefined ? conf.deleteCounters : true;
      conf.deleteTimers = conf.deleteTimers !== undefined ? conf.deleteTimers : true;
      conf.deleteSets = conf.deleteSets !== undefined ? conf.deleteSets : true;
      conf.deleteGauges = conf.deleteGauges !== undefined ? conf.deleteGauges : true;
    }

    // Clear the counters
    conf.deleteCounters = conf.deleteCounters || false;
    for (var counter_key in metrics.counters) {
      if (conf.deleteCounters) {
        if ((counter_key.indexOf("packets_received") != -1) || (counter_key.indexOf("bad_lines_seen") != -1)) {
          metrics.counters[counter_key] = 0;
        } else {
         delete(metrics.counters[counter_key]);
        }
      } else {
        metrics.counters[counter_key] = 0;
      }
    }

    // Clear the timers
    conf.deleteTimers = conf.deleteTimers || false;
    for (var timer_key in metrics.timers) {
      if (conf.deleteTimers) {
        delete(metrics.timers[timer_key]);
        delete(metrics.timer_counters[timer_key]);
      } else {
        metrics.timers[timer_key] = [];
        metrics.timer_counters[timer_key] = 0;
     }
    }

    // Clear the sets
    conf.deleteSets = conf.deleteSets || false;
    for (var set_key in metrics.sets) {
      if (conf.deleteSets) {
        delete(metrics.sets[set_key]);
      } else {
        metrics.sets[set_key] = new set.Set();
      }
    }

	// normally gauges are not reset.  so if we don't delete them, continue to persist previous value
    conf.deleteGauges = conf.deleteGauges || false;
    if (conf.deleteGauges) {
      for (var gauge_key in metrics.gauges) {
        delete(metrics.gauges[gauge_key]);
      }
    }
  });

  pm.process_metrics(metrics_hash, flushInterval, time_stamp, function emitFlush(metrics) {
    backendEvents.emit('flush', time_stamp, metrics);
  });

}

var stats = {
  messages: {
    last_msg_seen: startup_time,
    bad_lines_seen: 0
  }
};

function sanitizeKeyName(key) {
  if (keyNameSanitize) {
    return key.replace(/\s+/g, '_')
              .replace(/\//g, '-')
              .replace(/[^a-zA-Z_\-0-9\.]/g, '');
  } else {
    return key;
  }
}

// Global for the logger
var l;

config.configFile(process.argv[2], function (config) {
  conf = config;

  process_mgmt.init(config);

  l = new logger.Logger(config.log || {});

  // setup config for stats prefix
  var prefixStats = config.prefixStats;
  prefixStats = prefixStats !== undefined ? prefixStats : "statsd";
  //setup the names for the stats stored in counters{}
  bad_lines_seen   = prefixStats + ".bad_lines_seen";
  packets_received = prefixStats + ".packets_received";
  timestamp_lag_namespace = prefixStats + ".timestamp_lag";

  //now set to zero so we can increment them
  counters[bad_lines_seen]   = 0;
  counters[packets_received] = 0;

<<<<<<< HEAD
  if (!serverLoaded) {
=======
  if (config.keyNameSanitize !== undefined) {
    keyNameSanitize = config.keyNameSanitize;
  }

  if (server === undefined) {
>>>>>>> pr/155

    // key counting
    var keyFlushInterval = Number((config.keyFlush && config.keyFlush.interval) || 0);

    // The default server is UDP
    var server = config.server || './servers/udp'
    serverLoaded = startServer(config, server, function (msg, rinfo) {
      backendEvents.emit('packet', msg, rinfo);
      counters[packets_received]++;
      var packet_data = msg.toString();
      if (packet_data.indexOf("\n") > -1) {
        var metrics = packet_data.split("\n");
      } else {
        var metrics = [ packet_data ] ;
      }

      for (var midx in metrics) {
        if (metrics[midx].length === 0) {
          continue;
        }
        if (config.dumpMessages) {
          l.log(metrics[midx].toString());
        }
        var bits = metrics[midx].toString().split(':');
        var key = sanitizeKeyName(bits.shift());

        if (keyFlushInterval > 0) {
          if (! keyCounter[key]) {
            keyCounter[key] = 0;
          }
          keyCounter[key] += 1;
        }

        if (bits.length === 0) {
          bits.push("1");
        }

        for (var i = 0; i < bits.length; i++) {
          var sampleRate = 1;
          var fields = bits[i].split("|");
          if (!helpers.is_valid_packet(fields)) {
              l.log('Bad line: ' + fields + ' in msg "' + metrics[midx] +'"');
              counters[bad_lines_seen]++;
              stats.messages.bad_lines_seen++;
              continue;
          }
          if (fields[2]) {
            sampleRate = Number(fields[2].match(/^@([\d\.]+)/)[1]);
          }

          var metric_type = fields[1].trim();
          if (metric_type === "ms") {
            if (! timers[key]) {
              timers[key] = [];
              timer_counters[key] = 0;
            }
            timers[key].push(Number(fields[0] || 0));
            timer_counters[key] += (1 / sampleRate);
          } else if (metric_type === "g") {
            if (gauges[key] && fields[0].match(/^[-+]/)) {
              gauges[key] += Number(fields[0] || 0);
            } else {
              gauges[key] = Number(fields[0] || 0);
            }
          } else if (metric_type === "s") {
            if (! sets[key]) {
              sets[key] = new set.Set();
            }
            sets[key].insert(fields[0] || '0');
          } else {
            if (! counters[key]) {
              counters[key] = 0;
            }
            counters[key] += Number(fields[0] || 1) * (1 / sampleRate);
          }
        }
      }

      stats.messages.last_msg_seen = Math.round(new Date().getTime() / 1000);
    });

    mgmtServer = net.createServer(function(stream) {
      stream.setEncoding('ascii');

      stream.on('error', function(err) {
        l.log('Caught ' + err +', Moving on');
      });

      stream.on('data', function(data) {
        var cmdline = data.trim().split(" ");
        var cmd = cmdline.shift();

        switch(cmd) {
          case "help":
            stream.write("Commands: stats, counters, timers, gauges, delcounters, deltimers, delgauges, health, quit\n\n");
            break;

          case "health":
            if (cmdline.length > 0) {
              var cmdaction = cmdline[0].toLowerCase();
              if (cmdaction === 'up') {
                healthStatus = 'up';
              } else if (cmdaction === 'down') {
                healthStatus = 'down';
              }
            }
            stream.write("health: " + healthStatus + "\n");
            break;

          case "stats":
            var now    = Math.round(new Date().getTime() / 1000);
            var uptime = now - startup_time;

            stream.write("uptime: " + uptime + "\n");

            var stat_writer = function(group, metric, val) {
              var delta;

              if (metric.match("^last_")) {
                delta = now - val;
              }
              else {
                delta = val;
              }

              stream.write(group + "." + metric + ": " + delta + "\n");
            };

            // Loop through the base stats
            for (var group in stats) {
              for (var metric in stats[group]) {
                stat_writer(group, metric, stats[group][metric]);
              }
            }

            backendEvents.once('status', function(writeCb) {
              stream.write("END\n\n");
            });

            // Let each backend contribute its status
            backendEvents.emit('status', function(err, name, stat, val) {
              if (err) {
                l.log("Failed to read stats for backend " +
                        name + ": " + err);
              } else {
                stat_writer(name, stat, val);
              }
            });

            break;

          case "counters":
            stream.write(util.inspect(counters) + "\n");
            stream.write("END\n\n");
            break;

          case "timers":
            stream.write(util.inspect(timers) + "\n");
            stream.write("END\n\n");
            break;

          case "gauges":
            stream.write(util.inspect(gauges) + "\n");
            stream.write("END\n\n");
            break;

          case "delcounters":
            mgmt.delete_stats(counters, cmdline, stream);
            break;

          case "deltimers":
            mgmt.delete_stats(timers, cmdline, stream);
            break;

          case "delgauges":
            mgmt.delete_stats(gauges, cmdline, stream);
            break;

          case "quit":
            stream.end();
            break;

          default:
            stream.write("ERROR\n");
            break;
        }

      });
    });

    mgmtServer.listen(config.mgmt_port || 8126, config.mgmt_address || undefined);

    util.log("server is up");

    pctThreshold = config.percentThreshold || 90;
    if (!Array.isArray(pctThreshold)) {
      pctThreshold = [ pctThreshold ]; // listify percentiles so single values work the same
    }

    flushInterval = Number(config.flushInterval || 10000);
    config.flushInterval = flushInterval;

    if (config.backends) {
      for (var i = 0; i < config.backends.length; i++) {
        loadBackend(config, config.backends[i]);
      }
    } else {
      // The default backend is graphite
      loadBackend(config, './backends/graphite');
    }

    // Setup the flush timer
    var flushInt = setInterval(flushMetrics, flushInterval);

    if (keyFlushInterval > 0) {
      var keyFlushPercent = Number((config.keyFlush && config.keyFlush.percent) || 100);
      var keyFlushLog = config.keyFlush && config.keyFlush.log;

      keyFlushInt = setInterval(function () {
        var sortedKeys = [];

        for (var key in keyCounter) {
          sortedKeys.push([key, keyCounter[key]]);
        }

        sortedKeys.sort(function(a, b) { return b[1] - a[1]; });

        var logMessage = "";
        var timeString = (new Date()) + "";

        // only show the top "keyFlushPercent" keys
        for (var i = 0, e = sortedKeys.length * (keyFlushPercent / 100); i < e; i++) {
          logMessage += timeString + " count=" + sortedKeys[i][1] + " key=" + sortedKeys[i][0] + "\n";
        }

        if (keyFlushLog) {
          var logFile = fs.createWriteStream(keyFlushLog, {flags: 'a+'});
          logFile.write(logMessage);
          logFile.end();
        } else {
          process.stdout.write(logMessage);
        }

        // clear the counter
        keyCounter = {};
      }, keyFlushInterval);
    }
  }
});

process.on('exit', function () {
  flushMetrics();
});
<commit_msg>Fix merge conflicts with master<commit_after>/*jshint node:true, laxcomma:true */

var util    = require('util')
  , net    = require('net')
  , config = require('./lib/config')
  , helpers = require('./lib/helpers')
  , fs     = require('fs')
  , events = require('events')
  , logger = require('./lib/logger')
  , set = require('./lib/set')
  , pm = require('./lib/process_metrics')
  , process_mgmt = require('./lib/process_mgmt')
  , mgmt = require('./lib/mgmt_console');

// initialize data structures with defaults for statsd stats
var keyCounter = {};
var counters = {};
var timers = {};
var timer_counters = {};
var gauges = {};
var sets = {};
var counter_rates = {};
var timer_data = {};
var pctThreshold = null;
var flushInterval, keyFlushInt, serverLoaded, mgmtServer;
var startup_time = Math.round(new Date().getTime() / 1000);
var backendEvents = new events.EventEmitter();
var healthStatus = config.healthStatus || 'up';
var old_timestamp = 0;
var timestamp_lag_namespace;
var keyNameSanitize = true;

// Load and init the backend from the backends/ directory.
function loadBackend(config, name) {
  var backendmod = require(name);

  if (config.debug) {
    l.log("Loading backend: " + name, 'DEBUG');
  }

  var ret = backendmod.init(startup_time, config, backendEvents, l);
  if (!ret) {
    l.log("Failed to load backend: " + name);
    process.exit(1);
  }
}

// Load and init the server from the servers/ directory.
// The callback mimics the dgram 'message' event parameters (msg, rinfo)
//   msg: the message received by the server. may contain more than one metric
//   rinfo: contains remote address information and message length
//      (attributes are .address, .port, .family, .size - you're welcome)
function startServer(config, name, callback) {
  var servermod = require(name);

  if (config.debug) {
    l.log("Loading server: " + name, 'DEBUG');
  }

  var ret = servermod.start(config, callback);
  if (!ret) {
    l.log("Failed to load server: " + name);
    process.exit(1);
  }
}

// global for conf
var conf;

// Flush metrics to each backend.
function flushMetrics() {
  var time_stamp = Math.round(new Date().getTime() / 1000);
  if (old_timestamp > 0) {
    gauges[timestamp_lag_namespace] = (time_stamp - old_timestamp - (Number(conf.flushInterval)/1000));
  }
  old_timestamp = time_stamp;

  var metrics_hash = {
    counters: counters,
    gauges: gauges,
    timers: timers,
    timer_counters: timer_counters,
    sets: sets,
    counter_rates: counter_rates,
    timer_data: timer_data,
    pctThreshold: pctThreshold,
    histogram: conf.histogram
  };

  // After all listeners, reset the stats
  backendEvents.once('flush', function clear_metrics(ts, metrics) {
    // TODO: a lot of this should be moved up into an init/constructor so we don't have to do it every
    // single flushInterval....
    // allows us to flag all of these on with a single config but still override them individually
    conf.deleteIdleStats = conf.deleteIdleStats !== undefined ? conf.deleteIdleStats : false;
    if (conf.deleteIdleStats) {
      conf.deleteCounters = conf.deleteCounters !== undefined ? conf.deleteCounters : true;
      conf.deleteTimers = conf.deleteTimers !== undefined ? conf.deleteTimers : true;
      conf.deleteSets = conf.deleteSets !== undefined ? conf.deleteSets : true;
      conf.deleteGauges = conf.deleteGauges !== undefined ? conf.deleteGauges : true;
    }

    // Clear the counters
    conf.deleteCounters = conf.deleteCounters || false;
    for (var counter_key in metrics.counters) {
      if (conf.deleteCounters) {
        if ((counter_key.indexOf("packets_received") != -1) || (counter_key.indexOf("bad_lines_seen") != -1)) {
          metrics.counters[counter_key] = 0;
        } else {
         delete(metrics.counters[counter_key]);
        }
      } else {
        metrics.counters[counter_key] = 0;
      }
    }

    // Clear the timers
    conf.deleteTimers = conf.deleteTimers || false;
    for (var timer_key in metrics.timers) {
      if (conf.deleteTimers) {
        delete(metrics.timers[timer_key]);
        delete(metrics.timer_counters[timer_key]);
      } else {
        metrics.timers[timer_key] = [];
        metrics.timer_counters[timer_key] = 0;
     }
    }

    // Clear the sets
    conf.deleteSets = conf.deleteSets || false;
    for (var set_key in metrics.sets) {
      if (conf.deleteSets) {
        delete(metrics.sets[set_key]);
      } else {
        metrics.sets[set_key] = new set.Set();
      }
    }

	// normally gauges are not reset.  so if we don't delete them, continue to persist previous value
    conf.deleteGauges = conf.deleteGauges || false;
    if (conf.deleteGauges) {
      for (var gauge_key in metrics.gauges) {
        delete(metrics.gauges[gauge_key]);
      }
    }
  });

  pm.process_metrics(metrics_hash, flushInterval, time_stamp, function emitFlush(metrics) {
    backendEvents.emit('flush', time_stamp, metrics);
  });

}

var stats = {
  messages: {
    last_msg_seen: startup_time,
    bad_lines_seen: 0
  }
};

function sanitizeKeyName(key) {
  if (keyNameSanitize) {
    return key.replace(/\s+/g, '_')
              .replace(/\//g, '-')
              .replace(/[^a-zA-Z_\-0-9\.]/g, '');
  } else {
    return key;
  }
}

// Global for the logger
var l;

config.configFile(process.argv[2], function (config) {
  conf = config;

  process_mgmt.init(config);

  l = new logger.Logger(config.log || {});

  // setup config for stats prefix
  var prefixStats = config.prefixStats;
  prefixStats = prefixStats !== undefined ? prefixStats : "statsd";
  //setup the names for the stats stored in counters{}
  bad_lines_seen   = prefixStats + ".bad_lines_seen";
  packets_received = prefixStats + ".packets_received";
  timestamp_lag_namespace = prefixStats + ".timestamp_lag";

  //now set to zero so we can increment them
  counters[bad_lines_seen]   = 0;
  counters[packets_received] = 0;

  if (config.keyNameSanitize !== undefined) {
    keyNameSanitize = config.keyNameSanitize;
  }

    if (!serverLoaded) {

    // key counting
    var keyFlushInterval = Number((config.keyFlush && config.keyFlush.interval) || 0);

    // The default server is UDP
    var server = config.server || './servers/udp'
    serverLoaded = startServer(config, server, function (msg, rinfo) {
      backendEvents.emit('packet', msg, rinfo);
      counters[packets_received]++;
      var packet_data = msg.toString();
      if (packet_data.indexOf("\n") > -1) {
        var metrics = packet_data.split("\n");
      } else {
        var metrics = [ packet_data ] ;
      }

      for (var midx in metrics) {
        if (metrics[midx].length === 0) {
          continue;
        }
        if (config.dumpMessages) {
          l.log(metrics[midx].toString());
        }
        var bits = metrics[midx].toString().split(':');
        var key = sanitizeKeyName(bits.shift());

        if (keyFlushInterval > 0) {
          if (! keyCounter[key]) {
            keyCounter[key] = 0;
          }
          keyCounter[key] += 1;
        }

        if (bits.length === 0) {
          bits.push("1");
        }

        for (var i = 0; i < bits.length; i++) {
          var sampleRate = 1;
          var fields = bits[i].split("|");
          if (!helpers.is_valid_packet(fields)) {
              l.log('Bad line: ' + fields + ' in msg "' + metrics[midx] +'"');
              counters[bad_lines_seen]++;
              stats.messages.bad_lines_seen++;
              continue;
          }
          if (fields[2]) {
            sampleRate = Number(fields[2].match(/^@([\d\.]+)/)[1]);
          }

          var metric_type = fields[1].trim();
          if (metric_type === "ms") {
            if (! timers[key]) {
              timers[key] = [];
              timer_counters[key] = 0;
            }
            timers[key].push(Number(fields[0] || 0));
            timer_counters[key] += (1 / sampleRate);
          } else if (metric_type === "g") {
            if (gauges[key] && fields[0].match(/^[-+]/)) {
              gauges[key] += Number(fields[0] || 0);
            } else {
              gauges[key] = Number(fields[0] || 0);
            }
          } else if (metric_type === "s") {
            if (! sets[key]) {
              sets[key] = new set.Set();
            }
            sets[key].insert(fields[0] || '0');
          } else {
            if (! counters[key]) {
              counters[key] = 0;
            }
            counters[key] += Number(fields[0] || 1) * (1 / sampleRate);
          }
        }
      }

      stats.messages.last_msg_seen = Math.round(new Date().getTime() / 1000);
    });

    mgmtServer = net.createServer(function(stream) {
      stream.setEncoding('ascii');

      stream.on('error', function(err) {
        l.log('Caught ' + err +', Moving on');
      });

      stream.on('data', function(data) {
        var cmdline = data.trim().split(" ");
        var cmd = cmdline.shift();

        switch(cmd) {
          case "help":
            stream.write("Commands: stats, counters, timers, gauges, delcounters, deltimers, delgauges, health, quit\n\n");
            break;

          case "health":
            if (cmdline.length > 0) {
              var cmdaction = cmdline[0].toLowerCase();
              if (cmdaction === 'up') {
                healthStatus = 'up';
              } else if (cmdaction === 'down') {
                healthStatus = 'down';
              }
            }
            stream.write("health: " + healthStatus + "\n");
            break;

          case "stats":
            var now    = Math.round(new Date().getTime() / 1000);
            var uptime = now - startup_time;

            stream.write("uptime: " + uptime + "\n");

            var stat_writer = function(group, metric, val) {
              var delta;

              if (metric.match("^last_")) {
                delta = now - val;
              }
              else {
                delta = val;
              }

              stream.write(group + "." + metric + ": " + delta + "\n");
            };

            // Loop through the base stats
            for (var group in stats) {
              for (var metric in stats[group]) {
                stat_writer(group, metric, stats[group][metric]);
              }
            }

            backendEvents.once('status', function(writeCb) {
              stream.write("END\n\n");
            });

            // Let each backend contribute its status
            backendEvents.emit('status', function(err, name, stat, val) {
              if (err) {
                l.log("Failed to read stats for backend " +
                        name + ": " + err);
              } else {
                stat_writer(name, stat, val);
              }
            });

            break;

          case "counters":
            stream.write(util.inspect(counters) + "\n");
            stream.write("END\n\n");
            break;

          case "timers":
            stream.write(util.inspect(timers) + "\n");
            stream.write("END\n\n");
            break;

          case "gauges":
            stream.write(util.inspect(gauges) + "\n");
            stream.write("END\n\n");
            break;

          case "delcounters":
            mgmt.delete_stats(counters, cmdline, stream);
            break;

          case "deltimers":
            mgmt.delete_stats(timers, cmdline, stream);
            break;

          case "delgauges":
            mgmt.delete_stats(gauges, cmdline, stream);
            break;

          case "quit":
            stream.end();
            break;

          default:
            stream.write("ERROR\n");
            break;
        }

      });
    });

    mgmtServer.listen(config.mgmt_port || 8126, config.mgmt_address || undefined);

    util.log("server is up");

    pctThreshold = config.percentThreshold || 90;
    if (!Array.isArray(pctThreshold)) {
      pctThreshold = [ pctThreshold ]; // listify percentiles so single values work the same
    }

    flushInterval = Number(config.flushInterval || 10000);
    config.flushInterval = flushInterval;

    if (config.backends) {
      for (var i = 0; i < config.backends.length; i++) {
        loadBackend(config, config.backends[i]);
      }
    } else {
      // The default backend is graphite
      loadBackend(config, './backends/graphite');
    }

    // Setup the flush timer
    var flushInt = setInterval(flushMetrics, flushInterval);

    if (keyFlushInterval > 0) {
      var keyFlushPercent = Number((config.keyFlush && config.keyFlush.percent) || 100);
      var keyFlushLog = config.keyFlush && config.keyFlush.log;

      keyFlushInt = setInterval(function () {
        var sortedKeys = [];

        for (var key in keyCounter) {
          sortedKeys.push([key, keyCounter[key]]);
        }

        sortedKeys.sort(function(a, b) { return b[1] - a[1]; });

        var logMessage = "";
        var timeString = (new Date()) + "";

        // only show the top "keyFlushPercent" keys
        for (var i = 0, e = sortedKeys.length * (keyFlushPercent / 100); i < e; i++) {
          logMessage += timeString + " count=" + sortedKeys[i][1] + " key=" + sortedKeys[i][0] + "\n";
        }

        if (keyFlushLog) {
          var logFile = fs.createWriteStream(keyFlushLog, {flags: 'a+'});
          logFile.write(logMessage);
          logFile.end();
        } else {
          process.stdout.write(logMessage);
        }

        // clear the counter
        keyCounter = {};
      }, keyFlushInterval);
    }
  }
});

process.on('exit', function () {
  flushMetrics();
});
<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var Item = function(name, price) {
  return {
    name: function() {
      return name;
    },
    unitPrice: function() {
      return price;
    }
  }
};

var Basket = function() {
  var items = [];
  return {
    add: function(item) {
      items.push(item);
    },

    count: function() { return items.length; },

    remove: function(item) {
      var idx = -1;
      for (var i=0;i<items.length;++i) {
        if (items[i].name() === item.name()) {
          idx = i;
          break;
        }
      }
      if (idx !== -1) {
        items.splice(idx, 1);
      }
    }
  };
};

var Register = function() {

  var runningSum = 0;
  var offers = [];
  var items = [];

  return {
     price: function(item) {
       items.push(item);
     },

     insertOffer: function(offer) {
       offers.push(offer);
     },

     total: function() {
       var runningSum = 0;
       for (var i=0;i<items.length;++i) {
         runningSum += items[i].unitPrice();
       }

       return runningSum;
     }
  };
};

var numberOfItemsMatchingInBasket = function(item, basket) {
  var count = 0;
  for (var i=0;i<basket.length;++i) {
    if (basket[i].name() === item.name()) {
      count++;
    }
  }

  return count;
};

var Receipt = function() {
  var items = [];
  var total = 0;

  return {
    items: function() { return items; },

    total: function() { return total; },

    record: function(description, price) {
      items.push(description + ' : ' + price);
      total += price;
    },
  };
};

var Unit = function(item) {
  return {
    applies: function(basket, receipt) {
      if (numberOfItemsMatchingInBasket(item, basket) >= 1) {
        receipt.record(item.name(), item.unitPrice());
        return true;
      } else {
        return false;
      }
    }
  };
};

var BuyOneGetOneFree = function(item) {
  return {
    applies: function(basket, receipt) {
      if (numberOfItemsMatchingInBasket(item,basket) >= 2) {
        receipt.record("BOGOF " + item.name(), item.unitPrice());
        return true;
      } else {
        return false;
      }
    }
  };
};

var ThreeForTwo = function(item) {
  return {
    applies: function(basket, receipt) {
      if (numberOfItemsMatchingInBasket(item,basket) >= 3) {
        receipt.record("3 for 2 " + item.name(), 2 * item.unitPrice());
        return true;
      } else {
        return false;
      }
    }
  }
};

describe('super market', function() {

  var beans = new Item('beans', 45);
  var batteries = new Item('batteries', 60);

  describe('item', function() {
    it('have a unit price', function() {
      assert.equal(45, beans.unitPrice());
    });
  });

  describe('basket', function() {
    it('can keep track of items', function() {
      var basket = new Basket();
      basket.add(beans);

      assert.equal(1, basket.count());

      basket.remove(beans);
      assert.equal(0, basket.count());
    });
  });

  describe('receipt', function() {
    it('consists of items and a total price', function() {
      var receipt = new Receipt();
      receipt.record('2 x Beans', 45);
      receipt.record('4 x Shoes', 10);

      assert.equal(2, receipt.items().length);
      assert.equal(55, receipt.total());
    });
  });

  describe('register', function() {
    it('totals the price of items', function() {
      var register = new Register();
      register.price(beans);
      register.price(beans);

      assert.equal(90, register.total());
    });

    it('knows of offers', function() {
      var register = new Register();
      register.insertOffer(new BuyOneGetOneFree(beans));

      register.price(beans);
      register.price(beans);

      assert.equal(45, register.total());
    });
  });

  describe('offer', function() {

    var item, price, receipt;

    beforeEach(function() {
      item = undefined;
      price = undefined;
      receipt = {
        record: function(item_, price_) {
          item = item_;
          price = price_;
        }
      };
    });

    it('unit offer', function() {
      var offer = new Unit(beans);
      assert(offer.applies([beans], receipt));
      assert.equal(45, price);
    });

    it('buy-one-get-one-free', function() {
      var offer = new BuyOneGetOneFree(beans);
      assert(offer.applies([beans, beans], receipt));
      assert.equal(45, price);
    });

    it('3 for 2', function() {
      var offer = new ThreeForTwo(beans);
      assert(offer.applies([beans,beans,beans], receipt));
      assert.equal(90, price);
    });
  });
});
<commit_msg>notes<commit_after>"use strict";

var assert = require('assert');

var Item = function(name, price) {
  return {
    name: function() {
      return name;
    },
    unitPrice: function() {
      return price;
    }
  }
};

var Basket = function() {
  var items = [];
  return {
    add: function(item) {
      items.push(item);
    },

    count: function() { return items.length; },

    remove: function(item) {
      var idx = -1;
      for (var i=0;i<items.length;++i) {
        if (items[i].name() === item.name()) {
          idx = i;
          break;
        }
      }
      if (idx !== -1) {
        items.splice(idx, 1);
      }
    }
  };
};

var Register = function() {

  var runningSum = 0;
  var offers = [];
  var items = [];

  return {
     price: function(item) {
       items.push(item);
     },

     insertOffer: function(offer) {
       offers.push(offer);
     },

     total: function() {
       var runningSum = 0;
       for (var i=0;i<items.length;++i) {
         runningSum += items[i].unitPrice();
       }

       return runningSum;
     }
  };
};

var numberOfItemsMatchingInBasket = function(item, basket) {
  var count = 0;
  for (var i=0;i<basket.length;++i) {
    if (basket[i].name() === item.name()) {
      count++;
    }
  }

  return count;
};

var Receipt = function() {
  var items = [];
  var total = 0;

  return {
    items: function() { return items; },

    total: function() { return total; },

    record: function(description, price) {
      items.push(description + ' : ' + price);
      total += price;
    },
  };
};

var Unit = function(item) {
  return {
    applies: function(basket, receipt) {
      if (numberOfItemsMatchingInBasket(item, basket) >= 1) {
        receipt.record(item.name(), item.unitPrice());
        return true;
      } else {
        return false;
      }
    }
  };
};

var BuyOneGetOneFree = function(item) {
  return {
    applies: function(basket, receipt) {
      if (numberOfItemsMatchingInBasket(item,basket) >= 2) {
        receipt.record("BOGOF " + item.name(), item.unitPrice());
        return true;
      } else {
        return false;
      }
    }
  };
};

var ThreeForTwo = function(item) {
  return {
    applies: function(basket, receipt) {
      if (numberOfItemsMatchingInBasket(item,basket) >= 3) {
        receipt.record("3 for 2 " + item.name(), 2 * item.unitPrice());
        return true;
      } else {
        return false;
      }
    }
  }
};

describe('super market', function() {

  var beans = new Item('beans', 45);
  var batteries = new Item('batteries', 60);

  describe('item', function() {
    it('have a unit price', function() {
      assert.equal(45, beans.unitPrice());
    });
  });

  describe('basket', function() {
    it('can keep track of items', function() {
      var basket = new Basket();
      basket.add(beans);

      assert.equal(1, basket.count());

      basket.remove(beans);
      assert.equal(0, basket.count());
    });
  });

  describe('receipt', function() {
    it('consists of items and a total price', function() {
      var receipt = new Receipt();
      receipt.record('2 x Beans', 45);
      receipt.record('4 x Shoes', 10);

      assert.equal(2, receipt.items().length);
      assert.equal(55, receipt.total());
    });
  });

  describe('register', function() {
    it('totals the price of items', function() {
      var register = new Register();
      register.price(beans);
      register.price(beans);

      assert.equal(90, register.total());
    });

    it('knows of offers', function() {
      var register = new Register();
      register.insertOffer(new BuyOneGetOneFree(beans));

      register.price(beans);
      register.price(beans);

      assert.equal(45, register.total());
    });
  });

  describe('offer', function() {

    var item, price, receipt;

    beforeEach(function() {
      item = undefined;
      price = undefined;
      receipt = {
        record: function(item_, price_) {
          item = item_;
          price = price_;
        }
      };
    });

    it('unit offer', function() {
      var offer = new Unit(beans);
      assert(offer.applies([beans], receipt));
      assert.equal(45, price);
    });

    it('buy-one-get-one-free', function() {
      var offer = new BuyOneGetOneFree(beans);
      assert(offer.applies([beans, beans], receipt));
      assert.equal(45, price);
    });

    it('3 for 2', function() {
      var offer = new ThreeForTwo(beans);
      assert(offer.applies([beans,beans,beans], receipt));
      assert.equal(90, price);
    });
  });
});

// TODO
// Learnt about beforeEach and splice
// Would have made an offer apply for every item and applied in order
<|endoftext|>


<commit_before><commit_msg>step 5 complete<commit_after>const request = require('request');

const authToken = '<authToken>';

function returnTime(timeString) {
  request.post({
    uri: 'http://challenge.code2040.org/api/dating/validate',
    method: 'POST',
    json: { token: authToken, datestamp: timeString } },
    (err, response, body) => {
      if (!err && response.statusCode === 200) {
        console.log(body);
      }
    });
}

request.post({
  uri: 'http://challenge.code2040.org/api/dating',
  method: 'POST',
  json: { token: authToken } },
  (err, response, body) => {
    if (!err && response.statusCode === 200) {
      const parsedTime = new Date(body.datestamp).getTime();
      const finalTime = parsedTime + (body.interval * 1000);
      returnTime(new Date(finalTime).toISOString().split('.')[0] + 'Z');
    }
  });
<|endoftext|>


<commit_before>TC.control = TC.control || {};

if (!TC.control.MapContents) {
    TC.syncLoadJS(TC.apiLocation + 'TC/control/MapContents.js');
}

TC.control.BasemapSelector = function () {
    var self = this;

    TC.control.MapContents.apply(self, arguments);
};

TC.inherit(TC.control.BasemapSelector, TC.control.MapContents);

(function () {
    var ctlProto = TC.control.BasemapSelector.prototype;

    ctlProto.CLASS = 'tc-ctl-bms';

    ctlProto.template = {};
    if (TC.isDebug) {
        ctlProto.template[ctlProto.CLASS] = TC.apiLocation + "TC/templates/BasemapSelector.html";
        ctlProto.template[ctlProto.CLASS + '-node'] = TC.apiLocation + "TC/templates/BasemapSelectorNode.html";
    }
    else {
        ctlProto.template[ctlProto.CLASS] = function () { dust.register(ctlProto.CLASS, body_0); function body_0(chk, ctx) { return chk.w("
").h("i18n", ctx, {}, { "$key": "backgroundMaps" }).w("

<form>
").s(ctx.get(["baseLayers"], false), ctx, { "block": body_1 }, {}).w("
</form>
"); } body_0.__dustBody = !0; function body_1(chk, ctx) { return chk.p("tc-ctl-bms-node", ctx, ctx.rebase(ctx.getPath(true, [])), {}); } body_1.__dustBody = !0; return body_0 };
        ctlProto.template[ctlProto.CLASS + '-node'] = function () { dust.register(ctlProto.CLASS + '-node', body_0); function body_0(chk, ctx) { return chk.w("
<input type=\"radio\" name=\"bms\" value=\"").f(ctx.get(["name"], false), ctx, "h").w("\" />").f(ctx.get(["title"], false), ctx, "h").w("
"); } body_0.__dustBody = !0; function body_1(chk, ctx) { return chk.w(" style=\"background-size: 100% 100%; background-image: url(").f(ctx.getPath(false, ["legend", "src"]), ctx, "h").w(")\""); } body_1.__dustBody = !0; return body_0 };
    }

    ctlProto.register = function (map) {
        var self = this;

        TC.control.MapContents.prototype.register.call(self, map);

        map.on(TC.Consts.event.BASELAYERCHANGE, function () {
            self.update();
        });

        self._$div.on(TC.Consts.event.CLICK, 'label', function (e) {
            var $radio = $(e.target).closest('label').find('input[type=radio]');
            if (!Modernizr.canvas) {
                // IE8 support
                self._$div.find('label > span').removeClass(TC.Consts.classes.CHECKED);
                $radio.next().addClass(TC.Consts.classes.CHECKED);
            }
            var layer = $radio.closest('li').data('layer');
            if (layer != layer.map.getBaseLayer()) {
                layer.map.setBaseLayer(layer);
            }
            e.stopPropagation();

        });
    };

    ctlProto.update = function () {
        var self = this;

        self._$div.find('ul.' + self.CLASS + '-branch').children('li').each(function (idx, elm) {
            var $li = $(elm);
            var layer = $li.data('layer');
            var $radio = $li.find('input[type=radio]').first();
            var checked = self.map.baseLayer === layer;
            $radio.prop('checked', checked);
            if (!Modernizr.canvas) {
                // IE8 support
                $radio.next().toggleClass(TC.Consts.classes.CHECKED, checked);
            }
        });

        self.updateScale();
    };

    ctlProto.updateLayerTree = function (layer) {
        var self = this;
        if (layer.isBase && !layer.options.stealth) {
            TC.control.MapContents.prototype.updateLayerTree.call(self, layer);

            var template = self.CLASS + '-node';
            TC.loadJSInOrder(
                !window.dust,
                TC.url.templating,
                function () {
                    dust.render(template, self.layerTrees[layer.id], function (err, out) {
                        var $newLi = $(out);
                        var uid = $newLi.data('tcLayerUid');
                        var $ul = self._$div.find('.' + self.CLASS + '-branch');
                        var $li = $ul.find('li[data-tc-layer-uid="' + uid + '"]');
                        if ($li.length === 1) {
                            $li.html($newLi.html());
                        }
                        else {
                            $newLi.data('layer', layer);
                            $ul.append($newLi);
                        }
                        if (err) {
                            TC.error(err);
                        }
                    });
                    self.update();
                }
            );
        }
    };

    ctlProto.removeLayer = function (layer) {
        var self = this;
        if (layer.isBase) {
            var $lis = self._$div.find('.' + self.CLASS + '-branch').children('li');
            $lis.each(function (idx, elm) {
                var $li = $(elm);
                if ($li.data('layer') === layer) {
                    $li.remove();
                    return false;
                }
            });
        }
    };

})();
<commit_msg>Change selection handler<commit_after>TC.control = TC.control || {};

if (!TC.control.MapContents) {
    TC.syncLoadJS(TC.apiLocation + 'TC/control/MapContents.js');
}

TC.control.BasemapSelector = function () {
    var self = this;

    TC.control.MapContents.apply(self, arguments);
};

TC.inherit(TC.control.BasemapSelector, TC.control.MapContents);

(function () {
    var ctlProto = TC.control.BasemapSelector.prototype;

    ctlProto.CLASS = 'tc-ctl-bms';

    ctlProto.template = {};
    if (TC.isDebug) {
        ctlProto.template[ctlProto.CLASS] = TC.apiLocation + "TC/templates/BasemapSelector.html";
        ctlProto.template[ctlProto.CLASS + '-node'] = TC.apiLocation + "TC/templates/BasemapSelectorNode.html";
    }
    else {
        ctlProto.template[ctlProto.CLASS] = function () { dust.register(ctlProto.CLASS, body_0); function body_0(chk, ctx) { return chk.w("
").h("i18n", ctx, {}, { "$key": "backgroundMaps" }).w("

<form>
").s(ctx.get(["baseLayers"], false), ctx, { "block": body_1 }, {}).w("
</form>
"); } body_0.__dustBody = !0; function body_1(chk, ctx) { return chk.p("tc-ctl-bms-node", ctx, ctx.rebase(ctx.getPath(true, [])), {}); } body_1.__dustBody = !0; return body_0 };
        ctlProto.template[ctlProto.CLASS + '-node'] = function () { dust.register(ctlProto.CLASS + '-node', body_0); function body_0(chk, ctx) { return chk.w("
<input type=\"radio\" name=\"bms\" value=\"").f(ctx.get(["name"], false), ctx, "h").w("\" />").f(ctx.get(["title"], false), ctx, "h").w("
"); } body_0.__dustBody = !0; function body_1(chk, ctx) { return chk.w(" style=\"background-size: 100% 100%; background-image: url(").f(ctx.getPath(false, ["legend", "src"]), ctx, "h").w(")\""); } body_1.__dustBody = !0; return body_0 };
    }

    ctlProto.register = function (map) {
        var self = this;

        TC.control.MapContents.prototype.register.call(self, map);

        map.on(TC.Consts.event.BASELAYERCHANGE, function () {
            self.update();
        });

        self._$div.on('change', 'input[type=radio]', function (e) {
            var $radio = $(e.target);
            if (!Modernizr.canvas) {
                // IE8 support
                self._$div.find('label > span').removeClass(TC.Consts.classes.CHECKED);
                $radio.next().addClass(TC.Consts.classes.CHECKED);
            }
            var layer = $radio.closest('li').data('layer');
            if (layer != layer.map.getBaseLayer()) {
                layer.map.setBaseLayer(layer);
            }
            e.stopPropagation();

        });
    };

    ctlProto.update = function () {
        var self = this;

        self._$div.find('ul.' + self.CLASS + '-branch').children('li').each(function (idx, elm) {
            var $li = $(elm);
            var layer = $li.data('layer');
            var $radio = $li.find('input[type=radio]').first();
            var checked = self.map.baseLayer === layer;
            $radio.prop('checked', checked);
            if (!Modernizr.canvas) {
                // IE8 support
                $radio.next().toggleClass(TC.Consts.classes.CHECKED, checked);
            }
        });

        self.updateScale();
    };

    ctlProto.updateLayerTree = function (layer) {
        var self = this;
        if (layer.isBase && !layer.options.stealth) {
            TC.control.MapContents.prototype.updateLayerTree.call(self, layer);

            var template = self.CLASS + '-node';
            TC.loadJSInOrder(
                !window.dust,
                TC.url.templating,
                function () {
                    dust.render(template, self.layerTrees[layer.id], function (err, out) {
                        var $newLi = $(out);
                        var uid = $newLi.data('tcLayerUid');
                        var $ul = self._$div.find('.' + self.CLASS + '-branch');
                        var $li = $ul.find('li[data-tc-layer-uid="' + uid + '"]');
                        if ($li.length === 1) {
                            $li.html($newLi.html());
                        }
                        else {
                            $newLi.data('layer', layer);
                            $ul.append($newLi);
                        }
                        if (err) {
                            TC.error(err);
                        }
                    });
                    self.update();
                }
            );
        }
    };

    ctlProto.removeLayer = function (layer) {
        var self = this;
        if (layer.isBase) {
            var $lis = self._$div.find('.' + self.CLASS + '-branch').children('li');
            $lis.each(function (idx, elm) {
                var $li = $(elm);
                if ($li.data('layer') === layer) {
                    $li.remove();
                    return false;
                }
            });
        }
    };

})();
<|endoftext|>


<commit_before>/* Copyright (c) 2010-2012 Marcus Westin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

;(function(){
	var store = {},
		win = window,
		doc = win.document,
		localStorageName = 'localStorage',
		globalStorageName = 'globalStorage',
		namespace = '__storejs__',
		storage

	store.disabled = false
	store.set = function(key, value) {}
	store.get = function(key) {}
	store.remove = function(key) {}
	store.clear = function() {}
	store.transact = function(key, defaultVal, transactionFn) {
		var val = store.get(key)
		if (transactionFn == null) {
			transactionFn = defaultVal
			defaultVal = null
		}
		if (typeof val == 'undefined') { val = defaultVal || {} }
		transactionFn(val)
		store.set(key, val)
	}
	store.getAll = function() {}

	store.serialize = function(value) {
		return JSON.stringify(value)
	}
	store.deserialize = function(value) {
		if (typeof value != 'string') { return undefined }
		return JSON.parse(value)
	}

	// Functions to encapsulate questionable FireFox 3.6.13 behavior
	// when about.config::dom.storage.enabled === false
	// See https://github.com/marcuswestin/store.js/issues#issue/13
	function isLocalStorageNameSupported() {
		try { return (localStorageName in win && win[localStorageName]) }
		catch(err) { return false }
	}

	function isGlobalStorageNameSupported() {
		try { return (globalStorageName in win && win[globalStorageName] && win[globalStorageName][win.location.hostname]) }
		catch(err) { return false }
	}

	if (isLocalStorageNameSupported()) {
		storage = win[localStorageName]
		store.set = function(key, val) {
			if (val === undefined) { return store.remove(key) }
			storage.setItem(key, store.serialize(val))
		}
		store.get = function(key) { return store.deserialize(storage.getItem(key)) }
		store.remove = function(key) { storage.removeItem(key) }
		store.clear = function() { storage.clear() }
		store.getAll = function() {
			var ret = {}
			for (var i=0; i<storage.length; ++i) {
				var key = storage.key(i)
				ret[key] = store.get(key)
			}
			return ret
		}
	} else if (isGlobalStorageNameSupported()) {
		storage = win[globalStorageName][win.location.hostname]
		store.set = function(key, val) {
			if (val === undefined) { return store.remove(key) }
			storage[key] = store.serialize(val)
		}
		store.get = function(key) { return store.deserialize(storage[key] && storage[key].value) }
		store.remove = function(key) { delete storage[key] }
		store.clear = function() { for (var key in storage ) { delete storage[key] } }
		store.getAll = function() {
			var ret = {}
			for (var i=0; i<storage.length; ++i) {
				var key = storage.key(i)
				ret[key] = store.get(key)
			}
			return ret
		}

	} else if (doc.documentElement.addBehavior) {
		var storageOwner,
			storageContainer
		// Since #userData storage applies only to specific paths, we need to
		// somehow link our data to a specific path.  We choose /favicon.ico
		// as a pretty safe option, since all browsers already make a request to
		// this URL anyway and being a 404 will not hurt us here.  We wrap an
		// iframe pointing to the favicon in an ActiveXObject(htmlfile) object
		// (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
		// since the iframe access rules appear to allow direct access and
		// manipulation of the document element, even for a 404 page.  This
		// document can be used instead of the current document (which would
		// have been limited to the current path) to perform #userData storage.
		try {
			storageContainer = new ActiveXObject('htmlfile')
			storageContainer.open()
			storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></frame>')
			storageContainer.close()
			storageOwner = storageContainer.w.frames[0].document
			storage = storageOwner.createElement('div')
		} catch(e) {
			// somehow ActiveXObject instantiation failed (perhaps some special
			// security settings or otherwse), fall back to per-path storage
			storage = doc.createElement('div')
			storageOwner = doc.body
		}
		function withIEStorage(storeFunction) {
			return function() {
				var args = Array.prototype.slice.call(arguments, 0)
				args.unshift(storage)
				// See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
				// and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
				storageOwner.appendChild(storage)
				storage.addBehavior('#default#userData')
				storage.load(localStorageName)
				var result = storeFunction.apply(store, args)
				storageOwner.removeChild(storage)
				return result
			}
		}

		// In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
		var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
		function ieKeyFix(key) {
			return key.replace(forbiddenCharsRegex, '___')
		}
		store.set = withIEStorage(function(storage, key, val) {
			key = ieKeyFix(key)
			if (val === undefined) { return store.remove(key) }
			storage.setAttribute(key, store.serialize(val))
			storage.save(localStorageName)
		})
		store.get = withIEStorage(function(storage, key) {
			key = ieKeyFix(key)
			return store.deserialize(storage.getAttribute(key))
		})
		store.remove = withIEStorage(function(storage, key) {
			key = ieKeyFix(key)
			storage.removeAttribute(key)
			storage.save(localStorageName)
		})
		store.clear = withIEStorage(function(storage) {
			var attributes = storage.XMLDocument.documentElement.attributes
			storage.load(localStorageName)
			for (var i=0, attr; attr=attributes[i]; i++) {
				storage.removeAttribute(attr.name)
			}
			storage.save(localStorageName)
		})
		store.getAll = withIEStorage(function(storage) {
			var attributes = storage.XMLDocument.documentElement.attributes
			storage.load(localStorageName)
			var ret = {}
			for (var i=0, attr; attr=attributes[i]; ++i) {
				ret[attr] = store.get(attr)
			}
			return ret
		})
	}

	try {
		store.set(namespace, namespace)
		if (store.get(namespace) != namespace) { store.disabled = true }
		store.remove(namespace)
	} catch(e) {
		store.disabled = true
	}
	store.enabled = !store.disabled

	if (typeof module != 'undefined' && typeof module != 'function') { module.exports = store }
	else if (typeof define === 'function' && define.amd) { define(store) }
	else { this.store = store }
})();
<commit_msg>Useful return value for store#set - localStorage / globalStorage / IE<commit_after>/* Copyright (c) 2010-2012 Marcus Westin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

;(function(){
	var store = {},
		win = window,
		doc = win.document,
		localStorageName = 'localStorage',
		globalStorageName = 'globalStorage',
		namespace = '__storejs__',
		storage

	store.disabled = false
	store.set = function(key, value) {}
	store.get = function(key) {}
	store.remove = function(key) {}
	store.clear = function() {}
	store.transact = function(key, defaultVal, transactionFn) {
		var val = store.get(key)
		if (transactionFn == null) {
			transactionFn = defaultVal
			defaultVal = null
		}
		if (typeof val == 'undefined') { val = defaultVal || {} }
		transactionFn(val)
		store.set(key, val)
	}
	store.getAll = function() {}

	store.serialize = function(value) {
		return JSON.stringify(value)
	}
	store.deserialize = function(value) {
		if (typeof value != 'string') { return undefined }
		return JSON.parse(value)
	}

	// Functions to encapsulate questionable FireFox 3.6.13 behavior
	// when about.config::dom.storage.enabled === false
	// See https://github.com/marcuswestin/store.js/issues#issue/13
	function isLocalStorageNameSupported() {
		try { return (localStorageName in win && win[localStorageName]) }
		catch(err) { return false }
	}

	function isGlobalStorageNameSupported() {
		try { return (globalStorageName in win && win[globalStorageName] && win[globalStorageName][win.location.hostname]) }
		catch(err) { return false }
	}

	if (isLocalStorageNameSupported()) {
		storage = win[localStorageName]
		store.set = function(key, val) {
			if (val === undefined) { return store.remove(key) }
			storage.setItem(key, store.serialize(val))
			return val
		}
		store.get = function(key) { return store.deserialize(storage.getItem(key)) }
		store.remove = function(key) { storage.removeItem(key) }
		store.clear = function() { storage.clear() }
		store.getAll = function() {
			var ret = {}
			for (var i=0; i<storage.length; ++i) {
				var key = storage.key(i)
				ret[key] = store.get(key)
			}
			return ret
		}
	} else if (isGlobalStorageNameSupported()) {
		storage = win[globalStorageName][win.location.hostname]
		store.set = function(key, val) {
			if (val === undefined) { return store.remove(key) }
			storage[key] = store.serialize(val)
			return val
		}
		store.get = function(key) { return store.deserialize(storage[key] && storage[key].value) }
		store.remove = function(key) { delete storage[key] }
		store.clear = function() { for (var key in storage ) { delete storage[key] } }
		store.getAll = function() {
			var ret = {}
			for (var i=0; i<storage.length; ++i) {
				var key = storage.key(i)
				ret[key] = store.get(key)
			}
			return ret
		}

	} else if (doc.documentElement.addBehavior) {
		var storageOwner,
			storageContainer
		// Since #userData storage applies only to specific paths, we need to
		// somehow link our data to a specific path.  We choose /favicon.ico
		// as a pretty safe option, since all browsers already make a request to
		// this URL anyway and being a 404 will not hurt us here.  We wrap an
		// iframe pointing to the favicon in an ActiveXObject(htmlfile) object
		// (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
		// since the iframe access rules appear to allow direct access and
		// manipulation of the document element, even for a 404 page.  This
		// document can be used instead of the current document (which would
		// have been limited to the current path) to perform #userData storage.
		try {
			storageContainer = new ActiveXObject('htmlfile')
			storageContainer.open()
			storageContainer.write('<s' + 'cript>document.w=window</s' + 'cript><iframe src="/favicon.ico"></frame>')
			storageContainer.close()
			storageOwner = storageContainer.w.frames[0].document
			storage = storageOwner.createElement('div')
		} catch(e) {
			// somehow ActiveXObject instantiation failed (perhaps some special
			// security settings or otherwse), fall back to per-path storage
			storage = doc.createElement('div')
			storageOwner = doc.body
		}
		function withIEStorage(storeFunction) {
			return function() {
				var args = Array.prototype.slice.call(arguments, 0)
				args.unshift(storage)
				// See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
				// and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
				storageOwner.appendChild(storage)
				storage.addBehavior('#default#userData')
				storage.load(localStorageName)
				var result = storeFunction.apply(store, args)
				storageOwner.removeChild(storage)
				return result
			}
		}

		// In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
		var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
		function ieKeyFix(key) {
			return key.replace(forbiddenCharsRegex, '___')
		}
		store.set = withIEStorage(function(storage, key, val) {
			key = ieKeyFix(key)
			if (val === undefined) { return store.remove(key) }
			storage.setAttribute(key, store.serialize(val))
			storage.save(localStorageName)
			return val
		})
		store.get = withIEStorage(function(storage, key) {
			key = ieKeyFix(key)
			return store.deserialize(storage.getAttribute(key))
		})
		store.remove = withIEStorage(function(storage, key) {
			key = ieKeyFix(key)
			storage.removeAttribute(key)
			storage.save(localStorageName)
		})
		store.clear = withIEStorage(function(storage) {
			var attributes = storage.XMLDocument.documentElement.attributes
			storage.load(localStorageName)
			for (var i=0, attr; attr=attributes[i]; i++) {
				storage.removeAttribute(attr.name)
			}
			storage.save(localStorageName)
		})
		store.getAll = withIEStorage(function(storage) {
			var attributes = storage.XMLDocument.documentElement.attributes
			storage.load(localStorageName)
			var ret = {}
			for (var i=0, attr; attr=attributes[i]; ++i) {
				ret[attr] = store.get(attr)
			}
			return ret
		})
	}

	try {
		store.set(namespace, namespace)
		if (store.get(namespace) != namespace) { store.disabled = true }
		store.remove(namespace)
	} catch(e) {
		store.disabled = true
	}
	store.enabled = !store.disabled

	if (typeof module != 'undefined' && typeof module != 'function') { module.exports = store }
	else if (typeof define === 'function' && define.amd) { define(store) }
	else { this.store = store }
})();
<|endoftext|>


<commit_before>var fs=require('fs');
var pg=require('pg.js');
var Promise = require('es6-promise').Promise;

/** Return reference to function fn that when called, makes fn see scope
  * as "this" variable. */
function bindToScope(scope, fn) {
	return function() {
		fn.apply(scope, arguments);
	};
};

var PgDatabase=function() {
	this.client=null;
};

PgDatabase.prototype.connect=function(conf) {
	var self=this;

	return(new Promise(function(resolve,reject) {
		self.client=new pg.Client(conf);
		self.client.connect(function(err) {
			if(err) {
				return(reject('Unable to connect to database: '+err));
			}

			resolve();
		});
	}));
}

PgDatabase.prototype.close=function(conf) {
	var self=this;

	return(new Promise(function(resolve,reject) {
		resolve(self.client.end());
	}));
}

PgDatabase.prototype.exec=function() {
	var query=this.client.query.apply(this.client,arguments);

	return(new Promise(function(resolve,reject) {
		query.on('error',function(err) {
			reject(err);
		});

		query.on('end',function(state) {
			resolve(state);
		});
	}));
}

PgDatabase.prototype.querySingle=function() {
	var query=this.client.query.apply(this.client,arguments);

	return(new Promise(function(resolve,reject) {
		var result;

		query.on('row',function(row) {
			result=row;
		});

		query.on('error',function(err) {
			reject(err);
		});

		query.on('end',function(state) {
			resolve(result);
		});
	}));
}

PgDatabase.prototype.begin=function() {
	return(this.exec('BEGIN TRANSACTION'));
}

PgDatabase.prototype.commit=function() {
	return(this.exec('COMMIT'));
}

PgDatabase.prototype.rollback=function() {
	return(this.exec('ROLLBACK'));
}

var SldInserter=function() {
	this.db=null;
	this.dbConf=null;
}

SldInserter.prototype.connect=function(dbPath) {
	var self=this;

	this.db=new PgDatabase();

	return(new Promise(function(resolve,reject) {
		try {
			var dbJson=fs.readFileSync(dbPath,'utf-8');
			self.dbConf=JSON.parse(dbJson);
			resolve();
		} catch(e) {
			return(reject('Unable to read database configuration: '+e));
		}
	}).then(self.db.connect(this.dbConf)).then(this.db.begin()));
};

SldInserter.prototype.abort=function() {
	return(this.db.rollback().then(this.db.close()));
};

SldInserter.prototype.finish=function() {
	var self=this;

	console.log('COMMIT');

	return(this.db.commit().then(function() {
		return(self.db.close());
	}));
};

SldInserter.prototype.readFile=function(path,name) {
	return(new Promise(function(resolve,reject) {
		fs.readFile(path,{encoding:'utf-8'},function(err,data) {
			if(err) return(reject('Unable to read '+name+': '+err));
			resolve(data);
		})
	}));
};

SldInserter.prototype.insertTemplate=function(templatePath) {
	var self=this;

	return(this.readFile(templatePath,'SLD template').then(function(sldTemplate) {
		return(self.db.querySingle('INSERT INTO sld_template (content,name) VALUES ($1,$2) RETURNING id',[sldTemplate,'foobar']));
	}));
};

SldInserter.prototype.insertConfig=function(configPath,templateId) {
	var promiseList=[];
	var readyCount=0;

	var self=this;

	return(this.readFile(configPath,'SLD config').then(function(sldConfig) {
		return(new Promise(function(resolve,reject) {
			var lineList;
			var lineNum,lineCount;
			var fieldList;
			var featureTypeInserted,ruleInserted,fieldInserted;

			lineList=sldConfig.split(/\r?\n/);
			lineCount=sldConfig.length;

			for(lineNum=0;lineNum<lineCount;lineNum++) {
				line=lineList[lineNum];
				if(!line) continue;
				fieldList=line.split('\t');

				type=fieldList[0];

				if(type=='FeatureType') {
					featureTypeInserted=self.db.querySingle('INSERT INTO sld_featuretype (template_id,name,title,featuretype_name) VALUES ($1,$2,$3,$4) RETURNING id',[templateId.id,'','','']);
					featureTypeInserted.catch(function(err) {
						reject('Error inserting feature type: '+err);
					});
					promiseList.push(featureTypeInserted);
				}

				if(type=='Rule') {
					ruleInserted=(function(fieldList) {
						return(featureTypeInserted.then(function(featureTypeId) {
							var nameList=fieldList[3].split(';');
							return(self.db.querySingle('INSERT INTO sld_rule (featuretype_id,name,title,abstract) VALUES ($1,$2,$3,$4) RETURNING id',[featureTypeId.id,nameList[0],nameList[1],nameList[2]]));
						}));
					})(fieldList);
					ruleInserted.catch(function(err) {
						reject('Error inserting rule: '+err);
					});
					promiseList.push(ruleInserted);
				}

				if(type=='Field') {
					fieldInserted=(function(fieldList) {
						return(ruleInserted.then(function(ruleId) {
							return(self.db.querySingle('INSERT INTO sld_param (rule_id,template_offset,type_id,default_value) VALUES ($1,$2,$3,$4) RETURNING id',[ruleId.id,fieldList[3],1,fieldList[5]]));
						}));
					})(fieldList);
					fieldInserted.catch(function(err) {
						reject('Error inserting field: '+err);
					});
					promiseList.push(fieldInserted);
				}
			}

			Promise.all(promiseList).then(function() {
				resolve();
			});
		}));
	}));
};

function run() {
	var inserter=new SldInserter();

	var connected=inserter.connect('db.json');

	var templateInserted=connected.then(function() {
		return(inserter.insertTemplate(process.argv[2]));
	});

	var configInserted=templateInserted.then(function(templateId) {
		return(inserter.insertConfig(process.argv[3],templateId));
	});

	var ready=configInserted;

	ready.catch(function(err) {
		inserter.abort();
		console.error(err);
		return;
	});

	ready.then(function() {
		return(inserter.finish());
	}).then(function() {
		console.log('Success!');
	});
}

run();
<commit_msg>Automatically add missing field types to database and refactor. Use deferreds in database code to reduce indentation.<commit_after>var fs=require('fs');
var pg=require('pg.js');
var Promise = require('es6-promise').Promise;

var Deferred=function() {
	var self=this;

	this.promise=new Promise(function(resolve,reject) {
		self.resolve=resolve;
		self.reject=reject;
	});
};

/** Return reference to function fn that when called, makes fn see scope
  * as "this" variable. */
function bindToScope(scope, fn) {
	return function() {
		fn.apply(scope, arguments);
	};
};

var PgDatabase=function() {
	this.client=null;
};

PgDatabase.prototype.connect=function(conf) {
	var defer=new Deferred();

	this.client=new pg.Client(conf);
	this.client.connect(function(err) {
		if(err) return(defer.reject('Unable to connect to database: '+err));
		defer.resolve();
	});

	return(defer.promise);
}

PgDatabase.prototype.close=function(conf) {
	return(Promise.resolve(this.client.end()));
}

PgDatabase.prototype.exec=function() {
	var query=this.client.query.apply(this.client,arguments);
	var defer=new Deferred();

	query.on('error',function(err) {
		defer.reject(err);
	});

	query.on('end',function(state) {
		defer.resolve(state);
	});

	return(defer.promise);
}

PgDatabase.prototype.querySingle=function() {
	var query=this.client.query.apply(this.client,arguments);
	var defer=new Deferred();
	var result;

	query.on('row',function(row) {
		result=row;
	});

	query.on('error',function(err) {
		defer.reject(err);
	});

	query.on('end',function(state) {
		if(!result) return(defer.reject('Not found'));
		defer.resolve(result);
	});

	return(defer.promise);
}

PgDatabase.prototype.begin=function() {
	return(this.exec('BEGIN TRANSACTION'));
}

PgDatabase.prototype.commit=function() {
	return(this.exec('COMMIT'));
}

PgDatabase.prototype.rollback=function() {
	return(this.exec('ROLLBACK'));
}

var SldInserter=function() {
	this.db=null;
	this.dbConf=null;
}

SldInserter.prototype.connect=function(dbPath) {
	var defer=new Deferred();

	this.db=new PgDatabase();

	try {
		var dbJson=fs.readFileSync(dbPath,'utf-8');
		this.dbConf=JSON.parse(dbJson);
		defer.resolve();
	} catch(e) {
		defer.reject('Unable to read database configuration: '+e);
	}

	return(defer.promise.then(this.db.connect(this.dbConf)).then(this.db.begin()));
};

SldInserter.prototype.abort=function() {
	return(this.db.rollback().then(this.db.close()));
};

SldInserter.prototype.finish=function() {
	var self=this;

	console.log('COMMIT');

	return(this.db.commit().then(function() {
		return(self.db.close());
	}));
};

SldInserter.prototype.readFile=function(path,name) {
	var defer=new Deferred();

	fs.readFile(path,{encoding:'utf-8'},function(err,data) {
		if(err) defer.reject('Unable to read '+name+': '+err);
		defer.resolve(data);
	})

	return(defer.promise);
};

SldInserter.prototype.insertTemplate=function(templatePath) {
	var self=this;

	return(this.readFile(templatePath,'SLD template').then(function(sldTemplate) {
		return(self.db.querySingle('INSERT INTO sld_template (content,name) VALUES ($1,$2) RETURNING id',[sldTemplate,'foobar']));
	}));
};

SldInserter.prototype.insertFeatureType=function(templateId) {
	return(this.db.querySingle('INSERT INTO sld_featuretype (template_id,name,title,featuretype_name) VALUES ($1,$2,$3,$4) RETURNING id',[templateId,'','','']));
};

SldInserter.prototype.insertRule=function(featureTypeInserted,fieldList) {
	var self=this;

	return(featureTypeInserted.then(function(featureTypeId) {
		var nameList=fieldList[3].split(';');
		return(self.db.querySingle('INSERT INTO sld_rule (featuretype_id,name,title,abstract) VALUES ($1,$2,$3,$4) RETURNING id',[featureTypeId.id,nameList[0],nameList[1],nameList[2]]));
	}));
};

SldInserter.prototype.insertParam=function(ruleInserted,fieldList) {
	var self=this;
	var defer=new Deferred();

	ruleInserted.then(function(ruleId) {
		var typeFound=self.db.querySingle('SELECT id,name,symbolizer FROM sld_type WHERE symbolizer=$1',[fieldList[4]]);

		typeFound.then(function(typeRow) {
			self.db.querySingle('INSERT INTO sld_param (rule_id,template_offset,type_id,default_value) VALUES ($1,$2,$3,$4) RETURNING id',[ruleId.id,fieldList[3],typeRow.id,fieldList[5]]).then(function() {
				defer.resolve();
			});
		});

		typeFound.catch(function() {
			return(self.db.querySingle('INSERT INTO sld_type (name,symbolizer) VALUES ($1,$2) RETURNING id',['',fieldList[4]]));
		}).then(function(typeRow) {
			self.db.querySingle('INSERT INTO sld_param (rule_id,template_offset,type_id,default_value) VALUES ($1,$2,$3,$4) RETURNING id',[ruleId.id,fieldList[3],typeRow.id,fieldList[5]]).then(function() {
				defer.resolve();
			});
		}).catch(function(err) {defer.reject(err);});
	});

	return(defer.promise);
};

SldInserter.prototype.insertConfig=function(configPath,templateRow) {
	var promiseList=[];

	var self=this;

	var inputReady=this.readFile(configPath,'SLD config');
	var ready=inputReady.then(function(sldConfig) {
		var defer=new Deferred();
		var lineList;
		var lineNum,lineCount;
		var fieldList;
		var featureTypeInserted,ruleInserted,fieldInserted;

		lineList=sldConfig.split(/\r?\n/);
		lineCount=sldConfig.length;

		for(lineNum=0;lineNum<lineCount;lineNum++) {
			line=lineList[lineNum];
			if(!line) continue;
			fieldList=line.split('\t');

			type=fieldList[0];

			if(type=='FeatureType') {
				featureTypeInserted=self.insertFeatureType(templateRow.id);
				featureTypeInserted.catch(function(err) {
					defer.reject('Error inserting feature type: '+err);
				});
				promiseList.push(featureTypeInserted);
			}

			if(type=='Rule') {
				ruleInserted=self.insertRule(featureTypeInserted,fieldList);
				ruleInserted.catch(function(err) {
					defer.reject('Error inserting rule: '+err);
				});
				promiseList.push(ruleInserted);
			}

			if(type=='Field') {
				paramInserted=self.insertParam(ruleInserted,fieldList);
				paramInserted.catch(function(err) {
					defer.reject('Error inserting field: '+err);
				});
				promiseList.push(paramInserted);
			}
		}

		Promise.all(promiseList).then(function() {
			defer.resolve();
		});

		return(defer.promise);
	});

	return(ready);
};

function run() {
	var inserter=new SldInserter();

	var connected=inserter.connect('db.json');

	var templateInserted=connected.then(function() {
		return(inserter.insertTemplate(process.argv[2]));
	});

	var configInserted=templateInserted.then(function(templateRow) {
		return(inserter.insertConfig(process.argv[3],templateRow));
	});

	var ready=configInserted;

	ready.catch(function(err) {
		inserter.abort();
		console.error(err);
		return;
	});

	ready.then(function() {
		return(inserter.finish());
	}).then(function() {
		console.log('Success!');
	});
}

run();
<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var app = express();

app.use(bodyParser.urlencoded({ extended: false }));
var router = express.Router();
var db = require('../database/db');
var passwords = require('../passwords.json');

router.get('/', function(req, res) {
	res.render('staffingPointsForm', {status: ''});
});

router.post('/', function(req, res, next) {

	if (req.body.password != passwords.secretpassword)
		return res.end("Incorrect Password.");

	// Adds the proposed show to the database.
	db.addStaffingPoints(req.body, function(err, staffingPointsSaved) {
		console.log('adding staffing points');

		if (err) {
			console.log(err);
			console.log(staffingPointsSaved);
		}

		if (staffingPointsSaved)
			res.send('Points successfully submitted!');
		else
			res.send('Try again! Something went wrong :^(');
	});
});

router.get('/view', function(req, res) {
	res.render('staffingPointsView');
});

router.get('/points', function(req, res, next) {
	db.getStaffingPoints(function(err, points) {
		res.send(points);
	});
});

module.exports = router;
<commit_msg>add a route to update staffing points<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var app = express();

app.use(bodyParser.urlencoded({ extended: false }));
var router = express.Router();
var db = require('../database/db');
var passwords = require('../passwords.json');

router.get('/', function(req, res) {
	res.render('staffingPointsForm', {status: ''});
});

router.post('/', function(req, res, next) {

	if (req.body.password != passwords.secretpassword)
		return res.end("Incorrect Password.");

	// Adds the proposed show to the database.
	db.addStaffingPoints(req.body, function(err, staffingPointsSaved) {
		console.log('adding staffing points');

		if (err) {
			console.log(err);
			console.log(staffingPointsSaved);
		}

		if (staffingPointsSaved)
			res.send('Points successfully submitted!');
		else
			res.send('Try again! Something went wrong :^(');
	});
});

router.get('/view', function(req, res) {
	res.render('staffingPointsView');
});

router.post('/view/update', function(req, res, next) {
	if (req.body.password !== passwords.managerapprovalpassword) {
		res.end('incorrect password');
	} else {
		db.updateStaffingPointStatus(req.body.userId, req.body.status, req.body.managerNotes, function(err, success) {
			if (err) {
				res.end(err);
				console.log(err);
			}
			else
				res.end('point status updated!');
		});
	}
});

router.get('/points', function(req, res, next) {
	db.getStaffingPoints(function(err, points) {
		res.send(points);
	});
});

module.exports = router;
<|endoftext|>


<commit_before>define(["Tone/core/Tone", "Tone/instrument/Instrument", "Tone/source/FMOscillator", "Tone/component/Filter",
	"Tone/component/FrequencyEnvelope", "Tone/component/AmplitudeEnvelope", "Tone/core/Gain", "Tone/signal/Scale", "Tone/signal/Multiply"],
	function (Tone) {

	/**
	 *  Inharmonic ratio of frequencies based on the Roland TR-808
	 *  Taken from https://ccrma.stanford.edu/papers/tr-808-cymbal-physically-informed-circuit-bendable-digital-model
	 *  @private
	 *  @static
	 *  @type {Array}
	 */
	var inharmRatios = [1.0, 1.483, 1.932, 2.546, 2.630, 3.897];

	/**
	 *  @class  A highly inharmonic and spectrally complex source with a highpass filter
	 *          and amplitude envelope which is good for making metalophone sounds. Based
	 *          on CymbalSynth by [@polyrhythmatic](https://github.com/polyrhythmatic).
	 *          Inspiration from [Sound on Sound](http://www.soundonsound.com/sos/jul02/articles/synthsecrets0702.asp).
	 *
	 *  @constructor
	 *  @extends {Tone.Instrument}
	 *  @param {Object} [options] The options availble for the synth
	 *                             see defaults below
	 */
	Tone.MetalSynth = function(options){

		options = this.defaultArg(options, Tone.MetalSynth.defaults);
		Tone.Instrument.call(this, options);

		/**
		 *  The frequency of the cymbal
		 *  @type  {Frequency}
		 *  @signal
		 */
		this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency);

		/**
		 *  The array of FMOscillators
		 *  @type  {Array}
		 *  @private
		 */
		this._oscillators = [];

		/**
		 *  The frequency multipliers
		 *  @type {Array}
		 *  @private
		 */
		this._freqMultipliers = [];

		/**
		 *  The amplitude for the body
		 *  @type {Tone.Gain}
		 *  @private
		 */
		this._amplitue = new Tone.Gain(0).connect(this.output);

		/**
		 *  highpass the output
		 *  @type {Tone.Filter}
		 *  @private
		 */
		this._highpass = new Tone.Filter({
			"type" : "highpass",
			"Q" : -3.0102999566398125
		}).connect(this._amplitue);

		/**
		 *  The number of octaves the highpass
		 *  filter frequency ramps
		 *  @type {Number}
		 *  @private
		 */
		this._octaves = options.octaves;

		/**
		 *  Scale the body envelope
		 *  for the bandpass
		 *  @type {Tone.Scale}
		 *  @private
		 */
		this._filterFreqScaler = new Tone.Scale(options.resonance, 7000);

		/**
		 *  The envelope which is connected both to the
		 *  amplitude and highpass filter's cutoff frequency
		 *  @type  {Tone.Envelope}
		 */
		this.envelope = new Tone.Envelope({
			"attack" : options.envelope.attack,
			"attackCurve" : "linear",
			"decay" : options.envelope.decay,
			"sustain" : 0,
			"release" : options.envelope.release,
		}).chain(this._filterFreqScaler, this._highpass.frequency);
		this.envelope.connect(this._amplitue.gain);

		for (var i = 0; i < inharmRatios.length; i++){
			var osc = new Tone.FMOscillator({
				"type" : "square",
				"modulationType" : "square",
				"harmonicity" : options.harmonicity,
				"modulationIndex" : options.modulationIndex
			});
			osc.connect(this._highpass).start(0);
			this._oscillators[i] = osc;

			var mult = new Tone.Multiply(inharmRatios[i]);
			this._freqMultipliers[i] = mult;
			this.frequency.chain(mult, osc.frequency);
		}

		//set the octaves
		this.octaves = options.octaves;

	};

	Tone.extend(Tone.MetalSynth, Tone.Instrument);

	/**
	 *  default values
	 *  @static
	 *  @const
	 *  @type {Object}
	 */
	Tone.MetalSynth.defaults = {
		"frequency" : 200,
		"envelope" : {
			"attack" : 0.001,
			"decay" : 1.4,
			"release" : 0.2
		},
		"harmonicity" : 5.1,
		"modulationIndex" : 32,
		"resonance" : 4000,
		"octaves" : 1.5
	};

	/**
	 *  Trigger the attack.
	 *  @param  {Time}  time      When the attack should be triggered.
	 *  @param  {NormalRange=1}  velocity  The velocity that the envelope should be triggered at.
	 *  @return  {Tone.MetalSynth}  this
	 */
	Tone.MetalSynth.prototype.triggerAttack = function(time, vel) {
		time = this.toSeconds(time);
		vel = this.defaultArg(vel, 1);
		this.envelope.triggerAttack(time, vel);
		return this;
	};

	/**
	 *  Trigger the release of the envelope.
	 *  @param  {Time}  time      When the release should be triggered.
	 *  @return  {Tone.MetalSynth}  this
	 */
	Tone.MetalSynth.prototype.triggerRelease = function(time) {
		time = this.toSeconds(time);
		this.envelope.triggerRelease(time);
		return this;
	};

	/**
	 *  Trigger the attack and release of the envelope after the given
	 *  duration.
	 *  @param  {Time}  duration  The duration before triggering the release
	 *  @param  {Time}  time      When the attack should be triggered.
	 *  @param  {NormalRange=1}  velocity  The velocity that the envelope should be triggered at.
	 *  @return  {Tone.MetalSynth}  this
	 */
	Tone.MetalSynth.prototype.triggerAttackRelease = function(duration, time, velocity) {
		time = this.toSeconds(time);
		duration = this.toSeconds(duration);
		this.triggerAttack(time, velocity);
		this.triggerRelease(time + duration);
		return this;
	};

	/**
	 *  The modulationIndex of the oscillators which make up the source.
	 *  see Tone.FMOscillator.modulationIndex
	 *  @memberOf Tone.MetalSynth#
	 *  @type {Positive}
	 *  @name  modulationIndex
	 */
	Object.defineProperty(Tone.MetalSynth.prototype, "modulationIndex", {
		get : function(){
			return this._oscillators[0].modulationIndex.value;
		},
		set : function(val){
			for (var i = 0; i < this._oscillators.length; i++){
				this._oscillators[i].modulationIndex.value = val;
			}
		}
	});

	/**
	 *  The harmonicity of the oscillators which make up the source.
	 *  see Tone.FMOscillator.harmonicity
	 *  @memberOf Tone.MetalSynth#
	 *  @type {Positive}
	 *  @name  harmonicity
	 */
	Object.defineProperty(Tone.MetalSynth.prototype, "harmonicity", {
		get : function(){
			return this._oscillators[0].harmonicity.value;
		},
		set : function(val){
			for (var i = 0; i < this._oscillators.length; i++){
				this._oscillators[i].harmonicity.value = val;
			}
		}
	});

	/**
	 *  The frequency of the highpass filter attached to the envelope
	 *  @memberOf Tone.MetalSynth#
	 *  @type {Frequency}
	 *  @name  resonance
	 */
	Object.defineProperty(Tone.MetalSynth.prototype, "resonance", {
		get : function(){
			return this._filterFreqScaler.min;
		},
		set : function(val){
			this._filterFreqScaler.min = val;
			this.octaves = this._octaves;
		}
	});

	/**
	 *  The number of octaves above the "resonance" frequency
	 *  that the filter ramps during the attack/decay envelope
	 *  @memberOf Tone.MetalSynth#
	 *  @type {Number}
	 *  @name  octaves
	 */
	Object.defineProperty(Tone.MetalSynth.prototype, "octaves", {
		get : function(){
			return this._octaves;
		},
		set : function(octs){
			this._octaves = octs;
			this._filterFreqScaler.max = this._filterFreqScaler.min * Math.pow(2, octs);
		}
	});

	/**
	 *  Clean up
	 *  @returns {Tone.MetalSynth} this
	 */
	Tone.MetalSynth.prototype.dispose = function(){
		Tone.Instrument.prototype.dispose.call(this);
		for (var i = 0; i < this._oscillators.length; i++){
			this._oscillators[i].dispose();
			this._freqMultipliers[i].dispose();
		}
		this._oscillators = null;
		this._freqMultipliers = null;
		this.frequency.dispose();
		this.frequency = null;
		this._filterFreqScaler.dispose();
		this._filterFreqScaler = null;
		this._amplitue.dispose();
		this._amplitue = null;
		this.envelope.dispose();
		this.envelope = null;
		this._highpass.dispose();
		this._highpass = null;
	};

	return Tone.MetalSynth;
});<commit_msg>Fix typo in comments<commit_after>define(["Tone/core/Tone", "Tone/instrument/Instrument", "Tone/source/FMOscillator", "Tone/component/Filter",
	"Tone/component/FrequencyEnvelope", "Tone/component/AmplitudeEnvelope", "Tone/core/Gain", "Tone/signal/Scale", "Tone/signal/Multiply"],
	function (Tone) {

	/**
	 *  Inharmonic ratio of frequencies based on the Roland TR-808
	 *  Taken from https://ccrma.stanford.edu/papers/tr-808-cymbal-physically-informed-circuit-bendable-digital-model
	 *  @private
	 *  @static
	 *  @type {Array}
	 */
	var inharmRatios = [1.0, 1.483, 1.932, 2.546, 2.630, 3.897];

	/**
	 *  @class  A highly inharmonic and spectrally complex source with a highpass filter
	 *          and amplitude envelope which is good for making metalophone sounds. Based
	 *          on CymbalSynth by [@polyrhythmatic](https://github.com/polyrhythmatic).
	 *          Inspiration from [Sound on Sound](http://www.soundonsound.com/sos/jul02/articles/synthsecrets0702.asp).
	 *
	 *  @constructor
	 *  @extends {Tone.Instrument}
	 *  @param {Object} [options] The options available for the synth
	 *                             see defaults below
	 */
	Tone.MetalSynth = function(options){

		options = this.defaultArg(options, Tone.MetalSynth.defaults);
		Tone.Instrument.call(this, options);

		/**
		 *  The frequency of the cymbal
		 *  @type  {Frequency}
		 *  @signal
		 */
		this.frequency = new Tone.Signal(options.frequency, Tone.Type.Frequency);

		/**
		 *  The array of FMOscillators
		 *  @type  {Array}
		 *  @private
		 */
		this._oscillators = [];

		/**
		 *  The frequency multipliers
		 *  @type {Array}
		 *  @private
		 */
		this._freqMultipliers = [];

		/**
		 *  The amplitude for the body
		 *  @type {Tone.Gain}
		 *  @private
		 */
		this._amplitue = new Tone.Gain(0).connect(this.output);

		/**
		 *  highpass the output
		 *  @type {Tone.Filter}
		 *  @private
		 */
		this._highpass = new Tone.Filter({
			"type" : "highpass",
			"Q" : -3.0102999566398125
		}).connect(this._amplitue);

		/**
		 *  The number of octaves the highpass
		 *  filter frequency ramps
		 *  @type {Number}
		 *  @private
		 */
		this._octaves = options.octaves;

		/**
		 *  Scale the body envelope
		 *  for the bandpass
		 *  @type {Tone.Scale}
		 *  @private
		 */
		this._filterFreqScaler = new Tone.Scale(options.resonance, 7000);

		/**
		 *  The envelope which is connected both to the
		 *  amplitude and highpass filter's cutoff frequency
		 *  @type  {Tone.Envelope}
		 */
		this.envelope = new Tone.Envelope({
			"attack" : options.envelope.attack,
			"attackCurve" : "linear",
			"decay" : options.envelope.decay,
			"sustain" : 0,
			"release" : options.envelope.release,
		}).chain(this._filterFreqScaler, this._highpass.frequency);
		this.envelope.connect(this._amplitue.gain);

		for (var i = 0; i < inharmRatios.length; i++){
			var osc = new Tone.FMOscillator({
				"type" : "square",
				"modulationType" : "square",
				"harmonicity" : options.harmonicity,
				"modulationIndex" : options.modulationIndex
			});
			osc.connect(this._highpass).start(0);
			this._oscillators[i] = osc;

			var mult = new Tone.Multiply(inharmRatios[i]);
			this._freqMultipliers[i] = mult;
			this.frequency.chain(mult, osc.frequency);
		}

		//set the octaves
		this.octaves = options.octaves;

	};

	Tone.extend(Tone.MetalSynth, Tone.Instrument);

	/**
	 *  default values
	 *  @static
	 *  @const
	 *  @type {Object}
	 */
	Tone.MetalSynth.defaults = {
		"frequency" : 200,
		"envelope" : {
			"attack" : 0.001,
			"decay" : 1.4,
			"release" : 0.2
		},
		"harmonicity" : 5.1,
		"modulationIndex" : 32,
		"resonance" : 4000,
		"octaves" : 1.5
	};

	/**
	 *  Trigger the attack.
	 *  @param  {Time}  time      When the attack should be triggered.
	 *  @param  {NormalRange=1}  velocity  The velocity that the envelope should be triggered at.
	 *  @return  {Tone.MetalSynth}  this
	 */
	Tone.MetalSynth.prototype.triggerAttack = function(time, vel) {
		time = this.toSeconds(time);
		vel = this.defaultArg(vel, 1);
		this.envelope.triggerAttack(time, vel);
		return this;
	};

	/**
	 *  Trigger the release of the envelope.
	 *  @param  {Time}  time      When the release should be triggered.
	 *  @return  {Tone.MetalSynth}  this
	 */
	Tone.MetalSynth.prototype.triggerRelease = function(time) {
		time = this.toSeconds(time);
		this.envelope.triggerRelease(time);
		return this;
	};

	/**
	 *  Trigger the attack and release of the envelope after the given
	 *  duration.
	 *  @param  {Time}  duration  The duration before triggering the release
	 *  @param  {Time}  time      When the attack should be triggered.
	 *  @param  {NormalRange=1}  velocity  The velocity that the envelope should be triggered at.
	 *  @return  {Tone.MetalSynth}  this
	 */
	Tone.MetalSynth.prototype.triggerAttackRelease = function(duration, time, velocity) {
		time = this.toSeconds(time);
		duration = this.toSeconds(duration);
		this.triggerAttack(time, velocity);
		this.triggerRelease(time + duration);
		return this;
	};

	/**
	 *  The modulationIndex of the oscillators which make up the source.
	 *  see Tone.FMOscillator.modulationIndex
	 *  @memberOf Tone.MetalSynth#
	 *  @type {Positive}
	 *  @name  modulationIndex
	 */
	Object.defineProperty(Tone.MetalSynth.prototype, "modulationIndex", {
		get : function(){
			return this._oscillators[0].modulationIndex.value;
		},
		set : function(val){
			for (var i = 0; i < this._oscillators.length; i++){
				this._oscillators[i].modulationIndex.value = val;
			}
		}
	});

	/**
	 *  The harmonicity of the oscillators which make up the source.
	 *  see Tone.FMOscillator.harmonicity
	 *  @memberOf Tone.MetalSynth#
	 *  @type {Positive}
	 *  @name  harmonicity
	 */
	Object.defineProperty(Tone.MetalSynth.prototype, "harmonicity", {
		get : function(){
			return this._oscillators[0].harmonicity.value;
		},
		set : function(val){
			for (var i = 0; i < this._oscillators.length; i++){
				this._oscillators[i].harmonicity.value = val;
			}
		}
	});

	/**
	 *  The frequency of the highpass filter attached to the envelope
	 *  @memberOf Tone.MetalSynth#
	 *  @type {Frequency}
	 *  @name  resonance
	 */
	Object.defineProperty(Tone.MetalSynth.prototype, "resonance", {
		get : function(){
			return this._filterFreqScaler.min;
		},
		set : function(val){
			this._filterFreqScaler.min = val;
			this.octaves = this._octaves;
		}
	});

	/**
	 *  The number of octaves above the "resonance" frequency
	 *  that the filter ramps during the attack/decay envelope
	 *  @memberOf Tone.MetalSynth#
	 *  @type {Number}
	 *  @name  octaves
	 */
	Object.defineProperty(Tone.MetalSynth.prototype, "octaves", {
		get : function(){
			return this._octaves;
		},
		set : function(octs){
			this._octaves = octs;
			this._filterFreqScaler.max = this._filterFreqScaler.min * Math.pow(2, octs);
		}
	});

	/**
	 *  Clean up
	 *  @returns {Tone.MetalSynth} this
	 */
	Tone.MetalSynth.prototype.dispose = function(){
		Tone.Instrument.prototype.dispose.call(this);
		for (var i = 0; i < this._oscillators.length; i++){
			this._oscillators[i].dispose();
			this._freqMultipliers[i].dispose();
		}
		this._oscillators = null;
		this._freqMultipliers = null;
		this.frequency.dispose();
		this.frequency = null;
		this._filterFreqScaler.dispose();
		this._filterFreqScaler = null;
		this._amplitue.dispose();
		this._amplitue = null;
		this.envelope.dispose();
		this.envelope = null;
		this._highpass.dispose();
		this._highpass = null;
	};

	return Tone.MetalSynth;
});
<|endoftext|>


<commit_before>var thrift = require("thrift");
var ttypes = require("./gen-nodejs/2b_types");
var Controller = require("./gen-nodejs/ControlService");
var Notifier = require('./gen-nodejs/NotificationService');
transport = thrift.TBufferedTransport();
protocol = thrift.TBinaryProtocol();

var ip = "172.16.30.102";

var local = "192.168.10.23";
var port = 9990;
var connection = thrift.createConnection(ip, 9090, {
  transport : transport,
  protocol : protocol
});

connection.on('close', function() {
  console.log("connection close");
  process.exit();
});
connection.on('connect', function() {
  console.log("connection establish");
});
connection.on('error', function(err) {
  console.log(err);
});

var client = thrift.createClient(Controller, connection);

var address = "F4:06:A5:01:30:1E";
var server = thrift.createServer(Notifier, {
  userChallenge: function(session, result) {
    console.log("user challenge()");
    var info = new AuthInfo();
    info.userId = '123';
    info.name = 'liyi';
    info.secret = "234";
    result(null, info);
  },
  onConnectionStateChange : function(session, chipId, deviceId, status) {
    console.log('connect state change', chipId, deviceId, status);
    if (status == 1) {
      client.discoverServices(deviceId, function(err, response) {
        console.log(err, response);
        //set notification
        client.writeByHandle(address, 19, "0100", function(err, response) {
          console.log("write handle return", err, response);
        });
      });
    }
  },
  onScan : function(session, chipId, device, hexScanRecord, rssi, result) {
    console.log("scan result");
    console.log(chipId, device, hexScanRecord, rssi);
    result(null);
  },
  onServicesDiscovered : function(session, deviceId, s, result) {
    // console.log(deviceId, s);
    var util = require('util');
    console.log(util.inspect(s, {depth:100}));
    result(null);
  },
  onCharacteristicRead: function(session, chipId, deviceId, uuid, hexData) {

  },
  onCharacteristicWrite: function(session, chipId, deviceId, uuid) {

  },
  onCharacteristicChanged: function(session, deviceId, uuid, hexData) {
    console.log(deviceId, uuid, hexData);
  },
  onDescriptorRead: function(session, chipId, deviceId, uuid, hexData) {

  },
  onDescriptorWrite: function(session, chipId, deviceId, uuid) {

  },
  onNotify: function(session, deviceId, handle, hexData) {
    console.log("on notify");
    console.log(deviceId, handle, hexData);
  },
  onReadByHandle: function(session, deviceId, handle, hexData) {
    console.log(deviceId, handle, hexData);
  },
  onMessage : function(session, key, message) {
    console.log(key, message);
  }
});

server.listen(port);

client.setupNotify(local, port, function(err, response) {
  if (err) {
    console.log(err);
    return;
  }
   client.startScan('0', 50000, function(err, response) {
     if (err) {
     console.log(err);
   }
   });

  // client.connect('0', address, "public", function(err, response) {
  //   if (err) {
  //     console.log(err);
  //     return;
  //   }
  // });

});
<commit_msg>修改node sample bugs<commit_after>'use strict'

var dgram = require('dgram');

var thrift = require("thrift");
var Controller = require("./gen-nodejs/ControlService");
var Notifier = require('./gen-nodejs/NotificationService');

var message = new Buffer("CASSIA_HUB_DISCOVERY");
var client = dgram.createSocket("udp4");

var hubIP,
    hubPort,
    localIP,
    localPort = 9999;
var client;

//发现hub
client.bind(
  {},//配置一个和Cassia Hub在同一个内网的ip
  function() {
      client.setBroadcast(true);
      client.send(message, 0, message.length, 0x8888, "255.255.255.255");

  }
)
client.on('message', function (message, rinfo) {
    console.log('Message from: ' + rinfo.address + ':' + rinfo.port +' - ' + message);
    message = message.toString();
    var msgArr = message.split('-');
    hubIP = msgArr[0];
    hubPort = msgArr[1];

    var connection = thrift.createConnection(hubIP, hubPort, {
      transport : transport,
      protocol : protocol
    });

    connection.on('close', function() {
      console.log("connection close");
      process.exit();
    });
    connection.on('connect', function() {
      console.log("connection establish");
    });
    connection.on('error', function(err) {
      console.log(err);
    });

    client = thrift.createClient(Controller, connection);
    client.setupNotify(localIP, localPort, false, function(err) {
      if (err) {
        console.log(err);
        return;
      }

      client.startScan('0', 10000, function(err) {
        if (err) {
          console.log(err);
        }
      });

      // client.connect('0', address, "public", function(err, response) {
      //   if (err) {
      //     console.log(err);
      //     return;
      //   }
      // });

    });

});

var transport = thrift.TBufferedTransport();
var protocol = thrift.TBinaryProtocol();

//获取本机ip
var os = require('os');
let netInterface = os.networkInterfaces();
for(let k in netInterface) {
  if(k == 'vboxnet0') continue;
  netInterface[k].forEach(function (v) {
    if(v.internal == false && v.family == 'IPv4') {
      localIP = v.address;
    }
  })
}
console.log(localIP)

var server = thrift.createServer(Notifier, {
  userChallenge: function(result) {
    console.log("user challenge()");
    var info = new AuthInfo();
    info.userId = '123';
    info.name = 'liyi';
    info.secret = "234";
    result(null, info);
  },
  onConnectionStateChange : function(chipId, deviceId, status) {
    console.log('connect state change', chipId, deviceId, status);
    if (status == 1) {
      client.discoverServices(deviceId, function(err) {
        //set notification
        // client.writeByHandle(address, 19, "0100", function(err, response) {
        //   console.log("write handle return", err, response);
        // });
      });
      client.getConnectedDevices(function(err, lists) {
        if(err) console.error(err);
        console.log('connectedDevices: ')
        console.log(lists);
      })
    }
  },
  onScan : function(chipId, device, hexScanRecord, rssi, result) {
    // if(device.id == address) {
      console.log("scan result");
      console.log(chipId, device, hexScanRecord, rssi);
      client.connect('1', device.id, 'public')
    // }
    result(null);
  },
  onServicesDiscovered : function(deviceId, s, result) {
    console.log('service', deviceId)
    var util = require('util');
    console.log(util.inspect(s, {depth:100}));
    result(null);
  },
  onCharacteristicRead: function(chipId, deviceId, uuid, hexData) {

  },
  onCharacteristicWrite: function(chipId, deviceId, uuid) {

  },
  onCharacteristicChanged: function(deviceId, uuid, hexData) {
    console.log(deviceId, uuid, hexData);
  },
  onDescriptorRead: function(chipId, deviceId, uuid, hexData) {

  },
  onDescriptorWrite: function(chipId, deviceId, uuid) {

  },
  onNotify: function(deviceId, handle, hexData) {
    console.log("on notify");
    console.log(deviceId, handle, hexData);
  },
  onReadByHandle: function(deviceId, handle, hexData) {
    console.log(deviceId, handle, hexData);
  },
  onMessage : function(key, message) {
    console.log(key, message);
  }
});

server.listen(localPort);
<|endoftext|>