Consistency / some clean-up

This commit is contained in:
Evert Prants 2020-02-05 23:04:19 +02:00
parent 6c6aa85bf4
commit 43a136d293
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
8 changed files with 192 additions and 241 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "teemantirc", "name": "teemantirc",
"version": "2.0.1", "version": "2.0.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "teemantirc", "name": "teemantirc",
"version": "2.0.1", "version": "2.0.2",
"description": "A Web-based IRC client", "description": "A Web-based IRC client",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -4,17 +4,16 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>TeemantIRC</title> <title>TeemantIRC</title>
<script src="//twemoji.maxcdn.com/2/twemoji.min.js?11.2"></script> <script src="//twemoji.maxcdn.com/v/latest/twemoji.min.js" crossorigin="anonymous"></script>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css"> <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="style/layout.css"> <link rel="stylesheet" type="text/css" href="style/layout.css">
<link rel="stylesheet" type="text/css" href="style/theme_default.css" id="theme_stylesheet"> <link rel="stylesheet" type="text/css" href="style/theme_default.css" id="theme_stylesheet">
</head> </head>
<!-- Codepony Diamond -->
<body> <body>
<section class="ircclient"> <section class="ircclient">
<div class="coverwindow" id="authdialog"> <div class="coverwindow" id="authdialog">
<div class="wrapper"> <div class="wrapper">
<h1 class="grade1">Teemant</h1> <h1 class="grade1" title="Teemant"><img src="/image/diamond.svg" alt="Teemant"/></h1>
<i class="grade3" id="connmsg">Think of a nickname</i> <i class="grade3" id="connmsg">Think of a nickname</i>
<form action="" id="IRCConnector"> <form action="" id="IRCConnector">
<label for="nickname">Nickname</label> <label for="nickname">Nickname</label>

View File

@ -1,4 +1,5 @@
/* global WebSocket */ /* global WebSocket */
/* eslint-disable no-control-regex */
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import parse from './parser' import parse from './parser'
import { format } from 'util' import { format } from 'util'
@ -69,7 +70,7 @@ class FakeSocket {
} }
} }
async function _waitOpen (s) { async function waitForSocketOpen (s) {
let o let o
let c let c
@ -92,7 +93,7 @@ async function _waitOpen (s) {
return true return true
} }
async function _trySocket (address, port, ssl, useTranslator) { async function createSocket (address, port, ssl, useTranslator) {
// Attempt a direct ws connection // Attempt a direct ws connection
let proto = 'ws' let proto = 'ws'
if (ssl) proto = 'wss' if (ssl) proto = 'wss'
@ -104,10 +105,10 @@ async function _trySocket (address, port, ssl, useTranslator) {
} }
let tSock = new WebSocket(proto + '://' + conn) let tSock = new WebSocket(proto + '://' + conn)
try { try {
await _waitOpen(tSock) await waitForSocketOpen(tSock)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
return _trySocket(address, port, ssl, true) return createSocket(address, port, ssl, true)
} }
tSock.open = true tSock.open = true
@ -123,7 +124,7 @@ async function _trySocket (address, port, ssl, useTranslator) {
translatorSocket = new WebSocket(`ws${window.location.protocol === 'https:' ? 's' : ''}://${window.location.host}`) translatorSocket = new WebSocket(`ws${window.location.protocol === 'https:' ? 's' : ''}://${window.location.host}`)
console.log('Opening translator socket') console.log('Opening translator socket')
await _waitOpen(translatorSocket) await waitForSocketOpen(translatorSocket)
return new FakeSocket(address, translatorSocket) return new FakeSocket(address, translatorSocket)
} }
@ -152,11 +153,11 @@ class IRCConnectionHandler {
break break
case 'quit': case 'quit':
this.conn.write('%s :%s', data.command.toUpperCase(), (data.message === '' this.conn.write('%s :%s', data.command.toUpperCase(), (data.message === ''
? this.conn.globalConfig.default_quit_msg : data.message)) ? this.conn.defaultParams.default_quit_msg : data.message))
break break
case 'privmsg': case 'privmsg':
this.conn.write('PRIVMSG %s :%s', data.arguments[0], data.message) this.conn.write('PRIVMSG %s :%s', data.arguments[0], data.message)
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'message', type: 'message',
messageType: 'privmsg', messageType: 'privmsg',
to: data.arguments[0], to: data.arguments[0],
@ -169,7 +170,7 @@ class IRCConnectionHandler {
break break
case 'notice': case 'notice':
this.conn.write('NOTICE %s :%s', data.arguments[0], data.message) this.conn.write('NOTICE %s :%s', data.arguments[0], data.message)
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'message', type: 'message',
messageType: 'notice', messageType: 'notice',
to: data.arguments[0], to: data.arguments[0],
@ -193,9 +194,9 @@ class IRCConnectionHandler {
} }
this.conn.write('PRIVMSG %s :\x01%s\x01', data.arguments[0], ctcpmsg) this.conn.write('PRIVMSG %s :\x01%s\x01', data.arguments[0], ctcpmsg)
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'message', type: 'message',
messageType: 'ctcp_request', messageType: 'ctcpRequest',
to: this.conn.config.nickname, to: this.conn.config.nickname,
user: { user: {
nickname: data.arguments[0] nickname: data.arguments[0]
@ -209,7 +210,7 @@ class IRCConnectionHandler {
} }
if (data.targetType === 'channel' || data.targetType === 'message') { if (data.targetType === 'channel' || data.targetType === 'message') {
this.conn.write('PRIVMSG %s :%s', data.target, data.message) this.conn.write('PRIVMSG %s :%s', data.target, data.message)
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'message', type: 'message',
messageType: 'privmsg', messageType: 'privmsg',
to: data.target, to: data.target,
@ -283,7 +284,12 @@ class IRCConnectionHandler {
let list = null let list = null
switch (line.command) { switch (line.command) {
case 'error': case 'error':
this.conn.emit('connerror', { type: 'irc_error', raw: line.raw }) this.conn.emit('connectionError', new Error('IRCError' + line.raw))
break
case 'PONG':
this.conn.ping = Date.now() - this.conn.pingSent
this.conn.pingSent = 0
this.conn.emit('ping', this.conn.ping)
break break
case '001': case '001':
this.conn.data.actualServer = line.user.hostname this.conn.data.actualServer = line.user.hostname
@ -321,16 +327,16 @@ class IRCConnectionHandler {
break break
case 'JOIN': case 'JOIN':
if (line.trailing) { if (line.trailing) {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'event_join_channel', type: 'joinChannel',
user: line.user, user: line.user,
channel: line.trailing, channel: line.trailing,
server: serverName server: serverName
}) })
} else { } else {
for (let i in line.arguments) { for (let i in line.arguments) {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'event_join_channel', type: 'joinChannel',
user: line.user, user: line.user,
channel: line.arguments[i], channel: line.arguments[i],
server: serverName server: serverName
@ -339,8 +345,8 @@ class IRCConnectionHandler {
} }
break break
case 'PART': case 'PART':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'event_part_channel', type: 'partChannel',
user: line.user, user: line.user,
channel: line.arguments[0], channel: line.arguments[0],
reason: line.trailing, reason: line.trailing,
@ -348,8 +354,8 @@ class IRCConnectionHandler {
}) })
break break
case 'QUIT': case 'QUIT':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'event_quit', type: 'quit',
user: line.user, user: line.user,
reason: line.trailing, reason: line.trailing,
server: serverName server: serverName
@ -375,8 +381,8 @@ class IRCConnectionHandler {
case '366': case '366':
if (!this.conn.queue['names']) break if (!this.conn.queue['names']) break
if (this.conn.queue['names'][line.arguments[1]]) { if (this.conn.queue['names'][line.arguments[1]]) {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'channel_nicks', type: 'nicks',
channel: line.arguments[1], channel: line.arguments[1],
nicks: this.conn.queue['names'][line.arguments[1]], nicks: this.conn.queue['names'][line.arguments[1]],
server: serverName server: serverName
@ -395,7 +401,7 @@ class IRCConnectionHandler {
} }
if (line.user.nickname !== '') { if (line.user.nickname !== '') {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'message', type: 'message',
messageType: 'privmsg', messageType: 'privmsg',
to: line.arguments[0], to: line.arguments[0],
@ -404,8 +410,8 @@ class IRCConnectionHandler {
server: serverName server: serverName
}) })
} else { } else {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'server_message', type: 'serverMessage',
messageType: 'privmsg', messageType: 'privmsg',
message: line.trailing, message: line.trailing,
server: serverName, server: serverName,
@ -423,9 +429,9 @@ class IRCConnectionHandler {
message = Math.floor(Date.now() / 1000) - composethis[1] + 's' message = Math.floor(Date.now() / 1000) - composethis[1] + 's'
} }
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'message', type: 'message',
messageType: 'ctcp_response', messageType: 'ctcpResponse',
to: line.arguments[0], to: line.arguments[0],
user: line.user, user: line.user,
message: message, message: message,
@ -435,7 +441,7 @@ class IRCConnectionHandler {
} }
if (line.user.nickname !== '') { if (line.user.nickname !== '') {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'message', type: 'message',
messageType: 'notice', messageType: 'notice',
to: line.arguments[0], to: line.arguments[0],
@ -444,8 +450,8 @@ class IRCConnectionHandler {
server: serverName server: serverName
}) })
} else { } else {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'server_message', type: 'serverMessage',
messageType: 'notice', messageType: 'notice',
message: line.trailing, message: line.trailing,
server: serverName, server: serverName,
@ -458,35 +464,35 @@ class IRCConnectionHandler {
this.conn.config.nickname = line.arguments[0] this.conn.config.nickname = line.arguments[0]
} }
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'nick_change', nick: line.user.nickname, newNick: line.arguments[0], server: serverName type: 'nick', nick: line.user.nickname, newNick: line.arguments[0], server: serverName
}) })
break break
case 'KICK': case 'KICK':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'event_kick_channel', user: line.user, channel: line.arguments[0], reason: line.trailing, kickee: line.arguments[1], server: serverName type: 'kickedFromChannel', user: line.user, channel: line.arguments[0], reason: line.trailing, kickee: line.arguments[1], server: serverName
}) })
break break
case 'TOPIC': case 'TOPIC':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'channel_topic', channel: line.arguments[0], set_by: line.user.nickname, topic: line.trailing, server: serverName type: 'topic', channel: line.arguments[0], setBy: line.user.nickname, topic: line.trailing, server: serverName
}) })
break break
case '332': case '332':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'channel_topic', channel: line.arguments[1], topic: line.trailing, server: serverName type: 'topic', channel: line.arguments[1], topic: line.trailing, server: serverName
}) })
break break
case '333': case '333':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'channel_topic', channel: line.arguments[1], set_by: line.arguments[2], time: (line.arguments[3] || line.trailing), server: serverName type: 'topic', channel: line.arguments[1], setBy: line.arguments[2], time: (line.arguments[3] || line.trailing), server: serverName
}) })
break break
case '375': case '375':
case '372': case '372':
case '376': case '376':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'server_message', messageType: 'motd', message: line.trailing, server: serverName, from: realServerName type: 'serverMessage', messageType: 'motd', message: line.trailing, server: serverName, from: realServerName
}) })
break break
case '006': case '006':
@ -500,16 +506,16 @@ class IRCConnectionHandler {
case '351': case '351':
case '381': case '381':
case '489': case '489':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'server_message', messageType: 'regular', message: line.trailing, server: serverName, from: realServerName type: 'serverMessage', messageType: 'regular', message: line.trailing, server: serverName, from: realServerName
}) })
break break
case '252': case '252':
case '254': case '254':
case '396': case '396':
case '042': case '042':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'server_message', messageType: 'regular', message: line.arguments[1] + ' ' + line.trailing, server: serverName, from: realServerName type: 'serverMessage', messageType: 'regular', message: line.arguments[1] + ' ' + line.trailing, server: serverName, from: realServerName
}) })
break break
case '501': case '501':
@ -519,7 +525,7 @@ class IRCConnectionHandler {
case '482': case '482':
case '331': case '331':
case '432': case '432':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'message', type: 'message',
to: null, to: null,
message: line.arguments[1] + ': ' + line.trailing, message: line.arguments[1] + ': ' + line.trailing,
@ -556,8 +562,8 @@ class IRCConnectionHandler {
for (let i in modes) { for (let i in modes) {
let mode = modes[i] let mode = modes[i]
if (this.conn.data.supportedModes[mode]) { if (this.conn.data.supportedModes[mode]) {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'mode_' + (method === '+' ? 'add' : 'del'), type: 'mode' + (method === '+' ? 'Add' : 'Del'),
target: line.arguments[0], target: line.arguments[0],
mode: mode, mode: mode,
modeTarget: line.arguments[2 + parseInt(i)], modeTarget: line.arguments[2 + parseInt(i)],
@ -575,7 +581,7 @@ class IRCConnectionHandler {
} }
if (pass.length > 0) { if (pass.length > 0) {
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'mode', type: 'mode',
target: line.arguments[0], target: line.arguments[0],
message: method + pass.join(''), message: method + pass.join(''),
@ -622,7 +628,7 @@ class IRCConnectionHandler {
case '312': case '312':
list = { list = {
server: line.arguments[2], server: line.arguments[2],
server_name: line.trailing || '' serverName: line.trailing || ''
} }
this.whoisManage(line.arguments[1], list) this.whoisManage(line.arguments[1], list)
break break
@ -666,7 +672,7 @@ class IRCConnectionHandler {
case '318': case '318':
if (!this.conn.queue.whois || !this.conn.queue.whois[line.arguments[1]]) return if (!this.conn.queue.whois || !this.conn.queue.whois[line.arguments[1]]) return
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'whoisResponse', type: 'whoisResponse',
whois: this.conn.queue.whois[line.arguments[1]], whois: this.conn.queue.whois[line.arguments[1]],
server: serverName, server: serverName,
@ -676,8 +682,8 @@ class IRCConnectionHandler {
delete this.conn.queue.whois[line.arguments[1]] delete this.conn.queue.whois[line.arguments[1]]
break break
case '321': case '321':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'listedchan', type: 'listedChannel',
channel: 'Channel', channel: 'Channel',
users: 'Users', users: 'Users',
topic: 'Topic', topic: 'Topic',
@ -686,8 +692,8 @@ class IRCConnectionHandler {
}) })
break break
case '322': case '322':
this.conn.emit('pass_to_client', { this.conn.emit('fromServer', {
type: 'listedchan', type: 'listedChannel',
channel: line.arguments[1], channel: line.arguments[1],
users: line.arguments[2], users: line.arguments[2],
topic: line.trailing, topic: line.trailing,
@ -699,27 +705,37 @@ class IRCConnectionHandler {
// might come in the future, who knows // might come in the future, who knows
this.conn.write('CAP END') this.conn.write('CAP END')
break break
default:
let argc = line.arguments
if (argc.indexOf(this.conn.config.nickname) === 0) argc = argc.slice(1)
this.conn.emit('fromServer', {
type: 'serverMessage',
messageType: 'unknown',
message: (argc.length ? argc.join(' ') + ' :' : '') + line.trailing,
server: serverName,
from: realServerName
})
} }
} }
} }
class IRCConnection extends EventEmitter { class IRCConnection extends EventEmitter {
constructor (providedInfo, globalConfig, extras) { constructor (providedInfo, defaultParams, extras) {
super() super()
this.globalConfig = globalConfig this.defaultParams = defaultParams
this.extras = extras || { authenticationSteps: [], ctcps: {} } this.extras = extras || { authenticationSteps: [], ctcps: {} }
this.config = { this.config = {
nickname: 'teemant', nickname: 'teemant',
username: globalConfig.username, username: defaultParams.username,
realname: globalConfig.realname, realname: defaultParams.realname,
server: 'localhost', server: 'localhost',
port: 6667, port: 6667,
autojoin: [], autojoin: [],
secure: globalConfig.secure_by_default, secure: defaultParams.secure_by_default,
password: '', password: '',
address: providedInfo.server, address: providedInfo.server,
rejectUnauthorized: globalConfig.rejectUnauthorizedCertificates rejectUnauthorized: defaultParams.rejectUnauthorizedCertificates
} }
for (let a in providedInfo) { for (let a in providedInfo) {
@ -739,21 +755,19 @@ class IRCConnection extends EventEmitter {
maxChannelLength: 64, maxChannelLength: 64,
supportedModes: {} supportedModes: {}
} }
this.authorizationError = ''
this.queue = {} this.queue = {}
this.pingSent = 0
this.ping = 0
} }
connect () { connect () {
async function wrapped (argument) { async function wrapped (argument) {
try { this.socket = await createSocket(this.config.server, this.config.port, this.config.secure)
this.socket = await _trySocket(this.config.server, this.config.port, this.config.secure)
} catch (e) {
this.emit('connerror', { type: 'sock_error', message: 'A socket error occured.' })
throw e
}
this.socket.addEventListener('message', (e) => { this.socket.addEventListener('message', (e) => {
let line = e.data let line = e.data
// Handle from-server pings
if (line.indexOf('PING') === 0) { if (line.indexOf('PING') === 0) {
this.write('PONG %s', line.substring(4)) this.write('PONG %s', line.substring(4))
return return
@ -774,7 +788,7 @@ class IRCConnection extends EventEmitter {
this.socket.addEventListener('close', (data) => { this.socket.addEventListener('close', (data) => {
if (!this.queue['close']) { if (!this.queue['close']) {
this.emit('closed', { type: 'sock_closed', raw: data, message: 'Connection closed.' }) this.emit('connectionClosed', new Error('Socket has been closed.'))
} }
this.connected = false this.connected = false
@ -804,35 +818,37 @@ class IRCConnection extends EventEmitter {
this.write('NICK %s', this.config.nickname) this.write('NICK %s', this.config.nickname)
this.bindFinal() this.bindFinal()
this.sendPing()
} }
bindFinal () { bindFinal () {
this.on('userinput', (data) => { this.on('userInput', (data) => {
return this.handler.handleUserLine(data) return this.handler.handleUserLine(data)
}) })
this.once('authenticated', () => this.sendPing())
} }
sendPing () { sendPing () {
if (!this.connected) return if (!this.connected) return
this.write('PING :' + this.data.actualServer) this.write('PING :' + this.data.actualServer)
this.pingSent = Date.now()
setTimeout(() => this.sendPing(), 5000) setTimeout(() => this.sendPing(), 5000)
} }
disconnect (message) { disconnect (message) {
if (!this.connected) { if (!this.connected) {
this.emit('connerror', { type: 'sock_closed', message: 'Connection already closed.' }) this.emit('connectionError', new Error('SocketError: Socket is already closed.'))
return return
} }
this.queue['close'] = true this.queue['close'] = true
this.write('QUIT :%s', (message != null ? message : this.globalConfig.default_quit_msg)) this.write('QUIT :%s', (message != null ? message : this.defaultParams.default_quit_msg))
} }
write () { write () {
let message = format.apply(null, arguments) let message = format.apply(null, arguments)
if (!this.connected) { if (!this.connected) {
return this.emit('connerror', { type: 'sock_closed', message: 'Connection is closed.' }) return this.emit('connectionError', new Error('SocketError: Socket is closed.'))
} }
this.socket.send(message + '\r\n') this.socket.send(message + '\r\n')

View File

@ -38,13 +38,16 @@ const customCTCPs = {
}, },
SOURCE: function (data, connection) { SOURCE: function (data, connection) {
return 'https://gitlab.icynet.eu/IcyNetwork/teemant' return 'https://gitlab.icynet.eu/IcyNetwork/teemant'
},
TIME: function (data, connection) {
return new Date()
} }
} }
const clientdom = { connector: {}, settings: {} } const clientdom = { connector: {}, settings: {} }
const colorizer = { const colorizer = {
get_random_color: function (nickname) { getRandomColor: function (nickname) {
let themefunc = themes.available[irc.config.theme].nick_pallete let themefunc = themes.available[irc.config.theme].nick_pallete
let randgen = seedrandom(nickname) let randgen = seedrandom(nickname)
@ -61,9 +64,8 @@ const colorizer = {
// Utilities // Utilities
const validators = {} const validators = {
iporhost: function (str) {
validators.iporhost = function (str) {
let valid = false let valid = false
if (str.match(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/i)) { if (str.match(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/i)) {
@ -73,26 +75,22 @@ validators.iporhost = function (str) {
} }
return valid return valid
} },
nickname: function (str) {
validators.nickname = function (str) {
if (str.match(/[a-z_\-\[\]\\^{}|`][a-z0-9_\-\[\]\\^{}|`]*/i)) { if (str.match(/[a-z_\-\[\]\\^{}|`][a-z0-9_\-\[\]\\^{}|`]*/i)) {
return true return true
} }
return false return false
} },
escapeHTML: function (str) {
validators.escapeHTML = function (str) {
return str.replace(/</g, '&lt;').replace(/>/, '&gt;') return str.replace(/</g, '&lt;').replace(/>/, '&gt;')
}
} }
const processors = { const processors = {
inline_color: function (text) { inlineColor: function (text) {
// TODO: Figure out how to make hex colors behave
// const hexRegex = /(^|[^&])(\#[0-9a-f]{6};?)(?!\w)/gmi
const rgbRegex = /(.?)(rgba?\((?:\s*\d+\s*,){2}\s*\d+\s*(?:,\s*[\d.]+\s*)?\);?)/gmi const rgbRegex = /(.?)(rgba?\((?:\s*\d+\s*,){2}\s*\d+\s*(?:,\s*[\d.]+\s*)?\);?)/gmi
const substitute = '$1$2 <div class="color_sample" style="background-color:$2"></div>' const substitute = '$1$2 <div class="color_sample" style="background-color:$2"></div>'
// text = text.replace(hexRegex, substitute)
text = text.replace(rgbRegex, substitute) text = text.replace(rgbRegex, substitute)
return text return text
}, },
@ -156,9 +154,9 @@ function whoisMessage (whoisData, buffer) {
break break
case 'server': case 'server':
let adfd = sf('is on %s', span(whoisData[key], ['server', 'nick'])) let adfd = sf('is on %s', span(whoisData[key], ['server', 'nick']))
if (whoisData['server_name']) { if (whoisData['serverName']) {
adfd += '&nbsp;' adfd += '&nbsp;'
adfd += span(validators.escapeHTML(whoisData['server_name']), ['hostmask']) adfd += span(validators.escapeHTML(whoisData['serverName']), ['hostmask'])
} }
messages.push(adfd) messages.push(adfd)
break break
@ -198,7 +196,7 @@ let composer = {
message = colorizer.strip(message) message = colorizer.strip(message)
} }
message = processors.inline_color(message) message = processors.inlineColor(message)
message = processors.channelify(message) message = processors.channelify(message)
message = processors.linkify(message) message = processors.linkify(message)
message = processors.emojify(message) message = processors.emojify(message)
@ -220,11 +218,11 @@ let composer = {
case 'join': case 'join':
element.innerHTML += arrowin + '&nbsp;' + span(span(sender, ['actionee', 'nick']) + '&nbsp;' + message, ['content']) element.innerHTML += arrowin + '&nbsp;' + span(span(sender, ['actionee', 'nick']) + '&nbsp;' + message, ['content'])
break break
case 'ctcp_response': case 'ctcpResponse':
element.innerHTML += asterisk + '&nbsp;CTCP response from ' + span(sender, ['actionee', 'nick']) + '&nbsp;' element.innerHTML += asterisk + '&nbsp;CTCP response from ' + span(sender, ['actionee', 'nick']) + '&nbsp;'
element.innerHTML += span(message, ['content']) element.innerHTML += span(message, ['content'])
break break
case 'ctcp_request': case 'ctcpRequest':
element.innerHTML += asterisk + '&nbsp;CTCP request to ' + span(sender, ['actionee', 'nick']) + '&nbsp;' element.innerHTML += asterisk + '&nbsp;CTCP request to ' + span(sender, ['actionee', 'nick']) + '&nbsp;'
element.innerHTML += span(message, ['content']) element.innerHTML += span(message, ['content'])
break break
@ -241,7 +239,7 @@ let composer = {
if (sender) { if (sender) {
let sndr1 = element.querySelector('.sender') let sndr1 = element.querySelector('.sender')
if (sndr1) { if (sndr1) {
sndr1.style.color = colorizer.get_random_color(sndr1.innerHTML) sndr1.style.color = colorizer.getRandomColor(sndr1.innerHTML)
} }
} }
@ -249,7 +247,7 @@ let composer = {
if (sndr2.length > 0) { if (sndr2.length > 0) {
for (let a in sndr2) { for (let a in sndr2) {
if (sndr2[a] && sndr2[a]['style']) { if (sndr2[a] && sndr2[a]['style']) {
sndr2[a].style.color = colorizer.get_random_color(sndr2[a].innerHTML) sndr2[a].style.color = colorizer.getRandomColor(sndr2[a].innerHTML)
} }
} }
} }
@ -286,7 +284,7 @@ let composer = {
if (defaultNick) params.nickname = defaultNick if (defaultNick) params.nickname = defaultNick
if (port && port !== 6667) params.port = port if (port && port !== 6667) params.port = port
if (useSSL) params.secure = 1 if (useSSL) params.secure = 1
if (hideServerData) params.server_data = 0 if (hideServerData) params.extras = 0
if (Object.keys(params).length > 0) builder += '?' + serialize(params) if (Object.keys(params).length > 0) builder += '?' + serialize(params)
if (channels) { if (channels) {
@ -318,7 +316,7 @@ let composer = {
function sendToServer (server, obj) { function sendToServer (server, obj) {
let i = irc.handler.getClientByActualServer(server) let i = irc.handler.getClientByActualServer(server)
if (!i) throw new Error('Invalid connection ' + server) if (!i) throw new Error('Invalid connection ' + server)
i.emit('userinput', obj) i.emit('userInput', obj)
} }
// onclick food // onclick food
@ -509,7 +507,7 @@ let commands = {
arguments: [buffer.name] arguments: [buffer.name]
}) })
}, },
description: '<message> - act as yourself', description: '<message> - Act as yourself. (CTCP ACTION)',
aliases: ['me'] aliases: ['me']
}, },
@ -524,14 +522,14 @@ let commands = {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
if (!listargs[1]) { if (!listargs[1]) {
if (buffer.server !== '' && irc.serverData[buffer.server] != null) { if (buffer.server !== '' && irc.serverData[buffer.server] != null) {
let mynick = irc.serverData[buffer.server].my_nick let mynick = irc.serverData[buffer.server].userNick
buffer.addMessage('Your nickname is: <span class="nick">' + mynick + '</span>', null, 'help') buffer.addMessage('Your nickname is: <span class="nick">' + mynick + '</span>', null, 'help')
} }
return return
} }
sendToServer(buffer.server, { command: 'nick', message: '', arguments: listargs.splice(1) }) sendToServer(buffer.server, { command: 'nick', message: '', arguments: listargs.splice(1) })
}, },
description: '- List all channels on the current server.', description: '[<nickname>] - Set/display your nickname.',
aliases: ['nickname'] aliases: ['nickname']
}, },
@ -587,6 +585,14 @@ let commands = {
description: '- Create a new connection.' description: '- Create a new connection.'
}, },
ping: {
execute: function (buffer, handler, command, message, listargs) {
let server = irc.handler.getClientByActualServer(buffer.server)
buffer.addMessage('Connection latency: ' + server.ping + 'ms', null, 'help')
},
aliases: ['latency', 'lag']
},
help: { help: {
execute: function (buffer, handler, command, message, listargs) { execute: function (buffer, handler, command, message, listargs) {
if (!listargs[1]) { if (!listargs[1]) {
@ -677,7 +683,7 @@ class Nicklist {
} }
if (irc.config.colorNicklist) { if (irc.config.colorNicklist) {
construct += '<span class="nickname" style="color: ' + colorizer.get_random_color(nick.nickname) + ';">' + nick.nickname + '</span>' construct += '<span class="nickname" style="color: ' + colorizer.getRandomColor(nick.nickname) + ';">' + nick.nickname + '</span>'
} else { } else {
construct += span(nick.nickname, ['nickname']) construct += span(nick.nickname, ['nickname'])
} }
@ -700,7 +706,7 @@ class Nicklist {
this.appendToList(nick) this.appendToList(nick)
} }
irc.chat.input_handler.searchNicknames = this.simplifiedNicksList irc.chat.inputHandler.searchNicknames = this.simplifiedNicksList
} }
nickAdd (nickname) { nickAdd (nickname) {
@ -965,7 +971,7 @@ class ChatBuffer {
clientdom.letterbox.scrollTop = this.lastscroll clientdom.letterbox.scrollTop = this.lastscroll
} }
clientdom.currentNickname.innerHTML = irc.serverData[this.server] ? irc.serverData[this.server].my_nick clientdom.currentNickname.innerHTML = irc.serverData[this.server] ? irc.serverData[this.server].userNick
: el('i', 'Disconnected') : el('i', 'Disconnected')
irc.chat.changeTitle('TeemantIRC - ' + this.title) irc.chat.changeTitle('TeemantIRC - ' + this.title)
@ -1001,7 +1007,7 @@ class ChatBuffer {
let mesgConstr = composer.message[irc.chatType](meta.time, meta.sender, meta.message, meta.type, this.server) let mesgConstr = composer.message[irc.chatType](meta.time, meta.sender, meta.message, meta.type, this.server)
if (irc.serverData[this.server]) { if (irc.serverData[this.server]) {
let mynick = irc.serverData[this.server].my_nick let mynick = irc.serverData[this.server].userNick
if ((meta.type === 'privmsg' || meta.type === 'notice' || meta.type === 'action') && if ((meta.type === 'privmsg' || meta.type === 'notice' || meta.type === 'action') &&
meta.message.toLowerCase().indexOf(mynick.toLowerCase()) !== -1 && meta.sender !== mynick) { meta.message.toLowerCase().indexOf(mynick.toLowerCase()) !== -1 && meta.sender !== mynick) {
addClass(mesgConstr, 'mentioned') addClass(mesgConstr, 'mentioned')
@ -1040,7 +1046,7 @@ class ChatBuffer {
} else { } else {
this.unreadCount += 1 this.unreadCount += 1
if (irc.serverData[this.server]) { if (irc.serverData[this.server]) {
let mynick = irc.serverData[this.server].my_nick let mynick = irc.serverData[this.server].userNick
if ((type === 'privmsg' || type === 'notice' || type === 'action') && if ((type === 'privmsg' || type === 'notice' || type === 'action') &&
message.toLowerCase().indexOf(mynick.toLowerCase()) !== -1 && sender !== mynick) { message.toLowerCase().indexOf(mynick.toLowerCase()) !== -1 && sender !== mynick) {
// TODO: notify user of mentioned // TODO: notify user of mentioned
@ -1056,7 +1062,7 @@ class ChatBuffer {
} }
switchOff () { switchOff () {
irc.chat.input_handler.searchNicknames = [] irc.chat.inputHandler.searchNicknames = []
let lFactor = clientdom.letterbox.offsetHeight + clientdom.letterbox.scrollTop let lFactor = clientdom.letterbox.offsetHeight + clientdom.letterbox.scrollTop
if (lFactor > clientdom.letterbox.scrollHeight - 100) { if (lFactor > clientdom.letterbox.scrollHeight - 100) {
@ -1196,16 +1202,14 @@ class Settings extends AppletChatBuffer {
switchTheme () { switchTheme () {
if (this.themeSelection !== '') { if (this.themeSelection !== '') {
themes.change_theme(this.themeSelection) themes.changeTheme(this.themeSelection)
irc.config.theme = this.themeSelection irc.config.theme = this.themeSelection
this.themeSelector.setActiveSelection(this.themeSelection) this.themeSelector.setActiveSelection(this.themeSelection)
} }
} }
saveSpecified () { saveSpecified () {
if (this.timeout) { if (this.timeout) clearTimeout(this.timeout)
clearTimeout(this.timeout)
}
this.switchTheme() this.switchTheme()
@ -1317,29 +1321,26 @@ class ConnectionHandler {
bindAll (srv, client) { bindAll (srv, client) {
let server = client.data.actualServer let server = client.data.actualServer
client.on('closed', () => {
this.clients[srv] = null
})
client.on('pass_to_client', (data) => { client.on('fromServer', (data) => {
switch (data.type) { switch (data.type) {
case 'event_join_channel': case 'joinChannel':
if (data.user.nickname === irc.serverData[server].my_nick) { if (data.user.nickname === irc.serverData[server].userNick) {
irc.chat.createChatBuffer(server, data.channel, 'channel', true) irc.chat.createChatBuffer(server, data.channel, 'channel', true)
} }
irc.chat.handleJoin(server, data.user, data.channel) irc.chat.handleJoin(server, data.user, data.channel)
break break
case 'event_kick_channel': case 'kickedFromChannel':
irc.chat.handleLeave(server, data.kickee, data.channel, data.reason, data.user) irc.chat.handleLeave(server, data.kickee, data.channel, data.reason, data.user)
break break
case 'event_part_channel': case 'partChannel':
irc.chat.handleLeave(server, data.user, data.channel, data.reason) irc.chat.handleLeave(server, data.user, data.channel, data.reason)
break break
case 'event_quit': case 'quit':
irc.chat.handleQuit(server, data.user, data.reason) irc.chat.handleQuit(server, data.user, data.reason)
break break
case 'message': case 'message':
if (data.to === irc.serverData[server].my_nick) { if (data.to === irc.serverData[server].userNick) {
irc.chat.messageChatBuffer(data.user.nickname, server, { message: data.message, type: data.messageType, from: data.user.nickname }) irc.chat.messageChatBuffer(data.user.nickname, server, { message: data.message, type: data.messageType, from: data.user.nickname })
} else if (data.to == null) { } else if (data.to == null) {
let atest = irc.chat.getActiveChatBuffer() let atest = irc.chat.getActiveChatBuffer()
@ -1353,31 +1354,31 @@ class ConnectionHandler {
irc.chat.messageChatBuffer(data.to, server, { message: data.message, type: data.messageType, from: data.user.nickname }) irc.chat.messageChatBuffer(data.to, server, { message: data.message, type: data.messageType, from: data.user.nickname })
} }
break break
case 'channel_nicks': case 'nicks':
irc.chat.buildNicklist(data.channel, server, data.nicks) irc.chat.buildNicklist(data.channel, server, data.nicks)
break break
case 'channel_topic': case 'topic':
if (data['topic'] && data['set_by']) { if (data['topic'] && data['setBy']) {
irc.chat.topicChange(data.channel, server, data.topic, data['set_by']) irc.chat.topicChange(data.channel, server, data.topic, data['setBy'])
} else if (data['topic']) { } else if (data['topic']) {
irc.chat.topicChange(data.channel, server, data.topic, null) irc.chat.topicChange(data.channel, server, data.topic, null)
} else if (data['set_by']) { } else if (data['setBy']) {
irc.chat.messageChatBuffer(data.channel, server, { irc.chat.messageChatBuffer(data.channel, server, {
message: 'Topic set by ' + data.set_by + ' on ' + (new Date(data.time * 1000)), message: 'Topic set by ' + data.setBy + ' on ' + (new Date(data.time * 1000)),
type: 'topic', type: 'topic',
from: null from: null
}) })
} }
break break
case 'nick_change': case 'nick':
irc.chat.nickChange(server, data.nick, data.newNick) irc.chat.nickChange(server, data.nick, data.newNick)
break break
case 'mode_add': case 'modeAdd':
case 'mode_del': case 'modeDel':
case 'mode': case 'mode':
irc.chat.handleMode(server, data) irc.chat.handleMode(server, data)
break break
case 'server_message': case 'serverMessage':
if (data['error']) data.messageType = 'error' if (data['error']) data.messageType = 'error'
if (irc.chat.getServerChatBuffer(server) == null) { if (irc.chat.getServerChatBuffer(server) == null) {
if (!irc.serverChatQueue[server]) { if (!irc.serverChatQueue[server]) {
@ -1389,13 +1390,13 @@ class ConnectionHandler {
irc.chat.messageChatBuffer(server, server, { message: data.message, type: data.messageType, from: data.from || null }) irc.chat.messageChatBuffer(server, server, { message: data.message, type: data.messageType, from: data.from || null })
} }
break break
case 'connect_message': case 'connectMessage':
irc.auther.authMessage(data.message, data.error) irc.auther.authMessage(data.message, data.error)
break break
case 'whoisResponse': case 'whoisResponse':
whoisMessage(data.whois, irc.chat.getActiveChatBuffer()) whoisMessage(data.whois, irc.chat.getActiveChatBuffer())
break break
case 'listedchan': case 'listedChannel':
irc.chat.messageChatBuffer(server, server, irc.chat.messageChatBuffer(server, server,
{ {
message: span(data.channel, ['channel']) + '&nbsp;' + message: span(data.channel, ['channel']) + '&nbsp;' +
@ -1408,7 +1409,7 @@ class ConnectionHandler {
} }
}) })
client.on('closed', () => { client.on('connectionClosed', () => {
let serverz = irc.chat.getChatBuffersByServer(server) let serverz = irc.chat.getChatBuffersByServer(server)
for (let a in serverz) { for (let a in serverz) {
let serv = serverz[a] let serv = serverz[a]
@ -1421,6 +1422,7 @@ class ConnectionHandler {
} }
stopWarnings() stopWarnings()
this.clients[srv] = null
}) })
} }
} }
@ -1495,7 +1497,7 @@ class IRCConnector {
clientdom.connector.server.value = value clientdom.connector.server.value = value
} }
break break
case 'server_data': case 'serverinfo':
case 'extra': case 'extra':
case 'extras': case 'extras':
case 'connection': case 'connection':
@ -1809,7 +1811,7 @@ class IRCChatWindow {
this.buffers = [] this.buffers = []
this.firstServer = true this.firstServer = true
this.currentChatBuffer = null this.currentChatBuffer = null
this.input_handler = new InputHandler() this.inputHandler = new InputHandler()
clientdom.frame.style.display = 'none' clientdom.frame.style.display = 'none'
@ -1908,7 +1910,7 @@ class IRCChatWindow {
if (this.firstServer) { if (this.firstServer) {
clientdom.frame.style.display = 'block' clientdom.frame.style.display = 'block'
window.onbeforeunload = function (e) { window.onbeforeunload = function (e) {
return 'IRC will disconnect.' return 'You will be disconnected from all the channels you\'re currently in.'
} }
} }
@ -1922,8 +1924,8 @@ class IRCChatWindow {
modeTranslation: serverinfo.data.supportedModes, modeTranslation: serverinfo.data.supportedModes,
supportedPrefixes: prefixes, supportedPrefixes: prefixes,
network: serverinfo.data.network, network: serverinfo.data.network,
my_nick: serverinfo.config.nickname, userNick: serverinfo.config.nickname,
max_channel_length: serverinfo.data.max_channel_length maxChannelLength: serverinfo.data.maxChannelLength
} }
let newServer = new ChatBuffer(serverinfo.data.actualServer, serverinfo.data.actualServer, serverinfo.data.network, 'server') let newServer = new ChatBuffer(serverinfo.data.actualServer, serverinfo.data.actualServer, serverinfo.data.network, 'server')
@ -2038,8 +2040,8 @@ class IRCChatWindow {
nickChange (server, oldNick, newNick) { nickChange (server, oldNick, newNick) {
let buffers = this.getChatBuffersByServer(server) let buffers = this.getChatBuffersByServer(server)
if (irc.serverData[server].my_nick === oldNick) { if (irc.serverData[server].userNick === oldNick) {
irc.serverData[server].my_nick = newNick irc.serverData[server].userNick = newNick
let activeBuf = this.getActiveChatBuffer() let activeBuf = this.getActiveChatBuffer()
@ -2092,7 +2094,7 @@ class IRCChatWindow {
if (!buffer) return if (!buffer) return
if (user.nickname === irc.serverData[server].my_nick) { if (user.nickname === irc.serverData[server].userNick) {
buffer.setAliveStatus(true) buffer.setAliveStatus(true)
} else { } else {
buffer.nicklist.nickAdd(user.nickname) buffer.nicklist.nickAdd(user.nickname)
@ -2107,11 +2109,11 @@ class IRCChatWindow {
if (!buffer) return if (!buffer) return
if (user['nickname']) { if (user['nickname']) {
if (irc.serverData[server] && user.nickname === irc.serverData[server].my_nick) { if (irc.serverData[server] && user.nickname === irc.serverData[server].userNick) {
buffer.setAliveStatus(false) buffer.setAliveStatus(false)
} }
} else { } else {
if (irc.serverData[server] && user === irc.serverData[server].my_nick) { if (irc.serverData[server] && user === irc.serverData[server].userNick) {
buffer.setAliveStatus(false) buffer.setAliveStatus(false)
} }
} }
@ -2131,7 +2133,7 @@ class IRCChatWindow {
handleMode (server, data) { handleMode (server, data) {
let buf = null let buf = null
if (data.target === irc.serverData[server].my_nick) { if (data.target === irc.serverData[server].userNick) {
buf = this.getServerChatBuffer(server) buf = this.getServerChatBuffer(server)
} else { } else {
buf = this.getChatBufferByServerName(server, data.target) buf = this.getChatBufferByServerName(server, data.target)
@ -2139,11 +2141,11 @@ class IRCChatWindow {
if (!buf) return if (!buf) return
if (data.type === 'mode_add') { if (data.type === 'modeAdd') {
buf.nicklist.modeAdded(data.modeTarget, data.mode) buf.nicklist.modeAdded(data.modeTarget, data.mode)
buf.addMessage('set mode ' + span(data.target, ['channel']) + ' ' + span(sf('+%s %s', data.mode, data.modeTarget), ['mode']), buf.addMessage('set mode ' + span(data.target, ['channel']) + ' ' + span(sf('+%s %s', data.mode, data.modeTarget), ['mode']),
data.user.nickname, 'mode') data.user.nickname, 'mode')
} else if (data.type === 'mode_del') { } else if (data.type === 'modeDel') {
buf.nicklist.modeRemoved(data.modeTarget, data.mode) buf.nicklist.modeRemoved(data.modeTarget, data.mode)
buf.addMessage('set mode ' + span(data.target, ['channel']) + ' ' + span(sf('-%s %s', data.mode, data.modeTarget), ['mode']), buf.addMessage('set mode ' + span(data.target, ['channel']) + ' ' + span(sf('-%s %s', data.mode, data.modeTarget), ['mode']),
data.user.nickname, 'mode') data.user.nickname, 'mode')
@ -2189,7 +2191,7 @@ class IRCChatWindow {
render (buffer) { render (buffer) {
let activeNow = this.getActiveChatBuffer() let activeNow = this.getActiveChatBuffer()
this.input_handler.tabCompleteReset() this.inputHandler.tabCompleteReset()
if (activeNow) { if (activeNow) {
activeNow.switchOff() activeNow.switchOff()
@ -2256,7 +2258,7 @@ window.onload = function () {
clientdom['frame'] = irc.primaryFrame.querySelector('#chat') clientdom['frame'] = irc.primaryFrame.querySelector('#chat')
clientdom['letterbox'] = clientdom.frame.querySelector('.letterbox') clientdom['letterbox'] = clientdom.frame.querySelector('.letterbox')
clientdom['nicklist'] = clientdom.frame.querySelector('.nicklist') clientdom['nicklist'] = clientdom.frame.querySelector('.nicklist')
clientdom['currentNickname'] = clientdom.frame.querySelector('.my_nickname') clientdom['currentNickname'] = clientdom.frame.querySelector('.userNickname')
clientdom['input'] = clientdom.frame.querySelector('.userinput') clientdom['input'] = clientdom.frame.querySelector('.userinput')
clientdom['send'] = clientdom.frame.querySelector('.sendbutton') clientdom['send'] = clientdom.frame.querySelector('.sendbutton')
clientdom['chat'] = clientdom.frame.querySelector('.chatarea') clientdom['chat'] = clientdom.frame.querySelector('.chatarea')
@ -2265,12 +2267,11 @@ window.onload = function () {
clientdom.settings['open'] = irc.primaryFrame.querySelector('.open_settings') clientdom.settings['open'] = irc.primaryFrame.querySelector('.open_settings')
irc.settings = new Settings() irc.settings = new Settings()
irc.settings.setInitialValues()
irc.handler = new ConnectionHandler() irc.handler = new ConnectionHandler()
irc.auther = new IRCConnector() irc.auther = new IRCConnector()
irc.chat = new IRCChatWindow() irc.chat = new IRCChatWindow()
irc.settings.setInitialValues()
parseURL() parseURL()
window.onpopstate = parseURL window.onpopstate = parseURL
} }

View File

@ -1,73 +0,0 @@
const imgs = ['.png', '.jpg', '.jpeg', '.svg']
const embeds = [
{
match: /youtu.?be\//is,
exec: function (pgurl) {
let dat = pgurl.match(/(?:be|com)\/([^?&#]+)/i)
if (!dat) {
dat = pgurl.match('[\\?&]v=([^&#]*)')
}
if (!dat) return null
return 'https://www.youtube.com/embed/' + dat[1] + '?autoplay=1'
}
}
]
export function handleUrlElement (elem) {
let cover = null
let ext = elem.href.split('.')
ext = ext[ext.length - 1]
if (ext && imgs.indexOf(ext) !== -1) {
cover = document.createElement('img')
cover.src = elem.href
}
if (!cover) {
for (let a in embeds) {
if (cover) break
let fn = embeds[a]
if (elem.href.match(fn.match)) {
let r = fn.exec(elem.href)
if (r) {
cover = document.createElement('iframe')
cover.src = r
}
}
}
}
if (cover) {
cover.className = 'preview'
let contelem = document.createElement('span')
contelem.className = 'preview-box'
let contbutton = document.createElement('button')
contbutton.className = 'preview-btn'
contbutton.innerHTML = 'Show Preview'
elem.parentNode.appendChild(contelem)
contelem.appendChild(elem)
contelem.appendChild(contbutton)
contbutton.addEventListener('click', function (e) {
e.preventDefault()
if (cover.parentNode) {
cover.remove()
contbutton.innerHTML = 'Show Preview'
} else {
contelem.appendChild(cover)
contbutton.innerHTML = 'Hide Preview'
}
}, false)
return contelem
}
return elem
}

View File

@ -37,7 +37,7 @@ window.themes = module.exports = {
} }
}, },
change_theme: function (name) { changeTheme: function (name) {
if (name in window.themes.available) { if (name in window.themes.available) {
swapSheet(window.themes.available[name].stylesheet) swapSheet(window.themes.available[name].stylesheet)
window.irc.config.theme = name window.irc.config.theme = name

View File

@ -19,6 +19,14 @@ body
margin-top: 0 margin-top: 0
display: block display: block
text-align: center text-align: center
position: relative
img
max-width: 64px
top: -50px;
position: absolute
left: 0
right: 0
margin: auto
.grade2 .grade2
margin-bottom: 0 margin-bottom: 0