Skip to content

Commit ca2cae5

Browse files
committedJun 6, 2021
Initial import
1 parent 5a89bc0 commit ca2cae5

File tree

6 files changed

+1151
-0
lines changed

6 files changed

+1151
-0
lines changed
 

‎assets/loading.svg

+15
Loading

‎css/style.scss

+441
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,441 @@
1+
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,700;1,400&display=swap');
2+
3+
:root {
4+
--omnios-colour:#ff9800;
5+
--omnios-colour-pale: rgb(255, 152, 0, .4);
6+
}
7+
8+
html {
9+
font-family: 'Roboto Mono', monospace;
10+
font-size: 0.9em;
11+
12+
--bg-colour: white;
13+
--text-colour: black;
14+
--loading-overlay-bg: rgba(26, 26, 26, 0.7);
15+
--disabled-colour: #ccc;
16+
--border-colour: var(--omnios-colour);
17+
--status-colour: #ff4500;
18+
--channel-hover-bg-colour: #666;
19+
--channel-hover-text-colour: white;
20+
--topic-bg-colour: #eee;
21+
--topic-text-colour: black;
22+
--topic-border-colour: #ccc;
23+
--datepicker-bg-colour: #eee;
24+
--datepicker-text-colour: black;
25+
--datepicker-border-colour: #ccc;
26+
--ts-link-colour: black;
27+
--msg-hover-bg-colour: #eee;
28+
--icon-colour: black;
29+
--menu-bg-colour: white;
30+
--shortcut-key-colour: #ddd;
31+
}
32+
33+
html:not(.style-scope)[dark] {
34+
--bg-colour: #20252b;
35+
--text-colour: #c0c0c0;
36+
--icon-colour: #c0c0c0;
37+
--disabled-colour: #606060;
38+
--channel-hover-bg-colour: #364554;
39+
--channel-hover-text-colour: #c0c0c0;
40+
--ts-link-colour: #c0c0c0;
41+
--msg-hover-bg-colour: #364554;
42+
--topic-bg-colour: #2e3944;
43+
--topic-text-colour: #c0c0c0;
44+
--datepicker-bg-colour: #364554;
45+
--datepicker-text-colour: #c0c0c0;
46+
--datepicker-border-colour: #999;
47+
--menu-bg-colour: #2e3944;
48+
--shortcut-key-colour: #364554;
49+
}
50+
51+
body {
52+
margin: 0;
53+
background: var(--bg-colour);
54+
color: var(--text-colour);
55+
}
56+
57+
a {
58+
color: var(--omnios-colour);
59+
}
60+
61+
.hidden {
62+
display: none;
63+
}
64+
65+
#loading_overlay {
66+
display: none;
67+
background: var(--loading-overlay-bg);
68+
position: fixed;
69+
width: 100%;
70+
height: 100%;
71+
z-index: 9999;
72+
top: 0;
73+
}
74+
75+
#loading_container {
76+
display: none;
77+
position: fixed;
78+
z-index: 7;
79+
top: 50%;
80+
left: 50%;
81+
transform: translate( -50%, -50% );
82+
}
83+
84+
#overlay {
85+
position: fixed;
86+
display: none;
87+
width: 100%;
88+
height: 100%;
89+
top: 0;
90+
left: 0;
91+
right: 0;
92+
bottom: 0;
93+
background-color: rgba(0,0,0,0.5);
94+
z-index: 999;
95+
96+
& > * {
97+
display: none;
98+
position: absolute;
99+
top: 50%;
100+
left: 50%;
101+
width: 40vw;
102+
padding: 2em;
103+
min-height: 20vh;
104+
transform: translate(-50%,-50%);
105+
-ms-transform: translate(-50%,-50%);
106+
background: var(--topic-bg-colour);
107+
color: var(--topic-text-colour);
108+
109+
header {
110+
cursor: pointer;
111+
display: flex;
112+
justify-content: space-between;
113+
margin-bottom: .5em;
114+
padding-bottom: .7em;
115+
border-bottom: 1px solid var(--topic-border-colour);
116+
117+
h3 {
118+
margin: 0;
119+
}
120+
}
121+
}
122+
123+
#help_overlay {
124+
cursor: pointer;
125+
126+
main {
127+
display: grid;
128+
grid-template-columns: repeat(2, 1fr);
129+
130+
.shortcut {
131+
margin: 1em 0;
132+
133+
.key {
134+
background: var(--shortcut-key-colour);
135+
padding: 3px 9px;
136+
border-radius: 5px;
137+
}
138+
}
139+
}
140+
}
141+
}
142+
143+
#menu {
144+
display: flex;
145+
flex-wrap: wrap;
146+
align-items: center;
147+
148+
padding: 0.5em 2em;
149+
position: fixed;
150+
top: 0;
151+
z-index: 10;
152+
width: calc(100vw - 4em);
153+
background: var(--menu-bg-colour);
154+
155+
#title {
156+
color: var(--omnios-colour);
157+
padding-right: 1em;
158+
}
159+
160+
.fa, .fas, .far {
161+
padding: 0 .2em;
162+
cursor: pointer;
163+
color: var(--icon-colour);
164+
}
165+
166+
.disabled {
167+
.fa, .fas, .far {
168+
color: var(--disabled-colour);
169+
}
170+
}
171+
172+
#datepicker_container {
173+
display: inline;
174+
175+
.fa, .fas, .far {
176+
position: relative;
177+
top: .1em;
178+
}
179+
}
180+
181+
#datepicker {
182+
width: 12ch;
183+
background: var(--datepicker-bg-colour);
184+
color: var(--datepicker-text-colour);
185+
border: 1px solid var(--datepicker-border-colour);
186+
outline: none;
187+
vertical-align: middle;
188+
border-radius: 0;
189+
-webkit-appearance: none;
190+
-webkit-box-shadow: none;
191+
-moz-box-shadow: none;
192+
}
193+
194+
#toggle_sys {
195+
&.on {
196+
.fas {
197+
color: var(--status-colour);
198+
}
199+
}
200+
}
201+
202+
#toggle_sys_info {
203+
color: var(--status-colour);
204+
font-size: 0.8em;
205+
margin-left: -5px;
206+
}
207+
}
208+
209+
#container {
210+
margin-top: 2.2em;
211+
padding: 0 1em;
212+
display: flex;
213+
flex-wrap: wrap;
214+
215+
#channel_container, #log_container {
216+
border: 1px solid var(--omnios-colour);
217+
margin: .5em .5em;
218+
padding: .5em .5em;
219+
}
220+
221+
#channel_container {
222+
flex-basis: 15ch;
223+
flex-grow: 1;
224+
position: fixed;
225+
left: 0;
226+
top: 2.2em;
227+
margin-left: 2em;
228+
height: calc(100vh - 4.3em);
229+
230+
.channel_row {
231+
padding: 4px 5px 4px 5px;
232+
233+
&:hover {
234+
background: var(--channel-hover-bg-colour);
235+
color: var(--channel-hover-text-colour);
236+
cursor: pointer;
237+
}
238+
}
239+
240+
.current {
241+
color: var(--omnios-colour);
242+
}
243+
}
244+
245+
#log_container {
246+
margin-left: 10em;
247+
flex-basis: 0;
248+
flex-grow: 999;
249+
min-height: calc(100vh - 4.3em);
250+
251+
.fail_msg {
252+
padding: .75rem 1.25rem;
253+
border: 1px solid transparent;
254+
border-radius: .25rem;
255+
color: #721c24;
256+
background-color: #f8d7da;
257+
border-color: #f5c6cb;
258+
}
259+
260+
#topic {
261+
padding: .75rem 1.25rem;
262+
margin-bottom: .5em;
263+
border: 1px solid transparent;
264+
border-radius: .25rem;
265+
color: var(--topic-text-colour);
266+
background-color: var(--topic-bg-colour);
267+
border-color: var(--topic-border-colour);
268+
font-style: italic;
269+
}
270+
271+
#logs {
272+
.log_row {
273+
display: flex;
274+
line-height: 1.4em;
275+
276+
.ts, .nick, .message {
277+
padding: 0 .5em;
278+
}
279+
280+
a.ts_link {
281+
color: var(--ts-link-colour);
282+
text-decoration: none;
283+
284+
&:hover {
285+
text-decoration: underline;
286+
}
287+
}
288+
289+
.nick {
290+
min-width: 8em;
291+
text-align: right;
292+
font-weight: bold;
293+
}
294+
295+
&:hover {
296+
background: var(--msg-hover-bg-colour);
297+
}
298+
299+
.message {
300+
a {
301+
overflow-wrap: break-word;
302+
word-wrap: break-word;
303+
-ms-word-break: break-all;
304+
word-break: break-word;
305+
-ms-hyphens: auto;
306+
-moz-hyphens: auto;
307+
-webkit-hyphens: auto;
308+
hyphens: auto;
309+
cursor: pointer;
310+
}
311+
}
312+
313+
&.hl {
314+
background: var(--omnios-colour-pale);
315+
}
316+
317+
}
318+
}
319+
}
320+
}
321+
322+
@media only screen and (max-width: 600px) {
323+
#menu {
324+
width: 95vw;
325+
padding: .5em 1em;
326+
327+
#toggle_sys_info {
328+
display: none;
329+
}
330+
}
331+
332+
#container {
333+
margin-top: 3.5em;
334+
335+
#channel_container {
336+
position: static;
337+
height: auto;
338+
margin: 1em 0;
339+
}
340+
341+
#log_container {
342+
margin: 0;
343+
min-height: 0;
344+
345+
#logs {
346+
.log_row {
347+
flex-wrap: wrap;
348+
349+
.ts, .nick {
350+
flex-basis: 50%;
351+
}
352+
353+
.message {
354+
flex-basis: 100%;
355+
}
356+
357+
.nick {
358+
text-align: left;
359+
}
360+
}
361+
}
362+
}
363+
}
364+
365+
#toggle_help {
366+
display: none;
367+
}
368+
}
369+
370+
@media only screen and (max-width: 360px) {
371+
#container {
372+
#log-container {
373+
#topic {
374+
a {
375+
overflow-wrap: break-word;
376+
word-wrap: break-word;
377+
-ms-word-break: break-all;
378+
word-break: break-word;
379+
-ms-hyphens: auto;
380+
-moz-hyphens: auto;
381+
-webkit-hyphens: auto;
382+
hyphens: auto;
383+
cursor: pointer;
384+
385+
}
386+
}
387+
}
388+
}
389+
}
390+
391+
[class^="nick_col_"] {
392+
font-weight: bold;
393+
}
394+
395+
.nick_col_ooce {
396+
color: var(--omnios-colour);
397+
}
398+
399+
.nick_col_0 {
400+
color: #d72828;
401+
}
402+
403+
.nick_col_1 {
404+
color: #ed1aec;
405+
}
406+
407+
.nick_col_2 {
408+
color: #188181;
409+
}
410+
411+
.nick_col_3 {
412+
color: #969603;
413+
}
414+
415+
.nick_col_4 {
416+
color: #019898;
417+
}
418+
419+
.nick_col_5 {
420+
color: #059405;
421+
}
422+
423+
.nick_col_6 {
424+
color: #5c5ced;
425+
}
426+
427+
.nick_col_7 {
428+
color: #21dede;
429+
}
430+
431+
.join_col {
432+
color: #009200;
433+
}
434+
435+
.part_col {
436+
color: #800000;
437+
}
438+
439+
.channel_col {
440+
color: #f00;
441+
}

‎favicon.ico

1.12 KB
Binary file not shown.

‎index.html

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
<!DOCTYPE html>
2+
<html lang='en'>
3+
<head>
4+
<meta charset='UTF-8'>
5+
<meta name='viewport' content='width=device-width, initial-scale=1'>
6+
<meta name="description"
7+
content="IRC logs for #omnios and other illumos project channels">
8+
9+
<title>IRC logs</title>
10+
11+
<link rel='stylesheet' href='/css/style.css' />
12+
13+
<script src='https://code.jquery.com/jquery-3.6.0.min.js'
14+
integrity='sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4='
15+
crossorigin='anonymous'>
16+
</script>
17+
18+
<script src="https://kit.fontawesome.com/596031c7ad.js"
19+
crossorigin="anonymous">
20+
</script>
21+
22+
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.min.js">
23+
</script>
24+
<script src="https://cdn.jsdelivr.net/npm/pikaday@1.8.2/pikaday.min.js">
25+
</script>
26+
<script src="https://cdn.jsdelivr.net/npm/pikaday@1.8.2/plugins/pikaday.jquery.js"
27+
integrity="sha256-Msvs+RIuOUjcyMlMwEXek9DS7WaAcAS2JXCr5zloEQk="
28+
crossorigin="anonymous">
29+
</script>
30+
<link rel="stylesheet" type="text/css"
31+
href="https://cdn.jsdelivr.net/npm/pikaday/css/pikaday.css" />
32+
</head>
33+
<body>
34+
<header id='menu'>
35+
<span id='title'>IRC logs for <span></span></span>
36+
37+
<div id='datepicker_container'>
38+
<i id='date_dec' title='One day earlier'
39+
class='fas fa-caret-left fa-lg'></i>
40+
41+
<input type='text' id='datepicker'>
42+
43+
<span id='date_inc' title='One day later'>
44+
<i class='fas fa-caret-right fa-lg'></i>
45+
</span>
46+
</div>
47+
48+
<span id='date_today' title='Today'>
49+
<i class='far fa-clock fa-lg'></i>
50+
</span>
51+
52+
<span id='refresh' title='Refresh'>
53+
<i class='fas fa-sync-alt fa-lg'></i>
54+
</span>
55+
56+
<span id='scroll_up' title='Scroll to first message'>
57+
<i class='far fa-arrow-alt-circle-up fa-lg'></i>
58+
</span>
59+
60+
<span id='scroll_down' title='Scroll to last message'>
61+
<i class='far fa-arrow-alt-circle-down fa-lg'></i>
62+
</span>
63+
64+
<span id='toggle_dl' title='Toggle light/dark mode'>
65+
<i id='dl_icon' class='far fa-moon fa-lg'></i>
66+
</span>
67+
68+
<span id='toggle_settings' title='Show settings overlay'>
69+
<i id='dl_icon' class='fas fa-cogs fa-lg'></i>
70+
</span>
71+
72+
<span id='toggle_help' title='Show help overlay'>
73+
<i id='dl_icon' class='far fa-question-circle fa-lg'></i>
74+
</span>
75+
76+
<span id='toggle_sys' class='off'
77+
title='Toggle showing join/part messages'>
78+
<i class='fas fa-info-circle fa-lg'></i>
79+
<span id='toggle_sys_info'></span>
80+
</span>
81+
</header>
82+
83+
<div id='loading_overlay'></div>
84+
<div id='loading_container'>
85+
<img src='/assets/loading.svg' alt='loading'
86+
class='loading_img'>
87+
</div>
88+
89+
<div id="overlay">
90+
<div id="help_overlay">
91+
<header>
92+
<h3>Keyboard shortcuts</h3>
93+
<a id='help_close'>Close</a>
94+
</header>
95+
<main>
96+
<div class='shortcut'>
97+
<span class='key'>&lt;left arrow&gt;</span>
98+
<span>Previous day</span>
99+
</div>
100+
<div class='shortcut'>
101+
<span class='key'>&lt;right arrow&gt;</span>
102+
<span>Next day</span>
103+
</div>
104+
<div class='shortcut'>
105+
<span class='key'>t</span>
106+
<span>Go to today</span>
107+
</div>
108+
<div class='shortcut'>
109+
<span class='key'>d</span>
110+
<span>Toggle dark mode</span>
111+
</div>
112+
<div class='shortcut'>
113+
<span class='key'>r</span>
114+
<span>Refresh</span>
115+
</div>
116+
<div class='shortcut'>
117+
<span class='key'>m</span>
118+
<span>Toggle join/part messages</span>
119+
</div>
120+
<div class='shortcut'>
121+
<span class='key'>c</span>
122+
<span class='key'>i</span>
123+
<span>Go to #illumos</span>
124+
</div>
125+
<div class='shortcut'>
126+
<span class='key'>c</span>
127+
<span class='key'>o</span>
128+
<span>Go to #omnios</span>
129+
</div>
130+
<div class='shortcut'>
131+
<span class='key'>&lt;home&gt;</span>
132+
<span>Scroll to top</span>
133+
</div>
134+
<div class='shortcut'>
135+
<span class='key'>&lt;end&gt;</span>
136+
<span>Scroll to bottom</span>
137+
</div>
138+
<div class='shortcut'>
139+
<span class='key'>s</span>
140+
<span>Show settings</span>
141+
</div>
142+
<div class='shortcut'>
143+
<span class='key'>?</span>
144+
<span>Show this help</span>
145+
</div>
146+
</main>
147+
</div>
148+
149+
<div id="settings_overlay">
150+
<header>
151+
<h3>Settings</h3>
152+
<a id='settings_close'>Close</a>
153+
</header>
154+
<main>
155+
<div class='setting'>
156+
<input type='checkbox' id='utc_setting'>
157+
Show UTC times
158+
</input>
159+
</div>
160+
</main>
161+
</div>
162+
</div>
163+
164+
<div id='container'>
165+
<aside id='channel_container'>
166+
<div id='channels'> </div>
167+
168+
<div id='channel_line' class='hidden channel_row'>
169+
<span class='channel'></span>
170+
</div>
171+
</aside>
172+
173+
<main id='log_container'>
174+
<div id='topic'></div>
175+
<div id='logs'></div>
176+
<div id='fail_msg' class='hidden fail_msg'></div>
177+
<div id='nologs' class='hidden fail_msg'>
178+
There are no matching logs for
179+
<span></span> on <span></span>.
180+
</div>
181+
182+
<div id='log_line' class='hidden log_row'>
183+
<a href='#' class='ts_link'>
184+
<span class='ts'></span>
185+
</a>
186+
<span class='nick'></span>
187+
<span class='message'></span>
188+
</div>
189+
<div id='log_end'></div>
190+
</main>
191+
</div>
192+
193+
<script src='/script.js'></script>
194+
</body>
195+
</html>
196+

‎robots.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
User-agent: *
2+
Disallow: /api

‎script.js

+497
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,497 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it
3+
* under the terms of the GNU General Public License as published by the Free
4+
* Software Foundation, either version 3 of the License, or (at your option)
5+
* any later version.
6+
* This program is distributed in the hope that it will be useful, but WITHOUT
7+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
8+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
9+
* more details.
10+
* You should have received a copy of the GNU General Public License along with
11+
* this program. If not, see L<http://www.gnu.org/licenses/>.
12+
*
13+
* Copyright 2021 Matt Fiddaman
14+
*/
15+
16+
const ops = ['andyf', 'hadfl', 'oetiker', 'fenix', 'mrscowley'];
17+
const defaultchan = 'omnios';
18+
19+
// Taken from https://urlregex.com
20+
const url_regex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[-+\.\!\/\\\w]*))?)/g;
21+
22+
let channel_regex = /(?<!<[^>]*)#[-\w]+/g;
23+
const escapere = /[.*+?^${}()|[\]\\]/g;
24+
const actionre = /^\x01ACTION\s+(.*)\x01$/;
25+
26+
const online = {};
27+
const nick_classes = {};
28+
const nick_regexps = {};
29+
let channels = {};
30+
let curchan, curdate;
31+
let pik;
32+
let lastkey;
33+
34+
const zero_pad = (num, places) => String(num).padStart(places, '0')
35+
36+
const loader = {
37+
show : () => {
38+
$('#container, #menu').hide();
39+
$('#loading_overlay, #loading_container').show();
40+
},
41+
hide : () => {
42+
$('#container, #menu').show();
43+
$('#loading_overlay, #loading_container').fadeOut();
44+
}
45+
}
46+
47+
function format_date(d) {
48+
return String(
49+
zero_pad(d.getFullYear(), 4) + '-' +
50+
zero_pad(d.getMonth() + 1, 2) + '-' +
51+
zero_pad(d.getDate(), 2));
52+
}
53+
54+
function format_time(d) {
55+
if (localStorage.getItem('utc_setting') === 'true') {
56+
return String(
57+
zero_pad(d.getUTCHours(), 2) + ':' +
58+
zero_pad(d.getUTCMinutes(), 2) + ':' +
59+
zero_pad(d.getUTCSeconds(), 2));
60+
} else {
61+
return String(
62+
zero_pad(d.getHours(), 2) + ':' +
63+
zero_pad(d.getMinutes(), 2) + ':' +
64+
zero_pad(d.getSeconds(), 2));
65+
}
66+
}
67+
68+
function fail_msg(msg) {
69+
$('#fail_msg').text(msg).removeClass('hidden');
70+
}
71+
72+
function linkify(text) {
73+
return text.replaceAll(url_regex, (url) => {
74+
return '<a target="_blank" rel="noopener" href="'
75+
+ url + '">' + url + '</a>';
76+
})
77+
}
78+
79+
function channelify(text) {
80+
return text.replaceAll(channel_regex, (channel) => {
81+
return `<span class="channel_col">${channel}</span>`;
82+
})
83+
}
84+
85+
function nickify(text) {
86+
for (const user in online) {
87+
text = text.replaceAll(nick_regexps[user],
88+
nick_span(user, false).prop('outerHTML'));
89+
}
90+
91+
return text;
92+
}
93+
94+
function nick_class(nick) {
95+
if (ops.includes(nick)) return 'nick_col_ooce';
96+
97+
const hash = nick.split('').reduce((t, c) => {
98+
return c == '_' ? t : t + c.charCodeAt(0);
99+
}, 0);
100+
101+
return `nick_col_${hash % 8}`;
102+
}
103+
104+
function nick_span(nick, braces=true) {
105+
return $('<span/>')
106+
.addClass(nick_classes[nick])
107+
.text(braces ? `<${nick}>` : nick);
108+
}
109+
110+
function jp_span(jp, nick) {
111+
return $('<span/>')
112+
.addClass((jp ? 'join' : 'part') + '_col')
113+
.append($('<span/>').text('*** '))
114+
.append(nick_span(nick, false))
115+
.append($('<span/>').text(' has ' + (jp ? 'joined' : 'left')
116+
+ ' the channel ***'));
117+
}
118+
119+
function style_msg() {
120+
msg = $(this).html();
121+
122+
if (msg.includes('://')) msg = linkify(msg);
123+
msg = nickify(msg);
124+
if (msg.includes('#')) msg = channelify(msg);
125+
126+
$(this).html(msg);
127+
}
128+
129+
function nologs() {
130+
if (!$('.log_row:visible').length) {
131+
$('#nologs')
132+
.removeClass('hidden')
133+
.show()
134+
.find('span').text(`#${curchan}`)
135+
.next('span').text(curdate);
136+
} else {
137+
$('#nologs').hide();
138+
}
139+
}
140+
141+
function scroll_hash(hash) {
142+
$('div.hl').removeClass('hl');
143+
$(hash).addClass('hl');
144+
145+
$('html, body').animate({
146+
scrollTop: $(hash).offset().top - 60
147+
}, 500);
148+
document.location.hash = hash;
149+
}
150+
151+
const handlers = {
152+
'JOIN': (v, r) => {
153+
r.find('.message').append(jp_span(true, v.nick));
154+
},
155+
'PART': (v, r) => {
156+
r.find('.message').append(jp_span(false, v.nick));
157+
},
158+
'PRIVMSG': (v, r) => {
159+
const $msg = r.find('.message')
160+
const $nick = r.find('.nick');
161+
162+
let m;
163+
if (m = v.message.match(actionre)) {
164+
$nick.text('*');
165+
$msg.text(`${v.nick} ${m[1]}`);
166+
} else {
167+
$nick.append(nick_span(v.nick));
168+
$msg.text(v.message);
169+
}
170+
},
171+
};
172+
173+
$(() => {
174+
const path = document.location.pathname;
175+
176+
$('#toggle_sys').on('click', function(e) {
177+
var $rows =
178+
$('.log_row[data-cmd="PART"], .log_row[data-cmd="JOIN"]');
179+
180+
$(this).toggleClass('on off');
181+
localStorage.setItem('hidesys', $(this).hasClass('on'));
182+
183+
if ($(this).hasClass('on')) {
184+
$(this).find('span').text(
185+
`${$rows.length} join/part message` +
186+
($rows.length == 1 ? "" : "s") + ' not shown');
187+
$rows.hide();
188+
} else {
189+
$(this).find('span').text('');
190+
$rows.fadeIn(400);
191+
}
192+
nologs();
193+
});
194+
195+
$('#toggle_dl').on('click', function(e) {
196+
$icon = $(this).find('i');
197+
198+
if ($icon.hasClass('fas'))
199+
$('html').removeAttr('dark');
200+
else
201+
$('html').attr('dark', 'true');
202+
203+
$icon.toggleClass('fas far');
204+
205+
localStorage.setItem('darkmode', $icon.hasClass('fas'));
206+
});
207+
208+
$('#utc_setting').on('click', function(e) {
209+
localStorage.setItem('utc_setting', $(this).is(':checked'));
210+
window.location.reload(false);
211+
});
212+
213+
if (localStorage.getItem('darkmode') === null) {
214+
localStorage.setItem('darkmode',
215+
window.matchMedia('(prefers-color-scheme: dark)').matches);
216+
}
217+
218+
if (localStorage.getItem('darkmode') === 'true')
219+
$('#toggle_dl').trigger('click');
220+
221+
loader.show();
222+
223+
$.getJSON('/api/channel', (data) => {
224+
const $template = $('#channel_line');
225+
const $channels = $('#channels');
226+
channels = data;
227+
228+
$.each(data, (k, v) => {
229+
const channel = k;
230+
231+
const row = $template.clone()
232+
.attr('id', `channel_${channel}`)
233+
.attr('data-channel', channel);
234+
235+
row.find('.channel').text(`#${channel}`);
236+
row.removeClass('hidden');
237+
238+
$channels.append(row);
239+
});
240+
}).fail((err) => {
241+
fail_msg('Error fetching channels');
242+
243+
loader.hide();
244+
}).done(() => {
245+
246+
const today = format_date(new Date());
247+
248+
let result;
249+
if ((result = path.match(/^\/([a-z]+)\/(\d{4}-\d{2}-\d{2})$/i)) &&
250+
result[1] in channels) {
251+
curchan = result[1];
252+
curdate = result[2];
253+
} else if ((result = path.match(/^\/([a-z]+)/i)) &&
254+
result[1] in channels) {
255+
window.location.href = `/${result[1]}/${today}`;
256+
return;
257+
} else {
258+
window.location.href = `/${defaultchan}/${today}`;
259+
return;
260+
}
261+
262+
const api_location = `/api${path}`;
263+
264+
document.title = `#${curchan} on ${curdate}`;
265+
$('#title').find('span').text(`#${curchan}`);
266+
267+
try {
268+
const $topic = $('#topic');
269+
$topic.text(channels[curchan].topic);
270+
$topic.html(linkify($topic.html()));
271+
} catch (err) {
272+
console.error(err);
273+
}
274+
275+
channel_regex = new RegExp(
276+
`(?<!<[^>]*)#(?:${Object.keys(channels).join('|')})\\b`, 'g');
277+
278+
pik = new Pikaday({
279+
field: $('#datepicker')[0],
280+
firstDay: 1,
281+
defaultDate: new Date(curdate),
282+
setDefaultDate: true,
283+
minDate: new Date(channels[curchan]['begin']),
284+
maxDate: new Date(),
285+
format: 'YYYY-MM-DD',
286+
showDaysInNextAndPreviousMonths: true,
287+
enableSelectionDaysInNextAndPreviousMonths: true,
288+
onSelect: (date) => {
289+
$('#datepicker').prop('disabled', true);
290+
document.location.href = `/${curchan}/`
291+
+ pik.toString();
292+
},
293+
});
294+
295+
if (channels[curchan]['begin'] == curdate)
296+
$('#date_dec').prop('disabled', true).addClass('disabled');
297+
else if (curdate == today)
298+
$('#date_inc').prop('disabled', true).addClass('disabled');
299+
300+
$.getJSON(api_location, (data) => {
301+
const $template = $('#log_line');
302+
const $logs = $('#logs');
303+
let longest_nick = 0;
304+
305+
$.each(data, (k, v) => {
306+
const nick = v.nick;
307+
308+
// This is done here so that we don't count nicks
309+
// only used in JOIN/PART messages against the
310+
// required width of the nick column.
311+
if (v.command == 'PRIVMSG') {
312+
longest_nick =
313+
Math.max(longest_nick, nick.length);
314+
}
315+
316+
if (nick in online) return
317+
318+
online[nick] = true;
319+
nick_classes[nick] = nick_class(nick);
320+
321+
try {
322+
const safe_nick = nick.replace(
323+
escapere, '\\$&');
324+
nick_regexps[nick] = new
325+
RegExp(`(?<!<[^>]*)\\b${safe_nick}\\b`,
326+
'g')
327+
} catch (err) {
328+
fail_msg('Invalid characters found in nickname');
329+
loader.hide();
330+
331+
throw(err);
332+
}
333+
});
334+
335+
$.each(data, (k, v) => {
336+
const id = `${v['ts']}${k}`;
337+
338+
const row = $template.clone().attr('id', id);
339+
340+
const ts = format_time(new Date(v.ts * 1000));
341+
342+
row.find('.ts').html(`[${ts}]`);
343+
row.find('.ts_link').attr('href', `#${id}`);
344+
row.attr('data-cmd', v.command);
345+
346+
if (v.command in handlers) {
347+
handlers[v.command](v, row);
348+
} else {
349+
row.find('.message')
350+
.text(`UNHANDLED ${v.command}`);
351+
}
352+
353+
row.removeClass('hidden');
354+
355+
$logs.append(row);
356+
});
357+
358+
$logs.append($('<span/>').attr('id', 'end'));
359+
360+
$('#logs .log_row .nick')
361+
.css('min-width', `${longest_nick + 2}ch`);
362+
363+
$('.message').each(style_msg);
364+
365+
if (localStorage.getItem('hidesys') === 'true')
366+
$('#toggle_sys').trigger('click');
367+
368+
$('a.ts_link').on('click', function(e) {
369+
e.preventDefault();
370+
scroll_hash($(this).attr('href'));
371+
});
372+
373+
loader.hide();
374+
nologs();
375+
376+
if (document.location.hash)
377+
scroll_hash(document.location.hash);
378+
379+
}).fail((err) => {
380+
fail_msg(`Error fetching log for #${curchan}/${curdate}`);
381+
loader.hide();
382+
});
383+
384+
$(`div.channel_row[data-channel="${curchan}"]`).addClass('current');
385+
386+
$('div.channel_row').on('click', function() {
387+
document.location.href = '/' +
388+
$(this).attr('data-channel') + '/' + curdate;
389+
return;
390+
});
391+
392+
$('#date_today').on('click', () => {
393+
pik.setDate(today);
394+
});
395+
396+
$('#date_inc').on('click', function() {
397+
if ($(this).prop('disabled')) return;
398+
pik.setMoment(pik.getMoment().add(1, 'days'));
399+
});
400+
401+
$('#date_dec').on('click', function() {
402+
if ($(this).prop('disabled')) return;
403+
pik.setMoment(pik.getMoment().subtract(1, 'days'));
404+
});
405+
406+
$('#scroll_down').on('click', () => {
407+
$('html, body').animate({
408+
scrollTop: $('#log_end').offset().top
409+
}, 1000);
410+
});
411+
412+
$('#scroll_up').on('click', () => {
413+
$('html, body').animate({
414+
scrollTop: $('#logs .log_row:first').offset().top - 35
415+
}, 500);
416+
});
417+
418+
$('#refresh').on('click', () => {
419+
window.location.href = path +
420+
$('a.ts_link:visible:last').attr('href');
421+
window.location.reload(false);
422+
});
423+
424+
$('#toggle_help, #help_overlay > header').on('click', () => {
425+
if ($('#settings_overlay:visible').length) return;
426+
$('#overlay, #help_overlay').toggle();
427+
});
428+
429+
$('#toggle_settings, #settings_overlay > header').on('click', () => {
430+
if ($('#help_overlay:visible').length) return;
431+
$('#overlay, #settings_overlay').toggle();
432+
});
433+
434+
window.onkeydown = (e) => {
435+
if (e.altKey || e.ctrlKey || e.metaKey) return;
436+
437+
if (e.shiftKey) {
438+
if (e.key == '?')
439+
$('#toggle_help').trigger('click');
440+
return;
441+
}
442+
443+
switch(e.key) {
444+
case 'Escape':
445+
$('#overlay > * > header:visible')
446+
.trigger('click');
447+
break;
448+
case 'Left':
449+
case 'ArrowLeft':
450+
$('#date_dec').trigger('click');
451+
break;
452+
case 'Right':
453+
case 'ArrowRight':
454+
$('#date_inc').trigger('click');
455+
break;
456+
case 't':
457+
$('#date_today').trigger('click');
458+
break;
459+
case 'd':
460+
$('#toggle_dl').trigger('click');
461+
break;
462+
case 'r':
463+
$('#refresh').trigger('click');
464+
break;
465+
case 'm':
466+
$('#toggle_sys').trigger('click');
467+
break;
468+
case 'i':
469+
case 'o':
470+
if (last_key === undefined) break;
471+
472+
if (last_key === 'c') {
473+
let channel;
474+
475+
if (e.key === 'i')
476+
channel = 'illumos'
477+
else if (e.key === 'o')
478+
channel = 'omnios'
479+
480+
window.location.href =
481+
`/${channel}/${curdate}`;
482+
}
483+
break;
484+
case 's':
485+
$('#toggle_settings').trigger('click');
486+
break;
487+
case 'c': break;
488+
default: return;
489+
}
490+
491+
last_key = e.key;
492+
e.preventDefault();
493+
};
494+
495+
});
496+
});
497+

0 commit comments

Comments
 (0)
Please sign in to comment.