<commit_before>'use strict';
const please = require('./external/Please');
const util = require('./js/util');
const ipcRenderer = require('electron').ipcRenderer;
const remote = require('remote');
const Menu = remote.require('menu');
const MenuItem = remote.require('menu-item');

let displayedServers = [];
let selectedServer = '';
let selectedChannel;
let selectedUsername;

initializeMenus();
function initializeMenus() {
	//TEXT EDIT MENU
	let textMenu = Menu.buildFromTemplate([{
	        label: 'Copy',
	        role: 'copy',
	    }
	]);

	$('#messageArea').on('contextmenu', 'line', function (e) {
		e.preventDefault();
		textMenu.popup(remote.getCurrentWindow());
	})

	//CHANNEL REMOVE MENU
	let channelMenu = new Menu();
	let elementTargeted;
	channelMenu.append(new MenuItem({ label: 'Remove', click: function() {
		let serverName = elementTargeted.siblings('name').text();
		let channelName = elementTargeted.text();

		for (let key in displayedServers) {
			let server = displayedServers[key];

			if (server.address == serverName) {
				let indexOfChannel = server.channels.indexOf(channelName);
				if (indexOfChannel > -1) {
					server.channels.splice(indexOfChannel, 1);
				}
			}
		}

		elementTargeted.remove();

		ipcRenderer.send('channelRemoved', serverName, channelName);
	}}));

	$('#channelList').on('contextmenu', 'channel', function (e) {
		e.preventDefault();
		elementTargeted = $(this);
		channelMenu.popup(remote.getCurrentWindow());
	})
}

/*
New channel was selected, tell main and trigger visual changes
 */
$('#channelList').on('click', 'channel', function (e) {
	e.preventDefault();

	let serverAddress = $(this).siblings('name').text();

	$('#channelList server channel').removeClass('selected');
	$(this).removeClass('unread');
	$(this).addClass('selected');

	ipcRenderer.send('channelSelected', serverAddress, $(this).text());
})

ipcRenderer.on('channelSelected_reply', function (event, address, channel, username) {
	//Set global variables
	selectedServer = address;
	selectedChannel = channel;
	selectedUsername = username;

	//Make messages of now selectedChannel visible, hide all others
	$("#messageArea server").children('channel').css('display', 'none');
	let selServer = $('[name="' + address + '"]');
	let selChannel = selServer.children('[name="' + channel.name + '"]');
	selChannel.css('display', 'block');

	//Set new username und fill usermenu
	$('#usernameInput').attr('placeholder', username);
	util.fillUsermenu(channel.users);

	//Scroll to last appended message
	util.updateScrollState();
});

$('#titlebar').on('click', 'add',function (e) {
	$('body').toggleClass('prevent');
	$('.modal').toggleClass('active');

	$('.modal select').empty();

    //testing
    for(let key in displayedServers) {
        let server = displayedServers[key];
        $('.modal select').append('<option>' + server.address + '</option>');
    }
});

$('.modal .close').click(function (e) {
	$('body').toggleClass('prevent');
	$('.modal').toggleClass('active');
});

$('#addServer').click(function(e) {
	let serverAddress = $('#serverInput').val();
	let serverExists = $('select:contains("' + serverAddress + '")' ).length > 0 ;
	if (!serverExists) {
		let username = $('#nickInput').val();

		$('.modal select').append('<option>' + serverAddress + '</option>');

		ipcRenderer.send('serverAdded', username, serverAddress);
	} else {
		console.log('Server already added.');
	}
});

$('#addChannel').click(function(e) {
    let selServer = $('.modal select :selected').text();
	let newChannel = $('#channelInput').val();

    ipcRenderer.send('channelAdded', selServer, newChannel);
});

//////////////////////
// Receiving Events //
//////////////////////

ipcRenderer.on('channelData', function (event, address, channel) {
	//First channel to arrive, add it
	if (displayedServers.length < 1) {
		let serverData = {
			address: address,
			channels: [channel.name]
		}

		displayedServers.push(serverData);

		let line = '<server><name>' + address + '</name><channel>' +
			channel.name + '</channel></server>';
		$('#channelList').append(line);

		//Add channel-tag to messageArea, refactor to also add server-tag
		let msgLine = '<server name="' + address + '"><channel name="' +
			channel.name + '"></channel></server>';
		$('#messageArea').append(msgLine);
	}

	let serverExists = false;
	for (let key in displayedServers) {
		let server = displayedServers[key];

		if (server.address == address) {
			//Server exists, check if channel does in server
			serverExists = true;

			if (server.channels.indexOf(channel.name) == -1) {
				//Channel doesnt exist, add it to the server
				server.channels.push(channel.name)

				let line = '<channel>' + channel.name + '</channel>';
				$('server name:contains(' + address + ')').parent().append(line);

				//Add channel-tag to messageArea, refactor to also add server-tag
				let selServer = $('[name="' + address + '"]');
				let msgLine = '<channel name="' + channel.name + '"></channel>';
				selServer.append(msgLine);
			}
		}
	}

	//Server doesn't exist, add it
	if (!serverExists) {
		let serverData = {
			address: address,
			channels: [channel.name]
		}

		displayedServers.push(serverData);

		let line = '<server><name>' + address + '</name><channel>' +
			channel.name + '</channel></server>';
		$('#channelList').append(line);

		//Add channel-tag to messageArea, refactor to also add server-tag
		let msgLine = '<server name="' + address + '"><channel name="' +
			channel.name + '"></channel></server>';
		$('#messageArea').append(msgLine);
	}
});

ipcRenderer.on('messageReceived', function (event, address, message) {
	if (message.event === false && message.action === false) {
		//Mark as unread if not in selectedChannel
		if (address !== selectedServer || message.to !== selectedChannel.name) {
			let affectedServer = $('server name:contains(' + address + ')').parent();
			let affectedChannel = affectedServer.children('channel').filter(function () {
				return ($(this).text() === message.to)
			});

			$(affectedChannel).addClass('unread');
		}

		//Need to handle when channel is not clicked yet, edge case
		if (selectedChannel !== undefined) {
			//Make messages of now selectedChannel visible, hide all others
			//TODO code-duplication
			$("#messageArea server").children('channel').css('display', 'none');
			let selServer = $('[name="' + selectedServer + '"]');
			let selChannel = selServer.children('[name="' + selectedChannel.name + '"]');
			selChannel.css('display', 'block');
		} else {
			$("#messageArea server").children('channel').css('display', 'none');
		}

		appendMessage(address, message);

	} else if (message.event === true) {
		//This message is an event
		appendEvent(address, message);
	}
});

function appendMessage(address, message) {
	let nick = message.from;
	let messageEnc = util.encodeEntities(message.message);
	let selServer = $('[name="' + address + '"]');
	let selChannel = selServer.children('[name="' + message.to + '"]');

	//Remove nick if message before was sent by the same nick
	if (!util.lastNicksUnique(nick, selChannel)) {
		nick = '';
	}

	//Create line and append it
	let line = '<line><nick>' + nick + '</nick><message>' + messageEnc +
		'</message></line>';
	selChannel.append(line);

	//Color nick based on string-hash
	selChannel.find('line:last nick').css('color', util.stringToColour(nick));

	//Check if username is mentioned somewhere in the message,
	//send a notification if there is
	if (selectedUsername !== undefined) {
		let pattern = new RegExp('\\b' + selectedUsername + '\\b', 'ig');
		if (pattern.test(messageEnc)) {
			selChannel.find('line:last message').addClass('highlighted');
			util.doNotify(message.to + ' ' + message.from, message.message);
		}
	}

	//Check if message contains links, use raw message and not encoded one
	let links = util.findLinks(message.message);
	if (links !== null) {
		let insertStr = messageEnc;

		for (let key in links) {
			let link = links[key];

			let start = insertStr.indexOf(link);
			insertStr = insertStr.insert(start, '');

			let end = start + link.length + 3;
			insertStr = insertStr.insert(end, '');
		}

		selChannel.find('line:last message').html(insertStr);
	}

	//Scroll to last appended message
	util.updateScrollState();
}

function appendEvent(address, message) {
	let line = '<line><event>' + message.message + '</event></line>';

	let selServer = $('[name="' + address + '"]');
	let selChannel = selServer.children('[name="' + message.to + '"]');

	selChannel.append(line);

	//Scroll to last appended message
	util.updateScrollState();
}

/*
Usermenu was clicked, open it
 */
$('#titlebar').on('click', 'usermenu', function (e) {
	e.preventDefault();
	e.stopPropagation();

	$('#titlebar usermenu').toggleClass('closed');
})

/*
Close usermenu again if a click occurs somewhere on the document selector
 */
$(document).click(function () {
	$('#titlebar usermenu').addClass('closed');
});

/*
Enter was pressed, new message needs to be sent to main process
 */
$("#messageInput").keydown(function (e) {
	if (e.keyCode == 13) {
		let messageContent = $(this).val();

		if (messageContent !== '') {
			let message = {
				from: selectedUsername,
				to: selectedChannel.name,
				message: messageContent
			}

			$(this).val('');

			appendMessage(selectedServer, message);
			ipcRenderer.send('messageSent', selectedServer, message.message);
		}
	}
});

/*
Enter was pressed, new username needs to be sent to main process
 */
$("#usernameInput").keydown(function (e) {
	if (e.keyCode == 13) {
		let username = $(this).val();

		if (username !== '') {
			selectedUsername = username;

			$("#usernameInput").val('');
			$("#usernameInput").attr('placeholder', username);
			ipcRenderer.send('usernameChanged', selectedServer, username);
		}
	}
});

/*
Tab was pressed, autocomplete word to next username match
 */
$("#messageInput").keydown(function (e) {
	if (e.keyCode == 9) {
		e.preventDefault();

		let inputContent = $(this).val();
		let lastWord = inputContent.split(' ').pop();

		if (inputContent !== '') {
			util.autocomplete(lastWord, selectedChannel.users[0], function (name) {
				let cachedContent = inputContent.substring(
					0, inputContent.lastIndexOf(" "));

				if (cachedContent === '') {
					$("#messageInput").val(name + ': ');
				} else {
					$("#messageInput").val(cachedContent + ' ' + name);
				}
			})
		}
	}
});

/*
Link in a message was clicked, open it with default browser
 */
$('#messageArea').on('click', 'a', function (e) {
	e.preventDefault();
	util.openLink($(this).text());
})

/*
Tell main to close the window
 */
$('#titlebar').on('click', 'close', function (e) {
	e.preventDefault();
	ipcRenderer.send('closeWindow');
})

/**
 * Insert string into string at specified index
 */
String.prototype.insert = function (index, string) {
	if (index > 0)
		return this.substring(0, index) + string + this.substring(index, this.length);
	else
		return string + this;
};

/**
 * Returns a 32bit-int hashcode of a string
 */
String.prototype.hashCode = function() {
  var hash = 0, i, chr, len;
  if (this.length === 0) return hash;
  for (i = 0, len = this.length; i < len; i++) {
    chr   = this.charCodeAt(i);
    hash  = ((hash << 5) - hash) + chr;
    hash |= 0; // Convert to 32bit integer
  }
  return hash;
};

/**
 * Reverse the given array
 */
jQuery.fn.reverse = function () {
	return this.pushStack(this.get().reverse(), arguments);
};
<commit_msg>Bugfix: Jump to last char if input is very long on tab-compelete<commit_after>'use strict';
const please = require('./external/Please');
const util = require('./js/util');
const ipcRenderer = require('electron').ipcRenderer;
const remote = require('remote');
const Menu = remote.require('menu');
const MenuItem = remote.require('menu-item');

let displayedServers = [];
let selectedServer = '';
let selectedChannel;
let selectedUsername;

initializeMenus();
function initializeMenus() {
	//TEXT EDIT MENU
	let textMenu = Menu.buildFromTemplate([{
	        label: 'Copy',
	        role: 'copy',
	    }
	]);

	$('#messageArea').on('contextmenu', 'line', function (e) {
		e.preventDefault();
		textMenu.popup(remote.getCurrentWindow());
	})

	//CHANNEL REMOVE MENU
	let channelMenu = new Menu();
	let elementTargeted;
	channelMenu.append(new MenuItem({ label: 'Remove', click: function() {
		let serverName = elementTargeted.siblings('name').text();
		let channelName = elementTargeted.text();

		for (let key in displayedServers) {
			let server = displayedServers[key];

			if (server.address == serverName) {
				let indexOfChannel = server.channels.indexOf(channelName);
				if (indexOfChannel > -1) {
					server.channels.splice(indexOfChannel, 1);
				}
			}
		}

		elementTargeted.remove();

		ipcRenderer.send('channelRemoved', serverName, channelName);
	}}));

	$('#channelList').on('contextmenu', 'channel', function (e) {
		e.preventDefault();
		elementTargeted = $(this);
		channelMenu.popup(remote.getCurrentWindow());
	})
}

/*
New channel was selected, tell main and trigger visual changes
 */
$('#channelList').on('click', 'channel', function (e) {
	e.preventDefault();

	let serverAddress = $(this).siblings('name').text();

	$('#channelList server channel').removeClass('selected');
	$(this).removeClass('unread');
	$(this).addClass('selected');

	ipcRenderer.send('channelSelected', serverAddress, $(this).text());
})

ipcRenderer.on('channelSelected_reply', function (event, address, channel, username) {
	//Set global variables
	selectedServer = address;
	selectedChannel = channel;
	selectedUsername = username;

	//Make messages of now selectedChannel visible, hide all others
	$("#messageArea server").children('channel').css('display', 'none');
	let selServer = $('[name="' + address + '"]');
	let selChannel = selServer.children('[name="' + channel.name + '"]');
	selChannel.css('display', 'block');

	//Set new username und fill usermenu
	$('#usernameInput').attr('placeholder', username);
	util.fillUsermenu(channel.users);

	//Scroll to last appended message
	util.updateScrollState();
});

$('#titlebar').on('click', 'add',function (e) {
	$('body').toggleClass('prevent');
	$('.modal').toggleClass('active');

	$('.modal select').empty();

    //testing
    for(let key in displayedServers) {
        let server = displayedServers[key];
        $('.modal select').append('<option>' + server.address + '</option>');
    }
});

$('.modal .close').click(function (e) {
	$('body').toggleClass('prevent');
	$('.modal').toggleClass('active');
});

$('#addServer').click(function(e) {
	let serverAddress = $('#serverInput').val();
	let serverExists = $('select:contains("' + serverAddress + '")' ).length > 0 ;
	if (!serverExists) {
		let username = $('#nickInput').val();

		$('.modal select').append('<option>' + serverAddress + '</option>');

		ipcRenderer.send('serverAdded', username, serverAddress);
	} else {
		console.log('Server already added.');
	}
});

$('#addChannel').click(function(e) {
    let selServer = $('.modal select :selected').text();
	let newChannel = $('#channelInput').val();

    ipcRenderer.send('channelAdded', selServer, newChannel);
});

//////////////////////
// Receiving Events //
//////////////////////

ipcRenderer.on('channelData', function (event, address, channel) {
	//First channel to arrive, add it
	if (displayedServers.length < 1) {
		let serverData = {
			address: address,
			channels: [channel.name]
		}

		displayedServers.push(serverData);

		let line = '<server><name>' + address + '</name><channel>' +
			channel.name + '</channel></server>';
		$('#channelList').append(line);

		//Add channel-tag to messageArea, refactor to also add server-tag
		let msgLine = '<server name="' + address + '"><channel name="' +
			channel.name + '"></channel></server>';
		$('#messageArea').append(msgLine);
	}

	let serverExists = false;
	for (let key in displayedServers) {
		let server = displayedServers[key];

		if (server.address == address) {
			//Server exists, check if channel does in server
			serverExists = true;

			if (server.channels.indexOf(channel.name) == -1) {
				//Channel doesnt exist, add it to the server
				server.channels.push(channel.name)

				let line = '<channel>' + channel.name + '</channel>';
				$('server name:contains(' + address + ')').parent().append(line);

				//Add channel-tag to messageArea, refactor to also add server-tag
				let selServer = $('[name="' + address + '"]');
				let msgLine = '<channel name="' + channel.name + '"></channel>';
				selServer.append(msgLine);
			}
		}
	}

	//Server doesn't exist, add it
	if (!serverExists) {
		let serverData = {
			address: address,
			channels: [channel.name]
		}

		displayedServers.push(serverData);

		let line = '<server><name>' + address + '</name><channel>' +
			channel.name + '</channel></server>';
		$('#channelList').append(line);

		//Add channel-tag to messageArea, refactor to also add server-tag
		let msgLine = '<server name="' + address + '"><channel name="' +
			channel.name + '"></channel></server>';
		$('#messageArea').append(msgLine);
	}
});

ipcRenderer.on('messageReceived', function (event, address, message) {
	if (message.event === false && message.action === false) {
		//Mark as unread if not in selectedChannel
		if (address !== selectedServer || message.to !== selectedChannel.name) {
			let affectedServer = $('server name:contains(' + address + ')').parent();
			let affectedChannel = affectedServer.children('channel').filter(function () {
				return ($(this).text() === message.to)
			});

			$(affectedChannel).addClass('unread');
		}

		//Need to handle when channel is not clicked yet, edge case
		if (selectedChannel !== undefined) {
			//Make messages of now selectedChannel visible, hide all others
			//TODO code-duplication
			$("#messageArea server").children('channel').css('display', 'none');
			let selServer = $('[name="' + selectedServer + '"]');
			let selChannel = selServer.children('[name="' + selectedChannel.name + '"]');
			selChannel.css('display', 'block');
		} else {
			$("#messageArea server").children('channel').css('display', 'none');
		}

		appendMessage(address, message);

	} else if (message.event === true) {
		//This message is an event
		appendEvent(address, message);
	}
});

function appendMessage(address, message) {
	let nick = message.from;
	let messageEnc = util.encodeEntities(message.message);
	let selServer = $('[name="' + address + '"]');
	let selChannel = selServer.children('[name="' + message.to + '"]');

	//Remove nick if message before was sent by the same nick
	if (!util.lastNicksUnique(nick, selChannel)) {
		nick = '';
	}

	//Create line and append it
	let line = '<line><nick>' + nick + '</nick><message>' + messageEnc +
		'</message></line>';
	selChannel.append(line);

	//Color nick based on string-hash
	selChannel.find('line:last nick').css('color', util.stringToColour(nick));

	//Check if username is mentioned somewhere in the message,
	//send a notification if there is
	if (selectedUsername !== undefined) {
		let pattern = new RegExp('\\b' + selectedUsername + '\\b', 'ig');
		if (pattern.test(messageEnc)) {
			selChannel.find('line:last message').addClass('highlighted');
			util.doNotify(message.to + ' ' + message.from, message.message);
		}
	}

	//Check if message contains links, use raw message and not encoded one
	let links = util.findLinks(message.message);
	if (links !== null) {
		let insertStr = messageEnc;

		for (let key in links) {
			let link = links[key];

			let start = insertStr.indexOf(link);
			insertStr = insertStr.insert(start, '');

			let end = start + link.length + 3;
			insertStr = insertStr.insert(end, '');
		}

		selChannel.find('line:last message').html(insertStr);
	}

	//Scroll to last appended message
	util.updateScrollState();
}

function appendEvent(address, message) {
	let line = '<line><event>' + message.message + '</event></line>';

	let selServer = $('[name="' + address + '"]');
	let selChannel = selServer.children('[name="' + message.to + '"]');

	selChannel.append(line);

	//Scroll to last appended message
	util.updateScrollState();
}

/*
Usermenu was clicked, open it
 */
$('#titlebar').on('click', 'usermenu', function (e) {
	e.preventDefault();
	e.stopPropagation();

	$('#titlebar usermenu').toggleClass('closed');
})

/*
Close usermenu again if a click occurs somewhere on the document selector
 */
$(document).click(function () {
	$('#titlebar usermenu').addClass('closed');
});

/*
Enter was pressed, new message needs to be sent to main process
 */
$("#messageInput").keydown(function (e) {
	if (e.keyCode == 13) {
		let messageContent = $(this).val();

		if (messageContent !== '') {
			let message = {
				from: selectedUsername,
				to: selectedChannel.name,
				message: messageContent
			}

			$(this).val('');

			appendMessage(selectedServer, message);
			ipcRenderer.send('messageSent', selectedServer, message.message);
		}
	}
});

/*
Enter was pressed, new username needs to be sent to main process
 */
$("#usernameInput").keydown(function (e) {
	if (e.keyCode == 13) {
		let username = $(this).val();

		if (username !== '') {
			selectedUsername = username;

			$("#usernameInput").val('');
			$("#usernameInput").attr('placeholder', username);
			ipcRenderer.send('usernameChanged', selectedServer, username);
		}
	}
});

/*
Tab was pressed, autocomplete word to next username match
 */
$("#messageInput").keydown(function (e) {
	if (e.keyCode == 9) {
		e.preventDefault();

		let messageInput = $("#messageInput");
		let inputContent = $(this).val();
		let lastWord = inputContent.split(' ').pop();

		if (inputContent !== '') {
			util.autocomplete(lastWord, selectedChannel.users[0], function (name) {
				let cachedContent = inputContent.substring(
					0, inputContent.lastIndexOf(" "));

				if (cachedContent === '') {
					messageInput.val(name + ': ');
				} else {
					messageInput.val(cachedContent + ' ' + name);
				}

				//Scroll to the right in case the input field is long
				messageInput[0].scrollLeft = messageInput[0].scrollWidth;
			})
		}
	}
});

/*
Link in a message was clicked, open it with default browser
 */
$('#messageArea').on('click', 'a', function (e) {
	e.preventDefault();
	util.openLink($(this).text());
})

/*
Tell main to close the window
 */
$('#titlebar').on('click', 'close', function (e) {
	e.preventDefault();
	ipcRenderer.send('closeWindow');
})

/**
 * Insert string into string at specified index
 */
String.prototype.insert = function (index, string) {
	if (index > 0)
		return this.substring(0, index) + string + this.substring(index, this.length);
	else
		return string + this;
};

/**
 * Returns a 32bit-int hashcode of a string
 */
String.prototype.hashCode = function() {
  var hash = 0, i, chr, len;
  if (this.length === 0) return hash;
  for (i = 0, len = this.length; i < len; i++) {
    chr   = this.charCodeAt(i);
    hash  = ((hash << 5) - hash) + chr;
    hash |= 0; // Convert to 32bit integer
  }
  return hash;
};

/**
 * Reverse the given array
 */
jQuery.fn.reverse = function () {
	return this.pushStack(this.get().reverse(), arguments);
};
<|endoftext|>


<commit_before>// function completer.
//
// completer should be a class that takes an cell instance
var IPython = (function (IPython) {
    // that will prevent us from misspelling
    "use strict";

    // easier key mapping
    var key = IPython.utils.keycodes;

    function prepend_n_prc(str, n) {
        for( var i =0 ; i< n ; i++){
            str = '%'+str ;
        }
        return str;
    }

    function _existing_completion(item, completion_array){
        for( var c in completion_array ) {
            if(completion_array[c].trim().substr(-item.length) == item)
            { return true; }
        }
       return false;
    }

    // what is the common start of all completions
    function shared_start(B, drop_prct) {
        if (B.length == 1) {
            return B[0];
        }
        var A = new Array();
        var common;
        var min_lead_prct = 10;
        for (var i = 0; i < B.length; i++) {
            var str = B[i].str;
            var localmin = 0;
            if(drop_prct === true){
                while ( str.substr(0, 1) == '%') {
                    localmin = localmin+1;
                    str = str.substring(1);
                }
            }
            min_lead_prct = Math.min(min_lead_prct, localmin);
            A.push(str);
        }

        if (A.length > 1) {
            var tem1, tem2, s;
            A = A.slice(0).sort();
            tem1 = A[0];
            s = tem1.length;
            tem2 = A.pop();
            while (s && tem2.indexOf(tem1) == -1) {
                tem1 = tem1.substring(0, --s);
            }
            if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
                return {
                    str:prepend_n_prc('', min_lead_prct),
                    type: "computed",
                    from: B[0].from,
                    to: B[0].to
                    };
            }
            return {
                str: prepend_n_prc(tem1, min_lead_prct),
                type: "computed",
                from: B[0].from,
                to: B[0].to
            };
        }
        return null;
    }

    var Completer = function (cell) {
        this.cell = cell;
        this.editor = cell.code_mirror;
        var that = this;
        $([IPython.events]).on('status_busy.Kernel', function () {
            that.skip_kernel_completion = true;
        });
        $([IPython.events]).on('status_idle.Kernel', function () {
            that.skip_kernel_completion = false;
        });
    };

    Completer.prototype.startCompletion = function () {
        // call for a 'first' completion, that will set the editor and do some
        // special behaviour like autopicking if only one completion availlable
        //
        if (this.editor.somethingSelected()) return;
        this.done = false;
        // use to get focus back on opera
        this.carry_on_completion(true);
    };

    // easy access for julia to monkeypatch
    //
    Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;

    Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
        return Completer.reinvoke_re.test(pre_cursor);
    }

    /**
     *
     * pass true as parameter if this is the first invocation of the completer
     * this will prevent the completer to dissmiss itself if it is not on a
     * word boundary like pressing tab after a space, and make it autopick the
     * only choice if there is only one which prevent from popping the UI.  as
     * well as fast-forwarding the typing if all completion have a common
     * shared start
     **/
    Completer.prototype.carry_on_completion = function (first_invocation) {
        // Pass true as parameter if you want the completer to autopick when
        // only one completion. This function is automatically reinvoked at
        // each keystroke with first_invocation = false
        var cur = this.editor.getCursor();
        var line = this.editor.getLine(cur.line);
        var pre_cursor = this.editor.getRange({
            line: cur.line,
            ch: cur.ch - 1
        }, cur);

        // we need to check that we are still on a word boundary
        // because while typing the completer is still reinvoking itself
        // so dismiss if we are on a "bad" caracter
        if (!this.reinvoke(pre_cursor) && !first_invocation) {
            this.close();
            return;
        }

        this.autopick = false;
        if (first_invocation) {
            this.autopick = true;
        }

        // We want a single cursor position.
        if (this.editor.somethingSelected()) {
            return;
        };

        // one kernel completion came back, finish_completing will be called with the results
        // we fork here and directly call finish completing if kernel is busy
        if (this.skip_kernel_completion == true) {
            this.finish_completing({
                'matches': [],
                matched_text: ""
            })
        } else {
            this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
        }
    };

    Completer.prototype.finish_completing = function (msg) {
        // let's build a function that wrap all that stuff into what is needed
        // for the new completer:
        var content = msg.content;
        var matched_text = content.matched_text;
        var matches = content.matches;

        var cur = this.editor.getCursor();
        var results = CodeMirror.contextHint(this.editor);
        var filterd_results = Array();
        //remove results from context completion
        //that are already in kernel completion
        for(var elm in results) {
            if(_existing_completion(results[elm]['str'], matches) == false)
            { filterd_results.push(results[elm]); }
        }

        // append the introspection result, in order, at at the beginning of
        // the table and compute the replacement range from current cursor
        // positon and matched_text length.
        for (var i = matches.length - 1; i >= 0; --i) {
            filterd_results.unshift({
                str: matches[i],
                type: "introspection",
                from: {
                    line: cur.line,
                    ch: cur.ch - matched_text.length
                },
                to: {
                    line: cur.line,
                    ch: cur.ch
                }
            });
        }

        // one the 2 sources results have been merge, deal with it
        this.raw_result = filterd_results;

        // if empty result return
        if (!this.raw_result || !this.raw_result.length) return;

        // When there is only one completion, use it directly.
        if (this.autopick == true && this.raw_result.length == 1) {
            this.insert(this.raw_result[0]);
            return;
        }

        if (this.raw_result.length == 1) {
            // test if first and only completion totally matches
            // what is typed, in this case dismiss
            var str = this.raw_result[0].str;
            var pre_cursor = this.editor.getRange({
                line: cur.line,
                ch: cur.ch - str.length
            }, cur);
            if (pre_cursor == str) {
                this.close();
                return;
            }
        }

        this.complete = $('<div/>').addClass('completions');
        this.complete.attr('id', 'complete');

        // Currently webkit doesn't use the size attr correctly. See:
        // https://code.google.com/p/chromium/issues/detail?id=4579
        this.sel = $('<select style="width: auto"/>')
            .attr('multiple', 'true')
            .attr('size', Math.min(10, this.raw_result.length));
        this.complete.append(this.sel);
        $('body').append(this.complete);

        // After everything is on the page, compute the postion.
        // We put it above the code if it is too close to the bottom of the page.
        var cur = this.editor.getCursor();
        cur.ch = cur.ch-matched_text.length;
        var pos = this.editor.cursorCoords(cur);
        var left = pos.left-3;
        var top;
        var cheight = this.complete.height();
        var wheight = $(window).height();
        if (pos.bottom+cheight+5 > wheight) {
            top = pos.top-cheight-4;
        } else {
            top = pos.bottom+1;
        }
        this.complete.css('left', left + 'px');
        this.complete.css('top', top + 'px');

        //build the container
        var that = this;
        this.sel.dblclick(function () {
            that.pick();
        });
        this.sel.blur(this.close);
        this.sel.keydown(function (event) {
            that.keydown(event);
        });

        this.build_gui_list(this.raw_result);

        this.sel.focus();
        IPython.keyboard_manager.disable();
        // Opera sometimes ignores focusing a freshly created node
        if (window.opera) setTimeout(function () {
            if (!this.done) this.sel.focus();
        }, 100);
        return true;
    }

    Completer.prototype.insert = function (completion) {
        this.editor.replaceRange(completion.str, completion.from, completion.to);
    }

    Completer.prototype.build_gui_list = function (completions) {
        for (var i = 0; i < completions.length; ++i) {
            var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
            this.sel.append(opt);
        }
        this.sel.children().first().attr('selected', 'true');
        this.sel.scrollTop(0);
    }

    Completer.prototype.close = function () {
        if (this.done) return;
        this.done = true;
        $('.completions').remove();
        IPython.keyboard_manager.enable();
    }

    Completer.prototype.pick = function () {
        this.insert(this.raw_result[this.sel[0].selectedIndex]);
        this.close();
        var that = this;
        setTimeout(function () {
            that.editor.focus();
        }, 50);
    }

    Completer.prototype.keydown = function (event) {
        var code = event.keyCode;
        var that = this;
        var special_key = false;

        // detect special keys like SHIFT,PGUP,...
        for( var _key in key ) {
            if (code == key[_key] ) {
                special_key = true;
            }
        };

        // Enter
        if (code == key.ENTER) {
            CodeMirror.e_stop(event);
            this.pick();
        }
        // Escape or backspace
        else if (code == key.ESC) {
            CodeMirror.e_stop(event);
            this.close();
            this.editor.focus();
        } else if (code == key.SPACE || code == key.BACKSPACE) {
            this.close();
            this.editor.focus();
        } else if (code == key.TAB) {
            //all the fastforwarding operation,
            //Check that shared start is not null which can append with prefixed completion
            // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
            // to erase py
            var sh = shared_start(this.raw_result, true);
            if (sh) {
                this.insert(sh);
            }
            this.close();
            CodeMirror.e_stop(event);
            this.editor.focus();
            //reinvoke self
            setTimeout(function () {
                that.carry_on_completion();
            }, 50);
        } else if (code == key.UPARROW || code == key.DOWNARROW) {
            // need to do that to be able to move the arrow
            // when on the first or last line ofo a code cell
            event.stopPropagation();
        } else if (special_key != true) {
            this.close();
            this.editor.focus();
            //we give focus to the editor immediately and call sell in 50 ms
            setTimeout(function () {
                that.carry_on_completion();
            }, 50);
        }
    }

    IPython.Completer = Completer;

    return IPython;
}(IPython));
<commit_msg>band-aid for completion<commit_after>// function completer.
//
// completer should be a class that takes an cell instance
var IPython = (function (IPython) {
    // that will prevent us from misspelling
    "use strict";

    // easier key mapping
    var key = IPython.utils.keycodes;

    function prepend_n_prc(str, n) {
        for( var i =0 ; i< n ; i++){
            str = '%'+str ;
        }
        return str;
    }

    function _existing_completion(item, completion_array){
        for( var c in completion_array ) {
            if(completion_array[c].trim().substr(-item.length) == item)
            { return true; }
        }
       return false;
    }

    // what is the common start of all completions
    function shared_start(B, drop_prct) {
        if (B.length == 1) {
            return B[0];
        }
        var A = [];
        var common;
        var min_lead_prct = 10;
        for (var i = 0; i < B.length; i++) {
            var str = B[i].str;
            var localmin = 0;
            if(drop_prct === true){
                while ( str.substr(0, 1) == '%') {
                    localmin = localmin+1;
                    str = str.substring(1);
                }
            }
            min_lead_prct = Math.min(min_lead_prct, localmin);
            A.push(str);
        }

        if (A.length > 1) {
            var tem1, tem2, s;
            A = A.slice(0).sort();
            tem1 = A[0];
            s = tem1.length;
            tem2 = A.pop();
            while (s && tem2.indexOf(tem1) == -1) {
                tem1 = tem1.substring(0, --s);
            }
            if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
                return {
                    str:prepend_n_prc('', min_lead_prct),
                    type: "computed",
                    from: B[0].from,
                    to: B[0].to
                    };
            }
            return {
                str: prepend_n_prc(tem1, min_lead_prct),
                type: "computed",
                from: B[0].from,
                to: B[0].to
            };
        }
        return null;
    }

    var Completer = function (cell) {
        this.cell = cell;
        this.editor = cell.code_mirror;
        var that = this;
        $([IPython.events]).on('status_busy.Kernel', function () {
            that.skip_kernel_completion = true;
        });
        $([IPython.events]).on('status_idle.Kernel', function () {
            that.skip_kernel_completion = false;
        });
    };

    Completer.prototype.startCompletion = function () {
        // call for a 'first' completion, that will set the editor and do some
        // special behaviour like autopicking if only one completion availlable
        //
        if (this.editor.somethingSelected()) return;
        this.done = false;
        // use to get focus back on opera
        this.carry_on_completion(true);
    };

    // easy access for julia to monkeypatch
    //
    Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;

    Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
        return Completer.reinvoke_re.test(pre_cursor);
    };

    /**
     *
     * pass true as parameter if this is the first invocation of the completer
     * this will prevent the completer to dissmiss itself if it is not on a
     * word boundary like pressing tab after a space, and make it autopick the
     * only choice if there is only one which prevent from popping the UI.  as
     * well as fast-forwarding the typing if all completion have a common
     * shared start
     **/
    Completer.prototype.carry_on_completion = function (first_invocation) {
        // Pass true as parameter if you want the completer to autopick when
        // only one completion. This function is automatically reinvoked at
        // each keystroke with first_invocation = false
        var cur = this.editor.getCursor();
        var line = this.editor.getLine(cur.line);
        var pre_cursor = this.editor.getRange({
            line: cur.line,
            ch: cur.ch - 1
        }, cur);

        // we need to check that we are still on a word boundary
        // because while typing the completer is still reinvoking itself
        // so dismiss if we are on a "bad" caracter
        if (!this.reinvoke(pre_cursor) && !first_invocation) {
            this.close();
            return;
        }

        this.autopick = false;
        if (first_invocation) {
            this.autopick = true;
        }

        // We want a single cursor position.
        if (this.editor.somethingSelected()) {
            return;
        }

        // one kernel completion came back, finish_completing will be called with the results
        // we fork here and directly call finish completing if kernel is busy
        if (this.skip_kernel_completion) {
            this.finish_completing({
                'matches': [],
                matched_text: ""
            });
        } else {
            this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
        }
    };

    Completer.prototype.finish_completing = function (msg) {
        // let's build a function that wrap all that stuff into what is needed
        // for the new completer:
        var content = msg.content;
        var matched_text = content.matched_text;
        var matches = content.matches;

        var cur = this.editor.getCursor();
        var results = CodeMirror.contextHint(this.editor);
        var filtered_results = [];
        //remove results from context completion
        //that are already in kernel completion
        for (var elm in results) {
            if (!_existing_completion(results[elm].str, matches)) {
                filtered_results.push(results[elm]);
            }
        }

        // append the introspection result, in order, at at the beginning of
        // the table and compute the replacement range from current cursor
        // positon and matched_text length.
        for (var i = matches.length - 1; i >= 0; --i) {
            filtered_results.unshift({
                str: matches[i],
                type: "introspection",
                from: {
                    line: cur.line,
                    ch: cur.ch - matched_text.length
                },
                to: {
                    line: cur.line,
                    ch: cur.ch
                }
            });
        }

        // one the 2 sources results have been merge, deal with it
        this.raw_result = filtered_results;

        // if empty result return
        if (!this.raw_result || !this.raw_result.length) return;

        // When there is only one completion, use it directly.
        if (this.autopick && this.raw_result.length == 1) {
            this.insert(this.raw_result[0]);
            return;
        }

        if (this.raw_result.length == 1) {
            // test if first and only completion totally matches
            // what is typed, in this case dismiss
            var str = this.raw_result[0].str;
            var pre_cursor = this.editor.getRange({
                line: cur.line,
                ch: cur.ch - str.length
            }, cur);
            if (pre_cursor == str) {
                this.close();
                return;
            }
        }

        this.complete = $('<div/>').addClass('completions');
        this.complete.attr('id', 'complete');

        // Currently webkit doesn't use the size attr correctly. See:
        // https://code.google.com/p/chromium/issues/detail?id=4579
        this.sel = $('<select style="width: auto"/>')
            .attr('multiple', 'true')
            .attr('size', Math.min(10, this.raw_result.length));
        this.complete.append(this.sel);
        $('body').append(this.complete);

        // After everything is on the page, compute the postion.
        // We put it above the code if it is too close to the bottom of the page.
        cur.ch = cur.ch-matched_text.length;
        var pos = this.editor.cursorCoords(cur);
        var left = pos.left-3;
        var top;
        var cheight = this.complete.height();
        var wheight = $(window).height();
        if (pos.bottom+cheight+5 > wheight) {
            top = pos.top-cheight-4;
        } else {
            top = pos.bottom+1;
        }
        this.complete.css('left', left + 'px');
        this.complete.css('top', top + 'px');

        //build the container
        var that = this;
        this.sel.dblclick(function () {
            that.pick();
        });
        this.sel.blur(this.close);
        this.sel.keydown(function (event) {
            that.keydown(event);
        });
        this.sel.keypress(function (event) {
            that.keypress(event);
        });

        this.build_gui_list(this.raw_result);

        this.sel.focus();
        IPython.keyboard_manager.disable();
        // Opera sometimes ignores focusing a freshly created node
        if (window.opera) setTimeout(function () {
            if (!this.done) this.sel.focus();
        }, 100);
        return true;
    };

    Completer.prototype.insert = function (completion) {
        this.editor.replaceRange(completion.str, completion.from, completion.to);
    };

    Completer.prototype.build_gui_list = function (completions) {
        for (var i = 0; i < completions.length; ++i) {
            var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
            this.sel.append(opt);
        }
        this.sel.children().first().attr('selected', 'true');
        this.sel.scrollTop(0);
    };

    Completer.prototype.close = function () {
        if (this.done) return;
        this.done = true;
        $('.completions').remove();
        IPython.keyboard_manager.enable();
    };

    Completer.prototype.pick = function () {
        this.insert(this.raw_result[this.sel[0].selectedIndex]);
        this.close();
        var that = this;
        setTimeout(function () {
            that.editor.focus();
        }, 50);
    };

    Completer.prototype.keydown = function (event) {
        var code = event.keyCode;
        var that = this;
        var special_key = false;

        // detect special keys like SHIFT,PGUP,...
        for( var _key in key ) {
            if (code == key[_key] ) {
                special_key = true;
            }
        }

        // Enter
        if (code == key.ENTER) {
            CodeMirror.e_stop(event);
            this.pick();
        }
        // Escape or backspace
        else if (code == key.ESC) {
            CodeMirror.e_stop(event);
            this.close();
            this.editor.focus();
        } else if (code == key.BACKSPACE) {
            this.close();
            this.editor.focus();
        } else if (code == key.TAB) {
            //all the fastforwarding operation,
            //Check that shared start is not null which can append with prefixed completion
            // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
            // to erase py
            var sh = shared_start(this.raw_result, true);
            if (sh) {
                this.insert(sh);
            }
            this.close();
            CodeMirror.e_stop(event);
            this.editor.focus();
            //reinvoke self
            setTimeout(function () {
                that.carry_on_completion();
            }, 50);
        } else if (code == key.UPARROW || code == key.DOWNARROW) {
            // need to do that to be able to move the arrow
            // when on the first or last line ofo a code cell
            event.stopPropagation();
        }
    };

    Completer.prototype.keypress = function (event) {
        // FIXME: This is a band-aid.
        // on keypress, trigger insertion of a single character.
        // This simulates the old behavior of completion as you type,
        // before events were disconnected and CodeMirror stopped
        // receiving events while the completer is focused.

        var that = this;
        var code = event.keyCode;

        // don't handle keypress if it's not a character (arrows on FF)
        // or ENTER/TAB
        if (event.charCode === 0 ||
            code == key.ENTER ||
            code == key.TAB
        ) return;

        var cur = this.editor.getCursor();
        var completion = {
            str: String.fromCharCode(event.which),
            type: "introspection",
            from: cur,
            to: cur,
        };
        this.insert(completion);

        this.close();
        this.editor.focus();
        setTimeout(function () {
            that.carry_on_completion();
        }, 50);
    };

    IPython.Completer = Completer;

    return IPython;
}(IPython));
<|endoftext|>


<commit_before>var assert = require('assert'),
  express = require('express'),
  app = express(),
  request = require('supertest'),
  controller = require('../../api/digestController'),
  validator = require('validator');

controller.init(app);

describe('digestController', function () {
  describe('when creating a digest', function () {
    it('should be to a valid uri', function(done) {
      request(app)
            .post('/api/digest', 'myfirstdigest')
            .end(function (err, res) {
              assert.equal(validator.isURL(res.body.digestUrl), true);
              done();
      });
    });

    it('should have a valid uuid as an identifier', function(done) {
      request(app)
            .post('/api/digest', 'myfirstdigest')
            .end(function (err, res) {
              var digestUrlParts = res.body.digestUrl.split('/');
              var id = digestUrlParts[digestUrlParts.length - 1];
              assert.equal(validator.isUUID(id), true);
              done();
      });
    })

  });
});

// {
//   "digestUrl": "http://host/api/digest/1f1aa47629c44116a3ca08a9bb911309",
//   "_links": [
//     {
//     "href": "http://host/api/digest/1f1aa47629c44116a3ca08a9bb911309/inbox/new",
//     "rel": "inbox",
//     "name": "Navigate to form for creating an inbox for a repository",
//     "method": "GET"
//     }
//   ]
// }
<commit_msg>bring in chai, and alphabetize variable setup for the test file.<commit_after>var app = express(),
  assert = require('assert'),
  chai = require('chai');
  controller = require('../../api/digestController'),
  express = require('express'),
  request = require('supertest'),
  validator = require('validator'),

controller.init(app);

describe('digestController', function () {
  describe('when creating a digest', function () {
    it('should be to a valid uri', function(done) {
      request(app)
            .post('/api/digest', 'myfirstdigest')
            .end(function (err, res) {
              assert.equal(validator.isURL(res.body.digestUrl), true);
              done();
      });
    });

    it('should have a valid uuid as an identifier', function(done) {
      request(app)
            .post('/api/digest', 'myfirstdigest')
            .end(function (err, res) {
              var digestUrlParts = res.body.digestUrl.split('/');
              var id = digestUrlParts[digestUrlParts.length - 1];
              assert.equal(validator.isUUID(id), true);
              done();
      });
    })

  });
});

// {
//   "digestUrl": "http://host/api/digest/1f1aa47629c44116a3ca08a9bb911309",
//   "_links": [
//     {
//     "href": "http://host/api/digest/1f1aa47629c44116a3ca08a9bb911309/inbox/new",
//     "rel": "inbox",
//     "name": "Navigate to form for creating an inbox for a repository",
//     "method": "GET"
//     }
//   ]
// }
<|endoftext|>


<commit_before>import {CONTRACT_SAVE_SUCCESS, CONTRACT_SAVE_FAILURE} from ./ContractActions

const initialState = { data: [] };

export default function(state = initialState, action) {
    switch(action.type) {
        case CONTRACT_SAVE_SUCCESS :
            return data: action.text;
        case CONTRACT_SAVE_FAILURE :
            return data: action.text;

     default:
         return state;
  }
}
<commit_msg>function is now const and it is exported<commit_after>import {CONTRACT_SAVE_SUCCESS, CONTRACT_SAVE_FAILURE} from "./ContractActions"

const initialState = { data: [] };

const contractSave = (state = initialState, action) => {
    switch(action.type) {
        case CONTRACT_SAVE_SUCCESS :
            return {
              data: action.text,
            };
        case CONTRACT_SAVE_FAILURE :
            return {
              data: action.text,
            };
     default:
         return state;
  }
};

export default contractSave;
<|endoftext|>


<commit_before>'use strict';
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var fs = require('fs');
var path = require('path');
var questionsCollection = require('./questions');
var util = require('./util');
var _ = require('lodash');
var asciiArt = fs.readFileSync(path.join(__dirname, 'asciiArt.txt'),'utf8');

module.exports = yeoman.Base.extend({
  constructor: function(){
    yeoman.Base.apply(this, arguments);
    this.props = {};
    this.argument('appName', { type: String, required: false, desc: 'Project Name', defaults: null});
    this.props.appName = this.appName;
    this.log(chalk.blue(asciiArt));
  },
  prompting: function () {
    var done = this.async();
    var that = this;
    function askInitQuestions(){
      var questions = _.map(questionsCollection.initQuestions, function(question){
        var defaultValue = question.default;
        if(_.isString(defaultValue) && defaultValue.indexOf('appName') != -1){
          defaultValue = question.default.replace('appName', that.props.appName);
        }
        return _.extend(question, { default:defaultValue });
      });
      that.prompt(questions).then(function(answers){
        that.props = _.extend(that.props, answers);
        done();
      });
    }
    if(this.props.appName){
      askInitQuestions();
    }else{
      this.prompt(questionsCollection.appNameQuestion).then(function(answers){
        that.props.appName = answers.appName;
        askInitQuestions();
      });
    }
  },

  writing: function () {
    var templateFiles = ['*/app/views/index.scala.html', '*/build.sbt', '*/project/Config.scala', '*/conf/application.conf'];
    var globPattern = "+(" + templateFiles.join('|') + ")";
    var db = this.props.DatabaseType;
    var dbName = this.props.DatabaseName;
    this.fs.copy(this.sourceRoot(), this.destinationRoot(), { ignore:globPattern });
    this.fs.copy(this.templatePath('.*'), this.destinationRoot());
    //copy build.sbt
    var appName = this.props.appName;
    this.fs.copyTpl(this.templatePath('build.sbt'), this.destinationPath('build.sbt'), { appName: appName });
    //copy package.json and bower.json
    this.fs.copyTpl(this.templatePath('package.json'), this.destinationPath('package.json'), { appName: appName });
    this.fs.copyTpl(this.templatePath('bower.json'), this.destinationPath('bower.json'), { appName: appName });
    //copy index.html
    this.fs.copyTpl(this.templatePath('app/views/index.scala.html'), this.destinationPath('app/views/index.scala.html'), { appName: appName });
    //copy Config.scala
    var organization = this.props.OrganizationName;
    var SBTDBDriverDependency = util.getSBTDBDriverDependency(db);
    this.fs.copyTpl(this.templatePath('project/Config.scala'), this.destinationPath('project/Config.scala'), { organization: organization, SBTDBDriverDependency: SBTDBDriverDependency });
    //copy app.conf
    var slickDBDriver = util.getSlickDBDriver(db);
    var DBDriver = util.getDBDriver(db);
    var DBConnectionUrl = util.generateDBConnectionString(db, dbName);
    this.fs.copyTpl(this.templatePath('conf/application.conf'), this.destinationPath('conf/application.conf'), { slickDBDriver: slickDBDriver, DBDriver: DBDriver, DBConnectionUrl: DBConnectionUrl });
  },

  install: function () {
    this.installDependencies();
  },

  end: function(){
    this.log("it was good seeing you, have a nice day!");
  }
});
<commit_msg>changed color of bye message<commit_after>'use strict';
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var fs = require('fs');
var path = require('path');
var questionsCollection = require('./questions');
var util = require('./util');
var _ = require('lodash');
var asciiArt = fs.readFileSync(path.join(__dirname, 'asciiArt.txt'),'utf8');

module.exports = yeoman.Base.extend({
  constructor: function(){
    yeoman.Base.apply(this, arguments);
    this.props = {};
    this.argument('appName', { type: String, required: false, desc: 'Project Name', defaults: null});
    this.props.appName = this.appName;
    this.log(chalk.blue(asciiArt));
  },
  prompting: function () {
    var done = this.async();
    var that = this;
    function askInitQuestions(){
      var questions = _.map(questionsCollection.initQuestions, function(question){
        var defaultValue = question.default;
        if(_.isString(defaultValue) && defaultValue.indexOf('appName') != -1){
          defaultValue = question.default.replace('appName', that.props.appName);
        }
        return _.extend(question, { default:defaultValue });
      });
      that.prompt(questions).then(function(answers){
        that.props = _.extend(that.props, answers);
        done();
      });
    }
    if(this.props.appName){
      askInitQuestions();
    }else{
      this.prompt(questionsCollection.appNameQuestion).then(function(answers){
        that.props.appName = answers.appName;
        askInitQuestions();
      });
    }
  },

  writing: function () {
    var templateFiles = ['*/app/views/index.scala.html', '*/build.sbt', '*/project/Config.scala', '*/conf/application.conf'];
    var globPattern = "+(" + templateFiles.join('|') + ")";
    var db = this.props.DatabaseType;
    var dbName = this.props.DatabaseName;
    this.fs.copy(this.sourceRoot(), this.destinationRoot(), { ignore:globPattern });
    this.fs.copy(this.templatePath('.*'), this.destinationRoot());
    //copy build.sbt
    var appName = this.props.appName;
    this.fs.copyTpl(this.templatePath('build.sbt'), this.destinationPath('build.sbt'), { appName: appName });
    //copy package.json and bower.json
    this.fs.copyTpl(this.templatePath('package.json'), this.destinationPath('package.json'), { appName: appName });
    this.fs.copyTpl(this.templatePath('bower.json'), this.destinationPath('bower.json'), { appName: appName });
    //copy index.html
    this.fs.copyTpl(this.templatePath('app/views/index.scala.html'), this.destinationPath('app/views/index.scala.html'), { appName: appName });
    //copy Config.scala
    var organization = this.props.OrganizationName;
    var SBTDBDriverDependency = util.getSBTDBDriverDependency(db);
    this.fs.copyTpl(this.templatePath('project/Config.scala'), this.destinationPath('project/Config.scala'), { organization: organization, SBTDBDriverDependency: SBTDBDriverDependency });
    //copy app.conf
    var slickDBDriver = util.getSlickDBDriver(db);
    var DBDriver = util.getDBDriver(db);
    var DBConnectionUrl = util.generateDBConnectionString(db, dbName);
    this.fs.copyTpl(this.templatePath('conf/application.conf'), this.destinationPath('conf/application.conf'), { slickDBDriver: slickDBDriver, DBDriver: DBDriver, DBConnectionUrl: DBConnectionUrl });
  },

  install: function () {
    this.installDependencies();
  },

  end: function(){
    this.log(chalk.yellow("it was good seeing you, have a nice day!"));
  }
});
<|endoftext|>


<commit_before><commit_msg>use forEach and map, screw IE8<commit_after><|endoftext|>


<commit_before>const assignManager = {
  CLOSEABLE_WINDOWS: new Set([
    "about:startpage",
    "about:newtab",
    "about:home",
    "about:blank"
  ]),
  MENU_ASSIGN_ID: "open-in-this-container",
  MENU_REMOVE_ID: "remove-open-in-this-container",
  storageArea: {
    area: browser.storage.local,

    getSiteStoreKey(pageUrl) {
      const url = new window.URL(pageUrl);
      const storagePrefix = "siteContainerMap@@_";
      return `${storagePrefix}${url.hostname}`;
    },

    get(pageUrl) {
      const siteStoreKey = this.getSiteStoreKey(pageUrl);
      return new Promise((resolve, reject) => {
        this.area.get([siteStoreKey]).then((storageResponse) => {
          if (storageResponse && siteStoreKey in storageResponse) {
            resolve(storageResponse[siteStoreKey]);
          }
          resolve(null);
        }).catch((e) => {
          reject(e);
        });
      });
    },

    set(pageUrl, data) {
      const siteStoreKey = this.getSiteStoreKey(pageUrl);
      return this.area.set({
        [siteStoreKey]: data
      });
    },

    remove(pageUrl) {
      const siteStoreKey = this.getSiteStoreKey(pageUrl);
      return this.area.remove([siteStoreKey]);
    },

    deleteContainer(userContextId) {
      const removeKeys = [];
      this.area.get().then((siteConfigs) => {
        Object.keys(siteConfigs).forEach((key) => {
          // For some reason this is stored as string... lets check them both as that
          if (String(siteConfigs[key].userContextId) === String(userContextId)) {
            removeKeys.push(key);
          }
        });
        this.area.remove(removeKeys);
      }).catch((e) => {
        throw e;
      });
    }
  },

  init() {
    browser.runtime.onMessage.addListener((neverAskMessage) => {
      const pageUrl = neverAskMessage.pageUrl;
      if (neverAskMessage.neverAsk === true) {
        // If we have existing data and for some reason it hasn't been deleted etc lets update it
        this.storageArea.get(pageUrl).then((siteSettings) => {
          if (siteSettings) {
            siteSettings.neverAsk = true;
            this.storageArea.set(pageUrl, siteSettings);
          }
        }).catch((e) => {
          throw e;
        });
      }
    });

    browser.contextMenus.onClicked.addListener((info, tab) => {
      const userContextId = this.getUserContextIdFromCookieStore(tab);
      // Mapping ${URL(info.pageUrl).hostname} to ${userContextId}
      if (userContextId) {
        let actionName;
        let storageAction;
        if (info.menuItemId === this.MENU_ASSIGN_ID) {
          actionName = "added";
          storageAction = this.storageArea.set(info.pageUrl, {
            userContextId,
            neverAsk: false
          });
        } else {
          actionName = "removed";
          storageAction = this.storageArea.remove(info.pageUrl);
        }
        storageAction.then(() => {
          browser.notifications.create({
            type: "basic",
            title: "Containers",
            message: `Successfully ${actionName} site to always open in this container`,
            iconUrl: browser.extension.getURL("/img/onboarding-1.png")
          });
          browser.runtime.sendMessage({
            method: "sendTelemetryPayload",
            event: `${actionName}-container-assignment`,
            userContextId: userContextId,
          });
          this.calculateContextMenu(tab);
        }).catch((e) => {
          throw e;
        });
      }
    });

    // Before a request is handled by the browser we decide if we should route through a different container
    browser.webRequest.onBeforeRequest.addListener((options) => {
      if (options.frameId !== 0 || options.tabId === -1) {
        return {};
      }
      return Promise.all([
        browser.tabs.get(options.tabId),
        this.storageArea.get(options.url)
      ]).then(([tab, siteSettings]) => {
        const userContextId = this.getUserContextIdFromCookieStore(tab);
        if (!siteSettings
            || userContextId === siteSettings.userContextId
            || tab.incognito) {
          return {};
        }

        this.reloadPageInContainer(options.url, siteSettings.userContextId, tab.index + 1, siteSettings.neverAsk);
        this.calculateContextMenu(tab);

        /* Removal of existing tabs:
           We aim to open the new assigned container tab / warning prompt in it's own tab:
             - As the history won't span from one container to another it seems most sane to not try and reopen a tab on history.back()
             - When users open a new tab themselves we want to make sure we don't end up with three tabs as per: https://github.com/mozilla/testpilot-containers/issues/421
           If we are coming from an internal url that are used for the new tab page (CLOSEABLE_WINDOWS), we can safely close as user is unlikely losing history
           Detecting redirects on "new tab" opening actions is pretty hard as we don't get tab history:
           - Redirects happen from Short URLs and tracking links that act as a gateway
           - Extensions don't provide a way to history crawl for tabs, we could inject content scripts to do this
               however they don't run on about:blank so this would likely be just as hacky.
           We capture the time the tab was created and close if it was within the timeout to try to capture pages which haven't had user interaction or history.
        */
        if (this.CLOSEABLE_WINDOWS.has(tab.url)
            || (messageHandler.lastCreatedTab
            && messageHandler.lastCreatedTab.id === tab.id)) {
          browser.tabs.remove(tab.id);
        }
        return {
          cancel: true,
        };
      }).catch((e) => {
        throw e;
      });
    },{urls: ["<all_urls>"], types: ["main_frame"]}, ["blocking"]);
  },

  deleteContainer(userContextId) {
    this.storageArea.deleteContainer(userContextId);
  },

  getUserContextIdFromCookieStore(tab) {
    if (!("cookieStoreId" in tab)) {
      return false;
    }
    const cookieStore = tab.cookieStoreId;
    const container = cookieStore.replace("firefox-container-", "");
    if (container !== cookieStore) {
      return container;
    }
    return false;
  },

  isTabPermittedAssign(tab) {
    // Ensure we are not an important about url
    // Ensure we are not in incognito mode
    const url = new URL(tab.url);
    if (url.protocol === "about:"
        || tab.incognito) {
      return false;
    }
    return true;
  },

  calculateContextMenu(tab) {
    // There is a focus issue in this menu where if you change window with a context menu click
    // you get the wrong menu display because of async
    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1215376#c16
    // We also can't change for always private mode
    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1352102
    const cookieStore = this.getUserContextIdFromCookieStore(tab);
    browser.contextMenus.remove(this.MENU_ASSIGN_ID);
    browser.contextMenus.remove(this.MENU_REMOVE_ID);
    // Ensure we have a cookieStore to assign to
    if (cookieStore
        && this.isTabPermittedAssign(tab)) {
      this.storageArea.get(tab.url).then((siteSettings) => {
        // ✓ This is to mitigate https://bugzilla.mozilla.org/show_bug.cgi?id=1351418
        let prefix = "   "; // Alignment of non breaking space, unknown why this requires so many spaces to align with the tick
        let menuId = this.MENU_ASSIGN_ID;
        if (siteSettings) {
          prefix = "✓";
          menuId = this.MENU_REMOVE_ID;
        }
        browser.contextMenus.create({
          id: menuId,
          title: `${prefix} Always Open in This Container`,
          checked: true,
          contexts: ["all"],
        });
      }).catch((e) => {
        throw e;
      });
    }
  },

  reloadPageInContainer(url, userContextId, index, neverAsk = false) {
    const loadPage = browser.extension.getURL("confirm-page.html");
    // If the user has explicitly checked "Never Ask Again" on the warning page we will send them straight there
    if (neverAsk) {
      browser.tabs.create({url, cookieStoreId: `firefox-container-${userContextId}`, index});
      browser.runtime.sendMessage({
        method: "sendTelemetryPayload",
        event: "auto-reload-page-in-container",
        userContextId: userContextId,
      });
    } else {
      browser.runtime.sendMessage({
        method: "sendTelemetryPayload",
        event: "prompt-to-reload-page-in-container",
        userContextId: userContextId,
      });
      const confirmUrl = `${loadPage}?url=${url}`;
      browser.tabs.create({url: confirmUrl, cookieStoreId: `firefox-container-${userContextId}`, index}).then(() => {
        // We don't want to sync this URL ever nor clutter the users history
        browser.history.deleteUrl({url: confirmUrl});
      }).catch((e) => {
        throw e;
      });
    }
  }
};

const messageHandler = {
  // After the timer completes we assume it's a tab the user meant to keep open
  // We use this to catch redirected tabs that have just opened
  // If this were in platform we would change how the tab opens based on "new tab" link navigations such as ctrl+click
  LAST_CREATED_TAB_TIMER: 2000,

  init() {
    // Handles messages from index.js
    const port = browser.runtime.connect();
    port.onMessage.addListener(m => {
      switch (m.type) {
      case "lightweight-theme-changed":
        themeManager.update(m.message);
        break;
      case "delete-container":
        assignManager.deleteContainer(m.message.userContextId);
        break;
      default:
        throw new Error(`Unhandled message type: ${m.message}`);
      }
    });

    browser.tabs.onCreated.addListener((tab) => {
      // This works at capturing the tabs as they are created
      // However we need onFocusChanged and onActivated to capture the initial tab
      if (tab.id === -1) {
        return {};
      }
      tabPageCounter.initTabCounter(tab);
    });

    browser.tabs.onRemoved.addListener((tabId) => {
      if (tabId === -1) {
        return {};
      }
      tabPageCounter.sendTabCountAndDelete(tabId);
    });

    browser.tabs.onActivated.addListener((info) => {
      browser.tabs.get(info.tabId).then((tab) => {
        tabPageCounter.initTabCounter(tab);
        assignManager.calculateContextMenu(tab);
      }).catch((e) => {
        throw e;
      });
    });

    browser.windows.onFocusChanged.addListener((windowId) => {
      browser.tabs.query({active: true, windowId}).then((tabs) => {
        if (tabs && tabs[0]) {
          tabPageCounter.initTabCounter(tabs[0]);
          assignManager.calculateContextMenu(tabs[0]);
        }
      }).catch((e) => {
        throw e;
      });
    });

    browser.webRequest.onCompleted.addListener((details) => {
      if (details.frameId !== 0 || details.tabId === -1) {
        return {};
      }

      browser.tabs.get(details.tabId).then((tab) => {
        tabPageCounter.incrementTabCount(tab);
        assignManager.calculateContextMenu(tab);
      }).catch((e) => {
        throw e;
      });
    }, {urls: ["<all_urls>"], types: ["main_frame"]});

    // lets remember the last tab created so we can close it if it looks like a redirect
    browser.tabs.onCreated.addListener((details) => {
      this.lastCreatedTab = details;
      setTimeout(() => {
        this.lastCreatedTab = null;
      }, this.LAST_CREATED_TAB_TIMER);
    });

  }
};

const themeManager = {
  existingTheme: null,
  init() {
    this.check();
  },
  setPopupIcon(theme) {
    let icons = {
      16: "img/container-site-d-24.png",
      32: "img/container-site-d-48.png"
    };
    if (theme === "firefox-compact-dark@mozilla.org") {
      icons = {
        16: "img/container-site-w-24.png",
        32: "img/container-site-w-48.png"
      };
    }
    browser.browserAction.setIcon({
      path: icons
    });
  },
  check() {
    browser.runtime.sendMessage({
      method: "getTheme"
    }).then((theme) => {
      this.update(theme);
    }).catch(() => {
      throw new Error("Unable to get theme");
    });
  },
  update(theme) {
    if (this.existingTheme !== theme) {
      this.setPopupIcon(theme);
      this.existingTheme = theme;
    }
  }
};

const tabPageCounter = {
  counter: {},

  initTabCounter(tab) {
    if (tab.id in this.counter) {
      return;
    }
    this.counter[tab.id] = {
      "cookieStoreId": tab.cookieStoreId,
      "pageRequests": 0
    };
  },

  sendTabCountAndDelete(tabId) {
    browser.runtime.sendMessage({
      method: "sendTelemetryPayload",
      event: "page-requests-completed-per-tab",
      userContextId: this.counter[tabId].cookieStoreId,
      pageRequestCount: this.counter[tabId].pageRequests
    });
    delete this.counter[tabId];
  },

  incrementTabCount(tab) {
    this.counter[tab.id].pageRequests++;
  }
};

assignManager.init();
themeManager.init();
// Lets do this last as theme manager did a check before connecting before
messageHandler.init();

browser.runtime.sendMessage({
  method: "getPreference",
  pref: "browser.privatebrowsing.autostart"
}).then(pbAutoStart => {

  // We don't want to disable the addon if we are in auto private-browsing.
  if (!pbAutoStart) {
    browser.tabs.onCreated.addListener(tab => {
      if (tab.incognito) {
        disableAddon(tab.id);
      }
    });

    browser.tabs.query({}).then(tabs => {
      for (let tab of tabs) { // eslint-disable-line prefer-const
        if (tab.incognito) {
          disableAddon(tab.id);
        }
      }
    }).catch(() => {});
  }
}).catch(() => {});

function disableAddon(tabId) {
  browser.browserAction.disable(tabId);
  browser.browserAction.setTitle({ tabId, title: "Containers disabled in Private Browsing Mode" });
}
<commit_msg>Adding in context menu to tab context menu. Fixes #424.<commit_after>const assignManager = {
  CLOSEABLE_WINDOWS: new Set([
    "about:startpage",
    "about:newtab",
    "about:home",
    "about:blank"
  ]),
  MENU_ASSIGN_ID: "open-in-this-container",
  MENU_REMOVE_ID: "remove-open-in-this-container",
  storageArea: {
    area: browser.storage.local,

    getSiteStoreKey(pageUrl) {
      const url = new window.URL(pageUrl);
      const storagePrefix = "siteContainerMap@@_";
      return `${storagePrefix}${url.hostname}`;
    },

    get(pageUrl) {
      const siteStoreKey = this.getSiteStoreKey(pageUrl);
      return new Promise((resolve, reject) => {
        this.area.get([siteStoreKey]).then((storageResponse) => {
          if (storageResponse && siteStoreKey in storageResponse) {
            resolve(storageResponse[siteStoreKey]);
          }
          resolve(null);
        }).catch((e) => {
          reject(e);
        });
      });
    },

    set(pageUrl, data) {
      const siteStoreKey = this.getSiteStoreKey(pageUrl);
      return this.area.set({
        [siteStoreKey]: data
      });
    },

    remove(pageUrl) {
      const siteStoreKey = this.getSiteStoreKey(pageUrl);
      return this.area.remove([siteStoreKey]);
    },

    deleteContainer(userContextId) {
      const removeKeys = [];
      this.area.get().then((siteConfigs) => {
        Object.keys(siteConfigs).forEach((key) => {
          // For some reason this is stored as string... lets check them both as that
          if (String(siteConfigs[key].userContextId) === String(userContextId)) {
            removeKeys.push(key);
          }
        });
        this.area.remove(removeKeys);
      }).catch((e) => {
        throw e;
      });
    }
  },

  init() {
    browser.runtime.onMessage.addListener((neverAskMessage) => {
      const pageUrl = neverAskMessage.pageUrl;
      if (neverAskMessage.neverAsk === true) {
        // If we have existing data and for some reason it hasn't been deleted etc lets update it
        this.storageArea.get(pageUrl).then((siteSettings) => {
          if (siteSettings) {
            siteSettings.neverAsk = true;
            this.storageArea.set(pageUrl, siteSettings);
          }
        }).catch((e) => {
          throw e;
        });
      }
    });

    browser.contextMenus.onClicked.addListener((info, tab) => {
      const userContextId = this.getUserContextIdFromCookieStore(tab);
      // Mapping ${URL(info.pageUrl).hostname} to ${userContextId}
      if (userContextId) {
        let actionName;
        let storageAction;
        if (info.menuItemId === this.MENU_ASSIGN_ID) {
          actionName = "added";
          storageAction = this.storageArea.set(info.pageUrl, {
            userContextId,
            neverAsk: false
          });
        } else {
          actionName = "removed";
          storageAction = this.storageArea.remove(info.pageUrl);
        }
        storageAction.then(() => {
          browser.notifications.create({
            type: "basic",
            title: "Containers",
            message: `Successfully ${actionName} site to always open in this container`,
            iconUrl: browser.extension.getURL("/img/onboarding-1.png")
          });
          browser.runtime.sendMessage({
            method: "sendTelemetryPayload",
            event: `${actionName}-container-assignment`,
            userContextId: userContextId,
          });
          this.calculateContextMenu(tab);
        }).catch((e) => {
          throw e;
        });
      }
    });

    // Before a request is handled by the browser we decide if we should route through a different container
    browser.webRequest.onBeforeRequest.addListener((options) => {
      if (options.frameId !== 0 || options.tabId === -1) {
        return {};
      }
      return Promise.all([
        browser.tabs.get(options.tabId),
        this.storageArea.get(options.url)
      ]).then(([tab, siteSettings]) => {
        const userContextId = this.getUserContextIdFromCookieStore(tab);
        if (!siteSettings
            || userContextId === siteSettings.userContextId
            || tab.incognito) {
          return {};
        }

        this.reloadPageInContainer(options.url, siteSettings.userContextId, tab.index + 1, siteSettings.neverAsk);
        this.calculateContextMenu(tab);

        /* Removal of existing tabs:
           We aim to open the new assigned container tab / warning prompt in it's own tab:
             - As the history won't span from one container to another it seems most sane to not try and reopen a tab on history.back()
             - When users open a new tab themselves we want to make sure we don't end up with three tabs as per: https://github.com/mozilla/testpilot-containers/issues/421
           If we are coming from an internal url that are used for the new tab page (CLOSEABLE_WINDOWS), we can safely close as user is unlikely losing history
           Detecting redirects on "new tab" opening actions is pretty hard as we don't get tab history:
           - Redirects happen from Short URLs and tracking links that act as a gateway
           - Extensions don't provide a way to history crawl for tabs, we could inject content scripts to do this
               however they don't run on about:blank so this would likely be just as hacky.
           We capture the time the tab was created and close if it was within the timeout to try to capture pages which haven't had user interaction or history.
        */
        if (this.CLOSEABLE_WINDOWS.has(tab.url)
            || (messageHandler.lastCreatedTab
            && messageHandler.lastCreatedTab.id === tab.id)) {
          browser.tabs.remove(tab.id);
        }
        return {
          cancel: true,
        };
      }).catch((e) => {
        throw e;
      });
    },{urls: ["<all_urls>"], types: ["main_frame"]}, ["blocking"]);
  },

  deleteContainer(userContextId) {
    this.storageArea.deleteContainer(userContextId);
  },

  getUserContextIdFromCookieStore(tab) {
    if (!("cookieStoreId" in tab)) {
      return false;
    }
    const cookieStore = tab.cookieStoreId;
    const container = cookieStore.replace("firefox-container-", "");
    if (container !== cookieStore) {
      return container;
    }
    return false;
  },

  isTabPermittedAssign(tab) {
    // Ensure we are not an important about url
    // Ensure we are not in incognito mode
    const url = new URL(tab.url);
    if (url.protocol === "about:"
        || tab.incognito) {
      return false;
    }
    return true;
  },

  calculateContextMenu(tab) {
    // There is a focus issue in this menu where if you change window with a context menu click
    // you get the wrong menu display because of async
    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1215376#c16
    // We also can't change for always private mode
    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1352102
    const cookieStore = this.getUserContextIdFromCookieStore(tab);
    browser.contextMenus.remove(this.MENU_ASSIGN_ID);
    browser.contextMenus.remove(this.MENU_REMOVE_ID);
    // Ensure we have a cookieStore to assign to
    if (cookieStore
        && this.isTabPermittedAssign(tab)) {
      this.storageArea.get(tab.url).then((siteSettings) => {
        // ✓ This is to mitigate https://bugzilla.mozilla.org/show_bug.cgi?id=1351418
        let prefix = "   "; // Alignment of non breaking space, unknown why this requires so many spaces to align with the tick
        let menuId = this.MENU_ASSIGN_ID;
        if (siteSettings) {
          prefix = "✓";
          menuId = this.MENU_REMOVE_ID;
        }
        browser.contextMenus.create({
          id: menuId,
          title: `${prefix} Always Open in This Container`,
          checked: true,
          contexts: ["all", "tab"],
        });
      }).catch((e) => {
        throw e;
      });
    }
  },

  reloadPageInContainer(url, userContextId, index, neverAsk = false) {
    const loadPage = browser.extension.getURL("confirm-page.html");
    // If the user has explicitly checked "Never Ask Again" on the warning page we will send them straight there
    if (neverAsk) {
      browser.tabs.create({url, cookieStoreId: `firefox-container-${userContextId}`, index});
      browser.runtime.sendMessage({
        method: "sendTelemetryPayload",
        event: "auto-reload-page-in-container",
        userContextId: userContextId,
      });
    } else {
      browser.runtime.sendMessage({
        method: "sendTelemetryPayload",
        event: "prompt-to-reload-page-in-container",
        userContextId: userContextId,
      });
      const confirmUrl = `${loadPage}?url=${url}`;
      browser.tabs.create({url: confirmUrl, cookieStoreId: `firefox-container-${userContextId}`, index}).then(() => {
        // We don't want to sync this URL ever nor clutter the users history
        browser.history.deleteUrl({url: confirmUrl});
      }).catch((e) => {
        throw e;
      });
    }
  }
};

const messageHandler = {
  // After the timer completes we assume it's a tab the user meant to keep open
  // We use this to catch redirected tabs that have just opened
  // If this were in platform we would change how the tab opens based on "new tab" link navigations such as ctrl+click
  LAST_CREATED_TAB_TIMER: 2000,

  init() {
    // Handles messages from index.js
    const port = browser.runtime.connect();
    port.onMessage.addListener(m => {
      switch (m.type) {
      case "lightweight-theme-changed":
        themeManager.update(m.message);
        break;
      case "delete-container":
        assignManager.deleteContainer(m.message.userContextId);
        break;
      default:
        throw new Error(`Unhandled message type: ${m.message}`);
      }
    });

    browser.tabs.onCreated.addListener((tab) => {
      // This works at capturing the tabs as they are created
      // However we need onFocusChanged and onActivated to capture the initial tab
      if (tab.id === -1) {
        return {};
      }
      tabPageCounter.initTabCounter(tab);
    });

    browser.tabs.onRemoved.addListener((tabId) => {
      if (tabId === -1) {
        return {};
      }
      tabPageCounter.sendTabCountAndDelete(tabId);
    });

    browser.tabs.onActivated.addListener((info) => {
      browser.tabs.get(info.tabId).then((tab) => {
        tabPageCounter.initTabCounter(tab);
        assignManager.calculateContextMenu(tab);
      }).catch((e) => {
        throw e;
      });
    });

    browser.windows.onFocusChanged.addListener((windowId) => {
      browser.tabs.query({active: true, windowId}).then((tabs) => {
        if (tabs && tabs[0]) {
          tabPageCounter.initTabCounter(tabs[0]);
          assignManager.calculateContextMenu(tabs[0]);
        }
      }).catch((e) => {
        throw e;
      });
    });

    browser.webRequest.onCompleted.addListener((details) => {
      if (details.frameId !== 0 || details.tabId === -1) {
        return {};
      }

      browser.tabs.get(details.tabId).then((tab) => {
        tabPageCounter.incrementTabCount(tab);
        assignManager.calculateContextMenu(tab);
      }).catch((e) => {
        throw e;
      });
    }, {urls: ["<all_urls>"], types: ["main_frame"]});

    // lets remember the last tab created so we can close it if it looks like a redirect
    browser.tabs.onCreated.addListener((details) => {
      this.lastCreatedTab = details;
      setTimeout(() => {
        this.lastCreatedTab = null;
      }, this.LAST_CREATED_TAB_TIMER);
    });

  }
};

const themeManager = {
  existingTheme: null,
  init() {
    this.check();
  },
  setPopupIcon(theme) {
    let icons = {
      16: "img/container-site-d-24.png",
      32: "img/container-site-d-48.png"
    };
    if (theme === "firefox-compact-dark@mozilla.org") {
      icons = {
        16: "img/container-site-w-24.png",
        32: "img/container-site-w-48.png"
      };
    }
    browser.browserAction.setIcon({
      path: icons
    });
  },
  check() {
    browser.runtime.sendMessage({
      method: "getTheme"
    }).then((theme) => {
      this.update(theme);
    }).catch(() => {
      throw new Error("Unable to get theme");
    });
  },
  update(theme) {
    if (this.existingTheme !== theme) {
      this.setPopupIcon(theme);
      this.existingTheme = theme;
    }
  }
};

const tabPageCounter = {
  counter: {},

  initTabCounter(tab) {
    if (tab.id in this.counter) {
      return;
    }
    this.counter[tab.id] = {
      "cookieStoreId": tab.cookieStoreId,
      "pageRequests": 0
    };
  },

  sendTabCountAndDelete(tabId) {
    browser.runtime.sendMessage({
      method: "sendTelemetryPayload",
      event: "page-requests-completed-per-tab",
      userContextId: this.counter[tabId].cookieStoreId,
      pageRequestCount: this.counter[tabId].pageRequests
    });
    delete this.counter[tabId];
  },

  incrementTabCount(tab) {
    this.counter[tab.id].pageRequests++;
  }
};

assignManager.init();
themeManager.init();
// Lets do this last as theme manager did a check before connecting before
messageHandler.init();

browser.runtime.sendMessage({
  method: "getPreference",
  pref: "browser.privatebrowsing.autostart"
}).then(pbAutoStart => {

  // We don't want to disable the addon if we are in auto private-browsing.
  if (!pbAutoStart) {
    browser.tabs.onCreated.addListener(tab => {
      if (tab.incognito) {
        disableAddon(tab.id);
      }
    });

    browser.tabs.query({}).then(tabs => {
      for (let tab of tabs) { // eslint-disable-line prefer-const
        if (tab.incognito) {
          disableAddon(tab.id);
        }
      }
    }).catch(() => {});
  }
}).catch(() => {});

function disableAddon(tabId) {
  browser.browserAction.disable(tabId);
  browser.browserAction.setTitle({ tabId, title: "Containers disabled in Private Browsing Mode" });
}
<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, func<commit_msg>Update character<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, funct<|endoftext|>


<commit_before>"use strict";

const path = require( 'path' );
const fs = require( 'fs' );
const child_process = require( 'child_process' );
const fse = require( 'fs-extra' );
const moment = require( 'moment' );
const Backbone = require( 'backbone' );
Backbone.LocalStorage = require( "backbone.localstorage" );

//noinspection JSAccessibilityCheck
const ConfigModel = Backbone.Model.extend( {
  localStorage: new Backbone.LocalStorage( "timeStamperConfig" ),
  defaults    : {
    action         : 'rename',
    readOnly       : true,
    usePrefix      : false,
    useSuffix      : false,
    useTimestamp   : true,
    prefixString   : '',
    recentPrefix   : [],
    suffixString   : '',
    recentSuffix   : [],
    timeSrc        : 'today',
    timestampFormat: '_YYMMDD'
  },

  executeStamp      : function ( srcFilePath ) {
    const ext = path.extname( srcFilePath );
    const basename = path.basename( srcFilePath, ext );
    const dirname = path.dirname( srcFilePath );

    // resultFilePathを決める
    var resultBasename = '';
    if (this.get( 'usePrefix' )) {
      resultBasename += this.get( 'prefixString' );
    }
    resultBasename += basename;
    if (this.get( 'useSuffix' )) {
      resultBasename += this.get( 'suffixString' );
    }
    if (this.get( 'useTimestamp' )) {
      var time;
      if (this.get( 'timeSrc' ) == 'lastModified') {
        time = fs.statSync( srcFilePath ).mtime;
      } else {
        time = Date.now();
      }
      resultBasename += moment( time ).format( this.get( 'timestampFormat' ) );
    }

    const resultFilePath = ensureUniqueName( path.join( dirname, resultBasename + ext ) );
    //console.log( resultFilePath );

    // copy
    if (this.get( 'action' ) == 'copy') {
      fse.copySync( srcFilePath, resultFilePath, {preserveTimestamps: true} );
    }

    // rename
    if (this.get( 'action' ) == 'rename') {
      fs.renameSync( srcFilePath, resultFilePath );
    }

    // readOnly
    if (this.get( 'readOnly' )) {
      const isWindows = process.platform.indexOf( "win" ) === 0;
      if (isWindows) {
        try {
          //build済みelectronのwin版でerrorが出ます。咬み殺すのもやなのでログだけは残す。
          child_process.execSync( 'attrib +r "' + resultFilePath + '"' );
        } catch (e) {
          console.error( e );
        }
      } else {
        child_process.execSync( 'chflags uchg ' + resultFilePath.replace( /(\s)/g, '\\ ' ) );
      }
    }

    //updateRecent
    if (this.get( 'usePrefix' )) {
      this.updateRecentPrefix();
    }
    if (this.get( 'useSuffix' )) {
      this.updateRecentSuffix();
    }

  },
  updateRecentPrefix: function () {
    const prefix = this.get( 'prefixString' );
    const recentPrefix = this.get( 'recentPrefix' );
    this.save( 'recentPrefix', updateRecentListArray( recentPrefix, prefix ) );
  },
  updateRecentSuffix: function () {
    const suffix = this.get( 'suffixString' );
    const recentSuffix = this.get( 'recentSuffix' );
    this.save( 'recentSuffix', updateRecentListArray( recentSuffix, suffix ) );
  }

} );

const configModel = module.exports = new ConfigModel( {id: 0} );
configModel.fetch();
configModel.save();

function ensureUniqueName ( filePath ) {
  const dirname = path.dirname( filePath );
  const fileNameList = fs.readdirSync( dirname );
  const timestampSuffixDict = ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z'];

  function isUnique ( name ) {
    return !fileNameList.length || fileNameList.indexOf( name ) < 0;
  }

  var destName = path.basename( filePath );

  if (!isUnique( destName )) {
    var inc = 0;
    do {
      const mod = inc % 22;
      var timestampSuffix = timestampSuffixDict[mod];
      if (inc - mod) {timestampSuffix += (inc - mod)}
      var tempName = path.basename( destName, path.extname( destName ) ) + timestampSuffix + path.extname( destName );
      inc++;
    } while (!isUnique( tempName ));
    destName = tempName;
  }

  return path.join( dirname, destName );
}

function updateRecentListArray ( listArray, value, count ) {
  const _count = count || 5;
  var res = _.without( listArray, value );
  res.unshift( value );
  return res.slice( 0, _count );
}

<commit_msg>[change] copy&rename 非同期に<commit_after>"use strict";

const path = require( 'path' );
const fs = require( 'fs' );
const child_process = require( 'child_process' );
const fse = require( 'fs-extra' );
const moment = require( 'moment' );
const Backbone = require( 'backbone' );
Backbone.LocalStorage = require( "backbone.localstorage" );

//noinspection JSAccessibilityCheck
const ConfigModel = Backbone.Model.extend( {
  localStorage: new Backbone.LocalStorage( "timeStamperConfig" ),
  defaults    : {
    action         : 'rename',
    readOnly       : true,
    usePrefix      : false,
    useSuffix      : false,
    useTimestamp   : true,
    prefixString   : '',
    recentPrefix   : [],
    suffixString   : '',
    recentSuffix   : [],
    timeSrc        : 'today',
    timestampFormat: '_YYMMDD'
  },

  executeStamp      : function ( srcFilePath ) {
    const ext = path.extname( srcFilePath );
    const basename = path.basename( srcFilePath, ext );
    const dirname = path.dirname( srcFilePath );

    //updateRecent
    if (this.get( 'usePrefix' )) {
      this.updateRecentPrefix();
    }
    if (this.get( 'useSuffix' )) {
      this.updateRecentSuffix();
    }

    // resultFilePathを決める
    var resultBasename = '';
    if (this.get( 'usePrefix' )) {
      resultBasename += this.get( 'prefixString' );
    }
    resultBasename += basename;
    if (this.get( 'useSuffix' )) {
      resultBasename += this.get( 'suffixString' );
    }
    if (this.get( 'useTimestamp' )) {
      var time;
      if (this.get( 'timeSrc' ) == 'lastModified') {
        time = fs.statSync( srcFilePath ).mtime;
      } else {
        time = Date.now();
      }
      resultBasename += moment( time ).format( this.get( 'timestampFormat' ) );
    }

    const resultFilePath = ensureUniqueName( path.join( dirname, resultBasename + ext ) );
    //console.log( resultFilePath );

    const isReadOnly = this.get( 'readOnly' );

    // copy
    if (this.get( 'action' ) == 'copy') {
      fse.copy( srcFilePath, resultFilePath, {preserveTimestamps: true}, function ( err ) {
        if (err) {console.error( err )}
        applyReadOnly();
      } );
    }

    // rename
    if (this.get( 'action' ) == 'rename') {
      fs.rename( srcFilePath, resultFilePath, function ( err ) {
        if (err) {console.error( err )}
        applyReadOnly();
      } );
    }

    // readOnly
    function applyReadOnly () {
      if (isReadOnly) {
        const isWindows = process.platform.indexOf( "win" ) === 0;
        if (isWindows) {
          try {
            //build済みelectronのwin版でerrorが出ます。咬み殺すのもやなのでログだけは残す。
            child_process.execSync( 'attrib +r "' + resultFilePath + '"' );
          } catch (e) {
            console.error( e );
          }
        } else {
          child_process.execSync( 'chflags uchg ' + resultFilePath.replace( /(\s)/g, '\\ ' ) );
        }
      }
    }

  },
  updateRecentPrefix: function () {
    const prefix = this.get( 'prefixString' );
    const recentPrefix = this.get( 'recentPrefix' );
    this.save( 'recentPrefix', updateRecentListArray( recentPrefix, prefix ) );
  },
  updateRecentSuffix: function () {
    const suffix = this.get( 'suffixString' );
    const recentSuffix = this.get( 'recentSuffix' );
    this.save( 'recentSuffix', updateRecentListArray( recentSuffix, suffix ) );
  }

} );

const configModel = module.exports = new ConfigModel( {id: 0} );
configModel.fetch();
configModel.save();

function ensureUniqueName ( filePath ) {
  const dirname = path.dirname( filePath );
  const fileNameList = fs.readdirSync( dirname );
  const timestampSuffixDict = ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z'];

  function isUnique ( name ) {
    return !fileNameList.length || fileNameList.indexOf( name ) < 0;
  }

  var destName = path.basename( filePath );

  if (!isUnique( destName )) {
    var inc = 0;
    do {
      const mod = inc % 22;
      var timestampSuffix = timestampSuffixDict[mod];
      if (inc - mod) {timestampSuffix += (inc - mod)}
      var tempName = path.basename( destName, path.extname( destName ) ) + timestampSuffix + path.extname( destName );
      inc++;
    } while (!isUnique( tempName ));
    destName = tempName;
  }

  return path.join( dirname, destName );
}

function updateRecentListArray ( listArray, value, count ) {
  const _count = count || 5;
  var res = _.without( listArray, value );
  res.unshift( value );
  return res.slice( 0, _count );
}

<|endoftext|>