<commit_before>"use strict";

/**************************
 * Import important stuff *
 **************************/

const Discord = require("discord.js");
const md2html = require("./md2html");
const MessageMap = require("../MessageMap");
const LatestDiscordMessageIds = require("./LatestDiscordMessageIds");
const handleEmbed = require("./handleEmbed");
const relayOldMessages = require("./relayOldMessages");
const Bridge = require("../bridgestuff/Bridge");
const path = require("path");
const R = require("ramda");

/***********
 * Helpers *
 ***********/

/**
 * Creates a function to give to 'guildMemberAdd' or 'guildMemberRemove' on a Discord bot
 *
 * @param {Logger} logger	The Logger instance to log messages to
 * @param {String} verb	Either "joined" or "left"
 * @param {BridgeMap} bridgeMap	Map of existing bridges
 * @param {Telegraf} tgBot	The Telegram bot to send the messages to
 *
 * @returns {Function}	Function which can be given to the 'guildMemberAdd' or 'guildMemberRemove' events of a Discord bot
 *
 * @private
 */
function makeJoinLeaveFunc(logger, verb, bridgeMap, tgBot) {
	// Find out which setting property to check the bridges for
	const relaySetting = verb === "joined"
		? "relayJoinMessages"
		: "relayLeaveMessages"
	;

	return function(member) {
		// Get the bridges in the guild the member joined/left
		member.guild.channels
			// Get the bridges corresponding to the channels in this guild
			.map(({id}) => bridgeMap.fromDiscordChannelId(id))
			// Remove the ones which are not bridged
			.filter((bridges) => bridges !== undefined)
			// Flatten the bridge arrays
			.reduce(((flattened, bridges) => flattened.concat(bridges)))
			// Remove those which do not allow relaying join messages
			.filter((bridge) => bridge.discord[relaySetting])
			// Ignore the T2D bridges
			.filter((bridge) => bridge.direction !== Bridge.DIRECTION_TELEGRAM_TO_DISCORD)
			.forEach(async (bridge) => {
				// Make the text to send
				const text = `<b>${member.displayName} (@${member.user.username})</b> ${verb} the Discord side of the chat`;

				try {
					// Send it
					await tgBot.telegram.sendMessage(
						bridge.telegram.chatId,
						text,
						{
							parse_mode: "HTML"
						}
					);
				} catch (err) {
					logger.error(`[${bridge.name}] Could not notify Telegram about a user that ${verb} Discord`, err);
				}
			});
	};
}

/**********************
 * The setup function *
 **********************/

/**
 * Sets up the receiving of Discord messages, and relaying them to Telegram
 *
 * @param {Logger} logger	The Logger instance to log messages to
 * @param {Discord.Client} dcBot	The Discord bot
 * @param {Telegraf} tgBot	The Telegram bot
 * @param {MessageMap} messageMap	Map between IDs of messages
 * @param {BridgeMap} bridgeMap	Map of the bridges to use
 * @param {Settings} settings	Settings to use
 * @param {String} datadirPath	Path to the directory to put data files in
 */
function setup(logger, dcBot, tgBot, messageMap, bridgeMap, settings, datadirPath) {
	// Create the map of latest message IDs and bridges
	const latestDiscordMessageIds = new LatestDiscordMessageIds(logger, path.join(datadirPath, "latestDiscordMessageIds.json"));
	const useNickname = settings.discord.useNickname;

	// Listen for users joining the server
	dcBot.on("guildMemberAdd", makeJoinLeaveFunc(logger, "joined", bridgeMap, tgBot));

	// Listen for users joining the server
	dcBot.on("guildMemberRemove", makeJoinLeaveFunc(logger, "left", bridgeMap, tgBot));

	// Listen for Discord messages
	dcBot.on("message", async (message) => {

		// Ignore the bot's own messages
		if (message.author.id === dcBot.user.id) {
			return;
		}

		// Check if this is a request for server info
		if (message.channel.type === "text" && message.cleanContent === "/chatinfo") {
			// It is. Give it
			message.reply(
				"serverId: '" + message.guild.id + "'\n" +
				"channelId: '" + message.channel.id + "'\n"
			);

			// Don't process the message any further
			return;
		}

		// Get info about the sender
		const senderName = (useNickname && message.member ? message.member.displayName : message.author.username) + (settings.telegram.colonAfterSenderName ? ":" : "");

		// Check if the message is from the correct chat
		const bridges = bridgeMap.fromDiscordChannelId(message.channel.id);
		if (!R.isEmpty(bridges)) {
			bridges.forEach(async (bridge) => {
				// Ignore it if this is a telegram-to-discord bridge
				if (bridge.direction === Bridge.DIRECTION_TELEGRAM_TO_DISCORD) {
					return;
				}

				// This is now the latest message for this bridge
				latestDiscordMessageIds.setLatest(message.id, bridge);

				// Check for attachments and pass them on
				message.attachments.forEach(async ({url}) => {
					try {
						const textToSend = bridge.discord.sendUsernames
							? `<b>${senderName}</b>\n<a href="${url}">${url}</a>`
							: `<a href="${url}">${url}</a>`
						;
						const tgMessage = await tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							textToSend,
							{
								parse_mode: "HTML"
							}
						);
						messageMap.insert(MessageMap.DISCORD_TO_TELEGRAM, bridge, message.id, tgMessage.message_id);
					} catch (err) {
						logger.error(`[${bridge.name}] Telegram did not accept an attachment:`, err);
					}
				});

				// Check the message for embeds
				message.embeds.forEach((embed) => {
					// Ignore it if it is not a "rich" embed (image, link, video, ...)
					if (embed.type !== "rich") {
						return;
					}

					// Convert it to something Telegram likes
					const text = handleEmbed(embed, senderName);

					try {
						// Send it
						tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							text,
							{
								parse_mode: "HTML",
								disable_web_page_preview: true
							}
						);
					} catch (err) {
						logger.error(`[${bridge.name}] Telegram did not accept an embed:`, err);
					}
				});

				// Check if there is an ordinary text message
				if (message.cleanContent) {

					// Modify the message to fit Telegram
					const processedMessage = md2html(message.cleanContent);

					// Pass the message on to Telegram
					try {
						const textToSend = bridge.discord.sendUsernames
							? `<b>${senderName}</b>\n${processedMessage}`
							: processedMessage
						;
						const tgMessage = await tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							textToSend,
							{
								parse_mode: "HTML"
							}
						);

						// Make the mapping so future edits can work
						messageMap.insert(MessageMap.DISCORD_TO_TELEGRAM, bridge, message.id, tgMessage.message_id);
					} catch (err) {
						logger.error(`[${bridge.name}] Telegram did not accept a message:`, err);
						logger.error(`[${bridge.name}] Failed message:`, err);
					}
				}
			});
		} else if (R.isNil(message.channel.guild) || !bridgeMap.knownDiscordServer(message.channel.guild.id)) {	// Check if it is the correct server
			// The message is from the wrong chat. Inform the sender that this is a private bot
			message.reply(
				"This is an instance of a TediCross bot, bridging a chat in Telegram with one in Discord. "
				+ "If you wish to use TediCross yourself, please download and create an instance. "
				+ "See https://github.com/TediCross/TediCross"
			);
		}
	});

	// Listen for message edits
	dcBot.on("messageUpdate", async (oldMessage, newMessage) => {
		// Don't do anything with the bot's own messages
		if (newMessage.author.id === dcBot.user.id) {
			return;
		}

		// Pass it on to the bridges
		bridgeMap.fromDiscordChannelId(newMessage.channel.id).forEach(async (bridge) => {
			try {
				// Get the corresponding Telegram message ID
				const [tgMessageId] = messageMap.getCorresponding(MessageMap.DISCORD_TO_TELEGRAM, bridge, newMessage.id);

				// Get info about the sender
				const senderName = (useNickname && newMessage.member ? newMessage.member.displayName : newMessage.author.username) + (settings.telegram.colonAfterSenderName ? ":" : "");

				// Modify the message to fit Telegram
				const processedMessage = md2html(newMessage.cleanContent);

				// Send the update to Telegram
				const textToSend = bridge.discord.sendUsernames
					? `<b>${senderName}</b>\n${processedMessage}`
					: processedMessage
				;
				await tgBot.telegram.editMessageText(
					bridge.telegram.chatId,
					tgMessageId,
					null,
					textToSend,
					{
						parse_mode: "HTML"
					}
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not edit Telegram message:`, err);
			}
		});
	});

	// Listen for deleted messages
	function onMessageDelete(message) {
		// Check if it is a relayed message
		const isFromTelegram = message.author.id === dcBot.user.id;

		// Hand it on to the bridges
		bridgeMap.fromDiscordChannelId(message.channel.id).forEach(async (bridge) => {
			// Ignore it if cross deletion is disabled
			if (!bridge.discord.crossDeleteOnTelegram) {
				return;
			}

			try {
				// Get the corresponding Telegram message IDs
				const tgMessageIds = isFromTelegram
					? messageMap.getCorrespondingReverse(MessageMap.DISCORD_TO_TELEGRAM, bridge, message.id)
					: messageMap.getCorresponding(MessageMap.DISCORD_TO_TELEGRAM, bridge, message.id)
				;

				// Try to delete them
				await Promise.all(
					tgMessageIds.map((tgMessageId) => tgBot.telegram.deleteMessage(bridge.telegram.chatId, tgMessageId))
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not delete Telegram message:`, err);
			}
		});
	}
	dcBot.on("messageDelete", onMessageDelete);
	dcBot.on("messageDeleteBulk", (messages) => [...messages.values()].forEach(onMessageDelete));

	// Start the Discord bot
	dcBot.login(settings.discord.token)
		// Complain if it could not authenticate for some reason
		.catch((err) => logger.error("Could not authenticate the Discord bot:", err));

	// Listen for the 'disconnected' event
	dcBot.on("disconnected", async (evt) => {
		logger.error("Discord bot disconnected!", evt);

		bridgeMap.bridges.forEach(async (bridge) => {
			try {
				await tgBot.telegram.sendMessage(
					bridge.telegram.chatId,
					"**TEDICROSS**\nThe discord side of the bot disconnected! Please check the log"
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not send message to Telegram:`, err);
			}
		});
	});

	setTimeout(() => dcBot.emit("error", new Error()), 10000);

	// Listen for errors
	dcBot.on("error", (err) => {
		if (err.code === "ECONNRESET") {
			// Lost connection to the discord servers
			logger.warn("Lost connection to Discord's servers. The bot will resume when connection is reestablished, which should happen automatically. If it does not, please report this to the TediCross support channel");
		} else {
			// Unknown error. Tell the user to tell the devs
			logger.error("The Discord bot ran into an error. Please post the following error message in the TediCross support channel");
			logger.error(err);
		}
	});

	// Listen for debug messages
	if (settings.debug) {
		dcBot.on("debug", (str) => {
			logger.log(str);
		});

		// Check the Discord bot's status every now and then
		setInterval(() => {
			if (dcBot.status !== Discord.Constants.Status.READY) {
				let actualStatus = null;
				switch (dcBot.status) {
					case Discord.Constants.Status.CONNECTING:
						actualStatus = "CONNECTING";
						break;
					case Discord.Constants.Status.RECONNECTING:
						actualStatus = "RECONNECTING";
						break;
					case Discord.Constants.Status.IDLE:
						actualStatus = "IDLE";
						break;
					case Discord.Constants.Status.NEARLY:
						actualStatus = "NEARLY";
						break;
					case Discord.Constants.Status.DISCONNETED:
						actualStatus = "DISCONNECTED";
						break;
					default:
						actualStatus = "UNKNOWN";
						break;
				}
				logger.error(`Discord status not ready! Status is '${actualStatus}'`);
			}
		}, 1000);
	}

	// Make a promise which resolves when the dcBot is ready
	dcBot.ready = new Promise((resolve) => {
		// Listen for the 'ready' event
		dcBot.once("ready", () => {
			// Log the event
			logger.info(`Discord: ${dcBot.user.username} (${dcBot.user.id})`);

			// Mark the bot as ready
			resolve();
		});
	});

	// Relay old messages, if wanted by the user
	if (!settings.discord.skipOldMessages) {
		dcBot.ready = relayOldMessages(logger, dcBot, latestDiscordMessageIds, bridgeMap);
	}
}

/*****************************
 * Export the setup function *
 *****************************/

module.exports = setup;
<commit_msg>Removed debug message<commit_after>"use strict";

/**************************
 * Import important stuff *
 **************************/

const Discord = require("discord.js");
const md2html = require("./md2html");
const MessageMap = require("../MessageMap");
const LatestDiscordMessageIds = require("./LatestDiscordMessageIds");
const handleEmbed = require("./handleEmbed");
const relayOldMessages = require("./relayOldMessages");
const Bridge = require("../bridgestuff/Bridge");
const path = require("path");
const R = require("ramda");

/***********
 * Helpers *
 ***********/

/**
 * Creates a function to give to 'guildMemberAdd' or 'guildMemberRemove' on a Discord bot
 *
 * @param {Logger} logger	The Logger instance to log messages to
 * @param {String} verb	Either "joined" or "left"
 * @param {BridgeMap} bridgeMap	Map of existing bridges
 * @param {Telegraf} tgBot	The Telegram bot to send the messages to
 *
 * @returns {Function}	Function which can be given to the 'guildMemberAdd' or 'guildMemberRemove' events of a Discord bot
 *
 * @private
 */
function makeJoinLeaveFunc(logger, verb, bridgeMap, tgBot) {
	// Find out which setting property to check the bridges for
	const relaySetting = verb === "joined"
		? "relayJoinMessages"
		: "relayLeaveMessages"
	;

	return function(member) {
		// Get the bridges in the guild the member joined/left
		member.guild.channels
			// Get the bridges corresponding to the channels in this guild
			.map(({id}) => bridgeMap.fromDiscordChannelId(id))
			// Remove the ones which are not bridged
			.filter((bridges) => bridges !== undefined)
			// Flatten the bridge arrays
			.reduce(((flattened, bridges) => flattened.concat(bridges)))
			// Remove those which do not allow relaying join messages
			.filter((bridge) => bridge.discord[relaySetting])
			// Ignore the T2D bridges
			.filter((bridge) => bridge.direction !== Bridge.DIRECTION_TELEGRAM_TO_DISCORD)
			.forEach(async (bridge) => {
				// Make the text to send
				const text = `<b>${member.displayName} (@${member.user.username})</b> ${verb} the Discord side of the chat`;

				try {
					// Send it
					await tgBot.telegram.sendMessage(
						bridge.telegram.chatId,
						text,
						{
							parse_mode: "HTML"
						}
					);
				} catch (err) {
					logger.error(`[${bridge.name}] Could not notify Telegram about a user that ${verb} Discord`, err);
				}
			});
	};
}

/**********************
 * The setup function *
 **********************/

/**
 * Sets up the receiving of Discord messages, and relaying them to Telegram
 *
 * @param {Logger} logger	The Logger instance to log messages to
 * @param {Discord.Client} dcBot	The Discord bot
 * @param {Telegraf} tgBot	The Telegram bot
 * @param {MessageMap} messageMap	Map between IDs of messages
 * @param {BridgeMap} bridgeMap	Map of the bridges to use
 * @param {Settings} settings	Settings to use
 * @param {String} datadirPath	Path to the directory to put data files in
 */
function setup(logger, dcBot, tgBot, messageMap, bridgeMap, settings, datadirPath) {
	// Create the map of latest message IDs and bridges
	const latestDiscordMessageIds = new LatestDiscordMessageIds(logger, path.join(datadirPath, "latestDiscordMessageIds.json"));
	const useNickname = settings.discord.useNickname;

	// Listen for users joining the server
	dcBot.on("guildMemberAdd", makeJoinLeaveFunc(logger, "joined", bridgeMap, tgBot));

	// Listen for users joining the server
	dcBot.on("guildMemberRemove", makeJoinLeaveFunc(logger, "left", bridgeMap, tgBot));

	// Listen for Discord messages
	dcBot.on("message", async (message) => {

		// Ignore the bot's own messages
		if (message.author.id === dcBot.user.id) {
			return;
		}

		// Check if this is a request for server info
		if (message.channel.type === "text" && message.cleanContent === "/chatinfo") {
			// It is. Give it
			message.reply(
				"serverId: '" + message.guild.id + "'\n" +
				"channelId: '" + message.channel.id + "'\n"
			);

			// Don't process the message any further
			return;
		}

		// Get info about the sender
		const senderName = (useNickname && message.member ? message.member.displayName : message.author.username) + (settings.telegram.colonAfterSenderName ? ":" : "");

		// Check if the message is from the correct chat
		const bridges = bridgeMap.fromDiscordChannelId(message.channel.id);
		if (!R.isEmpty(bridges)) {
			bridges.forEach(async (bridge) => {
				// Ignore it if this is a telegram-to-discord bridge
				if (bridge.direction === Bridge.DIRECTION_TELEGRAM_TO_DISCORD) {
					return;
				}

				// This is now the latest message for this bridge
				latestDiscordMessageIds.setLatest(message.id, bridge);

				// Check for attachments and pass them on
				message.attachments.forEach(async ({url}) => {
					try {
						const textToSend = bridge.discord.sendUsernames
							? `<b>${senderName}</b>\n<a href="${url}">${url}</a>`
							: `<a href="${url}">${url}</a>`
						;
						const tgMessage = await tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							textToSend,
							{
								parse_mode: "HTML"
							}
						);
						messageMap.insert(MessageMap.DISCORD_TO_TELEGRAM, bridge, message.id, tgMessage.message_id);
					} catch (err) {
						logger.error(`[${bridge.name}] Telegram did not accept an attachment:`, err);
					}
				});

				// Check the message for embeds
				message.embeds.forEach((embed) => {
					// Ignore it if it is not a "rich" embed (image, link, video, ...)
					if (embed.type !== "rich") {
						return;
					}

					// Convert it to something Telegram likes
					const text = handleEmbed(embed, senderName);

					try {
						// Send it
						tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							text,
							{
								parse_mode: "HTML",
								disable_web_page_preview: true
							}
						);
					} catch (err) {
						logger.error(`[${bridge.name}] Telegram did not accept an embed:`, err);
					}
				});

				// Check if there is an ordinary text message
				if (message.cleanContent) {

					// Modify the message to fit Telegram
					const processedMessage = md2html(message.cleanContent);

					// Pass the message on to Telegram
					try {
						const textToSend = bridge.discord.sendUsernames
							? `<b>${senderName}</b>\n${processedMessage}`
							: processedMessage
						;
						const tgMessage = await tgBot.telegram.sendMessage(
							bridge.telegram.chatId,
							textToSend,
							{
								parse_mode: "HTML"
							}
						);

						// Make the mapping so future edits can work
						messageMap.insert(MessageMap.DISCORD_TO_TELEGRAM, bridge, message.id, tgMessage.message_id);
					} catch (err) {
						logger.error(`[${bridge.name}] Telegram did not accept a message:`, err);
						logger.error(`[${bridge.name}] Failed message:`, err);
					}
				}
			});
		} else if (R.isNil(message.channel.guild) || !bridgeMap.knownDiscordServer(message.channel.guild.id)) {	// Check if it is the correct server
			// The message is from the wrong chat. Inform the sender that this is a private bot
			message.reply(
				"This is an instance of a TediCross bot, bridging a chat in Telegram with one in Discord. "
				+ "If you wish to use TediCross yourself, please download and create an instance. "
				+ "See https://github.com/TediCross/TediCross"
			);
		}
	});

	// Listen for message edits
	dcBot.on("messageUpdate", async (oldMessage, newMessage) => {
		// Don't do anything with the bot's own messages
		if (newMessage.author.id === dcBot.user.id) {
			return;
		}

		// Pass it on to the bridges
		bridgeMap.fromDiscordChannelId(newMessage.channel.id).forEach(async (bridge) => {
			try {
				// Get the corresponding Telegram message ID
				const [tgMessageId] = messageMap.getCorresponding(MessageMap.DISCORD_TO_TELEGRAM, bridge, newMessage.id);

				// Get info about the sender
				const senderName = (useNickname && newMessage.member ? newMessage.member.displayName : newMessage.author.username) + (settings.telegram.colonAfterSenderName ? ":" : "");

				// Modify the message to fit Telegram
				const processedMessage = md2html(newMessage.cleanContent);

				// Send the update to Telegram
				const textToSend = bridge.discord.sendUsernames
					? `<b>${senderName}</b>\n${processedMessage}`
					: processedMessage
				;
				await tgBot.telegram.editMessageText(
					bridge.telegram.chatId,
					tgMessageId,
					null,
					textToSend,
					{
						parse_mode: "HTML"
					}
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not edit Telegram message:`, err);
			}
		});
	});

	// Listen for deleted messages
	function onMessageDelete(message) {
		// Check if it is a relayed message
		const isFromTelegram = message.author.id === dcBot.user.id;

		// Hand it on to the bridges
		bridgeMap.fromDiscordChannelId(message.channel.id).forEach(async (bridge) => {
			// Ignore it if cross deletion is disabled
			if (!bridge.discord.crossDeleteOnTelegram) {
				return;
			}

			try {
				// Get the corresponding Telegram message IDs
				const tgMessageIds = isFromTelegram
					? messageMap.getCorrespondingReverse(MessageMap.DISCORD_TO_TELEGRAM, bridge, message.id)
					: messageMap.getCorresponding(MessageMap.DISCORD_TO_TELEGRAM, bridge, message.id)
				;

				// Try to delete them
				await Promise.all(
					tgMessageIds.map((tgMessageId) => tgBot.telegram.deleteMessage(bridge.telegram.chatId, tgMessageId))
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not delete Telegram message:`, err);
			}
		});
	}
	dcBot.on("messageDelete", onMessageDelete);
	dcBot.on("messageDeleteBulk", (messages) => [...messages.values()].forEach(onMessageDelete));

	// Start the Discord bot
	dcBot.login(settings.discord.token)
		// Complain if it could not authenticate for some reason
		.catch((err) => logger.error("Could not authenticate the Discord bot:", err));

	// Listen for the 'disconnected' event
	dcBot.on("disconnected", async (evt) => {
		logger.error("Discord bot disconnected!", evt);

		bridgeMap.bridges.forEach(async (bridge) => {
			try {
				await tgBot.telegram.sendMessage(
					bridge.telegram.chatId,
					"**TEDICROSS**\nThe discord side of the bot disconnected! Please check the log"
				);
			} catch (err) {
				logger.error(`[${bridge.name}] Could not send message to Telegram:`, err);
			}
		});
	});

	// Listen for errors
	dcBot.on("error", (err) => {
		if (err.code === "ECONNRESET") {
			// Lost connection to the discord servers
			logger.warn("Lost connection to Discord's servers. The bot will resume when connection is reestablished, which should happen automatically. If it does not, please report this to the TediCross support channel");
		} else {
			// Unknown error. Tell the user to tell the devs
			logger.error("The Discord bot ran into an error. Please post the following error message in the TediCross support channel");
			logger.error(err);
		}
	});

	// Listen for debug messages
	if (settings.debug) {
		dcBot.on("debug", (str) => {
			logger.log(str);
		});

		// Check the Discord bot's status every now and then
		setInterval(() => {
			if (dcBot.status !== Discord.Constants.Status.READY) {
				let actualStatus = null;
				switch (dcBot.status) {
					case Discord.Constants.Status.CONNECTING:
						actualStatus = "CONNECTING";
						break;
					case Discord.Constants.Status.RECONNECTING:
						actualStatus = "RECONNECTING";
						break;
					case Discord.Constants.Status.IDLE:
						actualStatus = "IDLE";
						break;
					case Discord.Constants.Status.NEARLY:
						actualStatus = "NEARLY";
						break;
					case Discord.Constants.Status.DISCONNETED:
						actualStatus = "DISCONNECTED";
						break;
					default:
						actualStatus = "UNKNOWN";
						break;
				}
				logger.error(`Discord status not ready! Status is '${actualStatus}'`);
			}
		}, 1000);
	}

	// Make a promise which resolves when the dcBot is ready
	dcBot.ready = new Promise((resolve) => {
		// Listen for the 'ready' event
		dcBot.once("ready", () => {
			// Log the event
			logger.info(`Discord: ${dcBot.user.username} (${dcBot.user.id})`);

			// Mark the bot as ready
			resolve();
		});
	});

	// Relay old messages, if wanted by the user
	if (!settings.discord.skipOldMessages) {
		dcBot.ready = relayOldMessages(logger, dcBot, latestDiscordMessageIds, bridgeMap);
	}
}

/*****************************
 * Export the setup function *
 *****************************/

module.exports = setup;
<|endoftext|>


<commit_before>/*global d3 */

var params = d3.select("#api_key");

if (!params.empty()) {
  var api_key = params.attr('data-api-key');
  var page = params.attr('data-page');
  if (page == null) { page = 1; }
  var per_page = params.attr('data-per-page');
  var q = params.attr('data-q');
  var source_id = params.attr('data-source-id');
  var state = params.attr('data-state');

  var query = encodeURI("/api/deposits?page=" + page);
  if (per_page !== null) { query += "&per_page=" + per_page; }
  if (q !== null) { query += "&q=" + q; }
  if (source_id !== null) { query += "&source_id=" + source_id; }
  if (state !== null) { query += "&state=" + state; }
}

// asynchronously load data from the Lagotto API
queue()
  .defer(d3.json, encodeURI("/api/sources"))
  .defer(d3.json, encodeURI("/api/relation_types"))
  .defer(d3.json, query)
  .await(function(error, s, r, d) {
    if (error) { return console.warn(error); }
    eventsViz(d, s.sources, r.relation_types);
    paginate(d, "#content");
});

// add data to page
function eventsViz(json, sources, relation_types) {
  data = json.deposits;

  json.href = "?page={{number}}";
  if (q !== null) { json.href += "&q=" + q; }
  if (state !== null) { json.href += "&state=" + state; }
  if (source_id !== null) { json.href += "&source_id=" + source_id; }

  d3.select("#loading-results").remove();

  if (typeof data === "undefined" || data.length === 0) {
    d3.select("#content").text("")
      .insert("div")
      .attr("class", "alert alert-info")
      .text("There are currently no deposits");
    return;
  }

  if (json.meta.total > 1) {
    d3.select("#content").insert("h4")
      .attr("class", "results")
      .text(numberWithDelimiter(json.meta.total) + " Deposits");
  }

  for (var i=0; i<data.length; i++) {
    var deposit = data[i];
    var source = sources.filter(function(d) { return d.id === deposit.source_id; })[0];
    var relation_type = relation_types.filter(function(d) { return d.id === deposit.relation_type_id; })[0];

    d3.select("#content").insert("div")
      .attr("class", "panel panel-default")
      .attr("id", "panel-" + i).insert("div")
      .attr("class", "panel-body deposit")
      .attr("id", "panel-body-" + i);

    d3.select("#panel-body-" + i).append("h4")
      .attr("class", "work")
      .html(deposit.id).append("small")
      .attr("class", "pull-right")
      .html(formattedState(deposit.state));

    if (typeof Object.keys(deposit.subj).length > 0) {
      d3.select("#panel-body-" + i).append("h5")
        .text("Subj");
      d3.select("#panel-body-" + i).append("p")
        .html(function() { return deposit.subj; });
    }

    if (typeof Object.keys(deposit.obj).length > 0) {
      d3.select("#panel-body-" + i).append("h5")
        .text("Obj");
      d3.select("#panel-body-" + i).append("div")
        .html(function() { return deposit.obj; });
    }

    if (typeof deposit.obj_id !== "undefined") {
      d3.select("#panel-body-" + i).append("div")
        .text(relation_type.title + " ").append("a")
        .attr("href", function() { return pathForWork(deposit.obj_id); })
        .html(deposit.obj_id);
    }

    d3.select("#panel-body-" + i).append("div")
      .text("Occurred at " + formattedDate(deposit.occurred_at));

    if (typeof deposit.errors !== "undefined") {
      var error_key = Object.keys(deposit.errors);

      d3.select("#panel-body-" + i).append("h5")
        .text("Errors in " + error_key);
      d3.select("#panel-body-" + i).append("div")
        .html(deposit.errors[error_key]);
    }

    if (deposit.message_type === "publisher") {
      d3.select("#panel-" + i).insert("div")
        .attr("class", "panel-footer")
        .attr("id", "panel-footer-" + i).append("span")
        .html('<i class="fa fa-info-circle"/>').append("span")
        .text(deposit.subj_id);
    } else {
      d3.select("#panel-" + i).insert("div")
        .attr("class", "panel-footer")
        .attr("id", "panel-footer-" + i).append("a")
        .attr("href", function() { return deposit.subj_id; })
        .html('<i class="fa fa-external-link"/>').append('span')
        .text(deposit.subj_id);
    }

    if (typeof source !== "undefined") {
      d3.select("#panel-footer-" + i).append("a")
        .attr("class", "pull-right")
        .attr("href", function() { return "/deposits?source_id=" + deposit.source_id; })
        .text(source.title);
    }
  }
}
<commit_msg>tweaked display of deposits in admin UI<commit_after>/*global d3 */

var params = d3.select("#api_key");

if (!params.empty()) {
  var api_key = params.attr('data-api-key');
  var page = params.attr('data-page');
  if (page == null) { page = 1; }
  var per_page = params.attr('data-per-page');
  var q = params.attr('data-q');
  var source_id = params.attr('data-source-id');
  var state = params.attr('data-state');

  var query = encodeURI("/api/deposits?page=" + page);
  if (per_page !== null) { query += "&per_page=" + per_page; }
  if (q !== null) { query += "&q=" + q; }
  if (source_id !== null) { query += "&source_id=" + source_id; }
  if (state !== null) { query += "&state=" + state; }
}

// asynchronously load data from the Lagotto API
queue()
  .defer(d3.json, encodeURI("/api/sources"))
  .defer(d3.json, encodeURI("/api/relation_types"))
  .defer(d3.json, query)
  .await(function(error, s, r, d) {
    if (error) { return console.warn(error); }
    eventsViz(d, s.sources, r.relation_types);
    paginate(d, "#content");
});

// add data to page
function eventsViz(json, sources, relation_types) {
  data = json.deposits;

  json.href = "?page={{number}}";
  if (q !== null) { json.href += "&q=" + q; }
  if (state !== null) { json.href += "&state=" + state; }
  if (source_id !== null) { json.href += "&source_id=" + source_id; }

  d3.select("#loading-results").remove();

  if (typeof data === "undefined" || data.length === 0) {
    d3.select("#content").text("")
      .insert("div")
      .attr("class", "alert alert-info")
      .text("There are currently no deposits");
    return;
  }

  if (json.meta.total > 1) {
    d3.select("#content").insert("h4")
      .attr("class", "results")
      .text(numberWithDelimiter(json.meta.total) + " Deposits");
  }

  for (var i=0; i<data.length; i++) {
    var deposit = data[i];
    var source = sources.filter(function(d) { return d.id === deposit.source_id; })[0];
    var relation_type = relation_types.filter(function(d) { return d.id === deposit.relation_type_id; })[0];

    d3.select("#content").insert("div")
      .attr("class", "panel panel-default")
      .attr("id", "panel-" + i).insert("div")
      .attr("class", "panel-heading panel-title")
      .html(deposit.id).append("small")
      .attr("class", "pull-right")
      .html(formattedState(deposit.state));

      d3.select("#panel-" + i).insert("div")
        .attr("class", "panel-body deposit")
        .attr("id", "panel-body-" + i);

    if (typeof Object.keys(deposit.subj).length > 0) {
      d3.select("#panel-body-" + i).append("h5")
        .text("Subj");
      d3.select("#panel-body-" + i).append("div")
        .html(function() { return deposit.subj; });
    }

    if (typeof Object.keys(deposit.obj).length > 0) {
      d3.select("#panel-body-" + i).append("h5")
        .text("Obj");
      d3.select("#panel-body-" + i).append("div")
        .html(function() { return deposit.obj; });
    }

    if (typeof deposit.obj_id !== "undefined") {
      d3.select("#panel-body-" + i).append("div")
        .text(relation_type.title + " ").append("a")
        .attr("href", function() { return pathForWork(deposit.obj_id); })
        .html(deposit.obj_id);
    }

    d3.select("#panel-body-" + i).append("div")
      .text("Occurred at " + formattedDate(deposit.occurred_at));

    if (typeof deposit.errors !== "undefined") {
      var error_key = Object.keys(deposit.errors);

      d3.select("#panel-body-" + i).append("h5")
        .text("Errors in " + error_key);
      d3.select("#panel-body-" + i).append("div")
        .html(deposit.errors[error_key]);
    }

    if (deposit.message_type === "publisher") {
      d3.select("#panel-" + i).insert("div")
        .attr("class", "panel-footer")
        .attr("id", "panel-footer-" + i).append("span")
        .html('<i class="fa fa-info-circle"/>').append("span")
        .text(deposit.subj_id);
    } else {
      d3.select("#panel-" + i).insert("div")
        .attr("class", "panel-footer")
        .attr("id", "panel-footer-" + i).append("a")
        .attr("href", function() { return deposit.subj_id; })
        .html('<i class="fa fa-external-link"/>').append('span')
        .text(deposit.subj_id);
    }

    if (typeof source !== "undefined") {
      d3.select("#panel-footer-" + i).append("a")
        .attr("class", "pull-right")
        .attr("href", function() { return "/deposits?source_id=" + deposit.source_id; })
        .text(source.title);
    }
  }
}
<|endoftext|>


<commit_before>module.exports = function (value, key, option, lang) {
  if(option[0] === 'infrastructure'){
    return render_infrastructures(value, key);
  }else if(option[0] === 'project'){
    return render_projects(value, key, lang);
  }else if (option[0] === 'client') {
    return render_clients(value, key, lang);
  }else if (option[0] === 'serverspec') {
    return render_serverspecs(value, key, lang);
  }else{
    return value;
  }
};

function render_infrastructures(value, key){
  if(key === 'id'){
  var isEdit = $('#edit-'+value+'').attr('class');
  var href = $('#edit-'+value+'').attr('href');
  var isDelete = $('#delete-'+value+'').attr('class');
  var ret = ""+t('helpers.links.show')+" " +
    "Serverspecs " +
    ""+ t("helpers.links.edit")+" " +
    ""+t('helpers.links.detach')+" "+
    "
"+
        "" +
        "    "+t('infrastructures.btn.delete_stack')+"  " +
        "  " +
       "
"+
        "
 " +
         "Execute " +
      "
"+
      "
"+
     "
";

    return ret;
  }else{
    if(value == "CREATE_COMPLETE")
      return ""+value+"";
    else
      return value;
  }
}

function render_clients(value, key, lang){
  if(key === 'id'){
    var isEdit = $('#delete-'+value+'').attr('class');
    var isDelete = $('#delete-'+value+'').attr('class');
    var edit = '';
    var del = '';
    if(isEdit)
      edit = " "+t("helpers.links.edit")+"";
    if(isDelete)
      del = " Delete";

    var ret = ""+t('clients.btn.show_projects')+" ";
       return ret+edit+del;
  }else{
    return value;
  }
}

function render_projects(value, key, lang){
  if(key === 'id'){
    var isDelete = $('#delete-'+value+'').attr('class');
    var del;
    if(isDelete){
     del = "Delete";
    }else{
     del = '';
    }

    var ret = ""+t('projects.btn.show_infrastructures')+" " +
      "
" +
        "" +
          t ('common.btn.settings')+ " " +
        "" +
        "
" +
          "
" +
            ""+t('dishes.dishes')+"" +
            ""+t('key_pairs.key_pairs')+"" +
          "
" +
        "
" +
      "
" +
      ""+t("helpers.links.edit")+" ";
       return ret+del;
  }else{
    return value;
  }
}

function render_serverspecs(value, key, lang){
  if(key === 'id'){
    var isEdit = $('#edit-'+value+'').attr('class');
    var isDelete = $('#delete-'+value+'').attr('class');
    var edit = '';
    var del = '';
    if(isEdit)
      edit = " "+t("helpers.links.edit")+"";
    if(isDelete)
      del = " Delete";

    var ret = ""+t('helpers.links.show')+" ";
    return ret+edit+del;
  }else{
    return value;
  }
}
<commit_msg>added languange filter<commit_after>module.exports = function (value, key, option, lang) {
  if(option[0] === 'infrastructure'){
    return render_infrastructures(value, key, lang);
  }else if(option[0] === 'project'){
    return render_projects(value, key, lang);
  }else if (option[0] === 'client') {
    return render_clients(value, key, lang);
  }else if (option[0] === 'serverspec') {
    return render_serverspecs(value, key, lang);
  }else{
    return value;
  }
};

function render_infrastructures(value, key, lang){
  if(key === 'id'){
  var isEdit = $('#edit-'+value+'').attr('class');
  var href = $('#edit-'+value+'').attr('href');
  var isDelete = $('#delete-'+value+'').attr('class');
  var ret = ""+t('helpers.links.show')+" " +
    "Serverspecs " +
    ""+ t("helpers.links.edit")+" " +
    ""+t('helpers.links.detach')+" "+
    "
"+
        "" +
        "    "+t('infrastructures.btn.delete_stack')+"  " +
        "  " +
       "
"+
        "
 " +
         "Execute " +
      "
"+
      "
"+
     "
";

    return ret;
  }else{
    if(value == "CREATE_COMPLETE")
      return ""+value+"";
    else
      return value;
  }
}

function render_clients(value, key, lang){
  if(key === 'id'){
    var isEdit = $('#delete-'+value+'').attr('class');
    var isDelete = $('#delete-'+value+'').attr('class');
    var edit = '';
    var del = '';
    if(isEdit)
      edit = " "+t("helpers.links.edit")+"";
    if(isDelete)
      del = " Delete";

    var ret = ""+t('clients.btn.show_projects')+" ";
       return ret+edit+del;
  }else{
    return value;
  }
}

function render_projects(value, key, lang){
  if(key === 'id'){
    var isDelete = $('#delete-'+value+'').attr('class');
    var del;
    if(isDelete){
     del = "Delete";
    }else{
     del = '';
    }

    var ret = ""+t('projects.btn.show_infrastructures')+" " +
      "
" +
        "" +
          t ('common.btn.settings')+ " " +
        "" +
        "
" +
          "
" +
            ""+t('dishes.dishes')+"" +
            ""+t('key_pairs.key_pairs')+"" +
          "
" +
        "
" +
      "
" +
      ""+t("helpers.links.edit")+" ";
       return ret+del;
  }else{
    return value;
  }
}

function render_serverspecs(value, key, lang){
  if(key === 'id'){
    var isEdit = $('#edit-'+value+'').attr('class');
    var isDelete = $('#delete-'+value+'').attr('class');
    var edit = '';
    var del = '';
    if(isEdit)
      edit = " "+t("helpers.links.edit")+"";
    if(isDelete)
      del = " Delete";

    var ret = ""+t('helpers.links.show')+" ";
    return ret+edit+del;
  }else{
    return value;
  }
}
<|endoftext|>


<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const ccxt = require ('ccxt');
const { NotImplemented } = require ('ccxt/js/base/errors');

//  ---------------------------------------------------------------------------

module.exports = class poloniex extends ccxt.poloniex {
    describe () {
        return this.deepExtend (super.describe (), {
            'has': {
                'ws': true,
                'watchTicker': true,
                'watchOrderBook': true,
                'watchBalance': false, // not implemented yet
            },
            'urls': {
                'api': {
                    'ws': 'wss://api2.poloniex.com',
                },
            },
        });
    }

    handleTickers (client, response) {
        const data = response[2];
        const market = this.safeValue (this.options['marketsByNumericId'], data[0].toString ());
        const symbol = this.safeString (market, 'symbol');
        return {
            'info': response,
            'symbol': symbol,
            'last': parseFloat (data[1]),
            'ask': parseFloat (data[2]),
            'bid': parseFloat (data[3]),
            'change': parseFloat (data[4]),
            'baseVolume': parseFloat (data[5]),
            'quoteVolume': parseFloat (data[6]),
            'active': data[7] ? false : true,
            'high': parseFloat (data[8]),
            'low': parseFloat (data[9]),
        };
    }

    async watchBalance (params = {}) {
        await this.loadMarkets ();
        this.balance = await this.fetchBalance (params);
        const channelId = '1000';
        const subscribe = {
            'command': 'subscribe',
            'channel': channelId,
        };
        const messageHash = channelId + ':b:e';
        const url = this.urls['api']['ws'];
        return await this.watch (url, messageHash, subscribe, channelId);
    }

    async watchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        // rewrite
        throw new NotImplemented (this.id + 'watchTickers not implemented yet');
        // const market = this.market (symbol);
        // const numericId = market['info']['id'].toString ();
        // const url = this.urls['api']['websocket']['public'];
        // return await this.WsTickerMessage (url, '1002' + numericId, {
        //     'command': 'subscribe',
        //     'channel': 1002,
        // });
    }

    async loadMarkets (reload = false, params = {}) {
        const markets = await super.loadMarkets (reload, params);
        let marketsByNumericId = this.safeValue (this.options, 'marketsByNumericId');
        if ((marketsByNumericId === undefined) || reload) {
            marketsByNumericId = {};
            for (let i = 0; i < this.symbols.length; i++) {
                const symbol = this.symbols[i];
                const market = this.markets[symbol];
                const numericId = this.safeString (market, 'numericId');
                marketsByNumericId[numericId] = market;
            }
            this.options['marketsByNumericId'] = marketsByNumericId;
        }
        return markets;
    }

    async watchTrades (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const numericId = this.safeString (market, 'numericId');
        const messageHash = 'trades:' + numericId;
        const url = this.urls['api']['ws'];
        const subscribe = {
            'command': 'subscribe',
            'channel': numericId,
        };
        return await this.watch (url, messageHash, subscribe, numericId);
    }

    async watchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const numericId = this.safeString (market, 'numericId');
        const messageHash = 'orderbook:' + numericId;
        const url = this.urls['api']['ws'];
        const subscribe = {
            'command': 'subscribe',
            'channel': numericId,
        };
        const future = this.watch (url, messageHash, subscribe, numericId);
        return await this.after (future, this.limitOrderBook, symbol, limit, params);
    }

    limitOrderBook (orderbook, symbol, limit = undefined, params = {}) {
        return orderbook.limit (limit);
    }

    async watchHeartbeat (params = {}) {
        await this.loadMarkets ();
        const channelId = '1010';
        const url = this.urls['api']['ws'];
        return await this.watch (url, channelId);
    }

    signMessage (client, messageHash, message, params = {}) {
        if (messageHash.indexOf ('1000') === 0) {
            const throwOnError = false;
            if (this.checkRequiredCredentials (throwOnError)) {
                const nonce = this.nonce ();
                const payload = this.urlencode ({ 'nonce': nonce });
                const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha512');
                message = this.extend (message, {
                    'key': this.apiKey,
                    'payload': payload,
                    'sign': signature,
                });
            }
        }
        return message;
    }

    handleHeartbeat (client, message) {
        //
        // every second (approx) if no other updates are sent
        //
        //     [ 1010 ]
        //
        const channelId = '1010';
        client.resolve (message, channelId);
    }

    handleTrade (client, trade, market = undefined) {
        //
        // public trades
        //
        //     [
        //         "t", // trade
        //         "42706057", // id
        //         1, // 1 = buy, 0 = sell
        //         "0.05567134", // price
        //         "0.00181421", // amount
        //         1522877119, // timestamp
        //     ]
        //
        const id = trade[1].toString ();
        const side = trade[2] ? 'buy' : 'sell';
        const price = parseFloat (trade[3]);
        const amount = parseFloat (trade[4]);
        const timestamp = trade[5] * 1000;
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        return {
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'id': id,
            'order': undefined,
            'type': undefined,
            'takerOrMaker': undefined,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': price * amount,
            'fee': undefined,
        };
    }

    handleOrderBookAndTrades (client, message) {
        //
        // first response
        //
        //     [
        //         14, // channelId === market['numericId']
        //         8767, // nonce
        //         [
        //             [
        //                 "i", // initial snapshot
        //                 {
        //                     "currencyPair": "BTC_BTS",
        //                     "orderBook": [
        //                         { "0.00001853": "2537.5637", "0.00001854": "1567238.172367" }, // asks, price, size
        //                         { "0.00001841": "3645.3647", "0.00001840": "1637.3647" } // bids
        //                     ]
        //                 }
        //             ]
        //         ]
        //     ]
        //
        // subsequent updates
        //
        //     [
        //         14,
        //         8768,
        //         [
        //             [ "o", 1, "0.00001823", "5534.6474" ], // orderbook delta, bids, price, size
        //             [ "o", 0, "0.00001824", "6575.464" ], // orderbook delta, asks, price, size
        //             [ "t", "42706057", 1, "0.05567134", "0.00181421", 1522877119 ] // trade, id, side (1 for buy, 0 for sell), price, size, timestamp
        //         ]
        //     ]
        //
        const marketId = message[0].toString ();
        const nonce = message[1];
        const data = message[2];
        const market = this.safeValue (this.options['marketsByNumericId'], marketId);
        const symbol = this.safeString (market, 'symbol');
        let orderbookUpdatesCount = 0;
        let tradesCount = 0;
        for (let i = 0; i < data.length; i++) {
            const delta = data[i];
            if (delta[0] === 'i') {
                const snapshot = this.safeValue (delta[1], 'orderBook', []);
                const sides = [ 'asks', 'bids' ];
                this.orderbooks[symbol] = this.orderBook ();
                const orderbook = this.orderbooks[symbol];
                for (let j = 0; j < snapshot.length; j++) {
                    const side = sides[j];
                    const bookside = orderbook[side];
                    const orders = snapshot[j];
                    const prices = Object.keys (orders);
                    for (let k = 0; k < prices.length; k++) {
                        const price = prices[k];
                        const amount = orders[price];
                        bookside.store (parseFloat (price), parseFloat (amount));
                    }
                }
                orderbook['nonce'] = nonce;
                orderbookUpdatesCount += 1;
            } else if (delta[0] === 'o') {
                const orderbook = this.orderbooks[symbol];
                const side = delta[1] ? 'bids' : 'asks';
                const bookside = orderbook[side];
                const price = parseFloat (delta[2]);
                const amount = parseFloat (delta[3]);
                bookside.store (price, amount);
                orderbookUpdatesCount += 1;
                orderbook['nonce'] = nonce;
            } else if (delta[0] === 't') {
                // todo: add max limit to the dequeue of trades, unshift and push
                // const trade = this.handleTrade (client, delta, market);
                // this.trades.push (trade);
                tradesCount += 1;
            }
        }
        if (orderbookUpdatesCount) {
            // resolve the orderbook future
            const messageHash = 'orderbook:' + marketId;
            const orderbook = this.orderbooks[symbol];
            // the .limit () operation will be moved to the watchOrderBook
            client.resolve (orderbook, messageHash);
        }
        if (tradesCount) {
            // resolve the trades future
            const messageHash = 'trades:' + marketId;
            // todo: incremental trades
            client.resolve (this.trades, messageHash);
        }
    }

    handleAccountNotifications (client, message) {
        // not implemented yet
        // throw new NotImplemented (this.id + 'watchTickers not implemented yet');
        return message;
    }

    handleMessage (client, message) {
        const channelId = this.safeString (message, 0);
        const market = this.safeValue (this.options['marketsByNumericId'], channelId);
        if (market === undefined) {
            const methods = {
                // '<numericId>': 'handleOrderBookAndTrades', // Price Aggregated Book
                '1000': this.handleAccountNotifications, // Beta
                '1002': this.handleTickers, // Ticker Data
                // '1003': undefined, // 24 Hour Exchange Volume
                '1010': this.handleHeartbeat,
            };
            const method = this.safeValue (methods, channelId);
            if (method === undefined) {
                return message;
            } else {
                method.apply (this, client, message);
            }
        } else {
            return this.handleOrderBookAndTrades (client, message);
        }
    }
};
<commit_msg>poloniex has['fetchTicker'] = false for now<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const ccxt = require ('ccxt');
const { NotImplemented } = require ('ccxt/js/base/errors');

//  ---------------------------------------------------------------------------

module.exports = class poloniex extends ccxt.poloniex {
    describe () {
        return this.deepExtend (super.describe (), {
            'has': {
                'ws': true,
                'watchTicker': false,
                'watchOrderBook': true,
                'watchBalance': false, // not implemented yet
            },
            'urls': {
                'api': {
                    'ws': 'wss://api2.poloniex.com',
                },
            },
        });
    }

    handleTickers (client, response) {
        const data = response[2];
        const market = this.safeValue (this.options['marketsByNumericId'], data[0].toString ());
        const symbol = this.safeString (market, 'symbol');
        return {
            'info': response,
            'symbol': symbol,
            'last': parseFloat (data[1]),
            'ask': parseFloat (data[2]),
            'bid': parseFloat (data[3]),
            'change': parseFloat (data[4]),
            'baseVolume': parseFloat (data[5]),
            'quoteVolume': parseFloat (data[6]),
            'active': data[7] ? false : true,
            'high': parseFloat (data[8]),
            'low': parseFloat (data[9]),
        };
    }

    async watchBalance (params = {}) {
        await this.loadMarkets ();
        this.balance = await this.fetchBalance (params);
        const channelId = '1000';
        const subscribe = {
            'command': 'subscribe',
            'channel': channelId,
        };
        const messageHash = channelId + ':b:e';
        const url = this.urls['api']['ws'];
        return await this.watch (url, messageHash, subscribe, channelId);
    }

    async watchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        // rewrite
        throw new NotImplemented (this.id + 'watchTickers not implemented yet');
        // const market = this.market (symbol);
        // const numericId = market['info']['id'].toString ();
        // const url = this.urls['api']['websocket']['public'];
        // return await this.WsTickerMessage (url, '1002' + numericId, {
        //     'command': 'subscribe',
        //     'channel': 1002,
        // });
    }

    async loadMarkets (reload = false, params = {}) {
        const markets = await super.loadMarkets (reload, params);
        let marketsByNumericId = this.safeValue (this.options, 'marketsByNumericId');
        if ((marketsByNumericId === undefined) || reload) {
            marketsByNumericId = {};
            for (let i = 0; i < this.symbols.length; i++) {
                const symbol = this.symbols[i];
                const market = this.markets[symbol];
                const numericId = this.safeString (market, 'numericId');
                marketsByNumericId[numericId] = market;
            }
            this.options['marketsByNumericId'] = marketsByNumericId;
        }
        return markets;
    }

    async watchTrades (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const numericId = this.safeString (market, 'numericId');
        const messageHash = 'trades:' + numericId;
        const url = this.urls['api']['ws'];
        const subscribe = {
            'command': 'subscribe',
            'channel': numericId,
        };
        return await this.watch (url, messageHash, subscribe, numericId);
    }

    async watchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const numericId = this.safeString (market, 'numericId');
        const messageHash = 'orderbook:' + numericId;
        const url = this.urls['api']['ws'];
        const subscribe = {
            'command': 'subscribe',
            'channel': numericId,
        };
        const future = this.watch (url, messageHash, subscribe, numericId);
        return await this.after (future, this.limitOrderBook, symbol, limit, params);
    }

    limitOrderBook (orderbook, symbol, limit = undefined, params = {}) {
        return orderbook.limit (limit);
    }

    async watchHeartbeat (params = {}) {
        await this.loadMarkets ();
        const channelId = '1010';
        const url = this.urls['api']['ws'];
        return await this.watch (url, channelId);
    }

    signMessage (client, messageHash, message, params = {}) {
        if (messageHash.indexOf ('1000') === 0) {
            const throwOnError = false;
            if (this.checkRequiredCredentials (throwOnError)) {
                const nonce = this.nonce ();
                const payload = this.urlencode ({ 'nonce': nonce });
                const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha512');
                message = this.extend (message, {
                    'key': this.apiKey,
                    'payload': payload,
                    'sign': signature,
                });
            }
        }
        return message;
    }

    handleHeartbeat (client, message) {
        //
        // every second (approx) if no other updates are sent
        //
        //     [ 1010 ]
        //
        const channelId = '1010';
        client.resolve (message, channelId);
    }

    handleTrade (client, trade, market = undefined) {
        //
        // public trades
        //
        //     [
        //         "t", // trade
        //         "42706057", // id
        //         1, // 1 = buy, 0 = sell
        //         "0.05567134", // price
        //         "0.00181421", // amount
        //         1522877119, // timestamp
        //     ]
        //
        const id = trade[1].toString ();
        const side = trade[2] ? 'buy' : 'sell';
        const price = parseFloat (trade[3]);
        const amount = parseFloat (trade[4]);
        const timestamp = trade[5] * 1000;
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        return {
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'id': id,
            'order': undefined,
            'type': undefined,
            'takerOrMaker': undefined,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': price * amount,
            'fee': undefined,
        };
    }

    handleOrderBookAndTrades (client, message) {
        //
        // first response
        //
        //     [
        //         14, // channelId === market['numericId']
        //         8767, // nonce
        //         [
        //             [
        //                 "i", // initial snapshot
        //                 {
        //                     "currencyPair": "BTC_BTS",
        //                     "orderBook": [
        //                         { "0.00001853": "2537.5637", "0.00001854": "1567238.172367" }, // asks, price, size
        //                         { "0.00001841": "3645.3647", "0.00001840": "1637.3647" } // bids
        //                     ]
        //                 }
        //             ]
        //         ]
        //     ]
        //
        // subsequent updates
        //
        //     [
        //         14,
        //         8768,
        //         [
        //             [ "o", 1, "0.00001823", "5534.6474" ], // orderbook delta, bids, price, size
        //             [ "o", 0, "0.00001824", "6575.464" ], // orderbook delta, asks, price, size
        //             [ "t", "42706057", 1, "0.05567134", "0.00181421", 1522877119 ] // trade, id, side (1 for buy, 0 for sell), price, size, timestamp
        //         ]
        //     ]
        //
        const marketId = message[0].toString ();
        const nonce = message[1];
        const data = message[2];
        const market = this.safeValue (this.options['marketsByNumericId'], marketId);
        const symbol = this.safeString (market, 'symbol');
        let orderbookUpdatesCount = 0;
        let tradesCount = 0;
        for (let i = 0; i < data.length; i++) {
            const delta = data[i];
            if (delta[0] === 'i') {
                const snapshot = this.safeValue (delta[1], 'orderBook', []);
                const sides = [ 'asks', 'bids' ];
                this.orderbooks[symbol] = this.orderBook ();
                const orderbook = this.orderbooks[symbol];
                for (let j = 0; j < snapshot.length; j++) {
                    const side = sides[j];
                    const bookside = orderbook[side];
                    const orders = snapshot[j];
                    const prices = Object.keys (orders);
                    for (let k = 0; k < prices.length; k++) {
                        const price = prices[k];
                        const amount = orders[price];
                        bookside.store (parseFloat (price), parseFloat (amount));
                    }
                }
                orderbook['nonce'] = nonce;
                orderbookUpdatesCount += 1;
            } else if (delta[0] === 'o') {
                const orderbook = this.orderbooks[symbol];
                const side = delta[1] ? 'bids' : 'asks';
                const bookside = orderbook[side];
                const price = parseFloat (delta[2]);
                const amount = parseFloat (delta[3]);
                bookside.store (price, amount);
                orderbookUpdatesCount += 1;
                orderbook['nonce'] = nonce;
            } else if (delta[0] === 't') {
                // todo: add max limit to the dequeue of trades, unshift and push
                // const trade = this.handleTrade (client, delta, market);
                // this.trades.push (trade);
                tradesCount += 1;
            }
        }
        if (orderbookUpdatesCount) {
            // resolve the orderbook future
            const messageHash = 'orderbook:' + marketId;
            const orderbook = this.orderbooks[symbol];
            // the .limit () operation will be moved to the watchOrderBook
            client.resolve (orderbook, messageHash);
        }
        if (tradesCount) {
            // resolve the trades future
            const messageHash = 'trades:' + marketId;
            // todo: incremental trades
            client.resolve (this.trades, messageHash);
        }
    }

    handleAccountNotifications (client, message) {
        // not implemented yet
        // throw new NotImplemented (this.id + 'watchTickers not implemented yet');
        return message;
    }

    handleMessage (client, message) {
        const channelId = this.safeString (message, 0);
        const market = this.safeValue (this.options['marketsByNumericId'], channelId);
        if (market === undefined) {
            const methods = {
                // '<numericId>': 'handleOrderBookAndTrades', // Price Aggregated Book
                '1000': this.handleAccountNotifications, // Beta
                '1002': this.handleTickers, // Ticker Data
                // '1003': undefined, // 24 Hour Exchange Volume
                '1010': this.handleHeartbeat,
            };
            const method = this.safeValue (methods, channelId);
            if (method === undefined) {
                return message;
            } else {
                method.apply (this, client, message);
            }
        } else {
            return this.handleOrderBookAndTrades (client, message);
        }
    }
};
<|endoftext|>


<commit_before>$(document).ready(function(){
    $('#myCarousel-1').carousel({
    	interval: 1000
	});

	$('#myCarousel-2').carousel({
		interval: 1300
	});

	$('#myCarousel-3').carousel({
		interval: 1500
	});

	$('#myCarousel-4').carousel({
		interval: 1200
	});

	$('#myCarousel-5').carousel({
		interval: 1000
	});

	$('#myCarousel-6').carousel({
		interval: 1500
	});

	$('#myCarousel-7').carousel({
		interval: 1000
	});

	});

<commit_msg>ändrade intervaler<commit_after>$(document).ready(function(){
 $('#myCarousel-1').carousel({
    	interval: 3000
	});

	$('#myCarousel-2').carousel({
		interval: 4000
	});

	$('#myCarousel-3').carousel({
		interval: 4000
	});

	$('#myCarousel-4').carousel({
		interval: 3000
	});

	$('#myCarousel-5').carousel({
		interval: 3000
	});

	$('#myCarousel-6').carousel({
		interval: 4000
	});

	$('#myCarousel-7').carousel({
		interval: 8000
	});

	});
/*$('#myCarousel-1, #myCarousel-2, #myCarousel-3, #myCarousel-4, #myCarousel-5, #myCarousel-6, #myCarousel-7').carousel({
		interval: 4000
	});

	});*/
<|endoftext|>


<commit_before>// Tiny library for progress bars. Example usage:
/*

*/

function Progress(mainclass, barclass) {
  mainclass = mainclass || "";
  barclass = barclass || "";
  this.elem = document.createElement("div");
  this.elem.className = mainclass + " progressbar";
  this.bar = document.createElement("div");
  this.bar.className = barclass + " progressbar-indicator";
  this.elem.appendChild(this.bar);
  // Start at 0%
  this.bar.style.width = "0px";
  // Function to set progress
  this.progress = function(amount) {
    if (amount) {
      this.bar.style.width = this.elem.offsetWidth * amount + "px";
    } else {
      return this.bar.offsetWidth / this.elem.offsetWidth;
    }
  };
}
<commit_msg>Allow setting progress bar to 0%<commit_after>// Tiny library for progress bars. Example usage:
/*

*/

function Progress(mainclass, barclass) {
  mainclass = mainclass || "";
  barclass = barclass || "";
  this.elem = document.createElement("div");
  this.elem.className = mainclass + " progressbar";
  this.bar = document.createElement("div");
  this.bar.className = barclass + " progressbar-indicator";
  this.elem.appendChild(this.bar);
  // Start at 0%
  this.bar.style.width = "0px";
  // Function to set progress
  this.progress = function(amount) {
    if (amount === undefined) {
      this.bar.style.width = this.elem.offsetWidth * amount + "px";
    } else {
      return this.bar.offsetWidth / this.elem.offsetWidth;
    }
  };
}
<|endoftext|>


<commit_before>// Copyright (c) Martin Costello, 2017. All rights reserved.
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.

"use strict";

var api = require("./../api");
var responses = require("./../responses");

var intent = {
  name: "DisruptionIntent",
  enabled: true,
  slots: {},
  utterances: [
    "are there any {|line|tube} closures {|today}",
    "if there {are|is} {|any} {closures|disruption} {|today}"
  ]
};

/**
 * Generates the text to respond to the specified disruption response.
 * @param {Object[]} data - An array of disruptions.
 * @returns {String} The text response for the specified disuptions.
 */
intent.generateResponse = function (data) {

  if (!data || data.length === 0) {
    return "There is currently no disruption on the tube or the Docklands Light Railway."
  }

  // TODO For now just return the first disruption
  return data[0].description;
};

/**
 * Handles the intent for disruption.
 * @param {Object} request - The Alexa skill request.
 * @param {Object} response - The Alexa skill response.
 * @returns {Object} The result of the intent handler.
 */
intent.handler = function (request, response) {
  api.getDisruption(["dlr", "tube"])
    .then(function (data) {
      response
        .say(intent.generateResponse(data))
        .send();
    })
    .catch(function (err) {
      response.say(responses.onError);
    });
  return false;
};

module.exports = intent;
<commit_msg>Add more utterances for disruption<commit_after>// Copyright (c) Martin Costello, 2017. All rights reserved.
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.

"use strict";

var api = require("./../api");
var responses = require("./../responses");

var intent = {
  name: "DisruptionIntent",
  enabled: true,
  slots: {},
  utterances: [
    "about {|any} {|line|tube} {closures|disruption} {|today}",
    "are there any {|line|tube} closures {|today}",
    "if there {are|is} {|any} {|line|tube} {closures|disruption} {|today}"
  ]
};

/**
 * Generates the text to respond to the specified disruption response.
 * @param {Object[]} data - An array of disruptions.
 * @returns {String} The text response for the specified disuptions.
 */
intent.generateResponse = function (data) {

  if (!data || data.length === 0) {
    return "There is currently no disruption on the tube or the Docklands Light Railway."
  }

  // TODO For now just return the first disruption
  return data[0].description;
};

/**
 * Handles the intent for disruption.
 * @param {Object} request - The Alexa skill request.
 * @param {Object} response - The Alexa skill response.
 * @returns {Object} The result of the intent handler.
 */
intent.handler = function (request, response) {
  api.getDisruption(["dlr", "tube"])
    .then(function (data) {
      response
        .say(intent.generateResponse(data))
        .send();
    })
    .catch(function (err) {
      response.say(responses.onError);
    });
  return false;
};

module.exports = intent;
<|endoftext|>


<commit_before>/** @ignore */
const Command = require('./../Command');

class StatsCommand extends Command {
    constructor() {
        super('!', 'stats', [], {
            middleware: [
                'throttle.channel:1,5'
            ]
        });
    }

    onCommand(sender, message, args) {
        let members = this.getMemberStats();
        let channels = this.getChannelStats();
        let author = bot.Users.find(user => {
            return user.id === '88739639380172800';
        });
        let description = 'Created by Senither#8023 using the Discordie framework!';

        if (app.cache.has('github.commits')) {
            description = '**Latest changes:**\n';

            app.cache.get('github.commits').slice(0, 3).forEach(commit => {
                let message = commit.commit.message.split('\n')[0];
                description += `[\`${commit.sha.substr(0, 7)}\`](${commit.html_url}) ${message}\n`;
            });
        }

        message.channel.sendMessage('', false, {
            timestamp: new Date,
            color: 0x3498db,
            url: 'https://discordapp.com/invite/gt2FWER',
            title: 'Official Bot Server Invite',
            description: description.trim(),
            author: {
                icon_url: `https://cdn.discordapp.com/avatars/${author.id}/${author.avatar}.png?size=256`,
                name: `${author.username}#${author.discriminator}`
            },
            fields: [
                {
                    name: 'Members',
                    value: [
                        members.totalMembers + ' Total',
                        members.totalOnline + ' Online',
                        members.uniqueMembers + ' Unique',
                        members.uniqueOnline + ' Unique online'
                    ].join('\n'),
                    inline: true
                },
                {
                    name: 'Channels',
                    value: [
                        channels.totalChannels + ' Total',
                        channels.textChannels + ' Text',
                        channels.voiceChannels + ' Voice'
                    ].join('\n'),
                    inline: true
                },
                {
                    name: 'Uptime',
                    value: this.getProcessUptime(),
                    inline: true
                },
                {
                    name: 'Servers',
                    value: bot.Guilds.length,
                    inline: true
                },
                {
                    name: 'Commands Run',
                    value: app.bot.statistics.commands,
                    inline: true
                },
                {
                    name: 'Memory Usage',
                    value: this.getSystemMemoryUsage(),
                    inline: true
                }
            ],
            footer: {
                text: `Created using NodeJS and the Discordie framework`
            }
        });
    }

    getChannelStats() {
        return app.cache.remember('bot.stats.channels', 60, () => {
            let channels = bot.Channels.toArray();
            let textChannels = channels.reduce((a, channel) => {
                return (channel.constructor.name === 'ITextChannel') ? a + 1 : a;
            }, 0);

            return {
                totalChannels: channels.length,
                textChannels: textChannels,
                voiceChannels: channels.length - textChannels
            };
        });
    }

    getMemberStats() {
        return app.cache.remember('bot.stats.members', 60, () => {
            let guildMembers = bot.Guilds.map(guild => {
                return guild.members;
            });

            return {
                totalMembers: guildMembers.reduce((a, b) => {
                    return a + b.length;
                }, 0),
                totalOnline: guildMembers.reduce((a, b) => {
                    return a + b.reduce((c, member) => {
                        return (member.status === 'offline') ? c : c + 1;
                    }, 0);
                }, 0),

                uniqueMembers: bot.Users.length,
                uniqueOnline: bot.Users.toArray().reduce((a, member) => {
                    return (member.status === 'offline') ? a : a + 1;
                }, 0)
            };
        });
    }

    getProcessUptime() {
        let seconds = process.uptime();

        let d = Math.floor(seconds / 86400);
        let h = Math.floor((seconds % 86400) / 3600);
        let m = Math.floor(((seconds % 86400) % 3600) / 60);
        let s = Math.floor(((seconds % 86400) % 3600) % 60);

        if (d > 0) {
            return `${d}d ${h}h ${m}m ${s}s`;
        }

        if (h > 0) {
            return `${h}h ${m}m ${s}s`;
        }

        if (m > 0) {
            return `${m}m ${s}s`;
        }

        return `${s}s`;
    }

    getSystemMemoryUsage() {
        let memoryInBytes = process.memoryUsage().heapTotal - process.memoryUsage().heapUsed;
        let sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];

        let k = 1000;
        let i = Math.floor(Math.log(memoryInBytes) / Math.log(k));

        return parseFloat((memoryInBytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
}

module.exports = StatsCommand;
<commit_msg>Add limit to commit messages for the stats command<commit_after>/** @ignore */
const Command = require('./../Command');

class StatsCommand extends Command {
    constructor() {
        super('!', 'stats', [], {
            middleware: [
                'throttle.channel:1,5'
            ]
        });
    }

    onCommand(sender, message, args) {
        let members = this.getMemberStats();
        let channels = this.getChannelStats();
        let author = bot.Users.find(user => {
            return user.id === '88739639380172800';
        });
        let description = 'Created by Senither#8023 using the Discordie framework!';

        if (app.cache.has('github.commits')) {
            description = '**Latest changes:**\n';

            app.cache.get('github.commits').slice(0, 3).forEach(commit => {
                let message = commit.commit.message.split('\n')[0];

                if (message.length > 72) {
                    message = message.substr(0, 69) + '...';
                }

                description += `[\`${commit.sha.substr(0, 7)}\`](${commit.html_url}) ${message}\n`;
            });
        }

        message.channel.sendMessage('', false, {
            timestamp: new Date,
            color: 0x3498db,
            url: 'https://discordapp.com/invite/gt2FWER',
            title: 'Official Bot Server Invite',
            description: description.trim(),
            author: {
                icon_url: `https://cdn.discordapp.com/avatars/${author.id}/${author.avatar}.png?size=256`,
                name: `${author.username}#${author.discriminator}`
            },
            fields: [
                {
                    name: 'Members',
                    value: [
                        members.totalMembers + ' Total',
                        members.totalOnline + ' Online',
                        members.uniqueMembers + ' Unique',
                        members.uniqueOnline + ' Unique online'
                    ].join('\n'),
                    inline: true
                },
                {
                    name: 'Channels',
                    value: [
                        channels.totalChannels + ' Total',
                        channels.textChannels + ' Text',
                        channels.voiceChannels + ' Voice'
                    ].join('\n'),
                    inline: true
                },
                {
                    name: 'Uptime',
                    value: this.getProcessUptime(),
                    inline: true
                },
                {
                    name: 'Servers',
                    value: bot.Guilds.length,
                    inline: true
                },
                {
                    name: 'Commands Run',
                    value: app.bot.statistics.commands,
                    inline: true
                },
                {
                    name: 'Memory Usage',
                    value: this.getSystemMemoryUsage(),
                    inline: true
                }
            ],
            footer: {
                text: `Created using NodeJS and the Discordie framework`
            }
        });
    }

    getChannelStats() {
        return app.cache.remember('bot.stats.channels', 60, () => {
            let channels = bot.Channels.toArray();
            let textChannels = channels.reduce((a, channel) => {
                return (channel.constructor.name === 'ITextChannel') ? a + 1 : a;
            }, 0);

            return {
                totalChannels: channels.length,
                textChannels: textChannels,
                voiceChannels: channels.length - textChannels
            };
        });
    }

    getMemberStats() {
        return app.cache.remember('bot.stats.members', 60, () => {
            let guildMembers = bot.Guilds.map(guild => {
                return guild.members;
            });

            return {
                totalMembers: guildMembers.reduce((a, b) => {
                    return a + b.length;
                }, 0),
                totalOnline: guildMembers.reduce((a, b) => {
                    return a + b.reduce((c, member) => {
                        return (member.status === 'offline') ? c : c + 1;
                    }, 0);
                }, 0),

                uniqueMembers: bot.Users.length,
                uniqueOnline: bot.Users.toArray().reduce((a, member) => {
                    return (member.status === 'offline') ? a : a + 1;
                }, 0)
            };
        });
    }

    getProcessUptime() {
        let seconds = process.uptime();

        let d = Math.floor(seconds / 86400);
        let h = Math.floor((seconds % 86400) / 3600);
        let m = Math.floor(((seconds % 86400) % 3600) / 60);
        let s = Math.floor(((seconds % 86400) % 3600) % 60);

        if (d > 0) {
            return `${d}d ${h}h ${m}m ${s}s`;
        }

        if (h > 0) {
            return `${h}h ${m}m ${s}s`;
        }

        if (m > 0) {
            return `${m}m ${s}s`;
        }

        return `${s}s`;
    }

    getSystemMemoryUsage() {
        let memoryInBytes = process.memoryUsage().heapTotal - process.memoryUsage().heapUsed;
        let sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];

        let k = 1000;
        let i = Math.floor(Math.log(memoryInBytes) / Math.log(k));

        return parseFloat((memoryInBytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
}

module.exports = StatsCommand;
<|endoftext|>


<commit_before>---
---
function slugify(text) {
  text = text.replace(/[^-a-zA-Z0-9\s+]+/ig, '');
  text = text.replace(/\s+/gi, "-");
  return text;
}

var cfapi_url = 'http://codeforamerica.org/api/organizations/Code-for-San-Francisco/projects';

// Go get projects! Then show them off.
$.getJSON(cfapi_url, showProjects);

function showProjects(response){

  projects = response.objects;
  // loop through our project data
  $.each(projects, function(i, project){
    // check to see if this project has a corresponding Jekyll page
    if(projectData[project['id']]) {
      project['cfsf_url'] = projectData[project['id']]['url'];
    }
    // add some variables to support adding a new Jekyll project post
    project['repository_nwo'] = {% if site.github.repository_nwo %}"{{site.github.repository_nwo}}"{% else %}" "{% endif %};
    project['default_branch'] = "{{site.defaultbranch}}";
    project['file_name'] = moment().format('YYYY-MM-DD-') + slugify(project['name']).toLowerCase() + ".md";
    // to display text like 'x days ago' we use moment.js's awesome fromNow function
    // http://momentjs.com/docs/#/displaying/fromnow/
    project['last_updated_formatted'] = moment(project['last_updated']).fromNow();

  });
  var featuredDiv = document.createElement('div');
  featuredDiv.id = 'featured'
  $("#hack-night-projects").append(featuredDiv);
  $("#hack-night-projects").append(ich.projects({projects:projects}));
  // Follow next page links
  if (response.pages.next) {
    $.getJSON(response.pages.next, showProjects);
  }
//
}
<commit_msg>filtering featured projects<commit_after>---
---
function slugify(text) {
  text = text.replace(/[^-a-zA-Z0-9\s+]+/ig, '');
  text = text.replace(/\s+/gi, "-");
  return text;
}

var cfapi_url = 'http://codeforamerica.org/api/organizations/Code-for-San-Francisco/projects';

// Go get projects! Then show them off.
$.getJSON(cfapi_url, showProjects);

function showProjects(response){
  projects = response.objects;
  // loop through our project data
  $.each(projects, function(i, project) {
    // check to see if this project has a corresponding Jekyll page
    if (projectData[project['id']]) {
      project['cfsf_url'] = projectData[project['id']]['url'];
    }
    // add some variables to support adding a new Jekyll project post
    project['repository_nwo'] = {% if site.github.repository_nwo %}"{{site.github.repository_nwo}}"{% else %}" "{% endif %};
    project['default_branch'] = "{{site.defaultbranch}}";
    project['file_name'] = moment().format('YYYY-MM-DD-') + slugify(project['name']).toLowerCase() + ".md";
    // to display text like 'x days ago' we use moment.js's awesome fromNow function
    // http://momentjs.com/docs/#/displaying/fromnow/
    project['last_updated_formatted'] = moment(project['last_updated']).fromNow();

  });
  featuredProjects = $.grep(projects, function(x) {
    return x['tags'].indexOf("featured") !== -1;
  });
  var featuredDiv = document.createElement('div');
  featuredDiv.id = 'featured'
  $("#hack-night-projects").append(featuredDiv);
  $("#hack-night-projects").append(ich.projects({projects:projects}));
  // Follow next page links
  if (response.pages.next) {
    $.getJSON(response.pages.next, showProjects);
  }
//
}
<|endoftext|>


<commit_before>import createRegl from 'regl';
import { PipelineBuilder } from './shaders';

export default class Renderer {
  constructor(canvas) {
    this.canvas = canvas;
    this.regl = createRegl({ canvas });
    console.log(`max texture size: ${this.regl.limits.maxTextureSize}`);
    console.log(`point size dims: ${this.regl.limits.pointSizeDims[0]} ${this.regl.limits.pointSizeDims[1]}`);
    this.imageData = null;
    this.state = {
      backgroundColor: [0, 0, 0, 1],
      particleScaling: 1
    };
    this.command = null;
    this.regl.frame(() => {
      if (this.command === null) {
        return;
      }
      this.regl.clear({ color: this.state.backgroundColor });
      this.command();
    });
  }

  createImageData(img) {
    this.destroyImageData();

    const dataCanvas = document.createElement('canvas');
    const dataContext = dataCanvas.getContext('2d');
    dataCanvas.width = img.naturalWidth;
    dataCanvas.height = img.naturalHeight;
    // flipped y-axis
    dataContext.translate(0, img.naturalHeight);
    dataContext.scale(1, -1);
    dataContext.drawImage(img, 0, 0);
    const imgData = dataContext.getImageData(0, 0, img.naturalWidth, img.naturalHeight);

    const w = imgData.width;
    const h = imgData.height;

    const imagePixels = imgData.data;

    const pixelIndices = Array.from(Array(w * h).keys());

    const texcoords = pixelIndices.map((i) => [((i % w) + 0.5) / w, (Math.floor(i / w) + 0.5) / h]);

    const rgb = pixelIndices.map((i) => {
      const pixel = imagePixels.slice(i * 4, (i * 4) + 4);

      return [pixel[0] / 255, pixel[1] / 255, pixel[2] / 255];
    });

    const hsv = pixelIndices.map((i) => {
      const pixel = rgb[i];

      const cMax = Math.max(pixel[0], pixel[1], pixel[2]);
      const cMin = Math.min(pixel[0], pixel[1], pixel[2]);
      const d = cMax - cMin;

      if (d < 0.00001 || cMax < 0.00001) {
        return [0, 0, cMax];
      }

      let _h;
      if (cMax === pixel[0]) {
        _h = (pixel[1] - pixel[2]) / d;
        if (_h < 0) {
          _h += 6;
        }
      } else if (cMax === pixel[1]) {
        _h = ((pixel[2] - pixel[0]) / d) + 2;
      } else {
        _h = ((pixel[0] - pixel[1]) / d) + 4;
      }

      return [_h * 60, d / cMax, cMax];
    });

    this.imageData = {
      width: w,
      height: h,
      aspectRatio: w / h,
      texcoordsBuffer: this.regl.buffer(texcoords),
      rgbBuffer: this.regl.buffer(rgb),
      hsvBuffer: this.regl.buffer(hsv)
    };
  }

  destroyImageData() {
    if (this.imageData !== null) {
      this.imageData.texcoordsBuffer.destroy();
      this.imageData.rgbBuffer.destroy();
      this.imageData.hsvBuffer.destroy();
      this.imageData = null;
    }
  }

  rebuildCommand() {
    const data = this.imageData;
    const pipeline = PipelineBuilder.build(this.state);
    const cmd = Object.assign(pipeline, {
      uniforms: {
        time(ctx) {
          return ctx.time;
        },
        invImageAspectRatio(/* ctx */) {
          return 1 / data.aspectRatio;
        },
        viewProjectionMatrix(ctx) {
          const underscan = 1 - (ctx.viewportWidth / ctx.viewportHeight) / (data.width / data.height);
          return [2, 0, 0, 0,
            0, 2 * (ctx.viewportWidth / ctx.viewportHeight), 0, 0,
            0, 0, 1, 0,
            -1, underscan * 2 - 1, 0, 1];
        },
        particleSize(ctx) {
          return (ctx.viewportWidth / data.width) * 2 * this.state.particleScaling;
        }
      },
      attributes: {
        texcoord: data.texcoordsBuffer,
        rgb: data.rgbBuffer,
        hsv: data.hsvBuffer
      },
      count: data.width * data.height
    });
    this.command = this.regl(cmd);
  }

  loadImage(img) {
    this.createImageData(img);
    this.rebuildCommand();
  }

  setState(state) {
    const oldState = this.state;
    this.state = state;
    if (state.particleOverlap !== oldState.particleOverlap) {
      this.rebuildCommand();
    }
  }
}
<commit_msg>Log supported maximum number of uniforms<commit_after>import createRegl from 'regl';
import { PipelineBuilder } from './shaders';

export default class Renderer {
  constructor(canvas) {
    this.canvas = canvas;
    this.regl = createRegl({ canvas });
    console.log(`max texture size: ${this.regl.limits.maxTextureSize}`);
    console.log(`point size dims: ${this.regl.limits.pointSizeDims[0]} ${this.regl.limits.pointSizeDims[1]}`);
    console.log(`max uniforms: ${this.regl.limits.maxVertexUniforms} ${this.regl.limits.maxFragmentUniforms}`);
    this.imageData = null;
    this.state = {
      backgroundColor: [0, 0, 0, 1],
      particleScaling: 1
    };
    this.command = null;
    this.regl.frame(() => {
      if (this.command === null) {
        return;
      }
      this.regl.clear({ color: this.state.backgroundColor });
      this.command();
    });
  }

  createImageData(img) {
    this.destroyImageData();

    const dataCanvas = document.createElement('canvas');
    const dataContext = dataCanvas.getContext('2d');
    dataCanvas.width = img.naturalWidth;
    dataCanvas.height = img.naturalHeight;
    // flipped y-axis
    dataContext.translate(0, img.naturalHeight);
    dataContext.scale(1, -1);
    dataContext.drawImage(img, 0, 0);
    const imgData = dataContext.getImageData(0, 0, img.naturalWidth, img.naturalHeight);

    const w = imgData.width;
    const h = imgData.height;

    const imagePixels = imgData.data;

    const pixelIndices = Array.from(Array(w * h).keys());

    const texcoords = pixelIndices.map((i) => [((i % w) + 0.5) / w, (Math.floor(i / w) + 0.5) / h]);

    const rgb = pixelIndices.map((i) => {
      const pixel = imagePixels.slice(i * 4, (i * 4) + 4);

      return [pixel[0] / 255, pixel[1] / 255, pixel[2] / 255];
    });

    const hsv = pixelIndices.map((i) => {
      const pixel = rgb[i];

      const cMax = Math.max(pixel[0], pixel[1], pixel[2]);
      const cMin = Math.min(pixel[0], pixel[1], pixel[2]);
      const d = cMax - cMin;

      if (d < 0.00001 || cMax < 0.00001) {
        return [0, 0, cMax];
      }

      let _h;
      if (cMax === pixel[0]) {
        _h = (pixel[1] - pixel[2]) / d;
        if (_h < 0) {
          _h += 6;
        }
      } else if (cMax === pixel[1]) {
        _h = ((pixel[2] - pixel[0]) / d) + 2;
      } else {
        _h = ((pixel[0] - pixel[1]) / d) + 4;
      }

      return [_h * 60, d / cMax, cMax];
    });

    this.imageData = {
      width: w,
      height: h,
      aspectRatio: w / h,
      texcoordsBuffer: this.regl.buffer(texcoords),
      rgbBuffer: this.regl.buffer(rgb),
      hsvBuffer: this.regl.buffer(hsv)
    };
  }

  destroyImageData() {
    if (this.imageData !== null) {
      this.imageData.texcoordsBuffer.destroy();
      this.imageData.rgbBuffer.destroy();
      this.imageData.hsvBuffer.destroy();
      this.imageData = null;
    }
  }

  rebuildCommand() {
    const data = this.imageData;
    const pipeline = PipelineBuilder.build(this.state);
    const cmd = Object.assign(pipeline, {
      uniforms: {
        time(ctx) {
          return ctx.time;
        },
        invImageAspectRatio(/* ctx */) {
          return 1 / data.aspectRatio;
        },
        viewProjectionMatrix(ctx) {
          const underscan = 1 - (ctx.viewportWidth / ctx.viewportHeight) / (data.width / data.height);
          return [2, 0, 0, 0,
            0, 2 * (ctx.viewportWidth / ctx.viewportHeight), 0, 0,
            0, 0, 1, 0,
            -1, underscan * 2 - 1, 0, 1];
        },
        particleSize(ctx) {
          return (ctx.viewportWidth / data.width) * 2 * this.state.particleScaling;
        }
      },
      attributes: {
        texcoord: data.texcoordsBuffer,
        rgb: data.rgbBuffer,
        hsv: data.hsvBuffer
      },
      count: data.width * data.height
    });
    this.command = this.regl(cmd);
  }

  loadImage(img) {
    this.createImageData(img);
    this.rebuildCommand();
  }

  setState(state) {
    const oldState = this.state;
    this.state = state;
    if (state.particleOverlap !== oldState.particleOverlap) {
      this.rebuildCommand();
    }
  }
}
<|endoftext|>


<commit_before>var Renderer = function (canvas) {
  let self = this;

  this.canvas = canvas;

  this.stats = new Stats();
  this.stats.domElement.style.position = 'absolute';
  this.stats.domElement.style.bottom = '0px';
  document.body.appendChild(this.stats.domElement);

  this.renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true, preserveDrawingBuffer: true, alpha: true});

  this.camera = new THREE.PerspectiveCamera(50, 1, 1, 10000);
  // this.camera = new THREE.OrthographicCamera(GameState.WIDTH / -2, GameState.WIDTH / 2, GameState.HEIGHT / 2, GameState.HEIGHT / -2, 1, 1000);
  this.camera.position.set(GameState.WIDTH / 2, GameState.HEIGHT / 2 + 360, -520);
  this.camera.rotation.set(Math.PI + 0.5, 0, 0);

  this.camera2 = this.camera.clone();
  this.camera2.position = this.camera.position;
  this.camera2.rotation = this.camera.rotation;

  // this.cameraControls = new THREE.TrackballControls(this.camera, canvas);

  window.addEventListener('resize', function () {
    self.resize();
  });
};

Renderer.prototype.createGradient = function(scene) {
  var canvas = document.createElement( 'canvas' );
  canvas.width = 128;
  canvas.height = 128;
  var context = canvas.getContext( '2d' );
  var gradient = context.createLinearGradient( canvas.width / 2, canvas.height / 2, 0, canvas.width / 2);
  gradient.addColorStop( 0, '#EAECC6' );
  gradient.addColorStop( 1, '#2BC0E4' );
  context.fillStyle = gradient;
  context.fillRect( 0, 0, canvas.width, canvas.height );
  var shadowTexture = new THREE.Texture( canvas );
  shadowTexture.needsUpdate = true;
  var shadowMaterial = new THREE.MeshBasicMaterial( { map: shadowTexture } );
  var shadowGeo = new THREE.PlaneGeometry( GameState.HEIGHT * 1.5, GameState.WIDTH * 1.8, 1, 1 );

  let mesh = new THREE.Mesh( shadowGeo, shadowMaterial );
  mesh.position.set(GameState.WIDTH / 2, GameState.HEIGHT / 2, 10);
  mesh.rotation.set(-Math.PI, 0, Math.PI/2);
  scene.add( mesh );
};

Renderer.prototype.initScene = function (scene, effectsScene) {
  this.scene = scene;

  // this.createGradient(scene);

  // multi-pass technique based on: https://stemkoski.github.io/Three.js/Selective-Glow.html

  // prepare secondary composer
  var renderTargetParameters = {
    minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter,
    format: THREE.RGBFormat, stencilBuffer: false
  };
  this.renderTarget = new THREE.WebGLRenderTarget(GameState.WIDTH, GameState.HEIGHT, renderTargetParameters);
  this.effectsComposer = new THREE.EffectComposer(this.renderer, this.renderTarget);

  // prepare the secondary render's passes
  var render2Pass = new THREE.RenderPass(effectsScene, this.camera);
  this.effectsComposer.addPass(render2Pass);

  // special effects to be applied to secondary render:
  this.effectsComposer.addPass(new THREE.BloomPass(1, 25, 4.0, 1024));

  this.composer = new THREE.EffectComposer( this.renderer );

  this.composer.addPass( new THREE.RenderPass( this.scene, this.camera ) );

  var effectBlend = new THREE.ShaderPass( THREE.AdditiveBlendShader, "tDiffuse1" );
  effectBlend.uniforms[ 'tDiffuse2' ].value = this.effectsComposer.renderTarget2;
  this.composer.addPass( effectBlend );

  this.fxaa = new THREE.ShaderPass(THREE.FXAAShader);
  this.fxaa.renderToScreen = true;
  this.composer.addPass(this.fxaa);

  scene.add(this.camera);
  effectsScene.add(this.camera2);
};

Renderer.prototype.render = function () {
  this.cameraControls && this.cameraControls.update();
  this.effectsComposer.render();
  this.composer.render();
  this.stats.update();
};

Renderer.prototype.resize = function () {
  let width = window.innerWidth;
  let height = window.innerHeight;

  this.camera.left = width / -2;
  this.camera.right = width / 2;
  this.camera.top = height / 2;
  this.camera.bottom = height / -2;

  this.camera.aspect = width / height;
  this.camera.updateProjectionMatrix();

  // Camera position updates on its own with orthographic perspective
  // this.camera.position.x = width / 2;
  // this.camera.position.y = height / 2;

  // FIXME: THREE.EffectsComposer is incompatible with pixel ratio
  // this.renderer.setPixelRatio(window.devicePixelRatio);
  this.renderer.setSize(width, height);

  this.composer.setSize(width, height);
  this.effectsComposer.setSize(width, height);

  this.fxaa.uniforms.resolution.value.set(1 / width, 1 / height);
};
<commit_msg>Compute exact camera position for given angle<commit_after>var Renderer = function (canvas) {
  let self = this;

  this.canvas = canvas;

  this.stats = new Stats();
  this.stats.domElement.style.position = 'absolute';
  this.stats.domElement.style.bottom = '0px';
  document.body.appendChild(this.stats.domElement);

  this.renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true, preserveDrawingBuffer: true, alpha: true});

  this.camera = new THREE.PerspectiveCamera(45, 1, 1, 10000);
  // this.camera = new THREE.OrthographicCamera(GameState.WIDTH / -2, GameState.WIDTH / 2, GameState.HEIGHT / 2, GameState.HEIGHT / -2, 1, 1000);
  this._setCameraAngle(0.5);

  this.camera2 = this.camera.clone();
  this.camera2.position = this.camera.position;
  this.camera2.rotation = this.camera.rotation;

  // this.cameraControls = new THREE.TrackballControls(this.camera, canvas);

  window.addEventListener('resize', function () {
    self.resize();
  });
};

Renderer.prototype.createGradient = function(scene) {
  var canvas = document.createElement( 'canvas' );
  canvas.width = 128;
  canvas.height = 128;
  var context = canvas.getContext( '2d' );
  var gradient = context.createLinearGradient( canvas.width / 2, canvas.height / 2, 0, canvas.width / 2);
  gradient.addColorStop( 0, '#EAECC6' );
  gradient.addColorStop( 1, '#2BC0E4' );
  context.fillStyle = gradient;
  context.fillRect( 0, 0, canvas.width, canvas.height );
  var shadowTexture = new THREE.Texture( canvas );
  shadowTexture.needsUpdate = true;
  var shadowMaterial = new THREE.MeshBasicMaterial( { map: shadowTexture } );
  var shadowGeo = new THREE.PlaneGeometry( GameState.HEIGHT * 1.5, GameState.WIDTH * 1.8, 1, 1 );

  let mesh = new THREE.Mesh( shadowGeo, shadowMaterial );
  mesh.position.set(GameState.WIDTH / 2, GameState.HEIGHT / 2, 10);
  mesh.rotation.set(-Math.PI, 0, Math.PI/2);
  scene.add( mesh );
};

Renderer.prototype._setCameraAngle = function (theta) {
  let fov = this.camera.fov / 180 * Math.PI;
  let d = GameState.HEIGHT / (Math.tan(fov / 2 + theta) + Math.tan(fov / 2 - theta));
  let h = d * Math.tan(Math.PI / 8 + theta);
  this.camera.position.set(GameState.WIDTH / 2, h, -d);
  this.camera.rotation.set(Math.PI + theta, 0, 0);
};

Renderer.prototype.initScene = function (scene, effectsScene) {
  this.scene = scene;

  // this.createGradient(scene);

  // multi-pass technique based on: https://stemkoski.github.io/Three.js/Selective-Glow.html

  // prepare secondary composer
  var renderTargetParameters = {
    minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter,
    format: THREE.RGBFormat, stencilBuffer: false
  };
  this.renderTarget = new THREE.WebGLRenderTarget(GameState.WIDTH, GameState.HEIGHT, renderTargetParameters);
  this.effectsComposer = new THREE.EffectComposer(this.renderer, this.renderTarget);

  // prepare the secondary render's passes
  var render2Pass = new THREE.RenderPass(effectsScene, this.camera);
  this.effectsComposer.addPass(render2Pass);

  // special effects to be applied to secondary render:
  this.effectsComposer.addPass(new THREE.BloomPass(1, 25, 4.0, 1024));

  this.composer = new THREE.EffectComposer( this.renderer );

  this.composer.addPass( new THREE.RenderPass( this.scene, this.camera ) );

  var effectBlend = new THREE.ShaderPass( THREE.AdditiveBlendShader, "tDiffuse1" );
  effectBlend.uniforms[ 'tDiffuse2' ].value = this.effectsComposer.renderTarget2;
  this.composer.addPass( effectBlend );

  this.fxaa = new THREE.ShaderPass(THREE.FXAAShader);
  this.fxaa.renderToScreen = true;
  this.composer.addPass(this.fxaa);

  scene.add(this.camera);
  effectsScene.add(this.camera2);
};

Renderer.prototype.render = function () {
  this.cameraControls && this.cameraControls.update();
  this.effectsComposer.render();
  this.composer.render();
  this.stats.update();
};

Renderer.prototype.resize = function () {
  let width = window.innerWidth;
  let height = window.innerHeight;

  this.camera.left = width / -2;
  this.camera.right = width / 2;
  this.camera.top = height / 2;
  this.camera.bottom = height / -2;

  this.camera.aspect = width / height;
  this.camera.updateProjectionMatrix();

  // Camera position updates on its own with orthographic perspective
  // this.camera.position.x = width / 2;
  // this.camera.position.y = height / 2;

  // FIXME: THREE.EffectsComposer is incompatible with pixel ratio
  // this.renderer.setPixelRatio(window.devicePixelRatio);
  this.renderer.setSize(width, height);

  this.composer.setSize(width, height);
  this.effectsComposer.setSize(width, height);

  this.fxaa.uniforms.resolution.value.set(1 / width, 1 / height);
};
<|endoftext|>