Replaces vchat with tgchat/tg-panel

This commit is contained in:
ItsSelis
2023-12-08 17:25:51 +01:00
committed by Selis
parent a56bd267b3
commit bc40f61463
20 changed files with 503 additions and 2838 deletions
-120
View File
@@ -1,120 +0,0 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="semantic.min.css" />
<link rel="stylesheet" type="text/css" href="vchat-font-embedded.css" />
<link rel="stylesheet" type="text/css" href="ss13styles.css" />
</head>
<body>
<div class="ui top fixed pointing menu">
<div class="item">
Main
</div>
<div class="item">
OOC
<div class="ui top right attached red label">15</div>
</div>
<div class="right menu">
<div class="item" style="cursor: pointer;" title="New Tab"><i class="icon-folder-add"></i></div>
<div class="item" style="cursor: pointer;" title="Pause Autoscroll">
<i class="icon-pause-outline" :class="{ blinkwarn: paused }"></i>
</div>
<div class="item" style="cursor: pointer;" title="Edit Mode">
<i class="icon-cog-outline" :class="{ blinkwarn: editing }"></i>
</div>
<div class="item" style="padding-left: 0px !important;"><span class="ui circular label" title="VChat Latency (Not exactly network latency)" :class="ping_classes">{{latency}}ms</span></div>
</div>
</div>
<div id="contentbox">
<div class="ui segment">
<span class='game say'><b>Test Person</b> says, "Testing say message."</span><br>
<span class='notice'>Testing notice message.</span><br>
<span class='danger'>Testing danger message.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message."</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message.</span></span><br>
<span class='looc'>Testing LOOC message.</span><br>
<span class='rlooc'>Testing RLOOC message.</span><br>
<span class='admin_channel'>Testing asay message.</span><br>
<span class='game say'><b>Test Person</b> says, "Testing say message 2."</span><br>
<span class='notice'>Testing notice message 2.</span><br>
<span class='danger'>Testing danger message 2.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message 2".</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message 2.</span></span><br>
<span class='looc'>Testing LOOC message 2.</span><br>
<span class='rlooc'>Testing RLOOC message 2.</span><br>
<span class='admin_channel'>Testing asay message 2.</span><br>
<span class='game say'><b>Test Person</b> says, "Testing say message 3."</span><br>
<span class='notice'>Testing notice message 3.</span><br>
<span class='danger'>Testing danger message 3.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message 3".</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message 3.</span></span><br>
<span class='looc'>Testing LOOC message 3.</span><br>
<span class='rlooc'>Testing RLOOC message 3.</span><br>
<span class='admin_channel'>Testing asay message 3.</span><br>
<span class='game say'><b>Test Person</b> says, "Testing say message 4."</span><br>
<span class='notice'>Testing notice message 4.</span><br>
<span class='danger'>Testing danger message 4.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message 4".</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message 4.</span></span><br>
<span class='looc'>Testing LOOC message 4.</span><br>
<span class='rlooc'>Testing RLOOC message 4.</span><br>
<span class='admin_channel'>Testing asay message 4.</span><br>
<span class='game say'><b>Test Person</b> says, "Testing say message 5."</span><br>
<span class='notice'>Testing notice message 5.</span><br>
<span class='danger'>Testing danger message 5.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message 5".</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message 5.</span></span><br>
<span class='looc'>Testing LOOC message 5.</span><br>
<span class='rlooc'>Testing RLOOC message 5.</span><br>
<span class='admin_channel'>Testing asay message 5.</span><br>
</div>
<div class="ui segment inverted">
<span class='game say'><b>Test Person</b> says, "Testing say message."</span><br>
<span class='notice'>Testing notice message.</span><br>
<span class='danger'>Testing danger message.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message."</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message.</span></span><br>
<span class='looc'>Testing LOOC message.</span><br>
<span class='rlooc'>Testing RLOOC message.</span><br>
<span class='admin_channel'>Testing asay message.</span><br>
<span class='game say'><b>Test Person</b> says, "Testing say message 2."</span><br>
<span class='notice'>Testing notice message 2.</span><br>
<span class='danger'>Testing danger message 2.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message 2".</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message 2.</span></span><br>
<span class='looc'>Testing LOOC message 2.</span><br>
<span class='rlooc'>Testing RLOOC message 2.</span><br>
<span class='admin_channel'>Testing asay message 2.</span><br>
<span class='game say'><b>Test Person</b> says, "Testing say message 3."</span><br>
<span class='notice'>Testing notice message 3.</span><br>
<span class='danger'>Testing danger message 3.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message 3".</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message 3.</span></span><br>
<span class='looc'>Testing LOOC message 3.</span><br>
<span class='rlooc'>Testing RLOOC message 3.</span><br>
<span class='admin_channel'>Testing asay message 3.</span><br>
<span class='game say'><b>Test Person</b> says, "Testing say message 4."</span><br>
<span class='notice'>Testing notice message 4.</span><br>
<span class='danger'>Testing danger message 4.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message 4".</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message 4.</span></span><br>
<span class='looc'>Testing LOOC message 4.</span><br>
<span class='rlooc'>Testing RLOOC message 4.</span><br>
<span class='admin_channel'>Testing asay message 4.</span><br>
<span class='game say'><b>Test Person</b> says, "Testing say message 5."</span><br>
<span class='notice'>Testing notice message 5.</span><br>
<span class='danger'>Testing danger message 5.</span><br>
<span class='secradio'>[Security] <b>Secu Person</b> says, "Testing radio message 5".</span><br>
<span class='ooc'><span class='everyone'>Testing OOC message 5.</span></span><br>
<span class='looc'>Testing LOOC message 5.</span><br>
<span class='rlooc'>Testing RLOOC message 5.</span><br>
<span class='admin_channel'>Testing asay message 5.</span><br>
</div>
</div>
</body>
</html>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-268
View File
@@ -1,268 +0,0 @@
/* VChat Styles */
body.inverted {
background-color: #111111;
}
#topmenu {
overflow-x: auto;
overflow-y: hidden;
}
#contentbox {
margin-top: 3.3rem; /* Make room for the fixed top menu */
overflow-wrap: break-word;
word-wrap: break-word; /* IE, blah */
}
.menu, .item {
padding-top: 0.2em !important;
padding-bottom: 0.2em !important;
}
.ui.menu .item > .label:not(.floating) {
margin-left: 0;
padding-bottom: 0;
padding-top: 0;
text-align: center;
font-size: 10px;
}
.item {
cursor: pointer;
}
.inputbox {
width: 5em !important;
padding: 0.1em !important;
}
.icon-left-open-outline:hover, .icon-right-open-outline:hover, .icon-cancel-circled-outline:hover {
color: red;
}
.blinkwarn {
color: #FF0000;
animation: blink-animation 1s steps(5, start) infinite;
}
@keyframes blink-animation {
to {
visibility: hidden;
}
}
/* SS13 Styles */
#messagebox {font-family: Verdana, sans-serif;}
#messagebox h1, #messagebox h2, #messagebox h3, #messagebox h4, #messagebox h5, #messagebox h6 {color: #0000ff; font-family: Georgia, Verdana, sans-serif;}
#messagebox em {font-style: normal; font-weight: bold;}
#messagebox a {text-decoration: underline;}
.motd {color: #638500;font-family: Verdana, sans-serif;}
.motd h1, .motd h2, .motd h3, .motd h4, .motd h5, .motd h6
{color: #638500;text-decoration: underline;}
.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover
{color: #638500;}
.prefix {font-weight: bold;}
.log_message {color: #386AFF; font-weight: bold;}
/*.inverted .message {color: #386AFF; font-weight: bold;}*/
/* OOC */
.ooc {font-weight: bold;}
.ooc img.text_tag {width: 32px; height: 10px;}
.ooc .everyone {color: #002eb8;}
.inverted .ooc .everyone {color: #004ed8;} /* Dark mode */
.looc {color: #3A9696; font-weight: bold}
.rlooc {color: #3ABB96; font-weight: bold}
.ooc .elevated {color: #2e78d9;}
.ooc .moderator {color: #184880;}
.ooc .developer {color: #1b521f;}
.ooc .admin {color: #b82e00;}
.ooc .event_manager {color: #660033;}
.ooc .aooc {color: #960018;}
/* Admin: Private Messages */
.pm .howto {color: #ff0000; font-weight: bold; font-size: 200%;}
.pm .in {color: #ff0000;}
.pm .out {color: #ff0000;}
.pm .other {color: #0000ff;}
/* Admin: Channels */
.mentor {color: #808000;}
.mentor_channel {color: #808000; font-weight: bold;}
.mod_channel {color: #735638; font-weight: bold;}
.mod_channel .admin {color: #b82e00; font-weight: bold;}
.admin_channel {color: #9611D4; font-weight: bold;}
.event_channel {color: #cc3399; font-weight: bold;}
/* Radio: Misc */
.deadsay {color: #530FAD;}
.inverted .deadsay {color: #732FCD;} /* Dark mode */
.radio {color: #008000;}
.inverted .radio {color: #00a800;} /* Dark mode */
.deptradio {color: #ff00ff;} /* when all other department colors fail */
.newscaster {color: #750000;}
/* Radio Channels */
.comradio {color: #193A7A;}
.inverted .comradio {color: #395A9A;} /* Dark mode */
.syndradio {color: #6D3F40;}
.centradio {color: #5C5C8A;}
.airadio {color: #FF00FF;}
.entradio {color: #339966;}
.secradio {color: #A30000;}
.engradio {color: #A66300;}
.medradio {color: #008160;}
.sciradio {color: #993399;}
.supradio {color: #5F4519;}
.srvradio {color: #6eaa2c;}
.expradio {color: #555555;}
/* Miscellaneous */
.name {font-weight: bold;}
.say {color: #000000;}
.inverted .say {color: #FFFFFF;}
.emote {color: #000000;}
.inverted .emote {color: #FFFFFF;}
.alert {color: #ff0000;}
.valert {color: #ff0000;}
h1.alert, h2.alert {color: #000000;}
/* VOREStation Add Start */
.nif {}
.psay {color: #800080;font-style: italic;}
.inverted .psay {color: #e300e4;font-style: italic;}
.pemote {color: #800080;font-style: italic;}
.inverted .pemote {color: #e300e4;font-style: italic;}
/* VOREStation Add End */
/* Game Messages */
.attack {color: #ff0000;}
.moderate {color: #CC0000;}
.disarm {color: #990000;}
.passive {color: #660000;}
.critical {color: #ff0000; font-weight: bold; font-size: 150%;}
.danger {color: #ff0000; font-weight: bold;}
.vdanger {color: #ff0000; font-weight: bold;}
.warning {color: #ff0000; font-style: italic;}
.vwarning {color: #ff0000; font-style: italic;}
.rose {color: #ff5050;}
.info {color: #0000CC;}
.inverted .info {color: #6060c9;} /* Dark mode */
.notice {color: #000099;}
.inverted .notice {color: #6060c9;} /* Dark mode */
.vnotice {color: #000099;}
.inverted .vnotice {color: #6060c9;} /* Dark mode */
.alium {color: #00ff00;}
.cult {color: #800080; font-weight: bold; font-style: italic;}
.reflex_shoot {color: #000099; font-style: italic;}
/* Languages */
.alien {color: #543354;font-style: italic;}
.tajaran {color: #803B56;}
.tajaran_signlang {color: #941C1C;}
.akhani {color: #AC398C;}
.skrell {color: #00B0B3;}
.skrellfar {color: #70FCFF;}
.soghun {color: #50BA6C;}
.solcom {color: #3333CE;}
.inverted .solcom {color: #6da6f0;}
.changeling {color: #800080;font-style: italic;}
.inverted .changeling {color: #b000b1;}
.sergal {color: #0077FF;}
.birdsongc {color: #CC9900;}
.vulpkanin {color: #B97A57;}
.tavan {color: #f54298; font-family: Arial}
.echosong {color: #826D8C;}
.enochian {color: #848A33; letter-spacing:-1pt; word-spacing:4pt; font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;}
.daemon {color: #5E339E; letter-spacing:-1pt; word-spacing:0pt; font-family: "Courier New", Courier, monospace;}
.drudakar {color: #bb2463; word-spacing:0pt; font-family: "High Tower Text", monospace;}
.bug {color: #9e9e39;}
.vox {color: #AA00AA;}
.promethean {color: #5A5A5A; font-family:"Comic Sans MS","Comic Sans",cursive;}
.inverted .promethean {color: #A5A5A5; font-family:"Comic Sans MS","Comic Sans",cursive;}
.zaddat {color: #941C1C;}
.rough {font-family: "Trebuchet MS", cursive, sans-serif;}
.say_quote {font-family: Georgia, Verdana, sans-serif;}
.say_quote_italics {font-style: italic; font-family: Georgia, Verdana, sans-serif;}
.terminus {font-family: "Times New Roman", Times, serif, sans-serif}
.interface {color: #330033;}
.spacer {color: #9c660b;} /* VOREStation Add */
.blob {color: #ff950d; font-weight: bold; font-style: italic;}
.teppi {color: #816540; word-spacing:4pt; font-family: "Segoe Script Bold","Segoe Script",sans-serif,Verdana;}
.shadekin {color: #be3cc5; font-size: 150%; font-weight: bold; font-family: "Gabriola", cursive, sans-serif;}
.black {color: #000000;}
.darkgray {color: #808080;}
.gray {color: #A9A9A9;}
.red {color: #FF0000;}
.orange {color: #FF8C00;}
.blue {color: #0000FF;}
.green {color: #00DD00;}
.inverted .black {color: #606060;}
.inverted .darkgray {color: #808080;}
.inverted .gray {color: #A9A9A9;}
.inverted .red {color: #FF4444;}
.inverted .orange {color: #FF8C00;}
.inverted .blue {color: #6666FF;}
.inverted .green {color: #44FF44;}
.pnarrate {color: #009AB2;}
/*BIG IMG.icon {width: 32px; height: 32px;}*/
img.icon {vertical-align: middle; max-height: 1em;}
img.icon.bigicon {max-height: 32px;}
/* Zoom levels - Replaced with arbitrary font sizes
.zoom_normal {
font-size: 0.9em;
line-height: 1.35;
}
.zoom_more {
font-size: 1.2em;
line-height: 1.4;
}
.zoom_less {
font-size: 0.8em;
line-height: 1.2;
}
*/
/* Debug Logs */
.debug_error {color:#FF0000; font-weight:bold}
.debug_warning {color:#FF0000;}
.debug_info {color:#000000;}
.inverted .debug_info {color: #FFFFFF;}
.debug_debug {color:#0000FF;}
.debug_trace {color:#888888;}
/* Log animations */
.msgsanim-enter-active {
transition: opacity 0.5s, transform 0.5s;
}
.msgsanim-enter {
opacity: 0;
transform: translateX(15px);
}
/* Dog mode */
.woof {
background-color: rgba(255,255,255,0) !important;
background-image: url('dog.gif') !important;
background-size: cover !important;
transition: background-color 15s;
}
.woof div, .woof span {
background-color: rgba(0,0,0,0) !important;
transition: background-color 15s;
}
File diff suppressed because one or more lines are too long
-191
View File
@@ -1,191 +0,0 @@
<!DOCTYPE html>
<html debug="true">
<head>
<title>VChat</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- Styles -->
<link rel="stylesheet" type="text/css" href="semantic.min.css" />
<link rel="stylesheet" type="text/css" href="vchat-font-embedded.css" />
<link rel="stylesheet" type="text/css" href="ss13styles.css" />
<!-- Important scripts -->
<script type="text/javascript" src="polyfills.min.js"></script>
<script type="text/javascript" src="vue.min.js"></script>
<script type="text/javascript" src="vchat.min.js"></script>
</head>
<body>
<div id="loader">
<p>You probably shouldn't see this page. This generally means chat is very broken.</p>
<p>You can wait a few seconds to see if it loads, or try OOC > Reload VChat.</p>
</div>
<div id="app" @mouseup="on_mouseup" style="display: none;" :class="{ inverted: inverted }">
<!-- Top menu -->
<div id="topmenu" class="ui top fixed menu" :class="{ inverted: inverted }">
<div v-for="(tab,index) in tabs" class="item" :class="{ active: tab.active }" @click="switchtab(tab)" @click.ctrl="editmode">
<i v-show="editing && !tab.immutable && index >= 2" class="icon-left-open-outline" @click.stop.prevent="movetab(tab,-1)" title="Move tab left"></i>
{{tab.name}}
<i v-show="editing && !tab.immutable" class="icon-cancel-circled-outline" @click.stop.prevent="deltab(tab)" title="Delete tab"></i>
<i v-show="editing && !tab.immutable && index != tabs.length-1" class="icon-right-open-outline" @click.stop.prevent="movetab(tab,1)" title="Move tab right"></i>
<div v-if="tab_unread_count(tab) > 0" class="ui top right attached red label">{{tab_unread_count(tab)}}</div>
</div>
<div class="right menu">
<div class="item" @click="newtab" title="New Tab"><i class="icon-folder-add"></i></div>
<div class="item" @click="pause" title="Pause Autoscroll">
<i class="icon-pause-outline" :class="{ blinkwarn: paused }"></i>
</div>
<div class="item" @click="editmode" title="Edit Mode">
<i class="icon-cog-outline" :class="{ blinkwarn: editing }"></i>
</div>
<div class="item" title="Click to test latency">
<span class="ui circular label" :class="ping_classes" @click="do_latency_test">
<template v-if="latency">
{{latency}}ms
</template>
</span>
</div>
</div>
</div>
<!-- Editor box -->
<div id="contentbox">
<div v-show="editing" id="editbox" class="ui segment" :class="{ inverted: inverted }">
<div class="ui internally celled grid" :class="{ inverted: inverted }">
<div class="row">
<div class="sixteen wide column">
<div class="ui center aligned header" :class="{ inverted: inverted }">
<h2>VChat Settings</h2>
</div>
<div style="text-align: center;"><a href="#" @click="editmode">Close Settings</a></div>
</div>
</div>
<div class="row">
<div class="eight wide column">
<h3>'{{active_tab.name}}' Tab Settings</h3>
<span v-show="active_tab.immutable">This tab is immutable. You cannot make changes.</span>
<div v-show="!active_tab.immutable">
<a href="#" @click="renametab">Rename Tab</a> - <a href="#" @click="deltab">Delete Tab</a>
<h5>Messages to display:</h5>
<div class="ui form">
<div class="grouped fields">
<div class="field" v-for="type in type_table" :title="type.tooltip">
<div v-show="!type.admin || is_admin" class="ui slider checkbox" :class="{ inverted: inverted, disabled: type.required || active_tab.immutable, checked: type.required || active_tab.immutable }">
<input type="checkbox" id="type.becomes" :value="type.becomes" v-model="active_tab.categories" :disabled="type.required || active_tab.immutable" :checked="type.required || active_tab.immutable"><label :for="type.becomes">{{type.pretty}}</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="eight wide column">
<h3>Global Settings</h3>
<div class="ui form" :class="{ inverted: inverted }">
<div class="grouped fields">
<div class="field">
<div class="ui slider checkbox" :class="{ inverted: inverted }">
<input type="checkbox" id="darkmode" v-model="inverted"><label for="darkmode">Dark Mode</label>
</div>
</div>
<div class="field">
<div class="ui slider checkbox" :class="{ inverted: inverted }">
<input type="checkbox" id="combining" v-model="animated"><label for="animated">Animate Messages</label>
</div>
</div>
</div>
<div class="inline fields">
<label>Combine Messages</label>
<div class="field" title="Combine no messages together.">
<div class="ui radio checkbox">
<input type="radio" id="cnone" name="crushing" v-model.number="crushing" value="0">
<label for="cnone">None</label>
</div>
</div>
<div class="field" title="Try to combine the newest and last message.">
<div class="ui radio checkbox">
<input type="radio" id="cone" name="crushing" v-model.number="crushing" value="1">
<label for="cone">One</label>
</div>
</div>
<div class="field" title="Try to combine the newest and last 3 messages.">
<div class="ui radio checkbox">
<input type="radio" id="cthree" name="crushing" v-model.number="crushing" value="3">
<label for="cthree">Three</label>
</div>
</div>
</div>
<div class="inline fields">
<div class="field" title="Font size (Min 0.2, Max 5, Default 0.9)">
<label>Font Scale</label>
<input class="inputbox" type="number" name="fontsize" placeholder="0.9" v-model.lazy.number="fontsize" required @keyup.enter="blur_this($event)">
</div>
</div>
<div class="inline fields">
<div class="field" title="Line height % (Min 100, Max 200, Default 130)">
<label>Line Height %</label>
<input class="inputbox" type="number" name="lineheight" placeholder="130" v-model.lazy.number="lineheight" required @keyup.enter="blur_this($event)">
</div>
</div>
<div class="inline fields">
<div class="field" title="Hides messages for performance (Min 50, Max 2000, Default 200)">
<label># Stored Messages</label>
<input class="inputbox" type="number" name="showingnum" placeholder="200" v-model.lazy.number="showingnum" required @keyup.enter="blur_this($event)">
</div>
</div>
</div>
<button class="ui button" @click="save_chatlog">Export Chatlog</button>
</div>
</div>
</div>
</div>
<!-- Messages box -->
<div v-show="!editing" id="messagebox" class="ui segment" @click="click_message" :class="{ inverted: inverted }" :style="{fontSize: fontsize+'em', lineHeight: lineheight+'%'}">
<transition-group name="msgsanim" tag="span" :css="animated">
<!-- We're filtering -->
<template v-if="current_categories.length">
<div v-show="unshown_messages > 0" key="hidewarn1"><span class='notice'>[ {{unshown_messages}} previous messages archived for performance. ]</span></div>
<div v-for="message in shown_messages" :key="message.id">
<span v-html="message.content"></span>
<span v-show="message.repeats > 1" class="ui grey circular label">x{{message.repeats}}</span>
</div>
</template>
<!-- We're not filtering, messages go directly to dom -->
<template v-else>
<div v-show="archived_messages.length > 0" key="hidewarn2"><span class='notice'>[ {{archived_messages.length}} previous messages archived for performance. ]</span></div>
<div v-for="message in messages" :key="message.id">
<span v-html="message.content"></span>
<span v-show="message.repeats > 1" class="ui grey circular label">x{{message.repeats}}</span>
</div>
</template>
</transition-group>
</div>
</div>
<!--
<div class="ui segment">
active_tab: {{active_tab}}<br>
current_categories: {{current_categories}}<br>
messages.length: {{messages.length}}<br>
archived_messages.length: {{archived_messages.length}}<br>
shown_messages.length: {{shown_messages.length}}<br>
</div>
-->
</div>
<script type="text/javascript">
//Document Ready
(function(){
start_vchat();
document.getElementById("loader").style.display = 'none';
document.getElementById("app").style.display = 'block';
})();
</script>
</body>
</html>
-78
View File
@@ -1,78 +0,0 @@
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return kValue.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
// e. Increase k by 1.
k++;
}
// 7. Return undefined.
return undefined;
},
configurable: true,
writable: true
});
}
if (!Date.now) {
Date.now = function now() {
return new Date().getTime();
};
}
//IE ugh
function storageAvailable(type) {
var storage;
try {
storage = window[type];
var x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch(e) {
return e instanceof DOMException && (
// everything except Firefox
e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === 'QuotaExceededError' ||
// Firefox
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
// acknowledge QuotaExceededError only if there's something already stored
(storage && storage.length !== 0);
}
}
-1
View File
@@ -1 +0,0 @@
function storageAvailable(e){var t;try{t=window[e];var r="__storage_test__";return t.setItem(r,r),t.removeItem(r),!0}catch(e){return e instanceof DOMException&&(22===e.code||1014===e.code||"QuotaExceededError"===e.name||"NS_ERROR_DOM_QUOTA_REACHED"===e.name)&&t&&0!==t.length}}Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(e){if(null==this)throw TypeError('"this" is null or not defined');var t=Object(this),r=t.length>>>0;if("function"!=typeof e)throw TypeError("predicate must be a function");for(var n=arguments[1],o=0;o<r;){var a=t[o];if(e.call(n,a,o,t))return a;o++}},configurable:!0,writable:!0}),Date.now||(Date.now=function(){return(new Date).getTime()});
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-428
View File
@@ -1,428 +0,0 @@
//The 'V' is for 'VORE' but you can pretend it's for Vue.js if you really want.
//These are sent to the client via browse_rsc() in advance so the HTML can access them.
GLOBAL_LIST_INIT(vchatFiles, list(
"code/modules/vchat/css/vchat-font-embedded.css",
"code/modules/vchat/css/semantic.min.css",
"code/modules/vchat/css/ss13styles.css",
"code/modules/vchat/js/polyfills.min.js",
"code/modules/vchat/js/vue.min.js",
"code/modules/vchat/js/vchat.min.js"
))
// The to_chat() macro calls this proc
/proc/__to_chat(var/target, var/message)
// First do logging in database
if(isclient(target))
var/client/C = target
vchat_add_message(C.ckey, message)
else if(ismob(target))
var/mob/M = target
if(M.ckey)
vchat_add_message(M.ckey, message)
else if(target == world)
for(var/client/C in GLOB.clients)
if(!QDESTROYING(C)) // Might be necessary?
vchat_add_message(C.ckey, message)
// Now lets either queue it for sending, or send it right now
if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.subsystem_initialized)
to_chat_immediate(target, world.time, message)
else
SSchat.queue(target, world.time, message)
//This is used to convert icons to base64 <image> strings, because byond stores icons in base64 in savefiles.
GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of icons for the browser output
//The main object attached to clients, created when they connect, and has start() called on it in client/New()
/datum/chatOutput
var/client/owner = null
var/loaded = FALSE
var/list/message_queue = list()
var/broken = FALSE
var/resources_sent = FALSE
var/message_buffer = 200 // Number of messages being actively shown to the user, used to play back that many messages on reconnect
var/last_topic_time = 0
var/too_many_topics = 0
var/topic_spam_limit = 10 //Just enough to get over the startup and such
/datum/chatOutput/New(client/C)
. = ..()
owner = C
/datum/chatOutput/Destroy()
owner = null
. = ..()
/datum/chatOutput/proc/update_vis()
if(!loaded && !broken)
winset(owner, null, "outputwindow.htmloutput.is-visible=false;outputwindow.oldoutput.is-visible=false;outputwindow.chatloadlabel.is-visible=true")
else if(broken)
winset(owner, null, "outputwindow.htmloutput.is-visible=false;outputwindow.oldoutput.is-visible=true;outputwindow.chatloadlabel.is-visible=false")
else if(loaded)
return //It can do it's own winsets from inside the JS if it's working.
//Shove all the assets at them
/datum/chatOutput/proc/send_resources()
for(var/filename in GLOB.vchatFiles)
owner << browse_rsc(file(filename))
resources_sent = TRUE
//Called from client/New() in a spawn()
/datum/chatOutput/proc/start()
if(!owner)
qdel(src)
return FALSE
if(!winexists(owner, "htmloutput"))
tgui_alert_async(owner, "Updated chat window does not exist. If you are using a custom skin file please allow the game to update.")
become_broken()
return FALSE
if(!owner?.is_preference_enabled(/datum/client_preference/vchat_enable))
become_broken()
return FALSE
//Could be loaded from a previous round, are you still there?
if(winget(owner,"outputwindow.htmloutput","is-visible") == "true") //Winget returns strings
send_event(event = list("evttype" = "availability"))
sleep(3 SECONDS)
if(!owner) // In case the client vanishes before winexists returns
qdel(src)
return FALSE
if(!loaded)
update_vis()
if(!resources_sent)
send_resources()
load()
return TRUE
//Attempts to actually load the HTML page into the client's UI
/datum/chatOutput/proc/load()
if(!owner)
qdel(src)
return
owner << browse(file2text("code/modules/vchat/html/vchat.html"), "window=htmloutput")
//Check back later
spawn(15 SECONDS)
if(!src)
return
if(!src.loaded)
src.become_broken()
//var/list/joins = list() //Just for testing with the below
//Called by Topic, when the JS in the HTML page finishes loading
/datum/chatOutput/proc/done_loading()
if(loaded)
return
loaded = TRUE
broken = FALSE
owner.chatOutputLoadedAt = world.time
//update_vis() //It does it's own winsets
ping_cycle()
send_playerinfo()
load_database()
owner.verbs += /client/proc/vchat_export_log
//Perform DB shenanigans
/datum/chatOutput/proc/load_database()
set waitfor = FALSE
// Only send them the number of buffered messages, instead of the ENTIRE log
var/list/results = vchat_get_messages(owner.ckey, message_buffer) //If there's bad performance on reconnects, look no further
if(islist(results))
for(var/i in results.len to 1 step -1)
var/list/message = results[i]
var/count = 10
to_chat_immediate(owner, message["time"], message["message"])
count++
if(count >= 10)
count = 0
CHECK_TICK
//It din work
/datum/chatOutput/proc/become_broken()
broken = TRUE
loaded = FALSE
if(!owner)
qdel(src)
return
update_vis()
spawn()
if(owner.is_preference_enabled(/datum/client_preference/vchat_enable))
tgui_alert_async(owner,"VChat didn't load after some time. Switching to use oldchat as a fallback. Try using 'Reload VChat' verb in OOC verbs, or reconnecting to try again.")
//Provide the JS with who we are
/datum/chatOutput/proc/send_playerinfo()
if(!owner)
qdel(src)
return
var/list/playerinfo = list("evttype" = "byond_player", "cid" = owner.computer_id, "ckey" = owner.ckey, "address" = owner.address, "admin" = owner.holder ? "true" : "false")
send_event(playerinfo)
//Ugh byond doesn't handle UTF-8 well so we have to do this.
/proc/jsEncode(var/list/message) {
if(!islist(message))
CRASH("Passed a non-list to encode.")
return url_encode(url_encode(json_encode(message)))
}
//Send a side-channel event to the chat window
/datum/chatOutput/proc/send_event(var/event, var/client/C = owner)
C << output(jsEncode(event), "htmloutput:get_event")
//Looping sleeping proc that just pings the client and dies when we die
/datum/chatOutput/proc/ping_cycle()
set waitfor = FALSE
while(!QDELING(src))
if(!owner)
qdel(src)
return
send_event(event = keep_alive())
sleep(20 SECONDS) //Make sure this makes sense with what the js client is expecting
//Just produces a message for using in keepalives from the server to the client
/datum/chatOutput/proc/keep_alive()
return list("evttype" = "keepalive")
//A response to a latency check from the client
/datum/chatOutput/proc/latency_check()
return list("evttype" = "pong")
//Redirected from client/Topic when the user clicks a link that pertains directly to the chat (when src == "chat")
/datum/chatOutput/Topic(var/href, var/list/href_list)
if(usr.client != owner)
return 1
if(last_topic_time > (world.time - 3 SECONDS))
too_many_topics++
if(too_many_topics >= topic_spam_limit)
log_and_message_admins("Kicking [key_name(owner)] - VChat Topic() spam")
to_chat(owner,"<span class='danger'>You have been kicked due to VChat sending too many messages to the server. Try reconnecting.</span>")
qdel(owner)
qdel(src)
return
else
too_many_topics = 0
last_topic_time = world.time
var/list/params = list()
for(var/key in href_list)
if(length(key) > 7 && findtext(key, "param"))
var/param_name = copytext(key, 7, -1)
var/item = href_list[key]
params[param_name] = item
var/data
switch(href_list["proc"])
if("not_ready")
CRASH("Tried to send a message to [owner.ckey] chatOutput before it was ready!")
if("done_loading")
data = done_loading(arglist(params))
if("ping")
data = latency_check(arglist(params))
if("ident")
data = bancheck(arglist(params))
if("unloading")
loaded = FALSE
if("debug")
data = debugmsg(arglist(params))
if(href_list["showingnum"])
message_buffer = CLAMP(text2num(href_list["showingnum"]), 50, 2000)
if(data)
send_event(event = data)
//Print a message that was an error from a client
/datum/chatOutput/proc/debugmsg(var/message = "No String Provided")
log_debug("VChat: [owner] got: [message]")
//Check relevant client info reported from JS
/datum/chatOutput/proc/bancheck(var/clientdata)
var/list/info = json_decode(clientdata)
var/ckey = info["ckey"]
var/ip = info["ip"]
var/cid = info["cid"]
//Never connected? How sad!
if(!cid && !ip && !ckey)
return
if(cid && !isnum(cid) && !(cid == ""))
log_and_message_admins("[key_name(owner)] - bancheck with invalid cid! ([cid])")
if(ip && !findtext(ip, new/regex(@"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$")) && !(ip == ""))
log_and_message_admins("[key_name(owner)] - bancheck with invalid ip! ([ip])")
var/list/ban = world.IsBanned(key = ckey, address = ip, computer_id = cid)
if(ban)
log_and_message_admins("[key_name(owner)] has a cookie from a banned account! (Cookie: [ckey], [ip], [cid])")
//Converts an icon to base64. Operates by putting the icon in the iconCache savefile,
// exporting it as text, and then parsing the base64 from that.
// (This relies on byond automatically storing icons in savefiles as base64)
/proc/icon2base64(var/icon/icon, var/iconKey = "misc")
if (!isicon(icon)) return FALSE
GLOB.iconCache[iconKey] << icon
var/iconData = GLOB.iconCache.ExportText(iconKey)
var/list/partial = splittext(iconData, "{")
return replacetext(copytext(partial[2], 3, -5), "\n", "")
/proc/expire_bicon_cache(key)
if(GLOB.bicon_cache[key])
GLOB.bicon_cache -= key
return TRUE
return FALSE
GLOBAL_LIST_EMPTY(bicon_cache) // Cache of the <img> tag results, not the icons
/proc/bicon(var/obj, var/use_class = 1, var/custom_classes = "")
var/class = use_class ? "class='icon misc [custom_classes]'" : null
if(!obj)
return
// Try to avoid passing bicon an /icon directly. It is better to pass it an atom so it can cache.
if(isicon(obj)) // Passed an icon directly, nothing to cache-key on, as icon refs get reused *often*
return "<img [class] src='data:image/png;base64,[icon2base64(obj)]'>"
// Either an atom or somebody fucked up and is gonna get a runtime, which I'm fine with.
var/atom/A = obj
var/key
var/changes_often = ishuman(A) || isobserver(A) // If this ends up with more, move it into a proc or var on atom.
if(changes_often)
key = "\ref[A]"
else
key = "[istype(A.icon, /icon) ? "\ref[A.icon]" : A.icon]:[A.icon_state]"
var/base64 = GLOB.bicon_cache[key]
// Non-human atom, no cache
if(!base64) // Doesn't exist, make it.
base64 = icon2base64(A.examine_icon(), key)
GLOB.bicon_cache[key] = base64
if(changes_often)
addtimer(CALLBACK(GLOBAL_PROC, PROC_REF(expire_bicon_cache), key), 50 SECONDS, TIMER_UNIQUE)
// May add a class to the img tag created by bicon
if(use_class)
class = "class='icon [A.icon_state] [custom_classes]'"
return "<IMG [class] src='data:image/png;base64,[base64]'>"
//Checks if the message content is a valid to_chat message
/proc/is_valid_tochat_message(message)
return istext(message)
//Checks if the target of to_chat is something we can send to
/proc/is_valid_tochat_target(target)
return !istype(target, /savefile) && (ismob(target) || islist(target) || isclient(target) || target == world)
var/to_chat_filename
var/to_chat_line
var/to_chat_src
//This proc is only really used if the SSchat subsystem is unavailable (not started yet)
/proc/to_chat_immediate(target, time, message)
if(!is_valid_tochat_message(message) || !is_valid_tochat_target(target))
target << message
// Info about the "message"
if(isnull(message))
message = "(null)"
else if(istype(message, /datum))
var/datum/D = message
message = "([D.type]): '[D]'"
else if(!is_valid_tochat_message(message))
message = "(bad message) : '[message]'"
// Info about the target
var/targetstring = "'[target]'"
if(istype(target, /datum))
var/datum/D = target
targetstring += ", [D.type]"
// The final output
log_debug("to_chat called with invalid message/target: [to_chat_filename], [to_chat_line], [to_chat_src], Message: '[message]', Target: [targetstring]")
return
else if(is_valid_tochat_message(message))
if(istext(target))
log_debug("Somehow, to_chat got a text as a target")
return
var/original_message = message
message = replacetext(message, "\n", "<br>")
message = replacetext(message, "\improper", "")
message = replacetext(message, "\proper", "")
if(isnull(time))
time = world.time
var/client/C = CLIENT_FROM_VAR(target)
if(!C)
return // No client? No care.
else if(C.chatOutput.broken)
DIRECT_OUTPUT(C, original_message)
return
else if(!C.chatOutput.loaded)
return // If not loaded yet, do nothing and history-sending on load will get it.
var/list/tojson = list("time" = time, "message" = message);
target << output(jsEncode(tojson), "htmloutput:putmessage")
/client/proc/vchat_export_log()
set name = "Export chatlog"
set category = "OOC"
if(chatOutput.broken)
to_chat(src, "<span class='warning'>Error: VChat isn't processing your messages!</span>")
return
var/list/results = vchat_get_messages(ckey)
if(!LAZYLEN(results))
to_chat(src, "<span class='warning'>Error: No messages found! Please inform a dev if you do have messages!</span>")
return
var/o_file = "data/chatlog_tmp/[ckey]_chat_log"
if(fexists(o_file) && !fdel(o_file))
to_chat(src, "<span class='warning'>Error: Your chat log is already being prepared. Please wait until it's been downloaded before trying to export it again.</span>")
return
// Write the CSS file to the log
var/text_blob = "<html><head><style>"
text_blob += file2text(file("code/modules/vchat/css/ss13styles.css"))
text_blob += "</style></head><body>"
// Write the messages to the log
for(var/list/result in results)
text_blob += "[result["message"]]<br>"
CHECK_TICK
text_blob += "</body></html>"
rustg_file_write(text_blob, o_file)
// Send the log to the client
src << ftp(file(o_file), "log_[time2text(world.timeofday, "YYYY_MM_DD_(hh_mm)")].html")
// clean up the file on our end
spawn(10 SECONDS)
if(!fdel(o_file))
spawn(1 MINUTE)
if(!fdel(o_file))
log_debug("Warning: [ckey]'s chatlog could not be deleted one minute after file transfer was initiated. It is located at 'data/chatlog_tmp/[ckey]_chat_log' and will need to be manually removed.")
-124
View File
@@ -1,124 +0,0 @@
#define VCHAT_FILENAME "data/vchat.db"
GLOBAL_DATUM(vchatdb, /database)
//Boot up db file
/proc/init_vchat()
//Cleanup previous if exists
fdel(VCHAT_FILENAME)
fdel(VCHAT_FILENAME+"-shm") //Shared memory, and
fdel(VCHAT_FILENAME+"-wal") // write ahead. Only present on unclean stop.
//Create a new one
GLOB.vchatdb = new(VCHAT_FILENAME)
//Build our basic boring tables
vchat_create_tables()
//Check to see if it's init
/proc/check_vchat()
if(istype(GLOB.vchatdb))
return TRUE
else
return FALSE
//For INSERT/CREATE/DELETE, etc that return a RowsAffected.
/proc/vchat_exec_update(var/query)
if(!check_vchat())
log_world("There's no vchat database open but you tried to query it with: [query]")
return FALSE
//Solidify our query
var/database/query/q = vchat_build_query(query)
//Run it
q.Execute(GLOB.vchatdb)
//Handle errors
if(q.Error())
log_world("Query \"[islist(query)?query[1]:query]\" ended in error [q.ErrorMsg()]")
return FALSE
return q.RowsAffected()
//For SELECT, that return results.
/proc/vchat_exec_query(var/query)
if(!check_vchat())
log_world("There's no vchat database open but you tried to query it!")
return FALSE
//Solidify our query
var/database/query/q = vchat_build_query(query)
//Run it
q.Execute(GLOB.vchatdb)
//Handle errors
if(q.Error())
log_world("Query \"[islist(query)?query[1]:query]\" ended in error [q.ErrorMsg()]")
return FALSE
//Return any results
var/list/results = list()
//Return results if any.
while(q.NextRow())
results[++results.len] = q.GetRowData()
return results
//Create a query from string or list with params
/proc/vchat_build_query(var/query)
var/database/query/q
if(islist(query))
q = new(arglist(query))
else
q = new(query)
if(!istype(q))
return
return q
/proc/vchat_create_tables()
//Byond is so great half the time it doesn't delete the file
var/cleanup = "DROP TABLE IF EXISTS messages"
vchat_exec_update(cleanup)
//Messages table
var/tabledef = "CREATE TABLE messages(\
id INTEGER PRIMARY KEY AUTOINCREMENT,\
ckey VARCHAR(50) NOT NULL,\
worldtime INTEGER NOT NULL,\
message TEXT NOT NULL)"
vchat_exec_update(tabledef)
//Index on ckey
var/indexdef = "CREATE INDEX ckey_index ON messages (ckey)"
vchat_exec_update(indexdef)
//INSERT a new message
/proc/vchat_add_message(var/ckey, var/message)
if(!ckey || !message)
return
var/list/messagedef = list(
"INSERT INTO messages (ckey,worldtime,message) VALUES (?, ?, ?)",
ckey,
world.time || 0,
message)
return vchat_exec_update(messagedef)
//Get a player's message history. If limit is supplied, messages will be in reverse order.
/proc/vchat_get_messages(var/ckey, var/limit)
if(!ckey)
return
var/list/getdef
if (limit)
getdef = list("SELECT * FROM messages WHERE ckey = ? ORDER BY id DESC LIMIT [text2num(limit)]", ckey)
else
getdef = list("SELECT * FROM messages WHERE ckey = ?", ckey)
return vchat_exec_query(getdef)
#undef VCHAT_FILENAME