chore: initial commit — CS2 Simulator
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
// ─── Safe Mode (Recording Mode for Streamers/Bloggers) ─────────────────────
|
||||
// Replaces gambling terms with YouTube-safe synonyms
|
||||
|
||||
const SafeMode = (() => {
|
||||
const STORAGE_KEY = 'safe_mode';
|
||||
|
||||
const DICT = [
|
||||
// Currency
|
||||
[/₽(?=\s*\d)/g, 'тугриков'],
|
||||
[/(\d+)\s*₽/g, (_, n) => {
|
||||
const num = parseInt(n);
|
||||
const last = num % 10;
|
||||
const lastTwo = num % 100;
|
||||
if (lastTwo >= 11 && lastTwo <= 19) return n + ' тугриков';
|
||||
if (last === 1) return n + ' тугрик';
|
||||
if (last >= 2 && last <= 4) return n + ' тугрика';
|
||||
return n + ' тугриков';
|
||||
}],
|
||||
[/₽(?!\d)/g, 'тугриков'],
|
||||
|
||||
// Cases
|
||||
[/\b(открыть|открывай|открыл)\s+кейс/gi, (m) => m.replace(/кейс/i, 'бокс')],
|
||||
[/\bкейс[а-я]*\b/gi, (m) => {
|
||||
const lower = m.toLowerCase();
|
||||
if (lower === 'кейс') return 'бокс';
|
||||
if (lower === 'кейса') return 'бокса';
|
||||
if (lower === 'кейсу') return 'боксу';
|
||||
if (lower === 'кейсом') return 'боксом';
|
||||
if (lower === 'кейсе') return 'боксе';
|
||||
if (lower === 'кейсы') return 'боксы';
|
||||
if (lower === 'кейсов') return 'боксов';
|
||||
if (lower === 'кейсам') return 'боксам';
|
||||
if (lower === 'кейсами') return 'боксами';
|
||||
if (lower === 'кейсах') return 'боксах';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Case (English)
|
||||
[/\bcase\b/gi, 'box'],
|
||||
|
||||
// Skins
|
||||
[/\bскин[а-я]*\b/gi, (m) => {
|
||||
const lower = m.toLowerCase();
|
||||
if (lower === 'скин') return 'облик';
|
||||
if (lower === 'скина') return 'облика';
|
||||
if (lower === 'скину') return 'облику';
|
||||
if (lower === 'скином') return 'обликом';
|
||||
if (lower === 'скине') return 'облике';
|
||||
if (lower === 'скины' || lower === 'скин') return 'облики';
|
||||
if (lower === 'скинов') return 'обликов';
|
||||
if (lower === 'скинам') return 'обликам';
|
||||
if (lower === 'скинами') return 'обликами';
|
||||
if (lower === 'скинах') return 'обликах';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Contract
|
||||
[/\bконтракт[а-я]*\b/gi, (m) => {
|
||||
const lower = m.toLowerCase();
|
||||
if (lower === 'контракт') return 'сделка';
|
||||
if (lower === 'контракта') return 'сделки';
|
||||
if (lower === 'контракту') return 'сделке';
|
||||
if (lower === 'контрактом') return 'сделкой';
|
||||
if (lower === 'контракте') return 'сделке';
|
||||
if (lower === 'контракты' || lower === 'контракт') return 'сделки';
|
||||
if (lower === 'контрактов') return 'сделок';
|
||||
if (lower === 'контрактам') return 'сделкам';
|
||||
if (lower === 'контрактами') return 'сделками';
|
||||
if (lower === 'контрактах') return 'сделках';
|
||||
if (lower === 'контрактная' || lower === 'контрактный' || lower === 'контрактное') return 'договорная';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Upgrade
|
||||
[/\bапгрейд[а-я]*\b/gi, (m) => {
|
||||
if (m.toLowerCase() === 'апгрейд') return 'улучшение';
|
||||
if (m.toLowerCase() === 'апгрейда') return 'улучшения';
|
||||
if (m.toLowerCase() === 'апгрейду') return 'улучшению';
|
||||
if (m.toLowerCase() === 'апгрейдом') return 'улучшением';
|
||||
if (m.toLowerCase() === 'апгрейде') return 'улучшении';
|
||||
if (m.toLowerCase() === 'апгрейды') return 'улучшения';
|
||||
if (m.toLowerCase() === 'апгрейдов') return 'улучшений';
|
||||
return m;
|
||||
}],
|
||||
[/\bupgrade\b/gi, 'upgrade'],
|
||||
|
||||
// Float
|
||||
[/\bfloat\b/gi, 'состояние'],
|
||||
|
||||
// Rarity
|
||||
[/\bредкост[ьи][а-я]*\b/gi, (m) => {
|
||||
const lower = m.toLowerCase();
|
||||
if (lower === 'редкость') return 'класс';
|
||||
if (lower === 'редкости') return 'классы';
|
||||
if (lower === 'редкостью') return 'классом';
|
||||
if (lower === 'редкостей') return 'классов';
|
||||
if (lower === 'редкостям') return 'классам';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Chance
|
||||
[/\bшанс[а-я]*\b/gi, (m) => {
|
||||
const lower = m.toLowerCase();
|
||||
if (lower === 'шанс') return 'вероятность';
|
||||
if (lower === 'шанса') return 'вероятности';
|
||||
if (lower === 'шансу') return 'вероятности';
|
||||
if (lower === 'шансом') return 'вероятностью';
|
||||
if (lower === 'шансе') return 'вероятности';
|
||||
if (lower === 'шансы') return 'вероятности';
|
||||
if (lower === 'шансов') return 'вероятностей';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Bet
|
||||
[/\bставк[а-я]*\b/gi, (m) => {
|
||||
const lower = m.toLowerCase();
|
||||
if (lower === 'ставка') return 'вход';
|
||||
if (lower === 'ставки') return 'входы';
|
||||
if (lower === 'ставку') return 'вход';
|
||||
if (lower === 'ставкой') return 'входом';
|
||||
if (lower === 'ставок') return 'входов';
|
||||
if (lower === 'ставкам') return 'входам';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Win / Lose
|
||||
[/\bвыигрыш[а-я]*\b/gi, (m) => {
|
||||
if (m.toLowerCase() === 'выигрыш' || m.toLowerCase() === 'выигрыша') return 'приз';
|
||||
if (m.toLowerCase() === 'выигрышу') return 'призу';
|
||||
if (m.toLowerCase() === 'выигрышем') return 'призом';
|
||||
if (m.toLowerCase() === 'выигрыше') return 'призе';
|
||||
return m;
|
||||
}],
|
||||
[/\b(выиграл|выиграла|выиграли)\b/gi, (m) => {
|
||||
if (m.toLowerCase() === 'выиграл' || m.toLowerCase() === 'выиграла') return 'получил(a)';
|
||||
if (m.toLowerCase() === 'выиграли') return 'получили';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Lose
|
||||
[/\bпроиграл[а-я]*\b/gi, 'не получилось'],
|
||||
|
||||
// Crash game
|
||||
[/\b(краш|crash)\b/gi, 'риск-игра'],
|
||||
|
||||
// Open case
|
||||
[/\bоткры[тв][а-я]+\s+кейс/gi, (m) => m.replace(/кейс/i, 'бокс')],
|
||||
|
||||
// Wear
|
||||
[/\bизнос\b/gi, 'состояние'],
|
||||
|
||||
// Probability display
|
||||
[/\bвероятность\b/gi, 'шанс'],
|
||||
|
||||
// Quick mode tooltips
|
||||
[/\bбыстрый режим\b/gi, 'турбо-режим'],
|
||||
|
||||
// Balance
|
||||
[/\bбаланс\b/gi, 'счёт'],
|
||||
|
||||
// Sell
|
||||
[/\bпрода[жв][а-я]*\b/gi, (m) => {
|
||||
const lower = m.toLowerCase();
|
||||
if (lower === 'продажа' || lower === 'продать') return 'обменять';
|
||||
if (lower === 'продал') return 'обменял';
|
||||
if (lower === 'продайте') return 'обменяйте';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Inventory
|
||||
[/\bинвентар[ьи][а-я]*\b/gi, (m) => {
|
||||
const lower = m.toLowerCase();
|
||||
if (lower === 'инвентарь') return 'коллекция';
|
||||
if (lower === 'инвентаря') return 'коллекции';
|
||||
if (lower === 'инвентаре') return 'коллекции';
|
||||
if (lower === 'инвентарём') return 'коллекцией';
|
||||
return m;
|
||||
}],
|
||||
|
||||
// Trade up
|
||||
[/\btrade.?up\b/gi, 'treyd-ap'],
|
||||
|
||||
// Premium
|
||||
[/\bпремиум\b/gi, 'эксклюзив'],
|
||||
|
||||
// Rare names in English
|
||||
[/\bcovert\b/gi, 'covert'],
|
||||
[/\bclassified\b/gi, 'classified'],
|
||||
[/\bconsumers?\b/gi, 'consumer'],
|
||||
[/\bindustrials?\b/gi, 'industrial'],
|
||||
[/\bmil-spec\b/gi, 'mil-spec'],
|
||||
[/\brestricted\b/gi, 'restricted'],
|
||||
];
|
||||
|
||||
let observer = null;
|
||||
let active = false;
|
||||
|
||||
function isActive() {
|
||||
return localStorage.getItem(STORAGE_KEY) === 'true';
|
||||
}
|
||||
|
||||
function setActive(val) {
|
||||
localStorage.setItem(STORAGE_KEY, val ? 'true' : 'false');
|
||||
active = val;
|
||||
}
|
||||
|
||||
function replaceText(node) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
let text = node.textContent;
|
||||
let changed = false;
|
||||
for (const [pattern, replacement] of DICT) {
|
||||
const newText = text.replace(pattern, replacement);
|
||||
if (newText !== text) {
|
||||
text = newText;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
node.textContent = text;
|
||||
}
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
// Don't process script, style, input, textarea, canvas elements
|
||||
const tag = node.tagName.toLowerCase();
|
||||
if (tag === 'script' || tag === 'style' || tag === 'input' ||
|
||||
tag === 'textarea' || tag === 'canvas' || tag === 'svg' ||
|
||||
tag === 'code' || tag === 'pre') {
|
||||
return;
|
||||
}
|
||||
// Process title and alt attributes
|
||||
if (node.hasAttribute('title')) {
|
||||
let t = node.getAttribute('title');
|
||||
for (const [pattern, replacement] of DICT) {
|
||||
t = t.replace(pattern, replacement);
|
||||
}
|
||||
node.setAttribute('title', t);
|
||||
}
|
||||
if (node.hasAttribute('alt')) {
|
||||
let a = node.getAttribute('alt');
|
||||
for (const [pattern, replacement] of DICT) {
|
||||
a = a.replace(pattern, replacement);
|
||||
}
|
||||
node.setAttribute('alt', a);
|
||||
}
|
||||
// Process children
|
||||
for (let i = 0; i < node.childNodes.length; i++) {
|
||||
replaceText(node.childNodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function apply() {
|
||||
replaceText(document.body);
|
||||
// Update document title
|
||||
if (document.title) {
|
||||
let t = document.title;
|
||||
for (const [pattern, replacement] of DICT) {
|
||||
t = t.replace(pattern, replacement);
|
||||
}
|
||||
document.title = t;
|
||||
}
|
||||
// Update safe mode button appearance
|
||||
const btn = document.getElementById('safeModeToggle');
|
||||
if (btn) {
|
||||
btn.textContent = active ? '🛡️' : '🎙️';
|
||||
btn.title = active ? 'Режим записи активен' : 'Режим записи';
|
||||
btn.style.borderColor = active ? 'var(--success-color)' : '';
|
||||
}
|
||||
}
|
||||
|
||||
function startObserver() {
|
||||
if (observer) observer.disconnect();
|
||||
observer = new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
for (const node of mutation.addedNodes) {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
replaceText(node);
|
||||
} else if (node.nodeType === Node.TEXT_NODE) {
|
||||
replaceText(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
characterData: true
|
||||
});
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
active = !isActive();
|
||||
setActive(active);
|
||||
if (active) {
|
||||
apply();
|
||||
startObserver();
|
||||
} else {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
observer = null;
|
||||
}
|
||||
// Reload to restore original text
|
||||
location.reload();
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
function init() {
|
||||
active = isActive();
|
||||
if (active) {
|
||||
// Wait for DOM to be ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
apply();
|
||||
startObserver();
|
||||
});
|
||||
} else {
|
||||
apply();
|
||||
startObserver();
|
||||
}
|
||||
}
|
||||
// Set initial button state
|
||||
const btn = document.getElementById('safeModeToggle');
|
||||
if (btn) {
|
||||
btn.textContent = active ? '🛡️' : '🎙️';
|
||||
btn.title = active ? 'Режим записи активен' : 'Режим записи';
|
||||
if (active) btn.style.borderColor = 'var(--success-color)';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init,
|
||||
toggle,
|
||||
isActive
|
||||
};
|
||||
})();
|
||||
|
||||
// Global toggle function for onclick in navbars
|
||||
function toggleSafeMode() {
|
||||
SafeMode.toggle();
|
||||
}
|
||||
|
||||
// Auto-init
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => SafeMode.init());
|
||||
} else {
|
||||
SafeMode.init();
|
||||
}
|
||||
Reference in New Issue
Block a user