1
0
mirror of https://github.com/hack-chat/main.git synced 2024-03-22 13:20:33 +08:00

Fixed relative schema links & client readability

This commit is contained in:
marzavec 2018-03-28 21:34:27 -07:00
parent ce766fa1db
commit 00039be0df

View File

@ -26,452 +26,481 @@ var frontpage = [
"", "",
"Server and web client released under the WTFPL and MIT open source license.", "Server and web client released under the WTFPL and MIT open source license.",
"No message history is retained on the hack.chat server." "No message history is retained on the hack.chat server."
].join("\n") ].join("\n");
function $(query) {return document.querySelector(query)} function $(query) {
return document.querySelector(query);
}
function localStorageGet(key) { function localStorageGet(key) {
try { try {
return window.localStorage[key] return window.localStorage[key]
} } catch(e) {}
catch(e) {}
} }
function localStorageSet(key, val) { function localStorageSet(key, val) {
try { try {
window.localStorage[key] = val window.localStorage[key] = val
} } catch(e) {}
catch(e) {}
} }
var ws;
var ws var myNick = localStorageGet('my-nick');
var myNick = localStorageGet('my-nick') var myChannel = window.location.search.replace(/^\?/, '');
var myChannel = window.location.search.replace(/^\?/, '') var lastSent = [""];
var lastSent = [""] var lastSentPos = 0;
var lastSentPos = 0
// Ping server every 50 seconds to retain WebSocket connection // Ping server every 50 seconds to retain WebSocket connection
window.setInterval(function() { window.setInterval(function () {
send({cmd: 'ping'}) send({ cmd: 'ping' });
}, 50000) }, 50000);
function join(channel) { function join(channel) {
if (document.domain == 'hack.chat') { if (document.domain == 'hack.chat') {
// For https://hack.chat/ // For https://hack.chat/
ws = new WebSocket('wss://hack.chat/chat-ws') ws = new WebSocket('wss://hack.chat/chat-ws');
} else { } else {
// for local installs // for local installs
ws = new WebSocket('ws://' + document.domain + ':6060') ws = new WebSocket('ws://' + document.domain + ':6060');
} }
var wasConnected = false var wasConnected = false;
ws.onopen = function() { ws.onopen = function () {
if (!wasConnected) { if (!wasConnected) {
if (location.hash) { if (location.hash) {
myNick = location.hash.substr(1) myNick = location.hash.substr(1);
} } else {
else { myNick = prompt('Nickname:', myNick);
myNick = prompt('Nickname:', myNick)
} }
} }
if (myNick) { if (myNick) {
localStorageSet('my-nick', myNick) localStorageSet('my-nick', myNick);
send({cmd: 'join', channel: channel, nick: myNick}) send({ cmd: 'join', channel: channel, nick: myNick });
} }
wasConnected = true
wasConnected = true;
} }
ws.onclose = function() { ws.onclose = function () {
if (wasConnected) { if (wasConnected) {
pushMessage({nick: '!', text: "Server disconnected. Attempting to reconnect..."}) pushMessage({ nick: '!', text: "Server disconnected. Attempting to reconnect. . ." });
} }
window.setTimeout(function() {
join(channel) window.setTimeout(function () {
}, 2000) join(channel);
}, 2000);
} }
ws.onmessage = function(message) { ws.onmessage = function (message) {
var args = JSON.parse(message.data) var args = JSON.parse(message.data);
var cmd = args.cmd var cmd = args.cmd;
var command = COMMANDS[cmd] var command = COMMANDS[cmd];
command.call(null, args) command.call(null, args);
} }
} }
var COMMANDS = { var COMMANDS = {
chat: function(args) { chat: function (args) {
if (ignoredUsers.indexOf(args.nick) >= 0) { if (ignoredUsers.indexOf(args.nick) >= 0) {
return return;
} }
pushMessage(args)
},
info: function(args) {
args.nick = '*'
pushMessage(args)
},
warn: function(args) {
args.nick = '!'
pushMessage(args)
},
onlineSet: function(args) {
var nicks = args.nicks
usersClear()
nicks.forEach(function(nick) {
userAdd(nick)
})
pushMessage({nick: '*', text: "Users online: " + nicks.join(", ")})
},
onlineAdd: function(args) {
var nick = args.nick
userAdd(nick)
if ($('#joined-left').checked) {
pushMessage({nick: '*', text: nick + " joined"})
}
},
onlineRemove: function(args) {
var nick = args.nick
userRemove(nick)
if ($('#joined-left').checked) {
pushMessage({nick: '*', text: nick + " left"})
}
},
}
pushMessage(args);
},
info: function (args) {
args.nick = '*';
pushMessage(args);
},
warn: function (args) {
args.nick = '!';
pushMessage(args);
},
onlineSet: function (args) {
var nicks = args.nicks;
usersClear();
nicks.forEach(function (nick) {
userAdd(nick);
});
pushMessage({ nick: '*', text: "Users online: " + nicks.join(", ") })
},
onlineAdd: function (args) {
var nick = args.nick;
userAdd(nick);
if ($('#joined-left').checked) {
pushMessage({ nick: '*', text: nick + " joined" });
}
},
onlineRemove: function (args) {
var nick = args.nick;
userRemove(nick);
if ($('#joined-left').checked) {
pushMessage({ nick: '*', text: nick + " left" });
}
}
}
function pushMessage(args) { function pushMessage(args) {
// Message container // Message container
var messageEl = document.createElement('div') var messageEl = document.createElement('div');
messageEl.classList.add('message') messageEl.classList.add('message');
if (args.nick == myNick) { if (args.nick == myNick) {
messageEl.classList.add('me') messageEl.classList.add('me');
} } else if (args.nick == '!') {
else if (args.nick == '!') { messageEl.classList.add('warn');
messageEl.classList.add('warn') } else if (args.nick == '*') {
} messageEl.classList.add('info');
else if (args.nick == '*') { } else if (args.admin) {
messageEl.classList.add('info') messageEl.classList.add('admin');
} } else if (args.mod) {
else if (args.admin) { messageEl.classList.add('mod');
messageEl.classList.add('admin')
}
else if (args.mod) {
messageEl.classList.add('mod')
} }
// Nickname // Nickname
var nickSpanEl = document.createElement('span') var nickSpanEl = document.createElement('span');
nickSpanEl.classList.add('nick') nickSpanEl.classList.add('nick');
messageEl.appendChild(nickSpanEl) messageEl.appendChild(nickSpanEl);
if (args.trip) { if (args.trip) {
var tripEl = document.createElement('span') var tripEl = document.createElement('span');
tripEl.textContent = args.trip + " " tripEl.textContent = args.trip + " ";
tripEl.classList.add('trip') tripEl.classList.add('trip');
nickSpanEl.appendChild(tripEl) nickSpanEl.appendChild(tripEl);
} }
if (args.nick) { if (args.nick) {
var nickLinkEl = document.createElement('a') var nickLinkEl = document.createElement('a');
nickLinkEl.textContent = args.nick nickLinkEl.textContent = args.nick;
nickLinkEl.onclick = function() {
insertAtCursor("@" + args.nick + " ") nickLinkEl.onclick = function () {
$('#chatinput').focus() insertAtCursor("@" + args.nick + " ");
$('#chatinput').focus();
} }
var date = new Date(args.time || Date.now())
nickLinkEl.title = date.toLocaleString() var date = new Date(args.time || Date.now());
nickSpanEl.appendChild(nickLinkEl) nickLinkEl.title = date.toLocaleString();
nickSpanEl.appendChild(nickLinkEl);
} }
// Text // Text
var textEl = document.createElement('pre') var textEl = document.createElement('pre');
textEl.classList.add('text') textEl.classList.add('text');
textEl.textContent = args.text || '' textEl.textContent = args.text || '';
textEl.innerHTML = textEl.innerHTML.replace(/(\?|https?:\/\/)\S+?(?=[,.!?:)]?\s|$)/g, parseLinks) textEl.innerHTML = textEl.innerHTML.replace(/(\?|https?:\/\/)\S+?(?=[,.!?:)]?\s|$)/g, parseLinks);
if ($('#parse-latex').checked) { if ($('#parse-latex').checked) {
// Temporary hotfix for \rule spamming, see https://github.com/Khan/KaTeX/issues/109 // Temporary hotfix for \rule spamming, see https://github.com/Khan/KaTeX/issues/109
textEl.innerHTML = textEl.innerHTML.replace(/\\rule|\\\\\s*\[.*?\]/g, '') textEl.innerHTML = textEl.innerHTML.replace(/\\rule|\\\\\s*\[.*?\]/g, '');
try { try {
renderMathInElement(textEl, {delimiters: [ renderMathInElement(textEl, {delimiters: [
{left: "$$", right: "$$", display: true}, { left: "$$", right: "$$", display: true },
{left: "$", right: "$", display: false}, { left: "$", right: "$", display: false },
]}) ]})
} } catch (e) {
catch (e) { console.warn(e);
console.warn(e)
} }
} }
messageEl.appendChild(textEl) messageEl.appendChild(textEl);
// Scroll to bottom // Scroll to bottom
var atBottom = isAtBottom() var atBottom = isAtBottom();
$('#messages').appendChild(messageEl) $('#messages').appendChild(messageEl);
if (atBottom) { if (atBottom) {
window.scrollTo(0, document.body.scrollHeight) window.scrollTo(0, document.body.scrollHeight);
} }
unread += 1 unread += 1;
updateTitle() updateTitle();
} }
function insertAtCursor(text) { function insertAtCursor(text) {
var input = $('#chatinput') var input = $('#chatinput');
var start = input.selectionStart || 0 var start = input.selectionStart || 0;
var before = input.value.substr(0, start) var before = input.value.substr(0, start);
var after = input.value.substr(start) var after = input.value.substr(start);
before += text
input.value = before + after
input.selectionStart = input.selectionEnd = before.length
updateInputSize()
}
before += text;
input.value = before + after;
input.selectionStart = input.selectionEnd = before.length;
updateInputSize();
}
function send(data) { function send(data) {
if (ws && ws.readyState == ws.OPEN) { if (ws && ws.readyState == ws.OPEN) {
ws.send(JSON.stringify(data)) ws.send(JSON.stringify(data));
} }
} }
function parseLinks(g0) { function parseLinks(g0) {
var a = document.createElement('a') var a = document.createElement('a');
a.innerHTML = g0
var url = a.textContent a.innerHTML = g0;
a.href = url
a.target = '_blank' var url = a.textContent;
return a.outerHTML
a.href = url;
a.target = '_blank';
return a.outerHTML;
} }
var windowActive = true;
var unread = 0;
var windowActive = true window.onfocus = function () {
var unread = 0 windowActive = true;
window.onfocus = function() { updateTitle();
windowActive = true
updateTitle()
} }
window.onblur = function() { window.onblur = function () {
windowActive = false windowActive = false;
} }
window.onscroll = function() { window.onscroll = function () {
if (isAtBottom()) { if (isAtBottom()) {
updateTitle() updateTitle();
} }
} }
function isAtBottom() { function isAtBottom() {
return (window.innerHeight + window.scrollY) >= (document.body.scrollHeight - 1) return (window.innerHeight + window.scrollY) >= (document.body.scrollHeight - 1);
} }
function updateTitle() { function updateTitle() {
if (windowActive && isAtBottom()) { if (windowActive && isAtBottom()) {
unread = 0 unread = 0;
} }
var title var title;
if (myChannel) { if (myChannel) {
title = "?" + myChannel title = "?" + myChannel;
} } else {
else { title = "hack.chat";
title = "hack.chat"
} }
if (unread > 0) { if (unread > 0) {
title = '(' + unread + ') ' + title title = '(' + unread + ') ' + title;
} }
document.title = title
document.title = title;
} }
/* footer */ $('#footer').onclick = function () {
$('#chatinput').focus();
$('#footer').onclick = function() {
$('#chatinput').focus()
} }
$('#chatinput').onkeydown = function(e) { $('#chatinput').onkeydown = function (e) {
if (e.keyCode == 13 /* ENTER */ && !e.shiftKey) { if (e.keyCode == 13 /* ENTER */ && !e.shiftKey) {
e.preventDefault() e.preventDefault();
// Submit message // Submit message
if (e.target.value != '') { if (e.target.value != '') {
var text = e.target.value var text = e.target.value;
e.target.value = '' e.target.value = '';
send({cmd: 'chat', text: text})
lastSent[0] = text send({ cmd: 'chat', text: text });
lastSent.unshift("")
lastSentPos = 0 lastSent[0] = text;
updateInputSize() lastSent.unshift("");
lastSentPos = 0;
updateInputSize();
} }
} } else if (e.keyCode == 38 /* UP */) {
else if (e.keyCode == 38 /* UP */) {
// Restore previous sent messages // Restore previous sent messages
if (e.target.selectionStart === 0 && lastSentPos < lastSent.length - 1) { if (e.target.selectionStart === 0 && lastSentPos < lastSent.length - 1) {
e.preventDefault() e.preventDefault();
if (lastSentPos == 0) { if (lastSentPos == 0) {
lastSent[0] = e.target.value lastSent[0] = e.target.value;
} }
lastSentPos += 1
e.target.value = lastSent[lastSentPos] lastSentPos += 1;
e.target.selectionStart = e.target.selectionEnd = e.target.value.length e.target.value = lastSent[lastSentPos];
updateInputSize() e.target.selectionStart = e.target.selectionEnd = e.target.value.length;
updateInputSize();
} }
} } else if (e.keyCode == 40 /* DOWN */) {
else if (e.keyCode == 40 /* DOWN */) {
if (e.target.selectionStart === e.target.value.length && lastSentPos > 0) { if (e.target.selectionStart === e.target.value.length && lastSentPos > 0) {
e.preventDefault() e.preventDefault();
lastSentPos -= 1
e.target.value = lastSent[lastSentPos] lastSentPos -= 1;
e.target.selectionStart = e.target.selectionEnd = 0 e.target.value = lastSent[lastSentPos];
updateInputSize() e.target.selectionStart = e.target.selectionEnd = 0;
updateInputSize();
} }
} } else if (e.keyCode == 27 /* ESC */) {
else if (e.keyCode == 27 /* ESC */) { e.preventDefault();
e.preventDefault()
// Clear input field // Clear input field
e.target.value = "" e.target.value = "";
lastSentPos = 0 lastSentPos = 0;
lastSent[lastSentPos] = "" lastSent[lastSentPos] = "";
updateInputSize()
} updateInputSize();
else if (e.keyCode == 9 /* TAB */) { } else if (e.keyCode == 9 /* TAB */) {
// Tab complete nicknames starting with @ // Tab complete nicknames starting with @
e.preventDefault() e.preventDefault();
var pos = e.target.selectionStart || 0
var text = e.target.value var pos = e.target.selectionStart || 0;
var index = text.lastIndexOf('@', pos) var text = e.target.value;
var index = text.lastIndexOf('@', pos);
if (index >= 0) { if (index >= 0) {
var stub = text.substring(index + 1, pos).toLowerCase() var stub = text.substring(index + 1, pos).toLowerCase();
// Search for nick beginning with stub // Search for nick beginning with stub
var nicks = onlineUsers.filter(function(nick) { var nicks = onlineUsers.filter(function (nick) {
return nick.toLowerCase().indexOf(stub) == 0 return nick.toLowerCase().indexOf(stub) == 0
}) });
if (nicks.length == 1) { if (nicks.length == 1) {
insertAtCursor(nicks[0].substr(stub.length) + " ") insertAtCursor(nicks[0].substr(stub.length) + " ");
} }
} }
} }
} }
function updateInputSize() { function updateInputSize() {
var atBottom = isAtBottom() var atBottom = isAtBottom();
var input = $('#chatinput') var input = $('#chatinput');
input.style.height = 0 input.style.height = 0;
input.style.height = input.scrollHeight + 'px' input.style.height = input.scrollHeight + 'px';
document.body.style.marginBottom = $('#footer').offsetHeight + 'px' document.body.style.marginBottom = $('#footer').offsetHeight + 'px';
if (atBottom) { if (atBottom) {
window.scrollTo(0, document.body.scrollHeight) window.scrollTo(0, document.body.scrollHeight);
} }
} }
$('#chatinput').oninput = function() { $('#chatinput').oninput = function () {
updateInputSize() updateInputSize();
} }
updateInputSize() updateInputSize();
/* sidebar */ /* sidebar */
$('#sidebar').onmouseenter = $('#sidebar').ontouchstart = function(e) { $('#sidebar').onmouseenter = $('#sidebar').ontouchstart = function (e) {
$('#sidebar-content').classList.remove('hidden') $('#sidebar-content').classList.remove('hidden');
e.stopPropagation() e.stopPropagation();
} }
$('#sidebar').onmouseleave = document.ontouchstart = function() { $('#sidebar').onmouseleave = document.ontouchstart = function () {
if (!$('#pin-sidebar').checked) { if (!$('#pin-sidebar').checked) {
$('#sidebar-content').classList.add('hidden') $('#sidebar-content').classList.add('hidden');
} }
} }
$('#clear-messages').onclick = function() { $('#clear-messages').onclick = function () {
// Delete children elements // Delete children elements
var messages = $('#messages') var messages = $('#messages');
while (messages.firstChild) { while (messages.firstChild) {
messages.removeChild(messages.firstChild) messages.removeChild(messages.firstChild);
} }
} }
// Restore settings from localStorage // Restore settings from localStorage
if (localStorageGet('pin-sidebar') == 'true') { if (localStorageGet('pin-sidebar') == 'true') {
$('#pin-sidebar').checked = true $('#pin-sidebar').checked = true;
$('#sidebar-content').classList.remove('hidden') $('#sidebar-content').classList.remove('hidden');
}
if (localStorageGet('joined-left') == 'false') {
$('#joined-left').checked = false
}
if (localStorageGet('parse-latex') == 'false') {
$('#parse-latex').checked = false
} }
$('#pin-sidebar').onchange = function(e) { if (localStorageGet('joined-left') == 'false') {
localStorageSet('pin-sidebar', !!e.target.checked) $('#joined-left').checked = false;
} }
$('#joined-left').onchange = function(e) {
localStorageSet('joined-left', !!e.target.checked) if (localStorageGet('parse-latex') == 'false') {
$('#parse-latex').checked = false;
} }
$('#parse-latex').onchange = function(e) {
localStorageSet('parse-latex', !!e.target.checked) $('#pin-sidebar').onchange = function (e) {
localStorageSet('pin-sidebar', !!e.target.checked);
}
$('#joined-left').onchange = function (e) {
localStorageSet('joined-left', !!e.target.checked);
}
$('#parse-latex').onchange = function (e) {
localStorageSet('parse-latex', !!e.target.checked);
} }
// User list // User list
var onlineUsers = [];
var onlineUsers = [] var ignoredUsers = [];
var ignoredUsers = []
function userAdd(nick) { function userAdd(nick) {
var user = document.createElement('a') var user = document.createElement('a');
user.textContent = nick user.textContent = nick;
user.onclick = function(e) {
user.onclick = function (e) {
userInvite(nick) userInvite(nick)
} }
var userLi = document.createElement('li')
userLi.appendChild(user) var userLi = document.createElement('li');
$('#users').appendChild(userLi) userLi.appendChild(user);
onlineUsers.push(nick) $('#users').appendChild(userLi);
onlineUsers.push(nick);
} }
function userRemove(nick) { function userRemove(nick) {
var users = $('#users') var users = $('#users');
var children = users.children var children = users.children;
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
var user = children[i] var user = children[i];
if (user.textContent == nick) { if (user.textContent == nick) {
users.removeChild(user) users.removeChild(user);
} }
} }
var index = onlineUsers.indexOf(nick)
var index = onlineUsers.indexOf(nick);
if (index >= 0) { if (index >= 0) {
onlineUsers.splice(index, 1) onlineUsers.splice(index, 1);
} }
} }
function usersClear() { function usersClear() {
var users = $('#users') var users = $('#users');
while (users.firstChild) { while (users.firstChild) {
users.removeChild(users.firstChild) users.removeChild(users.firstChild);
} }
onlineUsers.length = 0
onlineUsers.length = 0;
} }
function userInvite(nick) { function userInvite(nick) {
send({cmd: 'invite', nick: nick}) send({ cmd: 'invite', nick: nick });
} }
function userIgnore(nick) { function userIgnore(nick) {
ignoredUsers.push(nick) ignoredUsers.push(nick);
} }
/* color scheme switcher */ /* color scheme switcher */
@ -495,44 +524,42 @@ var schemes = [
'pop', 'pop',
'railscasts', 'railscasts',
'solarized', 'solarized',
'tomorrow', 'tomorrow'
] ];
var currentScheme = 'atelier-dune' var currentScheme = 'atelier-dune';
function setScheme(scheme) { function setScheme(scheme) {
currentScheme = scheme currentScheme = scheme;
$('#scheme-link').href = "/schemes/" + scheme + ".css" $('#scheme-link').href = "schemes/" + scheme + ".css";
localStorageSet('scheme', scheme) localStorageSet('scheme', scheme);
} }
// Add scheme options to dropdown selector // Add scheme options to dropdown selector
schemes.forEach(function(scheme) { schemes.forEach(function (scheme) {
var option = document.createElement('option') var option = document.createElement('option');
option.textContent = scheme option.textContent = scheme;
option.value = scheme option.value = scheme;
$('#scheme-selector').appendChild(option) $('#scheme-selector').appendChild(option);
}) });
$('#scheme-selector').onchange = function(e) { $('#scheme-selector').onchange = function (e) {
setScheme(e.target.value) setScheme(e.target.value);
} }
// Load sidebar configaration values from local storage if available // Load sidebar configaration values from local storage if available
if (localStorageGet('scheme')) { if (localStorageGet('scheme')) {
setScheme(localStorageGet('scheme')) setScheme(localStorageGet('scheme'));
} }
$('#scheme-selector').value = currentScheme $('#scheme-selector').value = currentScheme;
/* main */ /* main */
if (myChannel == '') { if (myChannel == '') {
pushMessage({text: frontpage}) pushMessage({ text: frontpage });
$('#footer').classList.add('hidden') $('#footer').classList.add('hidden');
$('#sidebar').classList.add('hidden') $('#sidebar').classList.add('hidden');
} } else {
else { join(myChannel);
join(myChannel)
} }