From 11f606c269e3b7cca1848bc227e8e38321fb6e3e Mon Sep 17 00:00:00 2001
From: tomsmeding <tom.smeding@gmail.com>
Date: Sat, 25 Mar 2017 23:02:21 +0100
Subject: Start work on web client

---
 webclient/client.html | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 326 insertions(+)
 create mode 100644 webclient/client.html

(limited to 'webclient/client.html')

diff --git a/webclient/client.html b/webclient/client.html
new file mode 100644
index 0000000..3472cb8
--- /dev/null
+++ b/webclient/client.html
@@ -0,0 +1,326 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>tomsg webclient</title>
+<script>
+var sock=null,username=null;
+var roomlist=[":console"];
+var currentroom=":console";
+var roomlogs={":console":[]};
+
+(function(){
+	var id=1;
+	function uniqid(){
+		return id++;
+	}
+})();
+
+var net_callbacks={};
+
+function net_send(msg,cb){
+	var id=uniqid()+"";
+	sock.send(id+" "+msg);
+	net_callbacks[id]=cb;
+}
+
+function reconnect(){
+	if(sock)sock.close();
+
+	net_callbacks={};
+
+	var host=location.hostname||"localhost";
+	sock=new WebSocket("ws://"+host+":29546");
+	updateStatus();
+	updateRoomList();
+	sock.addEventListener("message",function(msg){
+		var spl=msg.split(" ",2);
+		var id=spl[0],type=spl[1];
+		var rest=msg.slice(id.length+type.length+2);
+		if(id=="_push"){
+			if(type=="message"){
+				spl=rest.split(" ",2);
+				var r=spl[0],u=spl[1];
+				addRoomEntry(r,"message",[u,rest.slice(r.length+u.length+2)]);
+			} else {
+				alert("Unknown push message type '"+type+"'!");
+			}
+		} else if(net_callbacks[id]){
+			var fn=net_callbacks[id];
+			var obj;
+			if(type=="ok")fn(true);
+			else if(type=="error")fn(null,rest);
+			else if(type=="name")fn(rest);
+			else if(type=="list")fn(rest.split(" ").slice(1));
+			else alert("Unknown server response message type '"+type+"'!");
+		} else {
+			alert("No callback for server message id '"+id+"'!");
+		}
+	});
+	sock.addEventListener("open",function(){
+		updateStatus();
+	});
+	sock.addEventListener("close",function(ev){
+		updateStatus();
+		if(ev.code!=1000){
+			setTimeout(function(){
+				reconnect();
+			},1000+Math.random()*1000);
+		}
+	});
+}
+
+function updateStatus(){
+	var str=null;
+	if(!sock||sock.readyState>=2){
+		str="not connected";
+	} else if(sock.readyState==0){
+		str="connecting...";
+	} else if(username){
+		str="u: "+username;
+	} else {
+		str="connected";
+	}
+	var div=document.getElementById("status");
+	if(!div.firstChild)div.appendChild(document.createTextNode(""));
+	div.firstChild.nodeValue=str;
+}
+
+function updateRoomList(){
+	var rldiv=document.getElementById("roomlist");
+	var ch=rldiv.children,len=ch.length;
+	var i;
+	for(i=len-1;i>=0;i--)rldiv.removeChild(ch[i]);
+
+	var div;
+	for(i=0;i<roomlist.length;i++){
+		div=document.createElement("div");
+		div.classList.add("roomlistitem");
+		if(roomlist[i]==currentroom)div.classList.add("selected");
+		div.addEventListener("click",function(roomid){
+			currentroom=roomid;
+			drawRoom(currentroom);
+		}.bind(this,roomlist[i]));
+		div.appendChild(document.createTextNode(roomlist[i]));
+		rldiv.appendChild(div);
+	}
+}
+
+function drawRoom(roomid){
+	var tbody=document.getElementById("roomlog");
+	var ch=tbody.children,len=ch.length;
+	var i;
+	for(i=len-1;i>=0;i--)tbody.removeChild(ch[i]);
+
+	var roomtitle=document.getElementById("roomtitle");
+	if(!roomtitle.firstChild)roomtitle.appendChild(document.createTextNode(roomid));
+	else roomtitle.firstChild.nodeValue=roomid;
+
+	var tr,td;
+	var items=roomlogs[roomid];
+	if(!items){
+		alert("drawRoom on nonexistent roomid '"+roomid+"'!");
+		return;
+	}
+	for(i=Math.max(0,items.length-20);i<items.length;i++){
+		drawRoomEntry(items[i][0],items[i][1]);
+	}
+}
+
+function drawRoomEntry(type,args){
+	var tbody=document.getElementById("roomlog");
+	var tr=document.createElement("tr");
+	var td;
+	switch(type){
+		case "message":
+			td=document.createElement("td");
+			td.appendChild(document.createTextNode(args[0]));
+			tr.appendChild(td);
+			td=document.createElement("td");
+			td.classList.add("message");
+			td.appendChild(document.createTextNode(args[1]));
+			tr.appendChild(td);
+			tbody.appendChild(tr);
+			break;
+
+		default:
+			alert("drawRoomEntry on unknown type '"+type+"'!");
+			break;
+	}
+}
+
+function addRoomEntry(roomid,type,args){
+	if(!roomlogs[roomid]){
+		alert("addRoomEntry on nonexistent roomid '"+roomid+"'!");
+		return;
+	}
+	roomlogs[roomid].push([type,args]);
+	if(roomid==currentroom){
+		drawRoomEntry(type,args);
+	}
+}
+
+function doSend(){
+	var inputelem=document.getElementById("roominput");
+	var text=inputelem.value;
+	inputelem.value="";
+	addRoomEntry(currentroom,"message",["-",text]);
+}
+
+function doKeypress(ev){
+	if(ev.keyCode==10||ev.keyCode==13)doSend();
+}
+
+window.addEventListener("load",function(){
+	reconnect();
+	drawRoom(currentroom);
+});
+</script>
+<style>
+html, body{
+	margin:0;
+	width:100%;
+	height:100%;
+}
+body{
+	background-color:#04040c;
+	color:#eee;
+	font-family:mononoki,meslo,monaco,monospace;
+	font-size:11pt;
+}
+table{
+	border-collapse:collapse;
+}
+#window{
+	width:100%;
+	height:100%;
+}
+
+/* SIDEBAR */
+#sidebar{
+	width:150px;
+	border-right:1px #668 solid;
+	vertical-align:top;
+}
+
+#branding{
+	background-color:#223;
+	padding:4px;
+	height:22px;
+	text-align:center;
+	font-size:13pt;
+}
+#status{
+	padding:2px;
+	margin:8px 0px 10px 0px;
+}
+.roomlistitem{
+	margin:1px 0px;
+	padding:3px 2px;
+	background-color:#223;
+	cursor:pointer;
+}
+.roomlistitem:hover{
+	background-color:#282833;
+}
+.roomlistitem.selected{
+	background-color:#335;
+	font-weight:bold;
+}
+.roomlistitem:hover{
+	background-color:#445;
+}
+
+/* ROOM VIEW */
+#room{
+	width:100%;
+	height:100%;
+}
+#room_td{
+	padding:0;
+}
+#roomtitle{
+	height:22px;
+	padding:4px;
+	background-color:#335;
+	font-weight:bold;
+}
+
+#roomlog_td{
+	vertical-align:top;
+}
+#roomlog_table{
+	width:100%;
+	/*height:100%;*/
+}
+#roomlog > tr > td:first-child{
+	width:150px;
+}
+
+#roombar_tr{
+	height:30px;
+	border-top:1px #668 solid;
+}
+#roominput_td{
+	position:relative;
+	vertical-align:top;
+	padding:0px 1px 0px 10px;
+}
+#roominput{
+	position:absolute;
+	background-color:rgba(0,0,0,0);
+	padding:0;
+	border-width:0;
+	width:calc(100% - 11px);
+	height:100%;
+	color:white;
+	font-family:inherit;
+	font-size:inherit;
+}
+#roomsend_td{
+	position:relative;
+	width:40px;
+	vertical-align:top;
+	padding:0;
+}
+#roomsend{
+	position:absolute;
+	width:100%;
+	height:100%;
+	background-color:#335;
+	border-width:0px;
+	color:#ccc;
+	font-family:inherit;
+	font-size:inherit;
+	cursor:pointer;
+}
+#roomsend:hover{
+	background-color:#445;
+}
+</style>
+<script src="https://use.fontawesome.com/ccf28a5626.js"></script>
+</head>
+<body>
+<table id="window"><tbody><tr>
+	<td id="sidebar">
+		<div id="branding">TOMSG</div>
+		<div id="status">&nbsp;</div>
+		<div id="roomlist"></div>
+	</td>
+	<td id="room_td">
+		<table id="room"><tbody>
+			<tr id="roomtitle_tr"><td id="roomtitle" colspan="2"></td></tr>
+			<tr id="roomlog_tr"><td id="roomlog_td" colspan="2">
+				<table id="roomlog_table"><tbody id="roomlog"></tbody></table>
+			</td></tr>
+			<tr id="roombar_tr">
+				<td id="roominput_td"><input type="text" id="roominput" onkeypress="doKeypress(event)"></td>
+				<td id="roomsend_td">
+					<button id="roomsend" onclick="doSend()"><i class="fa fa-send"></i></button>
+				</td>
+			</tr>
+		</tbody></table>
+	</td>
+</tr></tbody></table>
+</body>
+</html>
-- 
cgit v1.2.3-70-g09d2