<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

'use strict';

var aws = require('aws-sdk');
var chalk = require('chalk');
var fs = require('fs');
var debug = require('debug')('command:deploy');
var path = require('path');
var tmp = require('tmp');
var util = require('util');
var A = require('async');
var _ = require('lodash');
var archiver = require('archiver');

//This is not ideal but should work for now to seperate deploy vs lambda dependencies
var deployDependencies = ["archiver", "async", "aws-sdk", "chalk",
    "debug", "lodash", "tmp", "yargs"];
var allDependencies = Object.keys(require('./package.json').dependencies);
var lambdaDependencies = _.difference(allDependencies, deployDependencies);

aws.config.update({
  region: process.env.AWS_REGION || 'eu-west-1'
});

module.exports = deploy;

function deploy(yargs, argv) {
  var argv2 = yargs
        .usage('Usage: $0 deploy [options]')
        .option('r', {
          description: 'role - AWS role ARN for the lambda',
          type: 'string'
        })
        .option('c', {
          description: 'config - path to Llamafile configuration file',
          type: 'string'
        })
        .help('help')
        .argv;

  if (!process.env.AWS_REGION) {
    console.log(chalk.yellow('AWS_REGION not set, defaulting to eu-west-1'));
  }

  if (!process.env.AWS_PROFILE) {
    console.log(chalk.red('AWS_PROFILE not set'));
    console.log('Please add AWS IAM user credentials to ~/.aws/credentials and specify the profile to use with the AWS_PROFILE environment variable');
    return;
  }

  var role;
  if (argv2.r) {
    role = argv2.r;
  } else {
    try {
      var llamaConfig = JSON.parse(fs.readFileSync('./llama_config.json', 'utf8'));
      role = llamaConfig.LambdaRoleArn;
    } catch (err) {
      console.log(chalk.red('config_llama.json not found'), '- please specify the lambda role ARN to use with -r');
      return;
    }
  }

  var llamaFile = null;
  if (argv2.c) {
    try {
      llamaFile = JSON.parse(fs.readFileSync(argv2.c), 'utf8');
    } catch (err) {
      console.log(chalk.red(util.format('%s could not be read', argv2.c)));
      return;
    }
  }

  createLambda(role, llamaFile, function(err, data) {
    if (err) {
      console.log(chalk.red('Something went wrong:'));
      console.log(err);
      return;
    }

    debug(data);
    fs.writeFileSync('llama_config.json',
                     JSON.stringify({
                       FunctionArn: data.FunctionArn,
                       LambdaRoleArn: role
                     }, null, 4));
    console.log(chalk.green('+ Chaos Llama has been created'));

    if (llamaFile) {
      var interval = llamaFile.interval || 60;
      configSchedule(interval, llamaConfig.FunctionArn, function(err) {
        if (err) {
          console.log(chalk.red('Oops, something went wrong'));
          console.log(err);
          return;
        }

        console.log(chalk.green(util.format('+ Chaos Llama\'s schedule updated to once every %s minutes', interval)));
      });
    }
  });
}

function createLambda(role, llamaFile, done) {
  var lambda = new aws.Lambda({apiVersion: '2015-03-31'});

  var archive = archiver.create('zip', {});

  var lambdaPath = path.join(__dirname, '../../lambda/index.js');
  var tmpf = tmp.fileSync();
  archive.pipe(fs.createWriteStream(tmpf.name));
  archive.append(fs.readFileSync(lambdaPath), { name: 'index.js' });
  if (llamaFile) {
    debug('Adding real Llamafile to lambda zip');
    llamaFile.region = process.env.AWS_REGION; // FIXME
    archive.append(new Buffer(JSON.stringify(llamaFile)), { name: 'config.json' });
  } else {
    debug('Adding a stub Llamafile to lambda zip');
    archive.append(new Buffer(JSON.stringify({enableForASGs: []})), { name: 'config.json' });
  }
  lambdaDependencies.forEach( function (moduleName){
    archive.directory('./node_modules/' + moduleName, 'node_modules/'+ moduleName);
  })
  archive.finalize();

  archive.on('error', function(err) {
    console.log('error');
    throw err;
  });

  debug('Zip file written to %s', tmpf.name);

  var params = {
    Code: {
      ZipFile: fs.readFileSync(tmpf.name)
    },
    FunctionName: 'chaosLlama',
    Handler: 'index.handler',
    Role: role,
    Runtime: 'nodejs',
    Description: 'Chaos Llama - http://llamajs.com',
    MemorySize: 128,
    Publish: true,
    Timeout: 300
  };

  lambda.createFunction(params, function(err, data) {
    if (err) {
      if (err.code === 'ResourceConflictException' && err.statusCode === 409) {
        console.log('Updating existing lambda definition');
        lambda.updateFunctionCode({
          FunctionName: 'chaosLlama',
          Publish: true,
          ZipFile: fs.readFileSync(tmpf.name)
        }, done);
      } else {
        return done(err, null);
      }
    } else {
      return done(null, data);
    }
  });
}
function configSchedule(interval, functionArn, done) {
  A.waterfall(
    [
      A.constant({interval: interval}),
      createEventRule,
      addLambdaPermission,
      connectRuleToLambda
    ],
    function(err, result) {
      done(err, result);
    });
}

function createEventRule(context, next) {
  var events = new aws.CloudWatchEvents({apiVersion: '2014-02-03'});

  debug(context);

  var params = {
    Name: 'chaos_llama_schedule',
    Description: 'Schedule for invoking Chaos Llama - http://llamajs.com',
    ScheduleExpression: 'rate(' + context.interval + ' minutes)',
    State: 'ENABLED'
  };

  events.putRule(params, function(err, data) {
    if (err) {
      return next(err, context);
    }

    context.RuleArn = data.RuleArn;
    debug('event rule created/updated, arn = %s', context.RuleArn);
    return next(null, context);
  });
}

function addLambdaPermission(context, next) {
  var lambda = new aws.Lambda({apiVersion: '2015-03-31'});

  debug(context);

  // TODO: Remove the statement or make unique with Date.now
  var params = {
    FunctionName: 'chaosLlama',
    StatementId: 'chaosLlamaStatement',
    Action: 'lambda:InvokeFunction',
    Principal: 'events.amazonaws.com',
    SourceArn: context.RuleArn
  };

  lambda.addPermission(params, function (err, data) {
    if (err) {
      return next(err, context);
    }

    context.FunctionArn = JSON.parse(data.Statement).Resource;
    debug('permission added for resource = %s', context.FunctionArn);
    return next(null, context);
  });
}

function connectRuleToLambda(context, next) {
  var events = new aws.CloudWatchEvents({apiVersion: '2014-02-03'});

  debug(context);

  var params = {
    Rule: 'chaos_llama_schedule',
    Targets: [{
      Id: '1',
      Arn: context.FunctionArn
    }]
  };

  events.putTargets(params, function(err, data) {
    if (err) {
      return next(err, context);
    }

    return next(null, context);
  });
}
<commit_msg>small bug fixes<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

'use strict';

var aws = require('aws-sdk');
var chalk = require('chalk');
var fs = require('fs');
var debug = require('debug')('command:deploy');
var path = require('path');
var tmp = require('tmp');
var util = require('util');
var A = require('async');
var _ = require('lodash');
var archiver = require('archiver');

//This is not ideal but should work for now to seperate deploy vs lambda dependencies
var deployDependencies = ["archiver", "async", "aws-sdk", "chalk",
    "debug", "lodash", "tmp", "yargs"];
var allDependencies = Object.keys(require('../../package.json').dependencies);
var lambdaDependencies = _.difference(allDependencies, deployDependencies);

aws.config.update({
  region: process.env.AWS_REGION || 'eu-west-1'
});

module.exports = deploy;

function deploy(yargs, argv) {
  var argv2 = yargs
        .usage('Usage: $0 deploy [options]')
        .option('r', {
          description: 'role - AWS role ARN for the lambda',
          type: 'string'
        })
        .option('c', {
          description: 'config - path to Llamafile configuration file',
          type: 'string'
        })
        .help('help')
        .argv;

  if (!process.env.AWS_REGION) {
    console.log(chalk.yellow('AWS_REGION not set, defaulting to eu-west-1'));
  }

  if (!process.env.AWS_PROFILE) {
    console.log(chalk.red('AWS_PROFILE not set'));
    console.log('Please add AWS IAM user credentials to ~/.aws/credentials and specify the profile to use with the AWS_PROFILE environment variable');
    return;
  }

  var role;
  if (argv2.r) {
    role = argv2.r;
  } else {
    try {
      var llamaConfig = JSON.parse(fs.readFileSync('./llama_config.json', 'utf8'));
      role = llamaConfig.LambdaRoleArn;
    } catch (err) {
      console.log(chalk.red('config_llama.json not found'), '- please specify the lambda role ARN to use with -r');
      return;
    }
  }

  var llamaFile = null;
  if (argv2.c) {
    try {
      llamaFile = JSON.parse(fs.readFileSync(argv2.c), 'utf8');
    } catch (err) {
      console.log(chalk.red(util.format('%s could not be read', argv2.c)));
      return;
    }
  }

  createLambda(role, llamaFile, function(err, data) {
    if (err) {
      console.log(chalk.red('Something went wrong:'));
      console.log(err);
      return;
    }

    debug(data);
    fs.writeFileSync('llama_config.json',
                     JSON.stringify({
                       FunctionArn: data.FunctionArn,
                       LambdaRoleArn: role
                     }, null, 4));
    console.log(chalk.green('+ Chaos Llama has been created'));

    if (llamaFile) {
      var interval = llamaFile.interval || 60;
      configSchedule(interval, llamaConfig.FunctionArn, function(err) {
        if (err) {
          console.log(chalk.red('Oops, something went wrong'));
          console.log(err);
          return;
        }

        console.log(chalk.green(util.format('+ Chaos Llama\'s schedule updated to once every %s minutes', interval)));
      });
    }
  });
}

function createLambda(role, llamaFile, done) {
  var lambda = new aws.Lambda({apiVersion: '2015-03-31'});

  var archive = archiver.create('zip', {});

  var lambdaPath = path.join(__dirname, '../../lambda/index.js');
  var tmpf = tmp.fileSync();
  var tmpfStream = fs.createWriteStream(tmpf.name);
  archive.pipe(tmpfStream);
  archive.append(fs.readFileSync(lambdaPath), { name: 'index.js' });
  if (llamaFile) {
    debug('Adding real Llamafile to lambda zip');
    llamaFile.region = process.env.AWS_REGION; // FIXME
    archive.append(new Buffer(JSON.stringify(llamaFile)), { name: 'config.json' });
  } else {
    debug('Adding a stub Llamafile to lambda zip');
    archive.append(new Buffer(JSON.stringify({enableForASGs: []})), { name: 'config.json' });
  }
  lambdaDependencies.forEach( function (moduleName){
    archive.directory('./node_modules/' + moduleName, 'node_modules/'+ moduleName);
  })

  archive.on('error', function(err) {
    console.log('error');
    throw err;
  });

  tmpfStream.on('finish', function() {
      var params = {
      Code: {
          ZipFile: fs.readFileSync(tmpf.name)
        },
        FunctionName: 'chaosLlama',
        Handler: 'index.handler',
        Role: role,
        Runtime: 'nodejs',
        Description: 'Chaos Llama - http://llamajs.com',
        MemorySize: 128,
        Publish: true,
        Timeout: 300
      };

    lambda.createFunction(params, function(err, data) {
      if (err) {
        if (err.code === 'ResourceConflictException' && err.statusCode === 409) {
          console.log('Updating existing lambda definition');
          lambda.updateFunctionCode({
            FunctionName: 'chaosLlama',
            Publish: true,
            ZipFile: fs.readFileSync(tmpf.name)
          }, done);
        } else {
          return done(err, null);
        }
      } else {
        return done(null, data);
      }
    });
  });
  archive.on('close', function(){
    tmpfStream.close();
  })
  archive.finalize();
  debug('Zip file written to %s', tmpf.name);
}

function configSchedule(interval, functionArn, done) {
  A.waterfall(
    [
      A.constant({interval: interval}),
      createEventRule,
      addLambdaPermission,
      connectRuleToLambda
    ],
    function(err, result) {
      done(err, result);
    });
}

function createEventRule(context, next) {
  var events = new aws.CloudWatchEvents({apiVersion: '2014-02-03'});

  debug(context);

  var params = {
    Name: 'chaos_llama_schedule',
    Description: 'Schedule for invoking Chaos Llama - http://llamajs.com',
    ScheduleExpression: 'rate(' + context.interval + ' minutes)',
    State: 'ENABLED'
  };

  events.putRule(params, function(err, data) {
    if (err) {
      return next(err, context);
    }

    context.RuleArn = data.RuleArn;
    debug('event rule created/updated, arn = %s', context.RuleArn);
    return next(null, context);
  });
}

function addLambdaPermission(context, next) {
  var lambda = new aws.Lambda({apiVersion: '2015-03-31'});

  debug(context);

  // TODO: Remove the statement or make unique with Date.now
  var params = {
    FunctionName: 'chaosLlama',
    StatementId: 'chaosLlamaStatement',
    Action: 'lambda:InvokeFunction',
    Principal: 'events.amazonaws.com',
    SourceArn: context.RuleArn
  };

  lambda.addPermission(params, function (err, data) {
    if (err) {
      return next(err, context);
    }

    context.FunctionArn = JSON.parse(data.Statement).Resource;
    debug('permission added for resource = %s', context.FunctionArn);
    return next(null, context);
  });
}

function connectRuleToLambda(context, next) {
  var events = new aws.CloudWatchEvents({apiVersion: '2014-02-03'});

  debug(context);

  var params = {
    Rule: 'chaos_llama_schedule',
    Targets: [{
      Id: '1',
      Arn: context.FunctionArn
    }]
  };

  events.putTargets(params, function(err, data) {
    if (err) {
      return next(err, context);
    }

    return next(null, context);
  });
}
<|endoftext|>


<commit_before>const { Command } = require('discord.js-commando');
const { stripIndents } = require('common-tags');
const winston = require('winston');

module.exports = class LaunchCybernukeCommand extends Command {
	constructor(client) {
		super(client, {
			name: 'launch-cybernuke',
			aliases: ['cybernuke'],
			group: 'util',
			memberName: 'cybernuke',
			description: 'Bans all members that have joined recently, with new accounts.',
			guildOnly: true,

			args: [
				{
					key: 'join',
					label: 'member age',
					prompt: 'How old (in minutes) should a member be for the cybernuke to ignore them (server join date)?',
					type: 'float',
					min: 0.1,
					max: 120
				},
				{
					key: 'age',
					label: 'account age',
					prompt: 'How old (in minutes) should a member\'s account be for the cybernuke to ignore them (account age)?',
					type: 'float',
					min: 0.1
				}
			]
		});
	}

	hasPermission(msg) {
		return this.client.options.owner === msg.author.id || msg.member.hasPermission('ADMINISTRATOR');
	}

	async run(msg, args) {
		const statusMsg = await msg.reply('Calculating targeting parameters for cybernuke...');
		await msg.guild.fetchMembers();

		const memberCutoff = Date.now() - (args.join * 60000);
		const ageCutoff = Date.now() - (args.age * 60000);
		const members = msg.guild.members.filter(
			mem => mem.joinedTimestamp > memberCutoff && mem.user.createdTimestamp > ageCutoff
		);
		const booleanType = this.client.registry.types.get('boolean');

		await statusMsg.edit(`Cybernuke will strike ${members.size} members; proceed?`);
		let response;
		let statusMsg2;

		while(!statusMsg2) {
			const responses = await msg.channel.awaitMessages(msg2 => msg2.author.id === msg.author.id, {
				maxMatches: 1,
				time: 10000
			});

			if(!responses || responses.size !== 1) {
				await msg.reply('Cybernuke cancelled.');
				return null;
			}
			response = responses.first();

			if(booleanType.validate(response.content)) {
				if(!booleanType.parse(response.content)) {
					await response.reply('Cybernuke cancelled.');
					return null;
				}

				statusMsg2 = await response.reply('Launching cybernuke...');
			} else {
				await response.reply(stripIndents`
					Unknown response. Please confirm the cybernuke launch with a simple "yes" or "no".
					Awaiting your input, commander...
				`);
			}
		}

		const fatalities = [];
		const survivors = [];
		let processed = 0;

		for(const member of members) {
			await member.sendMessage(stripIndents`
				Sorry, but you've been automatically targetted by the cybernuke in the "${msg.guild.name}" server.
				This means that you have been banned, likely in the case of a server raid.
				Please contact them if you believe this ban to be in error.
			`).catch(winston.error);

			member.ban()
				.then(() => {
					fatalities.push(member);
				})
				.catch(err => {
					winston.error(err);
					survivors.push({
						member: member.id,
						error: err
					});
				})
				.then(() => {
					if(members.size <= 5) return;
					processed++;
					if(processed % 5 === 0) {
						statusMsg2.edit(`Launching cybernuke (${Math.round(processed / members.size * 100)}%)...`);
					}
				});
		}

		await statusMsg2.edit('Cybernuke impact confirmed. Casualty report incoming...');
		await response.reply(stripIndents`
			__**Fatalities**__
			${fatalities.length > 0 ? stripIndents`
				${fatalities.length} confirmed KIA.

				${fatalities.map(fat => `**-** ${fat.displayName} (fat.id)`).join('\n')}
			` : 'None'}

			${survivors.length > 0 ? stripIndents`
				__**Survivors**__
				${survivors.length} left standing.

				${survivors.map(srv => `**-** ${srv.member.displayName} (srv.member.id): \`${srv.error}\``).join('\n')}
			` : ''}
		`, { split: true });

		return null;
	}
};
<commit_msg>Update cybernuke.js (#32)<commit_after>const { Command } = require('discord.js-commando');
const { stripIndents } = require('common-tags');
const winston = require('winston');

module.exports = class LaunchCybernukeCommand extends Command {
	constructor(client) {
		super(client, {
			name: 'launch-cybernuke',
			aliases: ['cybernuke'],
			group: 'util',
			memberName: 'cybernuke',
			description: 'Bans all members that have joined recently, with new accounts.',
			guildOnly: true,

			args: [
				{
					key: 'join',
					label: 'member age',
					prompt: 'How old (in minutes) should a member be for the cybernuke to ignore them (server join date)?',
					type: 'float',
					min: 0.1,
					max: 120
				},
				{
					key: 'age',
					label: 'account age',
					prompt: 'How old (in minutes) should a member\'s account be for the cybernuke to ignore them (account age)?',
					type: 'float',
					min: 0.1
				}
			]
		});
	}

	hasPermission(msg) {
		return this.client.options.owner === msg.author.id || msg.member.hasPermission('ADMINISTRATOR');
	}

	async run(msg, args) {
		const statusMsg = await msg.reply('Calculating targeting parameters for cybernuke...');
		await msg.guild.fetchMembers();

		const memberCutoff = Date.now() - (args.join * 60000);
		const ageCutoff = Date.now() - (args.age * 60000);
		const members = msg.guild.members.filter(
			mem => mem.joinedTimestamp > memberCutoff && mem.user.createdTimestamp > ageCutoff
		);
		const booleanType = this.client.registry.types.get('boolean');

		await statusMsg.edit(`Cybernuke will strike ${members.size} members; proceed?`);
		let response;
		let statusMsg2;

		while(!statusMsg2) {
			const responses = await msg.channel.awaitMessages(msg2 => msg2.author.id === msg.author.id, {
				maxMatches: 1,
				time: 10000
			});

			if(!responses || responses.size !== 1) {
				await msg.reply('Cybernuke cancelled.');
				return null;
			}
			response = responses.first();

			if(booleanType.validate(response.content)) {
				if(!booleanType.parse(response.content)) {
					await response.reply('Cybernuke cancelled.');
					return null;
				}

				statusMsg2 = await response.reply('Launching cybernuke...');
			} else {
				await response.reply(stripIndents`
					Unknown response. Please confirm the cybernuke launch with a simple "yes" or "no".
					Awaiting your input, commander...
				`);
			}
		}

		const fatalities = [];
		const survivors = [];
		let processed = 0;

		for(const member of members.values()) {
			await member.sendMessage(stripIndents`
				Sorry, but you've been automatically targetted by the cybernuke in the "${msg.guild.name}" server.
				This means that you have been banned, likely in the case of a server raid.
				Please contact them if you believe this ban to be in error.
			`).catch(winston.error);

			member.ban()
				.then(() => {
					fatalities.push(member);
				})
				.catch(err => {
					winston.error(err);
					survivors.push({
						member: member.id,
						error: err
					});
				})
				.then(() => {
					if(members.size <= 5) return;
					processed++;
					if(processed % 5 === 0) {
						statusMsg2.edit(`Launching cybernuke (${Math.round(processed / members.size * 100)}%)...`);
					}
				});
		}

		await statusMsg2.edit('Cybernuke impact confirmed. Casualty report incoming...');
		await response.reply(stripIndents`
			__**Fatalities**__
			${fatalities.length > 0 ? stripIndents`
				${fatalities.length} confirmed KIA.

				${fatalities.map(fat => `**-** ${fat.displayName} (fat.id)`).join('\n')}
			` : 'None'}

			${survivors.length > 0 ? stripIndents`
				__**Survivors**__
				${survivors.length} left standing.

				${survivors.map(srv => `**-** ${srv.member.displayName} (srv.member.id): \`${srv.error}\``).join('\n')}
			` : ''}
		`, { split: true });

		return null;
	}
};
<|endoftext|>


<commit_before>'use strict';

var http = require('http');
var https = require('https');
var Zlib  = require('zlib');

var Zermelo = require('../index.js');

function buildQuery(data) {
  var query = '';

  for (var i in data) {
    query += (query ? '&' : '') + i + '=';
    query += encodeURIComponent(data[i]);
  }

  return query;
};

function defaultHeaders() {
  return {
      'Accept'  : 'text/html,*/*;q=0.9'
    , 'Accept-Encoding' : 'gzip,identity,*;q=0'
    , 'Accept-Charset'  : 'ISO-8859-1,utf-8,*;q=0.7'
  };
};

Zermelo.prototype.Post = function(uri, data, callback) {
  data.format = 'json';

  var query   = buildQuery(data);
  var headers = defaultHeaders();
      uri     = '/api/v3/' + uri;

  headers['Content-Type']   = 'application/x-www-form-urlencoded';
  headers['Content-Length'] = Buffer.byteLength(query);

  var options = {
      'hostname': this._school + '.zportal.nl'
    , 'path'    : uri
    , 'method'  : 'POST'
    , 'headers' : headers
  };

  var req = https.request(options, function(res) {
    if(res.statusCode !== 200 && res.statusCode !== 302) {
      return callback(res.statusCode);
    }

    var location = res.headers.location;
    if(location && location.indexOf('main/----success----?code=')) {
      var url = res.headers.location;
          url = url.substring(url.indexOf("=") + 1);
          url = url.substring(0, url.indexOf('&'));

      return callback(null, url);
    }

    var head    = res.headers['content-encoding'];
    var stream  = res;
    var responseData = "";

    stream = Zlib.createGunzip();
    res.pipe(stream);

    stream.on('data', (data) => {
      responseData += data;
    });

    stream.on('end', () => {
      return callback(null, JSON.parse(responseData));
    });
  });

  req.on('error', (err) => {
    return callback(err);
  });

  req.end(query);
};

Zermelo.prototype.Get = function(uri, data, callback) {
  var query   = buildQuery(data);
  var headers = defaultHeaders();
  var url     = 'https://' + this._school + '.zportal.nl/api/v3/' + uri + '?' + query;

  https.get(url, (res) => {
    if(res.statusCode !== 200) {
      return callback(res.statusCode);
    }

    var responseData;
    res.on('data', (data) => {
      responseData += data;
    });

    res.on('end', () => {
      callback(null, JSON.parse(responseData));
    });
  }).on('error', (err) => {
    return callback(err);
  });
};
<commit_msg>Fixed JSON Issue<commit_after>'use strict';

var http = require('http');
var https = require('https');
var Zlib  = require('zlib');

var Zermelo = require('../index.js');

function buildQuery(data) {
  var query = '';

  for (var i in data) {
    query += (query ? '&' : '') + i + '=';
    query += encodeURIComponent(data[i]);
  }

  return query;
};

function defaultHeaders() {
  return {
      'Accept'  : 'text/html,*/*;q=0.9'
    , 'Accept-Encoding' : 'gzip,identity,*;q=0'
    , 'Accept-Charset'  : 'ISO-8859-1,utf-8,*;q=0.7'
  };
};

Zermelo.prototype.Post = function(uri, data, callback) {
  data.format = 'json';

  var query   = buildQuery(data);
  var headers = defaultHeaders();
      uri     = '/api/v3/' + uri;

  headers['Content-Type']   = 'application/x-www-form-urlencoded';
  headers['Content-Length'] = Buffer.byteLength(query);

  var options = {
      'hostname': this._school + '.zportal.nl'
    , 'path'    : uri
    , 'method'  : 'POST'
    , 'headers' : headers
  };

  var req = https.request(options, function(res) {
    if(res.statusCode !== 200 && res.statusCode !== 302) {
      return callback(res.statusCode);
    }

    var location = res.headers.location;
    if(location && location.indexOf('main/----success----?code=')) {
      var url = res.headers.location;
          url = url.substring(url.indexOf("=") + 1);
          url = url.substring(0, url.indexOf('&'));

      return callback(null, url);
    }

    var head    = res.headers['content-encoding'];
    var stream  = res;
    var responseData = "";

    stream = Zlib.createGunzip();
    res.pipe(stream);

    stream.on('data', (data) => {
      responseData += data;
    });

    stream.on('end', () => {
      return callback(null, JSON.parse(responseData));
    });
  });

  req.on('error', (err) => {
    return callback(err);
  });

  req.end(query);
};

Zermelo.prototype.Get = function(uri, data, callback) {
  var query   = buildQuery(data);
  var headers = defaultHeaders();
  var url     = 'https://' + this._school + '.zportal.nl/api/v3/' + uri + '?' + query;

  https.get(url, (res) => {
    if(res.statusCode !== 200) {
      return callback(res.statusCode);
    }

    var responseData = '';
    res.on('data', (data) => {
      if(data) {
        responseData += data;
      }
    });

    res.on('end', () => {
      callback(null, JSON.parse(responseData).response);
    });
  }).on('error', (err) => {
    return callback(err);
  });
};
<|endoftext|>


<commit_before>/* global Blob,URL,requestAnimationFrame */
import React from 'react';
import Color from 'color';
import uuid from 'uuid';
import hterm from '../hterm';
import Component from '../component';
import getColorList from '../utils/colors';
import notify from '../utils/notify';

export default class Term extends Component {

  constructor(props) {
    super(props);
    this.handleWheel = this.handleWheel.bind(this);
    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.handleMouseUp = this.handleMouseUp.bind(this);
    this.handleScrollEnter = this.handleScrollEnter.bind(this);
    this.handleScrollLeave = this.handleScrollLeave.bind(this);
    this.onHyperCaret = this.onHyperCaret.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    props.ref_(this);
  }

  componentDidMount() {
    const {props} = this;
    this.term = props.term || new hterm.Terminal(uuid.v4());
    this.term.onHyperCaret(this.hyperCaret);

    // the first term that's created has unknown size
    // subsequent new tabs have size
    if (props.cols && props.rows) {
      this.term.realizeSize_(props.cols, props.rows);
    }

    const prefs = this.term.getPrefs();

    prefs.set('font-family', props.fontFamily);
    prefs.set('font-size', props.fontSize);
    prefs.set('font-smoothing', props.fontSmoothing);
    prefs.set('cursor-color', this.validateColor(props.cursorColor, 'rgba(255,255,255,0.5)'));
    prefs.set('enable-clipboard-notice', false);
    prefs.set('foreground-color', props.foregroundColor);

    // hterm.ScrollPort.prototype.setBackgroundColor is overriden
    // to make hterm's background transparent. we still need to set
    // background-color for proper text rendering
    prefs.set('background-color', props.backgroundColor);
    prefs.set('color-palette-overrides', getColorList(props.colors));
    prefs.set('user-css', this.getStylesheet(props.customCSS));
    prefs.set('scrollbar-visible', false);
    prefs.set('receive-encoding', 'raw');
    prefs.set('send-encoding', 'raw');
    prefs.set('alt-sends-what', 'browser-key');

    if (props.bell === 'SOUND') {
      prefs.set('audible-bell-sound', this.props.bellSoundURL);
    } else {
      prefs.set('audible-bell-sound', '');
    }

    if (props.copyOnSelect) {
      prefs.set('copy-on-select', true);
    } else {
      prefs.set('copy-on-select', false);
    }

    this.term.onTerminalReady = () => {
      const io = this.term.io.push();
      io.onVTKeystroke = io.sendString = props.onData;
      io.onTerminalResize = (cols, rows) => {
        if (cols !== this.props.cols || rows !== this.props.rows) {
          props.onResize(cols, rows);
        }
      };

      this.term.modifierKeys = props.modifierKeys;
      // this.term.CursorNode_ is available at this point.
      this.term.setCursorShape(props.cursorShape);

      // emit onTitle event when hterm instance
      // wants to set the title of its tab
      this.term.setWindowTitle = props.onTitle;
      this.term.focusHyperCaret();
    };
    this.term.decorate(this.termRef);
    this.term.installKeyboard();
    if (this.props.onTerminal) {
      this.props.onTerminal(this.term);
    }

    const iframeWindow = this.getTermDocument().defaultView;
    iframeWindow.addEventListener('wheel', this.handleWheel);

    this.getScreenNode().addEventListener('mouseup', this.handleMouseUp);
    this.getScreenNode().addEventListener('mousedown', this.handleMouseDown);
  }

  handleWheel(e) {
    if (this.props.onWheel) {
      this.props.onWheel(e);
    }
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', true);
    clearTimeout(this.scrollbarsHideTimer);
    if (!this.scrollMouseEnter) {
      this.scrollbarsHideTimer = setTimeout(() => {
        prefs.set('scrollbar-visible', false);
      }, 1000);
    }
  }

  handleScrollEnter() {
    clearTimeout(this.scrollbarsHideTimer);
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', true);
    this.scrollMouseEnter = true;
  }

  handleScrollLeave() {
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', false);
    this.scrollMouseEnter = false;
  }

  handleMouseUp() {
    this.props.onActive();
    // this makes sure that we focus the hyper caret only
    // if a click on the term does not result in a selection
    // otherwise, if we focus without such check, it'd be
    // impossible to select a piece of text
    if (this.term.document_.getSelection().type !== 'Range') {
      this.term.focusHyperCaret();
    }
  }
  handleFocus() {
    // This will in turn result in `this.focus()` being
    // called, which is unecessary.
    // Should investigate if it matters.
    this.props.onActive();
  }

  handleKeyDown(e) {
    if (e.ctrlKey && e.key === 'c') {
      this.props.onURLAbort();
    }
  }

  onHyperCaret(caret) {
    this.hyperCaret = caret;
  }

  write(data) {
    // sometimes the preference set above for
    // `receive-encoding` is not known by the vt
    // before we type to write (since the preference
    // manager is asynchronous), so we force it to
    // avoid buffering
    // this fixes a race condition where sometimes
    // opening new sessions results in broken
    // output due to the term attempting to decode
    // as `utf-8` instead of `raw`
    if (this.term.vt.characterEncoding !== 'raw') {
      this.term.vt.characterEncoding = 'raw';
    }

    this.term.io.writeUTF8(data);
  }

  focus() {
    this.term.focusHyperCaret();
  }

  clear() {
    this.term.clearPreserveCursorRow();

    // If cursor is still not at the top, a command is probably
    // running and we'd like to delete the whole screen.
    // Move cursor to top
    if (this.term.getCursorRow() !== 0) {
      this.write('\x1B[0;0H\x1B[2J');
    }
  }

  moveWordLeft() {
    this.term.onVTKeystroke('\x1bb');
  }

  moveWordRight() {
    this.term.onVTKeystroke('\x1bf');
  }

  deleteWordLeft() {
    this.term.onVTKeystroke('\x1b\x7f');
  }

  deleteWordRight() {
    this.term.onVTKeystroke('\x1bd');
  }

  deleteLine() {
    this.term.onVTKeystroke('\x1bw');
  }

  moveToStart() {
    this.term.onVTKeystroke('\x01');
  }

  moveToEnd() {
    this.term.onVTKeystroke('\x05');
  }

  selectAll() {
    this.term.selectAll();
  }

  getScreenNode() {
    return this.term.scrollPort_.getScreenNode();
  }

  getTermDocument() {
    return this.term.document_;
  }

  getStylesheet(css) {
    const hyperCaret = `
      .hyper-caret {
        outline: none;
        display: inline-block;
        color: transparent;
        text-shadow: 0 0 0 black;
        font-family: ${this.props.fontFamily};
        font-size: ${this.props.fontSize}px;
      }
    `;
    const scrollBarCss = `
      ::-webkit-scrollbar {
        width: 5px;
      }
      ::-webkit-scrollbar-thumb {
        -webkit-border-radius: 10px;
        border-radius: 10px;
        background: ${this.props.borderColor};
      }
      ::-webkit-scrollbar-thumb:window-inactive {
        background: ${this.props.borderColor};
      }
    `;
    return URL.createObjectURL(new Blob([`
      .cursor-node[focus="false"] {
         border-width: 1px !important;
      }
      .cursor-node[focus="true"] {
        border-left-width: 1px;
        border-bottom-width: 2px;
      }
      ${hyperCaret}
      ${scrollBarCss}
      ${css}
    `], {type: 'text/css'}));
  }

  validateColor(color, alternative = 'rgb(255,255,255)') {
    try {
      return Color(color).rgbString();
    } catch (err) {
      notify(`color "${color}" is invalid`);
    }
    return alternative;
  }

  handleMouseDown(ev) {
    // we prevent losing focus when clicking the boundary
    // wrappers of the main terminal element
    if (ev.target === this.termWrapperRef ||
        ev.target === this.termRef) {
      ev.preventDefault();
    }
    if (this.props.quickEdit) {
      this.term.onMouseDown_(ev);
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.url !== nextProps.url) {
      // when the url prop changes, we make sure
      // the terminal starts or stops ignoring
      // key input so that it doesn't conflict
      // with the <webview>
      if (nextProps.url) {
        this.term.io.push();
        window.addEventListener('keydown', this.handleKeyDown);
      } else {
        window.removeEventListener('keydown', this.handleKeyDown);
        this.term.io.pop();
      }
    }

    if (!this.props.cleared && nextProps.cleared) {
      this.clear();
    }

    const prefs = this.term.getPrefs();

    if (this.props.fontSize !== nextProps.fontSize) {
      prefs.set('font-size', nextProps.fontSize);
      this.hyperCaret.style.fontSize = nextProps.fontSize + 'px';
    }

    if (this.props.foregroundColor !== nextProps.foregroundColor) {
      prefs.set('foreground-color', nextProps.foregroundColor);
    }

    if (this.props.fontFamily !== nextProps.fontFamily) {
      prefs.set('font-family', nextProps.fontFamily);
      this.hyperCaret.style.fontFamily = nextProps.fontFamily;
    }

    if (this.props.fontSmoothing !== nextProps.fontSmoothing) {
      prefs.set('font-smoothing', nextProps.fontSmoothing);
    }

    if (this.props.cursorColor !== nextProps.cursorColor) {
      prefs.set('cursor-color', this.validateColor(nextProps.cursorColor, 'rgba(255,255,255,0.5)'));
    }

    if (this.props.cursorShape !== nextProps.cursorShape) {
      this.term.setCursorShape(nextProps.cursorShape);
    }

    if (this.props.colors !== nextProps.colors) {
      prefs.set('color-palette-overrides', getColorList(nextProps.colors));
    }

    if (this.props.customCSS !== nextProps.customCSS) {
      prefs.set('user-css', this.getStylesheet(nextProps.customCSS));
    }

    if (this.props.bell === 'SOUND') {
      prefs.set('audible-bell-sound', this.props.bellSoundURL);
    } else {
      prefs.set('audible-bell-sound', '');
    }

    if (this.props.copyOnSelect) {
      prefs.set('copy-on-select', true);
    } else {
      prefs.set('copy-on-select', false);
    }
  }

  componentWillUnmount() {
    clearTimeout(this.scrollbarsHideTimer);
    this.props.ref_(null);
  }

  template(css) {
    return (<div
      ref={component => {
        this.termWrapperRef = component;
      }}
      className={css('fit', this.props.isTermActive && 'active')}
      onMouseDown={this.handleMouseDown}
      style={{padding: this.props.padding}}
      >
      { this.props.customChildrenBefore }
      <div
        ref={component => {
          this.termRef = component;
        }}
        className={css('fit', 'term')}
        />
      { this.props.url ?
        <webview
          key="hyper-webview"
          src={this.props.url}
          onFocus={this.handleFocus}
          style={{
            background: '#fff',
            position: 'absolute',
            top: 0,
            left: 0,
            display: 'inline-flex',
            width: '100%',
            height: '100%'
          }}
          /> :
        <div // eslint-disable-line react/jsx-indent
          key="scrollbar"
          className={css('scrollbarShim')}
          onMouseEnter={this.handleScrollEnter}
          onMouseLeave={this.handleScrollLeave}
          />
      }
      <div key="hyper-caret" contentEditable className="hyper-caret" ref={this.onHyperCaret}/>
      { this.props.customChildren }
    </div>);
  }

  styles() {
    return {
      fit: {
        display: 'block',
        width: '100%',
        height: '100%'
      },

      term: {
        position: 'relative'
      },

      scrollbarShim: {
        position: 'fixed',
        right: 0,
        width: '50px',
        top: 0,
        bottom: 0,
        pointerEvents: 'none'
      }
    };
  }
}
<commit_msg>Revert "Define widths for BEAM and UNDERLINE cursors (#1389)"<commit_after>/* global Blob,URL,requestAnimationFrame */
import React from 'react';
import Color from 'color';
import uuid from 'uuid';
import hterm from '../hterm';
import Component from '../component';
import getColorList from '../utils/colors';
import notify from '../utils/notify';

export default class Term extends Component {

  constructor(props) {
    super(props);
    this.handleWheel = this.handleWheel.bind(this);
    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.handleMouseUp = this.handleMouseUp.bind(this);
    this.handleScrollEnter = this.handleScrollEnter.bind(this);
    this.handleScrollLeave = this.handleScrollLeave.bind(this);
    this.onHyperCaret = this.onHyperCaret.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    props.ref_(this);
  }

  componentDidMount() {
    const {props} = this;
    this.term = props.term || new hterm.Terminal(uuid.v4());
    this.term.onHyperCaret(this.hyperCaret);

    // the first term that's created has unknown size
    // subsequent new tabs have size
    if (props.cols && props.rows) {
      this.term.realizeSize_(props.cols, props.rows);
    }

    const prefs = this.term.getPrefs();

    prefs.set('font-family', props.fontFamily);
    prefs.set('font-size', props.fontSize);
    prefs.set('font-smoothing', props.fontSmoothing);
    prefs.set('cursor-color', this.validateColor(props.cursorColor, 'rgba(255,255,255,0.5)'));
    prefs.set('enable-clipboard-notice', false);
    prefs.set('foreground-color', props.foregroundColor);

    // hterm.ScrollPort.prototype.setBackgroundColor is overriden
    // to make hterm's background transparent. we still need to set
    // background-color for proper text rendering
    prefs.set('background-color', props.backgroundColor);
    prefs.set('color-palette-overrides', getColorList(props.colors));
    prefs.set('user-css', this.getStylesheet(props.customCSS));
    prefs.set('scrollbar-visible', false);
    prefs.set('receive-encoding', 'raw');
    prefs.set('send-encoding', 'raw');
    prefs.set('alt-sends-what', 'browser-key');

    if (props.bell === 'SOUND') {
      prefs.set('audible-bell-sound', this.props.bellSoundURL);
    } else {
      prefs.set('audible-bell-sound', '');
    }

    if (props.copyOnSelect) {
      prefs.set('copy-on-select', true);
    } else {
      prefs.set('copy-on-select', false);
    }

    this.term.onTerminalReady = () => {
      const io = this.term.io.push();
      io.onVTKeystroke = io.sendString = props.onData;
      io.onTerminalResize = (cols, rows) => {
        if (cols !== this.props.cols || rows !== this.props.rows) {
          props.onResize(cols, rows);
        }
      };

      this.term.modifierKeys = props.modifierKeys;
      // this.term.CursorNode_ is available at this point.
      this.term.setCursorShape(props.cursorShape);

      // emit onTitle event when hterm instance
      // wants to set the title of its tab
      this.term.setWindowTitle = props.onTitle;
      this.term.focusHyperCaret();
    };
    this.term.decorate(this.termRef);
    this.term.installKeyboard();
    if (this.props.onTerminal) {
      this.props.onTerminal(this.term);
    }

    const iframeWindow = this.getTermDocument().defaultView;
    iframeWindow.addEventListener('wheel', this.handleWheel);

    this.getScreenNode().addEventListener('mouseup', this.handleMouseUp);
    this.getScreenNode().addEventListener('mousedown', this.handleMouseDown);
  }

  handleWheel(e) {
    if (this.props.onWheel) {
      this.props.onWheel(e);
    }
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', true);
    clearTimeout(this.scrollbarsHideTimer);
    if (!this.scrollMouseEnter) {
      this.scrollbarsHideTimer = setTimeout(() => {
        prefs.set('scrollbar-visible', false);
      }, 1000);
    }
  }

  handleScrollEnter() {
    clearTimeout(this.scrollbarsHideTimer);
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', true);
    this.scrollMouseEnter = true;
  }

  handleScrollLeave() {
    const prefs = this.term.getPrefs();
    prefs.set('scrollbar-visible', false);
    this.scrollMouseEnter = false;
  }

  handleMouseUp() {
    this.props.onActive();
    // this makes sure that we focus the hyper caret only
    // if a click on the term does not result in a selection
    // otherwise, if we focus without such check, it'd be
    // impossible to select a piece of text
    if (this.term.document_.getSelection().type !== 'Range') {
      this.term.focusHyperCaret();
    }
  }
  handleFocus() {
    // This will in turn result in `this.focus()` being
    // called, which is unecessary.
    // Should investigate if it matters.
    this.props.onActive();
  }

  handleKeyDown(e) {
    if (e.ctrlKey && e.key === 'c') {
      this.props.onURLAbort();
    }
  }

  onHyperCaret(caret) {
    this.hyperCaret = caret;
  }

  write(data) {
    // sometimes the preference set above for
    // `receive-encoding` is not known by the vt
    // before we type to write (since the preference
    // manager is asynchronous), so we force it to
    // avoid buffering
    // this fixes a race condition where sometimes
    // opening new sessions results in broken
    // output due to the term attempting to decode
    // as `utf-8` instead of `raw`
    if (this.term.vt.characterEncoding !== 'raw') {
      this.term.vt.characterEncoding = 'raw';
    }

    this.term.io.writeUTF8(data);
  }

  focus() {
    this.term.focusHyperCaret();
  }

  clear() {
    this.term.clearPreserveCursorRow();

    // If cursor is still not at the top, a command is probably
    // running and we'd like to delete the whole screen.
    // Move cursor to top
    if (this.term.getCursorRow() !== 0) {
      this.write('\x1B[0;0H\x1B[2J');
    }
  }

  moveWordLeft() {
    this.term.onVTKeystroke('\x1bb');
  }

  moveWordRight() {
    this.term.onVTKeystroke('\x1bf');
  }

  deleteWordLeft() {
    this.term.onVTKeystroke('\x1b\x7f');
  }

  deleteWordRight() {
    this.term.onVTKeystroke('\x1bd');
  }

  deleteLine() {
    this.term.onVTKeystroke('\x1bw');
  }

  moveToStart() {
    this.term.onVTKeystroke('\x01');
  }

  moveToEnd() {
    this.term.onVTKeystroke('\x05');
  }

  selectAll() {
    this.term.selectAll();
  }

  getScreenNode() {
    return this.term.scrollPort_.getScreenNode();
  }

  getTermDocument() {
    return this.term.document_;
  }

  getStylesheet(css) {
    const hyperCaret = `
      .hyper-caret {
        outline: none;
        display: inline-block;
        color: transparent;
        text-shadow: 0 0 0 black;
        font-family: ${this.props.fontFamily};
        font-size: ${this.props.fontSize}px;
      }
    `;
    const scrollBarCss = `
      ::-webkit-scrollbar {
        width: 5px;
      }
      ::-webkit-scrollbar-thumb {
        -webkit-border-radius: 10px;
        border-radius: 10px;
        background: ${this.props.borderColor};
      }
      ::-webkit-scrollbar-thumb:window-inactive {
        background: ${this.props.borderColor};
      }
    `;
    return URL.createObjectURL(new Blob([`
      .cursor-node[focus="false"] {
         border-width: 1px !important;
      }
      ${hyperCaret}
      ${scrollBarCss}
      ${css}
    `], {type: 'text/css'}));
  }

  validateColor(color, alternative = 'rgb(255,255,255)') {
    try {
      return Color(color).rgbString();
    } catch (err) {
      notify(`color "${color}" is invalid`);
    }
    return alternative;
  }

  handleMouseDown(ev) {
    // we prevent losing focus when clicking the boundary
    // wrappers of the main terminal element
    if (ev.target === this.termWrapperRef ||
        ev.target === this.termRef) {
      ev.preventDefault();
    }
    if (this.props.quickEdit) {
      this.term.onMouseDown_(ev);
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.url !== nextProps.url) {
      // when the url prop changes, we make sure
      // the terminal starts or stops ignoring
      // key input so that it doesn't conflict
      // with the <webview>
      if (nextProps.url) {
        this.term.io.push();
        window.addEventListener('keydown', this.handleKeyDown);
      } else {
        window.removeEventListener('keydown', this.handleKeyDown);
        this.term.io.pop();
      }
    }

    if (!this.props.cleared && nextProps.cleared) {
      this.clear();
    }

    const prefs = this.term.getPrefs();

    if (this.props.fontSize !== nextProps.fontSize) {
      prefs.set('font-size', nextProps.fontSize);
      this.hyperCaret.style.fontSize = nextProps.fontSize + 'px';
    }

    if (this.props.foregroundColor !== nextProps.foregroundColor) {
      prefs.set('foreground-color', nextProps.foregroundColor);
    }

    if (this.props.fontFamily !== nextProps.fontFamily) {
      prefs.set('font-family', nextProps.fontFamily);
      this.hyperCaret.style.fontFamily = nextProps.fontFamily;
    }

    if (this.props.fontSmoothing !== nextProps.fontSmoothing) {
      prefs.set('font-smoothing', nextProps.fontSmoothing);
    }

    if (this.props.cursorColor !== nextProps.cursorColor) {
      prefs.set('cursor-color', this.validateColor(nextProps.cursorColor, 'rgba(255,255,255,0.5)'));
    }

    if (this.props.cursorShape !== nextProps.cursorShape) {
      this.term.setCursorShape(nextProps.cursorShape);
    }

    if (this.props.colors !== nextProps.colors) {
      prefs.set('color-palette-overrides', getColorList(nextProps.colors));
    }

    if (this.props.customCSS !== nextProps.customCSS) {
      prefs.set('user-css', this.getStylesheet(nextProps.customCSS));
    }

    if (this.props.bell === 'SOUND') {
      prefs.set('audible-bell-sound', this.props.bellSoundURL);
    } else {
      prefs.set('audible-bell-sound', '');
    }

    if (this.props.copyOnSelect) {
      prefs.set('copy-on-select', true);
    } else {
      prefs.set('copy-on-select', false);
    }
  }

  componentWillUnmount() {
    clearTimeout(this.scrollbarsHideTimer);
    this.props.ref_(null);
  }

  template(css) {
    return (<div
      ref={component => {
        this.termWrapperRef = component;
      }}
      className={css('fit', this.props.isTermActive && 'active')}
      onMouseDown={this.handleMouseDown}
      style={{padding: this.props.padding}}
      >
      { this.props.customChildrenBefore }
      <div
        ref={component => {
          this.termRef = component;
        }}
        className={css('fit', 'term')}
        />
      { this.props.url ?
        <webview
          key="hyper-webview"
          src={this.props.url}
          onFocus={this.handleFocus}
          style={{
            background: '#fff',
            position: 'absolute',
            top: 0,
            left: 0,
            display: 'inline-flex',
            width: '100%',
            height: '100%'
          }}
          /> :
        <div // eslint-disable-line react/jsx-indent
          key="scrollbar"
          className={css('scrollbarShim')}
          onMouseEnter={this.handleScrollEnter}
          onMouseLeave={this.handleScrollLeave}
          />
      }
      <div key="hyper-caret" contentEditable className="hyper-caret" ref={this.onHyperCaret}/>
      { this.props.customChildren }
    </div>);
  }

  styles() {
    return {
      fit: {
        display: 'block',
        width: '100%',
        height: '100%'
      },

      term: {
        position: 'relative'
      },

      scrollbarShim: {
        position: 'fixed',
        right: 0,
        width: '50px',
        top: 0,
        bottom: 0,
        pointerEvents: 'none'
      }
    };
  }
}
<|endoftext|>


<commit_before>dojo.provide("dojox.drawing.ui.Toolbar");
dojo.require("dojox.drawing.library.icons");

dojo.declare("dojox.drawing.ui.Toolbar", [], {
	// summary:
	//		A Toolbar used for holding buttons; typically representing the Stencils
	//		used for a DojoX Drawing.
	// description:
	//		Creates a GFX-based toobar that holds GFX-based buttons. Can be either created
	//		within the actual drawing or within a seperate DOM element. When within the
	//		drawing, the toolbar will cover a portion of the drawing; hence the option.
	//
	//		A Toolbar can be created programmtically or in markup. Currently markup is as
	//		a separate DOM element and programmtic is within the drawing.
	// examples:
	//		|	dojo.connect(myDrawing, "onSurfaceReady", function(){
	//		|		new dojox.drawing.ui.Toolbar({
	//		|			drawing:myDrawing,
	//		|			tools:"all",
	//		|			plugs:"all",
	//		|			selected:"ellipse"
	//		|		});
	//		|	});
	//
	//		|

	//
	//
	constructor: function(props, node){
		//console.warn("GFX Toolbar:", props, node)
		this.util = dojox.drawing.util.common;

		// no mixin. painful.
		if(props.drawing){
			// programmatic
			this.toolDrawing = props.drawing;
			this.drawing = this.toolDrawing;
			this.width = this.toolDrawing.width;
			this.height = this.toolDrawing.height;
			this.strSelected = props.selected;
			this.strTools = props.tools;
			this.strPlugs = props.plugs;
			this._mixprops(["padding", "margin", "size", "radius"], props);
			this.addBack()
		}else{
			// markup
			var box = dojo.marginBox(node);
			this.width = box.w;
			this.height = box.h;
			this.strSelected = dojo.attr(node, "selected");
			this.strTools = dojo.attr(node, "tools");
			this.strPlugs = dojo.attr(node, "plugs");
			this._mixprops(["padding", "margin", "size", "radius"], node);
			this.toolDrawing = new dojox.drawing.Drawing({mode:"ui"}, node);
		}

		this.horizontal = this.width > this.height;

		if(this.toolDrawing.ready){
			this.makeButtons();
		}else{
			var c = dojo.connect(this.toolDrawing, "onSurfaceReady", this, function(){
				//console.log("TB built")
				dojo.disconnect(c);
				this.drawing = dojox.drawing.getRegistered("drawing", dojo.attr(node, "drawingId")); //
				this.makeButtons();
			});
		}

	},

	// padding:Number
	//		The amount of spce between the top and left of the toolbar and the buttons.
	padding:10,
	// margin: Number
	//		The space between each button.
	margin:5,
	// size: Number
	//		The width and height of the button
	size:30,
	// radius: Number
	//		The size of the button's rounded corner
	radius:3,
	//
	// toolPlugGap: number
	//		The distnce between the tool buttons and plug buttons
	toolPlugGap:20,

	//	strSlelected | selected: String
	//		The button that should be selected at startup.
	strSelected:"",
	//	strTools | tools: String
	//		A comma delineated list of the Stencil-tools to include in the Toolbar.
	//		If "all" is used, all registered tools are included.
	strTools:"",
	//	strPlugs | plugs: String
	//		A comma delineated list of the plugins to include in the Toolbar.
	//		If "all" is used, all registered plugins are included.
	strPlugs:"",

	makeButtons: function(){
		// summary:
		//		Internal. create buttons.
		this.buttons = [];
		this.plugins = [];

		var x = this.padding, y = this.padding, w = this.size, h = this.size, r = this.radius, g = this.margin,
				 sym = dojox.drawing.library.icons,
				 s = {place:"BR", size:2, mult:4};

		if(this.strTools){
			var toolAr = [];
			if(this.strTools=="all"){
				for(var nm in dojox.drawing.getRegistered("tool")){
					toolAr.push(this.util.abbr(nm));
				}
			}else{
				toolAr = this.strTools.split(",");
				dojo.map(toolAr, function(t){ return dojo.trim(t); });
			}

			dojo.forEach(toolAr, function(t){
				t = dojo.trim(t);
				var btn = this.toolDrawing.addUI("button", {data:{x:x, y:y, width:w, height:h, r:r}, toolType:t, icon:sym[t], shadow:s, scope:this, callback:"onToolClick"})
				this.buttons.push(btn);
				if(this.strSelected==t){
					btn.select();
					this.selected = btn;
					this.drawing.setTool(btn.toolType);
				}
				if(this.horizontal){
					y += h + g;
				}else{
					y += h + g;
				}
			}, this);
		}

		if(this.horizontal){
			y += this.toolPlugGap;
		}else{
			y += this.toolPlugGap;
		}

		if(this.strPlugs){
			var plugAr = [];
			if(this.strPlugs=="all"){
				for(var nm in dojox.drawing.getRegistered("plugin")){
					plugAr.push(this.util.abbr(nm));
				}
			}else{
				plugAr = this.strPlugs.split(",")
				dojo.map(plugAr, function(p){ return dojo.trim(p); });
			}

			dojo.forEach(plugAr, function(p){
				t = dojo.trim(p);
				//console.log("   plugin:", p)
				var btn = this.toolDrawing.addUI("button", {data:{x:x, y:y, width:w, height:h, r:r}, toolType:t, icon:sym[t], shadow:s, scope:this, callback:"onPlugClick"})
				this.plugins.push(btn);
				if(this.horizontal){
					y += h + g;
				}else{
					y += h + g;
				}
				this.drawing.addPlugin({name:this.drawing.stencilTypeMap[p], options:{button:btn}});
			}, this);
		}

		dojo.connect(this.drawing, "onRenderStencil", this, "onRenderStencil")
	},

	onRenderStencil: function(/* Object */stencil){
		// summary:
		//		Stencil render event.
		this.selected && this.selected.deselect();
		this.selected = null;
	},

	addTool: function(){
		// TODO: add button here
	},

	addPlugin: function(){
		// TODO: add button here
	},

	addBack: function(){
		// summary:
		//		Internal. Adds the back, behind the toolbar.
		this.toolDrawing.addUI("rect", {data:{x:0, y:0, width:this.width, height:this.size + (this.padding*2), fill:"#ffffff", borderWidth:0}});
	},

	onToolClick: function(/*Object*/button){
		// summary:
		//		Tool click event. May be connected to.
		//
		dojo.forEach(this.buttons, function(b){
			if(b.id==button.id){
				b.select();
				this.selected = b;
				this.drawing.setTool(button.toolType)
			}else{
				b.deselect();
			}
		},this)
	},

	onPlugClick: function(/*Object*/button){
		// summary:
		//		Plugin click event. May be connected to.
	},

	_mixprops: function(/*Array*/props, /*Object | Node*/objNode){
		// summary:
		//		Internally used for mixing in props from an object or
		//		from a dom node.
		dojo.forEach(props, function(p){
			this[p] = objNode.tagName
				? dojo.attr(objNode, p)===null ? this[p] : dojo.attr(objNode, p)
				: objNode[p]===undefined ? this[p] : objNode[p];
		}, this);
	}

});<commit_msg>Fixes #10637 - Again. Toolbar wasn't checking the clickmode.. \!strict<commit_after>dojo.provide("dojox.drawing.ui.Toolbar");
dojo.require("dojox.drawing.library.icons");

dojo.declare("dojox.drawing.ui.Toolbar", [], {
	// summary:
	//		A Toolbar used for holding buttons; typically representing the Stencils
	//		used for a DojoX Drawing.
	// description:
	//		Creates a GFX-based toobar that holds GFX-based buttons. Can be either created
	//		within the actual drawing or within a seperate DOM element. When within the
	//		drawing, the toolbar will cover a portion of the drawing; hence the option.
	//
	//		A Toolbar can be created programmtically or in markup. Currently markup is as
	//		a separate DOM element and programmtic is within the drawing.
	// examples:
	//		|	dojo.connect(myDrawing, "onSurfaceReady", function(){
	//		|		new dojox.drawing.ui.Toolbar({
	//		|			drawing:myDrawing,
	//		|			tools:"all",
	//		|			plugs:"all",
	//		|			selected:"ellipse"
	//		|		});
	//		|	});
	//
	//		|

	//
	//
	constructor: function(props, node){
		//console.warn("GFX Toolbar:", props, node)
		this.util = dojox.drawing.util.common;

		// no mixin. painful.
		if(props.drawing){
			// programmatic
			this.toolDrawing = props.drawing;
			this.drawing = this.toolDrawing;
			this.width = this.toolDrawing.width;
			this.height = this.toolDrawing.height;
			this.strSelected = props.selected;
			this.strTools = props.tools;
			this.strPlugs = props.plugs;
			this._mixprops(["padding", "margin", "size", "radius"], props);
			this.addBack()
		}else{
			// markup
			var box = dojo.marginBox(node);
			this.width = box.w;
			this.height = box.h;
			this.strSelected = dojo.attr(node, "selected");
			this.strTools = dojo.attr(node, "tools");
			this.strPlugs = dojo.attr(node, "plugs");
			this._mixprops(["padding", "margin", "size", "radius"], node);
			this.toolDrawing = new dojox.drawing.Drawing({mode:"ui"}, node);
		}

		this.horizontal = this.width > this.height;

		if(this.toolDrawing.ready){
			this.makeButtons();
		}else{
			var c = dojo.connect(this.toolDrawing, "onSurfaceReady", this, function(){
				//console.log("TB built")
				dojo.disconnect(c);
				this.drawing = dojox.drawing.getRegistered("drawing", dojo.attr(node, "drawingId")); //
				this.makeButtons();
			});
		}

	},

	// padding:Number
	//		The amount of spce between the top and left of the toolbar and the buttons.
	padding:10,
	// margin: Number
	//		The space between each button.
	margin:5,
	// size: Number
	//		The width and height of the button
	size:30,
	// radius: Number
	//		The size of the button's rounded corner
	radius:3,
	//
	// toolPlugGap: number
	//		The distnce between the tool buttons and plug buttons
	toolPlugGap:20,

	//	strSlelected | selected: String
	//		The button that should be selected at startup.
	strSelected:"",
	//	strTools | tools: String
	//		A comma delineated list of the Stencil-tools to include in the Toolbar.
	//		If "all" is used, all registered tools are included.
	strTools:"",
	//	strPlugs | plugs: String
	//		A comma delineated list of the plugins to include in the Toolbar.
	//		If "all" is used, all registered plugins are included.
	strPlugs:"",

	makeButtons: function(){
		// summary:
		//		Internal. create buttons.
		this.buttons = [];
		this.plugins = [];

		var x = this.padding, y = this.padding, w = this.size, h = this.size, r = this.radius, g = this.margin,
				 sym = dojox.drawing.library.icons,
				 s = {place:"BR", size:2, mult:4};

		if(this.strTools){
			var toolAr = [];
			if(this.strTools=="all"){
				for(var nm in dojox.drawing.getRegistered("tool")){
					toolAr.push(this.util.abbr(nm));
				}
			}else{
				toolAr = this.strTools.split(",");
				dojo.map(toolAr, function(t){ return dojo.trim(t); });
			}

			dojo.forEach(toolAr, function(t){
				t = dojo.trim(t);
				var btn = this.toolDrawing.addUI("button", {data:{x:x, y:y, width:w, height:h, r:r}, toolType:t, icon:sym[t], shadow:s, scope:this, callback:"onToolClick"})
				this.buttons.push(btn);
				if(this.strSelected==t){
					btn.select();
					this.selected = btn;
					this.drawing.setTool(btn.toolType);
				}
				if(this.horizontal){
					y += h + g;
				}else{
					y += h + g;
				}
			}, this);
		}

		if(this.horizontal){
			y += this.toolPlugGap;
		}else{
			y += this.toolPlugGap;
		}

		if(this.strPlugs){
			var plugAr = [];
			if(this.strPlugs=="all"){
				for(var nm in dojox.drawing.getRegistered("plugin")){
					plugAr.push(this.util.abbr(nm));
				}
			}else{
				plugAr = this.strPlugs.split(",")
				dojo.map(plugAr, function(p){ return dojo.trim(p); });
			}

			dojo.forEach(plugAr, function(p){
				t = dojo.trim(p);
				//console.log("   plugin:", p)
				var btn = this.toolDrawing.addUI("button", {data:{x:x, y:y, width:w, height:h, r:r}, toolType:t, icon:sym[t], shadow:s, scope:this, callback:"onPlugClick"})
				this.plugins.push(btn);
				if(this.horizontal){
					y += h + g;
				}else{
					y += h + g;
				}
				this.drawing.addPlugin({name:this.drawing.stencilTypeMap[p], options:{button:btn}});
			}, this);
		}

		dojo.connect(this.drawing, "onRenderStencil", this, "onRenderStencil");
	},

	onRenderStencil: function(/* Object */stencil){
		// summary:
		//		Stencil render event.
		if(!this.toolDrawing.defaults.clickMode){
			this.selected && this.selected.deselect();
			this.selected = null;
		}
	},

	addTool: function(){
		// TODO: add button here
	},

	addPlugin: function(){
		// TODO: add button here
	},

	addBack: function(){
		// summary:
		//		Internal. Adds the back, behind the toolbar.
		this.toolDrawing.addUI("rect", {data:{x:0, y:0, width:this.width, height:this.size + (this.padding*2), fill:"#ffffff", borderWidth:0}});
	},

	onToolClick: function(/*Object*/button){
		// summary:
		//		Tool click event. May be connected to.
		//
		dojo.forEach(this.buttons, function(b){
			if(b.id==button.id){
				b.select();
				this.selected = b;
				this.drawing.setTool(button.toolType)
			}else{
				b.deselect();
			}
		},this)
	},

	onPlugClick: function(/*Object*/button){
		// summary:
		//		Plugin click event. May be connected to.
	},

	_mixprops: function(/*Array*/props, /*Object | Node*/objNode){
		// summary:
		//		Internally used for mixing in props from an object or
		//		from a dom node.
		dojo.forEach(props, function(p){
			this[p] = objNode.tagName
				? dojo.attr(objNode, p)===null ? this[p] : dojo.attr(objNode, p)
				: objNode[p]===undefined ? this[p] : objNode[p];
		}, this);
	}

});<|endoftext|>


<commit_before>var kafka = require('kafka-node'),
    config = require('../config'),
    consumers = {},
    log = require('../logger'),
    logger = log.logger,
    uuid = require('uuid');

var get = function (group, id, callback) {
        if (arguments.length < 1) {
            callback(new Error('Group and Instance ID or ID required.'));
        }

        if (typeof id === 'string'){
            group += '/' + id;
        }
        else if (typeof id === 'function') {
            callback = id;
        }

        var consumer = consumers[group];
        if (!consumer) {
            return callback( {message: 'consumer not found'}, null);
        }

        if (callback) {
            return callback(null, consumer);
        }

        return consumer;
    },

    add = function (consumer, callback) {
        consumer.instanceId = consumer.instanceId || uuid.v4();
        consumer.id = consumer.group + '/' + consumer.instanceId;
        if (!!consumers[consumer.id]) {
            callback(new Error('Consumer with ID ' + consumer.id + ' already exists.'));
        }

        consumer.autoOffsetReset = consumer.autoOffsetReset || 'largest';
        consumer.autoCommitEnable = typeof consumer.autoCommitEnable === 'undefined' ? config.consumer.autoCommitEnable : consumer.autoCommitEnable;
        consumer.instance = undefined;
        consumer.topics = [];
        consumer.messages = [];
        consumer.created = new Date();
        consumer.lastPoll = Date.now();
        consumer.lock = false;
        consumer.byteSize = 0;
        consumer.offsetMap = [];

        consumers[consumer.id] = consumer;
        logger.debug({consumer: consumer}, 'lib/consumerManager : consumer added');
        callback(null, consumer);
    },

    createConsumerInstance = function (consumer, topic) {
        if (consumer.lock) {
            logger.debug('lib/consumerManager : consumer is locked, return as there is already an instance or its being recreated');
            return;
        }

        logger.debug('lib/consumerManager : creating a kafka client');
        var client = new kafka.Client(config.kafka.zkConnect, config.kafka.clientId);
        logger.debug('lib/consumerManager : creating a new consumer');
        consumer.instance = new kafka.HighLevelConsumer(client, [{
            topic: topic
        }], {
            groupId: consumer.group,
            // Auto commit config
            autoCommit: false,
            // The max wait time is the maximum amount of time in milliseconds to block waiting if insufficient data is available at the time the request is issued, default 100ms
            fetchMaxWaitMs: 100,
            // This is the minimum number of bytes of messages that must be available to give a response, default 1 byte
            fetchMinBytes: 1,
            // The maximum bytes to include in the message set for this partition. This helps bound the size of the response.
            fetchMaxBytes: 4 * 1024 * 1024, // 4MB
            // If set true, consumer will fetch message from the given offset in the payloads
            fromOffset: false,
            // If set to 'buffer', values will be returned as raw buffer objects.
            encoding: 'utf8'
        });

        logger.debug('lib/consumerManager : consumer created');
        consumer.instance.on('message', function (m) {
            logger.trace({messages: consumer.messages, message: m}, 'lib/consumerManager : consumer message received');
            consumer.messages.push(m);
            consumer.byteSize += Buffer.byteLength(m.value);

            if (consumer.byteSize > (16 * 1024 * 1024)) {
                logger.info('lib/consumerManager : consumer message queue greater than acceptable size, pausing consumer');
                consumer.instance.pause();
            }
        });

        consumer.instance.on('rebalancing', function(event){
            logger.warn('lib/consumerManager : Consumer rebalancing');
            consumer.messages = [];
        });

        consumer.instance.on('error', function (e) {
            logger.error({ error: e, consumer: consumer.id }, 'lib/consumerManager : Error in consumer instance. Closing and recreating...');
            if (!consumer.lock) {
                consumer.lock = true;
                logger.debug({ consumer: consumer.id }, 'lock enabled on consumer');
                consumer.instance.close(false, function () {
                    setTimeout(function () {
                        logger.info({consumer: consumer.id}, 'lib/consumerManager : Recreating consumer');
                        createConsumerInstance(consumer, topic);
                        consumer.lock = false;
                        logger.debug({ consumer: consumer.id }, 'lock released on consumer');
                    }, 1000);
                });
            }
        });

        consumer.instance.on('offsetOutOfRange', function (e) {
            logger.warn({ error: e }, 'lib/consumerManager : Received alert for offset out of range.');
        });
    },

    findOffsetInMap = function(map, searchItem) {
        logger.trace({map : map}, 'searching the offset map');
        return map.find(function(item) {
            return  item.topic === searchItem.topic &&
                    item.partition === searchItem.partition;
        });
    },

    createOffsetItem = function(message) {
        // note : metadata is an arbitrary value for kafka-node clients
        return {
            topic : message.topic || '',
            partition: message.partition || 0,
            offset : message.offset || 0,
            metadata : 'm'
        };
    },

    createOffsetMap = function(messages) {
        return messages.reduce(function(map, message){
            logger.trace(message, 'creating offset map for message');
            var offsetItem = findOffsetInMap(map, message);

            logger.trace({offsetItem: offsetItem || 'nothing'}, 'offset item from map');
            if (offsetItem === undefined) {
                map.push(createOffsetItem(message));
            } else {
                if (message.offset > offsetItem.offset) {
                    offsetItem.offset = message.offset;
                }
            }
            return map;
        }, []);
    },

    getMessages = function (consumer, callback) {
        consumer.lastPoll = Date.now();

logger.trace({buffer: consumer.messages}, 'consumer message buffer');
        var messages = consumer.messages.splice(0, consumer.messages.length);
        consumer.offsetMap = createOffsetMap(messages);

        if (messages.length === 0) {
            return callback(null, []);
        }

        if (consumer.autoCommitEnable) {
            logger.trace({ consumer: consumer.id }, 'lib/consumerManager : Autocommit.');
            consumer.instance.commit(true);
        }

        if (consumer.instance.paused) {
            consumer.instance.resume();
        }

        logger.trace({ consumer: consumer.id }, 'lib/consumerManager : returning ' + messages.length + ' messages.');
        callback(null, messages.map(function (m) {
            return {
                topic: m.topic,
                partition: m.partition,
                offset: m.offset,
                key: m.key.toString(),
                value: m.value
            };
        }));
    },

    commitOffsets = function(consumer, cb) {
        consumer.instance.client.sendOffsetCommitRequest(
            consumer.groupid,
            consumer.offsetMap.map(function(item){
                item.fetchMaxBytes = consumer.options.fetchMaxBytes;
                return item;
            }),
            callback);
    },

    deleteConsumer = function (consumer, cb) {
        logger.debug({ consumer: consumer.id }, 'lib/consumerManager : Removing consumer from set.');
        delete consumers[consumer.id];
        logger.debug({ consumer: consumer.id }, 'lib/consumerManager : Closing consumer...');
        if (!!consumer.instance) {
            consumer.instance.close(false, function () {
                logger.debug({ consumer: consumer.id }, 'lib/consumerManager : Consumer closed.');
                if (!!cb) cb();
            });
        }
    },

    timeoutConsumers = function () {
        logger.debug('lib/consumerManager : Looking for timed-out consumers.');
        var timeoutTime = Date.now() - config.consumer.timeoutMs;
        for (var i in consumers) {
            var consumer = consumers[i];
            if (consumer.instance && consumer.lastPoll < timeoutTime) {
                logger.debug({ consumer : consumer.id }, 'lib/consumerManager : Consumer timed-out.');
                deleteConsumer(consumer);
            }
        }
        logger.debug('lib/consumerManager : Done looking for timed-out consumers.');
    };

module.exports = {
    get: get,
    add: add,
    createInstance: createConsumerInstance,
    getMessages: getMessages,
    commitOffsets: commitOffsets,
    delete: deleteConsumer,
    timeout: timeoutConsumers
};
<commit_msg>tabbing<commit_after>var kafka = require('kafka-node'),
    config = require('../config'),
    consumers = {},
    log = require('../logger'),
    logger = log.logger,
    uuid = require('uuid');

var get = function (group, id, callback) {
        if (arguments.length < 1) {
            callback(new Error('Group and Instance ID or ID required.'));
        }

        if (typeof id === 'string'){
            group += '/' + id;
        }
        else if (typeof id === 'function') {
            callback = id;
        }

        var consumer = consumers[group];
        if (!consumer) {
            return callback( {message: 'consumer not found'}, null);
        }

        if (callback) {
            return callback(null, consumer);
        }

        return consumer;
    },

    add = function (consumer, callback) {
        consumer.instanceId = consumer.instanceId || uuid.v4();
        consumer.id = consumer.group + '/' + consumer.instanceId;
        if (!!consumers[consumer.id]) {
            callback(new Error('Consumer with ID ' + consumer.id + ' already exists.'));
        }

        consumer.autoOffsetReset = consumer.autoOffsetReset || 'largest';
        consumer.autoCommitEnable = typeof consumer.autoCommitEnable === 'undefined' ? config.consumer.autoCommitEnable : consumer.autoCommitEnable;
        consumer.instance = undefined;
        consumer.topics = [];
        consumer.messages = [];
        consumer.created = new Date();
        consumer.lastPoll = Date.now();
        consumer.lock = false;
        consumer.byteSize = 0;
        consumer.offsetMap = [];

        consumers[consumer.id] = consumer;
        logger.debug({consumer: consumer}, 'lib/consumerManager : consumer added');
        callback(null, consumer);
    },

    createConsumerInstance = function (consumer, topic) {
        if (consumer.lock) {
            logger.debug('lib/consumerManager : consumer is locked, return as there is already an instance or its being recreated');
            return;
        }

        logger.debug('lib/consumerManager : creating a kafka client');
        var client = new kafka.Client(config.kafka.zkConnect, config.kafka.clientId);
        logger.debug('lib/consumerManager : creating a new consumer');
        consumer.instance = new kafka.HighLevelConsumer(client, [{
            topic: topic
        }], {
            groupId: consumer.group,
            // Auto commit config
            autoCommit: false,
            // The max wait time is the maximum amount of time in milliseconds to block waiting if insufficient data is available at the time the request is issued, default 100ms
            fetchMaxWaitMs: 100,
            // This is the minimum number of bytes of messages that must be available to give a response, default 1 byte
            fetchMinBytes: 1,
            // The maximum bytes to include in the message set for this partition. This helps bound the size of the response.
            fetchMaxBytes: 4 * 1024 * 1024, // 4MB
            // If set true, consumer will fetch message from the given offset in the payloads
            fromOffset: false,
            // If set to 'buffer', values will be returned as raw buffer objects.
            encoding: 'utf8'
        });

        logger.debug('lib/consumerManager : consumer created');
        consumer.instance.on('message', function (m) {
            logger.trace({messages: consumer.messages, message: m}, 'lib/consumerManager : consumer message received');
            consumer.messages.push(m);
            consumer.byteSize += Buffer.byteLength(m.value);

            if (consumer.byteSize > (16 * 1024 * 1024)) {
                logger.info('lib/consumerManager : consumer message queue greater than acceptable size, pausing consumer');
                consumer.instance.pause();
            }
        });

        consumer.instance.on('rebalancing', function(event){
            logger.warn('lib/consumerManager : Consumer rebalancing');
            consumer.messages = [];
        });

        consumer.instance.on('error', function (e) {
            logger.error({ error: e, consumer: consumer.id }, 'lib/consumerManager : Error in consumer instance. Closing and recreating...');
            if (!consumer.lock) {
                consumer.lock = true;
                logger.debug({ consumer: consumer.id }, 'lock enabled on consumer');
                consumer.instance.close(false, function () {
                    setTimeout(function () {
                        logger.info({consumer: consumer.id}, 'lib/consumerManager : Recreating consumer');
                        createConsumerInstance(consumer, topic);
                        consumer.lock = false;
                        logger.debug({ consumer: consumer.id }, 'lock released on consumer');
                    }, 1000);
                });
            }
        });

        consumer.instance.on('offsetOutOfRange', function (e) {
            logger.warn({ error: e }, 'lib/consumerManager : Received alert for offset out of range.');
        });
    },

    findOffsetInMap = function(map, searchItem) {
        logger.trace({map : map}, 'searching the offset map');
        return map.find(function(item) {
            return  item.topic === searchItem.topic &&
                    item.partition === searchItem.partition;
        });
    },

    createOffsetItem = function(message) {
        // note : metadata is an arbitrary value for kafka-node clients
        return {
            topic : message.topic || '',
            partition: message.partition || 0,
            offset : message.offset || 0,
            metadata : 'm'
        };
    },

    createOffsetMap = function(messages) {
        return messages.reduce(function(map, message){
            logger.trace(message, 'creating offset map for message');
            var offsetItem = findOffsetInMap(map, message);

            logger.trace({offsetItem: offsetItem || 'nothing'}, 'offset item from map');
            if (offsetItem === undefined) {
                map.push(createOffsetItem(message));
            } else {
                if (message.offset > offsetItem.offset) {
                    offsetItem.offset = message.offset;
                }
            }
            return map;
        }, []);
    },

    getMessages = function (consumer, callback) {
        consumer.lastPoll = Date.now();

        logger.trace({buffer: consumer.messages}, 'consumer message buffer');
        var messages = consumer.messages.splice(0, consumer.messages.length);
        consumer.offsetMap = createOffsetMap(messages);

        if (messages.length === 0) {
            return callback(null, []);
        }

        if (consumer.autoCommitEnable) {
            logger.trace({ consumer: consumer.id }, 'lib/consumerManager : Autocommit.');
            consumer.instance.commit(true);
        }

        if (consumer.instance.paused) {
            consumer.instance.resume();
        }

        logger.trace({ consumer: consumer.id }, 'lib/consumerManager : returning ' + messages.length + ' messages.');
        callback(null, messages.map(function (m) {
            return {
                topic: m.topic,
                partition: m.partition,
                offset: m.offset,
                key: m.key.toString(),
                value: m.value
            };
        }));
    },

    commitOffsets = function(consumer, cb) {
        consumer.instance.client.sendOffsetCommitRequest(
            consumer.groupid,
            consumer.offsetMap.map(function(item){
                item.fetchMaxBytes = consumer.options.fetchMaxBytes;
                return item;
            }),
            callback);
    },

    deleteConsumer = function (consumer, cb) {
        logger.debug({ consumer: consumer.id }, 'lib/consumerManager : Removing consumer from set.');
        delete consumers[consumer.id];
        logger.debug({ consumer: consumer.id }, 'lib/consumerManager : Closing consumer...');
        if (!!consumer.instance) {
            consumer.instance.close(false, function () {
                logger.debug({ consumer: consumer.id }, 'lib/consumerManager : Consumer closed.');
                if (!!cb) cb();
            });
        }
    },

    timeoutConsumers = function () {
        logger.debug('lib/consumerManager : Looking for timed-out consumers.');
        var timeoutTime = Date.now() - config.consumer.timeoutMs;
        for (var i in consumers) {
            var consumer = consumers[i];
            if (consumer.instance && consumer.lastPoll < timeoutTime) {
                logger.debug({ consumer : consumer.id }, 'lib/consumerManager : Consumer timed-out.');
                deleteConsumer(consumer);
            }
        }
        logger.debug('lib/consumerManager : Done looking for timed-out consumers.');
    };

module.exports = {
    get: get,
    add: add,
    createInstance: createConsumerInstance,
    getMessages: getMessages,
    commitOffsets: commitOffsets,
    delete: deleteConsumer,
    timeout: timeoutConsumers
};
<|endoftext|>


<commit_before>'use strict';

const sysPath = require('path');
const helpers = require('./helpers');
const shims = require('./shims');
const deepExtend = require('../helpers').deepExtend;
const mediator = require('../mediator');

const not = helpers.not;

const getModuleRootPath = path => {
  const split = path.split(sysPath.sep);
  const index = split.lastIndexOf('node_modules');
  return split.slice(0, index + 2).join(sysPath.sep);
};

let rootMainCache = {};
const getMainCached = path => rootMainCache[getModuleRootPath(path)];
const cacheMain = path => _getMainFile(path).then(file => rootMainCache[getModuleRootPath(path)] = file);

const resetCache = () => rootMainCache = {};

const getModuleRootName = path => {
  const split = path.split(sysPath.sep);
  const index = split.lastIndexOf('node_modules');
  return split[index + 1];
};

const getModuleFullRootName = path => {
  const split = path.split(sysPath.sep);
  const indexS = split.indexOf('node_modules');
  const indexE = split.lastIndexOf('node_modules');
  return split.slice(indexS + 1, indexE + 2).join('/');
};

const definition = (name, exp) => {
  return `require.register("${name}", function(exports, require, module) {
  module.exports = ${exp};
});`;
};

const aliasDef = (target, source) => {
  return definition(target, `require("${source}")`);
};

const simpleShimDef = (name, obj) => {
  return definition(name, JSON.stringify(obj));
};

const relativeToRoot = (filePath, relPath) => sysPath.join(
  getModuleRootPath(filePath), relPath
);

const applyPackageOverrides = pkg => {
  const pkgOverride = mediator.overrides[pkg.name];

  if (pkgOverride) {
    pkg = deepExtend(pkg, pkgOverride);
  }

  return pkg;
};
const getDepPackageJson = depPath => {
  const depJson = require(sysPath.join(depPath, 'package.json'));
  applyPackageOverrides(depJson);
  return depJson;
};

const packageJson = filePath => getDepPackageJson(
  getModuleRootPath(filePath)
);

const browserMappings = filePath => {
  const pkg = packageJson(filePath);
  const browser = pkg.browser || pkg.browserify;
  if (browser && typeof browser === 'object') {
    return browser;
  } else if (browser) {
    const obj = {};
    const path = sysPath.relative(getModuleRootPath(filePath), getMainCached(filePath));
    obj['./' + path] = './' + sysPath.join('.', browser);
    return obj;
  } else {
    return {};
  }
};

const _getMainFile = filePath => {
  const root = getModuleRootPath(filePath);
  const json = packageJson(filePath);

  return _mainFile(root, json);
};

const _mainFile = (root, json) => {
  const depMain = json.main || 'index.js';
  const fileOrDir = sysPath.join(root, depMain);
  return helpers.isDir(fileOrDir).then(isDir => {
    if (isDir) {
      return sysPath.join(fileOrDir, 'index.js');
    } else {
      return fileOrDir.indexOf('.js') === -1 ? fileOrDir + '.js' : fileOrDir;
    }
  });
};

const globalBrowserMappings = filePath => {
  const brMap = browserMappings(filePath);

  return Object.keys(brMap).filter(not(helpers.isRelative)).reduce((newBrMap, key) => {
    const val = brMap[key];
    if (val) {
      newBrMap[key] = helpers.isRelative(val) ? generateModuleName(relativeToRoot(filePath, val)) : val;
    }
    return newBrMap;
  }, {});
};

const expandedFilePath = filePath => {
  const brMap = browserMappings(filePath);

  Object.keys(brMap).filter(helpers.isRelative).forEach(key => {
    const val = brMap[key];
    if (val && filePath === relativeToRoot(filePath, val)) {
      filePath = relativeToRoot(filePath, key);
    }
  });

  return filePath;
};

const isMain = filePath => getMainCached(filePath) === sysPath.resolve(expandedFilePath(filePath));

const getNewHeader = (moduleName, source, filePath, origPath) => {
  const brMap = globalBrowserMappings(filePath);

  const p = filePath.replace(getModuleRootPath(filePath), '').replace('.json', '').replace('.js', '').split(sysPath.sep).slice(0, -1);
  const p2 = [moduleName].concat(p).join('/');
  const itemPath = isMain(filePath) ? `, '${p2}/'` : '';

  const glob = shims.findGlobals(source);

  if (shims.shouldOverrideModuleName(moduleName)) moduleName = shims.overrideModuleName(moduleName);

  if (filePath.indexOf('.json') === -1) {
    const fbModuleName = generateFileBasedModuleName(filePath);
    const fbAlias = mediator.npm.noFileBased ? shims.shouldIncludeFileBasedAlias(moduleName) : isMain(filePath) ?
      aliasDef(fbModuleName, moduleName) + '\n' :
      '';
    const fbModuleNameUnexp = generateFileBasedModuleName(origPath);
    const fbAliasUnexp = mediator.npm.noFileBased ?
      '' :
      fbModuleNameUnexp !== fbModuleName ?
        aliasDef(fbModuleNameUnexp, moduleName) :
        '';

    const mappings = JSON.stringify(brMap);
    const name = getModuleFullRootName(filePath);
    const aliases = (fbAlias + fbAliasUnexp).trim();
    const aliasesFull = aliases ? '\n\n' + aliases : '';
    return (
`\nrequire.register("${moduleName}", function(exports, require, module) {
  require = __makeRelativeRequire(require, ${mappings}, "${name}"${itemPath});
  ${glob}(function() {
    ${source.trim()}
  })();
});${aliasesFull}`);
  } else {
    return definition(moduleName, source);
  }
};

const generateModule = (filePath, source) => {
  const expandedPath = expandedFilePath(filePath);
  const mn = generateModuleName(expandedPath);
  return getNewHeader(mn, source, expandedPath, filePath);
};

const slashes = string => string.replace(/\\/g, '/');

const generateModuleName = filePath => {
  const rp = getModuleRootPath(filePath);
  const mn = getModuleFullRootName(filePath) +
    (isMain(filePath) ? '' : filePath.replace(rp, '').replace('.json', '').replace('.js', ''));

  return slashes(mn);
};

const generateFileBasedModuleName = filePath => {
  return slashes(getModuleFullRootName(filePath) + filePath.replace(getModuleRootPath(filePath), '').replace('.json', '').replace('.js', ''));
};

const makeRequire = (
`\nvar __makeRelativeRequire = function(require, mappings, pref, fullPath) {
  if (fullPath) {
    var req = require;
    require = function(path) {
      return req(path.replace('./', fullPath));
    };
  }
  var none = {};
  var tryReq = function(name, pref) {
    var val;
    try {
      val = require(pref + '/node_modules/' + name);
      return val;
    } catch (e) {
      if (e.toString().indexOf('Cannot find module') === -1) {
        throw e;
      }

      if (pref.indexOf('node_modules') !== -1) {
        var s = pref.split('/');
        var i = s.lastIndexOf('node_modules');
        var newPref = s.slice(0, i).join('/');
        return tryReq(name, newPref);
      }
    }
    return none;
  };
  return function(name) {
    if (mappings[name] !== undefined) name = mappings[name];
    name = name.replace(/\.(json|js)$/, '');
    if (name[0] !== '.' && pref) {
      var val = tryReq(name, pref);
      if (val !== none) return val;
    }
    return require(name);
  }
};
`);

module.exports = {aliasDef, simpleShimDef, applyPackageOverrides, generateModule, generateModuleName, getModuleRootName, makeRequire, cacheMain, packageJson, resetCache};
<commit_msg>Support npm @scoped/packages<commit_after>'use strict';

const sysPath = require('path');
const helpers = require('./helpers');
const shims = require('./shims');
const deepExtend = require('../helpers').deepExtend;
const mediator = require('../mediator');

const not = helpers.not;

const getModuleRootPath = path => {
  const split = path.split(sysPath.sep);
  const index = split.lastIndexOf('node_modules');
  const scoped = split[index + 1][0] === '@';
  return split.slice(0, index + 2 + (scoped ? 1 : 0)).join(sysPath.sep);
};

let rootMainCache = {};
const getMainCached = path => rootMainCache[getModuleRootPath(path)];
const cacheMain = path => _getMainFile(path).then(file => rootMainCache[getModuleRootPath(path)] = file);

const resetCache = () => rootMainCache = {};

const getModuleRootName = path => {
  const split = path.split(sysPath.sep);
  const index = split.lastIndexOf('node_modules');
  const scoped = split[index + 1][0] === '@';
  return split.slice(index + 1, index + 2 + (scoped ? 1 : 0)).join('/');
};

const getModuleFullRootName = path => {
  const split = path.split(sysPath.sep);
  const indexS = split.indexOf('node_modules');
  const indexE = split.lastIndexOf('node_modules');
  const scoped = split[indexE + 1][0] === '@';
  return split.slice(indexS + 1, indexE + 2 + (scoped ? 1 : 0)).join('/');
};

const definition = (name, exp) => {
  return `require.register("${name}", function(exports, require, module) {
  module.exports = ${exp};
});`;
};

const aliasDef = (target, source) => {
  return definition(target, `require("${source}")`);
};

const simpleShimDef = (name, obj) => {
  return definition(name, JSON.stringify(obj));
};

const relativeToRoot = (filePath, relPath) => sysPath.join(
  getModuleRootPath(filePath), relPath
);

const applyPackageOverrides = pkg => {
  const pkgOverride = mediator.overrides[pkg.name];

  if (pkgOverride) {
    pkg = deepExtend(pkg, pkgOverride);
  }

  return pkg;
};
const getDepPackageJson = depPath => {
  const depJson = require(sysPath.join(depPath, 'package.json'));
  applyPackageOverrides(depJson);
  return depJson;
};

const packageJson = filePath => getDepPackageJson(
  getModuleRootPath(filePath)
);

const browserMappings = filePath => {
  const pkg = packageJson(filePath);
  const browser = pkg.browser || pkg.browserify;
  if (browser && typeof browser === 'object') {
    return browser;
  } else if (browser) {
    const obj = {};
    const path = sysPath.relative(getModuleRootPath(filePath), getMainCached(filePath));
    obj['./' + path] = './' + sysPath.join('.', browser);
    return obj;
  } else {
    return {};
  }
};

const _getMainFile = filePath => {
  const root = getModuleRootPath(filePath);
  const json = packageJson(filePath);

  return _mainFile(root, json);
};

const _mainFile = (root, json) => {
  const depMain = json.main || 'index.js';
  const fileOrDir = sysPath.join(root, depMain);
  return helpers.isDir(fileOrDir).then(isDir => {
    if (isDir) {
      return sysPath.join(fileOrDir, 'index.js');
    } else {
      return fileOrDir.indexOf('.js') === -1 ? fileOrDir + '.js' : fileOrDir;
    }
  });
};

const globalBrowserMappings = filePath => {
  const brMap = browserMappings(filePath);

  return Object.keys(brMap).filter(not(helpers.isRelative)).reduce((newBrMap, key) => {
    const val = brMap[key];
    if (val) {
      newBrMap[key] = helpers.isRelative(val) ? generateModuleName(relativeToRoot(filePath, val)) : val;
    }
    return newBrMap;
  }, {});
};

const expandedFilePath = filePath => {
  const brMap = browserMappings(filePath);

  Object.keys(brMap).filter(helpers.isRelative).forEach(key => {
    const val = brMap[key];
    if (val && filePath === relativeToRoot(filePath, val)) {
      filePath = relativeToRoot(filePath, key);
    }
  });

  return filePath;
};

const isMain = filePath => getMainCached(filePath) === sysPath.resolve(expandedFilePath(filePath));

const getNewHeader = (moduleName, source, filePath, origPath) => {
  const brMap = globalBrowserMappings(filePath);

  const p = filePath.replace(getModuleRootPath(filePath), '').replace('.json', '').replace('.js', '').split(sysPath.sep).slice(0, -1);
  const p2 = [moduleName].concat(p).join('/');
  const itemPath = isMain(filePath) ? `, '${p2}/'` : '';

  const glob = shims.findGlobals(source);

  if (shims.shouldOverrideModuleName(moduleName)) moduleName = shims.overrideModuleName(moduleName);

  if (filePath.indexOf('.json') === -1) {
    const fbModuleName = generateFileBasedModuleName(filePath);
    const fbAlias = mediator.npm.noFileBased ? shims.shouldIncludeFileBasedAlias(moduleName) : isMain(filePath) ?
      aliasDef(fbModuleName, moduleName) + '\n' :
      '';
    const fbModuleNameUnexp = generateFileBasedModuleName(origPath);
    const fbAliasUnexp = mediator.npm.noFileBased ?
      '' :
      fbModuleNameUnexp !== fbModuleName ?
        aliasDef(fbModuleNameUnexp, moduleName) :
        '';

    const mappings = JSON.stringify(brMap);
    const name = getModuleFullRootName(filePath);
    const aliases = (fbAlias + fbAliasUnexp).trim();
    const aliasesFull = aliases ? '\n\n' + aliases : '';
    return (
`\nrequire.register("${moduleName}", function(exports, require, module) {
  require = __makeRelativeRequire(require, ${mappings}, "${name}"${itemPath});
  ${glob}(function() {
    ${source.trim()}
  })();
});${aliasesFull}`);
  } else {
    return definition(moduleName, source);
  }
};

const generateModule = (filePath, source) => {
  const expandedPath = expandedFilePath(filePath);
  const mn = generateModuleName(expandedPath);
  return getNewHeader(mn, source, expandedPath, filePath);
};

const slashes = string => string.replace(/\\/g, '/');

const generateModuleName = filePath => {
  const rp = getModuleRootPath(filePath);
  const mn = getModuleFullRootName(filePath) +
    (isMain(filePath) ? '' : filePath.replace(rp, '').replace('.json', '').replace('.js', ''));

  return slashes(mn);
};

const generateFileBasedModuleName = filePath => {
  return slashes(getModuleFullRootName(filePath) + filePath.replace(getModuleRootPath(filePath), '').replace('.json', '').replace('.js', ''));
};

const makeRequire = (
`\nvar __makeRelativeRequire = function(require, mappings, pref, fullPath) {
  if (fullPath) {
    var req = require;
    require = function(path) {
      return req(path.replace('./', fullPath));
    };
  }
  var none = {};
  var tryReq = function(name, pref) {
    var val;
    try {
      val = require(pref + '/node_modules/' + name);
      return val;
    } catch (e) {
      if (e.toString().indexOf('Cannot find module') === -1) {
        throw e;
      }

      if (pref.indexOf('node_modules') !== -1) {
        var s = pref.split('/');
        var i = s.lastIndexOf('node_modules');
        var newPref = s.slice(0, i).join('/');
        return tryReq(name, newPref);
      }
    }
    return none;
  };
  return function(name) {
    if (mappings[name] !== undefined) name = mappings[name];
    name = name.replace(/\.(json|js)$/, '');
    if (name[0] !== '.' && pref) {
      var val = tryReq(name, pref);
      if (val !== none) return val;
    }
    return require(name);
  }
};
`);

module.exports = {aliasDef, simpleShimDef, applyPackageOverrides, generateModule, generateModuleName, getModuleRootName, makeRequire, cacheMain, packageJson, resetCache};
<|endoftext|>


<commit_before>'use babel'

/* @flow */

import type { Linter$Message } from '../types'
import { getElement as getMessageElement } from './message'

export function getElement(messages: Set<Linter$Message>, includeProvider: boolean): HTMLElement {
  const bubble = document.createElement('div')
  bubble.id = 'linter-inline'
  for (const message of messages) {
    bubble.appendChild(getMessageElement(message, false, includeProvider))
    if (message.trace && message.trace.length) {
      for (const trace of message.trace) {
        bubble.appendChild(getMessageElement(trace, true, includeProvider))
      }
    }
  }
  return bubble
}
<commit_msg>:art: Improve rendering in bubble<commit_after>'use babel'

/* @flow */
/** @jsx React.h */

import React from 'preact'
import Message from './message'
import type { Linter$Message } from '../types'

export function getElement(messages: Set<Linter$Message>, includeProvider: boolean): HTMLElement {
  const bubble = document.createElement('div')
  const children = []
  bubble.id = 'linter-inline'
  for (const message of messages) {
    children.push(<Message message={message} includeLink="false" includeProvider={includeProvider} />)
    if (message.trace && message.trace.length) {
      for (const trace of message.trace) {
        children.push(<Message message={trace} includeLink="true" includeProvider={includeProvider} />)
      }
    }
  }
  React.render(<linter-messages>{ children }</linter-messages>, bubble)
  return bubble
}
<|endoftext|>