<commit_before>var e = module.exports = {};
var bu;

var bot;
e.init = (Tbot, blargutil) => {
    bot = Tbot;
    bu = blargutil;

    e.category = bu.CommandType.ADMIN;

};

e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'settings [help|set <key>]';
e.info = 'Gets or sets the settings for the current guild.';
e.longinfo = '
Gets or sets the settigns for the current guild.
';

e.execute = (msg, words) => {
    if (words.length == 1) {
        //do settings shit
        bu.db.query(`select name, value from guildsetting where guildid=?`, [msg.channel.guild.id], (err, rows) => {
            bu.db.query(`select channelid, nsfw, blacklisted from channel where guildid=?`, [msg.channel.guild.id], (err, rows2) => {
                var nsfw = [];
                var blacklisted = [];
                var i;
                for (i = 0; i < rows2.length; i++) {
                    if (rows2[i].nsfw) {
                        nsfw.push(rows2[i].channelid);
                    }
                    if (rows2[i].blacklisted) {
                        blacklisted.push(rows2[i].channelid);
                    }
                }
                var settings = {};
                for (i = 0; i < rows.length; i++) {
                    settings[rows[i].name] = rows[i].value;
                }

                var prefix = settings.prefix
                    ? settings.prefix : 'Not Set';
                var nsfwMessage = 'None Set';
                if (nsfw.length > 0) {
                    nsfwMessage = '';
                    for (i = 0; i < nsfw.length; i++) {
                        let channel = bot.getChannel(nsfw[i]);
                        if (channel)
                            nsfwMessage += `${channel.name} (${nsfw[i]})\n                - `;
                    }
                    nsfwMessage = nsfwMessage.substring(0, nsfwMessage.length - 19);
                }
                var blacklistMessage = 'None Set';
                if (blacklisted.length > 0) {
                    blacklistMessage = '';
                    for (i = 0; i < blacklisted.length; i++) {
                        let channel = bot.getChannel(blacklisted[i]);
                        if (channel)
                            blacklistMessage += `${channel.name} (${blacklisted[i]}\n                - `;
                    }
                    blacklistMessage = blacklistMessage.substring(0, blacklistMessage.length - 19);
                }
                var greeting = settings.greeting
                    ? settings.greeting : 'Not Set';
                var farewell = settings.farewell
                    ? settings.farewell : 'Not Set';
                var modlogChannel;
                if (settings.modlog) {
                    let channel = bot.getChannel(settings.modlog);
                    if (channel)
                        modlogChannel = `${channel.name} (${settings.modlog})`;
                    else
                    modlogChannel = `Channel Not Found (${settings.modlog})` ;
                } else {
                    modlogChannel = 'Not Set';
                }
                var deleteNotif = settings.deletenotif != 0 ? true : false;
                var cahNsfw = settings.cahnsfw && settings.cahnsfw != 0 ? true : false;
                var mutedRole = settings.mutedrole ? settings.mutedrole : 'Not Set';
                var tableFlip = settings.tableflip && settings.tableflip != 0 ? true : false;
                var parsedAntiMention;
                if (settings.antimention) {
                    parsedAntiMention = parseInt(settings.antimention);
                    if (parsedAntiMention == 0 || isNaN(parsedAntiMention)) {
                        parsedAntiMention = 'Disabled';
                    }
                } else {
                    parsedAntiMention = 'Disabled';
                }
                var antiMention = parsedAntiMention;
                let permOverride = settings.permoverride && settings.permoverride != 0 ? true : false;
                let dmHelp = settings.dmhelp && settings.dmhelp != 0 ? true : false;

                let staffPerms = settings.staffperms || bu.defaultStaff;
                var message = `\`\`\`prolog
Settings For ${msg.channel.guild.name}
         Prefix : ${prefix}
  NSFW Channels : ${nsfwMessage}
    Blacklisted : ${blacklistMessage}
       Greeting : ${greeting}
       Farewell : ${farewell}
 Modlog Channel : ${modlogChannel}
     Muted Role : ${mutedRole}
  Track Deletes : ${deleteNotif}
    CAH is NSFW : ${cahNsfw}
     Tableflips : ${tableFlip}
   Anti-Mention : ${antiMention}
        DM Help : ${dmHelp}
  Perm Override : ${permOverride}
    Staff Perms : ${staffPerms}
\`\`\``;
                bu.sendMessageToDiscord(msg.channel.id, message);
            });
        });
    } else {
        words.shift();
        var key;
        switch (words.shift().toLowerCase()) {
            case 'set':
                if (words.length > 0) {
                    key = words.shift();
                    var value = words.join(' ');
                    if (settings[key]) {
                        bu.guildSettings.set(msg.channel.guild.id, key, value).then(() => {
                            bu.sendMessageToDiscord(msg.channel.id, ':ok_hand:');
                        });
                    } else {
                        bu.sendMessageToDiscord(msg.channel.id, 'Invalid key!');
                    }
                }
                break;
            case 'help':
                var message = '\nYou can use \`settings set <key> [value]\` to set the following settings. All settings are case insensitive.\n';
                for (key in settings) {
                    message += '**__' + key.toUpperCase() + '__**' + ' - ' + settings[key] + '\n';
                }
                bu.sendMessageToDiscord(msg.channel.id, message);
                break;
        }
    }
};

var settings = {
    cahnsfw: `whether 'cah' can only be done in nsfw channels or not. Set to '0' to disable.`,
    deletenotif: `if enabled, notifies you if a user deleted their command. Set to '0' to disable.`,
    greeting: `what to say to new users when they join. You can also use the \`greet\` command`,
    farewell: `what to say when a user leaves. You can also use the \`farewell\` command`,
    prefix: `the custom command prefix. You can also use the \`setprefix\` command`,
    modlog: `the id of the modlog channel. You can also use the \`modlog\` command`,
    mutedrole: `the id of the muted role.`,
    tableflip: `whether the bot should respond to tableflips/unflips. Set to '0' to disable.`,
    antimention: `the number of unique mentions required to warrant a ban (for anti-mention spam). Set to '0' to disable. Recommended: 25`,
    dmhelp: `whether or not to dm help messages or output them in channels`,
    permoverride: `whether or not specific permissions override role requirement`,
    staffperms: `the numeric value of permissions that designate a staff member. If a user has any of the permissions and permoverride is enabled, allows them to execute any command regardless of role. See <https://discordapi.com/permissions.html> for a permission calculator.`
};<commit_msg>set is no longer required<commit_after>var e = module.exports = {};
var bu;

var bot;
e.init = (Tbot, blargutil) => {
    bot = Tbot;
    bu = blargutil;

    e.category = bu.CommandType.ADMIN;

};

e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = 'settings [help|set <key>]';
e.info = 'Gets or sets the settings for the current guild.';
e.longinfo = '
Gets or sets the settigns for the current guild.
';

e.execute = (msg, words) => {
    if (words.length == 1) {
        //do settings shit
        bu.db.query(`select name, value from guildsetting where guildid=?`, [msg.channel.guild.id], (err, rows) => {
            bu.db.query(`select channelid, nsfw, blacklisted from channel where guildid=?`, [msg.channel.guild.id], (err, rows2) => {
                var nsfw = [];
                var blacklisted = [];
                var i;
                for (i = 0; i < rows2.length; i++) {
                    if (rows2[i].nsfw) {
                        nsfw.push(rows2[i].channelid);
                    }
                    if (rows2[i].blacklisted) {
                        blacklisted.push(rows2[i].channelid);
                    }
                }
                var settings = {};
                for (i = 0; i < rows.length; i++) {
                    settings[rows[i].name] = rows[i].value;
                }

                var prefix = settings.prefix
                    ? settings.prefix : 'Not Set';
                var nsfwMessage = 'None Set';
                if (nsfw.length > 0) {
                    nsfwMessage = '';
                    for (i = 0; i < nsfw.length; i++) {
                        let channel = bot.getChannel(nsfw[i]);
                        if (channel)
                            nsfwMessage += `${channel.name} (${nsfw[i]})\n                - `;
                    }
                    nsfwMessage = nsfwMessage.substring(0, nsfwMessage.length - 19);
                }
                var blacklistMessage = 'None Set';
                if (blacklisted.length > 0) {
                    blacklistMessage = '';
                    for (i = 0; i < blacklisted.length; i++) {
                        let channel = bot.getChannel(blacklisted[i]);
                        if (channel)
                            blacklistMessage += `${channel.name} (${blacklisted[i]}\n                - `;
                    }
                    blacklistMessage = blacklistMessage.substring(0, blacklistMessage.length - 19);
                }
                var greeting = settings.greeting
                    ? settings.greeting : 'Not Set';
                var farewell = settings.farewell
                    ? settings.farewell : 'Not Set';
                var modlogChannel;
                if (settings.modlog) {
                    let channel = bot.getChannel(settings.modlog);
                    if (channel)
                        modlogChannel = `${channel.name} (${settings.modlog})`;
                    else
                        modlogChannel = `Channel Not Found (${settings.modlog})`;
                } else {
                    modlogChannel = 'Not Set';
                }
                var deleteNotif = settings.deletenotif != 0 ? true : false;
                var cahNsfw = settings.cahnsfw && settings.cahnsfw != 0 ? true : false;
                var mutedRole = settings.mutedrole ? settings.mutedrole : 'Not Set';
                var tableFlip = settings.tableflip && settings.tableflip != 0 ? true : false;
                var parsedAntiMention;
                if (settings.antimention) {
                    parsedAntiMention = parseInt(settings.antimention);
                    if (parsedAntiMention == 0 || isNaN(parsedAntiMention)) {
                        parsedAntiMention = 'Disabled';
                    }
                } else {
                    parsedAntiMention = 'Disabled';
                }
                var antiMention = parsedAntiMention;
                let permOverride = settings.permoverride && settings.permoverride != 0 ? true : false;
                let dmHelp = settings.dmhelp && settings.dmhelp != 0 ? true : false;

                let staffPerms = settings.staffperms || bu.defaultStaff;
                var message = `\`\`\`prolog
Settings For ${msg.channel.guild.name}
         Prefix : ${prefix}
  NSFW Channels : ${nsfwMessage}
    Blacklisted : ${blacklistMessage}
       Greeting : ${greeting}
       Farewell : ${farewell}
 Modlog Channel : ${modlogChannel}
     Muted Role : ${mutedRole}
  Track Deletes : ${deleteNotif}
    CAH is NSFW : ${cahNsfw}
     Tableflips : ${tableFlip}
   Anti-Mention : ${antiMention}
        DM Help : ${dmHelp}
  Perm Override : ${permOverride}
    Staff Perms : ${staffPerms}
\`\`\``;
                bu.sendMessageToDiscord(msg.channel.id, message);
            });
        });
    } else {
        words.shift();
        var key;
        switch (words[0].toLowerCase()) {
            case 'set':
                words.shift();
                if (words.length > 0) {
                    key = words.shift();
                    let value = words.join(' ');
                    if (settings[key]) {
                        bu.guildSettings.set(msg.channel.guild.id, key, value).then(() => {
                            bu.sendMessageToDiscord(msg.channel.id, ':ok_hand:');
                        });
                    } else {
                        bu.sendMessageToDiscord(msg.channel.id, 'Invalid key!');
                    }
                }
                break;
            case 'help':
                var message = '\nYou can use \`settings set <key> [value]\` to set the following settings. All settings are case insensitive.\n';
                for (key in settings) {
                    message += '**__' + key.toUpperCase() + '__**' + ' - ' + settings[key] + '\n';
                }
                bu.sendMessageToDiscord(msg.channel.id, message);
                break;
            default:
                if (words.length > 0) {
                    key = words.shift();
                    let value = words.join(' ');
                    if (settings[key]) {
                        bu.guildSettings.set(msg.channel.guild.id, key, value).then(() => {
                            bu.sendMessageToDiscord(msg.channel.id, ':ok_hand:');
                        });
                    } else {
                        bu.sendMessageToDiscord(msg.channel.id, 'Invalid key!');
                    }
                }
                break;
        }
    }
};

var settings = {
    cahnsfw: `whether 'cah' can only be done in nsfw channels or not. Set to '0' to disable.`,
    deletenotif: `if enabled, notifies you if a user deleted their command. Set to '0' to disable.`,
    greeting: `what to say to new users when they join. You can also use the \`greet\` command`,
    farewell: `what to say when a user leaves. You can also use the \`farewell\` command`,
    prefix: `the custom command prefix. You can also use the \`setprefix\` command`,
    modlog: `the id of the modlog channel. You can also use the \`modlog\` command`,
    mutedrole: `the id of the muted role.`,
    tableflip: `whether the bot should respond to tableflips/unflips. Set to '0' to disable.`,
    antimention: `the number of unique mentions required to warrant a ban (for anti-mention spam). Set to '0' to disable. Recommended: 25`,
    dmhelp: `whether or not to dm help messages or output them in channels`,
    permoverride: `whether or not specific permissions override role requirement`,
    staffperms: `the numeric value of permissions that designate a staff member. If a user has any of the permissions and permoverride is enabled, allows them to execute any command regardless of role. See <https://discordapi.com/permissions.html> for a permission calculator.`
};<|endoftext|>


<commit_before>function initialize() {
	if (!localStorage.data || !localStorage.hosts_data) {
		getChallengeData();
	}
	else {
		data = localStorage.getItem('data');
		hosts_data = localStorage.getItem('hosts_data');
		generateCards(data);
	    generateCardsArchive(data);
	    generateCardsPending(data);
	    loadDropDownWithHosts(hosts_data);
	}
}

initialize();

// get all the challenge data from server
function getChallengeData() {
	$.ajax({
		type:'GET',
		url: "http://challengehuntapp.appspot.com/v2",
		beforeSend: function () {
	      	// $("#target").loadingOverlay();
		},
	    success: function (data) {
	    	if(!((typeof localStorage["data"]) === 'undefined')) {
				localStorage.removeItem('data');
			}
	    	localStorage.setItem('data', data);
	    	generateCards(data);
	    	generateCardsArchive(data);
	    	generateCardsPending(data);

	    },
	    error: function(jq, status, message) {
        }
	});
}

// get all the hosts from the server
function getHosts() {
	$.ajax({
		type:'GET',
		url: "http://challengehuntapp.appspot.com/hosts",
		beforeSend: function () {

		},
	    success: function (data) {
	    	// $("#loader-select-menu").hide();
	    	loadDropDownWithHosts(data);
	    },
	    error: function(jq, status, message) {
        }
	});
}

// creates a new card and applies all the style to it. returns the created card
function applyStyle(data, type) {

	var newDiv = document.createElement('div');
 	newDiv.className = "cards grow";

	if (type == "upcoming") {
		var startDateTime = toTimeZone(data.start).split(",");
		var startDate = dateFormatted(startDateTime[0], startDateTime[1]);
		var startTime = timeFormatted(startDateTime[1]);
		if (startDateTime.length == 1) {
			startDateTime = toTimeZone(data.start).split(" ");
		}
	} else {
		var endDateTime = toTimeZone(data.end).split(",");
		if (endDateTime.length == 1) {
			endDateTime = toTimeZone(data.end).split(" ");
		}
	}

	// var startDate = dateFormatted(startDateTime[0], startDateTime[1]);
	// var startTime = timeFormatted(startDateTime[1]);
	var startDateTimeDiv = document.createElement('div');
	var lengthOfContestname = data.contest_name.length;

	var logoDiv = document.createElement('img');

	if(imageExists("/img/"+data.host_name+".png")) {
		logoDiv.setAttribute('src', '/img/'+ data.host_name  +'.png');
	}
	// special case for hackercup
	else if(data.host_name == "facebook.com/hackercup") {
		logoDiv.setAttribute('src', '/img/hackercup.jpg');
	}
	// another case for codejam
	else if(data.host_name == "google.com/codejam") {
		logoDiv.setAttribute('src', '/img/codejam.png');
	}
	else {
		logoDiv.setAttribute('src', '/img/default.jpg');
	}

	if (type == "upcoming") {
		logoDiv.className = "logo-upcoming";
	} else {
		logoDiv.className = "logo";
	}
	newDiv.appendChild(logoDiv);

	if (type == "upcoming") {
		startDateTimeDiv.className = "time start-time";
		startDateTimeDiv.innerHTML = "" + startDate + startTime ;
		newDiv.appendChild(startDateTimeDiv);
	}

	if (type == "upcoming") {
		// var startDateForCalender = dateForCalendar(startDateTime[0], startDateTime[1]) + 'T' + timeForCalendar(startDateTime[1]);
		// var endDateForCalendar = dateForCalendar(endDateTime[0], startDateTime[1]) + 'T' + timeForCalendar(endDateTime[1]);
		// var calendarTime = startDateForCalender + '/' + endDateForCalendar;
	    calendarLink = "https://www.google.com/calendar/render?action=TEMPLATE&text="+encodeURIComponent(data.contest_name)+"&location="+data.contest_url+"&pli=1&uid=&sf=true&output=xml#eventpage_6"
		var calendarDiv = document.createElement('div');
		calendarDiv.className = "calendar";
		calendarDiv.innerHTML = "";
		newDiv.appendChild(calendarDiv);
	}

	if(lengthOfContestname < 28) {

		var contestNameDiv = document.createElement('div');
		contestNameDiv.className =  "contest-name-short"
		contestNameDiv.innerHTML = "" +data.contest_name+ "";
		newDiv.appendChild(contestNameDiv);
	} else {

		var contestNameDiv = document.createElement('div');
		contestNameDiv.className =  "contest-name-long";
		contestNameDiv.innerHTML = "" +data.contest_name+ "
";
		newDiv.appendChild(contestNameDiv);
	}

	var contestUrlDiv = document.createElement('div');
	contestUrlDiv.className =  "contest-url";

	var contestUrlLink = document.createElement('a');
	contestUrlLink.href = data.host_url;
	contestUrlLink.target = "blank";
	contestUrlLink.innerHTML =  data.host_name ;
	contestUrlDiv.appendChild(contestUrlLink);
	newDiv.appendChild(contestUrlDiv);

	var durationDiv = document.createElement('div');
	durationDiv.className = "duration";
	durationDiv.innerHTML = "Duration: " + data.duration;
	newDiv.appendChild(durationDiv);

	if (type == "active" || type == "archived") {
		var endDate = dateFormatted(endDateTime[0], endDateTime[1]);
		var endTime = timeFormatted(endDateTime[1]);
		var endDateTimeDiv = document.createElement('div');
		endDateTimeDiv.className = "time end-time"
		endDateTimeDiv.innerHTML = ""+ endDate + endTime  ;
		newDiv.appendChild(endDateTimeDiv);
	}

	return newDiv;
}

// generates card for active contest
function generateCards(data) {

	var active_tabs = document.getElementById("active-contests");
	active_tabs.innerHTML = "";

	var active_contests = JSON.parse(data);
	var active_contest_data = active_contests["active"];
	var numberOfActiveContests = 0;

	var prevContestName = "";

	hosts = JSON.parse(localStorage.getItem('hosts'));

	for (var i = 0; i < active_contest_data.length; i++) {
		if(prevContestName != active_contest_data[i].contest_name){
			active_card = applyStyle(active_contest_data[i], "active");

	 		if ((typeof localStorage["hosts"]) === 'undefined') {
				document.getElementById("active-contests").appendChild(active_card);
				numberOfActiveContests += 1;
			}
			else if (hosts.hasOwnProperty(active_contest_data[i].host_name)) {
				document.getElementById("active-contests").appendChild(active_card);
				numberOfActiveContests += 1;
			}
		}
		prevContestName = active_contest_data[i].contest_name;
	}

	chrome.browserAction.setBadgeText({text:numberOfActiveContests.toString()});
}

// generates card for upcoming contest
function generateCardsPending(data) {

	var pending_tabs = document.getElementById("pending-contests");
	pending_tabs.innerHTML = "";

	var pending_contest_data = JSON.parse(data)["pending"];
	hosts = JSON.parse(localStorage.getItem('hosts'));

	var prevContestName = "";

	for (var i = 0; i < pending_contest_data.length; i++) {

		if(prevContestName != pending_contest_data[i].contest_name){

			upcomingDiv = applyStyle(pending_contest_data[i], "upcoming");
		 	if ((typeof localStorage["hosts"]) === 'undefined') {
				document.getElementById("pending-contests").appendChild(upcomingDiv);
			}
			else if (hosts.hasOwnProperty(pending_contest_data[i].host_name)) {
				document.getElementById("pending-contests").appendChild(upcomingDiv);
			}

			prevContestName = pending_contest_data[i].contest_name;
		}
	}
}

// generates card for archived contest
function generateCardsArchive(data) {

	var archived_tabs = document.getElementById("archived-contests");
	archived_tabs.innerHTML = "";

	var archived_contest_data = JSON.parse(data)["archived"];
	hosts = JSON.parse(localStorage.getItem('hosts'));

	var prevContestName = "";

	for (var i = 0; i < archived_contest_data.length; i++) {

		if(prevContestName != archived_contest_data[i].contest_name){

			archived_card = applyStyle(archived_contest_data[i], "archived");
		 	if ((typeof localStorage["hosts"]) === 'undefined') {
				document.getElementById("archived-contests").appendChild(archived_card);
			}
			else if (hosts.hasOwnProperty(archived_contest_data[i].host_name)) {
				document.getElementById("archived-contests").appendChild(archived_card);
			}

			prevContestName = archived_contest_data[i].contest_name;
		}
	}
}

// select all the options from drop down which are already in the local storage
function loadDropDownWithHosts(data) {

	var dropDown = document.getElementById("tokenize");
	var hosts = (JSON.parse(data))["hosts"]
	selected_hosts = JSON.parse(localStorage.getItem('hosts'));

	for (var i = 0; i < hosts.length; i++) {
		var newOption = document.createElement('option');
		newOption.value = hosts[i];

		if (!((typeof localStorage["hosts"]) === 'undefined') && selected_hosts.hasOwnProperty(hosts[i])) {
			newOption.selected = true;
		}
		newOption.innerText = hosts[i];
		dropDown.appendChild(newOption);
	}
	loadDropDown();
}

function loadDropDown() {
	$('#tokenize').tokenize({
		placeholder: "Filter by platform names...",
		// displayDropdownOnFocus: true,
		onAddToken: function(value, text){
			if((typeof localStorage["hosts"]) === 'undefined') {
				var hosts = {}
				hosts[value] = true;
				localStorage.setItem('hosts', JSON.stringify(hosts));
			} else {
				var hosts = JSON.parse(localStorage.getItem('hosts'));
				if (hosts.hasOwnProperty(value)) {
				} else {
					hosts[value] = true;
					localStorage.setItem('hosts', JSON.stringify(hosts));
				}
			}

			if((typeof localStorage["data"]) === 'undefined') {
				getChallengeData();
			} else {
				// if data already exists in localstorage, then call generateCards(data) else call getChallengeData()
				var data = localStorage.getItem('data');
				generateCards(data);
				generateCardsArchive(data);
				generateCardsPending(data);
			}
		},
		onRemoveToken: function(value){
			hosts = JSON.parse(localStorage.getItem('hosts'));

			// check if hosts contain that value. should not be the case
			if (!hosts.hasOwnProperty(value)) {
				console.log("not there")
			} else {
				delete hosts[value];
				var hostsLength = Object.keys(hosts).length;
				// if all the hosts have been removed, delete the host key
				if (hostsLength == 0) {
					localStorage.removeItem('hosts');
				} else {
					localStorage.setItem('hosts', JSON.stringify(hosts));
				}
			}

			// if data already exists in localstorage, then call generateCards(data) else call getChallengeData()
			if((typeof localStorage["data"]) === 'undefined') {
				getChallengeData();
			} else {
				var data = localStorage.getItem('data');
				generateCards(data);
				generateCardsArchive(data);
				generateCardsPending(data);
			}
		},
	});
}<commit_msg>removed redundant code<commit_after><|endoftext|>


<commit_before>/**
 * small demo node server that implements the EMU-webApp-websocket-protocol
 *
 * NOTE: on save no actions are performed (functions are just stubs)
 *
 * to run:
 *  > node nodeEmuProtocolWsServer.js
 *
 * author: Raphael Winkelmann
 */

'use strict';

// load deps
var fs = require('fs');
var os = require('os');
var filewalker = require('filewalker');

// allow to set vars from command line
if (process.argv.length === 2) {

  var portNr = 17890;
  var pathToDbRoot = '/Users/raphaelwinkelmann/Desktop/dfgvowels/';
  var configName = 'dfgvowels_DBconfig.json';
  // var pathToDbRoot = '../app/testData/newFormat/ae/';
  // var configName = 'ae_DBconfig.json';

} else if (process.argv.length === 3) {

  var portNr = process.argv[2];
  var pathToDbRoot = '../app/testData/newFormat/ae/';
  var configName = 'ae_DBconfig.json';

} else if (process.argv.length === 5) {

  var portNr = process.argv[2];
  var pathToDbRoot = process.argv[3];;
  var configName = process.argv[4];;

}

var dbConfig = {};
var ssffFilesMap = {};

// demo of user management
var doUserManagement = 'NO';
var userName = 'user1';
var userPwd = '1234'; // high security plain text password! This is for demo purposes only! Please store your passwords properly...

var WebSocketServer = require('ws').Server,
  wss = new WebSocketServer({
    port: portNr
  });

console.log('websocketserver running @: ws://' + os.hostname() + ':' + portNr);

wss.on('connection', function (ws) {

  console.log('INFO: client connected');

  ws.on('message', function (message) {
    // console.log('received: %s', message);
    var mJSO = JSON.parse(message);

    switch (mJSO.type) {
      // GETPROTOCOL method
    case 'GETPROTOCOL':
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'data': {
          'protocol': 'EMU-webApp-websocket-protocol',
          'version': '0.0.2'
        },
        'status': {
          'type': 'SUCCESS',
          'message': ''
        }
      }), undefined, 0);
      break;

      // GETDOUSERMANAGEMENT method
    case 'GETDOUSERMANAGEMENT':
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'data': doUserManagement,
        'status': {
          'type': 'SUCCESS',
          'message': ''
        }
      }), undefined, 0);
      break;

      // LOGONUSER method
    case 'LOGONUSER':

      if (mJSO.userName !== userName) {
        // handle wrong user name
        ws.send(JSON.stringify({
          'callbackID': mJSO.callbackID,
          'data': 'BADUSERNAME',
          'status': {
            'type': 'SUCCESS',
            'message': ''
          }
        }), undefined, 0);
      } else if (mJSO.pwd !== userPwd) {
        // handle wrong password
        ws.send(JSON.stringify({
          'callbackID': mJSO.callbackID,
          'data': 'BADPASSWORD',
          'status': {
            'type': 'SUCCESS',
            'message': ''
          }
        }), undefined, 0);
      } else {
        ws.send(JSON.stringify({
          'callbackID': mJSO.callbackID,
          'data': 'LOGGEDON',
          'status': {
            'type': 'SUCCESS',
            'message': ''
          }
        }), undefined, 0);
      }

      break;

      // GETGLOBALDBCONFIG method
    case 'GETGLOBALDBCONFIG':
      console.log('############## GETGLOBALDBCONFIG ####################')
      fs.readFile(pathToDbRoot + configName, 'utf8', function (err, data) {
        if (err) {
          console.log('Error: ' + err);
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'status': {
              'type': 'ERROR',
              'message': err
            }
          }), undefined, 0);
          return;
        } else {
          dbConfig = JSON.parse(data);

          // figure out which SSFF files should be sent with each bundle
          // (could further reduce the files being sent by looking at the perspectives)
          for (var i = 0; i < dbConfig.ssffTrackDefinitions.length; i++) {
            if (ssffFilesMap[dbConfig.ssffTrackDefinitions[i].fileExtension] !== undefined) {
              ssffFilesMap[dbConfig.ssffTrackDefinitions[i].fileExtension] += 1;
            } else {
              ssffFilesMap[dbConfig.ssffTrackDefinitions[i].fileExtension] = 1;
            }
          }
          console.log(ssffFilesMap);

          // curUttList = dbConfig;
          // curStrippedUttList = stripUttList(dbConfig);

          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'data': dbConfig,
            'status': {
              'type': 'SUCCESS',
              'message': ''
            }
          }), undefined, 0);
          // ws.send(labelData);
        }
      });
      break;

      // GETBUNDLELIST method
      // file walks through DB to get all the bundles
    case 'GETBUNDLELIST':
      var bundleList = [];
      filewalker(pathToDbRoot)
        .on('dir', function (p) {

          var patt = new RegExp('^.+_ses+/.+_bndl$');
          // var patt = new RegExp('^SES[^/]+/[^/]+$');

          if (patt.test(p)) {
            var arr = p.split('/');
            var nArr = arr[arr.length - 1].split('_');
            var sArr = arr[0].split('_');
            sArr.pop();
            nArr.pop();
            bundleList.push({
              'name': nArr.join('_'),
              'session': sArr.join('_')
            });
          }
        }).on('error', function (err) {
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'status': {
              'type': 'ERROR',
              'message': 'Error creating bundleList! Request type was: ' + mJSO.type + ' Error is: ' + err
            }
          }), undefined, 0);
        }).on('done', function () {
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'data': bundleList,
            'status': {
              'type': 'SUCCESS',
              'message': ''
            }
          }), undefined, 0);
        }).walk();
      break;

      // GETBUNDLE method
    case 'GETBUNDLE':

      var bundle = {};
      bundle.ssffFiles = [];
      filewalker(pathToDbRoot)
        .on('dir', function () {}).on('file', function (p) {
          // var pattMedia = new RegExp('^SES[^/]+/' + mJSO.name + '/[^/]+' + dbConfig.mediafileExtension + '$');
          var pattMedia = new RegExp('^.+_ses+/' + mJSO.name + '_bndl' + '/[^/]+' + dbConfig.mediafileExtension + '$');

          // var pattAnnot = new RegExp('^SES[^/]+/' + mJSO.name + '/[^/]+' + 'json' + '$');
          var pattAnnot = new RegExp('^.+_ses+/' + mJSO.name + '_bndl' + '/[^/]+' + 'json' + '$');

          // read media file
          if (pattMedia.test(p)) {
            console.log(p);
            bundle.mediaFile = {};
            bundle.mediaFile.encoding = 'BASE64';
            bundle.mediaFile.filePath = p;
          }
          // read annotation file
          if (pattAnnot.test(p)) {
            bundle.annotation = {};
            bundle.annotation.filePath = p;
          }
          // read ssffTrackDefinitions

          // for (var i = 0; i < dbConfig.ssffTrackDefinitions.length; i++) {
          //   // var pattTrack = new RegExp('^SES[^/]+/' + mJSO.name + '/[^/]+' + dbConfig.ssffTrackDefinitions[i].fileExtension + '$');
          //   var pattTrack = new RegExp('^.+_ses+/' + mJSO.name + '_bndl' + '/[^/]+' + dbConfig.ssffTrackDefinitions[i].fileExtension + '$');
          //   if (pattTrack.test(p)) {
          //     bundle.ssffFiles.push({
          //       ssffTrackName: dbConfig.ssffTrackDefinitions[i].name,
          //       encoding: 'BASE64',
          //       filePath: p
          //     });
          //   }
          // }

        }).on('error', function (err) {
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'status': {
              'type': 'ERROR',
              'message': 'Error getting bundle! Request type was: ' + mJSO.type + ' Error is: ' + err
            }
          }), undefined, 0);
        }).on('done', function () {
          console.log(mJSO)
          bundle.mediaFile.data = fs.readFileSync(pathToDbRoot + bundle.mediaFile.filePath, 'base64');
          delete bundle.mediaFile.filePath;

          bundle.annotation = JSON.parse(fs.readFileSync(pathToDbRoot + bundle.annotation.filePath, 'utf8'));
          delete bundle.annotation.filePath;
          for (var key in ssffFilesMap) {
              bundle.ssffFiles.push({
                fileExtension: key,
                encoding: 'BASE64',
                data: fs.readFileSync(pathToDbRoot + mJSO.session + '_ses/' + mJSO.name + '_bndl/' + mJSO.name + '.' + key, 'base64')
              });
          //   bundle.ssffFiles[i].data = fs.readFileSync(pathToDbRoot + bundle.ssffFiles[i].filePath, 'base64');
          //   delete bundle.ssffFiles[i].filePath;
          }

          console.log('##########################');
          console.log('done');
          // fs.writeFileSync('/Users/raphaelwinkelmann/Desktop/bundle.json', JSON.stringify(bundle, undefined, 0));
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'data': bundle,
            'status': {
              'type': 'SUCCESS',
              'message': ''
            }
          }), undefined, 0);

        }).walk();
      break;

      // SAVEBUNDLE method
    case 'SAVEBUNDLE':
      console.log('### Pretending to save bundle:');
      console.log('\tname:', mJSO.data.annotation.name);
      console.log('\tsession:', mJSO.data.session);
      // console.log(mJSO.data.annotation);
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'status': {
          'type': 'SUCCESS',
          'message': 'Pst... I did not really do anything. Please do not tell anyone...'
        }
      }), undefined, 0);
      break;

      // DISCONNECTING method
    case 'DISCONNECTWARNING':
      console.log('preparing to disconnect...');
      // console.log(mJSO.data.annotation);
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'status': {
          'type': 'SUCCESS',
          'message': ''
        }
      }), undefined, 0);
      break;

    default:
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'status': {
          'type': 'ERROR',
          'message': 'Sent request type that is unknown to server! Request type was: ' + mJSO.type
        }
      }), undefined, 0);
    }

  });

  // display that client has disconnected
  ws.on('close', function () {
    console.log('INFO: client disconnected');
  });
});

/**
 *
 */
function sendBundle(ws, bundle) {
  console.log(bundle);
}<commit_msg>back to ae<commit_after>/**
 * small demo node server that implements the EMU-webApp-websocket-protocol
 *
 * NOTE: on save no actions are performed (functions are just stubs)
 *
 * to run:
 *  > node nodeEmuProtocolWsServer.js
 *
 * author: Raphael Winkelmann
 */

'use strict';

// load deps
var fs = require('fs');
var os = require('os');
var filewalker = require('filewalker');

// allow to set vars from command line
if (process.argv.length === 2) {

  var portNr = 17890;
  // var pathToDbRoot = '/Users/raphaelwinkelmann/Desktop/dfgvowels/';
  // var configName = 'dfgvowels_DBconfig.json';
  var pathToDbRoot = '../app/testData/newFormat/ae/';
  var configName = 'ae_DBconfig.json';

} else if (process.argv.length === 3) {

  var portNr = process.argv[2];
  var pathToDbRoot = '../app/testData/newFormat/ae/';
  var configName = 'ae_DBconfig.json';

} else if (process.argv.length === 5) {

  var portNr = process.argv[2];
  var pathToDbRoot = process.argv[3];;
  var configName = process.argv[4];;

}

var dbConfig = {};
var ssffFilesMap = {};

// demo of user management
var doUserManagement = 'NO';
var userName = 'user1';
var userPwd = '1234'; // high security plain text password! This is for demo purposes only! Please store your passwords properly...

var WebSocketServer = require('ws').Server,
  wss = new WebSocketServer({
    port: portNr
  });

console.log('websocketserver running @: ws://' + os.hostname() + ':' + portNr);

wss.on('connection', function (ws) {

  console.log('INFO: client connected');

  ws.on('message', function (message) {
    // console.log('received: %s', message);
    var mJSO = JSON.parse(message);

    switch (mJSO.type) {
      // GETPROTOCOL method
    case 'GETPROTOCOL':
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'data': {
          'protocol': 'EMU-webApp-websocket-protocol',
          'version': '0.0.2'
        },
        'status': {
          'type': 'SUCCESS',
          'message': ''
        }
      }), undefined, 0);
      break;

      // GETDOUSERMANAGEMENT method
    case 'GETDOUSERMANAGEMENT':
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'data': doUserManagement,
        'status': {
          'type': 'SUCCESS',
          'message': ''
        }
      }), undefined, 0);
      break;

      // LOGONUSER method
    case 'LOGONUSER':

      if (mJSO.userName !== userName) {
        // handle wrong user name
        ws.send(JSON.stringify({
          'callbackID': mJSO.callbackID,
          'data': 'BADUSERNAME',
          'status': {
            'type': 'SUCCESS',
            'message': ''
          }
        }), undefined, 0);
      } else if (mJSO.pwd !== userPwd) {
        // handle wrong password
        ws.send(JSON.stringify({
          'callbackID': mJSO.callbackID,
          'data': 'BADPASSWORD',
          'status': {
            'type': 'SUCCESS',
            'message': ''
          }
        }), undefined, 0);
      } else {
        ws.send(JSON.stringify({
          'callbackID': mJSO.callbackID,
          'data': 'LOGGEDON',
          'status': {
            'type': 'SUCCESS',
            'message': ''
          }
        }), undefined, 0);
      }

      break;

      // GETGLOBALDBCONFIG method
    case 'GETGLOBALDBCONFIG':
      console.log('############## GETGLOBALDBCONFIG ####################')
      fs.readFile(pathToDbRoot + configName, 'utf8', function (err, data) {
        if (err) {
          console.log('Error: ' + err);
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'status': {
              'type': 'ERROR',
              'message': err
            }
          }), undefined, 0);
          return;
        } else {
          dbConfig = JSON.parse(data);

          // figure out which SSFF files should be sent with each bundle
          // (could further reduce the files being sent by looking at the perspectives)
          for (var i = 0; i < dbConfig.ssffTrackDefinitions.length; i++) {
            if (ssffFilesMap[dbConfig.ssffTrackDefinitions[i].fileExtension] !== undefined) {
              ssffFilesMap[dbConfig.ssffTrackDefinitions[i].fileExtension] += 1;
            } else {
              ssffFilesMap[dbConfig.ssffTrackDefinitions[i].fileExtension] = 1;
            }
          }
          console.log(ssffFilesMap);

          // curUttList = dbConfig;
          // curStrippedUttList = stripUttList(dbConfig);

          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'data': dbConfig,
            'status': {
              'type': 'SUCCESS',
              'message': ''
            }
          }), undefined, 0);
          // ws.send(labelData);
        }
      });
      break;

      // GETBUNDLELIST method
      // file walks through DB to get all the bundles
    case 'GETBUNDLELIST':
      var bundleList = [];
      filewalker(pathToDbRoot)
        .on('dir', function (p) {

          var patt = new RegExp('^.+_ses+/.+_bndl$');
          // var patt = new RegExp('^SES[^/]+/[^/]+$');

          if (patt.test(p)) {
            var arr = p.split('/');
            var nArr = arr[arr.length - 1].split('_');
            var sArr = arr[0].split('_');
            sArr.pop();
            nArr.pop();
            bundleList.push({
              'name': nArr.join('_'),
              'session': sArr.join('_')
            });
          }
        }).on('error', function (err) {
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'status': {
              'type': 'ERROR',
              'message': 'Error creating bundleList! Request type was: ' + mJSO.type + ' Error is: ' + err
            }
          }), undefined, 0);
        }).on('done', function () {
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'data': bundleList,
            'status': {
              'type': 'SUCCESS',
              'message': ''
            }
          }), undefined, 0);
        }).walk();
      break;

      // GETBUNDLE method
    case 'GETBUNDLE':

      var bundle = {};
      bundle.ssffFiles = [];
      filewalker(pathToDbRoot)
        .on('dir', function () {}).on('file', function (p) {
          // var pattMedia = new RegExp('^SES[^/]+/' + mJSO.name + '/[^/]+' + dbConfig.mediafileExtension + '$');
          var pattMedia = new RegExp('^.+_ses+/' + mJSO.name + '_bndl' + '/[^/]+' + dbConfig.mediafileExtension + '$');

          // var pattAnnot = new RegExp('^SES[^/]+/' + mJSO.name + '/[^/]+' + 'json' + '$');
          var pattAnnot = new RegExp('^.+_ses+/' + mJSO.name + '_bndl' + '/[^/]+' + 'json' + '$');

          // read media file
          if (pattMedia.test(p)) {
            console.log(p);
            bundle.mediaFile = {};
            bundle.mediaFile.encoding = 'BASE64';
            bundle.mediaFile.filePath = p;
          }
          // read annotation file
          if (pattAnnot.test(p)) {
            bundle.annotation = {};
            bundle.annotation.filePath = p;
          }
          // read ssffTrackDefinitions

          // for (var i = 0; i < dbConfig.ssffTrackDefinitions.length; i++) {
          //   // var pattTrack = new RegExp('^SES[^/]+/' + mJSO.name + '/[^/]+' + dbConfig.ssffTrackDefinitions[i].fileExtension + '$');
          //   var pattTrack = new RegExp('^.+_ses+/' + mJSO.name + '_bndl' + '/[^/]+' + dbConfig.ssffTrackDefinitions[i].fileExtension + '$');
          //   if (pattTrack.test(p)) {
          //     bundle.ssffFiles.push({
          //       ssffTrackName: dbConfig.ssffTrackDefinitions[i].name,
          //       encoding: 'BASE64',
          //       filePath: p
          //     });
          //   }
          // }

        }).on('error', function (err) {
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'status': {
              'type': 'ERROR',
              'message': 'Error getting bundle! Request type was: ' + mJSO.type + ' Error is: ' + err
            }
          }), undefined, 0);
        }).on('done', function () {
          console.log(mJSO)
          bundle.mediaFile.data = fs.readFileSync(pathToDbRoot + bundle.mediaFile.filePath, 'base64');
          delete bundle.mediaFile.filePath;

          bundle.annotation = JSON.parse(fs.readFileSync(pathToDbRoot + bundle.annotation.filePath, 'utf8'));
          delete bundle.annotation.filePath;
          for (var key in ssffFilesMap) {
              bundle.ssffFiles.push({
                fileExtension: key,
                encoding: 'BASE64',
                data: fs.readFileSync(pathToDbRoot + mJSO.session + '_ses/' + mJSO.name + '_bndl/' + mJSO.name + '.' + key, 'base64')
              });
          //   bundle.ssffFiles[i].data = fs.readFileSync(pathToDbRoot + bundle.ssffFiles[i].filePath, 'base64');
          //   delete bundle.ssffFiles[i].filePath;
          }

          console.log('##########################');
          console.log('done');
          // fs.writeFileSync('/Users/raphaelwinkelmann/Desktop/bundle.json', JSON.stringify(bundle, undefined, 0));
          ws.send(JSON.stringify({
            'callbackID': mJSO.callbackID,
            'data': bundle,
            'status': {
              'type': 'SUCCESS',
              'message': ''
            }
          }), undefined, 0);

        }).walk();
      break;

      // SAVEBUNDLE method
    case 'SAVEBUNDLE':
      console.log('### Pretending to save bundle:');
      console.log('\tname:', mJSO.data.annotation.name);
      console.log('\tsession:', mJSO.data.session);
      // console.log(mJSO.data.annotation);
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'status': {
          'type': 'SUCCESS',
          'message': 'Pst... I did not really do anything. Please do not tell anyone...'
        }
      }), undefined, 0);
      break;

      // DISCONNECTING method
    case 'DISCONNECTWARNING':
      console.log('preparing to disconnect...');
      // console.log(mJSO.data.annotation);
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'status': {
          'type': 'SUCCESS',
          'message': ''
        }
      }), undefined, 0);
      break;

    default:
      ws.send(JSON.stringify({
        'callbackID': mJSO.callbackID,
        'status': {
          'type': 'ERROR',
          'message': 'Sent request type that is unknown to server! Request type was: ' + mJSO.type
        }
      }), undefined, 0);
    }

  });

  // display that client has disconnected
  ws.on('close', function () {
    console.log('INFO: client disconnected');
  });
});

/**
 *
 */
function sendBundle(ws, bundle) {
  console.log(bundle);
}<|endoftext|>


<commit_before>/**
 * clipboard.js
 */
"use strict";
{
  /* api */
  const {i18n, runtime} = browser;

  /* constants */
  const COPY_ALL_TABS = "copyAllTabsURL";
  const COPY_LINK = "copyLinkURL";
  const COPY_PAGE = "copyPageURL";
  const COPY_TAB = "copyTabURL";
  const EXEC_COPY = "executeCopy";
  const EXEC_COPY_POPUP = "executeCopyPopup";
  const EXEC_COPY_TABS = "executeCopyAllTabs";
  const EXEC_COPY_TABS_POPUP = "executeCopyAllTabsPopup";
  const MIME_HTML = "text/html";
  const MIME_PLAIN = "text/plain";
  const NOTIFY_COPY = "notifyOnCopy";
  const TYPE_FROM = 8;
  const TYPE_TO = -1;
  const USER_INPUT = "userInput";
  const USER_INPUT_DEFAULT = "Edit content text of the link";

  const ASCIIDOC = "AsciiDoc";
  const BBCODE_TEXT = "BBCodeText";
  const BBCODE_URL = "BBCodeURL";
  const HTML_HYPER = "HTMLHyper";
  const HTML_PLAIN = "HTMLPlain";
  const LATEX = "LaTeX";
  const MARKDOWN = "Markdown";
  const MEDIAWIKI = "MediaWiki";
  const REST = "reStructuredText";
  const TEXTILE = "Textile";

  /* variables */
  const vars = {
    mimeType: MIME_PLAIN,
  };

  /**
   * throw error
   * @param {!Object} e - Error
   * @throws
   */
  const throwErr = e => {
    throw e;
  };

  /**
   * get type
   * @param {*} o - object to check
   * @returns {string} - type of object
   */
  const getType = o =>
    Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);

  /**
   * is string
   * @param {*} o - object to check
   * @returns {boolean} - result
   */
  const isString = o => typeof o === "string" || o instanceof String;

  /**
   * strip matching char
   * @param {string} str - string
   * @param {RegExp} re - RegExp
   * @returns {?string} - string
   */
  const stripChar = (str, re) =>
    isString(str) && re && re.global && str.replace(re, "") || null;

  /**
   * escape matching char
   * @param {string} str - string
   * @param {RegExp} re - RegExp
   * @returns {?string} - string
   */
  const escapeChar = (str, re) =>
    isString(str) && re && re.global &&
    str.replace(re, (m, c) => `\\${c}`) || null;

  /**
   * convert matching character to numeric character reference
   * @param {string} str - string
   * @param {RegExp} re - RegExp
   * @returns {?string} - string
   */
  const convertNumCharRef = (str, re) =>
    isString(str) && re && re.global &&
    str.replace(re, (m, c) => `&#${c.charCodeAt(0)};`) || null;

  /**
   * convert HTML specific character to character reference
   * @param {string} str - string
   * @returns {?string} - string
   */
  const convertHtmlChar = str =>
    isString(str) &&
    str.replace(/&(?!(?:(?:(?:[gl]|quo)t|amp)|[\dA-Za-z]+|#(?:\d+|x[\dA-Fa-f]+));)/g, "&")
      .replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """) ||
    null;

  /**
   * convert LaTeX special char
   * @param {string} str - string
   * @returns {?string} - string
   */
  const convertLaTeXChar = str =>
    isString(str) && escapeChar(
      str.replace(/\\/g, "\\textbackslash[]")
        .replace(/\^/g, "\\textasciicircum[]")
        .replace(/~/g, "\\textasciitilde[]"),
      /([%$#&_{}])/g
    ).replace(
      /(\\text(?:backslash|ascii(?:circum|tilde)))\[\]/g,
      (m, c) => `${c}{}`
    ) || null;

  /**
   * encode URL component part
   * @param {string} part - component part
   * @returns {string} - encoded component part
   */
  const encodeUrlPart = part =>
    isString(part) &&
    part.replace(/([\s<>[\]'^`{|}])/g, (m, c) => encodeURIComponent(c))
      .replace(/(')/g, (m, c) => escape(c)) || "";

  /**
   * encode special char in URL
   * @param {string} str - URL string
   * @returns {?string} - encoded URL
   */
  const encodeUrlSpecialChar = str => {
    let encodedUrl;
    if (isString(str)) {
      const url = new URL(str);
      if (url) {
        const {
          hash: frag, origin, pathname: path, protocol, search: query,
        } = url;
        const base = protocol === "file:" && `${protocol}//` || origin;
        encodedUrl = new URL(
          `${encodeUrlPart(path)}${encodeUrlPart(query)}${encodeUrlPart(frag)}`,
          base
        );
      }
    }
    return encodedUrl && encodedUrl.href || null;
  };

  /**
   * close window
   * @returns {void}
   */
  const closeWindow = () => {
    window.close();
  };

  /**
   * copy to clipboard
   * @param {string} text - text to copy
   * @returns {void}
   */
  const copyToClipboard = async (text = "") => {
    if (!isString(text)) {
      throw new TypeError(`Expected String but got ${getType(text)}.`);
    }
    text = text.trim() || "";
    if (text) {
      /**
       * set clipboard data
       * @param {!Object} evt - Event
       * @returns {void}
       */
      const setClipboardData = evt => {
        let {mimeType: type} = vars;
        if (!isString(type) || !/^text\/(?:plain|html)$/.test(type)) {
          type = MIME_PLAIN;
        }
        document.removeEventListener("copy", setClipboardData, true);
        evt.stopImmediatePropagation();
        evt.preventDefault();
        evt.clipboardData.setData(type, text);
        return runtime.sendMessage({
          [NOTIFY_COPY]: true,
        });
      };

      document.addEventListener("copy", setClipboardData, true);
      document.execCommand("copy");
    }
  };

  /**
   * edit content
   * @param {string} content - content to edit
   * @returns {?string} - edited content
   */
  const editContent = async (content = "") => {
    if (!isString(content)) {
      throw new TypeError(`Expected String but got ${getType(content)}.`);
    }
    const msg = await i18n.getMessage(USER_INPUT) || USER_INPUT_DEFAULT;
    content = window.prompt(msg, content.trim());
    return content;
  };

  /**
   * get format ID from menu item ID
   * @param {string} menuItemId - menu item ID
   * @returns {?string} - ID
   */
  const getFormatIdFromMenuItemId = async menuItemId => {
    let id;
    if (menuItemId.startsWith(COPY_ALL_TABS)) {
      id = menuItemId.replace(COPY_ALL_TABS, "");
    } else if (menuItemId.startsWith(COPY_LINK)) {
      id = menuItemId.replace(COPY_LINK, "");
    } else if (menuItemId.startsWith(COPY_PAGE)) {
      id = menuItemId.replace(COPY_PAGE, "");
    } else if (menuItemId.startsWith(COPY_TAB)) {
      id = menuItemId.replace(COPY_TAB, "");
    }
    return id || null;
  };

  /**
   * create link text
   * @param {Object} data - copy data
   * @returns {?string} - link text
   */
  const createLinkText = async (data = {}) => {
    const {
      content: contentText, menuItemId, promptContent, template, title, url,
    } = data;
    if (!isString(template)) {
      throw new TypeError(`Expected String but got ${getType(template)}.`);
    }
    const format = await getFormatIdFromMenuItemId(menuItemId);
    let content = isString(contentText) && contentText.replace(/\s+/g, " ") ||
                  "";
    let linkTitle = title || "";
    let linkUrl = url;
    if (promptContent) {
      content = await editContent(content) || "";
    }
    switch (format) {
      case ASCIIDOC:
        content = escapeChar(content, /\[[\]]/g) || "";
        linkUrl = encodeUrlSpecialChar(url);
        break;
      case BBCODE_TEXT:
      case BBCODE_URL:
        content = stripChar(content, /\[(?:url(?:=.*)?|\/url)\]/ig) || "";
        break;
      case HTML_HYPER:
      case HTML_PLAIN:
        content = convertHtmlChar(content) || "";
        linkTitle = convertHtmlChar(title) || "";
        break;
      case LATEX:
        content = convertLaTeXChar(content) || "";
        break;
      case MARKDOWN:
        content = escapeChar(convertHtmlChar(content), /([[\]])/g) || "";
        linkTitle = escapeChar(convertHtmlChar(title), /(")/g) || "";
        break;
      case MEDIAWIKI:
        content = convertNumCharRef(content, /([[\]'~<>{}=*#;:\-|])/g) || "";
        break;
      case REST:
        content = escapeChar(content, /([`<>])/g) || "";
        break;
      case TEXTILE:
        content = convertHtmlChar(convertNumCharRef(content, /([()])/g)) || "";
        break;
      default:
    }
    vars.mimeType = format === HTML_HYPER && MIME_HTML || MIME_PLAIN;
    return template.replace(/%content%/g, content.trim())
      .replace(/%title%/g, linkTitle.trim())
      .replace(/%url%/g, linkUrl.trim());
  };

  /**
   * create all tabs link text
   * @param {Array} arr - array of link text
   * @returns {string} - joined link text
   */
  const createAllTabsLinkText = async arr => {
    const {mimeType} = vars;
    const joiner = mimeType === MIME_HTML && "
\n" || "\n";
    return arr.filter(i => i).join(joiner);
  };

  /**
   * extract copy data
   * @param {Object} data - copy data
   * @returns {?string} - link text
   */
  const extractCopyData = async (data = {}) => {
    const {allTabs} = data;
    let text;
    if (Array.isArray(allTabs)) {
      const func = [];
      for (const tabData of allTabs) {
        func.push(createLinkText(tabData));
      }
      text = await Promise.all(func).then(createAllTabsLinkText);
    } else {
      text = await createLinkText(data);
    }
    return text || null;
  };

  /**
   * handle message
   * @param {*} msg - message
   * @returns {Promise.<Array>} - results of each handler
   */
  const handleMsg = async (msg = {}) => {
    const items = msg && Object.keys(msg);
    const func = [];
    if (items && items.length) {
      for (const item of items) {
        const obj = msg[item];
        switch (item) {
          case EXEC_COPY:
          case EXEC_COPY_TABS:
            func.push(extractCopyData(obj).then(copyToClipboard));
            break;
          case EXEC_COPY_POPUP:
          case EXEC_COPY_TABS_POPUP:
            func.push(
              extractCopyData(obj).then(copyToClipboard).then(closeWindow)
            );
            break;
          default:
        }
      }
    }
    return Promise.all(func);
  };

  /* listener */
  runtime.onMessage.addListener(msg => handleMsg(msg).catch(throwErr));
}
<commit_msg>Update clipboard.js<commit_after>/**
 * clipboard.js
 */
"use strict";
{
  /* api */
  const {i18n, runtime} = browser;

  /* constants */
  const COPY_ALL_TABS = "copyAllTabsURL";
  const COPY_LINK = "copyLinkURL";
  const COPY_PAGE = "copyPageURL";
  const COPY_TAB = "copyTabURL";
  const EXEC_COPY = "executeCopy";
  const EXEC_COPY_POPUP = "executeCopyPopup";
  const EXEC_COPY_TABS = "executeCopyAllTabs";
  const EXEC_COPY_TABS_POPUP = "executeCopyAllTabsPopup";
  const MIME_HTML = "text/html";
  const MIME_PLAIN = "text/plain";
  const NOTIFY_COPY = "notifyOnCopy";
  const TYPE_FROM = 8;
  const TYPE_TO = -1;
  const USER_INPUT = "userInput";
  const USER_INPUT_DEFAULT = "Edit content text of the link";

  const ASCIIDOC = "AsciiDoc";
  const BBCODE_TEXT = "BBCodeText";
  const BBCODE_URL = "BBCodeURL";
  const HTML_HYPER = "HTMLHyper";
  const HTML_PLAIN = "HTMLPlain";
  const LATEX = "LaTeX";
  const MARKDOWN = "Markdown";
  const MEDIAWIKI = "MediaWiki";
  const REST = "reStructuredText";
  const TEXTILE = "Textile";

  /* variables */
  const vars = {
    mimeType: MIME_PLAIN,
  };

  /**
   * throw error
   * @param {!Object} e - Error
   * @throws
   */
  const throwErr = e => {
    throw e;
  };

  /**
   * get type
   * @param {*} o - object to check
   * @returns {string} - type of object
   */
  const getType = o =>
    Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);

  /**
   * is string
   * @param {*} o - object to check
   * @returns {boolean} - result
   */
  const isString = o => typeof o === "string" || o instanceof String;

  /**
   * strip matching char
   * @param {string} str - string
   * @param {RegExp} re - RegExp
   * @returns {?string} - string
   */
  const stripChar = (str, re) =>
    isString(str) && re && re.global && str.replace(re, "") || null;

  /**
   * escape matching char
   * @param {string} str - string
   * @param {RegExp} re - RegExp
   * @returns {?string} - string
   */
  const escapeChar = (str, re) =>
    isString(str) && re && re.global &&
    str.replace(re, (m, c) => `\\${c}`) || null;

  /**
   * convert matching character to numeric character reference
   * @param {string} str - string
   * @param {RegExp} re - RegExp
   * @returns {?string} - string
   */
  const convertNumCharRef = (str, re) =>
    isString(str) && re && re.global &&
    str.replace(re, (m, c) => `&#${c.charCodeAt(0)};`) || null;

  /**
   * convert HTML specific character to character reference
   * @param {string} str - string
   * @returns {?string} - string
   */
  const convertHtmlChar = str =>
    isString(str) &&
    str.replace(/&(?!(?:(?:(?:[gl]|quo)t|amp)|[\dA-Za-z]+|#(?:\d+|x[\dA-Fa-f]+));)/g, "&")
      .replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """) ||
    null;

  /**
   * convert LaTeX special char
   * @param {string} str - string
   * @returns {?string} - string
   */
  const convertLaTeXChar = str =>
    isString(str) && escapeChar(
      str.replace(/\\/g, "\\textbackslash[]")
        .replace(/\^/g, "\\textasciicircum[]")
        .replace(/~/g, "\\textasciitilde[]"),
      /([%$#&_{}])/g
    ).replace(
      /(\\text(?:backslash|ascii(?:circum|tilde)))\[\]/g,
      (m, c) => `${c}{}`
    ) || null;

  /**
   * encode URL component part
   * @param {string} part - component part
   * @returns {string} - encoded component part
   */
  const encodeUrlPart = part =>
    isString(part) &&
    part.replace(/([\s<>[\]'^`{|}])/g, (m, c) => encodeURIComponent(c))
      .replace(/(')/g, (m, c) => escape(c)) || "";

  /**
   * encode special char in URL
   * @param {string} str - URL string
   * @returns {?string} - encoded URL
   */
  const encodeUrlSpecialChar = str => {
    let encodedUrl;
    if (isString(str)) {
      const url = new URL(str);
      if (url) {
        const {
          hash: frag, origin, pathname: path, protocol, search: query,
        } = url;
        const base = protocol === "file:" && `${protocol}//` || origin;
        encodedUrl = new URL(
          `${encodeUrlPart(path)}${encodeUrlPart(query)}${encodeUrlPart(frag)}`,
          base
        );
      }
    }
    return encodedUrl && encodedUrl.href || null;
  };

  /**
   * close window
   * @returns {void}
   */
  const closeWindow = () => {
    window.close();
  };

  /**
   * copy to clipboard
   * @param {string} text - text to copy
   * @returns {void}
   */
  const copyToClipboard = async (text = "") => {
    if (!isString(text)) {
      throw new TypeError(`Expected String but got ${getType(text)}.`);
    }
    text = text.trim() || "";
    if (text) {
      /**
       * set clipboard data
       * @param {!Object} evt - Event
       * @returns {void}
       */
      const setClipboardData = evt => {
        let {mimeType: type} = vars;
        if (!isString(type) || !/^text\/(?:plain|html)$/.test(type)) {
          type = MIME_PLAIN;
        }
        document.removeEventListener("copy", setClipboardData, true);
        evt.stopImmediatePropagation();
        evt.preventDefault();
        evt.clipboardData.setData(type, text);
        return runtime.sendMessage({
          [NOTIFY_COPY]: true,
        });
      };

      document.addEventListener("copy", setClipboardData, true);
      document.execCommand("copy");
    }
  };

  /**
   * edit content
   * @param {string} content - content to edit
   * @returns {?string} - edited content
   */
  const editContent = async (content = "") => {
    if (!isString(content)) {
      throw new TypeError(`Expected String but got ${getType(content)}.`);
    }
    const msg = await i18n.getMessage(USER_INPUT) || USER_INPUT_DEFAULT;
    content = window.prompt(msg, content.trim());
    return content;
  };

  /**
   * get format ID from menu item ID
   * @param {string} menuItemId - menu item ID
   * @returns {?string} - ID
   */
  const getFormatIdFromMenuItemId = async menuItemId => {
    let id;
    if (menuItemId.startsWith(COPY_ALL_TABS)) {
      id = menuItemId.replace(COPY_ALL_TABS, "");
    } else if (menuItemId.startsWith(COPY_LINK)) {
      id = menuItemId.replace(COPY_LINK, "");
    } else if (menuItemId.startsWith(COPY_PAGE)) {
      id = menuItemId.replace(COPY_PAGE, "");
    } else if (menuItemId.startsWith(COPY_TAB)) {
      id = menuItemId.replace(COPY_TAB, "");
    } else if (isString(menuItemId)) {
      id = menuItemId;
    }
    return id;
  };

  /**
   * create link text
   * @param {Object} data - copy data
   * @returns {?string} - link text
   */
  const createLinkText = async (data = {}) => {
    const {
      content: contentText, menuItemId, promptContent, template, title, url,
    } = data;
    if (!isString(template)) {
      throw new TypeError(`Expected String but got ${getType(template)}.`);
    }
    const format = await getFormatIdFromMenuItemId(menuItemId);
    let text = "";
    if (format) {
      let linkTitle = title || "";
      let linkUrl = url;
      let content = isString(contentText) && contentText.replace(/\s+/g, " ") ||
                    "";
      if (promptContent) {
        content = await editContent(content) || "";
      }
      switch (format) {
        case ASCIIDOC:
          content = escapeChar(content, /\[[\]]/g) || "";
          linkUrl = encodeUrlSpecialChar(url);
          break;
        case BBCODE_TEXT:
        case BBCODE_URL:
          content = stripChar(content, /\[(?:url(?:=.*)?|\/url)\]/ig) || "";
          break;
        case HTML_HYPER:
        case HTML_PLAIN:
          content = convertHtmlChar(content) || "";
          linkTitle = convertHtmlChar(title) || "";
          break;
        case LATEX:
          content = convertLaTeXChar(content) || "";
          break;
        case MARKDOWN:
          content = escapeChar(convertHtmlChar(content), /([[\]])/g) || "";
          linkTitle = escapeChar(convertHtmlChar(title), /(")/g) || "";
          break;
        case MEDIAWIKI:
          content = convertNumCharRef(content, /([[\]'~<>{}=*#;:\-|])/g) || "";
          break;
        case REST:
          content = escapeChar(content, /([`<>])/g) || "";
          break;
        case TEXTILE:
          content =
            convertHtmlChar(convertNumCharRef(content, /([()])/g)) || "";
          break;
        default:
      }
      vars.mimeType = format === HTML_HYPER && MIME_HTML || MIME_PLAIN;
      text = template.replace(/%content%/g, content.trim())
        .replace(/%title%/g, linkTitle.trim())
        .replace(/%url%/g, linkUrl.trim());
    }
    return text;
  };

  /**
   * create all tabs link text
   * @param {Array} arr - array of link text
   * @returns {string} - joined link text
   */
  const createAllTabsLinkText = async arr => {
    const {mimeType} = vars;
    const joiner = mimeType === MIME_HTML && "
\n" || "\n";
    return arr.filter(i => i).join(joiner);
  };

  /**
   * extract copy data
   * @param {Object} data - copy data
   * @returns {?string} - link text
   */
  const extractCopyData = async (data = {}) => {
    const {allTabs} = data;
    let text;
    if (Array.isArray(allTabs)) {
      const func = [];
      for (const tabData of allTabs) {
        func.push(createLinkText(tabData));
      }
      text = await Promise.all(func).then(createAllTabsLinkText);
    } else {
      text = await createLinkText(data);
    }
    return text || null;
  };

  /**
   * handle message
   * @param {*} msg - message
   * @returns {Promise.<Array>} - results of each handler
   */
  const handleMsg = async (msg = {}) => {
    const items = msg && Object.keys(msg);
    const func = [];
    if (items && items.length) {
      for (const item of items) {
        const obj = msg[item];
        switch (item) {
          case EXEC_COPY:
          case EXEC_COPY_TABS:
            func.push(extractCopyData(obj).then(copyToClipboard));
            break;
          case EXEC_COPY_POPUP:
          case EXEC_COPY_TABS_POPUP:
            func.push(
              extractCopyData(obj).then(copyToClipboard).then(closeWindow)
            );
            break;
          default:
        }
      }
    }
    return Promise.all(func);
  };

  /* listener */
  runtime.onMessage.addListener(msg => handleMsg(msg).catch(throwErr));
}
<|endoftext|>


<commit_before>/**
 * @fileOverview
 * Copyright (c) 2012 Adam Ranfelt
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 * OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * EventBus Module Definition
 * @author Adam Ranfelt <adamRenny@gmail.com>
 * @version 2.0
 */
define(function() {
    "use strict";

    /**
     * Type definition that all callbacks must be
     * Used to compare all callback types against
     *
     * @type {string}
     * @constant
     * @since 1.0
     */
    var CALLBACK_TYPE = 'function';

    var NAMESPACE_SEPARATOR = '.';

    var NOT_FOUND_INDEX = -1;

    var Event = function(name) {
        this.name = name;
        this.observers = [];
    };

    Event.prototype.add = function(callback) {
        var observers = this.observers;
        if (observers.indexOf(callback) !== NOT_FOUND_INDEX) {
            return;
        }

        observers.push(callback);
    };

    Event.prototype.remove = function(callback) {
        var observers = this.observers;
        var index = observers.indexOf(callback);
        if (index === NOT_FOUND_INDEX) {
            return;
        }

        observers.splice(index, 1);
    };

    Event.prototype.has = function(callback) {
        return this.observers.indexOf(callback) !== NOT_FOUND_INDEX;
    };

    Event.prototype.trigger = function(args) {
        var i = 0;
        var observers = this.observers;
        var length = observers.length;
        args[0] = this.name;
        for (; i < length; i++) {
            observers[i].apply(this, args);
        }
    };

    var Namespace = function(name) {
        this.name = name;
        this.event = new Event(name);
        this.events = {};
    };

    Namespace.prototype.add = function(callback, name) {
        var events = this.events;
        var event;

        if (name !== undefined) {
            if (!events.hasOwnProperty(name)) {
                events[name] = new Event(name);
            }

            event = events[name];
        } else {
            event = this.event;
        }

        event.add(callback);
    };

    Namespace.prototype.remove = function(callback, name) {
        var events = this.events;
        var event;

        if (name !== undefined) {
            if (!events.hasOwnProperty(name)) {
                events[name] = new Event(name);
            }

            event = events[name];
        } else {
            event = this.event;
        }

        event.remove(callback);
    };

    // trigger all namespaced and non-namespaced
    Namespace.prototype.trigger = function(name, args) {
        var events = this.events;
        var event;

        if (events.hasOwnProperty(name)) {
            event = events[name]
            event.trigger(args);
        }

        this.event.trigger(args);
    };

    var _getNamespace = function(namespace) {
        if (namespace === undefined) {
            return this.events;
        }

        var namespaces = this.namespaces;
        if (!namespaces.hasOwnProperty(namespace)) {
            namespaces[namespace] = new Namespace(namespace);
        }

        return namespaces[namespace];
    };

    /**
     * EventBus Constructor
     *
     * EventBus structure, using the observer pattern
     * Publishes messages based on topics on a topic-to-callback basis
     *
     * @name EventBus
     * @class Publish-subscribe observer model
     * @constructor
     * @since 1.0
     */
    var EventBus = function() {
        this.events = new Namespace('');
        this.namespaces = {};
    };

    // namespace + event
    // event
    // namespace
    EventBus.prototype.on = function(topic, callback) {
        if (topic === undefined || callback === undefined) {
            throw 'UndefinedError: On usage: on(topic, callback)';
        }

        if (typeof callback !== CALLBACK_TYPE) {
            throw 'TypeError: Callback subscribing is of type ' + (typeof callback) + ' not of type ' + CALLBACK_TYPE;
        }

        var event;
        var namespace;
        var namespaceIndex = topic.lastIndexOf(NAMESPACE_SEPARATOR);

        if (topic.charAt(0) === NAMESPACE_SEPARATOR && topic.length !== 1) {
            namespace = topic.substr(1);
        } else if (namespaceIndex !== NOT_FOUND_INDEX && namespaceIndex !== topic.length - 1) {
            namespace = topic.substr(namespaceIndex + 1);
            event = topic.substr(0, namespaceIndex);
        } else {
            event = topic;
        }

        var targetNamespace = _getNamespace.call(this, namespace);
        targetNamespace.add(callback, event);
    };

    // namespace + event
    // event
    // namespace
    EventBus.prototype.off = function(topic, callback) {
        if (topic === undefined || callback === undefined) {
            throw 'UndefinedError: Off usage: on(topic, callback)';
        }

        if (typeof callback !== CALLBACK_TYPE) {
            throw 'TypeError: Callback subscribing is of type ' + (typeof callback) + ' not of type ' + CALLBACK_TYPE;
        }

        var event;
        var namespace;
        var namespaceIndex = topic.lastIndexOf(NAMESPACE_SEPARATOR);

        if (topic.charAt(0) === NAMESPACE_SEPARATOR && topic.length !== 1) {
            namespace = topic.substr(1);
        } else if (namespaceIndex !== NOT_FOUND_INDEX && namespaceIndex !== topic.length - 1) {
            namespace = topic.substr(namespaceIndex + 1);
            event = topic.substr(0, namespaceIndex);
        } else {
            event = topic;
        }

        var targetNamespace = _getNamespace.call(this, namespace);
        targetNamespace.remove(callback, event);
    };

    // event
    // event + namespace
    EventBus.prototype.trigger = function(topic) {
        var event;
        var namespace;
        var namespaceIndex = topic.lastIndexOf(NAMESPACE_SEPARATOR);
        var namespaces = this.namespaces;

        if (namespaceIndex !== NOT_FOUND_INDEX && namespaceIndex !== topic.length - 1) {
            namespace = topic.substr(namespaceIndex + 1);
            event = topic.substr(0, namespaceIndex);
        } else {
            event = topic;
        }

        if (namespace && namespaces.hasOwnProperty(namespace)) {
            namespaces[namespace].trigger(event, arguments);
        }
        this.events.trigger(event, arguments);
    };

    return EventBus;
});<commit_msg> * Updating EventBus to throw an exception when trying to send a namespace<commit_after>/**
 * @fileOverview
 * Copyright (c) 2012 Adam Ranfelt
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 * OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * EventBus Module Definition
 * @author Adam Ranfelt <adamRenny@gmail.com>
 * @version 2.0
 */
define(function() {
    "use strict";

    /**
     * Type definition that all callbacks must be
     * Used to compare all callback types against
     *
     * @type {string}
     * @constant
     * @since 1.0
     */
    var CALLBACK_TYPE = 'function';

    var NAMESPACE_SEPARATOR = '.';

    var NOT_FOUND_INDEX = -1;

    var Event = function(name) {
        this.name = name;
        this.observers = [];
    };

    Event.prototype.add = function(callback) {
        var observers = this.observers;
        if (observers.indexOf(callback) !== NOT_FOUND_INDEX) {
            return;
        }

        observers.push(callback);
    };

    Event.prototype.remove = function(callback) {
        var observers = this.observers;
        var index = observers.indexOf(callback);
        if (index === NOT_FOUND_INDEX) {
            return;
        }

        observers.splice(index, 1);
    };

    Event.prototype.has = function(callback) {
        return this.observers.indexOf(callback) !== NOT_FOUND_INDEX;
    };

    Event.prototype.trigger = function(args) {
        var i = 0;
        var observers = this.observers;
        var length = observers.length;
        args[0] = this.name;
        for (; i < length; i++) {
            observers[i].apply(this, args);
        }
    };

    var Namespace = function(name) {
        this.name = name;
        this.event = new Event(name);
        this.events = {};
    };

    Namespace.prototype.add = function(callback, name) {
        var events = this.events;
        var event;

        if (name !== undefined) {
            if (!events.hasOwnProperty(name)) {
                events[name] = new Event(name);
            }

            event = events[name];
        } else {
            event = this.event;
        }

        event.add(callback);
    };

    Namespace.prototype.remove = function(callback, name) {
        var events = this.events;
        var event;

        if (name !== undefined) {
            if (!events.hasOwnProperty(name)) {
                events[name] = new Event(name);
            }

            event = events[name];
        } else {
            event = this.event;
        }

        event.remove(callback);
    };

    // trigger all namespaced and non-namespaced
    Namespace.prototype.trigger = function(name, args) {
        var events = this.events;
        var event;

        if (events.hasOwnProperty(name)) {
            event = events[name]
            event.trigger(args);
        }

        this.event.trigger(args);
    };

    var _getNamespace = function(namespace) {
        if (namespace === undefined) {
            return this.events;
        }

        var namespaces = this.namespaces;
        if (!namespaces.hasOwnProperty(namespace)) {
            namespaces[namespace] = new Namespace(namespace);
        }

        return namespaces[namespace];
    };

    /**
     * EventBus Constructor
     *
     * EventBus structure, using the observer pattern
     * Publishes messages based on topics on a topic-to-callback basis
     *
     * @name EventBus
     * @class Publish-subscribe observer model
     * @constructor
     * @since 1.0
     */
    var EventBus = function() {
        this.events = new Namespace('');
        this.namespaces = {};
    };

    // namespace + event
    // event
    // namespace
    EventBus.prototype.on = function(topic, callback) {
        if (topic === undefined || callback === undefined) {
            throw 'UndefinedError: On usage: on(topic, callback)';
        }

        if (typeof callback !== CALLBACK_TYPE) {
            throw 'TypeError: Callback subscribing is of type ' + (typeof callback) + ' not of type ' + CALLBACK_TYPE;
        }

        var event;
        var namespace;
        var namespaceIndex = topic.lastIndexOf(NAMESPACE_SEPARATOR);

        if (topic.charAt(0) === NAMESPACE_SEPARATOR && topic.length !== 1) {
            namespace = topic.substr(1);
        } else if (namespaceIndex !== NOT_FOUND_INDEX && namespaceIndex !== topic.length - 1) {
            namespace = topic.substr(namespaceIndex + 1);
            event = topic.substr(0, namespaceIndex);
        } else {
            event = topic;
        }

        var targetNamespace = _getNamespace.call(this, namespace);
        targetNamespace.add(callback, event);
    };

    // namespace + event
    // event
    // namespace
    EventBus.prototype.off = function(topic, callback) {
        if (topic === undefined || callback === undefined) {
            throw 'UndefinedError: Off usage: on(topic, callback)';
        }

        if (typeof callback !== CALLBACK_TYPE) {
            throw 'TypeError: Callback subscribing is of type ' + (typeof callback) + ' not of type ' + CALLBACK_TYPE;
        }

        var event;
        var namespace;
        var namespaceIndex = topic.lastIndexOf(NAMESPACE_SEPARATOR);

        if (topic.charAt(0) === NAMESPACE_SEPARATOR && topic.length !== 1) {
            namespace = topic.substr(1);
        } else if (namespaceIndex !== NOT_FOUND_INDEX && namespaceIndex !== topic.length - 1) {
            namespace = topic.substr(namespaceIndex + 1);
            event = topic.substr(0, namespaceIndex);
        } else {
            event = topic;
        }

        var targetNamespace = _getNamespace.call(this, namespace);
        targetNamespace.remove(callback, event);
    };

    // event
    // event + namespace
    EventBus.prototype.trigger = function(topic) {
        var event;
        var namespace;
        var namespaceIndex = topic.lastIndexOf(NAMESPACE_SEPARATOR);
        var namespaces = this.namespaces;

        if (topic.charAt(0) === NAMESPACE_SEPARATOR && topic.length !== 1) {
            throw 'Error: triggering topic is a namespace and should be an event';
        } else if (namespaceIndex !== NOT_FOUND_INDEX && namespaceIndex !== topic.length - 1) {
            namespace = topic.substr(namespaceIndex + 1);
            event = topic.substr(0, namespaceIndex);
        } else {
            event = topic;
        }

        if (namespace && namespaces.hasOwnProperty(namespace)) {
            namespaces[namespace].trigger(event, arguments);
        }
        this.events.trigger(event, arguments);
    };

    return EventBus;
});<|endoftext|>


<commit_before>const api = require('../localUserAPI')

const load = async (senderID, keys, api) => {
    const oldState = await api.getState(senderID)
    //contact that user for the first time. Dont have oldState of that user
    if (!oldState) {
        await api.setState(senderID, { state: "initial", done: 0, round: 0, keysLeftForThatUser: keys, welcomed: false, userScore: 0 })
    }
    let state = await api.getState(senderID)

    return new User(senderID, state, api)
}

class User {
    constructor(senderID, state, api) {
        this.senderID = senderID
        this.state = state
        this.api = api
    }

    setState(updateState) {
        const newState = Object.assign({}, this.state, updateState)
        this.api.setState(this.senderID, newState)
        this.state = newState
    }

    startQuiz(questionKey) {
        this.setState({
            keysLeftForThatUser: this.state.keysLeftForThatUser,
            currentQuestionKey: questionKey,
            done: this.state.done,
            round: this.state.round,
            state: 'playing',
            userScore: this.state.userScore
        })
    }

    // setStateWelcome(stateWelcome) {
    //     this.api.setStateWelcome(this.senderID, stateWelcome)
    //     this.stateWelcome = stateWelcome
    // }

    setRound(round) {
        // this.api.setRound(this.senderID, round)
        this.setState({ round: round })
    }

    welcome() {
        this.api.setStateWelcome(this.senderID, true)
        this.setState({ welcomed: true })
    }

    playing() {
        this.setState(
            {
                state: 'playing',
                welcomed: true
            }
        )
        // this.state.state = 'playing'
        // this.state.welcomed = true
        // this.state.keysLeftForThatUser = keysLeftForThatUser
    }

    startAgain(keysLeftForThatUser) {
        this.setState(
            {
                state: 'playing', keysLeftForThatUser: keysLeftForThatUser,
                welcomed: true
            }
        )
        // this.state.state = 'playing'
        // this.state.welcomed = true
        // this.state.keysLeftForThatUser = keysLeftForThatUser
    }

    pause() {
        this.setState(
            { state: 'pause', welcomed: true }
        )
        // this.state.state = 'pause'
        // this.state.welcomed = true
    }

    resume() {
        this.setState({ state: 'playing' })
        // api.setState(this.senderID,
        //     { state: 'playing', done: this.state.done, round: this.state.round, keysLeftForThatUser: this.state.keysLeftForThatUser,
        //     welcomed: this.state.welcomed, userScore: this.state.userScore }
        // )
        // this.state.state = 'playing'
    }

    async done() {
        let done = await this.state.done++
        console.log("__this.state.DONE__", this.state.done)
        console.log("__DONE__", done)
        this.setState({ done: done})
    }

    finish() {
        this.setState({ state: 'finish', welcomed: true, done: 0, userScore: 0 })
        // api.setState(this.senderID,
        //     { state: 'finish', done: 0, round: this.state.round, keysLeftForThatUser: this.state.keysLeftForThatUser,
        //     welcomed: this.state.welcomed, userScore: 0 }
        // )
        // this.state.state = 'finish'
        // this.state.welcomed = true
    }

    nextRound(keysLeftForThatUser) {
        this.setState({ state: "playing", done: 0, userScore: 0, keysLeftForThatUser: keysLeftForThatUser, welcomed: true })
        // api.setState(this.senderID,
        //     { state: 'playing', done: 0, round: this.state.round, keysLeftForThatUser: keysLeftForThatUser,
        //     welcomed: this.state.welcomed, userScore: 0 }
        // )
        // this.state.keysLeftForThatUser = keysLeftForThatUser
        // this.state.state = 'playing'
        // this.state.welcomed = true
    }

}

module.exports = { load, User }

<commit_msg>user.done<commit_after>const api = require('../localUserAPI')

const load = async (senderID, keys, api) => {
    const oldState = await api.getState(senderID)
    //contact that user for the first time. Dont have oldState of that user
    if (!oldState) {
        await api.setState(senderID, { state: "initial", done: 0, round: 0, keysLeftForThatUser: keys, welcomed: false, userScore: 0 })
    }
    let state = await api.getState(senderID)

    return new User(senderID, state, api)
}

class User {
    constructor(senderID, state, api) {
        this.senderID = senderID
        this.state = state
        this.api = api
    }

    setState(updateState) {
        const newState = Object.assign({}, this.state, updateState)
        this.api.setState(this.senderID, newState)
        this.state = newState
    }

    startQuiz(questionKey) {
        this.setState({
            keysLeftForThatUser: this.state.keysLeftForThatUser,
            currentQuestionKey: questionKey,
            done: this.state.done,
            round: this.state.round,
            state: 'playing',
            userScore: this.state.userScore
        })
    }

    // setStateWelcome(stateWelcome) {
    //     this.api.setStateWelcome(this.senderID, stateWelcome)
    //     this.stateWelcome = stateWelcome
    // }

    setRound(round) {
        // this.api.setRound(this.senderID, round)
        this.setState({ round: round })
    }

    welcome() {
        this.api.setStateWelcome(this.senderID, true)
        this.setState({ welcomed: true })
    }

    playing() {
        this.setState(
            {
                state: 'playing',
                welcomed: true
            }
        )
        // this.state.state = 'playing'
        // this.state.welcomed = true
        // this.state.keysLeftForThatUser = keysLeftForThatUser
    }

    startAgain(keysLeftForThatUser) {
        this.setState(
            {
                state: 'playing', keysLeftForThatUser: keysLeftForThatUser,
                welcomed: true
            }
        )
        // this.state.state = 'playing'
        // this.state.welcomed = true
        // this.state.keysLeftForThatUser = keysLeftForThatUser
    }

    pause() {
        this.setState(
            { state: 'pause', welcomed: true }
        )
        // this.state.state = 'pause'
        // this.state.welcomed = true
    }

    resume() {
        this.setState({ state: 'playing' })
        // api.setState(this.senderID,
        //     { state: 'playing', done: this.state.done, round: this.state.round, keysLeftForThatUser: this.state.keysLeftForThatUser,
        //     welcomed: this.state.welcomed, userScore: this.state.userScore }
        // )
        // this.state.state = 'playing'
    }

    done() {
        let done = this.state.done + 1
        console.log("__this.state.DONE__", this.state.done)
        console.log("__DONE__", done)
        this.setState({ done: done})
    }

    finish() {
        this.setState({ state: 'finish', welcomed: true, done: 0, userScore: 0 })
        // api.setState(this.senderID,
        //     { state: 'finish', done: 0, round: this.state.round, keysLeftForThatUser: this.state.keysLeftForThatUser,
        //     welcomed: this.state.welcomed, userScore: 0 }
        // )
        // this.state.state = 'finish'
        // this.state.welcomed = true
    }

    nextRound(keysLeftForThatUser) {
        this.setState({ state: "playing", done: 0, userScore: 0, keysLeftForThatUser: keysLeftForThatUser, welcomed: true })
        // api.setState(this.senderID,
        //     { state: 'playing', done: 0, round: this.state.round, keysLeftForThatUser: keysLeftForThatUser,
        //     welcomed: this.state.welcomed, userScore: 0 }
        // )
        // this.state.keysLeftForThatUser = keysLeftForThatUser
        // this.state.state = 'playing'
        // this.state.welcomed = true
    }

}

module.exports = { load, User }

<|endoftext|>


<commit_before>import React from 'react';
import Radium from 'radium';
import SplitPane from '@mnmtanish/react-split-pane';
import * as selectors from '../redux/selectors';
import {connect} from 'react-redux';
import Toolbar from './Toolbar';
import CodeWindow from './CodeWindow';
import Spinner from './Spinner';
import ErrorBox from './ErrorBox';
import {loadTexts} from '../redux/constants';
import REPLHistoryList from './REPLHistoryList';
import REPLInput from './REPLInput';

const styles = {
  editor: {
    display: 'flex',
    flexDirection: 'column',
    height: '100vh',
    alignItems: 'stretch',
  },
  spinner: {
    paddingTop: 200,
    height: 50,
    width: "auto",
    margin: "auto",
    display: "flex",
  },
  splitPaneWrapper: {
    flexBasis: '100%',
    position: 'relative',
  },
};

class Editor extends React.Component {
  render() {
    return (

        <Toolbar logo="https://code.pyret.org/img/pyret-logo.png" />

          <SplitPane defaultSize="50%" split="vertical">
            <CodeWindow/>

              {this.props.isLoadingRuntime &&

                 <Spinner style={styles.spinner}/>

                   {loadTexts[Math.floor(Math.random() * loadTexts.length)]}

              }
              <ErrorBox/>
              <REPLHistoryList/>
              <REPLInput/>

          </SplitPane>

    );
  }
}

Editor.propTypes = {
  result: React.PropTypes.any,
  hasHistory: React.PropTypes.bool,
  isLoadingRuntime: React.PropTypes.bool,
  hasLoadedRuntime: React.PropTypes.bool,
  fontSize: React.PropTypes.number,
};

export default connect(
  state => {
    return {
      result: selectors.getResult(state),
      hasHistory: selectors.hasHistory(state),
      isLoadingRuntime: selectors.isLoadingRuntime(state),
      hasLoadedRuntime: selectors.hasLoadedRuntime(state),
      fontSize: selectors.getFontSize(state),
    };
  }
)(Radium(Editor));
<commit_msg>fix CodeMirror source text hover overriding MoreMenu hover<commit_after>import React from 'react';
import Radium from 'radium';
import SplitPane from '@mnmtanish/react-split-pane';
import * as selectors from '../redux/selectors';
import {connect} from 'react-redux';
import Toolbar from './Toolbar';
import CodeWindow from './CodeWindow';
import Spinner from './Spinner';
import ErrorBox from './ErrorBox';
import {loadTexts} from '../redux/constants';
import REPLHistoryList from './REPLHistoryList';
import REPLInput from './REPLInput';

const styles = {
  editor: {
    display: 'flex',
    flexDirection: 'column',
    height: '100vh',
    alignItems: 'stretch',
  },
  spinner: {
    paddingTop: 200,
    height: 50,
    width: "auto",
    margin: "auto",
    display: "flex",
  },
  splitPaneWrapper: {
    flexBasis: '100%',
    position: 'relative',
    zIndex: 0,
  },
};

class Editor extends React.Component {
  render() {
    return (

        <Toolbar logo="https://code.pyret.org/img/pyret-logo.png" />

          <SplitPane defaultSize="50%" split="vertical">
            <CodeWindow/>

              {this.props.isLoadingRuntime &&

                 <Spinner style={styles.spinner}/>

                   {loadTexts[Math.floor(Math.random() * loadTexts.length)]}

              }
              <ErrorBox/>
              <REPLHistoryList/>
              <REPLInput/>

          </SplitPane>

    );
  }
}

Editor.propTypes = {
  result: React.PropTypes.any,
  hasHistory: React.PropTypes.bool,
  isLoadingRuntime: React.PropTypes.bool,
  hasLoadedRuntime: React.PropTypes.bool,
  fontSize: React.PropTypes.number,
};

export default connect(
  state => {
    return {
      result: selectors.getResult(state),
      hasHistory: selectors.hasHistory(state),
      isLoadingRuntime: selectors.isLoadingRuntime(state),
      hasLoadedRuntime: selectors.hasLoadedRuntime(state),
      fontSize: selectors.getFontSize(state),
    };
  }
)(Radium(Editor));
<|endoftext|>