<commit_before><!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="content-type" content="text/html; charset=utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<script src="https://cdn.firebase.com/js/client/2.2.1/firebase.js"></script>
		<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
    		<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/pbkdf2.js"></script>    
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
		<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>	
		<style>
		/*@import "https://fonts.googleapis.com/css?family=Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400";*/
			body { 
				background: #454044;
				font-family: Arial,sans-serif;
				font-weight: normal;
				font-size: 1.6em;
				line-height: 1.6;
				margin-bottom: 0.75em;
				text-rendering: optimizelegibility;
			 }
			.container-fluid {
				padding-right: 0px;
				padding-left: 0px;
				margin-right: auto;
				margin-left: auto;
			}
			#secureIcon	{
				position:fixed;
				top: 5px;
				right: 25px;		
			}
			.row {
				width: 95%;
				margin: auto;
				background: #d0cab4;
				border-radius: 0.3em 0.3em 0 0;
			}
			#inputs-container {
				position: fixed;
				bottom: 0px;
				width: 100%;
				background: #3c2e2d;
				border-radius: 0.3em 0.3em 0 0;
				overflow: hidden;
				min-height: 100px;
			}
			#inputs { padding: 0.4em; }
			#messages {
				position: fixed;
				top: 0px;
				width: 100%;
				height: 100%;
				overflow: auto; 
				padding-bottom:100px;
			}
			.msg.row.same {	border-radius: 0; }
			.msg.row.alternate { background: #f3ffee; }
			.msg.row.alternate.same { border-radius: 0; }
			.msg { padding-left: 0.2em;	}
			.msg.new { margin-top: 0.2em; }
			.msg.enc { border-right: 2px dotted green; }
			.msg.new .nickname {
				width: auto;
				float: right;
			}
			.msg.same .nickname { display:none; }
			.msg .nickname { display: inline-block; }
			.msg .msgBody {
				padding-left: 0.2em;
				display: inline-block;
			}
			.msg .time {
				color:#657797;
				font-size: 10px;
				margin:0;
			}
			.col-md-1 { width: auto; }
			img {
				padding: 0.1em 0 0 0;
				max-width: 100%;
				max-height: 100%;
			}
			@-moz-document url-prefix() {
			img { width: 100%; height: 100%; }
			}
			input, button, select, textarea {
				border-radius: 0.2em;
				background: #cdc7c7;
			}
			button { margin-top: 0.2em; }
			#inputs input { margin-bottom: 0.4em; }
			#inputs textarea { min-width: 10em; width: 100%; }
			#messages .alternate .msgBody { background: #f3ffee; }
			#options span { color:gray;}
			#optionsToggle {right: 15px; position:fixed; color:white; cursor: pointer;}
			#send { font-size:10px;margin:0; border:0; line-height:12px; }
			hr { margin: 3px; }
		</style>
		<title>Chat</title></head>
	</head>
  <body>
    <div class="container-fluid">	
    <div id="messages"></div>
	<div id="inputs-container">
	<div id="inputs">		
	<div id="options" style="display: none">	
		<input value="" id="nameInput" placeholder="Name" type="text">
		<span id="notificationsConfig">
		<input value="" type="password" id="passwordInput" placeholder="<PASSWORD>">	
		<button id="persistPass" title="Persist encryption key to survive refresh">#</button>
		<input type="checkbox" id="enableEncryption"> <label> Enable encryption </label>
		<input type="checkbox" id="enableNotifications"> <label> Enable notifications </label>
	</div>
	<div id = "msgInput">
		<div id="optionsToggle" title="Toggle options">🔨</div>
		<textarea id="messageInput" placeholder="Message" type="text"></textarea>
		<button id="send">Send</button>
	</div>
	</div>
	</div>
	
	</div>
	<script>
	/* ---------------------- CONFIGURATION ------------------------*/
	var databaseUrl = 'https://antano-chatas.firebaseio.com/';
	var messagesLimit = 147; //how many messages to show
	var pbkdf2Iterrations = 447; //password stretching strength. Bigger number puts more load on client;	
	var notificationsEnabled = true; //enables or disables desktop notifications globally
	var passwordSalt = '<PASSWORD>'; //change this to something random

	/* ------------------ END OF CONFIGURATION ---------------------*/
	
	
	var fb = new Firebase(databaseUrl);
	fbQuery = fb.endAt().limitToLast(messagesLimit);
	var lastName = "";
	var notifyEnabled = false;		
	passwordSalt = CryptoJS.enc.Hex.parse(passwordSalt);
	var iv = CryptoJS.enc.Hex.parse('ca123400777d00cd'); //IV should be random for each message, but it is currently not implemented
	var password;	
	var encrypt = false;

	/******** UNREAD MESSAGE NOTIFICATION *********/
	var unreadMsgCount = 0;
	var hidden, visibilityChange; 
	var title = "Chat";  
	document.title = title;
	/**********************************************/

	$(function(){
	
		$("#enableEncryption").prop('checked', false);
		$('#nameInput').val(localStorage.getItem("nickname"));
		notifyEnabled = JSON.parse(localStorage.getItem("enableNotifications"));
		$('#enableNotifications').prop('checked', notifyEnabled);
	
		fbAttach();
		/******** UNREAD MESSAGE NOTIFICATION *********/
		setupVisibilityProps();
		/**********************************************/
		if (window.location.hash)
		{
			$("#enableEncryption").prop('checked', true);
			enableEncryption();
		}
		//quick workaround to fix layout issue		
		if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1){
		     $("#messages").css("top","-150px");
		}
	});
	$('#enableNotifications').change(function(){localStorage.setItem("enableNotifications", $(this).prop('checked'));});
	$('#nameInput').change(function(){localStorage.setItem("nickname", $(this).val());});
	$('#passwordInput').change(function(){$("#enableEncryption").prop('checked', false);});
	$('#send').click(function(){send();});
	$('#persistPass').click(function(){
	  		window.location.hash = password.substring(password.length/2);
			localStorage.setItem("p", password.substring(0,password.length/2))
	})
	$("#optionsToggle").click(function() {
	  $("#options").slideToggle( "fast" );
	});
	$("#enableEncryption").change(function() {
			if(this.checked) {
				enableEncryption();
			} else
			{
				encrypt = false;
				$("#secureIcon").hide(1000);
			}
	});

	$('#messageInput').keypress(function (e) {
	if (e.keyCode == 13) {
		if (!e.ctrlKey) {				
			send();
			return false;
		} else {
			$('#messageInput').val($('#messageInput').val() + "\n");
		}
	}
	});
	function enableEncryption()
	{
				userpass = $('#passwordInput').val();
				locpass = localStorage.getItem("p");
				if (window.location.hash && locpass && !userpass)
				{
					password = <PASSWORD> + window.location.hash.substring(1);
				}
				else
				{
					password = CryptoJS.PBKDF2(userpass, passwordSalt, { keySize: 256/32, iterations: 447 }).toString();
				}
				$("#secureIcon").show(2200);
				fb.off();
				$('#messages').html('');				
				encrypt = true;
				fbAttach();
	}
	function fbAttach()
	{
		notifyEnabled = false;
		setTimeout(function(){notifyEnabled=true;},17000);
		fbQuery.on('child_added', function (snapshot) {
			var message = snapshot.val();
			if (message.ct) //encrypted
			{
				try {				
					msgIV = CryptoJS.enc.Hex.parse(message.iv || 'ca123400777d00cd');
					var decrypted = CryptoJS.AES.decrypt(message.ct, CryptoJS.enc.Hex.parse(password),{ iv:msgIV }); 
					message.text = decrypted.toString(CryptoJS.enc.Utf8);
				} catch (err) {console.log(err);}
				if (!message.text)
					message.text = "Decryption failed";
			}
			displayChatMessage(message.name, message.text, message._at, message.ct);
			/******** UNREAD MESSAGE NOTIFICATION *********/		
			notifyAboutUnread(snapshot);
			/**********************************************/		
			if (notifyEnabled)
				notify(message.name, message.text);
			lastName = message.name;
		});
	}


	function send()
	{
		var name = $('#nameInput').val();			
		var text = $('#messageInput').val();	
		if (!text)
			return;
		if (encrypt)
		{
			var encrypted = CryptoJS.AES.encrypt(text, CryptoJS.enc.Hex.parse(password),{ iv: CryptoJS.lib.WordArray.random(32)});				
			fb.push({name: name, text: "This message is encrypted", ct: encrypted.toString(), _at: new Date().toISOString(), iv: encrypted.iv.toString() });
		}
		else
		{
			fb.push({name : name, text : text, _at: new Date().toISOString()});
		}
		$('#messageInput').val('');
	}
	function displayChatMessage(name, text, time, encrypted) {
		var localTimeString = new Date(Date.parse(time)||time).toLocaleString("lt");
		var altClass="";
		var sameNickClass = "new"
		var encryptedClass = "enc";
		if (!encrypted)
			encryptedClass = "";
		if ($('#nameInput').val() != name)
			altClass = "alternate";
		if (lastName == name)
			sameNickClass = "same";
		text = formatText(text);
		var body  = $("<div class='msgBody col-md-11' />").html(text).append($('<p class="time" />').text(localTimeString));
		var textDiv= $('<div class="msg row '+altClass+' '+sameNickClass+' '+encryptedClass+'" />').html(body);
		textDiv = textDiv.prepend($('<div class="nickname col-md-1" />').text(name));

			
		textDiv.appendTo($('#messages'));
		$('#messages')[0].scrollTop = $('#messages')[0].scrollHeight;
	};

	function formatText(txt)
	{
	  var imgExp = /(\b(https?):.+?\.(jpg|gif|png|svg|jpeg))/i;
	  var linkExp = /(\b(https?):[^ ]+)/i;
	  txt = txt.replace("\n", "<br />");
	  txt = txt.replace(imgExp, '<img width="100%" height="100%" src="$1" />'); 
	  txt = txt.replace(linkExp, '<a href="$1" target="_blank">$1</a>'); 
	  return txt;

	}
	
	function notify(name, txt) {
		if (name == $('#nameInput').val() || !notifyEnabled)<commit_msg>Misc fixes<commit_after><!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="content-type" content="text/html; charset=utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<script src="https://cdn.firebase.com/js/client/2.2.1/firebase.js"></script>
		<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
    		<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/pbkdf2.js"></script>    
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
		<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>	
		<style>
		/*@import "https://fonts.googleapis.com/css?family=Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400";*/
			body { 
				background: #454044;
				font-family: Arial,sans-serif;
				font-weight: normal;
				font-size: 1.6em;
				line-height: 1.6;
				margin-bottom: 0.75em;
				text-rendering: optimizelegibility;
			 }
			.container-fluid {
				padding-right: 0px;
				padding-left: 0px;
				margin-right: auto;
				margin-left: auto;
			}
			#secureIcon	{
				position:fixed;
				top: 5px;
				right: 25px;		
			}
			.row {
				width: 95%;
				margin: auto;
				background: #d0cab4;
				border-radius: 0.3em 0.3em 0 0;
			}
			#inputs-container {
				position: fixed;
				bottom: 0px;
				width: 100%;
				background: #3c2e2d;
				border-radius: 0.3em 0.3em 0 0;
				overflow: hidden;
				min-height: 100px;
			}
			#inputs { padding: 0.4em; }
			#messages {
				position: fixed;
				top: 0px;
				width: 100%;
				height: 100%;
				overflow: auto; 
				padding-bottom:127px;
			}
			.msg pre { margin-top:5px; }
			.msg.row.same {	border-radius: 0; }
			.msg.row.alternate { background: #f3ffee; }
			.msg.row.alternate.same { border-radius: 0; }
			.msg { padding-left: 0.2em;	}
			.msg.new { margin-top: 0.2em; }
			.msg.enc { border-right: 2px dotted green; }
			.msg.new .nickname {
				width: auto;
				float: right;
			}
			.msg.same .nickname { display:none; }
			.msg .nickname { display: inline-block; }
			.msg .msgBody {
				padding-left: 0.2em;
				display: inline-block;
			}
			.msg .msgBody img
			{
				max-height:300px;
				width:auto;
				border: 1px solid black;	
				margin: 5px;				
			}
			.msg .time {
				color:#657797;
				font-size: 10px;
				margin:0;
			}
			.col-md-1 { width: auto; }
			img {
				padding: 0 0 0 0;
				max-width: 100%;
				max-height: 100%;
			}
			@-moz-document url-prefix() {
			img { width: 100%; height: 100%; }
			}
			input, button, select, textarea {
				border-radius: 0.2em;
				background: #cdc7c7;
			}
			button { margin-top: 0.2em; }
			#inputs input { margin-bottom: 0.4em; }
			#inputs textarea { min-width: 10em; width: 100%; height:82px; }
			#messages .alternate .msgBody { background: #f3ffee; }
			#options span { color:gray;}
			#optionsToggle {right: 15px; position:fixed; color:white; cursor: pointer;}
			#send { font-size:10px;margin:0; border:0; line-height:12px; }
			hr { margin: 3px; }
		</style>
		<title>Chat</title></head>
	</head>
  <body>
    <div class="container-fluid">	
    <div id="messages"></div>
	<div id="inputs-container">
	<div id="inputs">		
	<div id="options" style="display: none">	
		<input value="" id="nameInput" placeholder="Name" type="text">
		<span id="notificationsConfig">
		<input value="" type="password" id="passwordInput" placeholder="<PASSWORD>">	
		<button id="persistPass" title="Persist encryption key to survive refresh">#</button>
		<input type="checkbox" id="enableEncryption"> <label> Enable encryption </label>
		<input type="checkbox" id="enableNotifications"> <label> Enable notifications </label>
	</div>
	<div id = "msgInput">
		<div id="optionsToggle" title="Toggle options">🔨</div>
		<textarea id="messageInput" placeholder="Message" type="text"></textarea>
		<button id="send">Send</button>
	</div>
	</div>
	</div>
	
	</div>
	<script>
	/* ---------------------- CONFIGURATION ------------------------*/
	var databaseUrl = 'https://antano-chatas.firebaseio.com/';
	var messagesLimit = 147; //how many messages to show
	var pbkdf2Iterrations = 447; //password stretching strength. Bigger number puts more load on client;	
	var notificationsEnabled = true; //enables or disables desktop notifications globally
	var passwordSalt = 'ba<PASSWORD>'; //change this to something random

	/* ------------------ END OF CONFIGURATION ---------------------*/
	
	
	var fb = new Firebase(databaseUrl);
	fbQuery = fb.endAt().limitToLast(messagesLimit);
	var lastName = "";
	var notifyEnabled = false;		
	passwordSalt = <EMAIL>JS.enc.Hex.parse(passwordSalt);
	var iv = CryptoJS.enc.Hex.parse('ca123400777d00cd'); //IV should be random for each message, but it is currently not implemented
	var password;	
	var encrypt = false;

	/******** UNREAD MESSAGE NOTIFICATION *********/
	var unreadMsgCount = 0;
	var hidden, visibilityChange; 
	var title = "Chat";  
	document.title = title;
	/**********************************************/

	$(function(){
	
		$("#enableEncryption").prop('checked', false);
		$('#nameInput').val(localStorage.getItem("nickname"));
		notifyEnabled = JSON.parse(localStorage.getItem("enableNotifications"));
		$('#enableNotifications').prop('checked', notifyEnabled);
	
		fbAttach();
		/******** UNREAD MESSAGE NOTIFICATION *********/
		setupVisibilityProps();
		/**********************************************/
		if (window.location.hash)
		{
			$("#enableEncryption").prop('checked', true);
			enableEncryption();
		}
	});
	$('#enableNotifications').change(function(){localStorage.setItem("enableNotifications", $(this).prop('checked'));});
	$('#nameInput').change(function(){localStorage.setItem("nickname", $(this).val());});
	$('#passwordInput').change(function(){$("#enableEncryption").prop('checked', false);});
	$('#send').click(function(){send();});
	$('#persistPass').click(function(){
	  		window.location.hash = password.substring(password.length/2);
			localStorage.setItem("p", password.substring(0,password.length/2))
	})
	$("#optionsToggle").click(function() {
	  $("#options").slideToggle( "fast" );
	});
	$("#enableEncryption").change(function() {
			if(this.checked) {
				enableEncryption();
			} else
			{
				encrypt = false;
				$("#secureIcon").hide(1000);
			}
	});

	$('#messageInput').keydown(function (e) {
	if (e.keyCode == 13 && e.ctrlKey) {			
			//return true;
			$('#messageInput').val($('#messageInput').val() + "\n");
	}});
	$('#messageInput').keypress(function (e) {
	if (e.keyCode == 13 && !e.ctrlKey) {
			send();
			return false;
	}});
	function enableEncryption()
	{
				userpass = $('#passwordInput').val();
				locpass = localStorage.getItem("p");
				if (window.location.hash && locpass && !userpass)
				{
					password = <PASSWORD> + window.location.hash.substring(1);
				}
				else
				{
					password = CryptoJS.PBKDF2(userpass, passwordSalt, { keySize: 256/32, iterations: 447 }).toString();
				}
				$("#secureIcon").show(2200);
				fb.off();
				$('#messages').html('');				
				encrypt = true;
				fbAttach();
	}
	function fbAttach()
	{
		notifyEnabled = false;
		setTimeout(function(){notifyEnabled=true;},17000);
		fbQuery.on('child_added', function (snapshot) {
			var message = snapshot.val();
			if (message.ct) //encrypted
			{
				try {				
					msgIV = CryptoJS.enc.Hex.parse(message.iv || 'ca123400777d00cd');
					var decrypted = CryptoJS.AES.decrypt(message.ct, CryptoJS.enc.Hex.parse(password),{ iv:msgIV }); 
					message.text = decrypted.toString(CryptoJS.enc.Utf8);
				} catch (err) {console.log(err);}
				if (!message.text)
					message.text = "Decryption failed";
			}
			displayChatMessage(message.name, message.text, message._at, message.ct);
			/******** UNREAD MESSAGE NOTIFICATION *********/		
			notifyAboutUnread(snapshot);
			/**********************************************/		
			if (notifyEnabled)
				notify(message.name, message.text);
			lastName = message.name;
		});
	}


	function send()
	{
		var name = $('#nameInput').val();			
		var text = $('#messageInput').val();	
		if (!text)
			return;
		if (encrypt)
		{
			var encrypted = CryptoJS.AES.encrypt(text, CryptoJS.enc.Hex.parse(password),{ iv: CryptoJS.lib.WordArray.random(32)});				
			fb.push({name: name, text: "This message is encrypted", ct: encrypted.toString(), _at: new Date().toISOString(), iv: encrypted.iv.toString() });
		}
		else
		{
			fb.push({name : name, text : text, _at: new Date().toISOString()});
		}
		$('#messageInput').val('');
	}
	function displayChatMessage(name, text, time, encrypted) {
		var localTimeString = new Date(Date.parse(time)||time).toLocaleString("lt");
		var altClass="";
		var sameNickClass = "new"
		var encryptedClass = "enc";
		if (!encrypted)
			encryptedClass = "";
		if ($('#nameInput').val() != name)
			altClass = "alternate";
		if (lastName == name)
			sameNickClass = "same";
		text = formatText(text);
		var body  = $("<div class='msgBody col-md-11' />").html(text).append($('<p class="time" />').text(localTimeString));
		var textDiv= $('<div class="msg row '+altClass+' '+sameNickClass+' '+encryptedClass+'" />').html(body);
		textDiv = textDiv.prepend($('<div class="nickname col-md-1" />').text(name));

			
		textDiv.appendTo($('#messages'));
		$('#messages')[0].scrollTop = $('#messages')[0].scrollHeight;
	};

	function formatText(txt)
	{
	    //console.log(txt);
		if (txt.startsWith("`")) { //pre
			txt = txt.replace("`","");
			txt = txt.replace(/^\s+|\s+$/g, ''); //trim start
			return "<pre>"+txt+"</pre>";
		}
		var imgExp = /(\b(https?):.+?\.(jpg|gif|png|svg|jpeg))/i;
		var linkExp = /(\b(https?):[^ ]+)/i;
		txt = txt.split("\n").join("<br />");
		if (txt.search(imgExp) >= 0)
		{
			return txt.replace(imgExp, '<a href="$1" target="_blank"><img src="$1" /></a>'); 
		}
		txt = txt.replace(linkExp, '<a href="$1" target="_blank">$1</a>'); 
		return txt;

	}
	
	function notify(name, txt) {
		if (name == $('#nameInput').val())
{'fraction_non_alphanumeric': 0.21281145869597654, 'fraction_numerical': 0.02531456706619585, 'mean_word_length': 3.6310456011054812, 'pattern_counts': {'":': 0, '<': 138, '<?xml version=': 0, '>': 140, 'https://': 18, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1000463', 'n_tokens_mistral': 8249, 'n_tokens_neox': 6906, 'n_words': 1314}