var Chat = { socket: null, loading: document.getElementById("loading"), chat_box: document.getElementById("chat-box"), msgs_list: document.getElementById("msgs"), typing_list: document.getElementById("typing"), users: document.getElementById("users"), textarea: document.getElementById("form_input"), send_btn: document.getElementById("send"), is_focused: false, is_online: false, is_typing: false, last_sent_nick: null, original_title: document.title, new_title: "New messages...", scroll: function(){ setTimeout(function(){ Chat.chat_box.scrollTop = Chat.chat_box.scrollHeight; }, 0) }, notif: { enabled: true, toggle: function(){ return Chat.notif.enabled = !Chat.notif.enabled; }, // Title time-out ttout: undefined, active: undefined, msgs: 0, // Beep notification beep: undefined, beep_create: function(){ var audiotypes = { "mp3": "audio/mpeg", "mp4": "audio/mp4", "ogg": "audio/ogg", "wav": "audio/wav" }; var audios = [ 'static/beep.ogg' ]; var audio_element = document.createElement('audio'); if(audio_element.canPlayType){ for(var i = 0;i < audios.length;i++){ var source_element = document.createElement('source'); source_element.setAttribute('src', audios[i]); if(audios[i].match(/\.(\w+)$/i)){ source_element.setAttribute('type', audiotypes[RegExp.$1]); } audio_element.appendChild(source_element); } audio_element.load(); audio_element.playclip = function(){ audio_element.pause(); audio_element.volume = 0.5; audio_element.currentTime = 0; audio_element.play(); }; return audio_element; } }, // Create new notification create: function(from, message){ // If is focused, no notification if(Chat.is_focused || !Chat.notif.enabled){ return; } // Increase number in title Chat.notif.msgs++; // Create new ttout, if there is not any Chat.notif.favicon('blue'); document.title = '(' + Chat.notif.msgs + ') ' + Chat.new_title; if(typeof Chat.notif.ttout === "undefined"){ Chat.notif.ttout = setInterval(function(){ if(document.title == Chat.original_title){ Chat.notif.favicon('blue'); document.title = '(' + Chat.notif.msgs + ') ' + Chat.new_title; } else { Chat.notif.favicon('green'); document.title = Chat.original_title; } }, 1500); } // Do beep Chat.notif.beep.playclip(); // If are'nt allowed notifications if(Notification.permission !== "granted"){ Notification.requestPermission(); return; } // Clear notification Chat.notif.clear(); // Stip tags from = from.replace(/(<([^>]+)>)/ig, ""); message = message.replace(/(<([^>]+)>)/ig, ""); // Create new notification Chat.notif.active = new Notification(from, { icon: '/static/images/favicon-blue.png', //timeout: 10, body: message, }); // On click, focus this window Chat.notif.active.onclick = function(){ parent.focus(); window.focus(); }; }, // Clear notification clear: function(){ typeof Chat.notif.active === "undefined" || Chat.notif.active.close(); }, favicon: function(color){ var link = document.querySelector("link[rel*='icon']") || document.createElement('link'); link.type = 'image/x-icon'; link.rel = 'shortcut icon'; link.href = '/static/images/favicon-' + color + '.ico'; document.getElementsByTagName('head')[0].appendChild(link); } }, send_msg: function(text){ Chat.socket.emit("send-msg", { m: text }); }, send_event: function(){ var value = Chat.textarea.value.trim(); if(value == "") return; console.log("Send message."); Chat.send_msg({text: value}); Chat.textarea.value = ''; Chat.typing.update(); Chat.textarea.focus(); }, typing: { objects: {}, create: function(nick){ var li = document.createElement('li'); var prefix = document.createElement('span'); prefix.className = 'prefix'; prefix.innerText = nick; li.appendChild(prefix); var msg = document.createElement('div'); msg.className = 'message'; var body = document.createElement('span'); body.className = 'body writing' body.innerHTML = ''; msg.appendChild(body); li.appendChild(msg); Chat.typing_list.appendChild(li); Chat.typing.objects[nick] = li; // Scroll to new message Chat.scroll(); }, remove: function(nick){ if(Chat.typing.objects.hasOwnProperty(nick)){ var element = Chat.typing.objects[nick]; element.parentNode.removeChild(element); delete Chat.typing.objects[nick]; } }, event: function(r){ if(r.status){ Chat.typing.create(r.nick); } else { Chat.typing.remove(r.nick); } }, update: function(){ if(Chat.is_typing && Chat.textarea.value === ""){ Chat.socket.emit("typing", Chat.is_typing = false); } if(!Chat.is_typing && Chat.textarea.value !== ""){ Chat.socket.emit("typing", Chat.is_typing = true); } } }, new_msg: function(r){ console.log("New message."); const fromSelf = sessionStorage.nick == r.f; // Notify user Chat.notif.create(r.f, r.m); var li = document.createElement('div'); li.id = r.id; var prefix = document.createElement('span'); prefix.className = 'prefix'; prefix.innerText = r.f; li.appendChild(prefix); if(Chat.last_sent_nick === r.f){ prefix.style.display = "none"; li.prefix = prefix; } else { Chat.last_sent_nick = r.f; } var msg = document.createElement('div'); msg.className = 'message'; var body = document.createElement('span'); body.className = 'body' + (fromSelf ? ' out' : ' in'); Chat.append_msg(body, r.m); msg.appendChild(body); li.appendChild(msg); var c = document.createElement('li'); c.appendChild(li); if (fromSelf){ c.classList.add('message-from-self'); } // Prepend because flex-direction: column-reverse Chat.msgs_list.prepend(c); // Scroll to new message Chat.scroll(); }, append_msg: function(el, msg){ if(!msg) return; // If is object if(typeof msg.text !== 'undefined'){ // Escape HTML el.innerText = msg.text; var text = el.innerHTML; // Parse urls text = text.replace(/(https?:\/\/[^\s]+)/g, function(url, a, b){ var link = document.createElement('a'); link.target = "_blank"; // Un-escape link.innerHTML = url; url = link.innerText; link.href = url; // If link is image if(url.match(/.(png|jpe?g|gifv?)([?#].*)?$/g)){ var img = document.createElement('img'); img.style = 'max-width:100%;'; img.src = url; link.innerText = ""; link.appendChild(img); } return link.outerHTML; }); if(typeof Emic !== 'undefined'){ text = Emic.replace(text); } el.innerHTML = text; } if(typeof msg.type !== 'undefined'){ // Image if(msg.type.match(/image.*/)){ var img = document.createElement('img'); img.style = 'max-width:100%;'; img.src = msg.url; el.appendChild(img); return; } // Audio / Video if(m = msg.type.match(/(audio|video).*/)){ var audio = document.createElement(m[1]); audio.controls = 'controls'; var source = document.createElement("source"); source.src = msg.url; source.type = msg.type; audio.appendChild(source); el.appendChild(audio); return; } // Default var link = document.createElement('a'); link.href = msg.url; link.download = msg.name; link.innerText = msg.name; el.appendChild(link); } }, force_login: function(fail){ if(typeof fail !== "undefined"){ if (sessionStorage.nick || localStorage.nick){ sessionStorage.removeItem("nick"); localStorage.removeItem("nick"); } alert(fail); } var nick = (sessionStorage.nick != null)? localStorage.nick : prompt("Your nick:", sessionStorage.nick || localStorage.nick || ""); // var nick = (sessionStorage.nick)? localStorage.nick : (sessionStorage.nick)? sessionStorage.nick:prompt("Your nick:", sessionStorage.nick || localStorage.nick || ""); if(typeof nick !== "undefined" && nick){ sessionStorage.nick = localStorage.nick = nick; Chat.socket.emit("login", { nick: nick }); } }, reload: function(){ if(typeof sessionStorage.nick !== "undefined" && sessionStorage.nick){ Chat.socket.emit("login", { nick: sessionStorage.nick }); } }, user: { objects: {}, // Load all users start: function(r){ Chat.users.innerText = ''; for(var user in r.users){ var nick = document.createElement('li'); nick.innerText = r.users[user]; Chat.users.appendChild(nick); Chat.user.objects[r.users[user]] = nick; } }, previous_messages: function(data){ console.log(`msgs: ${data}`) data.msgs.forEach(element => { Chat.new_msg(element) }); }, // User joined room enter: function(r){ console.log("User " + r.nick + " joined."); var nick = document.createElement('li'); nick.innerText = r.nick; Chat.users.appendChild(nick); Chat.user.objects[r.nick] = nick; }, // User left room leave: function(r){ console.log("User " + r.nick + " left."); // Is not typing Chat.typing.remove(r.nick); // Remove user if(Chat.user.objects.hasOwnProperty(r.nick)){ var element = Chat.user.objects[r.nick]; element.parentNode.removeChild(element); delete Chat.user.objects[r.nick]; } } }, connect: function(){ // Set green favicon Chat.notif.favicon('green'); Chat.is_online = true; document.getElementById('offline').style.display = "none"; Chat.msgs_list.innerText = ''; Chat.typing_list.innerText = ''; Chat.users.innerText = ''; Chat.last_sent_nick = ''; // force user to login Chat.force_login(); }, disconnect: function(){ // Set green favicon Chat.notif.favicon('red'); Chat.is_online = false; document.getElementById('offline').style.display = "block"; Chat.msgs_list.innerText = ''; Chat.typing_list.innerText = ''; Chat.users.innerText = ''; }, init: function(socket){ // Set green favicon Chat.notif.favicon('red'); // Connect to socket.io Chat.socket = socket || io(); // Create beep object Chat.notif.beep = Chat.notif.beep_create(); // On focus window.addEventListener('focus', function(){ Chat.is_focused = true; // If chat is not online, dont care. if(!Chat.is_online){ return; } // Clear ttout, if there was typeof Chat.notif.ttout === "undefined" || clearInterval(Chat.notif.ttout); Chat.notif.ttout = undefined; // Clear notifications Chat.notif.clear(); Chat.notif.msgs = 0; Chat.notif.favicon('green'); // Set back page title document.title = Chat.original_title; }); // On blur window.addEventListener('blur', function(){ Chat.is_focused = false; }); // On click send message Chat.send_btn.onclick = Chat.send_event; // On enter send message Chat.textarea.onkeydown = function(e){ var key = e.keyCode || window.event.keyCode; // If the user has pressed enter if(key === 13){ Chat.send_event(); return false; } return true; }; // Check if is user typing Chat.textarea.onkeyup = Chat.typing.update; // On socket events Chat.socket.on("connect", Chat.connect); Chat.socket.on("disconnect", Chat.disconnect); Chat.socket.on("force-login", Chat.force_login); Chat.socket.on("typing", Chat.typing.event); Chat.socket.on("new-msg", Chat.new_msg); Chat.socket.on("previous-msg", Chat.user.previous_messages) Chat.socket.on("start", Chat.user.start); Chat.socket.on("ue", Chat.user.enter); Chat.socket.on("ul", Chat.user.leave); var dropZone = document.getElementsByTagName("body")[0]; // Optional. Show the copy icon when dragging over. Seems to only work for chrome. dropZone.addEventListener('dragover', function(e){ e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; }); // Get file data on drop dropZone.addEventListener('drop', function(e){ e.stopPropagation(); e.preventDefault(); var files = e.dataTransfer.files; // Array of all files for(var i = 0;i < files.length;i++){ var file = files[i]; // Max 10 MB if(file.size > 10485760){ alert("Max size of file is 10MB"); return; } var reader = new FileReader(); reader.onload = (function(file){ return function(e){ Chat.send_msg({ type: file.type, name: file.name, url: e.target.result }); }; })(file); reader.readAsDataURL(file); } }); // close socket upon refresh or tab close, free the username window.addEventListener("beforeunload", () => { if(!Chat.is_online){ return; } Chat.socket.disconnect(); }); } };