<commit_before>var spawn = require('child_process').spawn;
var BufferStream = require('bufferstream')
var regexp = /(\d+:\d+:\d+)\s?([^\s]+)\s?([^\s]+)\s?\[([^\]]+)\]([^\s]+)\s?(.*)/;
var events = require('events');
var util = require('util');

var objToArray = function(obj) {
  var args = [];
  for (var i in this.config) {
    var cur = this.config[i];
    cur = cur.split(' ');
    args = args.concat(cur);
  }

  return args;
};

var _onResponse = function(line) {
  var res = regexp.exec(line);
  if (res && res.length === 7) {
    res.splice(0, 1);
    res.splice(1, 1);
    var obj = {
      time: res[0]
      ,statusCode: res[1]
      ,location: res[2]
      ,path: res[3]
      ,message: res[4].replace(/\u001b\[.*?m/, '')
    };

    this.emit(this.events.REQUEST, obj);
  }
};

var _onRequest = function(line) {
  var res = regexp.exec(line);
  if (res && res.length === 7) {
    res.splice(0, 1);
    res.splice(1, 1);
    var obj = {
      time: res[0]
      ,method: res[1]
      ,location: res[2]
      ,path: res[3].replace(/\u001b\[.*?m/, '')
    };

    this.emit(this.events.REQUEST, obj);
  }
};

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

  var args = objToArray.call(self);

  var stubby = spawn('stubby', args);

  stream = new BufferStream({encoding:'utf8', size:'flexible'});
  stream.split('\n');

  stream.on('split', function (chunk, token) {
    var line = chunk.toString();
    if (line.indexOf('<-') > -1) {
      _onResponse.call(self, line);
    } else {
      _onRequest.call(self, line);
    }
  });

  stubby.stdout.pipe(stream);
  stubby.stderr.pipe(stream);

  stubby.on('close', function (code) {
    console.log('child process exited with code ' + code);
  });
};

var CLI = function(config) {
  if (!(this instanceof CLI)) {
    return new CLI(config);
  }

  this.config = config || {};
  this.events = {
    REQUEST: 'REQUEST',
    RESPONSE: 'RESPONSE'
  };
};

util.inherits(CLI, events.EventEmitter);

CLI.prototype.admin = function(port) {
  this.config.admin = '--admin ' + port;
  return this;
};

CLI.prototype.stubs = function(port) {
  this.config.stubs = '--stubs ' + port;
  return this;
};

CLI.prototype.tls = function(port) {
  this.config.tls = '--tls ' + port;
  return this;
};

CLI.prototype.data = function(data) {
  this.config.data = '--data ' + data;
  return this;
};

CLI.prototype.cert = function(cert) {
  this.config.cert = '--cert ' + cert;
  if (!this.config.key) {
    this.config.__validateSSL = true; // used to ensure both key and cert is set
  } else if (this.config.__validateSSL) {
    delete this.config.__validateSSL;
  }

  return this;
};

CLI.prototype.key = function(key) {
  this.config.key = '--key ' + key;
  if (!this.config.cert) {
    this.config.__validateSSL = true;
  } else if (this.config.__validateSSL) {
    delete this.config.__validateSSL;
  }

  return this;
};

CLI.prototype.location = function(location) {
  this.config.location = '--location ' + location;
  return this;
};

CLI.prototype.watch = function() {
  this.config.watch = '--watch';
  return this;
};

CLI.prototype.mute = function() {
  this.config.mute = '--mute';
  return this;
};

CLI.prototype.unmute = function() {
  if (this.config.mute) {
    delete this.config.mute;
  }

  return this;
};

CLI.prototype.help = function() {
  var _tmp = this.config;
  this.config = {help: '--help'};
  _spawn.apply(this);
  return new CLI(_tmp);
};

CLI.prototype.start = function() {
  if (this.config.__validateSSL) {
    if (!this.config.key) {
      throw new Error('Missing key: --key <key>');
    }

    if (!this.config.cert) {
      throw new Error('Missing certificate: --cert <cert>')
    }

    if (this.config.pfx) {
      delete this.config.pfx;
    }
  }

  _spawn.apply(this);

  return this;
};<commit_msg>Added exports<commit_after>var spawn = require('child_process').spawn;
var BufferStream = require('bufferstream')
var regexp = /(\d+:\d+:\d+)\s?([^\s]+)\s?([^\s]+)\s?\[([^\]]+)\]([^\s]+)\s?(.*)/;
var events = require('events');
var util = require('util');

var objToArray = function(obj) {
  var args = [];
  for (var i in this.config) {
    var cur = this.config[i];
    cur = cur.split(' ');
    args = args.concat(cur);
  }

  return args;
};

var _onResponse = function(line) {
  var res = regexp.exec(line);
  if (res && res.length === 7) {
    res.splice(0, 1);
    res.splice(1, 1);
    var obj = {
      time: res[0]
      ,statusCode: res[1]
      ,location: res[2]
      ,path: res[3]
      ,message: res[4].replace(/\u001b\[.*?m/, '')
    };

    this.emit(this.events.REQUEST, obj);
  }
};

var _onRequest = function(line) {
  var res = regexp.exec(line);
  if (res && res.length === 7) {
    res.splice(0, 1);
    res.splice(1, 1);
    var obj = {
      time: res[0]
      ,method: res[1]
      ,location: res[2]
      ,path: res[3].replace(/\u001b\[.*?m/, '')
    };

    this.emit(this.events.REQUEST, obj);
  }
};

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

  var args = objToArray.call(self);

  var stubby = spawn('stubby', args);

  stream = new BufferStream({encoding:'utf8', size:'flexible'});
  stream.split('\n');

  stream.on('split', function (chunk, token) {
    var line = chunk.toString();
    if (line.indexOf('<-') > -1) {
      _onResponse.call(self, line);
    } else {
      _onRequest.call(self, line);
    }
  });

  stubby.stdout.pipe(stream);
  stubby.stderr.pipe(stream);

  stubby.on('close', function (code) {
    console.log('child process exited with code ' + code);
  });
};

var CLI = function(config) {
  if (!(this instanceof CLI)) {
    return new CLI(config);
  }

  this.config = config || {};
  this.events = {
    REQUEST: 'REQUEST',
    RESPONSE: 'RESPONSE'
  };
};

util.inherits(CLI, events.EventEmitter);

CLI.prototype.admin = function(port) {
  this.config.admin = '--admin ' + port;
  return this;
};

CLI.prototype.stubs = function(port) {
  this.config.stubs = '--stubs ' + port;
  return this;
};

CLI.prototype.tls = function(port) {
  this.config.tls = '--tls ' + port;
  return this;
};

CLI.prototype.data = function(data) {
  this.config.data = '--data ' + data;
  return this;
};

CLI.prototype.cert = function(cert) {
  this.config.cert = '--cert ' + cert;
  if (!this.config.key) {
    this.config.__validateSSL = true; // used to ensure both key and cert is set
  } else if (this.config.__validateSSL) {
    delete this.config.__validateSSL;
  }

  return this;
};

CLI.prototype.key = function(key) {
  this.config.key = '--key ' + key;
  if (!this.config.cert) {
    this.config.__validateSSL = true;
  } else if (this.config.__validateSSL) {
    delete this.config.__validateSSL;
  }

  return this;
};

CLI.prototype.location = function(location) {
  this.config.location = '--location ' + location;
  return this;
};

CLI.prototype.watch = function() {
  this.config.watch = '--watch';
  return this;
};

CLI.prototype.mute = function() {
  this.config.mute = '--mute';
  return this;
};

CLI.prototype.unmute = function() {
  if (this.config.mute) {
    delete this.config.mute;
  }

  return this;
};

CLI.prototype.help = function() {
  var _tmp = this.config;
  this.config = {help: '--help'};
  _spawn.apply(this);
  return new CLI(_tmp);
};

CLI.prototype.start = function() {
  if (this.config.__validateSSL) {
    if (!this.config.key) {
      throw new Error('Missing key: --key <key>');
    }

    if (!this.config.cert) {
      throw new Error('Missing certificate: --cert <cert>')
    }

    if (this.config.pfx) {
      delete this.config.pfx;
    }
  }

  _spawn.apply(this);

  return this;
};

module.exports = CLI;<|endoftext|>


<commit_before>'use strict';

var util = require('./util');
var path = require('path');

var gulpfile = path.resolve(process.argv[1]);

exports = module.exports = function(gulp){

  // when the gulpfile is passed as argument default to process.argv
  if(!gulp.argv.length && gulpfile === gulp.gulpfile){
    gulp.argv = process.argv.slice(2);
  }

  if(!/(^|[\s])/.test(util.xargs)){
    gulp.argv.unshift('--cwd ' + path.dirname(gulp.gulpfile));
  }

  gulp.task(':cli(--silent)', function(next){
    gulp.log = false;
    util.log = util.gutil.log = util.silent;
    next();
  });

  gulp.task(':cli(--version|-v)', function(next){
    util.log('gulp-runtime version %s', require('../package.json').version);
    next();
  });

  gulp.task(':cli(--cwd) :dirname(\\S+)', function(next){
    var dirname = path.resolve(this.params.dirname);

    if(process.cwd() !== dirname){
      process.chdir(dirname);
      util.log('cwd changed to %s', util.format.path(dirname));
    }

    next();
  });

  gulp.task(':cli(--no-color|--color)', function(next){
    util.chalk.enabled = Boolean(this.params.cli === '--color');
    if(util.chalk.enabled){
      util.log('colors', util.format.enabled('enabled'));
    }
    next();
  });

  gulp.task(':cli(--tasks-simple|--tasks|-T)', function(next){
    var taskTree = gulp.tree({
      label: 'Tasks for ' + util.format.path(gulp.gulpfile),
      simple: this.params.cli === '--tasks-simple'
    });

    if(this.params.cli === '--tasks-simple'){
      console.log(taskTree.join('\n'));
    } else if(gulp.log){
      util.archy(taskTree).trim().split('\n').map(function(line){
        util.log(line);
      });
    }

    next();
  });

  if(gulp.argv.length){
    if(gulp.repl){
      gulp.repl.emit('line', gulp.argv.join(' '));
    } else {
      gulp.start.apply(gulp, gulp.argv);
    }
  }

};
<commit_msg>dev: simplifications for argv<commit_after>'use strict';

var util = require('./util');
var path = require('path');

var gulpfile = path.resolve(process.argv[1]);

exports = module.exports = function(gulp){
  var argv = [];

  // when the gulpfile is passed as argument default to process.argv
  if(gulpfile === gulp.gulpfile){
    argv = process.argv.slice(2);
  }

  // change CWD to the gulpfile's
  if(!/(^|[\s])(--cwd)/.test(util.xargs)){
    argv.unshift('--cwd ' + path.dirname(gulp.gulpfile));
  }

  gulp.task(':cli(--silent)', function(next){
    gulp.log = false;
    util.log = util.gutil.log = util.silent;
    next();
  });

  gulp.task(':cli(--version|-v)', function(next){
    util.log('gulp-runtime version', require('../package.json').version);
    next();
  });

  gulp.task(':cli(--cwd) :dirname(\\S+)', function(next){
    var dirname = path.resolve(this.params.dirname);

    if(process.cwd() !== dirname){
      process.chdir(dirname);
      util.log('cwd changed to', util.format.path(dirname));
    }

    next();
  });

  gulp.task(':cli(--no-color|--color)', function(next){
    util.chalk.enabled = Boolean(this.params.cli === '--color');
    if(util.chalk.enabled){
      util.log('colors', util.chalk.green('enabled'));
    }
    next();
  });

  gulp.task(':cli(--tasks-simple|--tasks|-T)', function(next){
    var taskTree = gulp.tree({
      label: 'Tasks for ' + util.format.path(gulp.gulpfile),
      simple: this.params.cli === '--tasks-simple'
    });

    if(this.params.cli === '--tasks-simple'){
      console.log(taskTree.join('\n'));
    } else if(gulp.log){
      util.archy(taskTree).trim().split('\n').map(function(line){
        util.log(line);
      });
    }

    next();
  });

  gulp.task(':cli(series|parallel):tasks([^)]+\\))', function(next){
    var mode = this.params.cli;
    var tasks = this.params.tasks.match(/[^,\s()]+/g);
    gulp[mode].apply(gulp, tasks)();
    next();
  })

  if(argv.length){
    if(gulp.repl){
      gulp.repl.emit('line', argv.join(' '));
    } else {
      gulp.start.apply(gulp, argv);
    }
  }
};
<|endoftext|>


<commit_before>/* global Opal */
'use strict'

const yargs = require('yargs')
const asciidoctor = require('asciidoctor.js')()

function convertOptions (argv) {
  const backend = argv['backend']
  const doctype = argv['doctype']
  const safeMode = argv['safe-mode']
  const noHeaderFooter = argv['no-header-footer']
  const sectionNumbers = argv['section-numbers']
  const baseDir = argv['base-dir']
  const destinationDir = argv['destination-dir']
  const outFile = argv['out-file']
  const quiet = argv['quiet']
  const verbose = argv['verbose']
  const timings = argv['timings']
  const trace = argv['trace']
  const requireLib = argv['require']
  if (verbose) {
    console.log('require ' + requireLib)
    console.log('backend ' + backend)
    console.log('doctype ' + doctype)
    console.log('header-footer ' + !noHeaderFooter)
    console.log('section-numbers ' + sectionNumbers)
    console.log('quiet ' + quiet)
    console.log('verbose ' + verbose)
    console.log('timings ' + timings)
    console.log('trace ' + trace)
    console.log('base-dir ' + baseDir)
    console.log('destination-dir ' + destinationDir)
  }
  if (requireLib) {
    require(requireLib)
  }
  const verboseMode = quiet ? 0 : verbose ? 2 : 1
  const attributes = []
  if (noHeaderFooter) {
    attributes.push('showtitle')
  }
  if (sectionNumbers) {
    attributes.push('sectnums')
  }
  const cliAttributes = argv['attribute']
  if (cliAttributes) {
    attributes.push(...cliAttributes)
  }
  if (verbose) {
    console.log('verbose-mode ' + verboseMode)
    console.log('attributes ' + attributes)
  }
  const options = {
    backend: backend,
    doctype: doctype,
    safe: safeMode,
    header_footer: !noHeaderFooter,
    verbose: verboseMode,
    timings: timings,
    trace: trace
  }
  if (baseDir != null) {
    options.base_dir = baseDir
  }
  if (destinationDir != null) {
    options.to_dir = destinationDir
  }
  if (outFile === '') {
    options.to_file = '-'
  } else {
    options.mkdirs = true
  }
  options.attributes = attributes
  if (verbose) {
    console.log('options ' + JSON.stringify(options))
  }
  if (options.to_file === '-') {
    options.to_file = Opal.gvars.stdout
  }
  return options
}

function argsParser () {
  return yargs
    .detectLocale(false)
    .wrap(Math.min(120, yargs.terminalWidth()))
    .command('$0 [files...]', '', function (yargs) {
      return yargs
        .option('backend', {
          alias: 'b',
          default: 'html5',
          describe: 'set output format backend',
          type: 'string'
        })
        .option('doctype', {
          alias: 'd',
          default: 'article',
          describe: 'document type to use when converting document',
          choices: ['article', 'book', 'manpage', 'inline']
        })
        .option('out-file', {
          alias: 'o',
          describe: 'output file (default: based on path of input file) use \'\' to output to STDOUT',
          type: 'string'
        })
        .option('safe-mode', {
          alias: 'S',
          default: 'unsafe',
          describe: 'set safe mode level explicitly, disables potentially dangerous macros in source files, such as include::[]',
          choices: ['unsafe', 'safe', 'server', 'secure']
        })
        .option('no-header-footer', {
          alias: 's',
          default: false,
          describe: 'suppress output of header and footer',
          type: 'boolean'
        })
        .option('section-numbers', {
          alias: 'n',
          default: false,
          describe: 'auto-number section titles in the HTML backend disabled by default',
          type: 'boolean'
        })
        .option('base-dir', {
          // QUESTION: should we check that the directory exists ? coerce to a directory ?
          alias: 'B',
          describe: 'base directory containing the document and resources (default: directory of source file)',
          type: 'string'
        })
        .option('destination-dir', {
          // QUESTION: should we check that the directory exists ? coerce to a directory ?
          alias: 'D',
          describe: 'destination output directory (default: directory of source file)',
          type: 'string'
        })
        .option('quiet', {
          alias: 'q',
          default: false,
          describe: 'suppress warnings',
          type: 'boolean'
        })
        .option('trace', {
          default: false,
          describe: 'include backtrace information on errors',
          type: 'boolean'
        })
        .option('verbose', {
          alias: 'v',
          default: false,
          describe: 'enable verbose mode',
          type: 'boolean'
        })
        .option('timings', {
          alias: 't',
          default: false,
          describe: 'enable timings mode',
          type: 'boolean'
        })
        .option('attribute', {
          alias: 'a',
          array: true,
          describe: 'a document attribute to set in the form of key, key! or key=value pair',
          type: 'string'
        })
        .option('require', {
          alias: 'r',
          array: true,
          describe: 'require the specified library before executing the processor, using the standard Node require',
          type: 'string'
        })
        .option('version', {
          alias: 'V',
          default: false,
          describe: 'display the version and runtime environment (or -v if no other flags or arguments)',
          type: 'boolean'
        })
    })
    .help()
}

function run (argv) {
  const verbose = argv['verbose']
  const version = argv['version']
  const files = argv['files']
  const options = convertOptions(argv)
  if (version || (verbose && files && files.length === 0)) {
    console.log(`Asciidoctor ${asciidoctor.getCoreVersion()} [http://asciidoctor.org]`)
    const releaseName = process.release ? process.release.name : 'node'
    console.log(`Runtime Environment (${releaseName} ${process.version} on ${process.platform})`)
  } else if (files && files.length > 0) {
    files.forEach(function (file) {
      if (verbose) {
        console.log(`converting file ${file}`)
      }
      if (argv['timings']) {
        const timings = asciidoctor.Timings.$new()
        const instanceOptions = Object.assign({}, options, {timings: timings})
        asciidoctor.convertFile(file, instanceOptions)
        timings.$print_report(Opal.gvars.stderr, file)
      } else {
        asciidoctor.convertFile(file, options)
      }
    })
  } else {
    yargs.showHelp()
  }
}

module.exports = {
  run: run,
  argsParser: argsParser,
  convertOptions: convertOptions
}
<commit_msg>Add missing to_file option<commit_after>/* global Opal */
'use strict'

const yargs = require('yargs')
const asciidoctor = require('asciidoctor.js')()

function convertOptions (argv) {
  const backend = argv['backend']
  const doctype = argv['doctype']
  const safeMode = argv['safe-mode']
  const noHeaderFooter = argv['no-header-footer']
  const sectionNumbers = argv['section-numbers']
  const baseDir = argv['base-dir']
  const destinationDir = argv['destination-dir']
  const outFile = argv['out-file']
  const quiet = argv['quiet']
  const verbose = argv['verbose']
  const timings = argv['timings']
  const trace = argv['trace']
  const requireLib = argv['require']
  if (verbose) {
    console.log('require ' + requireLib)
    console.log('backend ' + backend)
    console.log('doctype ' + doctype)
    console.log('header-footer ' + !noHeaderFooter)
    console.log('section-numbers ' + sectionNumbers)
    console.log('quiet ' + quiet)
    console.log('verbose ' + verbose)
    console.log('timings ' + timings)
    console.log('trace ' + trace)
    console.log('base-dir ' + baseDir)
    console.log('destination-dir ' + destinationDir)
  }
  if (requireLib) {
    require(requireLib)
  }
  const verboseMode = quiet ? 0 : verbose ? 2 : 1
  const attributes = []
  if (noHeaderFooter) {
    attributes.push('showtitle')
  }
  if (sectionNumbers) {
    attributes.push('sectnums')
  }
  const cliAttributes = argv['attribute']
  if (cliAttributes) {
    attributes.push(...cliAttributes)
  }
  if (verbose) {
    console.log('verbose-mode ' + verboseMode)
    console.log('attributes ' + attributes)
  }
  const options = {
    backend: backend,
    doctype: doctype,
    safe: safeMode,
    header_footer: !noHeaderFooter,
    to_file: outFile,
    verbose: verboseMode,
    timings: timings,
    trace: trace
  }
  if (baseDir != null) {
    options.base_dir = baseDir
  }
  if (destinationDir != null) {
    options.to_dir = destinationDir
  }
  if (outFile === '') {
    options.to_file = '-'
  } else {
    options.mkdirs = true
  }
  options.attributes = attributes
  if (verbose) {
    console.log('options ' + JSON.stringify(options))
  }
  if (options.to_file === '-') {
    options.to_file = Opal.gvars.stdout
  }
  return options
}

function argsParser () {
  return yargs
    .detectLocale(false)
    .wrap(Math.min(120, yargs.terminalWidth()))
    .command('$0 [files...]', '', function (yargs) {
      return yargs
        .option('backend', {
          alias: 'b',
          default: 'html5',
          describe: 'set output format backend',
          type: 'string'
        })
        .option('doctype', {
          alias: 'd',
          default: 'article',
          describe: 'document type to use when converting document',
          choices: ['article', 'book', 'manpage', 'inline']
        })
        .option('out-file', {
          alias: 'o',
          describe: 'output file (default: based on path of input file) use \'\' to output to STDOUT',
          type: 'string'
        })
        .option('safe-mode', {
          alias: 'S',
          default: 'unsafe',
          describe: 'set safe mode level explicitly, disables potentially dangerous macros in source files, such as include::[]',
          choices: ['unsafe', 'safe', 'server', 'secure']
        })
        .option('no-header-footer', {
          alias: 's',
          default: false,
          describe: 'suppress output of header and footer',
          type: 'boolean'
        })
        .option('section-numbers', {
          alias: 'n',
          default: false,
          describe: 'auto-number section titles in the HTML backend disabled by default',
          type: 'boolean'
        })
        .option('base-dir', {
          // QUESTION: should we check that the directory exists ? coerce to a directory ?
          alias: 'B',
          describe: 'base directory containing the document and resources (default: directory of source file)',
          type: 'string'
        })
        .option('destination-dir', {
          // QUESTION: should we check that the directory exists ? coerce to a directory ?
          alias: 'D',
          describe: 'destination output directory (default: directory of source file)',
          type: 'string'
        })
        .option('quiet', {
          alias: 'q',
          default: false,
          describe: 'suppress warnings',
          type: 'boolean'
        })
        .option('trace', {
          default: false,
          describe: 'include backtrace information on errors',
          type: 'boolean'
        })
        .option('verbose', {
          alias: 'v',
          default: false,
          describe: 'enable verbose mode',
          type: 'boolean'
        })
        .option('timings', {
          alias: 't',
          default: false,
          describe: 'enable timings mode',
          type: 'boolean'
        })
        .option('attribute', {
          alias: 'a',
          array: true,
          describe: 'a document attribute to set in the form of key, key! or key=value pair',
          type: 'string'
        })
        .option('require', {
          alias: 'r',
          array: true,
          describe: 'require the specified library before executing the processor, using the standard Node require',
          type: 'string'
        })
        .option('version', {
          alias: 'V',
          default: false,
          describe: 'display the version and runtime environment (or -v if no other flags or arguments)',
          type: 'boolean'
        })
    })
    .help()
}

function run (argv) {
  const verbose = argv['verbose']
  const version = argv['version']
  const files = argv['files']
  const options = convertOptions(argv)
  if (version || (verbose && files && files.length === 0)) {
    console.log(`Asciidoctor ${asciidoctor.getCoreVersion()} [http://asciidoctor.org]`)
    const releaseName = process.release ? process.release.name : 'node'
    console.log(`Runtime Environment (${releaseName} ${process.version} on ${process.platform})`)
  } else if (files && files.length > 0) {
    files.forEach(function (file) {
      if (verbose) {
        console.log(`converting file ${file}`)
      }
      if (argv['timings']) {
        const timings = asciidoctor.Timings.$new()
        const instanceOptions = Object.assign({}, options, {timings: timings})
        asciidoctor.convertFile(file, instanceOptions)
        timings.$print_report(Opal.gvars.stderr, file)
      } else {
        asciidoctor.convertFile(file, options)
      }
    })
  } else {
    yargs.showHelp()
  }
}

module.exports = {
  run: run,
  argsParser: argsParser,
  convertOptions: convertOptions
}
<|endoftext|>


<commit_before>/* global Module */

/* Magic Mirror
 * Module: Calendar
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

Module.register("calendar",{

	// Define module defaults
	defaults: {
		maximumEntries: 10, // Total Maximum Entries
		maximumNumberOfDays: 365,
		displaySymbol: true,
		defaultSymbol: "calendar", // Fontawesome Symbol see http://fontawesome.io/cheatsheet/
		displayRepeatingCountTitle: false,
		defaultRepeatingCountTitle: '',
		maxTitleLength: 25,
		fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
		animationSpeed: 2000,
		fade: true,
		urgency: 7,
		timeFormat: "relative",
		fadePoint: 0.25, // Start on 1/4th of the list.
		calendars: [
			{
				symbol: "calendar",
				url: "http://www.calendarlabs.com/templates/ical/US-Holidays.ics",
			},
		],
		titleReplace: {
			"De verjaardag van ": "",
			"'s birthday": ""
		},
	},

	// Define required scripts.
	getStyles: function() {
		return ["calendar.css", "font-awesome.css"];
	},

	// Define required scripts.
	getScripts: function() {
		return ["moment.js"];
	},

	// Define required translations.
	getTranslations: function() {
		// The translations for the defaut modules are defined in the core translation files.
		// Therefor we can just return false. Otherwise we should have returned a dictionairy.
		// If you're trying to build your own module including translations, check out the documentation.
		return false;
	},

	// Override start method.
	start: function() {
		Log.log("Starting module: " + this.name);

		// Set locale.
		moment.locale(config.language);

		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			calendar.url = calendar.url.replace("webcal://", "http://");
			this.addCalendar(calendar.url);
		}

		this.calendarData = {};
		this.loaded = false;
	},

	// Override socket notification handler.
	socketNotificationReceived: function(notification, payload) {
		if (notification === "CALENDAR_EVENTS") {
			if (this.hasCalendarURL(payload.url)) {
				this.calendarData[payload.url] = payload.events;
				this.loaded = true;
			}
		} else if (notification === "FETCH_ERROR") {
			Log.error("Calendar Error. Could not fetch calendar: " + payload.url);
		} else if (notification === "INCORRECT_URL") {
			Log.error("Calendar Error. Incorrect url: " + payload.url);
		} else {
			Log.log("Calendar received an unknown socket notification: " + notification);
		}

		this.updateDom(this.config.animationSpeed);
	},

	// Override dom generator.
	getDom: function() {

		var events = this.createEventList();
		var wrapper = document.createElement("table");
		wrapper.className = "small";

		if (events.length === 0) {
			wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
			wrapper.className = "small dimmed";
			return wrapper;
		}

		for (var e in events) {
			var event = events[e];

			var eventWrapper = document.createElement("tr");
			eventWrapper.className = "normal";

			if (this.config.displaySymbol) {
				var symbolWrapper =  document.createElement("td");
				symbolWrapper.className = "symbol";
				var symbol =  document.createElement("span");
				symbol.className = "fa fa-" + this.symbolForUrl(event.url);
				symbolWrapper.appendChild(symbol);
				eventWrapper.appendChild(symbolWrapper);
			}

			var titleWrapper = document.createElement("td"),
				repeatingCountTitle = '';

			if (this.config.displayRepeatingCountTitle) {

				repeatingCountTitle = this.countTitleForUrl(event.url);

				if(repeatingCountTitle !== '') {
					var thisYear = new Date().getFullYear(),
						yearDiff = thisYear - event.firstYear;

					repeatingCountTitle = ', '+ yearDiff + '. ' + repeatingCountTitle;
				}
			}

			titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;
			titleWrapper.className = "title bright";
			eventWrapper.appendChild(titleWrapper);

			var timeWrapper =  document.createElement("td");
			//console.log(event.today);
			var now = new Date();
			// Define second, minute, hour, and day variables
			var one_second = 1000; // 1,000 milliseconds
			var one_minute = one_second * 60;
			var one_hour = one_minute * 60;
			var one_day = one_hour * 24;
			if (event.fullDayEvent) {
				if (event.today) {
					timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
				} else if (event.startDate - now < one_day && event.startDate - now > 0) {
					timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
				} else if (event.startDate - now < 2*one_day && event.startDate - now > 0) {
					if (this.translate('DAYAFTERTOMORROW') !== 'DAYAFTERTOMORROW') {
					timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
					} else {
					timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
					}
				} else {
					/* Check to see if the user displays absolute or relative dates with their events
					 * Also check to see if an event is happening within an 'urgency' time frameElement
					 * For example, if the user set an .urgency of 7 days, those events that fall within that
					 * time frame will be displayed with 'in xxx' time format or moment.fromNow()
					 *
					 * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
					 */
					if (this.config.timeFormat === "absolute") {
						if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * one_day))) {
							// This event falls within the config.urgency period that the user has set
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						} else {
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format("MMM Do"));
						}
					} else {
						timeWrapper.innerHTML =  this.capFirst(moment(event.startDate, "x").fromNow());
					}
				}
			} else {
				if (event.startDate >= new Date()) {
					if (event.startDate - now < 2 * one_day) {
						// This event is within the next 48 hours (2 days)
						if (event.startDate - now < 6 * one_hour) {
							// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						} else {
							// Otherwise just say 'Today/Tomorrow at such-n-such time'
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
						}
					} else {
						/* Check to see if the user displays absolute or relative dates with their events
						 * Also check to see if an event is happening within an 'urgency' time frameElement
						 * For example, if the user set an .urgency of 7 days, those events that fall within that
						 * time frame will be displayed with 'in xxx' time format or moment.fromNow()
						 *
						 * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
						 */
						if (this.config.timeFormat === "absolute") {
							if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * one_day))) {
								// This event falls within the config.urgency period that the user has set
								timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
							} else {
								timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format("MMM Do"));
							}
						} else {
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						}
					}
				} else {
					timeWrapper.innerHTML =  this.capFirst(this.translate("RUNNING")) + ' ' + this.capFirst(moment(event.endDate,"x").fromNow(true));
				}
			}
			//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
			//console.log(event);
			timeWrapper.className = "time light";
			eventWrapper.appendChild(timeWrapper);

			wrapper.appendChild(eventWrapper);

			// Create fade effect.
			if (this.config.fade && this.config.fadePoint < 1) {
				if (this.config.fadePoint < 0) {
					this.config.fadePoint = 0;
				}
				var startingPoint = events.length * this.config.fadePoint;
				var steps = events.length - startingPoint;
				if (e >= startingPoint) {
					var currentStep = e - startingPoint;
					eventWrapper.style.opacity = 1 - (1 / steps * currentStep);
				}
			}
		}

		return wrapper;
	},

	/* hasCalendarURL(url)
	 * Check if this config contains the calendar url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return bool - Has calendar url
	 */
	hasCalendarURL: function(url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url) {
				return true;
			}
		}

		return false;
	},

	/* createEventList()
	 * Creates the sorted list of all events.
	 *
	 * return array - Array with events.
	 */
	createEventList: function() {
		var events = [];
		var today = moment().startOf("day");
		for (var c in this.calendarData) {
			var calendar = this.calendarData[c];
			for (var e in calendar) {
				var event = calendar[e];
				event.url = c;
				event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
				events.push(event);
			}
		}

		events.sort(function(a, b) {
			return a.startDate - b.startDate;
		});

		return events.slice(0, this.config.maximumEntries);
	},

	/* createEventList(url)
	 * Requests node helper to add calendar url.
	 *
	 * argument url sting - Url to add.
	 */
	addCalendar: function(url) {
		this.sendSocketNotification("ADD_CALENDAR", {
			url: url,
			maximumEntries: this.config.maximumEntries,
			maximumNumberOfDays: this.config.maximumNumberOfDays,
			fetchInterval: this.config.fetchInterval
		});
	},

	/* symbolForUrl(url)
	 * Retrieves the symbol for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Symbol
	 */
	symbolForUrl: function(url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.symbol === "string")  {
				return calendar.symbol;
			}
		}

		return this.config.defaultSymbol;
	},
	/* countTitleForUrl(url)
	 * Retrieves the name for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Symbol
	 */
	countTitleForUrl: function(url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.repeatingCountTitle === "string")  {
				return calendar.repeatingCountTitle;
			}
		}

		return this.config.defaultRepeatingCountTitle;
	},

	/* shorten(string, maxLength)
	 * Shortens a sting if it's longer than maxLenthg.
	 * Adds an ellipsis to the end.
	 *
	 * argument string string - The string to shorten.
	 * argument maxLength number - The max lenth of the string.
	 *
	 * return string - The shortened string.
	 */
	shorten: function(string, maxLength) {
		if (string.length > maxLength) {
			return string.slice(0,maxLength) + "…";
		}

		return string;
	},

	/* capFirst(string)
	 * Capitalize the first letter of a string
	 * Eeturn capitalized string
	 */

	capFirst: function(string) {
		return string.charAt(0).toUpperCase() + string.slice(1);
	},

	/* titleTransform(title)
	 * Transforms the title of an event for usage.
	 * Replaces parts of the text as defined in config.titleReplace.
	 * Shortens title based on config.maxTitleLength
	 *
	 * argument title string - The title to transform.
	 *
	 * return string - The transformed title.
	 */
	titleTransform: function(title) {
		for (var needle in this.config.titleReplace) {
			var replacement = this.config.titleReplace[needle];
			title = title.replace(needle, replacement);
		}

		title = this.shorten(title, this.config.maxTitleLength);
		return title;
	}
});
<commit_msg>poinst of time when calendar event gets relative now customizable<commit_after>/* global Module */

/* Magic Mirror
 * Module: Calendar
 *
 * By Michael Teeuw http://michaelteeuw.nl
 * MIT Licensed.
 */

Module.register("calendar",{

	// Define module defaults
	defaults: {
		maximumEntries: 10, // Total Maximum Entries
		maximumNumberOfDays: 365,
		displaySymbol: true,
		defaultSymbol: "calendar", // Fontawesome Symbol see http://fontawesome.io/cheatsheet/
		displayRepeatingCountTitle: false,
		defaultRepeatingCountTitle: '',
		maxTitleLength: 25,
		fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
		animationSpeed: 2000,
		fade: true,
		urgency: 7,
		timeFormat: "relative",
		getRelative: '6',
		fadePoint: 0.25, // Start on 1/4th of the list.
		calendars: [
			{
				symbol: "calendar",
				url: "http://www.calendarlabs.com/templates/ical/US-Holidays.ics",
			},
		],
		titleReplace: {
			"De verjaardag van ": "",
			"'s birthday": ""
		},
	},

	// Define required scripts.
	getStyles: function() {
		return ["calendar.css", "font-awesome.css"];
	},

	// Define required scripts.
	getScripts: function() {
		return ["moment.js"];
	},

	// Define required translations.
	getTranslations: function() {
		// The translations for the defaut modules are defined in the core translation files.
		// Therefor we can just return false. Otherwise we should have returned a dictionairy.
		// If you're trying to build your own module including translations, check out the documentation.
		return false;
	},

	// Override start method.
	start: function() {
		Log.log("Starting module: " + this.name);

		// Set locale.
		moment.locale(config.language);

		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			calendar.url = calendar.url.replace("webcal://", "http://");
			this.addCalendar(calendar.url);
		}

		this.calendarData = {};
		this.loaded = false;
	},

	// Override socket notification handler.
	socketNotificationReceived: function(notification, payload) {
		if (notification === "CALENDAR_EVENTS") {
			if (this.hasCalendarURL(payload.url)) {
				this.calendarData[payload.url] = payload.events;
				this.loaded = true;
			}
		} else if (notification === "FETCH_ERROR") {
			Log.error("Calendar Error. Could not fetch calendar: " + payload.url);
		} else if (notification === "INCORRECT_URL") {
			Log.error("Calendar Error. Incorrect url: " + payload.url);
		} else {
			Log.log("Calendar received an unknown socket notification: " + notification);
		}

		this.updateDom(this.config.animationSpeed);
	},

	// Override dom generator.
	getDom: function() {

		var events = this.createEventList();
		var wrapper = document.createElement("table");
		wrapper.className = "small";

		if (events.length === 0) {
			wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
			wrapper.className = "small dimmed";
			return wrapper;
		}

		for (var e in events) {
			var event = events[e];

			var eventWrapper = document.createElement("tr");
			eventWrapper.className = "normal";

			if (this.config.displaySymbol) {
				var symbolWrapper =  document.createElement("td");
				symbolWrapper.className = "symbol";
				var symbol =  document.createElement("span");
				symbol.className = "fa fa-" + this.symbolForUrl(event.url);
				symbolWrapper.appendChild(symbol);
				eventWrapper.appendChild(symbolWrapper);
			}

			var titleWrapper = document.createElement("td"),
				repeatingCountTitle = '';

			if (this.config.displayRepeatingCountTitle) {

				repeatingCountTitle = this.countTitleForUrl(event.url);

				if(repeatingCountTitle !== '') {
					var thisYear = new Date().getFullYear(),
						yearDiff = thisYear - event.firstYear;

					repeatingCountTitle = ', '+ yearDiff + '. ' + repeatingCountTitle;
				}
			}

			titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;
			titleWrapper.className = "title bright";
			eventWrapper.appendChild(titleWrapper);

			var timeWrapper =  document.createElement("td");
			//console.log(event.today);
			var now = new Date();
			// Define second, minute, hour, and day variables
			var one_second = 1000; // 1,000 milliseconds
			var one_minute = one_second * 60;
			var one_hour = one_minute * 60;
			var one_day = one_hour * 24;
			if (event.fullDayEvent) {
				if (event.today) {
					timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
				} else if (event.startDate - now < one_day && event.startDate - now > 0) {
					timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
				} else if (event.startDate - now < 2*one_day && event.startDate - now > 0) {
					if (this.translate('DAYAFTERTOMORROW') !== 'DAYAFTERTOMORROW') {
					timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
					} else {
					timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
					}
				} else {
					/* Check to see if the user displays absolute or relative dates with their events
					 * Also check to see if an event is happening within an 'urgency' time frameElement
					 * For example, if the user set an .urgency of 7 days, those events that fall within that
					 * time frame will be displayed with 'in xxx' time format or moment.fromNow()
					 *
					 * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
					 */
					if (this.config.timeFormat === "absolute") {
						if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * one_day))) {
							// This event falls within the config.urgency period that the user has set
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						} else {
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format("MMM Do"));
						}
					} else {
						timeWrapper.innerHTML =  this.capFirst(moment(event.startDate, "x").fromNow());
					}
				}
			} else {
				if (event.startDate >= new Date()) {
					if (event.startDate - now < 2 * one_day) {
						// This event is within the next 48 hours (2 days)
						if (event.startDate - now < this.config.getRelative * one_hour) {
							// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						} else {
							// Otherwise just say 'Today/Tomorrow at such-n-such time'
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
						}
					} else {
						/* Check to see if the user displays absolute or relative dates with their events
						 * Also check to see if an event is happening within an 'urgency' time frameElement
						 * For example, if the user set an .urgency of 7 days, those events that fall within that
						 * time frame will be displayed with 'in xxx' time format or moment.fromNow()
						 *
						 * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
						 */
						if (this.config.timeFormat === "absolute") {
							if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * one_day))) {
								// This event falls within the config.urgency period that the user has set
								timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
							} else {
								timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format("MMM Do"));
							}
						} else {
							timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
						}
					}
				} else {
					timeWrapper.innerHTML =  this.capFirst(this.translate("RUNNING")) + ' ' + this.capFirst(moment(event.endDate,"x").fromNow(true));
				}
			}
			//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
			//console.log(event);
			timeWrapper.className = "time light";
			eventWrapper.appendChild(timeWrapper);

			wrapper.appendChild(eventWrapper);

			// Create fade effect.
			if (this.config.fade && this.config.fadePoint < 1) {
				if (this.config.fadePoint < 0) {
					this.config.fadePoint = 0;
				}
				var startingPoint = events.length * this.config.fadePoint;
				var steps = events.length - startingPoint;
				if (e >= startingPoint) {
					var currentStep = e - startingPoint;
					eventWrapper.style.opacity = 1 - (1 / steps * currentStep);
				}
			}
		}

		return wrapper;
	},

	/* hasCalendarURL(url)
	 * Check if this config contains the calendar url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return bool - Has calendar url
	 */
	hasCalendarURL: function(url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url) {
				return true;
			}
		}

		return false;
	},

	/* createEventList()
	 * Creates the sorted list of all events.
	 *
	 * return array - Array with events.
	 */
	createEventList: function() {
		var events = [];
		var today = moment().startOf("day");
		for (var c in this.calendarData) {
			var calendar = this.calendarData[c];
			for (var e in calendar) {
				var event = calendar[e];
				event.url = c;
				event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
				events.push(event);
			}
		}

		events.sort(function(a, b) {
			return a.startDate - b.startDate;
		});

		return events.slice(0, this.config.maximumEntries);
	},

	/* createEventList(url)
	 * Requests node helper to add calendar url.
	 *
	 * argument url sting - Url to add.
	 */
	addCalendar: function(url) {
		this.sendSocketNotification("ADD_CALENDAR", {
			url: url,
			maximumEntries: this.config.maximumEntries,
			maximumNumberOfDays: this.config.maximumNumberOfDays,
			fetchInterval: this.config.fetchInterval
		});
	},

	/* symbolForUrl(url)
	 * Retrieves the symbol for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Symbol
	 */
	symbolForUrl: function(url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.symbol === "string")  {
				return calendar.symbol;
			}
		}

		return this.config.defaultSymbol;
	},
	/* countTitleForUrl(url)
	 * Retrieves the name for a specific url.
	 *
	 * argument url sting - Url to look for.
	 *
	 * return string - The Symbol
	 */
	countTitleForUrl: function(url) {
		for (var c in this.config.calendars) {
			var calendar = this.config.calendars[c];
			if (calendar.url === url && typeof calendar.repeatingCountTitle === "string")  {
				return calendar.repeatingCountTitle;
			}
		}

		return this.config.defaultRepeatingCountTitle;
	},

	/* shorten(string, maxLength)
	 * Shortens a sting if it's longer than maxLenthg.
	 * Adds an ellipsis to the end.
	 *
	 * argument string string - The string to shorten.
	 * argument maxLength number - The max lenth of the string.
	 *
	 * return string - The shortened string.
	 */
	shorten: function(string, maxLength) {
		if (string.length > maxLength) {
			return string.slice(0,maxLength) + "…";
		}

		return string;
	},

	/* capFirst(string)
	 * Capitalize the first letter of a string
	 * Eeturn capitalized string
	 */

	capFirst: function(string) {
		return string.charAt(0).toUpperCase() + string.slice(1);
	},

	/* titleTransform(title)
	 * Transforms the title of an event for usage.
	 * Replaces parts of the text as defined in config.titleReplace.
	 * Shortens title based on config.maxTitleLength
	 *
	 * argument title string - The title to transform.
	 *
	 * return string - The transformed title.
	 */
	titleTransform: function(title) {
		for (var needle in this.config.titleReplace) {
			var replacement = this.config.titleReplace[needle];
			title = title.replace(needle, replacement);
		}

		title = this.shorten(title, this.config.maxTitleLength);
		return title;
	}
});
<|endoftext|>


<commit_before>
require('core/string');
include('helma/engine');
include('helma/functional');

export('defineClass');

importPackage(com.google.appengine.api.datastore);
addHostObject(org.helma.util.Storable);

var __shared__ = true;
var datastore = DatastoreServiceFactory.getDatastoreService();
var registry = {};
var self = this;

// HACK: google datastore only allows entities in the same entity group
// to be modified in the same transaction, so we put all our app's data
// into one big entity group
var rootKey = KeyFactory.createKey("Root", "root");

var EQUAL =                 Query.FilterOperator.EQUAL;
var GREATER_THAN =          Query.FilterOperator.GREATER_THAN;
var GREATER_THAN_OR_EQUAL = Query.FilterOperator.GREATER_THAN_OR_EQUAL;
var LESS_THAN =             Query.FilterOperator.LESS_THAN;
var LESS_THAN_OR_EQUAL =    Query.FilterOperator.LESS_THAN_OR_EQUAL;

function defineClass(type) {
    var ctor = registry[type];
    if (!ctor) {
        ctor = registry[type] = Storable.defineClass(self, type);
        ctor.all = bindArguments(all, type);
        ctor.get = bindArguments(get, type);
        ctor.query = bindArguments(query, type);
    }
    return ctor;
}

function create(type, key, entity) {
    var ctor = registry[type];
    return ctor.createInstance(key, entity);
}

function evaluateQuery(query, property) {
    var result = [];
    var type = query.getKind();
    var i = datastore.prepare(query).asIterator();
    while (i.hasNext()) {
        var entity = i.next();
        var s = create(type, entity.getKey(), entity);
        result.push(property ? s[property] : s);
    }
    return result;
}

function BaseQuery(type) {
    this.select = function(property) {
        return evaluateQuery(this.getQuery(), property);
    };
    this.getQuery = function() {
        return new Query(type);
    };
}

function OperatorQuery(parentQuery, operator, property, value) {
    this.select = function(selectProperty) {
        return evaluateQuery(this.getQuery(), selectProperty);
    };
    this.getQuery = function() {
        var query = parentQuery.getQuery();
        return query.addFilter(property, operator, value);
    };
}

BaseQuery.prototype.equals = function(property, value) {
    return new OperatorQuery(this, EQUAL, property, value);
};

BaseQuery.prototype.greater = function(property, value) {
    return new OperatorQuery(this, GREATER_THAN, property, value);
};

BaseQuery.prototype.greaterEquals = function(property, value) {
    return new OperatorQuery(this, GREATER_THAN_OR_EQUAL, property, value);
};

BaseQuery.prototype.less = function(property, value) {
    return new OperatorQuery(this, LESS_THAN, property, value);
};

BaseQuery.prototype.lessEquals = function(property, value) {
    return new OperatorQuery(this, LESS_THAN_OR_EQUAL, property, value);
};

BaseQuery.prototype.clone(OperatorQuery.prototype);

function all(type) {
    return evaluateQuery(new Query(type));
}

function query(type) {
    return new BaseQuery(type);
}

function get(type, id) {
	var key = KeyFactory.createKey(rootKey, type, id);
	if (!isKey(key)) {
		throw Error("Storable.get() called with non-key argument");
	}
	return create(key.getKind(), key, datastore.get(key));
}

function save(props, entity, entities) {
    if (entities && entities.contains(entity)) {
        return;
    }
    var tx;
    var isRoot = false;
    if (!entities) {
        isRoot = true;
        entities = new java.util.HashSet();
        tx = datastore.beginTransaction();
    }
    entities.add(entity);
    for (var id in props) {
        var value = props[id];
        if (isStorable(value)) {
            value.save(entities);
            value = value._key;
        } else if (value instanceof Array) {
            var list = new java.util.ArrayList();
            value.forEach(function(obj) {
                if (obj instanceof Storable) {
                    obj.save(entities);
                    list.add(obj._key);
                } else {
                    list.add(obj);
                }
            });
            value = list;
        } else if (typeof value === 'string' && value.length > 500) {
            // maximal length for ordinary strings is 500 chars in datastore
            value = new Text(value);
        }
        entity.setProperty(id, value);
    }
    if (isRoot) {
        try {
            datastore.put(entities);
            tx.commit();
        } catch (e) {
            tx.rollback();
            throw e;
        }
    }
}

function remove(key) {
    datastore['delete'](key);
}

function equalKeys(key1, key2) {
    return key1 && key1.equals(key2);
}

function getEntity(type, arg) {
    if (isKey(arg)) {
        return datastore.get(arg);
    } else if (isEntity(arg)) {
        return arg;
    } else if (arg instanceof Object) {
        // HACK: we generate our own ids as google autogenerated ids
        // are only created when entities are actually stored, but we
        // need them before to resolve references in pre-store
        var id, idlength = 4;
        do {
            id = "k" + String.random(idlength++);
            var entity = new Entity(type, id, rootKey);
            try {
                datastore.get(entity.getKey()) != null
            } catch (notfound) {
                break;
            }
        } while(true);
        return entity;
    }
    return null;
}

function getKey(type, arg) {
    if (isKey(arg)) {
        return arg;
    } else if (arg instanceof Entity) {
        return arg.getKey();
    }
    return null;
}

function getProps(store, entity) {
    var props = {};
    var map = new ScriptableMap(entity.getProperties());
    for (var i in map) {
        var value = map[i];
        if (isKey(value)) {
            value = create(value.getKind(), value);
        } else if (value instanceof java.util.List) {
            var array = [];
            for (var it = value.iterator(); it.hasNext(); ) {
                var obj = it.next();
                array.push(isKey(obj) ?
                           create(obj.getKind(), obj) : obj);
            }
            value = array;
        } else if (value instanceof Text) {
            value = value.getValue();
        } else {
            value = Context.javaToJS(value, global);
        }
        props[i] = value;
    }
    return props;
}

function getId(key) {
    return key ? key.getName() : null;
}

function isEntity(value) {
    return value instanceof Entity;
}

function isKey(value) {
    return value instanceof Key;
}

function isStorable(value) {
    return value instanceof Storable;
}

<commit_msg>Initial transaction support for googlestore<commit_after>
require('core/string');
include('helma/engine');
include('helma/functional');

export('defineClass', 'beginTransaction', 'getTransaction', 'commitTransaction', 'abortTransaction');

importPackage(com.google.appengine.api.datastore);
addHostObject(org.helma.util.Storable);

var __shared__ = true;
var datastore = DatastoreServiceFactory.getDatastoreService();
var registry = {};
var self = this;

// HACK: google datastore only allows entities in the same entity group
// to be modified in the same transaction, so we put all our app's data
// into one big entity group
var rootKey = KeyFactory.createKey("Root", "root");

var EQUAL =                 Query.FilterOperator.EQUAL;
var GREATER_THAN =          Query.FilterOperator.GREATER_THAN;
var GREATER_THAN_OR_EQUAL = Query.FilterOperator.GREATER_THAN_OR_EQUAL;
var LESS_THAN =             Query.FilterOperator.LESS_THAN;
var LESS_THAN_OR_EQUAL =    Query.FilterOperator.LESS_THAN_OR_EQUAL;

function defineClass(type) {
    var ctor = registry[type];
    if (!ctor) {
        ctor = registry[type] = Storable.defineClass(self, type);
        ctor.all = bindArguments(all, type);
        ctor.get = bindArguments(get, type);
        ctor.query = bindArguments(query, type);
    }
    return ctor;
}

function beginTransaction() {
    return datastore.beginTransaction();
}

function getTransaction() {
    return datastore.getCurrentTransaction(null);
}

function commitTransaction() {
    var tx = getTransaction();
    if (tx) {
        tx.commit();
    }
}

function abortTransaction() {
    var tx = getTransaction();
    if (tx) {
        tx.rollback();
    }
}

function create(type, key, entity) {
    var ctor = registry[type];
    return ctor.createInstance(key, entity);
}

function evaluateQuery(query, property) {
    var result = [];
    var type = query.getKind();
    var i = datastore.prepare(query).asIterator();
    while (i.hasNext()) {
        var entity = i.next();
        var s = create(type, entity.getKey(), entity);
        result.push(property ? s[property] : s);
    }
    return result;
}

function BaseQuery(type) {
    this.select = function(property) {
        return evaluateQuery(this.getQuery(), property);
    };
    this.getQuery = function() {
        return new Query(type);
    };
}

function OperatorQuery(parentQuery, operator, property, value) {
    this.select = function(selectProperty) {
        return evaluateQuery(this.getQuery(), selectProperty);
    };
    this.getQuery = function() {
        var query = parentQuery.getQuery();
        return query.addFilter(property, operator, value);
    };
}

BaseQuery.prototype.equals = function(property, value) {
    return new OperatorQuery(this, EQUAL, property, value);
};

BaseQuery.prototype.greater = function(property, value) {
    return new OperatorQuery(this, GREATER_THAN, property, value);
};

BaseQuery.prototype.greaterEquals = function(property, value) {
    return new OperatorQuery(this, GREATER_THAN_OR_EQUAL, property, value);
};

BaseQuery.prototype.less = function(property, value) {
    return new OperatorQuery(this, LESS_THAN, property, value);
};

BaseQuery.prototype.lessEquals = function(property, value) {
    return new OperatorQuery(this, LESS_THAN_OR_EQUAL, property, value);
};

BaseQuery.prototype.clone(OperatorQuery.prototype);

function all(type) {
    return evaluateQuery(new Query(type));
}

function query(type) {
    return new BaseQuery(type);
}

function get(type, id) {
	var key = KeyFactory.createKey(rootKey, type, id);
	if (!isKey(key)) {
		throw Error("Storable.get() called with non-key argument");
	}
	return create(key.getKind(), key, datastore.get(getTransaction(), key));
}

function save(props, entity, entities) {
    if (entities && entities.contains(entity)) {
        return;
    }
    var isRoot = false;
    if (!entities) {
        isRoot = true;
        entities = new java.util.HashSet();
    }
    entities.add(entity);
    for (var id in props) {
        var value = props[id];
        if (isStorable(value)) {
            value.save(entities);
            value = value._key;
        } else if (value instanceof Array) {
            var list = new java.util.ArrayList();
            value.forEach(function(obj) {
                if (obj instanceof Storable) {
                    obj.save(entities);
                    list.add(obj._key);
                } else {
                    list.add(obj);
                }
            });
            value = list;
        } else if (typeof value === 'string' && value.length > 500) {
            // maximal length for ordinary strings is 500 chars in datastore
            value = new Text(value);
        }
        entity.setProperty(id, value);
    }
    if (isRoot) {
        datastore.put(getTransaction(), entities);
    }
}

function remove(key) {
    datastore['delete'](getTransaction(), key);
}

function equalKeys(key1, key2) {
    return key1 && key1.equals(key2);
}

function getEntity(type, arg) {
    if (isKey(arg)) {
        return datastore.get(getTransaction(), arg);
    } else if (isEntity(arg)) {
        return arg;
    } else if (arg instanceof Object) {
        // HACK: we generate our own ids as google autogenerated ids
        // are only created when entities are actually stored, but we
        // need them before to resolve references in pre-store
        var id, idlength = 10;
        do {
            id = "k" + String.random(idlength++);
            var entity = new Entity(type, id, rootKey);
            try {
                datastore.get(getTransaction(), entity.getKey()) != null
            } catch (notfound) {
                break;
            }
        } while(true);
        return entity;
    }
    return null;
}

function getKey(type, arg) {
    if (isKey(arg)) {
        return arg;
    } else if (arg instanceof Entity) {
        return arg.getKey();
    }
    return null;
}

function getProps(store, entity) {
    var props = {};
    var map = new ScriptableMap(entity.getProperties());
    for (var i in map) {
        var value = map[i];
        if (isKey(value)) {
            value = create(value.getKind(), value);
        } else if (value instanceof java.util.List) {
            var array = [];
            for (var it = value.iterator(); it.hasNext(); ) {
                var obj = it.next();
                array.push(isKey(obj) ?
                           create(obj.getKind(), obj) : obj);
            }
            value = array;
        } else if (value instanceof Text) {
            value = value.getValue();
        } else {
            value = Context.javaToJS(value, global);
        }
        props[i] = value;
    }
    return props;
}

function getId(key) {
    return key ? key.getName() : null;
}

function isEntity(value) {
    return value instanceof Entity;
}

function isKey(value) {
    return value instanceof Key;
}

function isStorable(value) {
    return value instanceof Storable;
}

<|endoftext|>


<commit_before>var cli = require('cli').enable('status');
var calculateDuration = require('./time.js').calculateDuration;
var EventEmitter = require('events');
var emitter = new EventEmitter();
var ffmpeg = require('fluent-ffmpeg');

var MERGE_EVENT = 'merge';

var cut = module.exports = {};
cut.start = function(input, output_dir, cut_file) {
  cli.ok('> Start', input, output_dir, cut_file);
  var details = require(cut_file);
  var film_name = details.data.title;
  var time_codes = details.times;

  cli.info('Beginning to cut ' + film_name);
  generateClip(input, output_dir, time_codes.slice(0));

  emitter.addListener(MERGE_EVENT, function() {
    cli.info('Beginning merge process...');
    merge(film_name, output_dir, time_codes);
  });

};

/**
 * [generateClip description]
 * @param  {[type]} time [description]
 * @return {[type]}      [description]
 */
function generateClip(filename, output_dir, time_codes){

  var duration;
  var video;
  var output_file_name;
  var time = time_codes.shift();

  if(!time) {
    cli.info('All time codes processed, making kidcut');
    emitter.emit(MERGE_EVENT);
    return;
  }

  output_file_name = _generate_clip_path(output_dir, time);
  cli.info('Output file name:' + output_file_name);

  duration = calculateDuration(time.start, time.end);
  cli.info('Generating clip start:' + time.start + 'end:' + time.end + 'dur:' + duration);

  video = ffmpeg(filename);
  video.seekInput(time.start);
  video.duration(duration);
  video.output(output_file_name);

  video.on('progress', _progress);
  video.on('error', _error);
  video.on('end', function() {
    generateClip(filename, output_dir, time_codes);
  });

  video.run();

}

/**
 *
 * @param  {String} film_name
 * @param  {String} output_dir
 * @param  {String[]} time_codes]
 * @return {void}
 */
function merge(film_name, output_dir, time_codes) {

  var video;
  var i;
  var input_file;
  var super_cut_output = _generate_super_clip_path(film_name, output_dir);

  for (i = 0; i < time_codes.length; i++) {
    input_file = _generate_clip_path(output_dir, time_codes[i]);

    //Need to initialize with an input_file
    if(!video) {
      video = ffmpeg(input_file);
    } else {
      video.input(input_file);
    }
  }

  video.on('progress', _progress);
  video.on('error', _error);
  video.on('end', function() {
    cli.ok('Supercut is made! ' + super_cut_output);
  });

  video.mergeToFile(super_cut_output);
}

/**
 * Helpers
 */

//The final file that is output
function _generate_super_clip_path(name, output_dir) {
  return output_dir.concat(name,'.kidcut', '.mp4');
}

function _generate_clip_path(output_dir, time) {
  return output_dir.concat(time.start, '_', time.end, '.mp4');
}

function _error(err) {
  cli.fatal('An error occurred:' + err.message);
}

function _progress(progress) {
  cli.info('Processing: ' + Math.round(progress.percent * 100) + '% done');
}<commit_msg>Add cleanup event<commit_after>var cli = require('cli').enable('status');
var calculateDuration = require('./time.js').calculateDuration;
var EventEmitter = require('events');
var emitter = new EventEmitter();
var ffmpeg = require('fluent-ffmpeg');
var fs = require('fs');

var MERGE_EVENT = 'merge';
var CLEANUP_EVENT = 'clean';
var cut = module.exports = {};
cut.start = function(input, output_dir, cut_file) {
  cli.ok('> Start', input, output_dir, cut_file);
  var details = require(cut_file);
  var film_name = details.data.title;
  var time_codes = details.times;

  cli.info('Beginning to cut ' + film_name);
  generateClip(input, output_dir, time_codes.slice(0));

  emitter.addListener(MERGE_EVENT, function() {
    cli.info('Beginning merge process...');
    merge(film_name, output_dir, time_codes);
  });

  emitter.addListener(CLEANUP_EVENT, function() {
    cli.info('Beginning cleanup process...');
    cleanup(time_codes);
  });

};

/**
 * [generateClip description]
 * @param  {[type]} time [description]
 * @return {[type]}      [description]
 */
function generateClip(filename, output_dir, time_codes){

  var duration;
  var video;
  var output_file_name;
  var time = time_codes.shift();

  if(!time) {
    cli.info('All time codes processed, making kidcut');
    emitter.emit(MERGE_EVENT);
    return;
  }

  output_file_name = _generate_clip_path(output_dir, time);
  cli.info('Output file name:' + output_file_name);

  duration = calculateDuration(time.start, time.end);
  cli.info('Generating clip start:' + time.start + 'end:' + time.end + 'dur:' + duration);

  video = ffmpeg(filename);
  video.seekInput(time.start);
  video.duration(duration);
  video.output(output_file_name);

  video.on('progress', _progress);
  video.on('error', _error);
  video.on('end', function() {
    generateClip(filename, output_dir, time_codes);
  });

  video.run();

}

/**
 *
 * @param  {String} film_name
 * @param  {String} output_dir
 * @param  {String[]} time_codes]
 * @return {void}
 */
function merge(film_name, output_dir, time_codes) {

  var video;
  var i;
  var input_file;
  var super_cut_output = _generate_super_clip_path(film_name, output_dir);

  for (i = 0; i < time_codes.length; i++) {
    input_file = _generate_clip_path(output_dir, time_codes[i]);

    //Need to initialize with an input_file
    if(!video) {
      video = ffmpeg(input_file);
    } else {
      video.input(input_file);
    }
  }

  video.on('progress', _progress);
  video.on('error', _error);
  video.on('end', function() {
    cli.ok('Supercut is made! ' + super_cut_output);
  });

  video.mergeToFile(super_cut_output);
}

/**
 * Helpers
 */

//The final file that is output
function _generate_super_clip_path(name, output_dir) {
  return output_dir.concat(name,'.kidcut', '.mp4');
}

function _generate_clip_path(output_dir, time) {
  return output_dir.concat(time.start, '_', time.end, '.mp4');
}

function _error(err) {
  cli.fatal('An error occurred:' + err.message);
}

function _progress(progress) {
  cli.info('Processing: ' + Math.round(progress.percent * 100) + '% done');
}<|endoftext|>


<commit_before>// Boiler
// Versão: 1.2.0
// Autor: Victor Bastos

if (typeof exports === 'object' && typeof define !== 'function') {
	var define = function (factory) {
		factory(require, exports, module);
	};
}

define(function (require, exports, module) {
	var	$ = require('jquery'),
		_ = require('underscore'),
		Backbone = require('backbone'),
		regionManager = new (require('marionette')).RegionManager(),
		loading,
		loadingView;

	// Boiler Controller
	var Controller = function() {};
	Controller.prototype.before = function() {};
	Controller.prototype.after = function() {};
	Controller.prototype.showView = showView;
	Controller.prototype.extend = function(methods) {

		var instance = _.extend({}, Backbone.Router.prototype, Controller.prototype, this, methods);

		_(methods).each(function(fn, fnName) {
			if(fnName !== 'before' && fnName !== 'after') {
				methods[fnName] = (function() {
					return function() {
						if(instance.before.apply(instance, [fnName, location.hash]) !== false) {
							if(fn.apply(instance, arguments) !== false) {
								instance.after.apply(instance, [fnName, location.hash]);
							}
						}
					};
				})();
			}
		});

		return _.extend({}, Backbone.Router.prototype, Controller.prototype, methods);
	};

	// Boiler showView
	function showView(region, view, options) {
		if(!regionManager.get(region)) {
			regionManager.addRegion(region, region);
		}

		if(typeof(view) === 'string') {
			view = new (require('../application/' + view))(options);
		}

		if(typeof(view) === 'function') {
			view = new view(options);
		}

		regionManager.get(region).show(view);

		return view;
	}

	// Boiler registerRoutes
	function registerRoutes(routes) {
		var router = new Backbone.Router({});

		_(routes).each(function(callback, route) {
			if(route[0] === '/' && route[route.length - 1] === '/') {
				route = route.slice(1).slice(0, -1);
				route = new RegExp(route);
			}

			callback = _.isArray(callback) ? callback : [callback];

			router.route(route, callback[1] || document.title, callback[0]);
		});

		router.on('route', function(route) {
			document.title = route;
		});

		Backbone.history.start();
	}

	// Insere o método showView no Backbone Router
	Backbone.Router.prototype.showView = showView;

	// Configura a view a ser renderizada no loading das requisições ajax
	function setLoadingView(view, region) {
		$(document).ajaxStart(function() {
			loading = setTimeout(function() {
				loadingView = this.showView(region, view);
			}.bind(this), 1000);
		}.bind(this));

		$(document).ajaxStop(function() {
			clearTimeout(loading);
			try {
				loadingView.destroy();
			} catch(err) {}
		});
	}

	// Configura a view a ser renderizada no erro das requisições ajax
	function setErrorView(view, region) {
		$(document).ajaxError(function(event, error) {
			clearTimeout(loading);
			this.showView(region, view, {model: new Backbone.Model(error)});
		}.bind(this));
	}

	module.exports = {
		Controller: new Controller(),
		showView: showView,
		registerRoutes: registerRoutes,
		setLoadingView: setLoadingView,
		setErrorView: setErrorView
	};
});
<commit_msg>remove as regions de mesmo nome ao adicionar novas regions.<commit_after>// Boiler
// Versão: 1.2.1
// Autor: Victor Bastos

if (typeof exports === 'object' && typeof define !== 'function') {
	var define = function (factory) {
		factory(require, exports, module);
	};
}

define(function (require, exports, module) {
	var	$ = require('jquery'),
		_ = require('underscore'),
		Backbone = require('backbone'),
		regionManager = new (require('marionette')).RegionManager(),
		loading,
		loadingView;

	// Boiler Controller
	var Controller = function() {};
	Controller.prototype.before = function() {};
	Controller.prototype.after = function() {};
	Controller.prototype.showView = showView;
	Controller.prototype.extend = function(methods) {

		var instance = _.extend({}, Backbone.Router.prototype, Controller.prototype, this, methods);

		_(methods).each(function(fn, fnName) {
			if(fnName !== 'before' && fnName !== 'after') {
				methods[fnName] = (function() {
					return function() {
						if(instance.before.apply(instance, [fnName, location.hash]) !== false) {
							if(fn.apply(instance, arguments) !== false) {
								instance.after.apply(instance, [fnName, location.hash]);
							}
						}
					};
				})();
			}
		});

		return _.extend({}, Backbone.Router.prototype, Controller.prototype, methods);
	};

	// Boiler showView
	function showView(region, view, options) {
		if(regionManager.get(region)) {
			regionManager.removeRegion(region);
		}

		regionManager.addRegion(region, region);

		if(typeof(view) === 'string') {
			view = new (require('../application/' + view))(options);
		}

		if(typeof(view) === 'function') {
			view = new view(options);
		}

		regionManager.get(region).show(view);

		return view;
	}

	// Boiler registerRoutes
	function registerRoutes(routes) {
		var router = new Backbone.Router({});

		_(routes).each(function(callback, route) {
			if(route[0] === '/' && route[route.length - 1] === '/') {
				route = route.slice(1).slice(0, -1);
				route = new RegExp(route);
			}

			callback = _.isArray(callback) ? callback : [callback];

			router.route(route, callback[1] || document.title, callback[0]);
		});

		router.on('route', function(route) {
			document.title = route;
		});

		Backbone.history.start();
	}

	// Insere o método showView no Backbone Router
	Backbone.Router.prototype.showView = showView;

	// Configura a view a ser renderizada no loading das requisições ajax
	function setLoadingView(view, region) {
		$(document).ajaxStart(function() {
			loading = setTimeout(function() {
				loadingView = this.showView(region, view);
			}.bind(this), 1000);
		}.bind(this));

		$(document).ajaxStop(function() {
			clearTimeout(loading);
			try {
				loadingView.destroy();
			} catch(err) {}
		});
	}

	// Configura a view a ser renderizada no erro das requisições ajax
	function setErrorView(view, region) {
		$(document).ajaxError(function(event, error) {
			clearTimeout(loading);
			this.showView(region, view, {model: new Backbone.Model(error)});
		}.bind(this));
	}

	module.exports = {
		Controller: new Controller(),
		showView: showView,
		registerRoutes: registerRoutes,
		setLoadingView: setLoadingView,
		setErrorView: setErrorView
	};
});
<|endoftext|>


<commit_before>var implementation      = require('./implementation.js')
  , putMetricData       = implementation.putMetricData
  , getMetricStatistics = implementation.getMetricStatistics
  , listMetrics         = implementation.listMetrics
  , transport           = implementation.transport
  , ISODateString       = implementation.ISODateString
  , isDate              = require('util').isDate
  , isArray             = require('util').isArray

module.exports = newMetric

function newMetric(awskey, secret, host, protocol, namespace) {

  Metric.metric = Metric
  return Metric

  function Metric(name) {
    // TODO validate len name < 255

    // a place to store options as they are set
    // TODO handle unit?  Required for get, not for put...
    var _metric = { unit        : false
                  , dimensions  : false
                  , period      : 60
                  , statistics  : ['Average']
                  , start       : false   // start and end are defaulted at
                  , end         : false   // run time for get
                  , name        : name
                  , namespace   : namespace}

    function put(value, op, cb) {

      if (typeof value !== 'number') {
        // TODO throw error
      }

      var ts
      op = op || {}

      if (typeof op === 'function') {
        cb = op
        op = {}
        ts = ISODateString(new Date())
      } else if (typeof op === 'string') {
        // I assume you have given me an ISO 8601 timestamp
        ts = op
        op = {}
      } else if (isDate(op)) {
        ts = ISODateString(ts)
      } else {
        ts = ISODateString(new Date())
      }

      var metric = {
          namespace   : namespace
        , name        : name
        , value       : value
        , unit        : op.unit       || _metric.unit
        , dimensions  : op.dimensions || _metric.dimensions}

      // TODO return request stream? vs metric obj?
      if (typeof cb === 'function') {
        transport(host
                , protocol
                , putMetricData(awskey
                              , secret
                              , host
                              , namespace
                              , ts
                              , metric)
                , cb)
      }

      return metric
    }

    function get(op, cb) {
      op = op || {}
      if (typeof op === 'function') {
        cb = op
        op = {}
      }

      var metric = {
          name      : name
        , unit      : op.unit       || _metric.unit
        , dimensions: op.dimensions || _metric.dimensions
        , statistics: op.statistics || _metric.statistics
        , period    : op.period     || _metric.period
        , start     : op.start      || _metric.start              // 1 hour
                                    || ISODateString(new Date() - 60*60*1000)
        , end       : op.end        || _metric.end
                                    || ISODateString(new Date())}

      if (typeof cb === 'function') {
        transport(host
                , protocol
                , getMetricStatistics(awskey
                                    , secret
                                    , host
                                    , namespace
                                    , metric)
                , makeResponse)
      } else {
        // TODO throw
      }

      function makeResponse(err, reqId, obj) {

        if (err) {
          return cb(err, reqId)
        }

        cb(err, obj
                  .getmetricstatisticsresponse
                  .getmetricstatisticsresult
                  .datapoints)
      }
    }

    function list(op, cb) {
      op = op || {}
      if (typeof op === 'function') {
        cb = op
        op = {}
      }

      var metric = {
          name      : name
        , dimensions: op.dimensions || _metric.dimensions}

      transport(host
              , protocol
              , listMetrics(awskey
                          , secret
                          , host
                          , namespace
                          , metric)
              , makeList)

      function makeList(err, reqId, obj) {

        if (err) {
          cb(err, reqId, obj)
        }

        // TODO some kind of structure check
        obj = obj.listmetricsresponse
                  .listmetricsresult
                  .metrics
                  .member
                  .map(function(member) {
                    return Metric(member.metricname)
                            .dimensions(member.dimensions
                                                .member)})

        cb(err, obj)
      }
    }

    return decorate(put
                  , get, list, _metric)
  }
}

var validStatistics = {
        average     : 'Average'
      , sum         : 'Sum'
      , samplecount : 'SampleCount'
      , maximum     : 'Maximum'
      , mimimum     : 'Minimum'}
  , validUnits = {
        seconds           : 'Seconds'
      , microseconds      : 'Microseconds'
      , milliseconds      : 'Milliseconds'
      , bytes             : 'Bytes'
      , kilobytes         : 'Kilobytes'
      , megabytes         : 'Megabytes'
      , gigabytes         : 'Gigabytes'
      , terabytes         : 'Terabytes'
      , bits              : 'Bits'
      , kilobits          : 'Kilobits'
      , megabits          : 'Megabits'
      , gigabits          : 'Gigabits'
      , terabits          : 'Terabits'
      , percent           : 'Percent'
      , count             : 'Count'
      , 'bytes/second'    : 'Bytes/Second'
      , 'kilobytes/second': 'Kilobytes/Second'
      , 'megabytes/second': 'Megabytes/Second'
      , 'gigabytes/second': 'Gigabytes/Second'
      , 'terabytes/second': 'Terabytes/Second'
      , 'bits/second'     : 'Bits/Second'
      , 'kilobits/second' : 'Kilobits/Second'
      , 'megabits/second' : 'Megabits/Second'
      , 'gigabits/second' : 'Gigabits/Second'
      , 'terabits/second' : 'Terabits/Second'
      , 'count/second'    :'Count/Second'
      , none              : 'None'}

function decorate(put, get, list, _metric) {

  put.unit        = unit
  put.statistics  = statistics
  put.period      = period
  put.dimensions  = dimensions
  put.start       = start
  put.end         = end
  put.get         = get
  put.list        = list
  put.put         = put

  return put

  // Option setter for AWS CloudWatch Metric Unit
  // see:
  // http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_concepts.html#Unit
  function unit(unit) {
    if (    typeof unit === 'string' && validUnits[unit.toLowerCase()]) {
        _metric.unit = validUnits[unit.toLowerCase()]
      } else if (!unit) {
        _metric.unit = validUnits['none']
      } else {
        // TODO throw error
      }
      return put
    }

  // Option setter for AWS CloudWatch Metric Statistics
  // see:
  // http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_concepts.html#Statistics
  function statistics(stat) {
    // TODO it would be nice to have an interface to request
    // multiple stats at the same time...  Untill that day, be a little lazy
    if (    typeof stat === 'string' && validStatistics[stat.toLowerCase()]) {
      _metric.statistics = [validStatistics[stat.toLowerCase()]]
    } else if (!stat) {
      // TODO throw error (required value)
    } else {
      // TODO throw error
    }
    return put
  }

  // Option setter for AWS CloudWatch Metric Period
  // see:
  // http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_concepts.html#CloudWatchPeriods
  function period(period) {
    if (period && typeof period === 'number') {
      if (!(period % 60)) {
        _metric.period = period
      } else {
        // TODO throw error
      }
    } else if (!period) {
      // TODO throw error (required value)
    } else {
      // TODO throw error
    }

    return put
  }

  // Option setter for AWS CloudWatch Metric Dimensions
  // see:
  // http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_concepts.html#Dimension
  function dimensions(dimensions) {
    // TODO validation
    if (dimensions) {
      if (isArray(dimensions)) {
        var tmp = {}
        dimensions.forEach(function(i) {
          tmp[i.name] = i.value})
        _metric.dimensions = tmp
      } else {
        _metric.dimensions = dimensions
      }
    } else {
      _metric.dimensions = false
    }

    return put
  }

  // Option setters for start and end
  // TODO need to do validation as well as formating
  function start(value) {
    if (typeof value === 'string') {
      _metric.start = value
    } else if (isDate(value)) {
      _metric.start = ISODateString(value)
    } else {
      _metric.start = false
    }

    return put
  }

  function end(value) {
    if (typeof value === 'string') {
      _metric.end = value
    } else if (isDate(value)) {
      _metric.end = ISODateString(value)
    } else {
      _metric.end = value
    }

    return put
  }

  function conf() {
    return Object.create(_metric)
  }
}
<commit_msg>a few things<commit_after>var implementation      = require('./implementation.js')
  , putMetricData       = implementation.putMetricData
  , getMetricStatistics = implementation.getMetricStatistics
  , listMetrics         = implementation.listMetrics
  , transport           = implementation.transport
  , ISODateString       = implementation.ISODateString
  , isDate              = require('util').isDate
  , isArray             = require('util').isArray

module.exports = newMetric

function newMetric(awskey, secret, host, protocol, namespace) {

  Metric.metric = Metric
  return Metric

  function Metric(name) {
    // TODO validate len name < 255

    // a place to store options as they are set
    // TODO handle unit?  Required for get, not for put...
    var _metric = { unit        : false
                  , dimensions  : false
                  , period      : 60
                  , statistics  : ['Average']
                  , start       : false   // start and end are defaulted at
                  , end         : false   // run time for get
                  , name        : name
                  , namespace   : namespace}

    return decorate(put
                  , get, list, _metric)

    // base function.  Used to put a value onto a metric
    function put(value, op, cb) {

      if (typeof value !== 'number') {
        // TODO throw error
      }

      var ts
      op = op || {}

      if (typeof op === 'function') {
        cb = op
        op = {}
        ts = ISODateString(new Date())
      } else if (typeof op === 'string') {
        // I assume you have given me an ISO 8601 timestamp
        ts = op
        op = {}
      } else if (isDate(op)) {
        ts = ISODateString(ts)
      } else {
        ts = ISODateString(new Date())
      }

      var metric = {
          namespace   : namespace
        , name        : name
        , value       : value
        , unit        : op.unit       || _metric.unit
        , dimensions  : op.dimensions || _metric.dimensions}

      // TODO return request stream? vs metric obj?
      if (typeof cb === 'function') {
        transport(host
                , protocol
                , putMetricData(awskey
                              , secret
                              , host
                              , namespace
                              , ts
                              , metric)
                , cb)
      }

      return metric
    }

    // just what it says, get values from CloudWatch
    function get(op, cb) {
      op = op || {}
      if (typeof op === 'function') {
        cb = op
        op = {}
      }

      var metric = {
          name      : name
        , unit      : op.unit       || _metric.unit
        , dimensions: op.dimensions || _metric.dimensions
        , statistics: op.statistics || _metric.statistics
        , period    : op.period     || _metric.period
        , start     : op.start      || _metric.start              // 1 hour
                                    || ISODateString(new Date() - 60*60*1000)
        , end       : op.end        || _metric.end
                                    || ISODateString(new Date())}

      if (typeof cb === 'function') {
        transport(host
                , protocol
                , getMetricStatistics(awskey
                                    , secret
                                    , host
                                    , namespace
                                    , metric)
                , makeResponse)
      } else {
        throw new Error('get requires a callback')
      }

      return put

      function makeResponse(err, reqId, obj) {

        if (err) {
          return cb(err, reqId)
        }

        var ret = obj
                  .getmetricstatisticsresponse
                  .getmetricstatisticsresult
                  .datapoints
                  .member

        ret = ret || []
        cb(err, ret)
      }
    }

    // Helper to list out metrics.  In case you don't know
    function list(op, cb) {
      op = op || {}
      if (typeof op === 'function') {
        cb = op
        op = {}
      }

      var metric = {
          name      : name
        , dimensions: op.dimensions || _metric.dimensions}

      if (typeof cb === 'function') {
        transport(host
                , protocol
                , listMetrics(awskey
                            , secret
                            , host
                            , namespace
                            , metric)
                , makeList)
      } else {
        throw new Error('list requires a callback')
      }

      return put

      function makeList(err, reqId, obj) {

        if (err) {
          cb(err, reqId, obj)
        }

        // TODO some kind of structure check
        obj = obj.listmetricsresponse
                  .listmetricsresult
                  .metrics
                  .member
                  .map(function(member) {
                    member = Metric(member.metricname)
                              .dimensions(member.dimensions
                                                  .member)
                    if (!op.raw) {
                      return member
                    } else {
                      return member.conf()
                    }
                  })

        cb(err, obj)
      }
    }
  }
}

var validStatistics = {
        average     : 'Average'
      , sum         : 'Sum'
      , samplecount : 'SampleCount'
      , maximum     : 'Maximum'
      , mimimum     : 'Minimum'}
  , validUnits = {
        seconds           : 'Seconds'
      , microseconds      : 'Microseconds'
      , milliseconds      : 'Milliseconds'
      , bytes             : 'Bytes'
      , kilobytes         : 'Kilobytes'
      , megabytes         : 'Megabytes'
      , gigabytes         : 'Gigabytes'
      , terabytes         : 'Terabytes'
      , bits              : 'Bits'
      , kilobits          : 'Kilobits'
      , megabits          : 'Megabits'
      , gigabits          : 'Gigabits'
      , terabits          : 'Terabits'
      , percent           : 'Percent'
      , count             : 'Count'
      , 'bytes/second'    : 'Bytes/Second'
      , 'kilobytes/second': 'Kilobytes/Second'
      , 'megabytes/second': 'Megabytes/Second'
      , 'gigabytes/second': 'Gigabytes/Second'
      , 'terabytes/second': 'Terabytes/Second'
      , 'bits/second'     : 'Bits/Second'
      , 'kilobits/second' : 'Kilobits/Second'
      , 'megabits/second' : 'Megabits/Second'
      , 'gigabits/second' : 'Gigabits/Second'
      , 'terabits/second' : 'Terabits/Second'
      , 'count/second'    :'Count/Second'
      , none              : 'None'}

// Decorate the put fuction with some convenience methods
function decorate(put, get, list, _metric) {

  put.unit        = unit
  put.statistics  = statistics
  put.period      = period
  put.dimensions  = dimensions
  put.start       = start
  put.end         = end
  put.get         = get
  put.list        = list
  put.put         = put
  put.conf        = conf
  put.endByEvents = endByEvents

  return put

  // Option setter for AWS CloudWatch Metric Unit
  // see:
  // http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_concepts.html#Unit
  function unit(unit) {
    if (    typeof unit === 'string' && validUnits[unit.toLowerCase()]) {
        _metric.unit = validUnits[unit.toLowerCase()]
      } else if (!unit) {
        _metric.unit = validUnits['none']
      } else {
        // TODO throw error
      }
      return put
    }

  // Option setter for AWS CloudWatch Metric Statistics
  // see:
  // http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_concepts.html#Statistics
  function statistics(stat) {
    // TODO it would be nice to have an interface to request
    // multiple stats at the same time...  Untill that day, be a little lazy
    if (    typeof stat === 'string' && validStatistics[stat.toLowerCase()]) {
      _metric.statistics = [validStatistics[stat.toLowerCase()]]
    } else if (!stat) {
      // TODO throw error (required value)
    } else {
      // TODO throw error
    }
    return put
  }

  // Option setter for AWS CloudWatch Metric Period
  // see:
  // http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_concepts.html#CloudWatchPeriods
  function period(period) {
    if (period && typeof period === 'number') {
      if (!(period % 60)) {
        _metric.period = period
      } else {
        // TODO throw error
      }
    } else if (!period) {
      // TODO throw error (required value)
    } else {
      // TODO throw error
    }

    return put
  }

  // Option setter for AWS CloudWatch Metric Dimensions
  // see:
  // http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_concepts.html#Dimension
  function dimensions(dimensions) {
    // TODO validation
    if (dimensions) {
      if (isArray(dimensions)) {
        var tmp = {}
        dimensions.forEach(function(i) {
          tmp[i.name] = i.value})
        _metric.dimensions = tmp
      } else {
        _metric.dimensions = dimensions
      }
    } else {
      _metric.dimensions = false
    }

    return put
  }

  // Option setters for start and end
  // TODO need to do validation as well as formating
  function start(value) {
    if (typeof value === 'string') {
      _metric.start = value
    } else if (isDate(value)) {
      _metric.start = ISODateString(value)
    } else {
      _metric.start = false
    }

    return put
  }

  function end(value) {
    if (typeof value === 'string') {
      _metric.end = value
    } else if (isDate(value)) {
      _metric.end = ISODateString(value)
    } else {
      _metric.end = value
    }

    return put
  }

  function endByEvents(eventsInPeriod, moveStartToEnd) {
    if (moveStartToEnd && _metric.end) {
      start(_metric.end)
    } else {
      // throw
    }

    if (!_metric.start) {
      // throw
    }

    var newEnd = new Date(_metric.start)
    newEnd.setSeconds(newEnd.getSeconds()
                        + eventsInPeriod * _metric.period *  1440)
    end(newEnd)

    return put
  }

  function conf() {
    return JSON.parse(JSON.stringify(_metric))
  }
}
<|endoftext|>