mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-27 22:18:27 +01:00
Replaces vchat with tgchat/tg-panel
This commit is contained in:
@@ -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>
|
||||
-404
File diff suppressed because one or more lines are too long
-110
File diff suppressed because one or more lines are too long
@@ -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
@@ -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>
|
||||
@@ -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
@@ -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
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
-11
File diff suppressed because one or more lines are too long
@@ -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.")
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user