<commit_before>'use babel';
import fs from 'fs';
import path from 'path';
import { CompositeDisposable } from 'atom';
import mkdirp from 'mkdirp';
import stylus from 'stylus';

var disposable;

export function activate() {
  disposable = new CompositeDisposable(
    atom.commands.add('atom-text-editor', 'core:save', event => handleSave(event.currentTarget.getModel()))
  );
}

export function deactivate() {
  disposable.dispose();
  disposable = null;
}

const stylusGrammar = atom.grammars.grammarForScopeName('source.stylus');

function handleSave(textEditor) {
  if(textEditor.getGrammar() == stylusGrammar) {
    compile(textEditor);
  }
}

function compile(textEditor) {
  var source = textEditor.getText();
  var params = getParams(source);

  if(!params) return;

  if(params.main) {
    var newPath = path.resolve(textEditor.getURI(), `../${params.main}`);
    source = fs.readFileSync(newPath, 'utf8');
    params = getParams(source);
    if(!params) return;

    params.inPath = newPath;
  } else if(params.out) {
    params.inPath = textEditor.getURI();
  }

  params.outPath = path.resolve(params.inPath, `../${params.out}`);
  render(source, params);
}

function render(source, params) {
  var renderer = stylus(source)
    .set('paths', [path.dirname(path.resolve(params.inPath))])
    .set('filename', path.basename(params.inPath))
    .set('sourcemap', params.sourcemap)
    .set('compress', params.compress);

  renderer.render((error, css) => {
    if(error) return atom.notifications.addError(error.message, {
      detail: `${error.filename}:${error.line}`,
      dismissable: true,
    });

    writeFile(css, params.outPath);
    if(renderer.sourcemap) writeFile(JSON.stringify(renderer.sourcemap), `${params.outPath}.map`);
  });
}

function writeFile(content, filePath) {
  mkdirp(path.dirname(filePath), error => {
    if(error) return atom.notifications.addError(error.message);

    fs.writeFile(filePath, content, error => {
      if(error) return atom.notifications.addError(error.message);

      atom.notifications.addSuccess(`Saved to ${filePath}`);
    });
  });
}

// match the first comment in the file
function getParams(source) {
  var [ , paramString ] = source.match(/^\s*\/\/\s*((?:[^\n:]+:[^\n,]+\s*,?\s*)+)\n/) || [];
  if(paramString) return parseParams(paramString);
}

function parseParams(string) {
  var params = {};
  for(let pair of string.replace(/\s/g, '').split(',')) {
    let [ key, value ] = pair.split(':');
    if(['true', 'yes', '1'].indexOf(value) > -1) value = true;
    else if(['false', 'no', '0'].indexOf(value) > -1) value = false;
    params[key] = value;
  }
  return params;
}
<commit_msg>improve parameter parsing<commit_after>'use babel';
import fs from 'fs';
import path from 'path';
import { CompositeDisposable } from 'atom';
import mkdirp from 'mkdirp';
import stylus from 'stylus';

var disposable;

export function activate() {
  disposable = new CompositeDisposable(
    atom.commands.add('atom-text-editor', 'core:save', ({currentTarget}) =>
      handleSave(currentTarget)
    ),
  );
}

export function deactivate() {
  disposable.dispose();
  disposable = null;
}

const stylusGrammar = atom.grammars.grammarForScopeName('source.stylus');

function handleSave(textEditorElement) {
  if(typeof textEditorElement.getModel != 'function') return;
  var textEditor = textEditorElement.getModel();
  if(textEditor.getGrammar() == stylusGrammar) {
    compile(textEditor);
  }
}

function compile(textEditor) {
  var source = textEditor.getText();
  var params = getParams(source);

  if(params.main) {
    var newPath = path.resolve(textEditor.getURI(), `../${params.main}`);
    source = fs.readFileSync(newPath, 'utf8');
    params = getParams(source);
    if(!params) return;

    params.inPath = newPath;
  } else if(params.out) {
    params.inPath = textEditor.getURI();
  }

  if(!params.inPath) return;

  params.outPath = path.resolve(params.inPath, `../${params.out}`);
  render(source, params);
}

function render(source, params) {
  var {inPath, compress, sourcemap} = params;
  var renderer = stylus(source)
    .set('paths', [path.dirname(path.resolve(inPath))])
    .set('filename', path.basename(inPath))
    .set('compress', compress)
    .set('sourcemap', sourcemap);

  renderer.render((error, css) => {
    if(error) return atom.notifications.addError(error.message, {
      detail: `${error.filename}:${error.line}`,
      dismissable: true,
    });

    writeFile(css, params.outPath);
    if(renderer.sourcemap) {
      writeFile(JSON.stringify(renderer.sourcemap), `${params.outPath}.map`);
    }
  });
}

function writeFile(content, filePath) {
  mkdirp(path.dirname(filePath), error => {
    if(error) return atom.notifications.addError(error.message);

    fs.writeFile(filePath, content, error => {
      if(error) return atom.notifications.addError(error.message);

      atom.notifications.addSuccess(`Saved to ${filePath}`);
    });
  });
}

// match the first comment in the file
function getParams(source) {
  var [, paramString] = source.match(/^\s*\/\/\s*([^\n]*)/) || [];
  if(!paramString) return;

  paramString = paramString.trim();
  var main = getParam(paramString, 'main');
  var out = getParam(paramString, 'out');
  var compress = getParam(paramString, 'compress', true);
  var sourcemap = getParam(paramString, 'sourcemap', true);
  return {main, out, compress, sourcemap};
}

function getParam(paramString, key, parseBool = false) {
  var regex = new RegExp(`${key}\s*:\s*([^,]+)`);
  var [, value] = paramString.match(regex) || [null, ''];
  value = value.trim();
  if(parseBool) {
    if(value && value == 'false') {
      value = false;
    } else {
      value = !!value;
    }
  }
  return value;
}
<|endoftext|>


<commit_before>$.fn.drawFullscreen = function() {
	var win = $(window);
	var me = $(this);
	me.trigger('saveDraw');

	var fullscreen = $('#svg-fullscreen');

	if (fullscreen.length == 0) {
		me.data('origParent', me.parent());

		var menuHeight = $('#drawMenu').height();
		$('body').addClass('full_screen_body');
		$('body,html').scrollTop(0);

		fullscreen = $('
')
			.html(me)
			.prependTo('body');

		var fullscreenIframe = fullscreen.find('iframe');

		win
			.resize(function() {
				fullscreen
					.height(win.height())
					.width(win.width());

				fullscreenIframe.height((fullscreen.height() - menuHeight));
			})
			.resize() //we do it double here to make sure it is all resized right
			.resize();

	} else {
		me.data('origParent').append(me);
		win.unbind('resize');
		fullscreen.remove();
		$('body').removeClass('full_screen_body');
	}

	return this;
};

$.fn.replaceDraw = function(o) {
	var me = $(this);
	if (o.error) {
		alert('error ' + o.error);
	} else {
		$.modal(tr("Saving..."));
		$.post('tiki-edit_draw.php', {
			galleryId: o.galleryId,
			fileId: o.fileId,
			name: o.name,
			data: o.data
		}, function(fileId) {
			fileId = (fileId ? fileId : o.fileId);
			o.fileId = fileId;

			me.data('fileId', o.fileId);
			me.data('galleryId', o.galleryId);
			me.data('name', o.name);

			$.modal(tr("Saved file id") + o.fileId + '!');

			if ($.wikiTrackingDraw) {
				$.wikiTrackingDraw.params.id = o.fileId;
				$.modal(tr("Updating Wiki Page"));
				$.post('tiki-wikiplugin_edit.php', $.wikiTrackingDraw, function() {
					me.trigger('savedDraw', o);
					$.modal();
				});
			} else {
				me.trigger('savedDraw', o);
				$.modal();
			}
		});
	}

	return this;
};

$.fn.saveDraw = function() {
	var me = $(this);
	me.data('canvas').getSvgString()(function(data, error) {
		me.replaceDraw({
			data: data,
			error: error,
			fileId: me.data('fileId'),
			galleryId: me.data('galleryId'),
			name: me.data('name')
		})
	});

	try {
		me.data('window').svgCanvas.undoMgr.resetUndoStack();
	} catch(e) {}

	return this;
};

$.fn.saveAndBackDraw = function() {
	$(this)
		.saveDraw()
		.one('savedDraw', function() {
			window.history.back();
		});
};

$.fn.renameDraw = function() {
	var me = $(this);
	var name = me.data('name');
	var newName = prompt(tr("Enter new name"), name);
	if (newName) {
		if (newName != name) {
			name = newName;
			me.data('name', name);
			me.trigger('renamedDraw', name);

			me.saveDraw();
		}
	}

	return this;
};

$.drawInstance = 0;

$.fn.loadDraw = function(o) {
	var me = $(this);

	//prevent from happeneing over and over again
	if (me.data('drawLoaded')) return me;

	me.data('drawLoaded', true);

	var options = [
		'canvas_expansion=2',
		'initFill[opacity]=0.5'
	];

	if ($.lang) {
		options.push('lang=' + $.lang);
	}

	var drawFrame = $('')
		.appendTo(me)
		.load(function() {
			me
				.data('drawInstance', $.drawInstance)
				.data('fileId', (o.fileId ? o.fileId : 0))
				.data('galleryId', (o.galleryId ? o.galleryId : 0))
				.data('name', (o.name ? o.name : ''))
				.data('doc', $(drawFrame[0].contentDocument ? drawFrame[0].contentDocument : drawFrame[0].contentWindow.document))
				.data('canvas', new embedded_svg_edit(drawFrame[0]))
				.data('window', drawFrame[0].contentWindow);

			// Hide un-needed buttons
			var doc = me.data('doc');
			doc.find('body').append(
				''
			);

			o.data = $.trim(o.data);
			if (o.data && o.fileId) {
				me.data('canvas').setSvgString(o.data);
			}

			me.data('window').onbeforeunload = function() {};

			window.onbeforeunload = function() {
				try {
					if ( me.data('window').svgCanvas.undoMgr.getUndoStackSize() > 1 ) {
						return tr("There are unsaved changes, leave page?");
					}
				} catch (e) {}
			};

			drawFrame.height($(window).height() * 0.9);

			$.drawInstance++;

			me.trigger('loadedDraw');

			$.getJSON('tiki-ajax_services.php', {
				controller: 'draw',
				action: 'removeButtons'
			}, function(data) {
				if (data.removeButtons) {
					if (!$.isArray(data.removeButtons)) data.removeButtons = data.removeButtons.split(',');
					for(id in data.removeButtons) {
						me.data('doc').find('#' + $.trim(data.removeButtons[id])).wrap('
');
					}
				}
			});
		});
	return me;
};<commit_msg>[ROLLBACK][r43667]Not stable enough for all browsers<commit_after>$.fn.drawFullscreen = function() {
	var win = $(window);
	var me = $(this);
	me.trigger('saveDraw');

	var fullscreen = $('#svg-fullscreen');

	if (fullscreen.length == 0) {
		me.data('origParent', me.parent());

		var menuHeight = $('#drawMenu').height();
		$('body').addClass('full_screen_body');
		$('body,html').scrollTop(0);

		fullscreen = $('
')
			.html(me)
			.prependTo('body');

		var fullscreenIframe = fullscreen.find('iframe');

		win
			.resize(function() {
				fullscreen
					.height(win.height())
					.width(win.width());

				fullscreenIframe.height((fullscreen.height() - menuHeight));
			})
			.resize() //we do it double here to make sure it is all resized right
			.resize();

	} else {
		me.data('origParent').append(me);
		win.unbind('resize');
		fullscreen.remove();
		$('body').removeClass('full_screen_body');
	}

	return this;
};

$.fn.replaceDraw = function(o) {
	var me = $(this);
	if (o.error) {
		alert('error ' + o.error);
	} else {
		$.modal(tr("Saving..."));
		$.post('tiki-edit_draw.php', {
			galleryId: o.galleryId,
			fileId: o.fileId,
			name: o.name,
			data: o.data
		}, function(fileId) {
			fileId = (fileId ? fileId : o.fileId);
			o.fileId = fileId;

			me.data('fileId', o.fileId);
			me.data('galleryId', o.galleryId);
			me.data('name', o.name);

			$.modal(tr("Saved file id") + o.fileId + '!');

			if ($.wikiTrackingDraw) {
				$.wikiTrackingDraw.params.id = o.fileId;
				$.modal(tr("Updating Wiki Page"));
				$.post('tiki-wikiplugin_edit.php', $.wikiTrackingDraw, function() {
					me.trigger('savedDraw', o);
					$.modal();
				});
			} else {
				me.trigger('savedDraw', o);
				$.modal();
			}
		});
	}

	return this;
};

$.fn.saveDraw = function() {
	var me = $(this);
	me.data('canvas').getSvgString()(function(data, error) {
		me.replaceDraw({
			data: data,
			error: error,
			fileId: me.data('fileId'),
			galleryId: me.data('galleryId'),
			name: me.data('name')
		})
	});

	try {
		me.data('window').svgCanvas.undoMgr.resetUndoStack();
	} catch(e) {}

	return this;
};

$.fn.saveAndBackDraw = function() {
	$(this)
		.saveDraw()
		.one('savedDraw', function() {
			window.history.back();
		});
};

$.fn.renameDraw = function() {
	var me = $(this);
	var name = me.data('name');
	var newName = prompt(tr("Enter new name"), name);
	if (newName) {
		if (newName != name) {
			name = newName;
			me.data('name', name);
			me.trigger('renamedDraw', name);

			me.saveDraw();
		}
	}

	return this;
};

$.drawInstance = 0;

$.fn.loadDraw = function(o) {
	var me = $(this);

	//prevent from happeneing over and over again
	if (me.data('drawLoaded')) return me;

	me.data('drawLoaded', true);

	var options = [
		'canvas_expansion=2'
	];

	if ($.lang) {
		options.push('lang=' + $.lang);
	}

	var drawFrame = $('')
		.appendTo(me)
		.load(function() {
			me
				.data('drawInstance', $.drawInstance)
				.data('fileId', (o.fileId ? o.fileId : 0))
				.data('galleryId', (o.galleryId ? o.galleryId : 0))
				.data('name', (o.name ? o.name : ''))
				.data('doc', $(drawFrame[0].contentDocument ? drawFrame[0].contentDocument : drawFrame[0].contentWindow.document))
				.data('canvas', new embedded_svg_edit(drawFrame[0]))
				.data('window', drawFrame[0].contentWindow);

			// Hide un-needed buttons
			var doc = me.data('doc');
			doc.find('body').append(
				''
			);

			o.data = $.trim(o.data);
			if (o.data && o.fileId) {
				me.data('canvas').setSvgString(o.data);
			}

			me.data('window').onbeforeunload = function() {};

			window.onbeforeunload = function() {
				try {
					if ( me.data('window').svgCanvas.undoMgr.getUndoStackSize() > 1 ) {
						return tr("There are unsaved changes, leave page?");
					}
				} catch (e) {}
			};

			drawFrame.height($(window).height() * 0.9);

			$.drawInstance++;

			me.trigger('loadedDraw');

			$.getJSON('tiki-ajax_services.php', {
				controller: 'draw',
				action: 'removeButtons'
			}, function(data) {
				if (data.removeButtons) {
					if (!$.isArray(data.removeButtons)) data.removeButtons = data.removeButtons.split(',');
					for(id in data.removeButtons) {
						me.data('doc').find('#' + $.trim(data.removeButtons[id])).wrap('
');
					}
				}
			});
		});
	return me;
};<|endoftext|>


<commit_before>const logger         = require('bole')('processor');
const db             = require('../db');
const botan          = require('./botan.js');
const TelegramClient = require('./client.js');
const geocoding      = require('../maps').geocoding;
const LocationsMap   = require('../maps').LocationsMap;
const stathat        = require('../stathat.js');

const tg = new TelegramClient();

class TelegramProcessor {
	constructor(update) {
		this.update = update;
	}

	process() {
		let tgChat;

		// Update type is message
		if (this.update['message']) {
			tgChat = this.update['message']['chat'];
		}
		// Update type is callback query (=> inline button press)
		else if (this.update['callback_query']) {
			tgChat = this.update['callback_query']['message']['chat'];
		}

		// Get chat details
		db.chats.get(tgChat, (err, chat) => {
			if (err) {
				logger.error(err, tgChat);
				return;
			}

			chat.send = (msg, callback) => {
				msg['chat'] = chat['id'];
				msg['type'] = msg['type'] || 'sendMessage';
				callback = callback || function() {};

				if (msg['type'] == 'sendMessage') {
					tg.sendMessage(msg, callback);
				}
				else if (msg['type'] == 'editMessage') {
					tg.editMessage(msg, callback);
				}
				else if (msg['type'] == 'sendPhoto') {
					tg.sendPhoto(msg, callback);
				}
				else if (msg['type'] == 'sendChatAction') {
					tg.sendChatAction(msg, callback);
				}
			};

			chat.error = (callback) => {
				let msg = {
					key: 'error',
					chat: chat['id'],
					type: 'sendMessage'
				};

				tg.sendMessage(msg);

				// ?
				callback && callback();
			};

			this.chat = chat;

			if (this.update['message']) {
				this._handleMessage(this.update['message']);
			}
			else if (this.update['callback_query']) {
				this._handleCallback(this.update['callback_query']);
			}
		});
	}

	_handleCallback(query) {
		logger.debug(`Incoming <query> from <${this.chat.id}>`);
		let data = query['data'].split(';');
		let messageId = query['message']['message_id'];

		if (data[0] == 'settings') {
			if (data[1] == 'radius') {
				let msg = {
					key: 'radius',
					keyboard: [
						[{ text: '25 km' }, { text: '50 km' }, { text: '75 km' }],
						[{ text: '100 km' }, { text: '150 km' }, { text: '200 km' }],
						[{ text: '$$close' }]
					]
				};

				this.chat.send(msg);
			}
			else if (data[1] == 'magnitude') {
				let msg = {
					key: 'magnitude',
					keyboard: [
						[{ text: '2.0+' }, { text: '3.0+' }],
						[{ text: '4.0+' }, { text: '5.0+' }],
						[{ text: '$$close' }]
					]
				};

				this.chat.send(msg);
			}
			else if (data[1] == 'broadcast') {
				if (data[2]) {
					// Enable or disable broadcast notifications
					let shouldEnable = !!+data[2]; // 0 becomes false, 1 becomes true. Horrible.

					db.chats.setBroadcast(this.chat['id'], shouldEnable, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setBroadcast error');
							return;
						}

						this.chat['settings']['broadcast'] = shouldEnable;
						this._sendBroadcastSettingMessage(messageId);
					});
				}
				else {
					this._sendBroadcastSettingMessage();
				}
			}
		}
		else if (data[0] == 'event') {
			let eventId = data[1];

			db.history.findById(eventId, (err, event) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'history.findById error');
					return;
				}
				else if (!event) {
					// TODO: answer callback query with error
					return;
				}

				let msg = {
					key: 'details',
					data: event,
					inline: [
						[{ text: '$$details_ingv', url: 'http://cnt.rm.ingv.it/event/' + event['id'] }]
					]
				};

				this.chat.send(msg);
			});
		}
	}

	_sendBroadcastSettingMessage(messageId) {
		let msg = {
			key: 'broadcast',
			inline: [
				[
					{
						text: '$$' + (this.chat['settings']['broadcast'] ? 'broadcast_enabled' : 'broadcast_enable'),
						callback_data: 'settings;broadcast;1'
					},
					{
						text: '$$' + (this.chat['settings']['broadcast'] ? 'broadcast_disable' : 'broadcast_disabled'),
						callback_data: 'settings;broadcast;0'
					}
				]
			]
		};

		if (messageId) {
			msg['type'] = 'editMessage';
			msg['messageId'] = messageId;
		}

		this.chat.send(msg);
	}

	_handleMessage(message) {
		this.message = message;
		message['date'] = new Date(message['date'] * 1000);
		db.incoming.insert(message);

		if (message['location']) {
			this._handleLocation();
		}
		else if (message['venue']) {
			this.message['location'] = this.message['venue']['location'];
			this._handleLocation();
		}
		else if (message['text']) {
			this._handleText();
		}
	}

	_handleLocation() {
		logger.debug(`Incoming <location> from <${this.chat.id}>`);
		botan.log(this.message, 'location');

		let loc = this.message['location'];
		let lat = loc['latitude'];
		let lon = loc['longitude'];

		geocoding.reverse(lat, lon, (err, result) => {
			if (err) {
				if (err['type'] == 'network' || err['type'] == 'response') {
					this.chat.error();
				}
				else if (err['type'] == 'invalid') {
					let msg = { key: 'location_invalid' };
					this.chat.send(msg);
				}
				else if (err['type'] == 'noresults') {
					let msg = { key: 'location_noresults' };
					this.chat.send(msg);
				}

				return;
			}

			let name = result['name'];

			db.locations.exists(this.chat['id'], name, (err, exists) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'locations exists');
					return;
				}

				if (exists) {
					let msg = { key: 'location_exists' };
					this.chat.send(msg);
				}
				else {
					db.locations.insert(this.chat['id'], lat, lon, name, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'locations insert');
							return;
						}

						let msg = {
							key: 'location_ok',
							data: { name }
						};

						this.chat.send(msg, () => {
							this._sendMap();
						});
					});
				}
			});
		});
	}

	_handleText() {
		logger.debug(`Incoming <text> from <${this.chat.id}>`);
		let text = this.message['text'];
		let cmd = text.replace(/\/ /, '').toLowerCase();

		if (cmd.includes('start') || cmd.includes('avvia')) {
			let msg = {
				key: 'start',
				data: {
					name: this.chat['first_name']
				}
			};

			this.chat.send(msg);
		}
		else if (cmd.includes('chiudi') || cmd.includes('annulla')) {
			let msg = { key: 'done' };
			this.chat.send(msg);

			botan.log(this.message, 'cancel');
		}
		else if (cmd.includes('aiuto') || cmd.includes('help')) {
			let msg = { key: 'help' };
			this.chat.send(msg);

			botan.log(this.message, 'help');
		}
		else if (cmd.includes('posizioni')) {
			db.locations.find(this.chat.id, (err, locations) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'locations find');
					return;
				}

				if (locations.length == 0) {
					let msg = { key: 'locations_empty' };
					this.chat.send(msg);
				}

				let kb = [];
				locations.forEach((loc) => {
					kb.push([{ text: '📍 ' + loc['name'] }]);
				});

				kb.push([{ text: '$$close' }]);

				let msg = {
					key: 'locations',
					keyboard: kb
				};

				this.chat.send(msg);
			});
		}
		else if (cmd.includes('impostazioni')) {
			let msg = {
				key: 'settings',
				data: {
					radius: this.chat['settings']['radius'],
					magnitude: this.chat['settings']['magnitude'],
					broadcast: this.chat['settings']['broadcast']
				},
				inline: [
					[{ text: '$$settings_menu_radius', callback_data: 'settings;radius' }],
					[{ text: '$$settings_menu_magnitude', callback_data: 'settings;magnitude' }],
					[{ text: '$$settings_menu_broadcast', callback_data: 'settings;broadcast' }]
				]
			};

			this.chat.send(msg);

			botan.log(this.message, 'settings');
		}
		else if (cmd == 'debug') {
			this.chat.send({ text: JSON.stringify(this.chat) });
			this.chat.send({ text: new Date().toString() });
		}
		else {
			// Remove a location
			if (text.startsWith('📍')) {
				let name = text.slice(2).trim();

				db.locations.removeByName(this.chat['id'], name, (err, res) => {
					if (err) {
						this.chat.error();
						logger.error(err, 'locations remove');
						return;
					}

					if (res.n == 0) {
						this.chat.error();
						logger.error(`Couldn\'t remove '${name}' for ${this.chat.id}`);
						return;
					}

					let msg = {
						key: 'location_removed',
						data: { name }
					};

					this.chat.send(msg, () => {
						this._sendMap();
					});
				});
			}
			else {
				let radiusMatch = text.match(/^([0-9]{2,3}) ?(?:km|chilometri|kilometri)?/i);
				// http://regexr.com/3ev1b
				let magMatch = text.match(/^M? ?([0-9]{1}(?:[,\.]{1}[0-9]{1})?)\+?$/i);

				if (radiusMatch) {
					let value = +radiusMatch[1];

					if (value > 300) {
						let msg = { key: 'radius_big' };
						this.chat.send(msg);
						return;
					}

					db.chats.setNotificationRadius(this.chat['id'], value, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setNotificationRadius');
							return;
						}

						this.chat['settings']['radius'] = value;

						let msg = { key: 'radius_ok', data: { value: value } };

						this.chat.send(msg, () => {
							this._sendMap();
						});
					});

					botan.log(this.message, 'radius');
				}
				else if (magMatch) {
					let value = +magMatch[1].replace(',', '.');

					if (value < 2) {
						let msg = { key: 'magnitude_small' };
						this.chat.send(msg);
						return;
					}

					db.chats.setNotificationMagnitude(this.chat['id'], value, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setNotificationMagnitude');
							return;
						}

						let msg = { key: 'magnitude_ok', data: { value: value.toFixed(1) } };
						this.chat.send(msg);
					});

					botan.log(this.message, 'magnitude');
				}
				else {
					let msg = { key: 'dunno' };
					this.chat.send(msg);

					botan.log(this.message, 'unknown');
				}
			}
		}
	}

	_sendMap() {
		logger.info(`Sending locations map to <${this.chat.id}>`);

		db.locations.find(this.chat['id'], (err, docs) => {
			if (err) {
				this.chat.error();
				logger.error(err, 'locations find');
				return;
			}

			if (docs.length == 0) {
				logger.debug(`No locations for map <${this.chat.id}>`);
				return;
			}

			this.chat.send({ type: 'sendChatAction', action: 'upload_photo' });
			stathat.count('terre map');

			let map = new LocationsMap(this.chat['settings']['radius']);

			map.addLocations(docs.map((loc) => loc['point']['coordinates']));

			map.generate((err, fn) => {
				if (err) {
					this.chat.error();
					return;
				}

				let msg = {
					type: 'sendPhoto',
					path: fn
				};

				this.chat.send(msg);
			});
		});
	}
}

module.exports = TelegramProcessor;
<commit_msg>ignore unrecognized messages coming in with webhook<commit_after>const logger         = require('bole')('processor');
const db             = require('../db');
const botan          = require('./botan.js');
const TelegramClient = require('./client.js');
const geocoding      = require('../maps').geocoding;
const LocationsMap   = require('../maps').LocationsMap;
const stathat        = require('../stathat.js');

const tg = new TelegramClient();

class TelegramProcessor {
	constructor(update) {
		this.update = update;
	}

	process() {
		let tgChat;

		// Update type is message
		if (this.update['message']) {
			tgChat = this.update['message']['chat'];
		}
		// Update type is callback query (=> inline button press)
		else if (this.update['callback_query']) {
			tgChat = this.update['callback_query']['message']['chat'];
		}
		// Ignore other types of updates
		else {
			return;
		}

		// Get chat details
		db.chats.get(tgChat, (err, chat) => {
			if (err) {
				logger.error(err, tgChat);
				return;
			}

			chat.send = (msg, callback) => {
				msg['chat'] = chat['id'];
				msg['type'] = msg['type'] || 'sendMessage';
				callback = callback || function() {};

				if (msg['type'] == 'sendMessage') {
					tg.sendMessage(msg, callback);
				}
				else if (msg['type'] == 'editMessage') {
					tg.editMessage(msg, callback);
				}
				else if (msg['type'] == 'sendPhoto') {
					tg.sendPhoto(msg, callback);
				}
				else if (msg['type'] == 'sendChatAction') {
					tg.sendChatAction(msg, callback);
				}
			};

			chat.error = (callback) => {
				let msg = {
					key: 'error',
					chat: chat['id'],
					type: 'sendMessage'
				};

				tg.sendMessage(msg);

				// ?
				callback && callback();
			};

			this.chat = chat;

			if (this.update['message']) {
				this._handleMessage(this.update['message']);
			}
			else if (this.update['callback_query']) {
				this._handleCallback(this.update['callback_query']);
			}
		});
	}

	_handleCallback(query) {
		logger.debug(`Incoming <query> from <${this.chat.id}>`);
		let data = query['data'].split(';');
		let messageId = query['message']['message_id'];

		if (data[0] == 'settings') {
			if (data[1] == 'radius') {
				let msg = {
					key: 'radius',
					keyboard: [
						[{ text: '25 km' }, { text: '50 km' }, { text: '75 km' }],
						[{ text: '100 km' }, { text: '150 km' }, { text: '200 km' }],
						[{ text: '$$close' }]
					]
				};

				this.chat.send(msg);
			}
			else if (data[1] == 'magnitude') {
				let msg = {
					key: 'magnitude',
					keyboard: [
						[{ text: '2.0+' }, { text: '3.0+' }],
						[{ text: '4.0+' }, { text: '5.0+' }],
						[{ text: '$$close' }]
					]
				};

				this.chat.send(msg);
			}
			else if (data[1] == 'broadcast') {
				if (data[2]) {
					// Enable or disable broadcast notifications
					let shouldEnable = !!+data[2]; // 0 becomes false, 1 becomes true. Horrible.

					db.chats.setBroadcast(this.chat['id'], shouldEnable, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setBroadcast error');
							return;
						}

						this.chat['settings']['broadcast'] = shouldEnable;
						this._sendBroadcastSettingMessage(messageId);
					});
				}
				else {
					this._sendBroadcastSettingMessage();
				}
			}
		}
		else if (data[0] == 'event') {
			let eventId = data[1];

			db.history.findById(eventId, (err, event) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'history.findById error');
					return;
				}
				else if (!event) {
					// TODO: answer callback query with error
					return;
				}

				let msg = {
					key: 'details',
					data: event,
					inline: [
						[{ text: '$$details_ingv', url: 'http://cnt.rm.ingv.it/event/' + event['id'] }]
					]
				};

				this.chat.send(msg);
			});
		}
	}

	_sendBroadcastSettingMessage(messageId) {
		let msg = {
			key: 'broadcast',
			inline: [
				[
					{
						text: '$$' + (this.chat['settings']['broadcast'] ? 'broadcast_enabled' : 'broadcast_enable'),
						callback_data: 'settings;broadcast;1'
					},
					{
						text: '$$' + (this.chat['settings']['broadcast'] ? 'broadcast_disable' : 'broadcast_disabled'),
						callback_data: 'settings;broadcast;0'
					}
				]
			]
		};

		if (messageId) {
			msg['type'] = 'editMessage';
			msg['messageId'] = messageId;
		}

		this.chat.send(msg);
	}

	_handleMessage(message) {
		this.message = message;
		message['date'] = new Date(message['date'] * 1000);
		db.incoming.insert(message);

		if (message['location']) {
			this._handleLocation();
		}
		else if (message['venue']) {
			this.message['location'] = this.message['venue']['location'];
			this._handleLocation();
		}
		else if (message['text']) {
			this._handleText();
		}
	}

	_handleLocation() {
		logger.debug(`Incoming <location> from <${this.chat.id}>`);
		botan.log(this.message, 'location');

		let loc = this.message['location'];
		let lat = loc['latitude'];
		let lon = loc['longitude'];

		geocoding.reverse(lat, lon, (err, result) => {
			if (err) {
				if (err['type'] == 'network' || err['type'] == 'response') {
					this.chat.error();
				}
				else if (err['type'] == 'invalid') {
					let msg = { key: 'location_invalid' };
					this.chat.send(msg);
				}
				else if (err['type'] == 'noresults') {
					let msg = { key: 'location_noresults' };
					this.chat.send(msg);
				}

				return;
			}

			let name = result['name'];

			db.locations.exists(this.chat['id'], name, (err, exists) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'locations exists');
					return;
				}

				if (exists) {
					let msg = { key: 'location_exists' };
					this.chat.send(msg);
				}
				else {
					db.locations.insert(this.chat['id'], lat, lon, name, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'locations insert');
							return;
						}

						let msg = {
							key: 'location_ok',
							data: { name }
						};

						this.chat.send(msg, () => {
							this._sendMap();
						});
					});
				}
			});
		});
	}

	_handleText() {
		logger.debug(`Incoming <text> from <${this.chat.id}>`);
		let text = this.message['text'];
		let cmd = text.replace(/\/ /, '').toLowerCase();

		if (cmd.includes('start') || cmd.includes('avvia')) {
			let msg = {
				key: 'start',
				data: {
					name: this.chat['first_name']
				}
			};

			this.chat.send(msg);
		}
		else if (cmd.includes('chiudi') || cmd.includes('annulla')) {
			let msg = { key: 'done' };
			this.chat.send(msg);

			botan.log(this.message, 'cancel');
		}
		else if (cmd.includes('aiuto') || cmd.includes('help')) {
			let msg = { key: 'help' };
			this.chat.send(msg);

			botan.log(this.message, 'help');
		}
		else if (cmd.includes('posizioni')) {
			db.locations.find(this.chat.id, (err, locations) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'locations find');
					return;
				}

				if (locations.length == 0) {
					let msg = { key: 'locations_empty' };
					this.chat.send(msg);
				}

				let kb = [];
				locations.forEach((loc) => {
					kb.push([{ text: '📍 ' + loc['name'] }]);
				});

				kb.push([{ text: '$$close' }]);

				let msg = {
					key: 'locations',
					keyboard: kb
				};

				this.chat.send(msg);
			});
		}
		else if (cmd.includes('impostazioni')) {
			let msg = {
				key: 'settings',
				data: {
					radius: this.chat['settings']['radius'],
					magnitude: this.chat['settings']['magnitude'],
					broadcast: this.chat['settings']['broadcast']
				},
				inline: [
					[{ text: '$$settings_menu_radius', callback_data: 'settings;radius' }],
					[{ text: '$$settings_menu_magnitude', callback_data: 'settings;magnitude' }],
					[{ text: '$$settings_menu_broadcast', callback_data: 'settings;broadcast' }]
				]
			};

			this.chat.send(msg);

			botan.log(this.message, 'settings');
		}
		else if (cmd == 'debug') {
			this.chat.send({ text: JSON.stringify(this.chat) });
			this.chat.send({ text: new Date().toString() });
		}
		else {
			// Remove a location
			if (text.startsWith('📍')) {
				let name = text.slice(2).trim();

				db.locations.removeByName(this.chat['id'], name, (err, res) => {
					if (err) {
						this.chat.error();
						logger.error(err, 'locations remove');
						return;
					}

					if (res.n == 0) {
						this.chat.error();
						logger.error(`Couldn\'t remove '${name}' for ${this.chat.id}`);
						return;
					}

					let msg = {
						key: 'location_removed',
						data: { name }
					};

					this.chat.send(msg, () => {
						this._sendMap();
					});
				});
			}
			else {
				let radiusMatch = text.match(/^([0-9]{2,3}) ?(?:km|chilometri|kilometri)?/i);
				// http://regexr.com/3ev1b
				let magMatch = text.match(/^M? ?([0-9]{1}(?:[,\.]{1}[0-9]{1})?)\+?$/i);

				if (radiusMatch) {
					let value = +radiusMatch[1];

					if (value > 300) {
						let msg = { key: 'radius_big' };
						this.chat.send(msg);
						return;
					}

					db.chats.setNotificationRadius(this.chat['id'], value, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setNotificationRadius');
							return;
						}

						this.chat['settings']['radius'] = value;

						let msg = { key: 'radius_ok', data: { value: value } };

						this.chat.send(msg, () => {
							this._sendMap();
						});
					});

					botan.log(this.message, 'radius');
				}
				else if (magMatch) {
					let value = +magMatch[1].replace(',', '.');

					if (value < 2) {
						let msg = { key: 'magnitude_small' };
						this.chat.send(msg);
						return;
					}

					db.chats.setNotificationMagnitude(this.chat['id'], value, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setNotificationMagnitude');
							return;
						}

						let msg = { key: 'magnitude_ok', data: { value: value.toFixed(1) } };
						this.chat.send(msg);
					});

					botan.log(this.message, 'magnitude');
				}
				else {
					let msg = { key: 'dunno' };
					this.chat.send(msg);

					botan.log(this.message, 'unknown');
				}
			}
		}
	}

	_sendMap() {
		logger.info(`Sending locations map to <${this.chat.id}>`);

		db.locations.find(this.chat['id'], (err, docs) => {
			if (err) {
				this.chat.error();
				logger.error(err, 'locations find');
				return;
			}

			if (docs.length == 0) {
				logger.debug(`No locations for map <${this.chat.id}>`);
				return;
			}

			this.chat.send({ type: 'sendChatAction', action: 'upload_photo' });
			stathat.count('terre map');

			let map = new LocationsMap(this.chat['settings']['radius']);

			map.addLocations(docs.map((loc) => loc['point']['coordinates']));

			map.generate((err, fn) => {
				if (err) {
					this.chat.error();
					return;
				}

				let msg = {
					type: 'sendPhoto',
					path: fn
				};

				this.chat.send(msg);
			});
		});
	}
}

module.exports = TelegramProcessor;
<|endoftext|>


<commit_before>/**
 * MapFactory constructor
 *
 * @class Roguelike.MapFactory
 * @classdesc The object that is responsible for generating rooms and corridors
 *
 * @param {Roguelike.Game} game - Reference to the current game object
 */
Roguelike.MapFactory = function(game) {

	/**
	 * @property {Roguelike.Game} game - Reference to the current game object
	 */
	this.game = game;

	/**
	 * @property {array} possibleDoorLocations - Stores all the positions of possible door locations
	 */
	this.possibleDoorLocations = [];

};

Roguelike.MapFactory.prototype = {

	/**
	 * Generate a random amount of rooms and add them to the room list
	 * @protected
	 */
	generateRooms: function() {

		//Define the map variable
		var map = this.game.map;

		//Maximum number of tries before stopping the placement of more rooms
		var maxTries = this.game.map.maxRooms + 10;
		var tries = 0;

		//Create rooms and add them to the list
		while(map.rooms.length < map.maxRooms) {

			//Check if the limit has been reached, this prevents the while loop from crashing your page
			//We assume there is no space left on the map and break the loop
			if(tries >= maxTries) {
				break;
			}

			//Generate random values ( in tiles )
			//Start with generating a random width
			var w = Roguelike.Utils.randomNumber(
				map.minRoomWidth,
				map.maxRoomWidth
			);

			//Generate a random height for the room
			var h = Roguelike.Utils.randomNumber(
				map.minRoomHeight,
				map.maxRoomHeight
			);

			//Generate a random width horizontal position for the room
			var x = Roguelike.Utils.randomNumber(
				1,
				map.tilesX - w - 1
			);

			//Generate a random vertical position for the room
			var y = Roguelike.Utils.randomNumber(
				1,
				map.tilesY - h - 1
			);

			//Create a new room with these values
			var room = new Roguelike.Room(x, y, w, h);

			//We tried to create a room at a certain position
			tries++;

			//Check if this room intersects with the other rooms, if not, add it to the list
			if(!map.roomIntersectsWith(room)) {

				//The room doesn't intersect, initialize the room layout
				room.initialize();
				room.generateExit();

				//Add the room to the room list
				map.rooms.push(room);

				//Reset tries back to zero, giving the next room equal chances of spawning
				tries = 0;

			}

		}

	},

	/**
	 * Generate corridors based on the room's exits
	 * @protected
	 */
	generateCorridors: function() {

		//Define the map variable
		var map = this.game.map;

		//After all the rooms are placed, go and generate the corridors
		//We start at the second room, so we can connect to the first room
		for(var i = 1; i < map.rooms.length; i++) {

			//Generate a corridor from this position to the previous room's exit
			this.generateCorridor(map.rooms[i], map.rooms[i - 1]);

		}

	},

	/**
	 * Check in which direction the corridor has to be generated
	 * @protected
	 *
	 * @param {object} firstRoom - The room from which we are going to generate a path to the second room
	 * @param {object} secondRoom - This room is going to be the endpoint of this corridor
	 */
	generateCorridor: function(firstRoom, secondRoom) {

		//Exit positions are stored in layout, so upper left is 0,0.
		//We need the map's position, so we'll add the top left coordinates of the room
		var firstExit = {
			x: firstRoom.x1 + firstRoom.exit.x,
			y: firstRoom.y1 + firstRoom.exit.y
		};

		var secondExit = {
			x: secondRoom.x1 + secondRoom.exit.x,
			y: secondRoom.y1 + secondRoom.exit.y
		};

		//Horizontal Corridors
		if((secondExit.x - firstExit.x) > 0) {

			//Corridor going left
			for(var i = secondExit.x; i >= firstExit.x; i--) {
				this.generateHorizontalCorridor(i, secondExit.y);
			}

		}else{

			//Corridor going right
			for(var i = secondExit.x; i <= firstExit.x; i++) {
				this.generateHorizontalCorridor(i, secondExit.y);
			}

		}

		//Vertical Corridors
		if((secondExit.y - firstExit.y) > 0) {

			//If the corridor is going up
			for(var i = secondExit.y; i >= firstExit.y; i--) {
				this.generateVerticalCorridor(firstExit.x, i);
			}

		}else{

			//If the corridor is going down
			for(var i = secondExit.y; i <= firstExit.y; i++) {
				this.generateVerticalCorridor(firstExit.x, i);
			}

		}

	},

	/**
	 * Generate a horizontal corridor tile, and also place doors and walls
	 * @protected
	 *
	 * @param {number} x - The horizontal position of the tile that has to become a corridor
	 * @param {number} y - The vertical position of the tile that has to become a corridor
	 */
	generateHorizontalCorridor: function(x, y) {

		//Define the map variable
		var map = this.game.map;

		//Get the current tile
		var currentTile = map.tiles[y][x];

		//Check the tiles type from the tiles above and below the current tile
		var aboveTile = map.tiles[y + 1][x];
		var belowTile = map.tiles[y - 1][x];

		//If the current tile type is a wall, and the tiles above and below here are also walls
		//this may be a possible door location
		if(currentTile.type === 1 && aboveTile.type === 1 && belowTile.type === 1) {

			//Push the coordinates into the array for later use
			this.possibleDoorLocations.push({x: x, y: y});

		}

		//Set the current tile type to floor
		currentTile.type = 2;
		currentTile.blockLight = false;

		//Generate walls below this hallway
		if(aboveTile.type === 0) {

			aboveTile.type = 1;

		}

		//Generate walls above this hallway
		if(belowTile.type === 0) {

			belowTile.type = 1;

		}

	},

	/**
	 * Generate a vertical corridor tile, and also place walls
	 * @protected
	 *
	 * @param {number} x - The horizontal position of the tile that has to become a corridor
	 * @param {number} y - The vertical position of the tile that has to become a corridor
	 */
	generateVerticalCorridor: function(x, y) {

		//Define the map variable
		var map = this.game.map;

		//Get the current tile
		var currentTile = map.tiles[y][x];

		//Check the tiles type from the tiles above and below the current tile
		var rightTile = map.tiles[y][x + 1];
		var leftTile = map.tiles[y][x - 1];

		//If the current tile type is a wall, and the tiles left and right here are also walls
		//this may be a possible door location
		if(currentTile.type === 1 && rightTile.type === 1 && leftTile.type === 1) {

			//Push the coordinates into the array for later use
			this.possibleDoorLocations.push({x: x, y: y});

		}

		//Set the current tile to floor
		//TODO: Maybe create a set tile to floor/wall/etc function, as this gets ugly really fast
		currentTile.type = 2;
		currentTile.tileRow = 0;
		currentTile.tileNumber = Roguelike.Utils.randomNumber(2, 5);
		currentTile.blockLight = false;

		//Generate walls right from this hallway
		if(rightTile.type === 0) {

			rightTile.type = 1;
			rightTile.tileRow = 0;
			rightTile.tileNumber = 1;

		}

		//Generate walls left from this hallway
		if(leftTile.type === 0) {

			leftTile.type = 1;
			leftTile.tileRow = 0;
			leftTile.tileNumber = 1;

		}

		//The following checks prevent corners ending without being attached to another wall
		//Because we don't know which direction the vertical corridor is being placed, we check
		//upperleft, upperright, bottomleft and bottomright
		//TODO: Find a better solution for these checks

		//Also check for the upperright tile to maybe place a wall
		if(map.tiles[y + 1][x + 1].type === 0) {

			map.tiles[y + 1][x + 1].type = 1;
			map.tiles[y + 1][x + 1].tileRow = 0;
			map.tiles[y + 1][x + 1].tileNumber = 1;

		}

		//Also check for the upperleft tile to maybe place a wall
		if(map.tiles[y - 1][x - 1].type === 0) {

			map.tiles[y - 1][x - 1].type = 1;
			map.tiles[y - 1][x - 1].tileRow = 0;
			map.tiles[y - 1][x - 1].tileNumber = 1;

		}

		//Also check for the bottomright tile to maybe place a wall
		if(map.tiles[y - 1][x + 1].type === 0) {

			map.tiles[y - 1][x + 1].type = 1;
			map.tiles[y - 1][x + 1].tileRow = 0;
			map.tiles[y - 1][x + 1].tileNumber = 1;

		}

		//Also check for the bottomleft tile to maybe place a wall
		if(map.tiles[y + 1][x - 1].type === 0) {

			map.tiles[y + 1][x - 1].type = 1;
			map.tiles[y + 1][x - 1].tileRow = 0;
			map.tiles[y + 1][x - 1].tileNumber = 1;

		}

	},

	/**
	 * Generate some doors and spread them out over the map!
	 * @protected
	 */
	generateDoors: function() {

		//Loop through all possible door locations
		for(var i = 0; i < this.possibleDoorLocations.length; i++) {

			//Store the current location in a local variable
			var doorLocation = this.possibleDoorLocations[i];

			//Get the tile at the location of the possible door location
			var tileLeft = this.game.map.tiles[doorLocation.y][doorLocation.x - 1];
			var tileRight = this.game.map.tiles[doorLocation.y][doorLocation.x + 1];
			var tileUp = this.game.map.tiles[doorLocation.y + 1][doorLocation.x];
			var tileDown = this.game.map.tiles[doorLocation.y - 1][doorLocation.x];

			var randomNumber = Roguelike.Utils.randomNumber(0, 100);

			//If the tiles left and right are walls and the tiles above and below are floors
			if(tileLeft.type === 1 && tileRight.type === 1 && tileUp.entities.length === 0 && tileDown.entities.length === 0 && tileUp.type === 2 && tileDown.type === 2 && randomNumber > 80) {

				//Place a door at this location
				this.placeDoor(doorLocation);

				//If the tiles left and right are floors and the tiles above and below are walls
			}else if(tileLeft.type === 2 && tileRight.type === 2 && tileLeft.entities.length === 0 && tileRight.entities.length === 0 && tileUp.type === 1 && tileDown.type === 1 && randomNumber > 60) {

				//Place a door at this location
				this.placeDoor(doorLocation);

			}

		}

	},

	/**
	 * Place the entrance and exit objects on the map
	 * @protected
	 */
	placeDoor: function(position) {

		//Get the tile at the door's position
		var tileAtPosition = this.game.map.tiles[position.y][position.x];

		//Create the door entity
		var doorEntity = Roguelike.PropFactory.newDoor({x: position.x, y: position.y});

		//Add the entity to the map
		this.game.map.entities.addEntity(doorEntity);

		//Add the door entity to the entities list on the current tile
		tileAtPosition.entities.push(doorEntity);

		//A door blocks light!
		tileAtPosition.blockLight = true;

	},

	/**
	 * Place the entrance and exit objects on the map
	 * @protected
	 */
	placeEntranceExitObjects: function() {

		//Generate a random number between 0 and the number of rooms - 2
		//Minus two because then we can select the last room in the list as an exit room
		var entranceRoomIndex = Roguelike.Utils.randomNumber(0, this.game.map.rooms.length - 2);
		var exitRoomIndex = this.game.map.rooms.length - 1;

		//Get the rooms from the room list
		var entranceRoom = this.game.map.rooms[entranceRoomIndex];
		var exitRoom = this.game.map.rooms[exitRoomIndex];

		//Let the rooms return a random tile
		var entrancePosition = entranceRoom.getRandomPosition();
		var exitPosition = exitRoom.getRandomPosition();

		//Store the entrance and exit positions in the map for later use
		//For example, player spawning
		this.game.map.entrance = entrancePosition;
		this.game.map.exit = exitPosition;

		//Create the entrance entity
		var entranceEntity = new Roguelike.Entity();

		//Add components to the entrance entity
		entranceEntity.addComponent(new Roguelike.Components.Position(entrancePosition.x, entrancePosition.y));
		entranceEntity.addComponent(new Roguelike.Components.Sprite("#BD2D2D"));

		//Create the entrance entity
		var exitEntity = new Roguelike.Entity();

		//Add components to the exit entity
		exitEntity.addComponent(new Roguelike.Components.Position(exitPosition.x, exitPosition.y));
		exitEntity.addComponent(new Roguelike.Components.Sprite("#BD2D2D"));

		//Add this entity to the map
		this.game.map.entities.addEntity(entranceEntity);
		this.game.map.entities.addEntity(exitEntity);

	},

	/**
	 * Changes a tile to a floor
	 * @protected
	 */
	changeTileToFloor: function(tile) {

	},

	/**
	 * Changes a tile to a wall
	 * @protected
	 */
	changeTileToWall: function(tile) {

	}

}

};
<commit_msg>Improved the map factory by using the new change  tile functions.<commit_after>/**
 * MapFactory constructor
 *
 * @class Roguelike.MapFactory
 * @classdesc The object that is responsible for generating rooms and corridors
 *
 * @param {Roguelike.Game} game - Reference to the current game object
 */
Roguelike.MapFactory = function(game) {

	/**
	 * @property {Roguelike.Game} game - Reference to the current game object
	 */
	this.game = game;

	/**
	 * @property {array} possibleDoorLocations - Stores all the positions of possible door locations
	 */
	this.possibleDoorLocations = [];

};

Roguelike.MapFactory.prototype = {

	/**
	 * Generate a random amount of rooms and add them to the room list
	 * @protected
	 */
	generateRooms: function() {

		//Define the map variable
		var map = this.game.map;

		//Maximum number of tries before stopping the placement of more rooms
		var maxTries = this.game.map.maxRooms + 10;
		var tries = 0;

		//Create rooms and add them to the list
		while(map.rooms.length < map.maxRooms) {

			//Check if the limit has been reached, this prevents the while loop from crashing your page
			//We assume there is no space left on the map and break the loop
			if(tries >= maxTries) {
				break;
			}

			//Generate random values ( in tiles )
			//Start with generating a random width
			var w = Roguelike.Utils.randomNumber(
				map.minRoomWidth,
				map.maxRoomWidth
			);

			//Generate a random height for the room
			var h = Roguelike.Utils.randomNumber(
				map.minRoomHeight,
				map.maxRoomHeight
			);

			//Generate a random width horizontal position for the room
			var x = Roguelike.Utils.randomNumber(
				1,
				map.tilesX - w - 1
			);

			//Generate a random vertical position for the room
			var y = Roguelike.Utils.randomNumber(
				1,
				map.tilesY - h - 1
			);

			//Create a new room with these values
			var room = new Roguelike.Room(x, y, w, h);

			//We tried to create a room at a certain position
			tries++;

			//Check if this room intersects with the other rooms, if not, add it to the list
			if(!map.roomIntersectsWith(room)) {

				//The room doesn't intersect, initialize the room layout
				room.initialize();
				room.generateExit();

				//Add the room to the room list
				map.rooms.push(room);

				//Reset tries back to zero, giving the next room equal chances of spawning
				tries = 0;

			}

		}

	},

	/**
	 * Generate corridors based on the room's exits
	 * @protected
	 */
	generateCorridors: function() {

		//Define the map variable
		var map = this.game.map;

		//After all the rooms are placed, go and generate the corridors
		//We start at the second room, so we can connect to the first room
		for(var i = 1; i < map.rooms.length; i++) {

			//Generate a corridor from this position to the previous room's exit
			this.generateCorridor(map.rooms[i], map.rooms[i - 1]);

		}

	},

	/**
	 * Check in which direction the corridor has to be generated
	 * @protected
	 *
	 * @param {object} firstRoom - The room from which we are going to generate a path to the second room
	 * @param {object} secondRoom - This room is going to be the endpoint of this corridor
	 */
	generateCorridor: function(firstRoom, secondRoom) {

		//Exit positions are stored in layout, so upper left is 0,0.
		//We need the map's position, so we'll add the top left coordinates of the room
		var firstExit = {
			x: firstRoom.x1 + firstRoom.exit.x,
			y: firstRoom.y1 + firstRoom.exit.y
		};

		var secondExit = {
			x: secondRoom.x1 + secondRoom.exit.x,
			y: secondRoom.y1 + secondRoom.exit.y
		};

		//Horizontal Corridors
		if((secondExit.x - firstExit.x) > 0) {

			//Corridor going left
			for(var i = secondExit.x; i >= firstExit.x; i--) {
				this.generateHorizontalCorridor(i, secondExit.y);
			}

		}else{

			//Corridor going right
			for(var i = secondExit.x; i <= firstExit.x; i++) {
				this.generateHorizontalCorridor(i, secondExit.y);
			}

		}

		//Vertical Corridors
		if((secondExit.y - firstExit.y) > 0) {

			//If the corridor is going up
			for(var i = secondExit.y; i >= firstExit.y; i--) {
				this.generateVerticalCorridor(firstExit.x, i);
			}

		}else{

			//If the corridor is going down
			for(var i = secondExit.y; i <= firstExit.y; i++) {
				this.generateVerticalCorridor(firstExit.x, i);
			}

		}

	},

	/**
	 * Generate a horizontal corridor tile, and also place doors and walls
	 * @protected
	 *
	 * @param {number} x - The horizontal position of the tile that has to become a corridor
	 * @param {number} y - The vertical position of the tile that has to become a corridor
	 */
	generateHorizontalCorridor: function(x, y) {

		//Define the map variable
		var map = this.game.map;

		//Get the current tile
		var currentTile = map.tiles[y][x];

		//Check the tiles type from the tiles above and below the current tile
		var aboveTile = map.tiles[y + 1][x];
		var belowTile = map.tiles[y - 1][x];

		//If the current tile type is a wall, and the tiles above and below here are also walls
		//this may be a possible door location
		if(currentTile.type === 1 && aboveTile.type === 1 && belowTile.type === 1) {

			//Push the coordinates into the array for later use
			this.possibleDoorLocations.push({x: x, y: y});

		}

		//Set the current tile to floor
		this.changeTileToFloor(currentTile);

		//Generate walls below this hallway
		if(aboveTile.type === 0) {

			//Set the current tile to wall
			this.changeTileToWall(aboveTile);

		}

		//Generate walls above this hallway
		if(belowTile.type === 0) {

			//Set the current tile to wall
			this.changeTileToWall(belowTile);

		}

	},

	/**
	 * Generate a vertical corridor tile, and also place walls
	 * @protected
	 *
	 * @param {number} x - The horizontal position of the tile that has to become a corridor
	 * @param {number} y - The vertical position of the tile that has to become a corridor
	 */
	generateVerticalCorridor: function(x, y) {

		//Define the map variable
		var map = this.game.map;

		//Get the current tile
		var currentTile = map.tiles[y][x];

		//Check the tiles type from the tiles above and below the current tile
		var rightTile = map.tiles[y][x + 1];
		var leftTile = map.tiles[y][x - 1];

		//If the current tile type is a wall, and the tiles left and right here are also walls
		//this may be a possible door location
		if(currentTile.type === 1 && rightTile.type === 1 && leftTile.type === 1) {

			//Push the coordinates into the array for later use
			this.possibleDoorLocations.push({x: x, y: y});

		}

		//Set the current tile to floor
		this.changeTileToFloor(currentTile);

		//The following checks prevent corners ending without being attached to another wall
		//Because we don't know which direction the vertical corridor is being placed, we check
		//upperleft, upperright, bottomleft and bottomright
		//TODO: Find a better solution for these checks

		//Also check for the upperright tile to maybe place a wall
		if(map.tiles[y + 1][x + 1].type === 0) {

			//Set the current tile to wall
			this.changeTileToWall(map.tiles[y + 1][x + 1]);

		}

		//Also check for the upperleft tile to maybe place a wall
		if(map.tiles[y - 1][x - 1].type === 0) {

			//Set the current tile to wall
			this.changeTileToWall(map.tiles[y - 1][x - 1]);

		}

		//Also check for the bottomright tile to maybe place a wall
		if(map.tiles[y - 1][x + 1].type === 0) {

			//Set the current tile to wall
			this.changeTileToWall(map.tiles[y - 1][x + 1]);

		}

		//Also check for the bottomleft tile to maybe place a wall
		if(map.tiles[y + 1][x - 1].type === 0) {

			//Set the current tile to wall
			this.changeTileToWall(map.tiles[y + 1][x - 1]);

		}

	},

	/**
	 * Generate some doors and spread them out over the map!
	 * @protected
	 */
	generateDoors: function() {

		//Loop through all possible door locations
		for(var i = 0; i < this.possibleDoorLocations.length; i++) {

			//Store the current location in a local variable
			var doorLocation = this.possibleDoorLocations[i];

			//Get the tile at the location of the possible door location
			var tileLeft = this.game.map.tiles[doorLocation.y][doorLocation.x - 1];
			var tileRight = this.game.map.tiles[doorLocation.y][doorLocation.x + 1];
			var tileUp = this.game.map.tiles[doorLocation.y + 1][doorLocation.x];
			var tileDown = this.game.map.tiles[doorLocation.y - 1][doorLocation.x];

			var randomNumber = Roguelike.Utils.randomNumber(0, 100);

			//If the tiles left and right are walls and the tiles above and below are floors
			if(tileLeft.type === 1 && tileRight.type === 1 && tileUp.entities.length === 0 && tileDown.entities.length === 0 && tileUp.type === 2 && tileDown.type === 2 && randomNumber > 80) {

				//Place a door at this location
				this.placeDoor(doorLocation);

				//If the tiles left and right are floors and the tiles above and below are walls
			}else if(tileLeft.type === 2 && tileRight.type === 2 && tileLeft.entities.length === 0 && tileRight.entities.length === 0 && tileUp.type === 1 && tileDown.type === 1 && randomNumber > 60) {

				//Place a door at this location
				this.placeDoor(doorLocation);

			}

		}

	},

	/**
	 * Place the entrance and exit objects on the map
	 * @protected
	 */
	placeDoor: function(position) {

		//Get the tile at the door's position
		var tileAtPosition = this.game.map.tiles[position.y][position.x];

		//Create the door entity
		var doorEntity = Roguelike.PropFactory.newDoor({x: position.x, y: position.y});

		//Add the entity to the map
		this.game.map.entities.addEntity(doorEntity);

		//Add the door entity to the entities list on the current tile
		tileAtPosition.entities.push(doorEntity);

		//A door blocks light!
		tileAtPosition.blockLight = true;

	},

	/**
	 * Place the entrance and exit objects on the map
	 * @protected
	 */
	placeEntranceExitObjects: function() {

		//Generate a random number between 0 and the number of rooms - 2
		//Minus two because then we can select the last room in the list as an exit room
		var entranceRoomIndex = Roguelike.Utils.randomNumber(0, this.game.map.rooms.length - 2);
		var exitRoomIndex = this.game.map.rooms.length - 1;

		//Get the rooms from the room list
		var entranceRoom = this.game.map.rooms[entranceRoomIndex];
		var exitRoom = this.game.map.rooms[exitRoomIndex];

		//Let the rooms return a random tile
		var entrancePosition = entranceRoom.getRandomPosition();
		var exitPosition = exitRoom.getRandomPosition();

		//Store the entrance and exit positions in the map for later use
		//For example, player spawning
		this.game.map.entrance = entrancePosition;
		this.game.map.exit = exitPosition;

		//Create the entrance entity
		var entranceEntity = new Roguelike.Entity();

		//Add components to the entrance entity
		entranceEntity.addComponent(new Roguelike.Components.Position(entrancePosition.x, entrancePosition.y));
		entranceEntity.addComponent(new Roguelike.Components.Sprite("#BD2D2D"));

		//Create the entrance entity
		var exitEntity = new Roguelike.Entity();

		//Add components to the exit entity
		exitEntity.addComponent(new Roguelike.Components.Position(exitPosition.x, exitPosition.y));
		exitEntity.addComponent(new Roguelike.Components.Sprite("#BD2D2D"));

		//Add this entity to the map
		this.game.map.entities.addEntity(entranceEntity);
		this.game.map.entities.addEntity(exitEntity);

	},

	/**
	 * Changes a tile to a floor
	 * @protected
	 */
	changeTileToFloor: function(tile) {

		tile.type = 2;
		tile.tileRow = 0;
		tile.tileNumber = Roguelike.Utils.randomNumber(2, 5);
		tile.blockLight = false;

	},

	/**
	 * Changes a tile to a wall
	 * @protected
	 */
	changeTileToWall: function(tile) {

		tile.type = 1;
		tile.tileRow = 0;
		tile.tileNumber = 1;
		tile.blockLight = true;

	},

	placeGrass: function(){

	}

};
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var Transport = require('../transport');
var util = require('../util');
var parseqs = require('parseqs');
var parser = require('engine.io-parser');
var debug = require('debug')('engine.io-client:polling');
var inherit = require('inherit');

/**
 * Module exports.
 */

module.exports = Polling;

/**
 * Is XHR2 supported?
 */

var hasXHR2 = (function() {
  var XMLHttpRequest = require('xmlhttprequest');
  var xhr = new XMLHttpRequest({ agent: this.agent, xdomain: false });
  return null != xhr.responseType;
})();

/**
 * Polling interface.
 *
 * @param {Object} opts
 * @api private
 */

function Polling(opts){
  var forceBase64 = (opts && opts.forceBase64);
  if (!hasXHR2 || forceBase64) {
    this.supportsBinary = false;
  }
  Transport.call(this, opts);
}

/**
 * Inherits from Transport.
 */

inherit(Polling, Transport);

/**
 * Transport name.
 */

Polling.prototype.name = 'polling';

/**
 * Opens the socket (triggers polling). We write a PING message to determine
 * when the transport is open.
 *
 * @api private
 */

Polling.prototype.doOpen = function(){
  this.poll();
};

/**
 * Pauses polling.
 *
 * @param {Function} callback upon buffers are flushed and transport is paused
 * @api private
 */

Polling.prototype.pause = function(onPause){
  var pending = 0;
  var self = this;

  this.readyState = 'pausing';

  function pause(){
    debug('paused');
    self.readyState = 'paused';
    onPause();
  }

  if (this.polling || !this.writable) {
    var total = 0;

    if (this.polling) {
      debug('we are currently polling - waiting to pause');
      total++;
      this.once('pollComplete', function(){
        debug('pre-pause polling complete');
        --total || pause();
      });
    }

    if (!this.writable) {
      debug('we are currently writing - waiting to pause');
      total++;
      this.once('drain', function(){
        debug('pre-pause writing complete');
        --total || pause();
      });
    }
  } else {
    pause();
  }
};

/**
 * Starts polling cycle.
 *
 * @api public
 */

Polling.prototype.poll = function(){
  debug('polling');
  this.polling = true;
  this.doPoll();
  this.emit('poll');
};

/**
 * Overloads onData to detect payloads.
 *
 * @api private
 */

Polling.prototype.onData = function(data){
  var self = this;
  debug('polling got data %s', data);
  var callback = function(packet, index, total) {
    // if its the first message we consider the transport open
    if ('opening' == self.readyState) {
      self.onOpen();
    }

    // if its a close packet, we close the ongoing requests
    if ('close' == packet.type) {
      self.onClose();
      return false;
    }

    // otherwise bypass onData and handle the message
    self.onPacket(packet);
  };

  // decode payload
  parser.decodePayload(data, this.socket.binaryType, callback);

  // if an event did not trigger closing
  if ('closed' != this.readyState) {
    // if we got data we're not polling
    this.polling = false;
    this.emit('pollComplete');

    if ('open' == this.readyState) {
      this.poll();
    } else {
      debug('ignoring poll - transport state "%s"', this.readyState);
    }
  }
};

/**
 * For polling, send a close packet.
 *
 * @api private
 */

Polling.prototype.doClose = function(){
  var self = this;

  function close(){
    debug('writing close packet');
    self.write([{ type: 'close' }]);
  }

  if ('open' == this.readyState) {
    debug('transport open - closing');
    close();
  } else {
    // in case we're trying to close while
    // handshaking is in progress (GH-164)
    debug('transport not open - deferring close');
    this.once('open', close);
  }
};

/**
 * Writes a packets payload.
 *
 * @param {Array} data packets
 * @param {Function} drain callback
 * @api private
 */

Polling.prototype.write = function(packets){
  var self = this;
  this.writable = false;
  var callbackfn = function() {
    self.writable = true;
    self.emit('drain');
  };

  var self = this;
  parser.encodePayload(packets, this.supportsBinary, function(data) {
    self.doWrite(data, callbackfn);
  });
};

/**
 * Generates uri for connection.
 *
 * @api private
 */

Polling.prototype.uri = function(){
  var query = this.query || {};
  var schema = this.secure ? 'https' : 'http';
  var port = '';

  var ua = {};
  ua.chromeframe = Boolean(global.externalHost);
  ua.ios6 = exports.ua.ios && /OS 6_/.test(navigator.userAgent);
  ua.android = 'undefined' != typeof navigator && /android/i.test(navigator.userAgent);

  // cache busting is forced
  if (false !== this.timestampRequests) {
    query[this.timestampParam] = +new Date + '-' + Transport.timestamps++;
  }

  if (!this.supportsBinary && !query.sid) {
    query.b64 = 1;
  }

  query = parseqs.encode(query);

  // avoid port if default for schema
  if (this.port && (('https' == schema && this.port != 443) ||
     ('http' == schema && this.port != 80))) {
    port = ':' + this.port;
  }

  // prepend ? to query
  if (query.length) {
    query = '?' + query;
  }

  return schema + '://' + this.hostname + port + this.path + query;
};
<commit_msg>removed unnecessary UA detection<commit_after>/**
 * Module dependencies.
 */

var Transport = require('../transport');
var util = require('../util');
var parseqs = require('parseqs');
var parser = require('engine.io-parser');
var debug = require('debug')('engine.io-client:polling');
var inherit = require('inherit');

/**
 * Module exports.
 */

module.exports = Polling;

/**
 * Is XHR2 supported?
 */

var hasXHR2 = (function() {
  var XMLHttpRequest = require('xmlhttprequest');
  var xhr = new XMLHttpRequest({ agent: this.agent, xdomain: false });
  return null != xhr.responseType;
})();

/**
 * Polling interface.
 *
 * @param {Object} opts
 * @api private
 */

function Polling(opts){
  var forceBase64 = (opts && opts.forceBase64);
  if (!hasXHR2 || forceBase64) {
    this.supportsBinary = false;
  }
  Transport.call(this, opts);
}

/**
 * Inherits from Transport.
 */

inherit(Polling, Transport);

/**
 * Transport name.
 */

Polling.prototype.name = 'polling';

/**
 * Opens the socket (triggers polling). We write a PING message to determine
 * when the transport is open.
 *
 * @api private
 */

Polling.prototype.doOpen = function(){
  this.poll();
};

/**
 * Pauses polling.
 *
 * @param {Function} callback upon buffers are flushed and transport is paused
 * @api private
 */

Polling.prototype.pause = function(onPause){
  var pending = 0;
  var self = this;

  this.readyState = 'pausing';

  function pause(){
    debug('paused');
    self.readyState = 'paused';
    onPause();
  }

  if (this.polling || !this.writable) {
    var total = 0;

    if (this.polling) {
      debug('we are currently polling - waiting to pause');
      total++;
      this.once('pollComplete', function(){
        debug('pre-pause polling complete');
        --total || pause();
      });
    }

    if (!this.writable) {
      debug('we are currently writing - waiting to pause');
      total++;
      this.once('drain', function(){
        debug('pre-pause writing complete');
        --total || pause();
      });
    }
  } else {
    pause();
  }
};

/**
 * Starts polling cycle.
 *
 * @api public
 */

Polling.prototype.poll = function(){
  debug('polling');
  this.polling = true;
  this.doPoll();
  this.emit('poll');
};

/**
 * Overloads onData to detect payloads.
 *
 * @api private
 */

Polling.prototype.onData = function(data){
  var self = this;
  debug('polling got data %s', data);
  var callback = function(packet, index, total) {
    // if its the first message we consider the transport open
    if ('opening' == self.readyState) {
      self.onOpen();
    }

    // if its a close packet, we close the ongoing requests
    if ('close' == packet.type) {
      self.onClose();
      return false;
    }

    // otherwise bypass onData and handle the message
    self.onPacket(packet);
  };

  // decode payload
  parser.decodePayload(data, this.socket.binaryType, callback);

  // if an event did not trigger closing
  if ('closed' != this.readyState) {
    // if we got data we're not polling
    this.polling = false;
    this.emit('pollComplete');

    if ('open' == this.readyState) {
      this.poll();
    } else {
      debug('ignoring poll - transport state "%s"', this.readyState);
    }
  }
};

/**
 * For polling, send a close packet.
 *
 * @api private
 */

Polling.prototype.doClose = function(){
  var self = this;

  function close(){
    debug('writing close packet');
    self.write([{ type: 'close' }]);
  }

  if ('open' == this.readyState) {
    debug('transport open - closing');
    close();
  } else {
    // in case we're trying to close while
    // handshaking is in progress (GH-164)
    debug('transport not open - deferring close');
    this.once('open', close);
  }
};

/**
 * Writes a packets payload.
 *
 * @param {Array} data packets
 * @param {Function} drain callback
 * @api private
 */

Polling.prototype.write = function(packets){
  var self = this;
  this.writable = false;
  var callbackfn = function() {
    self.writable = true;
    self.emit('drain');
  };

  var self = this;
  parser.encodePayload(packets, this.supportsBinary, function(data) {
    self.doWrite(data, callbackfn);
  });
};

/**
 * Generates uri for connection.
 *
 * @api private
 */

Polling.prototype.uri = function(){
  var query = this.query || {};
  var schema = this.secure ? 'https' : 'http';
  var port = '';

  // cache busting is forced
  if (false !== this.timestampRequests) {
    query[this.timestampParam] = +new Date + '-' + Transport.timestamps++;
  }

  if (!this.supportsBinary && !query.sid) {
    query.b64 = 1;
  }

  query = parseqs.encode(query);

  // avoid port if default for schema
  if (this.port && (('https' == schema && this.port != 443) ||
     ('http' == schema && this.port != 80))) {
    port = ':' + this.port;
  }

  // prepend ? to query
  if (query.length) {
    query = '?' + query;
  }

  return schema + '://' + this.hostname + port + this.path + query;
};
<|endoftext|>


<commit_before>'use babel'

const NewLine = /\r?\n/

class Message extends HTMLElement {
  initialize(message, scope) {
    this.message = message
    this.updateVisibility(scope)
    return this
  }
  updateVisibility(scope) {

    if (scope !== 'Line' && scope == this.scope) return ; // Nothing's changed

    let status = true
    if (scope === 'Line')
      status = this.message.currentLine
    else if (scope === 'File')
      status = this.message.currentFile

    if (this.children.length && this.message.filePath)
      if (scope === 'Project')
        this.children[this.children.length - 1].children[0].removeAttribute('hidden')
      else this.children[this.children.length - 1].children[0].setAttribute('hidden', true)

    if (status)
      this.removeAttribute('hidden')
    else
      this.setAttribute('hidden', true)
    this.scope = scope
  }
  attachedCallback() {
    this.appendChild(Message.getRibbon(this.message))
    this.appendChild(Message.getMessage(this.message))

    if (this.message.filePath) {
      this.appendChild(Message.getLink(this.message))
    }
  }
  static getLink(message) {
    const el = document.createElement('a')
    const pathEl = document.createElement('span')
    let displayFile = message.filePath

    el.className = 'linter-message-item'

    for (let path of atom.project.getPaths())
      if (displayFile.indexOf(path) === 0) {
        displayFile = displayFile.substr(path.length + 1) // Path + Path Separator
        break
      }

    if (message.range) {
      el.textContent = `at line ${message.range.start.row + 1} col ${message.range.start.column + 1}`
    }
    pathEl.textContent = ' in ' + displayFile
    el.appendChild(pathEl)
    el.addEventListener('click', function(){
      atom.workspace.open(message.filePath).then(function(){
        if (message.range) {
          atom.workspace.getActiveTextEditor().setCursorBufferPosition(message.range.start)
        }
      })
    })
    return el
  }
  static getMessage(message) {
    const el = document.createElement('span')
    el.className = 'linter-message-item'
    if (message.html && typeof message.html !== 'string') {
      el.appendChild(message.html.cloneNode(true))
    } else if (
      (message.html && message.html.match(NewLine)) ||
      (message.text && message.text.match(NewLine))
    ) {
      return Message.getMultiLineMessage(message.html || message.text)
    } else {
      if (message.html) {
        el.innerHTML = message.html
      } else if (message.text) {
        el.textContent = message.text
      }
    }
    return el
  }
  static getMultiLineMessage(message) {
    const container = document.createElement('linter-multiline-message')
    for (let line of message.split(NewLine)) {
      if (!line) continue
      const el = document.createElement('linter-message-line')
      el.textContent = line
      container.appendChild(el)
    }
    // TODO: Make expanding of multi-line-messages work
    return container
  }
  static getRibbon(message) {
    const el = document.createElement('span')
    el.className = `linter-message-item badge badge-flexible linter-highlight ${message.class}`
    el.textContent = message.type
    return el
  }
}

const MessageElement = document.registerElement('linter-message', {
  prototype: Message.prototype
})

export default function(message, scope) {
  return new MessageElement().initialize(message, scope)
}
<commit_msg>:bug: [message-element] Respect multiline field on message<commit_after>'use babel'

const NewLine = /\r?\n/

class Message extends HTMLElement {
  initialize(message, scope) {
    this.message = message
    this.updateVisibility(scope)
    return this
  }
  updateVisibility(scope) {

    if (scope !== 'Line' && scope == this.scope) return ; // Nothing's changed

    let status = true
    if (scope === 'Line')
      status = this.message.currentLine
    else if (scope === 'File')
      status = this.message.currentFile

    if (this.children.length && this.message.filePath)
      if (scope === 'Project')
        this.children[this.children.length - 1].children[0].removeAttribute('hidden')
      else this.children[this.children.length - 1].children[0].setAttribute('hidden', true)

    if (status)
      this.removeAttribute('hidden')
    else
      this.setAttribute('hidden', true)
    this.scope = scope
  }
  attachedCallback() {
    this.appendChild(Message.getRibbon(this.message))
    this.appendChild(Message.getMessage(this.message))

    if (this.message.filePath) {
      this.appendChild(Message.getLink(this.message))
    }
  }
  static getLink(message) {
    const el = document.createElement('a')
    const pathEl = document.createElement('span')
    let displayFile = message.filePath

    el.className = 'linter-message-item'

    for (let path of atom.project.getPaths())
      if (displayFile.indexOf(path) === 0) {
        displayFile = displayFile.substr(path.length + 1) // Path + Path Separator
        break
      }

    if (message.range) {
      el.textContent = `at line ${message.range.start.row + 1} col ${message.range.start.column + 1}`
    }
    pathEl.textContent = ' in ' + displayFile
    el.appendChild(pathEl)
    el.addEventListener('click', function(){
      atom.workspace.open(message.filePath).then(function(){
        if (message.range) {
          atom.workspace.getActiveTextEditor().setCursorBufferPosition(message.range.start)
        }
      })
    })
    return el
  }
  static getMessage(message) {
    const el = document.createElement('span')
    el.className = 'linter-message-item'
    if (message.html && typeof message.html !== 'string') {
      el.appendChild(message.html.cloneNode(true))
    } else if (
      message.multiline ||
      (message.html && message.html.match(NewLine)) ||
      (message.text && message.text.match(NewLine))
    ) {
      return Message.getMultiLineMessage(message.html || message.text)
    } else {
      if (message.html) {
        el.innerHTML = message.html
      } else if (message.text) {
        el.textContent = message.text
      }
    }
    return el
  }
  static getMultiLineMessage(message) {
    const container = document.createElement('linter-multiline-message')
    for (let line of message.split(NewLine)) {
      if (!line) continue
      const el = document.createElement('linter-message-line')
      el.textContent = line
      container.appendChild(el)
    }
    return container
  }
  static getRibbon(message) {
    const el = document.createElement('span')
    el.className = `linter-message-item badge badge-flexible linter-highlight ${message.class}`
    el.textContent = message.type
    return el
  }
}

const MessageElement = document.registerElement('linter-message', {
  prototype: Message.prototype
})

export default function(message, scope) {
  return new MessageElement().initialize(message, scope)
}
<|endoftext|>


<commit_before>const minimatch = require('minimatch');

const versioning = require('../versioning');
const { mergeChildConfig } = require('../config');

module.exports = {
  applyPackageRules,
};

function applyPackageRules(inputConfig) {
  let config = { ...inputConfig };
  const {
    versionScheme,
    packageFile,
    depType,
    depTypes,
    depName,
    currentValue,
    fromVersion,
    updateType,
    isBump,
    sourceUrl,
    language,
    manager,
  } = config;
  const packageRules = config.packageRules || [];
  logger.trace(
    { dependency: depName, packageRules },
    `Checking against ${packageRules.length} packageRules`
  );
  packageRules.forEach(packageRule => {
    let {
      paths,
      languages,
      managers,
      depTypeList,
      packageNames,
      packagePatterns,
      excludePackageNames,
      excludePackagePatterns,
      matchCurrentVersion,
      sourceUrlPrefixes,
      updateTypes,
    } = packageRule;
    // Setting empty arrays simplifies our logic later
    paths = paths || [];
    languages = languages || [];
    managers = managers || [];
    depTypeList = depTypeList || [];
    packageNames = packageNames || [];
    packagePatterns = packagePatterns || [];
    excludePackageNames = excludePackageNames || [];
    excludePackagePatterns = excludePackagePatterns || [];
    sourceUrlPrefixes = sourceUrlPrefixes || [];
    matchCurrentVersion = matchCurrentVersion || null;
    updateTypes = updateTypes || [];
    let positiveMatch = false;
    let negativeMatch = false;
    // Massage a positive patterns patch if an exclude one is present
    if (
      (excludePackageNames.length || excludePackagePatterns.length) &&
      !(packageNames.length || packagePatterns.length)
    ) {
      packagePatterns = ['.*'];
    }
    if (paths.length) {
      const isMatch = paths.some(
        rulePath =>
          packageFile.includes(rulePath) || minimatch(packageFile, rulePath)
      );
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (depTypeList.length) {
      const isMatch =
        depTypeList.includes(depType) ||
        (depTypes && depTypes.some(dt => depTypeList.includes(dt)));
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (languages.length) {
      const isMatch = languages.includes(language);
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (managers.length) {
      const isMatch = managers.includes(manager);
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (updateTypes.length) {
      const isMatch =
        updateTypes.includes(updateType) ||
        (isBump && updateTypes.includes('bump'));
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (packageNames.length || packagePatterns.length) {
      let isMatch = packageNames.includes(depName);
      // name match is "or" so we check patterns if we didn't match names
      if (!isMatch) {
        for (const packagePattern of packagePatterns) {
          const packageRegex = new RegExp(
            packagePattern === '^*$' || packagePattern === '*'
              ? '.*'
              : packagePattern
          );
          if (depName && depName.match(packageRegex)) {
            logger.trace(`${depName} matches against ${packageRegex}`);
            isMatch = true;
          }
        }
      }
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (excludePackageNames.length) {
      const isMatch = excludePackageNames.includes(depName);
      negativeMatch = negativeMatch || isMatch;
      positiveMatch = positiveMatch || !isMatch;
    }
    if (excludePackagePatterns.length) {
      let isMatch = false;
      for (const pattern of excludePackagePatterns) {
        const packageRegex = new RegExp(
          pattern === '^*$' || pattern === '*' ? '.*' : pattern
        );
        if (depName && depName.match(packageRegex)) {
          logger.trace(`${depName} matches against ${packageRegex}`);
          isMatch = true;
        }
      }
      negativeMatch = negativeMatch || isMatch;
      positiveMatch = positiveMatch || !isMatch;
    }
    if (sourceUrlPrefixes.length) {
      const isMatch = sourceUrlPrefixes.some(
        prefix => sourceUrl && sourceUrl.startsWith(prefix)
      );
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (matchCurrentVersion) {
      const { matches, isVersion } = versioning.get(versionScheme);
      const compareVersion = isVersion(currentValue)
        ? currentValue // it's a version so we can match against it
        : fromVersion; // need to match against this fromVersion, if available
      if (compareVersion) {
        const isMatch = matches(compareVersion, matchCurrentVersion);
        positiveMatch = positiveMatch || isMatch;
        negativeMatch = negativeMatch || !isMatch;
      } else {
        negativeMatch = true;
      }
    }
    // This rule is considered matched if there was at least one positive match and no negative matches
    if (positiveMatch && !negativeMatch) {
      // Package rule config overrides any existing config
      config = mergeChildConfig(config, packageRule);
      delete config.packageNames;
      delete config.packagePatterns;
      delete config.excludePackageNames;
      delete config.excludePackagePatterns;
      delete config.depTypeList;
      delete config.matchCurrentVersion;
    }
  });
  return config;
}
<commit_msg>fix(package-rules): check lockedVersion against matchCurrentVersion<commit_after>const minimatch = require('minimatch');

const versioning = require('../versioning');
const { mergeChildConfig } = require('../config');

module.exports = {
  applyPackageRules,
};

function applyPackageRules(inputConfig) {
  let config = { ...inputConfig };
  const {
    versionScheme,
    packageFile,
    depType,
    depTypes,
    depName,
    currentValue,
    fromVersion,
    lockedVersion,
    updateType,
    isBump,
    sourceUrl,
    language,
    manager,
  } = config;
  const packageRules = config.packageRules || [];
  logger.trace(
    { dependency: depName, packageRules },
    `Checking against ${packageRules.length} packageRules`
  );
  packageRules.forEach(packageRule => {
    let {
      paths,
      languages,
      managers,
      depTypeList,
      packageNames,
      packagePatterns,
      excludePackageNames,
      excludePackagePatterns,
      matchCurrentVersion,
      sourceUrlPrefixes,
      updateTypes,
    } = packageRule;
    // Setting empty arrays simplifies our logic later
    paths = paths || [];
    languages = languages || [];
    managers = managers || [];
    depTypeList = depTypeList || [];
    packageNames = packageNames || [];
    packagePatterns = packagePatterns || [];
    excludePackageNames = excludePackageNames || [];
    excludePackagePatterns = excludePackagePatterns || [];
    sourceUrlPrefixes = sourceUrlPrefixes || [];
    matchCurrentVersion = matchCurrentVersion || null;
    updateTypes = updateTypes || [];
    let positiveMatch = false;
    let negativeMatch = false;
    // Massage a positive patterns patch if an exclude one is present
    if (
      (excludePackageNames.length || excludePackagePatterns.length) &&
      !(packageNames.length || packagePatterns.length)
    ) {
      packagePatterns = ['.*'];
    }
    if (paths.length) {
      const isMatch = paths.some(
        rulePath =>
          packageFile.includes(rulePath) || minimatch(packageFile, rulePath)
      );
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (depTypeList.length) {
      const isMatch =
        depTypeList.includes(depType) ||
        (depTypes && depTypes.some(dt => depTypeList.includes(dt)));
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (languages.length) {
      const isMatch = languages.includes(language);
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (managers.length) {
      const isMatch = managers.includes(manager);
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (updateTypes.length) {
      const isMatch =
        updateTypes.includes(updateType) ||
        (isBump && updateTypes.includes('bump'));
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (packageNames.length || packagePatterns.length) {
      let isMatch = packageNames.includes(depName);
      // name match is "or" so we check patterns if we didn't match names
      if (!isMatch) {
        for (const packagePattern of packagePatterns) {
          const packageRegex = new RegExp(
            packagePattern === '^*$' || packagePattern === '*'
              ? '.*'
              : packagePattern
          );
          if (depName && depName.match(packageRegex)) {
            logger.trace(`${depName} matches against ${packageRegex}`);
            isMatch = true;
          }
        }
      }
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (excludePackageNames.length) {
      const isMatch = excludePackageNames.includes(depName);
      negativeMatch = negativeMatch || isMatch;
      positiveMatch = positiveMatch || !isMatch;
    }
    if (excludePackagePatterns.length) {
      let isMatch = false;
      for (const pattern of excludePackagePatterns) {
        const packageRegex = new RegExp(
          pattern === '^*$' || pattern === '*' ? '.*' : pattern
        );
        if (depName && depName.match(packageRegex)) {
          logger.trace(`${depName} matches against ${packageRegex}`);
          isMatch = true;
        }
      }
      negativeMatch = negativeMatch || isMatch;
      positiveMatch = positiveMatch || !isMatch;
    }
    if (sourceUrlPrefixes.length) {
      const isMatch = sourceUrlPrefixes.some(
        prefix => sourceUrl && sourceUrl.startsWith(prefix)
      );
      positiveMatch = positiveMatch || isMatch;
      negativeMatch = negativeMatch || !isMatch;
    }
    if (matchCurrentVersion) {
      const { matches, isVersion } = versioning.get(versionScheme);
      const compareVersion = isVersion(currentValue)
        ? currentValue // it's a version so we can match against it
        : lockedVersion || fromVersion; // need to match against this fromVersion, if available
      if (compareVersion) {
        const isMatch = matches(compareVersion, matchCurrentVersion);
        positiveMatch = positiveMatch || isMatch;
        negativeMatch = negativeMatch || !isMatch;
      } else {
        negativeMatch = true;
      }
    }
    // This rule is considered matched if there was at least one positive match and no negative matches
    if (positiveMatch && !negativeMatch) {
      // Package rule config overrides any existing config
      config = mergeChildConfig(config, packageRule);
      delete config.packageNames;
      delete config.packagePatterns;
      delete config.excludePackageNames;
      delete config.excludePackagePatterns;
      delete config.depTypeList;
      delete config.matchCurrentVersion;
    }
  });
  return config;
}
<|endoftext|>


<commit_before>'use strict';

var Promise = require('../ext/promise');

module.exports = function sequence(tasks) {
  var length = tasks.length;
  var current = Promise.resolve();
  var results = new Array(length);

  for (var i = 0; i < length; ++i) {
    current = results[i] = current.then(tasks[i]);
  }

  return Promise.all(results);
};
<commit_msg>add some docs to sequence helper<commit_after>'use strict';

var Promise = require('../ext/promise');
/*
 *
 * given an array of functions, that may or may not return promises sequence
 * will invoke them sequentially. If a promise is encountered sequence will
 * wait until it fulfills before moving to the next entry.
 *
 * ```js
 * var tasks = [
 *   function() { return Promise.resolve(1); },
 *   2,
 *   function() { return timeout(1000).then(function() { return 3; } },
 * ];
 *
 * sequence(tasks).then(function(results) {
 *   results === [
 *     1,
 *     2,
 *     3
 *   ]
 * });
 * ```
 *
 * @method sequence
 * @param tasks
 * @return Promise<Array>
 *
 */
module.exports = function sequence(tasks) {
  var length = tasks.length;
  var current = Promise.resolve();
  var results = new Array(length);

  for (var i = 0; i < length; ++i) {
    current = results[i] = current.then(tasks[i]);
  }

  return Promise.all(results);
};
<|endoftext|>