<commit_before>"use strict";
// Log class

var config    = require('./config');
var plugins;
var connection;
var constants = require('./constants');
var util      = require('util');

var logger = exports;

logger.LOGDATA      = 9;
logger.LOGPROTOCOL  = 8;
logger.LOGDEBUG     = 7;
logger.LOGINFO      = 6;
logger.LOGNOTICE    = 5;
logger.LOGWARN      = 4;
logger.LOGERROR     = 3;
logger.LOGCRIT      = 2;
logger.LOGALERT     = 1;
logger.LOGEMERG     = 0;

var loglevel = logger.LOGWARN;

var deferred_logs = [];

logger.dump_logs = function () {
    while (deferred_logs.length > 0) {
        var log_item = deferred_logs.shift();
        console.log(log_item.data);
    }
}

logger.log = function (level, data) {
    data = data.replace(/\r?\n/g, "\\n");
    // todo - just buffer these up (defer) until plugins are loaded
    if (plugins.plugin_list) {
        while (deferred_logs.length > 0) {
            var log_item = deferred_logs.shift();
            plugins.run_hooks('log', logger, log_item);
        }
        plugins.run_hooks('log', logger, {
            'level' : level,
            'data'  : data
        });
    }
    else {
        deferred_logs.push({
            'level' : level,
            'data'  : data
        });
    }
}

logger.log_respond = function (retval, msg, data) {
    // any other return code is irrelevant
    if (retval === constants.cont) {
        return console.log(data.data);
    }
};

// TODO: would be nice if there were some way to reload the loglevel
logger._init_loglevel = function () {
    var _loglevel = config.get('loglevel');
    if (_loglevel) {
        var loglevel_num = parseInt(_loglevel);
        if (!loglevel_num || loglevel_num === NaN) {
            loglevel = logger[_loglevel.toUpperCase()];
        }
        else {
            loglevel = loglevel_num;
        }
        if (!loglevel) {
            loglevel = logger.LOGWARN;
        }
    }
};

logger._init_loglevel();

var level, key;
for (key in logger) {
    if(logger.hasOwnProperty(key)) {
        if (key.match(/^LOG\w/)) {
            level = key.slice(3);
            logger[key.toLowerCase()] = (function(level, key) {
                return function() {
                    if (loglevel < logger[key])
                        return;
                    var levelstr = "[" + level + "]";
                    var str = "";
                    var uuidstr = "[no_connection]";
                    var pluginstr = "[haraka_core]";
                    for (var i = 0; i < arguments.length; i++) {
                        var data = arguments[i];
                        if (typeof(data) === 'object') {
                            // if the object is a connection, we wish to add
                            // the connection id
                            if (data instanceof connection.Connection) {
                                uuidstr = "[" + data.uuid;
                                if (data.tran_count > 0) {
                                  uuidstr += "." + data.tran_count;
                                }
                                uuidstr += "]";
                            } else if (data instanceof plugins.Plugin) {
                                pluginstr = "[" + data.name + "]";
                            } else {
                                str += util.inspect(data);
                            }
                        }
                        else {
                            str += data;
                        }
                    }
                    logger.log(level, [levelstr, uuidstr, pluginstr, str].join(" "));
                }
            })(level, key);
        }
    }
}

// load these down here so it sees all the logger methods compiled above
plugins = require('./plugins');
connection = require('./connection');
<commit_msg>Clean up else{} cuddling<commit_after>"use strict";
// Log class

var config    = require('./config');
var plugins;
var connection;
var constants = require('./constants');
var util      = require('util');

var logger = exports;

logger.LOGDATA      = 9;
logger.LOGPROTOCOL  = 8;
logger.LOGDEBUG     = 7;
logger.LOGINFO      = 6;
logger.LOGNOTICE    = 5;
logger.LOGWARN      = 4;
logger.LOGERROR     = 3;
logger.LOGCRIT      = 2;
logger.LOGALERT     = 1;
logger.LOGEMERG     = 0;

var loglevel = logger.LOGWARN;

var deferred_logs = [];

logger.dump_logs = function () {
    while (deferred_logs.length > 0) {
        var log_item = deferred_logs.shift();
        console.log(log_item.data);
    }
}

logger.log = function (level, data) {
    data = data.replace(/\r?\n/g, "\\n");
    // todo - just buffer these up (defer) until plugins are loaded
    if (plugins.plugin_list) {
        while (deferred_logs.length > 0) {
            var log_item = deferred_logs.shift();
            plugins.run_hooks('log', logger, log_item);
        }
        plugins.run_hooks('log', logger, {
            'level' : level,
            'data'  : data
        });
    }
    else {
        deferred_logs.push({
            'level' : level,
            'data'  : data
        });
    }
}

logger.log_respond = function (retval, msg, data) {
    // any other return code is irrelevant
    if (retval === constants.cont) {
        return console.log(data.data);
    }
};

// TODO: would be nice if there were some way to reload the loglevel
logger._init_loglevel = function () {
    var _loglevel = config.get('loglevel');
    if (_loglevel) {
        var loglevel_num = parseInt(_loglevel);
        if (!loglevel_num || loglevel_num === NaN) {
            loglevel = logger[_loglevel.toUpperCase()];
        }
        else {
            loglevel = loglevel_num;
        }
        if (!loglevel) {
            loglevel = logger.LOGWARN;
        }
    }
};

logger._init_loglevel();

var level, key;
for (key in logger) {
    if(logger.hasOwnProperty(key)) {
        if (key.match(/^LOG\w/)) {
            level = key.slice(3);
            logger[key.toLowerCase()] = (function(level, key) {
                return function() {
                    if (loglevel < logger[key])
                        return;
                    var levelstr = "[" + level + "]";
                    var str = "";
                    var uuidstr = "[no_connection]";
                    var pluginstr = "[haraka_core]";
                    for (var i = 0; i < arguments.length; i++) {
                        var data = arguments[i];
                        if (typeof(data) === 'object') {
                            // if the object is a connection, we wish to add
                            // the connection id
                            if (data instanceof connection.Connection) {
                                uuidstr = "[" + data.uuid;
                                if (data.tran_count > 0) {
                                  uuidstr += "." + data.tran_count;
                                }
                                uuidstr += "]";
                            }
                            else if (data instanceof plugins.Plugin) {
                                pluginstr = "[" + data.name + "]";
                            }
                            else {
                                str += util.inspect(data);
                            }
                        }
                        else {
                            str += data;
                        }
                    }
                    logger.log(level, [levelstr, uuidstr, pluginstr, str].join(" "));
                }
            })(level, key);
        }
    }
}

// load these down here so it sees all the logger methods compiled above
plugins = require('./plugins');
connection = require('./connection');
<|endoftext|>


<commit_before>var strftime = require('strftime').utc();
var fs = require('fs');
require('colors');

module.exports = function (config) {
	config = config || {};
	var exports = {};

	config.levels = config.levels || {
		"trace": 0,
		"debug": 1,
		"log": 2,
		"info": 3,
		"warn": 4,
		"error": 5,
		"fatal": 6
	}

	config.filename = config.filename || __dirname + '/logs.log';

	config.errorLevel = config.errorLevel || "log";

	var log_file = fs.createWriteStream(config.filename, {flags: 'a'});

	exports.setLevel = function (errorLevel) {
		config.errorLevel = errorLevel;
	}

	Object.keys(config.levels).forEach(function (name) {
		function log(caption, data) {
			var log = {
				"level": name,
				"message": caption,
				"timestamp": strftime('%F %T', new Date())
			}

			data && (log["data"] = data);

			if (config.levels[config.errorLevel] <= config.levels[log.level]) {
				log_file.write(JSON.stringify(log) + '\n');
			}
			if (config.echo && config.levels[config.echo] <= config.levels[log.level]) {
				try {
					console.log(log.level.bgYellow.black, log.timestamp.grey, log.message, log.data ? log.data : '');
				}catch (e){
					console.log(e)
				}
			}
		}

		exports[name] = log;
	})

	return exports;
}
<commit_msg>Beautifying code.<commit_after>var strftime = require("strftime").utc();
var fs = require("fs");
require("colors");

module.exports = function (config) {
	config = config || {};
	var exports = {};

	config.levels = config.levels || {
		"trace": 0,
		"debug": 1,
		"log": 2,
		"info": 3,
		"warn": 4,
		"error": 5,
		"fatal": 6
	}

	config.filename = config.filename || __dirname + "/logs.log";

	config.errorLevel = config.errorLevel || "log";

	var log_file = fs.createWriteStream(config.filename, { flags: "a" });

	exports.setLevel = function (errorLevel) {
		config.errorLevel = errorLevel;
	}

	Object.keys(config.levels).forEach(function (name) {
		function log(caption, data) {
			var log = {
				"level": name,
				"message": caption,
				"timestamp": strftime("%F %T", new Date())
			}

			data && (log["data"] = data);

			if (config.levels[config.errorLevel] <= config.levels[log.level]) {
				log_file.write(JSON.stringify(log) + "\n");
			}
			if (config.echo && config.levels[config.echo] <= config.levels[log.level]) {
				try {
					console.log(log.level.bgYellow.black, log.timestamp.grey, log.message, log.data ? log.data : "");
				}catch (e){
					console.log(e)
				}
			}
		}

		exports[name] = log;
	})

	return exports;
}
<|endoftext|>


<commit_before>Resources = new Mongo.Collection('resources');

Resources.attachSchema(new SimpleSchema({
    title: {
        type: String,
        label: "Title",
        max: 200
    }
});
<commit_msg>Additional fields.<commit_after>Resources = new Mongo.Collection('resources');

Resources.attachSchema(new SimpleSchema({
    title: {
        type: String,
        label: "Title",
        max: 200
    },
    keywords: {
        type: [String],
        label: "Keywords"
    },
    published: {
        type: Boolean,
        label: "Published"
    }
});
<|endoftext|>


<commit_before>// Made by Cr4xy - Helped by MastaCoder :)
// https://www.youtube.com/channel/UC1CJCNc6rrtjJzxiqYN97aQ - Support Cr4xy's YouTube!

var agarClient = require("agario-client")
	config = require("./config.js"),
	token = null,
	account = new agarClient.Account(),
	STATIC_NAME = config.name,
	debugObj = {},
	regions = config.regions,
	DefaultAi = new (require("./ai/default_ai.js")),
	AposAi = new (require("./ai/apos_ai.js"));

var VERSION = 0.93;

var currentSeconds = 0; // Starts at 0
var accountIndex = 0; // Gives number to each token and account.
var accountCount = 0; // Gives number of attempted accounts.
var regionCounter = 0; // Give number to each region.
var requestTries = 0; // Requests to get the server tokens.

Array.prototype.contains = function(element) {
	return this.indexOf(element) >= 0;
}
Array.prototype.add = Array.prototype.push;
Array.prototype.remove = function(element) {
	if (this.contains(element)) this.splice(this.indexOf(element), 1);
}

// Check if no region is enabled
!function() {
	if (config.regions.length > 0) return;
	console.log("No region enabled or found. oh well");
	process.exit();
}();

// Check for updates
!function() {
	require("https").get('https://raw.githubusercontent.com/Cr4xy/agar-lvlgen/master/version', (res) => {
		res.on('data', function (bytes) {
			var fetched_version = bytes.toString();

			console.log("\u001B[31m\n########################");
			console.log("\u001B[32m    _                         _         _  ____");
			console.log("   / \\   __ _  __ _ _ __     | | __   _| |/ ___| ___ _ __");
			console.log("  / _ \\ / _` |/ _` | '__|____| | \\ \\ / / | |  _ / _ \\ '_ \\ ");
			console.log(" / ___ \\ (_| | (_| | | |_____| |__\\ V /| | |_| |  __/ | | |");
			console.log("/_/   \\_\\__, |\\__,_|_|       |_____\\_/ |_|\\____|\\___|_| |_|");
        	console.log("	|___/ \u001B[33m- Open Source Agar.io Level Farming! \u001B[0m \n");

			if (!isNaN(fetched_version) && isFinite(fetched_version)) {
				if (VERSION < fetched_version) {
					console.log("Running version: " + VERSION + " (New version " + fetched_version + " found, download off agar-lvlgen Github repo)");
				} else {
					console.log("Running version: " + VERSION + " (Latest)");
				}
			} else {
				console.log("Running version: " + VERSION + " (Failed to fetch)");
			}

			console.log("Will reset in: " + config.reset + " minutes")

			console.log("\n\u001B[31m########################\u001B[0m\n");
		});
	});
}();

function getRegion() {
	regionCounter++;
	if (regionCounter >= regions.length) regionCounter = 0;
	return regions[regionCounter];
}

function getServerOptions() {
	return {region: getRegion()};
}

function requestToken(c_user, datr, xs) {
	account.c_user = c_user || config.accounts[accountIndex].c_user;
	account.datr = datr || config.accounts[accountIndex].datr;
	account.xs = xs || config.accounts[accountIndex].xs;

	account.requestFBToken(function(token, info) {
		if (!token) {
			if (requestTries++ >= 5) {
				accountCount++;
				console.log("[Account " + accountCount + "] Token Failed: Token failed after multiple tries.");
				process.exit();
			}
			console.log("[Account " + accountCount + "] Token Failed: Token failed after " + requestTries + " tries, will try again.");
			requestToken();
		} else {
			accountCount++;
			if (config.showtoken == true) {
				console.log("[Account " + accountCount + "] Token Success: ", token);
			} else {
				console.log("[Account " + accountCount + "] Token Success: Token Hidden!");
			}
			agarClient.servers.getFFAServer(getServerOptions(), function(e) {
				var server = e.server;
				var key = e.key;
				start(server, key, token, account);
			});
			requestTries = 0;
		}
	});
}

// Get token & server, then start
!function getTokenAndServer() {
	setTimeout(function() {
		requestToken();
	}, 500);
	accountIndex++;
	if (accountIndex >= config.accounts.length) {
		accountIndex = 0;
		return;
	}
	getTokenAndServer();
}();

var clientIdCounter = 0;
var bots = [];

function start(server, key, token, acc) {
	var myClient = new agarClient("Client_" + clientIdCounter++);
	myClient.debug = 0;
	myClient.auth_token = token;
	var myBotObj = {spawned: false, client: myClient, account: acc};
	myClient.on('disconnect', function() {
		bots.remove(myBotObj);
		clearInterval(myClient.sendInterval);
		myClient = null;
	});
	myClient.on('packetError', function(packet, error, preventCrash) {
		preventCrash();
	});
	myClient.on('connected', function() {
		bots.add(myBotObj);
		myClient.spawn(STATIC_NAME);
		myClient.sendInterval = setInterval(function() {
			if (config.ai == "default") {
				DefaultAi.update(myClient);
			} else {
				if (myClient.my_balls.length == 0) return;
				var myBalls = [];
				for (var i in myClient.my_balls) myClient.balls[myClient.my_balls[i]] && (myBalls.push(myClient.balls[myClient.my_balls[i]]));
				AposAi.setPlayer(myBalls);
				AposAi.setMemoryCells(myClient.balls);
				var destination = AposAi.mainLoop(myClient.balls);
				//console.log(destination);
				myClient.moveTo(destination[0], destination[1]);
			}
		}, 40);
	});
	myClient.on('myNewBall', function() {
		myBotObj.spawned = true;
	});
	myClient.on('lostMyBalls', function() {
		myBotObj.spawned = false;
		myClient.spawn(STATIC_NAME);
	});
	myClient.connect("ws://" + server, key);
}

setInterval(function() {
	for (var i = 0; i < bots.length; i++) {
		var acc = bots[i].account;
		if (Date.now() > acc.token_expire) {
			requestToken(acc.c_user, acc.datr, acc.xs)
			bots.remove(bots[i]);
		}
	}
}, 10000);

setInterval(function() {
	currentSeconds++;
}, 1000);

if (config.reset > 0) {
	setTimeout(function() {
		process.exit();
	}, config.reset * 1000);
}

setInterval(function() {
	var totalScore = 0;
	var spawnedCount = 0;
	var highestScore = 0;
	for (var i = 0; i < bots.length; i++) bots[i].spawned && (spawnedCount++, totalScore += bots[i].client.score, highestScore = Math.max(highestScore, bots[i].client.score));
	var avgScore = parseInt((totalScore / Math.max(1, spawnedCount)).toFixed(0));
	debugObj.connected = bots.length;
	debugObj.spawned = spawnedCount;
	debugObj.totalScore = totalScore;
	debugObj.avgScore = avgScore;
	debugObj.highest = highestScore;
	debugObj.time = currentSeconds;
	console.log(" ");
	console.log(debugObj);
}, config.statusDelay);<commit_msg>Update lvlgen.js<commit_after>// Made by Cr4xy - Helped by MastaCoder :)
// https://www.youtube.com/channel/UC1CJCNc6rrtjJzxiqYN97aQ - Support Cr4xy's YouTube!

var agarClient = require("agario-client")
	config = require("./config.js"),
	token = null,
	account = new agarClient.Account(),
	STATIC_NAME = config.name,
	debugObj = {},
	regions = config.regions,
	DefaultAi = new (require("./ai/default_ai.js")),
	AposAi = new (require("./ai/apos_ai.js"));

var VERSION = 0.93;

var currentSeconds = 0; // Starts at 0
var accountIndex = 0; // Gives number to each token and account.
var accountCount = 0; // Gives number of attempted accounts.
var regionCounter = 0; // Give number to each region.
var requestTries = 0; // Requests to get the server tokens.

Array.prototype.contains = function(element) {
	return this.indexOf(element) >= 0;
}
Array.prototype.add = Array.prototype.push;
Array.prototype.remove = function(element) {
	if (this.contains(element)) this.splice(this.indexOf(element), 1);
}

// Check if no region is enabled
!function() {
	if (config.regions.length > 0) return;
	console.log("No region enabled or found. oh well");
	process.exit();
}();

// Check for updates
!function() {
	require("https").get('https://raw.githubusercontent.com/Cr4xy/agar-lvlgen/master/version', function(res) {
		res.on('data', function (bytes) {
			var fetched_version = bytes.toString();

			console.log("\u001B[31m\n########################");
			console.log("\u001B[32m    _                         _         _  ____");
			console.log("   / \\   __ _  __ _ _ __     | | __   _| |/ ___| ___ _ __");
			console.log("  / _ \\ / _` |/ _` | '__|____| | \\ \\ / / | |  _ / _ \\ '_ \\ ");
			console.log(" / ___ \\ (_| | (_| | | |_____| |__\\ V /| | |_| |  __/ | | |");
			console.log("/_/   \\_\\__, |\\__,_|_|       |_____\\_/ |_|\\____|\\___|_| |_|");
        	console.log("	|___/ \u001B[33m- Open Source Agar.io Level Farming! \u001B[0m \n");

			if (!isNaN(fetched_version) && isFinite(fetched_version)) {
				if (VERSION < fetched_version) {
					console.log("Running version: " + VERSION + " (New version " + fetched_version + " found, download off agar-lvlgen Github repo)");
				} else {
					console.log("Running version: " + VERSION + " (Latest)");
				}
			} else {
				console.log("Running version: " + VERSION + " (Failed to fetch)");
			}

			console.log("Will reset in: " + config.reset + " minutes")

			console.log("\n\u001B[31m########################\u001B[0m\n");
		});
	});
}();

function getRegion() {
	regionCounter++;
	if (regionCounter >= regions.length) regionCounter = 0;
	return regions[regionCounter];
}

function getServerOptions() {
	return {region: getRegion()};
}

function requestToken(c_user, datr, xs) {
	account.c_user = c_user || config.accounts[accountIndex].c_user;
	account.datr = datr || config.accounts[accountIndex].datr;
	account.xs = xs || config.accounts[accountIndex].xs;

	account.requestFBToken(function(token, info) {
		if (!token) {
			if (requestTries++ >= 5) {
				accountCount++;
				console.log("[Account " + accountCount + "] Token Failed: Token failed after multiple tries.");
				process.exit();
			}
			console.log("[Account " + accountCount + "] Token Failed: Token failed after " + requestTries + " tries, will try again.");
			requestToken();
		} else {
			accountCount++;
			if (config.showtoken == true) {
				console.log("[Account " + accountCount + "] Token Success: ", token);
			} else {
				console.log("[Account " + accountCount + "] Token Success: Token Hidden!");
			}
			agarClient.servers.getFFAServer(getServerOptions(), function(e) {
				var server = e.server;
				var key = e.key;
				start(server, key, token, account);
			});
			requestTries = 0;
		}
	});
}

// Get token & server, then start
!function getTokenAndServer() {
	setTimeout(function() {
		requestToken();
	}, 500);
	accountIndex++;
	if (accountIndex >= config.accounts.length) {
		accountIndex = 0;
		return;
	}
	getTokenAndServer();
}();

var clientIdCounter = 0;
var bots = [];

function start(server, key, token, acc) {
	var myClient = new agarClient("Client_" + clientIdCounter++);
	myClient.debug = 0;
	myClient.auth_token = token;
	var myBotObj = {spawned: false, client: myClient, account: acc};
	myClient.on('disconnect', function() {
		bots.remove(myBotObj);
		clearInterval(myClient.sendInterval);
		myClient = null;
	});
	myClient.on('packetError', function(packet, error, preventCrash) {
		preventCrash();
	});
	myClient.on('connected', function() {
		bots.add(myBotObj);
		myClient.spawn(STATIC_NAME);
		myClient.sendInterval = setInterval(function() {
			if (config.ai == "default") {
				DefaultAi.update(myClient);
			} else {
				if (myClient.my_balls.length == 0) return;
				var myBalls = [];
				for (var i in myClient.my_balls) myClient.balls[myClient.my_balls[i]] && (myBalls.push(myClient.balls[myClient.my_balls[i]]));
				AposAi.setPlayer(myBalls);
				AposAi.setMemoryCells(myClient.balls);
				var destination = AposAi.mainLoop(myClient.balls);
				//console.log(destination);
				myClient.moveTo(destination[0], destination[1]);
			}
		}, 40);
	});
	myClient.on('myNewBall', function() {
		myBotObj.spawned = true;
	});
	myClient.on('lostMyBalls', function() {
		myBotObj.spawned = false;
		myClient.spawn(STATIC_NAME);
	});
	myClient.connect("ws://" + server, key);
}

setInterval(function() {
	for (var i = 0; i < bots.length; i++) {
		var acc = bots[i].account;
		if (Date.now() > acc.token_expire) {
			requestToken(acc.c_user, acc.datr, acc.xs)
			bots.remove(bots[i]);
		}
	}
}, 10000);

setInterval(function() {
	currentSeconds++;
}, 1000);

if (config.reset > 0) {
	setTimeout(function() {
		process.exit();
	}, config.reset * 1000);
}

setInterval(function() {
	var totalScore = 0;
	var spawnedCount = 0;
	var highestScore = 0;
	for (var i = 0; i < bots.length; i++) bots[i].spawned && (spawnedCount++, totalScore += bots[i].client.score, highestScore = Math.max(highestScore, bots[i].client.score));
	var avgScore = parseInt((totalScore / Math.max(1, spawnedCount)).toFixed(0));
	debugObj.connected = bots.length;
	debugObj.spawned = spawnedCount;
	debugObj.totalScore = totalScore;
	debugObj.avgScore = avgScore;
	debugObj.highest = highestScore;
	debugObj.time = currentSeconds;
	console.log(" ");
	console.log(debugObj);
}, config.statusDelay);
<|endoftext|>


<commit_before>import Animation from './Animation';

export default class Diascope {
	constructor(frame, reel, options = {}) {
		this.options = Object.assign(getDefaultOptions(), options);

		this.elementFrame = frame;
		this.elementReel = reel;
		this.elementsSlides = Array.from(this.elementReel.children);
		this.animationEasing = options.animationEasing;

		if (this.options.hasOwnProperty('elementNavigateNext')) {
			this.addElementNavigateNext(this.options.elementNavigateNext);
		}

		if (this.options.hasOwnProperty('elementNavigatePrevious')) {
			this.addElementNavigatePrevious(this.options.elementNavigatePrevious);
		}
	}

	next() {
		this.panSlides(this.options.step);
	}

	previous() {
		this.panSlides(this.options.step * -1);
	}

	panSlides(pan) {
		let newSlides = findNewVisibleSlides(pan, this.elementsSlides, this.elementFrame, this.options.loop);

		if (newSlides.length > 0) {
			let reelOffsetLeft = calculateLeftReelOffsetToBringSlidesIntoFrame(
				newSlides,
				this.elementReel,
				this.elementFrame,
				this.options.shouldCenter
			);

			this.reelAnimation = new Animation(this.elementReel, reelOffsetLeft, this.options.duration, this.animationEasing);
			this.reelAnimation.start();
		}
	}

	addElementNavigateNext(element) {
		if (element instanceof Element) {
			addEvent('click', element, this.next.bind(this));
		}
	}

	addElementNavigatePrevious(element) {
		if (element instanceof Element) {
			addEvent('click', element, this.previous.bind(this));
		}
	}

	setAnimationEasing(easing) {
		this.animationEasing = easing;
	}
}

function getDefaultOptions() {
	return {
		duration: 0.1,
		step: 1,
		loop: false,
		shouldCenter: false,
		animationEasing: 'linear',
	};
}

function findNewVisibleSlides(pan, slides, frame, shouldLoop) {
	const firstSlideIndex = 0;
	const lastSlideIndex = slides.length - 1;

	let newSlides = [],
		firstVisibleSlideIndex,
		lastVisibleSlideIndex,
		firstNewSlideIndex,
		lastNewSlideIndex;

	firstVisibleSlideIndex = findIndexOfFirstVisibleSlideInFrame(slides, frame);
	lastVisibleSlideIndex = findIndexOfLastVisibleSlideInFrame(slides, frame);

	// loop to the first slide.
	if (pan > 0 && shouldLoop && lastVisibleSlideIndex === lastSlideIndex) {
		return [slides[0]];
	}

	// loop to the last slide.
	if (pan < 0 && shouldLoop && firstVisibleSlideIndex === firstSlideIndex) {
		return [slides[lastSlideIndex]];
	}

	if (lastVisibleSlideIndex + pan > lastSlideIndex) {
		pan = lastSlideIndex - lastVisibleSlideIndex;
		firstNewSlideIndex = lastSlideIndex - pan;
		lastNewSlideIndex = lastSlideIndex;
	} else if (firstVisibleSlideIndex + pan < 0) {
		pan = firstSlideIndex - firstVisibleSlideIndex;
		firstNewSlideIndex = 0
		lastNewSlideIndex = 0 - pan;
	} else {
		firstNewSlideIndex = firstVisibleSlideIndex + pan;
		lastNewSlideIndex = lastVisibleSlideIndex + pan;
	}

	for (let i = firstNewSlideIndex; i <= lastNewSlideIndex; i++) {
		newSlides.push(slides[i]);
	}

	return newSlides;
}

function findIndexOfFirstVisibleSlideInFrame(slides, frame) {
	for (let currentSlideIndex = 0; currentSlideIndex < slides.length; currentSlideIndex++) {
		if (isSlideInFrame(slides[currentSlideIndex], frame)) {
			return currentSlideIndex;
		}
	}
}

function findIndexOfLastVisibleSlideInFrame(slides, frame) {
	let lastVisibleSlideIndex;

	for (let currentSlideIndex = 0; currentSlideIndex < slides.length; currentSlideIndex++) {
		if (isSlideInFrame(slides[currentSlideIndex], frame)) {
			lastVisibleSlideIndex = currentSlideIndex;
		}
	}

	return lastVisibleSlideIndex;
}

function calculateLeftReelOffsetToBringSlidesIntoFrame(slides, reel, frame, shouldCenter = false) {
	let slidesBounds = getHorizontalBoundsOfSlides(slides);
	let reelBounds = reel.getBoundingClientRect();
	let frameBounds = frame.getBoundingClientRect();

	if (shouldCenter) {
		let reelOffsetLeft = (frameBounds.left - slidesBounds.left) - (frameBounds.left - reelBounds.left) + ((frameBounds.width - slidesBounds.width) / 2);

		if ((reelOffsetLeft * -1) > (reelBounds.width - frameBounds.width)) {
			return (frameBounds.width - reelBounds.width);
		}

		if (reelOffsetLeft >= 0) {
			return 0;
		}

		return reelOffsetLeft;
	}

	if (slidesBounds.right > frameBounds.right) {
		return (reelBounds.left - frameBounds.left) - (slidesBounds.right - frameBounds.right);
	}

	if (slidesBounds.left < frameBounds.left) {
		return (frameBounds.left - slidesBounds.left) - (frameBounds.left - reelBounds.left);
	}

	return (reelBounds.left - frameBounds.left);
}

function getHorizontalBoundsOfSlides(slides) {
	let maxLeft;
	let maxRight;

	slides.forEach((slide) => {
		if (maxLeft === undefined || slide.getBoundingClientRect().left < maxLeft) {
			maxLeft = slide.getBoundingClientRect().left;
		}

		if (maxRight === undefined || slide.getBoundingClientRect().right > maxRight) {
			maxRight = slide.getBoundingClientRect().right;
		}
	});

	return {
		left: maxLeft,
		right: maxRight,
		width: maxRight - maxLeft,
	}
}

function isSlideInFrame(slide, frame) {
	let slideBounds = slide.getBoundingClientRect();
	let frameBounds = frame.getBoundingClientRect();

	return slideBounds.left >= frameBounds.left
		&& slideBounds.right <= frameBounds.right;
}

function addEvent(type, element, callback, options = {}, useCapture = true) {
	/**
	 * Browsers that support the `passive` option are those that allow
	 * for the usage of the options parameter in event listeners.
	 *
	 * See: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
	 */
	if (doesSupportPassive()) {
		/**
		 * The following events should never `preventDefault()`, due to heavy
		 * performance impact. The `passive` option enforces this, and
		 * tells the browser not to expect any `preventDefault()``.
		 *
		 * See: https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners
		 */
		if (type === 'wheel' || type === 'mousewheel' || type === 'touchstart' || type === 'touchmove') {
			options.passive = true;
		}

		element.addEventListener(type, callback, options, useCapture);
	} else {
		element.addEventListener(type, callback, useCapture);
	}
}

/**
 * Check if the browser knows about the `passive` option and actively looks for it.
 *
 * See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
 */
function doesSupportPassive() {
	let supportsPassive = false;

	try {
		let options = Object.defineProperty({}, 'passive', {
			get: function() {
				supportsPassive = true;
			}
		});

		window.addEventListener('test', null, options);
		window.removeEventListener('test', null, options);
	} catch (e) {
		// Do nothing
	}

	return supportsPassive;
}
<commit_msg>Cancel before starting again.<commit_after>import Animation from './Animation';

export default class Diascope {
	constructor(frame, reel, options = {}) {
		this.options = Object.assign(getDefaultOptions(), options);

		this.elementFrame = frame;
		this.elementReel = reel;
		this.elementsSlides = Array.from(this.elementReel.children);
		this.animationEasing = options.animationEasing;

		if (this.options.hasOwnProperty('elementNavigateNext')) {
			this.addElementNavigateNext(this.options.elementNavigateNext);
		}

		if (this.options.hasOwnProperty('elementNavigatePrevious')) {
			this.addElementNavigatePrevious(this.options.elementNavigatePrevious);
		}
	}

	next() {
		this.panSlides(this.options.step);
	}

	previous() {
		this.panSlides(this.options.step * -1);
	}

	panSlides(pan) {
		let newSlides = findNewVisibleSlides(pan, this.elementsSlides, this.elementFrame, this.options.loop);

		if (newSlides.length > 0) {
			let reelOffsetLeft = calculateLeftReelOffsetToBringSlidesIntoFrame(
				newSlides,
				this.elementReel,
				this.elementFrame,
				this.options.shouldCenter
			);

			if (this.reelAnimation) {
				this.reelAnimation.cancel();
			}

			this.reelAnimation = new Animation(this.elementReel, reelOffsetLeft, this.options.duration, this.animationEasing);
			this.reelAnimation.start();
		}
	}

	addElementNavigateNext(element) {
		if (element instanceof Element) {
			addEvent('click', element, this.next.bind(this));
		}
	}

	addElementNavigatePrevious(element) {
		if (element instanceof Element) {
			addEvent('click', element, this.previous.bind(this));
		}
	}

	setAnimationEasing(easing) {
		this.animationEasing = easing;
	}
}

function getDefaultOptions() {
	return {
		duration: 0.1,
		step: 1,
		loop: false,
		shouldCenter: false,
		animationEasing: 'linear',
	};
}

function findNewVisibleSlides(pan, slides, frame, shouldLoop) {
	const firstSlideIndex = 0;
	const lastSlideIndex = slides.length - 1;

	let newSlides = [],
		firstVisibleSlideIndex,
		lastVisibleSlideIndex,
		firstNewSlideIndex,
		lastNewSlideIndex;

	firstVisibleSlideIndex = findIndexOfFirstVisibleSlideInFrame(slides, frame);
	lastVisibleSlideIndex = findIndexOfLastVisibleSlideInFrame(slides, frame);

	// loop to the first slide.
	if (pan > 0 && shouldLoop && lastVisibleSlideIndex === lastSlideIndex) {
		return [slides[0]];
	}

	// loop to the last slide.
	if (pan < 0 && shouldLoop && firstVisibleSlideIndex === firstSlideIndex) {
		return [slides[lastSlideIndex]];
	}

	if (lastVisibleSlideIndex + pan > lastSlideIndex) {
		pan = lastSlideIndex - lastVisibleSlideIndex;
		firstNewSlideIndex = lastSlideIndex - pan;
		lastNewSlideIndex = lastSlideIndex;
	} else if (firstVisibleSlideIndex + pan < 0) {
		pan = firstSlideIndex - firstVisibleSlideIndex;
		firstNewSlideIndex = 0
		lastNewSlideIndex = 0 - pan;
	} else {
		firstNewSlideIndex = firstVisibleSlideIndex + pan;
		lastNewSlideIndex = lastVisibleSlideIndex + pan;
	}

	for (let i = firstNewSlideIndex; i <= lastNewSlideIndex; i++) {
		newSlides.push(slides[i]);
	}

	return newSlides;
}

function findIndexOfFirstVisibleSlideInFrame(slides, frame) {
	for (let currentSlideIndex = 0; currentSlideIndex < slides.length; currentSlideIndex++) {
		if (isSlideInFrame(slides[currentSlideIndex], frame)) {
			return currentSlideIndex;
		}
	}
}

function findIndexOfLastVisibleSlideInFrame(slides, frame) {
	let lastVisibleSlideIndex;

	for (let currentSlideIndex = 0; currentSlideIndex < slides.length; currentSlideIndex++) {
		if (isSlideInFrame(slides[currentSlideIndex], frame)) {
			lastVisibleSlideIndex = currentSlideIndex;
		}
	}

	return lastVisibleSlideIndex;
}

function calculateLeftReelOffsetToBringSlidesIntoFrame(slides, reel, frame, shouldCenter = false) {
	let slidesBounds = getHorizontalBoundsOfSlides(slides);
	let reelBounds = reel.getBoundingClientRect();
	let frameBounds = frame.getBoundingClientRect();

	if (shouldCenter) {
		let reelOffsetLeft = (frameBounds.left - slidesBounds.left) - (frameBounds.left - reelBounds.left) + ((frameBounds.width - slidesBounds.width) / 2);

		if ((reelOffsetLeft * -1) > (reelBounds.width - frameBounds.width)) {
			return (frameBounds.width - reelBounds.width);
		}

		if (reelOffsetLeft >= 0) {
			return 0;
		}

		return reelOffsetLeft;
	}

	if (slidesBounds.right > frameBounds.right) {
		return (reelBounds.left - frameBounds.left) - (slidesBounds.right - frameBounds.right);
	}

	if (slidesBounds.left < frameBounds.left) {
		return (frameBounds.left - slidesBounds.left) - (frameBounds.left - reelBounds.left);
	}

	return (reelBounds.left - frameBounds.left);
}

function getHorizontalBoundsOfSlides(slides) {
	let maxLeft;
	let maxRight;

	slides.forEach((slide) => {
		if (maxLeft === undefined || slide.getBoundingClientRect().left < maxLeft) {
			maxLeft = slide.getBoundingClientRect().left;
		}

		if (maxRight === undefined || slide.getBoundingClientRect().right > maxRight) {
			maxRight = slide.getBoundingClientRect().right;
		}
	});

	return {
		left: maxLeft,
		right: maxRight,
		width: maxRight - maxLeft,
	}
}

function isSlideInFrame(slide, frame) {
	let slideBounds = slide.getBoundingClientRect();
	let frameBounds = frame.getBoundingClientRect();

	return slideBounds.left >= frameBounds.left
		&& slideBounds.right <= frameBounds.right;
}

function addEvent(type, element, callback, options = {}, useCapture = true) {
	/**
	 * Browsers that support the `passive` option are those that allow
	 * for the usage of the options parameter in event listeners.
	 *
	 * See: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
	 */
	if (doesSupportPassive()) {
		/**
		 * The following events should never `preventDefault()`, due to heavy
		 * performance impact. The `passive` option enforces this, and
		 * tells the browser not to expect any `preventDefault()``.
		 *
		 * See: https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners
		 */
		if (type === 'wheel' || type === 'mousewheel' || type === 'touchstart' || type === 'touchmove') {
			options.passive = true;
		}

		element.addEventListener(type, callback, options, useCapture);
	} else {
		element.addEventListener(type, callback, useCapture);
	}
}

/**
 * Check if the browser knows about the `passive` option and actively looks for it.
 *
 * See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
 */
function doesSupportPassive() {
	let supportsPassive = false;

	try {
		let options = Object.defineProperty({}, 'passive', {
			get: function() {
				supportsPassive = true;
			}
		});

		window.addEventListener('test', null, options);
		window.removeEventListener('test', null, options);
	} catch (e) {
		// Do nothing
	}

	return supportsPassive;
}
<|endoftext|>


<commit_before>var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var jade = require('jade');
var fs = require('fs');

exports.sendMail = function(to, subject, templateFile, data) {
    var content = getEmailContent(templateFile, data)
    console.log('sending email. content: ' + content.replace(/&/g, "&"))
    if (process.env.SL_SMTP_SENDER_HOST != null && process.env.SL_SMTP_SENDER_FROM != null) {
        sendSimpleEmail(to, subject, content);
    } else {
        sendDummyEmail();
    }
};

sendSimpleEmail = function(to, subject, content) {
    var transport = nodemailer.createTransport(smtpTransport({
        host: process.env.SL_SMTP_SENDER_HOST,
        port: process.env.SL_SMTP_SENDER_PORT,
        auth: {
            user: process.env.SL_SMTP_SENDER_USERNAME,
            pass: process.env.SL_SMTP_SENDER_PASSWORD
        }
    }));
    console.log('sending mail to ' +  to);
    transport.sendMail({
        from: process.env.SL_SMTP_SENDER_FROM,
        to: to,
        subject: subject,
        html: content}, function(error, info){
        if(error){
            console.log(error);
        }else{
            console.log('Message sent: ' + info.response);
        }
    });
};

sendDummyEmail = function() {
    console.log("SMTP not configured! Related configuration entries: " + missingVars());
};

missingVars = function() {
    var vars = []
    if (process.env.SL_SMTP_SENDER_HOST == null) {
        vars.push("SL_SMTP_SENDER_HOST")
    }
    if (process.env.SL_SMTP_SENDER_PORT == null) {
        vars.push("SL_SMTP_SENDER_PORT")
    }
    if (process.env.SL_SMTP_SENDER_USERNAME == null) {
        vars.push("SL_SMTP_SENDER_USERNAME")
    }
    if (process.env.SL_SMTP_SENDER_PASSWORD == null) {
        vars.push("SL_SMTP_SENDER_PASSWORD")
    }
    if (process.env.SL_SMTP_SENDER_FROM == null) {
        vars.push("SL_SMTP_SENDER_FROM")
    }
    return vars.toString();
}

getEmailContent = function(templateFile, data) {
    var content = fs.readFileSync(templateFile,'utf8')
    var fn = jade.compile(content); // TODO: check data
    return fn(data);
}<commit_msg>CLOUD-46704 fixed mail sending<commit_after>var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var jade = require('jade');
var fs = require('fs');

exports.sendMail = function(to, subject, templateFile, data) {
    var content = getEmailContent(templateFile, data)
    console.log('sending email. content: ' + content.replace(/&/g, "&"))
    if (process.env.SL_SMTP_SENDER_HOST != null && process.env.SL_SMTP_SENDER_FROM != null) {
        sendSimpleEmail(to, subject, content);
    } else {
        sendDummyEmail();
    }
};

sendSimpleEmail = function(to, subject, content) {
    var transport = null;
    if (process.env.SL_SMTP_SENDER_USERNAME == null && process.env.SL_SMTP_SENDER_PASSWORD == null) {
      transport = nodemailer.createTransport(smtpTransport({
          host: process.env.SL_SMTP_SENDER_HOST,
          port: process.env.SL_SMTP_SENDER_PORT
      }));
    } else {
      transport = nodemailer.createTransport(smtpTransport({
          host: process.env.SL_SMTP_SENDER_HOST,
          port: process.env.SL_SMTP_SENDER_PORT,
          auth: {
              user: process.env.SL_SMTP_SENDER_USERNAME,
              pass: process.env.SL_SMTP_SENDER_PASSWORD
          }
      }));
    }
    console.log('sending mail to ' +  to);
    transport.sendMail({
        from: process.env.SL_SMTP_SENDER_FROM,
        to: to,
        subject: subject,
        html: content}, function(error, info){
        if(error){
            console.log(error);
        }else{
            console.log('Message sent: ' + info.response);
        }
    });
};

sendDummyEmail = function() {
    console.log("SMTP not configured! Related configuration entries: " + missingVars());
};

missingVars = function() {
    var vars = []
    if (process.env.SL_SMTP_SENDER_HOST == null) {
        vars.push("SL_SMTP_SENDER_HOST")
    }
    if (process.env.SL_SMTP_SENDER_PORT == null) {
        vars.push("SL_SMTP_SENDER_PORT")
    }
    if (process.env.SL_SMTP_SENDER_USERNAME == null) {
        vars.push("SL_SMTP_SENDER_USERNAME")
    }
    if (process.env.SL_SMTP_SENDER_PASSWORD == null) {
        vars.push("SL_SMTP_SENDER_PASSWORD")
    }
    if (process.env.SL_SMTP_SENDER_FROM == null) {
        vars.push("SL_SMTP_SENDER_FROM")
    }
    return vars.toString();
}

getEmailContent = function(templateFile, data) {
    var content = fs.readFileSync(templateFile,'utf8')
    var fn = jade.compile(content); // TODO: check data
    return fn(data);
}
<|endoftext|>


<commit_before>var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var jade = require('jade');
var fs = require('fs');

exports.sendMail = function(to, subject, templateFile, data) {
    var transport = nodemailer.createTransport(smtpTransport({
        host: process.env.SL_SMTP_SENDER_HOST,
        port: process.env.SL_SMTP_SENDER_PORT,
        auth: {
            user: process.env.SL_SMTP_SENDER_USERNAME,
            pass: process.env.SL_SMTP_SENDER_PASSWORD
        }
    }));
    console.log('sending mail to ' +  to);
    var content = getEmailContent(templateFile, data)
    transport.sendMail({
        from: process.env.SL_SMTP_SENDER_FROM,
        to: to,
        subject: subject,
        html: content}, function(error, info){
        if(error){
            console.log(error);
        }else{
            console.log('Message sent: ' + info.response);
        }
    });
};

getEmailContent = function(templateFile, data) {
    var content = fs.readFileSync(templateFile,'utf8')
    var fn = jade.compile(content); // TODO: check data
    return fn(data);
}<commit_msg>CLOUD-640 added dummy email service<commit_after>var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var jade = require('jade');
var fs = require('fs');

exports.sendMail = function(to, subject, templateFile, data) {
    var content = getEmailContent(templateFile, data)
    console.log('sending email. content: ' + content)
    if (process.env.SL_SMTP_SENDER_HOST != null && process.env.SL_SMTP_SENDER_FROM != null) {
        sendSimpleEmail(to, subject, content);
    } else {
        sendDummyEmail();
    }
};

sendSimpleEmail = function(to, subject, content) {
    var transport = nodemailer.createTransport(smtpTransport({
        host: process.env.SL_SMTP_SENDER_HOST,
        port: process.env.SL_SMTP_SENDER_PORT,
        auth: {
            user: process.env.SL_SMTP_SENDER_USERNAME,
            pass: process.env.SL_SMTP_SENDER_PASSWORD
        }
    }));
    console.log('sending mail to ' +  to);
    transport.sendMail({
        from: process.env.SL_SMTP_SENDER_FROM,
        to: to,
        subject: subject,
        html: content}, function(error, info){
        if(error){
            console.log(error);
        }else{
            console.log('Message sent: ' + info.response);
        }
    });
};

sendDummyEmail = function() {
    console.log("SMTP not configured! Related configuration entries: " + missingVars());
};

missingVars = function() {
    var vars = []
    if (process.env.SL_SMTP_SENDER_HOST == null) {
        vars.push("SL_SMTP_SENDER_HOST")
    }
    if (process.env.SL_SMTP_SENDER_PORT == null) {
        vars.push("SL_SMTP_SENDER_PORT")
    }
    if (process.env.SL_SMTP_SENDER_USERNAME == null) {
        vars.push("SL_SMTP_SENDER_USERNAME")
    }
    if (process.env.SL_SMTP_SENDER_PASSWORD == null) {
        vars.push("SL_SMTP_SENDER_PASSWORD")
    }
    if (process.env.SL_SMTP_SENDER_FROM == null) {
        vars.push("SL_SMTP_SENDER_FROM")
    }
    return vars.toString();
}

getEmailContent = function(templateFile, data) {
    var content = fs.readFileSync(templateFile,'utf8')
    var fn = jade.compile(content); // TODO: check data
    return fn(data);
}<|endoftext|>


<commit_before>function output(text) {
	var msgblock = $("<div>").text(text);
	$("#message-board").append(msgblock);
}

var ws;

$(document).ready(function () {
	var parseReason = function (failMsg) {
		var pos = failMsg.indexOf(":");
		if (pos == -1
			|| failMsg.substring(0, failMsg.indexOf(":")) != "failed") {
			return failMsg;
		} else {
			return failMsg.substring(failMsg.indexOf(":") + 1);
		}
	}

	var handlePayload = function (data) {
		var pos = data.indexOf(":");
		if (pos == -1) {
			output("FIXME: Unknown payload: " + data);
			return;
		}

		var protocol = data.substring(0, pos);
		var message = data.substring(pos + 1);

		switch (protocol) {
			case "list":
				if (message == "") {
					output("Nobody is current online");
					return;
				}

				message = message.split(" ").join(", ");
				output("Current online list: " + message);
				return;

			case "nick":
				if (message == "success") {
					output("Set nickname successfully!");
				} else {
					output("Set nick name failed: " + parseReason(message));
				}
				return;

			case "msg":
			case "pmsg":
				var pos = message.indexOf(":");
				if (pos == -1)
					return;
				output((protocol == "pmsg" ? "[Private] " : "")
					+ message.substring(0, pos) + ": "
					+ message.substring(pos + 1));
				return;

			case "to":
				if (message == "success") {
					output("Private message sent successfully!");
				} else {
					output("Private message sent failed: "
						+ parseReason(message));
				}
				return;

			case "login":
			case "logout":
				output(message + " just "
					+ (protocol == "login" ? "logged in" : "logged out"));
				return;

			case "error":
				output("Error: [" + message + "]");
				return;

			default:
				output("FIXME: Unknown protocol: [" + protocol + "] "
					+ "with message [" + message + "]");
				return;

		}
	}

	ws = new WebSocket("ws://tunnel.vince.im:8999");

	ws.onopen = function () {
		output("[Client has connected]");
	}

	ws.onerror = function () {
		output("[Error when writing to socket, hanging up]");
	}

	ws.onmessage = function (event) {
		handlePayload(event.data);
	}

	ws.onclose = function (event) {
		console.log("[Connection closed by the server]");
	}
});

$("[name=submit]").click(function (argument) {
	ws.send($("[name=input]").val());
	$("[name=input]").val("");
});<commit_msg>Add notice<commit_after>function output(text) {
	var msgblock = $("<div>").text(text);
	$("#message-board").append(msgblock);
}

var ws;

$(document).ready(function () {
	var parseReason = function (failMsg) {
		var pos = failMsg.indexOf(":");
		if (pos == -1
			|| failMsg.substring(0, failMsg.indexOf(":")) != "failed") {
			return failMsg;
		} else {
			return failMsg.substring(failMsg.indexOf(":") + 1);
		}
	}

	var handlePayload = function (data) {
		var pos = data.indexOf(":");
		if (pos == -1) {
			output("FIXME: Unknown payload: " + data);
			return;
		}

		var protocol = data.substring(0, pos);
		var message = data.substring(pos + 1);

		switch (protocol) {
			case "list":
				if (message == "") {
					output("Nobody is current online");
					return;
				}

				message = message.split(" ").join(", ");
				output("Current online list: " + message);
				return;

			case "nick":
				if (message == "success") {
					output("Set nickname successfully!");
				} else {
					output("Set nick name failed: " + parseReason(message));
				}
				return;

			case "msg":
			case "pmsg":
				var pos = message.indexOf(":");
				if (pos == -1)
					return;
				output((protocol == "pmsg" ? "[Private] " : "")
					+ message.substring(0, pos) + ": "
					+ message.substring(pos + 1));
				return;

			case "to":
				if (message == "success") {
					output("Private message sent successfully!");
				} else {
					output("Private message sent failed: "
						+ parseReason(message));
				}
				return;

			case "login":
			case "logout":
				output(message + " just "
					+ (protocol == "login" ? "logged in" : "logged out"));
				return;

			case "error":
				output("Error: [" + message + "]");
				return;

			default:
				output("FIXME: Unknown protocol: [" + protocol + "] "
					+ "with message [" + message + "]");
				return;

		}
	}

	ws = new WebSocket("ws://tunnel.vince.im:8999");

	ws.onopen = function () {
		output("[Client has connected]");
		output("Note: Please use \"/nick <Nickname>\" set the nickname first");
	}

	ws.onerror = function () {
		output("[Error when writing to socket, hanging up]");
	}

	ws.onmessage = function (event) {
		handlePayload(event.data);
	}

	ws.onclose = function (event) {
		console.log("[Connection closed by the server]");
	}
});

$("[name=submit]").click(function (argument) {
	ws.send($("[name=input]").val());
	$("[name=input]").val("");
});<|endoftext|>


<commit_before>'use strict';

/**
 * This is an event bus that can be used to connect
 * intermix (and other) components.
 * It has the usual message events that you can
 * subscribe/unsubscribe to like in Backbone, jquery etc.
 * The more interesting part is a relay
 * system that can be used to share control data between
 * relay attendees.
 * An attendee has to add itself to a relay to become
 * a relay endpoint. Now, all other bus attendees can
 * read out its controllers and send/receive data to/from it.
 * The event bus has two buildin relays 'instrument' and 'fx',
 * others can be added.
 * Sounds complex? Here are some examples:
 * @example
Registering to a relay

 * var myNewInstrument = function(eventBus) {
 *
 *   this.ctrlSpec = {
 *     'title': String,
 *     'volume': [0, 127],
 *     'pan': [-63, 64],
 *   };
 *
 *   this.uid = eventBus.addRelayEndpoint('instrument', ctrlSpec, this);
 *   ...
 * @example
Sending data to the relay is easy

 *   ...
 *   eventBus.fireEvent({'volume': 23}, uid);
 *   ...
 * @example
To receive events you have to implement 'handleRelayData'

 *   ...
 *   this.handleRelayData = function(data) {
 *     // do something with the data
 *   };
 * };
 * @constructor
 * @return {Void}
 */
var EventBus = function() {

  this.lookup = {};
  this.relays = {
    'instrument': {},
    'fx': {}
  };

  this.messages = {
    'onRelayAdd': [],
    'onRelayRemove': []
  };

};

EventBus.prototype.addRelayEndpoint = function(relay, data, context) {
  var uid = this.getUID();

  if (typeof this.relays[relay] !== 'undefined' &&
  this.isPlainObject(data) &&
  typeof context !== 'undefined') {
    this.relays[relay][uid] = {
      'context': context,
      'data': data,
      'relay': relay,
      'subscribers': []
    };
    this.lookup[uid] = this.relays[relay][uid];
    this.sendMessage('onRelayAdd', relay, uid);

    return uid;
  } else {
    throw new TypeError('One or more unvalid arguments');
  }

};

EventBus.prototype.removeRelayEndpoint = function(relay, uid) {
  if (this.relays[relay].hasOwnProperty(uid)) {
    delete this.relays[relay][uid];
    delete this.lookup[uid];
  } else {
    throw new Error('uid not found in relay ' + relay);
  }
};

EventBus.prototype.getEndpointSpec = function(uid) {
  return this.lookup[uid].data;
};

EventBus.prototype.getAllRelayEndpointSpecs = function(relay) {
  if (typeof this.relays[relay] !== 'undefined') {
    var specs = [];
    for (var uid in this.relays[relay]) {
      specs.push(this.relays[relay][uid].data);
    }
    return specs;
  } else {
    throw new TypeError('Relay: ' + relay + ' not found');
  }
};

EventBus.prototype.sendToRelay = function(relay, msg) {
  if (this.relays.hasOwnProperty(relay)) {
    for (var uid in this.relays[relay]) {
      this.sendToRelayEndpoint(uid, msg);
    }
  } else {
    throw new TypeError('Argument relay invalid or missing');
  }
};

EventBus.prototype.sendToRelayEndpoint = function(uid, msg) {
  var endpoint = this.lookup[uid].context;
  endpoint.handleRelayData.call(endpoint, msg);
};

EventBus.prototype.getRelayNames = function() {
  var names = [];
  for (var name in this.relays) {
    names.push(name);
  }
  return names;
};

EventBus.prototype.subscribe = function(msg, fn, context) {
  if (typeof msg === 'string' &&
  typeof fn === 'function' &&
  typeof context !== 'undefined') {
    if (typeof this.messages[msg] === 'undefined') {
      this.messages[msg] = [];
    }
    this.messages[msg].push({ 'context': context, 'fn': fn });
    return true;
  } else {
    throw new TypeError('One or more arguments of wrong type or missing');
  }
};

EventBus.prototype.unsubscribe = function(msg, context) {
  if (typeof context !== 'undefined') {
    var message = this.messages[msg];
    message.forEach(function(subscriber, index) {
      if (subscriber.context === context) {
        message.splice(index, 1);
      }
    });
  } else {
    throw new TypeError('context is undefined');
  }
};

EventBus.prototype.sendMessage = function(msg, data) {
  var subscribers = this.messages[msg];
  var length = subscribers.length;
  for (var i = 0; i < length; i++) {
    subscribers[i].fn.call(subscribers[i].context, data);
  }
};

EventBus.prototype.getAllMessageTypes = function() {
  var types = [];
  for (var type in this.messages) {
    types.push(type);
  }
  return types;
};

/**
 * Creates 16 bytes random data represented as a string
 * @return {String} Random, binary-like UID
 */
EventBus.prototype.getUID = function() {
  var uid = '';
  var i = 32;
  var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
                '9', 'a', 'b', 'c', 'd', 'e', 'f'];

  while (i--) {
    var nibble = Math.random() * 16 | 0;
    uid += lookup[nibble];
  }

  return uid;
};

/**
 * Tests if an object is a plain javascript object (object literal)
 * and not a constructor, instance, null or anything else.
 * as suggested by RobG:
 * http://stackoverflow.com/questions/5876332/how-can-i-differentiate-between-an-object-literal-other-javascript-objects
 * @param  {Object} obj Any javascript object
 * @return {Boolean}    True if plain js object, false if not
 */
EventBus.prototype.isPlainObject = function(obj) {
  if (typeof obj === 'object' && obj !== null) {
    var proto = Object.getPrototypeOf(obj);
    return proto === Object.prototype || proto === null;
  }
  return false;
};

module.exports = EventBus;
<commit_msg>API docs (inline) for EventBus.js<commit_after>'use strict';

/**
 * This is an event bus that can be used to connect
 * intermix (and other) components.
 * It has the usual message events that you can
 * subscribe/unsubscribe to like in Backbone, jquery etc.
 * The more interesting part is a relay
 * system that can be used to share control data between
 * relay attendees.
 * An attendee has to add itself to a relay to become
 * a relay endpoint. Now, all other bus attendees can
 * read out its controllers and send/receive data to/from it.
 * The event bus has two buildin relays 'instrument' and 'fx',
 * others can be added.
 * Sounds complex? Here are some examples:
 * @example
Registering to a relay

 * var myNewInstrument = function(eventBus) {
 *
 *   this.ctrlSpec = {
 *     'title': String,
 *     'volume': [0, 127],
 *     'pan': [-63, 64],
 *   };
 *
 *   this.uid = eventBus.addRelayEndpoint('instrument', ctrlSpec, this);
 *   ...
 * @example
Sending data to the relay is easy

 *   ...
 *   eventBus.fireEvent({'volume': 23}, uid);
 *   ...
 * @example
To receive events you have to implement 'handleRelayData'

 *   ...
 *   this.handleRelayData = function(data) {
 *     // do something with the data
 *   };
 * };
 * @constructor
 * @return {Void}
 */
var EventBus = function() {

  this.lookup = {};
  this.relays = {
    'sequencer': {},
    'instrument': {},
    'fx': {}
  };

  this.messages = {
    'onRelayAdd': [],
    'onRelayRemove': []
  };

};

/**
 * Adds an endpoint (sequencer, instrument, etc) to a
 * relay and returns a unique id that serves as a
 * key to identify this endpoint from now on.
 * @param  {String} relay   The relay to register to
 * @param  {Object} data    Custom controller definition, can be an empty object if the endpoint doesn't use custom controllers.
 * @param  {Object} context The endpoint object itself (this)
 * @return {String}         A binary-like string ([0-f]*) that serves as a uid
 */
EventBus.prototype.addRelayEndpoint = function(relay, data, context) {
  var uid = this.getUID();

  if (typeof this.relays[relay] !== 'undefined' &&
  this.isPlainObject(data) &&
  typeof context !== 'undefined') {
    this.relays[relay][uid] = {
      'context': context,
      'data': data,
      'relay': relay,
      'subscribers': []
    };
    this.lookup[uid] = this.relays[relay][uid];
    this.sendMessage('onRelayAdd', relay, uid);

    return uid;
  } else {
    throw new TypeError('One or more unvalid arguments');
  }

};

/**
 * Deletes an endpoint (sequencer, instrument, etc) from a relay.
 * @param  {String} relay The relay to be removed from
 * @param  {String} uid   Unique identifier of the endpoint
 * @return {Void}
 */
EventBus.prototype.removeRelayEndpoint = function(relay, uid) {
  if (this.relays[relay].hasOwnProperty(uid)) {
    delete this.relays[relay][uid];
    delete this.lookup[uid];
  } else {
    throw new Error('uid not found in relay ' + relay);
  }
};

/**
 * Get an object of controller definitions of a specific relay endpoint.
 * @param  {String} uid Unique idenifier of the endpoint
 * @return {Object}     Controller definitions
 */
EventBus.prototype.getEndpointSpec = function(uid) {
  return this.lookup[uid].data;
};

/**
 * Get all endpoint specifications for a specific relay.
 * @param  {String} relay The relay of interest
 * @return {Object}       Dictionary of endpoint ids and controller definitions
 */
EventBus.prototype.getAllRelayEndpointSpecs = function(relay) {
  if (typeof this.relays[relay] !== 'undefined') {
    var specs = {};
    for (var uid in this.relays[relay]) {
      specs[uid] = this.relays[relay][uid].data;
    }
    return specs;
  } else {
    throw new TypeError('Relay: ' + relay + ' not found');
  }
};

/**
 * Sends a message to all endpoints of a relay (e.g. all instruments)
 * @param  {String} relay Relay to send the message to
 * @param  {Object} msg   The message
 * @return {Void}
 */
EventBus.prototype.sendToRelay = function(relay, msg) {
  if (this.relays.hasOwnProperty(relay)) {
    for (var uid in this.relays[relay]) {
      this.sendToRelayEndpoint(uid, msg);
    }
  } else {
    throw new TypeError('Argument relay invalid or missing');
  }
};

/**
 * Sends a message to a specific endpoint of a relay.
 * @param  {String} uid Unique id of the relay endpoint
 * @param  {Object} msg The message
 * @return {Void}
 */
EventBus.prototype.sendToRelayEndpoint = function(uid, msg) {
  var endpoint = this.lookup[uid].context;
  endpoint.handleRelayData.call(endpoint, msg);
};

/**
 * Get a list with the names of all relays
 * @return {Array} List with relay names
 */
EventBus.prototype.getRelayNames = function() {
  var names = [];
  for (var name in this.relays) {
    names.push(name);
  }
  return names;
};

/**
 * Get a list with the names of all currently available message types
 * @return {Array} List with message types
 */
EventBus.prototype.getAllMessageTypes = function() {
  var types = [];
  for (var type in this.messages) {
    types.push(type);
  }
  return types;
};

/**
 * Subscribe to a message type
 * @param  {String}   msg     The message type to subscribe to
 * @param  {Function} fn      Callback function
 * @param  {Object}   context The subscriber context (this)
 * @return {Void}
 */
EventBus.prototype.subscribe = function(msg, fn, context) {
  if (typeof msg === 'string' &&
  typeof fn === 'function' &&
  typeof context !== 'undefined') {
    if (typeof this.messages[msg] === 'undefined') {
      this.messages[msg] = [];
    }
    this.messages[msg].push({ 'context': context, 'fn': fn });
  } else {
    throw new TypeError('One or more arguments of wrong type or missing');
  }
};

/**
 * Unsubscribe to a message type
 * @param  {String} msg     The message type to unsubscribe to
 * @param  {Object} context The (un)subscriber context (this)
 * @return {Void}
 */
EventBus.prototype.unsubscribe = function(msg, context) {
  if (typeof context !== 'undefined') {
    var message = this.messages[msg];
    message.forEach(function(subscriber, index) {
      if (subscriber.context === context) {
        message.splice(index, 1);
      }
    });
  } else {
    throw new TypeError('context is undefined');
  }
};

/**
 * Send a message to the bus
 * @param  {String} msg  The message type
 * @param  {Object|Array|String|Number|Boolean|ArrayBuffer} data A message of any format
 * @return {Void}
 */
EventBus.prototype.sendMessage = function(msg, data) {
  var subscribers = this.messages[msg];
  var length = subscribers.length;
  for (var i = 0; i < length; i++) {
    subscribers[i].fn.call(subscribers[i].context, data);
  }
};

/**
 * Creates 16 bytes random data represented as a string
 * @return {String} Random, binary-like UID
 */
EventBus.prototype.getUID = function() {
  var uid = '';
  var i = 32;
  var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
                '9', 'a', 'b', 'c', 'd', 'e', 'f'];

  while (i--) {
    var nibble = Math.random() * 16 | 0;
    uid += lookup[nibble];
  }

  return uid;
};

/**
 * Tests if an object is a plain javascript object (object literal)
 * and not a constructor, instance, null or anything else.
 * as suggested by RobG:
 * http://stackoverflow.com/questions/5876332/how-can-i-differentiate-between-an-object-literal-other-javascript-objects
 * @param  {Object} obj Any javascript object
 * @return {Boolean}    True if plain js object, false if not
 */
EventBus.prototype.isPlainObject = function(obj) {
  if (typeof obj === 'object' && obj !== null) {
    var proto = Object.getPrototypeOf(obj);
    return proto === Object.prototype || proto === null;
  }
  return false;
};

module.exports = EventBus;
<|endoftext|>


<commit_before>/* eslint no-param-reassign: 0 */

import path from 'path'
import crypto from 'crypto'
import Promise from 'bluebird'
import cheerio from 'cheerio'
import inlineCss from 'inline-css'
import hljs from 'highlight.js'
import MarkdownIt from 'markdown-it'
import mdEmoji from 'markdown-it-emoji'
import mdEnTodo from 'markdown-it-enml-todo'
import mdSub from 'markdown-it-sub'
import mdSup from 'markdown-it-sup'
import mermaidCli from 'mermaid/lib/cli'
import mermaidLib from 'mermaid/lib'
import { Evernote } from 'evernote'
import EvernoteClient, {
  OBJECT_NOT_FOUND,
  DEFAULT_RESOURCE_TYPE,
  RESOURCE_TYPES,
} from './EvernoteClient'
import Db from './db'
import fileUtils from './fileUtils'
import config, { APP_NAME } from './config'
import EvermarkError from './EvermarkError'

const debug = require('debug')('evermark')

const NOTE_PATH = 'notes'
const NOTE_DIAGRAM_PATH = `${NOTE_PATH}/diagrams`
const MARKDOWN_THEME_PATH = path.join(__dirname, '../themes')
const HIGHLIGHT_THEME_PATH = path.join(__dirname, '../node_modules/highlight.js/styles')

const DEFAULT_HIGHLIGHT_THEME = 'github'

export default class Evermark {
  constructor(workDir = `.${path.sep}`, options = {}) {
    this.workDir = workDir
    debug('workDir:', this.workDir)

    const md = new MarkdownIt({
      html: true, // Enable HTML tags in source
      linkify: true, // Autoconvert URL-like text to links

      // Highlighter function. Should return escaped HTML,
      // or '' if the source string is not changed
      highlight(code, lang) {
        if (code.match(/^graph/) || code.match(/^sequenceDiagram/) || code.match(/^gantt/)) {
          return `<div class="mermaid">${code}</div>`
        }

        if (lang && hljs.getLanguage(lang)) {
          try {
            return `<pre class="hljs"><code>${hljs.highlight(lang, code, true).value}</code></pre>`
          } catch (e) {
            // Ignore error
          }
        }

        return `<pre class="hljs"><code>${md.utils.escapeHtml(code)}</code></pre>`
      },
      ...options,
    })

    // Use some plugins
    md.use(mdEmoji)
      .use(mdEnTodo)
      .use(mdSub)
      .use(mdSup)

    // Add inline code class
    const inlineCodeRule = md.renderer.rules.code_inline
    md.renderer.rules.code_inline = (...args) => {
      const result = inlineCodeRule.call(md, ...args)
      return result.replace('', '')
    }

    this.md = md
  }

  async createLocalNote(title) {
    const configDir = await this.getConfigDir()

    // Remove some chars from title
    const filename = title.replace(/(\/|-)+/g, '-').replace(/^-|-$/g, '')

    // Get unique note path and create note file
    const notePath = await fileUtils.uniquePath(path.join(configDir, `${NOTE_PATH}/${filename}.md`))
    await fileUtils.writeFile(notePath, `# ${title}\n`)

    return notePath
  }

  async publishNote(notePath) {
    let content

    try {
      content = await fileUtils.readFile(notePath)
    } catch (err) {
      if (err.code === 'ENOENT') {
        throw new EvermarkError(`${notePath} does not exist`)
      }

      throw err
    }

    return this.saveNote(notePath, content)
  }

  async unpublishNote(notePath) {
    const db = await this.getDb()
    const Note = await db.model('notes', {
      guid: { type: String, required: true },
      path: { type: String, required: true },
      created: { type: Date, default: Date.now },
    })

    const { absolutePath, relativePath } = await this.getNotePathInfo(notePath)
    const note = Note.findOne({ path: relativePath })

    if (!note) {
      throw new EvermarkError(`${notePath} is not a published note`)
    }

    await this.expungeNote(note.guid)
    await Note.remove({ path: relativePath })
    await db.save()
    return absolutePath
  }

  async saveNote(notePath, content) {
    const note = new Evernote.Note()

    const noteAttrs = new Evernote.NoteAttributes()
    noteAttrs.source = APP_NAME
    noteAttrs.sourceApplication = APP_NAME
    noteAttrs.contentClass = APP_NAME // Make the note read-only
    note.attributes = noteAttrs

    const { absolutePath, relativePath } = await this.getNotePathInfo(notePath)
    note.absolutePath = absolutePath
    note.relativePath = relativePath

    const tokens = this.md.parse(content, {})

    const noteInfo = Evermark.parseNoteInfo(tokens)
    note.title = noteInfo.noteTitle

    if (noteInfo.tagNames && noteInfo.tagNames.length) {
      note.tagNames = noteInfo.tagNames
    }

    if (noteInfo.notebookName) {
      const createdNotebook = await this.createNotebookIfPossible(noteInfo.notebookName)
      note.notebookGuid = createdNotebook.guid
    }

    // The content of an Evernote note is represented using Evernote Markup Language
    // (ENML). The full ENML specification can be found in the Evernote API Overview
    // at http://dev.evernote.com/documentation/cloud/chapters/ENML.php
    const htmlContent = await this.generateHtml(note, tokens)
    note.content = '<?xml version="1.0" encoding="UTF-8"?>' +
      '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">' +
      `<en-note>${htmlContent}</en-note>`

    return this.doSaveNote(note)
  }

  async doSaveNote(note) {
    const db = await this.getDb()
    const Note = await db.model('notes', {
      guid: { type: String, required: true },
      path: { type: String, required: true },
      created: { type: Date, default: Date.now },
    })

    let isLocalUpdate = false
    const dbNote = Note.findOne({ path: note.relativePath })
    if (dbNote) {
      try {
        note.guid = dbNote.guid
        const updatedNote = await this.updateNote(note)
        updatedNote.absolutePath = note.absolutePath
        return updatedNote
      } catch (e) {
        if (e.code === OBJECT_NOT_FOUND) {
          delete note.guid
          isLocalUpdate = true
        }
      }
    }

    const createdNote = await this.createNote(note)
    createdNote.absolutePath = note.absolutePath

    if (isLocalUpdate) {
      await Note.update({ path: note.relativePath },
        { guid: createdNote.guid, path: note.relativePath })
    } else {
      await Note.insert({ guid: createdNote.guid, path: note.relativePath })
    }
    await db.save()

    return createdNote
  }

  async createNotebookIfPossible(name) {
    const notebooks = await this.listNotebooks()
    let notebook = notebooks.find(nb => nb.name === name)
    if (!notebook) {
      notebook = await this.createNotebook(name)
    }
    return notebook
  }

  listNotebooks() {
    return this.getEvernoteClient()
      .then(client => client.listNotebooks())
  }

  createNotebook(name) {
    return this.getEvernoteClient()
      .then(client => client.createNotebook(name))
  }

  createNote(note) {
    return this.getEvernoteClient()
      .then(client => client.createNote(note))
  }

  updateNote(note) {
    return this.getEvernoteClient()
      .then(client => client.updateNote(note))
  }

  expungeNote(guid) {
    return this.getEvernoteClient()
      .then(client => client.expungeNote(guid))
  }

  getEvernoteClient() {
    if (!this.evernoteClient) {
      this.evernoteClient = this.getConfig()
        .then(options => new EvernoteClient(options))
    }
    return this.evernoteClient
  }

  getConfig() {
    if (!this.config) {
      this.config = config.readConfig(this.workDir)
        .then(conf => conf)
    }
    return this.config
  }

  async getConfigDir() {
    const configPath = await config.getConfigPath(this.workDir)
    return path.dirname(configPath)
  }

  getDb() {
    if (!this.db) {
      this.db = config.getDbPath(this.workDir)
        .then(dbPath => new Db(dbPath))
    }
    return this.db
  }

  async getNotePathInfo(notePath) {
    const configDir = await this.getConfigDir()

    const absolutePath = path.isAbsolute(notePath) ?
      notePath : path.resolve(notePath)
    const relativePath = path.relative(configDir, absolutePath)
      .replace(/\\/g, '/') // Fix windows issue

    debug('absolute notePath: %s', absolutePath)
    debug('relative notePath: %s', relativePath)

    return { absolutePath, relativePath }
  }

  static parseNoteInfo(tokens = []) {
    const preTitleIndex = tokens.findIndex(token => token.type === 'heading_open')
    const titleToken = preTitleIndex >= 0 ? tokens[preTitleIndex + 1] : null
    const noteTitle = titleToken ? titleToken.content : 'untitled'

    let notebookName = null
    let tagNames = null
    const notebookToken = tokens.find(token => /^ *@\(.+\)(\[.+\])?$/.test(token.content))
    debug('notebookToken: %o', notebookToken)
    if (notebookToken) {
      const matched = notebookToken.content.trim().match(/^ *@\((.+)\)(\[(.+)\])?$/)
      notebookName = matched[1]
      debug('notebookName: %s', notebookName)

      tagNames = matched[3]
      if (tagNames) {
        tagNames = tagNames
          .split('|')
          .map(s => s.trim())
          .filter(s => !!s)
      }
      debug('tagNames: %o', tagNames)
    }

    return { noteTitle, notebookName, tagNames }
  }

  async generateHtml(note, tokens) {
    let markedHtml = this.md.renderer.render(tokens, this.md.options)
    debug('markedHtml: %s', markedHtml)

    // Generate mermaid diagrams
    markedHtml = await this.genMermaidImages(markedHtml)

    // Get highlight theme from configuration
    const conf = await this.getConfig()
    const highlightTheme = conf.highlight || DEFAULT_HIGHLIGHT_THEME

    // Html with styles
    const styles = await Promise.all([
      fileUtils.readFile(path.join(MARKDOWN_THEME_PATH, 'github.css')),
      fileUtils.readFile(path.join(HIGHLIGHT_THEME_PATH, `${highlightTheme}.css`)),
    ])
    const styleHtml = `<style>${styles[0]}${styles[1]}</style>` +
      `<div class="markdown-body">${markedHtml}</div>`
    debug('styleHtml: %s', styleHtml)

    // Change html classes to inline styles
    const inlineStyleHtml = await inlineCss(styleHtml, {
      url: '/',
      removeStyleTags: true,
      removeHtmlSelectors: true,
    })

    const $ = cheerio.load(inlineStyleHtml)
    $('en-todo').removeAttr('style')
    await this.attchResources(note, $)

    // ENML is a superset of XHTML, so change html to xhtml
    const inlineStyleXhtml = $.xml()
    debug('inlineStyleXhtml: %s', inlineStyleXhtml)
    return inlineStyleXhtml
  }

  async genMermaidImages(markedHtml) {
    const $ = cheerio.load(markedHtml)

    const mermaidCodes = []
    $('.mermaid').each((index, element) => {
      mermaidCodes.push($(element).text())
    })

    if (!mermaidCodes.length) {
      return $.html()
    }

    const mmdImgs = await Promise.map(mermaidCodes, async (code) => {
      const mmdFile = path.join(this.workDir, NOTE_DIAGRAM_PATH, `${Evermark.genHash(code)}.mmd`)
      await fileUtils.writeFile(mmdFile, code)
      await new Promise((resolve, reject) => {
        mermaidCli.parse(['-p', '-o', path.join(this.workDir, NOTE_DIAGRAM_PATH), mmdFile],
          (err, message, options) => {
            if (err) {
              reject(err)
            } else if (message) {
              reject(message)
            } else {
              mermaidLib.process(options.files, options, () => resolve())
            }
          }
        )
      })
      return `${mmdFile.slice(path.join(this.workDir, NOTE_PATH).length + 1)}.png`
    })
    debug('mermaid images:', mmdImgs)

    // Replace mermaid code with mermaid diagram
    $('.mermaid').each((index, element) => {
      $(element).replaceWith(`<img src="${mmdImgs[index]}" alt="mermaid diagram">`)
    })

    const result = $.html()
    debug('mermaided html:', result)
    return result
  }

  async attchResources(note, $) {
    const configDir = await this.getConfigDir()

    const imgs = $('img').toArray().filter(img => !/^.+:\/\//.test(img.attribs.src))
    note.resources = await Promise.map(imgs, async (img) => {
      const src = decodeURI(img.attribs.src)
      delete img.attribs.src
      img.name = 'en-media'

      const extname = path.extname(src)
      const imgType = RESOURCE_TYPES[extname] || DEFAULT_RESOURCE_TYPE
      img.attribs.type = imgType // eslint-disable-line

      const image = await fileUtils.readFile(path.join(configDir, `${NOTE_PATH}/${src}`), null)
      img.attribs.hash = Evermark.genHash(image)

      const resource = new Evernote.Resource()
      resource.mime = 'image/jpg'
      resource.data = new Evernote.Data()
      resource.data.body = image
      resource.data.bodyHash = image.toString('base64')
      resource.data.size = image.length
      return resource
    })
  }

  static genHash(data) {
    const md5 = crypto.createHash('md5')
    md5.update(data)
    return md5.digest('hex')
  }
}
<commit_msg>fix: fix duplicate notes when updateNote api error<commit_after>/* eslint no-param-reassign: 0 */

import path from 'path'
import crypto from 'crypto'
import Promise from 'bluebird'
import cheerio from 'cheerio'
import inlineCss from 'inline-css'
import hljs from 'highlight.js'
import MarkdownIt from 'markdown-it'
import mdEmoji from 'markdown-it-emoji'
import mdEnTodo from 'markdown-it-enml-todo'
import mdSub from 'markdown-it-sub'
import mdSup from 'markdown-it-sup'
import mermaidCli from 'mermaid/lib/cli'
import mermaidLib from 'mermaid/lib'
import { Evernote } from 'evernote'
import EvernoteClient, {
  OBJECT_NOT_FOUND,
  DEFAULT_RESOURCE_TYPE,
  RESOURCE_TYPES,
} from './EvernoteClient'
import Db from './db'
import fileUtils from './fileUtils'
import config, { APP_NAME } from './config'
import EvermarkError from './EvermarkError'

const debug = require('debug')('evermark')

const NOTE_PATH = 'notes'
const NOTE_DIAGRAM_PATH = `${NOTE_PATH}/diagrams`
const MARKDOWN_THEME_PATH = path.join(__dirname, '../themes')
const HIGHLIGHT_THEME_PATH = path.join(__dirname, '../node_modules/highlight.js/styles')

const DEFAULT_HIGHLIGHT_THEME = 'github'

export default class Evermark {
  constructor(workDir = `.${path.sep}`, options = {}) {
    this.workDir = workDir
    debug('workDir:', this.workDir)

    const md = new MarkdownIt({
      html: true, // Enable HTML tags in source
      linkify: true, // Autoconvert URL-like text to links

      // Highlighter function. Should return escaped HTML,
      // or '' if the source string is not changed
      highlight(code, lang) {
        if (code.match(/^graph/) || code.match(/^sequenceDiagram/) || code.match(/^gantt/)) {
          return `<div class="mermaid">${code}</div>`
        }

        if (lang && hljs.getLanguage(lang)) {
          try {
            return `<pre class="hljs"><code>${hljs.highlight(lang, code, true).value}</code></pre>`
          } catch (err) {
            // Ignore error
          }
        }

        return `<pre class="hljs"><code>${md.utils.escapeHtml(code)}</code></pre>`
      },
      ...options,
    })

    // Use some plugins
    md.use(mdEmoji)
      .use(mdEnTodo)
      .use(mdSub)
      .use(mdSup)

    // Add inline code class
    const inlineCodeRule = md.renderer.rules.code_inline
    md.renderer.rules.code_inline = (...args) => {
      const result = inlineCodeRule.call(md, ...args)
      return result.replace('', '')
    }

    this.md = md
  }

  async createLocalNote(title) {
    const configDir = await this.getConfigDir()

    // Remove some chars from title
    const filename = title.replace(/(\/|-)+/g, '-').replace(/^-|-$/g, '')

    // Get unique note path and create note file
    const notePath = await fileUtils.uniquePath(path.join(configDir, `${NOTE_PATH}/${filename}.md`))
    await fileUtils.writeFile(notePath, `# ${title}\n`)

    return notePath
  }

  async publishNote(notePath) {
    let content

    try {
      content = await fileUtils.readFile(notePath)
    } catch (err) {
      if (err.code === 'ENOENT') {
        throw new EvermarkError(`${notePath} does not exist`)
      }

      throw err
    }

    return this.saveNote(notePath, content)
  }

  async unpublishNote(notePath) {
    const db = await this.getDb()
    const Note = await db.model('notes', {
      guid: { type: String, required: true },
      path: { type: String, required: true },
      created: { type: Date, default: Date.now },
    })

    const { absolutePath, relativePath } = await this.getNotePathInfo(notePath)
    const note = Note.findOne({ path: relativePath })

    if (!note) {
      throw new EvermarkError(`${notePath} is not a published note`)
    }

    await this.expungeNote(note.guid)
    await Note.remove({ path: relativePath })
    await db.save()
    return absolutePath
  }

  async saveNote(notePath, content) {
    const note = new Evernote.Note()

    const noteAttrs = new Evernote.NoteAttributes()
    noteAttrs.source = APP_NAME
    noteAttrs.sourceApplication = APP_NAME
    noteAttrs.contentClass = APP_NAME // Make the note read-only
    note.attributes = noteAttrs

    const { absolutePath, relativePath } = await this.getNotePathInfo(notePath)
    note.absolutePath = absolutePath
    note.relativePath = relativePath

    const tokens = this.md.parse(content, {})

    const noteInfo = Evermark.parseNoteInfo(tokens)
    note.title = noteInfo.noteTitle

    if (noteInfo.tagNames && noteInfo.tagNames.length) {
      note.tagNames = noteInfo.tagNames
    }

    if (noteInfo.notebookName) {
      const createdNotebook = await this.createNotebookIfPossible(noteInfo.notebookName)
      note.notebookGuid = createdNotebook.guid
    }

    // The content of an Evernote note is represented using Evernote Markup Language
    // (ENML). The full ENML specification can be found in the Evernote API Overview
    // at http://dev.evernote.com/documentation/cloud/chapters/ENML.php
    const htmlContent = await this.generateHtml(note, tokens)
    note.content = '<?xml version="1.0" encoding="UTF-8"?>' +
      '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">' +
      `<en-note>${htmlContent}</en-note>`

    return this.doSaveNote(note)
  }

  async doSaveNote(note) {
    const db = await this.getDb()
    const Note = await db.model('notes', {
      guid: { type: String, required: true },
      path: { type: String, required: true },
      created: { type: Date, default: Date.now },
    })

    let isLocalUpdate = false
    const dbNote = Note.findOne({ path: note.relativePath })
    if (dbNote) {
      try {
        note.guid = dbNote.guid
        const updatedNote = await this.updateNote(note)
        updatedNote.absolutePath = note.absolutePath
        return updatedNote
      } catch (err) {
        if (err.code !== OBJECT_NOT_FOUND) {
          throw err
        }

        delete note.guid
        isLocalUpdate = true
      }
    }

    const createdNote = await this.createNote(note)
    createdNote.absolutePath = note.absolutePath

    if (isLocalUpdate) {
      await Note.update({ path: note.relativePath },
        { guid: createdNote.guid, path: note.relativePath })
    } else {
      await Note.insert({ guid: createdNote.guid, path: note.relativePath })
    }
    await db.save()

    return createdNote
  }

  async createNotebookIfPossible(name) {
    const notebooks = await this.listNotebooks()
    let notebook = notebooks.find(nb => nb.name === name)
    if (!notebook) {
      notebook = await this.createNotebook(name)
    }
    return notebook
  }

  listNotebooks() {
    return this.getEvernoteClient()
      .then(client => client.listNotebooks())
  }

  createNotebook(name) {
    return this.getEvernoteClient()
      .then(client => client.createNotebook(name))
  }

  createNote(note) {
    return this.getEvernoteClient()
      .then(client => client.createNote(note))
  }

  updateNote(note) {
    return this.getEvernoteClient()
      .then(client => client.updateNote(note))
  }

  expungeNote(guid) {
    return this.getEvernoteClient()
      .then(client => client.expungeNote(guid))
  }

  getEvernoteClient() {
    if (!this.evernoteClient) {
      this.evernoteClient = this.getConfig()
        .then(options => new EvernoteClient(options))
    }
    return this.evernoteClient
  }

  getConfig() {
    if (!this.config) {
      this.config = config.readConfig(this.workDir)
        .then(conf => conf)
    }
    return this.config
  }

  async getConfigDir() {
    const configPath = await config.getConfigPath(this.workDir)
    return path.dirname(configPath)
  }

  getDb() {
    if (!this.db) {
      this.db = config.getDbPath(this.workDir)
        .then(dbPath => new Db(dbPath))
    }
    return this.db
  }

  async getNotePathInfo(notePath) {
    const configDir = await this.getConfigDir()

    const absolutePath = path.isAbsolute(notePath) ?
      notePath : path.resolve(notePath)
    const relativePath = path.relative(configDir, absolutePath)
      .replace(/\\/g, '/') // Fix windows issue

    debug('absolute notePath: %s', absolutePath)
    debug('relative notePath: %s', relativePath)

    return { absolutePath, relativePath }
  }

  static parseNoteInfo(tokens = []) {
    const preTitleIndex = tokens.findIndex(token => token.type === 'heading_open')
    const titleToken = preTitleIndex >= 0 ? tokens[preTitleIndex + 1] : null
    const noteTitle = titleToken ? titleToken.content : 'untitled'

    let notebookName = null
    let tagNames = null
    const notebookToken = tokens.find(token => /^ *@\(.+\)(\[.+\])?$/.test(token.content))
    debug('notebookToken: %o', notebookToken)
    if (notebookToken) {
      const matched = notebookToken.content.trim().match(/^ *@\((.+)\)(\[(.+)\])?$/)
      notebookName = matched[1]
      debug('notebookName: %s', notebookName)

      tagNames = matched[3]
      if (tagNames) {
        tagNames = tagNames
          .split('|')
          .map(s => s.trim())
          .filter(s => !!s)
      }
      debug('tagNames: %o', tagNames)
    }

    return { noteTitle, notebookName, tagNames }
  }

  async generateHtml(note, tokens) {
    let markedHtml = this.md.renderer.render(tokens, this.md.options)
    debug('markedHtml: %s', markedHtml)

    // Generate mermaid diagrams
    markedHtml = await this.genMermaidImages(markedHtml)

    // Get highlight theme from configuration
    const conf = await this.getConfig()
    const highlightTheme = conf.highlight || DEFAULT_HIGHLIGHT_THEME

    // Html with styles
    const styles = await Promise.all([
      fileUtils.readFile(path.join(MARKDOWN_THEME_PATH, 'github.css')),
      fileUtils.readFile(path.join(HIGHLIGHT_THEME_PATH, `${highlightTheme}.css`)),
    ])
    const styleHtml = `<style>${styles[0]}${styles[1]}</style>` +
      `<div class="markdown-body">${markedHtml}</div>`
    debug('styleHtml: %s', styleHtml)

    // Change html classes to inline styles
    const inlineStyleHtml = await inlineCss(styleHtml, {
      url: '/',
      removeStyleTags: true,
      removeHtmlSelectors: true,
    })

    const $ = cheerio.load(inlineStyleHtml)
    $('en-todo').removeAttr('style')
    await this.attchResources(note, $)

    // ENML is a superset of XHTML, so change html to xhtml
    const inlineStyleXhtml = $.xml()
    debug('inlineStyleXhtml: %s', inlineStyleXhtml)
    return inlineStyleXhtml
  }

  async genMermaidImages(markedHtml) {
    const $ = cheerio.load(markedHtml)

    const mermaidCodes = []
    $('.mermaid').each((index, element) => {
      mermaidCodes.push($(element).text())
    })

    if (!mermaidCodes.length) {
      return $.html()
    }

    const mmdImgs = await Promise.map(mermaidCodes, async (code) => {
      const mmdFile = path.join(this.workDir, NOTE_DIAGRAM_PATH, `${Evermark.genHash(code)}.mmd`)
      await fileUtils.writeFile(mmdFile, code)
      await new Promise((resolve, reject) => {
        mermaidCli.parse(['-p', '-o', path.join(this.workDir, NOTE_DIAGRAM_PATH), mmdFile],
          (err, message, options) => {
            if (err) {
              reject(err)
            } else if (message) {
              reject(message)
            } else {
              mermaidLib.process(options.files, options, () => resolve())
            }
          }
        )
      })
      return `${mmdFile.slice(path.join(this.workDir, NOTE_PATH).length + 1)}.png`
    })
    debug('mermaid images:', mmdImgs)

    // Replace mermaid code with mermaid diagram
    $('.mermaid').each((index, element) => {
      $(element).replaceWith(`<img src="${mmdImgs[index]}" alt="mermaid diagram">`)
    })

    const result = $.html()
    debug('mermaided html:', result)
    return result
  }

  async attchResources(note, $) {
    const configDir = await this.getConfigDir()

    const imgs = $('img').toArray().filter(img => !/^.+:\/\//.test(img.attribs.src))
    note.resources = await Promise.map(imgs, async (img) => {
      const src = decodeURI(img.attribs.src)
      delete img.attribs.src
      img.name = 'en-media'

      const extname = path.extname(src)
      const imgType = RESOURCE_TYPES[extname] || DEFAULT_RESOURCE_TYPE
      img.attribs.type = imgType // eslint-disable-line

      const image = await fileUtils.readFile(path.join(configDir, `${NOTE_PATH}/${src}`), null)
      img.attribs.hash = Evermark.genHash(image)

      const resource = new Evernote.Resource()
      resource.mime = 'image/jpg'
      resource.data = new Evernote.Data()
      resource.data.body = image
      resource.data.bodyHash = image.toString('base64')
      resource.data.size = image.length
      return resource
    })
  }

  static genHash(data) {
    const md5 = crypto.createHash('md5')
    md5.update(data)
    return md5.digest('hex')
  }
}
<|endoftext|>


<commit_before>// Add an entry for each Layer: layer name as found in the mapfile and the text to be displayed
astun.layersHoverInfo = {
    'wards' : 'Ward boundaries maintained by the Office of National Statistics',
    'rightsofway' : 'Rights of way routes are approximately plotted, hence indicative only.'
};

(function() {

    if(jQuery('#atMyMaps').length != 0) {

        var timer = setInterval(function() {
            var panels = jQuery('#panels');
            if (panels.length) {
                clearInterval(timer);
                layerinfo();
            }
        }, 100);

    }

    function layerinfo() {

        jQuery('body').append(
            jQuery('
')
                .hide()
                .addClass('atLayerInfoTooltip ui-state-default ui-corner-all')
                .append('

')
        );

        jQuery.each(astun.layersHoverInfo, function(key, value) {
            jQuery('li[aria-describedby="'+key+'"]')
                .find('a span')
                .live('mouseover', function(){
                    var t = jQuery(this).offset().top + jQuery(this).parents('li').height() + 10;
                    var l = jQuery(this).parents('li').offset().left;
                    var w = jQuery(this).parents('li').width();
                    jQuery('.atLayerInfoTooltip')
                        .css({'top':t, 'left':l, 'width':w})
                        .show()
                        .find('div span:last').html(value);
                })
                .live('mouseout', function(){
                    jQuery('.atLayerInfoTooltip').hide();
                })
                .append('');
        });

    }

})();
<commit_msg>Changed timer loop to wait for dependencies to load<commit_after>// Add an entry for each Layer: layer name as found in the mapfile and the text to be displayed
astun.layersHoverInfo = {
    'wards' : 'Ward boundaries maintained by the Office of National Statistics',
    'rightsofway' : 'Rights of way routes are approximately plotted, hence indicative only.'
};

(function() {

    var timer = setInterval(function() {
        if (typeof jQuery != 'undefined') {
            if(jQuery('#atMyMaps').length) {
                var panels = jQuery('#panels');
                if (panels.length) {
                    clearInterval(timer);
                    layerinfo();
                }
            } else {
                clearInterval(timer);
            }
    }, 100);

    function layerinfo() {

        jQuery('body').append(
            jQuery('
')
                .hide()
                .addClass('atLayerInfoTooltip ui-state-default ui-corner-all')
                .append('

')
        );

        jQuery.each(astun.layersHoverInfo, function(key, value) {
            jQuery('li[aria-describedby="'+key+'"]')
                .find('a span')
                .live('mouseover', function(){
                    var t = jQuery(this).offset().top + jQuery(this).parents('li').height() + 10;
                    var l = jQuery(this).parents('li').offset().left;
                    var w = jQuery(this).parents('li').width();
                    jQuery('.atLayerInfoTooltip')
                        .css({'top':t, 'left':l, 'width':w})
                        .show()
                        .find('div span:last').html(value);
                })
                .live('mouseout', function(){
                    jQuery('.atLayerInfoTooltip').hide();
                })
                .append('');
        });

    }

})();
<|endoftext|>