Merge branch 'computernewb:master' into master
This commit is contained in:
commit
f4927e210f
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
.parcel-cache/
|
.parcel-cache/
|
||||||
|
config.json
|
||||||
18
Config.ts
18
Config.ts
|
|
@ -1,18 +0,0 @@
|
||||||
export const Config = {
|
|
||||||
ChatSound: "//computernewb.com/collab-vm/notify.ogg",
|
|
||||||
ServerAddresses: [
|
|
||||||
"wss://computernewb.com/collab-vm/vm0",
|
|
||||||
"wss://computernewb.com/collab-vm/vm1",
|
|
||||||
"wss://computernewb.com/collab-vm/vm2",
|
|
||||||
"wss://computernewb.com/collab-vm/vm3",
|
|
||||||
"wss://computernewb.com/collab-vm/vm4",
|
|
||||||
"wss://computernewb.com/collab-vm/vm5",
|
|
||||||
"wss://computernewb.com/collab-vm/vm6",
|
|
||||||
"wss://computernewb.com/collab-vm/vm7",
|
|
||||||
"wss://computernewb.com/collab-vm/vm8",
|
|
||||||
],
|
|
||||||
Auth: {
|
|
||||||
Enabled: false,
|
|
||||||
APIEndpoint: "http://127.0.0.1:5858"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,7 @@ The CollabVM Web App is the viewer for the CollabVM Server.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Edit Config.ts to your needs, then:
|
Copy config.example.json to config.json and edit to your needs, then:
|
||||||
|
|
||||||
## yarn
|
## yarn
|
||||||
- `yarn`
|
- `yarn`
|
||||||
|
|
|
||||||
28
config.example.json
Normal file
28
config.example.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"SiteNameOverride": null,
|
||||||
|
"WelcomeModalTitleOverride": null,
|
||||||
|
"WelcomeModalBodyOverride": null,
|
||||||
|
"WelcomeModalLocalStorageKey": "no-welcome-modal",
|
||||||
|
"ChatSound": "//computernewb.com/collab-vm/notify.ogg",
|
||||||
|
"ServerAddresses": [
|
||||||
|
"wss://computernewb.com/collab-vm/vm0",
|
||||||
|
"wss://computernewb.com/collab-vm/vm1",
|
||||||
|
"wss://computernewb.com/collab-vm/vm2",
|
||||||
|
"wss://computernewb.com/collab-vm/vm3",
|
||||||
|
"wss://computernewb.com/collab-vm/vm4",
|
||||||
|
"wss://computernewb.com/collab-vm/vm5",
|
||||||
|
"wss://computernewb.com/collab-vm/vm6",
|
||||||
|
"wss://computernewb.com/collab-vm/vm7",
|
||||||
|
"wss://computernewb.com/collab-vm/vm8"
|
||||||
|
],
|
||||||
|
"ServerAddressesListURI": null,
|
||||||
|
"NSFWVMs": ["vm0b0t"],
|
||||||
|
"RawMessages": {
|
||||||
|
"VMTitles": true,
|
||||||
|
"Messages": true
|
||||||
|
},
|
||||||
|
"Auth": {
|
||||||
|
"Enabled": false,
|
||||||
|
"APIEndpoint": "http://127.0.0.1:5858"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,12 +17,14 @@
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"bootstrap": "^5.3.2",
|
"bootstrap": "^5.3.2",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
"dompurify": "^3.1.0",
|
||||||
"nanoevents": "^7.0.1",
|
"nanoevents": "^7.0.1",
|
||||||
"simple-keyboard": "^3.7.53"
|
"simple-keyboard": "^3.7.53"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hcaptcha/types": "^1.0.3",
|
"@hcaptcha/types": "^1.0.3",
|
||||||
"@types/bootstrap": "^5.2.10",
|
"@types/bootstrap": "^5.2.10",
|
||||||
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"parcel": "^2.11.0",
|
"parcel": "^2.11.0",
|
||||||
|
|
|
||||||
BIN
src/assets/NotoColorEmoji.ttf
Normal file
BIN
src/assets/NotoColorEmoji.ttf
Normal file
Binary file not shown.
|
|
@ -1,5 +1,7 @@
|
||||||
#vmview {
|
#vmview {
|
||||||
display: none;
|
display: none;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
}
|
}
|
||||||
/*.vmtile {
|
/*.vmtile {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
@ -13,8 +15,6 @@
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}*/
|
}*/
|
||||||
#vmDisplay, #btns {
|
#vmDisplay, #btns {
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
@ -156,8 +156,9 @@ tr.user-waiting > td {
|
||||||
/* Start OSK */
|
/* Start OSK */
|
||||||
.osk-container {
|
.osk-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 1024px;
|
max-width: 1024px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
@ -168,8 +169,7 @@ tr.user-waiting > td {
|
||||||
}
|
}
|
||||||
|
|
||||||
.osk-main.simple-keyboard {
|
.osk-main.simple-keyboard {
|
||||||
width: 640px;
|
max-width: 640px;
|
||||||
min-width: 640px;
|
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,6 +279,12 @@ tr.user-waiting > td {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
.hg-button:not(:last-child) {
|
||||||
|
margin-right: 1px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Theme: cvmDark
|
Theme: cvmDark
|
||||||
*/
|
*/
|
||||||
|
|
@ -314,13 +320,23 @@ Theme: cvmDisabled
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* VM0 Blur */
|
/* NSFW Blur */
|
||||||
div[data-cvm-node=vm0b0t] {
|
.cvm-nsfw {
|
||||||
img {
|
img {
|
||||||
filter:blur(40px)!important;
|
filter:blur(40px)!important;
|
||||||
}
|
}
|
||||||
|
h5::before {
|
||||||
|
content: "[NSFW] ";
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#accountDropdownMenuLink, #accountModalError, #accountModalSuccess {
|
#accountDropdownMenuLink, #accountModalError, #accountModalSuccess {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Emoji font for systems without one */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Noto Color Emoji';
|
||||||
|
src: url('../assets/NotoColorEmoji.ttf');
|
||||||
|
}
|
||||||
|
|
@ -230,7 +230,7 @@
|
||||||
<div class="container-fluid" id="vmlist">
|
<div class="container-fluid" id="vmlist">
|
||||||
<div class="row"></div>
|
<div class="row"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container-fluid" id="vmview">
|
<div id="vmview">
|
||||||
<div id="vmDisplay"></div>
|
<div id="vmDisplay"></div>
|
||||||
<p id="turnstatus"></p>
|
<p id="turnstatus"></p>
|
||||||
<div id="voteResetPanel" style="display:none;">
|
<div id="voteResetPanel" style="display:none;">
|
||||||
|
|
@ -272,7 +272,7 @@
|
||||||
<div class="osk-numpadEnd"></div>
|
<div class="osk-numpadEnd"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row container-fluid">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="table-responsive username-table">
|
<div class="table-responsive username-table">
|
||||||
<table class="table table-hover table-borderless">
|
<table class="table table-hover table-borderless">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { StringLike } from './StringLike';
|
import { StringLike } from './StringLike';
|
||||||
import { Format } from './format';
|
import { Format } from './format';
|
||||||
import { Emitter, Unsubscribe, createNanoEvents } from 'nanoevents';
|
import { Emitter, Unsubscribe, createNanoEvents } from 'nanoevents';
|
||||||
|
import Config from '../../config.json';
|
||||||
|
|
||||||
/// All string keys.
|
/// All string keys.
|
||||||
export enum I18nStringKey {
|
export enum I18nStringKey {
|
||||||
|
|
@ -411,6 +412,9 @@ export class I18n {
|
||||||
|
|
||||||
// Returns a (raw, unformatted) string. Currently only used if we don't need formatting.
|
// Returns a (raw, unformatted) string. Currently only used if we don't need formatting.
|
||||||
GetStringRaw(key: I18nStringKey): string {
|
GetStringRaw(key: I18nStringKey): string {
|
||||||
|
if (key === I18nStringKey.kGeneric_CollabVM && Config.SiteNameOverride) return Config.SiteNameOverride;
|
||||||
|
if (key === I18nStringKey.kWelcomeModal_Header && Config.WelcomeModalTitleOverride) return Config.WelcomeModalTitleOverride;
|
||||||
|
if (key === I18nStringKey.kWelcomeModal_Body && Config.WelcomeModalBodyOverride) return Config.WelcomeModalBodyOverride;
|
||||||
let val = this.lang.stringKeys[key];
|
let val = this.lang.stringKeys[key];
|
||||||
|
|
||||||
// Look up the fallback language by default if the language doesn't
|
// Look up the fallback language by default if the language doesn't
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import CollabVMClient from './protocol/CollabVMClient.js';
|
import CollabVMClient from './protocol/CollabVMClient.js';
|
||||||
import VM from './protocol/VM.js';
|
import VM from './protocol/VM.js';
|
||||||
import { Config } from '../../Config.js';
|
import Config from '../../config.json';
|
||||||
import { Permissions, Rank } from './protocol/Permissions.js';
|
import { Permissions, Rank } from './protocol/Permissions.js';
|
||||||
import { User } from './protocol/User.js';
|
import { User } from './protocol/User.js';
|
||||||
import TurnStatus from './protocol/TurnStatus.js';
|
import TurnStatus from './protocol/TurnStatus.js';
|
||||||
|
|
@ -15,6 +15,7 @@ import { I18nStringKey, TheI18n } from './i18n.js';
|
||||||
import { Format } from './format.js';
|
import { Format } from './format.js';
|
||||||
import AuthManager from './AuthManager.js';
|
import AuthManager from './AuthManager.js';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import * as dompurify from 'dompurify';
|
||||||
|
|
||||||
// Elements
|
// Elements
|
||||||
const w = window as any;
|
const w = window as any;
|
||||||
|
|
@ -350,6 +351,7 @@ async function multicollab(url: string) {
|
||||||
div.classList.add('col-sm-5', 'col-md-3');
|
div.classList.add('col-sm-5', 'col-md-3');
|
||||||
let card = document.createElement('div');
|
let card = document.createElement('div');
|
||||||
card.classList.add('card');
|
card.classList.add('card');
|
||||||
|
if (Config.NSFWVMs.indexOf(vm.id) !== -1) card.classList.add('cvm-nsfw');
|
||||||
card.setAttribute('data-cvm-node', vm.id);
|
card.setAttribute('data-cvm-node', vm.id);
|
||||||
card.addEventListener('click', async () => {
|
card.addEventListener('click', async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -362,7 +364,7 @@ async function multicollab(url: string) {
|
||||||
let cardBody = document.createElement('div');
|
let cardBody = document.createElement('div');
|
||||||
cardBody.classList.add('card-body');
|
cardBody.classList.add('card-body');
|
||||||
let cardTitle = document.createElement('h5');
|
let cardTitle = document.createElement('h5');
|
||||||
cardTitle.innerHTML = vm.displayName;
|
cardTitle.innerHTML = Config.RawMessages.VMTitles ? vm.displayName : dompurify.sanitize(vm.displayName);
|
||||||
let usersOnline = document.createElement('span');
|
let usersOnline = document.createElement('span');
|
||||||
usersOnline.innerHTML = `(<i class="fa-solid fa-users"></i> ${online})`;
|
usersOnline.innerHTML = `(<i class="fa-solid fa-users"></i> ${online})`;
|
||||||
cardBody.appendChild(cardTitle);
|
cardBody.appendChild(cardTitle);
|
||||||
|
|
@ -501,8 +503,9 @@ function closeVM() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadList() {
|
async function loadList() {
|
||||||
|
var jsonVMs = Config.ServerAddressesListURI === null ? [] : await (await fetch(Config.ServerAddressesListURI)).json();
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Config.ServerAddresses.map((url) => {
|
[Config.ServerAddresses, jsonVMs].flat().map((url) => {
|
||||||
return multicollab(url);
|
return multicollab(url);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -543,6 +546,7 @@ function sortUserList() {
|
||||||
function chatMessage(username: string, message: string) {
|
function chatMessage(username: string, message: string) {
|
||||||
let tr = document.createElement('tr');
|
let tr = document.createElement('tr');
|
||||||
let td = document.createElement('td');
|
let td = document.createElement('td');
|
||||||
|
if (!Config.RawMessages.Messages) message = dompurify.sanitize(message);
|
||||||
// System message
|
// System message
|
||||||
if (username === '') td.innerHTML = message;
|
if (username === '') td.innerHTML = message;
|
||||||
else {
|
else {
|
||||||
|
|
@ -573,7 +577,7 @@ function chatMessage(username: string, message: string) {
|
||||||
tr.classList.add(msgclass);
|
tr.classList.add(msgclass);
|
||||||
td.innerHTML = `<b class="${userclass}">${username}▸</b> ${message}`;
|
td.innerHTML = `<b class="${userclass}">${username}▸</b> ${message}`;
|
||||||
// hacky way to allow scripts
|
// hacky way to allow scripts
|
||||||
Array.prototype.slice.call(td.children).forEach((curr) => {
|
if (Config.RawMessages.Messages) Array.prototype.slice.call(td.children).forEach((curr) => {
|
||||||
if (curr.nodeName === 'SCRIPT') {
|
if (curr.nodeName === 'SCRIPT') {
|
||||||
eval(curr.text);
|
eval(curr.text);
|
||||||
}
|
}
|
||||||
|
|
@ -1292,15 +1296,15 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
document.title = TheI18n.GetString(I18nStringKey.kGeneric_CollabVM);
|
document.title = TheI18n.GetString(I18nStringKey.kGeneric_CollabVM);
|
||||||
|
|
||||||
// Load all VMs
|
// Load all VMs
|
||||||
await loadList();
|
loadList();
|
||||||
|
|
||||||
// Welcome modal
|
// Welcome modal
|
||||||
let welcomeModal = new bootstrap.Modal(document.getElementById('welcomeModal') as HTMLDivElement);
|
let welcomeModal = new bootstrap.Modal(document.getElementById('welcomeModal') as HTMLDivElement);
|
||||||
let noWelcomeModal = window.localStorage.getItem('no-welcome-modal');
|
let noWelcomeModal = window.localStorage.getItem(Config.WelcomeModalLocalStorageKey);
|
||||||
if (noWelcomeModal !== '1') {
|
if (noWelcomeModal !== '1') {
|
||||||
let welcomeModalDismissBtn = document.getElementById('welcomeModalDismiss') as HTMLButtonElement;
|
let welcomeModalDismissBtn = document.getElementById('welcomeModalDismiss') as HTMLButtonElement;
|
||||||
welcomeModalDismissBtn.addEventListener('click', () => {
|
welcomeModalDismissBtn.addEventListener('click', () => {
|
||||||
window.localStorage.setItem('no-welcome-modal', '1');
|
window.localStorage.setItem(Config.WelcomeModalLocalStorageKey, '1');
|
||||||
});
|
});
|
||||||
welcomeModalDismissBtn.disabled = true;
|
welcomeModalDismissBtn.disabled = true;
|
||||||
welcomeModal.show();
|
welcomeModal.show();
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,11 @@ export default class CollabVMClient {
|
||||||
// Fields
|
// Fields
|
||||||
private socket: WebSocket;
|
private socket: WebSocket;
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
|
// A secondary canvas that is not scaled
|
||||||
|
unscaledCanvas: HTMLCanvasElement;
|
||||||
|
canvasScale : { width : number, height : number } = { width: 0, height: 0 };
|
||||||
|
actualScreenSize : { width : number, height : number } = { width: 0, height: 0 };
|
||||||
|
private unscaledCtx: CanvasRenderingContext2D;
|
||||||
private ctx: CanvasRenderingContext2D;
|
private ctx: CanvasRenderingContext2D;
|
||||||
private url: string;
|
private url: string;
|
||||||
private connectedToVM: boolean = false;
|
private connectedToVM: boolean = false;
|
||||||
|
|
@ -78,10 +83,12 @@ export default class CollabVMClient {
|
||||||
this.publicEmitter = createNanoEvents();
|
this.publicEmitter = createNanoEvents();
|
||||||
// Create the canvas
|
// Create the canvas
|
||||||
this.canvas = document.createElement('canvas');
|
this.canvas = document.createElement('canvas');
|
||||||
|
this.unscaledCanvas = document.createElement('canvas');
|
||||||
// Set tab index so it can be focused
|
// Set tab index so it can be focused
|
||||||
this.canvas.tabIndex = -1;
|
this.canvas.tabIndex = -1;
|
||||||
// Get the 2D context
|
// Get the 2D context
|
||||||
this.ctx = this.canvas.getContext('2d')!;
|
this.ctx = this.canvas.getContext('2d')!;
|
||||||
|
this.unscaledCtx = this.unscaledCanvas.getContext('2d')!;
|
||||||
// Bind canvas click
|
// Bind canvas click
|
||||||
this.canvas.addEventListener('click', (e) => {
|
this.canvas.addEventListener('click', (e) => {
|
||||||
if (this.users.find((u) => u.username === this.username)?.turn === -1) this.turn(true);
|
if (this.users.find((u) => u.username === this.username)?.turn === -1) this.turn(true);
|
||||||
|
|
@ -171,7 +178,7 @@ export default class CollabVMClient {
|
||||||
capture: true
|
capture: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
window.addEventListener('resize', (e) => this.onWindowResize(e));
|
||||||
this.canvas.addEventListener('contextmenu', (e) => e.preventDefault());
|
this.canvas.addEventListener('contextmenu', (e) => e.preventDefault());
|
||||||
// Create the WebSocket
|
// Create the WebSocket
|
||||||
this.socket = new WebSocket(url, 'guacamole');
|
this.socket = new WebSocket(url, 'guacamole');
|
||||||
|
|
@ -214,15 +221,28 @@ export default class CollabVMClient {
|
||||||
}
|
}
|
||||||
case 'size': {
|
case 'size': {
|
||||||
if (msgArr[1] !== '0') return;
|
if (msgArr[1] !== '0') return;
|
||||||
this.canvas.width = parseInt(msgArr[2]);
|
this.recalculateCanvasScale(parseInt(msgArr[2]), parseInt(msgArr[3]));
|
||||||
this.canvas.height = parseInt(msgArr[3]);
|
this.unscaledCanvas.width = this.actualScreenSize.width;
|
||||||
|
this.unscaledCanvas.height = this.actualScreenSize.height;
|
||||||
|
this.canvas.width = this.canvasScale.width;
|
||||||
|
this.canvas.height = this.canvasScale.height;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'png': {
|
case 'png': {
|
||||||
// Despite the opcode name, this is actually JPEG, because old versions of the server used PNG and yknow backwards compatibility
|
// Despite the opcode name, this is actually JPEG, because old versions of the server used PNG and yknow backwards compatibility
|
||||||
let img = new Image();
|
let img = new Image();
|
||||||
|
var x = parseInt(msgArr[3]);
|
||||||
|
var y = parseInt(msgArr[4]);
|
||||||
img.addEventListener('load', () => {
|
img.addEventListener('load', () => {
|
||||||
this.ctx.drawImage(img, parseInt(msgArr[3]), parseInt(msgArr[4]));
|
if (this.actualScreenSize.width !== this.canvasScale.width || this.actualScreenSize.height !== this.canvasScale.height)
|
||||||
|
this.unscaledCtx.drawImage(img, x, y);
|
||||||
|
// Scale the image to the canvas
|
||||||
|
this.ctx.drawImage(img, 0, 0, img.width, img.height,
|
||||||
|
(x / this.actualScreenSize.width) * this.canvas.width,
|
||||||
|
(y / this.actualScreenSize.height) * this.canvas.height,
|
||||||
|
(img.width / this.actualScreenSize.width) * this.canvas.width,
|
||||||
|
(img.height / this.actualScreenSize.height) * this.canvas.height
|
||||||
|
);
|
||||||
});
|
});
|
||||||
img.src = 'data:image/jpeg;base64,' + msgArr[5];
|
img.src = 'data:image/jpeg;base64,' + msgArr[5];
|
||||||
break;
|
break;
|
||||||
|
|
@ -395,6 +415,33 @@ export default class CollabVMClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onWindowResize(e: Event) {
|
||||||
|
if (!this.connectedToVM) return;
|
||||||
|
// If the canvas is the same size as the screen, don't bother redrawing
|
||||||
|
if (window.innerWidth >= this.actualScreenSize.width && this.canvas.width === this.actualScreenSize.width) return;
|
||||||
|
if (this.actualScreenSize.width === this.canvasScale.width && this.actualScreenSize.height === this.canvasScale.height) {
|
||||||
|
this.unscaledCtx.drawImage(this.canvas, 0, 0);
|
||||||
|
}
|
||||||
|
this.recalculateCanvasScale(this.actualScreenSize.width, this.actualScreenSize.height);
|
||||||
|
this.canvas.width = this.canvasScale.width;
|
||||||
|
this.canvas.height = this.canvasScale.height;
|
||||||
|
this.ctx.drawImage(this.unscaledCanvas, 0, 0, this.actualScreenSize.width, this.actualScreenSize.height, 0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private recalculateCanvasScale(width: number, height: number) {
|
||||||
|
this.actualScreenSize.width = width;
|
||||||
|
this.actualScreenSize.height = height;
|
||||||
|
// If the screen is bigger than the canvas, don't downscale
|
||||||
|
if (window.innerWidth >= this.actualScreenSize.width) {
|
||||||
|
this.canvasScale.width = this.actualScreenSize.width;
|
||||||
|
this.canvasScale.height = this.actualScreenSize.height;
|
||||||
|
} else {
|
||||||
|
// If the canvas is bigger than the screen, downscale
|
||||||
|
this.canvasScale.width = window.innerWidth;
|
||||||
|
this.canvasScale.height = (window.innerWidth / this.actualScreenSize.width) * this.actualScreenSize.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async WaitForOpen() {
|
async WaitForOpen() {
|
||||||
return new Promise<void>((res) => {
|
return new Promise<void>((res) => {
|
||||||
// TODO: should probably reject on close
|
// TODO: should probably reject on close
|
||||||
|
|
@ -488,7 +535,9 @@ export default class CollabVMClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send mouse instruction
|
// Send mouse instruction
|
||||||
sendmouse(x: number, y: number, mask: number) {
|
sendmouse(_x: number, _y: number, mask: number) {
|
||||||
|
let x = Math.round((_x / this.canvas.width) * this.actualScreenSize.width);
|
||||||
|
let y = Math.round((_y / this.canvas.height) * this.actualScreenSize.height);
|
||||||
this.send('mouse', x, y, mask);
|
this.send('mouse', x, y, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
98
static/lang/hr-hr.json
Normal file
98
static/lang/hr-hr.json
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
{
|
||||||
|
"languageName": "Hrvatski",
|
||||||
|
"translatedLanguageName": "Croatian",
|
||||||
|
"flag": "🇭🇷",
|
||||||
|
"author": "xDLiveRBLX",
|
||||||
|
|
||||||
|
"stringKeys": {
|
||||||
|
"kGeneric_CollabVM": "CollabVM",
|
||||||
|
"kGeneric_Yes": "Da",
|
||||||
|
"kGeneric_No": "Ne",
|
||||||
|
"kGeneric_Ok": "U redu",
|
||||||
|
"kGeneric_Cancel": "Odkaži",
|
||||||
|
"kGeneric_Send": "Pošalji",
|
||||||
|
"kGeneric_Understood": "Razumljeno",
|
||||||
|
"kGeneric_Username": "Korisničko ime",
|
||||||
|
"kGeneric_Password": "Lozinka",
|
||||||
|
"kGeneric_Login": "Prijavi se",
|
||||||
|
"kGeneric_Register": "Registriraj se",
|
||||||
|
"kGeneric_EMail": "E-Mail",
|
||||||
|
"kGeneric_DateOfBirth": "Datum rođenja",
|
||||||
|
"kGeneric_VerificationCode": "Verifikacijski kod",
|
||||||
|
"kGeneric_Verify": "Potvrdi",
|
||||||
|
"kGeneric_Update": "Ažuriraj",
|
||||||
|
"kGeneric_Logout": "Odjavi se",
|
||||||
|
|
||||||
|
"kWelcomeModal_Header": "Dobrodošli u CollabVM",
|
||||||
|
"kWelcomeModal_Body": "<p>Prije nastavka, upoznajte se sa našim pravilima:</p> <h3>R1. Nemojte kršiti zakon.</h3> Ne koristite CollabVM da biste kršili federalni zakon SAD-a, zakon savezne države New York ili međunarodni zakon. Ako CollabVM sazna da je preko usluge učinjen zakon, odmah ćete dobiti ban sa usluge a vaše aktivnosti mogu biti prijavljene policiji ako je potrebno.<br><br>CollabVM je dužan po zakonu obavijestiti agencije ako postane svjestan o prisutnosti dječje pornografije ili prijenosa kroz njihovu mrežu.<br><br>COPPA je također provođen, nemojte koristiti CollabVM ako ste mlađi od 13 godina. <h3>R2. Nema pokretanja/korištenja DOS/DDoS alata.</h3> Ne koristite CollabVM za provođenje DOS/DDoS napada na pojedince, tvrtke, kompanije ili bilo koga drugoga. <h3>R3. Ne šaljite neželjenu poštu.</h3> Ne šaljite neželjenu poštu kroz ovu uslugu i općenito ne šaljite neželjene stvari. <h3>R4. Nemojte zlorabiti exploite. </h3> Nemojte zlorabiti bilo kakve exploite, dodatno, ako vidite nekoga da zlorabljuje exploite ili trebate priajviti jedan, molim Vas kontaktirajte me na: computernewbab@gmail.com <h3>R5. Nemojte lažno predstavljati druge korisnike.</h3> Nemojte lažno predstavljati druge korisnike CollabVM-a. Ako ste ulovljeni, biti ćete privremeno odspojeni sa stranice i ban-ani ako potrebno. <h3>R6. Jedan glas po osobi.</h3> Nemojte koristiti nikakve metode ili alate za zaobilaženje ograničenja glasova. Samo jedan glas je dozvoljen po osobi, bez obzira na sve. Svatko tko bude ulovljen biti će ban-an. <h3>R7. Nema alata za daljinski pristup.</h3> Ne koristite nikakve alate za daljinski pristup (npr: DarkComet, NanoCore, Anydesk, TeamViewer, Orcus, itd.) <h3>R8. Nema zaobilaženja CollabNet-a.</h3> Ne pokušavajte zaobilaziti blokiranje koje nudi CollabNet, pogotovo ako se koristi za kršenje 1., 2., ili 7., pravila. (ili za pokretanje glupih previše korištenih stvari.). <h3>R9. Nema stalnog izvođenja uništavajućih radnji.</h3> Bilo koji korisnik ne smije uništiti VM (čineći ga stalno neupotrebljivim), instalirati/deinstalirati operativni sustav (osim na VM7 ili VM8), ili pokretati botove koje to čine. Ovo uključuje botove koji unose goleme količine unosa tipkovnice ili miša. (\"kitting\"). <h3>R10. Nema rudarenja kriptovaluta.</h3> Pokušavanje rudarenja kriptovaluta na ovim VM-ovima rezultirati će izbačenjem, a zatim trajnim banom ako nastavite. Osim toga, nije kao da ćete zaraditi ikakve novce na tome. <h3>NSFW upozorenje</h3> Imajte na umu da je NSFW sadržaj dopušten na anarchy VMu (VM0b0t) i da se redovito pregledava. Osim toga, dok se jako trudimo spriječiti NSFW od glavnih VMova, ponekad će se prošuljati.",
|
||||||
|
|
||||||
|
"kSiteButtons_Home": "Glavna stranica",
|
||||||
|
"kSiteButtons_FAQ": "FAQ",
|
||||||
|
"kSiteButtons_Rules": "Pravila",
|
||||||
|
"kSiteButtons_DarkMode": "Tamni način prikaza",
|
||||||
|
"kSiteButtons_LightMode": "Svjetli način prikaza",
|
||||||
|
"kSiteButtons_Languages": "Jezici",
|
||||||
|
|
||||||
|
"kVM_UsersOnlineText": "Korisnici na mreži:",
|
||||||
|
|
||||||
|
"kVM_TurnTimeTimer": "Red isteće za {0} sekundi.",
|
||||||
|
"kVM_WaitingTurnTimer": "Čekanje za red u {0} sekundi.",
|
||||||
|
"kVM_VoteCooldownTimer": "Molim Vas pričekajte {0} sekundi prije početka novog glasanja.",
|
||||||
|
|
||||||
|
"kVM_VoteForResetTitle": "Želite li resetirati VM?",
|
||||||
|
"kVM_VoteForResetTimer": "Glasanje završava za {0} sekundi",
|
||||||
|
|
||||||
|
"kVMButtons_TakeTurn": "Uzmi red",
|
||||||
|
"kVMButtons_EndTurn": "Završi red",
|
||||||
|
"kVMButtons_ChangeUsername": "Promijeni korisničko ime",
|
||||||
|
"kVMButtons_Keyboard": "Tipkovnica",
|
||||||
|
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
|
||||||
|
|
||||||
|
"kVMButtons_VoteForReset": "Glasaj za reset",
|
||||||
|
"kVMButtons_Screenshot": "Screenshot",
|
||||||
|
|
||||||
|
"kQEMUMonitor": "QEMU Monitor",
|
||||||
|
"kAdminVMButtons_PassVote": "Prođi glasovanje",
|
||||||
|
"kAdminVMButtons_CancelVote": "Otkaži glasovanje",
|
||||||
|
|
||||||
|
"kAdminVMButtons_Restore": "Vrati",
|
||||||
|
"kAdminVMButtons_Reboot": "Ponovno pokreni",
|
||||||
|
"kAdminVMButtons_ClearTurnQueue": "Očisti red",
|
||||||
|
"kAdminVMButtons_BypassTurn": "Preskoči red",
|
||||||
|
"kAdminVMButtons_IndefiniteTurn": "Neograničen red",
|
||||||
|
|
||||||
|
"kAdminVMButtons_Ban": "Ban",
|
||||||
|
"kAdminVMButtons_Kick": "Izbačaj",
|
||||||
|
"kAdminVMButtons_TempMute": "Privremeni mute",
|
||||||
|
"kAdminVMButtons_IndefMute": "Trajni mute",
|
||||||
|
"kAdminVMButtons_Unmute": "Maknite mute",
|
||||||
|
"kAdminVMButtons_GetIP": "Dohvati IP",
|
||||||
|
|
||||||
|
"kVMPrompts_AdminChangeUsernamePrompt": "Unesi novo korisničko ime za {0}:",
|
||||||
|
"kVMPrompts_AdminRestoreVMPrompt": "Jeste li sigurni da želite vratiti VM?",
|
||||||
|
"kVMPrompts_EnterNewUsernamePrompt": "Unesite novo korisničko ime ili ostavite prazno za korisničko ime posjetitelja:",
|
||||||
|
|
||||||
|
"kError_UnexpectedDisconnection": "Neočekivano ste se odspojili sa servera.",
|
||||||
|
"kError_UsernameTaken": "Korisničko ime je već zauzeto.",
|
||||||
|
"kError_UsernameInvalid": "Korisničko ime može sadržati samo brojeve, slova, razmak, crte, donje crte, i točke, i mora biti između 3 do 20 znamenaka.",
|
||||||
|
"kError_UsernameBlacklisted": "Ovo korisničko ime je zabranjeno.",
|
||||||
|
"kError_IncorrectPassword": "Netočna lozinka.",
|
||||||
|
|
||||||
|
"kAccountModal_Verify": "Potvrdi E-Mail",
|
||||||
|
"kAccountModal_AccountSettings": "Postavke računa",
|
||||||
|
"kAccountModal_ResetPassword": "Resetiraj lozinku",
|
||||||
|
|
||||||
|
"kAccountModal_NewPassword": "Nova lozinka",
|
||||||
|
"kAccountModal_ConfirmNewPassword": "Potvrdi novu lozinku",
|
||||||
|
"kAccountModal_CurrentPassword": "Trenutna lozinka",
|
||||||
|
"kAccountModal_ConfirmPassword": "Potvrdite lozinku",
|
||||||
|
|
||||||
|
"kMissingCaptcha": "Molim Vas ispunite captchu..",
|
||||||
|
"kPasswordsMustMatch": "Lozinke se moraju podudarati.",
|
||||||
|
"kAccountModal_VerifyText": "Poslali smo E-Mail na {0}. Da biste potvrdili svoj račun, unesite osmero-znamenkasti kod iz poruke ispod.",
|
||||||
|
"kAccountModal_VerifyPasswordResetText": "Poslali smo E-Mail na {0}. Da biste resetirali svoju lozinku, unesite osmero-znamenkasti kod iz poruke ispod.",
|
||||||
|
"kAccountModal_PasswordResetSuccess": "Vaša lozinka je uspješno resetirana. Sada se možete prijaviti sa svojom novom lozinkom.",
|
||||||
|
|
||||||
|
"kNotLoggedIn": "Niste prijavljeni."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"languages": ["en-us", "fr-fr", "de-de", "ja-jp", "pl-pl", "es-es", "ru-ru", "hu-hu"],
|
"languages": ["en-us", "fr-fr", "de-de", "ja-jp", "pl-pl", "es-es", "ru-ru", "hu-hu", "uk-ua", "hr-hr", "tok"],
|
||||||
"defaultLanguage": "en-us"
|
"defaultLanguage": "en-us"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,48 +2,48 @@
|
||||||
"languageName": "Русский",
|
"languageName": "Русский",
|
||||||
"translatedLanguageName": "Russian",
|
"translatedLanguageName": "Russian",
|
||||||
"flag": "🇷🇺",
|
"flag": "🇷🇺",
|
||||||
"author": "Undefishin, lua5.3",
|
"author": "Undefishin, lua5.3, Noname",
|
||||||
|
|
||||||
"stringKeys": {
|
"stringKeys": {
|
||||||
"kGeneric_CollabVM": "CollabVM",
|
"kGeneric_CollabVM": "CollabVM",
|
||||||
"kGeneric_Yes": "Да",
|
"kGeneric_Yes": "Да",
|
||||||
"kGeneric_No": "Нет",
|
"kGeneric_No": "Нет",
|
||||||
"kGeneric_Ok": "ОК",
|
"kGeneric_Ok": "ОК",
|
||||||
"kGeneric_Cancel": "Отменять",
|
"kGeneric_Cancel": "Отменить",
|
||||||
"kGeneric_Send": "Отправить",
|
"kGeneric_Send": "Отправить",
|
||||||
"kGeneric_Understood": "Принять",
|
"kGeneric_Understood": "Принять",
|
||||||
"kGeneric_Username": "Имя",
|
"kGeneric_Username": "Имя",
|
||||||
"kGeneric_Password": "Пароль",
|
"kGeneric_Password": "Пароль",
|
||||||
"kGeneric_Login": "Войти",
|
"kGeneric_Login": "Войти",
|
||||||
"kGeneric_Register": "Зарегистрировать",
|
"kGeneric_Register": "Зарегистрироватся",
|
||||||
"kGeneric_EMail": "Электронная почта",
|
"kGeneric_EMail": "Электронная почта",
|
||||||
"kGeneric_DateOfBirth": "День рождения",
|
"kGeneric_DateOfBirth": "День рождения",
|
||||||
"kGeneric_VerificationCode": "Проверичный код",
|
"kGeneric_VerificationCode": "Код верификации",
|
||||||
"kGeneric_Verify": "Подтвердить",
|
"kGeneric_Verify": "Подтвердить",
|
||||||
"kGeneric_Update": "Обновить",
|
"kGeneric_Update": "Обновить",
|
||||||
"kGeneric_Logout": "Выйти из аккаунта",
|
"kGeneric_Logout": "Выйти из аккаунта",
|
||||||
|
|
||||||
"kWelcomeModal_Header": "Добро пожаловать в CollabVM!",
|
"kWelcomeModal_Header": "Добро пожаловать в CollabVM!",
|
||||||
"kWelcomeModal_Body": "<p>Пожалуйста, ознакомьтесь с правилами:</p> <h3>Не нарушайте закон</h3> Не используйте CollabVM или сеть CollabVM для нарушения федерального законодательства США, законодательства штата Нью-Йорк или международного законодательства. В случае необходимости о ваших действиях может быть сообщено юридическим лицам. <h3>Не запускать инструменты DoS/DDoS.</h3> Не используйте CollabVM для DDoS отдельных лиц, компаний, предприятий или кого-либо еще. <h3>Не распространять спам.</h3> Не рассылайте спам по электронной почте и не рассылайте спам вообще, используя эту службу. <h3>Не злоупотреблять эксплойтами.</h3> Не злоупотребляйте эксплойтами, кроме того, если вы видите, что кто-то злоупотребляет эксплойтами или вам нужно сообщить об этом, пожалуйста, свяжитесь с computernewbab@gmail.com. <h3>Не выдавайте себя за других пользователей.</h3> Не выдавайте себя за других участников CollabVM. <h3>Один голос от одного человека.</h3> Разрешается только один голос для сброса от одного человека. Нельзя просить сброс для виртуальной машину более чем один раз для каждый пользователь. <h3>Никаких инструментов удаленного доступа</h3> Не используйте инструменты удаленного доступа (например, DarkComet или TeamViewer). <h3>Не обходить CollabNet</h3> Не пытайтесь обойти блокировку, предоставляемую CollabNet. <h3>Не уничтожайте ВМ постоянно</h3> Не уничтожайте постоянно ВМ/приводите ее в негодность, не переустанавливайте операционную систему (кроме VM7 или VM8) и не запускайте ботов, которые это делают. <h3>Не майнить криптовалюту</h3> Не добывайте криптовалюту на виртуальной машине, если вас поймают, вы будете забанены. Вы все равно не получаем из этого денег. <h3>Уведомление NSFW</h3> NSFW-контент разрешен на ВМ \"Анархия\" и регулярно просматривается. Хотя мы прилагаем все усилия, чтобы не допускать NSFW на основные ВМ, люди иногда проскальзывают туда.",
|
"kWelcomeModal_Body": "<p>Пожалуйста, ознакомьтесь с правилами:</p> <h3>Не нарушайте закон</h3> Не используйте CollabVM или сеть CollabVM для нарушения федерального законодательства США, законодательства штата Нью-Йорк или международного законодательства. В случае необходимости мы сообщим властями о вашей деятельности. <h3>Не запускать инструменты DoS/DDoS.</h3> Не используйте CollabVM для DDoS аттак отдельных лиц, компаний, предприятий или т.д. <h3>Не рассылайте почтовый спам.</h3> Не рассылайте спам по электронной почте используя эту CollabVM. <h3>Не злоупотребляйте эксплойтами.</h3> Не злоупотребляйте эксплойтами, кроме того, если вы видите, что кто-то злоупотребляет эксплойтами вам нужно сообщить об этом по почте computernewbab@gmail.com. <h3>Не выдавайте себя за других пользователей.</h3> Не выдавайте себя за других пользователй или персонал CollabVM. <h3>Один голос от одного человека.</h3> Разрешается только один голос для сброса от одного человека. Нельзя голосовать за сброс виртуальной машину больше чем 1 раз. <h3>Никаких инструментов удаленного доступа</h3> Не используйте инструменты удаленного доступа (например, AnyDesk , DarkComet , NjRAT или TeamViewer). <h3>Не обходить CollabNet</h3> Не пытайтесь обойти блокировку, предоставляемую CollabNet. <h3>Не уничтожайте Виртуальную Машину постоянно</h3> Не уничтожайте постоянно Виртуальную Машину/приводите ее в негодность, не переустанавливайте операционную систему (кроме VM7 или VM8) и не запускайте ботов, которые это делают. <h3>Никакого крипто-майнинга</h3> Не майните криптовалюту на виртуальной машине, если об этом узнают, вы будете забанены. Вы все равно вы не получите за это денег. <h3>Предупреждение о NSFW</h3> NSFW-контент разрешен только на Вертуальной Машине \"Анархия\" (ВМ0) . На всех остальных вертуальный машинах NSFW контент запрщен и блокирует через E2Guarden.",
|
||||||
|
|
||||||
"kSiteButtons_Home": "Главная",
|
"kSiteButtons_Home": "Главная",
|
||||||
"kSiteButtons_FAQ": "ЧаВо",
|
"kSiteButtons_FAQ": "ЧаВо",
|
||||||
"kSiteButtons_Rules": "Правила",
|
"kSiteButtons_Rules": "Правила",
|
||||||
"kSiteButtons_DarkMode": "Чёрный тема",
|
"kSiteButtons_DarkMode": "Темная тема",
|
||||||
"kSiteButtons_LightMode": "Белый тема",
|
"kSiteButtons_LightMode": "Светлая тема",
|
||||||
"kSiteButtons_Languages": "Языки",
|
"kSiteButtons_Languages": "Языки",
|
||||||
|
|
||||||
"kVM_UsersOnlineText": "Пользователей",
|
"kVM_UsersOnlineText": "Пользователи Онлайн",
|
||||||
|
|
||||||
"kVM_TurnTimeTimer": "Твоё место закончиться через {0} секунд(у)",
|
"kVM_TurnTimeTimer": "Твой ход закончиться через {0} секунд(у)",
|
||||||
"kVM_WaitingTurnTimer": "Ждём свободного места через {0} секунд(y)",
|
"kVM_WaitingTurnTimer": "Твой ход будет через {0} секунд(ы)",
|
||||||
"kVM_VoteCooldownTimer": "Пожалуйста подождите {0} секунд(ы) перед началом еще один сброс",
|
"kVM_VoteCooldownTimer": "Пожалуйста подождите {0} секунд(ы) перед началом голосования за сброс",
|
||||||
|
|
||||||
"kVM_VoteForResetTitle": "Вы хотите сбросить виртуальную машину?",
|
"kVM_VoteForResetTitle": "Вы хотите сбросить виртуальную машину?",
|
||||||
"kVM_VoteForResetTimer": "Выбор на восстановление машины закончится через {0} секунд(ы)",
|
"kVM_VoteForResetTimer": "Голосование закончится через {0} секунд(ы)",
|
||||||
|
|
||||||
"kVMButtons_TakeTurn": "Занять место",
|
"kVMButtons_TakeTurn": "Сделать ход",
|
||||||
"kVMButtons_EndTurn": "Закончить очередь",
|
"kVMButtons_EndTurn": "Закончить ход",
|
||||||
"kVMButtons_ChangeUsername": "Сменить имя",
|
"kVMButtons_ChangeUsername": "Сменить имя",
|
||||||
"kVMButtons_Keyboard": "Клавиатура",
|
"kVMButtons_Keyboard": "Клавиатура",
|
||||||
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
|
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
|
||||||
|
|
@ -52,14 +52,14 @@
|
||||||
"kVMButtons_Screenshot": "Скриншот",
|
"kVMButtons_Screenshot": "Скриншот",
|
||||||
|
|
||||||
"kQEMUMonitor": "QEMU Монитор",
|
"kQEMUMonitor": "QEMU Монитор",
|
||||||
"kAdminVMButtons_PassVote": "Принять выбор",
|
"kAdminVMButtons_PassVote": "Восстановить виртуальную машину",
|
||||||
"kAdminVMButtons_CancelVote": "Отказаться от выбора",
|
"kAdminVMButtons_CancelVote": "Отменить голосование",
|
||||||
|
|
||||||
"kAdminVMButtons_Restore": "Восстановить",
|
"kAdminVMButtons_Restore": "Восстановить",
|
||||||
"kAdminVMButtons_Reboot": "Перезагрузить",
|
"kAdminVMButtons_Reboot": "Перезагрузить",
|
||||||
"kAdminVMButtons_ClearTurnQueue": "Очистить очередь",
|
"kAdminVMButtons_ClearTurnQueue": "Очистить очередь ходов",
|
||||||
"kAdminVMButtons_BypassTurn": "Пропустить очередь",
|
"kAdminVMButtons_BypassTurn": "Обойти чужые ходы",
|
||||||
"kAdminVMButtons_IndefiniteTurn": "Бесконечный очередь",
|
"kAdminVMButtons_IndefiniteTurn": "Бесконечный ход",
|
||||||
|
|
||||||
"kAdminVMButtons_Ban": "Забанить",
|
"kAdminVMButtons_Ban": "Забанить",
|
||||||
"kAdminVMButtons_Kick": "Кикнуть",
|
"kAdminVMButtons_Kick": "Кикнуть",
|
||||||
|
|
@ -72,27 +72,27 @@
|
||||||
"kVMPrompts_AdminRestoreVMPrompt": "Вы уверены что хотите восстановить VM?",
|
"kVMPrompts_AdminRestoreVMPrompt": "Вы уверены что хотите восстановить VM?",
|
||||||
"kVMPrompts_EnterNewUsernamePrompt": "Введите новое имя пользователя",
|
"kVMPrompts_EnterNewUsernamePrompt": "Введите новое имя пользователя",
|
||||||
|
|
||||||
"kError_UnexpectedDisconnection": "Связь между нас и сервер отклонен.",
|
"kError_UnexpectedDisconnection": "Вы были отключены от сервера",
|
||||||
"kError_UsernameTaken": "Этот имя уже существует.",
|
"kError_UsernameTaken": "Этот имя уже существует.",
|
||||||
"kError_UsernameInvalid": "Именах могут только иметь цифры, буквы, пробелы, тере, нижнее подчеркивание, и точек, и должны быть между 3 и 20 букв.",
|
"kError_UsernameInvalid": "Именах могут только иметь цифры, буквы, пробелы, тере, нижнее подчеркивание, и точек, и должны быть от 3 до 20 букв.",
|
||||||
"kError_UsernameBlacklisted": "Этот имя запрещёно.",
|
"kError_UsernameBlacklisted": "Этот имя запрещёно.",
|
||||||
"kError_IncorrectPassword": "Неверный пароль.",
|
"kError_IncorrectPassword": "Неверный пароль.",
|
||||||
|
|
||||||
"kAccountModal_Verify": "Проверять электронною почту",
|
"kAccountModal_Verify": "Проверить электронною почту",
|
||||||
"kAccountModal_AccountSettings": "Настроить аккаунт",
|
"kAccountModal_AccountSettings": "Настройки аккаунта",
|
||||||
"kAccountModal_ResetPassword": "Сбросить пароль",
|
"kAccountModal_ResetPassword": "Сбросить пароль",
|
||||||
|
|
||||||
"kAccountModal_NewPassword": "Новый пароль",
|
"kAccountModal_NewPassword": "Новый пароль",
|
||||||
"kAccountModal_ConfirmNewPassword": "Принять новый пароль",
|
"kAccountModal_ConfirmNewPassword": "Принять новый пароль",
|
||||||
"kAccountModal_CurrentPassword": "Текущий пароль",
|
"kAccountModal_CurrentPassword": "Текущий пароль",
|
||||||
"kAccountModal_ConfirmPassword": "Принять пароль",
|
"kAccountModal_ConfirmPassword": "Подтвердите пароль",
|
||||||
|
|
||||||
"kMissingCaptcha": "Пожалуйста, выложите картчю.",
|
"kMissingCaptcha": "Пожалуйста, пройдите капчу.",
|
||||||
"kPasswordsMustMatch": "Паролы должны быть равны.",
|
"kPasswordsMustMatch": "Пароли должны быть одинаковы.",
|
||||||
"kAccountModal_VerifyText": "Мы отправили почту к {0}. Чтобы подтвердить нашу аккаунт, пожалуйста веди 8-значный код из почту ниже.",
|
"kAccountModal_VerifyText": "Мы отправили письмо на {0}. Чтобы подтвердить нашу аккаунт, пожалуйста веди 8-значный код из письма ниже.",
|
||||||
"kAccountModal_VerifyPasswordResetText": "Мы отправили почту к {0}. Чтобы сбросить нашу пароль, пожалуйста веди 8-значный код из почту ниже.",
|
"kAccountModal_VerifyPasswordResetText": "Мы отправили письмо на {0}. Чтобы сбросить ваш пароль, пожалуйста веди 8-значный код из письма ниже.",
|
||||||
"kAccountModal_PasswordResetSuccess": "Сброс пароль был успешно. Теперь, мы можём войти с новым паролем.",
|
"kAccountModal_PasswordResetSuccess": "Пароль сброшен успешно! Теперь, вы можёте войти с новым паролем.",
|
||||||
|
|
||||||
"kNotLoggedIn": "Не вошел/зарегистрирован"
|
"kNotLoggedIn": "Не авторизован"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
98
static/lang/tok.json
Normal file
98
static/lang/tok.json
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
{
|
||||||
|
"languageName": "Toki Pona",
|
||||||
|
"translatedLanguageName": "toki pona",
|
||||||
|
"flag": "🌐",
|
||||||
|
"author": "Computernewb",
|
||||||
|
|
||||||
|
"stringKeys": {
|
||||||
|
"kGeneric_CollabVM": "ilo CollabVM",
|
||||||
|
"kGeneric_Yes": "lon",
|
||||||
|
"kGeneric_No": "ala",
|
||||||
|
"kGeneric_Ok": "pona",
|
||||||
|
"kGeneric_Cancel": "o ala",
|
||||||
|
"kGeneric_Send": "o pana",
|
||||||
|
"kGeneric_Understood": "mi sona",
|
||||||
|
"kGeneric_Username": "nimi",
|
||||||
|
"kGeneric_Password": "nimi len",
|
||||||
|
"kGeneric_Login": "o lon sijelo",
|
||||||
|
"kGeneric_Register": "o pali e sijelo",
|
||||||
|
"kGeneric_EMail": "o toki E-mail",
|
||||||
|
"kGeneric_DateOfBirth": "tenpo pi kama lon",
|
||||||
|
"kGeneric_VerificationCode": "sitelen pi pona sona",
|
||||||
|
"kGeneric_Verify": "o pona e sona",
|
||||||
|
"kGeneric_Update": "o sin",
|
||||||
|
"kGeneric_Logout": "o weka tan sijelo",
|
||||||
|
|
||||||
|
"kWelcomeModal_Header": "ilo CollabVM la o kama pona",
|
||||||
|
"kWelcomeModal_Body": "<p>open la o sona e lawa mi:</p> <h3>nanpa wan la o ike ala tawa lawa ma.</h3> kepeken ilo mi la o ike ala tawa lawa pi ma Mewika, tawa lawa pi ma Nujo, tawa lawa pi kulupu ma. mi kama sona e ike ni sina la, mi weka e sina tan ilo. mi o toki tawa kulupu lawa ma la, mi toki.<br><br>mi kama sona e sitelen unpa pi jan lili lon ilo mi la, lawa ma la mi o toki tawa kulupu pi lawa ma.<br><br>sina majuna pi tenpo sike 13 ala la, lawa COPPA la o kepeken ala ilo CollabVM. <h3>nanpa tu la o pakala ala e ilo Internet ante kepeken ilo mi.</h3><!-- Rest omitted because redundant sentences do not translate well in toki pona. --> <h3>nanpa tu la o pana ala e jaki.</h3> kepeken ilo mi la o jaki ala kepeken ilo E-mail, kepeken ilo ante. <h3>nanpa tu tu la o ike ala kepeken pakala ilo.</h3> sina lukin e pakala ilo la, o ike ala kepeken ona. toki tawa mi kepeken nimi E-mail \"computernewbab@gmail.com\" <h3>nanpa luka la nasin ike la o sama ala jan ante.</h3> sina ni la, mi weka e sina lon tenpo lili. mi o weka e sina lon tenpo ale la, mi ni. <h3>nanpa luka wan la jan wan li ken toki e wile lon tenpo wan.</h3> o weka ala tan lawa ni kepeken ilo. sina toki e wile lon tenpo mute la, mi weka e sina. <h3>nanpa luka tu la o kepeken ala ilo pi lawa ilo.</h3> ala la o kepeken ilo DarkComet, kepeken ilo NanoCore, kepeken ilo Anydesk, kepeken ilo TeamViewer, kepeken ilo Orcus, kepeken ilo ante sama ni. <h3>nanpa luka tu wan la mi weka e ken la, o alasa ala e ni.</h3> ni la sina wile pali ike tawa lawa nanpa wan, anu tawa lawa nanpa tu, anu tawa lawa nanpa luka tu la, ni li ike mute. sina wile kepeken ilo jaki pi kepeken mute la, ni li ike mute.<!-- this is a bad translation but it's a badly-written rule --> <h3>lawa nanpa luka tu tu la o pakala suli ala e ilo.</h3> pakala ilo la o weka ala e ken pali ilo. ilo li ilo nanpa luka tu ala, li ilo nanpa luka tu wan ala la, o sin ala e nasin ilo, o kepeken ala ilo pi sin nasin. ilo li kepeken mute ilo sitelen, kepeken mute ilo wile la, ona li ni. <h3>lawa nanpa luka luka la o alasa ala e mani nanpa</h3> sina ni la, mi weka e sina pi tenpo wan. sina awen alasa, mi weka e sina pi tenpo ale. kin sina kama ala jo e mani tan ni. <h3>unpa la</h3> o sona e ni: ilo 0b0t la sitelen unpa li ken, li lon mute. mi alasa weka e sitelen unpa tan ilo ante. taso tenpo la ona li kama.",
|
||||||
|
|
||||||
|
"kSiteButtons_Home": "tomo",
|
||||||
|
"kSiteButtons_FAQ": "wile sona",
|
||||||
|
"kSiteButtons_Rules": "lawa",
|
||||||
|
"kSiteButtons_DarkMode": "pimeja",
|
||||||
|
"kSiteButtons_LightMode": "walo",
|
||||||
|
"kSiteButtons_Languages": "toki",
|
||||||
|
|
||||||
|
"kVM_UsersOnlineText": "jan ni li lon:",
|
||||||
|
|
||||||
|
"kVM_TurnTimeTimer": "ken pali li weka lon tenpo lili {0}.",
|
||||||
|
"kVM_WaitingTurnTimer": "sina ken pali lon tenpo lili {0}.",
|
||||||
|
"kVM_VoteCooldownTimer": "sina wile open sin e toki wile la, o awen lon tenpo lili {0}.",
|
||||||
|
|
||||||
|
"kVM_VoteForResetTitle": "sina wile ala wile sin e ilo?",
|
||||||
|
"kVM_VoteForResetTimer": "toki wile li pini lon tenpo lili {0}",
|
||||||
|
|
||||||
|
"kVMButtons_TakeTurn": "o pali",
|
||||||
|
"kVMButtons_EndTurn": "o pini e pali",
|
||||||
|
"kVMButtons_ChangeUsername": "o ante e nimi",
|
||||||
|
"kVMButtons_Keyboard": "ilo sitelen",
|
||||||
|
"KVMButtons_CtrlAltDel": "sitelen Ctrl+Alt+Del",
|
||||||
|
|
||||||
|
"kVMButtons_VoteForReset": "o wile e sin ilo",
|
||||||
|
"kVMButtons_Screenshot": "o jo e sitelen ni",
|
||||||
|
|
||||||
|
"kQEMUMonitor": "sinpin QEMU",
|
||||||
|
"kAdminVMButtons_PassVote": "mi pilin ala",
|
||||||
|
"kAdminVMButtons_CancelVote": "ni li wile mi ala",
|
||||||
|
|
||||||
|
"kAdminVMButtons_Restore": "o sin",
|
||||||
|
"kAdminVMButtons_Reboot": "o pini, o open",
|
||||||
|
"kAdminVMButtons_ClearTurnQueue": "o weka e nasin pali",
|
||||||
|
"kAdminVMButtons_BypassTurn": "o pana ala e pali",
|
||||||
|
"kAdminVMButtons_IndefiniteTurn": "pali pi tenpo ale",
|
||||||
|
|
||||||
|
"kAdminVMButtons_Ban": "o weka pi tenpo ale",
|
||||||
|
"kAdminVMButtons_Kick": "o weka pi tenpo wan",
|
||||||
|
"kAdminVMButtons_TempMute": "tenpo la o weka e ken toki",
|
||||||
|
"kAdminVMButtons_IndefMute": "ale la o weka e ken toki",
|
||||||
|
"kAdminVMButtons_Unmute": "o sin e ken toki",
|
||||||
|
"kAdminVMButtons_GetIP": "o sona e sitelen IP",
|
||||||
|
|
||||||
|
"kVMPrompts_AdminChangeUsernamePrompt": "jan {0} la o pana e nimi sin:",
|
||||||
|
"kVMPrompts_AdminRestoreVMPrompt": "sina wile ala wile sin e ilo?",
|
||||||
|
"kVMPrompts_EnterNewUsernamePrompt": "o pana e nimi. ala la, mi pana e nimi pi jan sin",
|
||||||
|
|
||||||
|
"kError_UnexpectedDisconnection": "sina weka tan ilo.",
|
||||||
|
"kError_UsernameTaken": "jan ante li jo e nimi ni",
|
||||||
|
"kError_UsernameInvalid": "nimi li ken jo taso e sitelen [A-Za-z0-9 \\-_.]. sitelen tu li lili ike. siten mute wan li suli ike.",
|
||||||
|
"kError_UsernameBlacklisted": "nimi ni li ken ala.",
|
||||||
|
"kError_IncorrectPassword": "ni li nimi len sina ala.",
|
||||||
|
|
||||||
|
"kAccountModal_Verify": "nimi E-mail la o pona e sona",
|
||||||
|
"kAccountModal_AccountSettings": "wile sijelo",
|
||||||
|
"kAccountModal_ResetPassword": "o ante e nimi len",
|
||||||
|
|
||||||
|
"kAccountModal_NewPassword": "nimi len sin",
|
||||||
|
"kAccountModal_ConfirmNewPassword": "sin la nimi len sin",
|
||||||
|
"kAccountModal_CurrentPassword": "nimi len lon",
|
||||||
|
"kAccountModal_ConfirmPassword": "sin la nimi len sin",
|
||||||
|
|
||||||
|
"kMissingCaptcha": "o toki e sitelen CAPTCHA.",
|
||||||
|
"kPasswordsMustMatch": "nimi len o sama.",
|
||||||
|
"kAccountModal_VerifyText": "mi toki E-mail tawa nimi {0}. pona sona la o pana e sitelen luka tu wan tan ni.",
|
||||||
|
"kAccountModal_VerifyPasswordResetText": "mi toki E-mail tawa nimi {0}. ante pi nimi len la o pana e sitelen luka tu wan tan ni.",
|
||||||
|
"kAccountModal_PasswordResetSuccess": "nimi len sina li kama ante. sina ken lon sijelo kepeken ona.",
|
||||||
|
|
||||||
|
"kNotLoggedIn": "sina lon sijelo ala"
|
||||||
|
}
|
||||||
|
}
|
||||||
98
static/lang/uk-ua.json
Normal file
98
static/lang/uk-ua.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -41,7 +41,7 @@
|
||||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
"resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user