<commit_before>var request = require('request'),
shim = require.once('../shim.js'),
WebSocket = require('ws'),
async = require.safe('async'),
sleep = require.safe('sleep'),
platform = null,
sockets = [],
eventReceivedCallback = null,
numSocketsToShutDown = 0,
shuttingDown = false,

sendMessage = function(message, thread) {
	var teamInfo = getChannelIdAndTeamId(thread);

	message.replace('<', '&lt');
	message.replace('>', '&gt');
	message.replace('&', '&amp');

	if (teamInfo.token != null) {
		var body = {
			"token": teamInfo.token,
			"channel": teamInfo.channel_id,
			"username": exports.config.name,
			"link_names": 1,
			"text": message,
			"unfurl_links": true,
			"icon_url": exports.config.icon
		};

		request({
			"uri": 'https://slack.com/api/chat.postMessage',
			"method": 'GET',
			"qs": body
		},
		function (error, response, body) {
			body = JSON.parse(body);
			if (response.statusCode != 200) {
				console.debug('error: ' + response.statusCode);
			}
			else if (!body.ok) {
				console.debug('Failed to send message to channel with id: ' + teamInfo.channel_id + ', error: ' + body.error);
			}
		});
	}
	else {
		console.debug('No slack team found!!!');
	}
},

getChannelIdAndTeamId = function(thread) {
	var slackTeams = exports.config.slack_teams,
	thread_components = thread.split('\0', 2),
	channelId = thread_components[0],
	teamId = thread_components[1],
	token = slackTeams[teamId].token;

	return { "channel_id": channelId, "team_id": teamId, "token": token };
},

createThreadId = function(channelId, teamId) {
	return channelId + '\0' + teamId;
},

sendFile = function(type, file, description, thread) {
	var teamInfo = getChannelIdAndTeamId(thread);

	if (teamInfo.token != null) {
		var body = {
			"token": teamInfo.token,
			"channel": teamInfo.channel_id,
			"file": file,
			"filetype": type,
			"title": description
		};

		request({
			"uri": 'https://slack.com/api/files.upload',
			"method": 'GET',
			"qs": body
		},
		function (error, response, body) {
			body = JSON.parse(body);
			if (response.statusCode != 200) {
				console.error('error: ' + response.statusCode);
			}
			else if (!body.ok) {
				console.warn('Failed send file to channel with id: ' + teamInfo.channel_id + ', error: ' + body.error);
			}
		});
	}
	else {
		console.warn('No slack team found!!!');
	}
},

renameChannel = function(title, thread) {
	var teamInfo = getChannelIdAndTeamId(thread);

	if (teamInfo.token != null) {
		var body = {
			"token": teamInfo.token,
			"channel": teamInfo.channel_id,
			"name": title
		};

		request({
			"uri": 'https://slack.com/api/channels.rename',
			"method": 'GET',
			"qs": body
		},
		function (error, response, body) {
			body = JSON.parse(body);
			if (response.statusCode != 200) {
				console.debug('error: ' + response.statusCode);
			}
			else if (!body.ok) {
				console.debug('Failed to rename channel with id: ' + teamInfo.channel_id + ', error: ' + body.error);
			}
		});
	}
	else {
		console.debug('No slack team found!!!');
	}
},

sendTyping = function(thread) {
	var slackTeams = exports.config.slack_teams,
	teamInfo = getChannelIdAndTeamId(thread),
	socket = sockets[teamInfo.team_id];

	if (socket != null) {
		var body = {};
		body.id = slackTeams[teamInfo.team_id].event_id++;
		body.type = "typing";
		body.channel = teamInfo.channel_id;

		console.log("typing");
		console.log(JSON.stringify(body));
		socket.send(JSON.stringify(body));
	}
	else {
		console.debug('No socket available for given team id');
	}
},

replaceUserIdWithUserName = function(userName, message, match) {
	if (userName) {
		var index = message.indexOf(match);
		return message.substr(0, index) + userName + message.substr(index + match.length);
	}
},

init = function(token, callback) {
	var body = {
		"token": token,
		mpim_aware: "true"
	};
	request({
		"uri": 'https://slack.com/api/rtm.start',
		"method": 'GET',
		"qs": body
	},
	function (error, response, body) {
		var teamId = null,
		slackTeams = exports.config.slack_teams,
		team;

		if (!slackTeams) {
			exports.config.slack_teams = {};
			slackTeams = exports.config.slack_teams;
		}

		try {
			body = JSON.parse(body);
		}
		catch(e) {
			console.debug("failed to parse message: " + body);
			console.critical(e);
			callback(false);
		}

		if (response.statusCode != 200) {
			console.debug('error: ' + response.statusCode);
			callback(false);
		}
		else if (!body.ok) {
			console.debug('Failed to connect to slack server with token: ' + token + ' error: ' + body.error);
			callback(false);
		}
		else {
			teamId = body.team.id;
			slackTeams[teamId] = body;
			team = slackTeams[teamId];
			team.token = token;
			team.event_id = 0;
			team.lastMessageSinceConnection = false;

			//Generate user map
			team.users = generateUserMap(body);
			team.allChannels = generateChannelsMap(body);

			// find slackbot
			findSlackBot(body, team);
			callback({"team_id": teamId, "url": body.url});
		}
	});
},

findSlackBot = function(body, slackTeam) {
	for (var i = 0; i < body.users.length; i++) {
		if (body.users[i].name === 'slackbot') {
			slackTeam.bot_id = body.users[i].id;
			break;
		}
	}
},

generateChannelsMap = function(data) {
	var channels = {};

	for (var i = 0; i < data.channels.length; i++) {
		channels[data.channels[i].id] = data.channels[i];
	}
	for (var j = 0; j < data.groups.length; j++) {
		channels[data.groups[j].id] = data.groups[j];
	}
	for (var k = 0; k < data.ims.length; k++) {
		channels[data.ims[k].id] = data.ims[k];
	}

	return channels;
},

generateUserMap = function(data) {
	var map = {};
	for (var i = 0; i < data.users.length; i++) {
		map[data.users[i].id] = data.users[i];
	}
	return map;
},

connect = function(connectionDetails) {
	var slackTeams = exports.config.slack_teams,
	s;
	console.debug("connect");

	if (connectionDetails) {
		(function (connectionDetails) {
			s = new WebSocket(connectionDetails.url);
			console.debug(connectionDetails.url);
			sockets[connectionDetails.team_id] = s;
			s.on('open', function() {
				console.debug("Connection to team: " + slackTeams[connectionDetails.team_id].team.name + " established");
			}).on('message', function(data) {
				eventReceived(JSON.parse(data), connectionDetails.team_id);
			}).on('close', function(data) {
				console.debug("Disconnected from team: " + slackTeams[connectionDetails.team_id].team.name);

				if (numSocketsToShutDown > 0) {
					numSocketsToShutDown--;
					return;
				}
				if (!shuttingDown) {
					init(slackTeams[connectionDetails.team_id].token, connect);
				}
			}).on('error', function(data) {
				console.debug("received error:\n" + data);
			});
		})(connectionDetails);
	}
	else {
		exports.start(eventReceivedCallback);
	}
},

openPrivateMessage = function(message, thread, senderId) {
	var slackTeams = exports.config.slack_teams,
	teamInfo = getChannelIdAndTeamId(thread);

	if (teamInfo.token != null) {
		var body = {
			"token": teamInfo.token,
			"user": senderId
		};

		request({
			"uri": 'https://slack.com/api/im.open',
			"method": 'GET',
			"qs": body
		},
		function (error, response, body) {
			body = JSON.parse(body);
			if (response.statusCode != 200) {
					console.debug('error: ' + response.statusCode);
			}
			else if (!body.ok) {
					console.debug('Failed to send message to user with id: ' + teamInfo.channel_id + ', error: ' + body.error);
			}
			else {
				slackTeams[teamInfo.team_id].channels[body.channel] = {"channel": body.channel, "user": senderId};
				sendMessage(message, createThreadId(body.channel.id, teamInfo.team_id));
			}
		});
	}
	else {
			console.debug('No slack team found!!!');
	}
},

eventReceived = function(event, teamId) {
	if (shuttingDown) return;
	switch (event.type) {
		case 'message':
		recMessage(event, teamId);
		break;
		case 'channel_created':
		channelCreated(event, teamId);
		break;
		case 'channel_deleted':
		channelDeleted(event, teamId);
		break;
		case 'channel_rename':
		channelRename(event, teamId);
		break;
		case 'im_created':
		channelCreated(event, teamId);
		break;
		case 'group_joined':
		channelCreated(event, teamId);
		break;
		case 'group_close':
		channelDeleted(event, teamId);
		break;
		case 'group_rename':
		channelRename(event, teamId);
		break;
		case 'user_change':
		userChange(event, teamId);
		break;
		case 'team_join':
		userChange(event, teamId);
		break;
		case 'team_rename':
		teamRename(event, teamId);
		break;
		default:

	}
},

teamRename = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	slackTeam.name = event.name;
},

userChange = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	slackTeam.users[event.user.id] = event.user;
},

channelCreated = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	slackTeam.channels[event.channel.id] = event.channel;
},

channelDeleted = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	removeHelper(slackTeam.channels, event.channel);
},

channelRename = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	slackTeam.channels[event.channel.id].name = event.channel.name;
},

removeHelper = function(object, key) {
	delete object.key;
},

recMessage = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId],
	userName,
	id,
	message = event.text,
	shimMessage;

	if (!slackTeam.lastMessageSinceConnection) {
		slackTeam.lastMessageSinceConnection = true;
		if (event.reply_to) {
			//throw away this message
			return;
		}
	}

	if (event.user!= slackTeam.bot_id) {
		var matches = null,
		lastMatchIndex = -1,
		regex = /<@([^>\|]+)(\|[^>]+)?>:?/;

		if (slackTeam) {
			matches = regex.exec(message);
			while (matches !== null) {
				if (lastMatchIndex === matches.index) {
					console.debug("broken beyond belief. GLHF");
					break;
				}
				lastMatchIndex = matches.index;
				var match = matches[1];
				if (match != null) {
					userName = slackTeam.users[match].name;
					if (!userName) {
						// User not found, this shouldn't happen, but in case it does, Kassy doesn't know who you are so lets call you "Bob"
						message = replaceUserIdWithUserName("Bob", message, matches[0]);
							console.debug("User not found in team, with id: " + match);
					}
					else {
						message = replaceUserIdWithUserName(userName, message, matches[0]);
					}
				}
				matches = regex.exec(message);
			}
		}
		else {
				console.debug('No slack team found matching: ' + teamId);
		}

		event.body = message;
		event.threadID = createThreadId(event.channel, teamId);
		event.senderID = event.user;
		event.senderName = slackTeam.users[event.user].name;
		shimMessage = shim.createEvent(event.threadID, event.senderID, event.senderName, event.body);
		eventReceivedCallback(platform, shimMessage);
	}
},

closeSockets = function() {
	console.debug("closing sockets");
	Object.keys(sockets).forEach(function (element, index) {
		sockets[element].terminate();
	});
	sockets = {};
},

timeout = function(){
	if (numSocketsToShutDown <= 0) {
		sync.done();
	}
	else {
		setTimeout(timeout, 1000);
	}
};

exports.start = function (callback) {
	var slackTokens = exports.config.slack_tokens;

	shuttingDown = false;
	eventReceivedCallback = callback;
	numSocketsToShutDown = 0;

	console.debug("starting");
	if (slackTokens) {
		for (var i = 0; i < slackTokens.length; i++) {
			init(slackTokens[i], connect);
		}

		platform = shim.createPlatformModule({
			sendMessage: sendMessage,
			sendFile: sendFile,
			setTitle: renameChannel,
			sendTyping: sendTyping,
			sendPrivateMessage: openPrivateMessage,
			commandPrefix: exports.config.commandPrefix
		});
	}
	else {
		console.debug("Whoops looks like you don't have the slack config set up correctly");
	}
};

exports.stop = function() {
	console.debug("start shutdown");
	shuttingDown = true;
	numSocketsToShutDown = Object.keys(sockets).length;
	async.series([
		closeSockets,
		function(){
			while (numSocketsToShutDown > 0) {
				sleep.usleep(500000);
			}
		}
	]);
};
<commit_msg>Remove debug messages from send typing slack<commit_after>var request = require('request'),
shim = require.once('../shim.js'),
WebSocket = require('ws'),
async = require.safe('async'),
sleep = require.safe('sleep'),
platform = null,
sockets = [],
eventReceivedCallback = null,
numSocketsToShutDown = 0,
shuttingDown = false,

sendMessage = function(message, thread) {
	var teamInfo = getChannelIdAndTeamId(thread);

	message.replace('<', '&lt');
	message.replace('>', '&gt');
	message.replace('&', '&amp');

	if (teamInfo.token != null) {
		var body = {
			"token": teamInfo.token,
			"channel": teamInfo.channel_id,
			"username": exports.config.name,
			"link_names": 1,
			"text": message,
			"unfurl_links": true,
			"icon_url": exports.config.icon
		};

		request({
			"uri": 'https://slack.com/api/chat.postMessage',
			"method": 'GET',
			"qs": body
		},
		function (error, response, body) {
			body = JSON.parse(body);
			if (response.statusCode != 200) {
				console.debug('error: ' + response.statusCode);
			}
			else if (!body.ok) {
				console.debug('Failed to send message to channel with id: ' + teamInfo.channel_id + ', error: ' + body.error);
			}
		});
	}
	else {
		console.debug('No slack team found!!!');
	}
},

getChannelIdAndTeamId = function(thread) {
	var slackTeams = exports.config.slack_teams,
	thread_components = thread.split('\0', 2),
	channelId = thread_components[0],
	teamId = thread_components[1],
	token = slackTeams[teamId].token;

	return { "channel_id": channelId, "team_id": teamId, "token": token };
},

createThreadId = function(channelId, teamId) {
	return channelId + '\0' + teamId;
},

sendFile = function(type, file, description, thread) {
	var teamInfo = getChannelIdAndTeamId(thread);

	if (teamInfo.token != null) {
		var body = {
			"token": teamInfo.token,
			"channel": teamInfo.channel_id,
			"file": file,
			"filetype": type,
			"title": description
		};

		request({
			"uri": 'https://slack.com/api/files.upload',
			"method": 'GET',
			"qs": body
		},
		function (error, response, body) {
			body = JSON.parse(body);
			if (response.statusCode != 200) {
				console.error('error: ' + response.statusCode);
			}
			else if (!body.ok) {
				console.warn('Failed send file to channel with id: ' + teamInfo.channel_id + ', error: ' + body.error);
			}
		});
	}
	else {
		console.warn('No slack team found!!!');
	}
},

renameChannel = function(title, thread) {
	var teamInfo = getChannelIdAndTeamId(thread);

	if (teamInfo.token != null) {
		var body = {
			"token": teamInfo.token,
			"channel": teamInfo.channel_id,
			"name": title
		};

		request({
			"uri": 'https://slack.com/api/channels.rename',
			"method": 'GET',
			"qs": body
		},
		function (error, response, body) {
			body = JSON.parse(body);
			if (response.statusCode != 200) {
				console.debug('error: ' + response.statusCode);
			}
			else if (!body.ok) {
				console.debug('Failed to rename channel with id: ' + teamInfo.channel_id + ', error: ' + body.error);
			}
		});
	}
	else {
		console.debug('No slack team found!!!');
	}
},

sendTyping = function(thread) {
	var slackTeams = exports.config.slack_teams,
	teamInfo = getChannelIdAndTeamId(thread),
	socket = sockets[teamInfo.team_id];

	if (socket != null) {
		var body = {
		'id' = slackTeams[teamInfo.team_id].event_id,
		'type' = 'typing',
		'channel' = teamInfo.channel_id;
		};
		body = JSON.stringify(body);
		socket.send(body);
		slackTeams[teamInfo.team_id].event_id++,
	}
	else {
		console.debug('No socket available for given team id');
	}
},

replaceUserIdWithUserName = function(userName, message, match) {
	if (userName) {
		var index = message.indexOf(match);
		return message.substr(0, index) + userName + message.substr(index + match.length);
	}
},

init = function(token, callback) {
	var body = {
		"token": token,
		mpim_aware: "true"
	};
	request({
		"uri": 'https://slack.com/api/rtm.start',
		"method": 'GET',
		"qs": body
	},
	function (error, response, body) {
		var teamId = null,
		slackTeams = exports.config.slack_teams,
		team;

		if (!slackTeams) {
			exports.config.slack_teams = {};
			slackTeams = exports.config.slack_teams;
		}

		try {
			body = JSON.parse(body);
		}
		catch(e) {
			console.debug("failed to parse message: " + body);
			console.critical(e);
			callback(false);
		}

		if (response.statusCode != 200) {
			console.debug('error: ' + response.statusCode);
			callback(false);
		}
		else if (!body.ok) {
			console.debug('Failed to connect to slack server with token: ' + token + ' error: ' + body.error);
			callback(false);
		}
		else {
			teamId = body.team.id;
			slackTeams[teamId] = body;
			team = slackTeams[teamId];
			team.token = token;
			team.event_id = 0;
			team.lastMessageSinceConnection = false;

			//Generate user map
			team.users = generateUserMap(body);
			team.allChannels = generateChannelsMap(body);

			// find slackbot
			findSlackBot(body, team);
			callback({"team_id": teamId, "url": body.url});
		}
	});
},

findSlackBot = function(body, slackTeam) {
	for (var i = 0; i < body.users.length; i++) {
		if (body.users[i].name === 'slackbot') {
			slackTeam.bot_id = body.users[i].id;
			break;
		}
	}
},

generateChannelsMap = function(data) {
	var channels = {};

	for (var i = 0; i < data.channels.length; i++) {
		channels[data.channels[i].id] = data.channels[i];
	}
	for (var j = 0; j < data.groups.length; j++) {
		channels[data.groups[j].id] = data.groups[j];
	}
	for (var k = 0; k < data.ims.length; k++) {
		channels[data.ims[k].id] = data.ims[k];
	}

	return channels;
},

generateUserMap = function(data) {
	var map = {};
	for (var i = 0; i < data.users.length; i++) {
		map[data.users[i].id] = data.users[i];
	}
	return map;
},

connect = function(connectionDetails) {
	var slackTeams = exports.config.slack_teams,
	s;
	console.debug("connect");

	if (connectionDetails) {
		(function (connectionDetails) {
			s = new WebSocket(connectionDetails.url);
			console.debug(connectionDetails.url);
			sockets[connectionDetails.team_id] = s;
			s.on('open', function() {
				console.debug("Connection to team: " + slackTeams[connectionDetails.team_id].team.name + " established");
			}).on('message', function(data) {
				eventReceived(JSON.parse(data), connectionDetails.team_id);
			}).on('close', function(data) {
				console.debug("Disconnected from team: " + slackTeams[connectionDetails.team_id].team.name);

				if (numSocketsToShutDown > 0) {
					numSocketsToShutDown--;
					return;
				}
				if (!shuttingDown) {
					init(slackTeams[connectionDetails.team_id].token, connect);
				}
			}).on('error', function(data) {
				console.debug("received error:\n" + data);
			});
		})(connectionDetails);
	}
	else {
		exports.start(eventReceivedCallback);
	}
},

openPrivateMessage = function(message, thread, senderId) {
	var slackTeams = exports.config.slack_teams,
	teamInfo = getChannelIdAndTeamId(thread);

	if (teamInfo.token != null) {
		var body = {
			"token": teamInfo.token,
			"user": senderId
		};

		request({
			"uri": 'https://slack.com/api/im.open',
			"method": 'GET',
			"qs": body
		},
		function (error, response, body) {
			body = JSON.parse(body);
			if (response.statusCode != 200) {
					console.debug('error: ' + response.statusCode);
			}
			else if (!body.ok) {
					console.debug('Failed to send message to user with id: ' + teamInfo.channel_id + ', error: ' + body.error);
			}
			else {
				slackTeams[teamInfo.team_id].channels[body.channel] = {"channel": body.channel, "user": senderId};
				sendMessage(message, createThreadId(body.channel.id, teamInfo.team_id));
			}
		});
	}
	else {
			console.debug('No slack team found!!!');
	}
},

eventReceived = function(event, teamId) {
	if (shuttingDown) return;
	switch (event.type) {
		case 'message':
		recMessage(event, teamId);
		break;
		case 'channel_created':
		channelCreated(event, teamId);
		break;
		case 'channel_deleted':
		channelDeleted(event, teamId);
		break;
		case 'channel_rename':
		channelRename(event, teamId);
		break;
		case 'im_created':
		channelCreated(event, teamId);
		break;
		case 'group_joined':
		channelCreated(event, teamId);
		break;
		case 'group_close':
		channelDeleted(event, teamId);
		break;
		case 'group_rename':
		channelRename(event, teamId);
		break;
		case 'user_change':
		userChange(event, teamId);
		break;
		case 'team_join':
		userChange(event, teamId);
		break;
		case 'team_rename':
		teamRename(event, teamId);
		break;
		default:

	}
},

teamRename = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	slackTeam.name = event.name;
},

userChange = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	slackTeam.users[event.user.id] = event.user;
},

channelCreated = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	slackTeam.channels[event.channel.id] = event.channel;
},

channelDeleted = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	removeHelper(slackTeam.channels, event.channel);
},

channelRename = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId];
	slackTeam.channels[event.channel.id].name = event.channel.name;
},

removeHelper = function(object, key) {
	delete object.key;
},

recMessage = function(event, teamId) {
	var slackTeam = exports.config.slack_teams[teamId],
	userName,
	id,
	message = event.text,
	shimMessage;

	if (!slackTeam.lastMessageSinceConnection) {
		slackTeam.lastMessageSinceConnection = true;
		if (event.reply_to) {
			//throw away this message
			return;
		}
	}

	if (event.user!= slackTeam.bot_id) {
		var matches = null,
		lastMatchIndex = -1,
		regex = /<@([^>\|]+)(\|[^>]+)?>:?/;

		if (slackTeam) {
			matches = regex.exec(message);
			while (matches !== null) {
				if (lastMatchIndex === matches.index) {
					console.debug("broken beyond belief. GLHF");
					break;
				}
				lastMatchIndex = matches.index;
				var match = matches[1];
				if (match != null) {
					userName = slackTeam.users[match].name;
					if (!userName) {
						// User not found, this shouldn't happen, but in case it does, Kassy doesn't know who you are so lets call you "Bob"
						message = replaceUserIdWithUserName("Bob", message, matches[0]);
							console.debug("User not found in team, with id: " + match);
					}
					else {
						message = replaceUserIdWithUserName(userName, message, matches[0]);
					}
				}
				matches = regex.exec(message);
			}
		}
		else {
				console.debug('No slack team found matching: ' + teamId);
		}

		event.body = message;
		event.threadID = createThreadId(event.channel, teamId);
		event.senderID = event.user;
		event.senderName = slackTeam.users[event.user].name;
		shimMessage = shim.createEvent(event.threadID, event.senderID, event.senderName, event.body);
		eventReceivedCallback(platform, shimMessage);
	}
},

closeSockets = function() {
	console.debug("closing sockets");
	Object.keys(sockets).forEach(function (element, index) {
		sockets[element].terminate();
	});
	sockets = {};
},

timeout = function(){
	if (numSocketsToShutDown <= 0) {
		sync.done();
	}
	else {
		setTimeout(timeout, 1000);
	}
};

exports.start = function (callback) {
	var slackTokens = exports.config.slack_tokens;

	shuttingDown = false;
	eventReceivedCallback = callback;
	numSocketsToShutDown = 0;

	console.debug("starting");
	if (slackTokens) {
		for (var i = 0; i < slackTokens.length; i++) {
			init(slackTokens[i], connect);
		}

		platform = shim.createPlatformModule({
			sendMessage: sendMessage,
			sendFile: sendFile,
			setTitle: renameChannel,
			sendTyping: sendTyping,
			sendPrivateMessage: openPrivateMessage,
			commandPrefix: exports.config.commandPrefix
		});
	}
	else {
		console.debug("Whoops looks like you don't have the slack config set up correctly");
	}
};

exports.stop = function() {
	console.debug("start shutdown");
	shuttingDown = true;
	numSocketsToShutDown = Object.keys(sockets).length;
	async.series([
		closeSockets,
		function(){
			while (numSocketsToShutDown > 0) {
				sleep.usleep(500000);
			}
		}
	]);
};
<|endoftext|>


<commit_before>var gulp = require('gulp');
var runSequence = require('run-sequence');
var paths = require('../paths');
var changelog = require('conventional-changelog');
var fs = require('fs');
var bump = require('gulp-bump');

gulp.task('bump-version', function(){
  return gulp.src(['./package.json'])
    .pipe(bump({type:'patch'})) //major|minor|patch|prerelease
    .pipe(gulp.dest('./'));
});

gulp.task('changelog', function(callback) {
  var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));

  return changelog({
    repository: pkg.repository.url,
    version: pkg.version,
    file: paths.doc + '/CHANGELOG.md'
  }, function(err, log) {
    fs.writeFileSync(paths.doc + '/CHANGELOG.md', log);
  });
});

gulp.task('prepare-release', function(callback){
  return runSequence(
    'build',
    'lint',
    'bump-version',
    'doc',
    'changelog',
    callback
  );
});
<commit_msg>fix(build): add missing bower bump<commit_after>var gulp = require('gulp');
var runSequence = require('run-sequence');
var paths = require('../paths');
var changelog = require('conventional-changelog');
var fs = require('fs');
var bump = require('gulp-bump');

gulp.task('bump-version', function(){
  return gulp.src(['./package.json', './bower.json'])
    .pipe(bump({type:'patch'})) //major|minor|patch|prerelease
    .pipe(gulp.dest('./'));
});

gulp.task('changelog', function(callback) {
  var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));

  return changelog({
    repository: pkg.repository.url,
    version: pkg.version,
    file: paths.doc + '/CHANGELOG.md'
  }, function(err, log) {
    fs.writeFileSync(paths.doc + '/CHANGELOG.md', log);
  });
});

gulp.task('prepare-release', function(callback){
  return runSequence(
    'build',
    'lint',
    'bump-version',
    'doc',
    'changelog',
    callback
  );
});
<|endoftext|>


<commit_before>(function () {
    var ui = glinamespace("gli.ui");

    var StateView = function (w, elementRoot) {
        var self = this;
        this.window = w;
        this.elements = {
            view: elementRoot.getElementsByClassName("window-whole-inner")[0]
        };
    };

    function generateParameterRow(w, gl, table, state, param) {
        var tr = document.createElement("tr");
        tr.className = "info-parameter-row";

        var tdkey = document.createElement("td");
        tdkey.className = "info-parameter-key";
        tdkey.innerHTML = param.name;
        tr.appendChild(tdkey);

        var value;
        if (param.value) {
            value = state[param.value];
        } else {
            value = state[param.name];
        }

        // Grab tracked objects
        if (value && value.trackedObject) {
            value = value.trackedObject;
        }

        var tdvalue = document.createElement("td");
        tdvalue.className = "info-parameter-value";

        var text = "";
        var clickhandler = null;

        var UIType = gli.UIType;
        var ui = param.ui;
        switch (ui.type) {
            case UIType.ENUM:
                var anyMatches = false;
                for (var i = 0; i < ui.values.length; i++) {
                    var enumName = ui.values[i];
                    if (value == gl[enumName]) {
                        anyMatches = true;
                        text = enumName;
                    }
                }
                if (anyMatches == false) {
                    if (value === undefined) {
                        text = "undefined";
                    } else {
                        text = "?? 0x" + value.toString(16) + " ??";
                    }
                }
                break;
            case UIType.ARRAY:
                text = "[" + value + "]";
                break;
            case UIType.BOOL:
                text = value ? "true" : "false";
                break;
            case UIType.LONG:
                text = value;
                break;
            case UIType.ULONG:
                text = value;
                break;
            case UIType.COLORMASK:
                text = value;
                break;
            case UIType.OBJECT:
                // TODO: custom object output based on type
                text = value ? value : "null";
                if (value && value.target && gli.util.isWebGLResource(value.target)) {
                    var typename = glitypename(value.target);
                    switch (typename) {
                        case "WebGLBuffer":
                            clickhandler = function () {
                                w.showBuffer(value, true);
                            };
                            break;
                        case "WebGLFramebuffer":
                            break;
                        case "WebGLProgram":
                            clickhandler = function () {
                                w.showProgram(value, true);
                            };
                            break;
                        case "WebGLRenderbuffer":
                            break;
                        case "WebGLShader":
                            break;
                        case "WebGLTexture":
                            clickhandler = function () {
                                w.showTexture(value, true);
                            };
                            break;
                    }
                    text = "[" + value.getName() + "]";
                } else if (gli.util.isTypedArray(value)) {
                    text = "[" + value + "]";
                } else if (value) {
                    var typename = glitypename(value);
                    switch (typename) {
                        case "WebGLUniformLocation":
                            text = '"' + value.sourceUniformName + '"';
                            break;
                    }
                }
                break;
            case UIType.WH:
                text = value[0] + " x " + value[1];
                break;
            case UIType.RECT:
                text = value[0] + ", " + value[1] + " " + value[2] + " x " + value[3];
                break;
            case UIType.STRING:
                text = '"' + value + '"';
                break;
            case UIType.COLOR:
                text = value;
                //                outputHTML += "rgba(" +
                //                                "<input type='text' " + (readOnly ? "readonly='readonly'" : "") + " value='" + value[0] + "'/>, " +
                //                                "<input type='text' " + (readOnly ? "readonly='readonly'" : "") + " value='" + value[1] + "'/>, " +
                //                                "<input type='text' " + (readOnly ? "readonly='readonly'" : "") + " value='" + value[2] + "'/>, " +
                //                                "<input type='text' " + (readOnly ? "readonly='readonly'" : "") + " value='" + value[3] + "'/>" +
                //                                ")";
                // TODO: color tip
                break;
            case UIType.FLOAT:
                text = value;
                break;
            case UIType.BITMASK:
                text = "0x" + value.toString(16);
                // TODO: bitmask tip
                break;
            case UIType.RANGE:
                text = value[0] + " - " + value[1];
                break;
            case UIType.MATRIX:
                switch (value.length) {
                    default: // ?
                        text = "[matrix]";
                        break;
                    case 4: // 2x2
                        text = "[matrix 2x2]";
                        break;
                    case 9: // 3x3
                        text = "[matrix 3x3]";
                        break;
                    case 16: // 4x4
                        text = "[matrix 4x4]";
                        break;
                }
                // TODO: matrix tip
                break;
        }

        tdvalue.innerHTML = text;
        if (clickhandler) {
            tdvalue.className += " trace-call-clickable";
            tdvalue.onclick = function (e) {
                clickhandler();
                e.preventDefault();
                e.stopPropagation();
            };
        }

        tr.appendChild(tdvalue);

        table.appendChild(tr);
    };

    function generateStateDisplay(w, el, state) {
        var gl = w.context;

        var titleDiv = document.createElement("div");
        titleDiv.className = "info-title-master";
        titleDiv.innerHTML = "State Snapshot";
        el.appendChild(titleDiv);

        var table = document.createElement("table");
        table.className = "info-parameters";

        var stateParameters = gli.info.stateParameters;
        for (var n = 0; n < stateParameters.length; n++) {
            var param = stateParameters[n];
            generateParameterRow(w, gl, table, state, param);
        }

        el.appendChild(table);
    };

    StateView.prototype.setState = function () {
        var rawgl = this.window.context.rawgl;
        var state = null;
        switch (this.window.activeVersion) {
            case null:
                state = new gli.host.StateSnapshot(rawgl);
                break;
            case "current":
                state = this.window.controller.getCurrentState();
                break;
        }

        this.elements.view.innerHTML = "";
        if (state) {
            generateStateDisplay(this.window, this.elements.view, state);
        }

        this.elements.view.scrollTop = 0;
    };

    ui.StateView = StateView;
})();
<commit_msg>fix for ff returning null for gl.VIEWPORT queries<commit_after>(function () {
    var ui = glinamespace("gli.ui");

    var StateView = function (w, elementRoot) {
        var self = this;
        this.window = w;
        this.elements = {
            view: elementRoot.getElementsByClassName("window-whole-inner")[0]
        };
    };

    function generateParameterRow(w, gl, table, state, param) {
        var tr = document.createElement("tr");
        tr.className = "info-parameter-row";

        var tdkey = document.createElement("td");
        tdkey.className = "info-parameter-key";
        tdkey.innerHTML = param.name;
        tr.appendChild(tdkey);

        var value;
        if (param.value) {
            value = state[param.value];
        } else {
            value = state[param.name];
        }

        // Grab tracked objects
        if (value && value.trackedObject) {
            value = value.trackedObject;
        }

        var tdvalue = document.createElement("td");
        tdvalue.className = "info-parameter-value";

        var text = "";
        var clickhandler = null;

        var UIType = gli.UIType;
        var ui = param.ui;
        switch (ui.type) {
            case UIType.ENUM:
                var anyMatches = false;
                for (var i = 0; i < ui.values.length; i++) {
                    var enumName = ui.values[i];
                    if (value == gl[enumName]) {
                        anyMatches = true;
                        text = enumName;
                    }
                }
                if (anyMatches == false) {
                    if (value === undefined) {
                        text = "undefined";
                    } else {
                        text = "?? 0x" + value.toString(16) + " ??";
                    }
                }
                break;
            case UIType.ARRAY:
                text = "[" + value + "]";
                break;
            case UIType.BOOL:
                text = value ? "true" : "false";
                break;
            case UIType.LONG:
                text = value;
                break;
            case UIType.ULONG:
                text = value;
                break;
            case UIType.COLORMASK:
                text = value;
                break;
            case UIType.OBJECT:
                // TODO: custom object output based on type
                text = value ? value : "null";
                if (value && value.target && gli.util.isWebGLResource(value.target)) {
                    var typename = glitypename(value.target);
                    switch (typename) {
                        case "WebGLBuffer":
                            clickhandler = function () {
                                w.showBuffer(value, true);
                            };
                            break;
                        case "WebGLFramebuffer":
                            break;
                        case "WebGLProgram":
                            clickhandler = function () {
                                w.showProgram(value, true);
                            };
                            break;
                        case "WebGLRenderbuffer":
                            break;
                        case "WebGLShader":
                            break;
                        case "WebGLTexture":
                            clickhandler = function () {
                                w.showTexture(value, true);
                            };
                            break;
                    }
                    text = "[" + value.getName() + "]";
                } else if (gli.util.isTypedArray(value)) {
                    text = "[" + value + "]";
                } else if (value) {
                    var typename = glitypename(value);
                    switch (typename) {
                        case "WebGLUniformLocation":
                            text = '"' + value.sourceUniformName + '"';
                            break;
                    }
                }
                break;
            case UIType.WH:
                text = value[0] + " x " + value[1];
                break;
            case UIType.RECT:
                if (value) {
                    text = value[0] + ", " + value[1] + " " + value[2] + " x " + value[3];
                } else {
                    text = "null";
                }
                break;
            case UIType.STRING:
                text = '"' + value + '"';
                break;
            case UIType.COLOR:
                text = value;
                //                outputHTML += "rgba(" +
                //                                "<input type='text' " + (readOnly ? "readonly='readonly'" : "") + " value='" + value[0] + "'/>, " +
                //                                "<input type='text' " + (readOnly ? "readonly='readonly'" : "") + " value='" + value[1] + "'/>, " +
                //                                "<input type='text' " + (readOnly ? "readonly='readonly'" : "") + " value='" + value[2] + "'/>, " +
                //                                "<input type='text' " + (readOnly ? "readonly='readonly'" : "") + " value='" + value[3] + "'/>" +
                //                                ")";
                // TODO: color tip
                break;
            case UIType.FLOAT:
                text = value;
                break;
            case UIType.BITMASK:
                text = "0x" + value.toString(16);
                // TODO: bitmask tip
                break;
            case UIType.RANGE:
                text = value[0] + " - " + value[1];
                break;
            case UIType.MATRIX:
                switch (value.length) {
                    default: // ?
                        text = "[matrix]";
                        break;
                    case 4: // 2x2
                        text = "[matrix 2x2]";
                        break;
                    case 9: // 3x3
                        text = "[matrix 3x3]";
                        break;
                    case 16: // 4x4
                        text = "[matrix 4x4]";
                        break;
                }
                // TODO: matrix tip
                break;
        }

        tdvalue.innerHTML = text;
        if (clickhandler) {
            tdvalue.className += " trace-call-clickable";
            tdvalue.onclick = function (e) {
                clickhandler();
                e.preventDefault();
                e.stopPropagation();
            };
        }

        tr.appendChild(tdvalue);

        table.appendChild(tr);
    };

    function generateStateDisplay(w, el, state) {
        var gl = w.context;

        var titleDiv = document.createElement("div");
        titleDiv.className = "info-title-master";
        titleDiv.innerHTML = "State Snapshot";
        el.appendChild(titleDiv);

        var table = document.createElement("table");
        table.className = "info-parameters";

        var stateParameters = gli.info.stateParameters;
        for (var n = 0; n < stateParameters.length; n++) {
            var param = stateParameters[n];
            generateParameterRow(w, gl, table, state, param);
        }

        el.appendChild(table);
    };

    StateView.prototype.setState = function () {
        var rawgl = this.window.context.rawgl;
        var state = null;
        switch (this.window.activeVersion) {
            case null:
                state = new gli.host.StateSnapshot(rawgl);
                break;
            case "current":
                state = this.window.controller.getCurrentState();
                break;
        }

        this.elements.view.innerHTML = "";
        if (state) {
            generateStateDisplay(this.window, this.elements.view, state);
        }

        this.elements.view.scrollTop = 0;
    };

    ui.StateView = StateView;
})();
<|endoftext|>


<commit_before>myDemoApp.controller('BUsingOptionsCtrl',['$scope', '$data', '$timeout', function($scope, $data, $timeout) {

    $scope.record = $data.record;

    $scope.doAlert = function(message, showId) {
        alertMessage = message;
        if (showId) {
            alertMessage += "\nThe id is " + $scope.record["_id"];
        }
        alert(alertMessage);
    };

    $scope.changeCase = function() {

        function changeCaseInt(newCase) {
            for (var property in $scope.record) {
                if (property !== '_id' && typeof $scope.record[property] === 'string') {
                    if (newCase === 'lower') {
                        $scope.record[property] = $scope.record[property].toLowerCase();
                    } else {
                        $scope.record[property] = $scope.record[property].toUpperCase();
                    }
                }
            }
        }

        if ($scope.record.surname == $scope.record.surname.toLowerCase()) {
            changeCaseInt('upper');
        } else {
            changeCaseInt('lower');
        }
    };

    $scope.contextMenu = [
        {
            fn : $scope.doAlert,
            args : ['Reading the data',true],
            listing: false,
            creating: false,
            editing: true,
            text:"Demonstrate reading the data"
        },
        {
            fn : $scope.changeCase,
            listing: false,
            creating: false,
            editing: true,
            text:"Demonstrate modifying the data"
        },
        {
            fn : $scope.doAlert,
            args : ['Big process',false],
            listing: true,
            creating: false,
            editing: false,
            text:"Run some file wide process"
        }
    ];

    function setColour(number) {
        var colours = ['#81B7DB','#C2A369','#6DDB4F','#47820C'];
        if (number != "") {
            $('#cg_f_eyeColour').css('background-color', colours[parseInt(number)]);
        } else {
            $('#cg_f_eyeColour').css('background-color', 'white');
        }
    };

    $scope.$on('formInputDone', function(event, info) {
        switch (info.id) {
            case 'f_eyeColour' :
                var eyeColor = $('#f_eyeColour');
                eyeColor.on("change", function(e) {
                    console.log("change "+JSON.stringify({val:e.val, added:e.added, removed:e.removed}));
                    setColour(e.val);
                });
                $timeout(function(){
                    setColour(eyeColor.select2("val"))
                },100);
                break;
        }
    })
}]);
<commit_msg>Remove trace<commit_after>myDemoApp.controller('BUsingOptionsCtrl',['$scope', '$data', '$timeout', function($scope, $data, $timeout) {

    $scope.record = $data.record;

    $scope.doAlert = function(message, showId) {
        alertMessage = message;
        if (showId) {
            alertMessage += "\nThe id is " + $scope.record["_id"];
        }
        alert(alertMessage);
    };

    $scope.changeCase = function() {

        function changeCaseInt(newCase) {
            for (var property in $scope.record) {
                if (property !== '_id' && typeof $scope.record[property] === 'string') {
                    if (newCase === 'lower') {
                        $scope.record[property] = $scope.record[property].toLowerCase();
                    } else {
                        $scope.record[property] = $scope.record[property].toUpperCase();
                    }
                }
            }
        }

        if ($scope.record.surname == $scope.record.surname.toLowerCase()) {
            changeCaseInt('upper');
        } else {
            changeCaseInt('lower');
        }
    };

    $scope.contextMenu = [
        {
            fn : $scope.doAlert,
            args : ['Reading the data',true],
            listing: false,
            creating: false,
            editing: true,
            text:"Demonstrate reading the data"
        },
        {
            fn : $scope.changeCase,
            listing: false,
            creating: false,
            editing: true,
            text:"Demonstrate modifying the data"
        },
        {
            fn : $scope.doAlert,
            args : ['Big process',false],
            listing: true,
            creating: false,
            editing: false,
            text:"Run some file wide process"
        }
    ];

    function setColour(number) {
        var colours = ['#81B7DB','#C2A369','#6DDB4F','#47820C'];
        if (number != "") {
            $('#cg_f_eyeColour').css('background-color', colours[parseInt(number)]);
        } else {
            $('#cg_f_eyeColour').css('background-color', 'white');
        }
    };

    $scope.$on('formInputDone', function(event, info) {
        switch (info.id) {
            case 'f_eyeColour' :
                var eyeColor = $('#f_eyeColour');
                eyeColor.on("change", function(e) {
//                    console.log("change "+JSON.stringify({val:e.val, added:e.added, removed:e.removed}));
                    setColour(e.val);
                });
                $timeout(function(){
                    setColour(eyeColor.select2("val"))
                },100);
                break;
        }
    })
}]);
<|endoftext|>


<commit_before>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    return    return var move = Math.random();
}

function getComputerMove(move) {
    return    return var move = Math.random();
}

function getWinner(playerMove,computerMove) {
    var winner;
var rock = “R”;
var paper = “P”;
var scissors = “S”;

if (playerMove.equals(computerMove) {
console.log(“Tie”);
}

else if (playerMove.equals(‘R’)) {
  if (computerMove.equals(’S’))
console.log(“Rock beats scissors, you win”);
else if (computerMove.equals(‘P’))
console.log(“Paper beats rock, you lose”);
}

else if (playerMove.equals(“S”)) {
  if (computerMove.equals(“P”))
console.log(“Scissors beats Paper, you win”);
else if (computerMove.equals(“R”))
console.log(“Rock beats scissors, you lose”);
}

else if (playerMove.equals(“P”)) {
  if (computerMove.equals(“R”))
console.log(“Paper beats rock, you win”);
else if (computerMove.equals(“S”))
console.log(“Scissors beats paper, you lose”);
}

else
console.log (“Invalid input”)
    return winner;
}

function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
  for (var playerWins = 0; playerWins < 5; playerWins += 1){
if (playerWins === 5) {
break;
}
for (var computerWins = 0; computerWins < 5; computerWins += 1){

if (computerWins === 5) {
break;
}
    return [playerWins, computerWins];
}

<commit_msg>rps changes to end code<commit_after>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    return    return var move = Math.random();
}

function getComputerMove(move) {
    return    return var move = Math.random();
}

function getWinner(playerMove,computerMove) {
    var winner;
var rock = “R”;
var paper = “P”;
var scissors = “S”;

if (playerMove.equals(computerMove) {
console.log(“Tie”);
}

else if (playerMove.equals(‘R’)) {
  if (computerMove.equals(’S’))
console.log(“Rock beats scissors, you win”);
else if (computerMove.equals(‘P’))
console.log(“Paper beats rock, you lose”);
}

else if (playerMove.equals(“S”)) {
  if (computerMove.equals(“P”))
console.log(“Scissors beats Paper, you win”);
else if (computerMove.equals(“R”))
console.log(“Rock beats scissors, you lose”);
}

else if (playerMove.equals(“P”)) {
  if (computerMove.equals(“R”))
console.log(“Paper beats rock, you win”);
else if (computerMove.equals(“S”))
console.log(“Scissors beats paper, you lose”);
}

else
console.log (“Invalid input”)
    return winner;
}

function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;
  for (var playerWins = 0; playerWins < 5; playerWins += 1){
if (playerWins === 5) {
break;
}
for (var computerWins = 0; computerWins < 5; computerWins += 1){

if (computerWins === 5) {
break;
}
    return [playerWins, computerWins];
}

<|endoftext|>


<commit_before><commit_msg>fixed 1rm tag suggestions not showing up<commit_after><|endoftext|>


<commit_before><commit_msg>delete extra seeds<commit_after><|endoftext|>


<commit_before>var boomer = require("boomer")

module.exports = function ( grunt ){

  grunt.loadNpmTasks("grunt-dustin")
  grunt.loadNpmTasks("grunt-premailer")

  grunt.config("dustin.render", {
    options: {
      render: true,
      resolve: "template/",
      // we don't care about white space in compiled templates
      preserveWhiteSpace: true
    },
    expand: true,
    cwd: "template/mails",
    src: "*.dust",
    dest: "rendered/",
    ext: ".html"
  })

  grunt.config("premailer.inline", {
    options: {
      baseUrl: "",
      removeClasses: false,
      removeComments: true,
      preserveStyles: true
    },
    expand: true,
    cwd: "rendered/",
    src: "*.html",
    dest: "rendered/"
  })

  boomer(grunt, "default")
    .connect({
      hostname: "*",
      base: "rendered/"
    })
    .lr({
      html: "rendered/*.html"
    })
    .watch({
      options: {
        spawn: false,
        interrupt: true
      },
      html: {
        files: ["template/**/*.dust"],
        tasks: ["dustin:render", "premailer", "lr:html"]
      }
    })
    .started(function( options ){
      grunt.config("premailer.inline.options.baseUrl", options.host)
    })

}<commit_msg>set correct premailer task baseUrl<commit_after>var boomer = require("boomer")

module.exports = function ( grunt ){

  grunt.loadNpmTasks("grunt-dustin")
  grunt.loadNpmTasks("grunt-premailer")

  grunt.config("dustin.render", {
    options: {
      render: true,
      resolve: "template/",
      // we don't care about white space in compiled templates
      preserveWhiteSpace: true
    },
    expand: true,
    cwd: "template/mails",
    src: "*.dust",
    dest: "rendered/",
    ext: ".html"
  })

  grunt.config("premailer.inline", {
    options: {
      baseUrl: "",
      removeClasses: false,
      removeComments: true,
      preserveStyles: true
    },
    expand: true,
    cwd: "rendered/",
    src: "*.html",
    dest: "rendered/"
  })

  boomer(grunt, "default")
    .connect({
      hostname: "*",
      base: "rendered/"
    })
    .lr({
      html: "rendered/*.html"
    })
    .watch({
      options: {
        spawn: false,
        interrupt: true
      },
      html: {
        files: ["template/**/*.dust"],
        tasks: ["dustin:render", "premailer", "lr:html"]
      }
    })
    .started(function( options ){
      grunt.config("premailer.inline.options.baseUrl", "http://"+options.host+":"+options.port)
    })

}<|endoftext|>


<commit_before>'use strict';

var chai = require('chai');
var sinon = require('sinon');
var sinonChai = require('sinon-chai');
var expect = chai.expect;
chai.use(sinonChai);

var Copyright = require('../helpers/modules/Copyright');
var CopyrightValidator = require('../helpers/modules/CopyrightValidator');

describe('copyright', function() {

    var copyright;

    beforeEach(function() {
        copyright = new Copyright();
    });

    afterEach(function() {
        copyright = null;
    });

    describe('Return values', function() {

        describe('format not provided', function() {

            it('should call validateCurrentYear() of CopyrightValidator with correct arguments', function() {
                var currentYear = 2018;
                var spy = sinon.spy(CopyrightValidator.prototype, 'validateCurrentYear');

                copyright.get(2013, currentYear);

                expect(spy.withArgs(currentYear).calledOnce);

                CopyrightValidator.prototype.validateCurrentYear.restore();
            });

            it('should return correct text', function() {
                var startYear = 2013,
                    currentYear = 2018,
                    expected = `${currentYear}`;

                var actual = copyright.get(startYear, currentYear);

                expect(actual).to.equal(expected);
            });

        });

        describe('format "current"', function() {

            var format;

            beforeEach(function() {
                format = 'current';
            });

            it('should call validateCurrentYear() of CopyrightValidator with correct arguments', function() {
                var currentYear = 2018;
                var spy = sinon.spy(CopyrightValidator.prototype, 'validateCurrentYear');

                copyright.get(2013, currentYear, format);

                expect(spy.withArgs(currentYear).calledOnce);

                CopyrightValidator.prototype.validateCurrentYear.restore();
            });

            it('should return correct text', function() {
                var startYear = 2013,
                    currentYear = 2018,
                    expected = `${currentYear}`;

                var actual = copyright.get(startYear, currentYear, format);

                expect(actual).to.equal(expected);
            });

        });

        describe('format "range"', function() {

            var format;

            beforeEach(function() {
                format = 'range';
            });

            it('should call validateRange() of CopyrightValidator with correct arguments', function() {
                var startYear = 2013,
                    currentYear = 2018;

                var spy = sinon.spy(CopyrightValidator.prototype, 'validateRange');

                copyright.get(startYear, currentYear, format);

                expect(spy.withArgs(currentYear).calledOnce);

                CopyrightValidator.prototype.validateRange.restore();
            });

            it('should return correct text if startYear is equal to currentYear', function() {
                var startYear = 2018,
                    currentYear = 2018,
                    expectedText = `${currentYear}`;

                var actualText = copyright.get(startYear, currentYear, format);

                expect(actualText).to.equal(expectedText);
            });

            it('should return correct text if currentYear is greater than startYear', function() {
                var startYear = 2014,
                    currentYear = 2020,
                    expectedText = `${startYear} - ${currentYear}`;

                var actualText = copyright.get(startYear, currentYear, format);

                expect(actualText).to.equal(expectedText);
            });

        });

    });

});<commit_msg>Fix Copyright tests<commit_after>'use strict';

var chai = require('chai');
var sinon = require('sinon');
var sinonChai = require('sinon-chai');
var expect = chai.expect;
chai.use(sinonChai);

var Copyright = require('../helpers/modules/Copyright');
var CopyrightValidator = require('../helpers/modules/CopyrightValidator');

describe('copyright', function() {

    var copyright;

    beforeEach(function() {
        copyright = new Copyright();
    });

    afterEach(function() {
        copyright = null;
    });

    describe('Return values', function() {

        describe('format not provided', function() {

            it('should call validateCurrentYear() of CopyrightValidator with correct arguments', function() {
                var currentYear = 2018;
                var spy = sinon.spy(CopyrightValidator.prototype, 'validateCurrentYear');

                copyright.get(2013, currentYear);

                expect(spy.withArgs(currentYear)).calledOnce;

                CopyrightValidator.prototype.validateCurrentYear.restore();
            });

            it('should return correct text', function() {
                var startYear = 2013,
                    currentYear = 2018,
                    expected = `${currentYear}`;

                var actual = copyright.get(startYear, currentYear);

                expect(actual).to.equal(expected);
            });

        });

        describe('format "current"', function() {

            var format;

            beforeEach(function() {
                format = 'current';
            });

            it('should call validateCurrentYear() of CopyrightValidator with correct arguments', function() {
                var currentYear = 2018;
                var spy = sinon.spy(CopyrightValidator.prototype, 'validateCurrentYear');

                copyright.get(2013, currentYear, format);

                expect(spy.withArgs(currentYear)).calledOnce;

                CopyrightValidator.prototype.validateCurrentYear.restore();
            });

            it('should return correct text', function() {
                var startYear = 2013,
                    currentYear = 2018,
                    expected = `${currentYear}`;

                var actual = copyright.get(startYear, currentYear, format);

                expect(actual).to.equal(expected);
            });

        });

        describe('format "range"', function() {

            var format;

            beforeEach(function() {
                format = 'range';
            });

            it('should call validateRange() of CopyrightValidator with correct arguments', function() {
                var startYear = 2013,
                    currentYear = 2018;

                var spy = sinon.spy(CopyrightValidator.prototype, 'validateRange');

                copyright.get(startYear, currentYear, format);

                expect(spy.withArgs(startYear, currentYear)).calledOnce;

                CopyrightValidator.prototype.validateRange.restore();
            });

            it('should return correct text if startYear is equal to currentYear', function() {
                var startYear = 2018,
                    currentYear = 2018,
                    expectedText = `${currentYear}`;

                var actualText = copyright.get(startYear, currentYear, format);

                expect(actualText).to.equal(expectedText);
            });

            it('should return correct text if currentYear is greater than startYear', function() {
                var startYear = 2014,
                    currentYear = 2020,
                    expectedText = `${startYear} - ${currentYear}`;

                var actualText = copyright.get(startYear, currentYear, format);

                expect(actualText).to.equal(expectedText);
            });

        });

    });

});<|endoftext|>


<commit_before><commit_msg>fixes #172 (#173)<commit_after><|endoftext|>


<commit_before>import express from 'express';
import https from 'https';
import favicon from 'serve-favicon';
import program from 'commander';
import path from 'path';
import fs from 'fs';
import chalk from 'chalk';
import shelljs from 'shelljs';
import storybook, { webpackValid } from './middleware';
import packageJson from '../../package.json';
import { parseList, getEnvConfig } from './utils';

process.env.NODE_ENV = process.env.NODE_ENV || 'development';

const logger = console;

program
  .version(packageJson.version)
  .option('-p, --port [number]', 'Port to run Storybook (Required)', str => parseInt(str, 10))
  .option('-h, --host [string]', 'Host to run Storybook')
  .option('-s, --static-dir <dir-names>', 'Directory where to load static files from')
  .option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from')
  .option(
    '--https',
    'Serve Storybook over HTTPS. Note: You must provide your own certificate information.'
  )
  .option(
    '--ssl-ca <ca>',
    'Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)',
    parseList
  )
  .option('--ssl-cert <cert>', 'Provide an SSL certificate. (Required with --https)')
  .option('--ssl-key <key>', 'Provide an SSL key. (Required with --https)')
  .option('--smoke-test', 'Exit after successful start')
  .option('-d, --db-path [db-file]', 'DEPRECATED!')
  .option('--enable-db', 'DEPRECATED!')
  .parse(process.argv);

logger.info(chalk.bold(`${packageJson.name} v${packageJson.version}`) + chalk.reset('\n'));

if (program.enableDb || program.dbPath) {
  logger.error(
    [
      'Error: the experimental local database addon is no longer bundled with',
      'react-storybook. Please remove these flags (-d,--db-path,--enable-db)',
      'from the command or npm script and try again.',
    ].join(' ')
  );
  process.exit(1);
}

// The key is the field created in `program` variable for
// each command line argument. Value is the env variable.
getEnvConfig(program, {
  port: 'SBCONFIG_PORT',
  host: 'SBCONFIG_HOSTNAME',
  staticDir: 'SBCONFIG_STATIC_DIR',
  configDir: 'SBCONFIG_CONFIG_DIR',
});

if (!program.port) {
  logger.error('Error: port to run Storybook is required!\n');
  program.help();
  process.exit(-1);
}

// Used with `app.listen` below
const listenAddr = [program.port];

if (program.host) {
  listenAddr.push(program.host);
}

const app = express();
let server = app;

if (program.https) {
  if (!program.sslCert) {
    logger.error('Error: --ssl-cert is required with --https');
    process.exit(-1);
  }
  if (!program.sslKey) {
    logger.error('Error: --ssl-key is required with --https');
    process.exit(-1);
  }

  const sslOptions = {
    ca: (program.sslCa || []).map(ca => fs.readFileSync(ca, 'utf-8')),
    cert: fs.readFileSync(program.sslCert, 'utf-8'),
    key: fs.readFileSync(program.sslKey, 'utf-8'),
  };

  server = https.createServer(sslOptions, app);
}

let hasCustomFavicon = false;

if (program.staticDir) {
  program.staticDir = parseList(program.staticDir);
  program.staticDir.forEach(dir => {
    const staticPath = path.resolve(dir);
    if (!fs.existsSync(staticPath)) {
      logger.error(`Error: no such directory to load static files: ${staticPath}`);
      process.exit(-1);
    }
    logger.log(`=> Loading static files from: ${staticPath} .`);
    app.use(express.static(staticPath, { index: false }));

    const faviconPath = path.resolve(staticPath, 'favicon.ico');
    if (fs.existsSync(faviconPath)) {
      hasCustomFavicon = true;
      app.use(favicon(faviconPath));
    }
  });
}

if (!hasCustomFavicon) {
  app.use(favicon(path.resolve(__dirname, 'public/favicon.ico')));
}

// Build the webpack configuration using the `baseConfig`
// custom `.babelrc` file and `webpack.config.js` files
const configDir = program.configDir || './.storybook';

// The repository info is sent to the storybook while running on
// development mode so it'll be easier for tools to integrate.
const exec = cmd => shelljs.exec(cmd, { silent: true }).stdout.trim();
process.env.STORYBOOK_GIT_ORIGIN =
  process.env.STORYBOOK_GIT_ORIGIN || exec('git remote get-url origin');
process.env.STORYBOOK_GIT_BRANCH =
  process.env.STORYBOOK_GIT_BRANCH || exec('git symbolic-ref HEAD --short');

// NOTE changes to env should be done before calling `getBaseConfig`
// `getBaseConfig` function which is called inside the middleware
app.use(storybook(configDir));

let serverResolve = () => {};
let serverReject = () => {};
const serverListening = new Promise((resolve, reject) => {
  serverResolve = resolve;
  serverReject = reject;
});
server.listen(...listenAddr, error => {
  if (error) {
    serverReject(error);
  } else {
    serverResolve();
  }
});

Promise.all([webpackValid, serverListening])
  .then(() => {
    const proto = program.https ? 'https' : 'http';
    const address = `${proto}://${program.host || 'localhost'}:${program.port}/`;
    logger.info(`Storybook started on => ${chalk.cyan(address)}\n`);
    if (program.smokeTest) {
      process.exit(0);
    }
  })
  .catch(error => logger.error(error));
<commit_msg>Copypaste the change to vue app<commit_after>import express from 'express';
import https from 'https';
import favicon from 'serve-favicon';
import program from 'commander';
import path from 'path';
import fs from 'fs';
import chalk from 'chalk';
import shelljs from 'shelljs';
import storybook, { webpackValid } from './middleware';
import packageJson from '../../package.json';
import { parseList, getEnvConfig } from './utils';

process.env.NODE_ENV = process.env.NODE_ENV || 'development';

const logger = console;

program
  .version(packageJson.version)
  .option('-p, --port [number]', 'Port to run Storybook (Required)', str => parseInt(str, 10))
  .option('-h, --host [string]', 'Host to run Storybook')
  .option('-s, --static-dir <dir-names>', 'Directory where to load static files from')
  .option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from')
  .option(
    '--https',
    'Serve Storybook over HTTPS. Note: You must provide your own certificate information.'
  )
  .option(
    '--ssl-ca <ca>',
    'Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)',
    parseList
  )
  .option('--ssl-cert <cert>', 'Provide an SSL certificate. (Required with --https)')
  .option('--ssl-key <key>', 'Provide an SSL key. (Required with --https)')
  .option('--smoke-test', 'Exit after successful start')
  .option('-d, --db-path [db-file]', 'DEPRECATED!')
  .option('--enable-db', 'DEPRECATED!')
  .parse(process.argv);

logger.info(chalk.bold(`${packageJson.name} v${packageJson.version}`) + chalk.reset('\n'));

if (program.enableDb || program.dbPath) {
  logger.error(
    [
      'Error: the experimental local database addon is no longer bundled with',
      'react-storybook. Please remove these flags (-d,--db-path,--enable-db)',
      'from the command or npm script and try again.',
    ].join(' ')
  );
  process.exit(1);
}

// The key is the field created in `program` variable for
// each command line argument. Value is the env variable.
getEnvConfig(program, {
  port: 'SBCONFIG_PORT',
  host: 'SBCONFIG_HOSTNAME',
  staticDir: 'SBCONFIG_STATIC_DIR',
  configDir: 'SBCONFIG_CONFIG_DIR',
});

if (!program.port) {
  logger.error('Error: port to run Storybook is required!\n');
  program.help();
  process.exit(-1);
}

// Used with `app.listen` below
const listenAddr = [program.port];

if (program.host) {
  listenAddr.push(program.host);
}

const app = express();
let server = app;

if (program.https) {
  if (!program.sslCert) {
    logger.error('Error: --ssl-cert is required with --https');
    process.exit(-1);
  }
  if (!program.sslKey) {
    logger.error('Error: --ssl-key is required with --https');
    process.exit(-1);
  }

  const sslOptions = {
    ca: (program.sslCa || []).map(ca => fs.readFileSync(ca, 'utf-8')),
    cert: fs.readFileSync(program.sslCert, 'utf-8'),
    key: fs.readFileSync(program.sslKey, 'utf-8'),
  };

  server = https.createServer(sslOptions, app);
}

let hasCustomFavicon = false;

if (program.staticDir) {
  program.staticDir = parseList(program.staticDir);
  program.staticDir.forEach(dir => {
    const staticPath = path.resolve(dir);
    if (!fs.existsSync(staticPath)) {
      logger.error(`Error: no such directory to load static files: ${staticPath}`);
      process.exit(-1);
    }
    logger.log(`=> Loading static files from: ${staticPath} .`);
    app.use(express.static(staticPath, { index: false }));

    const faviconPath = path.resolve(staticPath, 'favicon.ico');
    if (fs.existsSync(faviconPath)) {
      hasCustomFavicon = true;
      app.use(favicon(faviconPath));
    }
  });
}

if (!hasCustomFavicon) {
  app.use(favicon(path.resolve(__dirname, 'public/favicon.ico')));
}

// Build the webpack configuration using the `baseConfig`
// custom `.babelrc` file and `webpack.config.js` files
const configDir = program.configDir || './.storybook';

// The repository info is sent to the storybook while running on
// development mode so it'll be easier for tools to integrate.
const exec = cmd => shelljs.exec(cmd, { silent: true }).stdout.trim();
process.env.STORYBOOK_GIT_ORIGIN =
  process.env.STORYBOOK_GIT_ORIGIN || exec('git remote get-url origin');
process.env.STORYBOOK_GIT_BRANCH =
  process.env.STORYBOOK_GIT_BRANCH || exec('git symbolic-ref HEAD --short');

// NOTE changes to env should be done before calling `getBaseConfig`
// `getBaseConfig` function which is called inside the middleware
app.use(storybook(configDir));

let serverResolve = () => {};
let serverReject = () => {};
const serverListening = new Promise((resolve, reject) => {
  serverResolve = resolve;
  serverReject = reject;
});
server.listen(...listenAddr, error => {
  if (error) {
    serverReject(error);
  } else {
    serverResolve();
  }
});

Promise.all([webpackValid, serverListening])
  .then(() => {
    const proto = program.https ? 'https' : 'http';
    const address = `${proto}://${program.host || 'localhost'}:${program.port}/`;
    logger.info(`Storybook started on => ${chalk.cyan(address)}\n`);
    if (program.smokeTest) {
      process.exit(0);
    }
  })
  .catch(error => {
    if (error instanceof Error) {
      logger.error(error);
    }
  });
<|endoftext|>


<commit_before><commit_msg>Remove stale comment<commit_after><|endoftext|>


<commit_before>; (function($, undefined) {

    // Initial idea: http://blog.teamtreehouse.com/handlebars-js-part-3-tips-and-tricks

    // Plugin Logic
    $.fn.extend({

        // date doubles up as options when the action is 'clear' or 'remove'
        handlebars: function(action, template, data_or_options, options) {

            // jQuery object reference for this. Only select the first selector of the collection
            var $this = $(this).first();

            // Set our options from the defaults, overriding with the
            // parameter we pass into this function
            options = $.extend({}, $.fn.handlebars.options, options);

            // START: Sanitize the options

            // Check if the option type is a string
            if (typeof options.type === 'string') {

                // Set to uppercase
                options.type = options.type.toUpperCase();

            }

            // If the removal type is a string and valid, then set to uppercase
            if (typeof options.remove_type === 'string' && /^ALL|NONE|SAME$/i.test(options.remove_type)) {

                options.remove_type = options.remove_type.toUpperCase();

            } else {

                // Otherwise default to 'none' i.e. if null or is simply invalid
                options.remove_type = Remove.NONE;

            }

            // END: Sanitize the options

            // If a clear action is provided then remove the template
            if (/^CLEAR|REMOVE$/i.test(action)) {

                // Extend the options again, as the data_or_options acts as an alias for options when removal is used
                options = $.extend(options, data_or_options);

                // If the options parameter was undefined and the default is 'none', then temporarily
                // overwrite as 'all'; otherwise the user knows what they'e doing (I hope)
                if ((typeof data_or_options === 'undefined' || typeof data_or_options.remove_type === 'undefined') && options.remove_type === Remove.NONE) {

                    options.remove_type = Remove.ALL;

                    // Debugging only
                    console.log('jquery-handlebars: Overriding default removal type from "none" to "all"');
                }

                if (typeof template === 'undefined') {
                    template = null;
                }
                return removeTemplate(this, $this, template, options); // data is basically options in this instance

            }

            // Assume it's an addition i.e. 'add'

            // The data object literal must contain data
            if (options.validate && $.isEmptyObject(data_or_options)) {

                console.log('jquery-handlebars: Data was not passed or is an empty plain object');
                return this;

            }

            // jQuery object reference
            var $selector = null;

            // If a string is passed then check if it's compiled or get the jQuery object
            if (typeof template === 'string') {

                // If compiled already
                if (typeof compiled[template] === 'function') {

                    // Debugging only
                    console.log('jquery-handlebars: ' + template + ' already exists');

                    // Return to continue chaining
                    return setTemplate(this, $this, template, data_or_options, options);

                }

                // Not compiled

                // Get the first selection only
                $selector = $(template).first();

                // If the selector doesn't exist for whatever reason, then set to null
                if ($selector.length === 0) {
                    $selector = null;
                }

                // If a valid jQuery selector object
            } else if (template instanceof $ && typeof template.length === 'number' && template.length > 0) {

                // Get the first selection only
                $selector = template.first();

                // Get the selector name for using with the compiled object literal
                // If the selector property doesn't exist, then this will be set to null
                template = typeof $selector.selector !== 'undefined' ? $selector.selector : null;

                // If compiled already
                if (template !== null && typeof compiled[template] === 'function') {

                    // Debugging only
                    console.log('jquery-handlebars: ' + template + ' already exists');

                    // Return to continue chaining
                    return setTemplate(this, $this, template, data_or_options, options);

                }
            }

            // If an invalid selector, then return this to continue chaining
            if ($selector === null || template === null || $selector.length === 0) {

                // Debugging only
                console.log('jquery-handlebars: Template cannot be found');
                return this;

            }

            // Get the HTML of the template only
            var html = $selector.html();

            // Store the compiled template in the compiled store
            compiled[template] = Handlebars.compile(html);

            // Return to continue chaining
            return setTemplate(this, $this, template, data_or_options, options);
        }

    });

    // Fields (Private)

    // Store the compiled templates using the template string as the identifier i.e. key
    var compiled = {};

    // Methods (Private)

    // Note: Variable are called 'self' to avoid conflict with 'this'

    // Get the template in the content selector
    // include refers to whether to include the template in the selection
    var getTemplate = function ($self, template, include) {

            // Get the divs with the template data attribute and optional specified template e.g. #some-template
            var templateLocate = (include && template ? '="' + template + '"' : '');
            return $self.find('div[' + DATA_ATTRIBUTE + templateLocate + ']');

        },

        // Remove the specified template from the content selector. If a template is not provided
        // then all templates that are contained within the content selector will be removed
        removeTemplate = function (self, $self, template, options) {

            // If the option has been passed to remove 'none', then respect this choice
            if (options.remove_type === Remove.NONE) {

                // Debugging only
                console.log('jquery-handlebars: Templates not removed [' + options.remove_type + ']');

                // Return self to maintain chaining if there is nothing to remove
                return self;

            }

            var filtered = getTemplate($self, template, options.remove_type === Remove.SAME);
            if (filtered.length === 0) {

                // Debugging only
                console.log('jquery-handlebars: Unable to find any templates to remove [' + options.remove_type + ']');

                // Return self to maintain chaining if there is nothing to remove
                return self;

            }

            // Remove from the DOM
            filtered.remove();

            // Remove the template from the compiled store, if a template is provided and the option of remove 'same'
            if (options.remove_type === Remove.SAME && template) {

                // Set to undefined to mimic deletion of the template
                compiled[template] = undefined;

                // Debugging only
                console.log('jquery-handlebars: Removed from the compiled store [' + options.remove_type + ']');

            }

        },

        // Set the specified template to the content selector
        setTemplate = function (self, $self, template, data, options) {

            // Previous implementations
            /*
                $self.children('*').not('script[type="text/x-handlebars-template"]').empty();
                $self.children('*:not(script[type="text/x-handlebars-template"])').empty();

                // Get all nodes apart from the Handlebarjs template script elements
                 var filtered = $self.contents().filter(function() {

                    return this.nodeType !== Node.COMMENT_NODE && // Not a comment
                        (this.nodeName !== 'SCRIPT' && this.type !== 'text/x-handlebars-template') // Not a handlebars template
                        ; // /[^\t\n\r ]/.test(this.textContent); // Not whitespace

                });
             */

            var filtered = getTemplate($self, template, options.remove_type === Remove.SAME);

            // Remove from the DOM if allowed
            if (options.remove_type !== Remove.NONE) {

                // Remove from the DOM
                filtered.remove();

                // Debugging only
                console.log('jquery-handlebars: Removed previous templates [' + options.remove_type + ']');

            }

            // If set to not refill and template nodes exist, then return this
            if (!options.refill) {

                // Get the templates after possible removal. include has been set to true, as we are checking if only
                // the same templates exist
                filtered = getTemplate($self, template, true);
                if (filtered.length > 0) {

                    // Debugging only
                    console.log('jquery-handlebars: Refill has been set to false and the content element is not empty');

                    return self;

                }

            }

            // Append to this by checking the type. Default is append
            switch (options.type) {
                case 'FILL':
                case 'REFILL':
                    // $self.html(compiled[template](data)); // Dangerous to do if handlebarsjs templates are embedded inside the element
                    break;

                default:
                    // Create a div element with the template appended
                    // This contains a data-* attribute called data-jquery-handlebars for easy association
                    // that it's a template
                    var $div = $('<div/>')
                        .attr(DATA_ATTRIBUTE, template)
                        .append(compiled[template](data));

                    // Append the div to self i.e. the content block
                    $self.append($div);
                    break;
            }

            return self;
        };

    // Constants

    // The data-* attribute name
    var DATA_ATTRIBUTE = 'data-jquery-handlebars',

        // Removal constants. As who enjoys magic values?
        Remove = {
            ALL: 'ALL',
            NONE: 'NONE',
            SAME: 'SAME'
        };

    // Defaults

    $.fn.handlebars.options = {
        // Allow the option of adding multiple templates inside an element
        refill: true,

        // Removal options
        // 'all': Remove all valid templates
        // 'same': Remove only those templates that match the provided template
        // 'none' (default): Don't remove any templates
        remove_type: 'none',

        // Type of writing: fill, refill, append (default)
        type: 'append',

        // Check whether the data passed is empty
        validate: true
    };

})(jQuery);<commit_msg>Improved comments and debugging lines<commit_after>; (function($, undefined) {

    // Initial idea: http://blog.teamtreehouse.com/handlebars-js-part-3-tips-and-tricks

    // Plugin Logic
    $.fn.extend({

        // date doubles up as options when the action is 'clear' or 'remove'
        handlebars: function(action, template, data_or_options, options) {

            // jQuery object reference for this. Only select the first selector of the collection
            var $this = $(this).first();

            // Set our options from the defaults, overriding with the
            // parameter we pass into this function
            options = $.extend({}, $.fn.handlebars.options, options);

            // START: Sanitize the options

            // Check if the option type is a string
            if (typeof options.type === 'string') {

                // Set to uppercase
                options.type = options.type.toUpperCase();

            }

            // If the removal type is a string and valid, then set to uppercase
            if (typeof options.remove_type === 'string' && /^ALL|NONE|SAME$/i.test(options.remove_type)) {

                options.remove_type = options.remove_type.toUpperCase();

            } else {

                // Otherwise default to 'none' i.e. if null or is simply invalid
                options.remove_type = Remove.NONE;

            }

            // END: Sanitize the options

            // If a clear action is provided then remove the template
            if (/^CLEAR|REMOVE$/i.test(action)) {

                // Extend the options again, as the data_or_options acts as an alias for options when removal is used
                options = $.extend(options, data_or_options);

                // If the options parameter was undefined and the default is 'none', then temporarily
                // overwrite as 'all'; otherwise the user knows what they'e doing (I hope)
                if ((typeof data_or_options === 'undefined' || typeof data_or_options.remove_type === 'undefined') && options.remove_type === Remove.NONE) {

                    options.remove_type = Remove.ALL;

                    // Debugging only
                    console.log('jquery-handlebars: Overriding default removal type from "none" to "all" since the remove action was passed to the plugin');
                }

                if (typeof template === 'undefined') {
                    template = null;
                }

                return removeTemplate(this, $this, template, options); // data is basically options in this instance

            }

            // Assume it's an addition i.e. 'add'

            // The data object literal must contain data
            if (options.validate && $.isEmptyObject(data_or_options)) {

                console.log('jquery-handlebars: Data was not passed or is simpley an empty plain object');
                return this;

            }

            // jQuery object reference
            var $selector = null;

            // If a string is passed then check if it's compiled or get the jQuery object
            if (typeof template === 'string') {

                // If compiled already
                if (typeof compiled[template] === 'function') {

                    // Debugging only
                    console.log('jquery-handlebars: ' + template + ' already exists');

                    // Return to continue chaining
                    return setTemplate(this, $this, template, data_or_options, options);

                }

                // Not compiled

                // Get the first selection only
                $selector = $(template).first();

                // If the selector doesn't exist for whatever reason, then set to null
                if ($selector.length === 0) {
                    $selector = null;
                }

                // If a valid jQuery selector object
            } else if (template instanceof $ && typeof template.length === 'number' && template.length > 0) {

                // Get the first selection only
                $selector = template.first();

                // Get the selector name for using with the compiled object literal
                // If the selector property doesn't exist, then this will be set to null
                template = typeof $selector.selector !== 'undefined' ? $selector.selector : null;

                // If compiled already
                if (template !== null && typeof compiled[template] === 'function') {

                    // Debugging only
                    console.log('jquery-handlebars: ' + template + ' already exists');

                    // Return to continue chaining
                    return setTemplate(this, $this, template, data_or_options, options);

                }
            }

            // If an invalid selector, then return this to continue chaining
            if ($selector === null || template === null || $selector.length === 0) {

                // Debugging only
                console.log('jquery-handlebars: Template cannot be found');
                return this;

            }

            // Get the HTML of the template only
            var html = $selector.html();

            // Store the compiled template in the compiled store
            compiled[template] = Handlebars.compile(html);

            // Return to continue chaining
            return setTemplate(this, $this, template, data_or_options, options);
        }

    });

    // Fields (Private)

    // Store the compiled template(s) using the template string as the identifier i.e. key
    var compiled = {};

    // Methods (Private)

    // Note: Variable are called 'self' to avoid conflict with 'this'

    // Get the template in the content selector
    // include refers to whether to include the template in the selection
    var getTemplate = function ($self, template, include) {

            // Get the divs with the template data attribute and optional specified template e.g. #some-template
            var templateLocate = (include && template ? '="' + template + '"' : '');
            return $self.find('div[' + DATA_ATTRIBUTE + templateLocate + ']');

        },

        // Remove the specified template from the content selector. If a template is not provided
        // then all template(s) that are contained within the content selector will be removed
        removeTemplate = function (self, $self, template, options) {

            // If the option has been passed to remove 'none', then respect this choice
            if (options.remove_type === Remove.NONE) {

                // Debugging only
                console.log('jquery-handlebars: Template(s) were not removed [' + options.remove_type + ']');

                // Return self to maintain chaining if there is nothing to remove
                return self;

            }

            var filtered = getTemplate($self, template, options.remove_type === Remove.SAME);
            if (filtered.length === 0) {

                // Debugging only
                console.log('jquery-handlebars: Unable to find any template(s) for removal [' + options.remove_type + ']');

                // Return self to maintain chaining if there is nothing to remove
                return self;

            }

            // Remove from the DOM
            filtered.remove();

            // Debugging only
            console.log('jquery-handlebars: Removing template(s) [' + options.remove_type + ']');

            // Remove the template from the compiled store, if a template is provided and the option of remove 'same'
            if (options.remove_type === Remove.SAME && template) {

                // Set to undefined to mimic deletion of the template
                compiled[template] = undefined;

                // Debugging only
                console.log('jquery-handlebars: Removed the template from the compiled store [' + template + ']');

            }

        },

        // Set the specified template to the content selector
        setTemplate = function (self, $self, template, data, options) {

            // Previous implementations
            /*
                $self.children('*').not('script[type="text/x-handlebars-template"]').empty();
                $self.children('*:not(script[type="text/x-handlebars-template"])').empty();

                // Get all nodes apart from the Handlebarjs template script elements
                 var filtered = $self.contents().filter(function() {

                    return this.nodeType !== Node.COMMENT_NODE && // Not a comment
                        (this.nodeName !== 'SCRIPT' && this.type !== 'text/x-handlebars-template') // Not a handlebars template
                        ; // /[^\t\n\r ]/.test(this.textContent); // Not whitespace

                });
             */

            var filtered = getTemplate($self, template, options.remove_type === Remove.SAME);

            // Remove from the DOM if specified
            if (options.remove_type !== Remove.NONE) {

                // Remove from the DOM
                filtered.remove();

                // Debugging only
                console.log('jquery-handlebars: Removed previous template(s) [' + options.remove_type + ']');

            }

            // If set to not refill and template node(s) exist, then return this
            if (!options.refill) {

                // Get the template(s) after possible removal. include has been set to true, as we are checking if only
                // the same template(s) exist
                filtered = getTemplate($self, template, true);
                if (filtered.length > 0) {

                    // Debugging only
                    console.log('jquery-handlebars: Refill has been set to false and the content element contains template(s) [' + options.remove_type + ']');

                    return self;

                }

            }

            // Append to this by checking the type. Default is append
            switch (options.type) {
                case 'FILL':
                case 'REFILL':
                    // $self.html(compiled[template](data)); // Dangerous to do if handlebarsjs template(s) are embedded inside the element
                    // Debugging only
                    console.log('jquery-handlebars: fill/refill is NOT IMPLEMENTED [' + template + ']');
                    break;

                default:
                    // Create a div element with the template appended
                    // This contains a data-* attribute called data-jquery-handlebars for easy association
                    // that it's a template
                    var $div = $('<div/>')
                        .attr(DATA_ATTRIBUTE, template)
                        .append(compiled[template](data));

                    // Append the div to self i.e. the content block
                    $self.append($div);

                    // Debugging only
                    console.log('jquery-handlebars: Appending template to the content element [' + template + ']');
                    break;
            }

            return self;
        };

    // Constants

    // The data-* attribute name
    var DATA_ATTRIBUTE = 'data-jquery-handlebars',

        // Removal constants. As who enjoys magic values?
        Remove = {
            ALL: 'ALL',
            NONE: 'NONE',
            SAME: 'SAME'
        };

    // Defaults

    $.fn.handlebars.options = {
        // Allow the option of adding multiple template(s) inside an element
        refill: true,

        // Removal options
        // 'all': Remove all valid template(s)
        // 'same': Remove only those template(s) that match the provided template
        // 'none' (default): Don't remove any template(s)
        remove_type: 'none',

        // Type of writing: fill, refill, append (default)
        type: 'append',

        // Check whether the data passed is empty
        validate: true
    };

})(jQuery);<|endoftext|>