2021-04-26 19:34:24 +02:00
"use strict" ;
2021-05-02 16:46:05 +02:00
let identityName = undefined ;
2021-04-26 19:34:24 +02:00
let socket = null ;
let notificationAllowed = false ;
2021-05-04 22:26:36 +02:00
let localIps = [ ] ;
2021-04-28 17:57:49 +02:00
let currentSessionId = - 1 ;
let sessionsData = new Map ( ) ;
let msgHistory = new Map ( ) ;
2021-05-06 18:24:29 +02:00
let pendingFilesTransfers = new Map ( ) ;
2021-04-26 19:34:24 +02:00
2021-04-28 17:57:49 +02:00
function onClickSession ( event ) {
let sessionId = event . currentTarget . getAttribute ( "data-sessionId" ) ;
if ( sessionId != null ) {
currentSessionId = sessionId ;
let session = sessionsData . get ( sessionId ) ;
2021-04-26 19:34:24 +02:00
if ( ! session . seen ) {
session . seen = true ;
2021-04-28 17:57:49 +02:00
socket . send ( "set_seen " + sessionId ) ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
displaySessions ( ) ;
displayHeader ( ) ;
displayChatBottom ( ) ;
2021-05-10 20:09:02 +02:00
displayHistory ( ) ;
2021-04-26 19:34:24 +02:00
}
}
let ip _input = document . getElementById ( "ip_input" ) ;
ip _input . addEventListener ( "keyup" , function ( event ) {
2021-05-04 11:51:08 +02:00
if ( event . key === "Enter" ) {
2021-04-26 19:34:24 +02:00
socket . send ( "connect " + ip _input . value ) ;
ip _input . value = "" ;
}
} ) ;
2021-05-04 22:26:36 +02:00
document . getElementById ( "show_local_ips" ) . onclick = function ( ) {
let mainDiv = document . createElement ( "div" ) ;
let h2Title = document . createElement ( "h2" ) ;
h2Title . textContent = "Your IP addresses:" ;
mainDiv . appendChild ( h2Title ) ;
let ul = document . createElement ( "ul" ) ;
ul . classList . add ( "ips" ) ;
for ( let i = 0 ; i < localIps . length ; ++ i ) {
let li = document . createElement ( "li" ) ;
li . textContent = localIps [ i ] ;
ul . appendChild ( li ) ;
}
mainDiv . appendChild ( ul ) ;
showPopup ( mainDiv ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
let message _input = document . getElementById ( "message_input" ) ;
message _input . addEventListener ( "keyup" , function ( event ) {
2021-05-04 11:51:08 +02:00
if ( event . key === "Enter" ) {
2021-04-28 17:57:49 +02:00
socket . send ( "send " + currentSessionId + " " + message _input . value ) ;
2021-04-26 19:34:24 +02:00
message _input . value = "" ;
}
} ) ;
document . getElementById ( "delete_conversation" ) . onclick = function ( ) {
2021-04-28 17:57:49 +02:00
let mainDiv = document . createElement ( "div" ) ;
mainDiv . appendChild ( generatePopupWarningTitle ( ) ) ;
2021-04-26 19:34:24 +02:00
let p1 = document . createElement ( "p" ) ;
p1 . textContent = "Deleting a conversation only affects you. Your contact will still have a copy of this conversation if he/she doesn't delete it too." ;
let p2 = document . createElement ( "p" ) ;
p2 . textContent = "Do you really want to delete all this conversation (messages and files) ?" ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( p1 ) ;
mainDiv . appendChild ( p2 ) ;
2021-04-26 19:34:24 +02:00
let button = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
button . classList . add ( "classic_button" ) ;
2021-04-26 19:34:24 +02:00
button . textContent = "Delete" ;
button . onclick = function ( ) {
2021-04-28 17:57:49 +02:00
socket . send ( "delete_conversation " + currentSessionId ) ;
msgHistory . get ( currentSessionId ) . length = 0 ;
removePopup ( ) ;
2021-05-10 20:09:02 +02:00
displayHistory ( ) ;
} ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( button ) ;
showPopup ( mainDiv ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
document . getElementById ( "add_contact" ) . onclick = function ( ) {
2021-04-28 17:57:49 +02:00
socket . send ( "contact " + currentSessionId + " " + sessionsData . get ( currentSessionId ) . name ) ;
2021-05-02 16:46:05 +02:00
sessionsData . get ( currentSessionId ) . isContact = true ;
2021-04-28 17:57:49 +02:00
displayHeader ( ) ;
displaySessions ( ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
document . getElementById ( "remove_contact" ) . onclick = function ( ) {
2021-04-28 17:57:49 +02:00
let mainDiv = document . createElement ( "div" ) ;
mainDiv . appendChild ( generatePopupWarningTitle ( ) ) ;
2021-04-26 19:34:24 +02:00
let p1 = document . createElement ( "p" ) ;
p1 . textContent = "Deleting contact will remove her/his identity key and your conversation (messages and files). You won\'t be able to recognize her/him anymore. This action only affects you." ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( p1 ) ;
2021-04-26 19:34:24 +02:00
let p2 = document . createElement ( "p" ) ;
p2 . textContent = "Do you really want to remove this contact ?" ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( p2 ) ;
2021-04-26 19:34:24 +02:00
let button = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
button . classList . add ( "classic_button" ) ;
2021-04-26 19:34:24 +02:00
button . textContent = "Delete" ;
button . onclick = function ( ) {
2021-04-28 17:57:49 +02:00
socket . send ( "uncontact " + currentSessionId ) ;
let session = sessionsData . get ( currentSessionId ) ;
2021-05-02 16:46:05 +02:00
session . isContact = false ;
session . isVerified = false ;
if ( ! session . isOnline ) {
2021-04-28 17:57:49 +02:00
sessionsData . delete ( currentSessionId ) ;
msgHistory . get ( currentSessionId ) . length = 0 ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
displayHeader ( ) ;
displaySessions ( ) ;
2021-05-10 20:09:02 +02:00
displayHistory ( ) ;
2021-04-28 17:57:49 +02:00
removePopup ( ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( button ) ;
showPopup ( mainDiv ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
document . getElementById ( "verify" ) . onclick = function ( ) {
2021-05-02 16:46:05 +02:00
let session = sessionsData . get ( currentSessionId ) ;
if ( typeof session !== "undefined" ) {
let mainDiv = document . createElement ( "div" ) ;
mainDiv . appendChild ( generatePopupWarningTitle ( ) ) ;
let instructions = document . createElement ( "p" ) ;
instructions . textContent = "Compare the following fingerprints by a trusted way of communication (such as real life) and be sure they match." ;
mainDiv . appendChild ( instructions ) ;
let p _local = document . createElement ( "p" ) ;
p _local . textContent = "Local fingerprint:" ;
mainDiv . appendChild ( p _local ) ;
let pre _local = document . createElement ( "pre" ) ;
pre _local . textContent = beautifyFingerprint ( identityFingerprint ) ;
mainDiv . appendChild ( pre _local ) ;
let p _peer = document . createElement ( "p" ) ;
p _peer . textContent = "Peer fingerprint:" ;
mainDiv . appendChild ( p _peer ) ;
let pre _peer = document . createElement ( "pre" ) ;
pre _peer . textContent = beautifyFingerprint ( session . fingerprint ) ;
mainDiv . appendChild ( pre _peer ) ;
let buttonRow = document . createElement ( "div" ) ;
buttonRow . classList . add ( "button_row" ) ;
let verifyButton = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
verifyButton . classList . add ( "classic_button" ) ;
2021-05-02 16:46:05 +02:00
verifyButton . textContent = "They match" ;
verifyButton . onclick = function ( ) {
socket . send ( "verify " + currentSessionId ) ;
sessionsData . get ( currentSessionId ) . isVerified = true ;
removePopup ( ) ;
displayHeader ( ) ;
displaySessions ( ) ;
} ;
buttonRow . appendChild ( verifyButton ) ;
let cancelButton = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
cancelButton . classList . add ( "classic_button" ) ;
2021-05-02 16:46:05 +02:00
cancelButton . textContent = "They don't match" ;
cancelButton . onclick = removePopup ;
buttonRow . appendChild ( cancelButton ) ;
mainDiv . appendChild ( buttonRow ) ;
showPopup ( mainDiv ) ;
}
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
document . getElementById ( "logout" ) . onclick = function ( ) {
2021-04-28 17:57:49 +02:00
let mainDiv = document . createElement ( "div" ) ;
mainDiv . appendChild ( generatePopupWarningTitle ( ) ) ;
2021-04-26 19:34:24 +02:00
let p _warning = document . createElement ( "p" ) ;
p _warning . textContent = "If you log out, you will no longer receive messages and pending messages will not be sent until you log in back." ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( p _warning ) ;
2021-04-26 19:34:24 +02:00
let p _ask = document . createElement ( "p" ) ;
p _ask . textContent = "Do you really want to log out ?" ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( p _ask ) ;
2021-04-26 19:34:24 +02:00
let button = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
button . classList . add ( "classic_button" ) ;
2021-04-26 19:34:24 +02:00
button . textContent = "Log out" ;
button . onclick = logout ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( button ) ;
showPopup ( mainDiv ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
document . getElementById ( "attach_file" ) . onchange = function ( event ) {
2021-05-06 18:24:29 +02:00
let files = event . target . files ;
let useLargeFileTransfer = false ;
for ( let i = 0 ; i < files . length ; ++ i ) {
if ( files [ i ] . size > 32760000 ) {
useLargeFileTransfer = true ;
break ;
}
}
if ( useLargeFileTransfer ) {
if ( pendingFilesTransfers . has ( currentSessionId ) ) {
2021-05-05 18:00:03 +02:00
let mainDiv = document . createElement ( "div" ) ;
mainDiv . appendChild ( generatePopupWarningTitle ( ) ) ;
let p = document . createElement ( "p" ) ;
p . textContent = "Another file transfer is already in progress." ;
mainDiv . appendChild ( p ) ;
showPopup ( mainDiv ) ;
} else {
2021-05-06 18:24:29 +02:00
let fileTransfers = [ ] ;
let fileInfo = "" ;
for ( let i = 0 ; i < files . length ; ++ i ) {
fileTransfers . push ( {
"file" : files [ i ] ,
"name" : files [ i ] . name ,
"size" : files [ i ] . size ,
"transferred" : 0 ,
"lastChunk" : Date . now ( )
} ) ;
fileInfo += ' ' + files [ i ] . size + ' ' + b64EncodeUnicode ( files [ i ] . name ) ;
} ;
pendingFilesTransfers . set ( currentSessionId , {
"files" : fileTransfers ,
"index" : 0 ,
2021-04-28 17:57:49 +02:00
"state" : "waiting" ,
} ) ;
2021-05-06 18:24:29 +02:00
socket . send ( "large_files " + currentSessionId + fileInfo ) ;
2021-04-28 17:57:49 +02:00
displayChatBottom ( ) ;
}
2021-04-26 19:34:24 +02:00
} else {
2021-05-06 18:24:29 +02:00
for ( let i = 0 ; i < files . length ; ++ i ) {
let formData = new FormData ( ) ;
formData . append ( "session_id" , currentSessionId ) ;
formData . append ( "" , files [ i ] ) ;
fetch ( "/send_file" , { method : "POST" , body : formData } ) . then ( response => {
if ( response . ok ) {
response . text ( ) . then ( uuid => onFileSent ( currentSessionId , uuid , files [ i ] . name ) ) ;
} else {
console . log ( response ) ;
}
} ) ;
} ;
2021-04-26 19:34:24 +02:00
}
2021-05-10 20:09:02 +02:00
} ;
2021-04-28 17:57:49 +02:00
document . getElementById ( "file_cancel" ) . onclick = function ( ) {
socket . send ( "abort " + currentSessionId ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
let msg _log = document . getElementById ( "msg_log" ) ;
msg _log . onscroll = function ( ) {
2021-05-02 16:46:05 +02:00
if ( sessionsData . get ( currentSessionId ) . isContact ) {
2021-04-26 19:34:24 +02:00
if ( msg _log . scrollTop < 30 ) {
2021-04-28 17:57:49 +02:00
socket . send ( "load_msgs " + currentSessionId ) ;
2021-04-26 19:34:24 +02:00
}
}
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
let profile _div = document . querySelector ( "#me>div" ) ;
profile _div . onclick = function ( ) {
2021-04-28 17:57:49 +02:00
let mainDiv = document . createElement ( "div" ) ;
2021-05-02 16:46:05 +02:00
let avatar = generateAvatar ( identityName ) ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( avatar ) ;
2021-05-02 16:46:05 +02:00
let fingerprint = document . createElement ( "pre" ) ;
fingerprint . id = "identity_fingerprint" ;
fingerprint . textContent = beautifyFingerprint ( identityFingerprint ) ;
mainDiv . appendChild ( fingerprint ) ;
2021-04-28 17:57:49 +02:00
let sectionName = document . createElement ( "section" ) ;
2021-05-13 20:28:45 +02:00
let titleName = document . createElement ( "h3" ) ;
titleName . textContent = "Name:" ;
sectionName . appendChild ( titleName ) ;
2021-04-28 17:57:49 +02:00
let inputName = document . createElement ( "input" ) ;
inputName . id = "new_name" ;
inputName . type = "text" ;
2021-05-02 16:46:05 +02:00
inputName . value = identityName ;
2021-04-28 17:57:49 +02:00
sectionName . appendChild ( inputName ) ;
let saveNameButton = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
saveNameButton . classList . add ( "classic_button" ) ; ;
2021-04-28 17:57:49 +02:00
saveNameButton . textContent = "Save" ;
saveNameButton . onclick = function ( ) {
2021-04-26 19:34:24 +02:00
socket . send ( "change_name " + document . getElementById ( "new_name" ) . value ) ;
} ;
2021-04-28 17:57:49 +02:00
sectionName . appendChild ( saveNameButton ) ;
mainDiv . appendChild ( sectionName ) ;
2021-05-13 20:28:45 +02:00
let sectionPadding = document . createElement ( "section" ) ;
sectionPadding . appendChild ( generateSwitchPreference ( "Use PSEC padding" , "PSEC padding obfuscates the length of your messages but uses more network bandwidth." , usePadding , function ( checked ) {
socket . send ( "set_use_padding " + checked ) ;
usePadding = checked ;
} ) ) ;
mainDiv . appendChild ( sectionPadding ) ;
2021-04-28 17:57:49 +02:00
let sectionPassword = document . createElement ( "section" ) ;
2021-05-13 20:28:45 +02:00
let titlePassword = document . createElement ( "h3" ) ;
titlePassword . textContent = "Change your password:" ;
sectionPassword . appendChild ( titlePassword ) ;
2021-04-28 17:57:49 +02:00
if ( isIdentityProtected ) {
2021-04-26 19:34:24 +02:00
let input _old _password = document . createElement ( "input" ) ;
input _old _password . type = "password" ;
input _old _password . placeholder = "Current password" ;
2021-04-28 17:57:49 +02:00
sectionPassword . appendChild ( input _old _password ) ;
}
let inputPassword1 = document . createElement ( "input" ) ;
let inputPassword2 = document . createElement ( "input" ) ;
inputPassword1 . type = "password" ;
inputPassword1 . placeholder = "New password (empty for no password)" ;
inputPassword2 . type = "password" ;
inputPassword2 . placeholder = "New password (confirmation)" ;
sectionPassword . appendChild ( inputPassword1 ) ;
sectionPassword . appendChild ( inputPassword2 ) ;
let errorMsg = document . createElement ( "p" ) ;
errorMsg . id = "password_errorMsg" ;
errorMsg . style . color = "red" ;
sectionPassword . appendChild ( errorMsg ) ;
let changePasswordButton = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
changePasswordButton . classList . add ( "classic_button" ) ;
2021-04-28 17:57:49 +02:00
changePasswordButton . textContent = "Change password" ;
changePasswordButton . onclick = function ( ) {
2021-04-26 19:34:24 +02:00
let inputs = document . querySelectorAll ( "input[type=\"password\"]" ) ;
2021-04-28 17:57:49 +02:00
let newPassword , newPasswordConfirm ;
if ( isIdentityProtected ) {
newPassword = inputs [ 1 ] ;
newPasswordConfirm = inputs [ 2 ] ;
2021-04-26 19:34:24 +02:00
} else {
2021-04-28 17:57:49 +02:00
newPassword = inputs [ 0 ] ;
newPasswordConfirm = inputs [ 1 ] ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
if ( newPassword . value == newPasswordConfirm . value ) {
let newPassword _set = newPassword . value . length > 0 ;
if ( isIdentityProtected || newPassword _set ) { //don't change password if identity is not protected and new password is blank
2021-04-26 19:34:24 +02:00
let msg = "change_password" ;
2021-04-28 17:57:49 +02:00
if ( isIdentityProtected ) {
2021-05-06 18:24:29 +02:00
msg += " " + b64EncodeUnicode ( inputs [ 0 ] . value ) ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
if ( newPassword _set ) {
2021-05-06 18:24:29 +02:00
msg += " " + b64EncodeUnicode ( newPassword . value ) ;
2021-04-26 19:34:24 +02:00
}
socket . send ( msg ) ;
} else {
2021-04-28 17:57:49 +02:00
removePopup ( ) ;
2021-04-26 19:34:24 +02:00
}
} else {
2021-04-28 17:57:49 +02:00
newPassword . value = "" ;
newPasswordConfirm . value = "" ;
errorMsg . textContent = "Passwords don't match" ;
2021-04-26 19:34:24 +02:00
}
} ;
2021-04-28 17:57:49 +02:00
sectionPassword . appendChild ( changePasswordButton ) ;
mainDiv . appendChild ( sectionPassword ) ;
let sectionDelete = document . createElement ( "section" ) ;
2021-05-13 20:28:45 +02:00
let deleteTitle = document . createElement ( "h3" ) ;
deleteTitle . textContent = "Delete identity:" ;
sectionDelete . appendChild ( deleteTitle ) ;
2021-04-28 17:57:49 +02:00
sectionDelete . style . borderTop = "1px solid red" ;
2021-04-26 19:34:24 +02:00
let p = document . createElement ( "p" ) ;
p . textContent = "Deleting your identity will delete all your conversations (messages and files), all your contacts, and your private key. You won't be able to be recognized by your contacts anymore." ;
p . style . color = "red" ;
2021-04-28 17:57:49 +02:00
sectionDelete . appendChild ( p ) ;
let deleteButton = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
deleteButton . classList . add ( "classic_button" ) ;
2021-04-28 17:57:49 +02:00
deleteButton . textContent = "Delete" ;
deleteButton . style . backgroundColor = "red" ;
deleteButton . onclick = function ( ) {
let mainDiv = document . createElement ( "div" ) ;
mainDiv . appendChild ( generatePopupWarningTitle ( ) ) ;
2021-04-26 19:34:24 +02:00
let p = document . createElement ( "p" ) ;
p . textContent = "This action is irreversible. Are you sure you want to delete all your data ?" ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( p ) ;
let deleteButton = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
deleteButton . classList . add ( "classic_button" ) ;
2021-04-28 17:57:49 +02:00
deleteButton . style . backgroundColor = "red" ;
deleteButton . textContent = "Delete" ;
deleteButton . onclick = function ( ) {
2021-04-26 19:34:24 +02:00
socket . send ( "disappear" ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( deleteButton ) ;
showPopup ( mainDiv ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-28 17:57:49 +02:00
sectionDelete . appendChild ( deleteButton ) ;
mainDiv . appendChild ( sectionDelete ) ;
showPopup ( mainDiv ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-05-02 16:46:05 +02:00
let chatHeader = document . getElementById ( "chat_header" ) ;
2021-05-11 16:11:24 +02:00
chatHeader . children [ 0 ] . onclick = showSessionInfoPopup ;
2021-04-26 19:34:24 +02:00
document . querySelector ( "#refresher button" ) . onclick = function ( ) {
socket . send ( "refresh" ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-26 19:34:24 +02:00
2021-05-02 16:46:05 +02:00
//source: https://stackoverflow.com/a/14919494
function humanFileSize ( bytes , dp = 1 ) {
const thresh = 1000 ;
if ( Math . abs ( bytes ) < thresh ) {
2021-05-06 18:24:29 +02:00
return bytes + " B" ;
2021-05-02 16:46:05 +02:00
}
2021-05-06 18:24:29 +02:00
const units = [ "kB" , "MB" , "GB" , "TB" , "PB" , "EB" , "ZB" , "YB" ] ;
2021-05-02 16:46:05 +02:00
let u = - 1 ;
const r = 10 * * dp ;
do {
bytes /= thresh ;
++ u ;
} while ( Math . round ( Math . abs ( bytes ) * r ) / r >= thresh && u < units . length - 1 ) ;
return bytes . toFixed ( dp ) + ' ' + units [ u ] ;
}
2021-05-06 18:24:29 +02:00
//source: https://stackoverflow.com/a/30106551
function b64EncodeUnicode ( str ) {
return btoa ( encodeURIComponent ( str ) . replace ( /%([0-9A-F]{2})/g ,
function toSolidBytes ( match , p1 ) {
return String . fromCharCode ( '0x' + p1 ) ;
} ) ) ;
}
function b64DecodeUnicode ( str ) {
return decodeURIComponent ( atob ( str ) . split ( '' ) . map ( function ( c ) {
return '%' + ( "00" + c . charCodeAt ( 0 ) . toString ( 16 ) ) . slice ( - 2 ) ;
} ) . join ( '' ) ) ;
}
2021-05-02 16:46:05 +02:00
//source: https://www.w3schools.com/js/js_cookies.asp
function getCookie ( cname ) {
var name = cname + "=" ;
var decodedCookie = decodeURIComponent ( document . cookie ) ;
var ca = decodedCookie . split ( ';' ) ;
for ( var i = 0 ; i < ca . length ; i ++ ) {
var c = ca [ i ] ;
while ( c . charAt ( 0 ) == ' ' ) {
c = c . substring ( 1 ) ;
}
if ( c . indexOf ( name ) == 0 ) {
return c . substring ( name . length , c . length ) ;
}
}
return "" ;
}
socket = new WebSocket ( "ws://" + location . hostname + ":" + websocketPort + "/ws" ) ;
socket . onopen = function ( ) {
console . log ( "Connected" ) ;
socket . send ( getCookie ( "aira_auth" ) ) ; //authenticating websocket connection
window . onfocus = function ( ) {
if ( currentSessionId != - 1 ) {
socket . send ( "set_seen " + currentSessionId ) ;
}
2021-05-10 20:09:02 +02:00
} ;
2021-05-02 16:46:05 +02:00
if ( Notification . permission === "granted" ) {
notificationAllowed = true ;
} else if ( Notification . permission !== "denied" ) {
Notification . requestPermission ( ) . then ( function ( permission ) {
if ( permission === "granted" ) {
notificationAllowed = true ;
}
} ) ;
}
} ;
socket . onmessage = function ( msg ) {
if ( typeof msg . data == "string" ) {
console . log ( "Message: " + msg . data ) ;
let args = msg . data . split ( " " ) ;
switch ( args [ 0 ] ) {
case "disconnected" :
onDisconnected ( args [ 1 ] ) ;
break ;
case "new_session" :
onNewSession ( args [ 1 ] , args [ 2 ] === "true" , args [ 3 ] , args [ 4 ] , msg . data . slice ( args [ 0 ] . length + args [ 1 ] . length + args [ 2 ] . length + args [ 3 ] . length + args [ 4 ] . length + 5 ) ) ;
break ;
case "new_message" :
onNewMessage ( args [ 1 ] , args [ 2 ] === "true" , msg . data . slice ( args [ 0 ] . length + args [ 1 ] . length + args [ 2 ] . length + 3 ) ) ;
break ;
case "file" :
onFileReceived ( args [ 1 ] , args [ 2 ] , msg . data . slice ( args [ 0 ] . length + args [ 1 ] . length + args [ 2 ] . length + 3 ) ) ;
break ;
2021-05-06 18:24:29 +02:00
case "files_transfer" :
onNewFilesTransfer ( args [ 1 ] , args [ 2 ] , msg . data . slice ( args [ 0 ] . length + args [ 1 ] . length + args [ 2 ] . length + 3 ) ) ;
2021-05-02 16:46:05 +02:00
break ;
2021-05-06 18:24:29 +02:00
case "ask_large_files" :
onAskLargeFiles ( args [ 1 ] , args [ 2 ] , msg . data . slice ( args [ 0 ] . length + args [ 1 ] . length + args [ 2 ] . length + 3 ) ) ;
2021-05-02 16:46:05 +02:00
break ;
2021-05-06 18:24:29 +02:00
case "files_accepted" :
onFilesAccepted ( args [ 1 ] ) ;
2021-05-02 16:46:05 +02:00
break ;
case "aborted" :
2021-05-06 18:24:29 +02:00
onFilesTransferAborted ( args [ 1 ] ) ;
2021-05-02 16:46:05 +02:00
break ;
case "inc_file_transfer" :
2021-05-06 18:24:29 +02:00
onIncFilesTransfer ( args [ 1 ] , parseInt ( args [ 2 ] ) ) ;
2021-05-02 16:46:05 +02:00
break ;
2021-05-14 15:14:40 +02:00
case "load_msgs" :
onMsgsLoad ( args [ 1 ] , msg . data . slice ( args [ 0 ] . length + args [ 1 ] . length + 2 ) ) ;
2021-05-02 16:46:05 +02:00
break ;
case "name_told" :
onNameTold ( args [ 1 ] , msg . data . slice ( args [ 0 ] . length + args [ 1 ] . length + 2 ) ) ;
break ;
case "is_contact" :
onIsContact ( args [ 1 ] , args [ 2 ] === "true" , args [ 3 ] , msg . data . slice ( args [ 0 ] . length + args [ 1 ] . length + args [ 2 ] . length + args [ 3 ] . length + 4 ) ) ;
break ;
case "not_seen" :
setNotSeen ( msg . data . slice ( args [ 0 ] . length + 1 ) ) ;
break ;
2021-05-04 22:26:36 +02:00
case "local_ips" :
setLocalIps ( msg . data . slice ( args [ 0 ] . length + 1 ) ) ;
break ;
2021-05-02 16:46:05 +02:00
case "set_name" :
onNameSet ( msg . data . slice ( args [ 0 ] . length + 1 ) ) ;
break ;
case "password_changed" :
onPasswordChanged ( args [ 1 ] === "true" , args [ 2 ] === "true" ) ;
break ;
case "logout" :
logout ( ) ;
}
}
2021-05-10 20:09:02 +02:00
} ;
2021-05-02 16:46:05 +02:00
socket . onclose = function ( ) {
console . log ( "Disconnected" ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-05-02 16:46:05 +02:00
function onNewSession ( sessionId , outgoing , fingerprint , ip , name ) {
2021-04-28 17:57:49 +02:00
if ( sessionsData . has ( sessionId ) ) {
let session = sessionsData . get ( sessionId ) ;
2021-05-02 16:46:05 +02:00
session . isOnline = true ;
2021-04-26 19:34:24 +02:00
session . outgoing = outgoing ;
2021-05-02 16:46:05 +02:00
session . ip = ip ;
2021-04-28 17:57:49 +02:00
displaySessions ( ) ;
if ( currentSessionId == sessionId ) {
displayChatBottom ( ) ;
2021-04-26 19:34:24 +02:00
}
} else {
2021-05-02 16:46:05 +02:00
addSession ( sessionId , name , outgoing , fingerprint , ip , false , false , true ) ;
2021-04-26 19:34:24 +02:00
}
}
2021-04-28 17:57:49 +02:00
function onNameTold ( sessionId , name ) {
sessionsData . get ( sessionId ) . name = name ;
if ( sessionId == currentSessionId ) {
displayHeader ( ) ;
2021-05-11 16:11:24 +02:00
if ( document . getElementById ( "session_info" ) !== null ) {
removePopup ( ) ;
showSessionInfoPopup ( ) ;
}
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
displaySessions ( ) ;
2021-04-26 19:34:24 +02:00
}
2021-05-14 15:14:40 +02:00
function setNotSeen ( strSessionIds ) {
let sessionIds = strSessionIds . split ( ' ' ) ;
2021-04-28 17:57:49 +02:00
for ( let i = 0 ; i < sessionIds . length ; ++ i ) {
sessionsData . get ( sessionIds [ i ] ) . seen = false ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
displaySessions ( ) ;
2021-04-26 19:34:24 +02:00
}
2021-05-14 15:14:40 +02:00
function setLocalIps ( strIPs ) {
localIps = strIPs . split ( ' ' ) ;
2021-05-04 22:26:36 +02:00
}
2021-05-02 16:46:05 +02:00
function onIsContact ( sessionId , verified , fingerprint , name ) {
2021-04-28 17:57:49 +02:00
if ( sessionsData . has ( sessionId ) ) {
let session = sessionsData . get ( sessionId ) ;
2021-05-02 16:46:05 +02:00
session . isContact = true ;
session . isVerified = verified ;
2021-04-28 17:57:49 +02:00
onNameTold ( sessionId , name ) ;
2021-04-26 19:34:24 +02:00
} else {
2021-05-02 16:46:05 +02:00
addSession ( sessionId , name , undefined , fingerprint , undefined , true , verified , false ) ;
2021-04-26 19:34:24 +02:00
}
}
2021-04-28 17:57:49 +02:00
function onMsgOrFileReceived ( sessionId , outgoing , body ) {
if ( currentSessionId == sessionId ) {
2021-05-10 20:09:02 +02:00
displayHistory ( ) ;
2021-04-26 19:34:24 +02:00
if ( ! document . hidden && ! outgoing ) {
2021-04-28 17:57:49 +02:00
socket . send ( "set_seen " + sessionId ) ;
2021-04-26 19:34:24 +02:00
}
} else {
2021-04-28 17:57:49 +02:00
sessionsData . get ( sessionId ) . seen = false ;
displaySessions ( ) ;
2021-04-26 19:34:24 +02:00
}
if ( document . hidden && ! outgoing ) {
if ( notificationAllowed ) {
2021-04-28 17:57:49 +02:00
new Notification ( sessionsData . get ( sessionId ) . name , {
2021-04-26 19:34:24 +02:00
"body" : body
} ) ;
}
}
}
2021-04-28 17:57:49 +02:00
function onNewMessage ( sessionId , outgoing , msg ) {
msgHistory . get ( sessionId ) . push ( [ outgoing , false , msg ] ) ;
onMsgOrFileReceived ( sessionId , outgoing , msg ) ;
2021-04-26 19:34:24 +02:00
}
2021-05-06 18:24:29 +02:00
function onNewFilesTransfer ( sessionId , index , filesInfo ) {
let split = filesInfo . split ( ' ' ) ;
let files = [ ] ;
for ( let i = 0 ; i < split . length ; i += 4 ) {
files . push ( {
"file" : undefined ,
"name" : b64DecodeUnicode ( split [ i ] ) ,
"size" : parseInt ( split [ i + 1 ] ) ,
"transferred" : parseInt ( split [ i + 2 ] ) ,
"lastChunk" : parseInt ( split [ i + 3 ] )
} ) ;
}
pendingFilesTransfers . set ( sessionId , {
"files" : files ,
"index" : parseInt ( index ) ,
"state" : "transferring"
2021-04-28 17:57:49 +02:00
} ) ;
if ( currentSessionId == sessionId ) {
displayChatBottom ( ) ;
2021-04-26 19:34:24 +02:00
}
}
2021-05-06 18:24:29 +02:00
function onAskLargeFiles ( sessionId , encodedDownloadLocation , filesInfo ) {
2021-04-28 17:57:49 +02:00
let sessionName = sessionsData . get ( sessionId ) . name ;
let mainDiv = document . createElement ( "div" ) ;
let h2 = document . createElement ( "h2" ) ;
2021-05-06 18:24:29 +02:00
h2 . textContent = sessionName + " wants to send you some files:" ;
2021-04-28 17:57:49 +02:00
mainDiv . appendChild ( h2 ) ;
2021-05-06 18:24:29 +02:00
let ul = document . createElement ( "ul" ) ;
let split = filesInfo . split ( ' ' ) ;
for ( let i = 0 ; i < split . length ; i += 2 ) {
let p = document . createElement ( "p" ) ;
generateFileInfo ( b64DecodeUnicode ( split [ i ] ) , parseInt ( split [ i + 1 ] ) , p ) ;
let li = document . createElement ( "li" ) ;
li . appendChild ( p ) ;
ul . appendChild ( li ) ;
}
mainDiv . appendChild ( ul ) ;
2021-04-28 17:57:49 +02:00
let spanDownloadLocation = document . createElement ( "span" ) ;
2021-05-06 18:24:29 +02:00
spanDownloadLocation . textContent = b64DecodeUnicode ( encodedDownloadLocation ) ;
2021-04-28 17:57:49 +02:00
let pQuestion = document . createElement ( "p" ) ;
2021-05-06 18:24:29 +02:00
pQuestion . appendChild ( document . createTextNode ( "Download them in " ) ) ;
2021-04-28 17:57:49 +02:00
pQuestion . appendChild ( spanDownloadLocation ) ;
pQuestion . appendChild ( document . createTextNode ( " ?" ) ) ;
mainDiv . appendChild ( pQuestion ) ;
let buttonRow = document . createElement ( "div" ) ;
buttonRow . classList . add ( "button_row" ) ;
let buttonDownload = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
buttonDownload . classList . add ( "classic_button" ) ;
2021-04-28 17:57:49 +02:00
buttonDownload . textContent = "Download" ;
buttonDownload . onclick = function ( ) {
removePopup ( ) ;
2021-05-06 18:24:29 +02:00
let files = [ ] ;
for ( let i = 0 ; i < split . length ; i += 2 ) {
files . push ( {
"file" : undefined ,
"name" : b64DecodeUnicode ( split [ i ] ) ,
"size" : parseInt ( split [ i + 1 ] ) ,
"transferred" : 0 ,
"lastChunk" : Date . now ( )
} ) ;
}
pendingFilesTransfers . set ( sessionId , {
"files" : files ,
"index" : 0 ,
"state" : "transferring"
2021-04-28 17:57:49 +02:00
} ) ;
socket . send ( "download " + sessionId ) ;
if ( currentSessionId == sessionId ) {
displayChatBottom ( ) ;
}
2021-05-10 20:09:02 +02:00
} ;
2021-04-28 17:57:49 +02:00
buttonRow . appendChild ( buttonDownload ) ;
let buttonRefuse = document . createElement ( "button" ) ;
2021-05-11 16:11:24 +02:00
buttonRefuse . classList . add ( "classic_button" ) ;
2021-04-28 17:57:49 +02:00
buttonRefuse . textContent = "Refuse" ;
buttonRefuse . onclick = function ( ) {
removePopup ( ) ;
socket . send ( "abort " + sessionId ) ;
2021-05-10 20:09:02 +02:00
} ;
2021-04-28 17:57:49 +02:00
buttonRow . appendChild ( buttonRefuse ) ;
mainDiv . appendChild ( buttonRow ) ;
showPopup ( mainDiv , false ) ;
if ( document . hidden && notificationAllowed ) {
new Notification ( sessionName , {
2021-05-06 18:24:29 +02:00
"body" : "Files download request"
2021-04-28 17:57:49 +02:00
} ) ;
2021-04-26 19:34:24 +02:00
}
}
2021-05-06 18:24:29 +02:00
function onFilesAccepted ( sessionId ) {
if ( pendingFilesTransfers . has ( sessionId ) ) {
sendNextLargeFile ( sessionId ) ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
}
2021-05-06 18:24:29 +02:00
function onFilesTransferAborted ( sessionId ) {
if ( pendingFilesTransfers . has ( sessionId ) ) {
pendingFilesTransfers . get ( sessionId ) . state = "aborted" ;
2021-04-28 17:57:49 +02:00
if ( sessionId == currentSessionId ) {
displayChatBottom ( ) ;
}
}
}
2021-05-06 18:24:29 +02:00
function onIncFilesTransfer ( sessionId , chunkSize ) {
if ( pendingFilesTransfers . has ( sessionId ) ) {
let filesTransfer = pendingFilesTransfers . get ( sessionId ) ;
let fileTransfer = filesTransfer . files [ filesTransfer . index ] ;
fileTransfer . transferred += chunkSize ;
2021-04-28 17:57:49 +02:00
let now = Date . now ( ) ;
2021-05-06 18:24:29 +02:00
let speed = chunkSize / ( now - fileTransfer . lastChunk ) * 1000 ;
fileTransfer . lastChunk = now ;
if ( fileTransfer . transferred >= fileTransfer . size ) {
if ( filesTransfer . index == filesTransfer . files . length - 1 ) {
filesTransfer . state = "completed" ;
socket . send ( "sending_ended " + sessionId ) ;
} else {
filesTransfer . index += 1 ;
if ( typeof fileTransfer . file !== "undefined" ) {
sendNextLargeFile ( sessionId ) ;
}
}
2021-04-28 17:57:49 +02:00
}
if ( currentSessionId == sessionId ) {
displayChatBottom ( speed ) ;
}
}
}
2021-05-14 15:14:40 +02:00
function onMsgsLoad ( sessionId , strMsgs ) {
let msgs = strMsgs . split ( ' ' ) ;
let n = 0 ;
while ( n < msgs . length ) {
let outgoing = msgs [ n + 1 ] === "true" ;
switch ( msgs [ n ] ) {
case 'm' :
let msg = b64DecodeUnicode ( msgs [ n + 2 ] ) ;
msgHistory . get ( sessionId ) . unshift ( [ outgoing , false , msg ] ) ;
n += 3 ;
break ;
case 'f' :
let uuid = msgs [ n + 2 ] ;
let fileName = b64DecodeUnicode ( msgs [ n + 3 ] ) ;
msgHistory . get ( sessionId ) . unshift ( [ outgoing , true , [ uuid , fileName ] ] ) ;
n += 4 ;
}
2021-04-28 17:57:49 +02:00
}
if ( currentSessionId == sessionId ) {
2021-05-14 15:14:40 +02:00
if ( msg _log . scrollHeight - msg _log . scrollTop === msg _log . clientHeight ) {
displayHistory ( ) ;
} else {
let backupHeight = msg _log . scrollHeight ;
displayHistory ( false ) ;
msg _log . scrollTop = msg _log . scrollHeight - backupHeight ;
}
2021-04-28 17:57:49 +02:00
}
}
function onDisconnected ( sessionId ) {
2021-05-06 18:24:29 +02:00
pendingFilesTransfers . delete ( sessionId ) ;
2021-04-28 17:57:49 +02:00
let session = sessionsData . get ( sessionId ) ;
2021-05-02 16:46:05 +02:00
if ( session . isContact ) {
session . isOnline = false ;
2021-04-26 19:34:24 +02:00
} else {
2021-04-28 17:57:49 +02:00
sessionsData . delete ( sessionId ) ;
2021-04-30 18:54:48 +02:00
}
if ( currentSessionId == sessionId ) {
displayChatBottom ( ) ;
}
2021-05-02 16:46:05 +02:00
if ( currentSessionId == sessionId && ! session . isContact ) {
2021-04-30 18:54:48 +02:00
currentSessionId = - 1 ;
2021-05-02 16:46:05 +02:00
chatHeader . classList . add ( "offline" ) ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
displaySessions ( ) ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
function onFileReceived ( sessionId , uuid , file _name ) {
msgHistory . get ( sessionId ) . push ( [ false , true , [ uuid , file _name ] ] ) ;
onMsgOrFileReceived ( sessionId , false , file _name ) ;
}
function onFileSent ( sessionId , uuid , file _name ) {
msgHistory . get ( sessionId ) . push ( [ true , true , [ uuid , file _name ] ] ) ;
if ( currentSessionId == sessionId ) {
2021-05-10 20:09:02 +02:00
displayHistory ( ) ;
2021-04-28 17:57:49 +02:00
}
}
2021-05-02 16:46:05 +02:00
function onNameSet ( newName ) {
2021-04-28 17:57:49 +02:00
removePopup ( ) ;
2021-05-02 16:46:05 +02:00
identityName = newName ;
2021-04-28 17:57:49 +02:00
displayProfile ( ) ;
2021-04-26 19:34:24 +02:00
}
2021-05-02 16:46:05 +02:00
function onPasswordChanged ( success , isProtected ) {
2021-04-26 19:34:24 +02:00
if ( success ) {
2021-04-28 17:57:49 +02:00
removePopup ( ) ;
2021-05-02 16:46:05 +02:00
isIdentityProtected = isProtected ;
2021-04-26 19:34:24 +02:00
} else {
let input = document . querySelector ( "input[type=\"password\"]" ) ;
input . value = "" ;
2021-04-28 17:57:49 +02:00
let errorMsg = document . getElementById ( "password_errorMsg" ) ;
errorMsg . textContent = "Operation failed. Please check your old password." ;
2021-04-26 19:34:24 +02:00
}
}
2021-05-06 18:24:29 +02:00
function sendNextLargeFile ( sessionId ) {
let filesTransfer = pendingFilesTransfers . get ( sessionId ) ;
filesTransfer . state = "transferring" ;
let fileTransfer = filesTransfer . files [ filesTransfer . index ] ;
fileTransfer . lastChunk = Date . now ( ) ;
if ( currentSessionId == sessionId ) {
displayChatBottom ( ) ;
}
let formData = new FormData ( ) ;
formData . append ( "session_id" , currentSessionId ) ;
formData . append ( "" , fileTransfer . file ) ;
fetch ( "/send_large_file" , { method : "POST" , body : formData } ) . then ( response => {
if ( ! response . ok ) {
console . log ( response ) ;
}
} ) ;
}
2021-05-02 16:46:05 +02:00
function beautifyFingerprint ( f ) {
for ( let i = 4 ; i < f . length ; i += 5 ) {
f = f . slice ( 0 , i ) + " " + f . slice ( i ) ;
}
return f ;
2021-05-10 20:09:02 +02:00
}
2021-05-11 16:11:24 +02:00
function showSessionInfoPopup ( ) {
let session = sessionsData . get ( currentSessionId ) ;
if ( typeof session !== "undefined" ) {
let mainDiv = document . createElement ( "div" ) ;
mainDiv . id = "session_info" ;
mainDiv . appendChild ( generateAvatar ( session . name ) ) ;
let nameDiv = document . createElement ( "div" ) ;
nameDiv . classList . add ( "name" ) ;
let h2 = document . createElement ( "h2" ) ;
h2 . textContent = session . name ;
nameDiv . appendChild ( h2 ) ;
if ( session . isOnline ) {
let button = document . createElement ( "button" ) ;
button . onclick = function ( ) {
socket . send ( "ask_name " + currentSessionId ) ;
} ;
nameDiv . appendChild ( button ) ;
}
mainDiv . appendChild ( nameDiv ) ;
let pFingerprint = document . createElement ( "p" ) ;
pFingerprint . textContent = "Fingerprint:" ;
mainDiv . appendChild ( pFingerprint ) ;
let pre = document . createElement ( "pre" ) ;
pre . textContent = ' ' + beautifyFingerprint ( session . fingerprint ) ;
mainDiv . appendChild ( pre ) ;
if ( session . isOnline ) {
let pIp = document . createElement ( "p" ) ;
pIp . textContent = "IP: " + session . ip ;
mainDiv . appendChild ( pIp ) ;
let pConnection = document . createElement ( "p" ) ;
pConnection . textContent = "Connection: " ;
if ( session . outgoing ) {
pConnection . textContent += "outgoing" ;
} else {
pConnection . textContent += "incomming" ;
}
mainDiv . appendChild ( pConnection ) ;
}
showPopup ( mainDiv ) ;
}
}
2021-05-02 16:46:05 +02:00
function addSession ( sessionId , name , outgoing , fingerprint , ip , isContact , isVerified , isOnline ) {
2021-04-28 17:57:49 +02:00
sessionsData . set ( sessionId , {
2021-04-26 19:34:24 +02:00
"name" : name ,
"outgoing" : outgoing ,
2021-05-02 16:46:05 +02:00
"fingerprint" : fingerprint ,
"ip" : ip ,
"isContact" : isContact ,
"isVerified" : isVerified ,
2021-04-26 19:34:24 +02:00
"seen" : true ,
2021-05-02 16:46:05 +02:00
"isOnline" : isOnline ,
2021-04-26 19:34:24 +02:00
} ) ;
2021-04-28 17:57:49 +02:00
msgHistory . set ( sessionId , [ ] ) ;
displaySessions ( ) ;
2021-04-26 19:34:24 +02:00
}
2021-04-28 17:57:49 +02:00
function displaySessions ( ) {
2021-05-02 16:46:05 +02:00
let onlineSessions = document . getElementById ( "online_sessions" ) ;
onlineSessions . innerHTML = "" ;
let offlineSessions = document . getElementById ( "offline_sessions" ) ;
offlineSessions . innerHTML = "" ;
2021-04-28 17:57:49 +02:00
sessionsData . forEach ( function ( session , sessionId ) {
2021-05-02 16:46:05 +02:00
let sessionElement = generateSession ( sessionId , session ) ;
if ( session . isOnline ) {
onlineSessions . appendChild ( sessionElement ) ;
2021-04-26 19:34:24 +02:00
} else {
2021-05-02 16:46:05 +02:00
offlineSessions . appendChild ( sessionElement ) ;
2021-04-26 19:34:24 +02:00
}
} ) ;
}
function logout ( ) {
window . location = "/logout" ;
}
2021-04-28 17:57:49 +02:00
function displayProfile ( ) {
2021-04-26 19:34:24 +02:00
profile _div . innerHTML = "" ;
2021-05-02 16:46:05 +02:00
profile _div . appendChild ( generateAvatar ( identityName ) ) ;
2021-04-26 19:34:24 +02:00
let p = document . createElement ( "p" ) ;
2021-05-02 16:46:05 +02:00
p . textContent = identityName ;
2021-04-26 19:34:24 +02:00
profile _div . appendChild ( p ) ;
}
2021-04-28 17:57:49 +02:00
function displayHeader ( ) {
2021-05-02 16:46:05 +02:00
chatHeader . children [ 0 ] . innerHTML = "" ;
chatHeader . className = 0 ;
2021-04-28 17:57:49 +02:00
let session = sessionsData . get ( currentSessionId ) ;
2021-04-26 19:34:24 +02:00
if ( typeof session === "undefined" ) {
2021-05-02 16:46:05 +02:00
chatHeader . style . display = "none" ;
2021-04-26 19:34:24 +02:00
} else {
2021-05-02 16:46:05 +02:00
chatHeader . children [ 0 ] . appendChild ( generateAvatar ( session . name ) ) ;
chatHeader . children [ 0 ] . appendChild ( generateName ( session . name ) ) ;
chatHeader . style . display = "flex" ;
if ( session . isContact ) {
chatHeader . classList . add ( "is_contact" ) ;
if ( session . isVerified ) {
chatHeader . classList . add ( "is_verified" ) ;
2021-04-26 19:34:24 +02:00
}
}
}
}
2021-05-06 18:24:29 +02:00
function showPopup ( content , cancelable = true ) {
2021-04-26 19:34:24 +02:00
let popup _background = document . createElement ( "div" ) ;
popup _background . classList . add ( "popup_background" ) ;
let popup = document . createElement ( "div" ) ;
popup . classList . add ( "popup" ) ;
2021-05-06 18:24:29 +02:00
if ( cancelable ) {
popup _background . onclick = function ( e ) {
if ( e . target == popup _background ) {
removePopup ( ) ;
}
} ;
2021-04-28 17:57:49 +02:00
let close = document . createElement ( "button" ) ;
close . classList . add ( "close" ) ;
close . onclick = removePopup ;
popup . appendChild ( close ) ;
}
2021-04-26 19:34:24 +02:00
popup . appendChild ( content ) ;
popup _background . appendChild ( popup ) ;
let main = document . querySelector ( "main" ) ;
main . appendChild ( popup _background ) ;
}
2021-04-28 17:57:49 +02:00
function removePopup ( ) {
2021-04-26 19:34:24 +02:00
let popups = document . querySelectorAll ( ".popup_background" ) ;
if ( popups . length > 0 ) {
popups [ popups . length - 1 ] . remove ( ) ;
}
}
2021-04-28 17:57:49 +02:00
function generatePopupWarningTitle ( ) {
2021-04-26 19:34:24 +02:00
let h2 = document . createElement ( "h2" ) ;
2021-04-28 17:57:49 +02:00
h2 . classList . add ( "warning" ) ;
2021-04-26 19:34:24 +02:00
h2 . textContent = "Warning!" ;
return h2 ;
}
2021-05-13 20:28:45 +02:00
function generateSwitchPreference ( title , summary , checked , onSwitch ) {
let label = document . createElement ( "label" ) ;
label . classList . add ( "switch_preference" ) ;
let divDesc = document . createElement ( "div" ) ;
divDesc . classList . add ( "preference_description" ) ;
let h3 = document . createElement ( "h3" ) ;
h3 . textContent = title ;
divDesc . appendChild ( h3 ) ;
let pSummary = document . createElement ( "p" ) ;
pSummary . textContent = summary ;
divDesc . appendChild ( pSummary ) ;
label . appendChild ( divDesc ) ;
let switchDiv = document . createElement ( "div" ) ;
switchDiv . classList . add ( "switch" ) ;
let input = document . createElement ( "input" ) ;
input . type = "checkbox" ;
input . checked = checked ;
input . onchange = function ( ) {
onSwitch ( input . checked ) ;
} ;
switchDiv . appendChild ( input ) ;
let span = document . createElement ( "span" ) ;
switchDiv . appendChild ( span ) ;
label . appendChild ( switchDiv ) ;
return label ;
}
2021-04-28 17:57:49 +02:00
function generateName ( name ) {
2021-04-26 19:34:24 +02:00
let p = document . createElement ( "p" ) ;
if ( typeof name == "undefined" ) {
p . appendChild ( document . createTextNode ( "Unknown" ) ) ;
} else {
p . appendChild ( document . createTextNode ( name ) ) ;
}
return p ;
}
2021-04-28 17:57:49 +02:00
function generateSession ( sessionId , session ) {
2021-04-26 19:34:24 +02:00
let li = document . createElement ( "li" ) ;
2021-04-28 17:57:49 +02:00
li . setAttribute ( "data-sessionId" , sessionId ) ;
li . appendChild ( generateAvatar ( session . name ) ) ;
li . appendChild ( generateName ( session . name ) ) ;
2021-05-02 16:46:05 +02:00
if ( session . isContact ) {
2021-04-26 19:34:24 +02:00
li . classList . add ( "is_contact" ) ;
}
2021-05-02 16:46:05 +02:00
if ( session . isVerified ) {
2021-04-26 19:34:24 +02:00
li . classList . add ( "is_verified" ) ;
}
if ( ! session . seen ) {
let marker = document . createElement ( "div" ) ;
marker . classList . add ( "not_seen_marker" ) ;
li . appendChild ( marker ) ;
}
2021-04-28 17:57:49 +02:00
if ( sessionId == currentSessionId ) {
2021-04-26 19:34:24 +02:00
li . classList . add ( "current" ) ;
}
2021-04-28 17:57:49 +02:00
li . onclick = onClickSession ;
2021-04-26 19:34:24 +02:00
return li ;
}
2021-04-28 17:57:49 +02:00
function generateMsgHeader ( name ) {
2021-04-26 19:34:24 +02:00
let p = document . createElement ( "p" ) ;
2021-04-28 17:57:49 +02:00
p . appendChild ( document . createTextNode ( name ) ) ;
2021-04-26 19:34:24 +02:00
let div = document . createElement ( "div" ) ;
2021-05-04 11:51:08 +02:00
div . classList . add ( "header" ) ;
2021-04-28 17:57:49 +02:00
div . appendChild ( generateAvatar ( name ) ) ;
2021-04-26 19:34:24 +02:00
div . appendChild ( p ) ;
return div ;
}
2021-04-28 17:57:49 +02:00
function generateMessage ( name , msg ) {
2021-04-26 19:34:24 +02:00
let p = document . createElement ( "p" ) ;
2021-04-28 17:57:49 +02:00
p . appendChild ( document . createTextNode ( msg ) ) ;
2021-04-26 19:34:24 +02:00
let div = document . createElement ( "div" ) ;
2021-05-04 11:51:08 +02:00
div . classList . add ( "content" ) ;
2021-04-26 19:34:24 +02:00
div . appendChild ( linkifyElement ( p ) ) ;
let li = document . createElement ( "li" ) ;
2021-05-04 11:51:08 +02:00
if ( typeof name !== "undefined" ) {
li . appendChild ( generateMsgHeader ( name ) ) ;
}
2021-04-26 19:34:24 +02:00
li . appendChild ( div ) ;
return li ;
}
2021-04-28 17:57:49 +02:00
function generateFile ( name , outgoing , file _info ) {
2021-04-26 19:34:24 +02:00
let div1 = document . createElement ( "div" ) ;
div1 . classList . add ( "file" ) ;
2021-05-04 11:51:08 +02:00
div1 . classList . add ( "content" ) ;
2021-04-26 19:34:24 +02:00
let div2 = document . createElement ( "div" ) ;
let h4 = document . createElement ( "h4" ) ;
if ( outgoing ) {
h4 . textContent = "File sent:" ;
} else {
h4 . textContent = "File received:" ;
}
div2 . appendChild ( h4 ) ;
let p = document . createElement ( "p" ) ;
p . textContent = file _info [ 1 ] ;
div2 . appendChild ( p ) ;
div1 . appendChild ( div2 ) ;
let a = document . createElement ( "a" ) ;
a . href = "/load_file?uuid=" + file _info [ 0 ] + "&file_name=" + encodeURIComponent ( file _info [ 1 ] ) ;
a . target = "_blank" ;
div1 . appendChild ( a ) ;
let li = document . createElement ( "li" ) ;
2021-05-04 11:51:08 +02:00
if ( typeof name !== "undefined" ) {
li . appendChild ( generateMsgHeader ( name ) ) ;
}
2021-04-26 19:34:24 +02:00
li . appendChild ( div1 ) ;
return li ;
}
2021-04-28 17:57:49 +02:00
function generateFileInfo ( fileName , fileSize , p ) {
let span = document . createElement ( "span" ) ;
span . textContent = fileName ;
p . appendChild ( span ) ;
p . appendChild ( document . createTextNode ( " (" + humanFileSize ( fileSize ) + ")" ) ) ;
}
function displayChatBottom ( speed = undefined ) {
2021-04-30 18:54:48 +02:00
let msgBox = document . getElementById ( "message_box" ) ;
2021-05-06 18:24:29 +02:00
let fileTransfer = document . getElementById ( "file_transfer" ) ;
2021-04-28 17:57:49 +02:00
let session = sessionsData . get ( currentSessionId ) ;
2021-04-30 18:54:48 +02:00
if ( typeof session === "undefined" ) {
msgBox . removeAttribute ( "style" ) ;
2021-05-06 18:24:29 +02:00
fileTransfer . classList . remove ( "active" ) ;
2021-04-28 17:57:49 +02:00
} else {
2021-05-02 16:46:05 +02:00
if ( session . isOnline ) {
2021-04-30 18:54:48 +02:00
msgBox . style . display = "flex" ;
} else {
msgBox . removeAttribute ( "style" ) ;
}
2021-05-06 18:24:29 +02:00
if ( pendingFilesTransfers . has ( currentSessionId ) ) {
2021-04-30 18:54:48 +02:00
let fileInfo = document . getElementById ( "file_info" ) ;
fileInfo . innerHTML = "" ;
2021-05-06 18:24:29 +02:00
let filesTransfer = pendingFilesTransfers . get ( currentSessionId ) ;
let file = filesTransfer . files [ filesTransfer . index ] ;
2021-04-30 18:54:48 +02:00
generateFileInfo ( file . name , file . size , fileInfo ) ;
let fileProgress = document . getElementById ( "file_progress" ) ;
fileProgress . style . display = "none" ; //hide by default
let fileStatus = document . getElementById ( "file_status" ) ;
fileStatus . removeAttribute ( "style" ) ; //show by default
let fileCancel = document . getElementById ( "file_cancel" ) ;
fileCancel . style . display = "none" ; //hide by default
document . querySelector ( "#file_progress_bar>div" ) . style . width = 0 ;
2021-05-06 18:24:29 +02:00
switch ( filesTransfer . state ) {
2021-04-30 18:54:48 +02:00
case "transferring" :
fileCancel . removeAttribute ( "style" ) ; //show
fileStatus . style . display = "none" ;
fileProgress . removeAttribute ( "style" ) ; //show
let percent = ( file . transferred / file . size ) * 100 ;
document . getElementById ( "file_percent" ) . textContent = percent . toFixed ( 2 ) + "%" ;
if ( typeof speed !== "undefined" ) {
document . getElementById ( "file_speed" ) . textContent = humanFileSize ( speed ) + "/s" ;
}
document . querySelector ( "#file_progress_bar>div" ) . style . width = Math . round ( percent ) + "%" ;
break ;
case "waiting" :
fileStatus . textContent = "Waiting for peer confirmation..." ;
break ;
case "aborted" :
fileStatus . textContent = "Transfer aborted." ;
2021-05-06 18:24:29 +02:00
pendingFilesTransfers . delete ( currentSessionId ) ;
2021-04-30 18:54:48 +02:00
break ;
case "completed" :
fileStatus . textContent = "Transfer completed." ;
2021-05-06 18:24:29 +02:00
pendingFilesTransfers . delete ( currentSessionId ) ;
2021-04-30 18:54:48 +02:00
}
fileTransfer . classList . add ( "active" ) ;
} else {
fileTransfer . classList . remove ( "active" ) ;
2021-04-28 17:57:49 +02:00
}
}
}
2021-05-10 20:09:02 +02:00
function displayHistory ( scrollToBottom = true ) {
2021-04-26 19:34:24 +02:00
msg _log . style . display = "block" ;
msg _log . innerHTML = "" ;
2021-05-04 11:51:08 +02:00
let previousOutgoing = undefined ;
2021-04-28 17:57:49 +02:00
msgHistory . get ( currentSessionId ) . forEach ( entry => {
2021-05-04 11:51:08 +02:00
let name = undefined ;
if ( previousOutgoing != entry [ 0 ] ) {
previousOutgoing = entry [ 0 ] ;
if ( entry [ 0 ] ) { //outgoing msg
name = identityName ;
} else {
name = sessionsData . get ( currentSessionId ) . name ;
}
2021-04-26 19:34:24 +02:00
}
2021-05-04 11:51:08 +02:00
if ( entry [ 1 ] ) { //is file
2021-04-28 17:57:49 +02:00
msg _log . appendChild ( generateFile ( name , entry [ 0 ] , entry [ 2 ] ) ) ;
2021-04-26 19:34:24 +02:00
} else {
2021-04-28 17:57:49 +02:00
msg _log . appendChild ( generateMessage ( name , entry [ 2 ] ) ) ;
2021-04-26 19:34:24 +02:00
}
} ) ;
if ( scrollToBottom ) {
msg _log . scrollTop = msg _log . scrollHeight ;
}
2021-05-14 15:14:40 +02:00
if ( msg _log . scrollHeight <= msg _log . clientHeight ) {
socket . send ( "load_msgs " + currentSessionId ) ;
}
2021-04-30 18:54:48 +02:00
}