import io from 'socket.io-client';
import $ from 'jquery';
import {Remarkable} from 'remarkable';
import DOMPurify from 'dompurify';
import rkatex from 'remarkable-katex';
import hljs from 'highlightjs';
import 'katex';
let username;
let is_reconnection = false;
let last_command = null;
const server = `ws://${location.host}`;
let ws;
let md;
let unread_message = 0;
$(() => {
init().then(x => ws = x).catch(err => console.log(err))
});
function clear_message() {
$('#message').empty();
}
function clear_send() {
$('#send').val('');
}
function format_time(time) {
return `${time.getMonth() + 1}-${time.getDate()} ${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}`;
}
function render_chess(msg) {
if (!msg.toUpperCase().includes("!{LC}")) {
return msg;
}
url = msg.substring(msg.indexOf("(") + 1, msg.indexOf(")"));
if (url.length == 0) {
return msg;
}
if (url.toUpperCase() == "TV") {
return '';
}
if (url.toUpperCase() == "TRAINING" || url.toUpperCase() == "PUZZLE") {
return ``;
}
return ``;
}
function render_bilibili(msg) {
if (!msg.toUpperCase().includes("!{BI}")) {
return msg;
}
url = msg.substring(msg.indexOf("(") + 1, msg.indexOf(")"));
if (url.length == 0) {
return msg;
}
return ``
}
function write_message(data) {
const message = data.msg;
let message_class = '';
let message_source = data.from;
if (data.type === 'system-message') {
message_class = 'msg-from-info';
message_source = 'SYSTEM INFO';
} else if (data.type === 'command-block') {
message_class = 'msg-from-cb';
message_source = 'COMMAND BLOCK';
}
let rendered_message;
if (!data.plain) {
rendered_message = md.render(message);
} else {
rendered_message = `${message}`;
}
rendered_message = DOMPurify.sanitize(rendered_message);
rendered_message = render_chess(rendered_message);
rendered_message = render_bilibili(rendered_message);
$('#message').append(`
${message_source}
${format_time(new Date())}
${rendered_message}
`
);
scroll_to_bottom();
}
function notify_new_message(msg, is_private) {
if (document.visibilityState === 'visible') { return; }
unread_message += 1;
$('title').text(`(${unread_message} new message) WebSocket Chat Room`);
if (Notification.permission === 'granted' || is_private || msg.startsWith(`@${username}`) || msg.endsWith(`@${username}`)) {
const n = new Notification(`WS-Chat: You have ${unread_message} new messages unread.`);
setTimeout(() => { n.close() }, 3000);
}
}
async function init() {
$('#prompt-data').on('keyup', (e) => {
if (e.key === 'Enter') { $('#confirm-prompt').click(); }
});
clear_message();
write_message({
type: 'system-message',
msg: 'Notification permission are use to get you informated. Please allow it.',
is_private: true,
plain: true,
});
await Notification.requestPermission();
await login_name();
md = new Remarkable({
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(lang, str).value;
} catch (err) { }
}
try {
return hljs.highlightAuto(str).value;
} catch (err) { }
return '';
}
});
md.use(rkatex);
md.inline.ruler.enable(['mark', 'sup', 'sub']);
const ws = new io(server);
ws.on('connect', () => {
if (is_reconnection) {
write_message({
type: 'system-message',
msg: 'Reconnected.',
is_private: true,
plain: true,
});
} else {
write_message({
type: 'system-message',
msg: 'Connected.',
is_private: true,
plain: true,
});
is_reconnection = true;
}
ws.emit('set-name', username);
});
ws.on('system-message', msg => {
write_message({
type: 'system-message',
msg: msg,
is_private: true,
plain: true,
});
});
ws.on('change username', new_name => {
username = new_name;
});
ws.on('new message', evt => {
write_message({
type: 'normal',
from: evt.sender,
msg: evt.data,
});
notify_new_message(evt.data, false);
});
ws.on('private message', evt => {
write_message({
type: 'normal',
from: evt.sender,
msg: evt.data,
is_private: true,
});
notify_new_message(evt.data, true);
})
ws.on('command-block-reply', data => {
write_message({
type: 'command-block',
msg: `>> ${last_command}\n<- ${data}`,
is_private: true,
plain: true,
});
});
ws.on('disconnect', () => {
write_message({
type: 'system-message',
msg: 'Disconnected.',
is_private: true,
plain: true,
});
});
$('#send').focus();
return ws;
}
function open_prompt(title) {
$('#prompt-box-title').text(title);
$('#prompt-data').val('');
$('#prompt-box').show('fast', () => { $('#prompt-data').focus(); });
$('#prompt-background').show();
let resolve_callback;
let res = new Promise((resolve) => {
resolve_callback = resolve;
});
$('#confirm-prompt').one('click', () => {
if (resolve_callback) {
$('#prompt-box').hide('fast');
$('#prompt-background').hide();
resolve_callback($('#prompt-data').val());
}
});
return res;
}
async function get_url_query(query_name) {
return new Promise(resolve => {
var reg = new RegExp("(^|&)" + query_name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
var context = "";
if (r != null)
context = r[2];
reg = null;
r = null;
resolve(context == null || context == "" || context == "undefined" ? "" : context);
});
}
async function login_name() {
username = await get_url_query("name")
if (username === undefined || username.length == 0) {
username = await open_prompt('[Login] Input your name');
}
}
async function send() {
let data = $('#send').val();
if (data === '') {
return;
}
if (data === '/clear' || data === '/cls') {
clear_message();
clear_send();
return;
}
if (data.startsWith('/')) {
// a command
last_command = data;
}
if (data.startsWith('/su')) {
// a administrator login command.
// now ask for passcode
const code = await open_prompt('[su] Input the administration passcode');
data = `/su ${code}`;
}
if (ws.connected) {
ws.emit('message', data);
} else {
write_message({
type: 'system-message',
msg: 'Socket not connected or has already disconnected. Failed to send.',
is_private: true,
plain: true,
});
}
clear_send();
}
function scroll_to_bottom() {
if ($('#scroll-option').is(':checked')) {
document.getElementById('message').scrollTop = document.getElementById('message').scrollHeight;
}
}
document.addEventListener('keydown', async function (key_event) {
if (key_event.code === 'Enter' && key_event.ctrlKey) {
key_event.preventDefault();
await send();
}
});
document.addEventListener("visibilitychange", function () {
if (document.visibilityState === 'visible') { unread_message = 0; }
$('title').text(`WebSocket Chat Room`);
});