-
Notifications
You must be signed in to change notification settings - Fork 27
/
shared.js
527 lines (510 loc) · 22 KB
/
shared.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
/* eslint-disable jsdoc/require-jsdoc */
/* eslint-disable @typescript-eslint/no-this-alias */
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
// Contains shared resources across pages
const DEFAULT_SERVER = "wss://server.rplace.live:443"
const DEFAULT_BOARD = "https://raw.githubusercontent.com/rplacetk/canvas1/main/place"
const DEFAULT_AUTH = "https://server.rplace.live/auth"
const TRANSLATIONS = {
en: {
// Game
connecting: "Connecting...",
connectingFail: "Could not connect!",
disconnectedFromServer: "Disconnected from server:",
downloadingImage: "Downloading image...",
placeTile: "Place a tile",
donate: "Donate",
myAccount: "My Account",
chat: "Chat",
liveChat: "Live Chat:",
nicknameToContinue: "Enter a nickname to continue:",
changeChannel: "Change channel:",
captchaPrompt: "Solve this small captcha to help keep rplace.live fun for all...",
webappInstall: "Install rplace.live web app",
connectionProblems: "Connection problems?",
tryClickingHere: "try clicking here",
pleaseBeRespectful: "Please be respectful and try not to spam!",
enterNickname: "Enter nickname...",
enterMessage: "Enter message...",
signInInstead: "Sign in instead",
createNewAccount: "Create a new account",
mention: "Mention",
replyTo: "Reply to",
addReaction: "Add reaction",
report: "Report",
block: "Block",
unblock: "Unblock",
changeMyName: "Change my name",
putOnCanvas: "🫧 Put on canvas",
sendInLiveChat: "📨 Send in live chat",
overlayMenu: "Overlay menu",
modalAboutContent: "There is an empty canvas.<br><br>You may place a tile upon it, but you must wait to place another.<br><br>Individually you can create something.<br><br>Together you can create something more.",
overlayMenuDesciption: "Visualise your build with a template image!",
messageNotFound: "Message could not be loaded",
placedBy: "Placed by:",
lockMessage: "This canvas is locked... You can't place pixels here anymore",
adHidden: "Ad hidden for 14 days!",
specialEventTitle: "Special event - August 21st!",
copiedToClipboard: "Copied to clipboard!",
// Posts
rplaceLivePosts: "rplace.live posts",
searchKeyword: "Search keyword",
createPost: "Create post",
communityPosts: "Community posts",
sortBy: "Sort by:",
hideSensitive: "Hide sensitive:",
date: "Date",
upvotes: "Upvotes",
},
fa: {
connecting: "در حال وصل شدن",
connectingFail: "متصل نشد",
downloadingImage: "در حال بارگزاری عکس ها",
placeTile: "نقاشی کردن",
donate: "حمایت",
myAccount: "حساب کاربری من",
chat: "چت",
liveChat: "چت زنده",
nicknameToContinue: "لطفا نام خود را برای چت کردن وارد کنید",
changeChannel: "تغییر کانال:",
captchaPrompt: "لطفا روی دکمه حاوی ایموجی که در زیر می بینید کلیک کنید",
webappInstall: "برنامه وب rplace.live را نصب کنید",
connectionProblems: "مشکلات اتصال؟",
tryClickingHere: "برای حل اینجا کلیک کنید",
pleaseBeRespectful: "لطفا محترمانه رفتار کنید و سعی کنید اسپم نکنید!",
enterNickname: "نام مستعار را وارد کنید...",
enterMessage: "پیام را وارد کنید..."
},
tr: {
connecting: "Bağlanıyor...",
connectingFail: "Bağlanamadı!",
downloadingImage: "Resim indiriliyor...",
placeTile: "Bir piksel yerleştir",
donate: "Bağış yap",
myAccount: "Hesabım",
chat: "Sohbet",
// liveChat: "Canlı sohbet:", // TOO LONG
nicknameToContinue: "Devam etmek için bir takma ad girin:",
changeChannel: "Kanalı değiştir:",
captchaPrompt: "Lütfen aşağıda gördüğünüz emojiyi içeren butona tıklayın",
webappInstall: "rplace.live web uygulamasını kurun",
connectionProblems: "Bağlantı problemleri?",
tryClickingHere: "buraya tıklamayı dene",
pleaseBeRespectful: "Lütfen saygılı olun ve spam yapmamaya çalışın!",
enterNickname: "Takma ad girin...",
enterMessage: "Mesaj girin...",
signInInstead: "Oturum aç",
createNewAccount: "Hesap oluştur",
mention: "Etiketle",
block: "Engellemek",
changeMyName: "İsimi değiştir",
putOnCanvas: "🫧 Haritanın üstüne yaz",
sendInLiveChat: "📨 Sohbete yaz",
overlayMenu: "Bindirme menüsü",
modalAboutContent: "Boş bir tuval var.<br><br>Üzerine renkli bir piksel koyabilirsiniz ama yenisini yerleştirmek için beklemeniz gerekir.<br><br>Tek başınıza bir şeyler yapabilirsiniz.<br><br>Topluluk ile daha fazlasını yapabilirsiniz.",
overlayMenuDesciption: "Haritanın üzerine bir fotoğraf koyun ve onu çizin!",
specialEventTitle: "Özel etkinlik - August 21st!"
},
ro: {
connecting: "Se conectează...",
connectingFail: "Nu s-a putut conecta!",
downloadingImage: "Se descarcă imaginea...",
placeTile: "Pune un pixel",
donate: "Donează",
myAccount: "Contul meu",
chat: "Conversații",
//liveChat: "Conversații în direct:", // TOO LONG
nicknameToContinue: "Introdu un nume pentru a continua:",
changeChannel: "Schimbați canalul:",
captchaPrompt: "Dați clic pe butonul care conține emoji-ul pe care îl vedeți mai jos",
webappInstall: "Instalați aplicația web rplace.live",
connectionProblems: "Probleme de conectare?",
tryClickingHere: "incearca sa dai click aici",
pleaseBeRespectful: "Vă rugăm să fiți respectuos și să nu trimiteți spam!",
enterNickname: "Introduceți porecla...",
enterMessage: "Introdu mesajul..."
},
el: {
connecting: "Συνδετικός...",
connectingFail: "Δεν μπορούσε να συνδεθεί!",
downloadingImage: "Λήψη εικόνας...",
placeTile: "τοποθετώ ένα πίξελ",
donate: "δανεισω",
myAccount: "Ο λογαριασμός μου",
chat: "συζήτηση",
//liveChat: "ζωντανή συζήτηση", // TOO LONG
nicknameToContinue: "Εισαγάγετε ένα ψευδώνυμο για να συνεχίσετε:",
changeChannel: "Αλλαγή καναλιού:",
captchaPrompt: "Κάντε κλικ στο κουμπί που περιέχει το emoji που βλέπετε παρακάτω",
webappInstall: "Εγκαταστήστε την εφαρμογή web rplace.live",
connectionProblems: "Προβλήματα σύνδεσης;",
tryClickingHere: "δοκιμάστε να κάνετε κλικ εδώ",
pleaseBeRespectful: "Παρακαλώ να είστε σεβαστές!",
enterNickname: "Εισαγάγετε ψευδώνυμο...",
enterMessage: "Εισαγάγετε μήνυμα..."
},
es: {
connecting: "Conectando...",
connectingFail: "¡No podía conectar!",
downloadingImage: "Descargando imagen...",
placeTile: "Coloca un pixel",
donate: "Donar",
myAccount: "Mi cuenta",
chat: "Chat",
liveChat: "Chat:", // en vivo
nicknameToContinue: "Introduce un apodo para continuar:",
changeChannel: "Cambia el canal:",
captchaPrompt: "Haga clic en el botón que contiene el emoji que ve a continuación",
webappInstall: "Instale la aplicación web rplace.live",
connectionProblems: "¿Problemas de conexión?",
tryClickingHere: "intente hacer clic aquí",
pleaseBeRespectful: "Por favor se respetuoso!",
enterNickname: "Introduce el apodo...",
enterMessage: "Introduce el mensaje...",
specialEventTitle: "Evento especial - August 21st!"
},
fr: {
connecting: "De liaison...",
connectingFail: "Impossible de se connecter!",
downloadingImage: "Télécharger l'image...",
placeTile: "Place un pixel",
donate: "Faire un don",
myAccount: "Mon compte",
chat: "Discuter",
//liveChat: "Chat en direct:", // TOO LONG
nicknameToContinue: "Entrez un surnom pour continuer :",
changeChannel: "Changer de chaîne:",
captchaPrompt: "Veuillez cliquer sur le bouton contenant l'emoji que vous voyez ci-dessous",
webappInstall: "Installez l'application Web rplace.live",
connectionProblems: "Problèmes de connexion?",
tryClickingHere: "résoudre en cliquant ici",
pleaseBeRespectful: "Soyez respectueux et essayez de ne pas spammer !",
enterNickname: "Entrez le pseudo...",
enterMessage: "Saisissez le message..."
},
ru: {
connecting: "Подключение...",
connectingFail: "Не могу подключиться!",
downloadingImage: "Загрузка изображения...",
placeTile: "Поместите пиксель",
donate: "Пожертвовать",
myAccount: "Мой счет",
chat: "Чат",
liveChat: "Живой чат:",
nicknameToContinue: "Введите псевдоним:",
changeChannel: "Изменить канал:",
captchaPrompt: "Пожалуйста, решите эту небольшую капчу, чтобы сделать rplace.live приятным для всех...",
webappInstall: "Установите веб-приложение rplace.live",
connectionProblems: "Проблемы с подключением?",
tryClickingHere: "попробуйте нажать здесь",
pleaseBeRespectful: "Пожалуйста, будьте уважительны и старайтесь не спамить!",
enterNickname: "Введите псевдоним...",
enterMessage: "Введите сообщение...",
signInInstead: "зарегистрироваться вместо этого",
createNewAccount: "создайте новый аккаунт",
mention: "упомянуть",
replyto: "ответить",
report: "пожаловаться",
block: "заблокировать",
unblock: "разблокировать",
changeMyName: "изменить моё имя",
//putOnCanvas: ,
sendInLiveChat: "Incoming_envelope:отправить в живой чат",
overlaymenu: "меню наложения",
modelAboutContent: "есть пустое полотно.<br><br> Вы можете поставить пиксель на нём,но вы должны подождать чтобы поставить ещё.<br<br>Вы можете создать что-то.<br><br> Вместе вы можете создать много.",
overlayMenuDescription: "визуализируйте вашу постройку с шаблоном вашего изображения!"
},
uk: {
connecting: "підключення...",
connectingFail: "Не вдалося підключитися!",
downloadingImage: "Завантажую зображення",
placeTile: "Покласти піксель",
donate: "Пожертвувати",
myAccount: "Мій обліковий запис",
chat: "Чат",
liveChat: "Живий чат",
nicknameToContinue: "Введіть ім'я щоб продовжити",
changeChannel: "Зменіть канал",
captchaPrompt: "вирішіть цю мальнеку captcha щоб допомогти залишити rplace.live веселим для всіх",
webappInstall: "скачайте rplace.live веб-программу",
connectionProblems: "Проблеми с підключенням?",
tryClickingHere: "спробуйте клацнути сюди",
pleaseBeRespectful: "будь ласка будьте поважними і не спамьте!",
enterNickname: "Введіть ім'я...",
enterMessage: "Введіть повідомлення",
signInInstead: "Увійти замість цього",
createNewAccount: "Створіть новий аккаунт",
mention: "згадати",
replyto: "Відповісти на",
report: "Доповідь",
block: "Заблокувати",
unblock: "Разблокувати",
changeMyName: "Змінити ім'я",
putOnCanvas: "🫧поставити на полотно",
sendInLiveChat: "📨відправьте в живий чат",
overlaymenu: "Меню накладки",
modelAboutContent: "Є пусте полотно.<br><br>Ви можете поставити піксель на ньому,<br><br>але вам треба почекати перед тим як поставити іще один.<br><br>Індиаідуально ви можете створити багато чого.",
overlayMenuDescription: "Візуалізуйте вашу творчість с шаблоном зображення!"
},
de: {
connecting: "Zugreifen...",
connectingFail: "Konnte keine Verbindung herstellen!",
downloadingImage: "Bild wird heruntergeladen...",
placeTile: "Platziere ein Pixel",
donate: "Spenden",
myAccount: "Mein Konto",
chat: "Chat",
liveChat: "Live-Chat:",
nicknameToContinue: "Geben Sie einen Spitznamen ein, um fortzufahren:",
changeChannel: "Kanal wechseln:",
captchaPrompt: "Bitte lösen Sie dieses kleine Captcha, damit rplace.live allen Spaß macht...",
webappInstall: "Installieren Sie die Web-App rplace.live",
connectionProblems: "Verbindungsprobleme?",
tryClickingHere: "klicken Sie hier, um zu lösen",
pleaseBeRespectful: "Bitte seien Sie respektvoll, nicht zu spammen!",
enterNickname: "Spitznamen eingeben...",
enterMessage: "Nachricht eingeben..."
},
hi: {
connecting: "कनेक्टिंग ...",
connectingFail: "कनेक्ट नहीं हो सका!",
downloadingImage: "इमेज डाउनलोड हो रही है...",
placeTile: "एक टाइल रखें",
donate: "दान करें",
myAccount: "मेरा खाता",
chat: "चैट",
liveChat: "लाइव चैट:",
nicknameToContinue: "जारी रखने के लिए एक उपनाम दर्ज करें:",
changeChannel: "चैनल बदलें:",
captchaPrompt: "rplace.live को सभी के लिए मज़ेदार बनाए रखने में मदद के लिए कृपया इस छोटे कैप्चा को हल करें...",
webappInstall: "rplace.live वेब ऐप इंस्टॉल करें",
connectionProblems: "कनेक्शन समस्याएं?",
tryClickingHere: "यहाँ क्लिक करने का प्रयास करें",
pleaseBeRespectful: "कृपया सम्मान करें और स्पैम न करने का प्रयास करें!",
enterNickname: "उपनाम दर्ज करें ...",
enterMessage: "संदेश दर्ज करें ..."
},
ar: {
connecting: "جار الاتصال...",
connectingFail: "لقد فشل الاتصال!",
downloadingImage: "جار تحميل الصورة...",
placeTile: "ضع بلاطة",
donate: "تبرع",
myAccount: "حسابي",
chat: "الدردشة",
liveChat: "الدردشة المباشرة:",
nicknameToContinue: "اكتب اسم مستعار للمواصلة:",
changeChannel: "تغيير القناة:",
captchaPrompt: "رجاءا قم بحل هذا اللغز...",
webappInstall: "تحميل الموقع كتطبيق",
connectionProblems: "مشاكل في الاتصال?",
tryClickingHere: "جرب ان تضغط هنا",
pleaseBeRespectful: "رجاءا كن محترما ولا تزعج الاخرين!",
enterNickname: "اكتب اسم مستعار...",
enterMessage: "اكتب الرسالة...",
signInInstead: "قم بتسجيل الدخول من هنا بدلا من ذلك",
createNewAccount: "انشئ حساب جديد",
mention: "ذِكر",
replyTo: "الرد على",
report: "ابلاغ",
block: "حظر",
unblock: "ازالة الحظر",
changeMyName: "غير اسمي",
putOnCanvas: "🫧 ضع على اللوحه",
sendInLiveChat: "📨 ارسل في الدردشة المباشرة",
overlayMenu: "قائمة الصوره",
modalAboutContent: "هنالك لوح فاضي.<br><br>تستطيع اضافة بلاطه, ولكن يجب عليك الانتظار لإضافة اخرى<br><br>لوحدك, تستطيع انشاء شيء.<br><br>مع الاخرين, تستطيع انشاء الكثير.",
overlayMenuDesciption: "تصور بناءك بصورة نموذج!",
messageNotFound: "لا يمكن تحميل الرسالة"
},
jp: {
connecting: "接続中...",
connectingFail: "接続できませんでした!",
downloadingImage: "画像をダウンロードしています...",
placeTile: "タイルを配置する",
donate: "寄付する",
myAccount: "マイアカウント",
chat: "チャット",
liveChat: "ライブチャット:",
nicknameToContinue: "続行するにはニックネームを入力してください:",
changeChannel: "チャンネルを変更:",
captchaPrompt: "rplace.live をすべての人が楽しめるように、この小さなキャプチャを解決してください...",
webappInstall: "rplace.live Web アプリをインストール",
connectionProblems: "接続の問題?",
tryClickingHere: "ここをクリックしてみてください",
pleaseBeRespectful: "敬意を払い、スパム行為をしないようにしてください!",
enterNickname: "ニックネームを入力してください...",
enterMessage: "メッセージを入力してください..."
}
}
const lang = navigator.language.split("-")[0]
//Thanks to (Discord) @carkan988"<%#1409, @anonimbiri#4089 for providing turkish translations, and @sorenaxmee#4191 and @rplacetk telegram contributors for persian translations, a big thanks (ig) to Cyart#9657 for romanian, greek and spanish translations, thanks to embed#2752 for french translation.
function translate(key) {
if (TRANSLATIONS[lang] != null)
return TRANSLATIONS[lang][key] || TRANSLATIONS["en"][key]
else
return TRANSLATIONS["en"][key] || key
}
function translateAll() {
document.querySelectorAll("[translate]").forEach((element) => {
const key = element.getAttribute("translate")
if (TRANSLATIONS[lang] == null) return
if (element.nodeName === "INPUT" || element.nodeName === "TEXTAREA") {
if (element.getAttribute("type") == "text")
element.placeholder = TRANSLATIONS[lang][key] || element.placeholder
else
element.value = TRANSLATIONS[lang][key] || element.value
}
else
element.innerHTML = TRANSLATIONS[lang][key] || element.innerHTML
})
}
class PublicPromise {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
}
}
class PublicPromiseSync {
locked
resolve
reject
#promise
constructor() {
this.#promise = new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
this.locked = false
}
async acquireAwaitPromise() {
if (this.locked) {
throw new Error("This promise is already being awaited.")
}
this.locked = true
try {
const result = await this.#promise
this.locked = false
return result
}
catch (error) {
this.locked = false
throw error
}
}
}
function sanitise(txt) {
return txt.replaceAll(/&/g,"&").replaceAll(/</g,"<").replaceAll(/"/g,""")
}
function markdownParse(text) {
// Headers
text = text.replace(/^(#{3}\s)(.+)/gm, (match, p1, p2) => {
return `<h3>${p2}</h3>`
})
text = text.replace(/^(#{2}\s)(.+)/gm, (match, p1, p2) => {
return `<h2>${p2}</h2>`
})
text = text.replace(/^(#{1}\s)(.+)/gm, (match, p1, p2) => {
return `<h1>${p2}</h1>`
})
// Bold
text = text.replace(/\*\*(.+?)\*\*/g, (match) => {
return `<b>${match.slice(2, -2)}</b>`
})
// Underline
text = text.replace(/__(.+?)__/g, (match) => {
return `<u>${match.slice(2, -2)}</u>`
})
// Italic
text = text.replace(/\*([^*]+?)\*/g, (match) => {
return `<i>${match.slice(1, -1)}</i>`
})
text = text.replace(/_(.+?)_/g, (match) => {
return `<i>${match.slice(1, -1)}</i>`
})
// Spoiler
text = text.replace(/\|\|([\s\S]+?)\|\|/g, (match) => {
return `<r-spoiler hidden="true">${match.slice(2, -2)}</r-spoiler>`
})
// Strikethrough
text = text.replace(/~(.+?)~/g, (match) => {
return `<s>${match.slice(1, -1)}</s>`
})
// Separator
text = text.replace(/^\s*---\s*$/gm, () => {
return "<hr>"
})
return text
}
// Utility functions for Auth DB IndexedDB caches
const currentAuthUrl = new URL(localStorage.auth || DEFAULT_AUTH) // i.e server.rplace.live/auth
const currentAuthDb = `${currentAuthUrl.host}${currentAuthUrl.pathname}`
function openCurrentAuthDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(currentAuthDb, 2)
request.onupgradeneeded = event => {
const db = event.target.result
db.createObjectStore("profiles", { keyPath: "id" })
db.createObjectStore("users", { keyPath: "id" })
}
request.onsuccess = event => resolve(event.target.result)
request.onerror = event => reject(event.target.error)
})
}
function getCachedData(storeName, key) {
return new Promise(async (resolve, reject) => {
const db = await openCurrentAuthDB()
const transaction = db.transaction(storeName, "readonly")
const store = transaction.objectStore(storeName)
const request = store.get(key)
request.onsuccess = event => resolve(event.target.result)
request.onerror = event => reject(event.target.error)
})
}
function setCachedData(storeName, data) {
return new Promise(async (resolve, reject) => {
const db = await openCurrentAuthDB()
const transaction = db.transaction(storeName, "readwrite")
const store = transaction.objectStore(storeName)
const request = store.put(data)
request.onsuccess = () => resolve()
request.onerror = event => reject(event.target.error)
})
}
// Responsible for setting and retrieving form DB, will attempt to grab the object from the DB, if it can't
// it will grab the object from the URL and then cache it in the database
async function cachedFetch(keystore, id, url, expiry) {
const now = Date.now()
let cachedObject = await getCachedData(keystore, id)
if (!cachedObject || (now - cachedObject.timestamp) > expiry) {
const res = await fetch(url)
if (!res.ok) {
console.error(`Could not fetch object ${id} belonging to ${keystore}: ${res.status} ${res.statusText}:`, await res.json())
return null
}
cachedObject = await res.json()
await setCachedData(keystore, { id, data: cachedObject, timestamp: now })
}
else {
cachedObject = cachedObject.data
}
return cachedObject
}
window.moduleExports = {
...window.moduleExports,
get DEFAULT_AUTH() {
return DEFAULT_AUTH
},
get markdownParse() {
return markdownParse
},
get sanitise() {
return sanitise
},
get cachedFetch() {
return cachedFetch
}
}