chat
chat
to your build and add startChat()
to your init.go to #import getscript
#import parallel
#import peer
#import jquery
function isconnchatserver(conn,csid,peer,cb) {
console.log('isconnchatserver',conn);
var cbd = false;
var timeout;
conn.send(JSON.stringify({cmd:'register',user:user}));
conn.on('data',data=>{
data = JSON.parse(data);
if(!cbd && data.cmd=='registered') {
console.log('Registered successfully');
cbd = true;
clearTimeout(timeout);
cb(data.registered);
}
});
timeout=setTimeout(()=>{
cbd = true;
cb(false);
},1000); //We did not get a useful response for too long.
}
function connecttocsidasserver(csid,peer,cb) {
console.log('connecttocsidasserver',csid,peer);
//returns (err,connection)
var conn = peer.connect(csid);
conn.on('open',()=>{
isconnchatserver(conn,csid,peer,yes=>{
if(!yes) { return cb('Is not a chat server'); }
cb(null,conn);
});
});
peer.on('error',error=>{
if(error.message === 'Could not connect to peer '+csid) {
cb(error);
}
});
}
function connecttopeerasserver(peername,peer,cb) {
console.log('connecttopeerasserver',peername,peer);
$.post('/peer/getid/'+peername,csid=>{
if(!csid) {
return cb('Peer does not have id');
}
connecttocsidasserver(csid,peer,cb);
});
}
function runityourself(servername,peer,cb) {
console.log('runityourself',peer);
$.post('/announce/chat-server/'+servername);
var iframe = $('<iframe>').attr('src','/app/chat-server');
$('body').append(iframe);
setTimeout(()=>{
connecttopeerasserver(window.user,peer,(error,conn)=>{
if(error) { return console.log('iframe error',error); }
cb(conn);
})
},5000);
}
function tryconnectingtopeers(servername,peer,cb) {
console.log('tryconectingtopeers',servername,peer);
$.post('/findpeers/chat-server/'+servername,peerlist=>{
peerlist = peerlist.reverse();
console.log('peerlist',peerlist);
mapserialuntilsuccess(peerlist,(trypeer,cb)=>{
if(trypeer == window.user) { return cb('Can\'t connect to self at this stage.'); }
console.log('trypeer',trypeer);
connecttopeerasserver(trypeer,peer,cb);
},cb);
});
}
function get_chat_server_conn(servername,cb,first) {
var registernewpeercount=0;
registernewpeer(peer=>{
console.log('registernewpeercount',++registernewpeercount);
window.peer = peer;
peer.on('error',error=>{
console.log('peer error:',error);
window.args = error;
});
peer.on('connection',conn=>{
conn.send(JSON.stringify({cmd:'registered',registered:'false'})); //Reject peers right away so they can find good peers quicker;
});
var csidcount=0;
connecttopeerasserver(servername,peer,(error,conn)=>{
if(error) {
return tryconnectingtopeers(servername,peer,(error,conn)=>{
console.log('returned from tryconnectedtopeers');
if(error) {
console.log('error causes runityourself',error);
return runityourself(servername,peer,cb);
}
cb(conn);
});
}
cb(conn);
});
});
}
function startChat(servername) {
servername = location.pathname.replace(/\/run/,'/').replace(/\/app\/\w+\/?/,'/').split('/').slice('-1')[0] || servername || 'chat-server';
console.log('servername',servername);
init_chat_ui();
getjquery(()=>{
});
get_chat_server_conn(servername,conn=>{
console.log('conn',conn);
$('h3').text('Chatting');
add_user_to_ui(user);
function sendclick (e) {
var msg=$('#compose').val().trim();
if(msg.length===0) {
return;
}
conn.send(JSON.stringify({cmd:'post',user:user,msg:msg}));
add_msg_to_ui(user,msg);
$('#compose').val('');
}
$('#send').click(sendclick);
$('#compose').on('keydown',e=>{
if(e.keyCode==13) {
e.preventDefault();
sendclick(e);
}
});
conn.on('data',handle_server_msg);
});
}
function getchathtml () {
return `
<div class="dialogform" title="Embed">
<input class="image hide" id="imagetyperadio" type="radio" name="type" value="jpg"/>
<label for="imagetyperadio" class="hide">Image</label>
<input class="video" id="videotyperadio" type="radio" name="type" value="mp4"/>
<label for="videotyperadio">Video</label>
<input class="poll" id="polltyperadio" type="radio" name="type" value="poll"/>
<lable for="polltyperadio">Poll</lable><br/>
<input class="title" placeholder="Title (optional)"/><br class="onvideo onimage"/>
<input class="onimage onvideo url" placeholder="Url"/><br class="onvideo"/>
<input class="onvideo" id="syncbox" type="checkbox"/>
<label class="onvideo" for="syncbox">Synchonized</label><br class="onsync"/>
<input class="onsync" id="loopbox" type="checkbox"/>
<label class="onsync" for="loopbox">Loop</label><br class="onsync"/>
<input class="onsync delay" placeholder="Delay, use h,m,s suffixes"/><br class="onpoll"/>
<input class="onpoll polloption" placeholder="Option"/><br/>
<button>Post</button>
</div>
<div class="flow flowv container">
<div class="flow flowh chat">
<table class="content flow flowv">
<tbody>
</tbody>
</table>
<div class="flow flowv users" id="joined">
<h3>Connecting</h3>
<ul>
</ul>
</div>
</div>
<div class="flow flowh compose">
<span class="self" title="Embed"></span>
<textarea id='compose' placeholder='Send message...'></textarea>
<button id='send' class='hide'>Send</button>
</div>
</div>
<div class="side flow flowv">
</div>`
}
function media_filters () {
return [video_msg_filter,poll_msg_filter];
}
function media_message_to_td(msg,user) {
console.log('msg',msg);
var media_attributes=JSON.parse(msg.replace('::{"','{"'));
var msgtd;
media_filters().some(mf=>{
var [handled,tmpmsgtd]=mf(media_attributes,user);
if(handled) {
msgtd=tmpmsgtd;
return true;
}
return false;
});
return msgtd;
}
function video_msg_filter(attr,user) {
if(attr.type==='mp4') {
var td = $('<td>').html((attr.title||attr.url)+'<button class="embedbutton">embed'+(attr.sync?' sync':'')+(attr.loop?' loop':'')+'</button>');
td.css('color','blue').css('text-decoration','underline').css('cursor','pointer');
td.click(()=>{
display_mp4_in_chat(attr.url,attr.sync?attr.start:null,attr.loop);
});
return [true,td];
}
return [false,null];
}
function poll_msg_filter(attr,user) {
if(attr.type=='poll') {
var td = $('<td>').attr('id','poll'+attr.id);
td[0].votes=[];
var title = $('<h3>').text('Poll: '+attr.title)
.css('font-size','24px')
.css('font-family','Helvetica Neue",Helvetica,Arial,sans-serif')
.css('font-weight','600')
.css('color','#068f06')
.css('margin','16px 0 8px 0');
td.append(title);
var table = $('<table>');
var tbody = $('<tbody>');
table.append(tbody);
attr.options.forEach((o,i)=>{
var clicked = false;
var tr=$('<tr>').css('cursor','pointer');
var count=$('<td>').text(0);
var option = $('<td>').text(o);
td[0].votes[i]={};
tbody.append(tr.append(count,option));
tr.click(()=>{
var retr={};
retr.type='pollvote'
retr.forid=attr.id;
retr.for=i;
if(clicked) {
retr.nullify=true;
}
clicked=!clicked;
$('#compose').val('::'+JSON.stringify(retr));
$('#send').click();
});
});
td.append(table);
return [true,td];
}
else if(attr.type=='pollvote') {
var polltd = $('#poll'+attr.forid)
var votes = polltd[0].votes[attr.for];
if(attr.nullify) {
delete votes[user];
}
else {
votes[user]=true;
}
polltd.find(`tr:nth-child(${attr.for+1}) td`).first().text(Object.keys(votes).length);
return [true,null];
}
else {
return [false,null];
}
}
function add_msg_to_ui(user,msg) {
var msgtd;
if(msg.startsWith('::{"')) {
msgtd = media_message_to_td(msg,user);
}
else {
msgtd = $('<td>').html(msg).addClass('msg');
}
if(!msgtd) {
return;
}
var table = $('.chat > table.content');
var tr = $('<tr>').attr('data-user',user);
var usertext;
if(table.find('tr').last().attr('data-user')==user) {
usertext="";
}
else {
usertext=user;
}
var usertd = $('<td>').append($('<a>').attr('href','/u/'+user).attr('target','_blank').html(usertext)).addClass('user');
var t1 = table[0];
var isbottom = t1.scrollTop === t1.scrollTopMax;
table.find('>tbody').append(tr.append(usertd).append(msgtd));
if(isbottom) {
t1.scrollTop = t1.scrollTopMax;
}
}
function add_user_to_ui(user) {
if($('#user_'+user).length===0) {
$('#joined ul').append($('<li>').attr('id','user_'+user).append($('<a>').attr('href','/u/'+user).attr('target','_blank').html(user+(user==window.user?'(you)':''))));
if(user==window.user) {
$('.compose .self').append($('<span>').html(user));
}
}
}
function remove_user_from_ui(user) {
$('#joined #user_'+user).remove();
}
function handle_server_msg(data) {
data=JSON.parse(data);
console.log('revieved',data);
if(data.cmd=='post'&&data.msg&&data.user) {
add_msg_to_ui(data.user,data.msg);
}
else if(data.cmd=='left'&&data.user) {
remove_user_from_ui(data.user);
}
else if(data.cmd=='joined'&&data.user) {
add_user_to_ui(data.user);
}
else if(data.cmd=='posts'&&data.msg) {
data.msg.forEach(msg=>{
add_msg_to_ui(msg.user,msg.msg);
});
}
else if(data.cmd=='peerlist'&&data.msg) {
data.msg.forEach(add_user_to_ui);
}
}
function getchatcss () {
return `
/*@import https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css;
@import https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.structure.min.css;
@import https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.theme.min.css;*/
html,body {
width:100%;
height:100%;
margin:0;
padding:0;
display:flex;
font-family:sans;
}
ul {
list-style-type: none;
padding:0;
margin:0;
height:auto;
}
.flow {
display:flex;
}
.flowv {
flex-flow:column;
}
.flowh {
flex-flow:row;
}
.users {
}
.chat {
flex: 1 1 auto
}
.content {
flex:1 1 auto;
overflow-y:scroll;
}
.compose {
background:#5668b5;
height:2em;
border:8px solid #f6f6f6;
}
.compose .self {
background: #f0f0f0;
padding: 4px 16px;
margin: 2px;
align-content: center;
cursor:pointer
}
.compose textarea {
flex: 1 1 auto;
resize:none;
padding-top:4px;
padding-left:4px;
}
.indent {
padding-left:74px;
}
td {
vertical-align:middle;
padding-left:8px;
}
.content .user {
width:75px;
overflow-y:wrap;
text-align:right;
color:green;
}
.container {
width:100%;
}
body.openside .container {
max-width:50%;
}
body.openside .chat {
flex-direction:row-reverse;
}
#joined {
width:200px;
background-color:#f6f6f6;
}
#joined a {
color:black;
text-decoration:none;
}
#joined a:hover {
text-decoration:underline;
}
/*.self .more {
display:none;
position:absolute;
margin-top:0;
transition:margin-top 0.5s;
height:100px;
width:100px;
}
.more.show {
display:block;
margin-top:-37px;
}
.more li:hover {
background:lightblue;
}*/
.dialogform {
display:none;
}
video {
max-width:100%;
max-height:100%;
flex-shrink:1;
}
.side {
font-size:1.8em;
}
.side i {
background:#ffffff4d;
}
.side i:hover {
font-size:1.4em;
}
.side .control {
transition:opacity 0.4s;
}
.side.hasinteracted .control {
opacity:0;
}
.side .control:hover {
font-size:0.8823529411764706em;
opacity:1;
}
.hide {
display:none;
}
.container > .chat {
overflow-y:scroll;
}
.dialogform .onimage, .dialogform .onvideo, .dialogform .onpoll, .dialogform .onsync {
display:none;
}
.dialogform.onimage .onimage, .dialogform.onvideo .onvideo, .dialogform.onpoll .onpoll, .dialogform.onvideo.onsync .onsync {
display:inline;
}
td.user {
max-width:128px;
overflow:hidden;
}
td.msg {
min-width:256px;
}
body.openside #joined {
overflow:hidden;
}
iframe {
display:none;
}
`
}
function register_embed_form_listeners () {
$('#imagetyperadio').click(()=>{
$('.dialogform').addClass('onimage')
.removeClass('onvideo')
.removeClass('onpoll');
});
$('#videotyperadio').click(()=>{
$('.dialogform').addClass('onvideo')
.removeClass('onimage')
.removeClass('onpoll');
});
$('#polltyperadio').click(()=>{
$('.dialogform').addClass('onpoll')
.removeClass('onimage')
.removeClass('onvideo');
});
$('#syncbox').click(()=>{
if($('#syncbox').prop('checked')) {
$('.dialogform').addClass('onsync');
}
else {
$('#syncbox').removeClass('onsync');
}
});
$('.dialogform .polloption')[0].addEventListener('keydown',bottomoptionkeydown);
$('.dialogform button').click(()=>{
console.log('clicked');
var embedobject = makeembedobject();
if(!embedobject) {
return;
}
$('#compose').val('::'+JSON.stringify(embedobject));
//sendclick();
$('#send').click();
$('.ui-dialog-titlebar-close').click();
});
}
function bottomoptionkeydown () {
var newoption = $('<input>').addClass('polloption')
.addClass('onpoll')
.attr('placeholder','Option');
var lastoption = $(this);
var br = $('<br>').addClass('onpoll');
lastoption[0].removeEventListener('keydown',bottomoptionkeydown);
newoption[0].addEventListener('keydown',bottomoptionkeydown);
lastoption.change(()=>{
if(lastoption.val().length===0) {
lastoption.remove();
br.remove();
}
});
lastoption.after(newoption);
lastoption.after(br);
}
function makeembedobject () {
var retr = {};
var form = $('.dialogform');
var thetype=form.find("input[name='type']:checked").val();
if(!thetype) {
alert('Please select a type');
return null;
}
retr['type']=thetype;
var title=form.find('.title').val();
if(title) {
retr.title = title;
}
if(thetype=='jpg'||thetype=='mp4') {
var url=form.find('.url').val();
if(!url) {
alert('Missing URL');
return null;
}
retr.url = url;
}
if(thetype=='mp4') {
if($('#syncbox').prop('checked')) {
retr.sync=true;
if($('#loopbox').prop('checked')) {
retr.loop=true;
}
var delay=form.find('.delay').val();
var seconds=0;
delay.split(' ').forEach(i=>{
var value = parseFloat(i);
var mult = 1;
if(isNaN(value)) {
return;
}
switch(i.slice(-1)) {
case 'w':
mult*=7;
case 'd':
mult*=24;
case 'h':
mult*=60;
case 'm':
mult*=60
}
seconds+=value*mult;
});
retr.start = Date.now()+seconds*1000;
}
}
if(thetype=='poll') {
retr.id = Math.floor(Math.random()*10000);
retr.options = form.find('.polloption').toArray().map(e=>{
return e.value
}).filter(e=>{
return e.length;
});
}
form.find('input').empty();
['image,video,poll,sync'].forEach(kind=>{
form.removeClass('on'+kind);
});
retr.target='side';
return retr;
}
function init_chat_ui () {
//We want a user list on the right
//We want a text bar on the bottom
//We want a content on the top
add_style(getchatcss());
setTimeout(()=>{
document.body.innerHTML=getchathtml();
getcss('https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css');
getcss('https://use.fontawesome.com/releases/v5.6.3/css/all.css');
getscripts(['https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js','https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js'],()=>{
register_embed_form_listeners();
$('.compose .self').click(()=>{
$('.dialogform').dialog();
});
});
//add_ui_listeners();
},50);
}
function start (servername) {
startChat(servername);
}
function display_mp4_in_chat(mp4url,start,loop) {
var video = $('<video>').attr('src',mp4url).attr('controls',true).attr('autoplay',true).attr('loop',loop);
$('body').addClass('flex').addClass('flowh').addClass('openside');
var controls = $('<div>').addClass('control')
.attr('position','absolute')
.css('width','100%')
.css('height','0')
.css('text-align','right')
.css('z-index',1);
var close = $('<i>').addClass('far').addClass('fa-window-close');
var grow = $('<i>').addClass('far').addClass('fa-plus-square');
var shrink = $('<i>').addClass('far').addClass('fa-minus-square');
$('body > .side').prepend(video);
$('body > .side').prepend(controls.append(shrink).append(grow).append(close));
close.click(()=>{
controls.remove();
video.remove();
if($('body > .side').children().length===0) {
$('body').removeClass('openside');
$('body .container').css('max-width','');
$('body > .side').removeClass('hasinteracted');
}
});
grow.click(()=>{
$('.container').css('max-width',parseInt($('.container').css('max-width'))-10+'%');
});
shrink.click(()=>{
$('.container').css('max-width',parseInt($('.container').css('max-width'))+10+'%');
});
controls.find('i').mouseover(()=>{
$('.side').addClass('hasinteracted');
});
var initialnow = Date.now();
function resync () {
if(start>Date.now()) {
video[0].pause();
video[0].controls=false;
setTimeout(resync,start-Date.now());
setTimeout(()=>{
video[0].play();
video[0].controls=true;
initialnow = start;
},start-Date.now());
return;
}
var currentTime = ((Date.now()-start)%(loop?video[0].duration*1000:Infinity))/1000;
video[0].currentTime = currentTime;
console.log('offset set',currentTime,start);
setTimeout(resync,(Date.now()-initialnow)*3);
}
if(start) {
video[0].addEventListener('loadedmetadata',resync,false);
}
}
startChat('');
wchat