<commit_before>'use babel';
'use strict';

import Command from './command';

/** Builds commands with a fluent API */
export default class CommandBuilder {
	/**
	 * @param {Bot} bot - The bot the command is for
	 * @param {CommandInfo} [info] - The command info
	 * @param {CommandBuilderFunctions} [funcs] - The command functions to set
	 */
	constructor(bot, info = null, funcs = null) {
		if(!bot) throw new Error('A bot must be specified.');

		/** @type {Bot} */
		this.bot = bot;
		/** @type {CommandInfo} */
		this.commandInfo = info;
		/** @type {Command} */
		this.command = null;

		if(info) this.command = new Command(bot, info);
		if(funcs) {
			if(funcs.run) this.run(funcs.run);
			if(funcs.hasPermission) this.hasPermission(funcs.hasPermission);
		}
	}

	/**
	 * Sets the command information. This must be used before any other method if info was not provided to the constructor.
	 * @param {CommandInfo} info - The command info
	 * @return {CommandBuilder} This builder
	 */
	info(info) {
		this.commandInfo = info;
		this.command = new Command(this.bot, info);
		return this;
	}

	/**
	 * Sets the command's run method
	 * @param {function} fn - The function to use
	 * @param {*[]} [extras=[]] - Extra values to pass to the function
	 * @return {CommandBuilder} This builder
	 */
	run(fn, extras = []) {
		if(typeof fn !== 'function') throw new TypeError('run must be provided a function.');
		if(!this.command) throw new Error('.info(obj) must be called first.');
		this.command.run = _bindAppend(fn, this.command, ...extras);
		return this;
	}

	/**
	 * Sets the command's hasPermission method
	 * @param {function} fn - The function to use
	 * @param {*[]} [extras=[]] - Extra values to pass to the function
	 * @return {CommandBuilder} This builder
	 */
	hasPermission(fn, extras = []) {
		if(typeof fn !== 'function') throw new TypeError('hasPermission must be provided a function.');
		if(!this.command) throw new Error('.info(obj) must be called first.');
		this.command.hasPermission = _bindAppend(fn, this.command, ...extras);
		return this;
	}

	/**
	 * Registers the command to the bot
	 * @return {void}
	 */
	register() {
		this.bot.registerCommand(this.command);
		return this.command;
	}
}

function _bindAppend(fn, self, ...args) {
	return function boundFunction(...args2) {
		return fn.apply(self, args2.concat(args));
	};
}

/**
 * @typedef {Object} CommandBuilderFunctions
 * @property {function} [run] - The run function to set
 * @property {function} [hasPermission] - The hasPermission function to set
 */
<commit_msg>Updated ESDoc<commit_after>'use babel';
'use strict';

import Command from './command';

/** Builds commands with a fluent API */
export default class CommandBuilder {
	/**
	 * @param {Bot} bot - The bot the command is for
	 * @param {CommandInfo} [info] - The command info
	 * @param {CommandBuilderFunctions} [funcs] - The command functions to set
	 */
	constructor(bot, info = null, funcs = null) {
		if(!bot) throw new Error('A bot must be specified.');

		/** @type {Bot} */
		this.bot = bot;
		/** @type {CommandInfo} */
		this.commandInfo = info;
		/** @type {Command} */
		this.command = null;

		if(info) this.command = new Command(bot, info);
		if(funcs) {
			if(funcs.run) this.run(funcs.run);
			if(funcs.hasPermission) this.hasPermission(funcs.hasPermission);
		}
	}

	/**
	 * Sets the command information. This must be used before any other method if info was not provided to the constructor.
	 * @param {CommandInfo} info - The command info
	 * @return {CommandBuilder} This builder
	 */
	info(info) {
		this.commandInfo = info;
		this.command = new Command(this.bot, info);
		return this;
	}

	/**
	 * Sets the command's run method
	 * @param {function} fn - The function to use
	 * @param {*[]} [extras=[]] - Extra values to pass to the function
	 * @return {CommandBuilder} This builder
	 */
	run(fn, extras = []) {
		if(typeof fn !== 'function') throw new TypeError('run must be provided a function.');
		if(!this.command) throw new Error('.info(obj) must be called first.');
		this.command.run = _bindAppend(fn, this.command, ...extras);
		return this;
	}

	/**
	 * Sets the command's hasPermission method
	 * @param {function} fn - The function to use
	 * @param {*[]} [extras=[]] - Extra values to pass to the function
	 * @return {CommandBuilder} This builder
	 */
	hasPermission(fn, extras = []) {
		if(typeof fn !== 'function') throw new TypeError('hasPermission must be provided a function.');
		if(!this.command) throw new Error('.info(obj) must be called first.');
		this.command.hasPermission = _bindAppend(fn, this.command, ...extras);
		return this;
	}

	/**
	 * Registers the command to the bot
	 * @return {Command} The command that was registered
	 */
	register() {
		this.bot.registerCommand(this.command);
		return this.command;
	}
}

function _bindAppend(fn, self, ...args) {
	return function boundFunction(...args2) {
		return fn.apply(self, args2.concat(args));
	};
}

/**
 * @typedef {Object} CommandBuilderFunctions
 * @property {function} [run] - The run function to set
 * @property {function} [hasPermission] - The hasPermission function to set
 */
<|endoftext|>


<commit_before>import fs from "fs-promise";
import path from "path";
import {fromPairs} from "lodash";

function fetchFile(f) {
	return fs.readFile(path.resolve(f), {
		encoding: "utf-8"
	}).then(src => {
		return [f, src];
	});
}

function fetchStdin() {
	return new Promise((resolve, reject) => {
		let src = "";
		process.stdin.setEncoding("utf-8");
		process.stdin.on("data", (d) => src += d);
		process.stdin.on("end", () => resolve(["_input.js",src]));
		process.stdin.on("error", reject);
	});
}

export default function(argv, Temple) {
	let p = argv._.map(fetchFile);
	if (!process.stdin.isTTY) p.push(fetchStdin());

	return Promise.all(p).then(r => {
		let result = Temple.compile(fromPairs(r));
		let mapFile = argv["source-map"];
		let output = argv.output ? path.resolve(argv.output) : null;
		let p = [];
		let code;

		if (!mapFile) {
			code = result.code;
		} else {
			if (mapFile === "inline") mapFile = null;
			code = result.toString(mapFile);
			if (typeof mapFile === "string") {
				if (!output) throw new Error("You must specify an --output (-o) option when creating a file with a sourcemap");
				p.push(fs.writeFile(path.resolve(path.dirname(output), mapFile), result.map.toString()));
			}
		}

		if (output) return fs.writeFile(output, code);
		else {
			console.log(code);
			process.exit(0);
		}
	}).catch(e => {
		console.error(e.stack || e);
		process.exit(1);
	});
}
<commit_msg>fix: output not needed for source map<commit_after>import fs from "fs-promise";
import path from "path";
import {fromPairs} from "lodash";

function fetchFile(f) {
	return fs.readFile(path.resolve(f), {
		encoding: "utf-8"
	}).then(src => {
		return [f, src];
	});
}

function fetchStdin() {
	return new Promise((resolve, reject) => {
		let src = "";
		process.stdin.setEncoding("utf-8");
		process.stdin.on("data", (d) => src += d);
		process.stdin.on("end", () => resolve(["_input.js",src]));
		process.stdin.on("error", reject);
	});
}

export default function(argv, Temple) {
	let p = argv._.map(fetchFile);
	if (!process.stdin.isTTY) p.push(fetchStdin());

	return Promise.all(p).then(r => {
		let result = Temple.compile(fromPairs(r));
		let mapFile = argv["source-map"];
		let output = argv.output ? path.resolve(argv.output) : null;
		let p = [];
		let code;

		if (!mapFile) {
			code = result.code;
		} else {
			if (mapFile === "inline") mapFile = null;
			code = result.toString(mapFile);

			if (typeof mapFile === "string") {
				if (output) mapFile = path.resolve(path.dirname(output), mapFile);
				else mapFile = path.resolve(mapFile);
				p.push(fs.writeFile(mapFile, result.map.toString()));
			}
		}

		if (output) return fs.writeFile(output, code);
		else {
			console.log(code);
			process.exit(0);
		}
	}).catch(e => {
		console.error(e.stack || e);
		process.exit(1);
	});
}
<|endoftext|>


<commit_before>exports.run = function (client, msg, args) {

	msg.channel.createMessage({ embed: {
		color: 0x1E90FF,
		title: "Become a patreon!",
		url  : "https://patreon.com/crimsonxv",
		description: "By becoming a patreon, you help keep the servers running & encourage me to continue developing new features",
		fields: [
			{
				name: "Donator Benefits",
				value: "- Import up to 100 songs from a playlist instead of 20\n" +
					   "- Bypassed queue limits. Both for the servers you're in, and for the servers you own.\n" +
					   "- Patreon role in JukeBot's Support server\n" +
					   "\nMore benefits will be listed soon. Feel free to suggest some!",
				inline: true
			}
		],
		footer: {
			text: "Any benefits marked with * are still yet to be added. Early support is still appreciated!"
		}
	}});

}

exports.usage = {
	main: "{prefix}{command}",
	args: "",
	description: "Shows patreon benefits and general info"
};
<commit_msg>undocumented patreon feature, oops<commit_after>exports.run = function (client, msg, args) {

	msg.channel.createMessage({ embed: {
		color: 0x1E90FF,
		title: "Become a patreon!",
		url  : "https://patreon.com/crimsonxv",
		description: "By becoming a patreon, you help keep the servers running & encourage me to continue developing new features",
		fields: [
			{
				name: "Donator Benefits",
				value: "- Import up to 100 songs from a playlist instead of 20\n" +
					   "- Bypassed queue limits. Both for the servers you're in, and for the servers you own.\n" +
					   "- Patreon role in JukeBot's Support server\n" +
					   "- Increased song duration limit (2 hours)\n" +
					   "\nMore benefits will be listed soon. Feel free to suggest some!",
				inline: true
			}
		],
		footer: {
			text: "Any benefits marked with * are still yet to be added. Early support is still appreciated!"
		}
	}});

}

exports.usage = {
	main: "{prefix}{command}",
	args: "",
	description: "Shows patreon benefits and general info"
};
<|endoftext|>


<commit_before>/**
 * @flow
 */

import chalk from 'chalk';
import simpleSpinner from '@expo/simple-spinner';

import { Project } from 'xdl';

import log from '../log';
import sendTo from '../sendTo';
import { installExitHooks } from '../exit';

type Options = {
  clear?: boolean,
  sendTo?: string,
  quiet?: boolean,
  releaseChannel?: string,
};

export async function action(projectDir: string, options: Options = {}) {
  let channelRe = new RegExp(/^[a-z\d][a-z\d._-]*$/);
  if (options.releaseChannel && !channelRe.test(options.releaseChannel)) {
    log.error(
      'Release channel name can only contain lowercase letters, numbers and special characters . _ and -'
    );
    process.exit(1);
  }
  const status = await Project.currentStatus(projectDir);

  let startedOurOwn = false;
  if (status !== 'running') {
    log('Unable to find an existing exp instance for this directory, starting a new one...');
    installExitHooks(projectDir);
    await Project.startAsync(
      projectDir,
      { reset: options.clear, nonPersistent: true },
      !options.quiet
    );
    startedOurOwn = true;
  }

  let recipient = await sendTo.getRecipient(options.sendTo);
  log('Publishing...');

  if (options.quiet) {
    simpleSpinner.start();
  }

  let result = await Project.publishAsync(projectDir, {
    releaseChannel: options.releaseChannel,
  });

  let url = result.url;

  // Append the query param for the release channel to the URL.
  // When we integrate release channels into XDE, we can revisit this and
  // perhaps push the logic for this into xdl
  if (options.releaseChannel && options.releaseChannel !== 'default') {
    url = `${url}?release-channel=${options.releaseChannel}`;
  }

  if (options.quiet) {
    simpleSpinner.stop();
  }

  log('Published');
  log('Your URL is\n\n' + chalk.underline(url) + '\n');
  log.raw(url);

  if (recipient) {
    await sendTo.sendUrlAsync(url, recipient);
  }

  if (startedOurOwn) {
    await Project.stopAsync(projectDir);
  }

  return result;
}

export default (program: any) => {
  program
    .command('publish [project-dir]')
    .alias('p')
    .description('Publishes your project to exp.host')
    .option('-q, --quiet', 'Suppress verbose output from the React Native packager.')
    .option('-s, --send-to [dest]', 'A phone number or e-mail address to send a link to')
    .option('-c, --clear', 'Clear the React Native packager cache')
    .option(
      '-c --release-channel <release channel>',
      "The release channel to publish to. Default is 'default'.",
      'default'
    )
    .allowNonInteractive()
    .asyncActionProjectDir(action, true);
};
<commit_msg>Remove alias for release channel<commit_after>/**
 * @flow
 */

import chalk from 'chalk';
import simpleSpinner from '@expo/simple-spinner';

import { Project } from 'xdl';

import log from '../log';
import sendTo from '../sendTo';
import { installExitHooks } from '../exit';

type Options = {
  clear?: boolean,
  sendTo?: string,
  quiet?: boolean,
  releaseChannel?: string,
};

export async function action(projectDir: string, options: Options = {}) {
  let channelRe = new RegExp(/^[a-z\d][a-z\d._-]*$/);
  if (options.releaseChannel && !channelRe.test(options.releaseChannel)) {
    log.error(
      'Release channel name can only contain lowercase letters, numbers and special characters . _ and -'
    );
    process.exit(1);
  }
  const status = await Project.currentStatus(projectDir);

  let startedOurOwn = false;
  if (status !== 'running') {
    log('Unable to find an existing exp instance for this directory, starting a new one...');
    installExitHooks(projectDir);
    await Project.startAsync(
      projectDir,
      { reset: options.clear, nonPersistent: true },
      !options.quiet
    );
    startedOurOwn = true;
  }

  let recipient = await sendTo.getRecipient(options.sendTo);
  log('Publishing...');

  if (options.quiet) {
    simpleSpinner.start();
  }

  let result = await Project.publishAsync(projectDir, {
    releaseChannel: options.releaseChannel,
  });

  let url = result.url;

  // Append the query param for the release channel to the URL.
  // When we integrate release channels into XDE, we can revisit this and
  // perhaps push the logic for this into xdl
  if (options.releaseChannel && options.releaseChannel !== 'default') {
    url = `${url}?release-channel=${options.releaseChannel}`;
  }

  if (options.quiet) {
    simpleSpinner.stop();
  }

  log('Published');
  log('Your URL is\n\n' + chalk.underline(url) + '\n');
  log.raw(url);

  if (recipient) {
    await sendTo.sendUrlAsync(url, recipient);
  }

  if (startedOurOwn) {
    await Project.stopAsync(projectDir);
  }

  return result;
}

export default (program: any) => {
  program
    .command('publish [project-dir]')
    .alias('p')
    .description('Publishes your project to exp.host')
    .option('-q, --quiet', 'Suppress verbose output from the React Native packager.')
    .option('-s, --send-to [dest]', 'A phone number or e-mail address to send a link to')
    .option('-c, --clear', 'Clear the React Native packager cache')
    .option(
      '--release-channel <release channel>',
      "The release channel to publish to. Default is 'default'.",
      'default'
    )
    .allowNonInteractive()
    .asyncActionProjectDir(action, true);
};
<|endoftext|>


<commit_before>'use strict';

const Bacon = require('baconjs');

const AppConfig = require('../models/app_configuration.js');
const Application = require('../models/application.js');
const git = require('../models/git');
const handleCommandStream = require('../command-stream-handler');
const Log = require('../models/log');
const Logger = require('../logger.js');

function restart (api, params) {
  const { alias, quiet, commit, 'without-cache': withoutCache } = params.options;

  const s_appData = AppConfig.getAppData(alias);
  const s_fullCommitId = git.resolveFullCommitId(commit);
  const s_remoteCommitId = s_appData
    .flatMapLatest(({ app_id, app_orga }) => Application.get(api, app_id, app_orga))
    .flatMapLatest(({ commitId }) => commitId);

  const s_allLogs = Bacon
    .combineAsArray(s_appData, s_fullCommitId, s_remoteCommitId)
    .flatMapLatest(([appData, fullCommitId, remoteCommitId]) => {
      const commitId = fullCommitId || remoteCommitId;
      const cacheSuffix = withoutCache ? ' without using cache' : '';
      Logger.println(`Restarting ${appData.name} on commit #${commitId}${cacheSuffix}`);
      const s_redeploy = Application.redeploy(api, appData.app_id, appData.org_id, fullCommitId, withoutCache);
      return Bacon.combineAsArray(s_redeploy, appData, remoteCommitId);
    })
    .flatMapLatest(([redeploy, appData, remoteCommitId]) => {
      return Log.getAllLogs(api, redeploy, appData, remoteCommitId, quiet);
    })
    .map(Logger.println);

  handleCommandStream(s_allLogs);
};

module.exports = restart;
<commit_msg>Use same color display for commits in `clever restart` and `clever deploy`<commit_after>'use strict';

const Bacon = require('baconjs');
const colors = require('colors/safe');

const AppConfig = require('../models/app_configuration.js');
const Application = require('../models/application.js');
const git = require('../models/git');
const handleCommandStream = require('../command-stream-handler');
const Log = require('../models/log');
const Logger = require('../logger.js');

function restart (api, params) {
  const { alias, quiet, commit, 'without-cache': withoutCache } = params.options;

  const s_appData = AppConfig.getAppData(alias);
  const s_fullCommitId = git.resolveFullCommitId(commit);
  const s_remoteCommitId = s_appData
    .flatMapLatest(({ app_id, app_orga }) => Application.get(api, app_id, app_orga))
    .flatMapLatest(({ commitId }) => commitId);

  const s_allLogs = Bacon
    .combineAsArray(s_appData, s_fullCommitId, s_remoteCommitId)
    .flatMapLatest(([appData, fullCommitId, remoteCommitId]) => {
      const commitId = fullCommitId || remoteCommitId;
      const cacheSuffix = withoutCache ? ' without using cache' : '';
      Logger.println(`Restarting ${appData.name} on commit ${colors.green(commitId)}${cacheSuffix}`);
      const s_redeploy = Application.redeploy(api, appData.app_id, appData.org_id, fullCommitId, withoutCache);
      return Bacon.combineAsArray(s_redeploy, appData, remoteCommitId);
    })
    .flatMapLatest(([redeploy, appData, remoteCommitId]) => {
      return Log.getAllLogs(api, redeploy, appData, remoteCommitId, quiet);
    })
    .map(Logger.println);

  handleCommandStream(s_allLogs);
};

module.exports = restart;
<|endoftext|>


<commit_before>// npm modules
import moment from 'moment';
import momentParseformat from 'moment-parseformat';
import chalk from 'chalk';
import currencyFormatter from 'currency-formatter';
// our modules
import {api, config} from '../utils';

const periods = [];

function definePeriods(args, options) {
  // Add one hour to the default end time to take inconsistencies into account.
  const defaultEndTime = moment.utc().add(1, 'h');
  const defaultStartTime = moment.utc('2014-01-01');
  const currentMonth = moment.utc().format('YYYY-MM');
  const currentYear = moment.utc().format('YYYY');

  const logErrorAndExit = (arg, reason) => {
    console.error(chalk.red(`\nYou have entered an invalid date: "${arg}": ${reason}`));
    console.error('\nPlease enter dates in the following format: "YYYY-MM-DD", "YYYY-MM" or "YYYY"');
    console.error('You can also use the special shortcuts "today" and "yesterday" (ex.: urcli revenue today)');
    process.exit(0);
  };

  function validateDate(date) {
    if (!moment(date).isValid()) {
      const errorReason = `The date "${date}" doesn't exist.`;
      logErrorAndExit(date, errorReason);
    }
    return true;
  }

  function createOptionsPeriod() {
    const start = options.from ? moment.utc(options.from) : defaultStartTime;
    const end = options.to ? moment.utc(options.to) : defaultEndTime;
    periods.push([start, end]);
  }

  function createYearPeriod(year) {
    const start = moment(year, 'YYYY').utc();
    // If year is the current year the end should be today, otherwise it should be the end of the year.
    const end = year === currentYear ? defaultEndTime : moment(year, 'YYYY').utc().endOf('year');
    periods.push([start, end]);
  }

  function createMonthPeriod(month) {
    const start = moment(month).utc();
    // If month is the current month the end should be today, otherwise it should be the end of the month.
    const end = month === currentMonth ? defaultEndTime : moment(month).utc().add(1, 'M');
    periods.push([start, end]);
  }

  function createDayPeriod(day) {
    const start = moment(day).startOf('day');
    const end = moment(day).endOf('day');
    periods.push([start, end]);
  }

  function createDefaultPeriods() {
    periods.push([defaultStartTime, defaultEndTime]);
    createMonthPeriod(currentMonth);
  }

  if (!args.length && !options.to && !options.from) {
    createDefaultPeriods();
  }
  if (options.from || options.to) {
    if (options.from) validateDate(options.from);
    if (options.to) validateDate(options.to);
    createOptionsPeriod();
  }

  args.forEach((arg) => {
    // Regex expressions to match user input. Note that MM and YYYY-MM inputs
    // are validated by the regex itself, while YYYY-MM-DD has to be validated
    // seperately by the moment library because of leap years and such nonsense.
    const matchYear = /^201\d{1}$/; // YYYY.
    const matchMonth = /^\d{1}$|^[01]{1}[012]{1}$/; // 1-9, 01-09 and 10-12.
    const matchYearMonth = /^201\d{1}-\d{2}$|^201\d{1}-1{1}[012]{1}$/; // YYYY-MM.
    const matchYearMonthDay = /^201\d{1}-\d{2}-\d{2}$|^201\d{1}-1{1}[012]{1}-\d{2}$/; // YYYY-MM-DD

    if (matchYearMonthDay.test(arg)) {
      validateDate(arg);
      createDayPeriod(moment.utc(arg));
    } else if (matchYearMonth.test(arg)) {
      createMonthPeriod(arg);
    } else if (matchMonth.test(arg)) {
      const year = moment().month() + 1 < arg ? moment().year() - 1 : moment().year();
      createMonthPeriod(moment().year(year).month(arg - 1).format('YYYY-MM'));
    } else if (matchYear.test(arg)) {
      createYearPeriod(arg);
    } else if (arg === 'today') {
      createDayPeriod(moment.utc());
    } else if (arg === 'yesterday') {
      createDayPeriod(moment.utc().subtract(1, 'd'));
    } else {
      if (validateDate(arg)) {
        const errorReason = `The format "${momentParseformat(arg)}" is not supported.`;
        logErrorAndExit(arg, errorReason);
      }
      throw new Error('validateDate failed to throw an error.');
    }
  });
}

class Report {
  constructor(reviews, period) {
    this.startDate = period[0].format('YYYY-MM-DD');
    this.endDate = period[1].format('YYYY-MM-DD');
    // We need to check if we're using the default startDate, so that we can
    // find the earliest date a submission was assigned. This date is needed to
    // get the correct number of days of the period, so we can calculate the
    // average daily earnings for the period.
    if (moment(this.startDate).format('DD') === '01') {
      const firstDate = reviews
        .map(review => moment(review.assigned_at)) // returns date of review
        .map(date => date.valueOf()) // returns date in Unix Time (milliseconds from 1970)
        .reduce((acc, val) => {
          if (acc < val) {
            return acc;
          }
          return val;
        }); // returns the smallest number
      this.startDate = moment(firstDate).format('YYYY-MM-DD');
    }
    this.reviews = reviews;
    this.projects = {};
    this.totalEarned = 0;
    this.totalAssigned = 0;
  }

  create() {
    this.reviews.forEach((review) => {
      this.countReview(review);
    });
  }

  countReview(review) {
    const id = review.project_id;
    const price = parseInt(review.price, 10);
    const assignedAt = moment(review.assigned_at);
    const completedAt = moment(review.completed_at);
    const turnaroundTime = completedAt.diff(assignedAt);

    // If the report does not yet contain an entry for the project type, create
    // the entry and try counting the review again.
    if (!Object.prototype.hasOwnProperty.call(this.projects, id)) {
      this.projects[id] = {
        id,
        name: review.project.name,
        passed: 0,
        failed: 0,
        ungradeable: 0,
        earned: 0,
        turnaroundTime: 0,
      };
      this.countReview(review);
    } else {
      this.projects[id][review.result] += 1;
      this.projects[id].earned += price;
      this.projects[id].turnaroundTime += turnaroundTime;
      this.totalEarned += price;
      this.totalAssigned += 1;
    }
  }

  print() {
    console.log(chalk.blue(`\nEarnings Report for ${this.startDate} to ${this.endDate}:`));
    console.log('=============================================');
    console.log(chalk.bgBlack.white(`Total Projects Assigned: ${this.totalAssigned}`));

    Object.keys(this.projects).forEach((key) => {
      const {name, id, ungradeable, passed, failed, earned, turnaroundTime} = this.projects[key];
      const totalAssigned = passed + failed + ungradeable;
      const avgTurnaroundTime = moment.utc(turnaroundTime / (totalAssigned));
      const projectInfo = `Project: ${name} (${id}):`;

      console.log(`
      ${chalk.white(projectInfo)}
      ${chalk.white(new Array(projectInfo.length + 1).join('-'))}
          ${chalk.white(`Total Assigned: ${totalAssigned}`)}
              ${chalk.white(`Reviewed: ${passed + failed}`)}
              ${chalk.white(`Ungradeable: ${ungradeable}`)}
          ${chalk.white(`Earned: ${currencyFormatter.format(earned, {code: 'USD'})}`)}
          ${chalk.white(`Average Turnaround Time: ${avgTurnaroundTime.format('HH:mm')}`)}
      `);
    });

    console.log(chalk.white(`Total Earned: ${currencyFormatter.format(this.totalEarned, {code: 'USD'})}`));

    // Only print Daily Average if we have more than one day to average
    let numberOfDays = moment(this.endDate).diff(this.startDate, 'days');
    // console.log(moment.utc().diff(this.startDate, 'days', true));
    if (numberOfDays > 1) {
      // If current month, add hours of current day to numberOfDays
      const month = moment(this.startDate).format('YYYY-MM');
      const isCurrentMonth = () => month === moment.utc().format('YYYY-MM');
      if (isCurrentMonth()) {
        numberOfDays = moment.utc().diff(moment.utc(this.startDate), 'days', true);
      }
      // Print Daily Average
      console.log(chalk.white(`Daily Average: ${
        currencyFormatter.format(this.totalEarned / numberOfDays, {code: 'USD'})}`));
    }
    console.log('=============================================');
  }
}

function printReports() {
  const resolvedReports = periods.map(async (period) => {
    const task = 'completed';
    const token = config.token;
    const body = {
      start_date: period[0].format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
      end_date: period[1].format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
    };

    const completed = await api({token, task, body});
    const report = new Report(completed.body, period);
    report.create();
    report.print();
    return Promise.resolve();
  });
  Promise.all(resolvedReports).then(() => process.exit(0));
}

export const revenueCmd = (args, options) => {
  definePeriods(args, options);
  printReports();
};
<commit_msg>Fix beginning and ending times.<commit_after>// npm modules
import moment from 'moment';
import momentParseformat from 'moment-parseformat';
import chalk from 'chalk';
import currencyFormatter from 'currency-formatter';
// our modules
import {api, config} from '../utils';

const periods = [];

function definePeriods(args, options) {
  // Add one hour to the default end time to take inconsistencies into account.
  const defaultEndTime = moment.utc().add(1, 'h');
  const defaultStartTime = moment.utc('2014-01-01');
  const currentMonth = moment.utc().format('YYYY-MM');
  const currentYear = moment.utc().format('YYYY');

  const logErrorAndExit = (arg, reason) => {
    console.error(chalk.red(`\nYou have entered an invalid date: "${arg}": ${reason}`));
    console.error('\nPlease enter dates in the following format: "YYYY-MM-DD", "YYYY-MM" or "YYYY"');
    console.error('You can also use the special shortcuts "today" and "yesterday" (ex.: urcli revenue today)');
    process.exit(0);
  };

  function validateDate(date) {
    if (!moment(date).isValid()) {
      const errorReason = `The date "${date}" doesn't exist.`;
      logErrorAndExit(date, errorReason);
    }
    return true;
  }

  function createOptionsPeriod() {
    const start = options.from ? moment.utc(options.from) : defaultStartTime;
    const end = options.to ? moment.utc(options.to) : defaultEndTime;
    periods.push([start, end]);
  }

  function createYearPeriod(year) {
    const start = moment(year, 'YYYY');
    // If year is the current year the end should be today, otherwise it should be the end of the year.
    const end = year === currentYear ? defaultEndTime : moment(year, 'YYYY').endOf('year');
    periods.push([start, end]);
  }

  function createMonthPeriod(month) {
    const start = moment(month);
    // If month is the current month the end should be today, otherwise it should be the end of the month.
    const end = month === currentMonth ? defaultEndTime : moment(month).add(1, 'M');
    console.log([start, end]);
    periods.push([start, end]);
  }

  function createDayPeriod(day) {
    const start = moment(day).startOf('day');
    const end = moment(day).endOf('day');
    periods.push([start, end]);
  }

  function createDefaultPeriods() {
    periods.push([defaultStartTime, defaultEndTime]);
    createMonthPeriod(currentMonth);
  }

  if (!args.length && !options.to && !options.from) {
    createDefaultPeriods();
  }
  if (options.from || options.to) {
    if (options.from) validateDate(options.from);
    if (options.to) validateDate(options.to);
    createOptionsPeriod();
  }

  args.forEach((arg) => {
    // Regex expressions to match user input. Note that MM and YYYY-MM inputs
    // are validated by the regex itself, while YYYY-MM-DD has to be validated
    // seperately by the moment library because of leap years and such nonsense.
    const matchYear = /^201\d{1}$/; // YYYY.
    const matchMonth = /^\d{1}$|^[01]{1}[012]{1}$/; // 1-9, 01-09 and 10-12.
    const matchYearMonth = /^201\d{1}-\d{2}$|^201\d{1}-1{1}[012]{1}$/; // YYYY-MM.
    const matchYearMonthDay = /^201\d{1}-\d{2}-\d{2}$|^201\d{1}-1{1}[012]{1}-\d{2}$/; // YYYY-MM-DD

    if (matchYearMonthDay.test(arg)) {
      validateDate(arg);
      createDayPeriod(moment.utc(arg));
    } else if (matchYearMonth.test(arg)) {
      createMonthPeriod(arg);
    } else if (matchMonth.test(arg)) {
      const year = moment().month() + 1 < arg ? moment().year() - 1 : moment().year();
      createMonthPeriod(moment().year(year).month(arg - 1).format('YYYY-MM'));
    } else if (matchYear.test(arg)) {
      createYearPeriod(arg);
    } else if (arg === 'today') {
      createDayPeriod(moment.utc());
    } else if (arg === 'yesterday') {
      createDayPeriod(moment.utc().subtract(1, 'd'));
    } else {
      if (validateDate(arg)) {
        const errorReason = `The format "${momentParseformat(arg)}" is not supported.`;
        logErrorAndExit(arg, errorReason);
      }
      throw new Error('validateDate failed to throw an error.');
    }
  });
}

class Report {
  constructor(reviews, period) {
    this.startDate = period[0].format('YYYY-MM-DD');
    this.endDate = period[1].format('YYYY-MM-DD');
    // We need to check if we're using the default startDate, so that we can
    // find the earliest date a submission was assigned. This date is needed to
    // get the correct number of days of the period, so we can calculate the
    // average daily earnings for the period.
    if (moment(this.startDate).format('DD') === '01') {
      const firstDate = reviews
        .map(review => moment(review.assigned_at)) // returns date of review
        .map(date => date.valueOf()) // returns date in Unix Time (milliseconds from 1970)
        .reduce((acc, val) => {
          if (acc < val) {
            return acc;
          }
          return val;
        }); // returns the smallest number
      this.startDate = moment(firstDate).format('YYYY-MM-DD');
    }
    this.reviews = reviews;
    this.projects = {};
    this.totalEarned = 0;
    this.totalAssigned = 0;
  }

  create() {
    this.reviews.forEach((review) => {
      this.countReview(review);
    });
  }

  countReview(review) {
    const id = review.project_id;
    const price = parseInt(review.price, 10);
    const assignedAt = moment(review.assigned_at);
    const completedAt = moment(review.completed_at);
    const turnaroundTime = completedAt.diff(assignedAt);

    // If the report does not yet contain an entry for the project type, create
    // the entry and try counting the review again.
    if (!Object.prototype.hasOwnProperty.call(this.projects, id)) {
      this.projects[id] = {
        id,
        name: review.project.name,
        passed: 0,
        failed: 0,
        ungradeable: 0,
        earned: 0,
        turnaroundTime: 0,
      };
      this.countReview(review);
    } else {
      this.projects[id][review.result] += 1;
      this.projects[id].earned += price;
      this.projects[id].turnaroundTime += turnaroundTime;
      this.totalEarned += price;
      this.totalAssigned += 1;
    }
  }

  print() {
    console.log(chalk.blue(`\nEarnings Report for ${this.startDate} to ${this.endDate}:`));
    console.log('=============================================');
    console.log(chalk.bgBlack.white(`Total Projects Assigned: ${this.totalAssigned}`));

    Object.keys(this.projects).forEach((key) => {
      const {name, id, ungradeable, passed, failed, earned, turnaroundTime} = this.projects[key];
      const totalAssigned = passed + failed + ungradeable;
      const avgTurnaroundTime = moment.utc(turnaroundTime / (totalAssigned));
      const projectInfo = `Project: ${name} (${id}):`;

      console.log(`
      ${chalk.white(projectInfo)}
      ${chalk.white(new Array(projectInfo.length + 1).join('-'))}
          ${chalk.white(`Total Assigned: ${totalAssigned}`)}
              ${chalk.white(`Reviewed: ${passed + failed}`)}
              ${chalk.white(`Ungradeable: ${ungradeable}`)}
          ${chalk.white(`Earned: ${currencyFormatter.format(earned, {code: 'USD'})}`)}
          ${chalk.white(`Average Turnaround Time: ${avgTurnaroundTime.format('HH:mm')}`)}
      `);
    });

    console.log(chalk.white(`Total Earned: ${currencyFormatter.format(this.totalEarned, {code: 'USD'})}`));

    // Only print Daily Average if we have more than one day to average
    let numberOfDays = moment(this.endDate).diff(this.startDate, 'days');
    // console.log(moment.utc().diff(this.startDate, 'days', true));
    if (numberOfDays > 1) {
      // If current month, add hours of current day to numberOfDays
      const month = moment(this.startDate).format('YYYY-MM');
      const isCurrentMonth = () => month === moment.utc().format('YYYY-MM');
      if (isCurrentMonth()) {
        numberOfDays = moment.utc().diff(moment.utc(this.startDate), 'days', true);
      }
      // Print Daily Average
      console.log(chalk.white(`Daily Average: ${
        currencyFormatter.format(this.totalEarned / numberOfDays, {code: 'USD'})}`));
    }
    console.log('=============================================');
  }
}

function printReports() {
  const resolvedReports = periods.map(async (period) => {
    const task = 'completed';
    const token = config.token;
    const body = {
      start_date: period[0].format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
      end_date: period[1].format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
    };

    const completed = await api({token, task, body});
    const report = new Report(completed.body, period);
    report.create();
    report.print();
    return Promise.resolve();
  });
  Promise.all(resolvedReports).then(() => process.exit(0));
}

export const revenueCmd = (args, options) => {
  definePeriods(args, options);
  printReports();
};
<|endoftext|>


<commit_before>$(function () {
  $.getJSON('http://represent.opennorth.ca/boundary-sets/?limit=0', function (data) {
    var boundary_sets = {}
      , key_map = {
        'NL': gettext('Newfoundland and Labrador councils')
      , 'PE': gettext('Prince Edward Island councils')
      , 'NS': gettext('Nova Scotia councils')
      , 'NB': gettext('New Brunswick councils')
      , 'QC': gettext('Quebec councils')
      , 'ON': gettext('Ontario councils')
      , 'MB': gettext('Manitoba councils')
      , 'SK': gettext('Saskatchewan councils')
      , 'AB': gettext('Alberta councils')
      , 'BC': gettext('British Columbia councils')
      , 'YT': gettext('Yukon councils')
      , 'NT': gettext('Northwest Territories councils')
      , 'NU': gettext('Nunavut councils')
      }, uncategorized_map = {
        '/representative-sets/house-of-commons/': 'Canada',
        '/representative-sets/grande-prairie-city-council/': 'AB',
        '/representative-sets/lambton-county-council/': 'ON',
        '/representative-sets/lasalle-town-council/': 'ON',
        '/representative-sets/oshawa-city-council/': 'ON',
        '/representative-sets/peel-regional-council/': 'ON',
        '/representative-sets/waterloo-regional-council/': 'ON',
        '/representative-sets/abbotsford-city-council/': 'BC',
        '/representative-sets/burnaby-city-council/': 'BC',
        '/representative-sets/coquitlam-city-council/': 'BC',
        '/representative-sets/kelowna-city-council/': 'BC',
        '/representative-sets/langley-city-council/': 'BC',
        '/representative-sets/richmond-city-council/': 'BC',
        '/representative-sets/saanich-district-council/': 'BC',
        '/representative-sets/surrey-city-council/': 'BC',
        '/representative-sets/vancouver-city-council/': 'BC',
        '/representative-sets/victoria-city-council/': 'BC',
      }, provinces_key = gettext('Provincial legislatures');

    $.each(data.objects, function (i, boundary_set) {
      boundary_sets[boundary_set.url] = boundary_set.domain.split(', ')[1];
    });

    $.getJSON('http://represent.opennorth.ca/representative-sets/?limit=0', function (data) {
      var representative_sets = {'Canada': ['
' + gettext('All elected officials') + '
']}
        , keys = []
        , key
        , i
        , l
        , j
        , m
        , list
        , $ul;

      $.each(data.objects, function (i, representative_set) {
        key = boundary_sets[representative_set.related.boundary_set_url];
        if (!key) {
          if (/Assembl/.test(representative_set.name)) {
            key = provinces_key;
          }
          else {
            key = uncategorized_map[representative_set.url] || gettext('Uncategorized');
          }
        }
        if (!representative_sets[key]) {
          representative_sets[key] = [];
        }
        representative_sets[key].push('
' + representative_set.name + '
');
      });

      for (key in representative_sets) {
        if (representative_sets.hasOwnProperty(key)) {
          keys.push(key);
        }
      }

      $('#loading').remove();

      keys.sort();
      keys.splice($.inArray('Canada', keys), 1);
      keys.splice($.inArray(provinces_key, keys), 1);
      keys.unshift(provinces_key);
      keys.unshift('Canada');

      for (i = 0, l = keys.length; i < l; i++) {
        key = keys[i];
        list = representative_sets[key];
        list.sort();

        if (key != 'Canada') {
          $('#downloads').append('
' + (key_map[key] || key) + '
');
        }
        $ul = $('

');
        for (j = 0, m = list.length; j < m; j++) {
          $ul.append($(list[j]));
        }
        $('#downloads').append($ul);
      }
    });
  });
});
<commit_msg>fix langley categorization<commit_after>$(function () {
  $.getJSON('http://represent.opennorth.ca/boundary-sets/?limit=0', function (data) {
    var boundary_sets = {}
      , key_map = {
        'NL': gettext('Newfoundland and Labrador councils')
      , 'PE': gettext('Prince Edward Island councils')
      , 'NS': gettext('Nova Scotia councils')
      , 'NB': gettext('New Brunswick councils')
      , 'QC': gettext('Quebec councils')
      , 'ON': gettext('Ontario councils')
      , 'MB': gettext('Manitoba councils')
      , 'SK': gettext('Saskatchewan councils')
      , 'AB': gettext('Alberta councils')
      , 'BC': gettext('British Columbia councils')
      , 'YT': gettext('Yukon councils')
      , 'NT': gettext('Northwest Territories councils')
      , 'NU': gettext('Nunavut councils')
      }, uncategorized_map = {
        '/representative-sets/house-of-commons/': 'Canada',
        '/representative-sets/grande-prairie-city-council/': 'AB',
        '/representative-sets/lambton-county-council/': 'ON',
        '/representative-sets/lasalle-town-council/': 'ON',
        '/representative-sets/oshawa-city-council/': 'ON',
        '/representative-sets/peel-regional-council/': 'ON',
        '/representative-sets/waterloo-regional-council/': 'ON',
        '/representative-sets/abbotsford-city-council/': 'BC',
        '/representative-sets/burnaby-city-council/': 'BC',
        '/representative-sets/coquitlam-city-council/': 'BC',
        '/representative-sets/kelowna-city-council/': 'BC',
        '/representative-sets/langley-township-council/': 'BC',
        '/representative-sets/langley-city-council/': 'BC',
        '/representative-sets/richmond-city-council/': 'BC',
        '/representative-sets/saanich-district-council/': 'BC',
        '/representative-sets/surrey-city-council/': 'BC',
        '/representative-sets/vancouver-city-council/': 'BC',
        '/representative-sets/victoria-city-council/': 'BC',
      }, provinces_key = gettext('Provincial legislatures');

    $.each(data.objects, function (i, boundary_set) {
      boundary_sets[boundary_set.url] = boundary_set.domain.split(', ')[1];
    });

    $.getJSON('http://represent.opennorth.ca/representative-sets/?limit=0', function (data) {
      var representative_sets = {'Canada': ['
' + gettext('All elected officials') + '
']}
        , keys = []
        , key
        , i
        , l
        , j
        , m
        , list
        , $ul;

      $.each(data.objects, function (i, representative_set) {
        key = boundary_sets[representative_set.related.boundary_set_url];
        if (!key) {
          if (/Assembl/.test(representative_set.name)) {
            key = provinces_key;
          }
          else {
            key = uncategorized_map[representative_set.url] || gettext('Uncategorized');
          }
        }
        if (!representative_sets[key]) {
          representative_sets[key] = [];
        }
        representative_sets[key].push('
' + representative_set.name + '
');
      });

      for (key in representative_sets) {
        if (representative_sets.hasOwnProperty(key)) {
          keys.push(key);
        }
      }

      $('#loading').remove();

      keys.sort();
      keys.splice($.inArray('Canada', keys), 1);
      keys.splice($.inArray(provinces_key, keys), 1);
      keys.unshift(provinces_key);
      keys.unshift('Canada');

      for (i = 0, l = keys.length; i < l; i++) {
        key = keys[i];
        list = representative_sets[key];
        list.sort();

        if (key != 'Canada') {
          $('#downloads').append('
' + (key_map[key] || key) + '
');
        }
        $ul = $('

');
        for (j = 0, m = list.length; j < m; j++) {
          $ul.append($(list[j]));
        }
        $('#downloads').append($ul);
      }
    });
  });
});
<|endoftext|>


<commit_before>import * as THREE from '../lib/three';
import { loadModel } from '../utils/three';
import settings from '../settings';

import { recordCostumeColor, orbColor, controllerButtonColor } from '../theme/colors';

import wallUrl from './models/first-wall.obj';
import roomUrl from './models/space-bigger-holes.obj';
import isometricWallUrl from './models/first-wall-isometric.obj';
import isometricRoomUrl from './models/space-isometric.obj';
import roomTextureUrl from './models/bake/VR_AOMap.png';
import isometricRoomTextureUrl from './models/bake/ISO_AOMap.png';
import shadowTextureUrl from './shadow.png';

const {
  Mesh,
  MeshLambertMaterial,
  MeshBasicMaterial,
  ConeBufferGeometry,
  CylinderBufferGeometry,
  SphereGeometry,
  GridHelper,
  Group,
} = THREE;

const controllerMaterial = new MeshLambertMaterial({ color: recordCostumeColor });

const props = {
  perspectiveWall: [wallUrl],
  perspectiveRoom: [roomUrl, roomTextureUrl, false],
  orthographicWall: [isometricWallUrl],
  orthographicRoom: [isometricRoomUrl, isometricRoomTextureUrl],
  hand: (function createHand() {
    const radius = 0.02;
    const height = 0.2;
    const segments = 32;
    const cylinder = new Mesh(
      new CylinderBufferGeometry(radius, radius, height, segments),
      new MeshLambertMaterial(),
    );

    cylinder.rotation.x = Math.PI * 0.5 * 7;
    cylinder.updateMatrix();
    cylinder.geometry.applyMatrix(cylinder.matrix);
    return cylinder;
  }()),

  controller: (function createHand() {
    const radius = 0.02;
    const height = 0.2;
    const segments = 32;
    const cylinder = new Mesh(
      new CylinderBufferGeometry(radius, radius, height, segments),
      controllerMaterial,
    );
    cylinder.rotation.x = Math.PI * 0.5 * 7;
    cylinder.updateMatrix();

    const thumbpadRadius = 0.015;
    const thumbpadHeight = 0.0225;
    const thumbpad = new THREE.Mesh(
      new CylinderBufferGeometry(thumbpadRadius, thumbpadRadius, thumbpadHeight, segments),
      new MeshLambertMaterial({ color: controllerButtonColor.getHex() })
    );
    thumbpad.name = 'button';
    thumbpad.position.z = -0.05;
    thumbpad.position.y = 0.01;
    thumbpad.updateMatrix();

    const group = new Group();

    group.add(cylinder, thumbpad);

    return group;
  }()),

  head: (function createHead() {
    const radius = 0.12;
    const height = 0.2;
    const segments = 32;
    const cone = new Mesh(
      new ConeBufferGeometry(radius, height, segments),
      new MeshLambertMaterial(),
    );

    cone.rotation.x = Math.PI * 0.5 * 7;
    cone.updateMatrix();
    cone.geometry.applyMatrix(cone.matrix);
    return cone;
  }()),

  sphere: (function createSphere() {
    const segments = 32;
    const geometry = new SphereGeometry(
      settings.sphereRadius,
      segments,
      segments,
    );
    const material = new MeshBasicMaterial({
      color: orbColor.clone(),
    });
    const mesh = new Mesh(geometry, material);
    return mesh;
  }()),

  grid: (function createGrid() {
    return new GridHelper(50, 50, 0xaaaa00, 0xaaaa00);
  }()),

  shadow: (function createShadow() {
    const texture = new THREE.TextureLoader().load(shadowTextureUrl);
    const material = new THREE.MeshBasicMaterial({
      map: texture,
      transparent: true,
      side: THREE.DoubleSide,
      depthWrite: false,
      blending: THREE.SubtractiveBlending,
    });
    const geometry = new THREE.PlaneBufferGeometry(0.5, 0.5, 1, 1);
    const plane = new THREE.Mesh(geometry, material);
    return plane;
  }()),

  prepare: () => Promise.all(
    Object.keys(props)
      .filter(id => Array.isArray(props[id]))
      .map(
        (id) => loadModel(props[id])
          .then((model) => {
            props[id] = model;
          })
      )
  ),
};

export default props;
<commit_msg>render shadows backside<commit_after>import * as THREE from '../lib/three';
import { loadModel } from '../utils/three';
import settings from '../settings';

import { recordCostumeColor, orbColor, controllerButtonColor } from '../theme/colors';

import wallUrl from './models/first-wall.obj';
import roomUrl from './models/space-bigger-holes.obj';
import isometricWallUrl from './models/first-wall-isometric.obj';
import isometricRoomUrl from './models/space-isometric.obj';
import roomTextureUrl from './models/bake/VR_AOMap.png';
import isometricRoomTextureUrl from './models/bake/ISO_AOMap.png';
import shadowTextureUrl from './shadow.png';

const {
  Mesh,
  MeshLambertMaterial,
  MeshBasicMaterial,
  ConeBufferGeometry,
  CylinderBufferGeometry,
  SphereGeometry,
  GridHelper,
  Group,
} = THREE;

const controllerMaterial = new MeshLambertMaterial({ color: recordCostumeColor });

const props = {
  perspectiveWall: [wallUrl],
  perspectiveRoom: [roomUrl, roomTextureUrl, false],
  orthographicWall: [isometricWallUrl],
  orthographicRoom: [isometricRoomUrl, isometricRoomTextureUrl],
  hand: (function createHand() {
    const radius = 0.02;
    const height = 0.2;
    const segments = 32;
    const cylinder = new Mesh(
      new CylinderBufferGeometry(radius, radius, height, segments),
      new MeshLambertMaterial(),
    );

    cylinder.rotation.x = Math.PI * 0.5 * 7;
    cylinder.updateMatrix();
    cylinder.geometry.applyMatrix(cylinder.matrix);
    return cylinder;
  }()),

  controller: (function createHand() {
    const radius = 0.02;
    const height = 0.2;
    const segments = 32;
    const cylinder = new Mesh(
      new CylinderBufferGeometry(radius, radius, height, segments),
      controllerMaterial,
    );
    cylinder.rotation.x = Math.PI * 0.5 * 7;
    cylinder.updateMatrix();

    const thumbpadRadius = 0.015;
    const thumbpadHeight = 0.0225;
    const thumbpad = new THREE.Mesh(
      new CylinderBufferGeometry(thumbpadRadius, thumbpadRadius, thumbpadHeight, segments),
      new MeshLambertMaterial({ color: controllerButtonColor.getHex() })
    );
    thumbpad.name = 'button';
    thumbpad.position.z = -0.05;
    thumbpad.position.y = 0.01;
    thumbpad.updateMatrix();

    const group = new Group();

    group.add(cylinder, thumbpad);

    return group;
  }()),

  head: (function createHead() {
    const radius = 0.12;
    const height = 0.2;
    const segments = 32;
    const cone = new Mesh(
      new ConeBufferGeometry(radius, height, segments),
      new MeshLambertMaterial(),
    );

    cone.rotation.x = Math.PI * 0.5 * 7;
    cone.updateMatrix();
    cone.geometry.applyMatrix(cone.matrix);
    return cone;
  }()),

  sphere: (function createSphere() {
    const segments = 32;
    const geometry = new SphereGeometry(
      settings.sphereRadius,
      segments,
      segments,
    );
    const material = new MeshBasicMaterial({
      color: orbColor.clone(),
    });
    const mesh = new Mesh(geometry, material);
    return mesh;
  }()),

  grid: (function createGrid() {
    return new GridHelper(50, 50, 0xaaaa00, 0xaaaa00);
  }()),

  shadow: (function createShadow() {
    const texture = new THREE.TextureLoader().load(shadowTextureUrl);
    const material = new THREE.MeshBasicMaterial({
      map: texture,
      transparent: true,
      side: THREE.BackSide,
      depthWrite: false,
      blending: THREE.SubtractiveBlending,
    });
    const geometry = new THREE.PlaneBufferGeometry(0.5, 0.5, 1, 1);
    const plane = new THREE.Mesh(geometry, material);
    return plane;
  }()),

  prepare: () => Promise.all(
    Object.keys(props)
      .filter(id => Array.isArray(props[id]))
      .map(
        (id) => loadModel(props[id])
          .then((model) => {
            props[id] = model;
          })
      )
  ),
};

export default props;
<|endoftext|>


<commit_before>/*
  backgrid-text-cell
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
  Licensed under the MIT @license.
*/
(function (root, factory) {

  // CommonJS
  if (typeof exports == "object") {
    module.exports = factory(require("underscore"),
                             require("backgrid"));
  }
  // Browser
  else factory(root._, root.Backgrid);

}(this, function (_, Backgrid)  {

  /**
     Renders a form with a text area and a save button in a modal dialog.

     @class Backgrid.Extension.TextareaEditor
     @extends Backgrid.CellEditor
  */
  var TextareaEditor = Backgrid.Extension.TextareaEditor = Backgrid.CellEditor.extend({

    /** @property */
    tagName: "div",

    /** @property */
    className: "modal fade",

    /** @property {function(Object, ?Object=): string} template */
    template: _.template('

<form>
<button type="button" class="close" data-dismiss="modal">×</button>
<%- column.get("label") %>

<textarea cols="<%= cols %>" rows="<%= rows %>"><%- content %></textarea>

<input class="btn btn-primary" type="submit" value="Save"/>
</form>

', null, {variable: null}),

    /** @property */
    cols: 80,

    /** @property */
    rows: 10,

    /** @property */
    events: {
      "keydown textarea": "clearError",
      "submit": "saveOrCancel",
      "hide.bs.modal": "saveOrCancel",
      "hidden.bs.modal": "close",
      "shown.bs.modal": "focus"
    },

    /**
       @property {Object} modalOptions The options passed to Bootstrap's modal
       plugin.
    */
    modalOptions: {
      backdrop: false
    },

    /**
       Renders a modal form dialog with a textarea, submit button and a close button.
    */
    render: function () {
      this.$el.html($(this.template({
        column: this.column,
        cols: this.cols,
        rows: this.rows,
        content: this.formatter.fromRaw(this.model.get(this.column.get("name")))
      })));

      this.delegateEvents();

      this.$el.modal(this.modalOptions);

      return this;
    },

    /**
       Event handler. Saves the text in the text area to the model when
       submitting. When cancelling, if the text area is dirty, a confirmation
       dialog will pop up. If the user clicks confirm, the text will be saved to
       the model.

       Triggers a Backbone `backgrid:error` event from the model along with the
       model, column and the existing value as the parameters if the value
       cannot be converted.

       @param {Event} e
    */
    saveOrCancel: function (e) {
      if (e && e.type == "submit") {
        e.preventDefault();
        e.stopPropagation();
      }

      var model = this.model;
      var column = this.column;
      var val = this.$el.find("textarea").val();
      var newValue = this.formatter.toRaw(val);

      if (_.isUndefined(newValue)) {
        model.trigger("backgrid:error", model, column, val);

        if (e) {
          e.preventDefault();
          e.stopPropagation();
        }
      }
      else if (!e || e.type == "submit" ||
               (e.type == "hide" &&
                newValue !== (this.model.get(this.column.get("name")) || '').replace(/\r/g, '') &&
                confirm("Would you like to save your changes?"))) {

        model.set(column.get("name"), newValue);
        this.$el.modal("hide");
      }
      else if (e.type != "hide") this.$el.modal("hide");
    },

    /**
       Clears the error class on the parent cell.
     */
    clearError: _.debounce(function () {
      if (!_.isUndefined(this.formatter.toRaw(this.$el.find("textarea").val()))) {
        this.$el.parent().removeClass("error");
      }
    }, 150),

    /**
       Triggers a `backgrid:edited` event along with the cell editor as the
       parameter after the modal is hidden.

       @param {Event} e
    */
    close: function (e) {
      var model = this.model;
      model.trigger("backgrid:edited", model, this.column,
                    new Backgrid.Command(e));
    },

    /**
       Focuses the textarea when the modal is shown.
    */
    focus: function () {
      this.$el.find("textarea").focus();
    }

  });

  /**
     TextCell is a string cell type that renders a form with a text area in a
     modal dialog instead of an input box editor. It is best suited for entering
     a large body of text.

     @class Backgrid.Extension.TextCell
     @extends Backgrid.StringCell
  */
  Backgrid.Extension.TextCell = Backgrid.StringCell.extend({

    /** @property */
    className: "text-cell",

    /** @property  */
    editor: TextareaEditor

  });

}));
<commit_msg>add AMD support<commit_after>/*
  backgrid-text-cell
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
  Licensed under the MIT @license.
*/
(function (root, factory) {

  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['underscore', 'backgrid'], factory);
  } else if (typeof exports === 'object') {
    // CommonJS
    module.exports = factory(require("underscore"),
                             require("backgrid"));
  } else {
    // Browser globals
    factory(root._, root.Backgrid);
  }

}(this, function (_, Backgrid)  {

  /**
     Renders a form with a text area and a save button in a modal dialog.

     @class Backgrid.Extension.TextareaEditor
     @extends Backgrid.CellEditor
  */
  var TextareaEditor = Backgrid.Extension.TextareaEditor = Backgrid.CellEditor.extend({

    /** @property */
    tagName: "div",

    /** @property */
    className: "modal fade",

    /** @property {function(Object, ?Object=): string} template */
    template: _.template('

<form>
<button type="button" class="close" data-dismiss="modal">×</button>
<%- column.get("label") %>

<textarea cols="<%= cols %>" rows="<%= rows %>"><%- content %></textarea>

<input class="btn btn-primary" type="submit" value="Save"/>
</form>

', null, {variable: null}),

    /** @property */
    cols: 80,

    /** @property */
    rows: 10,

    /** @property */
    events: {
      "keydown textarea": "clearError",
      "submit": "saveOrCancel",
      "hide.bs.modal": "saveOrCancel",
      "hidden.bs.modal": "close",
      "shown.bs.modal": "focus"
    },

    /**
       @property {Object} modalOptions The options passed to Bootstrap's modal
       plugin.
    */
    modalOptions: {
      backdrop: false
    },

    /**
       Renders a modal form dialog with a textarea, submit button and a close button.
    */
    render: function () {
      this.$el.html($(this.template({
        column: this.column,
        cols: this.cols,
        rows: this.rows,
        content: this.formatter.fromRaw(this.model.get(this.column.get("name")))
      })));

      this.delegateEvents();

      this.$el.modal(this.modalOptions);

      return this;
    },

    /**
       Event handler. Saves the text in the text area to the model when
       submitting. When cancelling, if the text area is dirty, a confirmation
       dialog will pop up. If the user clicks confirm, the text will be saved to
       the model.

       Triggers a Backbone `backgrid:error` event from the model along with the
       model, column and the existing value as the parameters if the value
       cannot be converted.

       @param {Event} e
    */
    saveOrCancel: function (e) {
      if (e && e.type == "submit") {
        e.preventDefault();
        e.stopPropagation();
      }

      var model = this.model;
      var column = this.column;
      var val = this.$el.find("textarea").val();
      var newValue = this.formatter.toRaw(val);

      if (_.isUndefined(newValue)) {
        model.trigger("backgrid:error", model, column, val);

        if (e) {
          e.preventDefault();
          e.stopPropagation();
        }
      }
      else if (!e || e.type == "submit" ||
               (e.type == "hide" &&
                newValue !== (this.model.get(this.column.get("name")) || '').replace(/\r/g, '') &&
                confirm("Would you like to save your changes?"))) {

        model.set(column.get("name"), newValue);
        this.$el.modal("hide");
      }
      else if (e.type != "hide") this.$el.modal("hide");
    },

    /**
       Clears the error class on the parent cell.
     */
    clearError: _.debounce(function () {
      if (!_.isUndefined(this.formatter.toRaw(this.$el.find("textarea").val()))) {
        this.$el.parent().removeClass("error");
      }
    }, 150),

    /**
       Triggers a `backgrid:edited` event along with the cell editor as the
       parameter after the modal is hidden.

       @param {Event} e
    */
    close: function (e) {
      var model = this.model;
      model.trigger("backgrid:edited", model, this.column,
                    new Backgrid.Command(e));
    },

    /**
       Focuses the textarea when the modal is shown.
    */
    focus: function () {
      this.$el.find("textarea").focus();
    }

  });

  /**
     TextCell is a string cell type that renders a form with a text area in a
     modal dialog instead of an input box editor. It is best suited for entering
     a large body of text.

     @class Backgrid.Extension.TextCell
     @extends Backgrid.StringCell
  */
  Backgrid.Extension.TextCell = Backgrid.StringCell.extend({

    /** @property */
    className: "text-cell",

    /** @property  */
    editor: TextareaEditor

  });

}));
<|endoftext|>


<commit_before>exports.orgs = ['mozilla-appmaker', 'mozilla', 'mozilla-b2g', 'mozilla-services'];
//exports.orgs = ['mozilla-appmaker'];
<commit_msg>bleruthsght<commit_after>//exports.orgs = ['mozilla-appmaker', 'mozilla', 'mozilla-b2g', 'mozilla-services'];
exports.orgs = ['mozilla-b2g'];
<|endoftext|>


<commit_before>var sys = require('sys');
var EventEmitter = require('events').EventEmitter;

function Transfer(shareInfo, req, res) {
    var that = this;
    EventEmitter.call(this);

    this.shareInfo = shareInfo;
    this.downReq = req;
    this.downRes = res;  // HTTP response

    res.writeHead(200, { 'Content-Type': 'binary/octet-stream',
			 'Content-Disposition': 'attachment; filename=' + shareInfo.name,
			 'Content-Length': shareInfo.size });

    req.on('error', function() {
	that.end();
    });

    this.timeout = setTimeout(function() {
	that.end();
    }, 30 * 1000);
}
sys.inherits(Transfer, EventEmitter);
module.exports.Transfer = Transfer;

Transfer.prototype.acceptUpload = function(req, res) {
    var that = this;

    this.emit('invalidate');
    this.upReq = req;
    this.upRes = res;

    req.setEncoding('utf-8');
    var buf = '';
    req.on('data', function(data) {
	buf += data;
	var i = Math.floor(buf.length / 4) * 4;
	data = new Buffer(buf.slice(0, i), 'base64');
	buf = buf.slice(i, buf.length);
	var written = that.downRes.write(data);
	console.log({written:written});
	if (!written) {
	    req.socket.pause();
	    console.log('pause');
	}
    });
    this.downRes.socket.on('drain', function() {
	console.log('drain, resume');
	req.socket.resume();
    });
    req.on('end', function() {
	that.end();
    });

    res.writeHead(200, { });
};

Transfer.prototype.end = function() {
    if (this.upRes) {
	this.upRes.end();
    } else
	this.emit('invalidate');

    this.downRes.end();
};

<commit_msg>transfer: refactor out into base64Decoder()<commit_after>var sys = require('sys');
var EventEmitter = require('events').EventEmitter;

function Transfer(shareInfo, req, res) {
    var that = this;
    EventEmitter.call(this);

    this.shareInfo = shareInfo;
    this.downReq = req;
    this.downRes = res;  // HTTP response

    res.writeHead(200, { 'Content-Type': 'binary/octet-stream',
			 'Content-Disposition': 'attachment; filename=' + shareInfo.name,
			 'Content-Length': shareInfo.size });

    req.on('error', function() {
	that.end();
    });

    this.timeout = setTimeout(function() {
	that.end();
    }, 30 * 1000);
}
sys.inherits(Transfer, EventEmitter);
module.exports.Transfer = Transfer;

Transfer.prototype.acceptUpload = function(req, res) {
    var that = this;

    this.emit('invalidate');
    this.upReq = req;
    this.upRes = res;

    var decoder = base64Decoder();
    req.setEncoding('utf-8');  // FIXME

    var buf = '';
    req.on('data', function(data) {
	var written = that.downRes.write(decoder(data), 'binary');
	if (!written) {
	    req.pause();
	    console.log('pause');
	}
    });
    this.downRes.on('drain', function() {
	console.log('drain, resume');
	req.resume();
    });
    req.on('end', function() {
	that.downRes.write(decoder('', true), 'binary');
	that.end();
    });

    res.writeHead(200, { });
};

Transfer.prototype.end = function() {
console.log({transferEnd:this});
    if (this.upRes) {
	this.upRes.end();
    } else
	this.emit('invalidate');

    this.downRes.end();
};

function base64Decoder() {
    var buf = '';  // string to decode
    var _in = 0, _out = 0;
    return function(data, flush) {
	buf += data;
	_in += data.length;

	if (flush) {
	    data = new Buffer(buf, 'base64');
	    console.log('flush '+data.length);
	    buf = '';
	} else {
	    var i = Math.floor(buf.length / 4) * 4;
	    data = new Buffer(buf.slice(0, i), 'base64');
	    buf = buf.slice(i, buf.length);
	}

	_out += data.length;
console.log({'in':_in, out:_out});
	return data;
    };
}
<|endoftext|>


<commit_before>const Context = require('../lib/context');
const { expect } = require('chai');
const sinon = require('sinon');

describe('Context', () => {
    describe('#startTimeout', () => {
        it('sets the execution timeout', (done) => {
            const ctx = new Context({});
            ctx.done = done;
            ctx.startTimeout();
            expect(ctx.timeout._called).to.be.equal(false);
            ctx.stop();
            done();
        });
    });
    describe('#start', () => {
        const ctx = new Context({});
        it('Starts a timeout', (done) => {
            ctx.done = done;
            const spy = sinon.spy(ctx, 'startTimeout');
            ctx.start((ctx, done) => {
                expect(spy.called).to.be.ok;
                ctx.stop();
                spy.restore();
                done();
            });
        });
        it('Execute sync job', (done) => {
            ctx.done = () => {
                ctx.stop();
                done();
            };
            ctx.start((ctx, done) => {
                done();
            });
        });
        it('Execute resolved job', (done) => {
            ctx.done = () => {
                ctx.stop();
                done();
            };
            ctx.start(async (ctx, done) => {
                return Promise.resolve(true);
            });
        });
        it('Execute rejected job', (end) => {
            ctx.done = (err, res) => {
                expect(err).to.exist;
                ctx.stop();
                end();
            };
            ctx.start((ctx, done) => {
                return Promise.reject(new Error('Rejected test'));
            });
        });
    });

    describe('#retryable', () => {
        it('Returns the date/time it is going to retry', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                retries: 2,
                retry: {
                    limit: 3,
                    interval: {
                        year: 2
                    }
                }
            });
            expect(ctx.retryable()).to.be.equal('2020-12-20T12:14:25.864Z');
        });
        it('Returns false when it have reached retry limit', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                retries: 3,
                retry: {
                    limit: 3,
                    interval: {
                        year: 2
                    }
                }
            });
            expect(ctx.retryable()).to.be.equal(false);
        });
        it('Returns false when retry is not defined', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                retries: 3
            });
            expect(ctx.retryable()).to.be.equal(false);
        });
    });

    describe('#repeatable', () => {
        it('Returns the date of next execution', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                repetitions: 2,
                repeat: {
                    limit: 3,
                    interval: {
                        year: 2
                    }
                }
            });
            expect(ctx.repeatable()).to.be.equal('2020-12-20T12:14:25.864Z');
        });
        it('Returns false when have reached its recurrency cycle', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                repetitions: 3,
                repeat: {
                    limit: 3,
                    interval: {
                        year: 2
                    }
                }
            });
            expect(ctx.repeatable()).to.be.equal(false);
        });
    });
    describe('#next', () => {
        let task = {};
        const job = (ctx, done) => {
            done();
        }
        beforeEach(() => {
            task = {
                id: '101020teste2010',
                date: '2018-12-20T12:14:25.864Z',
                job: 'compress',
                worker: 'manobi',
                retries: 0,
                data: {
                    src: 'https://images.dog.ceo/breeds/pointer-german/n02100236_1941.jpg',
                    level: 100
                }
            }
        });

        it('Increment the execution counter', () => {
            const incrementalTask = {
                ...task,
                executions: 1
            }
            const ctx = new Context(incrementalTask);
            ctx.done = () => {
                ctx.stop();
            }
            ctx.start(job);

            const next = {
                ...task,
                status: 'failed',
                error: {error: true},
                result: false,
                duration: ctx.duration,
                executions: 2,
                repetitions: 0
            };
            expect(ctx.next({error: true})).to.be.deep.equal(next);
        });

        describe('When error', () => {
            it('without retry the status is "failed"', () => {
                const ctx = new Context(task);
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...task,
                    status: 'failed',
                    error: {error: true},
                    result: false,
                    duration: ctx.duration,
                    executions: 1,
                    repetitions: 0
                };
                expect(ctx.next({error: true})).to.be.deep.equal(next);
            });

            it('with retry the status is retry and future date', () => {
                const retryableTask = {
                    ...task,
                    retries: 1,
                    retry: {
                        limit: 2,
                        interval: {
                            year: 1
                        }
                    }
                };
                const ctx = new Context(retryableTask);
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...retryableTask,
                    date: '2019-12-20T12:14:25.864Z',
                    status: 'retry',
                    error: {error: true},
                    result: false,
                    duration: ctx.duration,
                    executions: 1,
                    repetitions: 0
                };
                expect(ctx.next({error: true})).to.be.deep.equal(next);
            });
        });

        describe('When success', () => {
            it('without repeat option', () => {
                const ctx = new Context(task);
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...task,
                    status: 'complete',
                    error: null,
                    result: {success: true},
                    duration: ctx.duration,
                    executions: 1,
                    repetitions: 0
                };
                expect(ctx.next(null, {success: true})).to.be.deep.equal(next);
            });

            it('with retries reset attempts', () => {
                const ctx = new Context({
                    ...task,
                    retries: 3
                });
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...task,
                    status: 'complete',
                    retries: 0,
                    error: null,
                    result: {success: true},
                    duration: ctx.duration,
                    executions: 1,
                    repetitions: 0
                };
                expect(ctx.next(null, {success: true})).to.be.deep.equal(next);
            });

            it('with repeat status is done and future date', () => {
                const repeatableTask = {
                    ...task,
                    repetitions: 1,
                    repeat: {
                        limit: 2,
                        interval: {
                            year: 1
                        }
                    }
                };
                const ctx = new Context(repeatableTask);
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...repeatableTask,
                    date: '2019-12-20T12:14:25.864Z',
                    status: 'done',
                    error: null,
                    result: {success: true},
                    duration: ctx.duration,
                    repetitions: 1,
                    executions: 1
                };
                expect(ctx.next(null, {success: true})).to.be.deep.equal(next);
            });
        });
    });
});<commit_msg>Fix test<commit_after>const Context = require('../lib/context');
const { expect } = require('chai');
const sinon = require('sinon');

describe('Context', () => {
    describe('#startTimeout', () => {
        it('sets the execution timeout', (done) => {
            const ctx = new Context({});
            ctx.done = done;
            ctx.startTimeout();
            expect(ctx.timeout).to.not.be.equal(undefined);
            ctx.stop();
            done();
        });
    });
    describe('#start', () => {
        const ctx = new Context({});
        it('Starts a timeout', (done) => {
            ctx.done = done;
            const spy = sinon.spy(ctx, 'startTimeout');
            ctx.start((ctx, done) => {
                expect(spy.called).to.be.ok;
                ctx.stop();
                spy.restore();
                done();
            });
        });
        it('Execute sync job', (done) => {
            ctx.done = () => {
                ctx.stop();
                done();
            };
            ctx.start((ctx, done) => {
                done();
            });
        });
        it('Execute resolved job', (done) => {
            ctx.done = () => {
                ctx.stop();
                done();
            };
            ctx.start(async (ctx, done) => {
                return Promise.resolve(true);
            });
        });
        it('Execute rejected job', (end) => {
            ctx.done = (err, res) => {
                expect(err).to.exist;
                ctx.stop();
                end();
            };
            ctx.start((ctx, done) => {
                return Promise.reject(new Error('Rejected test'));
            });
        });
    });

    describe('#retryable', () => {
        it('Returns the date/time it is going to retry', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                retries: 2,
                retry: {
                    limit: 3,
                    interval: {
                        year: 2
                    }
                }
            });
            expect(ctx.retryable()).to.be.equal('2020-12-20T12:14:25.864Z');
        });
        it('Returns false when it have reached retry limit', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                retries: 3,
                retry: {
                    limit: 3,
                    interval: {
                        year: 2
                    }
                }
            });
            expect(ctx.retryable()).to.be.equal(false);
        });
        it('Returns false when retry is not defined', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                retries: 3
            });
            expect(ctx.retryable()).to.be.equal(false);
        });
    });

    describe('#repeatable', () => {
        it('Returns the date of next execution', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                repetitions: 2,
                repeat: {
                    limit: 3,
                    interval: {
                        year: 2
                    }
                }
            });
            expect(ctx.repeatable()).to.be.equal('2020-12-20T12:14:25.864Z');
        });
        it('Returns false when have reached its recurrency cycle', () => {
            const ctx = new Context({
                date: '2018-12-20T12:14:25.864Z',
                repetitions: 3,
                repeat: {
                    limit: 3,
                    interval: {
                        year: 2
                    }
                }
            });
            expect(ctx.repeatable()).to.be.equal(false);
        });
    });
    describe('#next', () => {
        let task = {};
        const job = (ctx, done) => {
            done();
        }
        beforeEach(() => {
            task = {
                id: '101020teste2010',
                date: '2018-12-20T12:14:25.864Z',
                job: 'compress',
                worker: 'manobi',
                retries: 0,
                data: {
                    src: 'https://images.dog.ceo/breeds/pointer-german/n02100236_1941.jpg',
                    level: 100
                }
            }
        });

        it('Increment the execution counter', () => {
            const incrementalTask = {
                ...task,
                executions: 1
            }
            const ctx = new Context(incrementalTask);
            ctx.done = () => {
                ctx.stop();
            }
            ctx.start(job);

            const next = {
                ...task,
                status: 'failed',
                error: {error: true},
                result: false,
                duration: ctx.duration,
                executions: 2,
                repetitions: 0
            };
            expect(ctx.next({error: true})).to.be.deep.equal(next);
        });

        describe('When error', () => {
            it('without retry the status is "failed"', () => {
                const ctx = new Context(task);
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...task,
                    status: 'failed',
                    error: {error: true},
                    result: false,
                    duration: ctx.duration,
                    executions: 1,
                    repetitions: 0
                };
                expect(ctx.next({error: true})).to.be.deep.equal(next);
            });

            it('with retry the status is retry and future date', () => {
                const retryableTask = {
                    ...task,
                    retries: 1,
                    retry: {
                        limit: 2,
                        interval: {
                            year: 1
                        }
                    }
                };
                const ctx = new Context(retryableTask);
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...retryableTask,
                    date: '2019-12-20T12:14:25.864Z',
                    status: 'retry',
                    error: {error: true},
                    result: false,
                    duration: ctx.duration,
                    executions: 1,
                    repetitions: 0
                };
                expect(ctx.next({error: true})).to.be.deep.equal(next);
            });
        });

        describe('When success', () => {
            it('without repeat option', () => {
                const ctx = new Context(task);
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...task,
                    status: 'complete',
                    error: null,
                    result: {success: true},
                    duration: ctx.duration,
                    executions: 1,
                    repetitions: 0
                };
                expect(ctx.next(null, {success: true})).to.be.deep.equal(next);
            });

            it('with retries reset attempts', () => {
                const ctx = new Context({
                    ...task,
                    retries: 3
                });
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...task,
                    status: 'complete',
                    retries: 0,
                    error: null,
                    result: {success: true},
                    duration: ctx.duration,
                    executions: 1,
                    repetitions: 0
                };
                expect(ctx.next(null, {success: true})).to.be.deep.equal(next);
            });

            it('with repeat status is done and future date', () => {
                const repeatableTask = {
                    ...task,
                    repetitions: 1,
                    repeat: {
                        limit: 2,
                        interval: {
                            year: 1
                        }
                    }
                };
                const ctx = new Context(repeatableTask);
                ctx.done = () => {
                    ctx.stop();
                }
                ctx.start(job);
                const next = {
                    ...repeatableTask,
                    date: '2019-12-20T12:14:25.864Z',
                    status: 'done',
                    error: null,
                    result: {success: true},
                    duration: ctx.duration,
                    repetitions: 1,
                    executions: 1
                };
                expect(ctx.next(null, {success: true})).to.be.deep.equal(next);
            });
        });
    });
});<|endoftext|>


<commit_before>// !ref: local://simpleavatar.js

// Avatar application. Will handle switching logic between avatar & freelook camera (clientside), and
// spawning avatars for clients (serverside). Note: this is not a startup script, but is meant to be
// placed in an entity in a scene that wishes to implement avatar functionality.

var avatar_area_size = 10;
var avatar_area_x = 0;
var avatar_area_y = 5;
var avatar_area_z = 0;

var isserver = server.IsRunning();

if (isserver == false) {
    var inputmapper = me.GetOrCreateComponent("EC_InputMapper");
    inputmapper.SetTemporary(true);
    inputmapper.contextPriority = 102;
    inputmapper.RegisterMapping("Ctrl+Tab", "ToggleCamera", 1);

    me.Action("ToggleCamera").Triggered.connect(ClientHandleToggleCamera);
} else {
    server.UserAboutToConnect.connect(ServerHandleUserAboutToConnect);
    server.UserConnected.connect(ServerHandleUserConnected);
    server.UserDisconnected.connect(ServerHandleUserDisconnected);

    // If there are connected users when this script was added, add av for all of them
    var userIdList = server.GetConnectionIDs();
    if (userIdList.length > 0)
        print("[Avatar Application] Application started. Creating avatars for logged in clients.");

    for (var i=0; i < userIdList.length; i++)
    {
        var userId = userIdList[i];
        var userConnection = server.GetUserConnection(userId);
        if (userConnection != null)
            ServerHandleUserConnected(userId, userConnection);
    }
}

function ClientHandleToggleCamera() {
    // For camera switching to work, must have both the freelookcamera & avatarcamera in the scene
    var freelookcameraentity = scene.GetEntityByName("FreeLookCamera");
    var avatarcameraentity = scene.GetEntityByName("AvatarCamera");
    var freecameralistener = freelookcameraentity.GetComponent("EC_SoundListener");
    var avatarent = scene.GetEntityByName("Avatar" + client.GetConnectionID());
    var avatarlistener = avatarent.GetComponent("EC_SoundListener");
    if ((freelookcameraentity == null) || (avatarcameraentity == null))
        return;
    var freelookcamera = freelookcameraentity.camera;
    var avatarcamera = avatarcameraentity.camera;

    if (avatarcamera.IsActive()) {
        var trans = freelookcameraentity.placeable.transform;
        trans.pos = avatarcameraentity.placeable.GetWorldPosition();
        trans.rot = avatarcameraentity.placeable.GetWorldOrientationEuler();

        // If there is roll in the rotation, adjust it away
        if (trans.rot.z > 170.0)
        {
            trans.rot.x -= 180.0;
            trans.rot.z = 0;
            trans.rot.y = -90.0 - (90.0 + trans.rot.y);
        }
        if (trans.rot.z < -170.0)
        {
            trans.rot.x += 180.0;
            trans.rot.z = 0;
            trans.rot.y = -90.0 - (90.0 + trans.rot.y);
        }

        freelookcameraentity.placeable.transform = trans;
        freelookcamera.SetActive();
        freecameralistener.active = true;
        avatarlistener.active = false;
    } else {
        avatarcamera.SetActive();
        avatarlistener.active = true;
        freecameralistener.active = false;
    }

    // Ask entity to check his camera state
    avatarent.Exec(1, "CheckState");
}

function ServerHandleUserAboutToConnect(connectionID, user) {
    // Uncomment to test access control
    //if (user.GetProperty("password") != "xxx")
    //    user.DenyConnection();
}

function ServerHandleUserConnected(connectionID, user) {
    var avatarEntityName = "Avatar" + connectionID;

    // Create necessary components to the avatar entity:
    // - Script for the main avatar script simpleavatar.js
    // - Placeable for position
    // - AnimationController for skeletal animation control
    // - DynamicComponent for holding disabled/enabled avatar features
    var avatarEntity = scene.CreateEntity(scene.NextFreeId(), ["EC_Script", "EC_Placeable", "EC_AnimationController", "EC_DynamicComponent"]);
    avatarEntity.SetTemporary(true); // We never want to save the avatar entities to disk.
    avatarEntity.SetName(avatarEntityName);

    if (user != null) {
	avatarEntity.SetDescription(user.GetProperty("username"));
    }

    var script = avatarEntity.script;
    script.type = "js";
    script.runOnLoad = true;
    var r = script.scriptRef;
    r.ref = "local://simpleavatar.js";
    script.scriptRef = r;

    // Simpleavatar.js implements the basic avatar movement and animation.
    // Also load an additional script file to the same entity to demonstrate adding features to the avatar.
    var script2 = avatarEntity.GetOrCreateComponent("EC_Script", "Addon", 0, true);
    script2.type = "js"
    script2.runOnLoad = true;
    r = script2.scriptRef;
    r.ref = "local://exampleavataraddon.js";
    script2.scriptRef = r;

    // Set random starting position for avatar
    var placeable = avatarEntity.placeable;
    var transform = placeable.transform;
    transform.pos.x = (Math.random() - 0.5) * avatar_area_size + avatar_area_x;
    transform.pos.y = avatar_area_y;
    transform.pos.z = (Math.random() - 0.5) * avatar_area_size + avatar_area_z;
    placeable.transform = transform;

    scene.EmitEntityCreatedRaw(avatarEntity);

    if (user != null) {
        print("[Avatar Application] Created avatar for " + user.GetProperty("username"));
    }
}

function ServerHandleUserDisconnected(connectionID, user) {
    var avatarEntityName = "Avatar" + connectionID;
    var avatarEntity = scene.GetEntityByName(avatarEntityName);
    if (avatarEntity != null) {
        var entityID = avatarEntity.id;
        scene.RemoveEntityRaw(entityID);

        if (user != null) {
        print("[Avatar Application] User " + user.GetProperty("username") + " disconnected, destroyed avatar entity.");
        }
    }
}
<commit_msg>Fixed avatarapplication scripts to new math library.<commit_after>// !ref: local://simpleavatar.js

// Avatar application. Will handle switching logic between avatar & freelook camera (clientside), and
// spawning avatars for clients (serverside). Note: this is not a startup script, but is meant to be
// placed in an entity in a scene that wishes to implement avatar functionality.

var avatar_area_size = 10;
var avatar_area_x = 0;
var avatar_area_y = 5;
var avatar_area_z = 0;

var isserver = server.IsRunning();

if (isserver == false) {
    var inputmapper = me.GetOrCreateComponent("EC_InputMapper");
    inputmapper.SetTemporary(true);
    inputmapper.contextPriority = 102;
    inputmapper.RegisterMapping("Ctrl+Tab", "ToggleCamera", 1);

    me.Action("ToggleCamera").Triggered.connect(ClientHandleToggleCamera);
} else {
    server.UserAboutToConnect.connect(ServerHandleUserAboutToConnect);
    server.UserConnected.connect(ServerHandleUserConnected);
    server.UserDisconnected.connect(ServerHandleUserDisconnected);

    // If there are connected users when this script was added, add av for all of them
    var userIdList = server.GetConnectionIDs();
    if (userIdList.length > 0)
        print("[Avatar Application] Application started. Creating avatars for logged in clients.");

    for (var i=0; i < userIdList.length; i++)
    {
        var userId = userIdList[i];
        var userConnection = server.GetUserConnection(userId);
        if (userConnection != null)
            ServerHandleUserConnected(userId, userConnection);
    }
}

function ClientHandleToggleCamera() {
    // For camera switching to work, must have both the freelookcamera & avatarcamera in the scene
    var freelookcameraentity = scene.GetEntityByName("FreeLookCamera");
    var avatarcameraentity = scene.GetEntityByName("AvatarCamera");
    var freecameralistener = freelookcameraentity.GetComponent("EC_SoundListener");
    var avatarent = scene.GetEntityByName("Avatar" + client.GetConnectionID());
    var avatarlistener = avatarent.GetComponent("EC_SoundListener");
    if ((freelookcameraentity == null) || (avatarcameraentity == null))
        return;
    var freelookcamera = freelookcameraentity.camera;
    var avatarcamera = avatarcameraentity.camera;

    if (avatarcamera.IsActive()) {
        var trans = freelookcameraentity.placeable.transform;
        trans.pos = avatarcameraentity.placeable.WorldPosition();
        trans.rot = avatarcameraentity.placeable.WorldOrientation().ToEulerZYX();

        // If there is roll in the rotation, adjust it away
        if (trans.rot.z > 170.0)
        {
            trans.rot.x -= 180.0;
            trans.rot.z = 0;
            trans.rot.y = -90.0 - (90.0 + trans.rot.y);
        }
        if (trans.rot.z < -170.0)
        {
            trans.rot.x += 180.0;
            trans.rot.z = 0;
            trans.rot.y = -90.0 - (90.0 + trans.rot.y);
        }

        freelookcameraentity.placeable.transform = trans;
        freelookcamera.SetActive();
        freecameralistener.active = true;
        avatarlistener.active = false;
    } else {
        avatarcamera.SetActive();
        avatarlistener.active = true;
        freecameralistener.active = false;
    }

    // Ask entity to check his camera state
    avatarent.Exec(1, "CheckState");
}

function ServerHandleUserAboutToConnect(connectionID, user) {
    // Uncomment to test access control
    //if (user.GetProperty("password") != "xxx")
    //    user.DenyConnection();
}

function ServerHandleUserConnected(connectionID, user) {
    var avatarEntityName = "Avatar" + connectionID;

    // Create necessary components to the avatar entity:
    // - Script for the main avatar script simpleavatar.js
    // - Placeable for position
    // - AnimationController for skeletal animation control
    // - DynamicComponent for holding disabled/enabled avatar features
    var avatarEntity = scene.CreateEntity(scene.NextFreeId(), ["EC_Script", "EC_Placeable", "EC_AnimationController", "EC_DynamicComponent"]);
    avatarEntity.SetTemporary(true); // We never want to save the avatar entities to disk.
    avatarEntity.SetName(avatarEntityName);

    if (user != null) {
	avatarEntity.SetDescription(user.GetProperty("username"));
    }

    var script = avatarEntity.script;
    script.type = "js";
    script.runOnLoad = true;
    var r = script.scriptRef;
    r.ref = "local://simpleavatar.js";
    script.scriptRef = r;

    // Simpleavatar.js implements the basic avatar movement and animation.
    // Also load an additional script file to the same entity to demonstrate adding features to the avatar.
    var script2 = avatarEntity.GetOrCreateComponent("EC_Script", "Addon", 0, true);
    script2.type = "js"
    script2.runOnLoad = true;
    r = script2.scriptRef;
    r.ref = "local://exampleavataraddon.js";
    script2.scriptRef = r;

    // Set random starting position for avatar
    var placeable = avatarEntity.placeable;
    var transform = placeable.transform;
    transform.pos.x = (Math.random() - 0.5) * avatar_area_size + avatar_area_x;
    transform.pos.y = avatar_area_y;
    transform.pos.z = (Math.random() - 0.5) * avatar_area_size + avatar_area_z;
    placeable.transform = transform;

    scene.EmitEntityCreatedRaw(avatarEntity);

    if (user != null) {
        print("[Avatar Application] Created avatar for " + user.GetProperty("username"));
    }
}

function ServerHandleUserDisconnected(connectionID, user) {
    var avatarEntityName = "Avatar" + connectionID;
    var avatarEntity = scene.GetEntityByName(avatarEntityName);
    if (avatarEntity != null) {
        var entityID = avatarEntity.id;
        scene.RemoveEntityRaw(entityID);

        if (user != null) {
        print("[Avatar Application] User " + user.GetProperty("username") + " disconnected, destroyed avatar entity.");
        }
    }
}
<|endoftext|>