servatrice/webclient/index.html
Fabio Bas a68df3611b Fix #2088
Add an autocomplete with known server names to the connect dialog;
Added support for server messages (identification, shutdown, user
warning and promotion)
Connection dialog’s inputs are now wider
Only declare implemented client features
Added check for protocol version on connect before login
Avoid listbox being rendered as dropdown on mobiles
2016-08-09 10:39:03 +02:00

403 lines
13 KiB
HTML
Executable file

<!doctype html>
<html>
<head>
<title>Cockatrice web client</title>
<link rel="shortcut icon" href="imgs/cockatrice.png" />
<link rel="stylesheet" href="js/jquery-ui-1.11.4.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1><img src="imgs/cockatrice.png" align="absmiddle"/> Cockatrice web client</h1>
</header>
<div id="loading">
Loading cockatrice web client...
</div>
<div id="error-dialog" title="Error" style="display:none"></div>
<div id="info-dialog" title="Information" style="display:none"></div>
<div id="tabs" style="display:none">
<ul>
<li><a href="#tab-login">Login</a></li>
<li><a href="#tab-server">Server</a></li>
<li><a href="#tab-account">Account</a></li>
</ul>
<div id="tab-login">
<h3>Login to server</h3>
<label for="host">Host</label>
<input type="text" id="host" value="" />
<br/>
<label for="port">Port</label>
<input type="text" id="port" value ="4748" />
<br/>
<label for="user">Username</label>
<input type="text" id="user" />
<br/>
<label for="pass">Password</label>
<input type="password" id="pass" />
<br/>
<button id="loginnow">Connect</button>
<button id="quit" style="display:none">Disconnect</button>
<span id="status"></span>
</div>
<div id="tab-server">
<!--
<h3>Rooms</h3>
<span id="roomslist"></span>
-->
<h3>Server messages</h3>
<div id="servermessages"></div>
</div>
<div id="tab-account">
<div class="buddies-container">
<h3>Buddies</h3>
<ul id="buddies" size="10"></ul>
</div>
<div class="ignores-container">
<h3>Ignores</h3>
<ul id="ignores" size="10"></ul>
</div>
<div class="userinfo-container">
<h3>User info</h3>
<span id="userinfo"></span>
</div>
<div class="missingfeatures-container">
<h3>Missing features</h3>
<span id="features"></span>
</div>
<div class="clearfix"></div>
</div>
</div>
<script src="js/jquery-1.11.3.js"></script>
<script src="js/jquery-ui-1.11.4.js"></script>
<script src="js/long.js"></script>
<script src="js/bytebuffer.js"></script>
<script src="js/protobuf.js"></script>
<script src="webclient.js"></script>
<script>
$("#tabs").tabs();
$("#tabs").tabs("disable").tabs("enable", "tab-login");
$("#loading").hide();
$("#tabs").show();
$( "#host" ).autocomplete({
source: [
// add custom servers here
"cockatrice.woogerworks.com",
"vps.poixen.com",
"chickatrice.net",
"127.0.0.1"
]
});
$( document ).ready(function() {
function ts2time(ts) {
var d = new Date(Number(ts));
return ('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2) + ':' + ('0' + d.getSeconds()).slice(-2);
}
function getTime() {
var d = new Date();
return ('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2) + ':' + ('0' + d.getSeconds()).slice(-2);
}
function htmlEscape(msg) {
return $("<div>").text(msg).html();
}
function htmlSanitize(msg) {
var div = $("<div>").html(msg);
// remove all tags except some
var tagsWhitelist = "br,a,img,center,b,font";
$(div).find("*").not(tagsWhitelist).each(function() {
$(this).replaceWith(this.innerHTML);
});
// remove all attributes except some
var attributesWhitelist = ["href","color"];
$(div).find("*").each(function() {
var attributes = this.attributes;
var i = attributes.length;
while( i-- ) {
var attr = attributes[i];
if( $.inArray(attr.name,attributesWhitelist) == -1 )
this.removeAttributeNode(attr);
}
});
// permit only some protocols in href
var hrefWhitelist = ["http://","https://","ftp://","//"];
$(div).find("[href]").each(function() {
var attributeValue = $(this).attr('href');
for(var protocol in hrefWhitelist)
if(attributeValue.indexOf(hrefWhitelist[protocol]) == 0)
return;
$(this).removeAttr('href');
});
return $(div).html();
}
function timeAgoToInterval(secs)
{
var days = Math.floor(secs / 86400);
var years = Math.floor(days / 365);
days -= years * 365;
var txt = '';
switch(years)
{
case 0:
break
case 1:
txt += '1 year ';
break;
default:
txt += years + ' years ';
break;
}
switch(days)
{
case 0:
txt += '0 days ';
case 1:
txt += '1 day ';
break;
default:
txt += days + ' days ';
break;
}
return txt;
}
function decodeUserLevel(id) {
var levels = WebClient.pb.ServerInfo_User.UserLevelFlag;
if (id & levels.IsAdmin)
return "Administrator";
else if (id & levels.IsModerator)
return "Moderator";
else if (id & levels.IsRegistered)
return "Registered user";
else
return "Unregistered user";
}
function decodeGender(id) {
var genders = WebClient.pb.ServerInfo_User.Gender;
for(var key in genders)
{
if(id == genders[key])
return key;
}
return "Unknown";
}
$("#loginnow").click(connect);
$("#host, #port, #user, #pass").keydown(function(e) {
if (e.keyCode == 13) { connect(); }
});
function connect() {
var host = $("#host").val();
var port = $("#port").val();
if(!host.length || !port.length)
{
alert('Please enter a valid host and port.');
return;
}
var options = {
"debug": true,
"autojoinrooms" : true,
"host": host,
"port": port,
"user": $("#user").val(),
"pass": $("#pass").val(),
"statusCallback" : function(id, desc) {
$("#status").text(desc);
if(id == StatusEnum.LOGGEDIN)
{
$("#tabs").tabs("enable").tabs("option", "active", 1);
$("#loginnow").hide();
$("#quit").show();
} else {
$("#tabs").tabs("disable").tabs("enable", "tab-login").tabs("option", "active", 0);
$("#quit").hide();
$("#loginnow").show().prop("disabled", false);
// close rooms
$(".room-header, .room-container").remove();
}
},
"connectionClosedCallback" : function(id, desc) {
$("#status").text('Connection closed: ' + desc);
$("#tabs").tabs("disable").tabs("enable", "tab-login").tabs("option", "active", 0);
$("#quit").hide();
$("#loginnow").show().prop("disabled", false);
// close rooms
$(".room-header, .room-container").remove();
},
"serverMessageCallback" : function(message) {
$("#servermessages").append(htmlSanitize(message) + '<br/>');
},
"serverIdentificationCallback" : function(data) {
console.log('Connected to: ' + data.serverName + ' version ' + data.serverVersion + ' protocol ' + data.protocolVersion);
},
"serverShutdownCallback" : function(data) {
$("#info-dialog").text('The server is going to be restarted in ' + data.minutes + ' minute(s).\nAll running games will be lost.\nReason for shutdown: ' + data.reason);
$("#info-dialog").dialog();
},
"notifyUserCallback" : function(data) {
switch(data.type)
{
case WebClient.pb.Event_NotifyUser.NotificationType.PROMOTED:
$("#info-dialog").text('You have been promoted to moderator. Please log out and back in for changes to take effect.');
$("#info-dialog").dialog();
break;
case WebClient.pb.Event_NotifyUser.NotificationType.WARNING:
$("#info-dialog").text('You have received a warning due to ' + data.warningReason + '.\nPlease refrain from engaging in this activity or further actions may be taken against you. If you have any questions, please private message a moderator.');
$("#info-dialog").dialog();
break;
}
},
"userInfoCallback" : function(data) {
$("#userinfo").empty();
$.each(data.userInfo, function(key, value) {
// filter out inherited properties
if (!data.userInfo.hasOwnProperty(key))
return true;
// filter out empty values
if(value === null)
return true;
switch(key)
{
case 'avatarBmp':
$('#userinfo').prepend("<img id='avatar' src='data:image/JPEG;base64," + value.toBase64() + "' /><br/>");
break;
case 'accountageSecs':
$('#userinfo').append('Registered since: ' + timeAgoToInterval(value) + '<br/>');
break;
case 'userLevel':
$('#userinfo').append('User level: ' + decodeUserLevel(value) + '<br/>');
break;
case 'gender':
$('#userinfo').append('Gender: ' + decodeGender(value) + '<br/>');
break;
default:
$('#userinfo').append(key + ': ' + value + '<br/>');
break;
}
});
$('#buddies').empty();
$.each(data.buddyList, function(key, value) {
debugger;
$('#buddies').append('<li>' + value.name + '</li>');
});
$("#buddies").selectable();
$('#ignores').empty();
$.each(data.ignoreList, function(key, value) {
$('#ignores').append('<li>' + value.name + '</li>');
});
$("#ignores").selectable();
$("#features").empty();
$.each(data.missingFeatures, function(key, value) {
$('#features').append(value + '<br/>');
});
},
"listRoomsCallback" : function(rooms) {
// hide
//$("#roomslist").text(JSON.stringify(rooms, null, 4));
},
"errorCallback" : function(id, desc) {
$("#error-dialog").text(desc);
$("#error-dialog").dialog();
},
"joinRoomCallback" : function(room) {
$("div#tabs > ul").append(
"<li class='room-header'><a href='#tab-room-" + room["roomId"] + "'>" + room["name"] + "</a></li>"
);
$("div#tabs").append(
"<div class='room-container' id='tab-room-" + room["roomId"] + "'>" +
"<div class='userlist-container'>" +
"<h3>Userlist</h3>" +
"<ul class=\"userlist\" size=\"10\"></ul>" +
"</div><div class='chat-container'>" +
"<h3>Chat</h3>" +
"<div class=\"output\"></div>" +
"<br/><input type=\"text\" class=\"input\" />" +
"<button class=\"say\">say</button>" +
"</div></div><div class='clearfix'></div>"
);
$("div#tabs").tabs("refresh");
$("#tab-room-" + room["roomId"] + " .userlist").empty();
$.each(room["userList"], function(key, value) {
$("#tab-room-" + room["roomId"] + " .userlist").append('<li>' + value.name + '</li>');
});
$("#tab-room-" + room["roomId"] + " .userlist").selectable();
$("#tab-room-" + room["roomId"] + " .say").click(function() {
var msg = $("#tab-room-" + room["roomId"] + " .input").val();
$("#tab-room-" + room["roomId"] + " .input").val("");
WebClient.roomSay(room["roomId"], msg);
});
$("#tab-room-" + room["roomId"] + " .input").keydown(function(e) {
if (e.keyCode == 13) {
var msg = $("#tab-room-" + room["roomId"] + " .input").val();
$("#tab-room-" + room["roomId"] + " .input").val("");
WebClient.roomSay(room["roomId"], msg);
}
});
},
"roomMessageCallback" : function(roomId, message) {
var text;
switch(message["messageType"])
{
case WebClient.pb.Event_RoomSay.RoomMessageType.Welcome:
text = "<span class='serverwelcome'>" + htmlEscape(message["message"]) + "</span>";
break;
case WebClient.pb.Event_RoomSay.RoomMessageType.ChatHistory:
text = "<span class='chathistory'>[" + ts2time(message["timeOf"]) + "] " + htmlEscape(message["message"]) + "</span>";
break;
default:
text = "[" + getTime() + "] " + htmlEscape(message["name"]) + ": " + htmlEscape(message["message"]);
break;
}
$("#tab-room-" + roomId + " .output").append(text + '<br/>');
},
};
$(this).prop("disabled", true);
WebClient.connect(options);
};
$("#quit").click(function() {
WebClient.disconnect();
});
});
</script>
</body>
</html>