<commit_before>const models = require('./models')
const moment = require('moment')
const i18n = require('i18n')
const DeadlineMail = require('./modules/mail/deadline')
const TaskMail = require('./modules/mail/task')

i18n.configure({
  directory: process.env.NODE_ENV !== 'production' ? `${__dirname}/locales` : `${__dirname}/locales/result`,
  locales: process.env.NODE_ENV !== 'production' ? ['en'] : ['en', 'br'],
  defaultLocale: 'en',
  updateFiles: false
})

i18n.init()

const Report = {
  montlyBounties: async () => {
    const tasks = await models.Task.findAll({ where: {
      value: {
        $gt: 0
      }
    },
    include: [ models.User ]
    })
    // eslint-disable-next-line no-console
    // console.log('tasks from cron job weekly bounties', tasks)
    if (tasks[0]) {
      const taskSort = tasks.sort((ta, tb) => {
        return Math.abs(new Date(ta.created_at) - new Date(tb.created_at))
      })
      let currentObj = {}
      taskSort.forEach((t) => {
        const currentDate = t.createdAt.toLocaleString('en-GB', { month: 'long' })
        currentObj[currentDate] = 0
      })
      taskSort.forEach((t) => {
        const currentDate = t.createdAt.toLocaleString('en-GB', { month: 'long' })
        currentObj[currentDate] += parseInt(t.value)
      })
      // eslint-disable-next-line no-console
      console.log('tasks', currentObj, taskSort.map((t) => t.value))
    }
    return new Error('no issues found')
  },
  latestTasks: async () => {
    const tasks = await models.Task.findAll({
      where: {
        assigned: {
          $eq: null
        },
        status: {
          $eq: 'open'
        }
      },
      limit: 5,
      order: [['id', 'DESC']],
      include: [ models.User ]
    })
    // eslint-disable-next-line no-console
    console.log('tasks from cron job latest tasks', tasks)
    if (tasks[0]) {
      TaskMail.weeklyLatest({ tasks })
    }
    return tasks
  },
  rememberDeadline: async () => {
    const tasks = await models.Task.findAll({ where: {
      status: 'in_progress',
      deadline: {
        $lt: moment(new Date()).format(),
        $gt: moment(new Date()).subtract(2, 'days').format()
      }
    },
    include: [ models.User ]
    })
    // eslint-disable-next-line no-console
    console.log('tasks from cron job to remember deadline', tasks)
    if (tasks[0]) {
      tasks.map(async t => {
        if (t.assigned) {
          if (t.dataValues && t.assigned) {
            const userAssigned = await models.Assign.findAll({ where: { id: t.assigned }, include: [models.User] })
            if (userAssigned[0].dataValues) {
              DeadlineMail.deadlineEndOwner(t.User.dataValues, t.dataValues, t.User.name || t.User.username)
              DeadlineMail.deadlineEndAssigned(userAssigned[0].dataValues.User, t.dataValues, userAssigned[0].dataValues.User.dataValues.name)
            }
          }
        }
      })
    }
    return tasks
  }
}

Report.montlyBounties()

// module.exports = { Report }
<commit_msg>reporting of users<commit_after>const models = require('./models')
const moment = require('moment')
const i18n = require('i18n')
const DeadlineMail = require('./modules/mail/deadline')
const TaskMail = require('./modules/mail/task')

i18n.configure({
  directory: process.env.NODE_ENV !== 'production' ? `${__dirname}/locales` : `${__dirname}/locales/result`,
  locales: process.env.NODE_ENV !== 'production' ? ['en'] : ['en', 'br'],
  defaultLocale: 'en',
  updateFiles: false
})

i18n.init()

const Report = {
  montlyBounties: async () => {
    const tasks = await models.Task.findAll({ where: {
      value: {
        $gt: 0
      }
    },
    include: [ models.User ]
    })
    // eslint-disable-next-line no-console
    // console.log('tasks from cron job weekly bounties', tasks)
    if (tasks[0]) {
      const taskSort = tasks.sort((ta, tb) => {
        return Math.abs(new Date(ta.created_at) - new Date(tb.created_at))
      })
      let currentObj = {}
      let total = 0
      taskSort.forEach((t) => {
        const currentDate = t.createdAt.toLocaleString('en-GB', { month: 'long', year: 'numeric' })
        currentObj[currentDate] = 0
      })
      taskSort.forEach((t) => {
        const currentDate = t.createdAt.toLocaleString('en-GB', { month: 'long', year: 'numeric' })
        currentObj[currentDate] += parseInt(t.value)
        total += parseInt(t.value)
      })
      // eslint-disable-next-line no-console
      currentObj.total = total
      console.log('tasks', currentObj)
    }
    return new Error('no issues found')
  },
  montlyUsers: async () => {
    const users = await models.User.findAll({
      where: {},
      order: [['id', 'DESC']]
    })

    if (users[0]) {
      const usersSort = users.sort((ua, ub) => {
        return Math.abs(new Date(ua.created_at) - new Date(ub.created_at))
      })
      let currentObj = {}
      let total = 0
      usersSort.forEach((t) => {
        const currentDate = t.createdAt.toLocaleString('en-GB', { month: 'long', year: 'numeric' })
        currentObj[currentDate] = 0
      })
      usersSort.forEach((t) => {
        const currentDate = t.createdAt.toLocaleString('en-GB', { month: 'long', year: 'numeric' })
        currentObj[currentDate] += 1
        total += 1
      })
      // eslint-disable-next-line no-console
      currentObj.total = total
      console.log('users', currentObj)
    }
    return new Error('no issues found')

  },
  rememberDeadline: async () => {
    const tasks = await models.Task.findAll({ where: {
      status: 'in_progress',
      deadline: {
        $lt: moment(new Date()).format(),
        $gt: moment(new Date()).subtract(2, 'days').format()
      }
    },
    include: [ models.User ]
    })
    // eslint-disable-next-line no-console
    console.log('tasks from cron job to remember deadline', tasks)
    if (tasks[0]) {
      tasks.map(async t => {
        if (t.assigned) {
          if (t.dataValues && t.assigned) {
            const userAssigned = await models.Assign.findAll({ where: { id: t.assigned }, include: [models.User] })
            if (userAssigned[0].dataValues) {
              DeadlineMail.deadlineEndOwner(t.User.dataValues, t.dataValues, t.User.name || t.User.username)
              DeadlineMail.deadlineEndAssigned(userAssigned[0].dataValues.User, t.dataValues, userAssigned[0].dataValues.User.dataValues.name)
            }
          }
        }
      })
    }
    return tasks
  }
}

Report.montlyBounties()

Report.montlyUsers()

// module.exports = { Report }
<|endoftext|>


<commit_before>var fs = require("fs");
var termui = require("./src/termui.js");
var p3js = require("./src/p3js/p3js.js");

var p3sim = new p3js.Simulator();

if (!process.stdin.isTTY || !process.stdout.isTTY) {
	process.stdout.write("no TTY");
}

if (typeof process.argv[2] != "undefined") {
	termui.initialize(p3sim);
	fs.readFile(process.argv[2], "utf8", function (err, data) {
		var result = p3js.assembly.assembleWithDefaultValidator(data);
		p3sim.loadMemory(result.buffer);
		p3sim.start();
	});
} else {
	console.log("No input file.");
}
<commit_msg>Cleanup offline simulator code (better error handling)<commit_after>var fs = require("fs");
var path = require("path");

var termui = require("./src/termui.js");
var p3js = require("./src/p3js/p3js.js");

var argv = require("minimist")(process.argv.slice(2), {
	unknown: function(opt) {
		if (opt[0] == '-') {
			console.error("Unknown option '" + opt + "'.");
			process.exit(1);
			return false;
		}
	}
});

var p3sim = new p3js.Simulator();

if (!process.stdin.isTTY || !process.stdout.isTTY) {
	console.error("Not connected to a tty.");
	process.exit(2);
}

if (typeof argv._[0] == "undefined") {
	console.error("No input file.");
	process.exit(3);
}

try {
	var data = fs.readFileSync(path.normalize(argv._[0]), "utf8");
} catch (e) {
	console.error(e.message);
	process.exit(4);
}

try {
	var result = p3js.assembly.assembleWithDefaultValidator(data);
} catch (e) {
	console.error("Assembly Error: " + e);
	process.exit(5);
}

termui.initialize(p3sim);
p3sim.loadMemory(result.buffer);
p3sim.start();
<|endoftext|>


<commit_before>steal('can/util', 'can/construct', function(can, Construct){
var isFunction = can.isFunction,
	isArray = can.isArray,
	makeArray = can.makeArray,

proxy = function( funcs ) {

			//args that should be curried
			var args = makeArray(arguments),
				self;

			// get the functions to callback
			funcs = args.shift();

			// if there is only one function, make funcs into an array
			if (!isArray(funcs) ) {
				funcs = [funcs];
			}

			// keep a reference to us in self
			self = this;

			//!steal-remove-start
			for( var i =0; i< funcs.length;i++ ) {
				if(typeof funcs[i] == "string" && !isFunction(this[funcs[i]])){
					throw ("class.js "+( this.fullName || this.Class.fullName)+" does not have a "+funcs[i]+"method!");
				}
			}
			//!steal-remove-end
			return function class_cb() {
				// add the arguments after the curried args
				var cur = args.concat(makeArray(arguments)),
					isString,
					length = funcs.length,
					f = 0,
					func;

				// go through each function to call back
				for (; f < length; f++ ) {
					func = funcs[f];
					if (!func ) {
						continue;
					}

					// set called with the name of the function on self (this is how this.view works)
					isString = typeof func == "string";

					// call the function
					cur = (isString ? self[func] : func).apply(self, cur || []);

					// pass the result to the next function (if there is a next function)
					if ( f < length - 1 ) {
						cur = !isArray(cur) || cur._use_call ? [cur] : cur
					}
				}
				return cur;
			}
		}
	can.Construct.proxy = can.Construct.prototype.proxy = proxy;
	// this corrects the case where can/control loads after can/construct/proxy, so static props don't have proxy
	var correctedClasses = [can.Control, can.Model],
		i = 0;
	for (; i < correctedClasses.length; i++ ) {
		if(correctedClasses[i]){
			correctedClasses[i].proxy = proxy;
		}
	}
	return can;
});
<commit_msg>adding can.Observe.proxy in the case that can/observe loads before can/construct/proxy<commit_after>steal('can/util', 'can/construct', function(can, Construct){
var isFunction = can.isFunction,
	isArray = can.isArray,
	makeArray = can.makeArray,

proxy = function( funcs ) {

			//args that should be curried
			var args = makeArray(arguments),
				self;

			// get the functions to callback
			funcs = args.shift();

			// if there is only one function, make funcs into an array
			if (!isArray(funcs) ) {
				funcs = [funcs];
			}

			// keep a reference to us in self
			self = this;

			//!steal-remove-start
			for( var i =0; i< funcs.length;i++ ) {
				if(typeof funcs[i] == "string" && !isFunction(this[funcs[i]])){
					throw ("class.js "+( this.fullName || this.Class.fullName)+" does not have a "+funcs[i]+"method!");
				}
			}
			//!steal-remove-end
			return function class_cb() {
				// add the arguments after the curried args
				var cur = args.concat(makeArray(arguments)),
					isString,
					length = funcs.length,
					f = 0,
					func;

				// go through each function to call back
				for (; f < length; f++ ) {
					func = funcs[f];
					if (!func ) {
						continue;
					}

					// set called with the name of the function on self (this is how this.view works)
					isString = typeof func == "string";

					// call the function
					cur = (isString ? self[func] : func).apply(self, cur || []);

					// pass the result to the next function (if there is a next function)
					if ( f < length - 1 ) {
						cur = !isArray(cur) || cur._use_call ? [cur] : cur
					}
				}
				return cur;
			}
		}
	can.Construct.proxy = can.Construct.prototype.proxy = proxy;
	// this corrects the case where can/control loads after can/construct/proxy, so static props don't have proxy
	var correctedClasses = [can.Observe, can.Control, can.Model],
		i = 0;
	for (; i < correctedClasses.length; i++ ) {
		if(correctedClasses[i]){
			correctedClasses[i].proxy = proxy;
		}
	}
	return can;
});
<|endoftext|>


<commit_before>
var url = require('url');
var http = require('http');
var https = require('https');

var extend = require('extend');

/**
 * Default options for the request.
 * These options are extend()'ed by the main method.
**/
var defaultOptions = {
    'protocol': 'http:',
    'method': 'HEAD',
    'headers': {
        'Connection': 'close'
    }
};

/**
 * Settings options. For now only timeout settings :)
**/
var defaultSetOptions = {
    'timeout': {
        'enabled': false,
        'when': 2500
    }
};

/**
 * Request method. The callback uses the default (error, response) style.
 * The options are extend()'ed with the defaultOptions above.
 * Returns an http(s) error or a default http(S) response with the
 * `response.elapsed` var added whith contains the response time in ms.
**/
exports = module.exports = function(options, setOptions, callback) {
    if(!callback && typeof setOptions === 'function') {
        callback = setOptions;
        setOptions = {};
    }
    setOptions = extend(true, defaultSetOptions, setOptions);

    var elapsed = 0;
    if(typeof options === 'string') options = url.parse(options);
    options = extend(true, defaultOptions, options);
    if(!options.port) options.port = (options.protocol.indexOf('https') != -1) ? 443 : 80;
    var protocol = (options.protocol.indexOf('https') != -1) ? https : http;

    var request = protocol.request(options, function(response) {
        response.elapsed = process.hrtime(elapsed)[1] / 1000000;
        return callback(null, response);
    });

    if(setOptions.timeout.enabled) {
        request.setTimeout(setOptions.timeout.when);
    }

    request.on('error', function(error) {
        return callback(error);
    });

    request.on('socket', function(error) {
        elapsed = process.hrtime();
    });

    request.on('timeout', function(error) {
        return callback(new Error('Request timed out'));
    });

    request.end();

};
<commit_msg>Simplify timeout settings<commit_after>
var url = require('url');
var http = require('http');
var https = require('https');

var extend = require('extend');

/**
 * Default options for the request.
 * These options are extend()'ed by the main method.
**/
var defaultOptions = {
    'protocol': 'http:',
    'method': 'HEAD',
    'headers': {
        'Connection': 'close'
    }
};

/**
 * Settings options. For now only timeout settings :)
**/
var defaultSetOptions = {
    'timeout': 2500
};

/**
 * Request method. The callback uses the default (error, response) style.
 * The options are extend()'ed with the defaultOptions above.
 * Returns an http(s) error or a default http(S) response with the
 * `response.elapsed` var added whith contains the response time in ms.
**/
exports = module.exports = function(options, setOptions, callback) {
    if(!callback && typeof setOptions === 'function') {
        callback = setOptions;
        setOptions = {};
    }
    setOptions = extend(true, defaultSetOptions, setOptions);

    var elapsed = 0;
    if(typeof options === 'string') options = url.parse(options);
    options = extend(true, defaultOptions, options);
    if(!options.port) options.port = (options.protocol.indexOf('https') != -1) ? 443 : 80;
    var protocol = (options.protocol.indexOf('https') != -1) ? https : http;

    var request = protocol.request(options, function(response) {
        response.elapsed = process.hrtime(elapsed)[1] / 1000000;
        return callback(null, response);
    });

    if(setOptions.timeout && setOptions.timeout > 0) {
        request.setTimeout(setOptions.timeout);
    }

    request.on('error', function(error) {
        return callback(error);
    });

    request.on('socket', function(error) {
        elapsed = process.hrtime();
    });

    request.on('timeout', function(error) {
        return callback(new Error('Request timed out'));
    });

    request.end();

};
<|endoftext|>


<commit_before>const uuid = require('uuid')
let api

module.exports = class Connection {
  /**
   * The generic represenation of a connection for all server types is an ActionHero.Connection.  You will never be creating these yourself via an action or task, but you will find them in your Actons and Action Middleware.
   * All Connections share common properties:
   *
   * @class ActionHero.Connection
   *
   * @property {string} id             - A unique string identifer for this connection.
   * @property {string} fingerprint    - A unique string identifer for this connection, but common among subsequent requests.  For example, all web requests from the same client have the same fingerprint, but not the same id.
   * @property {string} type           - The type of this connection (web, websocket, etc) as defined by the name of the server which created it.
   * @property {Array} rooms           - Any rooms this connection is a member of, it it can chat.
   * @property {Boolean} canChat       - Can this connection use the chat system?
   * @property {Object} params         - Any params this connection has saved for use in subsequent Actions.
   * @property {Number} pendingActions - How many actions are currently running for this connection?  Most server types have a limit.
   * @property {Number} totalActions   - How many actions has this connection run since it connected.
   * @property {Number} messageId      - The Id of the latest message this connection has sent to the server.
   * @property {Number} connectedAt    - The timestamp of when this connection was created.
   * @property {string} remoteIP       - The remote connection's IP address (as best as we can tell).  May be either IPv4 or IPv6.
   * @property {Number} remotePort     - The remote connection's port.
   * @property {Object} rawConnection  - Any connection-specific properties.  For, example, the HTTP res and req objects for `web` connections are here
   *
   * @see ActionHero.Server
   *
   * @param  {Object} data The specifics of this connection
   * @param  {Boolean} syncSetup The specifics of this connection initializing sync or async
   */
  constructor (data, syncSetup = true) {
    // Only in files required by `index.js` do we need to delay the loading of the API object
    // This is due to cyclical require issues
    api = require('./../index.js').api

    this.setup(data)

    if (syncSetup) {
      this.constructor.callConnectionCreateMethods(this)
    }

    api.connections.connections[this.id] = this
  }

  /**
   * @async
   * @static
   * @function createAsync
   * @memberof ActionHero.Connection
   * @param  {Object}  data The specifics of this connection
   */
  static async createAsync (data) {
    let connection = new this(data, false)

    await this.callConnectionCreateMethods(connection)

    return connection
  }

  /**
   * @private
   * @async
   * @static
   * @function callConnectionCreateMethods
   * @memberof ActionHero.Connection
   * @param  {Object}  connection ActionHero.Connection
   */
  static async callConnectionCreateMethods (connection) {
    for (let i in api.connections.globalMiddleware) {
      let middlewareName = api.connections.globalMiddleware[i]
      if (typeof api.connections.middleware[middlewareName].create === 'function') {
        await api.connections.middleware[middlewareName].create(connection)
      }
    }
  }

  setup (data) {
    if (data.id) {
      this.id = data.id
    } else {
      this.id = this.generateID()
    }
    this.connectedAt = new Date().getTime();

    ['type', 'rawConnection'].forEach((req) => {
      if (data[req] === null || data[req] === undefined) { throw new Error(`${req} is required to create a new connection object`) }
      this[req] = data[req]
    });

    ['remotePort', 'remoteIP'].forEach((req) => {
      if (data[req] === null || data[req] === undefined) {
        if (api.config.general.enforceConnectionProperties === true) {
          throw new Error(`${req} is required to create a new connection object`)
        } else {
          data[req] = 0 // could be a random uuid as well?
        }
      }
      this[req] = data[req]
    })

    const connectionDefaults = {
      error: null,
      fingerprint: this.id,
      rooms: [],
      params: {},
      pendingActions: 0,
      totalActions: 0,
      messageId: 0,
      canChat: false
    }

    for (let i in connectionDefaults) {
      if (this[i] === undefined && data[i] !== undefined) { this[i] = data[i] }
      if (this[i] === undefined) { this[i] = connectionDefaults[i] }
    }

    let connection = this
    let server = api.servers.servers[connection.type]
    if (server && server.connectionCustomMethods) {
      for (let name in server.connectionCustomMethods) {
        connection[name] = async (...args) => {
          args.unshift(connection)
          return server.connectionCustomMethods[name].apply(null, args)
        }
      }
    }

    api.i18n.invokeConnectionLocale(this)
  }

  /**
   * Localize a key for this connection's locale.  Keys usually look like `messages.errors.notFound`, and are defined in your locales directory.  Strings can be interploated as well, connection.localize('the count was {{count}}', {count: 4})
   *
   * @function localize
   * @memberof ActionHero.Connection
   * @param  {String} message The mesage key to be translated.
   * @return {String}         The translated message.  If an appropraite translation cannot be found, the key will be returned.
   * @see api.i18n
   */
  localize (message) {
    // this.locale will be sourced automatically
    return api.i18n.localize(message, this)
  }

  /**
   * Send a file to a connection (usually in the context of an Action).  Be sure to set `data.toRender = false` in the action!
   * Uses Server#processFile and will set `connection.params.file = path`
   *
   * @function sendFile
   * @memberof ActionHero.Connection
   * @param  {String} path The path of the file to send, within one of your `api.config.general.paths.public` directories.
   * @tutorial file-server
   */

  /**
   * Send a message to a connection.  Uses Server#sendMessage.
   *
   * @function sendMessage
   * @memberof ActionHero.Connection
   * @param  {String} message The message to send.  Can be an Object or String... but it depends on the server in use.
   */

  generateID () {
    return uuid.v4()
  }

  /**
   * Destroys the connection.  If the type/sever of the connection has a goodbye message, it will be sent.  The connection will be removed from all rooms.  The connection's socket will be closed when possible.
   *
   * @function destroy
   * @memberof ActionHero.Connection
   */
  async destroy () {
    this.destroyed = true

    for (let i in api.connections.globalMiddleware) {
      let middlewareName = api.connections.globalMiddleware[i]
      if (typeof api.connections.middleware[middlewareName].destroy === 'function') {
        await api.connections.middleware[middlewareName].destroy(this)
      }
    }

    if (this.canChat === true) {
      let promises = []
      for (let i in this.rooms) {
        let room = this.rooms[i]
        promises.push(api.chatRoom.removeMember(this.id, room))
      }
      await Promise.all(promises)
    }

    const server = api.servers.servers[this.type]

    if (server) {
      if (server.attributes.logExits === true) {
        server.log('connection closed', 'info', { to: this.remoteIP })
      }
      if (typeof server.goodbye === 'function') { server.goodbye(this) }
    }

    delete api.connections.connections[this.id]
  }

  /**
   * @private
   * @function set
   * @memberof ActionHero.Connection
   */
  set (key, value) {
    this[key] = value
  }

  /**
   * Try to run a verb command for a connection
   *
   * @function verbs
   * @memberof ActionHero.Connection
   * @async
   * @private
   * @param  {String}  verb  The verb
   * @param  {Array}   words All the arguments sent by the client
   * @return {Promise}
   */
  async verbs (verb, words) {
    let key
    let value
    let room
    const server = api.servers.servers[this.type]
    const allowedVerbs = server.attributes.verbs

    if (!(words instanceof Array)) { words = [words] }

    if (server && allowedVerbs.indexOf(verb) >= 0) {
      server.log('verb', 'debug', { verb: verb, to: this.remoteIP, params: JSON.stringify(words) })

      // TODO: investigate allowedVerbs being an array of Constatnts or Symbols

      switch (verb) {
        case 'quit':
        case 'exit':
          return this.destroy()
        case 'paramAdd':
          key = words[0]
          value = words[1]
          if ((words[0]) && (words[0].indexOf('=') >= 0)) {
            let parts = words[0].split('=')
            key = parts[0]
            value = parts[1]
          }

          if (api.config.general.disableParamScrubbing || api.params.postVariables.indexOf(key) >= 0) {
            this.params[key] = value
          }
          return
        case 'paramDelete':
          key = words[0]
          delete this.params[key]
          return
        case 'paramView':
          key = words[0]
          return this.params[key]
        case 'paramsView':
          return this.params
        case 'paramsDelete':
          for (let i in this.params) { delete this.params[i] }
          return
        case 'roomAdd':
          room = words[0]
          return api.chatRoom.addMember(this.id, room)
        case 'roomLeave':
          room = words[0]
          return api.chatRoom.removeMember(this.id, room)
        case 'roomView':
          room = words[0]
          if (this.rooms.indexOf(room) >= 0) {
            return api.chatRoom.roomStatus(room)
          }
          let error = new Error(await api.config.errors.connectionNotInRoom(this, room))
          throw error
        case 'detailsView':
          return {
            id: this.id,
            fingerprint: this.fingerprint,
            remoteIP: this.remoteIP,
            remotePort: this.remotePort,
            params: this.params,
            connectedAt: this.connectedAt,
            rooms: this.rooms,
            totalActions: this.totalActions,
            pendingActions: this.pendingActions
          }
        case 'documentation':
          return api.documentation.documentation
        case 'say':
          room = words.shift()
          await api.chatRoom.broadcast(this, room, words.join(' '))
          return
      }

      let error = new Error(await api.config.errors.verbNotFound(this, verb))
      error.verbNotFound = true
      throw error
    } else {
      let error = new Error(await api.config.errors.verbNotAllowed(this, verb))
      error.verbNotFound = true
      throw error
    }
  }
}
<commit_msg>wording<commit_after>const uuid = require('uuid')
let api

module.exports = class Connection {
  /**
   * The generic represenation of a connection for all server types is an ActionHero.Connection.  You will never be creating these yourself via an action or task, but you will find them in your Actons and Action Middleware.
   * All Connections share common properties:
   *
   * @class ActionHero.Connection
   *
   * @property {string} id             - A unique string identifer for this connection.
   * @property {string} fingerprint    - A unique string identifer for this connection, but common among subsequent requests.  For example, all web requests from the same client have the same fingerprint, but not the same id.
   * @property {string} type           - The type of this connection (web, websocket, etc) as defined by the name of the server which created it.
   * @property {Array} rooms           - Any rooms this connection is a member of, it it can chat.
   * @property {Boolean} canChat       - Can this connection use the chat system?
   * @property {Object} params         - Any params this connection has saved for use in subsequent Actions.
   * @property {Number} pendingActions - How many actions are currently running for this connection?  Most server types have a limit.
   * @property {Number} totalActions   - How many actions has this connection run since it connected.
   * @property {Number} messageId      - The Id of the latest message this connection has sent to the server.
   * @property {Number} connectedAt    - The timestamp of when this connection was created.
   * @property {string} remoteIP       - The remote connection's IP address (as best as we can tell).  May be either IPv4 or IPv6.
   * @property {Number} remotePort     - The remote connection's port.
   * @property {Object} rawConnection  - Any connection-specific properties.  For, example, the HTTP res and req objects for `web` connections are here
   *
   * @see ActionHero.Server
   *
   * @param  {Object} data The specifics of this connection
   * @param  {Boolean} callCreateMethods The specifics of this connection will calls create methods in the constructor. This property will exist for backward compatibility. If you want to construct connection and call create methods within async, you can use `await ActionHero.Connection.createAsync(details)` for construction.
   */
  constructor (data, callCreateMethods = true) {
    // Only in files required by `index.js` do we need to delay the loading of the API object
    // This is due to cyclical require issues
    api = require('./../index.js').api

    this.setup(data)

    if (callCreateMethods) {
      this.constructor.callConnectionCreateMethods(this)
    }

    api.connections.connections[this.id] = this
  }

  /**
   * @async
   * @static
   * @function createAsync
   * @memberof ActionHero.Connection
   * @param  {Object}  data The specifics of this connection
   */
  static async createAsync (data) {
    let connection = new this(data, false)

    await this.callConnectionCreateMethods(connection)

    return connection
  }

  /**
   * @private
   * @async
   * @static
   * @function callConnectionCreateMethods
   * @memberof ActionHero.Connection
   * @param  {Object}  connection ActionHero.Connection
   */
  static async callConnectionCreateMethods (connection) {
    for (let i in api.connections.globalMiddleware) {
      let middlewareName = api.connections.globalMiddleware[i]
      if (typeof api.connections.middleware[middlewareName].create === 'function') {
        await api.connections.middleware[middlewareName].create(connection)
      }
    }
  }

  setup (data) {
    if (data.id) {
      this.id = data.id
    } else {
      this.id = this.generateID()
    }
    this.connectedAt = new Date().getTime();

    ['type', 'rawConnection'].forEach((req) => {
      if (data[req] === null || data[req] === undefined) { throw new Error(`${req} is required to create a new connection object`) }
      this[req] = data[req]
    });

    ['remotePort', 'remoteIP'].forEach((req) => {
      if (data[req] === null || data[req] === undefined) {
        if (api.config.general.enforceConnectionProperties === true) {
          throw new Error(`${req} is required to create a new connection object`)
        } else {
          data[req] = 0 // could be a random uuid as well?
        }
      }
      this[req] = data[req]
    })

    const connectionDefaults = {
      error: null,
      fingerprint: this.id,
      rooms: [],
      params: {},
      pendingActions: 0,
      totalActions: 0,
      messageId: 0,
      canChat: false
    }

    for (let i in connectionDefaults) {
      if (this[i] === undefined && data[i] !== undefined) { this[i] = data[i] }
      if (this[i] === undefined) { this[i] = connectionDefaults[i] }
    }

    let connection = this
    let server = api.servers.servers[connection.type]
    if (server && server.connectionCustomMethods) {
      for (let name in server.connectionCustomMethods) {
        connection[name] = async (...args) => {
          args.unshift(connection)
          return server.connectionCustomMethods[name].apply(null, args)
        }
      }
    }

    api.i18n.invokeConnectionLocale(this)
  }

  /**
   * Localize a key for this connection's locale.  Keys usually look like `messages.errors.notFound`, and are defined in your locales directory.  Strings can be interploated as well, connection.localize('the count was {{count}}', {count: 4})
   *
   * @function localize
   * @memberof ActionHero.Connection
   * @param  {String} message The mesage key to be translated.
   * @return {String}         The translated message.  If an appropraite translation cannot be found, the key will be returned.
   * @see api.i18n
   */
  localize (message) {
    // this.locale will be sourced automatically
    return api.i18n.localize(message, this)
  }

  /**
   * Send a file to a connection (usually in the context of an Action).  Be sure to set `data.toRender = false` in the action!
   * Uses Server#processFile and will set `connection.params.file = path`
   *
   * @function sendFile
   * @memberof ActionHero.Connection
   * @param  {String} path The path of the file to send, within one of your `api.config.general.paths.public` directories.
   * @tutorial file-server
   */

  /**
   * Send a message to a connection.  Uses Server#sendMessage.
   *
   * @function sendMessage
   * @memberof ActionHero.Connection
   * @param  {String} message The message to send.  Can be an Object or String... but it depends on the server in use.
   */

  generateID () {
    return uuid.v4()
  }

  /**
   * Destroys the connection.  If the type/sever of the connection has a goodbye message, it will be sent.  The connection will be removed from all rooms.  The connection's socket will be closed when possible.
   *
   * @function destroy
   * @memberof ActionHero.Connection
   */
  async destroy () {
    this.destroyed = true

    for (let i in api.connections.globalMiddleware) {
      let middlewareName = api.connections.globalMiddleware[i]
      if (typeof api.connections.middleware[middlewareName].destroy === 'function') {
        await api.connections.middleware[middlewareName].destroy(this)
      }
    }

    if (this.canChat === true) {
      let promises = []
      for (let i in this.rooms) {
        let room = this.rooms[i]
        promises.push(api.chatRoom.removeMember(this.id, room))
      }
      await Promise.all(promises)
    }

    const server = api.servers.servers[this.type]

    if (server) {
      if (server.attributes.logExits === true) {
        server.log('connection closed', 'info', { to: this.remoteIP })
      }
      if (typeof server.goodbye === 'function') { server.goodbye(this) }
    }

    delete api.connections.connections[this.id]
  }

  /**
   * @private
   * @function set
   * @memberof ActionHero.Connection
   */
  set (key, value) {
    this[key] = value
  }

  /**
   * Try to run a verb command for a connection
   *
   * @function verbs
   * @memberof ActionHero.Connection
   * @async
   * @private
   * @param  {String}  verb  The verb
   * @param  {Array}   words All the arguments sent by the client
   * @return {Promise}
   */
  async verbs (verb, words) {
    let key
    let value
    let room
    const server = api.servers.servers[this.type]
    const allowedVerbs = server.attributes.verbs

    if (!(words instanceof Array)) { words = [words] }

    if (server && allowedVerbs.indexOf(verb) >= 0) {
      server.log('verb', 'debug', { verb: verb, to: this.remoteIP, params: JSON.stringify(words) })

      // TODO: investigate allowedVerbs being an array of Constatnts or Symbols

      switch (verb) {
        case 'quit':
        case 'exit':
          return this.destroy()
        case 'paramAdd':
          key = words[0]
          value = words[1]
          if ((words[0]) && (words[0].indexOf('=') >= 0)) {
            let parts = words[0].split('=')
            key = parts[0]
            value = parts[1]
          }

          if (api.config.general.disableParamScrubbing || api.params.postVariables.indexOf(key) >= 0) {
            this.params[key] = value
          }
          return
        case 'paramDelete':
          key = words[0]
          delete this.params[key]
          return
        case 'paramView':
          key = words[0]
          return this.params[key]
        case 'paramsView':
          return this.params
        case 'paramsDelete':
          for (let i in this.params) { delete this.params[i] }
          return
        case 'roomAdd':
          room = words[0]
          return api.chatRoom.addMember(this.id, room)
        case 'roomLeave':
          room = words[0]
          return api.chatRoom.removeMember(this.id, room)
        case 'roomView':
          room = words[0]
          if (this.rooms.indexOf(room) >= 0) {
            return api.chatRoom.roomStatus(room)
          }
          let error = new Error(await api.config.errors.connectionNotInRoom(this, room))
          throw error
        case 'detailsView':
          return {
            id: this.id,
            fingerprint: this.fingerprint,
            remoteIP: this.remoteIP,
            remotePort: this.remotePort,
            params: this.params,
            connectedAt: this.connectedAt,
            rooms: this.rooms,
            totalActions: this.totalActions,
            pendingActions: this.pendingActions
          }
        case 'documentation':
          return api.documentation.documentation
        case 'say':
          room = words.shift()
          await api.chatRoom.broadcast(this, room, words.join(' '))
          return
      }

      let error = new Error(await api.config.errors.verbNotFound(this, verb))
      error.verbNotFound = true
      throw error
    } else {
      let error = new Error(await api.config.errors.verbNotAllowed(this, verb))
      error.verbNotFound = true
      throw error
    }
  }
}
<|endoftext|>


<commit_before>/*
This module provides functions and classes for streaming video all over town.

Highlights include:

### CT.stream.record(ondata, onrecorder, onfail)
Starts up a video stream from your webcam. Breaks stream into segments as
defined by CT.stream.opts.chunk (default 3000) and CT.stream.opts.segments
(default 10).

### CT.stream.Multiplexer
Uses WebSocket pubsub server (ctpubsub/CT.pubsub) to manage multiple channels,
each streaming metadata pertaining to multiple videos, over a single connection.
The video segments are acquired from the server with the CT.memcache module
and passed to individual CT.stream.Video instances.

### CT.stream.Video
Wraps an HTML5 video tag in a class with functions for streaming a video
that arrives in chunks.
*/

CT.require("CT.stream.util");
CT.require("CT.stream.Audio");
CT.require("CT.stream.Video");
CT.require("CT.stream.Streamer");
CT.require("CT.stream.Multiplexer");

CT.stream.opts = {
	requiresInput: CT.info.android,
	requestedInput: false,
	segments: 10,
	chunk: 1000,
	cutoff: 30000,
	width: 320,
	height: 240,
	waiting: [],
	transcoder: null, // set to func, such as web hook procedure
	setTranscoder: function(cb) {
		CT.stream.opts.transcoder = cb;
	},
	codecs: {
		av: 'video/webm; codecs="vp9,opus"',
		video: 'video/webm; codecs="vp9"',
		audio: 'audio/webm; codecs="opus"'
	},
	startWaiting: function() {
		CT.stream.opts.waiting.forEach(function(w) { w.play(); });
	}
};
CT.stream.opts.mropts = {
	mimeType: CT.stream.opts.codecs.video, // for OSX
	videoBitsPerSecond: 512000,
	audioBitsPerSecond: 64000
};<commit_msg>CT.stream: reduced video bitrate (512->256)<commit_after>/*
This module provides functions and classes for streaming video all over town.

Highlights include:

### CT.stream.record(ondata, onrecorder, onfail)
Starts up a video stream from your webcam. Breaks stream into segments as
defined by CT.stream.opts.chunk (default 3000) and CT.stream.opts.segments
(default 10).

### CT.stream.Multiplexer
Uses WebSocket pubsub server (ctpubsub/CT.pubsub) to manage multiple channels,
each streaming metadata pertaining to multiple videos, over a single connection.
The video segments are acquired from the server with the CT.memcache module
and passed to individual CT.stream.Video instances.

### CT.stream.Video
Wraps an HTML5 video tag in a class with functions for streaming a video
that arrives in chunks.
*/

CT.require("CT.stream.util");
CT.require("CT.stream.Audio");
CT.require("CT.stream.Video");
CT.require("CT.stream.Streamer");
CT.require("CT.stream.Multiplexer");

CT.stream.opts = {
	requiresInput: CT.info.android,
	requestedInput: false,
	segments: 10,
	chunk: 1000,
	cutoff: 30000,
	width: 320,
	height: 240,
	waiting: [],
	transcoder: null, // set to func, such as web hook procedure
	setTranscoder: function(cb) {
		CT.stream.opts.transcoder = cb;
	},
	codecs: {
		av: 'video/webm; codecs="vp9,opus"',
		video: 'video/webm; codecs="vp9"',
		audio: 'audio/webm; codecs="opus"'
	},
	startWaiting: function() {
		CT.stream.opts.waiting.forEach(function(w) { w.play(); });
	}
};
CT.stream.opts.mropts = {
	mimeType: CT.stream.opts.codecs.video, // for OSX
	videoBitsPerSecond: 256000,
	audioBitsPerSecond: 64000
};<|endoftext|>


<commit_before>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else if (typeof exports === 'object') {
        module.exports = factory;
    } else {
        root.request = factory(root);
    }
})(this, function (root) {

    'use strict';

    var exports = {},
        utils = {},
        xhr;

    utils.parse = function(req){
        var result;
        try {
            result = JSON.parse(req.responseText);
        } catch (e) {
            result = req.responseText;
        }
        return [result, req];
    };

    utils.toQuery = function(q){
        var key,
            params = [];
        for(key in q){
            params.push(key+'='+q[key]);
        }
        return params.join('&');
    };

    utils.verifyQuery = function(url,query){
        var result = {},
            parameter = null;
        for(parameter in query){
            if(!(new RegExp('[?|&]'+parameter+'=')).test(url)){
                result[parameter] = query[parameter];
            }
        }
        return result;
    };

    utils.validateHTTPStatus = function(code){
        var result = false;
        code = parseInt(code);
        /*
        200 OK
        201 Created
        202 Accepted
        203 Non-Authoritative Information (since HTTP/1.1)
        204 No Content
        205 Reset Content
        206 Partial Content
        207 Multi-Status (WebDAV; RFC 4918)
        208 Already Reported (WebDAV; RFC 5842)
        226 IM Used (RFC 3229)
         */
        if(code >= 200 && code <230){
            result = true;
        }
        return result;
    };

    xhr = function(method, url, data, query, headers){
        var methods = {
                success: function(){},
                error: function(){},
                always: function(){}
            },
            request = null,
            callbacks = {},
            protocol = (window.location.protocol === 'file:') ? 'https:' : window.location.protocol,
            supportHeaders = false;

        if(method === 'GET'){
            query = utils.verifyQuery(url,query);
        }

        if(window.XMLHttpRequest){
            request = new XMLHttpRequest();
            supportHeaders = true;
        }else if(window.XDomainRequest){
            request = new XDomainRequest();
            request.onprogress = function(){ };
            request.ontimeout = function(){ };
        }else if(window.ActiveXObject){
            request = new ActiveXObject('Microsoft.XMLHTTP');
        }

        if(request){
            if(query) url += ((url.indexOf('?') > -1) ? '&' : '?') + utils.toQuery(query);
            request.open(method, (url.indexOf('http') > -1) ? url : protocol+url, true);
            request.onload = function(){
                if(utils.validateHTTPStatus(request.status) || request.statusText === 'OK' || typeof request.statusText === 'undefined'){
                    methods.success.apply(request, utils.parse(request));
                } else {
                    methods.error.apply(request, utils.parse(request));
                }
                methods.always.apply(request, []);
            };
            request.onerror = function(e){
                methods.error.apply(request, [e]);
                methods.always.apply(request, []);
            };
            if(headers && supportHeaders){
                for(var header in headers){
                    request.setRequestHeader(header, headers[header]);
                }
            }
            if(method === 'POST'){
                if(supportHeaders){
                    request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                }
                data = utils.toQuery(data);
            }
            if(method === 'GET'){
                data = null;
            }
            setTimeout(function(){
                request.send(data);
            },0);
        }

        callbacks = {
            success: function(callback){
                methods.success = callback;
                return callbacks;
            },
            error: function(callback){
                methods.error = callback;
                return callbacks;
            },
            always: function(callback){
                methods.always = callback;
                return callbacks;
            }
        };

        return callbacks;
    };

    /*jshint -W069 */
    exports['get'] = function (url, query, headers) {
        return xhr('GET', url, {}, query, headers);
    };

    exports['put'] = function (url, data, query, headers) {
        return xhr('PUT', url, data, query, headers);
    };

    exports['post'] = function (url, data, query, headers) {
        return xhr('POST', url, data, query, headers);
    };
    /*jshint +W069 */

    exports['delete'] = function (url, query, headers) {
        return xhr('DELETE', url, {}, query, headers);
    };

    return exports;
});
<commit_msg>Fix XDomainRequest IF order<commit_after>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else if (typeof exports === 'object') {
        module.exports = factory;
    } else {
        root.request = factory(root);
    }
})(this, function (root) {

    'use strict';

    var exports = {},
        utils = {},
        xhr;

    utils.parse = function(req){
        var result;
        try {
            result = JSON.parse(req.responseText);
        } catch (e) {
            result = req.responseText;
        }
        return [result, req];
    };

    utils.toQuery = function(q){
        var key,
            params = [];
        for(key in q){
            params.push(key+'='+q[key]);
        }
        return params.join('&');
    };

    utils.verifyQuery = function(url,query){
        var result = {},
            parameter = null;
        for(parameter in query){
            if(!(new RegExp('[?|&]'+parameter+'=')).test(url)){
                result[parameter] = query[parameter];
            }
        }
        return result;
    };

    utils.validateHTTPStatus = function(code){
        var result = false;
        code = parseInt(code);
        /*
        200 OK
        201 Created
        202 Accepted
        203 Non-Authoritative Information (since HTTP/1.1)
        204 No Content
        205 Reset Content
        206 Partial Content
        207 Multi-Status (WebDAV; RFC 4918)
        208 Already Reported (WebDAV; RFC 5842)
        226 IM Used (RFC 3229)
         */
        if(code >= 200 && code <230){
            result = true;
        }
        return result;
    };

    xhr = function(method, url, data, query, headers){
        var methods = {
                success: function(){},
                error: function(){},
                always: function(){}
            },
            request = null,
            callbacks = {},
            protocol = (window.location.protocol === 'file:') ? 'https:' : window.location.protocol,
            supportHeaders = false;

        if(method === 'GET'){
            query = utils.verifyQuery(url,query);
        }

        if(window.XDomainRequest){
            request = new XDomainRequest();
            request.onprogress = function(){ };
            request.ontimeout = function(){ };
        }else if(window.XMLHttpRequest){
            request = new XMLHttpRequest();
            supportHeaders = true;
        }else if(window.ActiveXObject){
            request = new ActiveXObject('Microsoft.XMLHTTP');
        }

        if(request){
            if(query) url += ((url.indexOf('?') > -1) ? '&' : '?') + utils.toQuery(query);
            request.open(method, (url.indexOf('http') > -1) ? url : protocol+url, true);
            request.onload = function(){
                if(utils.validateHTTPStatus(request.status) || request.statusText === 'OK' || typeof request.statusText === 'undefined'){
                    methods.success.apply(request, utils.parse(request));
                } else {
                    methods.error.apply(request, utils.parse(request));
                }
                methods.always.apply(request, []);
            };
            request.onerror = function(e){
                methods.error.apply(request, [e]);
                methods.always.apply(request, []);
            };
            if(headers && supportHeaders){
                for(var header in headers){
                    request.setRequestHeader(header, headers[header]);
                }
            }
            if(method === 'POST'){
                if(supportHeaders){
                    request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                }
                data = utils.toQuery(data);
            }
            if(method === 'GET'){
                data = null;
            }
            setTimeout(function(){
                request.send(data);
            },0);
        }

        callbacks = {
            success: function(callback){
                methods.success = callback;
                return callbacks;
            },
            error: function(callback){
                methods.error = callback;
                return callbacks;
            },
            always: function(callback){
                methods.always = callback;
                return callbacks;
            }
        };

        return callbacks;
    };

    /*jshint -W069 */
    exports['get'] = function (url, query, headers) {
        return xhr('GET', url, {}, query, headers);
    };

    exports['put'] = function (url, data, query, headers) {
        return xhr('PUT', url, data, query, headers);
    };

    exports['post'] = function (url, data, query, headers) {
        return xhr('POST', url, data, query, headers);
    };
    /*jshint +W069 */

    exports['delete'] = function (url, query, headers) {
        return xhr('DELETE', url, {}, query, headers);
    };

    return exports;
});
<|endoftext|>


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

'use strict';

var assert = require('assert');
var EventEmitter = require('./lib/event_emitter');
var inherits = require('util').inherits;

var errors = require('./errors');

function TChannelRequest(channel, options) {
    options = options || {};
    assert(!options.streamed, "streaming request federation not implemented");
    var self = this;
    EventEmitter.call(self);
    self.errorEvent = self.defineEvent('error');
    self.responseEvent = self.defineEvent('response');

    self.channel = channel;
    self.logger = self.channel.logger;
    self.random = self.channel.random;
    self.timers = self.channel.timers;

    self.options = options;

    if (!self.options.retryFlags) {
        self.options.retryFlags = {
            never: false,
            onConnectionError: true,
            onTimeout: false
        };
    }

    self.triedRemoteAddrs = {};
    self.outReqs = [];
    self.timeout = self.options.timeout || TChannelRequest.defaultTimeout;
    self.limit = self.options.retryLimit || TChannelRequest.defaultRetryLimit;
    self.start = 0;
    self.end = 0;
    self.elapsed = 0;
    self.resendSanity = 0;

    self.serviceName = options.serviceName || '';
    self.headers = self.options.headers || {}; // so that as-foo can punch req.headers.X
    self.options.headers = self.headers; // for passing to peer.request(opts) later

    self.arg1 = null;
    self.arg2 = null;
    self.arg3 = null;

    self.err = null;
    self.res = null;

    self.errorEvent.on(self.onError);
    self.responseEvent.on(self.onResponse);
}

inherits(TChannelRequest, EventEmitter);

TChannelRequest.defaultRetryLimit = 5;
TChannelRequest.defaultTimeout = 5000;

TChannelRequest.prototype.type = 'tchannel.request';

TChannelRequest.prototype.onError = function onError(err, self) {
    if (!self.end) self.end = self.timers.now();
    self.err = err;
};

TChannelRequest.prototype.onResponse = function onResponse(res, self) {
    if (!self.end) self.end = self.timers.now();
    self.res = res;
};

TChannelRequest.prototype.hookupStreamCallback = function hookupCallback(callback) {
    throw new Error('not implemented');
};

TChannelRequest.prototype.hookupCallback = function hookupCallback(callback) {
    var self = this;
    if (callback.canStream) {
        return self.hookupStreamCallback(callback);
    }
    var called = false;

    self.errorEvent.on(onError);
    self.responseEvent.on(onResponse);

    function onError(err) {
        if (called) return;
        called = true;
        callback(err, null, null, null);
    }

    function onResponse(res) {
        if (called) return;
        called = true;
        res.withArg23(function gotArg23(err, arg2, arg3) {
            callback(err, res, arg2, arg3);
        });
    }

    return self;
};

TChannelRequest.prototype.choosePeer = function choosePeer() {
    var self = this;
    return self.channel.peers.choosePeer(self, self.options);
};

TChannelRequest.prototype.send = function send(arg1, arg2, arg3, callback) {
    var self = this;
    self.arg1 = arg1;
    self.arg2 = arg2;
    self.arg3 = arg3;
    if (callback) {
        self.hookupCallback(callback);
    }
    self.start = self.timers.now();
    self.resendSanity = self.limit + 1;
    self.resend();
};

TChannelRequest.prototype.resend = function resend() {
    var self = this;

    if (self.checkTimeout()) return;

    var peer = self.choosePeer();
    if (!peer) {
        if (self.outReqs.length) {
            var lastReq = self.outReqs[self.outReqs.length - 1];
            if (lastReq.err) {
                self.errorEvent.emit(self, lastReq.err);
            } else {
                self.responseEvent.emit(self, lastReq.res);
            }
        } else {
            self.errorEvent.emit(self, errors.NoPeerAvailable());
        }
        return;
    }

    if (self.checkTimeout()) return;

    var opts = {};
    var keys = Object.keys(self.options);
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        opts[key] = self.options[key];
    }
    opts.timeout = self.timeout - self.elapsed;
    var outReq = peer.request(opts);
    self.outReqs.push(outReq);

    self.triedRemoteAddrs[outReq.remoteAddr] = (self.triedRemoteAddrs[outReq.remoteAddr] || 0) + 1;
    outReq.responseEvent.on(onResponse);
    outReq.errorEvent.on(onError);
    outReq.send(self.arg1, self.arg2, self.arg3);

    function onError(err) {
        self.onSubreqError(err);
    }

    function onResponse(res) {
        // TODO: skip buffering if res.ok
        res.withArg23(function onArg23(err, arg2, arg3) {
            self.onSubreqResponse(err, res, arg2, arg3);
        });
    }
};

TChannelRequest.prototype.onSubreqError = function onSubreqError(err) {
    var self = this;
    if (self.checkTimeout(err)) return;
    if (self.shouldRetry(err)) {
        self.deferResend();
    } else {
        self.errorEvent.emit(self, err);
    }
};

TChannelRequest.prototype.onSubreqResponse = function onSubreqResponse(err, res, arg2, arg3) {
    var self = this;
    if (self.checkTimeout(err, res)) return;
    if (self.shouldRetry(err, res, arg2, arg3)) {
        self.deferResend();
    } else if (err) {
        self.errorEvent.emit(self, err);
    } else {
        self.responseEvent.emit(self, res);
    }
};

TChannelRequest.prototype.deferResend = function deferResend() {
    var self = this;
    if (--self.resendSanity <= 0) {
        self.errorEvent.emit(self, new Error('TChannelRequest out of resend sanity'));
    } else {
        process.nextTick(doResend);
    }
    function doResend() {
        self.resend();
    }
};

TChannelRequest.prototype.checkTimeout = function checkTimeout(err, res) {
    var self = this;
    var now = self.timers.now();
    self.elapsed = now - self.start;
    if (self.elapsed < self.timeout) return false;
    if (err) {
        if (!self.err) {
            self.errorEvent.emit(self, err);
        }
    } else if (res) {
        if (!self.err && !self.res) {
            self.responseEvent.emit(self, res);
        }
    } else if (!self.err) {
        self.errorEvent.emit(self, errors.TimeoutError({
            start: self.start,
            elapsed: self.elapsed,
            timeout: self.timeout
        }));
    }
    return true;
};

TChannelRequest.prototype.shouldRetry = function shouldRetry(err, res, arg2, arg3) {
    var self = this;

    if (self.outReqs.length >= self.limit) {
        return false;
    }

    if (self.options.retryFlags.never) {
        return false;
    }

    if (err) {
        switch (err.type) {
            case 'tchannel.bad-request':
            case 'tchannel.canceled':
                return false;

            case 'tchannel.busy':
            case 'tchannel.declined':
                return true;

            case 'tchannel.timeout':
                return !!self.options.retryFlags.onTimeout;

            case 'tchannel.socket':
            case 'tchannel.socket-closed':
            case 'tchannel.network':
            case 'tchannel.protocol':
            case 'tchannel.unexpected':
                return !!self.options.retryFlags.onConnectionError;

            default:
                self.logger.error('unknown error type in request retry', {
                    error: err
                });
                return true;
        }
    }

    if (!res.ok && self.options.shouldApplicationRetry) {
        return self.options.shouldApplicationRetry(self, res, arg2, arg3);
    }

    return false;
};

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

'use strict';

var assert = require('assert');
var EventEmitter = require('./lib/event_emitter');
var inherits = require('util').inherits;

var errors = require('./errors');

function TChannelRequest(channel, options) {
    options = options || {};
    assert(!options.streamed, "streaming request federation not implemented");
    var self = this;
    EventEmitter.call(self);
    self.errorEvent = self.defineEvent('error');
    self.responseEvent = self.defineEvent('response');

    self.channel = channel;
    self.logger = self.channel.logger;
    self.random = self.channel.random;
    self.timers = self.channel.timers;

    self.options = options;

    if (!self.options.retryFlags) {
        self.options.retryFlags = {
            never: false,
            onConnectionError: true,
            onTimeout: false
        };
    }

    self.triedRemoteAddrs = {};
    self.outReqs = [];
    self.timeout = self.options.timeout || TChannelRequest.defaultTimeout;
    self.limit = self.options.retryLimit || TChannelRequest.defaultRetryLimit;
    self.start = 0;
    self.end = 0;
    self.elapsed = 0;
    self.resendSanity = 0;

    self.serviceName = options.serviceName || '';
    self.headers = self.options.headers || {}; // so that as-foo can punch req.headers.X
    self.options.headers = self.headers; // for passing to peer.request(opts) later

    self.arg1 = null;
    self.arg2 = null;
    self.arg3 = null;

    self.err = null;
    self.res = null;

    self.errorEvent.on(self.onError);
    self.responseEvent.on(self.onResponse);
}

inherits(TChannelRequest, EventEmitter);

TChannelRequest.defaultRetryLimit = 5;
TChannelRequest.defaultTimeout = 5000;

TChannelRequest.prototype.type = 'tchannel.request';

TChannelRequest.prototype.onError = function onError(err, self) {
    if (!self.end) self.end = self.timers.now();
    self.err = err;
};

TChannelRequest.prototype.onResponse = function onResponse(res, self) {
    if (!self.end) self.end = self.timers.now();
    self.res = res;
};

TChannelRequest.prototype.hookupStreamCallback = function hookupCallback(callback) {
    throw new Error('not implemented');
};

TChannelRequest.prototype.hookupCallback = function hookupCallback(callback) {
    var self = this;
    if (callback.canStream) {
        return self.hookupStreamCallback(callback);
    }
    var called = false;

    self.errorEvent.on(onError);
    self.responseEvent.on(onResponse);

    function onError(err) {
        if (called) return;
        called = true;
        callback(err, null, null, null);
    }

    function onResponse(res) {
        if (called) return;
        called = true;
        res.withArg23(function gotArg23(err, arg2, arg3) {
            callback(err, res, arg2, arg3);
        });
    }

    return self;
};

TChannelRequest.prototype.choosePeer = function choosePeer() {
    var self = this;
    return self.channel.peers.choosePeer(self, self.options);
};

TChannelRequest.prototype.send = function send(arg1, arg2, arg3, callback) {
    var self = this;
    self.arg1 = arg1;
    self.arg2 = arg2;
    self.arg3 = arg3;
    if (callback) {
        self.hookupCallback(callback);
    }
    self.start = self.timers.now();
    self.resendSanity = self.limit + 1;
    self.resend();
};

TChannelRequest.prototype.resend = function resend() {
    var self = this;

    if (self.checkTimeout()) return;

    var peer = self.choosePeer();
    if (!peer) {
        if (self.outReqs.length) {
            var lastReq = self.outReqs[self.outReqs.length - 1];
            if (lastReq.err) {
                self.errorEvent.emit(self, lastReq.err);
            } else {
                self.responseEvent.emit(self, lastReq.res);
            }
        } else {
            self.errorEvent.emit(self, errors.NoPeerAvailable());
        }
        return;
    }

    if (self.checkTimeout()) return;

    var opts = {};
    var keys = Object.keys(self.options);
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        opts[key] = self.options[key];
    }
    opts.timeout = self.timeout - self.elapsed;
    var outReq = peer.request(opts);
    self.outReqs.push(outReq);

    self.triedRemoteAddrs[outReq.remoteAddr] = (self.triedRemoteAddrs[outReq.remoteAddr] || 0) + 1;
    outReq.responseEvent.on(onResponse);
    outReq.errorEvent.on(onError);
    outReq.send(self.arg1, self.arg2, self.arg3);

    function onError(err) {
        self.onSubreqError(err);
    }

    function onResponse(res) {
        // TODO: skip buffering if res.ok
        self.onSubreqResponse(res);
    }
};

TChannelRequest.prototype.onSubreqError = function onSubreqError(err) {
    var self = this;
    if (self.checkTimeout(err)) return;
    if (self.shouldRetry(err)) {
        self.deferResend();
    } else {
        self.errorEvent.emit(self, err);
    }
};

TChannelRequest.prototype.onSubreqResponse = function onSubreqResponse(res) {
    var self = this;
    res.withArg23(function onArg23(err, arg2, arg3) {
        if (self.checkTimeout(err, res)) return;
        if (self.shouldRetry(err, res, arg2, arg3)) {
            self.deferResend();
        } else if (err) {
            self.errorEvent.emit(self, err);
        } else {
            self.responseEvent.emit(self, res);
        }
    });
};

TChannelRequest.prototype.deferResend = function deferResend() {
    var self = this;
    if (--self.resendSanity <= 0) {
        self.errorEvent.emit(self, new Error('TChannelRequest out of resend sanity'));
    } else {
        process.nextTick(doResend);
    }
    function doResend() {
        self.resend();
    }
};

TChannelRequest.prototype.checkTimeout = function checkTimeout(err, res) {
    var self = this;
    var now = self.timers.now();
    self.elapsed = now - self.start;
    if (self.elapsed < self.timeout) return false;
    if (err) {
        if (!self.err) {
            self.errorEvent.emit(self, err);
        }
    } else if (res) {
        if (!self.err && !self.res) {
            self.responseEvent.emit(self, res);
        }
    } else if (!self.err) {
        self.errorEvent.emit(self, errors.TimeoutError({
            start: self.start,
            elapsed: self.elapsed,
            timeout: self.timeout
        }));
    }
    return true;
};

TChannelRequest.prototype.shouldRetry = function shouldRetry(err, res, arg2, arg3) {
    var self = this;

    if (self.outReqs.length >= self.limit) {
        return false;
    }

    if (self.options.retryFlags.never) {
        return false;
    }

    if (err) {
        switch (err.type) {
            case 'tchannel.bad-request':
            case 'tchannel.canceled':
                return false;

            case 'tchannel.busy':
            case 'tchannel.declined':
                return true;

            case 'tchannel.timeout':
                return !!self.options.retryFlags.onTimeout;

            case 'tchannel.socket':
            case 'tchannel.socket-closed':
            case 'tchannel.network':
            case 'tchannel.protocol':
            case 'tchannel.unexpected':
                return !!self.options.retryFlags.onConnectionError;

            default:
                self.logger.error('unknown error type in request retry', {
                    error: err
                });
                return true;
        }
    }

    if (!res.ok && self.options.shouldApplicationRetry) {
        return self.options.shouldApplicationRetry(self, res, arg2, arg3);
    }

    return false;
};

module.exports = TChannelRequest;
<|endoftext|>


<commit_before>var html2canvas = require('html2canvas');
var THUMBNAIL_SIZE = 128;
var GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2;
var THRESHOLD = GOLDEN_RATIO * 10;
var NUM_ATTEMPTS = 3;

// Public functions
var thumbnail = {
  generateThumbnailDataUrl: function(iframe, callback) {
    // Get the canvas inside the iframe.
    var innerDoc = iframe.contentDocument || iframe.contentWindow.document;
    var innerBody = innerDoc.body;
    var framejQuery = iframe.contentWindow.$;

    // Get the current scroll positions.
    var offsetX = innerBody.scrollLeft;
    var offsetY = innerBody.scrollTop;

    // An extra array to store modified elements.
    var hiddenElements = [];

    var turtles = innerDoc.getElementsByClassName('turtle');
    // If there is only a single turtle.
    if (typeof(framejQuery) === 'function' && framejQuery.turtle &&
        turtles.length === 1 && turtles[0].id === 'turtle') {
      var turtle = turtles[0];
      var coordinates = framejQuery(turtle).getxy();
      var direction = framejQuery(turtle).direction();
      // If the turtle is at its original position and direction, ignore it.
      if (coordinates && coordinates[0] === 0 && coordinates[1] === 0 &&
          direction === 0) {
        hiddenElements.push({
          object: turtle,
          display: turtle.style.display
        });
        turtle.style.display = 'none';
      }
    }

    // Copy the NodeList into an array.
    var children = Array.prototype.slice.call(innerBody.children);

    // Hide the test panel and coordinates before capturing the thumbnail.
    // Keep a copy of the original style settings in the `hiddenElements` array.
    children.forEach(function(child) {
      if (child.tagName.toLowerCase() === 'samp' &&
          (child.className !== 'turtlefield' || child.id == '_testpanel')) {
        hiddenElements.push({
          object: child,
          display: child.style.display
        });
        child.style.display = 'none';
      }
    });

    function onRendered(canvas) {
      // Restore the display attribute of the elements.
      hiddenElements.forEach(function(element) {
        element.object.style.display = element.display;
      });
      callback(getImageDataUrl(canvas, offsetX, offsetY));
    }

    function tryHtml2canvas(numAttempts) {
      if (numAttempts > 0) {
        html2canvas(innerBody).then(onRendered, function(e) {
          console.log('html2canvas failed, retrying...');
          tryHtml2canvas(numAttempts - 1);
        });
      } else {
        // If it gets here, that means all attempts have failed.
        // Then just call the callback with empty string.
        callback('');
      }
    }

    // Try calling `html2canvas`.
    tryHtml2canvas(NUM_ATTEMPTS);
  }
}

// Private functions
function getImageDataUrl(canvas, offsetX, offsetY) {
  var w = canvas.width;
  var h = canvas.height;
  var ctx = canvas.getContext('2d');
  var imageData;
  try {
    // Try to get image data. Would fail if canvas is tainted.
    imageData = ctx.getImageData(0, 0, w, h);
  } catch (e) {
    console.log('Get image data failed, skipping...')
    return '';
  }

  // Initialize the coordinates for the image region,
  // topLeft is initialized to bottom right,
  // and bottomRight is initialized to top left.
  var topLeft = { x: w, y: h };
  var bottomRight = { x: 0, y: 0 };

  // Iterate through all the points to find the "interesting" region.
  var x, y, index;
  for (y = offsetY; y < h; y++) {
    for (x = offsetX; x < w; x++) {
      // Every pixel takes up 4 slots in the array, contains R, G, B, A.
      index = (y * w + x) * 4;
      // Thus `index + 3` is the index of the Alpha value.
      if (imageData.data[index + 3] > 0) {
        if (x < topLeft.x) {
          topLeft.x = x;
        }
        if (x > bottomRight.x) {
          bottomRight.x = x;
        }
        if (y < topLeft.y) {
          topLeft.y = y;
        }
        if (y > bottomRight.y) {
          bottomRight.y = y;
        }
      }
    }
  }

  // Calculate the actual image size.
  var imageWidth = bottomRight.x - topLeft.x + 1;
  var imageHeight = bottomRight.y - topLeft.y + 1;

  // This means the thumbnail is blank, should just return.
  if (imageWidth <= 0 || imageHeight <= 0) {
    return '';
  }

  // Find the longer edge and make it a square.
  var longerEdge = Math.max(Math.min(imageWidth, h), Math.min(imageHeight, w));
  var shorterEdge = Math.min(Math.min(imageWidth, h), Math.min(imageHeight, w));

  // Initialize `finalSize` and `truncateAmount`.
  var finalSize = longerEdge;
  var truncateAmount = 0;

  // If the ratio is greater than the predefined threshold,
  // and the `longerEdge` is long enough, then we should truncate it.
  // There is no point truncating thumbnails that are too small.
  var shouldTruncate = (longerEdge / shorterEdge) > THRESHOLD &&
                       longerEdge > THUMBNAIL_SIZE;
  if (shouldTruncate) {
    // Make sure that it is at least as large as `THUMBNAIL_SIZE`.
    finalSize = Math.max(shorterEdge * GOLDEN_RATIO, THUMBNAIL_SIZE);
    truncateAmount = longerEdge - finalSize;
  }

  // The amount that the `shorterEdge` needed to extend is the difference
  // between the `longerEdge` and the `shorterEdge` minus `truncateAmount`.
  var diff = (Math.abs(imageWidth - imageHeight) - truncateAmount) / 2;
  if (imageWidth > imageHeight) {
    topLeft.y = Math.max(topLeft.y - diff, 0);
  } else {
    topLeft.x = Math.max(topLeft.x - diff, 0);
  }

  // Draw the cropped image in a temp canvas and scale it down.
  var tempCanvas = document.createElement('canvas');
  var tempCanvasCtx = tempCanvas.getContext('2d');
  tempCanvas.width = THUMBNAIL_SIZE;
  tempCanvas.height = THUMBNAIL_SIZE;
  if (finalSize < THUMBNAIL_SIZE) {
    var offset = THUMBNAIL_SIZE / 2 - finalSize / 2;
    tempCanvasCtx.drawImage(canvas,       // Src canvas.
        topLeft.x, topLeft.y,             // Src coordinates.
        finalSize, finalSize,             // Src coordinates.
        offset, offset,                   // Dest coordinates.
        finalSize, finalSize);            // Dest size.
  } else {
    tempCanvasCtx.drawImage(canvas,       // Src canvas.
        topLeft.x, topLeft.y,             // Src coordinates.
        finalSize, finalSize,             // Src coordinates.
        0, 0,                             // Dest coordinates.
        THUMBNAIL_SIZE, THUMBNAIL_SIZE);  // Dest size.
  }

  // Convert the temp canvas to data url and return.
  return tempCanvas.toDataURL();
}

module.exports = thumbnail;
<commit_msg>Avoid warnings on save.<commit_after>var html2canvas = require('html2canvas');
var THUMBNAIL_SIZE = 128;
var GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2;
var THRESHOLD = GOLDEN_RATIO * 10;
var NUM_ATTEMPTS = 3;

// Public functions
var thumbnail = {
  generateThumbnailDataUrl: function(iframe, callback) {
    // Get the canvas inside the iframe.
    var innerDoc = iframe.contentDocument || iframe.contentWindow.document;
    var innerBody = innerDoc.body;
    var framejQuery = iframe.contentWindow.$;

    // Get the current scroll positions.
    var offsetX = innerBody.scrollLeft;
    var offsetY = innerBody.scrollTop;

    // An extra array to store modified elements.
    var hiddenElements = [];

    var turtles = innerDoc.getElementsByClassName('turtle');
    // If there is only a single turtle.
    if (typeof(framejQuery) === 'function' && framejQuery.turtle &&
        turtles.length === 1 && turtles[0].id === 'turtle') {
      var nowarn = framejQuery.turtle.nowarn;
      framejQuery.turtle.nowarn = true;
      var turtle = turtles[0];
      var coordinates = framejQuery(turtle).getxy();
      var direction = framejQuery(turtle).direction();
      framejQuery.turtle.nowarn = nowarn;
      // If the turtle is at its original position and direction, ignore it.
      if (coordinates && coordinates[0] === 0 && coordinates[1] === 0 &&
          direction === 0) {
        hiddenElements.push({
          object: turtle,
          display: turtle.style.display
        });
        turtle.style.display = 'none';
      }
    }

    // Copy the NodeList into an array.
    var children = Array.prototype.slice.call(innerBody.children);

    // Hide the test panel and coordinates before capturing the thumbnail.
    // Keep a copy of the original style settings in the `hiddenElements` array.
    children.forEach(function(child) {
      if (child.tagName.toLowerCase() === 'samp' &&
          (child.className !== 'turtlefield' || child.id == '_testpanel')) {
        hiddenElements.push({
          object: child,
          display: child.style.display
        });
        child.style.display = 'none';
      }
    });

    function onRendered(canvas) {
      // Restore the display attribute of the elements.
      hiddenElements.forEach(function(element) {
        element.object.style.display = element.display;
      });
      callback(getImageDataUrl(canvas, offsetX, offsetY));
    }

    function tryHtml2canvas(numAttempts) {
      if (numAttempts > 0) {
        html2canvas(innerBody).then(onRendered, function(e) {
          console.log('html2canvas failed, retrying...');
          tryHtml2canvas(numAttempts - 1);
        });
      } else {
        // If it gets here, that means all attempts have failed.
        // Then just call the callback with empty string.
        callback('');
      }
    }

    // Try calling `html2canvas`.
    tryHtml2canvas(NUM_ATTEMPTS);
  }
}

// Private functions
function getImageDataUrl(canvas, offsetX, offsetY) {
  var w = canvas.width;
  var h = canvas.height;
  var ctx = canvas.getContext('2d');
  var imageData;
  try {
    // Try to get image data. Would fail if canvas is tainted.
    imageData = ctx.getImageData(0, 0, w, h);
  } catch (e) {
    console.log('Get image data failed, skipping...')
    return '';
  }

  // Initialize the coordinates for the image region,
  // topLeft is initialized to bottom right,
  // and bottomRight is initialized to top left.
  var topLeft = { x: w, y: h };
  var bottomRight = { x: 0, y: 0 };

  // Iterate through all the points to find the "interesting" region.
  var x, y, index;
  for (y = offsetY; y < h; y++) {
    for (x = offsetX; x < w; x++) {
      // Every pixel takes up 4 slots in the array, contains R, G, B, A.
      index = (y * w + x) * 4;
      // Thus `index + 3` is the index of the Alpha value.
      if (imageData.data[index + 3] > 0) {
        if (x < topLeft.x) {
          topLeft.x = x;
        }
        if (x > bottomRight.x) {
          bottomRight.x = x;
        }
        if (y < topLeft.y) {
          topLeft.y = y;
        }
        if (y > bottomRight.y) {
          bottomRight.y = y;
        }
      }
    }
  }

  // Calculate the actual image size.
  var imageWidth = bottomRight.x - topLeft.x + 1;
  var imageHeight = bottomRight.y - topLeft.y + 1;

  // This means the thumbnail is blank, should just return.
  if (imageWidth <= 0 || imageHeight <= 0) {
    return '';
  }

  // Find the longer edge and make it a square.
  var longerEdge = Math.max(Math.min(imageWidth, h), Math.min(imageHeight, w));
  var shorterEdge = Math.min(Math.min(imageWidth, h), Math.min(imageHeight, w));

  // Initialize `finalSize` and `truncateAmount`.
  var finalSize = longerEdge;
  var truncateAmount = 0;

  // If the ratio is greater than the predefined threshold,
  // and the `longerEdge` is long enough, then we should truncate it.
  // There is no point truncating thumbnails that are too small.
  var shouldTruncate = (longerEdge / shorterEdge) > THRESHOLD &&
                       longerEdge > THUMBNAIL_SIZE;
  if (shouldTruncate) {
    // Make sure that it is at least as large as `THUMBNAIL_SIZE`.
    finalSize = Math.max(shorterEdge * GOLDEN_RATIO, THUMBNAIL_SIZE);
    truncateAmount = longerEdge - finalSize;
  }

  // The amount that the `shorterEdge` needed to extend is the difference
  // between the `longerEdge` and the `shorterEdge` minus `truncateAmount`.
  var diff = (Math.abs(imageWidth - imageHeight) - truncateAmount) / 2;
  if (imageWidth > imageHeight) {
    topLeft.y = Math.max(topLeft.y - diff, 0);
  } else {
    topLeft.x = Math.max(topLeft.x - diff, 0);
  }

  // Draw the cropped image in a temp canvas and scale it down.
  var tempCanvas = document.createElement('canvas');
  var tempCanvasCtx = tempCanvas.getContext('2d');
  tempCanvas.width = THUMBNAIL_SIZE;
  tempCanvas.height = THUMBNAIL_SIZE;
  if (finalSize < THUMBNAIL_SIZE) {
    var offset = THUMBNAIL_SIZE / 2 - finalSize / 2;
    tempCanvasCtx.drawImage(canvas,       // Src canvas.
        topLeft.x, topLeft.y,             // Src coordinates.
        finalSize, finalSize,             // Src coordinates.
        offset, offset,                   // Dest coordinates.
        finalSize, finalSize);            // Dest size.
  } else {
    tempCanvasCtx.drawImage(canvas,       // Src canvas.
        topLeft.x, topLeft.y,             // Src coordinates.
        finalSize, finalSize,             // Src coordinates.
        0, 0,                             // Dest coordinates.
        THUMBNAIL_SIZE, THUMBNAIL_SIZE);  // Dest size.
  }

  // Convert the temp canvas to data url and return.
  return tempCanvas.toDataURL();
}

module.exports = thumbnail;
<|endoftext|>


<commit_before>var display = null;

var positionSpans = [];
positionSpans[ 0 ] = document.getElementById( 'position-x' );
positionSpans[ 1 ] = document.getElementById( 'position-y' );
positionSpans[ 2 ] = document.getElementById( 'position-z' );

var orientationSpans = [];
orientationSpans[ 0 ] = document.getElementById( 'orientation-x' );
orientationSpans[ 1 ] = document.getElementById( 'orientation-y' );
orientationSpans[ 2 ] = document.getElementById( 'orientation-z' );
orientationSpans[ 3 ] = document.getElementById( 'orientation-w' );

var container = document.getElementById( 'canvas-container' );

var renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
container.appendChild( renderer.domElement );

var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera( 1 / - 2, 1 / 2, 1 / 2, 1 / - 2, - 5, 10 );
camera.position.set( -2, 2, 2 );
camera.lookAt( scene.position );

var textureLoader = new THREE.TextureLoader();
var chaperoneTexture = textureLoader.load( 'assets/chaperone-texture.png', invalidate );
chaperoneTexture.wrapS = chaperoneTexture.wrapT = THREE.RepeatWrapping;

var roomGeometry = new THREE.BoxGeometry( 5, 2, 3 );

var size = .4;
var w = 5.2, d = 3.1, h = 2;
var sw = Math.round( 2 * w );
var sd = Math.round( 2 * d );
var sh = Math.round( 2 * h );
roomGeometry.faceVertexUvs[ 0 ][ 0 ][ 0 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 0 ][ 2 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 0 ][ 2 ].x = sd;

roomGeometry.faceVertexUvs[ 0 ][ 1 ][ 1 ].x = sd;
roomGeometry.faceVertexUvs[ 0 ][ 1 ][ 2 ].x = sd;
roomGeometry.faceVertexUvs[ 0 ][ 1 ][ 2 ].y = sh;

roomGeometry.faceVertexUvs[ 0 ][ 2 ][ 0 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 2 ][ 2 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 2 ][ 2 ].x = sd;

roomGeometry.faceVertexUvs[ 0 ][ 3 ][ 1 ].x = sd;
roomGeometry.faceVertexUvs[ 0 ][ 3 ][ 2 ].x = sd;
roomGeometry.faceVertexUvs[ 0 ][ 3 ][ 2 ].y = sh;

roomGeometry.faceVertexUvs[ 0 ][ 8 ][ 0 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 8 ][ 2 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 8 ][ 2 ].x = sw;

roomGeometry.faceVertexUvs[ 0 ][ 9 ][ 1 ].x = sw;
roomGeometry.faceVertexUvs[ 0 ][ 9 ][ 2 ].x = sw;
roomGeometry.faceVertexUvs[ 0 ][ 9 ][ 2 ].y = sh;

roomGeometry.faceVertexUvs[ 0 ][ 10 ][ 0 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 10 ][ 2 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 10 ][ 2 ].x = sw;

roomGeometry.faceVertexUvs[ 0 ][ 11 ][ 1 ].x = sw;
roomGeometry.faceVertexUvs[ 0 ][ 11 ][ 2 ].x = sw;
roomGeometry.faceVertexUvs[ 0 ][ 11 ][ 2 ].y = sh;

roomGeometry.uvsNeedUpdate = true;

var room = new THREE.Mesh( roomGeometry, new THREE.MeshBasicMaterial( {
    map: chaperoneTexture,
    side: THREE.BackSide,
    transparent: true
} ) );
room.position.y = 1;
scene.add( room );

var hmd = new THREE.Mesh( new THREE.BoxGeometry( .20, .11, .12 ), new THREE.MeshNormalMaterial() );
scene.add( hmd );

var invalidated = true;
function invalidate() {
    invalidated = true;
}

var controls = new THREE.OrbitControls( camera, container );
controls.addEventListener( 'change', invalidate );
controls.target.set( 0, 0, 0 );

var control = new THREE.TransformControls( camera, renderer.domElement );
control.addEventListener( 'change', invalidate );
control.attach( hmd );
control.setSize( .5 );
control.setMode( "translate" );
scene.add( control );

function onWindowResize() {

    var w = container.clientWidth;
    var h = container.clientHeight;

    renderer.setSize( w, h );

    var a = w / h;
    w = 7;
    h = w / a;

    camera.left = w / - 2;
    camera.right = w / 2;
    camera.top = h / 2;
    camera.bottom = h / - 2;

    camera.updateProjectionMatrix();

    invalidate();

}

window.addEventListener( 'resize', onWindowResize );

function render() {

    var str = 'window.__extHMDResetPose';
    chrome.devtools.inspectedWindow.eval( str, function(result, isException) {
        if( result === true ) {
            invalidate();
            hmd.position.set( 0, 0,0 );
            hmd.quaternion.set( 0, 0, 0, 0 );
            control.detach( hmd );
            control.attach( hmd );
            var str = 'window.__extHMDResetPose = false;';
            chrome.devtools.inspectedWindow.eval( str );
        }
    } );

    if( invalidated ) {

        positionSpans[ 0 ].textContent = hmd.position.x.toFixed( 2 );
        positionSpans[ 1 ].textContent = hmd.position.y.toFixed( 2 );
        positionSpans[ 2 ].textContent = hmd.position.z.toFixed( 2 );

        orientationSpans[ 0 ].textContent = hmd.quaternion.x.toFixed( 2 );
        orientationSpans[ 1 ].textContent = hmd.quaternion.y.toFixed( 2 );
        orientationSpans[ 2 ].textContent = hmd.quaternion.z.toFixed( 2 );
        orientationSpans[ 2 ].textContent = hmd.quaternion.w.toFixed( 2 );

        var str = 'window.__extHMDPosition = [' +
            hmd.position.x + ', ' +
            hmd.position.y + ', ' +
            hmd.position.z + '];';
        str += 'window.__extHMDOrientation = [' +
            hmd.quaternion.x + ', ' +
            hmd.quaternion.y + ', ' +
            hmd.quaternion.z + ', ' +
            hmd.quaternion.w + '];';

        chrome.devtools.inspectedWindow.eval( str );

        renderer.render( scene, camera );
        invalidated = false;
    }

    requestAnimationFrame( render );

}

window.addEventListener( 'keydown', function ( event ) {

    switch ( event.keyCode ) {

        case 81: // Q
        control.setSpace( control.space === "local" ? "world" : "local" );
        break;

        case 17: // Ctrl
        control.setTranslationSnap( 1 );
        control.setRotationSnap( THREE.Math.degToRad( 15 ) );
        break;

        case 87: // W
        control.setMode( "translate" );
        break;

        case 69: // E
        control.setMode( "rotate" );
        break;

        /*case 82: // R
        control.setMode( "scale" );
        break;*/

        case 187:
        case 107: // +, =, num+
        control.setSize( control.size + 0.01 );
        break;

        case 189:
        case 109: // -, _, num-
        control.setSize( Math.max( control.size - 0.01, 0.01 ) );
        break;

    }

});

onWindowResize();
render();<commit_msg>Local space by default.<commit_after>var display = null;

var positionSpans = [];
positionSpans[ 0 ] = document.getElementById( 'position-x' );
positionSpans[ 1 ] = document.getElementById( 'position-y' );
positionSpans[ 2 ] = document.getElementById( 'position-z' );

var orientationSpans = [];
orientationSpans[ 0 ] = document.getElementById( 'orientation-x' );
orientationSpans[ 1 ] = document.getElementById( 'orientation-y' );
orientationSpans[ 2 ] = document.getElementById( 'orientation-z' );
orientationSpans[ 3 ] = document.getElementById( 'orientation-w' );

var container = document.getElementById( 'canvas-container' );

var renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
container.appendChild( renderer.domElement );

var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera( 1 / - 2, 1 / 2, 1 / 2, 1 / - 2, - 5, 10 );
camera.position.set( -2, 2, 2 );
camera.lookAt( scene.position );

var textureLoader = new THREE.TextureLoader();
var chaperoneTexture = textureLoader.load( 'assets/chaperone-texture.png', invalidate );
chaperoneTexture.wrapS = chaperoneTexture.wrapT = THREE.RepeatWrapping;

var roomGeometry = new THREE.BoxGeometry( 5, 2, 3 );

var size = .4;
var w = 5.2, d = 3.1, h = 2;
var sw = Math.round( 2 * w );
var sd = Math.round( 2 * d );
var sh = Math.round( 2 * h );
roomGeometry.faceVertexUvs[ 0 ][ 0 ][ 0 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 0 ][ 2 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 0 ][ 2 ].x = sd;

roomGeometry.faceVertexUvs[ 0 ][ 1 ][ 1 ].x = sd;
roomGeometry.faceVertexUvs[ 0 ][ 1 ][ 2 ].x = sd;
roomGeometry.faceVertexUvs[ 0 ][ 1 ][ 2 ].y = sh;

roomGeometry.faceVertexUvs[ 0 ][ 2 ][ 0 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 2 ][ 2 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 2 ][ 2 ].x = sd;

roomGeometry.faceVertexUvs[ 0 ][ 3 ][ 1 ].x = sd;
roomGeometry.faceVertexUvs[ 0 ][ 3 ][ 2 ].x = sd;
roomGeometry.faceVertexUvs[ 0 ][ 3 ][ 2 ].y = sh;

roomGeometry.faceVertexUvs[ 0 ][ 8 ][ 0 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 8 ][ 2 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 8 ][ 2 ].x = sw;

roomGeometry.faceVertexUvs[ 0 ][ 9 ][ 1 ].x = sw;
roomGeometry.faceVertexUvs[ 0 ][ 9 ][ 2 ].x = sw;
roomGeometry.faceVertexUvs[ 0 ][ 9 ][ 2 ].y = sh;

roomGeometry.faceVertexUvs[ 0 ][ 10 ][ 0 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 10 ][ 2 ].y = sh;
roomGeometry.faceVertexUvs[ 0 ][ 10 ][ 2 ].x = sw;

roomGeometry.faceVertexUvs[ 0 ][ 11 ][ 1 ].x = sw;
roomGeometry.faceVertexUvs[ 0 ][ 11 ][ 2 ].x = sw;
roomGeometry.faceVertexUvs[ 0 ][ 11 ][ 2 ].y = sh;

roomGeometry.uvsNeedUpdate = true;

var room = new THREE.Mesh( roomGeometry, new THREE.MeshBasicMaterial( {
    map: chaperoneTexture,
    side: THREE.BackSide,
    transparent: true
} ) );
room.position.y = 1;
scene.add( room );

var hmd = new THREE.Mesh( new THREE.BoxGeometry( .20, .11, .12 ), new THREE.MeshNormalMaterial() );
scene.add( hmd );

var invalidated = true;
function invalidate() {
    invalidated = true;
}

var controls = new THREE.OrbitControls( camera, container );
controls.addEventListener( 'change', invalidate );
controls.target.set( 0, 0, 0 );

var control = new THREE.TransformControls( camera, renderer.domElement );
control.addEventListener( 'change', invalidate );
control.attach( hmd );
control.setSize( .5 );
control.setMode( "translate" );
control.setSpace( "local" );
scene.add( control );

function onWindowResize() {

    var w = container.clientWidth;
    var h = container.clientHeight;

    renderer.setSize( w, h );

    var a = w / h;
    w = 7;
    h = w / a;

    camera.left = w / - 2;
    camera.right = w / 2;
    camera.top = h / 2;
    camera.bottom = h / - 2;

    camera.updateProjectionMatrix();

    invalidate();

}

window.addEventListener( 'resize', onWindowResize );

function render() {

    var str = 'window.__extHMDResetPose';
    chrome.devtools.inspectedWindow.eval( str, function(result, isException) {
        if( result === true ) {
            invalidate();
            hmd.position.set( 0, 0,0 );
            hmd.quaternion.set( 0, 0, 0, 0 );
            control.detach( hmd );
            control.attach( hmd );
            var str = 'window.__extHMDResetPose = false;';
            chrome.devtools.inspectedWindow.eval( str );
        }
    } );

    if( invalidated ) {

        positionSpans[ 0 ].textContent = hmd.position.x.toFixed( 2 );
        positionSpans[ 1 ].textContent = hmd.position.y.toFixed( 2 );
        positionSpans[ 2 ].textContent = hmd.position.z.toFixed( 2 );

        orientationSpans[ 0 ].textContent = hmd.quaternion.x.toFixed( 2 );
        orientationSpans[ 1 ].textContent = hmd.quaternion.y.toFixed( 2 );
        orientationSpans[ 2 ].textContent = hmd.quaternion.z.toFixed( 2 );
        orientationSpans[ 2 ].textContent = hmd.quaternion.w.toFixed( 2 );

        var str = 'window.__extHMDPosition = [' +
            hmd.position.x + ', ' +
            hmd.position.y + ', ' +
            hmd.position.z + '];';
        str += 'window.__extHMDOrientation = [' +
            hmd.quaternion.x + ', ' +
            hmd.quaternion.y + ', ' +
            hmd.quaternion.z + ', ' +
            hmd.quaternion.w + '];';

        chrome.devtools.inspectedWindow.eval( str );

        renderer.render( scene, camera );
        invalidated = false;
    }

    requestAnimationFrame( render );

}

window.addEventListener( 'keydown', function ( event ) {

    switch ( event.keyCode ) {

        case 81: // Q
        control.setSpace( control.space === "local" ? "world" : "local" );
        break;

        case 17: // Ctrl
        control.setTranslationSnap( 1 );
        control.setRotationSnap( THREE.Math.degToRad( 15 ) );
        break;

        case 87: // W
        control.setMode( "translate" );
        break;

        case 69: // E
        control.setMode( "rotate" );
        break;

        /*case 82: // R
        control.setMode( "scale" );
        break;*/

        case 187:
        case 107: // +, =, num+
        control.setSize( control.size + 0.01 );
        break;

        case 189:
        case 109: // -, _, num-
        control.setSize( Math.max( control.size - 0.01, 0.01 ) );
        break;

    }

});

onWindowResize();
render();
<|endoftext|>