Release 0.9.0

This commit is contained in:
2025-11-21 07:26:02 +01:00
committed by ecv
commit 472f0812e7
240 changed files with 20033 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
@import url("100-variables.css"); /* contains css-variables for most major elements */
@import url("200-framework.css");
@import url("300-header.css");
@import url("400-main.css");
@import url("500-footer.css");

View File

@@ -0,0 +1,81 @@
@import url("110-fonts.css");
:root {
font-size: 16px;
/* colors have to be issued in HEX, so we can use them without trouble inside out JavaScript portion */
--background: #000000;
--main-bar-color: #939598;
--main-bar-border-color: #6F7172;
/* StarTrek color information found on: https://web.archive.org/web/20190717130329/http://www.lcarscom.net/ */
--lcars-pale-canary: #FF9999;
--lcars-tanoi: #FFCC99;
--lcars-golden-tanoi: #FFCC66;
--lcars-neon-carrot: #FF9933;
--lcars-eggplant: #664466;
--lcars-lilac: #CC99CC;
--lcars-anakiwa: #99CCFF;
--lcars-mariner: #3366CC;
--lcars-bahama-blue: #006699;
--lcars-blue-bell: #9999CC;
--lcars-melrose: #9999FF;
--lcars-hopbush: #CC6699;
--lcars-chestnut-rose: #CC6666;
--lcars-orange-peel: #FF9966;
--lcars-atomic-tangerine: #FF9900;
--lcars-danub: #6688CC;
--lcars-indigo: #4455BB;
--lcars-lavender-purple: #9977AA;
--lcars-cosmic: #774466;
--lcars-red-damask: #DD6644;
--lcars-medium-carmine: #AA5533;
--lcars-bourbon: #BB6622;
--lcars-sandy-brown: #EE9955;
--lcars-periwinkle: #CCDDFF;
--lcars-dodger-pale: #5599FF;
--lcars-dodger-soft: #3366FF;
--lcars-near-blue: #0011EE;
--lcars-navy-blue: #000088;
--lcars-husk: #BBAA55;
--lcars-rust: #BB4411;
--lcars-tamarillo: #882211;
/* pixel values taken from the used images */
--main_shoulder_width: 16.875em;
--main_shoulder_height: 4.5em;
--main_shoulder_gap: calc(
var(--main_shoulder_width) / 10
);
--main_bar_height: 2.625em;
--main_bar_terminator_width: 2.5em;
--main_table_gutter: 0.25em;
--main_table_header_height: 2em;
--main_table_footer_height: 1.5em;
--main_table_caption_height: 1.5em;
/* main area height */
--main_area_height: calc(
100vh
- var(--main_shoulder_height) * 2
- var(--main_table_gutter) * 2
);
--main_widget_shoulder_width: var(--main_shoulder_width);
--main_widget_height: var(--main_area_height);
--main_widget_bar_height: var(--main_bar_height);
}
@keyframes blinker {
33% {
opacity: 33%;
}
}

View File

@@ -0,0 +1,9 @@
@font-face {
font-family: 'SWISS 911 Ultra Compressed BT';
src: url('fonts/SWISS911UltraCompressedBT.ttf'); /*URL to font*/
}
@font-face {
font-family: 'SWISS 911 Extra Compressed BT';
src: url('fonts/SWISS911ExtraCompressedBT.ttf'); /*URL to font*/
}

View File

@@ -0,0 +1,127 @@
@import url("210-scrollbar_hacks.css");
@import url("220-screen_adjustments.css");
html {
background: var(--background);
}
a, a:visited {
color: var(--background);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
body {
margin: 0 auto;
font-family: "SWISS 911 Extra Compressed BT", sans-serif;
display: flex;
flex-direction: column;
}
body > header, body > footer {
flex: 0 0 auto;
}
body > main {
flex: 1 1 auto;
}
/* header and footer are special due to their graphical shoulders */
body > header > div, body > header > div:before,
body > footer > div, body > footer > div:before {
height: var(--main_shoulder_height);
}
body > header {
margin-bottom: var(--main_table_gutter);
}
body > footer {
margin-top: var(--main_table_gutter);
}
body > header > div {
position: relative;
display: flex;
align-items: flex-end;
margin:
0 var(--main_bar_terminator_width)
0 var(--main_shoulder_width);
overflow: hidden;
}
body > header,
body > footer {
border-top-left-radius: calc(
var(--main_bar_height) * 2.66)
calc(var(--main_bar_height) * 1.33
);
background:
url(ui/main_shoulder.png) no-repeat top left,
url(ui/main_horizontal_bar_end.png) no-repeat top right;
}
body > footer,
body > footer > div,
body > footer > div:before {
transform: scaleY(-1);
}
body > header > div:before,
body > footer > div:before {
width: 100%;
content: "";
background:
url(ui/main_horizontal_bar.png)
repeat-x;
}
body > footer > div {
display: flex;
align-items: flex-end;
margin:
0 var(--main_bar_terminator_width)
0 var(--main_shoulder_width);
overflow: hidden;
}
/* every element inside main > div is a widget! */
body > main > div {
color: var(--lcars-atomic-tangerine);
display: flex;
flex-wrap: wrap;
height: var(--main_area_height);
overflow-y: scroll; /* this has to stay for scroll-snapping to work */
overflow-x: hidden;
scroll-snap-type: y mandatory;
}
body > main > div > .widget {
display: flex;
flex-wrap: wrap;
flex: 0 0 auto;
height: var(--main_widget_height);
scroll-snap-align: start;
border-bottom: var(--main_table_gutter) solid var(--background);
border-right: calc(var(--main_table_gutter) * 2) solid var(--background);
}
body > main > div .single_screen {
width: 100%;
height: var(--main_area_height);
padding-left: calc(var(--main_widget_shoulder_width));
background: linear-gradient(
to right,
var(--main-bar-color) 0,
var(--main-bar-color) calc(var(--main_widget_shoulder_width) - var(--main_shoulder_gap)),
var(--background) calc(var(--main_widget_shoulder_width) - var(--main_shoulder_gap)),
var(--background) 100%
);
}
body > main > div .single_screen a:hover {
text-decoration: underline;
}

View File

@@ -0,0 +1,15 @@
::-webkit-scrollbar {
width: var(--main_bar_terminator_width);
}
::-webkit-scrollbar-track {
background: var(--background);
}
::-webkit-scrollbar-thumb {
background: var(--lcars-tanoi);
border-radius: 1.25em;
}
::-webkit-scrollbar-thumb:hover {
background: var(--lcars-atomic-tangerine);
}

View File

@@ -0,0 +1,19 @@
/* This file contains some nasty style adjustments to accommodate ridiculously small screens.
* I don't care how it looks like there as long as it's usable.
*/
@media only screen and (max-width: 960px) {
:root {
--main_widget_shoulder_width: calc(
var(--main_shoulder_width) / 2
)
}
}
/* this one will remove table-cell restrictions and kinda condense the table-row. This will be a bit untidy in looks,
but more data will be available on very small screens */
@media only screen and (max-width: 960px) {
body > main > div > .widget > main > table.data_table > tbody > tr {
display: unset;
}
}

View File

@@ -0,0 +1,25 @@
@import url("310-header_widgets.css");
body > header > div > hgroup {
position: absolute;
right: 0; top: 0;
}
body > header > div > hgroup > h1 {
font-size: var(--main_bar_height);
color: var(--lcars-tanoi);
background-color: var(--background);
font-family: "SWISS 911 Ultra Compressed BT", sans-serif;
text-transform: uppercase;
padding: 0 0.25em;
white-space: nowrap;
}
body > header > div #header_widgets {
position: absolute;
height: calc(var(--main_shoulder_height) - var(--main_bar_height));
right: 0; bottom: 0; left: 0;
display: flex;
}

View File

@@ -0,0 +1,69 @@
body > header > div > #header_widgets > .widget {
padding-right: 0.25em;
color: black;
padding-top: var(--main_table_gutter)
}
body > header > div > #header_widgets > .widget > div {
background-color: var(--lcars-atomic-tangerine);
border-radius: 12px; padding: 0 1em;
white-space: nowrap;
line-height: 1.5em;
}
body > header > div > #header_widgets > #login_logout_widget {
margin-left: auto;
}
body > header > div > #header_widgets > #login_logout_widget a {
color: var(--background)
}
body > header > div > #header_widgets > #gameserver_status_widget > div.active {
background-color: var(--lcars-tanoi);
}
body > header > div > #header_widgets > #gameserver_status_widget > div.inactive {
background-color: var(--lcars-chestnut-rose);
}
body > header > div > #header_widgets > #gameserver_status_widget span > a,
body > header > div > #header_widgets > #gameserver_status_widget span > a:visited {
color: var(--lcars-blue-bell);
}
body > header > div > #header_widgets > #gameserver_status_widget span > a {
display: inline-block;
padding: 0 calc(var(--main_table_gutter) / 2);
border-left: calc(var(--main_table_gutter) / 2) solid var(--background);
border-right: calc(var(--main_table_gutter) / 2) solid var(--background);
color: var(--background); /* Edge seems to require this */
background-color: var(--lcars-chestnut-rose);
}
body > header > div > #header_widgets > #gameserver_status_widget span > a:hover,
body > header > div > #header_widgets > #gameserver_status_widget span > a:visited {
color: var(--background);
text-decoration: none;
}
body > header > div > #header_widgets > #gameserver_status_widget span:hover > a {
background-color: var(--lcars-chestnut-rose);
}
body > header > div > #header_widgets > #gametime_widget {
order: -1;
}
body > header > div > #header_widgets > #gametime_widget span.time {
padding: 0 var(--main_table_gutter);
}
body > header > div > #header_widgets > #gametime_widget span.day.bloodday,
body > header > div > #header_widgets > #gametime_widget span.time.bloodmoon {
color: var(--lcars-chestnut-rose);
}
body > header > div > #header_widgets > #gametime_widget span.time.bloodmoon {
background-color: var(--background);
}

View File

@@ -0,0 +1,12 @@
@import url("410-main_widgets.css");
body > main > div #unauthorized_disclaimer p {
font-size: 1.5em;
padding-bottom: 1em;
}
body > main > div #unauthorized_disclaimer a,
body > main > div #unauthorized_disclaimer a:visited {
color: var(--lcars-melrose);
text-decoration: None;
}

View File

@@ -0,0 +1,466 @@
@import url("411-main_widgets_webserver_status_widget.css");
@import url("412-main_widgets_telnet_log_widget.css");
@import url("413-main_widgets_manage_players_widget.css");
@import url("414-main_widgets_manage_locations_widget.css");
@import url("415-main_widgets_manage_entities_widget.css");
body > main > div > .widget > main {
position: relative;
}
body > main > div > .widget main a {
border-radius: 0.5em;
font-family: "SWISS 911 Ultra Compressed BT", sans-serif;
padding: 0 0.5em;
}
body > main > div > .widget main a,
body > main > div > .widget main a:visited {
background-color: var(--lcars-melrose);
color: black;
}
body > main > div > .widget main a:hover {
text-decoration: none;
}
body > main > div > .widget main span.active a {
background-color: var(--lcars-tanoi);
}
body > main > div > .widget main span.inactive a {
background-color: var(--lcars-chestnut-rose);
}
body > main > div > .widget main .select_button a {
border-radius: 0;
padding: 0 0.75em;
}
body > main > div > .widget > main > table > tbody > tr > td {
font-size: 1em;
line-height: 1.5em;
padding: 0 calc(var(--main_table_gutter) / 2);
vertical-align: middle;
}
body > main > div > .widget > main > table > tbody > tr > td:last-child {
padding-right: var(--main_table_gutter);
}
body > main > div > .widget > main > table > thead tr:last-child {
/* this contains the header stuff for the widget-content */
background: var(--background);
}
body > main > div > .widget > main > table.data_table > tbody > tr > td {
white-space: nowrap;
}
body > main > div > .widget > main > table > tfoot > tr > td > div > span.active,
body > main > div > .widget .pull_out > div > span.active {
background-color: var(--lcars-lilac);
}
body > main > div > .widget > main > table > tfoot > tr > td > span.inactive,
body > main > div > .widget .pull_out > div > span.inactive {
background-color: var(--lcars-hopbush);
}
body > main > div > .widget .pull_out > div > span.info {
background-color: var(--lcars-tanoi);
}
body > main > div > .widget .pull_out > div > span.info > div > span {
margin-left: 0.5em;
flex: 1;
text-align: left;
}
body > main > div > .widget .pull_out span a,
body > main > div > .widget .pull_out span a:visited,
body > main > div > .widget .pull_out span > div {
text-decoration: none;
display: block;
margin-right: 0.5em;
color: var(--background);
}
body > main > div > .widget > main .dialog {
position: absolute;
top: var(--main_table_gutter);
bottom: 5.5em;
display: none;
}
body > main > div > .widget > main .dialog.open {
display: block;
}
body > main > div > .widget > main .dialog .modal-content {
padding: 1em;
color: var(--lcars-blue-bell);
background-color: var(--background);
border: var(--main_table_gutter) solid var(--lcars-chestnut-rose);
border-radius: 0 1em 0.5em 1.5em / 0 1em 0.5em 1em;
height: calc(100% + 1em);
}
body > main > div > .widget > main .dialog .modal-content a,
body > main > div > .widget > main .dialog .modal-content p {
font-size: 2em;
}
body > main > div > .widget > main .dialog.open .modal-content .delete_modal {
color: var(--lcars-tanoi);
height: 100%;
display: flex;
flex-direction: column;
}
body > main > div > .widget > main .dialog.open .modal-content .delete_modal header,
body > main > div > .widget > main .dialog.open .modal-content .delete_modal div {
text-align: center;
}
body > main > div > .widget > main .dialog.open .modal-content .delete_modal header {
flex: 0 0 auto;
}
body > main > div > .widget > main .dialog.open .modal-content .delete_modal div {
flex: 1 1 auto;
}
body > main > div > .widget > main .dialog.open .modal-content .delete_modal div.dynamic_content_size {
overflow: auto;
margin: calc(var(--main_bar_height) / 2) 0;
}
body > main > div > .widget > main .dialog.open .modal-content .delete_modal div.dynamic_content_size::-webkit-scrollbar {
width: calc(var(--main_bar_terminator_width) / 1.66);
}
body > main > div > .widget > main .dialog.open .modal-content .delete_modal div:last-child {
flex: 0 0 auto;
display: flex;
}
body > main > div > .widget > main .dialog.open .modal-content .delete_modal div section {
flex: 1 1 auto;
width: 50%;
}
body > main > div > .widget > main > table > tbody {
display: block;
overflow: auto;
height: calc(
var(--main_widget_height)
- var(--main_widget_bar_height)
- var(--main_table_caption_height)
);
padding-right: 0.5em;
}
body > main > div > .widget > main > table.data_table > tbody {
height: calc(
var(--main_widget_height)
- var(--main_widget_bar_height)
- var(--main_table_header_height)
- var(--main_table_footer_height)
- var(--main_table_caption_height)
);
max-width: calc(
100vw
- var(--main_widget_shoulder_width)
- var(--main_bar_terminator_width)
- 1em
);
}
body > main > div > .widget > main > table.data_table > tbody tr {
max-width: calc(
100vw
- var(--main_widget_shoulder_width)
- var(--main_bar_terminator_width)
- 1em
- var(--main_bar_terminator_width)
);
}
body > main > div > .widget > main > table > thead > tr {
display: none;
}
body > main > div > .widget > main > table.data_table > thead > tr {
display: inline-block;
width: 100%;
height: var(--main_table_header_height);
text-align: right;
}
body > main > div > .widget > main > table.data_table > thead > tr > th {
display: inline-block;
margin: var(--main_table_gutter);
height: calc(
var(--main_table_header_height)
- (var(--main_table_gutter) * 2)
);
line-height: calc(
var(--main_table_header_height)
- (var(--main_table_gutter) * 2)
);
}
body > main > div > .widget > main > table.data_table > thead > tr > th:last-child {
margin-right: var(--main_bar_terminator_width);
}
body > main > div > .widget > main table.box_input,
body > main > div > .widget > main table.box_select {
margin-bottom: 1.5em;
}
body > main > div > .widget > main table.box_select tfoot td,
body > main > div > .widget > main table.box_input tfoot td {
white-space: normal;
}
body > main > div > .widget > main table.box_select input[disabled],
body > main > div > .widget > main table.box_input input[disabled] {
background-color: var(--lcars-chestnut-rose);
}
body > main > div > .widget > main table.box_select tfoot tr td,
body > main > div > .widget > main table.box_input tfoot tr td {
color: var(--background);
padding: 0.5em 0;
}
body > main > div > .widget > main table.box_select tfoot tr td > div,
body > main > div > .widget > main table.box_input tfoot tr td > div {
/* background-image: linear-gradient(to right, var(--background), var(--lcars-melrose), var(--lcars-melrose), var(--lcars-melrose)); */
background-color: var(--lcars-melrose);
text-align: right;
line-height: 1em;
font-size: 0.9em;
padding: 0.8em;
/* display: none; */
}
body > main > div > .widget > main table.box_select td {
width: 50%;
}
/* checkbox styling */
body > main > div > .widget > main table.box_select label.slider {
position: relative;
cursor: pointer;
}
body > main > div > .widget > main table.box_select label.slider [type="checkbox"] {
display: none;
}
body > main > div > .widget > main table.box_select .slider [type="checkbox"] + span {
color: var(--background);
display: block;
background: var(--lcars-chestnut-rose);
padding: var(--main_table_gutter);
margin: var(--main_table_gutter);
border-radius: 0.5em;
}
body > main > div > .widget > main table.box_select .slider:hover [type="checkbox"] + span,
body > main > div > .widget > main table.box_select .slider :checked + span {
background: var(--lcars-tanoi);
}
body > main > div > .widget > main table.box_select .slider [type="checkbox"][disabled] + span {
background: var(--lcars-orange-peel);
}
body > main > div > .widget > main > table > thead {
max-height: var(--main_table_header_height);
}
body > main > div > .widget > main > table > tfoot {
max-height: var(--main_table_footer_height);
}
body > main > div > .widget > main > table > caption {
/* display: none; */
max-height: var(--main_table_caption_height);
border-top: 0.5em solid var(--background);
margin-right: calc(
var(--main_bar_terminator_width) / 2
);
overflow: hidden;
}
body > main > div > .widget > header {
width: 100%;
background-color: var(--lcars-hopbush);
border-radius: 1.5em 1.5em 1.5em 0;
height: var(--main_bar_height);
}
body > main > div > .widget > header > div > span {
font-size: var(--main_widget_bar_height);
line-height: 1em;
color: var(--background);
margin-left: 1.5em;
}
body > main > div > .widget > aside {
flex: 0 0 var(--main_widget_shoulder_width);
background: linear-gradient(
to right,
var(--main-bar-color) 0,
var(--main-bar-color) calc(var(--main_widget_shoulder_width) - var(--main_shoulder_gap)),
var(--background) calc(var(--main_widget_shoulder_width) - var(--main_shoulder_gap)),
var(--background) 100%
);
height: calc(
var(--main_widget_height) - var(--main_widget_bar_height)
);
}
body > main > div > .widget > main {
flex: 1 0 calc(
100%
- var(--main_widget_shoulder_width)
);
overflow-y: auto;
height: calc(
var(--main_widget_height) - var(--main_widget_bar_height)
);
}
body > main > div > .widget > aside > div {
width: var(--main_widget_shoulder_width);
}
body > main > div > .widget > aside > div > div {
border-top: var(--main_table_gutter) solid var(--background);
background-color: var(--background);
}
body > main > div > .widget > aside > div > div:last-child {
border-bottom: var(--main_table_gutter) solid var(--background);
}
body > main > div > .widget > aside > div > div > span {
display: block;
background-color: var(--lcars-hopbush);
margin-right: var(--main_shoulder_gap);
line-height: var(--main_shoulder_height);
}
body > main > div > .widget > aside > div > div > span {
border-radius: var(--main_table_gutter);
}
body > main > div > .widget > aside > div > div > span.info {
border-radius: unset;
line-height: calc(var(--main_shoulder_height) / 2);
}
body > main > div > .widget > main > table > tfoot > tr {
height: var(--main_table_footer_height);
line-height: var(--main_table_footer_height);
}
body > main > div > .widget > main > table > tfoot > tr > td {
height: calc(
var(--main_table_footer_height)
- var(--main_table_gutter)
);
line-height: calc(
var(--main_table_footer_height)
- var(--main_table_gutter)
);
vertical-align: bottom;
}
body > main > div > .widget > main > table > tfoot > tr > td > div {
background-color: var(--lcars-tanoi);
padding-left: calc(var(--main_shoulder_width) / 2);
border-radius: 0 1em 1em 2em / 0 1em 1em 2em;
}
body > main > div > .widget main > table > tfoot > tr > td > div .delete_button {
display: inline-block;
border-left: 0.25em solid var(--background);
border-right: 0.25em solid var(--background);
}
body > main > div > .widget main > table > tfoot > tr > td > div .delete_button span a,
body > main > div > .widget main > table > tfoot > tr > td > div .delete_button span {
display: inline-block;
}
body > main > div > .widget main > table > tfoot > tr > td > div .delete_button span.active a,
body > main > div > .widget main > table > tfoot > tr > td > div .delete_button span.active {
background-color: var(--lcars-chestnut-rose);
}
body > main > div > .widget main > table > tfoot > tr > td > div .delete_button span.inactive a,
body > main > div > .widget main > table > tfoot > tr > td > div .delete_button span.inactive {
background-color: var(--lcars-tanoi);
}
body > main > div > .widget > main tr td[id$="_actions"] > span {
padding-right: var(--main_table_gutter);
}
body > main > div > .widget > main tr td[id$="_actions"] > span:last-child {
padding-right: 0;
}
/* making the standard order the middle instead of 0 */
body > main > div > .widget {
order: 500;
}
body > main > div > #manage_players_widget {
order: -4;
flex: 1 0 calc(
960px
- var(--main_table_gutter) * 2
- var(--main_bar_terminator_width)
);
}
body > main > div > #webserver_status_widget {
order: -3;
flex: 1 0 calc(
480px
- var(--main_table_gutter) * 2
);
}
body > main > div > #manage_locations_widget {
order: -2;
flex: 1 0 calc(
860px
- var(--main_table_gutter) * 2
);
}
body > main > div > #manage_entities_widget {
order: -1;
flex: 1 0 calc(
768px
- var(--main_table_gutter) * 2
);
}
body > main > div > #telnet_log_widget {
order: 999;
flex: 1 0 calc(
768px
- var(--main_table_gutter) * 2
);
}

View File

@@ -0,0 +1,30 @@
body > main > div > #telnet_log_widget {
margin-bottom: 0;
width: 100%;
}
body > main > div > #telnet_log_widget .log_line {
/* log entries seem to be traditionally blue in LCARS ^^ */
color: var(--lcars-blue-bell);
}
body > main > div > #telnet_log_widget .log_line td {
white-space: normal;
padding-left: calc(var(--main_shoulder_width) / 3);
text-indent: calc(-1 * calc(var(--main_shoulder_width) / 3));
}
body > main > div > #telnet_log_widget tr.game_chat,
body > main > div > #telnet_log_widget caption span.game_chat {
color: var(--lcars-hopbush);
}
body > main > div > #telnet_log_widget tr.player_logged,
body > main > div > #telnet_log_widget caption span.player_logged {
color: var(--lcars-anakiwa);
}
body > main > div > #telnet_log_widget tr.bot_command,
body > main > div > #telnet_log_widget caption span.bot_command {
color: var(--lcars-cosmic);
}

View File

@@ -0,0 +1,92 @@
body > main > div > #manage_players_widget > main > table > tbody > tr > td[id$='_name'] {
width: 100%;
}
body > main > div > #manage_players_widget tbody#player_table > tr:hover * {
background-color: var(--lcars-tanoi);
color: var(--background)
}
/* player status */
/* offline */
body > main > div > #manage_players_widget caption span:not(.is_online):not(.is_initialized),
body > main > div > #manage_players_widget tbody > span:not(.is_online):not(.is_initialized),
body > main > div > #manage_players_widget tbody > tr:not(.is_online):not(.is_initialized) {
color: var(--lcars-chestnut-rose);
}
/* offline and dead */
body > main > div > #manage_players_widget caption span.in_limbo:not(.is_online):not(.is_initialized):not(.has_health),
body > main > div > #manage_players_widget tbody > span.in_limbo:not(.is_online):not(.is_initialized):not(.has_health),
body > main > div > #manage_players_widget tbody > tr.in_limbo:not(.is_online):not(.is_initialized):not(.has_health) {
color: var(--lcars-chestnut-rose);
}
/* special fading animation for offline players currently dead */
body > main > div > #manage_players_widget caption span.in_limbo:not(.is_online):not(.is_initialized):not(.has_health),
body > main > div > #manage_players_widget tbody > span.in_limbo:not(.is_online):not(.is_initialized):not(.has_health) td:nth-child(n+3),
body > main > div > #manage_players_widget tbody > tr.in_limbo:not(.is_online):not(.is_initialized):not(.has_health) td:nth-child(n+3) {
animation: blinker 4s linear infinite;
}
/* online and logging in */
body > main > div > #manage_players_widget caption span.is_online:not(.is_initialized).in_limbo,
body > main > div > #manage_players_widget tbody > span.is_online:not(.is_initialized).in_limbo,
body > main > div > #manage_players_widget tbody > tr.is_online:not(.is_initialized).in_limbo {
color: var(--lcars-tanoi);
}
/* special fading animation for players currently logging in */
body > main > div > #manage_players_widget caption span.is_online:not(.is_initialized).in_limbo,
body > main > div > #manage_players_widget tbody > span.is_online:not(.is_initialized).in_limbo td:nth-child(n+3),
body > main > div > #manage_players_widget tbody > tr.is_online:not(.is_initialized).in_limbo td:nth-child(n+3) {
animation: blinker 3s linear infinite;
}
/* online */
body > main > div > #manage_players_widget caption span.is_online.is_initialized:not(.in_limbo),
body > main > div > #manage_players_widget tbody > span.is_online.is_initialized:not(.in_limbo),
body > main > div > #manage_players_widget tbody > tr.is_online.is_initialized:not(.in_limbo) {
color: var(--lcars-tanoi);
}
/* online and dead */
body > main > div > #manage_players_widget caption span.is_online.is_initialized.in_limbo,
body > main > div > #manage_players_widget tbody > span.is_online.is_initialized.in_limbo,
body > main > div > #manage_players_widget tbody > tr.is_online.is_initialized.in_limbo {
color: var(--lcars-atomic-tangerine);
}
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_id"],
body > main > div > #manage_players_widget tr[id^="player_table_row_"] td[class="position"],
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_ping"],
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_last_updated_servertime"],
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_last_seen_gametime"] {
font-size: 0.90em;
}
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_name"] {
max-width: 10em;
}
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_health"]:before {
content: "\2665";
padding-right: var(--main_table_gutter);
padding-left: calc(var(--main_table_gutter) * 2);
color: var(--lcars-chestnut-rose);
}
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_pos"] span {
width: 1.5em;
display: inline-block;
}
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_pos"]:before {
padding-left: calc(var(--main_table_gutter) * 2);
color: var(--lcars-chestnut-rose);
content: "\2691";
}
body > main > div > #manage_players_widget td[id^="player_table_row_"][id$="_zombies"]:before {
color: var(--lcars-chestnut-rose);
content: "\2620";
padding-right: var(--main_table_gutter);
padding-left: calc(var(--main_table_gutter) * 2);
}

View File

@@ -0,0 +1,52 @@
body > main > div > #manage_locations_widget > main > table > tbody > tr > td[id$='_name'] {
width: 100%;
}
body > main > div > #manage_locations_widget tbody#location_table > tr:hover * {
background-color: var(--lcars-tanoi);
color: var(--background)
}
body > main > div > #manage_locations_widget #current_player_pos > span > div {
display: flex;
justify-content: flex-end;
}
body > main > div > #manage_locations_widget #current_player_pos > span > div > div {
margin-right: 0.5em;
}
body > main > div > #manage_locations_widget #current_player_pos > span > div > div:last-child {
margin-right: 0;
}
body > main > div > #manage_locations_widget #current_player_pos > span > div > label {
flex: 0;
white-space: nowrap;
margin-right: 0.5em;
}
body > main > div > #manage_locations_widget #current_player_pos > span > div > label:last-child {
margin-right: 0;
}
body > main > div > #manage_locations_widget #current_player_pos > span > div > label input {
border: 0;
display: inline-block;
margin: 0; padding: 0;
width: 2.5em;
text-align: right;
background-color: transparent;
}
body > main > div > main > div > #manage_locations_widget #current_player_pos > span > div > label:nth-child(2) input {
width: 2em;
}
body > main > div > main > div > #manage_locations_widget td[id^="location_table_row_"][id$="_id"],
body > main > div > main > div > #manage_locations_widget td[id^="location_table_row_"][id$="_last_changed"],
body > main > div > main > div > #manage_locations_widget td[id^="location_table_row_"][id$="_coordinates"],
body > main > div > main > div > #manage_locations_widget td[id^="location_table_row_"][id$="_owner"] {
font-size: 0.90em;
vertical-align: middle;
}

View File

@@ -0,0 +1,8 @@
body > main > div > #manage_entities_widget > main > table > tbody > tr > td[id$='_name'] {
width: 100%;
}
body > main > div > #manage_entities_widget tbody#entity_table > tr:hover * {
background-color: var(--lcars-tanoi);
color: var(--background)
}

View File

@@ -0,0 +1,6 @@
@import url("310-header_widgets.css");
footer > div > p {
position: absolute;
right: 0;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,661 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg {
max-width: none !important;
max-height: none !important;
}
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
max-width: none !important;
max-height: none !important;
width: auto;
padding: 0;
}
.leaflet-container img.leaflet-tile {
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
mix-blend-mode: plus-lighter;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
svg.leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline-offset: 1px;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
font-size: 12px;
font-size: 0.75rem;
line-height: 1.5;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover,
.leaflet-bar a:focus {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
font-size: 13px;
font-size: 1.08333em;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.8);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
line-height: 1.4;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover,
.leaflet-control-attribution a:focus {
text-decoration: underline;
}
.leaflet-attribution-flag {
display: inline !important;
vertical-align: baseline !important;
width: 1em;
height: 0.6669em;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
white-space: nowrap;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.8);
text-shadow: 1px 1px #fff;
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 24px 13px 20px;
line-height: 1.3;
font-size: 13px;
font-size: 1.08333em;
min-height: 1px;
}
.leaflet-popup-content p {
margin: 17px 0;
margin: 1.3em 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-top: -1px;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
pointer-events: auto;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
border: none;
text-align: center;
width: 24px;
height: 24px;
font: 16px/24px Tahoma, Verdana, sans-serif;
color: #757575;
text-decoration: none;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus {
color: #585858;
}
.leaflet-popup-scrolled {
overflow: auto;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
-ms-zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-interactive {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}
/* Printing */
@media print {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,49 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
table {
caption-side: bottom;
}
table caption {
text-align: right;
}
footer {
text-align: right;
}
.right {
text-align: right;
}
.center {
text-align: center;
}

View File

@@ -0,0 +1,528 @@
document.addEventListener("DOMContentLoaded", function(event) {
// based on https://stackoverflow.com/a/56279295/8967590
Audio.prototype.play = (function(play) {
return function () {
let audio = this;
let promise = play.apply(audio, arguments);
if (promise !== undefined) {
promise.catch(_ => {
console.log("autoplay of audiofile failed :(");
});
}
};
}) (Audio.prototype.play);
let audio_files = [];
function load_audio_files() {
audio_files["computer_work_beep"] = new Audio('/static/lcars/audio/computer_work_beep.mp3');
audio_files["computer_error"] = new Audio('/static/lcars/audio/computer_error.mp3');
audio_files["keyok1"] = new Audio('/static/lcars/audio/keyok1.mp3');
audio_files["keyok1"].volume = 0.05;
audio_files["input_ok_2_clean"] = new Audio('/static/lcars/audio/input_ok_2_clean.mp3');
audio_files["processing"] = new Audio('/static/lcars/audio/processing.mp3');
audio_files["processing"].volume = 0.25;
audio_files["computerbeep_11"] = new Audio('/static/lcars/audio/computerbeep_11.mp3');
audio_files["computerbeep_11"].volume = 0.5;
audio_files["computerbeep_38"] = new Audio('/static/lcars/audio/computerbeep_38.mp3');
audio_files["computerbeep_38"].volume = 0.1;
audio_files["computerbeep_65"] = new Audio('/static/lcars/audio/computerbeep_65.mp3');
audio_files["alarm01"] = new Audio('/static/lcars/audio/alarm01.mp3');
audio_files["alarm03"] = new Audio('/static/lcars/audio/alarm03.mp3');
audio_files["alert12"] = new Audio('/static/lcars/audio/alert12.mp3');
}
function play_audio_file(identifier) {
try {
if (audio_files[identifier].readyState === 4) { // 4 = HAVE_ENOUGH_DATA
if (!audio_files[identifier].ended) {
audio_files[identifier].currentTime = 0;
audio_files[identifier].play();
} else {
audio_files[identifier].play();
}
}
} catch(err) {
console.error("[AUDIO] Failed to play audio file:", identifier, err);
}
}
/* found on https://stackoverflow.com/questions/14267781/sorting-html-table-with-javascript/49041392#49041392
* slightly modified
*/
let index; // cell index
let toggleBool; // sorting asc, desc
window.sorting = function sorting(th, tbody, index) {
function compareCells(a, b) {
let aVal = a.cells[index].innerText.replace(/,/g, '');
let bVal = b.cells[index].innerText.replace(/,/g, '');
if (toggleBool) {
let temp = aVal;
aVal = bVal;
bVal = temp;
}
if (aVal.match(/^[0-9]+$/) && bVal.match(/^[0-9]+$/)) {
return parseFloat(aVal) - parseFloat(bVal);
} else {
if (aVal < bVal) {
return -1;
} else if (aVal > bVal) {
return 1;
} else {
return 0;
}
}
}
this.index = index;
toggleBool = !toggleBool;
let datas = [];
for (let i = 0; i < tbody.rows.length; i++) {
datas[i] = tbody.rows[i];
}
// sort by cell[index]
datas.sort(compareCells);
for (let i = 0; i < tbody.rows.length; i++) {
// rearrange table rows by sorted rows
tbody.appendChild(datas[i]);
}
};
/* found on https://stackoverflow.com/a/21648508/8967590
* slightly modified to only return the rgb value and getting rid of type-warnings
*/
function hexToRgb(hex){
let char;
if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
char = hex.substring(1).split('');
if (char.length === 3) {
char = [char[0], char[0], char[1], char[1], char[2], char[2]];
}
char = '0x' + char.join('');
return [(char >> 16) & 255, (char >> 8) & 255, char & 255].join(', ');
} else {
alert(hex);
throw new Error('Bad Hex');
}
}
let lcars_colors = [];
function load_lcars_colors() {
/* https://davidwalsh.name/css-variables-javascript */
lcars_colors["lcars-pale-canary"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-pale-canary').trim()
);
lcars_colors["lcars-tanoi"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-tanoi').trim()
);
lcars_colors["lcars-golden-tanoi"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-golden-tanoi').trim()
);
lcars_colors["lcars-neon-carrot"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-neon-carrot').trim()
);
lcars_colors["lcars-eggplant"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-eggplant').trim()
);
lcars_colors["lcars-lilac"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-lilac').trim()
);
lcars_colors["lcars-anakiwa"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-anakiwa').trim()
);
lcars_colors["lcars-mariner"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-mariner').trim()
);
lcars_colors["lcars-bahama-blue"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-bahama-blue').trim()
);
lcars_colors["lcars-blue-bell"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-blue-bell').trim()
);
lcars_colors["lcars-melrose"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-melrose').trim()
);
lcars_colors["lcars-hopbush"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-hopbush').trim()
);
lcars_colors["lcars-chestnut-rose"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-chestnut-rose').trim()
);
lcars_colors["lcars-orange-peel"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-orange-peel').trim()
);
lcars_colors["lcars-atomic-tangerine"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-atomic-tangerine').trim()
);
lcars_colors["lcars-danub"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-danub').trim()
);
lcars_colors["lcars-indigo"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-indigo').trim()
);
lcars_colors["lcars-lavender-purple"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-lavender-purple').trim()
);
lcars_colors["lcars-cosmic"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-cosmic').trim()
);
lcars_colors["lcars-red-damask"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-red-damask').trim()
);
lcars_colors["lcars-medium-carmine"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-medium-carmine').trim()
);
lcars_colors["lcars-bourbon"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-bourbon').trim()
);
lcars_colors["lcars-sandy-brown"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-sandy-brown').trim()
);
lcars_colors["lcars-periwinkle"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-periwinkle').trim()
);
lcars_colors["lcars-dodger-pale"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-dodger-pale').trim()
);
lcars_colors["lcars-dodger-soft"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-dodger-soft').trim()
);
lcars_colors["lcars-near-blue"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-near-blue').trim()
);
lcars_colors["lcars-navy-blue"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-navy-blue').trim()
);
lcars_colors["lcars-husk"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-husk').trim()
);
lcars_colors["lcars-rust"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-rust').trim()
);
lcars_colors["lcars-tamarillo"] = hexToRgb(
getComputedStyle(document.documentElement).getPropertyValue('--lcars-tamarillo').trim()
);
}
// https://stackoverflow.com/a/38311629/8967590
$.fn.setClass = function(classes) {
this.attr('class', classes);
return this;
};
// https://stackoverflow.com/a/46308265,
// slightly modified for better readability
$.fn.selectText = function(){
let element = this[0], range, selection;
if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(element);
range.select();
document.execCommand('copy');
} else if (window.getSelection) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
document.execCommand('copy');
}
};
$.fn.upsert = function(target_element_id, htmlString) {
// upsert - find or create new element
let $el = $(this).find(target_element_id);
if ($el.length === 0) {
// didn't exist, create and add to caller
$el = $(htmlString);
$(this).prepend($el);
}
return $el;
};
let flash = function(elements, color=false) {
let opacity = 40;
if (color === false) {
color = lcars_colors["lcars-tanoi"]; // has to be in this format since we use rgba
}
let interval = setInterval(function() {
opacity -= 2.5;
if (opacity <= 0) {
clearInterval(interval);
$(elements).removeAttr('style');
} else {
$(elements).css({
"background-color": "rgba(" + color + ", " + (opacity / 50) + ")"
});
}
}, 20)
};
//connect to the socket server.
window.socket = io.connect(
'http://' + document.domain + ':' + location.port, {
'sync disconnect on unload': true
}
);
window.socket.on('connected', function() {
window.socket.emit('ding');
});
let start_time = (new Date).getTime();
const PING_TIMEOUT_THRESHOLD = 5000; // Only log if ping takes >5 seconds
window.setInterval(function() {
start_time = (new Date).getTime();
socket.emit('ding');
play_audio_file("processing");
// No log for normal ping - would be spam (every 10 seconds)
}, 10000);
window.socket.on('dong', function() {
let latency = (new Date).getTime() - start_time;
play_audio_file("keyok1");
// Only log slow pings
if (latency > PING_TIMEOUT_THRESHOLD) {
console.warn("[PING] Slow response: " + latency + "ms (threshold: " + PING_TIMEOUT_THRESHOLD + "ms)");
}
});
// Session conflict handling
window.socket.on('session_conflict', function(data) {
console.log('[SESSION] Conflict detected:', data);
play_audio_file("alert12");
let message = data.message + '\n\nAktive Sessions: ' + data.existing_sessions;
if (confirm(message)) {
// User wants to take over
console.log('[SESSION] Taking over existing session');
window.socket.emit('session_takeover_accept');
} else {
// User declined
console.log('[SESSION] Takeover declined');
window.socket.emit('session_takeover_decline');
}
});
window.socket.on('session_accepted', function() {
console.log('[SESSION] Session accepted');
play_audio_file("computerbeep_11");
});
window.socket.on('session_declined', function(data) {
console.log('[SESSION] Session declined:', data.message);
play_audio_file("computer_error");
alert(data.message);
// Browser will be disconnected by server
});
window.socket.on('session_taken_over', function(data) {
console.log('[SESSION] Session taken over by another browser');
play_audio_file("alarm01");
alert(data.message);
// Connection will be closed by server
// Show a visual indicator that session is no longer active
document.body.style.opacity = '0.5';
document.body.style.pointerEvents = 'none';
});
load_audio_files();
load_lcars_colors();
window.socket.on('data', function(data) {
try {
// Log event for debugging (can be disabled in production)
if (window.socketDebugMode) {
console.log('[SOCKET] Received event:', data.data_type, data);
}
if ([
"element_content",
"widget_content",
"modal_content",
"remove_table_row",
"table_row",
"table_row_content"
].includes(data["data_type"])) {
/* target element needs to be present for these operations */
// Validate data structure before accessing
if (!data["target_element"]) {
console.error('[SOCKET] Missing target_element in data:', data);
return false;
}
let target_element_id = data["target_element"]["id"];
if (target_element_id == null) {
console.warn('[SOCKET] target_element.id is null for data_type:', data["data_type"]);
return false;
}
if (data["data_type"] === "widget_content") {
/* widget content requires a selector, in case the widget is not yet rendered in the browser
* with the help of the selector, we can create it in the right place
*/
let html_string = '<div id="' + target_element_id + '" class="widget"></div>';
let selector = data["target_element"]["selector"];
let target_element = $(selector).upsert(
'#' + target_element_id,
html_string
);
if (data["method"] === "update") {
target_element.html(data["payload"]);
} else if (data["method"] === "append") {
target_element.append(data["payload"]);
} else if (data["method"] === "prepend") {
play_audio_file("computerbeep_38");
let target_table = $('#' + target_element_id + ' ' + data["target_element"]["type"]);
/* prepend adds a row on top */
target_table.prepend(data["payload"]);
let $entries = target_table.find('tr');
if ($entries.length >= 50) {
$entries.last().remove();
}
}
}
if (data["data_type"] === "element_content") {
let target_element = document.getElementById(target_element_id);
if (target_element == null) {
return false;
}
if (data["method"] === "update") {
if (target_element.innerHTML !== data["payload"]) {
target_element.innerHTML = data["payload"];
} else {
return false;
}
} else if (data["method"] === "replace") {
// Note: After outerHTML replacement, target_element reference becomes invalid
// Flash BEFORE replacing, or flash the parent element
let parent = target_element.parentElement;
target_element.outerHTML = data["payload"];
if (parent) {
// Flash the new element by finding it in the parent
let new_element = document.getElementById(target_element_id);
if (new_element) {
flash(new_element);
}
}
}
}
if (data["data_type"] === "modal_content") {
let target_element = document.getElementById(target_element_id);
if (target_element == null) {
return false;
}
let modal_container = target_element.parentElement;
modal_container.classList.toggle("open");
$(target_element).html(data["payload"])
}
if (data["data_type"] === "table_row") {
/* the whole row will be swapped out, not very economic ^^
* can be suitable for smaller widgets, not needing the hassle of sub-element id's and stuff
* table_row content requires a selector, in case the row is not yet rendered in the browser
* with the help of the selector, we can create it in the right place
*/
play_audio_file("processing");
let parent_element = $(data["target_element"]["selector"]);
let target_element = parent_element.find("#" + target_element_id);
if (target_element.length === 0) {
/* If the row doesn't exist, append it */
parent_element.append(data["payload"]);
} else {
target_element.replaceWith(data["payload"]);
}
}
if (data["data_type"] === "table_row_content") {
play_audio_file("keyok1");
let parent_element = $('#' + target_element_id);
if (parent_element.length === 0) {
return false;
}
if (data["target_element"]["class"].length >= 1) {
parent_element.setClass(data["target_element"]["class"]);
} else {
parent_element[0].removeAttribute("class");
}
let elements_to_update = data["payload"];
$.each(elements_to_update, function (key, value) {
if ($.type(value) === 'object') {
$.each(value, function (sub_key, sub_value) {
let element_to_update = $('#' + target_element_id + '_' + key + '_' + sub_key);
if (element_to_update.length !== 0 && element_to_update.text() !== sub_value.toString()) {
element_to_update.html(sub_value);
}
});
} else {
let element_to_update = $('#' + target_element_id + '_' + key);
if (element_to_update.length !== 0 && element_to_update.text() !== value.toString()) {
element_to_update.html(value);
}
}
});
}
if (data["data_type"] === "remove_table_row") {
let target_element = document.getElementById(target_element_id);
if (target_element && target_element.parentElement) {
target_element.parentElement.removeChild(target_element);
} else {
console.warn('[SOCKET] Cannot remove table row - element not found:', target_element_id);
}
}
} else if (data["data_type"] === "status_message") {
/* this does not require any website containers. we simply play sounds and echo logs */
if (data['status']) {
let json = data["status"];
if (json["status"]) {
let status = json["status"];
let action = data["payload"][0];
if (status === "success") {
play_audio_file("computerbeep_11");
} else if (status === "fail") {
play_audio_file("computer_error");
flash(document.body, lcars_colors["lcars-chestnut-rose"])
}
console.log(
"received status\n\"" + status + ":" + json["uuid4"] + "\"\n" +
"for action\n\"" + action + "\""
);
}
}
}
} catch (error) {
// Catch any errors to prevent handler from breaking
console.error('[SOCKET ERROR] Failed to process event:', {
error: error.message,
stack: error.stack,
data_type: data ? data.data_type : 'unknown',
data: data
});
// Play error sound to alert user
play_audio_file("computer_error");
// Flash screen red to indicate error
flash(document.body, lcars_colors["lcars-chestnut-rose"]);
}
});
});