<commit_before>#!/usr/bin/env node

// Native
const http = require('http');
const path = require('path');
const fs = require('fs');
const {promisify} = require('util');
const {parse} = require('url');
const dns = require('dns');
const os = require('os');

// Packages
const Ajv = require('ajv');
const checkForUpdate = require('update-check');
const chalk = require('chalk');
const arg = require('arg');
const {write: copy} = require('clipboardy');
const handler = require('serve-handler');
const schema = require('@zeit/schemas/deployment/config-static');
const boxen = require('boxen');

// Utilities
const pkg = require('../package');

const readFile = promisify(fs.readFile);
const lookup = promisify(dns.lookup);

const warning = (message) => chalk`{yellow WARNING:} ${message}`;
const info = (message) => chalk`{magenta INFO:} ${message}`;
const error = (message) => chalk`{red ERROR:} ${message}`;

const updateCheck = async (isDebugging) => {
	let update = null;

	try {
		update = await checkForUpdate(pkg);
	} catch (err) {
		const suffix = isDebugging ? ':' : ' (use `--debug` to see full error)';
		console.error(warning(`Checking for updates failed${suffix}`));

		if (isDebugging) {
			console.error(err);
		}
	}

	if (!update) {
		return;
	}

	console.log(`${chalk.bgRed('UPDATE AVAILABLE')} The latest version of \`serve\` is ${update.latest}`);
};

const getHelp = () => chalk`
  {bold.cyan serve} - Static file serving and directory listing

  {bold USAGE}

      {bold $} {cyan serve} --help
      {bold $} {cyan serve} --version
      {bold $} {cyan serve} [-l {underline listen_uri} [-l ...]] [{underline directory}]

      By default, {cyan serve} will listen on {bold 0.0.0.0:5000} and serve the
      current working directory on that address.

      Specifying a single {bold --listen} argument will overwrite the default, not supplement it.

  {bold OPTIONS}

      --help                              Shows this help message

      -v, --version                       Displays the current version of serve

      -l, --listen {underline listen_uri}             Specify a URI endpoint on which to listen (see below) -
                                          more than one may be specified to listen in multiple places

      -d, --debug                         Show debugging information

      -s, --single                        Rewrite all not-found requests to \`index.html\`

      -c, --config                        Specify custom path to \`serve.json\`

      -n, --no-clipboard                  Do not copy the local address to the clipboard

  {bold ENDPOINTS}

      Listen endpoints (specified by the {bold --listen} or {bold -l} options above) instruct {cyan serve}
      to listen on one or more interfaces/ports, UNIX domain sockets, or Windows named pipes.

      For TCP ports on hostname "localhost":

          {bold $} {cyan serve} -l {underline 1234}

      For TCP (traditional host/port) endpoints:

          {bold $} {cyan serve} -l tcp://{underline hostname}:{underline 1234}

      For UNIX domain socket endpoints:

          {bold $} {cyan serve} -l unix:{underline /path/to/socket.sock}

      For Windows named pipe endpoints:

          {bold $} {cyan serve} -l pipe:\\\\.\\pipe\\{underline PipeName}
`;

const parseEndpoint = (str) => {
	if (!isNaN(str)) {
		return [str];
	}

	// We cannot use `new URL` here, otherwise it will not
	// parse the host properly and it would drop support for IPv6.
	const url = parse(str);

	switch (url.protocol) {
	case 'pipe:': {
		// some special handling
		const cutStr = str.replace(/^pipe:/, '');

		if (cutStr.slice(0, 4) !== '\\\\.\\') {
			throw new Error(`Invalid Windows named pipe endpoint: ${str}`);
		}

		return [cutStr];
	}
	case 'unix:':
		if (!url.pathname) {
			throw new Error(`Invalid UNIX domain socket endpoint: ${str}`);
		}

		return [url.pathname];
	case 'tcp:':
		url.port = url.port || '5000';
		return [parseInt(url.port, 10), url.hostname];
	default:
		throw new Error(`Unknown --listen endpoint scheme (protocol): ${url.protocol}`);
	}
};

const registerShutdown = (fn) => {
	let run = false;

	const wrapper = () => {
		if (!run) {
			run = true;
			fn();
		}
	};

	process.on('SIGINT', wrapper);
	process.on('SIGTERM', wrapper);
	process.on('exit', wrapper);
};

const startEndpoint = (endpoint, config, args) => {
	const server = http.createServer((request, response) => handler(request, response, config));
	const {isTTY} = process.stdout;
	const clipboard = args['--no-clipboard'] !== true;

	server.on('error', (err) => {
		console.error(error(`Failed to serve: ${err.stack}`));
		process.exit(1);
	});

	server.listen(...endpoint, async () => {
		const details = server.address();
		registerShutdown(() => server.close());

		let localAddress = null;
		let networkAddress = null;

		if (typeof details === 'string') {
			localAddress = details;
		} else if (typeof details === 'object' && details.port) {
			const address = details.address === '::' ? 'localhost' : details.address;

			localAddress = `http://${address}:${details.port}`;
			try {
				const {address: ip} = await lookup(os.hostname());
				networkAddress = `http://${ip}:${details.port}`;
			} catch (err) {
				console.error(error(`DNS lookup failed: ${err.message}`));
			}
		}

		if (isTTY && process.env.NODE_ENV !== 'production') {
			let message = chalk.green('Serving!');

			if (localAddress) {
				const prefix = networkAddress ? '- ' : '';
				const space = networkAddress ? '            ' : '  ';

				message += `\n\n${chalk.bold(`${prefix}Local:`)}${space}${localAddress}`;
			}

			if (networkAddress) {
				message += `\n${chalk.bold('- On Your Network:')}  ${networkAddress}`;
			}

			if (clipboard) {
				try {
					await copy(localAddress);
					message += `\n\n${chalk.grey('Copied local address to clipboard!')}`;
				} catch (err) {
					console.error(error(`Cannot copy to clipboard: ${err.message}`));
				}
			}

			console.log(boxen(message, {
				padding: 1,
				borderColor: 'green',
				margin: 1
			}));
		} else {
			const suffix = localAddress ? ` at ${localAddress}` : '';
			console.log(info(`Accepting connections${suffix}`));
		}
	});
};

const loadConfig = async (cwd, entry, args) => {
	const files = [
		'serve.json',
		'now.json',
		'package.json'
	];

	if (args['--config']) {
		files.unshift(args['--config']);
	}

	const config = {};

	for (const file of files) {
		const location = path.join(entry, file);
		let content = null;

		try {
			content = await readFile(location, 'utf8');
		} catch (err) {
			if (err.code === 'ENOENT') {
				continue;
			}

			console.error(error(`Not able to read ${location}: ${err.message}`));
			process.exit(1);
		}

		try {
			content = JSON.parse(content);
		} catch (err) {
			console.error(error(`Could not parse ${location} as JSON: ${err.message}`));
			process.exit(1);
		}

		if (typeof content !== 'object') {
			console.error(warning(`Didn't find a valid object in ${location}. Skipping...`));
			continue;
		}

		try {
			switch (file) {
			case 'now.json':
				content = content.static;
				break;
			case 'package.json':
				content = content.now.static;
				break;
			}
		} catch (err) {
			continue;
		}

		Object.assign(config, content);
		console.log(info(`Discovered configuration in \`${file}\``));

		break;
	}

	if (entry) {
		const {public} = config;
		config.public = path.relative(cwd, (public ? path.join(entry, public) : entry));
	}

	if (Object.keys(config).length !== 0) {
		const ajv = new Ajv();
		const validateSchema = ajv.compile(schema);

		if (!validateSchema(config)) {
			const defaultMessage = error('The configuration you provided is wrong:');
			const {message, params} = validateSchema.errors[0];

			console.error(`${defaultMessage}\n${message}\n${JSON.stringify(params)}`);
			process.exit(1);
		}
	}

	return config;
};

(async () => {
	let args = null;

	try {
		args = arg({
			'--help': Boolean,
			'--version': Boolean,
			'--listen': [parseEndpoint],
			'--single': Boolean,
			'--debug': Boolean,
			'--config': String,
			'--no-clipboard': Boolean,
			'-h': '--help',
			'-v': '--version',
			'-l': '--listen',
			'-s': '--single',
			'-d': '--debug',
			'-c': '--config',
			'-n': '--no-clipboard',
			// This is deprecated and only for backwards-compatibility.
			'-p': '--listen'
		});
	} catch (err) {
		console.error(error(err.message));
		process.exit(1);
	}

	await updateCheck(args['--debug']);

	if (args['--version']) {
		console.log(pkg.version);
		return;
	}

	if (args['--help']) {
		console.log(getHelp());
		return;
	}

	if (!args['--listen']) {
		// Default endpoint
		args['--listen'] = [[process.env.PORT || 5000]];
	}

	if (args._.length > 1) {
		console.error(error('Please provide one path argument at maximum'));
		process.exit(1);
	}

	const cwd = process.cwd();
	const entry = args._.length > 0 ? path.join(cwd, args._[0]) : cwd;

	const config = await loadConfig(cwd, entry, args);

	if (args['--single']) {
		const {rewrites} = config;
		const existingRewrites = Array.isArray(rewrites) ? rewrites : [];

		// As the first rewrite rule, make `--single` work
		config.rewrites = [{
			source: '**',
			destination: '/index.html'
		}, ...existingRewrites];
	}

	for (const endpoint of args['--listen']) {
		startEndpoint(endpoint, config, args);
	}

	registerShutdown(() => {
		console.log(`\n${info('Gracefully shutting down. Please wait...')}`);

		process.on('SIGINT', () => {
			console.log(`\n${warning('Force-closing all open sockets...')}`);
			process.exit(0);
		});
	});
})();
<commit_msg>Support for absolute paths (#445)<commit_after>#!/usr/bin/env node

// Native
const http = require('http');
const path = require('path');
const fs = require('fs');
const {promisify} = require('util');
const {parse} = require('url');
const dns = require('dns');
const os = require('os');

// Packages
const Ajv = require('ajv');
const checkForUpdate = require('update-check');
const chalk = require('chalk');
const arg = require('arg');
const {write: copy} = require('clipboardy');
const handler = require('serve-handler');
const schema = require('@zeit/schemas/deployment/config-static');
const boxen = require('boxen');

// Utilities
const pkg = require('../package');

const readFile = promisify(fs.readFile);
const lookup = promisify(dns.lookup);

const warning = (message) => chalk`{yellow WARNING:} ${message}`;
const info = (message) => chalk`{magenta INFO:} ${message}`;
const error = (message) => chalk`{red ERROR:} ${message}`;

const updateCheck = async (isDebugging) => {
	let update = null;

	try {
		update = await checkForUpdate(pkg);
	} catch (err) {
		const suffix = isDebugging ? ':' : ' (use `--debug` to see full error)';
		console.error(warning(`Checking for updates failed${suffix}`));

		if (isDebugging) {
			console.error(err);
		}
	}

	if (!update) {
		return;
	}

	console.log(`${chalk.bgRed('UPDATE AVAILABLE')} The latest version of \`serve\` is ${update.latest}`);
};

const getHelp = () => chalk`
  {bold.cyan serve} - Static file serving and directory listing

  {bold USAGE}

      {bold $} {cyan serve} --help
      {bold $} {cyan serve} --version
      {bold $} {cyan serve} [-l {underline listen_uri} [-l ...]] [{underline directory}]

      By default, {cyan serve} will listen on {bold 0.0.0.0:5000} and serve the
      current working directory on that address.

      Specifying a single {bold --listen} argument will overwrite the default, not supplement it.

  {bold OPTIONS}

      --help                              Shows this help message

      -v, --version                       Displays the current version of serve

      -l, --listen {underline listen_uri}             Specify a URI endpoint on which to listen (see below) -
                                          more than one may be specified to listen in multiple places

      -d, --debug                         Show debugging information

      -s, --single                        Rewrite all not-found requests to \`index.html\`

      -c, --config                        Specify custom path to \`serve.json\`

      -n, --no-clipboard                  Do not copy the local address to the clipboard

  {bold ENDPOINTS}

      Listen endpoints (specified by the {bold --listen} or {bold -l} options above) instruct {cyan serve}
      to listen on one or more interfaces/ports, UNIX domain sockets, or Windows named pipes.

      For TCP ports on hostname "localhost":

          {bold $} {cyan serve} -l {underline 1234}

      For TCP (traditional host/port) endpoints:

          {bold $} {cyan serve} -l tcp://{underline hostname}:{underline 1234}

      For UNIX domain socket endpoints:

          {bold $} {cyan serve} -l unix:{underline /path/to/socket.sock}

      For Windows named pipe endpoints:

          {bold $} {cyan serve} -l pipe:\\\\.\\pipe\\{underline PipeName}
`;

const parseEndpoint = (str) => {
	if (!isNaN(str)) {
		return [str];
	}

	// We cannot use `new URL` here, otherwise it will not
	// parse the host properly and it would drop support for IPv6.
	const url = parse(str);

	switch (url.protocol) {
	case 'pipe:': {
		// some special handling
		const cutStr = str.replace(/^pipe:/, '');

		if (cutStr.slice(0, 4) !== '\\\\.\\') {
			throw new Error(`Invalid Windows named pipe endpoint: ${str}`);
		}

		return [cutStr];
	}
	case 'unix:':
		if (!url.pathname) {
			throw new Error(`Invalid UNIX domain socket endpoint: ${str}`);
		}

		return [url.pathname];
	case 'tcp:':
		url.port = url.port || '5000';
		return [parseInt(url.port, 10), url.hostname];
	default:
		throw new Error(`Unknown --listen endpoint scheme (protocol): ${url.protocol}`);
	}
};

const registerShutdown = (fn) => {
	let run = false;

	const wrapper = () => {
		if (!run) {
			run = true;
			fn();
		}
	};

	process.on('SIGINT', wrapper);
	process.on('SIGTERM', wrapper);
	process.on('exit', wrapper);
};

const startEndpoint = (endpoint, config, args) => {
	const server = http.createServer((request, response) => handler(request, response, config));
	const {isTTY} = process.stdout;
	const clipboard = args['--no-clipboard'] !== true;

	server.on('error', (err) => {
		console.error(error(`Failed to serve: ${err.stack}`));
		process.exit(1);
	});

	server.listen(...endpoint, async () => {
		const details = server.address();
		registerShutdown(() => server.close());

		let localAddress = null;
		let networkAddress = null;

		if (typeof details === 'string') {
			localAddress = details;
		} else if (typeof details === 'object' && details.port) {
			const address = details.address === '::' ? 'localhost' : details.address;

			localAddress = `http://${address}:${details.port}`;
			try {
				const {address: ip} = await lookup(os.hostname());
				networkAddress = `http://${ip}:${details.port}`;
			} catch (err) {
				console.error(error(`DNS lookup failed: ${err.message}`));
			}
		}

		if (isTTY && process.env.NODE_ENV !== 'production') {
			let message = chalk.green('Serving!');

			if (localAddress) {
				const prefix = networkAddress ? '- ' : '';
				const space = networkAddress ? '            ' : '  ';

				message += `\n\n${chalk.bold(`${prefix}Local:`)}${space}${localAddress}`;
			}

			if (networkAddress) {
				message += `\n${chalk.bold('- On Your Network:')}  ${networkAddress}`;
			}

			if (clipboard) {
				try {
					await copy(localAddress);
					message += `\n\n${chalk.grey('Copied local address to clipboard!')}`;
				} catch (err) {
					console.error(error(`Cannot copy to clipboard: ${err.message}`));
				}
			}

			console.log(boxen(message, {
				padding: 1,
				borderColor: 'green',
				margin: 1
			}));
		} else {
			const suffix = localAddress ? ` at ${localAddress}` : '';
			console.log(info(`Accepting connections${suffix}`));
		}
	});
};

const loadConfig = async (cwd, entry, args) => {
	const files = [
		'serve.json',
		'now.json',
		'package.json'
	];

	if (args['--config']) {
		files.unshift(args['--config']);
	}

	const config = {};

	for (const file of files) {
		const location = path.join(entry, file);
		let content = null;

		try {
			content = await readFile(location, 'utf8');
		} catch (err) {
			if (err.code === 'ENOENT') {
				continue;
			}

			console.error(error(`Not able to read ${location}: ${err.message}`));
			process.exit(1);
		}

		try {
			content = JSON.parse(content);
		} catch (err) {
			console.error(error(`Could not parse ${location} as JSON: ${err.message}`));
			process.exit(1);
		}

		if (typeof content !== 'object') {
			console.error(warning(`Didn't find a valid object in ${location}. Skipping...`));
			continue;
		}

		try {
			switch (file) {
			case 'now.json':
				content = content.static;
				break;
			case 'package.json':
				content = content.now.static;
				break;
			}
		} catch (err) {
			continue;
		}

		Object.assign(config, content);
		console.log(info(`Discovered configuration in \`${file}\``));

		break;
	}

	if (entry) {
		const {public} = config;
		config.public = path.relative(cwd, (public ? path.join(entry, public) : entry));
	}

	if (Object.keys(config).length !== 0) {
		const ajv = new Ajv();
		const validateSchema = ajv.compile(schema);

		if (!validateSchema(config)) {
			const defaultMessage = error('The configuration you provided is wrong:');
			const {message, params} = validateSchema.errors[0];

			console.error(`${defaultMessage}\n${message}\n${JSON.stringify(params)}`);
			process.exit(1);
		}
	}

	return config;
};

(async () => {
	let args = null;

	try {
		args = arg({
			'--help': Boolean,
			'--version': Boolean,
			'--listen': [parseEndpoint],
			'--single': Boolean,
			'--debug': Boolean,
			'--config': String,
			'--no-clipboard': Boolean,
			'-h': '--help',
			'-v': '--version',
			'-l': '--listen',
			'-s': '--single',
			'-d': '--debug',
			'-c': '--config',
			'-n': '--no-clipboard',
			// This is deprecated and only for backwards-compatibility.
			'-p': '--listen'
		});
	} catch (err) {
		console.error(error(err.message));
		process.exit(1);
	}

	await updateCheck(args['--debug']);

	if (args['--version']) {
		console.log(pkg.version);
		return;
	}

	if (args['--help']) {
		console.log(getHelp());
		return;
	}

	if (!args['--listen']) {
		// Default endpoint
		args['--listen'] = [[process.env.PORT || 5000]];
	}

	if (args._.length > 1) {
		console.error(error('Please provide one path argument at maximum'));
		process.exit(1);
	}

	const cwd = process.cwd();
	const entry = args._.length > 0 ? path.resolve(args._[0]) : cwd;

	const config = await loadConfig(cwd, entry, args);

	if (args['--single']) {
		const {rewrites} = config;
		const existingRewrites = Array.isArray(rewrites) ? rewrites : [];

		// As the first rewrite rule, make `--single` work
		config.rewrites = [{
			source: '**',
			destination: '/index.html'
		}, ...existingRewrites];
	}

	for (const endpoint of args['--listen']) {
		startEndpoint(endpoint, config, args);
	}

	registerShutdown(() => {
		console.log(`\n${info('Gracefully shutting down. Please wait...')}`);

		process.on('SIGINT', () => {
			console.log(`\n${warning('Force-closing all open sockets...')}`);
			process.exit(0);
		});
	});
})();
<|endoftext|>


<commit_before>'use strict'

let _ = require('lodash')
let moment = require('moment')
let winston = require('winston')
let request = require('request')
let WebClient = require('@slack/client').WebClient
let config = require('../config')
let web = new WebClient(config.SLACK_API_TOKEN)

let isWeekend = () => {
  let weekDay = moment().day()
  return weekDay === 0 || weekDay === 6
}

let postMessageHandler = (err, info) => {
  if (err) return winston.error('SLACK: postMessage failed:', err)
  if (info && !info.ok) return winston.error('SLACK: invalid postMessage response:', info)
}

let onlySubscribed = (allowed) => (service) => allowed.indexOf(service.name) !== -1
let onlyUnsubscribed = (allowed) => (service) => allowed.indexOf(service.name) === -1
let getTitle = (service) => service.title

let prepareMessage = (json, subscribedServices) => {
  let allServices = config.SERVICES
  let availableServices = _.map(_.filter(allServices, onlyUnsubscribed(subscribedServices)), getTitle)
  let message = 'Other available restaurants: ' + availableServices.join(', ') + '\n\n'

  let jsonMenus = _.filter(json, onlySubscribed(subscribedServices))
  for (let i = 0; i < jsonMenus.length; i++) {
    message += `*${jsonMenus[i].title}*\n`

    if (!jsonMenus[i].menu[0]) {
      message += 'No menu was found today, check it out yourself and report bugs to @michal.brasna!\n'
    } else {
      for (let j = 0; j < jsonMenus[i].menu[0].items.length; j++) {
        let menuItem = jsonMenus[i].menu[0].items[j]
        message += `- ${menuItem.item} (${menuItem.amount}) _${menuItem.price}_\n`
      }
    }

    message += '\n'
  }

  return message
}

// don't bother on weekends
if (isWeekend()) process.exit(0)

request(config.URL + 'api/next', (err, response, body) => {
  if (err) return winston.error('SLACK: request failed', err)

  let json = null
  try {
    json = JSON.parse(body)
  } catch (e) {
    return winston.error('SLACK: invalid JSON', e, body, response.statusCode, response.statusMessage)
  }

  let options = {
    username: 'sbks-luncher'
  }

  for (let i = 0; i < config.NOTIFICATIONS.length; i++) {
    let message = prepareMessage(json, config.NOTIFICATIONS[i].services)
    web.chat.postMessage(config.NOTIFICATIONS[i].user, message, options, postMessageHandler)
  }
})
<commit_msg>Retry slack norifications on error<commit_after>'use strict'

let _ = require('lodash')
let async = require('async')
let moment = require('moment')
let winston = require('winston')
let request = require('request')
let WebClient = require('@slack/client').WebClient
let config = require('../config')
let web = new WebClient(config.SLACK_API_TOKEN)

let isWeekend = () => {
  let weekDay = moment().day()
  return weekDay === 0 || weekDay === 6
}

let postMessageHandler = (err, info) => {
  if (err) return winston.error('SLACK: postMessage failed:', err)
  if (info && !info.ok) return winston.error('SLACK: invalid postMessage response:', info)
}

let onlySubscribed = (allowed) => (service) => allowed.indexOf(service.name) !== -1
let getTitle = (service) => service.title

let prepareMessage = (json, subscribedServices) => {
  let allServices = config.SERVICES
  let availableServices = _.map(_.reject(allServices, onlySubscribed(subscribedServices)), getTitle)
  let message = 'Other available restaurants: ' + availableServices.join(', ') + '\n\n'

  let jsonMenus = _.filter(json, onlySubscribed(subscribedServices))
  for (let i = 0; i < jsonMenus.length; i++) {
    message += `*${jsonMenus[i].title}*\n`

    if (!jsonMenus[i].menu[0]) {
      message += 'No menu was found today, check it out yourself and report bugs to @michal.brasna!\n'
    } else {
      for (let j = 0; j < jsonMenus[i].menu[0].items.length; j++) {
        let menuItem = jsonMenus[i].menu[0].items[j]
        message += `- ${menuItem.item} (${menuItem.amount}) _${menuItem.price}_\n`
      }
    }

    message += '\n'
  }

  return message
}

let notifyClients = (next) => {
  request(config.URL + 'api/next', (err, response, body) => {
    if (err) {
      winston.error('SLACK: request failed', err)
      return next(err)
    }

    let json = null
    try {
      json = JSON.parse(body)
    } catch (e) {
      winston.error('SLACK: invalid JSON', e, body, response.statusCode, response.statusMessage)
      return next(e)
    }

    let options = {
      username: 'sbks-luncher'
    }

    try {
      for (let i = 0; i < config.NOTIFICATIONS.length; i++) {
        let message = prepareMessage(json, config.NOTIFICATIONS[i].services)
        web.chat.postMessage(config.NOTIFICATIONS[i].user, message, options, postMessageHandler)
      }
    } catch (e) {
      winston.error('SLACK: failed preparing message', e, json)
      return next(e)
    }

    next()
  })
}

// don't bother on weekends
if (isWeekend()) process.exit(0)

// try it few times as heroku can be slow in spawning instances
async.retry(
  {times: 3, interval: 5000},
  notifyClients,
  (err) => {
    if (err) return winston.error('SLACK: failed to send messages', err)
    return winston.info('SLACK: all messages were sent')
  }
)
<|endoftext|>


<commit_before>module.exports = {
  components: {
    'overview-nav': require('./nav'),
    'overview-footer': require('./footer'),
    'error': require('../notification')
  },

  data: function() {
    return {
      store: window.Store.shareList,
      uiState: {
        selected: []
      }
    }
  },

  computed: {
    listHasItems: function() {
      return this.uiState.selected.length > 0;
    }
  },

  created: function() {
    this.store.actions.status(() => {
      this.store.actions.poll().start();
    });

  },

  destroyed: function() {
    this.store.actions.poll().stop();
  },

  methods: {
    toggleAll: function() {
      if(this.listHasItems) {
        this.uiState.selected = [];
      } else {
        this.store.shares.forEach((share) => {
          this.uiState.selected.push(share.id);
        });
      }
    },
    toggleItem: function(id) {
      let index = this.uiState.selected.indexOf(id);
      if(index >= 0) {
        this.uiState.selected.splice(index, 1);
      } else {
        this.uiState.selected.push(id);
      }
    }
  },

  template: `
<transition name="fade">

    <error class="error-stream alert alert-danger alert-dismissible" v-bind:notes="store.errors" v-bind:dismiss-action="store.actions.clearErrors"></error>

    <overview-nav></overview-nav>

Overview

                <input type="checkbox" v-model="listHasItems" v-on:change="toggleAll()" class="checkbox" id="selectAll">
                #
                Status

                Location
                Uptime
                Restarts
                Peers
                Shared

                    Start
                    Stop
                    Restart

                    Delete

                  <input v-model="uiState.selected"
                    v-on:change="toggleItem(share.id)"
                    v-bind:value="share.id"
                    type="checkbox"
                    class="checkbox">

                #{{index}}
                ON
                OFF

                {{share.config.storagePath}}
                {{share.meta.uptimeReadable}}
                {{share.meta.numRestarts}}
                {{share.meta.farmerState.totalPeers}}
                {{share.meta.farmerState.spaceUsed}} ({{share.meta.farmerState.percentUsed}}%)

                    Start
                    Stop
                    Restart
                    Logs
                    Edit

                    Delete

                <router-link :to="{path: '/share-wizard/wizard1?edit=true'}">Add Drive to Get Started</router-link>

    <overview-footer></overview-footer>

</transition>
  `
};
<commit_msg>fixing checkbox bug<commit_after>module.exports = {
  components: {
    'overview-nav': require('./nav'),
    'overview-footer': require('./footer'),
    'error': require('../notification')
  },

  data: function() {
    return {
      store: window.Store.shareList,
      uiState: {
        selected: []
      }
    }
  },

  computed: {
    listHasItems: function() {
      return this.uiState.selected.length > 0;
    }
  },

  created: function() {
    this.store.actions.status(() => {
      this.store.actions.poll().start();
    });

  },

  destroyed: function() {
    this.store.actions.poll().stop();
  },

  methods: {
    toggleAll: function() {
      if(this.listHasItems) {
        this.uiState.selected = [];
      } else {
        this.store.shares.forEach((share) => {
          this.uiState.selected.push(share.id);
        });
      }
    }
  },

  template: `
<transition name="fade">

    <error class="error-stream alert alert-danger alert-dismissible" v-bind:notes="store.errors" v-bind:dismiss-action="store.actions.clearErrors"></error>

    <overview-nav></overview-nav>

Overview

                <input type="checkbox" v-model="listHasItems" v-on:change="toggleAll()" class="checkbox" id="selectAll">
                #
                Status

                Location
                Uptime
                Restarts
                Peers
                Shared

                    Start
                    Stop
                    Restart

                    Delete

                  <input v-model="uiState.selected"
                    v-bind:value="share.id"
                    type="checkbox"
                    class="checkbox">

                #{{index}}
                ON
                OFF

                {{share.config.storagePath}}
                {{share.meta.uptimeReadable}}
                {{share.meta.numRestarts}}
                {{share.meta.farmerState.totalPeers}}
                {{share.meta.farmerState.spaceUsed}} ({{share.meta.farmerState.percentUsed}}%)

                    Start
                    Stop
                    Restart
                    Logs
                    Edit

                    Delete

                <router-link :to="{path: '/share-wizard/wizard1?edit=true'}">Add Drive to Get Started</router-link>

    <overview-footer></overview-footer>

</transition>
  `
};
<|endoftext|>


<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ajax.org Code Editor (ACE).
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {

var oop = require("pilot/oop");
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;

var DocCommentHighlightRules = function() {

    this.$rules = {
        "start" : [ {
            token : "comment.doc.tag",
            regex : "@[\\w\\d_]+" // TODO: fix email addresses
        }, {
            token : "comment.doc",
            regex : "\s+"
        }, {
            token : "comment.doc",
            regex : "TODO"
        }, {
            token : "comment.doc",
            regex : "[^@\\*]+"
        }, {
            token : "comment.doc",
            regex : "."
        }]
    };
};

oop.inherits(DocCommentHighlightRules, TextHighlightRules);

(function() {

    this.getStartRule = function(start) {
        return {
            token : "comment.doc", // doc comment
            regex : "\\/\\*(?=\\*)",
            next: start
        };
    };

    this.getEndRule = function (start) {
        return {
            token : "comment.doc", // closing comment
            regex : "\\*\\/",
            next  : start
        };
    }

}).call(DocCommentHighlightRules.prototype);

exports.DocCommentHighlightRules = DocCommentHighlightRules;

});
<commit_msg>Fix'd warnings.<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ajax.org Code Editor (ACE).
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {

var oop = require("pilot/oop");
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;

var DocCommentHighlightRules = function() {

    this.$rules = {
        "start" : [ {
            token : "comment.doc.tag",
            regex : "@[\\w\\d_]+" // TODO: fix email addresses
        }, {
            token : "comment.doc",
            regex : "\\s+"
        }, {
            token : "comment.doc",
            regex : "TODO"
        }, {
            token : "comment.doc",
            regex : "[^@\\*]+"
        }, {
            token : "comment.doc",
            regex : "."
        }]
    };
};

oop.inherits(DocCommentHighlightRules, TextHighlightRules);

(function() {

    this.getStartRule = function(start) {
        return {
            token : "comment.doc", // doc comment
            regex : "\\/\\*(?=\\*)",
            next: start
        };
    };

    this.getEndRule = function (start) {
        return {
            token : "comment.doc", // closing comment
            regex : "\\*\\/",
            next  : start
        };
    };

}).call(DocCommentHighlightRules.prototype);

exports.DocCommentHighlightRules = DocCommentHighlightRules;

});
<|endoftext|>


<commit_before>var options = {
    hosts: {
        call_control: "callcontrol.chaos.hipchat.me",
        focus: "focus.chaos.hipchat.me",
        domain: 'chaos.hipchat.me',
        muc: 'conference.chaos.hipchat.me', // FIXME: use XEP-0030
        bridge: 'jitsi-videobridge.chaos.hipchat.me', // FIXME: use XEP-0030
    },
    bosh: '//chaos.hipchat.me/http-bind', // FIXME: use xep-0156 for that
    clientNode: 'http://jitsi.org/jitsimeet', // The name of client node advertised in XEP-0115 'c' stanza
}

var confOptions = {
    openSctp: true
}

var isJoined = false;

/**
 * Handles local tracks.
 * @param tracks Array with JitsiTrack objects
 */
function onLocalTracks(tracks)
{
    localTracks = tracks;
    for(var i = 0; i < localTracks.length; i++)
    {
        localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
            function (audioLevel) {
                console.log("Audio Level local: " + audioLevel);
            });
        localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED,
            function () {
                console.log("local track muted");
            });
        localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_STOPPED,
            function () {
                console.log("local track stoped");
            });
        if(localTracks[i].getType() == "video") {
            $("body").append("<video autoplay='1' id='localVideo" + i + "' />");
            localTracks[i].attach($("#localVideo" + i ));
        } else {
            $("body").append("<audio autoplay='1' muted='true' id='localAudio" + i + "' />");
            localTracks[i].attach($("#localAudio" + i ));
        }
        if(isJoined)
            room.addTrack(localTracks[i]);
    }
}

/**
 * Handles remote tracks
 * @param track JitsiTrack object
 */
function onRemoteTrack(track) {
    if(track.isLocal())
        return;
    var participant = track.getParticipantId();
    if(!remoteTracks[participant])
        remoteTracks[participant] = [];
    var idx = remoteTracks[participant].push(track);
    track.addEventListener(JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
        function (audioLevel) {
            console.log("Audio Level remote: " + audioLevel);
        });
    track.addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED,
        function () {
            console.log("remote track muted");
        });
    track.addEventListener(JitsiMeetJS.events.track.TRACK_STOPPED,
        function () {
            console.log("remote track stoped");
        });
    var id = participant + track.getType() + idx;
    if(track.getType() == "video") {
        $("body").append("<video autoplay='1' id='" + participant + "video" + idx + "' />");
    } else {
        $("body").append("<audio autoplay='1' id='" + participant + "audio" + idx + "' />");
    }
    track.attach($("#" + id));
}

/**
 * That function is executed when the conference is joined
 */
function onConferenceJoined () {
    console.log("conference joined!");
    isJoined = true;
    for(var i = 0; i < localTracks.length; i++)
        room.addTrack(localTracks[i]);
}

function onUserLeft(id) {
    console.log("user left");
    if(!remoteTracks[id])
        return;
    var tracks = remoteTracks[id];
    for(var i = 0; i< tracks.length; i++)
        tracks[i].detach($("#" + id + tracks[i].getType()))
}

/**
 * That function is called when connection is established successfully
 */
function onConnectionSuccess(){
    room = connection.initJitsiConference("conference11", confOptions);
    room.on(JitsiMeetJS.events.conference.TRACK_ADDED, onRemoteTrack);
    room.on(JitsiMeetJS.events.conference.TRACK_REMOVED, function (track) {
        console.log("track removed!!!" + track);
    });
    room.on(JitsiMeetJS.events.conference.CONFERENCE_JOINED, onConferenceJoined);
    room.on(JitsiMeetJS.events.conference.USER_JOINED, function(id){ console.log("user join");remoteTracks[id] = [];});
    room.on(JitsiMeetJS.events.conference.USER_LEFT, onUserLeft);
    room.on(JitsiMeetJS.events.conference.TRACK_MUTE_CHANGED, function (track) {
        console.log(track.getType() + " - " + track.isMuted());
    });
    room.on(JitsiMeetJS.events.conference.DISPLAY_NAME_CHANGED, function (userID, displayName) {
        console.log(userID + " - " + displayName);
    });
    room.on(JitsiMeetJS.events.conference.TRACK_AUDIO_LEVEL_CHANGED,
      function(userID, audioLevel){
          console.log(userID + " - " + audioLevel);
      });
    room.on(JitsiMeetJS.events.conference.RECORDING_STATE_CHANGED, function () {
        console.log(room.isRecordingSupported() + " - " +
            room.getRecordingState() + " - " +
            room.getRecordingURL());
    });
    room.on(JitsiMeetJS.events.conference.PHONE_NUMBER_CHANGED, function () {
        console.log(
            room.getPhoneNumber() + " - " +
            room.getPhonePin());
    });
    room.join();
};

/**
 * This function is called when the connection fail.
 */
function onConnectionFailed(){console.error("Connection Failed!")};

/**
 * This function is called when we disconnect.
 */
function disconnect(){
    console.log("disconnect!");
    connection.removeEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, onConnectionSuccess);
    connection.removeEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, onConnectionFailed);
    connection.removeEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, disconnect);
}

function unload() {
    for(var i = 0; i < localTracks.length; i++)
        localTracks[i].stop();
    room.leave();
    connection.disconnect();
}
var isVideo = true;
function switchVideo() {
    isVideo = !isVideo;
    if(localTracks[1]) {
        localTracks[1].stop();
        localTracks.pop();
    }
    JitsiMeetJS.createLocalTracks({devices: isVideo? ["video"] : ["desktop"]}).
        then(function (tracks) {
            localTracks.push(tracks[0]);
            localTracks[1].addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED,
                function () {
                    console.log("local track muted");
                });
            localTracks[1].addEventListener(JitsiMeetJS.events.track.TRACK_STOPPED,
                function () {
                    console.log("local track stoped");
                });
            localTracks[1].attach($("#localVideo1"));
            room.addTrack(localTracks[1]);
        }).catch(function (error) {
            console.log(error);
        });
}

$(window).bind('beforeunload', unload);
$(window).bind('unload', unload);

// JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
var initOptions = {
    disableAudioLevels: true,
    // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
    desktopSharingChromeMethod: 'ext',
    // The ID of the jidesha extension for Chrome.
    desktopSharingChromeExtId: 'mbocklcggfhnbahlnepmldehdhpjfcjp',
    // The media sources to use when using screen sharing with the Chrome
    // extension.
    desktopSharingChromeSources: ['screen', 'window'],
    // Required version of Chrome extension
    desktopSharingChromeMinExtVersion: '0.1',

    // The ID of the jidesha extension for Firefox. If null, we assume that no
    // extension is required.
    desktopSharingFirefoxExtId: null,
    // Whether desktop sharing should be disabled on Firefox.
    desktopSharingFirefoxDisabled: true,
    // The maximum version of Firefox which requires a jidesha extension.
    // Example: if set to 41, we will require the extension for Firefox versions
    // up to and including 41. On Firefox 42 and higher, we will run without the
    // extension.
    // If set to -1, an extension will be required for all versions of Firefox.
    desktopSharingFirefoxMaxVersionExtRequired: -1,
    // The URL to the Firefox extension for desktop sharing.
    desktopSharingFirefoxExtensionURL: null
}
JitsiMeetJS.init(initOptions).then(function(){
    connection = new JitsiMeetJS.JitsiConnection(null, null, options);

    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, onConnectionSuccess);
    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, onConnectionFailed);
    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, disconnect);

    connection.connect();
    JitsiMeetJS.createLocalTracks({devices: ["audio", "video"]}).
        then(onLocalTracks).catch(function (error) {
            console.log(error);
        });
}).catch(function (error) {
    console.log(error);
});

var connection = null;
var room = null;
var localTracks = [];
var remoteTracks = {};
<commit_msg>Fixes issues in example.js<commit_after>var options = {
    hosts: {
        domain: 'jitsi-meet.example.com',
        muc: 'conference.jitsi-meet.example.com', // FIXME: use XEP-0030
        bridge: 'jitsi-videobridge.jitsi-meet.example.com', // FIXME: use XEP-0030
    },
    bosh: '//jitsi-meet.example.com/http-bind', // FIXME: use xep-0156 for that
    clientNode: 'http://jitsi.org/jitsimeet', // The name of client node advertised in XEP-0115 'c' stanza
}

var confOptions = {
    openSctp: true
}

var isJoined = false;

/**
 * Handles local tracks.
 * @param tracks Array with JitsiTrack objects
 */
function onLocalTracks(tracks)
{
    localTracks = tracks;
    for(var i = 0; i < localTracks.length; i++)
    {
        localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
            function (audioLevel) {
                console.log("Audio Level local: " + audioLevel);
            });
        localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED,
            function () {
                console.log("local track muted");
            });
        localTracks[i].addEventListener(JitsiMeetJS.events.track.TRACK_STOPPED,
            function () {
                console.log("local track stoped");
            });
        if(localTracks[i].getType() == "video") {
            $("body").append("<video autoplay='1' id='localVideo" + i + "' />");
            localTracks[i].attach($("#localVideo" + i)[0]);
        } else {
            $("body").append("<audio autoplay='1' muted='true' id='localAudio" + i + "' />");
            localTracks[i].attach($("#localAudio" + i)[0]);
        }
        if(isJoined)
            room.addTrack(localTracks[i]);
    }
}

/**
 * Handles remote tracks
 * @param track JitsiTrack object
 */
function onRemoteTrack(track) {
    if(track.isLocal())
        return;
    var participant = track.getParticipantId();
    if(!remoteTracks[participant])
        remoteTracks[participant] = [];
    var idx = remoteTracks[participant].push(track);
    track.addEventListener(JitsiMeetJS.events.track.TRACK_AUDIO_LEVEL_CHANGED,
        function (audioLevel) {
            console.log("Audio Level remote: " + audioLevel);
        });
    track.addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED,
        function () {
            console.log("remote track muted");
        });
    track.addEventListener(JitsiMeetJS.events.track.TRACK_STOPPED,
        function () {
            console.log("remote track stoped");
        });
    var id = participant + track.getType() + idx;
    if(track.getType() == "video") {
        $("body").append("<video autoplay='1' id='" + participant + "video" + idx + "' />");
    } else {
        $("body").append("<audio autoplay='1' id='" + participant + "audio" + idx + "' />");
    }
    track.attach($("#" + id)[0]);
}

/**
 * That function is executed when the conference is joined
 */
function onConferenceJoined () {
    console.log("conference joined!");
    isJoined = true;
    for(var i = 0; i < localTracks.length; i++)
        room.addTrack(localTracks[i]);
}

function onUserLeft(id) {
    console.log("user left");
    if(!remoteTracks[id])
        return;
    var tracks = remoteTracks[id];
    for(var i = 0; i< tracks.length; i++)
        tracks[i].detach($("#" + id + tracks[i].getType()))
}

/**
 * That function is called when connection is established successfully
 */
function onConnectionSuccess(){
    room = connection.initJitsiConference("conference", confOptions);
    room.on(JitsiMeetJS.events.conference.TRACK_ADDED, onRemoteTrack);
    room.on(JitsiMeetJS.events.conference.TRACK_REMOVED, function (track) {
        console.log("track removed!!!" + track);
    });
    room.on(JitsiMeetJS.events.conference.CONFERENCE_JOINED, onConferenceJoined);
    room.on(JitsiMeetJS.events.conference.USER_JOINED, function(id){ console.log("user join");remoteTracks[id] = [];});
    room.on(JitsiMeetJS.events.conference.USER_LEFT, onUserLeft);
    room.on(JitsiMeetJS.events.conference.TRACK_MUTE_CHANGED, function (track) {
        console.log(track.getType() + " - " + track.isMuted());
    });
    room.on(JitsiMeetJS.events.conference.DISPLAY_NAME_CHANGED, function (userID, displayName) {
        console.log(userID + " - " + displayName);
    });
    room.on(JitsiMeetJS.events.conference.TRACK_AUDIO_LEVEL_CHANGED,
      function(userID, audioLevel){
          console.log(userID + " - " + audioLevel);
      });
    room.on(JitsiMeetJS.events.conference.RECORDING_STATE_CHANGED, function () {
        console.log(room.isRecordingSupported() + " - " +
            room.getRecordingState() + " - " +
            room.getRecordingURL());
    });
    room.on(JitsiMeetJS.events.conference.PHONE_NUMBER_CHANGED, function () {
        console.log(
            room.getPhoneNumber() + " - " +
            room.getPhonePin());
    });
    room.join();
};

/**
 * This function is called when the connection fail.
 */
function onConnectionFailed(){console.error("Connection Failed!")};

/**
 * This function is called when we disconnect.
 */
function disconnect(){
    console.log("disconnect!");
    connection.removeEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, onConnectionSuccess);
    connection.removeEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, onConnectionFailed);
    connection.removeEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, disconnect);
}

function unload() {
    for(var i = 0; i < localTracks.length; i++)
        localTracks[i].stop();
    room.leave();
    connection.disconnect();
}
var isVideo = true;
function switchVideo() {
    isVideo = !isVideo;
    if(localTracks[1]) {
        localTracks[1].stop();
        localTracks.pop();
    }
    JitsiMeetJS.createLocalTracks({devices: isVideo? ["video"] : ["desktop"]}).
        then(function (tracks) {
            localTracks.push(tracks[0]);
            localTracks[1].addEventListener(JitsiMeetJS.events.track.TRACK_MUTE_CHANGED,
                function () {
                    console.log("local track muted");
                });
            localTracks[1].addEventListener(JitsiMeetJS.events.track.TRACK_STOPPED,
                function () {
                    console.log("local track stoped");
                });
            localTracks[1].attach($("#localVideo1")[0]);
            room.addTrack(localTracks[1]);
        }).catch(function (error) {
            console.log(error);
        });
}

$(window).bind('beforeunload', unload);
$(window).bind('unload', unload);

// JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
var initOptions = {
    disableAudioLevels: true,
    // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
    desktopSharingChromeMethod: 'ext',
    // The ID of the jidesha extension for Chrome.
    desktopSharingChromeExtId: 'mbocklcggfhnbahlnepmldehdhpjfcjp',
    // The media sources to use when using screen sharing with the Chrome
    // extension.
    desktopSharingChromeSources: ['screen', 'window'],
    // Required version of Chrome extension
    desktopSharingChromeMinExtVersion: '0.1',

    // The ID of the jidesha extension for Firefox. If null, we assume that no
    // extension is required.
    desktopSharingFirefoxExtId: null,
    // Whether desktop sharing should be disabled on Firefox.
    desktopSharingFirefoxDisabled: true,
    // The maximum version of Firefox which requires a jidesha extension.
    // Example: if set to 41, we will require the extension for Firefox versions
    // up to and including 41. On Firefox 42 and higher, we will run without the
    // extension.
    // If set to -1, an extension will be required for all versions of Firefox.
    desktopSharingFirefoxMaxVersionExtRequired: -1,
    // The URL to the Firefox extension for desktop sharing.
    desktopSharingFirefoxExtensionURL: null
}
JitsiMeetJS.init(initOptions).then(function(){
    connection = new JitsiMeetJS.JitsiConnection(null, null, options);

    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, onConnectionSuccess);
    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_FAILED, onConnectionFailed);
    connection.addEventListener(JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, disconnect);

    connection.connect();
    JitsiMeetJS.createLocalTracks({devices: ["audio", "video"]}).
        then(onLocalTracks).catch(function (error) {
            throw error;
        });
}).catch(function (error) {
    console.log(error);
});

var connection = null;
var room = null;
var localTracks = [];
var remoteTracks = {};
<|endoftext|>


<commit_before>/*
 * 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var async = require('async');
var Buffer = require('buffer').Buffer;
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');
var util = require('util');
var settings = require('./Settings');
var semver = require('semver');

var existsSync = (semver.satisfies(process.version, '>=0.8.0')) ? fs.existsSync : path.existsSync;

console.warn("foo", settings.cacheDirPath);
var CACHE_DIR = settings.cacheDirPath
CACHE_DIR = existsSync(CACHE_DIR) ? CACHE_DIR : undefined;

var responseCache = {};

/*
  This caches and compresses 200 and 404 responses to GET and HEAD requests.
  TODO: Caching and compressing are solved problems, a middleware configuration
  should replace this.
*/

function CachingMiddleware() {
}
CachingMiddleware.prototype = new function () {
  function handle(req, res, next) {
    if (!(req.method == "GET" || req.method == "HEAD") || !CACHE_DIR) {
      return next(undefined, req, res);
    }

    var old_req = {};
    var old_res = {};

    var supportsGzip =
        (req.get('Accept-Encoding') || '').indexOf('gzip') != -1;

    var path = require('url').parse(req.url).path;
    var cacheKey = (new Buffer(path)).toString('base64').replace(/[\/\+=]/g, '');

    fs.stat(CACHE_DIR + 'minified_' + cacheKey, function (error, stats) {
      var modifiedSince = (req.headers['if-modified-since']
          && new Date(req.headers['if-modified-since']));
      var lastModifiedCache = !error && stats.mtime;
      if (lastModifiedCache && responseCache[cacheKey]) {
        req.headers['if-modified-since'] = lastModifiedCache.toUTCString();
      } else {
        delete req.headers['if-modified-since'];
      }

      // Always issue get to downstream.
      old_req.method = req.method;
      req.method = 'GET';

      var expirationDate = new Date(((responseCache[cacheKey] || {}).headers || {})['expires']);
      if (expirationDate > new Date()) {
        // Our cached version is still valid.
        return respond();
      }

      var _headers = {};
      old_res.setHeader = res.setHeader;
      res.setHeader = function (key, value) {
        // Don't set cookies, see issue #707
        if (key.toLowerCase() === 'set-cookie') return;

        _headers[key.toLowerCase()] = value;
        old_res.setHeader.call(res, key, value);
      };

      old_res.writeHead = res.writeHead;
      res.writeHead = function (status, headers) {
        var lastModified = (res.getHeader('last-modified')
            && new Date(res.getHeader('last-modified')));

        res.writeHead = old_res.writeHead;
        if (status == 200) {
          // Update cache
          var buffer = '';

          Object.keys(headers || {}).forEach(function (key) {
            res.setHeader(key, headers[key]);
          });
          headers = _headers;

          old_res.write = res.write;
          old_res.end = res.end;
          res.write = function(data, encoding) {
            buffer += data.toString(encoding);
          };
          res.end = function(data, encoding) {
            async.parallel([
              function (callback) {
                var path = CACHE_DIR + 'minified_' + cacheKey;
                fs.writeFile(path, buffer, function (error, stats) {
                  callback();
                });
              }
            , function (callback) {
                var path = CACHE_DIR + 'minified_' + cacheKey + '.gz';
                zlib.gzip(buffer, function(error, content) {
                  if (error) {
                    callback();
                  } else {
                    fs.writeFile(path, content, function (error, stats) {
                      callback();
                    });
                  }
                });
              }
            ], function () {
              responseCache[cacheKey] = {statusCode: status, headers: headers};
              respond();
            });
          };
        } else if (status == 304) {
          // Nothing new changed from the cached version.
          old_res.write = res.write;
          old_res.end = res.end;
          res.write = function(data, encoding) {};
          res.end = function(data, encoding) { respond(); };
        } else {
          res.writeHead(status, headers);
        }
      };

      next(undefined, req, res);

      // This handles read/write synchronization as well as its predecessor,
      // which is to say, not at all.
      // TODO: Implement locking on write or ditch caching of gzip and use
      // existing middlewares.
      function respond() {
        req.method = old_req.method || req.method;
        res.write = old_res.write || res.write;
        res.end = old_res.end || res.end;

        var headers = responseCache[cacheKey].headers;
        var statusCode = responseCache[cacheKey].statusCode;

        var pathStr = CACHE_DIR + 'minified_' + cacheKey;
        if (supportsGzip && (headers['content-type'] || '').match(/^text\//)) {
          pathStr = pathStr + '.gz';
          headers['content-encoding'] = 'gzip';
        }

        var lastModified = (headers['last-modified']
            && new Date(headers['last-modified']));

        if (statusCode == 200 && lastModified <= modifiedSince) {
          res.writeHead(304, headers);
          res.end();
        } else if (req.method == 'GET') {
          var readStream = fs.createReadStream(pathStr);
          res.writeHead(statusCode, headers);
          readStream.pipe(res);
        } else {
          res.writeHead(statusCode, headers);
          res.end();
        }
      }
    });
  }

  this.handle = handle;
}();

module.exports = CachingMiddleware;
<commit_msg>Remove debug trace<commit_after>/*
 * 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var async = require('async');
var Buffer = require('buffer').Buffer;
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');
var util = require('util');
var settings = require('./Settings');
var semver = require('semver');

var existsSync = (semver.satisfies(process.version, '>=0.8.0')) ? fs.existsSync : path.existsSync;

var CACHE_DIR = settings.cacheDirPath
CACHE_DIR = existsSync(CACHE_DIR) ? CACHE_DIR : undefined;

var responseCache = {};

/*
  This caches and compresses 200 and 404 responses to GET and HEAD requests.
  TODO: Caching and compressing are solved problems, a middleware configuration
  should replace this.
*/

function CachingMiddleware() {
}
CachingMiddleware.prototype = new function () {
  function handle(req, res, next) {
    if (!(req.method == "GET" || req.method == "HEAD") || !CACHE_DIR) {
      return next(undefined, req, res);
    }

    var old_req = {};
    var old_res = {};

    var supportsGzip =
        (req.get('Accept-Encoding') || '').indexOf('gzip') != -1;

    var path = require('url').parse(req.url).path;
    var cacheKey = (new Buffer(path)).toString('base64').replace(/[\/\+=]/g, '');

    fs.stat(CACHE_DIR + 'minified_' + cacheKey, function (error, stats) {
      var modifiedSince = (req.headers['if-modified-since']
          && new Date(req.headers['if-modified-since']));
      var lastModifiedCache = !error && stats.mtime;
      if (lastModifiedCache && responseCache[cacheKey]) {
        req.headers['if-modified-since'] = lastModifiedCache.toUTCString();
      } else {
        delete req.headers['if-modified-since'];
      }

      // Always issue get to downstream.
      old_req.method = req.method;
      req.method = 'GET';

      var expirationDate = new Date(((responseCache[cacheKey] || {}).headers || {})['expires']);
      if (expirationDate > new Date()) {
        // Our cached version is still valid.
        return respond();
      }

      var _headers = {};
      old_res.setHeader = res.setHeader;
      res.setHeader = function (key, value) {
        // Don't set cookies, see issue #707
        if (key.toLowerCase() === 'set-cookie') return;

        _headers[key.toLowerCase()] = value;
        old_res.setHeader.call(res, key, value);
      };

      old_res.writeHead = res.writeHead;
      res.writeHead = function (status, headers) {
        var lastModified = (res.getHeader('last-modified')
            && new Date(res.getHeader('last-modified')));

        res.writeHead = old_res.writeHead;
        if (status == 200) {
          // Update cache
          var buffer = '';

          Object.keys(headers || {}).forEach(function (key) {
            res.setHeader(key, headers[key]);
          });
          headers = _headers;

          old_res.write = res.write;
          old_res.end = res.end;
          res.write = function(data, encoding) {
            buffer += data.toString(encoding);
          };
          res.end = function(data, encoding) {
            async.parallel([
              function (callback) {
                var path = CACHE_DIR + 'minified_' + cacheKey;
                fs.writeFile(path, buffer, function (error, stats) {
                  callback();
                });
              }
            , function (callback) {
                var path = CACHE_DIR + 'minified_' + cacheKey + '.gz';
                zlib.gzip(buffer, function(error, content) {
                  if (error) {
                    callback();
                  } else {
                    fs.writeFile(path, content, function (error, stats) {
                      callback();
                    });
                  }
                });
              }
            ], function () {
              responseCache[cacheKey] = {statusCode: status, headers: headers};
              respond();
            });
          };
        } else if (status == 304) {
          // Nothing new changed from the cached version.
          old_res.write = res.write;
          old_res.end = res.end;
          res.write = function(data, encoding) {};
          res.end = function(data, encoding) { respond(); };
        } else {
          res.writeHead(status, headers);
        }
      };

      next(undefined, req, res);

      // This handles read/write synchronization as well as its predecessor,
      // which is to say, not at all.
      // TODO: Implement locking on write or ditch caching of gzip and use
      // existing middlewares.
      function respond() {
        req.method = old_req.method || req.method;
        res.write = old_res.write || res.write;
        res.end = old_res.end || res.end;

        var headers = responseCache[cacheKey].headers;
        var statusCode = responseCache[cacheKey].statusCode;

        var pathStr = CACHE_DIR + 'minified_' + cacheKey;
        if (supportsGzip && (headers['content-type'] || '').match(/^text\//)) {
          pathStr = pathStr + '.gz';
          headers['content-encoding'] = 'gzip';
        }

        var lastModified = (headers['last-modified']
            && new Date(headers['last-modified']));

        if (statusCode == 200 && lastModified <= modifiedSince) {
          res.writeHead(304, headers);
          res.end();
        } else if (req.method == 'GET') {
          var readStream = fs.createReadStream(pathStr);
          res.writeHead(statusCode, headers);
          readStream.pipe(res);
        } else {
          res.writeHead(statusCode, headers);
          res.end();
        }
      }
    });
  }

  this.handle = handle;
}();

module.exports = CachingMiddleware;
<|endoftext|>


<commit_before>import { shell } from 'electron';

import {
  PUBLISH_USER_GIST, PUBLISH_ANONYMOUS_GIST
} from './../constants';

import {
  overwriteMetadata,
} from '../actions';

const commutable = require('commutable');
const path = require('path');

const Rx = require('rxjs/Rx');

const Observable = Rx.Observable;

const Github = require('github');

/**
 * Notify the notebook user that it has been published as a gist.
 * @param {string} filename - Filename of the notebook.
 * @param {string} gistID - ID of the published gist, given after URL
 * @param {object} notificationSystem - To be passed information for
 * notification of the user that the gist has been published.
 */
export function notifyUser(filename, gistID, notificationSystem) {
  notificationSystem.addNotification({
    title: 'Gist uploaded',
    message: `${filename} is ready`,
    dismissible: true,
    position: 'tr',
    level: 'success',
    action: {
      label: 'Open Gist',
      callback: function openGist() {
        shell.openExternal(`https://nbviewer.jupyter.org/${gistID}`);
      },
    },
  });
}

/**
 * Callback function to be used in publishNotebookObservable such that the
 * response from the github API can be used for user notification.
 * @param {boolean} firstTimePublish - If false, overwrite gist_id metdata.
 * @param {object} observer - The publishNotebookObserver that will be
 * completed after the callback.
 * @param {string} filename - Filename of the notebook.
 * @param {function} notificationSystem - To be passed information for
 * notification of the user that the gist has been published.
 * @return callbackFunction for use in publishNotebookObservable
 */
export function createGistCallback(store, observer, filename, notificationSystem) {
  return function gistCallback(err, response) {
    if (err) {
      observer.error(err);
      observer.complete();
      return;
    }
    const gistID = response.id;
    observer.next(store.dispatch(overwriteMetadata('gist_id', gistID)));
    notifyUser(filename, gistID, notificationSystem);
  };
}

/**
 * Notebook Observable for the purpose of tracking every time a user
 * wishes to publish a gist.
 * @param {object} github - The github api for authenticating and publishing
 * the gist.
 * @param {object} notebook - The notebook to be converted to its JSON.
 * @param {string} filename - The filename of the notebook to be published.
 * @param {function} notificationSystem - To be passed information for
 * notification of the user that the gist has been published.
 */
export function publishNotebookObservable(github, notebook, filepath,
  notificationSystem, publishAsUser, store) {
  return Rx.Observable.create((observer) => {
    const notebookString = JSON.stringify(
      commutable.toJS(notebook.update('cellMap', cells =>
        cells.map(value =>
          value
            .deleteIn(['metadata', 'inputHidden'])
            .deleteIn(['metadata', 'outputHidden'])
            .delete(['metadata', 'status'])))),
      undefined,
      1);

    const filename = filepath ? path.parse(filepath).base : 'Untitled.ipynb';
    const files = {};
    files[filename] = { content: notebookString };

    if (publishAsUser) {
      github.users.get({}, (err, res) => {
        if (err) throw err;
        notificationSystem.addNotification({
          title: 'Authenticated',
          message: `Authenticated as ${res.login}`,
          level: 'info',
        });
      });
    }
    notificationSystem.addNotification({
      title: 'Uploading gist...',
      message: 'Your notebook is being uploaded as a GitHub gist',
      level: 'info',
    });
    // Already in a gist, update the gist
    const gistRequest = notebook.hasIn(['metadata', 'gist_id']) ?
      { files, id: notebook.getIn(['metadata', 'gist_id']), public: false } :
      { files, public: false };
    if (gistRequest.id) {
      github.gists.edit(gistRequest,
        createGistCallback(store, observer, filename, notificationSystem));
    } else {
      github.gists.create(gistRequest,
        createGistCallback(store, observer, filename, notificationSystem));
    }
    observer.complete();
  });
}

/**
 * Handle gist errors for the publish epic.
 * @param  {String} error - Error response to be parsed and handled.
 *
 */
export function handleGistError(err) {
  return Observable.of({ type: 'ERROR', payload: err, err: true });
}

/**
 * Handle user vs. anonymous gist actions in publishEpic
 * @param {action} action - The action being processed by the epic.
 * @param {store} reduxStore - The store containing state data.
 * return {Observable} publishNotebookObservable with appropriate parameters.
*/
export function handleGistAction(action, store) {
  const github = new Github();
  const state = store.getState();
  const notebook = state.document.get('notebook');
  const filename = state.metadata.get('filename');
  const notificationSystem = state.app.get('notificationSystem');
  let publishAsUser = false;
  if (action.type === 'PUBLISH_USER_GIST') {
    const githubToken = state.app.get('token');
    github.authenticate({ type: 'oauth', token: githubToken });
    publishAsUser = true;
  }
  return publishNotebookObservable(github, notebook, filename,
                                   notificationSystem, publishAsUser, store);
}

/**
 * Epic to capture the end to end action of publishing and receiving the
 * response from the Github API.
 */
export const publishEpic = (action$, store) =>
  action$.ofType(PUBLISH_USER_GIST, PUBLISH_ANONYMOUS_GIST)
    .mergeMap((action) => handleGistAction(action, store))
    .catch(handleGistError);
<commit_msg>refactor(publish): Initial work<commit_after>import { shell } from 'electron';

import {
  PUBLISH_USER_GIST, PUBLISH_ANONYMOUS_GIST
} from './../constants';

import {
  overwriteMetadata,
} from '../actions';

const commutable = require('commutable');
const path = require('path');

const Rx = require('rxjs/Rx');

const Observable = Rx.Observable;

const Github = require('github');

/**
 * Notify the notebook user that it has been published as a gist.
 * @param {string} filename - Filename of the notebook.
 * @param {string} gistID - ID of the published gist, given after URL
 * @param {object} notificationSystem - To be passed information for
 * notification of the user that the gist has been published.
 */
export function notifyUser(filename, gistID, notificationSystem) {
  notificationSystem.addNotification({
    title: 'Gist uploaded',
    message: `${filename} is ready`,
    dismissible: true,
    position: 'tr',
    level: 'success',
    action: {
      label: 'Open Gist',
      callback: function openGist() {
        shell.openExternal(`https://nbviewer.jupyter.org/${gistID}`);
      },
    },
  });
}

/**
 * Callback function to be used in publishNotebookObservable such that the
 * response from the github API can be used for user notification.
 * @param {boolean} firstTimePublish - If false, overwrite gist_id metdata.
 * @param {object} observer - The publishNotebookObserver that will be
 * completed after the callback.
 * @param {string} filename - Filename of the notebook.
 * @param {function} notificationSystem - To be passed information for
 * notification of the user that the gist has been published.
 * @return callbackFunction for use in publishNotebookObservable
 */
export function createGistCallback(store, observer, filename, notificationSystem) {
  return function gistCallback(err, response) {
    if (err) {
      observer.error(err);
      observer.complete();
      return;
    }
    const gistID = response.id;
    observer.next(store.dispatch(overwriteMetadata('gist_id', gistID)));
    notifyUser(filename, gistID, notificationSystem);
  };
}

/**
 * Notebook Observable for the purpose of tracking every time a user
 * wishes to publish a gist.
 * @param {object} github - The github api for authenticating and publishing
 * the gist.
 * @param {object} notebook - The notebook to be converted to its JSON.
 * @param {string} filename - The filename of the notebook to be published.
 * @param {function} notificationSystem - To be passed information for
 * notification of the user that the gist has been published.
 */
export function publishNotebookObservable(github, notebook, filepath,
  notificationSystem, publishAsUser, store) {
  return Rx.Observable.create((observer) => {
    const notebookString = JSON.stringify(
      commutable.toJS(notebook.update('cellMap', cells =>
        cells.map(value =>
          value
            .deleteIn(['metadata', 'inputHidden'])
            .deleteIn(['metadata', 'outputHidden'])
            .delete(['metadata', 'status'])))),
      undefined,
      1);

    const filename = filepath ? path.parse(filepath).base : 'Untitled.ipynb';
    const files = {};
    files[filename] = { content: notebookString };

    if (publishAsUser) {
      github.users.get({}, (err, res) => {
        if (err) throw err;
        notificationSystem.addNotification({
          title: 'Authenticated',
          message: `Authenticated as ${res.login}`,
          level: 'info',
        });
        if(notebook.getIn(['metadata', 'github_username']) != res.login) {
          console.log(res.login, notebook.getIn(['metadata', 'github_username']))
          store.dispatch(overwriteMetadata('github_username', res.login))
          //delete gistID
        }
      });
    }
    notificationSystem.addNotification({
      title: 'Uploading gist...',
      message: 'Your notebook is being uploaded as a GitHub gist',
      level: 'info',
    });
    // Already in a gist belonging to the user, update the gist

    const gistRequest = notebook.hasIn(['metadata', 'gist_id']) ?
      { files, id: notebook.getIn(['metadata', 'gist_id']), public: false } :
      { files, public: false };
    if (gistRequest.id) {
      github.gists.edit(gistRequest,
        createGistCallback(store, observer, filename, notificationSystem));
    } else {
      github.gists.create(gistRequest,
        createGistCallback(store, observer, filename, notificationSystem));
    }
    observer.complete();
  });
}

/**
 * Handle gist errors for the publish epic.
 * @param  {String} error - Error response to be parsed and handled.
 *
 */
export function handleGistError(err) {
  return Observable.of({ type: 'ERROR', payload: err, err: true });
}

/**
 * Handle user vs. anonymous gist actions in publishEpic
 * @param {action} action - The action being processed by the epic.
 * @param {store} reduxStore - The store containing state data.
 * return {Observable} publishNotebookObservable with appropriate parameters.
*/
export function handleGistAction(action, store) {
  const github = new Github();
  const state = store.getState();
  const notebook = state.document.get('notebook');
  const filename = state.metadata.get('filename');
  const notificationSystem = state.app.get('notificationSystem');
  let publishAsUser = false;
  if (action.type === 'PUBLISH_USER_GIST') {
    const githubToken = state.app.get('token');
    github.authenticate({ type: 'oauth', token: githubToken });
    publishAsUser = true;
  }
  return publishNotebookObservable(github, notebook, filename,
                                   notificationSystem, publishAsUser, store);
}

/**
 * Epic to capture the end to end action of publishing and receiving the
 * response from the Github API.
 */
export const publishEpic = (action$, store) =>
  action$.ofType(PUBLISH_USER_GIST, PUBLISH_ANONYMOUS_GIST)
    .mergeMap((action) => handleGistAction(action, store))
    .catch(handleGistError);
<|endoftext|>


<commit_before>'use strict';

module.exports = function ($http, $location, resultsService) {

	const self = this;
	self.periodUnits = 'minutes';
	self.totalPeriods = 0;
	self.isProbability = false;
	self.isLinear = false;

	self.showLinearConfig = () => {
		self.isProbability = false;
		self.isLinear = true;
	};

	self.showProbabilityConfig = () => {
		self.isProbability = true;
		self.isLinear = false;
	};

};<commit_msg>linear task config will now be default on advanced page<commit_after>'use strict';

module.exports = function ($http, $location, resultsService) {

	const self = this;
	self.periodUnits = 'minutes';
	self.totalPeriods = 0;
	self.isProbability = false;
	self.isLinear = true; // default

	self.showLinearConfig = () => {
		self.isProbability = false;
		self.isLinear = true;
	};

	self.showProbabilityConfig = () => {
		self.isProbability = true;
		self.isLinear = false;
	};

};<|endoftext|>


<commit_before>const ICAL = require( 'ical.js' );
const request = require("request");
const cheerio = require("cheerio");
const moment = require('moment-timezone');

const teamData = require( './teamdata' );

const shl_url = "https://calendar.ramses.nu/calendar/66/show/shl.ics";
const ha_url = "http://www.hockeyallsvenskan.se/spelschema/HA_2018_regular";
const momentFormat = 'YYYY-MM-DDTHH:mm:ss';

let ha_games = [];
let shl_games = [];

let lastFetch = false;

const get_ha_games = () => {
    return new Promise( ( resolve, reject ) => {
        request( ha_url, ( error, response, body ) => {
            if ( error ) {
                reject( error );
                return false;
            }

            const $ = cheerio.load( body, {
                decodeEntities: false,
            } );
            const gamesHtml = $( '.rmss_c-schedule-game__row-wrapper' );
            ha_games = [];

            gamesHtml.each( ( index, element ) => {
                const $element = $( element );

                // Skip played games
                if ( $element.data( 'game-mode' ) === 'played' ) {
                    return true;
                }

                const date = $element.data( 'game-date' );
                const time = $element.find( '.rmss_c-schedule-game__live-info' ).first().text();
                const start_date_string = `${ date } ${ time }`;

                if ( !moment( start_date_string, 'YYYY-MM-DD HH:mm', true ).isValid() ) {
                    return true;
                }

                const start_date = moment.tz( start_date_string, 'Europe/Stockholm');
                const end_date = moment( start_date ).add( 150, 'minutes' );

                const game = $element.find( '.rmss_c-schedule-game__info__round-number' ).text().match( /\d+/g )[ 0 ];
                const venue = $element.find( '.rmss_c-schedule-game__info__venue' ).first().text();

                let home = $element.find( '.rmss_c-schedule-game__team.is-home-team .rmss_c-schedule-game__team-name' ).first().text();
                let away = $element.find( '.rmss_c-schedule-game__team.is-away-team .rmss_c-schedule-game__team-name' ).first().text();
                let homeData = teamData( home );
                let awayData = teamData( away );
                home = homeData.name
                away = awayData.name;

                const event = new ICAL.Component( 'vevent' );
                event.addPropertyWithValue( 'uid', `${ game }-${ home }-${ away }` );
                event.addPropertyWithValue( 'summary', `${ home } - ${ away }` );
                event.addPropertyWithValue( 'description', `Omgång ${ game }` );
                event.addPropertyWithValue( 'location', venue );
                event.addPropertyWithValue( 'dtstart', ICAL.Time.fromString( start_date.format( momentFormat ) ) );
                event.addPropertyWithValue( 'dtend', ICAL.Time.fromString( end_date.format( momentFormat ) ) );
                event.addPropertyWithValue( 'dtstamp', ICAL.Time.now() );

                ha_games.push( {
                    event: event,
                    home: home,
                    away: away,
                } );
            } );

            resolve();

            return true;
        });

        return true;
    });
};

const get_shl_games = () => {
    return new Promise( ( resolve, reject ) => {
        request( shl_url, ( error, response, body ) => {
            if ( error ) {
                reject( error );

                return false;
            }
            const shl_data = ICAL.parse( body );
            const component = new ICAL.Component( shl_data );

            shl_games = [];
            component.getAllSubcomponents( 'vevent' ).forEach( ( vevent ) => {
                let summary = vevent.getFirstPropertyValue( 'summary' );
                const event = new ICAL.Component( 'vevent' );

                // Check if the summary contains a number prefixed by a space
                // Probably means it's a score
                if ( /\s\d/.test( summary ) ) {
                    let matches = summary.match( /(.+?)\s\d/ );

                    summary = matches[ 1 ].trim();
                }

                const [ home, away ] = summary.split( ' - ' );

                if( !home || !away ) {
                    console.log( `Unable to parse ${ summary } as home & away, skipping` );

                    return true;
                }

                event.addPropertyWithValue( 'dtstamp', ICAL.Time.now() );
                event.addPropertyWithValue( 'summary', `${ home } - ${ away }` );

                event.addPropertyWithValue( 'dtstart', vevent.getFirstPropertyValue( 'dtstart' ) );
                event.addPropertyWithValue( 'dtend', vevent.getFirstPropertyValue( 'dtend' ) );
                event.addPropertyWithValue( 'description', vevent.getFirstPropertyValue( 'description' ) );
                event.addPropertyWithValue( 'location', vevent.getFirstPropertyValue( 'location' ) );
                event.addPropertyWithValue( 'url', vevent.getFirstPropertyValue( 'url' ) );
                event.addPropertyWithValue( 'uid', vevent.getFirstPropertyValue( 'uid' ) );

                const homeData = teamData( home );
                const awayData = teamData( away );

                shl_games.push( {
                    event: event,
                    home: homeData.name,
                    away: awayData.name,
                } );
            } );

            resolve();

            return true;
        });

        return true;
    } );
};

const update_games = function() {
    return new Promise( ( resolve, reject ) => {
        let fetch = true;

        const now = new Date().getTime() / 1000;
        const diff = now - 3600;

        if ( diff < lastFetch ) {
            resolve();

            return true;
        };

        lastFetch = new Date().getTime() / 1000;

        const updates = [];
        updates.push( get_shl_games() );
        updates.push( get_ha_games() );

        Promise.all( updates )
            .then( () => {
                resolve();
            })
            .catch( ( someError ) => {
                reject( someError );
            });
    });
};

const calendar = (f,cb) => {
    const filter = [].concat(f);
    const calendar = new ICAL.Component( 'vcalendar' );
    let include_teams = [];

    calendar.addPropertyWithValue( 'prodid', '-//Hockey McHF//Hockey McHockeyFace//EN' );
    calendar.addPropertyWithValue( 'version', '2.0' );
    calendar.addPropertyWithValue( 'calscale', 'GREGORIAN' );
    calendar.addPropertyWithValue( 'x-wr-timezone', 'Europe/Stockholm' );

    filter.forEach(function(team) {
        const tempData = teamData( team );
        include_teams.push( tempData.name );
    });

    if ( include_teams.length === 0 ) {
        const allTeams = teamData();

        include_teams = allTeams.map( ( team ) => {
            return team.name;
        } );
    }

    calendar.addPropertyWithValue( 'x-wr-calname', 'Svensk hockey' );
    calendar.addPropertyWithValue( 'x-wr-caldesc', 'Spelschema för svensk hockey' );

    if( filter.length === 1 && filter[0]){
        calendar.updatePropertyWithValue( 'x-wr-calname', include_teams[ 0 ] );
        calendar.updatePropertyWithValue( 'x-wr-caldesc', `Spelschema för ${ include_teams[ 0 ] }` );
    }

    update_games()
        .then( () => {
            for ( let i = 0; i < shl_games.length; i = i + 1 ) {
                if ( include_teams.includes( shl_games[ i ].home ) || include_teams.includes( shl_games[ i ].away ) ) {
                    calendar.addSubcomponent( shl_games[ i ].event );
                }
            }

            for ( let i = 0; i < ha_games.length; i = i + 1 ) {
                if ( include_teams.includes( ha_games[ i ].home ) || include_teams.includes( ha_games[ i ].away ) ) {
                    calendar.addSubcomponent( ha_games[ i ].event );
                }
            }

            cb( null, calendar.toString() );
        } )
        .catch( ( someError ) => {
            console.error( someError );
        } );
};

module.exports = calendar;
<commit_msg>Guess that HA just will bump their URL<commit_after>const ICAL = require( 'ical.js' );
const request = require("request");
const cheerio = require("cheerio");
const moment = require('moment-timezone');

const teamData = require( './teamdata' );

const shl_url = "https://calendar.ramses.nu/calendar/66/show/shl.ics";
const ha_url = "http://www.hockeyallsvenskan.se/spelschema/HA_2019_regular";
const momentFormat = 'YYYY-MM-DDTHH:mm:ss';

let ha_games = [];
let shl_games = [];

let lastFetch = false;

const get_ha_games = () => {
    return new Promise( ( resolve, reject ) => {
        request( ha_url, ( error, response, body ) => {
            if ( error ) {
                reject( error );
                return false;
            }

            const $ = cheerio.load( body, {
                decodeEntities: false,
            } );
            const gamesHtml = $( '.rmss_c-schedule-game__row-wrapper' );
            ha_games = [];

            gamesHtml.each( ( index, element ) => {
                const $element = $( element );

                // Skip played games
                if ( $element.data( 'game-mode' ) === 'played' ) {
                    return true;
                }

                const date = $element.data( 'game-date' );
                const time = $element.find( '.rmss_c-schedule-game__live-info' ).first().text();
                const start_date_string = `${ date } ${ time }`;

                if ( !moment( start_date_string, 'YYYY-MM-DD HH:mm', true ).isValid() ) {
                    return true;
                }

                const start_date = moment.tz( start_date_string, 'Europe/Stockholm');
                const end_date = moment( start_date ).add( 150, 'minutes' );

                const game = $element.find( '.rmss_c-schedule-game__info__round-number' ).text().match( /\d+/g )[ 0 ];
                const venue = $element.find( '.rmss_c-schedule-game__info__venue' ).first().text();

                let home = $element.find( '.rmss_c-schedule-game__team.is-home-team .rmss_c-schedule-game__team-name' ).first().text();
                let away = $element.find( '.rmss_c-schedule-game__team.is-away-team .rmss_c-schedule-game__team-name' ).first().text();
                let homeData = teamData( home );
                let awayData = teamData( away );
                home = homeData.name
                away = awayData.name;

                const event = new ICAL.Component( 'vevent' );
                event.addPropertyWithValue( 'uid', `${ game }-${ home }-${ away }` );
                event.addPropertyWithValue( 'summary', `${ home } - ${ away }` );
                event.addPropertyWithValue( 'description', `Omgång ${ game }` );
                event.addPropertyWithValue( 'location', venue );
                event.addPropertyWithValue( 'dtstart', ICAL.Time.fromString( start_date.format( momentFormat ) ) );
                event.addPropertyWithValue( 'dtend', ICAL.Time.fromString( end_date.format( momentFormat ) ) );
                event.addPropertyWithValue( 'dtstamp', ICAL.Time.now() );

                ha_games.push( {
                    event: event,
                    home: home,
                    away: away,
                } );
            } );

            resolve();

            return true;
        });

        return true;
    });
};

const get_shl_games = () => {
    return new Promise( ( resolve, reject ) => {
        request( shl_url, ( error, response, body ) => {
            if ( error ) {
                reject( error );

                return false;
            }
            const shl_data = ICAL.parse( body );
            const component = new ICAL.Component( shl_data );

            shl_games = [];
            component.getAllSubcomponents( 'vevent' ).forEach( ( vevent ) => {
                let summary = vevent.getFirstPropertyValue( 'summary' );
                const event = new ICAL.Component( 'vevent' );

                // Check if the summary contains a number prefixed by a space
                // Probably means it's a score
                if ( /\s\d/.test( summary ) ) {
                    let matches = summary.match( /(.+?)\s\d/ );

                    summary = matches[ 1 ].trim();
                }

                const [ home, away ] = summary.split( ' - ' );

                if( !home || !away ) {
                    console.log( `Unable to parse ${ summary } as home & away, skipping` );

                    return true;
                }

                event.addPropertyWithValue( 'dtstamp', ICAL.Time.now() );
                event.addPropertyWithValue( 'summary', `${ home } - ${ away }` );

                event.addPropertyWithValue( 'dtstart', vevent.getFirstPropertyValue( 'dtstart' ) );
                event.addPropertyWithValue( 'dtend', vevent.getFirstPropertyValue( 'dtend' ) );
                event.addPropertyWithValue( 'description', vevent.getFirstPropertyValue( 'description' ) );
                event.addPropertyWithValue( 'location', vevent.getFirstPropertyValue( 'location' ) );
                event.addPropertyWithValue( 'url', vevent.getFirstPropertyValue( 'url' ) );
                event.addPropertyWithValue( 'uid', vevent.getFirstPropertyValue( 'uid' ) );

                const homeData = teamData( home );
                const awayData = teamData( away );

                shl_games.push( {
                    event: event,
                    home: homeData.name,
                    away: awayData.name,
                } );
            } );

            resolve();

            return true;
        });

        return true;
    } );
};

const update_games = function() {
    return new Promise( ( resolve, reject ) => {
        let fetch = true;

        const now = new Date().getTime() / 1000;
        const diff = now - 3600;

        if ( diff < lastFetch ) {
            resolve();

            return true;
        };

        lastFetch = new Date().getTime() / 1000;

        const updates = [];
        updates.push( get_shl_games() );
        updates.push( get_ha_games() );

        Promise.all( updates )
            .then( () => {
                resolve();
            })
            .catch( ( someError ) => {
                reject( someError );
            });
    });
};

const calendar = (f,cb) => {
    const filter = [].concat(f);
    const calendar = new ICAL.Component( 'vcalendar' );
    let include_teams = [];

    calendar.addPropertyWithValue( 'prodid', '-//Hockey McHF//Hockey McHockeyFace//EN' );
    calendar.addPropertyWithValue( 'version', '2.0' );
    calendar.addPropertyWithValue( 'calscale', 'GREGORIAN' );
    calendar.addPropertyWithValue( 'x-wr-timezone', 'Europe/Stockholm' );

    filter.forEach(function(team) {
        const tempData = teamData( team );
        include_teams.push( tempData.name );
    });

    if ( include_teams.length === 0 ) {
        const allTeams = teamData();

        include_teams = allTeams.map( ( team ) => {
            return team.name;
        } );
    }

    calendar.addPropertyWithValue( 'x-wr-calname', 'Svensk hockey' );
    calendar.addPropertyWithValue( 'x-wr-caldesc', 'Spelschema för svensk hockey' );

    if( filter.length === 1 && filter[0]){
        calendar.updatePropertyWithValue( 'x-wr-calname', include_teams[ 0 ] );
        calendar.updatePropertyWithValue( 'x-wr-caldesc', `Spelschema för ${ include_teams[ 0 ] }` );
    }

    update_games()
        .then( () => {
            for ( let i = 0; i < shl_games.length; i = i + 1 ) {
                if ( include_teams.includes( shl_games[ i ].home ) || include_teams.includes( shl_games[ i ].away ) ) {
                    calendar.addSubcomponent( shl_games[ i ].event );
                }
            }

            for ( let i = 0; i < ha_games.length; i = i + 1 ) {
                if ( include_teams.includes( ha_games[ i ].home ) || include_teams.includes( ha_games[ i ].away ) ) {
                    calendar.addSubcomponent( ha_games[ i ].event );
                }
            }

            cb( null, calendar.toString() );
        } )
        .catch( ( someError ) => {
            console.error( someError );
        } );
};

module.exports = calendar;
<|endoftext|>


<commit_before>module.exports = function(ircbot, config) {
	var requireDir = require('require-dir');
	var commands = requireDir('commands');

	ircbot.addListener('message', function (from, to, message) {
		if (to !== config.irc.announceChannel) {
			return;
		}

		Object.keys(commands).forEach(function(key) {
			var command = commands[key];
			var commandPrefix = '!' + command.key;

			if (message.indexOf(commandPrefix) !== 0) {
				return;
			}

			command.execute(ircbot, config, from, to, message.substr(commandPrefix.length), commands);
		});
	});
};
<commit_msg>Fix bug in command parsing<commit_after>module.exports = function(ircbot, config) {
	var requireDir = require('require-dir');
	var commands = requireDir('commands');

	ircbot.addListener('message', function (from, to, message) {
		if (to !== config.irc.announceChannel) {
			return;
		}

		Object.keys(commands).forEach(function(key) {
			var command = commands[key];
			var commandPrefix = '!' + command.key;

			if (message !== commandPrefix && message.indexOf(commandPrefix + ' ') !== 0) {
				return;
			}

			command.execute(ircbot, config, from, to, message.substr(commandPrefix.length + 1), commands);
		});
	});
};
<|endoftext|>