Wrong Chat

Just chat to chat with nearby players

当前为 2023-10-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Wrong Chat
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3.6
  5. // @description Just chat to chat with nearby players
  6. // @author unnamed
  7. // @match https://splix.io/
  8. // @match https://splix.io/flags
  9. // @grant none
  10. // @run-at document-start
  11. // @require https://greasyfork.org/scripts/478491-splix-js-demodularizer/code/splix-js-demodularizer.js?version=1271872
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. "use strict";
  16.  
  17. var source = `"use strict";
  18.  
  19. var wcFlags = {};
  20. var wcFlagList = [
  21. {
  22. "name": "wcConvertEmoticons",
  23. "caption": "Convert emoticons in your messages to emoji",
  24. "description": ":D ⟶ 😄",
  25. "type": "checkbox",
  26. "default": true
  27. },
  28. {
  29. "name": "wcConsoleLog",
  30. "caption": "Print messages to the browser console",
  31. "description": "Until the page is refreshed, you can see the message history in the console.",
  32. "type": "checkbox",
  33. "default": true
  34. },
  35. {
  36. "name": "wcConsoleLogTimeAmPm",
  37. "caption": "Use AM/PM time format for console log",
  38. "description": "Uncheck if you want to use 24 hour format",
  39. "type": "checkbox",
  40. "default": true
  41. },
  42. {
  43. "name": "wcMsgDisplayTime",
  44. "caption": "Message display time",
  45. "description": "The total display time (seconds) of the message.<br>If this value is zero, messages will only be deleted when the message limit is reached.",
  46. "type": "number",
  47. "default": 90,
  48. "min": 0,
  49. "max": 3600
  50. },
  51. {
  52. "name": "wcMsgDecayTime",
  53. "caption": "Message decay time",
  54. "description": "The time (seconds) during which the messages fade out.<br>It is part of the total display time.",
  55. "type": "number",
  56. "default": 20,
  57. "min": 0,
  58. "max": 3600
  59. },
  60. {
  61. "name": "wcMessageLimit",
  62. "caption": "Number of messages on screen",
  63. "description": "Maximum number of messages displayed on the screen.",
  64. "type": "number",
  65. "default": 20,
  66. "min": 0,
  67. "max": 50
  68. },
  69. {
  70. "name": "wcDefaultNotifications",
  71. "caption": "Show default notifications",
  72. "description": "Show default splix.io notifications instead of bottom right corner notifications.<br>This parameter only affects the notifications shown by the Wrong Chat script.",
  73. "type": "checkbox",
  74. "default": false
  75. },
  76. {
  77. "name": "wcPrivacyWarnings",
  78. "caption": "Enable privacy warnings",
  79. "description": "Disable this flag if you understand the risks of communicating in this chat.<br>Even if you don't see anyone nearby, someone can see your messages.<br>Also, do not disclose important information as the interlocutor may impersonate someone else.",
  80. "type": "checkbox",
  81. "default": true
  82. },
  83. {
  84. "name": "wcBlockedPlayers",
  85. "caption": "Blocked players",
  86. "description": "List of blocked players in JSON format.<br>Clear the field if you want to clear the list.",
  87. "type": "text",
  88. "default": {}
  89. },
  90. ];
  91.  
  92. (function wcGetFlags() {
  93. for (var i = 0; i < wcFlagList.length; i++) {
  94. var value = localStorage.getItem(wcFlagList[i].name);
  95. if (value !== null) {
  96. if (wcFlagList[i].type == "number") {
  97. value = Number.parseInt(value);
  98. if (Number.isNaN(value)) {
  99. value = wcFlagList[i].default;
  100. }
  101. }
  102. else if (wcFlagList[i].type == "checkbox") {
  103. value = (value == "true");
  104. }
  105. else if (wcFlagList[i].type == "text" && wcFlagList[i].name == "wcBlockedPlayers") {
  106. wcLoadBlockedPlayers();
  107. continue;
  108. }
  109. }
  110. else {
  111. value = wcFlagList[i].default;
  112. }
  113. wcFlags[wcFlagList[i].name] = value;
  114. }
  115. }());
  116.  
  117. if (window.location.pathname == "/flags") {
  118. (function wcFlagListAddControls() {
  119. addHTML('<h4 style="margin-top: 1.5em; margin-bottom: 1em">Wrong Chat Flags</h4>', 'body');
  120. for (var i = 0; i < wcFlagList.length; i++) {
  121. var flag = wcFlagList[i];
  122. var control = \`<label>\${flag.caption}\\t\${flag.name == "wcBlockedPlayers" ? "<br><textarea" : "<input"} name="\${flag.name}" type="\${flag.type}" \${(flag.type == "number") ?
  123. (' style="width: 50px"' + (('min' in flag) ? ' min="' + flag.min + '"' : " ") + (('max' in flag) ? ' max="' + flag.max + '"' : " ") + ' value="' + wcFlags[flag.name] + '"') :
  124. (flag.type == "text" ? (' style="width: 600px; height: 50px"') :
  125. ((flag.type == "checkbox" && wcFlags[flag.name]) ? " checked" : ""))}>\${flag.name == "wcBlockedPlayers" ? "</textarea>" : ""}</label>
  126. <div class="st">\${flag.description}</div>\`;
  127.  
  128. addHTML(control, "body");
  129.  
  130. if (flag.name == "wcBlockedPlayers") {
  131. document.querySelector('textarea[name="wcBlockedPlayers"]').value = JSON.stringify(wcFlags[flag.name]);
  132. }
  133.  
  134. document.querySelector("label:last-of-type input, label:last-of-type textarea").addEventListener("change", function (e) {
  135. if (e.target.type == "number") {
  136. if (!Number.isNaN(e.target.valueAsNumber)) {
  137. localStorage.setItem(e.target.name, e.target.value);
  138. wcFlags[e.target.name] = e.target.valueAsNumber;
  139. }
  140. }
  141. else if (e.target.type == "checkbox") {
  142. localStorage.setItem(e.target.name, e.target.checked);
  143. wcFlags[e.target.name] = e.target.checked;
  144. }
  145. else if (e.target.tagName == "TEXTAREA") {
  146. try {
  147. if (e.target.value.trim() === "") {
  148. localStorage.setItem("wcBlockedPlayers", "{}");
  149. }
  150. else {
  151. var obj = JSON.parse(e.target.value);
  152. localStorage.setItem("wcBlockedPlayers", JSON.stringify(obj));
  153. }
  154. wcLoadBlockedPlayers(false);
  155. }
  156. catch { }
  157. }
  158. });
  159. }
  160. }());
  161. throw new Error("Nobody will read the text of this error"); /** Stop code execution from here */
  162. }
  163.  
  164. var dict = ["especially", "understand", "everything", "themselves", "associated", "experience", "something", "sometimes", "different", "necessary", "important", "condition", "operation", "direction", "attention", "therefore", "character", "situation", "according", "questions", "described", "territory", "establish", "https://", "immortal", "parasite", "anything", "actually", "happened", "national", "question", "possible", "although", "probably", "business", "pressure", "together", "couldn't", "remember", "southern", "frequent", "interest", "children", "everyone", "continue", "whatever", "position", "features", "wouldn't", "movement", "declared", "complete", "presence", "consider", "appeared", "industry", "thousand", "thinking", "language", "majority", "consists", "distance", "involved", "yourself", "entirely", "increase", "standing", "property", "surround", "private", "browser", "careful", "connect", "discord", "http://", "latency", "message", "players", "protect", "refresh", "restart", "running", "through", "because", "against", "thought", "without", "between", "nothing", "another", "usually", "however", "country", "himself", "patient", "certain", "general", "brother", "already", "someone", "example", "finally", "surface", "believe", "brought", "doesn't", "several", "english", "perhaps", "becomes", "process", "present", "looking", "there's", "carried", "friends", "minutes", "they're", "applied", "whether", "morning", "control", "explain", "measure", "foreign", "hundred", "talking", "instead", "outside", "reached", "opinion", "removed", "fingers", "project", "forward", "similar", "primary", "further", "getting", "quickly", "changes", "towards", "opening", "natural", "account", "comment", "strange", "subject", "prevent", "serious", "special", "portion", "somehow", "decided", "western", "problem", "imagine", "growing", "clearly", "beneath", "college", "exactly", "feeling", "divided", "section", "contact", "parents", "despite", "support", "healing", "neither", "attack", "better", "blocks", "bottom", "coming", "fought", "inside", "killed", "leader", "played", "player", "script", "server", "square", "should", "before", "people", "little", "didn't", "around", "really", "though", "that's", "things", "enough", "course", "always", "almost", "during", "you're", "within", "become", "behind", "matter", "others", "either", "school", "second", "friend", "system", "having", "wasn't", "public", "common", "across", "number", "moment", "passed", "person", "follow", "itself", "pretty", "making", "stupid", "growth", "result", "myself", "reason", "became", "rather", "trying", "thread", "father", "figure", "beyond", "french", "master", "family", "saying", "months", "action", "nearly", "middle", "taking", "period", "affect", "anyone", "rights", "single", "nature", "longer", "ground", "toward", "twenty", "spread", "raised", "indeed", "window", "except", "cancer", "street", "answer", "muscle", "effect", "slowly", "severe", "change", "cannot", "strong", "europe", "office", "normal", "spirit", "mother", "living", "likely", "liable", "german", "closed", "entire", "employ", "occurs", "reddit", "caught", "unless", "sudden", "please", "happen", "sounds", "simple", "nation", "filled", "points", "placed", "source", "region", "hardly", "silver", "seeing", "easily", "giving", "chance", "amount", "method", "direct", "appear", "remain", "barely", "remark", "finger", "return", "broken", "manner", "anyway", "crash", "block", "board", "chase", "fight", "later", "loose", "north", "paint", "right", "south", "speak", "splix", "spoke", "start", "worst", "esque", "cracy", "cycle", "which", "there", "their", "would", "could", "other", "about", "after", "first", "those", "these", "still", "think", "don't", "where", "being", "under", "great", "years", "while", "never", "every", "state", "again", "place", "blood", "might", "house", "shall", "until", "found", "three", "thing", "small", "power", "night", "heard", "cases", "maybe", "whole", "least", "light", "asked", "point", "world", "large", "going", "often", "can't", "known", "hands", "labor", "party", "along", "nerve", "among", "treat", "parts", "wound", "cause", "black", "taken", "money", "given", "women", "since", "above", "means", "close", "young", "times", "tried", "voice", "forms", "white", "woman", "sense", "early", "hours", "human", "death", "trade", "bones", "leave", "class", "quite", "doing", "sound", "stood", "began", "front", "wrong", "words", "local", "alone", "force", "water", "short", "takes", "mouth", "civil", "order", "occur", "peace", "paper", "lower", "clear", "heart", "terms", "doubt", "brain", "makes", "we're", "isn't", "popul", "round", "bring", "third", "cells", "meant", "child", "moved", "sorry", "sleep", "below", "floor", "watch", "seems", "whose", "earth", "heavy", "guess", "field", "sight", "upper", "issue", "lines", "weeks", "polic", "cover", "eight", "court", "lived", "stuff", "level", "space", "table", "thank", "works", "rapid", "teach", "event", "mini", "nope", "afai", "ahah", "area", "asap", "best", "chat", "died", "draw", "drew", "east", "fake", "good", "haha", "head", "imho", "keep", "kill", "left", "lmao", "mass", "near", "noob", "ping", "play", "rofl", "semi", "side", "tail", "talk", "tell", "wall", "west", "yolo", "ance", "ence", "ible", "ical", "ious", "ment", "ness", "ship", "sion", "tion", "ways", "ette", "hood", "ward", "wise", "that", "inte", "comp", "cons", "cont", "with", "from", "comm", "this", "they", "have", "disc", "tran", "were", "when", "conf", "what", "been", "into", "conc", "more", "like", "just", "conv", "unde", "dist", "them", "reco", "some", "then", "time", "your", "only", "said", "even", "than", "coun", "over", "disp", "prop", "pres", "know", "supe", "upon", "such", "back", "spec", "prot", "fore", "char", "very", "down", "made", "para", "well", "it's", "disa", "will", "most", "also", "much", "long", "same", "part", "must", "here", "make", "many", "skin", "came", "away", "hand", "ight", "once", "come", "room", "take", "face", "work", "both", "bone", "high", "last", "look", "eyes", "each", "life", "went", "knew", "ever", "took", "call", "body", "case", "give", "days", "seen", "turn", "door", "told", "name", "used", "free", "does", "year", "less", "find", "half", "mind", "felt", "i've", "seem", "next", "four", "pain", "girl", "fact", "else", "land", "done", "dark", "soon", "home", "sure", "five", "shit", "mean", "want", "kind", "gave", "stor", "need", "feel", "read", "deep", "real", "help", "able", "dead", "hard", "held", "rest", "line", "gone", "show", "rise", "fuck", "kept", "size", "true", "past", "thus", "idea", "limb", "full", "yeah", "arms", "hear", "love", "i'll", "city", "lost", "dumb", "he's", "late", "king", "farm", "form", "cold", "open", "word", "post", "neck", "term", "soft", "sent", "hope", "self", "laid", "fire", "hair", "lead", "feet", "stop", "view", "live", "none", "fear", "care", "mark", "foot", "hold", "hour", "game", "miss", "vote", "seat", "wait", "cute", "army", "easy", "step", "grew", "main", "join", "save", "move", "hunt", "says", "loss", "whom", "fell", "meat", "wish", "ones", "pass", "gold", "fine", "town", "edge", "food", "serv", "rule", "deal", "type", "ends", "we'd", "bill", "tha", "tio", "nde", "nce", "edt", "tis", "oft", "sth", "boy", "con", "yep", "afk", "aka", "art", "bad", "brb", "cya", "d/c", "die", "dot", "faq", "fyi", "gr8", "hit", "idk", "ikr", "kek", "lag", "lmc", "lol", "net", "omg", "out", "pen", "say", "sup", "thx", "top", "wtf", "'ll", "'ve", "acy", "ate", "dom", "ely", "ent", "ess", "ful", "ify", "ing", "ise", "ish", "ism", "ist", "ity", "ive", "ize", "ked", "ous", "ted", "ant", "ary", "eer", "est", "ion", "ure", "pre", "dis", "the", "pro", "com", "and", "int", "tra", "per", "sta", "res", "gra", "str", "imp", "par", "rec", "was", "cha", "ins", "sub", "exp", "ove", "you", "inc", "rep", "for", "cor", "inf", "ind", "app", "had", "mar", "his", "ste", "not", "but", "rea", "col", "und", "cla", "cou", "pla", "mis", "ass", "acc", "che", "sha", "are", "dec", "sho", "des", "spe", "tri", "har", "bra", "chi", "all", "hea", "one", "ref", "pri", "rel", "cra", "bri", "ret", "mon", "her", "bar", "she", "cre", "def", "blo", "inv", "fla", "sca", "fre", "cal", "exc", "may", "shi", "sto", "whi", "tre", "him", "wor", "han", "spi", "bla", "spo", "att", "sur", "fra", "min", "gen", "dep", "bre", "rem", "pat", "mor", "thr", "who", "rev", "ext", "pur", "its", "unc", "scr", "bur", "ser", "bal", "uns", "thi", "pos", "ele", "sen", "reg", "any", "pol", "did", "del", "mal", "sti", "has", "sch", "lea", "cat", "spa", "stu", "win", "can", "bea", "bac", "squ", "fac", "bro", "new", "now", "mas", "adv", "cri", "cap", "mat", "cur", "ver", "how", "det", "fin", "dev", "see", "emp", "our", "clo", "sec", "two", "gro", "aut", "hor", "mil", "sol", "ter", "syn", "way", "i'm", "get", "man", "too", "map", "own", "off", "war", "why", "old", "got", "uni", "saw", "few", "day", "far", "men", "met", "let", "end", "yet", "use", "put", "yes", "act", "law", "fig", "set", "i'd", "due", "cut", "son", "bed", "red", "lot", "air", "god", "arm", "ran", "guy", "ten", "try", "add", "lay", "ask", "six", "big", "ago", "eye", "sir", "age", "bit", "run", "nor", "mom", "led", "ect", "sat", "kid", "dad", "ies", "pay", "car", "hot", "low", "fix", "ics", "iel", "iar", "ian", "re", "en", "nt", "ea", "ti", "io", "le", "ou", "ar", "de", "rt", "ve", "am", "go", "of", "in", "he", "to", "it", "an", "is", "on", "at", "as", "or", "be", "me", "ha", "by", "no", "so", "we", "hi", "my", "us", "if", "do", "up", "ur", "ed", "yo", "dr", "ah", "oh", "ok", "al", "ic", "er", "nd", "es", "st", "th", "ly", "fy"]
  165.  
  166. function decToAnyRadixStr(dec, radix = 186, shift = 186) {
  167. let result = "";
  168. if (dec < 0 || dec > 0x10FFFF) return result;
  169. let r;
  170. while (dec > 0) {
  171. r = dec % radix;
  172. dec = ~~(dec / radix);
  173. if (r > 0 || dec > 0) {
  174. result += String.fromCharCode(r + shift); // + shift to avoid interpretation "bytes" as ascii chars
  175. }
  176. }
  177. return result;
  178. }
  179.  
  180. function anyRadixStrToChar(str, radix = 186) {
  181. if (str.length > 3) return "*";
  182. let i, m = 1, result = 0;
  183. for (i = 0; i < str.length; i++) {
  184. result += str.charCodeAt(i) * m;
  185. m *= radix;
  186. }
  187. if (result < 0 || result > 0x10FFFF) return "*";
  188. return String.fromCodePoint(result);
  189. }
  190.  
  191. function wordToSymbols(wordCase, wordSides, wordIndex) {
  192. var control = wordCase * 24 + wordSides * 6 + Math.floor(wordIndex / 186);
  193.  
  194. if (control < 10) { }
  195. else if (control >= 10 && control < 31) {
  196. control += 1
  197. }
  198. else if (control == 31) {
  199. control = 127
  200. }
  201. else if (control > 31 && control < 72) {
  202. control += 96;
  203. }
  204. else {
  205. return "??";
  206. }
  207. return String.fromCharCode(control) + String.fromCharCode(wordIndex % 186 + 186);
  208. }
  209.  
  210. function codesToWord(control, wordCode) {
  211. if (wordCode > 185) {
  212. console.error(\`Bad word code (\${wordCode})\`)
  213. return "<Error>"
  214. }
  215.  
  216. if (control < 10) { }
  217. else if (control >= 11 && control < 32) {
  218. control -= 1
  219. }
  220. else if (control == 127) {
  221. control = 31
  222. }
  223. else if (control > 127 && control < 168) {
  224. control -= 96
  225. }
  226. else {
  227. console.error(\`Bad control code (\${control})\`);
  228. return "<Error>";
  229. }
  230.  
  231. var wordCase = Math.floor(control / 24);
  232. var wordSides = Math.floor((control % 24) / 6);
  233. var wordIndex = (control % 6) * 186 + wordCode;
  234. var word;
  235. if (wordIndex < 1116) {
  236. var word = dict[wordIndex];
  237. }
  238. else {
  239. console.error(\`Unknown word (\${control} \${wordCode})\`)
  240. return "<Error>";
  241. }
  242. return ((wordSides < 2) ? " " : "") +
  243. ((wordCase == 0) ? word : (wordCase == 1 ? word[0].toUpperCase() + word.substring(1) : word.toUpperCase())) +
  244. ((wordSides % 2 == 0) ? " " : "");
  245. }
  246.  
  247. function prepareASCIIString(src) {
  248. var code;
  249. var result = "";
  250. src = src.normalize("NFC");
  251. for (var letter of src) {
  252. code = letter.codePointAt();
  253. if (code == 9) {
  254. result += " ";
  255. }
  256. else if ((code >= 32 && code < 127) || code == 10) {
  257. result += letter;
  258. }
  259. else if (code > 127) {
  260. var base186 = decToAnyRadixStr(code);
  261. result += String.fromCharCode(167 + base186.length) + base186;
  262. }
  263. }
  264. return result;
  265. }
  266.  
  267. function compressWithDict(src) {
  268. var dictWordLength = 0,
  269. dictPosNewLength = 0,
  270. reResult,
  271. re,
  272. srcWord,
  273. srcWordPos,
  274. srcWordLC,
  275. srcWordCase,
  276. srcWordSides,
  277. srcWordLeftSide,
  278. srcWordRightSide,
  279. rSPos,
  280. rEPos,
  281. dictPos = 0,
  282. dictPos2;
  283.  
  284. src = prepareASCIIString(src);
  285.  
  286. while (dictPos < dict.length) {
  287. dictWordLength = dict[dictPos].length;
  288. re = new RegExp(\`['A-Za-z:\\/]{\${dictWordLength}}\`, 'g');
  289.  
  290. while (dictPosNewLength < dict.length && dictWordLength == dict[dictPosNewLength].length) { dictPosNewLength++ }
  291.  
  292. while ((reResult = re.exec(src)) != null) {
  293. srcWord = reResult[0];
  294. srcWordLC = srcWord.toLowerCase();
  295. srcWordPos = reResult.index;
  296. re.lastIndex = srcWordPos + 1;
  297. for (dictPos2 = dictPos; dictPos2 < dictPosNewLength; dictPos2++) {
  298. if (dict[dictPos2] == srcWordLC) {
  299. if (srcWord == srcWordLC) { srcWordCase = 0; }
  300. else if (srcWord == srcWord[0].toUpperCase() + srcWord.substring(1).toLocaleLowerCase()) { srcWordCase = 1; }
  301. else if (srcWord == srcWord.toUpperCase()) { srcWordCase = 2; }
  302. else {
  303. break;
  304. }
  305.  
  306. srcWordLeftSide = src.substring(srcWordPos - 1, srcWordPos);
  307. srcWordRightSide = src.substring(srcWordPos + dictWordLength, srcWordPos + dictWordLength + 1);
  308. if (srcWordLeftSide == " " && srcWordRightSide == " ") { srcWordSides = 0 }
  309. else if (srcWordLeftSide == " ") { srcWordSides = 1 }
  310. else if (srcWordRightSide == " ") { srcWordSides = 2 }
  311. else if (dictWordLength > 2) { srcWordSides = 3 }
  312. else {
  313. break;
  314. }
  315.  
  316. rSPos = srcWordSides < 2 ? srcWordPos - 1 : srcWordPos;
  317. rEPos = (srcWordSides % 2 == 0) ? srcWordPos + dictWordLength + 1 : srcWordPos + dictWordLength;
  318. src = src.substring(0, rSPos) + wordToSymbols(srcWordCase, srcWordSides, dictPos2) + src.substring(rEPos);
  319.  
  320. if (srcWordSides > 1) { re.lastIndex = srcWordPos + 2; }
  321. break;
  322. }
  323. }
  324. }
  325. dictPos = dictPosNewLength;
  326. }
  327.  
  328. let result = "";
  329. let code;
  330. for (var i = 0; i < src.length; i++) {
  331. code = src.charCodeAt(i);
  332. if (code >= 186) {
  333. result += String.fromCharCode(code - 186);
  334. }
  335. else {
  336. result += src[i];
  337. }
  338. }
  339. return result;
  340. }
  341.  
  342. function decompressWithDict(src) {
  343. var result = "",
  344. i = 0,
  345. code;
  346. while (i < src.length) {
  347. code = src.codePointAt(i);
  348. if ((code > 31 && code < 127) || code == 10) {
  349. result += src[i];
  350. i += 1;
  351. }
  352. else if (code > 167 && code <= 170) {
  353. code -= 167;
  354. result += anyRadixStrToChar(src.substring(i + 1, i + 1 + code));
  355. i += 1 + code;
  356. }
  357. else if (code > 170) {
  358. console.error(\`decompressASCII: bad code (\${code})\`);
  359. return result + " ... <Error>";
  360. }
  361. else {
  362. result += codesToWord(code, src.codePointAt(i + 1));
  363. i += 2;
  364. }
  365. }
  366. return result;
  367. }
  368.  
  369. /**
  370. * SCSU - Standard Compression Scheme for Unicode implementation for JavaScript
  371. *
  372. * Provides SCSU encoding/decoding of UTF-8 strings
  373. * Suitable for better LZF compression for UTF-8 strings
  374. * Based on Java source of SCSU by Unicode, Inc. (http:
  375. *
  376. * @class Provides methods for SCSU encoding/decoding
  377. * @author Alexey A.Znaev (znaeff@mail.ru) (http:
  378. * @copyright Copyright (C) 2011-2012 Alexey A.Znaev
  379. * @license http:
  380. * @version 1.0
  381. */
  382. function SCSU() { }
  383.  
  384. SCSU.prototype._SQ0 = 0x01;
  385. SCSU.prototype._SQ1 = 0x02;
  386. SCSU.prototype._SQ2 = 0x03;
  387. SCSU.prototype._SQ3 = 0x04;
  388. SCSU.prototype._SQ4 = 0x05;
  389. SCSU.prototype._SQ5 = 0x06;
  390. SCSU.prototype._SQ6 = 0x07;
  391. SCSU.prototype._SQ7 = 0x08;
  392.  
  393. SCSU.prototype._SDX = 0x0B;
  394. SCSU.prototype._Srs = 0x0C;
  395.  
  396. SCSU.prototype._SQU = 0x0E;
  397. SCSU.prototype._SCU = 0x0F;
  398.  
  399. SCSU.prototype._SC0 = 0x10;
  400. SCSU.prototype._SC1 = 0x11;
  401. SCSU.prototype._SC2 = 0x12;
  402. SCSU.prototype._SC3 = 0x13;
  403. SCSU.prototype._SC4 = 0x14;
  404. SCSU.prototype._SC5 = 0x15;
  405. SCSU.prototype._SC6 = 0x16;
  406. SCSU.prototype._SC7 = 0x17;
  407. SCSU.prototype._SD0 = 0x18;
  408. SCSU.prototype._SD1 = 0x19;
  409. SCSU.prototype._SD2 = 0x1A;
  410. SCSU.prototype._SD3 = 0x1B;
  411. SCSU.prototype._SD4 = 0x1C;
  412. SCSU.prototype._SD5 = 0x1D;
  413. SCSU.prototype._SD6 = 0x1E;
  414. SCSU.prototype._SD7 = 0x1F;
  415.  
  416. SCSU.prototype._UC0 = 0xE0;
  417. SCSU.prototype._UC1 = 0xE1;
  418. SCSU.prototype._UC2 = 0xE2;
  419. SCSU.prototype._UC3 = 0xE3;
  420. SCSU.prototype._UC4 = 0xE4;
  421. SCSU.prototype._UC5 = 0xE5;
  422. SCSU.prototype._UC6 = 0xE6;
  423. SCSU.prototype._UC7 = 0xE7;
  424. SCSU.prototype._UD0 = 0xE8;
  425. SCSU.prototype._UD1 = 0xE9;
  426. SCSU.prototype._UD2 = 0xEA;
  427. SCSU.prototype._UD3 = 0xEB;
  428. SCSU.prototype._UD4 = 0xEC;
  429. SCSU.prototype._UD5 = 0xED;
  430. SCSU.prototype._UD6 = 0xEE;
  431. SCSU.prototype._UD7 = 0xEF;
  432.  
  433. SCSU.prototype._UQU = 0xF0;
  434. SCSU.prototype._UDX = 0xF1;
  435. SCSU.prototype._Urs = 0xF2;
  436.  
  437. SCSU.prototype._gapThreshold = 0x68;
  438. SCSU.prototype._gapOffset = 0xAC00;
  439.  
  440. SCSU.prototype._reservedStart = 0xA8;
  441. SCSU.prototype._fixedThreshold = 0xF9;
  442.  
  443. SCSU.prototype._fixedOffset = [0x00C0, 0x0250, 0x0370, 0x0530, 0x3040, 0x30A0, 0xFF60];
  444.  
  445. SCSU.prototype._staticOffset = [0x0000, 0x0080, 0x0100, 0x0300, 0x2000, 0x2080, 0x2100, 0x3000];
  446.  
  447. SCSU.prototype._initialDynamicOffset = [0x0080, 0x00C0, 0x0400, 0x0600, 0x0900, 0x3040, 0x30A0, 0xFF00];
  448.  
  449. /**
  450. * Encodes UTF-8 string using SCSU algorithm
  451. *
  452. * @param {String} str UTF-8 string
  453. * @return {String} SCSU-encoded string
  454. * @throws {SCSUError}
  455. */
  456. SCSU.prototype.compress = function (str) {
  457. var iLen = 0, ch, ch2, iprevWindow;
  458. this._reset();
  459. this.sIn = str;
  460. this.iInLen = str.length;
  461. this.aOut = [];
  462. while (this.iIn < this.iInLen) {
  463. if (this.iSCU != -1) {
  464. ch = this._outputUnicodeRun();
  465. if (this.aOut.length - this.iSCU == 3) {
  466. this.aOut[this.iSCU] = this._SQU;
  467. this.iSCU = -1;
  468. continue;
  469. } else {
  470. this.iSCU = -1;
  471. this.fUnicodeMode = true;
  472. }
  473. } else ch = this._outputSingleByteRun();
  474. if (this.iIn == this.iInLen) break;
  475. for (var ich = this.iIn; ch < 0x80; ich++) {
  476. if (ich == this.iInLen || !this._isCompressible(this.sIn.charCodeAt(ich))) {
  477. ch = this.sIn.charCodeAt(this.iIn);
  478. break;
  479. }
  480. ch = this.sIn.charCodeAt(ich);
  481. }
  482. iprevWindow = this.iSelectedWindow;
  483. if (ch < 0x80 || this._locateWindow(ch, this.dynamicOffset)) {
  484. if (!this.fUnicodeMode && this.iIn < this.iInLen - 1) {
  485. ch2 = this.sIn.charCodeAt(this.iIn + 1);
  486. if (ch2 >= this.dynamicOffset[iprevWindow] && ch2 < this.dynamicOffset[iprevWindow] + 0x80) {
  487. this._quoteSingleByte(ch);
  488. this.iSelectedWindow = iprevWindow;
  489. continue;
  490. }
  491. }
  492. this.aOut.push(((this.fUnicodeMode ? this._UC0 : this._SC0) + this.iSelectedWindow) & 255);
  493. this.fUnicodeMode = false;
  494. } else if (!this.fUnicodeMode && this._locateWindow(ch, this._staticOffset)) {
  495. this._quoteSingleByte(ch);
  496. this.iSelectedWindow = iprevWindow;
  497. continue;
  498. } else if (this._positionWindow(ch)) {
  499. this.fUnicodeMode = false;
  500. } else {
  501. this.iSCU = this.aOut.length;
  502. this.aOut.push(this._SCU);
  503. continue;
  504. }
  505. }
  506. delete this.sIn;
  507. var a_out_symbols = [];
  508. for (var i = 0; i < this.aOut.length; i++) a_out_symbols.push(String.fromCharCode(this.aOut[i]));
  509. delete this.aOut;
  510. return a_out_symbols.join('');
  511. }
  512.  
  513. /**
  514. * Decodes SCSU-encoded string to UTF-8 one
  515. *
  516. * @param {String} str SCSU-encoded string
  517. * @return {String} UTF-8 string
  518. * @throws {SCSUError}
  519. */
  520. SCSU.prototype.decompress = function (str) {
  521. this._reset();
  522. this.sIn = str;
  523. this.iInLen = str.length;
  524. var sOut = '';
  525. var iStaticWindow, iDynamicWindow, ch;
  526. Loop:
  527. for (var iCur = 0; iCur < this.iInLen; iCur++) {
  528. iStaticWindow = 0;
  529. iDynamicWindow = this.iSelectedWindow;
  530. Switch:
  531. switch (this.sIn.charCodeAt(iCur)) {
  532. case this._SQ0:
  533. case this._SQ1:
  534. case this._SQ2:
  535. case this._SQ3:
  536. case this._SQ4:
  537. case this._SQ5:
  538. case this._SQ6:
  539. case this._SQ7:
  540. if (iCur >= this.iInLen - 1) break Loop;
  541. iDynamicWindow = iStaticWindow = this.sIn.charCodeAt(iCur) - this._SQ0;
  542. iCur++;
  543. default:
  544. if (this.sIn.charCodeAt(iCur) < 128) {
  545. ch = this.sIn.charCodeAt(iCur) + this._staticOffset[iStaticWindow];
  546. sOut += String.fromCharCode(ch);
  547. } else {
  548. ch = this.sIn.charCodeAt(iCur);
  549. ch -= 0x80;
  550. ch += this.dynamicOffset[iDynamicWindow];
  551. if (ch < 1 << 16) {
  552. sOut += String.fromCharCode(ch);
  553. } else {
  554. ch -= 0x10000;
  555. sOut += String.fromCharCode(0xD800 + (ch >> 10));
  556. sOut += String.fromCharCode(0xDC00 + (ch & ~0xFC00));
  557. }
  558. }
  559. break;
  560. case this._SDX:
  561. iCur += 2;
  562. if (iCur >= this.iInLen) break Loop;
  563. this._defineExtendedWindow(this._charFromTwoBytes(aIn[iCur - 1], this.sIn.charCodeAt(iCur)));
  564. break;
  565. case this._SD0:
  566. case this._SD1:
  567. case this._SD2:
  568. case this._SD3:
  569. case this._SD4:
  570. case this._SD5:
  571. case this._SD6:
  572. case this._SD7:
  573. iCur++;
  574. if (iCur >= this.iInLen) break Loop;
  575. this._defineWindow(this.sIn.charCodeAt(iCur - 1) - this._SD0, this.sIn.charCodeAt(iCur));
  576. break;
  577. case this._SC0:
  578. case this._SC1:
  579. case this._SC2:
  580. case this._SC3:
  581. case this._SC4:
  582. case this._SC5:
  583. case this._SC6:
  584. case this._SC7:
  585. this.iSelectedWindow = this.sIn.charCodeAt(iCur) - this._SC0;
  586. break;
  587. case this._SCU:
  588. iCur++;
  589. for (var b; iCur < this.iInLen - 1; iCur += 2) {
  590. b = this.sIn.charCodeAt(iCur);
  591. if (b >= this._UC0 && b <= this._UC7) {
  592. this.iSelectedWindow = b - this._UC0;
  593. break Switch;
  594. } else if (b >= this._UD0 && b <= this._UD7) {
  595. this._defineWindow(b - this._UD0, this.sIn.charCodeAt(iCur + 1));
  596. iCur++;
  597. break Switch;
  598. } else if (b == this._UDX) {
  599. this._defineExtendedWindow(this._charFromTwoBytes(this.sIn.charCodeAt(iCur + 1), this.sIn.charCodeAt(iCur + 2)));
  600. iCur += 2;
  601. break Switch;
  602. } else if (b == this._UQU) {
  603. iCur++;
  604. }
  605. sOut += String.fromCharCode(this._charFromTwoBytes(this.sIn.charCodeAt(iCur), this.sIn.charCodeAt(iCur + 1)));
  606. }
  607. if (iCur != this.iInLen) throw new SCSUError(this._errorText(0x11));
  608. break;
  609. case this._SQU:
  610. iCur += 2;
  611. if (iCur >= this.iInLen) {
  612. break Loop;
  613. } else {
  614. ch = this._charFromTwoBytes(this.sIn.charCodeAt(iCur - 1), this.sIn.charCodeAt(iCur));
  615. sOut += String.fromCharCode(ch);
  616. }
  617. break;
  618. case this._Srs:
  619. throw new SCSUError(this._errorText(0x16, 'Pos. ' + iCur + '.'));
  620. }
  621. }
  622. delete this.sIn;
  623. if (iCur < this.iInLen) throw new SCSUError(this._errorText(0x11));
  624. return sOut;
  625. }
  626.  
  627. SCSU.prototype._isCompressible = function (ch) {
  628. return (ch < 0x3400 || ch >= 0xE000);
  629. }
  630.  
  631. SCSU.prototype._reset = function () {
  632. this.iIn = 0;
  633. this.iSelectedWindow = 0;
  634. this.dynamicOffset = this._initialDynamicOffset.slice(0);
  635. this.iSCU = -1;
  636. this.fUnicodeMode = false;
  637. this.iNextWindow = 3;
  638. }
  639.  
  640. SCSU.prototype._locateWindow = function (ch, offsetTable) {
  641. var iWin = this.iSelectedWindow;
  642. if (iWin != - 1 && ch >= offsetTable[iWin] && ch < offsetTable[iWin] + 0x80) return true;
  643. for (iWin = 0; iWin < offsetTable.length; iWin++) {
  644. if (ch >= offsetTable[iWin] && ch < offsetTable[iWin] + 0x80) {
  645. this.iSelectedWindow = iWin;
  646. return true;
  647. }
  648. }
  649. return false;
  650. }
  651.  
  652. SCSU.prototype._isAsciiCrLfOrTab = function (ch) {
  653. return (ch >= 0x20 && ch <= 0x7F) || ch == 0x09 || ch == 0x0A || ch == 0x0D;
  654. }
  655.  
  656. SCSU.prototype._outputSingleByteRun = function () {
  657. var iWin = this.iSelectedWindow, ch, ch2, byte1, byte2, aInLen;
  658. while (this.iIn < this.iInLen) {
  659. this.iOutLen = 0;
  660. byte1 = 0;
  661. byte2 = 0;
  662. ch = this.sIn.charCodeAt(this.iIn);
  663. aInLen = 1;
  664. if ((ch & 0xF800) == 0xD800) {
  665. if ((ch & 0xFC00) == 0xDC00) {
  666. throw new SCSUError(this._errorText(0x12, 'Byte #' + this.iIn + '.'));
  667. } else {
  668. if (this.iIn >= this.iInLen - 1) throw new SCSUError(this._errorText(0x11));
  669. ch2 = this.sIn.charCodeAt(this.iIn + 1);
  670. if ((ch2 & 0xFC00) != 0xDC00) throw new SCSUError(this._errorText(0x13, 'Byte #' + (this.iIn + 1) + '.'));
  671. ch = ((ch - 0xD800) << 10 | (ch2 - 0xDC00)) + 0x10000;
  672. aInLen = 2;
  673. }
  674. }
  675. if (this._isAsciiCrLfOrTab(ch) || ch == 0) {
  676. byte2 = ch & 0x7F;
  677. this.iOutLen = 1;
  678. } else if (ch < 0x20) {
  679. byte1 = this._SQ0;
  680. byte2 = ch;
  681. this.iOutLen = 2;
  682. } else if (ch >= this.dynamicOffset[iWin] && ch < this.dynamicOffset[iWin] + 0x80) {
  683. ch -= this.dynamicOffset[iWin];
  684. byte2 = (ch | 0x80) & 255;
  685. this.iOutLen = 1;
  686. }
  687. switch (this.iOutLen) {
  688. default:
  689. return ch;
  690. case 2:
  691. this.aOut.push(byte1);
  692. case 1:
  693. this.aOut.push(byte2);
  694. break;
  695. }
  696. this.iIn += aInLen;
  697. }
  698. return 0;
  699. }
  700.  
  701. SCSU.prototype._quoteSingleByte = function (ch) {
  702. var iWin = this.iSelectedWindow, ch;
  703. this.aOut.push((this._SQ0 + iWin) & 255);
  704. if (ch >= this.dynamicOffset[iWin] && ch < this.dynamicOffset[iWin] + 0x80) {
  705. ch -= this.dynamicOffset[iWin];
  706. this.aOut.push((ch | 0x80) & 255);
  707. } else if (ch >= this._staticOffset[iWin] && ch < this._staticOffset[iWin] + 0x80) {
  708. ch -= this._staticOffset[iWin];
  709. this.aOut.push(ch & 255);
  710. } else throw new SCSUError(this._errorText(0x00, 'ch = ' + ch + ' not valid in _quoteSingleByte.'));
  711. this.iIn++;
  712. }
  713.  
  714. SCSU.prototype._outputUnicodeRun = function () {
  715. var ch = 0, ch2;
  716. while (this.iIn < this.iInLen) {
  717. ch = this.sIn.charCodeAt(this.iIn);
  718. this.iOutLen = 2;
  719. if (this._isCompressible(ch)) {
  720. if (this.iIn < this.iInLen - 1) {
  721. ch2 = this.sIn.charCodeAt(this.iIn + 1);
  722. if (this._isCompressible(ch2)) break;
  723. }
  724. if (ch >= 0xE000 && ch <= 0xF2FF) this.iOutLen = 3;
  725. }
  726. if (this.iOutLen == 3) this.aOut.push(this._UQU);
  727. this.aOut.push((ch >> 8) & 255);
  728. this.aOut.push(ch & 0xFF);
  729. this.iIn++;
  730. }
  731. return ch;
  732. }
  733.  
  734. SCSU.prototype._positionWindow = function (ch) {
  735. var iWin = this.iNextWindow % 8, iPosition = 0, ch;
  736. if (ch < 0x80) throw new SCSUError(this._errorText(0x00, 'ch < 0x80.'));
  737. for (var i = 0; i < this._fixedOffset.length; i++) {
  738. if (ch >= this._fixedOffset[i] && ch < this._fixedOffset[i] + 0x80) {
  739. iPosition = i;
  740. break;
  741. }
  742. }
  743. if (iPosition != 0) {
  744. this.dynamicOffset[iWin] = this._fixedOffset[iPosition];
  745. iPosition += 0xF9;
  746. } else if (ch < 0x3400) {
  747. iPosition = ch >> 7;
  748. this.dynamicOffset[iWin] = ch & 0xFF80;
  749. } else if (ch < 0xE000) {
  750. return false;
  751. } else if (ch <= 0xFFFF) {
  752. iPosition = ((ch - this._gapOffset) >> 7);
  753. this.dynamicOffset[iWin] = ch & 0xFF80;
  754. } else {
  755. iPosition = (ch - 0x10000) >> 7;
  756. iPosition |= iWin << 13;
  757. this.dynamicOffset[iWin] = ch & 0x1FFF80;
  758. }
  759. if (iPosition < 0x100) {
  760. this.aOut.push(((this.fUnicodeMode ? this._UD0 : this._SD0) + iWin) & 255);
  761. this.aOut.push(iPosition & 0xFF);
  762. } else if (iPosition >= 0x100) {
  763. this.aOut.push(this.fUnicodeMode ? this._UDX : this._SDX);
  764. this.aOut.push((iPosition >> 8) & 0xFF);
  765. this.aOut.push(iPosition & 0xFF);
  766. }
  767. this.iSelectedWindow = iWin;
  768. this.iNextWindow++;
  769. return true;
  770. }
  771.  
  772. SCSU.prototype._defineWindow = function (iWin, bOffset) {
  773. var iOffset = (bOffset < 0 ? bOffset + 256 : bOffset);
  774. if (iOffset == 0) {
  775. throw new SCSUError(this._errorText(0x14));
  776. } else if (iOffset < this._gapThreshold) {
  777. this.dynamicOffset[iWin] = iOffset << 7;
  778. } else if (iOffset < this._reservedStart) {
  779. this.dynamicOffset[iWin] = (iOffset << 7) + this._gapOffset;
  780. } else if (iOffset < this._fixedThreshold) {
  781. throw new SCSUError(this._errorText(0x15, 'Value = ' + iOffset + '.'));
  782. } else {
  783. this.dynamicOffset[iWin] = this._fixedOffset[iOffset - this._fixedThreshold];
  784. }
  785. this.iSelectedWindow = iWin;
  786. }
  787.  
  788. SCSU.prototype._defineExtendedWindow = function (chOffset) {
  789. var iWin = chOffset >> 13;
  790. this.dynamicOffset[iWin] = ((chOffset & 0x1FFF) << 7) + (1 << 16);
  791. this.iSelectedWindow = iWin;
  792. }
  793.  
  794. SCSU.prototype._charFromTwoBytes = function (hi, lo) {
  795. var ch = (lo >= 0 ? lo : 256 + lo);
  796. return (ch + ((hi >= 0 ? hi : 256 + hi) << 8));
  797. }
  798.  
  799. SCSU.prototype._ERRORS = {
  800. 0x00: 'Internal error.',
  801. 0x10: 'Illegal input.',
  802. 0x11: 'Ended prematurely.',
  803. 0x12: 'Unpaired low surrogate.',
  804. 0x13: 'Unpaired high surrogate.',
  805. 0x14: 'Zero offset.',
  806. 0x15: 'Bad offset.',
  807. 0x16: 'Srs byte found.',
  808. 0x20: 'Bad output.'
  809. };
  810.  
  811. SCSU.prototype._errorText = function (code, text) {
  812. if (code == null || (typeof code != 'number') || code < 0 || code > 0xFF) code = 0x00;
  813. if (text == null || (typeof text != 'string')) text = '';
  814. var message = '';
  815. var code_class = code & 0xF0;
  816. if (this._ERRORS[code_class]) message = this._ERRORS[code_class];
  817. if ((code != code_class) && this._ERRORS[code]) message += ' ' + this._ERRORS[code];
  818. return ('SCSU 0x' + code.toString(16) + ': ' + message + (text == '' ? '' : ' ') + text);
  819. }
  820.  
  821. /**
  822. * SCSU Errors exceptions
  823. *
  824. * @class Constructs exceptions of SCSU errors
  825. * @author Alexey A.Znaev (znaeff@mail.ru) (http:
  826. * @copyright Copyright (C) 2011-2012 Alexey A.Znaev
  827. * @license http:
  828. * @version 1.0
  829. */
  830. function SCSUError(msg) { this.message = msg; this.name = 'SCSUError' };
  831. SCSUError.prototype = new Error();
  832.  
  833. function removeControlASCIISymbols(str) {
  834. var code;
  835. var result = "";
  836. for (var i = 0; i < str.length; i++) {
  837. code = str.codePointAt(i);
  838. if (code == 9) {
  839. result += " ";
  840. }
  841. else if ((code >= 32 && code < 127) || code > 127 || code == 10) {
  842. result += str[i]
  843. }
  844. }
  845. return result;
  846. }
  847.  
  848. function compressWithSCSU(src) {
  849. src = removeControlASCIISymbols(src);
  850. var result;
  851. try {
  852. result = SCSU.prototype.compress(src);
  853. }
  854. catch (e) {
  855. console.error("SCSU compression error:", e);
  856. return null;
  857. }
  858. return result;
  859. }
  860.  
  861. function decompressWithSCSU(src) {
  862. var result;
  863. try {
  864. result = SCSU.prototype.decompress(src);
  865. }
  866. catch (e) {
  867. //console.error("SCSU decompression error:", e);
  868. return null;
  869. }
  870. return result;
  871. }
  872.  
  873. function unicodeToRadix183Str(src) {
  874. var result = "",
  875. code;
  876. for (var letter of src) {
  877. code = letter.codePointAt();
  878. if (code < 183) {
  879. result += letter;
  880. }
  881. else if (code > 127) {
  882. var base183 = decToAnyRadixStr(code, 183, 0);
  883. result += String.fromCharCode(182 + base183.length) + base183;
  884. }
  885. }
  886. return result;
  887. }
  888.  
  889. function radix183StrToUnicode(src) {
  890. var result = "",
  891. i = 0,
  892. code;
  893. while (i < src.length) {
  894. code = src.codePointAt(i);
  895. if (code < 183) {
  896. result += src[i];
  897. i += 1;
  898. }
  899. else {
  900. code -= 182;
  901. result += anyRadixStrToChar(src.substring(i + 1, i + 1 + code), 183, 0);
  902. i += 1 + code;
  903. }
  904. }
  905. return result;
  906. }
  907.  
  908. function strToHonkMsg(src) {
  909. var encodingType,
  910. result;
  911.  
  912. if (src.length == 0) {
  913. encodingType = 3;
  914. result = " ";
  915. }
  916. else {
  917. var codedWithDict = compressWithDict(src);
  918. var codedWithoutDict = compressWithSCSU(src);
  919.  
  920. var scsu = false;
  921. if (codedWithoutDict !== null) {
  922. if (decompressWithSCSU(codedWithoutDict) !== null) {
  923. codedWithoutDict = unicodeToRadix183Str(codedWithoutDict);
  924. scsu = true;
  925. }
  926. }
  927.  
  928. if (!scsu) {
  929. codedWithoutDict = unicodeToRadix183Str(src);
  930. }
  931.  
  932. if (codedWithDict.length < codedWithoutDict.length) {
  933. result = codedWithDict;
  934. encodingType = 1;
  935. }
  936. else {
  937. result = codedWithoutDict;
  938. encodingType = scsu ? 2 : 3;
  939. }
  940. }
  941.  
  942. if (result.length > 186 || result.length < 1) {
  943. return result.length;
  944. }
  945. else {
  946. return String.fromCharCode(encodingType) + String.fromCharCode(result.length - 1) + result;
  947. }
  948. }
  949.  
  950. function honkMsgToString(src) {
  951. const errMsg = "<Decoding error>";
  952. var srcLen = src.length,
  953. result = "";
  954. if (srcLen < 3 || srcLen > 186 + 2) {
  955. console.error(\`honkMsgToString: incorrect length of source (\${src.length})\`);
  956. return errMsg;
  957. }
  958. else {
  959. var encodingType = src.codePointAt(0);
  960. if (encodingType < 1 || encodingType > 3) {
  961. console.error(\`honkMsgToString: incorrect encoding type (\${encodingType})\`);
  962. return errMsg;
  963. }
  964.  
  965. var textLen = src.codePointAt(1);
  966. if (srcLen - 2 != textLen) {
  967. console.error(\`honkMsgToString: incorrect message text length (\${srcLen} / \${textLen})\`);
  968. return errMsg;
  969. }
  970.  
  971. try {
  972. if (encodingType == 1) {
  973. result = decompressWithDict(src.substring(2));
  974. }
  975. else if (encodingType == 2) {
  976. result = decompressWithSCSU(radix183StrToUnicode(src.substring(2)));
  977. }
  978. else if (encodingType == 3) {
  979. result = radix183StrToUnicode(src.substring(2));
  980. }
  981.  
  982. return result.replace(/(\\r\\n|\\r|\\n){2,}/g, '\$1\\n');
  983. }
  984. catch (e) {
  985. console.error(\`honkMsgToString: decompression error (type \${encodingType})\`, e);
  986. return errMsg;
  987. }
  988. }
  989. }
  990.  
  991. var wcMessage = "";
  992. var wcMessagePos = 0;
  993.  
  994. function wcSendMsgHonk() {
  995. if (playingAndReady) {
  996. var code = wcMessage.charCodeAt(wcMessagePos) + 70;
  997. wcMessagePos += 1;
  998. if (!wsSendMsg(sendAction.HONK, code)) wsMsgSendStatus = 0;
  999. myPlayer.doHonk(code);
  1000. if (wcMessagePos < wcMessage.length) {
  1001. setTimeout(wcSendMsgHonk, ((wcMessagePos < 13e0) ? 50e0 : ((wcMessagePos < 26e0) ? 65e0 : 85e0)));
  1002. }
  1003. else {
  1004. wsMsgSendStatus = 0;
  1005. }
  1006. }
  1007. else {
  1008. wsMsgSendStatus = 0;
  1009. }
  1010. }
  1011.  
  1012. function wcSendMessage(message) {
  1013. wcMessage = String.fromCharCode(1) + String.fromCharCode(57) + message;
  1014. wcMessagePos = 0;
  1015. wsMsgSendStatus = 1;
  1016. wcSendMsgHonk();
  1017. }
  1018.  
  1019. function wcMsgHonkHandler(player, value) {
  1020. if (player.msgStatus == 0) {
  1021. if (value == 71 && !wcIsPlayerBlocked(player.name, player.skinBlock)) {
  1022. player.msgStatus = 1;
  1023. }
  1024. else return;
  1025. }
  1026. else if (player.msgStatus == 1) {
  1027. if (value == 127) {
  1028. player.msgStatus = 2;
  1029. }
  1030. else if (value !== 71) {
  1031. player.msgStatus = 0;
  1032. }
  1033. }
  1034. else if (player.msgStatus == 2) {
  1035. if (value >= 71 && value <= 73) {
  1036. player.msgStatus = 3;
  1037. player.message = String.fromCharCode(value - 70);
  1038. }
  1039. else {
  1040. player.msgStatus = 0;
  1041. }
  1042. }
  1043. else if (player.msgStatus == 3) {
  1044. player.msgStatus = 4;
  1045. player.msgLength = value - 69 + 2;
  1046. player.message += String.fromCharCode(value - 69);
  1047. }
  1048. else {
  1049. player.message += String.fromCharCode(value - 70);
  1050. if (player.message.length >= player.msgLength) {
  1051. wcNewMessage(player.message, player.name, player.skinBlock);
  1052. player.msgStatus = 0;
  1053. }
  1054. }
  1055. }
  1056.  
  1057. function honkEndWC() {
  1058. if (wsMsgSendStatus === 0) {
  1059. var e = Date.now();
  1060. if (lastHonkTime < e) {
  1061. var t = clamp(t = e - honkStartTime, 0, 1e3);
  1062. lastHonkTime = e + t,
  1063. t = iLerp(0, 1e3, t),
  1064. t *= 255,
  1065. t = Math.floor(t),
  1066. wsSendMsg(sendAction.HONK, t);
  1067. for (var n = 0; n < players.length; n++) {
  1068. var a = players[n];
  1069. a.isMyPlayer && a.doHonk(Math.max(70, t))
  1070. }
  1071. }
  1072. }
  1073. }
  1074.  
  1075. function getPlayerWC(e, t) {
  1076. var n;
  1077. void 0 === t && (t = players);
  1078. for (var a = 0; a < t.length; a++)
  1079. if ((n = t[a]).id == e)
  1080. return n;
  1081. return n = {
  1082. id: e,
  1083. pos: [0, 0],
  1084. drawPos: [-1, -1],
  1085. drawPosSet: !1,
  1086. serverPos: [0, 0],
  1087. dir: 0,
  1088. isMyPlayer: 0 === e,
  1089. isDead: !1,
  1090. deathWasCertain: !1,
  1091. didUncertainDeathLastTick: !1,
  1092. isDeadTimer: 0,
  1093. uncertainDeathPosition: [0, 0],
  1094. message: "",
  1095. msgStatus: 0,
  1096. msgLength: 0,
  1097. die: function (e) {
  1098. if (e = !!e, this.isDead)
  1099. this.deathWasCertain = e || this.deathWasCertain;
  1100. else if (e || !this.didUncertainDeathLastTick) {
  1101. e || (this.didUncertainDeathLastTick = !0, this.uncertainDeathPosition = [this.pos[0], this.pos[1]]),
  1102. this.isDead = !0,
  1103. this.deathWasCertain = e,
  1104. this.deadAnimParts = [0],
  1105. this.isDeadTimer = 0,
  1106. this.isMyPlayer && doCamShakeDir(this.dir);
  1107. for (var t = 0; ;) {
  1108. if ((t += .4 * Math.random() + .5) >= 2 * Math.PI) {
  1109. this.deadAnimParts.push(2 * Math.PI);
  1110. break
  1111. }
  1112. this.deadAnimParts.push(t),
  1113. this.deadAnimPartsRandDist.push(Math.random())
  1114. }
  1115. }
  1116. },
  1117. undoDie: function () {
  1118. this.isDead = !1
  1119. },
  1120. deadAnimParts: [],
  1121. deadAnimPartsRandDist: [],
  1122. addHitLine: function (e, t) {
  1123. this.hitLines.push({
  1124. pos: e,
  1125. vanishTimer: 0,
  1126. color: t
  1127. })
  1128. },
  1129. hitLines: [],
  1130. doHonk: function (e) {
  1131. this.honkTimer = 0,
  1132. this.honkMaxTime = e,
  1133. "joris" == this.name.toLowerCase() && (null == honkSfx && (honkSfx = new Audio("/honk.mp3")), honkSfx.play());
  1134. wcMsgHonkHandler(this, e);
  1135. },
  1136. moveRelativeToServerPosNextFrame: !1,
  1137. lastServerPosSentTime: 0,
  1138. honkTimer: 0,
  1139. honkMaxTime: 0,
  1140. trails: [],
  1141. name: "",
  1142. skinBlock: 0,
  1143. lastBlock: null,
  1144. hasReceivedPosition: !1
  1145. },
  1146. t.push(n),
  1147. n.isMyPlayer && (myPlayer = n),
  1148. n
  1149. }
  1150.  
  1151. getPlayer = getPlayerWC;
  1152. honkEnd = honkEndWC;
  1153.  
  1154. var style = \`
  1155. #chatbox {
  1156. position: fixed;
  1157. right: 0;
  1158. bottom: 0;
  1159. }
  1160.  
  1161. #chatbox > * {
  1162. clear:both;
  1163. float:right;
  1164. }
  1165.  
  1166. #chatMessages > * {
  1167. clear:both;
  1168. float:right;
  1169. }
  1170.  
  1171. #chatNotifications > * {
  1172. clear:both;
  1173. float:right;
  1174. }
  1175.  
  1176. .chatMessage, .chatNotification {
  1177. display: flex;
  1178. background: #2d2824;
  1179. border: solid 2px #332d29;
  1180. margin: 1px 7px;
  1181. border-radius: 9px;
  1182. overflow: hidden;
  1183. max-width: 450px;
  1184. user-select: text;
  1185. opacity: 1;
  1186. }
  1187.  
  1188. .chatNotification {
  1189. max-height: 100px;
  1190. }
  1191.  
  1192. .chatNotification > span {
  1193. color: #fff;
  1194. font-style: italic;
  1195. display: flex;
  1196. align-items: center;
  1197. padding: 4px 10px;
  1198. }
  1199.  
  1200. #chatInput {
  1201. display: inline-block;
  1202. background: #7a6d62;
  1203. color: #000000;
  1204. border: solid 4px #3a342f;
  1205. margin: 1px 0 0 0;
  1206. padding: 0 0.2em;
  1207. font-size: 1.1em;
  1208. line-height: 1.4em;
  1209. outline: none;
  1210. min-width: 250px;
  1211. font-family: "Lucida Console", Monaco, monospace;
  1212. visibility: hidden;
  1213. }
  1214.  
  1215. .chatMessage > span {
  1216. display: flex;
  1217. align-items: center;
  1218. padding-top: 4px;
  1219. padding-bottom: 4px;
  1220. }
  1221.  
  1222. .chatMessage span:last-of-type{
  1223. color: #fff;
  1224. background: #413a35;
  1225. padding-left: 10px;
  1226. padding-right: 10px;
  1227. overflow-wrap: anywhere;
  1228. }
  1229.  
  1230. .chatMessage span:first-of-type{
  1231. color: #ffffff80;
  1232. font-weight: bold;
  1233. padding-left: 5px;
  1234. padding-right: 5px;
  1235. cursor: pointer;
  1236. max-width: 300px;
  1237. }
  1238.  
  1239. #wcBlock {
  1240. position: fixed;
  1241. min-width: 300px;
  1242. background: #4d453e;
  1243. border: solid 1px #000000;
  1244. color: #ffffff;
  1245. text-align: center;
  1246. box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
  1247. cursor: default;
  1248. user-select: none;
  1249. overflow: hidden;
  1250. font-size: 0;
  1251. opacity: 0;
  1252. display: none;
  1253. transition: font-size 0.15s ease-out, opacity 0.05s ease-out 0.05s;
  1254. right: 100px;
  1255. bottom: 100px;
  1256. }
  1257.  
  1258. #wcBlock input[type="checkbox"],
  1259. #wcBlock input[type="radio"] {
  1260. display: none;
  1261. }
  1262.  
  1263. #wcBlock label {
  1264. padding: 0.9em 0.2em;
  1265. }
  1266.  
  1267. #wcBlock input[type="radio"]:checked+label {
  1268. background: #a22929;
  1269. }
  1270.  
  1271. #wcBlock input:hover+label {
  1272. background: #3a342f;
  1273. }
  1274.  
  1275. #wcBlockAttributes input+label::before {
  1276. content: 'Current ';
  1277. }
  1278.  
  1279. #wcBlockAttributes input:checked+label::before {
  1280. content: 'Any ';
  1281. }
  1282.  
  1283. #wcBlockHeader {
  1284. background: #3a342f;
  1285. font-weight: bold;
  1286. padding: 0.3em 0.3em;
  1287. }
  1288.  
  1289. #wcBlockPeriod,
  1290. #wcBlockAttributes,
  1291. #wcBlockButtons {
  1292. display: grid;
  1293. }
  1294.  
  1295. #wcBlockPeriod {
  1296. grid-template-columns: 23% 23% 23% 31%;
  1297. }
  1298.  
  1299. #wcBlockButtons {
  1300. grid-template-columns: 59% 40%;
  1301. column-gap: 1%;
  1302. }
  1303.  
  1304. .wcBlockButton {
  1305. background: #3a342f;
  1306. border: none;
  1307. padding: 0.6em 0.1em;
  1308. color: white;
  1309. font-size: 1em;
  1310. }
  1311.  
  1312. #wcBlockBlock {
  1313. background: #631717;
  1314. }
  1315.  
  1316. #wcBlockCancel:hover {
  1317. background: #3bad48;
  1318. }
  1319.  
  1320. #wcBlockBlock:hover {
  1321. background: #a22929;
  1322. }
  1323. \`
  1324.  
  1325. addStyle(style);
  1326.  
  1327. var html = \`
  1328. <div id="chatbox">
  1329. <div id="chatMessages"></div>
  1330. <div id="chatNotifications"></div>
  1331. <input id="chatInput" type="text" maxlength="372">
  1332. </div>
  1333. <div id="wcBlock">
  1334. <div id="wcBlockHeader">Block user for ...</div>
  1335. <div id="wcBlockPeriod">
  1336. <input class="wcBlockPeriodRadio" type="radio" name="wcBlockPeriod" value="year" id="wcBlockPeriodRadio1">
  1337. <label for="wcBlockPeriodRadio1">Year</label>
  1338. <input class="wcBlockPeriodRadio" type="radio" name="wcBlockPeriod" value="week" id="wcBlockPeriodRadio2">
  1339. <label for="wcBlockPeriodRadio2">Week</label>
  1340. <input class="wcBlockPeriodRadio" type="radio" name="wcBlockPeriod" value="day" id="wcBlockPeriodRadio3">
  1341. <label for="wcBlockPeriodRadio3">Day</label>
  1342. <input class="wcBlockPeriodRadio" type="radio" name="wcBlockPeriod" value="2h" id="wcBlockPeriodRadio4" checked>
  1343. <label for="wcBlockPeriodRadio4">Two&nbsp;hours</label>
  1344. </div>
  1345. <div id="wcBlockAttributes">
  1346. <input class="wcBlockAttribute" type="checkbox" id="wcBlockAnyColor" checked>
  1347. <label for="wcBlockAnyColor">color</label>
  1348. </div>
  1349. <div id="wcBlockButtons">
  1350. <input class="wcBlockButton" type="button" id="wcBlockBlock" value="Block">
  1351. <input class="wcBlockButton" type="button" id="wcBlockCancel" value="Cancel">
  1352. </div>
  1353. </div>
  1354. \`
  1355.  
  1356. addHTML(html, "#playUI");
  1357.  
  1358. var wcBlock = document.getElementById("wcBlock");
  1359. var wcPlayerMenuHeader = document.getElementById("wcBlockHeader");
  1360. var wcBlockPeriodRadio1 = document.getElementById("wcBlockPeriodRadio1");
  1361. var wcBlockPeriodRadio2 = document.getElementById("wcBlockPeriodRadio2");
  1362. var wcBlockPeriodRadio3 = document.getElementById("wcBlockPeriodRadio3");
  1363. var wcBlockPeriodRadio4 = document.getElementById("wcBlockPeriodRadio4");
  1364. var wcBlockAnyColor = document.getElementById("wcBlockAnyColor");
  1365. var wcBlockBlock = document.getElementById("wcBlockBlock");
  1366. var wcBlockCancel = document.getElementById("wcBlockCancel");
  1367.  
  1368. wcBlockAnyColor.addEventListener("change", function () {
  1369. if (!wcBlockAnyColor.checked) {
  1370. document.querySelector("#wcBlockAnyColor+label").style.backgroundColor = wcBlock.getAttribute("data-color");
  1371. }
  1372. else {
  1373. document.querySelector("#wcBlockAnyColor+label").style.background = "none";
  1374. }
  1375. })
  1376.  
  1377. function wcShowBlockMenu(e) {
  1378. var name = e.target.getAttribute("data-name");
  1379. var colorId = e.target.getAttribute("data-colorid");
  1380. wcBlock.setAttribute("data-name", name);
  1381. wcBlock.setAttribute("data-color", bgColorById(colorId));
  1382. wcBlock.setAttribute("data-colorid", colorId);
  1383. wcPlayerMenuHeader.textContent = \`Block \${name} for a...\`;
  1384. wcBlockPeriodRadio4.checked = true;
  1385. wcBlockAnyColor.checked = false;
  1386. document.querySelector("#wcBlockAnyColor+label").style.backgroundColor = bgColorById(colorId);
  1387. wcBlock.style.right = \`\${document.body.clientWidth - e.clientX + 20}px\`
  1388. wcBlock.style.bottom = \`\${document.body.clientHeight - e.clientY}px\`
  1389. wcBlock.style.opacity = "1";
  1390. wcBlock.style.fontSize = "1em";
  1391. wcBlock.style.display = "block";
  1392. }
  1393.  
  1394. function wcHideBlockMenu() {
  1395. wcBlock.style.opacity = "0";
  1396. wcBlock.style.fontSize = "0";
  1397. wcBlock.style.display = "none";
  1398. }
  1399.  
  1400. wcBlockCancel.addEventListener("click", wcHideBlockMenu);
  1401. wcBlockBlock.addEventListener("click", function () {
  1402. var until = Date.now() +
  1403. (wcBlockPeriodRadio4.checked ? 2 * 60 * 60 * 1000 :
  1404. (wcBlockPeriodRadio3.checked ? 24 * 60 * 60 * 1000 :
  1405. (wcBlockPeriodRadio2.checked ? 7 * 24 * 60 * 60 * 1000 :
  1406. 365 * 24 * 60 * 60 * 1000)));
  1407.  
  1408. var name = wcBlock.getAttribute("data-name");
  1409. var colorId = !(wcBlockAnyColor.checked) ? Number.parseInt(wcBlock.getAttribute("data-colorid")) : -1;
  1410. if (isNaN(colorId)) {
  1411. colorId = -1;
  1412. }
  1413. wcBlockPlayer(name, until, colorId);
  1414.  
  1415. wcNewNotification(\`\${name} has been blocked\`, 6);
  1416. wcHideBlockMenu();
  1417. });
  1418.  
  1419. var chatNotifications = document.getElementById("chatNotifications");
  1420. var chatMessages = document.getElementById("chatMessages");
  1421. var chatInput = document.getElementById("chatInput");
  1422. chatInput.addEventListener('input', resizeChatInput);
  1423.  
  1424. function resizeChatInput() {
  1425. chatInput.style.width = (chatInput.value.length + 1) + "ch";
  1426. }
  1427.  
  1428. function addStyle(styleStr) {
  1429. const style = document.createElement('style');
  1430. style.textContent = styleStr;
  1431. document.head.append(style);
  1432. }
  1433.  
  1434. function addHTML(htmlStr, selector) {
  1435. var template = document.createElement('template');
  1436. template.innerHTML = htmlStr.trim();
  1437. document.querySelector(selector).appendChild(template.content);
  1438. }
  1439.  
  1440. function bgColorById(colorId) {
  1441. if (colorId < 0 || colorId > 12) { return 0 }
  1442. var hue = [0, 342.9, 322.81, 265.97, 274.04, 227.18, 218.93, 125.19, 126.95, 71.69, 49.88, 27.39, 40.99];
  1443. return \`hsl(\${hue[colorId]}, 94%, 50%)\`;
  1444. }
  1445.  
  1446. function colorNameById(colorId) {
  1447. if (colorId < 0 || colorId > 12) { return "" }
  1448. var colors = ["red", "pink", "dark pink", "light purple", "dark purple", "dark blue", "light blue", "light green", "dark green", "olive", "yellow", "orange", "gold"];
  1449. return (colors[colorId]);
  1450. }
  1451.  
  1452. var msgDivs = [];
  1453. var notifDivs = [];
  1454. var wsMsgSendStatus = 0;
  1455. var chatVisible = true;
  1456. var showPrivacyWarning = wcFlags.wcPrivacyWarnings;
  1457. removeExtraMessages();
  1458.  
  1459. function wcPrivacyWarning() {
  1460. var strings = [
  1461. "Walls have ears, and so do blocks",
  1462. "Do not pass any important information through this chat",
  1463. "The interlocutor may not be who he seems",
  1464. ];
  1465. var emojis = ["👀", "🤐", "🎭", "👺", "👂", "🕵️"];
  1466. wcNewNotification(strings[Math.floor(Math.random() * strings.length)] + " " + emojis[Math.floor(Math.random() * emojis.length)], 10, 1);
  1467. showPrivacyWarning = false;
  1468. }
  1469.  
  1470. function wcNewMessage(honkMsg, name, colorId) {
  1471. if (showPrivacyWarning) {
  1472. wcPrivacyWarning();
  1473. }
  1474. var color = bgColorById(colorId);
  1475. var text = wcCompatibleReplace(honkMsgToString(honkMsg));
  1476. var trimName = name.trim();
  1477. if (wcFlags.wcConsoleLog) {
  1478. console.log(new Date().toLocaleTimeString(wcFlags.wcConsoleLogTimeAmPm ? "en-US" : "en-GB", { timeStyle: "short" }) + " " +
  1479. (trimName === "" ? \`(\${colorNameById(colorId)})\` : trimName) + ": " + text);
  1480. }
  1481.  
  1482. var msgDiv = document.createElement("div");
  1483. msgDiv.classList.add("chatMessage");
  1484.  
  1485. var msgSpanName = document.createElement("span");
  1486. msgSpanName.textContent = trimName === "" ? "⬤" : trimName;
  1487. msgSpanName.setAttribute("data-name", trimName);
  1488. msgSpanName.setAttribute("data-colorid", colorId);
  1489. msgSpanName.style.color = color;
  1490. msgSpanName.addEventListener("click", wcShowBlockMenu);
  1491. var msgSpanText = document.createElement("span");
  1492. msgSpanText.textContent = text;
  1493. msgDiv.appendChild(msgSpanName);
  1494. msgDiv.appendChild(msgSpanText);
  1495. chatMessages.appendChild(msgDiv);
  1496. msgDivs.unshift(msgDiv);
  1497. if (wcFlags.wcMsgDisplayTime !== 0) { // don't delete at all if 0. But will be deleted if limit reached
  1498. setTimeout(function () {
  1499. msgDiv.style.transition = \`opacity \${wcFlags.wcMsgDecayTime <= wcFlags.wcMsgDisplayTime ? wcFlags.wcMsgDecayTime : wcFlags.wcMsgDisplayTime}s ease-in-out\`;
  1500. msgDiv.style.opacity = "0";
  1501. }, (wcFlags.wcMsgDecayTime < wcFlags.wcMsgDisplayTime ? wcFlags.wcMsgDisplayTime - wcFlags.wcMsgDecayTime : 0.1) * 1000);
  1502. setTimeout(function () {
  1503. var index = msgDivs.indexOf(msgDiv);
  1504. if (index != -1) {
  1505. msgDiv.remove();
  1506. msgDivs.splice(index, 1);
  1507. }
  1508. }, wcFlags.wcMsgDisplayTime * 1000);
  1509. }
  1510. }
  1511.  
  1512. function showTopNotification(text, timeAlive = 4) {
  1513. var notification = doTopNotification(text);
  1514. setTimeout(function () { notification.animateOut(); notification.destroy(); }, timeAlive * 1000);
  1515. }
  1516.  
  1517. function wcNewCornerNotification(text, timeAlive = 4, type = 0) {
  1518. var notifDiv = document.createElement("div");
  1519. notifDiv.classList.add("chatNotification");
  1520. var notifSpan = document.createElement("span");
  1521. notifSpan.textContent = text;
  1522. if (type == 1) {
  1523. notifSpan.style.fontStyle = "normal";
  1524. notifSpan.style.color = "yellow";
  1525. }
  1526. notifDiv.appendChild(notifSpan);
  1527. chatNotifications.appendChild(notifDiv);
  1528. notifDivs.unshift(notifDiv);
  1529. setTimeout(function () {
  1530. notifDiv.style.transition = "opacity 1s ease-in-out, max-height .35s ease-in-out";
  1531. notifDiv.style.transitionDelay = "0s, .5s"
  1532. notifDiv.style.opacity = "0";
  1533. notifDiv.style.maxHeight = "0";
  1534. }, (timeAlive - 1) * 1000);
  1535. setTimeout(function () {
  1536. var index = notifDivs.indexOf(notifDiv);
  1537. if (index != -1) {
  1538. notifDiv.remove();
  1539. notifDivs.splice(index, 1);
  1540. }
  1541. }, timeAlive * 1000);
  1542. }
  1543.  
  1544. function wcNewNotification(text, timeAlive = 4, type = 0) {
  1545. text = wcCompatibleReplace(text);
  1546.  
  1547. if (timeAlive == undefined) {
  1548. timeAlive = 4;
  1549. }
  1550. if (wcFlags.wcDefaultNotifications) {
  1551. showTopNotification(text, timeAlive);
  1552. }
  1553. else {
  1554. wcNewCornerNotification(text, timeAlive, type);
  1555. }
  1556. }
  1557.  
  1558. function removeExtraMessages() {
  1559. if (msgDivs.length > wcFlags.wcMessageLimit) {
  1560. for (var i = msgDivs.length - 1; i > wcFlags.wcMessageLimit; i--) {
  1561. msgDivs[i].remove();
  1562. msgDivs.splice(i);
  1563. }
  1564. }
  1565. setTimeout(removeExtraMessages, 2000);
  1566. }
  1567.  
  1568. function hideChatInput() {
  1569. chatInput.style.visibility = 'hidden';
  1570. chatInput.blur();
  1571. }
  1572.  
  1573. function toggleChatInput() {
  1574. if (chatInput.style.visibility !== 'visible') {
  1575. chatInput.style.visibility = 'visible';
  1576. showChat();
  1577. chatInput.focus();
  1578. if (document.activeElement !== chatInput) hideChatInput();
  1579. }
  1580. else {
  1581. hideChatInput();
  1582. }
  1583. }
  1584.  
  1585. function showChat() {
  1586. chatVisible = true;
  1587. chatMessages.style.opacity = '1';
  1588. chatMessages.style.pointerEvents = 'auto';
  1589. }
  1590.  
  1591. function toggleChat() {
  1592. chatVisible = !chatVisible;
  1593. if (chatVisible) {
  1594. chatMessages.style.opacity = '1';
  1595. chatMessages.style.pointerEvents = 'auto';
  1596. wcNewNotification("Chat is visible", 2);
  1597. }
  1598. else {
  1599. chatMessages.style.opacity = '0';
  1600. chatMessages.style.pointerEvents = 'none';
  1601. hideChatInput();
  1602. wcNewNotification("Chat is hidden", 2);
  1603. }
  1604. }
  1605.  
  1606. function wcIsPlayerBlocked(name, colorId) {
  1607. if (!(name in wcFlags.wcBlockedPlayers)) {
  1608. return false;
  1609. }
  1610. else {
  1611. for (const one of wcFlags.wcBlockedPlayers[name]) {
  1612. if (one.until > Date.now() && (!('colorId' in one) || one.colorId == colorId)) {
  1613. return true;
  1614. }
  1615. }
  1616. }
  1617. return false;
  1618. }
  1619.  
  1620. function wcBlockPlayer(name, until, colorId = -1) {
  1621. wcLoadBlockedPlayers(false);
  1622.  
  1623. if (!(name in wcFlags.wcBlockedPlayers)) {
  1624. wcFlags.wcBlockedPlayers[name] = [];
  1625. }
  1626.  
  1627. if (colorId === -1) {
  1628. wcFlags.wcBlockedPlayers[name].push({ 'until': until });
  1629. }
  1630. else {
  1631. wcFlags.wcBlockedPlayers[name].push({ 'until': until, 'colorId': colorId });
  1632. }
  1633. wcSaveBlockedPlayers();
  1634. }
  1635.  
  1636. function wcSaveBlockedPlayers() {
  1637. localStorage.setItem("wcBlockedPlayers", JSON.stringify(wcFlags.wcBlockedPlayers));
  1638. }
  1639.  
  1640. function wcLoadBlockedPlayers(saveIfChanged = true) {
  1641. var lsBlockedPlayers = localStorage.getItem("wcBlockedPlayers");
  1642. var wasChanged = false;
  1643.  
  1644. try {
  1645. var parsedBlockedPlayers = JSON.parse(lsBlockedPlayers);
  1646. if (parsedBlockedPlayers === null) {
  1647. parsedBlockedPlayers = {}
  1648. }
  1649. }
  1650. catch {
  1651. var parsedBlockedPlayers = {};
  1652. wasChanged = true;
  1653. }
  1654.  
  1655. for (var name in parsedBlockedPlayers) {
  1656. const properties = parsedBlockedPlayers[name];
  1657. if (Array.isArray(properties)) {
  1658. var i = properties.length;
  1659. while (i--) {
  1660. const one = properties[i];
  1661. if (typeof one !== 'object'
  1662. || one === null
  1663. || !('until' in one)
  1664. || one.until < Date.now()
  1665. || ('colorId' in one && (!Number.isInteger(one.colorId) || one.colorId < 0 || one.colorId > 12))) {
  1666. properties.splice(i, 1);
  1667. wasChanged = true;
  1668. }
  1669. }
  1670. if (properties.length === 0) {
  1671. delete parsedBlockedPlayers[name];
  1672. wasChanged = true;
  1673. }
  1674. }
  1675. else {
  1676. delete parsedBlockedPlayers.name;
  1677. wasChanged = true;
  1678. }
  1679. }
  1680.  
  1681. wcFlags.wcBlockedPlayers = parsedBlockedPlayers;
  1682.  
  1683. if (saveIfChanged && wasChanged) {
  1684. wcSaveBlockedPlayers();
  1685. }
  1686. }
  1687.  
  1688. var wcReplacements = {}, wcRECompat, wcCompatibleReplace;
  1689.  
  1690. (function () {
  1691. var compatibleReplacements = {
  1692. "💔": ["</3"],
  1693. "😕": [":-/"],
  1694. "😢": [":'("],
  1695. "🙁": ["😟", ":("],
  1696. "❤️": ["<3"],
  1697. "😇": ["0:-)"],
  1698. "😂": [":'-)"],
  1699. "😗": [":*"],
  1700. "😐": [":|"],
  1701. "😮": [":-O"],
  1702. "😡": [":@"],
  1703. "🙂": ["😊", ":-)"],
  1704. "😄": [":D"],
  1705. "😭": [";("],
  1706. "😛": [":P"],
  1707. "😅": [",:-)"],
  1708. "😒": [":s"],
  1709. "😉": [";)"],
  1710. "🙃": ["🙂", "😊", "(-:"],
  1711. "🤐": ["🙊", ":secret:"],
  1712. "🕵️": ["👁", ":detective:"],
  1713. }
  1714.  
  1715. var testContext = document.createElement("canvas").getContext('2d');
  1716. function isEmojiAvailable(em) {
  1717. return testContext.measureText(em).width > 8;
  1718. }
  1719.  
  1720. for (var em in compatibleReplacements) {
  1721. if (!isEmojiAvailable(em)) {
  1722. for (var i = 0; i < compatibleReplacements[em].length; i++) {
  1723. if (i == compatibleReplacements[em].length - 1 || isEmojiAvailable(compatibleReplacements[em][i])) {
  1724. wcReplacements[em] = compatibleReplacements[em][i];
  1725. break;
  1726. }
  1727. }
  1728. }
  1729. }
  1730.  
  1731. testContext.canvas.remove();
  1732.  
  1733. if (Object.keys(wcReplacements).length > 0) {
  1734. wcRECompat = new RegExp(Object.keys(wcReplacements).join("|"), "gi");
  1735. wcCompatibleReplace = function (str) {
  1736. return str.replace(wcRECompat, function (matched) {
  1737. return wcReplacements[matched];
  1738. });
  1739. }
  1740. }
  1741. else {
  1742. wcReplacements = null;
  1743. wcCompatibleReplace = function (str) {
  1744. return str;
  1745. };
  1746. }
  1747. }());
  1748.  
  1749. var wcEmoticons = {
  1750. "</3": "💔",
  1751. "<\\\\3": "💔",
  1752. ":-/": "😕",
  1753. ":'-(": "😢",
  1754. ":'(": "😢",
  1755. ":(": "🙁",
  1756. ":-(": "🙁",
  1757. "<3": "❤️",
  1758. "O:-)": "😇",
  1759. "0:-)": "😇",
  1760. "O:)": "😇",
  1761. "0:)": "😇",
  1762. ":'-)": "😂",
  1763. ":')": "😂",
  1764. ":-*": "😗",
  1765. ":*": "😗",
  1766. ":-|": "😐",
  1767. ":|": "😐",
  1768. ":-O": "😮",
  1769. ":O": "😮",
  1770. ":@": "😡",
  1771. ":)": "🙂",
  1772. ":-)": "🙂",
  1773. "=)": "🙂",
  1774. "^^": "😄",
  1775. ":D": "😃",
  1776. ":-D": "😃",
  1777. "=D": "😃",
  1778. "XD": "😆",
  1779. ";(": "😭",
  1780. ":-P": "😛",
  1781. ":P": "😛",
  1782. ",:-)": "😅",
  1783. ":\$": "😒",
  1784. ":S": "😒",
  1785. ";-)": "😉",
  1786. ";)": "😉",
  1787. "(:": "🙃",
  1788. "(-:": "🙃",
  1789. "(=": "🙃",
  1790. ":F": "🐸",
  1791. "(F)": "🐸",
  1792. ":(|)": "🐸"
  1793. }
  1794.  
  1795. var wcEmRe = /(^|\\s)(['\\-\\\$\\(\\)*,/:;@\\\\\\^\\|<=03DOPSXF]{2,4})(\$|\\s)/gi;
  1796.  
  1797. function wcEmoticonsToEmoji(src) {
  1798. wcEmRe.lastIndex = 0;
  1799. var result = "",
  1800. prevIndex = 0,
  1801. REsult;
  1802. while (REsult = wcEmRe.exec(src)) {
  1803. if (REsult[2].toUpperCase() in wcEmoticons) {
  1804. if (REsult[1].length == 1) {
  1805. result += src.substring(prevIndex, REsult.index + 1) + wcEmoticons[REsult[2].toUpperCase()];
  1806. }
  1807. else {
  1808. result += wcEmoticons[REsult[2].toUpperCase()];
  1809. }
  1810.  
  1811. if (REsult[3].length != 0) {
  1812. wcEmRe.lastIndex -= 1;
  1813. }
  1814.  
  1815. prevIndex = wcEmRe.lastIndex;
  1816. }
  1817. else if (REsult[3].length != 0) {
  1818. wcEmRe.lastIndex -= 1;
  1819. }
  1820. }
  1821. result += src.substring(prevIndex);
  1822. return result;
  1823. }
  1824.  
  1825. document.body.addEventListener('keydown', function (e) {
  1826. if (!playingAndReady) return;
  1827. if (e.keyCode == 13) {
  1828. toggleChatInput();
  1829. }
  1830. else if (e.keyCode == 27) {
  1831. hideChatInput()
  1832. }
  1833. else if (e.keyCode == 67) {
  1834. toggleChat();
  1835. }
  1836. })
  1837.  
  1838. chatInput.addEventListener('focusout', function () {
  1839. chatInput.style.visibility = 'hidden';
  1840. });
  1841.  
  1842. chatInput.addEventListener('keydown', function (e) {
  1843. e.stopPropagation();
  1844. if (e.keyCode == 27) {
  1845. hideChatInput();
  1846. }
  1847. else if (e.keyCode == 13) {
  1848. if (chatInput.value.length == 0) {
  1849. hideChatInput();
  1850. }
  1851. else {
  1852. if (wsMsgSendStatus === 0) {
  1853. var message = wcFlags.wcConvertEmoticons ? strToHonkMsg(wcEmoticonsToEmoji(chatInput.value)) : strToHonkMsg(chatInput.value);
  1854. if (typeof (message) == "number") {
  1855. if (message == 0) {
  1856. wcNewNotification("Message length is 0");
  1857. }
  1858. else {
  1859. wcNewNotification(\`Shorten the message by about \${message - 186} characters\`, 5);
  1860. }
  1861. }
  1862. else {
  1863. wcSendMessage(message);
  1864. chatInput.value = "";
  1865. hideChatInput();
  1866. resizeChatInput();
  1867. }
  1868. }
  1869. else {
  1870. wcNewNotification("Please wait...", 2);
  1871. }
  1872. }
  1873. }
  1874. })
  1875.  
  1876. chatInput.addEventListener('keyup', function (e) {
  1877. e.stopPropagation();
  1878. })`
  1879.  
  1880. function waitPageReady(callback) {
  1881. if (typeof(getPlayer) != "function" && typeof(ddEl) != "object") {
  1882. requestAnimationFrame(function(){waitPageReady(callback)});
  1883. }
  1884. else {
  1885. setTimeout(callback,0);
  1886. }
  1887. }
  1888.  
  1889. function addScript() {
  1890. var template = document.createElement('script');
  1891. template.innerHTML = source;
  1892. document.body.appendChild(template);
  1893. }
  1894.  
  1895. waitPageReady(addScript);
  1896.  
  1897. })();