<commit_before>'use strict';

module.exports = function(grunt) {
  var helpers = require('./helpers')(grunt),
    path = require('path'),
    changeCase = require('change-case'),
    processors = require('./processors'),
    array = require("array-extended");

  var Generator = function(source, filepath) {
    this.source = source;
    this.filepath = filepath;

    this.validations = [];
    this.uses = [];
    this.stored = {};
    this.id = 0;
    this.indent = 0;

    this.classname = path.basename(filepath, path.extname(filepath));
    this.classname = changeCase.upperCaseFirst(changeCase.camelCase(this.classname));
  };

  Generator.prototype.run = function() {
    // Start building validations
    this.buildObject(this.source, 'data', 'valid');

    var uses = '';
    if (this.uses.length > 0) {
      uses = 'use ' + array.unique(this.uses).join(';\nuse ') + ';';
    }

    var data = {
      classname: this.classname,
      filepath: this.filepath,
      validations: this.validations.join('\n'),
      uses: uses,
    };
    return helpers.template('base', data);
  };

  // Generates validations for an object field.
  //   obj: info of the generator about this object field
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  Generator.prototype.buildObject = function(obj, objname, resname) {
    for (var key in obj) {
      var name = '\'' + key + '\'';

      // Object specific validations per key
      if (key != 'conditional') {
        this.outputBlock(helpers.template('pre-object-key', {
          object: objname,
          name: name,
        }));
      }

      var omitPost = this.runProcessors(name, objname, resname, obj[key]);

      // Object specific validations per key
      if (!omitPost) {
        this.outputBlock(helpers.template('post-object-key', {
          result: resname,
          name: name,
        }));
      }
    }
  };

  // Generates the validations for an array field.
  //   arr: info of the generator about this array field
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  Generator.prototype.buildArray = function(arr, objname, resname) {
    var id = this.generateArrayId();

    // Open the for loop
    this.outputBlock(helpers.template('pre-array', {
      object: objname,
      result: resname,
      id: id,
    }));

    // Indent & generate the sub-validations
    this.indent++;
    var omitPost = this.runProcessors('$i' + id, objname, resname, arr.fields);

    if (!omitPost) {
      this.outputBlock(helpers.template('post-array-item', {
        result: resname,
        name: '$i' + id,
      }));
    }
    this.indent--;

    // This post block always has to be here, closing the for loop
    this.outputBlock(helpers.template('post-array'));
  };

  // Generates the code for a conditional field.
  //   info: info of the generator about this conditional field
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  Generator.prototype.buildConditional = function(name, info, objname, resname) {
    // Open the if block
    this.outputBlock(helpers.template('pre-conditional', {
      condition: info.condition,
    }));

    // Indent & generate the sub-validations
    this.indent++;
    var omitPost = this.runProcessors(name, objname, resname, info.fields);

    if (!omitPost) {
      this.outputBlock(helpers.template('post-conditional-item', {
        result: resname,
        name: name,
      }));
    }
    this.indent--;

    // This post block always has to be here, closing the if block
    this.outputBlock(helpers.template('post-conditional'));
  };

  // Run processors recursively if needed over all generator fields
  //   name: key of the data array
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  //   procs: processors to apply to the field
  Generator.prototype.runProcessors = function(name, objname, resname, procs) {
    var runned = {};
    var alreadyStored = '';

    // Check proccesors type
    if (!Array.isArray(procs)) {
      return this.runSpecialProcessors(name, objname, resname, procs);
    }

    for (var i = 0; i < procs.length; i++) {
      var proc = procs[i];

      if (Array.isArray(proc)) {
        // Extract advanced format
        args = proc[1];
        proc = proc[0];
      } else {
        // Extract the processor name & args in simple format
        var args = '';
        if (proc.indexOf(':') != -1) {
          var idx = proc.indexOf(':');
          args = proc.substring(idx + 1, proc.length);
          proc = proc.substring(0, idx);
        }
        args = args.split(',');
      }

      // Flag incorrectly typed values
      if (i == 0 && proc !== 'string' && proc !== 'integer' && proc !== 'boolean') {
        this.fail(name, 'field not fully typed, use string, integer or boolean processor first');
      }

      grunt.verbose.debug('Processing key [' + name + '] with processor [' +
          proc + '] with args: ' + grunt.log.wordlist(args))

      // Check if it exists
      if (!processors[proc]) {
        this.fail(name, 'validator processor not recognized: ' + proc);
      }

      // Don't let processors store value before they're really validated
      if (proc != 'custom' && alreadyStored) {
        this.fail(name, 'processor [' + alreadyStored + '] has tried to store a value ' +
            'before it was fully validated, move this processor to the end');
      }

      // Run the processor
      try {
        var result = processors[proc](objname, name, args);
      } catch(e) {
        this.fail(name, e + '\n\tin processor: ' + proc + '\n\twith args: ' + args);
      }

      // Processor dependencies
      if (result.requires) {
        for (var j = 0; j < result.requires.length; j++) {
          var r = result.requires[j];
          if (!runned[r]) {
            this.fail(name, 'processor [' + proc + '] needs [' +
                result.requires[j] + '] before to run correctly');
          }
        }
      }
      if (result.requiresStored) {
        for (var j = 0; j < result.requiresStored.length; j++) {
          var s = result.requiresStored[j];
          if (!this.stored[s]) {
            this.fail(name, 'processor [' + proc + '] needs stored value [' +
                result.requiresStored[j] + '] before to run correctly');
          }
        }
      }

      // Processor PHP dependencies
      if (result.uses) {
        this.uses = this.uses.concat(result.uses);
      }

      // Processor template
      if (result.template) {
        this.outputBlock(helpers.template(result.template, result.data));
      }

      // Save stored values
      if (result.stored) {
        alreadyStored = proc;

        // Mark each stored key as used
        for (var j = 0; j < result.stored.length; j++) {
          var s = result.stored[j];
          if (this.stored[s]) {
            this.fail(name, 'processor [' + proc + '] has tried to store again [' + s +
                + '], though it is already used by other processor');
          }
          this.stored[s] = true;
        }
      }

      runned[proc] = true;
    }
  };

  // Run recursive validation block (objects, arrays & conditionals)
  //   name: key of the data array
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  //   info: generator data about this field
  Generator.prototype.runSpecialProcessors = function(name, objname, resname, info) {
    if (typeof info !== 'object') {
      this.fail(name, 'proccesors should be an array or an object');
    }

    if (info.kind === 'object') {
      if (name != '\'conditional\'') {
        this.outputBlock(helpers.template('object', {
          object: objname,
          result: resname,
          name: name,
        }));

        objname += '[' + name + ']';
        resname += '[' + name + ']';
      }
      this.buildObject(info.fields, objname, resname);

      return true;
    }

    if (info.kind === 'array') {
      if (name != '\'conditional\'') {
        this.outputBlock(helpers.template('array', {
          object: objname,
          result: resname,
          name: name,
          mincount: info.mincount || 0,
        }));

        objname += '[' + name + ']';
        resname += '[' + name + ']';
      }
      this.buildArray(info, objname, resname);

      return true;
    }

    if (info.kind === 'conditional') {
      if (info.requiresStored) {
        for (var i = 0; i < info.requiresStored.length; i++) {
          if (!this.stored[info.requiresStored[i]]) {
            this.fail(name, 'conditional needs stored values [' + info.requiresStored[i] +
                '] before to run correctly');
          }
        }
      }

      this.buildConditional(name, info, objname, resname);

      return true;
    }

    // Recursive field not recognized
    this.fail(name, 'collection kind not recognized: ' + info.kind);
  };

  // Generates and returns a new array id (used for loop counters)
  Generator.prototype.generateArrayId = function() {
    return this.id++;
  };

  // Prepare a new validation block, indenting it line by line if needed
  Generator.prototype.outputBlock = function(block) {
    var prefix = grunt.util.repeat(this.indent, '  ');

    var lines = block.split('\n');
    for (var i = 0; i < lines.length; i++) {
      if (lines[i].length > 0) {
        lines[i] = prefix + lines[i];
      }
    }

    this.validations.push(lines.join('\n'));
  };

  // Generate a better fail message when an error it's found in the
  // input data format
  Generator.prototype.fail = function(name, msg) {
    grunt.fatal('\n\t' + msg + '\n\tin key: ' + name + '\n\tin file: ' + this.filepath);
  };

  return Generator;
};

<commit_msg>Fix mini-typo.<commit_after>'use strict';

module.exports = function(grunt) {
  var helpers = require('./helpers')(grunt),
    path = require('path'),
    changeCase = require('change-case'),
    processors = require('./processors'),
    array = require("array-extended");

  var Generator = function(source, filepath) {
    this.source = source;
    this.filepath = filepath;

    this.validations = [];
    this.uses = [];
    this.stored = {};
    this.id = 0;
    this.indent = 0;

    this.classname = path.basename(filepath, path.extname(filepath));
    this.classname = changeCase.upperCaseFirst(changeCase.camelCase(this.classname));
  };

  Generator.prototype.run = function() {
    // Start building validations
    this.buildObject(this.source, 'data', 'valid');

    var uses = '';
    if (this.uses.length > 0) {
      uses = 'use ' + array.unique(this.uses).join(';\nuse ') + ';';
    }

    var data = {
      classname: this.classname,
      filepath: this.filepath,
      validations: this.validations.join('\n'),
      uses: uses,
    };
    return helpers.template('base', data);
  };

  // Generates validations for an object field.
  //   obj: info of the generator about this object field
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  Generator.prototype.buildObject = function(obj, objname, resname) {
    for (var key in obj) {
      var name = '\'' + key + '\'';

      // Object specific validations per key
      if (key != 'conditional') {
        this.outputBlock(helpers.template('pre-object-key', {
          object: objname,
          name: name,
        }));
      }

      var omitPost = this.runProcessors(name, objname, resname, obj[key]);

      // Object specific validations per key
      if (!omitPost) {
        this.outputBlock(helpers.template('post-object-key', {
          result: resname,
          name: name,
        }));
      }
    }
  };

  // Generates the validations for an array field.
  //   arr: info of the generator about this array field
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  Generator.prototype.buildArray = function(arr, objname, resname) {
    var id = this.generateArrayId();

    // Open the for loop
    this.outputBlock(helpers.template('pre-array', {
      object: objname,
      result: resname,
      id: id,
    }));

    // Indent & generate the sub-validations
    this.indent++;
    var omitPost = this.runProcessors('$i' + id, objname, resname, arr.fields);

    if (!omitPost) {
      this.outputBlock(helpers.template('post-array-item', {
        result: resname,
        name: '$i' + id,
      }));
    }
    this.indent--;

    // This post block always has to be here, closing the for loop
    this.outputBlock(helpers.template('post-array'));
  };

  // Generates the code for a conditional field.
  //   info: info of the generator about this conditional field
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  Generator.prototype.buildConditional = function(name, info, objname, resname) {
    // Open the if block
    this.outputBlock(helpers.template('pre-conditional', {
      condition: info.condition,
    }));

    // Indent & generate the sub-validations
    this.indent++;
    var omitPost = this.runProcessors(name, objname, resname, info.fields);

    if (!omitPost) {
      this.outputBlock(helpers.template('post-conditional-item', {
        result: resname,
        name: name,
      }));
    }
    this.indent--;

    // This post block always has to be here, closing the if block
    this.outputBlock(helpers.template('post-conditional'));
  };

  // Run processors recursively if needed over all generator fields
  //   name: key of the data array
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  //   procs: processors to apply to the field
  Generator.prototype.runProcessors = function(name, objname, resname, procs) {
    var runned = {};
    var alreadyStored = '';

    // Check proccesors type
    if (!Array.isArray(procs)) {
      return this.runSpecialProcessors(name, objname, resname, procs);
    }

    for (var i = 0; i < procs.length; i++) {
      var proc = procs[i];

      if (Array.isArray(proc)) {
        // Extract advanced format
        args = proc[1];
        proc = proc[0];
      } else {
        // Extract the processor name & args in simple format
        var args = [];
        if (proc.indexOf(':') != -1) {
          var idx = proc.indexOf(':');
          args = proc.substring(idx + 1, proc.length);
          proc = proc.substring(0, idx);
        }
        args = args.split(',');
      }

      // Flag incorrectly typed values
      if (i == 0 && proc !== 'string' && proc !== 'integer' && proc !== 'boolean') {
        this.fail(name, 'field not fully typed, use string, integer or boolean processor first');
      }

      grunt.verbose.debug('Processing key [' + name + '] with processor [' +
          proc + '] with args: ' + grunt.log.wordlist(args))

      // Check if it exists
      if (!processors[proc]) {
        this.fail(name, 'validator processor not recognized: ' + proc);
      }

      // Don't let processors store value before they're really validated
      if (proc != 'custom' && alreadyStored) {
        this.fail(name, 'processor [' + alreadyStored + '] has tried to store a value ' +
            'before it was fully validated, move this processor to the end');
      }

      // Run the processor
      try {
        var result = processors[proc](objname, name, args);
      } catch(e) {
        this.fail(name, e + '\n\tin processor: ' + proc + '\n\twith args: ' + args);
      }

      // Processor dependencies
      if (result.requires) {
        for (var j = 0; j < result.requires.length; j++) {
          var r = result.requires[j];
          if (!runned[r]) {
            this.fail(name, 'processor [' + proc + '] needs [' +
                result.requires[j] + '] before to run correctly');
          }
        }
      }
      if (result.requiresStored) {
        for (var j = 0; j < result.requiresStored.length; j++) {
          var s = result.requiresStored[j];
          if (!this.stored[s]) {
            this.fail(name, 'processor [' + proc + '] needs stored value [' +
                result.requiresStored[j] + '] before to run correctly');
          }
        }
      }

      // Processor PHP dependencies
      if (result.uses) {
        this.uses = this.uses.concat(result.uses);
      }

      // Processor template
      if (result.template) {
        this.outputBlock(helpers.template(result.template, result.data));
      }

      // Save stored values
      if (result.stored) {
        alreadyStored = proc;

        // Mark each stored key as used
        for (var j = 0; j < result.stored.length; j++) {
          var s = result.stored[j];
          if (this.stored[s]) {
            this.fail(name, 'processor [' + proc + '] has tried to store again [' + s +
                + '], though it is already used by other processor');
          }
          this.stored[s] = true;
        }
      }

      runned[proc] = true;
    }
  };

  // Run recursive validation block (objects, arrays & conditionals)
  //   name: key of the data array
  //   objname: name of the base object to extract the value from
  //   resname: name of the base object to save the value to
  //   info: generator data about this field
  Generator.prototype.runSpecialProcessors = function(name, objname, resname, info) {
    if (typeof info !== 'object') {
      this.fail(name, 'proccesors should be an array or an object');
    }

    if (info.kind === 'object') {
      if (name != '\'conditional\'') {
        this.outputBlock(helpers.template('object', {
          object: objname,
          result: resname,
          name: name,
        }));

        objname += '[' + name + ']';
        resname += '[' + name + ']';
      }
      this.buildObject(info.fields, objname, resname);

      return true;
    }

    if (info.kind === 'array') {
      if (name != '\'conditional\'') {
        this.outputBlock(helpers.template('array', {
          object: objname,
          result: resname,
          name: name,
          mincount: info.mincount || 0,
        }));

        objname += '[' + name + ']';
        resname += '[' + name + ']';
      }
      this.buildArray(info, objname, resname);

      return true;
    }

    if (info.kind === 'conditional') {
      if (info.requiresStored) {
        for (var i = 0; i < info.requiresStored.length; i++) {
          if (!this.stored[info.requiresStored[i]]) {
            this.fail(name, 'conditional needs stored values [' + info.requiresStored[i] +
                '] before to run correctly');
          }
        }
      }

      this.buildConditional(name, info, objname, resname);

      return true;
    }

    // Recursive field not recognized
    this.fail(name, 'collection kind not recognized: ' + info.kind);
  };

  // Generates and returns a new array id (used for loop counters)
  Generator.prototype.generateArrayId = function() {
    return this.id++;
  };

  // Prepare a new validation block, indenting it line by line if needed
  Generator.prototype.outputBlock = function(block) {
    var prefix = grunt.util.repeat(this.indent, '  ');

    var lines = block.split('\n');
    for (var i = 0; i < lines.length; i++) {
      if (lines[i].length > 0) {
        lines[i] = prefix + lines[i];
      }
    }

    this.validations.push(lines.join('\n'));
  };

  // Generate a better fail message when an error it's found in the
  // input data format
  Generator.prototype.fail = function(name, msg) {
    grunt.fatal('\n\t' + msg + '\n\tin key: ' + name + '\n\tin file: ' + this.filepath);
  };

  return Generator;
};

<|endoftext|>


<commit_before>var FlowerPower = require('./index')
  , dgram       = require('dgram')
  , os          = require('os')
  ;

var devices = {};

FlowerPower.discover(function(peripheral) {
  var uuid;

  uuid = peripheral.uuid;
  if (!devices[uuid]) devices[uuid] = { uuid: uuid, name: peripheral.name, props: {}, peripheral: peripheral };
  devices[uuid].state = 'discovered';
  devices[uuid].props.lastSample = new Date().getTime();

  peripheral.on('disconnect', function() {
    var device;

    device = devices[uuid];
    device.state = 'disconnected';
    device.props.lastSample = new Date().getTime();

    tsrp(uuid);
  }).connect(onconnect(uuid));
});

var onconnect = function(uuid) {
  return function(err) { didconnect(err, uuid); };
};

var didconnect = function(err, uuid) {
  var device     = devices[uuid]
    , peripheral = device.peripheral;

  if (!!err) return console.log(device.name + ': ' + err.message);

  device.state = 'connected';
  device.props.rssi = peripheral._peripheral.rssi;
  device.props.lastSample = new Date().getTime();

  peripheral.discoverServicesAndCharacteristics(function() {
    var i = 4;

    if (!device.serialNumber) {
      peripheral.readSerialNumber(function(serialNumber) {
        device.serialNumber = serialNumber;
        if (--i === 0) return peripheral.disconnect();
      });
      i++;
    }
    peripheral.readBatteryLevel(function(batteryLevel) {
      device.props.batteryLevel = batteryLevel;
      if (--i === 0) return peripheral.disconnect();
      device.props.lastSample = new Date().getTime();
    });
    peripheral.readSunlight(function(sunlight) {
      // sunlight is PPF (photos per square meter), convert to lux
      // according to http://www.apogeeinstruments.com/conversion-ppf-to-lux/
      device.props.light = sunlight * 54;
      if (--i === 0) return peripheral.disconnect();
      device.props.lastSample = new Date().getTime();
    });
    peripheral.readTemperature(function(temperatureC, temperatureF) {/* jshint unused: false */
      var antoine;

      antoine = 8.07131 - (1730.63 / (233.426 + temperatureC));

      device.props.temperature = temperatureC;
      device.lastPressure = Math.pow(10, antoine - 2);
      if (--i === 0) return peripheral.disconnect();
      device.props.lastSample = new Date().getTime();
    });
    peripheral.readSoilMoisture(function(relativeHumidity) {
      // moisture = relativeHumidity * (saturationVaporPressure(temperatureC) / 100);
      if (device.lastPressure) device.props.moisture = relativeHumidity * device.lastPressure;
      if (--i === 0) return peripheral.disconnect();
      device.props.lastSample = new Date().getTime();
    });
  });
};

setInterval(function() {
  var device, now, uuid;

  now = new Date().getTime();
  for (uuid in devices) {
    if (!devices.hasOwnProperty(uuid)) continue;
    device = devices[uuid];
    if ((device.state !== 'disconnected') || (!!device.remote)) continue;

    device.peripheral.connect(onconnect(uuid));
  }
}, 60 * 60 * 1000);

setInterval(function() {
  var now, uuid;

  now = new Date().getTime();
  for (uuid in devices) {
    if ((devices.hasOwnProperty(uuid)) && (devices[uuid].state === 'disconnected') && (!devices[uuid].remote)) tsrp(uuid);
  }
}, 45 * 1000);

var ipaddr    = '224.192.32.20'
  , portno    = 22601
  , requestID = Math.round(Math.random() * Math.pow(2, 31)) - 1
  , socket
  ;

var ifApply = function(cb) {
  var ifa, ifaddrs, ifname, ifaces;

  ifaces = os.networkInterfaces();
  for (ifname in ifaces) {
    if (!ifaces.hasOwnProperty(ifname)) continue;

    ifaddrs = ifaces[ifname];
    for (ifa = 0; ifa < ifaddrs.length; ifa++) {
      if ((!ifaddrs[ifa].internal) && (ifaddrs[ifa].family === 'IPv4')) cb(ifname, ifaddrs[ifa].address);
    }
  }
};

ifApply(function(ifname, ifaddr) {
  var ifsock = dgram.createSocket('udp4');

  ifsock.on('message', function(message, rinfo) {
    var device, deviceType, i, instance, remote, report, rssi, thing, uuid;

/*
    console.log('>>> rinfo:   ' + JSON.stringify(rinfo));
    console.log('>>> message: ' + message.toString());
 */

    try { report = JSON.parse(message); } catch(ex) { return console.log('TSRP error: ' + ex.message); }

    if ((!report.requestID) || (report.path !== '/api/v1/thing/reporting')) return;

    for (deviceType in report.things) {
      if (!report.things.hasOwnProperty(deviceType)) continue;
      thing = report.things[deviceType];

      if (!thing.instances) continue;
      for (i = 0; i < thing.instances.length; i++) {
        instance = thing.instances[i];
        if ((!instance.unit) || (!instance.unit.udn) || (instance.unit.udn.indexOf('uuid:') !== 0)
                || (!instance.info) || (isNaN(instance.info.rssi))) continue;

        rssi = instance.info.rssi;
        uuid = instance.unit.udn.substring(5);
        for (device in devices) {
          if ((!devices.hasOwnProperty(device)) || (device !== uuid)) continue;
          remote = devices[device].remote;

          if ((!!remote) && ((remote.rssi < rssi) || ((remote.rssi === rssi) && (remote.requestID < report.requestID)))) {
            devices[device].remote = { rinfo: rinfo, requestID: report.requestID, rssi: rssi };
          }
          devices[device].remote.nextUpdate = new Date().getTime() + (60 * 1000 * 1000);

          if ((device.props.rssi > device.remote.rssi) || (requestID > device.remote.requestID)) delete(device.remote);
        }
      }
    }
  }).on('listening', function() {
    console.log('listening on ' + ifname + ' for multicast udp://' + ipaddr + ':' + portno);

    this.addMembership(ipaddr);
  }).on('error', function(err) {
    console.log('socket error: ' + err.message);
  }).bind(portno, ifaddr);

  if (!socket) socket = ifsock;
});

var tsrp = function(uuid) {
  var device, json, packet;

  device = devices[uuid];
  json = { path                                    : '/api/v1/thing/reporting'
         , requestID                               : requestID.toString()
         , things                                  :
           { '/device/climate/flower-power/plant'  :
             { prototype                           :
               { device                            :
                 { name                            : 'Flower Power'
                 , maker                           : 'Parrot'
                 }
               , name                              : true
               , status                            : [ 'recent', 'absent' ]
               , properties                        :
                 { 'temperature'                   : 'celsius'
                 , 'light'                         : 'lux'
                 , 'moisture'                      : 'millibars'
                 , 'batteryLevel'                  : 'percentage'
                 , 'rssi'                          : 's8'
                 }
               }
             , instances                           :
               [
                 { name                            : device.name
                 , status                          : 'recent'
                 , unit                            :
                   { 'serial'                      : device.serialNumber
                   , 'udn'                         : 'uuid:' + device.uuid
                   }
                 , info                            : device.props
                 }
               ]
             }
           }
         };
  requestID++;

  packet = new Buffer(JSON.stringify(json));
  socket.send(packet, 0, packet.length, portno, ipaddr, function(err, octets) {/* jshint unused: false */
    if (!!err) console.log('socket.send: ' + err.message);
  });
};
<commit_msg>- jshint warnings<commit_after>var FlowerPower = require('./index');
var dgram       = require('dgram');
var os          = require('os');

var devices = {};

FlowerPower.discover(function(peripheral) {
  var uuid;

  uuid = peripheral.uuid;
  if (!devices[uuid]) devices[uuid] = { uuid: uuid, name: peripheral.name, props: {}, peripheral: peripheral };
  devices[uuid].state = 'discovered';
  devices[uuid].props.lastSample = new Date().getTime();

  peripheral.on('disconnect', function() {
    var device;

    device = devices[uuid];
    device.state = 'disconnected';
    device.props.lastSample = new Date().getTime();

    tsrp(uuid);
  }).connect(onconnect(uuid));
});

var onconnect = function(uuid) {
  return function(err) { didconnect(err, uuid); };
};

var didconnect = function(err, uuid) {
  var device     = devices[uuid];
  var peripheral = device.peripheral;

  if (!!err) return console.log(device.name + ': ' + err.message);

  device.state = 'connected';
  device.props.rssi = peripheral._peripheral.rssi;
  device.props.lastSample = new Date().getTime();

  peripheral.discoverServicesAndCharacteristics(function() {
    var i = 4;

    if (!device.serialNumber) {
      peripheral.readSerialNumber(function(serialNumber) {
        device.serialNumber = serialNumber;
        if (--i === 0) return peripheral.disconnect();
      });
      i++;
    }
    peripheral.readBatteryLevel(function(batteryLevel) {
      device.props.batteryLevel = batteryLevel;
      if (--i === 0) return peripheral.disconnect();
      device.props.lastSample = new Date().getTime();
    });
    peripheral.readSunlight(function(sunlight) {
      // sunlight is PPF (photos per square meter), convert to lux
      // according to http://www.apogeeinstruments.com/conversion-ppf-to-lux/
      device.props.light = sunlight * 54;
      if (--i === 0) return peripheral.disconnect();
      device.props.lastSample = new Date().getTime();
    });
    peripheral.readTemperature(function(temperatureC, temperatureF) {/* jshint unused: false */
      var antoine;

      antoine = 8.07131 - (1730.63 / (233.426 + temperatureC));

      device.props.temperature = temperatureC;
      device.lastPressure = Math.pow(10, antoine - 2);
      if (--i === 0) return peripheral.disconnect();
      device.props.lastSample = new Date().getTime();
    });
    peripheral.readSoilMoisture(function(relativeHumidity) {
      // moisture = relativeHumidity * (saturationVaporPressure(temperatureC) / 100);
      if (device.lastPressure) device.props.moisture = relativeHumidity * device.lastPressure;
      if (--i === 0) return peripheral.disconnect();
      device.props.lastSample = new Date().getTime();
    });
  });
};

setInterval(function() {
  var device, now, uuid;

  now = new Date().getTime();
  for (uuid in devices) {
    if (!devices.hasOwnProperty(uuid)) continue;
    device = devices[uuid];
    if ((device.state !== 'disconnected') || (!!device.remote)) continue;

    device.peripheral.connect(onconnect(uuid));
  }
}, 60 * 60 * 1000);

setInterval(function() {
  var now, uuid;

  now = new Date().getTime();
  for (uuid in devices) {
    if ((devices.hasOwnProperty(uuid)) && (devices[uuid].state === 'disconnected') && (!devices[uuid].remote)) tsrp(uuid);
  }
}, 45 * 1000);

var ipaddr    = '224.192.32.20';
var portno    = 22601;
var requestID = Math.round(Math.random() * Math.pow(2, 31)) - 1;
var socket;

var ifApply = function(cb) {
  var ifa, ifaddrs, ifname, ifaces;

  ifaces = os.networkInterfaces();
  for (ifname in ifaces) {
    if (!ifaces.hasOwnProperty(ifname)) continue;

    ifaddrs = ifaces[ifname];
    for (ifa = 0; ifa < ifaddrs.length; ifa++) {
      if ((!ifaddrs[ifa].internal) && (ifaddrs[ifa].family === 'IPv4')) cb(ifname, ifaddrs[ifa].address);
    }
  }
};

ifApply(function(ifname, ifaddr) {
  var ifsock = dgram.createSocket('udp4');

  ifsock.on('message', function(message, rinfo) {
    var device, deviceType, i, instance, remote, report, rssi, thing, uuid;

/*
    console.log('>>> rinfo:   ' + JSON.stringify(rinfo));
    console.log('>>> message: ' + message.toString());
 */

    try { report = JSON.parse(message); } catch(ex) { return console.log('TSRP error: ' + ex.message); }

    if ((!report.requestID) || (report.path !== '/api/v1/thing/reporting')) return;

    for (deviceType in report.things) {
      if (!report.things.hasOwnProperty(deviceType)) continue;
      thing = report.things[deviceType];

      if (!thing.instances) continue;
      for (i = 0; i < thing.instances.length; i++) {
        instance = thing.instances[i];
        if ((!instance.unit) || (!instance.unit.udn) || (instance.unit.udn.indexOf('uuid:') !== 0) ||
              (!instance.info) || (isNaN(instance.info.rssi))) continue;

        rssi = instance.info.rssi;
        uuid = instance.unit.udn.substring(5);
        for (device in devices) {
          if ((!devices.hasOwnProperty(device)) || (device !== uuid)) continue;
          remote = devices[device].remote;

          if ((!!remote) && ((remote.rssi < rssi) || ((remote.rssi === rssi) && (remote.requestID < report.requestID)))) {
            devices[device].remote = { rinfo: rinfo, requestID: report.requestID, rssi: rssi };
          }
          devices[device].remote.nextUpdate = new Date().getTime() + (60 * 1000 * 1000);

          if ((device.props.rssi > device.remote.rssi) || (requestID > device.remote.requestID)) delete(device.remote);
        }
      }
    }
  }).on('listening', function() {
    console.log('listening on ' + ifname + ' for multicast udp://' + ipaddr + ':' + portno);

    this.addMembership(ipaddr);
  }).on('error', function(err) {
    console.log('socket error: ' + err.message);
  }).bind(portno, ifaddr);

  if (!socket) socket = ifsock;
});

var tsrp = function(uuid) {
  var device, json, packet;

  device = devices[uuid];
  json = { path                                    : '/api/v1/thing/reporting',
           requestID                               : requestID.toString(),
           things                                  :
           { '/device/climate/flower-power/plant'  :
             { prototype                           :
               { device                            :
                 { name                            : 'Flower Power',
                   maker                           : 'Parrot'
                 },
                 name                              : true,
                 status                            : [ 'recent', 'absent' ],
                 properties                        :
                 { 'temperature'                   : 'celsius',
                   'light'                         : 'lux',
                   'moisture'                      : 'millibars',
                   'batteryLevel'                  : 'percentage',
                   'rssi'                          : 's8'
                 }
               },
               instances                           :
               [
                 { name                            : device.name,
                   status                          : 'recent',
                   unit                            :
                   { 'serial'                      : device.serialNumber,
                     'udn'                         : 'uuid:' + device.uuid
                   },
                   info                            : device.props
                 }
               ]
             }
           }
         };
  requestID++;

  packet = new Buffer(JSON.stringify(json));
  socket.send(packet, 0, packet.length, portno, ipaddr, function(err, octets) {/* jshint unused: false */
    if (!!err) console.log('socket.send: ' + err.message);
  });
};
<|endoftext|>


<commit_before>'use strict';

function click_button(){
    // Add clicks-per-click to clicks.
    document.getElementById('clicks').innerHTML =
      parseInt(document.getElementById('clicks').innerHTML)
      + Math.floor(parseInt(document.getElementById('clicks-per-click').innerHTML)
      * (parseInt(document.getElementById('clicks-multiplier').innerHTML) / 100));
}

function purchase(upgrade, cost, target, free){
    var clicks = parseInt(document.getElementById('clicks').innerHTML);
    free = free || false;

    if(!free
      && clicks < parseInt(document.getElementById('upgrade-' + upgrade + '-cost').innerHTML)){
        return;
    }

    if(!free){
        // If user can afford upgrade...
        //   ...subtract cost of upgrade from clicks...
        document.getElementById('clicks').innerHTML =
          clicks - parseInt(document.getElementById('upgrade-' + upgrade + '-cost').innerHTML);
    }

    // ...and increase upgrade...
    upgrades[upgrade]['level'] += 1;
    document.getElementById('upgrade-' + upgrade).innerHTML = upgrades[upgrade]['level'];

    // ...and update upgrade cost...
    upgrades[upgrade]['cost'] *= upgrades[upgrade]['multiplier'];
    document.getElementById('upgrade-' + upgrade + '-cost').innerHTML =
      upgrades[upgrade]['base'] + upgrades[upgrade]['cost'];

    // ...and increase target value, either clicks-per-click or clicks-per-second...
    document.getElementById(target).innerHTML = parseInt(document.getElementById(target).innerHTML)
      + upgrades[upgrade]['bonus'];

    // ...and recalculate multiplied values.
    var multiplier = (parseInt(document.getElementById('clicks-multiplier').innerHTML) / 100);
    document.getElementById('clicks-per-click-multiplied').innerHTML =
      Math.floor(parseInt(document.getElementById('clicks-per-click').innerHTML) * multiplier);
    document.getElementById('clicks-per-second-multiplied').innerHTML =
      Math.floor(parseInt(document.getElementById('clicks-per-second').innerHTML) * multiplier);
}

function reset_score(){
    if(!window.confirm('Reset score?')){
        return;
    }

    var ids = {
      'clicks': 0,
      'clicks-multiplier': 100,
      'clicks-per-click': 1,
      'clicks-per-click-multiplied': 1,
      'clicks-per-second': 0,
      'clicks-per-second-multiplied': 0,
    };
    for(var id in ids){
        window.localStorage.removeItem('Click.htm-' + id);
        document.getElementById(id).innerHTML = ids[id];
    }

    for(id in upgrades){
        upgrades[id]['cost'] = 1;
        upgrades[id]['level'] = 0;
    }

    for(var id in upgrades){
        window.localStorage.removeItem('Click.htm-upgrade-' + id);

        document.getElementById('upgrade-' + id).innerHTML = upgrades[id]['level'];
        document.getElementById('upgrade-' + id + '-cost').innerHTML =
          upgrades[id]['base'] + upgrades[id]['cost'];
    }
}

function reset_settings(){
    if(!window.confirm('Reset settings?')){
        return;
    }

    window.localStorage.removeItem('Click.htm-keybind-click');
    document.getElementById('keybind-click').value = 'C';

    for(var id in upgrades){
        window.localStorage.removeItem('Click.htm-keybind-' + id);
        document.getElementById('keybind-' + id).value = upgrades[id]['keybind'];
    }
}

function second(){
    if(parseInt(document.getElementById('clicks-per-second').innerHTML) > 0){
        document.getElementById('clicks').innerHTML =
          parseInt(document.getElementById('clicks').innerHTML)
        + Math.floor(parseInt(document.getElementById('clicks-per-second').innerHTML)
          * (parseInt(document.getElementById('clicks-multiplier').innerHTML) / 100));
    }

    // Setting the title to # of clicks makes idling easier.
    document.title = document.getElementById('clicks').innerHTML;

    // There is always another second.
    window.setTimeout(
      second,
      1000
    );
}

function settings_toggle(state){
    state = state == void 0
      ? document.getElementById('settings-button').value === '+'
      : state;
    var display = 'inline';

    if(state){
        document.getElementById('settings-button').value = '-';

    }else{
        display = 'none';
        document.getElementById('settings-button').value = '+';
    }

    for(var id in upgrades){
        document.getElementById('keybind-' + id).style.display = display;
    }
    document.getElementById('keybind-click').style.display = display;
    document.getElementById('settings-div').style.display = display;
}

var keyclick_ready = 1;
var upgrades = {
  'manual': {
    'base': 9,
    'bonus': 1,
    'cost': 1,
    'level': 0,
    'keybind': '1',
    'multiplier': 2,
    'type': 'per-click',
  },
  'script': {
    'base': 99,
    'bonus': 1,
    'cost': 1,
    'level': 0,
    'keybind': '2',
    'multiplier': 4,
    'type': 'per-second',
  },
  'employee': {
    'base': 249,
    'bonus': 2,
    'cost': 1,
    'level': 0,
    'keybind': '3',
    'multiplier': 8,
    'type': 'per-second',
  },
  'server': {
    'base': 499,
    'bonus': 3,
    'cost': 1,
    'level': 0,
    'keybind': '4',
    'multiplier': 16,
    'type': 'per-second',
  },
  'cluster': {
    'base': 999,
    'bonus': 4,
    'cost': 1,
    'level': 0,
    'keybind': '5',
    'multiplier': 32,
    'type': 'per-second',
  },
  'investor': {
    'base': 99,
    'bonus': 1,
    'cost': 1,
    'level': 0,
    'keybind': '6',
    'multiplier': 64,
    'type': 'multiplier',
  },
};

window.onbeforeunload = function(e){
    // Save clicks into window.localStorage.
    var clicks = parseInt(document.getElementById('clicks').innerHTML);
    if(clicks > 0){
        window.localStorage.setItem(
          'Click.htm-clicks',
          clicks
        );

    }else{
        window.localStorage.removeItem('Click.htm-clicks');
    }

    // Save upgrades into window.localStorage.
    for(var id in upgrades){
        // Save upgrade keybinds, if different from default.
        if(document.getElementById('keybind-' + id).value != upgrades[id]['keybind']){
            window.localStorage.setItem(
              'Click.htm-keybind-' + id,
              document.getElementById('keybind-' + id).value
            );

        }else{
            window.localStorage.removeItem('Click.htm-keybind-' + id);
        }

        // Only save level if greater than 0.
        if(upgrades[id]['level'] > 0){
            window.localStorage.setItem(
              'Click.htm-upgrade-' + id,
              upgrades[id]['level']
            );
        }

    }

    // Save click keybind, if different from default.
    if(document.getElementById('keybind-click').value != 'C'){
        window.localStorage.setItem(
          'Click.htm-keybind-click',
          document.getElementById('keybind-click').value
        );

    }else{
        window.localStorage.removeItem('Click.htm-keybind-click');
    }
};

window.onkeydown = function(e){
    var key = e.keyCode || e.which;

    // +: show settings.
    if(key === 187){
        settings_toggle(true);
        return;

    // -: hide settings.
    }else if(key === 189){
        settings_toggle(false);
        return;
    }

    key = String.fromCharCode(key);

    if(key === document.getElementById('keybind-click').value){
        if(keyclick_ready > 0){
            keyclick_ready = 0;
            click_button();
        }

        return;
    }

    for(var id in upgrades){
        if(key != document.getElementById('keybind-' + id).value){
            continue;
        }

        purchase(
          id,
          upgrades[id]['cost'],
          'clicks-' + upgrades[id]['type']
        );
    }
};

window.onkeyup = function(e){
    var key = String.fromCharCode(e.keyCode || e.which);

    if(key === document.getElementById('keybind-click').value){
        keyclick_ready = 1;
    }
};

window.onload = function(){
    // Load values from window.localStorage, if they exist.
    document.getElementById('keybind-click').value =
      window.localStorage.getItem('Click.htm-keybind-click') === null
        ? 'C'
        : window.localStorage.getItem('Click.htm-keybind-click');

    var ids = {
      'clicks': 0,
      'clicks-multiplier': 100,
      'clicks-per-click': 1,
      'clicks-per-second': 0,
    };

    for(var id in ids){
        document.getElementById(id).innerHTML =
          window.localStorage.getItem('Click.htm-' + id) === null
            ? ids[id]
            : window.localStorage.getItem('Click.htm-' + id);
    }

    var multiplier = (parseInt(document.getElementById('clicks-multiplier').innerHTML) / 100);
    document.getElementById('clicks-per-click-multiplied').innerHTML =
      Math.floor(parseInt(document.getElementById('clicks-per-click').innerHTML) * multiplier);
    document.getElementById('clicks-per-second-multiplied').innerHTML =
      Math.floor(parseInt(document.getElementById('clicks-per-second').innerHTML) * multiplier);

    for(id in upgrades){
        var upgrade = id[0].toUpperCase() + id.substring(1);

        document.getElementById('upgrades').innerHTML +=
          '0'
          + ' <input onclick=purchase("'
            + id + '",'
            + upgrades[id]['cost'] + ',"'
            + 'clicks-' + upgrades[id]['type']
          + '") type=button value=' + upgrade + '>'
          + '<input class=keybind id=keybind-' + id + ' maxlength=1>'
          + ' ' + (upgrades[id]['base'] + upgrades[id]['cost']) +'
';
    }

    for(id in upgrades){
        document.getElementById('keybind-' + id).value =
          window.localStorage.getItem('Click.htm-keybind-' + id) === null
            ? upgrades[id]['keybind']
            : window.localStorage.getItem('Click.htm-keybind-' + id);

        var level = parseInt(window.localStorage.getItem('Click.htm-upgrade-' + id));
        level = isNaN(level)
          ? 0
          : level;
        while(level > 0){
            purchase(
              id,
              upgrades[id]['cost'],
              'clicks-' + upgrades[id]['type'],
              true
            );
            level -= 1;
        }
    }

    window.setTimeout(
      second,
      1000
    );
};
<commit_msg>modified cost balance<commit_after>'use strict';

function click_button(){
    // Add clicks-per-click to clicks.
    document.getElementById('clicks').innerHTML =
      parseInt(document.getElementById('clicks').innerHTML)
      + Math.floor(parseInt(document.getElementById('clicks-per-click').innerHTML)
      * (parseInt(document.getElementById('clicks-multiplier').innerHTML) / 100));
}

function purchase(upgrade, cost, target, free){
    var clicks = parseInt(document.getElementById('clicks').innerHTML);
    free = free || false;

    if(!free
      && clicks < parseInt(document.getElementById('upgrade-' + upgrade + '-cost').innerHTML)){
        return;
    }

    if(!free){
        // If user can afford upgrade...
        //   ...subtract cost of upgrade from clicks...
        document.getElementById('clicks').innerHTML =
          clicks - parseInt(document.getElementById('upgrade-' + upgrade + '-cost').innerHTML);
    }

    // ...and increase upgrade...
    upgrades[upgrade]['level'] += 1;
    document.getElementById('upgrade-' + upgrade).innerHTML = upgrades[upgrade]['level'];

    // ...and update upgrade cost...
    upgrades[upgrade]['cost'] *= upgrades[upgrade]['multiplier'];
    document.getElementById('upgrade-' + upgrade + '-cost').innerHTML =
      upgrades[upgrade]['base'] + upgrades[upgrade]['cost'];

    // ...and increase target value, either clicks-per-click or clicks-per-second...
    document.getElementById(target).innerHTML = parseInt(document.getElementById(target).innerHTML)
      + upgrades[upgrade]['bonus'];

    // ...and recalculate multiplied values.
    var multiplier = (parseInt(document.getElementById('clicks-multiplier').innerHTML) / 100);
    document.getElementById('clicks-per-click-multiplied').innerHTML =
      Math.floor(parseInt(document.getElementById('clicks-per-click').innerHTML) * multiplier);
    document.getElementById('clicks-per-second-multiplied').innerHTML =
      Math.floor(parseInt(document.getElementById('clicks-per-second').innerHTML) * multiplier);
}

function reset_score(){
    if(!window.confirm('Reset score?')){
        return;
    }

    var ids = {
      'clicks': 0,
      'clicks-multiplier': 100,
      'clicks-per-click': 1,
      'clicks-per-click-multiplied': 1,
      'clicks-per-second': 0,
      'clicks-per-second-multiplied': 0,
    };
    for(var id in ids){
        window.localStorage.removeItem('Click.htm-' + id);
        document.getElementById(id).innerHTML = ids[id];
    }

    for(id in upgrades){
        upgrades[id]['cost'] = upgrades[id]['base'] / 4;
        upgrades[id]['level'] = 0;
    }

    for(var id in upgrades){
        window.localStorage.removeItem('Click.htm-upgrade-' + id);

        document.getElementById('upgrade-' + id).innerHTML = upgrades[id]['level'];
        document.getElementById('upgrade-' + id + '-cost').innerHTML =
          upgrades[id]['base'] + upgrades[id]['cost'];
    }
}

function reset_settings(){
    if(!window.confirm('Reset settings?')){
        return;
    }

    window.localStorage.removeItem('Click.htm-keybind-click');
    document.getElementById('keybind-click').value = 'C';

    for(var id in upgrades){
        window.localStorage.removeItem('Click.htm-keybind-' + id);
        document.getElementById('keybind-' + id).value = upgrades[id]['keybind'];
    }
}

function second(){
    if(parseInt(document.getElementById('clicks-per-second').innerHTML) > 0){
        document.getElementById('clicks').innerHTML =
          parseInt(document.getElementById('clicks').innerHTML)
        + Math.floor(parseInt(document.getElementById('clicks-per-second').innerHTML)
          * (parseInt(document.getElementById('clicks-multiplier').innerHTML) / 100));
    }

    // Setting the title to # of clicks makes idling easier.
    document.title = document.getElementById('clicks').innerHTML;

    // There is always another second.
    window.setTimeout(
      second,
      1000
    );
}

function settings_toggle(state){
    state = state == void 0
      ? document.getElementById('settings-button').value === '+'
      : state;
    var display = 'inline';

    if(state){
        document.getElementById('settings-button').value = '-';

    }else{
        display = 'none';
        document.getElementById('settings-button').value = '+';
    }

    for(var id in upgrades){
        document.getElementById('keybind-' + id).style.display = display;
    }
    document.getElementById('keybind-click').style.display = display;
    document.getElementById('settings-div').style.display = display;
}

var keyclick_ready = 1;
var upgrades = {
  'manual': {
    'base': 8,
    'bonus': 1,
    'cost': 2,
    'level': 0,
    'keybind': '1',
    'multiplier': 2,
    'type': 'per-click',
  },
  'script': {
    'base': 80,
    'bonus': 1,
    'cost': 20,
    'level': 0,
    'keybind': '2',
    'multiplier': 4,
    'type': 'per-second',
  },
  'employee': {
    'base': 200,
    'bonus': 2,
    'cost': 50,
    'level': 0,
    'keybind': '3',
    'multiplier': 8,
    'type': 'per-second',
  },
  'server': {
    'base': 400,
    'bonus': 3,
    'cost': 100,
    'level': 0,
    'keybind': '4',
    'multiplier': 16,
    'type': 'per-second',
  },
  'cluster': {
    'base': 800,
    'bonus': 4,
    'cost': 200,
    'level': 0,
    'keybind': '5',
    'multiplier': 32,
    'type': 'per-second',
  },
  'investor': {
    'base': 80,
    'bonus': 1,
    'cost': 20,
    'level': 0,
    'keybind': '6',
    'multiplier': 64,
    'type': 'multiplier',
  },
};

window.onbeforeunload = function(e){
    // Save clicks into window.localStorage.
    var clicks = parseInt(document.getElementById('clicks').innerHTML);
    if(clicks > 0){
        window.localStorage.setItem(
          'Click.htm-clicks',
          clicks
        );

    }else{
        window.localStorage.removeItem('Click.htm-clicks');
    }

    // Save upgrades into window.localStorage.
    for(var id in upgrades){
        // Save upgrade keybinds, if different from default.
        if(document.getElementById('keybind-' + id).value != upgrades[id]['keybind']){
            window.localStorage.setItem(
              'Click.htm-keybind-' + id,
              document.getElementById('keybind-' + id).value
            );

        }else{
            window.localStorage.removeItem('Click.htm-keybind-' + id);
        }

        // Only save level if greater than 0.
        if(upgrades[id]['level'] > 0){
            window.localStorage.setItem(
              'Click.htm-upgrade-' + id,
              upgrades[id]['level']
            );
        }

    }

    // Save click keybind, if different from default.
    if(document.getElementById('keybind-click').value != 'C'){
        window.localStorage.setItem(
          'Click.htm-keybind-click',
          document.getElementById('keybind-click').value
        );

    }else{
        window.localStorage.removeItem('Click.htm-keybind-click');
    }
};

window.onkeydown = function(e){
    var key = e.keyCode || e.which;

    // +: show settings.
    if(key === 187){
        settings_toggle(true);
        return;

    // -: hide settings.
    }else if(key === 189){
        settings_toggle(false);
        return;
    }

    key = String.fromCharCode(key);

    if(key === document.getElementById('keybind-click').value){
        if(keyclick_ready > 0){
            keyclick_ready = 0;
            click_button();
        }

        return;
    }

    for(var id in upgrades){
        if(key != document.getElementById('keybind-' + id).value){
            continue;
        }

        purchase(
          id,
          upgrades[id]['cost'],
          'clicks-' + upgrades[id]['type']
        );
    }
};

window.onkeyup = function(e){
    var key = String.fromCharCode(e.keyCode || e.which);

    if(key === document.getElementById('keybind-click').value){
        keyclick_ready = 1;
    }
};

window.onload = function(){
    // Load values from window.localStorage, if they exist.
    document.getElementById('keybind-click').value =
      window.localStorage.getItem('Click.htm-keybind-click') === null
        ? 'C'
        : window.localStorage.getItem('Click.htm-keybind-click');

    var ids = {
      'clicks': 0,
      'clicks-multiplier': 100,
      'clicks-per-click': 1,
      'clicks-per-second': 0,
    };

    for(var id in ids){
        document.getElementById(id).innerHTML =
          window.localStorage.getItem('Click.htm-' + id) === null
            ? ids[id]
            : window.localStorage.getItem('Click.htm-' + id);
    }

    var multiplier = (parseInt(document.getElementById('clicks-multiplier').innerHTML) / 100);
    document.getElementById('clicks-per-click-multiplied').innerHTML =
      Math.floor(parseInt(document.getElementById('clicks-per-click').innerHTML) * multiplier);
    document.getElementById('clicks-per-second-multiplied').innerHTML =
      Math.floor(parseInt(document.getElementById('clicks-per-second').innerHTML) * multiplier);

    for(id in upgrades){
        var upgrade = id[0].toUpperCase() + id.substring(1);

        document.getElementById('upgrades').innerHTML +=
          '0'
          + ' <input onclick=purchase("'
            + id + '",'
            + upgrades[id]['cost'] + ',"'
            + 'clicks-' + upgrades[id]['type']
          + '") type=button value=' + upgrade + '>'
          + '<input class=keybind id=keybind-' + id + ' maxlength=1>'
          + ' ' + (upgrades[id]['base'] + upgrades[id]['cost']) +'
';
    }

    for(id in upgrades){
        document.getElementById('keybind-' + id).value =
          window.localStorage.getItem('Click.htm-keybind-' + id) === null
            ? upgrades[id]['keybind']
            : window.localStorage.getItem('Click.htm-keybind-' + id);

        var level = parseInt(window.localStorage.getItem('Click.htm-upgrade-' + id));
        level = isNaN(level)
          ? 0
          : level;
        while(level > 0){
            purchase(
              id,
              upgrades[id]['cost'],
              'clicks-' + upgrades[id]['type'],
              true
            );
            level -= 1;
        }
    }

    window.setTimeout(
      second,
      1000
    );
};
<|endoftext|>


<commit_before>// Read data files and generates presskit from templates
'use strict'

const fs = require('fs')
const path = require('path')
const converter = require('../lib/converter')
const handlebars = require('handlebars')

const templatesPath = { product: '../assets/project.html', company: '../assets/index.html' }

// -------------------------------------------------------------
// Module.
// -------------------------------------------------------------
var Generator = function () {
}

Generator.prototype.generate = function (dataPath) {
  console.log('Looking for data files in ' + dataPath)

  // Inspect given path for data files
  explore(dataPath, function (file) {
    var ext = path.extname(file)
    var filename = path.basename(file, ext)

    if (filename === 'data') {
      generateForData(dataPath, file)
    }
  })
}

// Find all files in the given folder and raise the callback for each
function explore (dirPath, callback) {
  fs.readdirSync(dirPath).forEach(function (name) {
    var filePath = path.join(dirPath, name)
    var stat = fs.statSync(filePath)
    if (stat.isFile()) {
      callback(filePath, stat)
    } else if (stat.isDirectory()) {
      // Resursivity
      explore(filePath, callback)
    }
  })
}

// Build the HTML for the given data file
function generateForData (basePath, dataFile) {
  var ext = path.extname(dataFile)

  // Find the right parser
  if (ext === '.xml') {
    // Old presskit() format
    generateDoPresskit(basePath, dataFile)
  } else {
    // TODO .json .md
    console.warn(ext + ' files cannot be processed... yet!')
  }
}

// Build the HTML for a data.xml (presskit()) file
function generateDoPresskit (basePath, dataFile) {
  // Convert data.xml into the nex JSON format
  // -- Read file
  fs.readFile(dataFile, function (err, xml) {
    if (err) console.error(err)
    else {
      // -- Call converter
      converter.convert(xml, function (err, presskit) {
        if (err) console.error(err)
        else {
          // Generate template
          build(basePath, dataFile, presskit)
        }
      })
    }
  })
}

// Build the HTML
function build (basePath, dataFile, presskit) {
  // Get template
  var templatePath
  if (presskit.type === 'product') {
    templatePath = templatesPath.product
  } else if (presskit.type === 'company') {
    templatePath = templatesPath.company
  }

  var t = fs.readFileSync(templatePath, 'utf-8')
  var template = handlebars.compile(t)

  // Call Handlebars
  var html = template({ presskit: presskit })

  // Export result in HTML
  // -- Ensure ./build exists
  var buildDir = path.join(path.dirname(basePath), 'build')

  if (!fs.existsSync(buildDir)) {
    fs.mkdirSync(buildDir)
  }

// Prepare output filename
  var dataFilename = path.basename(path.dirname(dataFile))
  var htmlFilename = (presskit.type === 'company' ? 'index' : dataFilename) + '.html'
  var htmlDestination = path.join(buildDir, htmlFilename)

  console.log('Generating: ' + dataFile + ' ->  ' + htmlDestination)

    // -- Write HTML
  fs.writeFileSync(htmlDestination, html)
}

// -------------------------------------------------------------
// Exports.
// -------------------------------------------------------------
module.exports = new Generator()
<commit_msg>Fix: output /build folder in the working directory<commit_after>// Read data files and generates presskit from templates
'use strict'

const fs = require('fs')
const path = require('path')
const converter = require('../lib/converter')
const handlebars = require('handlebars')

const templatesPath = { product: '../assets/project.html', company: '../assets/index.html' }

// -------------------------------------------------------------
// Module.
// -------------------------------------------------------------
var Generator = function () {
}

Generator.prototype.generate = function (dataPath) {
  console.log('Looking for data files in ' + dataPath)

  // Inspect given path for data files
  explore(dataPath, function (file) {
    var ext = path.extname(file)
    var filename = path.basename(file, ext)

    if (filename === 'data') {
      generateForData(dataPath, file)
    }
  })
}

// Find all files in the given folder and raise the callback for each
function explore (dirPath, callback) {
  fs.readdirSync(dirPath).forEach(function (name) {
    var filePath = path.join(dirPath, name)
    var stat = fs.statSync(filePath)
    if (stat.isFile()) {
      callback(filePath, stat)
    } else if (stat.isDirectory()) {
      // Resursivity
      explore(filePath, callback)
    }
  })
}

// Build the HTML for the given data file
function generateForData (basePath, dataFile) {
  var ext = path.extname(dataFile)

  // Find the right parser
  if (ext === '.xml') {
    // Old presskit() format
    generateDoPresskit(basePath, dataFile)
  } else {
    // TODO .json .md
    console.warn(ext + ' files cannot be processed... yet!')
  }
}

// Build the HTML for a data.xml (presskit()) file
function generateDoPresskit (basePath, dataFile) {
  // Convert data.xml into the nex JSON format
  // -- Read file
  fs.readFile(dataFile, function (err, xml) {
    if (err) console.error(err)
    else {
      // -- Call converter
      converter.convert(xml, function (err, presskit) {
        if (err) console.error(err)
        else {
          // Generate template
          build(basePath, dataFile, presskit)
        }
      })
    }
  })
}

// Build the HTML
function build (basePath, dataFile, presskit) {
  // Get template
  var templatePath
  if (presskit.type === 'product') {
    templatePath = templatesPath.product
  } else if (presskit.type === 'company') {
    templatePath = templatesPath.company
  }

  var t = fs.readFileSync(templatePath, 'utf-8')
  var template = handlebars.compile(t)

  // Call Handlebars
  var html = template({ presskit: presskit })

  // Export result in HTML
  // -- Ensure ./build exists
  var buildDir = path.join(basePath, 'build')

  if (!fs.existsSync(buildDir)) {
    fs.mkdirSync(buildDir)
  }

// Prepare output filename
  var dataFilename = path.basename(path.dirname(dataFile))
  var htmlFilename = (presskit.type === 'company' ? 'index' : dataFilename) + '.html'
  var htmlDestination = path.join(buildDir, htmlFilename)

  console.log('Generating: ' + dataFile + ' ->  ' + htmlDestination)

    // -- Write HTML
  fs.writeFileSync(htmlDestination, html)
}

// -------------------------------------------------------------
// Exports.
// -------------------------------------------------------------
module.exports = new Generator()
<|endoftext|>


<commit_before>var fs = require('fs')
  , path = require('path')
  , inherits = require('util').inherits
  , _ = require('lodash')
  , escapeRegex = require('regexp-quote')
  , Cantina = require('cantina')
  , etc = require('etc')
  , etcYaml = require('etc-yaml');

module.exports = function (app) {
  var conf = app.conf.get('vhosts');

  // Cache of instantiated vhosts.
  app._vhosts = {};

  // API namespace.
  app.vhosts = {};

  // Vhost class.
  function Vhost (options) {
    Cantina.call(this, options);
  }
  inherits(Vhost, Cantina);

  // Custom boot method.
  Vhost.prototype.boot = function (root, cb) {
    var vhost = this;

    vhost.root = root;
    vhost.parent = app;
    vhost.top = app.top || app.parent || app;
    vhost.conf = etc()
      .use(etcYaml)
      .argv()
      .env()
      .folder(path.join(vhost.root, 'etc'));

    process.nextTick(cb);
  };

  // Create a new vhost.
  app.vhosts.create = function (root, defaults) {
    var vhost = new Vhost();

    // On app start, boot the vhost, loading ./etc conf and setting the root.
    app.hook('start').add(function (next) {
      vhost.boot(root, function (err) {
        if (err) return next(err);

        // Add default conf.
        vhost.conf.add(defaults || {});

        // We NEVER want the vhost to create its own server.
        vhost.conf.set('web:server:disabled', true);

        // Dependencies.
        vhost.require('cantina-web');

        // Load the (optional) vhost index file.
        var main;
        if (fs.existsSync(path.resolve(vhost.root, 'index.js'))) {
          main = vhost.require(vhost.root + '/index.js');
        }
        if (fs.existsSync(path.resolve(vhost.root, 'vhost.js'))) {
          main = vhost.require(vhost.root + '/vhost.js');
        }
        var name = vhost.conf.get('vhost:name');
        if (name && fs.existsSync(path.resolve(vhost.root, name + '.js'))) {
          main = vhost.require(vhost.root + '/' + name + '.js');
        }

        // Set weight.
        vhost.weight = (main && main.weight) ? main.weight : 0;

        // Load web stuff.
        vhost.load('web', {parent: vhost.root});

        // Add vhost req-prep middleware.
        vhost.middleware.first(-9000, function vhostReqPrepMiddleware (req, res, next) {
          app.vhosts.rewrite(vhost, req);
          next();
        });

        // Start the vhost.
        vhost.start(next);
      });
    });

    return vhost;
  };

  // Get a vhost by name.
  app.vhosts.get = function (name) {
    return app._vhosts[name];
  };

  // Destroy a vhost by name.
  app.vhosts.destroy = function (name, cb) {
    var vhost = app._vhosts[name];
    delete app._vhosts[name];
    vhost.destroy(cb);
  };

  // Find and return a vhost based on request data.
  app.vhosts.find = function (req) {
    var keys = Object.keys(app._vhosts)
      , key
      , matches = [];

    // Loop over vhosts and look for matches.
    for (var i = 0; i < keys.length; i++) {
      key = keys[i];
      vhost = app._vhosts[key];
      if (app.vhosts.match(vhost, req)) {
        matches.push(vhost);
      }
    }

    // Return the highest-weight match.
    if (matches.length) {
      matches.sort(function (a, b) {
        return a.weight - b.weight;
      });
      return matches[0];
    }
  };

  // Check if a vhost matches a request.
  app.vhosts.match = function (vhost, req) {
    var config = _.extend({}, conf, vhost.conf.get('vhost') || {})
      , name = config.name
      , regex;

    // Custom match?
    if (typeof vhost.match === 'function') {
      if (vhost.match(req)) {
        return true;
      }
    }

    // Match against full hostname?
    if (config.match == 'hostname') {
      if (req.href.hostname === (config.hostname || name)) {
        return true;
      }
    }

    // Match against domain?
    if (config.match === 'domain') {
      regex = new RegExp(escapeRegex(config.domain || name) + '$');
      if (regex.test(req.href.hostname)) {
        return true;
      }
    }

    // Match against subdomain?
    if (config.match === 'subdomain') {
      regex = new RegExp('^' + escapeRegex(config.subdomain || name) + '\\.');
      if (regex.test(req.href.hostname)) {
        return true;
      }
    }

    // Match against path?
    if (config.match === 'pathname') {
      regex = new RegExp('^\\/' + escapeRegex(config.pathname || name));
      if (regex.test(req.href.pathname)) {
        return true;
      }
    }
  };

  // Rewrite URL for vhost middleware stack.
  app.vhosts.rewrite = function (vhost, req) {
    var config = _.extend({}, conf, vhost.conf.get('vhost') || {});
    if (config.match === 'pathname') {
      req.url = req.url.replace(new RegExp('^\\/' + config.name), '');
    }
  };

  // Define a loader for vhosts.
  app.loader('vhosts', function (options) {
    if (fs.existsSync(options.path)) {
      var files = fs.readdirSync(options.path);
      files.forEach(function (name) {
        var root = path.resolve(options.path, name);
        if (fs.statSync(root).isDirectory()) {
          // We don't override existing vhosts.
          if (!app._vhosts[name]) {
            app._vhosts[name] = app.vhosts.create(root, {vhost: {name: name}});
          }
        }
      });
    }
  });
};
<commit_msg>Adds host matching.<commit_after>var fs = require('fs')
  , path = require('path')
  , inherits = require('util').inherits
  , _ = require('lodash')
  , escapeRegex = require('regexp-quote')
  , Cantina = require('cantina')
  , etc = require('etc')
  , etcYaml = require('etc-yaml');

module.exports = function (app) {
  var conf = app.conf.get('vhosts');

  // Cache of instantiated vhosts.
  app._vhosts = {};

  // API namespace.
  app.vhosts = {};

  // Vhost class.
  function Vhost (options) {
    Cantina.call(this, options);
  }
  inherits(Vhost, Cantina);

  // Custom boot method.
  Vhost.prototype.boot = function (root, cb) {
    var vhost = this;

    vhost.root = root;
    vhost.parent = app;
    vhost.top = app.top || app.parent || app;
    vhost.conf = etc()
      .use(etcYaml)
      .argv()
      .env()
      .folder(path.join(vhost.root, 'etc'));

    process.nextTick(cb);
  };

  // Create a new vhost.
  app.vhosts.create = function (root, defaults) {
    var vhost = new Vhost();

    // On app start, boot the vhost, loading ./etc conf and setting the root.
    app.hook('start').add(function (next) {
      vhost.boot(root, function (err) {
        if (err) return next(err);

        // Add default conf.
        vhost.conf.add(defaults || {});

        // We NEVER want the vhost to create its own server.
        vhost.conf.set('web:server:disabled', true);

        // Dependencies.
        vhost.require('cantina-web');

        // Load the (optional) vhost index file.
        var main;
        if (fs.existsSync(path.resolve(vhost.root, 'index.js'))) {
          main = vhost.require(vhost.root + '/index.js');
        }
        if (fs.existsSync(path.resolve(vhost.root, 'vhost.js'))) {
          main = vhost.require(vhost.root + '/vhost.js');
        }
        var name = vhost.conf.get('vhost:name');
        if (name && fs.existsSync(path.resolve(vhost.root, name + '.js'))) {
          main = vhost.require(vhost.root + '/' + name + '.js');
        }

        // Set weight.
        vhost.weight = (main && main.weight) ? main.weight : 0;

        // Load web stuff.
        vhost.load('web', {parent: vhost.root});

        // Add vhost req-prep middleware.
        vhost.middleware.first(-9000, function vhostReqPrepMiddleware (req, res, next) {
          app.vhosts.rewrite(vhost, req);
          next();
        });

        // Start the vhost.
        vhost.start(next);
      });
    });

    return vhost;
  };

  // Get a vhost by name.
  app.vhosts.get = function (name) {
    return app._vhosts[name];
  };

  // Destroy a vhost by name.
  app.vhosts.destroy = function (name, cb) {
    var vhost = app._vhosts[name];
    delete app._vhosts[name];
    vhost.destroy(cb);
  };

  // Find and return a vhost based on request data.
  app.vhosts.find = function (req) {
    var keys = Object.keys(app._vhosts)
      , key
      , matches = [];

    // Loop over vhosts and look for matches.
    for (var i = 0; i < keys.length; i++) {
      key = keys[i];
      vhost = app._vhosts[key];
      if (app.vhosts.match(vhost, req)) {
        matches.push(vhost);
      }
    }

    // Return the highest-weight match.
    if (matches.length) {
      matches.sort(function (a, b) {
        return a.weight - b.weight;
      });
      return matches[0];
    }
  };

  // Check if a vhost matches a request.
  app.vhosts.match = function (vhost, req) {
    var config = _.extend({}, conf, vhost.conf.get('vhost') || {})
      , name = config.name
      , regex;

    // Custom match?
    if (typeof vhost.match === 'function') {
      if (vhost.match(req)) {
        return true;
      }
    }

    // Match against full host?
    if (config.match == 'host') {
      if (req.href.host === (config.host || name)) {
        return true;
      }
    }

    // Match against full hostname?
    if (config.match == 'hostname') {
      if (req.href.hostname === (config.hostname || name)) {
        return true;
      }
    }

    // Match against domain?
    if (config.match === 'domain') {
      regex = new RegExp(escapeRegex(config.domain || name) + '$');
      if (regex.test(req.href.hostname)) {
        return true;
      }
    }

    // Match against subdomain?
    if (config.match === 'subdomain') {
      regex = new RegExp('^' + escapeRegex(config.subdomain || name) + '\\.');
      if (regex.test(req.href.hostname)) {
        return true;
      }
    }

    // Match against path?
    if (config.match === 'pathname') {
      regex = new RegExp('^\\/' + escapeRegex(config.pathname || name));
      if (regex.test(req.href.pathname)) {
        return true;
      }
    }
  };

  // Rewrite URL for vhost middleware stack.
  app.vhosts.rewrite = function (vhost, req) {
    var config = _.extend({}, conf, vhost.conf.get('vhost') || {});
    if (config.match === 'pathname') {
      req.url = req.url.replace(new RegExp('^\\/' + config.name), '');
    }
  };

  // Define a loader for vhosts.
  app.loader('vhosts', function (options) {
    if (fs.existsSync(options.path)) {
      var files = fs.readdirSync(options.path);
      files.forEach(function (name) {
        var root = path.resolve(options.path, name);
        if (fs.statSync(root).isDirectory()) {
          // We don't override existing vhosts.
          if (!app._vhosts[name]) {
            app._vhosts[name] = app.vhosts.create(root, {vhost: {name: name}});
          }
        }
      });
    }
  });
};
<|endoftext|>


<commit_before>var DOT = '·';
var COLORS = ['228DFF', 'FF00DE', 'FF1177', 'FF9900', 'FFDD1B', 'B6FF00'];

var langs = {};
var state = {color:0, lang:'', fx: 1, dots: 1, width:''};
var timeoutId;

var body = document.body;
var grid = document.getElementById('grid');
var overlay = document.getElementById('overlay');

function each(list, cb) {
	for (var i = 0; i < list.length; i++) {
		cb(list[i], i);
	}
}

function repeat(str, count) {
	return Array(count+1).join(str);
}

function readHash() {
	location.hash.slice(1).split('&').forEach(function(str) {
		var p = str.split('=');
		if (p[0] in state) {
			state[p[0]] = /^\d+$/.test(p[1]) ? parseInt(p[1]) : p[1];
		}
	});
}

function updateHash() {
	var hash = [];
	for (var key in state) {
		hash.push(key + '=' + state[key]);
	}
	location.hash = hash.join('&');
}

function generateChars() {
	grid.innerHTML = '';
	var cols = getLang().columns;
	var chars = getLang().chars;
	var rows = chars.length / cols;
	each(chars, function (chr, i) {
		var li = document.createElement('li');
		li.innerHTML = chr;
		li.style.width = percent(cols);
		li.style.height = percent(rows);
		if (chr === DOT) {
			li.classList.add('dot');
		}
		if (i && i % cols === 0) {
			li.classList.add('break');
		}
		grid.appendChild(li);
	});
}

function percent(items) {
	return (100 / items).toFixed(1) + '%';
}

var OFFSET = 5 * 60 * 1e3;
var fixedTime = null;

function getTime() {
	return fixedTime || Date.now();
}

function updateTime(sign) {
	fixedTime = sign && getTime() + OFFSET * sign;
	updateChars();
}

function getWords() {
	var now = new Date(getTime());
	var hour = now.getHours() % 12;
	var mins = now.getMinutes();
	var rest = mins % 5;
	var dots = repeat(DOT, rest);
	return getLang().timeToWords(hour, mins - rest).concat(dots);
}

function resetChars(nodes) {
	each(nodes, function (node) {
		node.classList.remove('on');
	});
}

function updateChars() {
	var nodes = grid.getElementsByTagName('li');
	var words = getWords();
	var index = 0;

	resetChars(nodes);
	each(words, function (word) {
		index = getLang().chars.indexOf(word, index);
		// Word not found, also to avoid failing when dots=0
		if (index === -1) return;
		each(word, function (chr) {
			nodes[index++].classList.add('on');
		});
		index++;
	});
}

function scheduleUpdate() {
	clearTimeout(timeoutId);
	var elapsed = Date.now() % 1000;
	timeoutId = setTimeout(scheduleUpdate, 1000 - elapsed);
	updateChars();
}

// Multi language support

function toggleLang() {
	var list = Object.keys(langs);
	state.lang = list[(list.indexOf(state.lang) + 1) % list.length];
	generateChars();
	updateHash();
}

function getLang() {
	return langs[state.lang];
}

function detectLang() {
	var userPref = navigator.language || navigator.userLanguage || '';
	var pref = userPref.slice(0, 2).toLowerCase();
	state.lang = (pref in langs ? pref : 'en');
}

function addDots() {
	for (var key in langs) {
		var cols = langs[key].columns;
		langs[key].chars +=	repeat(' ', (cols - 4) / 2) + repeat(DOT, 4);
	}
}

// Theming

function changeTheme(delta) {
	state.color = (state.color + delta) % 6;
	overlay.style.backgroundColor = '#' + COLORS[state.color];
	overlay.style.display = 'block';
	updateHash();
}

var hammer = new Hammer(body);
hammer.get('swipe').set({ direction: Hammer.DIRECTION_ALL });

hammer.on('doubletap', function () { screenfull.toggle(); } );
hammer.on('swipeleft', function () { updateTime(-1); });
hammer.on('swiperight', function () { updateTime(1); });
hammer.on('press', function () {	updateTime(null); });
hammer.on('swipeup', function() { toggleLang(); });
hammer.on('swipedown', function() { changeTheme(1); });

Hammer.on(window, 'load', function () {
	readHash();
	if (state.dots) {
		addDots();
	}
	if (!state.lang) {
		detectLang();
	}
	if (!state.fx) {
		body.classList.add('no-transitions');
	}
	if (state.width) {
		body.style.width = state.width + '%';
		body.style.marginLeft = (100 - state.width) / 2 + '%';
	}
	generateChars();
	scheduleUpdate();
	changeTheme(0);
});
<commit_msg>If user modified the hash, reload so they get applied<commit_after>var DOT = '·';
var COLORS = ['228DFF', 'FF00DE', 'FF1177', 'FF9900', 'FFDD1B', 'B6FF00'];

var langs = {};
var state = {color:0, lang:'', fx: 1, dots: 1, width:''};
var timeoutId;

var body = document.body;
var grid = document.getElementById('grid');
var overlay = document.getElementById('overlay');

function each(list, cb) {
	for (var i = 0; i < list.length; i++) {
		cb(list[i], i);
	}
}

function repeat(str, count) {
	return Array(count+1).join(str);
}

function readHash() {
	location.hash.slice(1).split('&').forEach(function(str) {
		var p = str.split('=');
		if (p[0] in state) {
			state[p[0]] = /^\d+$/.test(p[1]) ? parseInt(p[1]) : p[1];
		}
	});
}

function stateToHash() {
	var hash = [];
	for (var key in state) {
		hash.push(key + '=' + state[key]);
	}
	return hash.join('&');
}

function updateHash() {
	location.hash = stateToHash();
}

function generateChars() {
	grid.innerHTML = '';
	var cols = getLang().columns;
	var chars = getLang().chars;
	var rows = chars.length / cols;
	each(chars, function (chr, i) {
		var li = document.createElement('li');
		li.innerHTML = chr;
		li.style.width = percent(cols);
		li.style.height = percent(rows);
		if (chr === DOT) {
			li.classList.add('dot');
		}
		if (i && i % cols === 0) {
			li.classList.add('break');
		}
		grid.appendChild(li);
	});
}

function percent(items) {
	return (100 / items).toFixed(1) + '%';
}

var OFFSET = 5 * 60 * 1e3;
var fixedTime = null;

function getTime() {
	return fixedTime || Date.now();
}

function updateTime(sign) {
	fixedTime = sign && getTime() + OFFSET * sign;
	updateChars();
}

function getWords() {
	var now = new Date(getTime());
	var hour = now.getHours() % 12;
	var mins = now.getMinutes();
	var rest = mins % 5;
	var dots = repeat(DOT, rest);
	return getLang().timeToWords(hour, mins - rest).concat(dots);
}

function resetChars(nodes) {
	each(nodes, function (node) {
		node.classList.remove('on');
	});
}

function updateChars() {
	var nodes = grid.getElementsByTagName('li');
	var words = getWords();
	var index = 0;

	resetChars(nodes);
	each(words, function (word) {
		index = getLang().chars.indexOf(word, index);
		// Word not found, also to avoid failing when dots=0
		if (index === -1) return;
		each(word, function (chr) {
			nodes[index++].classList.add('on');
		});
		index++;
	});
}

function scheduleUpdate() {
	clearTimeout(timeoutId);
	var elapsed = Date.now() % 1000;
	timeoutId = setTimeout(scheduleUpdate, 1000 - elapsed);
	updateChars();
}

// Multi language support

function toggleLang() {
	var list = Object.keys(langs);
	state.lang = list[(list.indexOf(state.lang) + 1) % list.length];
	generateChars();
	updateHash();
}

function getLang() {
	return langs[state.lang];
}

function detectLang() {
	var userPref = navigator.language || navigator.userLanguage || '';
	var pref = userPref.slice(0, 2).toLowerCase();
	state.lang = (pref in langs ? pref : 'en');
}

function addDots() {
	for (var key in langs) {
		var cols = langs[key].columns;
		langs[key].chars +=	repeat(' ', (cols - 4) / 2) + repeat(DOT, 4);
	}
}

// Theming

function changeTheme(delta) {
	state.color = (state.color + delta) % 6;
	overlay.style.backgroundColor = '#' + COLORS[state.color];
	overlay.style.display = 'block';
	updateHash();
}

var hammer = new Hammer(body);
hammer.get('swipe').set({ direction: Hammer.DIRECTION_ALL });

hammer.on('doubletap', function () { screenfull.toggle(); } );
hammer.on('swipeleft', function () { updateTime(-1); });
hammer.on('swiperight', function () { updateTime(1); });
hammer.on('press', function () {	updateTime(null); });
hammer.on('swipeup', function() { toggleLang(); });
hammer.on('swipedown', function() { changeTheme(1); });

Hammer.on(window, 'load', function () {
	readHash();
	if (state.dots) {
		addDots();
	}
	if (!state.lang) {
		detectLang();
	}
	if (!state.fx) {
		body.classList.add('no-transitions');
	}
	if (state.width) {
		body.style.width = state.width + '%';
		body.style.marginLeft = (100 - state.width) / 2 + '%';
	}
	generateChars();
	scheduleUpdate();
	changeTheme(0);
});

Hammer.on(window, 'hashchange', function () {
	// If user modified the hash, reload so they get applied
	if (location.hash.slice(1) !== stateToHash()) {
		location.reload();
	}
});
<|endoftext|>


<commit_before>module.exports = {

  kuzzle: null,

  init: function (kuzzle) {
    this.kuzzle = kuzzle;
  },

  log: function (object,hookEvent) {
    this.kuzzle.services.list.logger.log(object,hookEvent);
  },

  error: function (error,hookEvent) {
    this.kuzzle.services.list.logger.error(error,hookEvent);
  }

};<commit_msg>nitpicking coma<commit_after>module.exports = {

  kuzzle: null,

  init: function (kuzzle) {
    this.kuzzle = kuzzle;
  },

  log: function (object, hookEvent) {
    this.kuzzle.services.list.logger.log(object, hookEvent);
  },

  error: function (error, hookEvent) {
    this.kuzzle.services.list.logger.error(error, hookEvent);
  }

};<|endoftext|>


<commit_before>// Peity jQuery plugin version 0.3.5
// (c) 2010 Ben Pickles
//
// http://benpickles.github.com/peity/
//
// Released under MIT license.
(function(j,m){function l(e,a){var b=m.createElement("canvas");b.setAttribute("width",e);b.setAttribute("height",a);return b}var i=j.fn.peity=function(e,a){m.createElement("canvas").getContext&&this.each(function(){j(this).change(function(){var b=j(this).html();i.graphers[e](j(this),j.extend({},i.defaults[e],a));j(this).trigger("chart:changed",b)}).trigger("change")});return this};i.graphers={};i.defaults={};i.add=function(e,a,b){i.graphers[e]=b;i.defaults[e]=a};i.add("pie",{colours:["#FFF4DD","#FF9900"],
delimeter:"/",radius:16},function(e,a){var b=a.radius/2,d=e.text().split(a.delimeter),h=parseFloat(d[0]),k=parseFloat(d[1]);d=-Math.PI/2;h=h/k*Math.PI*2;k=l(a.radius,a.radius);var c=k.getContext("2d");c.beginPath();c.moveTo(b,b);c.arc(b,b,b,h+d,h==0?Math.PI*2:d,false);c.fillStyle=a.colours[0];c.fill();c.beginPath();c.moveTo(b,b);c.arc(b,b,b,d,h+d,false);c.fillStyle=a.colours[1];c.fill();e.wrapInner(j("<span>").hide()).append(k)});i.add("line",{colour:"#c6d9fd",strokeColour:"#4d89f9",strokeWidth:1,
delimeter:",",height:16,max:null,width:32},function(e,a){var b=l(a.width,a.height),d=e.text().split(a.delimeter),h=Math.max.apply(Math,d.concat([a.max]));h=a.height/h;var k=a.width/(d.length-1),c=[],f,g=b.getContext("2d");g.beginPath();g.moveTo(0,a.height);for(f=0;f<d.length;f++){var n=f*k,o=a.height-h*d[f];c.push({x:n,y:o});g.lineTo(n,o)}g.lineTo(a.width,a.height);g.fillStyle=a.colour;g.fill();g.beginPath();g.moveTo(0,c[0].y);for(f=0;f<c.length;f++)g.lineTo(c[f].x,c[f].y);g.lineWidth=a.strokeWidth;
g.strokeStyle=a.strokeColour;g.stroke();e.wrapInner(j("<span>").hide()).append(b)});i.add("bar",{colour:"#4D89F9",delimeter:",",height:16,max:null,width:32},function(e,a){var b=l(a.width,a.height),d=e.text().split(a.delimeter),h=Math.max.apply(Math,d.concat([a.max]));h=a.height/h;var k=a.width/d.length,c=b.getContext("2d");c.fillStyle=a.colour;for(var f=0;f<d.length;f++){var g=h*d[f];c.fillRect(f*k,a.height-g,k,g)}e.wrapInner(j("<span>").hide()).append(b)})})(jQuery,document);
<commit_msg>Version 0.4.0.<commit_after>// Peity jQuery plugin version 0.3.5
// (c) 2010 Ben Pickles
//
// http://benpickles.github.com/peity/
//
// Released under MIT license.
(function(f,k){function l(a,f){var b=k.createElement("canvas");b.setAttribute("width",a);b.setAttribute("height",f);return b}var e=f.fn.peity=function(a,n){k.createElement("canvas").getContext&&this.each(function(){f(this).change(function(){var b=f.extend({},n),d=this;f.each(b||{},function(a,c){f.isFunction(c)&&(b[a]=c.call(d))});var g=f(this).html();e.graphers[a].call(this,f.extend({},e.defaults[a],b));f(this).trigger("chart:changed",g)}).trigger("change")});return this};e.graphers={};e.defaults=
{};e.add=function(a,f,b){e.graphers[a]=b;e.defaults[a]=f};e.add("pie",{colours:["#FFF4DD","#FF9900"],delimeter:"/",radius:16},function(a){var e=f(this),b=a.radius/2,d=e.text().split(a.delimeter),g=parseFloat(d[0]),j=parseFloat(d[1]);d=-Math.PI/2;g=g/j*Math.PI*2;j=l(a.radius,a.radius);var c=j.getContext("2d");c.beginPath();c.moveTo(b,b);c.arc(b,b,b,g+d,g==0?Math.PI*2:d,!1);c.fillStyle=a.colours[0];c.fill();c.beginPath();c.moveTo(b,b);c.arc(b,b,b,d,g+d,!1);c.fillStyle=a.colours[1];c.fill();e.wrapInner(f("<span>").hide()).append(j)});
e.add("line",{colour:"#c6d9fd",strokeColour:"#4d89f9",strokeWidth:1,delimeter:",",height:16,max:null,width:32},function(a){var e=f(this),b=l(a.width,a.height),d=e.text().split(a.delimeter),g=Math.max.apply(Math,d.concat([a.max]));g=a.height/g;var j=a.width/(d.length-1),c=[],h,i=b.getContext("2d");i.beginPath();i.moveTo(0,a.height);for(h=0;h<d.length;h++){var k=h*j,m=a.height-g*d[h];c.push({x:k,y:m});i.lineTo(k,m)}i.lineTo(a.width,a.height);i.fillStyle=a.colour;i.fill();i.beginPath();i.moveTo(0,c[0].y);
for(h=0;h<c.length;h++)i.lineTo(c[h].x,c[h].y);i.lineWidth=a.strokeWidth;i.strokeStyle=a.strokeColour;i.stroke();e.wrapInner(f("<span>").hide()).append(b)});e.add("bar",{colour:"#4D89F9",delimeter:",",height:16,max:null,width:32},function(a){var e=f(this),b=l(a.width,a.height),d=e.text().split(a.delimeter),g=Math.max.apply(Math,d.concat([a.max]));g=a.height/g;var j=a.width/d.length,c=b.getContext("2d");c.fillStyle=a.colour;for(var h=0;h<d.length;h++){var i=g*d[h];c.fillRect(h*j,a.height-i,j,i)}e.wrapInner(f("<span>").hide()).append(b)})})(jQuery,
document);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  StyleSheet,
  View,
  Text,
  Button,
  TouchableOpacity,
  Easing,
  Animated,
  Image,
  Vibration,
  Alert
} from 'react-native';
import * as Colors from './Colors';
import { BakinBaconApi } from './BakinBaconApi';
import { BaconOptimizer, StaticOptimizer } from './BaconOptimizer';
import { PushService } from './push/PushService';
import { Notifications } from 'expo';

var moment = require('moment');
require("moment-duration-format");

export class BaconTimerScreen extends Component {
    static navigationOptions = {
        title: "Bakin' Bacon Timer",
        tabBarLabel: 'Timer',
        headerStyle: {backgroundColor: Colors.primary },
        headerTitleStyle: { color: Colors.titleColor },
        headerLeft: null
    };

    componentWillMount() {
      PushService.Register();

      // Handle notifications that are received or selected while the app
      // is open. If the app was closed and then opened by tapping the
      // notification (rather than just tapping the app icon to open it),
      // this function will fire on the next tick after the app starts
      // with the notification data.
      this._notificationSubscription = Notifications.addListener(() => this.props.navigation.navigate('Feedback'));
    }

    constructor(props) {
      super(props);
      //this.baconOptimizer = new StaticOptimizer(moment.duration(20, "seconds"));
      this.baconOptimizer = new BaconOptimizer();
      this.state = {
          duration: this.baconOptimizer.optimize([], null),
          startTime: null,
          running: false
      };
      this.api = new BakinBaconApi();
      this.api.getBaconBits(this.handleBaconBits.bind(this));
    }

    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.timerText}>{this.timeLeft().format("m:ss", {trim: false})}</Text>
                <Image
                    style={ this.getPigImageRotationStyle() }
                    source={require('./images/pig.png')}
                />
                <TouchableOpacity
                    style={ styles.controlContainer }
                    onPress={this.toggleRunning.bind(this)}
                    >
                    {this.controlImage()}
                </TouchableOpacity>
            </View>
        );
    }

    handleBaconBits(bacon_bits) {
      this.setState({duration: this.baconOptimizer.optimize(bacon_bits)});
    }

    getPigImageRotationStyle(){
        return {
            height:156,
            width: 156,
            transform: [{rotate: this.degreeRotation()}]
        };
     }

    alertUser() {
        Vibration.vibrate([0, 200, 400, 600, 0, 100, 300, 500, 0, 200, 400, 500, 0, 100, 300, 500, 0, 200, 400, 600]);
        Alert.alert('Finished',
            'Go eat your perfect bacon.',
            [
                {text: 'OK', onPress: () => {
                    Vibration.cancel();
                }}
            ],
            { cancelable: false }
        )
    }

    startTimer() {
        this._interval = setInterval(() => {
            if (this.isTimerExpired()) {
                this.stopTimer();
                this.alertUser();
            }
            // this gets the ball rolling, or keeps it rolling
            this.setState({
                running: true,
                startTime: this.state.startTime || moment()
            });
        }, 1000);
    }

    stopTimer() {
        clearInterval(this._interval);
        this.setState({
            running: false,
            startTime: null,
        });
    }
    toggleRunning() {
        if (this.state.running) {
            this.stopTimer();
        } else {
            this.startTimer();
        }
    }

    timeLeft() {
        if (this.state.running) {
            let timeLeft = moment
                .duration(this.state.duration)
                .subtract(moment().diff(this.state.startTime));

            return timeLeft.asMilliseconds <= 0
                ? moment.duration(0)
                : timeLeft;
        }
        return moment.duration(this.state.duration);
    }

    isTimerExpired() {
        return this.timeLeft().asMilliseconds() <= 0;
    }

    degreeRotation() {
        var difference = moment.duration(this.state.duration).subtract(this.timeLeft());
        var rotation = 180 * difference / this.state.duration;

        return rotation + "deg";
    }

    controlImage() {
        return (
            <Image
                style={styles.controlimage}
                source={this.state.running
                    ? require('./images/bacon_reset.png')
                    : require('./images/bacon_play.png')}
            />
        );
    }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: Colors.backgroundColor,
    alignItems: 'center'
  },
  timerText: {
      fontSize: 72,
      textAlign: 'center',
      margin: 10,
      marginBottom: 20,
      marginTop: 30,
  },
  button: {
      height: 60,
      justifyContent: 'center',
      alignItems: 'center',
  },
  imageContainer: {
    height:156,
    width: 156,
    borderRadius: 64,
  },
  pig: {
    height:156,
    width: 156
  },
  controlContainer: {
    height:96,
    width: 96,
    borderRadius: 48,
    marginTop: 40,
  },
  controlImage: {
    height:96,
    width: 96,
    borderRadius: 48
  },
});
<commit_msg>refactor: inline a couple functions<commit_after>import React, { Component } from 'react';
import {
  StyleSheet,
  View,
  Text,
  Button,
  TouchableOpacity,
  Easing,
  Animated,
  Image,
  Vibration,
  Alert
} from 'react-native';
import * as Colors from './Colors';
import { BakinBaconApi } from './BakinBaconApi';
import { BaconOptimizer, StaticOptimizer } from './BaconOptimizer';
import { PushService } from './push/PushService';
import { Notifications } from 'expo';

var moment = require('moment');
require("moment-duration-format");

export class BaconTimerScreen extends Component {
    static navigationOptions = {
        title: "Bakin' Bacon Timer",
        tabBarLabel: 'Timer',
        headerStyle: {backgroundColor: Colors.primary },
        headerTitleStyle: { color: Colors.titleColor },
        headerLeft: null
    };

    componentWillMount() {
      PushService.Register();

      // Handle notifications that are received or selected while the app
      // is open. If the app was closed and then opened by tapping the
      // notification (rather than just tapping the app icon to open it),
      // this function will fire on the next tick after the app starts
      // with the notification data.
      this._notificationSubscription = Notifications.addListener(() => this.props.navigation.navigate('Feedback'));
    }

    constructor(props) {
      super(props);
      //let baconOptimizer = new StaticOptimizer(moment.duration(20, "seconds"));
      let baconOptimizer = new BaconOptimizer();
      this.state = {
          duration: baconOptimizer.optimize([], null),
          startTime: null,
          running: false
      };
      new BakinBaconApi().getBaconBits(bacon_bits => {
        this.setState({duration: baconOptimizer.optimize(bacon_bits)})
      });
    }

    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.timerText}>{this.timeLeft().format("m:ss", {trim: false})}</Text>
                <Image
                    style={{
                        height: 156,
                        width: 156,
                        transform: [{rotate: this.pigRotation()}]
                    }}
                    source={require('./images/pig.png')}
                />
                <TouchableOpacity
                    style={ styles.controlContainer }
                    onPress={this.toggleRunning.bind(this)}
                    >
                    <Image
                        style={styles.controlimage}
                        source={this.state.running
                            ? require('./images/bacon_reset.png')
                            : require('./images/bacon_play.png')}
                    />
                </TouchableOpacity>
            </View>
        );
    }

    alertUser() {
        Vibration.vibrate([0, 200, 400, 600, 0, 100, 300, 500, 0, 200, 400, 500, 0, 100, 300, 500, 0, 200, 400, 600]);
        Alert.alert('Finished',
            'Go eat your perfect bacon.',
            [
                {text: 'OK', onPress: () => {
                    Vibration.cancel();
                }}
            ],
            { cancelable: false }
        )
    }

    startTimer() {
        this._interval = setInterval(() => {
            if (this.isTimerExpired()) {
                this.stopTimer();
                this.alertUser();
            }
            // this gets the ball rolling, or keeps it rolling
            this.setState({
                running: true,
                startTime: this.state.startTime || moment()
            });
        }, 1000);
    }

    stopTimer() {
        clearInterval(this._interval);
        this.setState({
            running: false,
            startTime: null,
        });
    }
    toggleRunning() {
        if (this.state.running) {
            this.stopTimer();
        } else {
            this.startTimer();
        }
    }

    timeLeft() {
        if (this.state.running) {
            let timeLeft = moment
                .duration(this.state.duration)
                .subtract(moment().diff(this.state.startTime));

            return timeLeft.asMilliseconds <= 0
                ? moment.duration(0)
                : timeLeft;
        }
        return moment.duration(this.state.duration);
    }

    isTimerExpired() {
        return this.timeLeft().asMilliseconds() <= 0;
    }

    pigRotation() {
        var difference = moment.duration(this.state.duration).subtract(this.timeLeft());
        var rotation = 180 * difference / this.state.duration;

        return rotation + "deg";
    }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: Colors.backgroundColor,
    alignItems: 'center'
  },
  timerText: {
      fontSize: 72,
      textAlign: 'center',
      margin: 10,
      marginBottom: 20,
      marginTop: 30,
  },
  button: {
      height: 60,
      justifyContent: 'center',
      alignItems: 'center',
  },
  imageContainer: {
    height:156,
    width: 156,
    borderRadius: 64,
  },
  pig: {
    height:156,
    width: 156
  },
  controlContainer: {
    height:96,
    width: 96,
    borderRadius: 48,
    marginTop: 40,
  },
  controlImage: {
    height:96,
    width: 96,
    borderRadius: 48
  },
});
<|endoftext|>