<commit_before>'use strict';

const { expect } = require('chai'),
    { shouldReturnErrors, shouldNotReturnErrors } = require('../helpers/test-templates'),
    { string, number, bool, object } = require('../fluent-validator')().schemas;

const ERROR_TYPES = {
    RANGE: 'range',
    ARGUMENT: 'argument',
    TYPE: 'type',
    PREDICATE: 'predicate'
};

describe('ObjectSchema keys', () => {
    it('ObjectSchema.validateType(): should return true for objects that are not arrays and functions', () => {
        expect(object().validateType({})).to.equal(true);
        expect(object().validateType(Object.create(null))).to.equal(true);
    });

    it('ObjectSchema.validateType(): should return false for arrays and functions', () => {
        expect(object().validateType([])).to.equal(false);
        expect(object().validateType(function () { })).to.equal(false);
    });

    it('ObjectSchema.validateType(): should return true for arrays when .allowArrays() has been called', () => {
        expect(object().allowArrays().validateType([])).to.equal(true);
    });

    it('ObjectSchema.validate(): should return true for functions when .allowFunctions() has been called', () => {
        expect(object().allowFunctions().validateType(function () { })).to.equal(true);
    });

    it('Should return errors for invalid keys of expected type', () => {
        const personSchema = object({
            name: string().minlength(3).maxlength(10),
            age: number().min(0).max(100),
            isStudent: bool().required()
        }),
            invalidPerson = {
                name: '1',
                age: -1
            };

        const errors = personSchema.validate(invalidPerson, 'person');

        const nameError = errors.find(e => e.path === 'person.name'),
            ageError = errors.find(e => e.path === 'person.age'),
            isStudentError = errors.find(e => e.path === 'person.isStudent');

        expect(nameError.type).to.equal(ERROR_TYPES.RANGE);
        expect(ageError.type).to.equal(ERROR_TYPES.RANGE);
        expect(isStudentError.type).to.equal(ERROR_TYPES.TYPE);
    });

    it('Should return errors for keys with invalid values when .required() has been called on the subschemas', () => {
        const softwareProjectSchema = object({
            mainLang: string().required(),
            loc: number().required(),
            size: number().required(),
            isPrivate: bool().required()
        });

        const invalidProject = {
            mainLang: null,
            loc: NaN,
            size: '10'
        };

        const [
            isPrivateError,
            locError,
            mainLangError,
            sizeError
        ] = softwareProjectSchema.validate(invalidProject, 'proj')
            .sort((a, b) => a.path.localeCompare(b.path));

        expect(isPrivateError.path).to.equal('proj.isPrivate');
        expect(locError.path).to.equal('proj.loc');
        expect(mainLangError.path).to.equal('proj.mainLang');
        expect(sizeError.path).to.equal('proj.size');
    });

    it('Should not return errors for valid keys', () => {
        const personSchema = object({
            name: string().minlength(3).maxlength(10),
            age: number().min(0).max(100),
            isStudent: bool().required()
        }),
            pencho = {
                name: 'pencho',
                age: 5,
                isStudent: false
            };

        expect(personSchema.validate(pencho, '') + '').to.equal([] + '');
    });

    it('Should not return errors for keys that have value of invalid type but are not required', () => {
        const animalSchema = object({
            breed: string().minlength(2).maxlength(10),
            weightKg: number().min(0),
            carnivore: bool()
        });

        const blankObj = {};

        expect(animalSchema.validate(blankObj, '') + '').to.equal([] + '');
    });
});<commit_msg>More ObjectSchema tests<commit_after>'use strict';

const { expect } = require('chai'),
    { shouldReturnErrors, shouldNotReturnErrors } = require('../helpers/test-templates'),
    { string, number, bool, object } = require('../fluent-validator')().schemas;

const ERROR_TYPES = {
    RANGE: 'range',
    ARGUMENT: 'argument',
    TYPE: 'type',
    PREDICATE: 'predicate'
};

describe('ObjectSchema keys', () => {
    it('ObjectSchema.type should return "object"', () => {
        expect(object().type).to.equal('object');
    });

    it('ObjectSchema.validateType(): should return true for objects that are not arrays and functions', () => {
        expect(object().validateType({})).to.equal(true);
        expect(object().validateType(Object.create(null))).to.equal(true);
    });

    it('ObjectSchema.validateType(): should return false for arrays and functions', () => {
        expect(object().validateType([])).to.equal(false);
        expect(object().validateType(function () { })).to.equal(false);
    });

    it('ObjectSchema.validateType(): should return true for arrays when .allowArrays() has been called', () => {
        expect(object().allowArrays().validateType([])).to.equal(true);
    });

    it('ObjectSchema.validate(): should return true for functions when .allowFunctions() has been called', () => {
        expect(object().allowFunctions().validateType(function () { })).to.equal(true);
    });

    it('Should return errors for invalid keys of expected type', () => {
        const personSchema = object({
            name: string().minlength(3).maxlength(10),
            age: number().min(0).max(100),
            isStudent: bool().required()
        }),
            invalidPerson = {
                name: '1',
                age: -1
            };

        const errors = personSchema.validate(invalidPerson, 'person');

        const nameError = errors.find(e => e.path === 'person.name'),
            ageError = errors.find(e => e.path === 'person.age'),
            isStudentError = errors.find(e => e.path === 'person.isStudent');

        expect(nameError.type).to.equal(ERROR_TYPES.RANGE);
        expect(ageError.type).to.equal(ERROR_TYPES.RANGE);
        expect(isStudentError.type).to.equal(ERROR_TYPES.TYPE);
    });

    it('Should return errors for keys with invalid values when .required() has been called on the subschemas', () => {
        const softwareProjectSchema = object({
            mainLang: string().required(),
            loc: number().required(),
            size: number().required(),
            isPrivate: bool().required()
        });

        const invalidProject = {
            mainLang: null,
            loc: NaN,
            size: '10'
        };

        const [
            isPrivateError,
            locError,
            mainLangError,
            sizeError
        ] = softwareProjectSchema.validate(invalidProject, 'proj')
            .sort((a, b) => a.path.localeCompare(b.path));

        expect(isPrivateError.path).to.equal('proj.isPrivate');
        expect(locError.path).to.equal('proj.loc');
        expect(mainLangError.path).to.equal('proj.mainLang');
        expect(sizeError.path).to.equal('proj.size');
    });

    it('Should return errors for keys with invalid values but of correct type when .required() has not been called', () => {
        const postSchema = object({
            id: string().pattern(/^[0-9]{10}$/i),
            likesCount: number().integer(),
            content: string().minlength(5)
        });

        const invalidPost = {
            id: 'aaaaaaaaa',
            likesCount: -1.5,
            content: 'aa'
        };

        const [
            contentError,
            idError,
            likesCountError
        ] = postSchema.validate(invalidPost, 'post').sort((err1, err2) => err1.path.localeCompare(err2.path));

        expect(contentError.path).to.equal('post.content');
        expect(contentError.type).to.equal(ERROR_TYPES.RANGE);

        expect(idError.path).to.equal('post.id');
        expect(idError.type).to.equal(ERROR_TYPES.ARGUMENT);

        expect(likesCountError.path).to.equal('post.likesCount');
        expect(likesCountError.type).to.equal(ERROR_TYPES.ARGUMENT);
    });

    it('Should not return errors for valid keys', () => {
        const personSchema = object({
            name: string().minlength(3).maxlength(10),
            age: number().min(0).max(100),
            isStudent: bool().required()
        }),
            pencho = {
                name: 'pencho',
                age: 5,
                isStudent: false
            };

        expect(personSchema.validate(pencho, '') + '').to.equal([] + '');
    });

    it('Should not return errors for keys that have value of invalid type but are not required', () => {
        const animalSchema = object({
            breed: string().minlength(2).maxlength(10),
            weightKg: number().min(0),
            carnivore: bool()
        });

        const blankObj = {};

        expect(animalSchema.validate(blankObj, '') + '').to.equal([] + '');
    });
});

describe('ObjectSchema nesting', () => {
    it('Nested object schemas should also return errors for invalid values', () => {
        const compilerSchema = object({
            lang: object({
                name: string().minlength(1).required(),
                crossPlatform: bool().required(),
                version: number().min(0).required()
            }),
            name: string().minlength(1)
        });

        const invalidCompiler = {
            name: 'Pesho',
            lang: {
                name: '',
                crossPlatform: 10,
                version: '39393'
            }
        };

        const [
            crossPlatformError,
            nameError,
            versionError
        ] = compilerSchema.validate(invalidCompiler, 'compiler').sort((err1, err2) => err1.path.localeCompare(err2.path));

        expect(crossPlatformError.path).to.equal('compiler.lang.crossPlatform');
        expect(crossPlatformError.type).to.equal(ERROR_TYPES.TYPE);

        expect(nameError.path).to.equal('compiler.lang.name');
        expect(nameError.type).to.equal(ERROR_TYPES.RANGE);

        expect(versionError.path).to.equal('compiler.lang.version');
        expect(versionError.type).to.equal(ERROR_TYPES.TYPE);
    });

    it('Nesting should return error and not crash when the nested object is not present on the actual value', () => {
        const schema = object({
            options: object({ opt: bool() }).required()
        });

        const invalidObj = {};

        const errors = schema.validate(invalidObj, 'obj');

        expect(errors.length).to.equal(1);

        const [ error ] = errors;

        expect(error.type).to.equal(ERROR_TYPES.TYPE);
        expect(error.path).to.equal('obj.options');
    });

    it('Nesting should not return errors when the nested schema is not required', () => {
        const schema = object({
            options: object({ opts: string() })
        });

        const invalidObj = {};

        expect(schema.validate(invalidObj, '') + '').to.equal([] + '');
    });
});<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var Timeline = require('pebble-api');
var MongoClient = require('mongodb').MongoClient;
var exphbs  = require('express-handlebars');
var cors = require('cors');

var mongoUri = process.env.MONGOLAB_URI ||
  process.env.MONGOHQ_URL ||
  'mongodb://localhost/mydb';

// create a new Timeline object with our API key passed as an environment variable
var timeline = new Timeline();

var app = express();
app.use(cors());
app.use(bodyParser.json());

var hbs = exphbs.create({
  defaultLayout: 'main',
});

app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

app.use(express.static(__dirname + '/public'));
app.set('port', (process.env.PORT || 5000)); // set the port on the instance

Date.prototype.isLeapYear = function() {
  var year = this.getFullYear();
  if((year & 3) != 0) return false;
  return ((year % 100) != 0 || (year % 400) == 0);
};

var dayCount = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];

function createId() {
  var d = new Date();
  var mn = d.getMonth();
  var dn = d.getDate();
  var dayOfYear = dayCount[mn] + dn;
  if(mn > 1 && d.isLeapYear()) {
    dayOfYear++;
  }
  if (dayOfYear > 182) {
    dayOfYear -= 182;
  }
  var cs = d.getHours() * 60 * 60 * 100 +
    d.getMinutes() * 60 * 100 +
    d.getSeconds() * 100 +
    Math.floor(d.getMilliseconds()/10);
  return parseInt('' + dayOfYear.toString() + cs);
}

function pushPin(place, res) {
  var pin;
  // var pinID = parseInt(place._id.toHexString(), 16);
  var pinID = place._id.toString();
  if (place.pin) {
    place.pin.id = pinID;
    place.pin.time = new Date(place.pin.time || place.time);
    console.log(place.pin);
    try {
     pin = new Timeline.Pin(place.pin);
    }
    catch(e) {
      console.warn('Failed to create pin: ' + e);
      res.status(400);
      res.send('Failed to create pin: ' + e);
      return;
    }
  }
  else {
    pin = new Timeline.Pin({
      id: pinID,
      time: new Date(place.time),
      layout: new Timeline.Pin.Layout({
        type: Timeline.Pin.LayoutType.GENERIC_PIN,
        tinyIcon: Timeline.Pin.Icon.PIN,
        title: 'Get Back target',
        body: 'Set from watch'
      })
    });
    console.log(pin);
  }
  pin.addAction(new Timeline.Pin.Action({
    title: 'Get Back',
    type: Timeline.Pin.ActionType.OPEN_WATCH_APP,
    launchCode: parseInt(place._id)
  }));
  pin.createNotification = new Timeline.Pin.Notification(
    {
      "layout": {
        "type": "genericNotification",
        "title": pin.layout.title || "Get Back target",
        "tinyIcon": "system://images/NOTIFICATION_FLAG",
        "body": "A new target added from watch."
      }
    }
  );
  place.pin = pin;
  console.log(place);

  console.log('Sending pin for ' + place.timelineToken + ': ' + JSON.stringify(pin));
  timeline.sendUserPin(place.timelineToken, pin, function (err, body, resp) {
    if(err) {
      console.warn('Failed to push pin to timeline: ' + err);
      res.status(400);
      res.send('Failed to push pin to timeline: ' + err);
    }
    else {
      console.log('Pin successfully pushed!');
      res.json(place);
    }
  });
}

// add new place into history
app.post('/:userToken/place/new', function(req, res) {
  // console.log(req.body);
  var userToken = req.params.userToken;
  // var place = JSON.parse(req.body) || {};
  var place = req.body || {};
  place.user = userToken;
  console.log(place);
  // if id is specified in params try to use it as mondodb id
  // otherwise create a new id, just use milliseconds since a nearby epoch...
  // This may result to duplicates!
  place._id = parseInt(place.id) || createId();
  place.time = new Date().getTime();
  if (place.pin && place.pin.time && Date.parse(place.pin.time)) {
    place.time = new Date(place.pin.time).getTime();
  }
  var pos = place.position; // || {};
  if (!pos || !pos.coords || !pos.coords.latitude || !pos.coords.longitude) {
    if (req.param('latitude') && req.param('longitude')) {
      pos.coords = {
        'latitude': req.param('latitude'),
        'longitude': req.param('longitude')
      }
      place.pos = pos;
    }
    else {
      var errorText = 'No coordinates found in parameters!';
      console.warn(errorText);
      res.status(400);
      res.send(errorText);
      return;
    }
  }
  // console.log(place);

  // store place to db
  // console.log('Trying to connect to mongodb at ' + mongoUri);
  MongoClient.connect(mongoUri, function(err, db) {
    if (err || !db) {
      console.warn('Failed to open db connection: ' + err);
      res.status(400);
      res.send('Failed to open db connection: ' + err);
      return;
    }
    db.collection('places', function(err, collection) {
      if (err || !collection) {
        console.warn('Could not find collection "places": ' + err);
        res.status(400);
        res.send('Could not find collection "places": ' + err);
        return;
      }
      collection.insert(place, {safe: true}, function(err, result) {
        if (err || !result) {
          if (err.code == 11000) {
            console.warn('Failed to save place into db: ' + err + ', Retrying...');
            place._id++;
            // just one retry, no infinite loop...
            collection.insert(place, {safe: true}, function(err, result) {
              if (err || !result) {
                console.warn('Failed again to save place into db: ' + err);
                res.status(400);
                res.send('Failed to save place into db: ' + err);
                return;
              }
              console.log('Place saved to db! id = ' + place._id);
              if (place.timelineToken) {
                pushPin(place, res);
              }
              else {
                console.log('No timeline token in place. ' + JSON.stringify(place));
                res.json(place);
              }
            });
          }
          else {
            res.status(400);
            res.send('Failed to save place into db: ' + err);
            return;
          }
        }
        else {
          console.log('Place saved to db! id = ' + place._id);
          if (place.timelineToken) {
            pushPin(place, res);
          }
          else {
            console.log('No timeline token in place. ' + JSON.stringify(place));
            res.json(place);
          }
        }
      });
    });
  });
});

// get a certain point
app.get('/:userToken/place/:id', function(req, res) {
  var userToken = req.params.userToken;
  var id = parseInt(req.params.id);
  // console.log('Trying to connect to mongodb at ' + mongoUri);
  MongoClient.connect(mongoUri, function(err, db) {
    if (err) {
      console.warn('Failed to open db connection: ' + err);
      res.status(400);
      res.send('Failed to open db connection: ' + err);
      return;
    }
    db.collection('places', function(er, collection) {
      collection.findOne({"user": userToken, "_id": id}, function(err, doc) {
        if (err) {
          res.status(400);
          res.send('Failed to retrieve place from db: ' + err);
          return;
        }
        // console.log(doc);
        if (!doc) {
          res.status(403);
          res.send('No place with id ' + id + ' found for user token ' + userToken);
          return;
        }
        res.json(doc);
      });
    });
  });
});

// get last :max points from history
app.get('/:userToken/places/:max?', function(req, res) {
  var userToken = req.params.userToken;
  var max = parseInt(req.params.max);
  // console.log('Trying to connect to mongodb at ' + mongoUri);
  MongoClient.connect(mongoUri, function(err, db) {
    if (err) {
      console.warn('Failed to open db connection: ' + err);
      res.status(400);
      res.send('Failed to open db connection: ' + err);
      return;
    }
    db.collection('places', function(er, collection) {
      var query = {"user": userToken, "time": {$type: 1}};
      collection.find(query).sort({time: -1}).limit(max).toArray(function(err, places) {
        if (err) {
          console.warn('Failed to retrieve places from db: ' + err);
          res.status(400);
          res.send('Failed to retrieve places from db: ' + err);
          return;
        }
        if (places) {
          res.json(places);
        }
      });
    });
  });
});

// show configuration page
app.get('/:userToken/configure', function(req, res) {
  var userToken = req.params.userToken;
  var return_to = req.query.return_to || 'pebblejs://close#';
  var context = {'userToken': userToken,
                 'return_to': return_to};
  if (req.query.conf) {
    context.conf = JSON.parse(req.query.conf);
  }
  res.render('configure', context);
});

app.get('/:userToken?/', function (req, res) {
  var context = {'userToken': req.params.userToken,
                 'timelineToken': req.query.timelineToken};
  res.render('home', context);
});

// start the webserver
var server = app.listen(app.get('port'), function () {
  console.log('Get Back server listening on port %s', app.get('port'));
});<commit_msg>correctly handle reminder times<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var Timeline = require('pebble-api');
var MongoClient = require('mongodb').MongoClient;
var exphbs  = require('express-handlebars');
var cors = require('cors');

var mongoUri = process.env.MONGOLAB_URI ||
  process.env.MONGOHQ_URL ||
  'mongodb://localhost/mydb';

// create a new Timeline object with our API key passed as an environment variable
var timeline = new Timeline();

var app = express();
app.use(cors());
app.use(bodyParser.json());

var hbs = exphbs.create({
  defaultLayout: 'main',
});

app.engine('handlebars', hbs.engine);
app.set('view engine', 'handlebars');

app.use(express.static(__dirname + '/public'));
app.set('port', (process.env.PORT || 5000)); // set the port on the instance

Date.prototype.isLeapYear = function() {
  var year = this.getFullYear();
  if((year & 3) != 0) return false;
  return ((year % 100) != 0 || (year % 400) == 0);
};

var dayCount = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];

function createId() {
  var d = new Date();
  var mn = d.getMonth();
  var dn = d.getDate();
  var dayOfYear = dayCount[mn] + dn;
  if(mn > 1 && d.isLeapYear()) {
    dayOfYear++;
  }
  if (dayOfYear > 182) {
    dayOfYear -= 182;
  }
  var cs = d.getHours() * 60 * 60 * 100 +
    d.getMinutes() * 60 * 100 +
    d.getSeconds() * 100 +
    Math.floor(d.getMilliseconds()/10);
  return parseInt('' + dayOfYear.toString() + cs);
}

function pushPin(place, res) {
  var pin;
  // var pinID = parseInt(place._id.toHexString(), 16);
  var pinID = place._id.toString();
  var pinTime = place.pin.time || place.time || null;
  if (place.pin) {
    place.pin.id = pinID;
    place.pin.time = new Date(pinTime);
    for (var i=0; i<place.pin.reminders.length; i++) {
      place.pin.reminders[i].time = new Date(place.pin.reminders[i].time);
    }
    console.log(place.pin);
    try {
     pin = new Timeline.Pin(place.pin);
    }
    catch(e) {
      console.warn('Failed to create pin: ' + e);
      res.status(400);
      res.send('Failed to create pin: ' + e);
      return;
    }
  }
  else {
    pin = new Timeline.Pin({
      id: pinID,
      time: new Date(pinTime),
      layout: new Timeline.Pin.Layout({
        type: Timeline.Pin.LayoutType.GENERIC_PIN,
        tinyIcon: Timeline.Pin.Icon.PIN,
        title: 'Get Back target',
        body: 'Set from watch'
      })
    });
    console.log(JSON.stringify(pin));
  }
  pin.addAction(new Timeline.Pin.Action({
    title: 'Get Back',
    type: Timeline.Pin.ActionType.OPEN_WATCH_APP,
    launchCode: parseInt(place._id)
  }));
  if (!place.pin || !place.pin.createNotification) {
    pin.createNotification = new Timeline.Pin.Notification(
      {
        "layout": {
          "type": "genericNotification",
          "title": pin.layout.title || "Get Back target",
          "tinyIcon": "system://images/NOTIFICATION_FLAG",
          "body": "A new target added from watch."
        }
      }
    );
  }
  place.pin = pin;
  console.log(place);

  console.log('Sending pin for ' + place.timelineToken + ': ' + JSON.stringify(pin));
  timeline.sendUserPin(place.timelineToken, pin, function (err, body, resp) {
    if(err) {
      console.warn('Failed to push pin to timeline: ' + err);
      res.status(400);
      res.send('Failed to push pin to timeline: ' + err);
    }
    else {
      console.log('Pin successfully pushed!');
      res.json(place);
    }
  });
}

// add new place into history
app.post('/:userToken/place/new', function(req, res) {
  // console.log(req.body);
  var userToken = req.params.userToken;
  // var place = JSON.parse(req.body) || {};
  var place = req.body || {};
  place.user = userToken;
  console.log(place);
  // if id is specified in params try to use it as mondodb id
  // otherwise create a new id, just use milliseconds since a nearby epoch...
  // This may result to duplicates!
  place._id = parseInt(place.id) || createId();
  place.time = new Date().getTime();
  if (place.pin && place.pin.time && Date.parse(place.pin.time)) {
    place.time = new Date(place.pin.time).getTime();
  }
  var pos = place.position; // || {};
  if (!pos || !pos.coords || !pos.coords.latitude || !pos.coords.longitude) {
    if (req.param('latitude') && req.param('longitude')) {
      pos.coords = {
        'latitude': req.param('latitude'),
        'longitude': req.param('longitude')
      }
      place.pos = pos;
    }
    else {
      var errorText = 'No coordinates found in parameters!';
      console.warn(errorText);
      res.status(400);
      res.send(errorText);
      return;
    }
  }
  // console.log(place);

  // store place to db
  // console.log('Trying to connect to mongodb at ' + mongoUri);
  MongoClient.connect(mongoUri, function(err, db) {
    if (err || !db) {
      console.warn('Failed to open db connection: ' + err);
      res.status(400);
      res.send('Failed to open db connection: ' + err);
      return;
    }
    db.collection('places', function(err, collection) {
      if (err || !collection) {
        console.warn('Could not find collection "places": ' + err);
        res.status(400);
        res.send('Could not find collection "places": ' + err);
        return;
      }
      collection.insert(place, {safe: true}, function(err, result) {
        if (err || !result) {
          if (err.code == 11000) {
            console.warn('Failed to save place into db: ' + err + ', Retrying...');
            place._id++;
            // just one retry, no infinite loop...
            collection.insert(place, {safe: true}, function(err, result) {
              if (err || !result) {
                console.warn('Failed again to save place into db: ' + err);
                res.status(400);
                res.send('Failed to save place into db: ' + err);
                return;
              }
              console.log('Place saved to db! id = ' + place._id);
              if (place.timelineToken) {
                pushPin(place, res);
              }
              else {
                console.log('No timeline token in place. ' + JSON.stringify(place));
                res.json(place);
              }
            });
          }
          else {
            res.status(400);
            res.send('Failed to save place into db: ' + err);
            return;
          }
        }
        else {
          console.log('Place saved to db! id = ' + place._id);
          if (place.timelineToken) {
            pushPin(place, res);
          }
          else {
            console.log('No timeline token in place. ' + JSON.stringify(place));
            res.json(place);
          }
        }
      });
    });
  });
});

// get a certain point
app.get('/:userToken/place/:id', function(req, res) {
  var userToken = req.params.userToken;
  var id = parseInt(req.params.id);
  // console.log('Trying to connect to mongodb at ' + mongoUri);
  MongoClient.connect(mongoUri, function(err, db) {
    if (err) {
      console.warn('Failed to open db connection: ' + err);
      res.status(400);
      res.send('Failed to open db connection: ' + err);
      return;
    }
    db.collection('places', function(er, collection) {
      collection.findOne({"user": userToken, "_id": id}, function(err, doc) {
        if (err) {
          res.status(400);
          res.send('Failed to retrieve place from db: ' + err);
          return;
        }
        // console.log(doc);
        if (!doc) {
          res.status(403);
          res.send('No place with id ' + id + ' found for user token ' + userToken);
          return;
        }
        res.json(doc);
      });
    });
  });
});

// get last :max points from history
app.get('/:userToken/places/:max?', function(req, res) {
  var userToken = req.params.userToken;
  var max = parseInt(req.params.max);
  // console.log('Trying to connect to mongodb at ' + mongoUri);
  MongoClient.connect(mongoUri, function(err, db) {
    if (err) {
      console.warn('Failed to open db connection: ' + err);
      res.status(400);
      res.send('Failed to open db connection: ' + err);
      return;
    }
    db.collection('places', function(er, collection) {
      var query = {"user": userToken, "time": {$type: 1}};
      collection.find(query).sort({time: -1}).limit(max).toArray(function(err, places) {
        if (err) {
          console.warn('Failed to retrieve places from db: ' + err);
          res.status(400);
          res.send('Failed to retrieve places from db: ' + err);
          return;
        }
        if (places) {
          res.json(places);
        }
      });
    });
  });
});

// show configuration page
app.get('/:userToken/configure', function(req, res) {
  var userToken = req.params.userToken;
  var return_to = req.query.return_to || 'pebblejs://close#';
  var context = {'userToken': userToken,
                 'return_to': return_to};
  if (req.query.conf) {
    context.conf = JSON.parse(req.query.conf);
  }
  res.render('configure', context);
});

app.get('/:userToken?/', function (req, res) {
  var context = {'userToken': req.params.userToken,
                 'timelineToken': req.query.timelineToken};
  res.render('home', context);
});

// start the webserver
var server = app.listen(app.get('port'), function () {
  console.log('Get Back server listening on port %s', app.get('port'));
});<|endoftext|>


<commit_before>describe.skip('An unrated track in Google Music', function () {
  it('has no rating', function () {
    // Placeholder for linter
  });
});

describe.skip('A track in Google Music', function () {
  describe('when \'thumbs up\'-ed', function () {
    it('has a high rating', function () {
      // Placeholder for linter
    });
  });

  describe('when \'thumbs down\'-ed', function () {
    it('has a low rating', function () {
      // Placeholder for linter
    });
  });
});
<commit_msg>Starting on rating tests<commit_after>// Load in dependencies
var expect = require('chai').expect;
var browserUtils = require('./utils/browser');
var browserMusicUtils = require('./utils/browser-music');

// Start our tests
describe.skip('An unrated track in Google Music', function () {
  it('has no rating', function () {
    // Placeholder for linter
  });
});

describe.skip('A track in Google Music', function () {
  browserUtils.openMusic({killBrowser: false});

  describe('when \'thumbs up\'-ed', function () {
    it('has a high rating', function () {
      // Placeholder for linter
    });
  });

  describe('when \'thumbs down\'-ed', function () {
    it('has a low rating', function () {
      // Placeholder for linter
    });
  });
});
<|endoftext|>


<commit_before>'use strict'

/*
  nodeErrPerf.js

  - This code is to test out the performance of Error creation on node.js
  - Please only use this in Node.js 4 or up as it uses ES6 features
  - This code utilizes call stack randomization in order to bypass any
      possible stack processing optimizations that may exist in V8

  tests to run:
      node ./nodeErrPerf.js Error false &
      node ./nodeErrPerf.js Error true &
      node ./nodeErrPerf.js MyErr false &
      node ./nodeErrPerf.js MyErr true &
      node ./nodeErrPerf.js Dog &

*/
var util = require('util')
var clazz,getStack;
const ITERATIONS=10000

function doTests(){

    if(process.argv.length < 3){q$.qErr('Need 1 or two args passed')}
        clazz = process.argv[2]
    if(process.argv.length > 3){
        getStack = process.argv[3]
    }

    var timer = new Timer(),iCt=0;
    for(var i=0;i<ITERATIONS;i++){
        var o,stk
        if(clazz === 'Error'){
            o = nextFunc()(10,"new Error('test',123)")
            if(getStack){
                stk = o.stack
                iCt = stk.length>1500?iCt+1:iCt-1
                // console.log(`stk.length = ${stk.length}`)
            }
        } else if(clazz === 'MyErr'){
            o = nextFunc()(10,"new MyErr('test',123)")
            if(getStack){
                stk = o.stack
                iCt = stk.length>2500?iCt+1:iCt-1
                // console.log(`stk.length = ${stk.length}`)
            }
        } else if(clazz === 'Dog'){
            o = nextFunc()(10,"new Dog('test',123)")
        } else {
            throw new Error(`Unexpected class: ${clazz}`)
        }
    }

    console.log(`\nclazz: ${clazz}, getStack: ${getStack}, time: ${timer.elapsed(5)}`)

    process.exit(0)
}

// Test classes:
class MyErr extends Error {
    constructor(msg,code){
        super(msg)
        this.code=code
    }
}

class Animal {
    constructor(name) {
        this.name = name;
    }
}

class Dog extends Animal {
    constructor(name,likesCats) {
        super(name);
        this.likesCats = likesCats
    }
}

// Helpers
class Timer {
    constructor() {this.start();}
    start() {this.startT=process.hrtime();}
    elapsed(prec) {
        prec=!prec||!isNaN(prec)?3:prec;
        var endT=process.hrtime(this.startT);
        return endT[0]+'.'+(new String(endT[1])).substring(0,prec);
    }
}

function nRand(){
    return (new String(Math.random())).replace(/\./g,"").substring(1).substring(0,1)
}

// This is the funky call stack randomization stuff
var sFuncBody = "global.%s = function %s(c,ev){if(c===0){return eval(ev)}else{return nextFunc()(--c,ev)} }"
for(let i=0;i<10;i++){
    var sFN=`f${i}`
    eval(util.format(sFuncBody,sFN,sFN))
}
function nextFunc(){return eval('f'+nRand(1))}

doTests()
<commit_msg>Moving file<commit_after>'use strict'
/*
  nodeErrPerf.js

  - This code is to test out the performance of Error creation on node.js
  - Please only use this in Node.js 4 or up as it uses ES6 features
  - This code utilizes call stack randomization in order to bypass any
      possible stack processing optimizations that may exist in V8

  tests to run:
      node ./nodeErrPerf.js Error false &
      node ./nodeErrPerf.js Error true &
      node ./nodeErrPerf.js MyErr false &
      node ./nodeErrPerf.js MyErr true &
      node ./nodeErrPerf.js Dog &

*/
var util = require('util')
var clazz,getStack;
const ITERATIONS=10000

function doTests(){

    if(process.argv.length < 3){q$.qErr('Need 1 or two args passed')}
        clazz = process.argv[2]
    if(process.argv.length > 3){
        getStack = process.argv[3]
    }

    var timer = new Timer(),iCt=0;
    for(var i=0;i<ITERATIONS;i++){
        var o,stk
        if(clazz === 'Error'){
            o = nextFunc()(10,"new Error('test',123)")
            if(getStack){
                stk = o.stack
                iCt = stk.length>1500?iCt+1:iCt-1
                // console.log(`stk.length = ${stk.length}`)
            }
        } else if(clazz === 'MyErr'){
            o = nextFunc()(10,"new MyErr('test',123)")
            if(getStack){
                stk = o.stack
                iCt = stk.length>2500?iCt+1:iCt-1
                // console.log(`stk.length = ${stk.length}`)
            }
        } else if(clazz === 'Dog'){
            o = nextFunc()(10,"new Dog('test',123)")
        } else {
            throw new Error(`Unexpected class: ${clazz}`)
        }
    }

    console.log(`\nclazz: ${clazz}, getStack: ${getStack}, time: ${timer.elapsed(5)}`)

    process.exit(0)
}

// Test classes:
class MyErr extends Error {
    constructor(msg,code){
        super(msg)
        this.code=code
    }
}

class Animal {
    constructor(name) {
        this.name = name;
    }
}

class Dog extends Animal {
    constructor(name,likesCats) {
        super(name);
        this.likesCats = likesCats
    }
}

// Helpers
class Timer {
    constructor() {this.start();}
    start() {this.startT=process.hrtime();}
    elapsed(prec) {
        prec=!prec||!isNaN(prec)?3:prec;
        var endT=process.hrtime(this.startT);
        return endT[0]+'.'+(new String(endT[1])).substring(0,prec);
    }
}

function nRand(){
    return (new String(Math.random())).replace(/\./g,"").substring(1).substring(0,1)
}

// This is the funky call stack randomization stuff
var sFuncBody = "global.%s = function %s(c,ev){if(c===0){return eval(ev)}else{return nextFunc()(--c,ev)} }"
for(let i=0;i<10;i++){
    var sFN=`f${i}`
    eval(util.format(sFuncBody,sFN,sFN))
}
function nextFunc(){return eval('f'+nRand(1))}

doTests()
<|endoftext|>


<commit_before>// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the License.txt file included with this file.

/** @scope modules */

const Modes = Module("modes", {
    requires: ["config", "util"],

    init: function () {
        this._main = 1;     // NORMAL
        this._extended = 0; // NONE

        this._lastShown = null;

        this._passNextKey = false;
        this._passAllKeys = false;
        this._passKeysExceptions = null; // Which keys are still allowed in ignore mode. ONLY set when an :ignorekeys LocationChange triggered it
        this._isRecording = false;
        this._isReplaying = false; // playing a macro

        this._modeStack = [];

        this._mainModes = [this.NONE];
        this._lastMode = 0;
        this._modeMap = {};

        // main modes, only one should ever be active
        this.addMode("NORMAL",   { char: "n", display: -1 });
        this.addMode("INSERT",   { char: "i", input: true });
        this.addMode("VISUAL",   { char: "v", display: function () "VISUAL" + (editor.getVisualMode() ? " " + editor.getVisualMode() : "") });
        this.addMode("COMMAND_LINE", { char: "c", input: true, display: -1 });
        this.addMode("CARET"); // text cursor is visible
        this.addMode("TEXTAREA", { char: "i" });
        this.addMode("EMBED",    { input: true });
        this.addMode("CUSTOM",   { display: function () plugins.mode });
        // this._extended modes, can include multiple modes, and even main modes
        this.addMode("EX", true);
        this.addMode("HINTS", true);
        this.addMode("INPUT_MULTILINE", true);
        this.addMode("OUTPUT_MULTILINE", true);
        this.addMode("SEARCH_FORWARD", true);
        this.addMode("SEARCH_BACKWARD", true);
        this.addMode("SEARCH_VIEW_FORWARD", true);
        this.addMode("SEARCH_VIEW_BACKWARD", true);
        this.addMode("LINE", true); // linewise visual mode
        this.addMode("PROMPT", true);

        config.modes.forEach(function (mode) { this.addMode.apply(this, mode); }, this);
    },

    _getModeMessage: function () {
        if (this._passNextKey) {
            return "IGNORE";
        } else if (this._passAllKeys) {
            if (!this._passKeysExceptions || this._passKeysExceptions.length == 0)
                return "IGNORE ALL KEYS (Press <S-Esc> or <Insert> to exit)";
            else
                return "IGNORE MOST KEYS (All except " + this._passKeysExceptions + ")";
        }

        // when recording or replaying a macro
        if (modes.isRecording)
            return "RECORDING";
        else if (modes.isReplaying)
            return "REPLAYING";

        if (this._main in this._modeMap && typeof this._modeMap[this._main].display === "function")
            return this._modeMap[this._main].display();

        return ""; // default mode message
    },

    // NOTE: Pay attention that you don't run into endless loops
    // Usually you should only indicate to leave a special mode like HINTS
    // by calling modes.reset() and adding the stuff which is needed
    // for its cleanup here
    _handleModeChange: function (oldMode, newMode, oldExtended) {
        switch (oldMode) {
            case modes.TEXTAREA:
            case modes.INSERT:
                editor.unselectText();
                break;

            case modes.VISUAL:
                if (newMode == modes.CARET) {
                    try { // clear any selection made; a simple if (selection) does not work
                        let selection = Buffer.focusedWindow.getSelection();
                        selection.collapseToStart();
                    }
                    catch (e) {}
                }
                else
                    editor.unselectText();
                break;

            case modes.CUSTOM:
                plugins.stop();
                break;

            case modes.COMMAND_LINE:
                // clean up for HINT mode
                if (oldExtended & modes.HINTS)
                    hints.hide();
                commandline.close();
                break;
        }

        if (newMode == modes.NORMAL) {
            // disable caret mode when we want to switch to normal mode
            if (options.getPref("accessibility.browsewithcaret"))
                options.setPref("accessibility.browsewithcaret", false);

            if (oldMode == modes.COMMAND_LINE)
                liberator.focusContent(false); // when coming from the commandline, we might want to keep the focus (after a Search, e.g.)
            else
                liberator.focusContent(true); // unfocus textareas etc.
        }
    },

    NONE: 0,

    __iterator__: function () util.Array.itervalues(this.all),

    get all() this._mainModes.slice(),

    get mainModes() (mode for ([k, mode] in Iterator(modes._modeMap)) if (!mode.extended && mode.name == k)),

    get mainMode() this._modeMap[this._main],

    addMode: function (name, extended, options) {
        let disp = name.replace("_", " ", "g");
        this[name] = 1 << this._lastMode++;
        if (typeof extended == "object") {
            options = extended;
            extended = false;
        }
        this._modeMap[name] = this._modeMap[this[name]] = util.extend({
            extended: extended,
            count: true,
            input: false,
            mask: this[name],
            name: name,
            disp: disp
        }, options);
        this._modeMap[name].display = this._modeMap[name].display || function () disp;
        if (!extended)
            this._mainModes.push(this[name]);
        if ("mappings" in modules)
            mappings.addMode(this[name]);
    },

    getMode: function (name) this._modeMap[name],

    getCharModes: function (chr) [m for (m in values(this._modeMap)) if (m.char == chr)],

    matchModes: function (obj) [m for (m in values(this._modeMap)) if (array(keys(obj)).every(function (k) obj[k] == (m[k] || false)))],

    // show the current mode string in the command line
    show: function () {
        if (options["showmode"])
            commandline.setModeMessage(this._getModeMessage());
    },

    // add/remove always work on the this._extended mode only
    add: function (mode) {
        this._extended |= mode;
        this.show();
    },

    // helper function to set both modes in one go
    // if silent == true, you also need to take care of the mode handling changes yourself
    set: function (mainMode, extendedMode, silent, stack) {
        silent = (silent || this._main == mainMode && this._extended == extendedMode);
        // if a this._main mode is set, the this._extended is always cleared
        let oldMain = this._main, oldExtended = this._extended;
        if (typeof extendedMode === "number")
            this._extended = extendedMode;
        if (typeof mainMode === "number") {
            this._main = mainMode;
            if (!extendedMode)
                this._extended = modes.NONE;

            if (this._main != oldMain) {
                this._handleModeChange(oldMain, mainMode, oldExtended);
                liberator.triggerObserver("modeChange", [oldMain, oldExtended], [this._main, this._extended]);
                // liberator.log("Changing mode from " + oldMain + "/" + oldExtended + " to " + this._main + "/" + this._extended + "(" + this._getModeMessage() + ")");

                if (!silent)
                    this.show();
            }
        }

    },

    // TODO: Deprecate this in favor of addMode? --Kris
    //       Ya --djk
    setCustomMode: function (modestr, oneventfunc, stopfunc) {
        // TODO this.plugin[id]... ('id' maybe submode or what..)
        plugins.mode = modestr;
        plugins.onEvent = oneventfunc;
        plugins.stop = stopfunc;
    },

    passAllKeysExceptSome: function passAllKeysExceptSome(exceptions) {
        this._passAllKeys = true;
        this._passKeysExceptions = exceptions || [];
        this.show();
    },

    isKeyIgnored: function isKeyIgnored(key) {
        if (modes.passNextKey)
            return true;

        if (modes.passAllKeys) { // handle Escape-all-keys mode (Shift-Esc)
            // Respect "unignored" keys
            if (modes._passKeysExceptions == null || modes._passKeysExceptions.indexOf(key) < 0)
                return true;
            else
                return false;
            // TODO: "unignore" option
            // TODO: config.ignoreKeys support or remove config.ignoreKeys
        }

        return false;
    },

    // keeps recording state
    reset: function (silent) {
        this._modeStack = [];
        if (config.isComposeWindow)
            this.set(modes.COMPOSE, modes.NONE, silent);
        else
            this.set(modes.NORMAL, modes.NONE, silent);
    },

    remove: function (mode) {
        if (this._extended & mode) {
            this._extended &= ~mode;
            this.show();
        }
    },

    isMenuShown: false, // when a popup menu is active

    get passNextKey() this._passNextKey,
    set passNextKey(value) { this._passNextKey = value; this.show(); },

    get passAllKeys() this._passAllKeys,
    set passAllKeys(value) {
        this._passAllKeys = value;
        this._passKeysExceptions = null;
        this.show();
    },

    get isRecording()  this._isRecording,
    set isRecording(value) { this._isRecording = value; this.show(); },

    get isReplaying() this._isReplaying,
    set isReplaying(value) { this._isReplaying = value; this.show(); },

    get main() this._main,
    set main(value) { this.set(value); },

    get extended() this._extended,
    set extended(value) { this.set(null, value); }
});

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>fix white space<commit_after>// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the License.txt file included with this file.

/** @scope modules */

const Modes = Module("modes", {
    requires: ["config", "util"],

    init: function () {
        this._main = 1;     // NORMAL
        this._extended = 0; // NONE

        this._lastShown = null;

        this._passNextKey = false;
        this._passAllKeys = false;
        this._passKeysExceptions = null; // Which keys are still allowed in ignore mode. ONLY set when an :ignorekeys LocationChange triggered it
        this._isRecording = false;
        this._isReplaying = false; // playing a macro

        this._modeStack = [];

        this._mainModes = [this.NONE];
        this._lastMode = 0;
        this._modeMap = {};

        // main modes, only one should ever be active
        this.addMode("NORMAL",   { char: "n", display: -1 });
        this.addMode("INSERT",   { char: "i", input: true });
        this.addMode("VISUAL",   { char: "v", display: function () "VISUAL" + (editor.getVisualMode() ? " " + editor.getVisualMode() : "") });
        this.addMode("COMMAND_LINE", { char: "c", input: true, display: -1 });
        this.addMode("CARET"); // text cursor is visible
        this.addMode("TEXTAREA", { char: "i" });
        this.addMode("EMBED",    { input: true });
        this.addMode("CUSTOM",   { display: function () plugins.mode });
        // this._extended modes, can include multiple modes, and even main modes
        this.addMode("EX", true);
        this.addMode("HINTS", true);
        this.addMode("INPUT_MULTILINE", true);
        this.addMode("OUTPUT_MULTILINE", true);
        this.addMode("SEARCH_FORWARD", true);
        this.addMode("SEARCH_BACKWARD", true);
        this.addMode("SEARCH_VIEW_FORWARD", true);
        this.addMode("SEARCH_VIEW_BACKWARD", true);
        this.addMode("LINE", true); // linewise visual mode
        this.addMode("PROMPT", true);

        config.modes.forEach(function (mode) { this.addMode.apply(this, mode); }, this);
    },

    _getModeMessage: function () {
        if (this._passNextKey) {
            return "IGNORE";
        } else if (this._passAllKeys) {
            if (!this._passKeysExceptions || this._passKeysExceptions.length == 0)
                return "IGNORE ALL KEYS (Press <S-Esc> or <Insert> to exit)";
            else
                return "IGNORE MOST KEYS (All except " + this._passKeysExceptions + ")";
        }

        // when recording or replaying a macro
        if (modes.isRecording)
            return "RECORDING";
        else if (modes.isReplaying)
            return "REPLAYING";

        if (this._main in this._modeMap && typeof this._modeMap[this._main].display === "function")
            return this._modeMap[this._main].display();

        return ""; // default mode message
    },

    // NOTE: Pay attention that you don't run into endless loops
    // Usually you should only indicate to leave a special mode like HINTS
    // by calling modes.reset() and adding the stuff which is needed
    // for its cleanup here
    _handleModeChange: function (oldMode, newMode, oldExtended) {
        switch (oldMode) {
            case modes.TEXTAREA:
            case modes.INSERT:
                editor.unselectText();
                break;

            case modes.VISUAL:
                if (newMode == modes.CARET) {
                    try { // clear any selection made; a simple if (selection) does not work
                        let selection = Buffer.focusedWindow.getSelection();
                        selection.collapseToStart();
                    }
                    catch (e) {}
                }
                else
                    editor.unselectText();
                break;

            case modes.CUSTOM:
                plugins.stop();
                break;

            case modes.COMMAND_LINE:
                // clean up for HINT mode
                if (oldExtended & modes.HINTS)
                    hints.hide();
                commandline.close();
                break;
        }

        if (newMode == modes.NORMAL) {
            // disable caret mode when we want to switch to normal mode
            if (options.getPref("accessibility.browsewithcaret"))
                options.setPref("accessibility.browsewithcaret", false);

            if (oldMode == modes.COMMAND_LINE)
                liberator.focusContent(false); // when coming from the commandline, we might want to keep the focus (after a Search, e.g.)
            else
                liberator.focusContent(true); // unfocus textareas etc.
        }
    },

    NONE: 0,

    __iterator__: function () util.Array.itervalues(this.all),

    get all() this._mainModes.slice(),

    get mainModes() (mode for ([k, mode] in Iterator(modes._modeMap)) if (!mode.extended && mode.name == k)),

    get mainMode() this._modeMap[this._main],

    addMode: function (name, extended, options) {
        let disp = name.replace("_", " ", "g");
        this[name] = 1 << this._lastMode++;
        if (typeof extended == "object") {
            options = extended;
            extended = false;
        }
        this._modeMap[name] = this._modeMap[this[name]] = util.extend({
            extended: extended,
            count: true,
            input: false,
            mask: this[name],
            name: name,
            disp: disp
        }, options);
        this._modeMap[name].display = this._modeMap[name].display || function () disp;
        if (!extended)
            this._mainModes.push(this[name]);
        if ("mappings" in modules)
            mappings.addMode(this[name]);
    },

    getMode: function (name) this._modeMap[name],

    getCharModes: function (chr) [m for (m in values(this._modeMap)) if (m.char == chr)],

    matchModes: function (obj) [m for (m in values(this._modeMap)) if (array(keys(obj)).every(function (k) obj[k] == (m[k] || false)))],

    // show the current mode string in the command line
    show: function () {
        if (options["showmode"])
            commandline.setModeMessage(this._getModeMessage());
    },

    // add/remove always work on the this._extended mode only
    add: function (mode) {
        this._extended |= mode;
        this.show();
    },

    // helper function to set both modes in one go
    // if silent == true, you also need to take care of the mode handling changes yourself
    set: function (mainMode, extendedMode, silent, stack) {
        silent = (silent || this._main == mainMode && this._extended == extendedMode);
        // if a this._main mode is set, the this._extended is always cleared
        let oldMain = this._main, oldExtended = this._extended;
        if (typeof extendedMode === "number")
            this._extended = extendedMode;
        if (typeof mainMode === "number") {
            this._main = mainMode;
            if (!extendedMode)
                this._extended = modes.NONE;

            if (this._main != oldMain) {
                this._handleModeChange(oldMain, mainMode, oldExtended);
                liberator.triggerObserver("modeChange", [oldMain, oldExtended], [this._main, this._extended]);
                // liberator.log("Changing mode from " + oldMain + "/" + oldExtended + " to " + this._main + "/" + this._extended + "(" + this._getModeMessage() + ")");

                if (!silent)
                    this.show();
            }
        }

    },

    // TODO: Deprecate this in favor of addMode? --Kris
    //       Ya --djk
    setCustomMode: function (modestr, oneventfunc, stopfunc) {
        // TODO this.plugin[id]... ('id' maybe submode or what..)
        plugins.mode = modestr;
        plugins.onEvent = oneventfunc;
        plugins.stop = stopfunc;
    },

    passAllKeysExceptSome: function passAllKeysExceptSome(exceptions) {
        this._passAllKeys = true;
        this._passKeysExceptions = exceptions || [];
        this.show();
    },

    isKeyIgnored: function isKeyIgnored(key) {
        if (modes.passNextKey)
            return true;

        if (modes.passAllKeys) { // handle Escape-all-keys mode (Shift-Esc)
            // Respect "unignored" keys
            if (modes._passKeysExceptions == null || modes._passKeysExceptions.indexOf(key) < 0)
                return true;
            else
                return false;
            // TODO: "unignore" option
            // TODO: config.ignoreKeys support or remove config.ignoreKeys
        }

        return false;
    },

    // keeps recording state
    reset: function (silent) {
        this._modeStack = [];
        if (config.isComposeWindow)
            this.set(modes.COMPOSE, modes.NONE, silent);
        else
            this.set(modes.NORMAL, modes.NONE, silent);
    },

    remove: function (mode) {
        if (this._extended & mode) {
            this._extended &= ~mode;
            this.show();
        }
    },

    isMenuShown: false, // when a popup menu is active

    get passNextKey() this._passNextKey,
    set passNextKey(value) { this._passNextKey = value; this.show(); },

    get passAllKeys() this._passAllKeys,
    set passAllKeys(value) {
        this._passAllKeys = value;
        this._passKeysExceptions = null;
        this.show();
    },

    get isRecording()  this._isRecording,
    set isRecording(value) { this._isRecording = value; this.show(); },

    get isReplaying() this._isReplaying,
    set isReplaying(value) { this._isReplaying = value; this.show(); },

    get main() this._main,
    set main(value) { this.set(value); },

    get extended() this._extended,
    set extended(value) { this.set(null, value); }
});

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>var uuid = require("tiny-uuid4");

module.exports.get = {
	"/": ["empty", "items", "somethings", "test", "things", "users", "uuid"],
	"/empty": [],
	"/items(\/?)": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
	"/test": "Get to the chopper!",
	"/things": [{id: 1, name: "thing 1", user_id: 1}, {id: 2, name: "thing 2", user_id: 1}, {
		id: 3,
		name: "thing 3",
		user_id: 2
	}],
	"/somethings": ["abc", "def"],
	"/somethings/abc": {
		"_id": "abc",
		"user_id": 123,
		"title": "This is a title",
		"body": "Where is my body?",
		"source_url": "http://source.tld"
	},
	"/somethings/def": {"_id": "def", "user_id": 123, "source_url": "http://source.tld"},
	"/users": [123],
	"/users/123": {"_id": 123, firstName: "Jason", lastName: "Mulligan"},
	"/uuid": function (req, res) {
		this.respond(req, res, uuid());
	}
};

module.exports.post = {
	"/test": function (req, res) {
		this.respond(req, res, "OK!");
	}
};

module.exports.socket = {
	connection: function (socket, server) {
		console.log('[Connection]');
		server.send(socket, new Buffer('a text message'), false);
		server.send(socket, new Buffer('a binary message'), true);
		server.setUserData(socket, 'persistent per socket data');
	},
	message: function (socket, message, binary, server) {
		console.log('[Message: ' + message + ']');
		server.send(socket, new Buffer('You sent me this: \"' + message + '\"'), false);
	},
	close: function () {
		console.log('[Closed connection]');
	}
};

module.exports.coap = {
	request: function (req, res) {
		res.end('Hello World!');
	}
};
<commit_msg>Updating test with a parameter route<commit_after>var uuid = require("tiny-uuid4");

module.exports.get = {
	"/": [
		"empty",
		"items",
		"somethings",
		"test",
		"things",
		"users",
		"uuid"
	],
	"/empty": [],
	"/items(\/?)": [
		1,
		2,
		3,
		4,
		5,
		6,
		7,
		8,
		9,
		10,
		11,
		12,
		13,
		14,
		15
	],
	"/test": "Get to the chopper!",
	"/test/:hidden": function (req, res) {
		res.send(req.params.hidden);
	},
	"/things": [
		{
			id: 1,
			name: "thing 1",
			user_id: 1
		}, {
			id: 2,
			name: "thing 2",
			user_id: 1
		}, {
			id: 3,
			name: "thing 3",
			user_id: 2
		}
	],
	"/somethings": [
		"abc",
		"def"
	],
	"/somethings/abc": {
		"_id": "abc",
		"user_id": 123,
		"title": "This is a title",
		"body": "Where is my body?",
		"source_url": "http://source.tld"
	},
	"/somethings/def": {
		"_id": "def",
		"user_id": 123,
		"source_url": "http://source.tld"
	},
	"/users": [
		123
	],
	"/users/123": {
		"_id": 123,
		firstName: "Jason",
		lastName: "Mulligan"
	},
	"/uuid": function (req, res) {
		this.respond(req, res, uuid());
	}
};

module.exports.post = {
	"/test": function (req, res) {
		this.respond(req, res, "OK!");
	}
};

module.exports.socket = {
	connection: function (socket, server) {
		console.log('[Connection]');
		server.send(socket, new Buffer('a text message'), false);
		server.send(socket, new Buffer('a binary message'), true);
		server.setUserData(socket, 'persistent per socket data');
	},
	message: function (socket, message, binary, server) {
		console.log('[Message: ' + message + ']');
		server.send(socket, new Buffer('You sent me this: \"' + message + '\"'), false);
	},
	close: function () {
		console.log('[Closed connection]');
	}
};

module.exports.coap = {
	request: function (req, res) {
		res.end('Hello World!');
	}
};
<|endoftext|>


<commit_before>import { obj } from "lively.lang";

class EvalStrategy {

  async runEval(source, options) {
    return Promise.reject(`runEval(source, options) not yet implemented for ${this.constructor.name}`);
  }

  async keysOfObject(prefix, options) {
    return Promise.reject(`keysOfObject(prefix, options) not yet implemented for ${this.constructor.name}`);
  }

}

class SimpleEvalStrategy extends EvalStrategy {

  async runEval(source, options) {
    return Promise.resolve().then(() => {
      try {
        return Promise.resolve({value: eval(source)});
      } catch (err) {
        return {isError: true, value: err}
      }
    });
  }

  async keysOfObject(prefix, options) {
    // for dynamic object completions
    var result = await lively.vm.completions.getCompletions(
      code => this.runEval(code, options), prefix);
    return {completions: result.completions, prefix: result.startLetters};
  }

}

class LivelyVmEvalStrategy extends EvalStrategy {

  normalizeOptions(options) {
    if (!options.targetModule)
      throw new Error("runEval called but options.targetModule not specified!");

    return Object.assign({
      sourceURL: options.targetModule + "_doit_" + Date.now(),
    }, options);
  }

  async runEval(source, options) {
    options = this.normalizeOptions(options)
    var System = options.System || lively.modules.System;
    System.config({meta: {[options.targetModule]: {format: "esm"}}});
    return lively.vm.runEval(source, options);
  }

  async keysOfObject(prefix, options) {
    // for dynamic object completions
    var result = await lively.vm.completions.getCompletions(
      code => lively.vm.runEval(code, options), prefix);
    return {completions: result.completions, prefix: result.startLetters}
  }

}

export class RemoteEvalStrategy extends LivelyVmEvalStrategy {

  sourceForRemote(action, arg, options) {
    options = obj.dissoc(options, ["systemInterface", "System", "context"]);
    return `
(function() {
  var arg = ${JSON.stringify(arg)},
      options = ${JSON.stringify(options)};
  if (typeof lively === "undefined" || !lively.vm) {
    return Promise.resolve({
      isEvalResult: true,
      isError: true,
      value: 'lively.vm not available!'
    });
  }
  var hasSystem = typeof System !== "undefined"
  options.context = hasSystem
    ? System.global
    : typeof window !== "undefined"
        ? window
        : typeof global !== "undefined"
            ? global
            : typeof self !== "undefined" ? self : this;
  function evalFunction(source, options) {
    if (hasSystem) {
      var conf = {meta: {}}; conf.meta[options.targetModule] = {format: "esm"};
      System.config(conf);
    } else {
      options = Object.assign({topLevelVarRecorderName: "GLOBAL"}, options);
      delete options.targetModule;
    }
    return lively.vm.runEval(source, options);
  }
  function keysOfObjectFunction(prefix, options) {
    return lively.vm.completions.getCompletions(code => evalFunction(code, options), prefix)
      .then(result => ({isEvalResult: true, completions: result.completions, prefix: result.startLetters}));
  }
  return ${action === "eval" ? "evalFunction" : "keysOfObjectFunction"}(arg, options)
    .catch(err => ({isEvalResult: true, isError: true, value: String(err.stack || err)}));
})();
`;
  }

  async runEval(source, options) {
    return this.remoteEval(this.sourceForRemote("eval", source, options), options)
  }

  async keysOfObject(prefix, options) {
    return this.remoteEval(this.sourceForRemote("keysOfObject", prefix, options), options)
  }

  async remoteEval(source, options) {
    try {
      var result = await this.basicRemoteEval(source, options)
      return typeof result === "string" ? JSON.parse(result) : result;
    } catch (e) {
      return {isError: true, value: `Remote eval failed: ${result || e}`};
    }
  }

  async basicRemoteEval(source, options) {
    throw new Error("Not yet implemented");
  }

}

class HttpEvalStrategy extends RemoteEvalStrategy {

  static get defaultURL() { return "http://localhost:3000/lively" }

  constructor(url) {
    super();
    this.url = url || this.constructor.defaultURL;
  }

  normalizeOptions(options) {
    options = super.normalizeOptions(options);
    return Object.assign(
      {serverEvalURL: this.url},
      options,
      {context: null});
  }

  async basicRemoteEval(source, options) {
    options = this.normalizeOptions(options);
    var method = "basicRemoteEval_" + (System.get("@system-env").node ? "node" : "web");
    return await this[method]({method: "POST", body: source}, options.serverEvalURL);
  }

  async basicRemoteEval_web(payload, url) {
    var res;
    try {
      res = await window.fetch(url, payload);
    } catch (e) {
      throw new Error(`Cannot reach server at ${url}: ${e.message}`)
    }

    if (!res.ok) {
      throw new Error(`Server at ${url}: ${res.statusText}`)
    }

    return res.text();
  }

  async basicRemoteEval_node(payload, url) {
    var urlParse = System._nodeRequire("url").parse,
        http = System._nodeRequire("http"),
        opts = Object.assign({method: payload.method || "GET"}, urlParse(url));
    return new Promise((resolve, reject) => {
      var request = http.request(opts, res => {
        res.setEncoding('utf8');
        var data = "";
        res.on('data', (chunk) => data += chunk);
        res.on('end', () => resolve(data));
        res.on('error', err => reject(err));
      })
      request.on('error', err => reject(err));
      request.end(payload.body)
    });
  }

}

class L2LEvalStrategy extends RemoteEvalStrategy {

  constructor(l2lClient, targetId) {
    super();
    this.l2lClient = l2lClient;
    this.targetId = targetId;
  }

  async basicRemoteEval(source, options) {
    let {l2lClient, targetId} = this,
        {data: evalResult} = await new Promise((resolve, reject) =>
          l2lClient.sendTo(targetId, "remote-eval", {source}, resolve));
    if (evalResult && evalResult.value && evalResult.value.isEvalResult)
      evalResult = evalResult.value;
    return evalResult;
  }

}

export {
  EvalStrategy,
  SimpleEvalStrategy,
  LivelyVmEvalStrategy,
  HttpEvalStrategy,
  L2LEvalStrategy
}
<commit_msg>use lively proxy for cross-domain eval... needs an option<commit_after>import { obj } from "lively.lang";

class EvalStrategy {

  async runEval(source, options) {
    return Promise.reject(`runEval(source, options) not yet implemented for ${this.constructor.name}`);
  }

  async keysOfObject(prefix, options) {
    return Promise.reject(`keysOfObject(prefix, options) not yet implemented for ${this.constructor.name}`);
  }

}

class SimpleEvalStrategy extends EvalStrategy {

  async runEval(source, options) {
    return Promise.resolve().then(() => {
      try {
        return Promise.resolve({value: eval(source)});
      } catch (err) {
        return {isError: true, value: err}
      }
    });
  }

  async keysOfObject(prefix, options) {
    // for dynamic object completions
    var result = await lively.vm.completions.getCompletions(
      code => this.runEval(code, options), prefix);
    return {completions: result.completions, prefix: result.startLetters};
  }

}

class LivelyVmEvalStrategy extends EvalStrategy {

  normalizeOptions(options) {
    if (!options.targetModule)
      throw new Error("runEval called but options.targetModule not specified!");

    return Object.assign({
      sourceURL: options.targetModule + "_doit_" + Date.now(),
    }, options);
  }

  async runEval(source, options) {
    options = this.normalizeOptions(options)
    var System = options.System || lively.modules.System;
    System.config({meta: {[options.targetModule]: {format: "esm"}}});
    return lively.vm.runEval(source, options);
  }

  async keysOfObject(prefix, options) {
    // for dynamic object completions
    var result = await lively.vm.completions.getCompletions(
      code => lively.vm.runEval(code, options), prefix);
    return {completions: result.completions, prefix: result.startLetters}
  }

}

export class RemoteEvalStrategy extends LivelyVmEvalStrategy {

  sourceForRemote(action, arg, options) {
    options = obj.dissoc(options, ["systemInterface", "System", "context"]);
    return `
(function() {
  var arg = ${JSON.stringify(arg)},
      options = ${JSON.stringify(options)};
  if (typeof lively === "undefined" || !lively.vm) {
    return Promise.resolve({
      isEvalResult: true,
      isError: true,
      value: 'lively.vm not available!'
    });
  }
  var hasSystem = typeof System !== "undefined"
  options.context = hasSystem
    ? System.global
    : typeof window !== "undefined"
        ? window
        : typeof global !== "undefined"
            ? global
            : typeof self !== "undefined" ? self : this;
  function evalFunction(source, options) {
    if (hasSystem) {
      var conf = {meta: {}}; conf.meta[options.targetModule] = {format: "esm"};
      System.config(conf);
    } else {
      options = Object.assign({topLevelVarRecorderName: "GLOBAL"}, options);
      delete options.targetModule;
    }
    return lively.vm.runEval(source, options);
  }
  function keysOfObjectFunction(prefix, options) {
    return lively.vm.completions.getCompletions(code => evalFunction(code, options), prefix)
      .then(result => ({isEvalResult: true, completions: result.completions, prefix: result.startLetters}));
  }
  return ${action === "eval" ? "evalFunction" : "keysOfObjectFunction"}(arg, options)
    .catch(err => ({isEvalResult: true, isError: true, value: String(err.stack || err)}));
})();
`;
  }

  async runEval(source, options) {
    return this.remoteEval(this.sourceForRemote("eval", source, options), options)
  }

  async keysOfObject(prefix, options) {
    return this.remoteEval(this.sourceForRemote("keysOfObject", prefix, options), options)
  }

  async remoteEval(source, options) {
    try {
      var result = await this.basicRemoteEval(source, options)
      return typeof result === "string" ? JSON.parse(result) : result;
    } catch (e) {
      return {isError: true, value: `Remote eval failed: ${result || e}`};
    }
  }

  async basicRemoteEval(source, options) {
    throw new Error("Not yet implemented");
  }

}

class HttpEvalStrategy extends RemoteEvalStrategy {

  static get defaultURL() { return "http://localhost:3000/lively" }

  constructor(url) {
    super();
    this.url = url || this.constructor.defaultURL;
  }

  normalizeOptions(options) {
    options = super.normalizeOptions(options);
    return Object.assign(
      {serverEvalURL: this.url},
      options,
      {context: null});
  }

  async basicRemoteEval(source, options) {
    options = this.normalizeOptions(options);
    var method = "basicRemoteEval_" + (System.get("@system-env").node ? "node" : "web");
    return await this[method]({method: "POST", body: source}, options.serverEvalURL);
  }

  async basicRemoteEval_web(payload, url) {
    let [domain] = url.match(/[^:]+:\/\/[^\/]+/) || [url],
        crossDomain = document.location.origin !== domain;

    if (crossDomain) { // use lively.server proxy plugin
      payload.headers = {
        ...payload.headers,
        'pragma': 'no-cache',
        'cache-control': 'no-cache',
        "x-lively-proxy-request": url
      }
      url = document.location.origin;
    }

    var res;
    try {
      res = await window.fetch(url, payload);
    } catch (e) {
      throw new Error(`Cannot reach server at ${url}: ${e.message}`)
    }

    if (!res.ok) {
      throw new Error(`Server at ${url}: ${res.statusText}`)
    }

    return res.text();
  }

  async basicRemoteEval_node(payload, url) {
    var urlParse = System._nodeRequire("url").parse,
        http = System._nodeRequire("http"),
        opts = Object.assign({method: payload.method || "GET"}, urlParse(url));
    return new Promise((resolve, reject) => {
      var request = http.request(opts, res => {
        res.setEncoding('utf8');
        var data = "";
        res.on('data', (chunk) => data += chunk);
        res.on('end', () => resolve(data));
        res.on('error', err => reject(err));
      })
      request.on('error', err => reject(err));
      request.end(payload.body)
    });
  }

}

class L2LEvalStrategy extends RemoteEvalStrategy {

  constructor(l2lClient, targetId) {
    super();
    this.l2lClient = l2lClient;
    this.targetId = targetId;
  }

  async basicRemoteEval(source, options) {
    let {l2lClient, targetId} = this,
        {data: evalResult} = await new Promise((resolve, reject) =>
          l2lClient.sendTo(targetId, "remote-eval", {source}, resolve));
    if (evalResult && evalResult.value && evalResult.value.isEvalResult)
      evalResult = evalResult.value;
    return evalResult;
  }

}

export {
  EvalStrategy,
  SimpleEvalStrategy,
  LivelyVmEvalStrategy,
  HttpEvalStrategy,
  L2LEvalStrategy
}
<|endoftext|>


<commit_before>/**
 * Sitespeed.io - How speedy is your site? (http://www.sitespeed.io)
 * Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
 * and other contributors
 * Released under the Apache 2.0 License
 */
'use strict';
var util = require('../util/util'),
	winston = require('winston'),
	net = require('net');

function GraphiteCollector(config) {
	this.config = config;
	this.namespace = this.config.graphiteNamespace;
	this.log = winston.loggers.get('sitespeed.io');
	this.timeStamp = ' ' + Math.round(new Date().getTime() / 1000) + '\n';
}

GraphiteCollector.prototype.collect = function(aggregates, pages, domains) {

	var config = this.config;
	var self = this;
	var statistics = '';
	pages.forEach(function(page) {
		statistics += self._getPageStats(page);
	});

	if (this.config.graphiteData.indexOf('summary') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		statistics += this._getSummaryStats(aggregates, config.urlObject.hostname);
	}

	statistics += self._getDomainStats(domains, config.urlObject.hostname);

	return statistics;

};

GraphiteCollector.prototype._getPageStats = function(page) {

	var statistics = '';

	var urlKey = util.getGraphiteURLKey(decodeURIComponent(page.url));

	// lets collect the specific data per
	statistics += this._getRuleStats(page, urlKey);
	statistics += this._getTimingStats(page, urlKey);
	statistics += this._getPageMetricsStats(page, urlKey);
	statistics += this._getGPSIStats(page, urlKey);
	statistics += this._getWPTStats(page, urlKey);
	// statistics += this._getAssetsStats(page, urlKey);
	return statistics;

};

GraphiteCollector.prototype._getRuleStats = function(page, urlKey) {

	var statistics = '';
	var self = this;
	if (page.yslow && (this.config.graphiteData.indexOf('rules') > -1 || this.config.graphiteData.indexOf(
			'all') > -1)) {
		Object.keys(page.rules).forEach(function(rule) {
			statistics += self.namespace + '.' + urlKey + 'rules.' + rule + ' ' +
				page.rules[rule].v + self.timeStamp;
		});
	}

	return statistics;
};

GraphiteCollector.prototype._getTimingStats = function(page, urlKey) {

	var statistics = '';
	var timingsWeWillPush = ['min', 'median', 'p90', 'max'];
	var self = this;

	// the timings that are not browser specific
	if (this.config.graphiteData.indexOf('timings') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		// check that we actually collect browser data
		if (page.timings) {
			Object.keys(page.timings).forEach(function(timing) {
				timingsWeWillPush.forEach(function(val) {
					// is it a browser?
					if (self.config.supportedBrowsers.indexOf(timing) < 0) {
						statistics += self.namespace + '.' + urlKey + 'timings.' + timing +
							'.' + val + ' ' + page.timings[timing][val].v + self.timeStamp;
					}
				});
			});

			// and the browsers
			Object.keys(page.timings).forEach(function(browser) {
				if (self.config.supportedBrowsers.indexOf(browser) > -1) {
					Object.keys(page.timings[browser]).forEach(function(timing) {
						timingsWeWillPush.forEach(function(val) {
							statistics += self.namespace + '.' + urlKey + 'timings.' +
								browser + '.' + timing + '.' + val + ' ' + page.timings[
									browser][timing][val].v + self.timeStamp;
						});
					});
				}
			});
		}
	}
	return statistics;
};

GraphiteCollector.prototype._getPageMetricsStats = function(page, urlKey) {

	var statistics = '';
	var self = this;

	if (this.config.graphiteData.indexOf('pagemetrics') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		// and all the assets
		if (page.yslow) {
			Object.keys(page.yslow.assets).forEach(function(asset) {
				statistics += self.namespace + '.' + urlKey + 'assets.' + asset +
					' ' + page.yslow.assets[asset].v + self.timeStamp;
			});

			// and page specific
			statistics += self.namespace + '.' + urlKey + 'score' + ' ' + page.score +
				self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'requests' + ' ' + page.yslow
				.requests.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'requestsMissingExpire' +
				' ' + page.yslow.requestsMissingExpire.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey +
				'timeSinceLastModification' + ' ' + page.yslow.timeSinceLastModification
				.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'cacheTime' + ' ' + page.yslow
				.cacheTime.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'pageWeight' + ' ' + page.yslow
				.pageWeight.v + self.timeStamp;
		}
	}

	return statistics;
};

GraphiteCollector.prototype._getGPSIStats = function(page, urlKey) {
	// add gspi score
	if (page.gpsi) {
		return this.namespace + '.' + urlKey + 'gpsi' + ' ' + page.gpsi.gscore.v + this.timeStamp;
	} else {
		return '';
	}
};

GraphiteCollector.prototype._getWPTStats = function(page, urlKey) {

	var statistics = '';
	var self = this;
	// add wpt data
	if (page.wpt) {
		Object.keys(page.wpt).forEach(function(location) {
			Object.keys(page.wpt[location]).forEach(function(browser) {
				Object.keys(page.wpt[location][browser]).forEach(function(connectivity) {
					Object.keys(page.wpt[location][browser][connectivity]).forEach(function(view) {
						Object.keys(page.wpt[location][browser][connectivity][view]).forEach(function(metric) {
							statistics += self.namespace + '.' + urlKey + 'wpt.' + location + '.' +
								connectivity + '.' + browser + '.' + view + '.' + metric + '.median ' +
								page.wpt[location][browser][connectivity][view][metric].v +
								self.timeStamp;
						});
					});
				});
			});
		});
	}

	return statistics;
};

// maybe later we want to send specific asset data
/*
GraphiteCollector.prototype._getAssetsStats = function (page, urlKey) {

  // TODO separate runs
  var stats = '';
  var self = this;
  page.har.forEach(function(har) {
    har.log.entries.forEach(function(entry) {
      var assetURL =  util.getGraphiteURLKey(decodeURIComponent(entry.request.url));
      var total;
        if (entry.timings) {
          total = entry.timings.blocked + entry.timings.dns + entry.timings.connect + entry.timings.ssl +
          entry.timings
              .send + entry.timings.wait + entry.timings.receive;

        stats +=  self.namespace + '.' + urlKey + 'assets.' +  assetURL + ' ' + total + self.timeStamp;

        }
  });

});
  return stats;
};
*/

GraphiteCollector.prototype._getDomainStats = function(domains, hostname) {

	var stats = '';
	if (domains) {

		var self = this;
		var timings = ['blocked', 'dns', 'connect', 'ssl', 'send', 'wait', 'receive', 'total'];
		var values = ['min', 'median', 'max'];

		domains.forEach(function(domain) {
			timings.forEach(function(timing) {
				values.forEach(function(value) {
					stats += self.namespace + '.summary.' + hostname + '.domains.timings.' + domain.domain + '.' + timing + '.' +
						value + ' ' + util.getStatisticsObject(domain[timing].stats, 0)[value] + self.timeStamp;
          				});
			});
			stats += self.namespace + '.summary.' + hostname + '.domains.' + domain.domain + '.requests' + ' ' + domain.count +
				self.timeStamp;
		});
	}

	return stats;
};

GraphiteCollector.prototype._getSummaryStats = function(aggregates, hostname) {
	var statistics = '';
	var self = this;
	var values = ['min', 'p10', 'median', 'mean', 'p90', 'p99', 'max'];

	aggregates.forEach(function(aggregate) {
		values.forEach(function(value) {
			// special handling for WPT values for now
			if (aggregate.id.indexOf('WPT') > -1) {
				statistics += self.namespace + '.summary.' + hostname + '.' + aggregate.type + '.wpt.' +
					aggregate.key + '.' + value + ' ' + aggregate.stats[value] + self.timeStamp;

			} else {
				statistics += self.namespace + '.summary.' + hostname + '.' + aggregate.type + '.' + aggregate.id +
					'.' + value + ' ' + aggregate.stats[value] + self.timeStamp;
			}
		});
	});
	return statistics;
};

module.exports = GraphiteCollector;
<commit_msg>structure<commit_after>/**
 * Sitespeed.io - How speedy is your site? (http://www.sitespeed.io)
 * Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
 * and other contributors
 * Released under the Apache 2.0 License
 */
'use strict';
var util = require('../util/util'),
	winston = require('winston'),
	net = require('net');

function GraphiteCollector(config) {
	this.config = config;
	this.namespace = this.config.graphiteNamespace;
	this.log = winston.loggers.get('sitespeed.io');
	this.timeStamp = ' ' + Math.round(new Date().getTime() / 1000) + '\n';
}

GraphiteCollector.prototype.collect = function(aggregates, pages, domains) {

	var config = this.config;
	var self = this;
	var statistics = '';
	pages.forEach(function(page) {
		statistics += self._getPageStats(page);
	});

	if (this.config.graphiteData.indexOf('summary') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		statistics += this._getSummaryStats(aggregates, config.urlObject.hostname);
	}

	statistics += self._getDomainStats(domains, config.urlObject.hostname);

	return statistics;

};

GraphiteCollector.prototype._getPageStats = function(page) {

	var statistics = '';

	var urlKey = util.getGraphiteURLKey(decodeURIComponent(page.url));

	// lets collect the specific data per
	statistics += this._getRuleStats(page, urlKey);
	statistics += this._getTimingStats(page, urlKey);
	statistics += this._getPageMetricsStats(page, urlKey);
	statistics += this._getGPSIStats(page, urlKey);
	statistics += this._getWPTStats(page, urlKey);
	// statistics += this._getAssetsStats(page, urlKey);
	return statistics;

};

GraphiteCollector.prototype._getRuleStats = function(page, urlKey) {

	var statistics = '';
	var self = this;
	if (page.yslow && (this.config.graphiteData.indexOf('rules') > -1 || this.config.graphiteData.indexOf(
			'all') > -1)) {
		Object.keys(page.rules).forEach(function(rule) {
			statistics += self.namespace + '.' + urlKey + 'rules.' + rule + ' ' +
				page.rules[rule].v + self.timeStamp;
		});
	}

	return statistics;
};

GraphiteCollector.prototype._getTimingStats = function(page, urlKey) {

	var statistics = '';
	var timingsWeWillPush = ['min', 'median', 'p90', 'max'];
	var self = this;

	// the timings that are not browser specific
	if (this.config.graphiteData.indexOf('timings') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		// check that we actually collect browser data
		if (page.timings) {
			Object.keys(page.timings).forEach(function(timing) {
				timingsWeWillPush.forEach(function(val) {
					// is it a browser?
					if (self.config.supportedBrowsers.indexOf(timing) < 0) {
						statistics += self.namespace + '.' + urlKey + 'timings.' + timing +
							'.' + val + ' ' + page.timings[timing][val].v + self.timeStamp;
					}
				});
			});

			// and the browsers
			Object.keys(page.timings).forEach(function(browser) {
				if (self.config.supportedBrowsers.indexOf(browser) > -1) {
					Object.keys(page.timings[browser]).forEach(function(timing) {
						timingsWeWillPush.forEach(function(val) {
							statistics += self.namespace + '.' + urlKey + 'timings.' +
								browser + '.' + timing + '.' + val + ' ' + page.timings[
									browser][timing][val].v + self.timeStamp;
						});
					});
				}
			});
		}
	}
	return statistics;
};

GraphiteCollector.prototype._getPageMetricsStats = function(page, urlKey) {

	var statistics = '';
	var self = this;

	if (this.config.graphiteData.indexOf('pagemetrics') > -1 || this.config.graphiteData.indexOf(
			'all') > -1) {
		// and all the assets
		if (page.yslow) {
			Object.keys(page.yslow.assets).forEach(function(asset) {
				statistics += self.namespace + '.' + urlKey + 'assets.' + asset +
					' ' + page.yslow.assets[asset].v + self.timeStamp;
			});

			// and page specific
			statistics += self.namespace + '.' + urlKey + 'score' + ' ' + page.score +
				self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'requests' + ' ' + page.yslow
				.requests.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'requestsMissingExpire' +
				' ' + page.yslow.requestsMissingExpire.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey +
				'timeSinceLastModification' + ' ' + page.yslow.timeSinceLastModification
				.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'cacheTime' + ' ' + page.yslow
				.cacheTime.v + self.timeStamp;
			statistics += self.namespace + '.' + urlKey + 'pageWeight' + ' ' + page.yslow
				.pageWeight.v + self.timeStamp;
		}
	}

	return statistics;
};

GraphiteCollector.prototype._getGPSIStats = function(page, urlKey) {
	// add gspi score
	if (page.gpsi) {
		return this.namespace + '.' + urlKey + 'gpsi' + ' ' + page.gpsi.gscore.v + this.timeStamp;
	} else {
		return '';
	}
};

GraphiteCollector.prototype._getWPTStats = function(page, urlKey) {

	var statistics = '';
	var self = this;
	// add wpt data
	if (page.wpt) {
		Object.keys(page.wpt).forEach(function(location) {
			Object.keys(page.wpt[location]).forEach(function(browser) {
				Object.keys(page.wpt[location][browser]).forEach(function(connectivity) {
					Object.keys(page.wpt[location][browser][connectivity]).forEach(function(view) {
						Object.keys(page.wpt[location][browser][connectivity][view]).forEach(function(metric) {
							statistics += self.namespace + '.' + urlKey + 'wpt.' + location + '.' +
								connectivity + '.' + browser + '.' + view + '.' + metric + '.median ' +
								page.wpt[location][browser][connectivity][view][metric].v +
								self.timeStamp;
						});
					});
				});
			});
		});
	}

	return statistics;
};

// maybe later we want to send specific asset data
/*
GraphiteCollector.prototype._getAssetsStats = function (page, urlKey) {

  // TODO separate runs
  var stats = '';
  var self = this;
  page.har.forEach(function(har) {
    har.log.entries.forEach(function(entry) {
      var assetURL =  util.getGraphiteURLKey(decodeURIComponent(entry.request.url));
      var total;
        if (entry.timings) {
          total = entry.timings.blocked + entry.timings.dns + entry.timings.connect + entry.timings.ssl +
          entry.timings
              .send + entry.timings.wait + entry.timings.receive;

        stats +=  self.namespace + '.' + urlKey + 'assets.' +  assetURL + ' ' + total + self.timeStamp;

        }
  });

});
  return stats;
};
*/

GraphiteCollector.prototype._getDomainStats = function(domains, hostname) {

	var stats = '';
	if (domains) {

		var self = this;
		var timings = ['blocked', 'dns', 'connect', 'ssl', 'send', 'wait', 'receive', 'total'];
		var values = ['min', 'median', 'max'];

		domains.forEach(function(domain) {
			timings.forEach(function(timing) {
				values.forEach(function(value) {
					stats += self.namespace + '.summary.' + hostname + '.domains.timings.' + domain.domain + '.' + timing + '.' +
						value + ' ' + util.getStatisticsObject(domain[timing].stats, 0)[value] + self.timeStamp;
          });
			});
			stats += self.namespace + '.summary.' + hostname + '.domains.' + domain.domain + '.requests' + ' ' + domain.count +
				self.timeStamp;
		});
	}

	return stats;
};

GraphiteCollector.prototype._getSummaryStats = function(aggregates, hostname) {
	var statistics = '';
	var self = this;
	var values = ['min', 'p10', 'median', 'mean', 'p90', 'p99', 'max'];

	aggregates.forEach(function(aggregate) {
		values.forEach(function(value) {
			// special handling for WPT values for now
			if (aggregate.id.indexOf('WPT') > -1) {
				statistics += self.namespace + '.summary.' + hostname + '.' + aggregate.type + '.wpt.' +
					aggregate.key + '.' + value + ' ' + aggregate.stats[value] + self.timeStamp;

			} else {
				statistics += self.namespace + '.summary.' + hostname + '.' + aggregate.type + '.' + aggregate.id +
					'.' + value + ' ' + aggregate.stats[value] + self.timeStamp;
			}
		});
	});
	return statistics;
};

module.exports = GraphiteCollector;
<|endoftext|>


<commit_before>// node test/rundir.js "path/to/files/*.*"

const fs = require('fs');
const Streat = require('../');

const streat = new Streat();
streat.start();

const pattern = process.argv.pop();

const glob = require('glob');

glob(pattern, {}, (err, files) => {
	Promise.all(files.map(file => {
		console.info(file);
		return new Promise(resolve => {
			streat.run(fs.createReadStream(file, {
				encoding: 'binary'
			}), (err, tags) => {
				if (err) console.error(err);
				console.info("got", Object.keys(tags).length, "tags");
				if (tags.Error) console.error("exiftool returns Error", tags.Error);
				resolve();
			});
		});
	})).catch((err) => {
		console.error("interrupted because of", err);
	}).then(() => {
		streat.stop();
		console.info("done");
	});
});

<commit_msg>Drop that useless test<commit_after><|endoftext|>