Complete shoutbox overhaul
// ==UserScript==
// @name TorrentBD Shoutbox Manager
// @namespace TBD-Shoutbox-Manager
// @version 1.3.8.1
// @description Complete shoutbox overhaul
// @author notPULS3
// @license MIT
// @match https://www.torrentbd.com/
// @match https://www.torrentbd.net/
// @match https://www.torrentbd.org/
// @match https://www.torrentbd.me/
// @match https://www.torrentbd.net/?spotlight
// @match https://www.torrentbd.com/?spotlight
// @match https://www.torrentbd.org/?spotlight
// @icon https://www.google.com/s2/favicons?sz=64&domain=torrentbd.net
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
// Prevent multiple instances
if (window.TBDMLoaded) return;
window.TBDMLoaded = true;
// ============================================================================
// GLOBAL CONFIGURATION & STORAGE
// ============================================================================
const CONFIG = {
// Cleaner Module
cleaner_enabled: GM_getValue('tbdm_cleaner_enabled', false),
cleaner_filters: GM_getValue('tbdm_cleaner_filters', {
torrent: true,
forumPost: true,
forumTopic: true,
request: true
}),
cleaner_hide_all_users: GM_getValue('tbdm_cleaner_hide_all_users', false),
cleaner_block_users_enabled: GM_getValue('tbdm_cleaner_block_users_enabled', false),
cleaner_blocked_user_ids: GM_getValue('tbdm_cleaner_blocked_user_ids', []),
cleaner_block_keywords_enabled: GM_getValue('tbdm_cleaner_block_keywords_enabled', false),
cleaner_blocked_keywords: GM_getValue('tbdm_cleaner_blocked_keywords', []),
// Notifier Module
notifier_enabled: GM_getValue('tbdm_notifier_enabled', false),
notifier_username: GM_getValue('tbdm_notifier_username', ''),
notifier_keywords: GM_getValue('tbdm_notifier_keywords', []),
notifier_sound_volume: GM_getValue('tbdm_notifier_sound_volume', 0.5),
notifier_highlight_color: GM_getValue('tbdm_notifier_highlight_color', '#2E4636'),
notifier_processed_messages: GM_getValue('tbdm_notifier_processed_messages', []),
// Image Upload Module
image_upload_enabled: GM_getValue('tbdm_image_upload_enabled', true),
// Easy Mention Module
easy_mention_enabled: GM_getValue('tbdm_easy_mention_enabled', true),
// URL Sender Module
url_sender_enabled: GM_getValue('tbdm_url_sender_enabled', false),
// GIF Picker Module
gif_picker_enabled: GM_getValue('tbdm_gif_picker_enabled', true),
gif_picker_tenor_key: GM_getValue('tbdm_gif_picker_tenor_key', 'AIzaSyCGj4Qj1j0MBns1v2rWhlvJWRBkCNgIFyo'),
gif_picker_giphy_key: GM_getValue('tbdm_gif_picker_giphy_key', ''),
meme_creator_enabled: GM_getValue('tbdm_meme_creator_enabled', false),
// Unicode Emoji Module
unicode_emoji_enabled: GM_getValue('tbdm_unicode_emoji_enabled', false),
// Image Viewer Module
image_viewer_enabled: GM_getValue('tbdm_image_viewer_enabled', true),
// Autocomplete Module
autocomplete_username_enabled: GM_getValue('tbdm_autocomplete_username_enabled', false),
autocomplete_sticker_enabled: GM_getValue('tbdm_autocomplete_sticker_enabled', false),
autocomplete_mappings: GM_getValue('tbdm_autocomplete_mappings', {
'hi': ':hello',
'hello': ':hello',
'no': ':negative',
'nahi': ':sticker-mb-no',
'sad': ':sticker-pepe-face',
'lmao': ':lmao',
'wow': ':sticker-omg-wow',
'lol': ':sticker-jjj-laugh',
'why': ':sticker-cat-why',
'ty': ':thankyou',
'wont': ':sticker-sr-no',
'bruh': ':sticker-facepalm',
'yay': ':yepdance',
'aww': ':sticker-pepe-aw',
'laugh': ':sticker-pepe-laugh',
'salute': ':sticker-pepe-salute',
'ohh': ':sticker-cp-ohh',
'doge': ':sticker-doge',
'dance': ':sticker-dancing-doge',
'pepeog': ':sticker-pepe-og',
'ekb': ':sticker-ekb',
'police': ':sticker-pepe-police',
'yes': ':sticker-yes',
'wut': ':sticker-pepe-wut',
'exp': ':sticker-pepe-exp',
'knock': ':sticker-death-knock',
'rules': 'https://www.torrentbd.net/rules.php',
'faq': 'https://www.torrentbd.net/faq.php'
}),
// Focus Lock Module
focus_lock_enabled: GM_getValue('tbdm_focus_lock_enabled', true),
// Idle Prevention Module
idle_prevention_enabled: GM_getValue('tbdm_idle_prevention_enabled', true)
};
// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================
function saveConfig(key, value) {
CONFIG[key] = value;
GM_setValue(`tbdm_${key}`, value);
}
function detectUsername() {
const allRankElements = document.querySelectorAll('.tbdrank');
for (const rankElement of allRankElements) {
if (!rankElement.closest('#shoutbox-container')) {
if (rankElement && rankElement.firstChild && rankElement.firstChild.nodeType === Node.TEXT_NODE) {
return rankElement.firstChild.nodeValue.trim();
}
}
}
return 'Not Found';
}
// ============================================================================
// MODULE 1: SHOUTBOX CLEANER
// ============================================================================
const CleanerModule = {
systemFilters: {
torrent: { phrase: "New Torrent :", label: "New Torrents" },
forumPost: { phrase: "New Forum Post", label: "New Forum Posts" },
forumTopic: { phrase: "New Forum Topic", label: "New Forum Topics" },
request: { phrase: "New Request :", label: "New Requests" }
},
check() {
if (!CONFIG.cleaner_enabled) return;
const shoutItems = document.querySelectorAll(".shout-item");
const blockedUserIDs = CONFIG.cleaner_blocked_user_ids.filter(Boolean);
const blockedKeywords = CONFIG.cleaner_blocked_keywords.filter(Boolean);
shoutItems.forEach((item) => {
item.style.display = "";
const textField = item.querySelector(".shout-text");
if (!textField) return;
const lowerCaseText = textField.textContent.toLowerCase();
let shouldHide = false;
let isSystemMessage = false;
let systemMessageType = null;
for (const key in this.systemFilters) {
if (lowerCaseText.includes(this.systemFilters[key].phrase.toLowerCase())) {
isSystemMessage = true;
systemMessageType = key;
break;
}
}
if (isSystemMessage) {
if (CONFIG.cleaner_filters[systemMessageType]) {
shouldHide = true;
}
} else {
if (CONFIG.cleaner_hide_all_users) {
shouldHide = true;
} else if (CONFIG.cleaner_block_users_enabled && blockedUserIDs.length > 0) {
const idElement = item.querySelector('.shout-user [data-tid]');
const userID = idElement ? idElement.getAttribute('data-tid') : null;
if (userID && blockedUserIDs.includes(userID)) {
shouldHide = true;
}
}
// Check for blocked keywords
if (!shouldHide && CONFIG.cleaner_block_keywords_enabled && blockedKeywords.length > 0) {
for (const keyword of blockedKeywords) {
if (keyword && lowerCaseText.includes(keyword.toLowerCase())) {
shouldHide = true;
break;
}
}
}
}
if (shouldHide) {
item.style.display = "none";
}
});
},
init() {
const shoutContainer = document.querySelector("#shouts-container");
if (shoutContainer) {
const observer = new MutationObserver(() => this.check());
observer.observe(shoutContainer, { childList: true, subtree: true });
this.check();
}
}
};
//============================================================================
// MODULE 2: SHOUTBOX NOTIFIER
// ============================================================================
const NotifierModule = {
MAX_PROCESSED_MESSAGES: 200,
playSound() {
if (CONFIG.notifier_sound_volume < 0.01) return;
try {
const audio = new Audio("https://raw.githubusercontent.com/5ifty6ix/custom-sounds/refs/heads/main/new-notification-010-352755.mp3");
audio.volume = CONFIG.notifier_sound_volume;
audio.play();
} catch (e) {
console.error('Notifier: Could not play sound.', e);
}
},
notifyUser() {
if (!document.title.startsWith('(1)')) {
document.title = '(1) ' + document.title;
}
this.playSound();
},
highlightShout(shoutElement) {
shoutElement.style.backgroundColor = CONFIG.notifier_highlight_color;
shoutElement.style.borderLeft = '3px solid #14a76c';
shoutElement.style.paddingLeft = '5px';
},
checkShout(shoutElement) {
if (!CONFIG.notifier_enabled || !shoutElement || !shoutElement.id) return;
const messageBody = shoutElement.querySelector('.shout-text');
if (!messageBody) return;
// Skip system messages (New Forum Post, New Torrent, etc.)
const messageText = messageBody.textContent.toLowerCase();
const systemPhrases = ['new forum post', 'new forum topic', 'new torrent', 'new request'];
for (const phrase of systemPhrases) {
if (messageText.includes(phrase)) {
return; // Skip this message
}
}
const activeUsername = (CONFIG.notifier_username && CONFIG.notifier_username !== 'Not Found')
? [CONFIG.notifier_username] : [];
const allKeywords = [...activeUsername, ...CONFIG.notifier_keywords]
.filter(k => k && k.trim() !== '')
.map(k => k.toLowerCase());
if (allKeywords.length === 0) return;
let keywordFound = false;
for (const keyword of allKeywords) {
const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const normalizedKeyword = keyword.replace(/^@/, '').replace(/[.\-_]+$/, '');
const escapedKeyword = escapeRegExp(normalizedKeyword);
const keywordRegex = new RegExp(
`(?<![\\w\\u0080-\\uFFFF])@?${escapedKeyword}[.\\-_]*(?![\\w\\u0080-\\uFFFF])`,
'i'
);
if (keywordRegex.test(messageText)) {
this.highlightShout(shoutElement);
keywordFound = true;
break;
}
}
if (keywordFound) {
const processedMessages = CONFIG.notifier_processed_messages;
if (!processedMessages.includes(shoutElement.id)) {
this.notifyUser();
processedMessages.push(shoutElement.id);
if (processedMessages.length > this.MAX_PROCESSED_MESSAGES) {
processedMessages.shift();
}
saveConfig('notifier_processed_messages', processedMessages);
}
}
},
init() {
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.classList.contains('shout-item')) {
this.checkShout(node);
}
});
}
}
});
const shoutbox = document.getElementById('shouts-container');
if (shoutbox) {
shoutbox.querySelectorAll('.shout-item').forEach(item => this.checkShout(item));
observer.observe(shoutbox, { childList: true });
}
}
};
// ============================================================================
// MODULE 3: IMAGE UPLOAD
// ============================================================================
const ImageUploadModule = {
ENDPOINTS: [
'https://timepass.fpureit.top/'
],
FREEIMAGE_API_KEY: '6d207e02198a847aa98d0a2a901485a5',
FREEIMAGE_API_URL: 'https://freeimage.host/api/1/upload',
fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(',')[1]);
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsDataURL(file);
});
},
async convertToJPG(file) {
if (file.type !== 'image/png') return file;
return new Promise((resolve) => {
const img = new Image();
const canvas = document.createElement('canvas');
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0);
canvas.toBlob((blob) => {
resolve(blob
? new File([blob], file.name.replace(/\.\w+$/, '.jpg'), { type: 'image/jpeg' })
: file
);
}, 'image/jpeg', 1.0);
};
img.onerror = () => resolve(file);
img.src = URL.createObjectURL(file);
});
},
uploadFreeImage(file, input, original) {
this.convertToJPG(file).then(async (convertedFile) => {
try {
const base64Data = await this.fileToBase64(convertedFile);
const formData = new FormData();
formData.append('key', this.FREEIMAGE_API_KEY);
formData.append('action', 'upload');
formData.append('source', base64Data);
formData.append('format', 'json');
GM_xmlhttpRequest({
method: 'POST',
url: this.FREEIMAGE_API_URL,
data: formData,
onload: (res) => {
try {
const data = JSON.parse(res.responseText);
if (res.status === 200 && data.status_code === 200 && data.image?.url) {
input.value = (original ? original + ' ' : '') + data.image.url + ' ';
input.disabled = false;
input.focus();
} else {
input.value = '[Upload failed]';
setTimeout(() => { input.value = original; input.disabled = false; }, 2500);
}
} catch (e) {
input.value = '[Upload failed]';
setTimeout(() => { input.value = original; input.disabled = false; }, 2500);
}
},
onerror: () => {
input.value = '[Upload failed]';
setTimeout(() => { input.value = original; input.disabled = false; }, 2500);
}
});
} catch (e) {
input.value = '[Upload failed]';
setTimeout(() => { input.value = original; input.disabled = false; }, 2500);
}
});
},
upload(file, input, i = 0, original = '') {
if (i >= this.ENDPOINTS.length) {
// All timepass endpoints failed, silently try freeimage
this.uploadFreeImage(file, input, original);
return;
}
const fd = new FormData();
fd.append('file', file);
GM_xmlhttpRequest({
method: 'POST',
url: this.ENDPOINTS[i],
data: fd,
onload: res => {
if (res.status === 200) {
const match = res.responseText.match(/https?:\/\/timepass\.fpureit\.top\/[a-zA-Z0-9]+\.(png|jpe?g|gif|webp)/i);
if (match) {
input.value = (original ? original + ' ' : '') + match[0] + ' ';
input.disabled = false;
input.focus();
return;
}
}
this.upload(file, input, i + 1, original);
},
onerror: () => this.upload(file, input, i + 1, original)
});
},
startUpload(file, input) {
const original = input.value.replace(/\[Uploading.*?\]/, '').trim();
input.value = '[Uploading...]';
input.disabled = true;
this.upload(file, input, 0, original);
},
init() {
if (!CONFIG.image_upload_enabled) return;
const input = document.querySelector('#shout_text');
const tray = document.querySelector('#shout-ibb-container');
if (!input || !tray || document.getElementById('tbdm-image-upload-btn')) return;
// Paste handler
input.addEventListener('paste', e => {
const file = [...(e.clipboardData || e.originalEvent.clipboardData).items]
.find(i => i.type.startsWith('image/'))?.getAsFile();
if (file) {
e.preventDefault();
this.startUpload(file, input);
}
});
// Drag & drop handlers
input.addEventListener('dragover', e => {
e.preventDefault();
input.style.border = '2px dashed #4cafef';
});
input.addEventListener('dragleave', e => {
e.preventDefault();
input.style.border = '';
});
input.addEventListener('drop', e => {
e.preventDefault();
input.style.border = '';
const file = e.dataTransfer.files[0];
if (file && file.type.startsWith('image/')) {
this.startUpload(file, input);
}
});
// File picker
const picker = Object.assign(document.createElement('input'), {
type: 'file',
accept: 'image/*',
style: 'display:none'
});
document.body.appendChild(picker);
picker.addEventListener('change', e => {
if (e.target.files[0]) this.startUpload(e.target.files[0], input);
picker.value = null;
});
// Upload button
const btn = document.createElement('span');
btn.id = 'tbdm-image-upload-btn';
btn.className = 'inline-submit-btn';
btn.title = 'Upload Image';
btn.innerHTML = '<i class="material-icons">add_photo_alternate</i>';
btn.style.cursor = 'pointer';
btn.addEventListener('click', () => picker.click());
tray.insertBefore(btn, tray.querySelector('#urlBtn') || null);
}
};
// ============================================================================
// MODULE 4: EASY MENTION
// ============================================================================
const EasyMentionModule = {
isEnabled: false,
handler: null,
init() {
if (!CONFIG.easy_mention_enabled || window.location.pathname !== "/") return;
if (this.isEnabled) return; // Already initialized
this.isEnabled = true;
const shout = document.querySelector("#shout_text");
if (!shout) return;
// Detect browser
if (navigator.vendor === "") {
if (!document.body.classList.contains("firefox")) document.body.classList.add("firefox");
} else {
if (!document.body.classList.contains("chromium")) document.body.classList.add("chromium");
}
this.handler = (e) => {
// Check if clicked element or its parent is the shout-time
let target = e.target;
let shoutTime = null;
// If clicked on AM/PM span, get parent shout-time
if (target.classList.contains('shout-time-lam')) {
shoutTime = target.parentElement;
} else if (target.classList.contains('shout-time')) {
shoutTime = target;
} else {
return;
}
if (document.body.classList.contains("chromium")) {
if (!shoutTime.matches(".chromium .shout-time:has(+ .shout-user [href^='account'])")) return;
} else if (document.body.classList.contains("firefox")) {
if (!shoutTime.matches(".firefox .shout-time")) return;
}
const name = shoutTime.nextElementSibling.querySelector('[href^="account"] span');
if (name) {
if (shout.value !== "" && shout.value.slice(-1) !== " ") shout.value += " ";
shout.value += "@" + name.innerText.trim() + " ";
}
};
document.addEventListener("click", this.handler);
},
stop() {
if (this.handler) {
document.removeEventListener("click", this.handler);
this.handler = null;
}
this.isEnabled = false;
}
};
// ============================================================================
// MODULE 5: URL SENDER
// ============================================================================
const URLSenderModule = {
urlWindow: null,
urlField: null,
labelField: null,
showUrlWindow() {
this.urlWindow.classList.add("show");
// Close unicode emoji picker if open
const unicodePicker = document.getElementById("spotlight-emojis");
if (unicodePicker && unicodePicker.classList.contains("active")) {
unicodePicker.classList.remove("active");
}
const shoutCont = document.querySelector("#shout-send-container");
shoutCont.querySelectorAll("[id^='spotlight']").forEach(spotlight => {
if (spotlight.classList.contains("shiner")) {
spotlight.classList.remove("shiner");
spotlight.classList.add("fader");
}
});
},
hideUrlWindow() {
this.urlWindow.classList.remove("show");
},
clearFields() {
this.urlField.value = "";
this.labelField.value = "";
},
urlTagCreate() {
const shout = document.querySelector("#shout_text");
let rawURL = this.urlField.value.trim();
let label = this.labelField.value;
if (rawURL.length > 150) return "URL is too long.\nConsider shortening it using URL shorteners.";
if (!/^https:\/\//i.test(rawURL)) return "Please enter a safe https URL.";
if (!/^https:\/\/.*\./i.test(rawURL)) return "Please make sure the URL is correct.";
if (shout.value !== "" && shout.value.slice(-1) !== " ") shout.value += " ";
if (label !== "") {
shout.value += `[url=${rawURL}]${label}[/url] `;
} else {
shout.value += `[url]${rawURL}[/url] `;
}
this.hideUrlWindow();
this.clearFields();
shout.focus();
},
init() {
if (!CONFIG.url_sender_enabled || window.location.pathname !== "/") return;
if (window.location.search.includes("spotlight")) document.body.classList.add("spotlight");
const shout = document.querySelector("#shout_text");
const ibbCont = document.querySelector("#shout-ibb-container");
const shoutCont = document.querySelector("#shout-send-container");
if (!shout || !ibbCont || !shoutCont) return;
// Create URL window
this.urlWindow = document.createElement("div");
this.urlWindow.id = "urlWindow";
shoutCont.appendChild(this.urlWindow);
// URL field
this.urlField = document.createElement("input");
this.urlField.id = "urlField";
this.urlField.classList.add("url-inputs");
this.urlField.placeholder = "URL";
this.urlWindow.appendChild(this.urlField);
this.urlField.onmouseover = () => this.urlField.focus();
// Label field
this.labelField = document.createElement("input");
this.labelField.id = "labelField";
this.labelField.classList.add("url-inputs");
this.labelField.placeholder = "Label (Optional)";
this.urlWindow.appendChild(this.labelField);
// Submit button
const submitURL = document.createElement("input");
submitURL.type = "button";
submitURL.id = "submitURL";
submitURL.value = "Submit";
this.urlWindow.appendChild(submitURL);
// URL button
const urlBtn = document.createElement("span");
urlBtn.id = "urlBtn";
urlBtn.innerHTML = `<i class="material-icons">URL</i>`;
urlBtn.classList.add("inline-submit-btn");
ibbCont.insertBefore(urlBtn, ibbCont.childNodes[4]);
// Event listeners
urlBtn.addEventListener("click", e => {
e.stopPropagation();
if (shout.value !== "") shout.value += " ";
this.urlWindow.classList.contains("show") ? this.hideUrlWindow() : this.showUrlWindow();
this.urlField.focus();
});
document.addEventListener("click", e => {
if (e.target.matches(".inline-submit-btn:not(#urlBtn) i")) {
this.hideUrlWindow();
}
});
submitURL.onclick = () => {
let error = this.urlTagCreate();
if (error) {
alert(error);
this.clearFields();
}
};
document.addEventListener("keypress", e => {
if (e.target.matches(".url-inputs") && e.key === "Enter") {
let error = this.urlTagCreate();
if (error) {
alert(error);
this.clearFields();
}
}
});
let initHeight = window.innerHeight;
window.onresize = () => {
if (window.innerHeight < initHeight && this.urlWindow.classList.contains("show")) {
shoutCont.scrollIntoView(false);
}
};
}
};
// ============================================================================
// MODULE 6: AUTOCOMPLETE
// ============================================================================
const AutocompleteModule = {
isEnabled: false,
keyboardHandler: null,
inputHandler: null,
lastInputValue: '',
lastInputTime: 0,
performAutocomplete() {
let shoutInputBox = document.querySelector("#shout_text");
if (!shoutInputBox) return false;
let typedText = shoutInputBox.value;
let words = typedText.split(" ");
let lastWord = words[words.length - 1].toLowerCase();
// Skip if last word is empty
if (!lastWord) return false;
// Username autocomplete
if (CONFIG.autocomplete_username_enabled) {
let myShoutContainer = document.querySelector("#shouts-container");
if (myShoutContainer) {
let shoutUsers = myShoutContainer.querySelectorAll("div.shout-item span.shout-user span.tbdrank");
for (let user of shoutUsers) {
if (user.innerText.toLowerCase().startsWith(lastWord) && typedText !== "") {
words[words.length - 1] = "@" + user.innerText;
shoutInputBox.value = words.join(" ");
// Set cursor to end
shoutInputBox.setSelectionRange(shoutInputBox.value.length, shoutInputBox.value.length);
return true;
}
}
}
}
// Sticker/emoji autocomplete
if (CONFIG.autocomplete_sticker_enabled) {
const mapping = CONFIG.autocomplete_mappings;
if (mapping.hasOwnProperty(lastWord)) {
words[words.length - 1] = mapping[lastWord];
shoutInputBox.value = words.join(" ");
// Set cursor to end
shoutInputBox.setSelectionRange(shoutInputBox.value.length, shoutInputBox.value.length);
return true;
}
}
return false;
},
init() {
if (this.isEnabled) return; // Already initialized
this.isEnabled = true;
// Keyboard handler for desktop (Tab key)
this.keyboardHandler = (event) => {
if (event.key !== "Tab") return;
event.preventDefault();
let shoutInputBox = document.activeElement;
if (
shoutInputBox.tagName !== 'TEXTAREA' &&
!(shoutInputBox.tagName === 'INPUT' && shoutInputBox.type === 'text')
) return;
this.performAutocomplete();
};
// Input handler for mobile (detect double space)
this.inputHandler = (event) => {
const shoutInput = event.target;
// Only trigger on the shoutbox input
if (shoutInput.id !== 'shout_text') return;
// Only use double space on mobile/touch devices
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|| ('ontouchstart' in window)
|| (navigator.maxTouchPoints > 0);
if (!isMobile) return; // Skip on desktop
const currentValue = shoutInput.value;
const currentTime = Date.now();
// Check if user typed two spaces in a row within 500ms
if (currentValue.endsWith(' ') &&
this.lastInputValue.length > 0 &&
currentValue.length === this.lastInputValue.length + 1 &&
currentTime - this.lastInputTime < 500) {
// Remove both spaces
shoutInput.value = currentValue.slice(0, -2);
// Perform autocomplete
const completed = this.performAutocomplete();
// If autocomplete happened, add a space at the end for natural flow
if (completed) {
shoutInput.value += ' ';
shoutInput.setSelectionRange(shoutInput.value.length, shoutInput.value.length);
} else {
// If no autocomplete, restore single space
shoutInput.value += ' ';
shoutInput.setSelectionRange(shoutInput.value.length, shoutInput.value.length);
}
}
// Store current value and time for next comparison
this.lastInputValue = shoutInput.value;
this.lastInputTime = currentTime;
};
// Add both handlers
document.addEventListener("keydown", this.keyboardHandler);
document.addEventListener("input", this.inputHandler);
},
stop() {
if (this.keyboardHandler) {
document.removeEventListener("keydown", this.keyboardHandler);
this.keyboardHandler = null;
}
if (this.inputHandler) {
document.removeEventListener("input", this.inputHandler);
this.inputHandler = null;
}
this.isEnabled = false;
this.lastInputValue = '';
this.lastInputTime = 0;
}
};
// ============================================================================
// MODULE 7: INPUT LOCK
// ============================================================================
const FocusLockModule = {
shoutbox: null,
shoutboxContainer: null,
hoverHandler: null,
focusInput() {
// Only focus if user is not selecting text
if (!window.getSelection().toString() && this.shoutbox) {
this.shoutbox.focus();
}
},
init() {
if (!CONFIG.focus_lock_enabled) return;
this.shoutbox = document.querySelector('input#shout_text.shoutbox-text');
this.shoutboxContainer = document.querySelector('#shoutbox-container');
if (!this.shoutbox || !this.shoutboxContainer) {
setTimeout(() => this.init(), 500);
return;
}
// Remove any existing hover handler
if (this.hoverHandler) {
this.shoutboxContainer.removeEventListener('mouseover', this.hoverHandler);
}
// Create new hover handler
this.hoverHandler = (e) => {
this.focusInput();
};
// Add hover event listener to shoutbox container
this.shoutboxContainer.addEventListener('mouseover', this.hoverHandler);
},
stop() {
if (this.hoverHandler && this.shoutboxContainer) {
this.shoutboxContainer.removeEventListener('mouseover', this.hoverHandler);
this.hoverHandler = null;
}
}
};
// Updated settings handler for Focus Lock
function setupFocusLockSettings() {
const focusLock = document.getElementById('tbdm-focus-lock');
if (!focusLock) return;
focusLock.checked = CONFIG.focus_lock_enabled;
focusLock.addEventListener('change', (e) => {
saveConfig('focus_lock_enabled', e.target.checked);
if (e.target.checked) {
FocusLockModule.init();
} else {
FocusLockModule.stop();
}
});
}
// ============================================================================
// MODULE 8: IDLE PREVENTION
// ============================================================================
const IdlePreventionModule = {
intervalId: null,
observer: null,
checkIntervalId: null,
simulateActivity() {
// Simulate multiple types of activity
const events = ['mousemove', 'mousedown', 'keypress', 'scroll', 'touchstart'];
events.forEach(eventType => {
const event = new Event(eventType, { bubbles: true });
document.dispatchEvent(event);
});
},
hideIdleOverlays() {
// Check for any overlay elements - cast wider net
const selectors = [
'#shoutbox-idle-overlay',
'.shoutbox-idle',
'.idle-overlay',
'[class*="idle"]',
'[id*="idle"]'
];
selectors.forEach(selector => {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(el => {
// Only hide if it looks like an overlay (has certain styles)
const computed = window.getComputedStyle(el);
if (computed.position === 'fixed' || computed.position === 'absolute') {
if (computed.display !== 'none') {
el.style.display = 'none';
el.style.visibility = 'hidden';
el.style.opacity = '0';
el.style.pointerEvents = 'none';
}
}
});
} catch (e) {
// Ignore invalid selectors
}
});
},
init() {
if (!CONFIG.idle_prevention_enabled) return;
// Simulate activity more frequently (every 5 seconds)
this.intervalId = setInterval(() => this.simulateActivity(), 5000);
// Check for and hide overlays frequently (every 2 seconds)
this.checkIntervalId = setInterval(() => this.hideIdleOverlays(), 2000);
// Initial check
this.hideIdleOverlays();
// Watch for new overlays being added
this.observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) { // Element node
// Check if the added node or its children match idle patterns
const nodeStr = node.className || node.id || '';
if (nodeStr.toLowerCase().includes('idle')) {
this.hideIdleOverlays();
}
}
});
});
});
this.observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'id', 'style']
});
// Prevent visibility change detection
document.addEventListener('visibilitychange', (e) => {
if (document.hidden) {
e.stopImmediatePropagation();
}
}, true);
},
stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
if (this.checkIntervalId) {
clearInterval(this.checkIntervalId);
this.checkIntervalId = null;
}
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
}
};
// ============================================================================
// MODULE 9: GIF PICKER (FIXED)
// ============================================================================
const GifPickerModule = {
TENOR_API_KEY: CONFIG.gif_picker_tenor_key,
GIPHY_KEY_POOL_URL: 'https://giphyapitbd.mushi53566.workers.dev/keys',
GIFS_PER_REQUEST: 20,
modal: null,
searchBox: null,
resultsArea: null,
gifButton: null,
headerBar: null,
currentGiphyKey: null,
keyExpiryTime: 0,
currentPage: 1,
lastQuery: '',
tenorPos: '',
seenGifUrls: new Set(),
activeInput: null,
currentInputType: 'shoutbox',
commandStartPos: 0,
activeRequests: new Set(),
requestId: 0,
isDragging: false,
hasMoved: false,
favMode: false,
favGifs: JSON.parse(localStorage.getItem('tbd_fav_gifs') || '[]'),
CUSTOM_GIF_SVG: `
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 7C4 5.89543 4.89543 5 6 5H18C19.1046 5 20 5.89543 20 7V17C20 18.1046 19.1046 19 18 19H6C4.89543 19 4 18.1046 4 17V7Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 10H9.5C9.22386 10 9 10.2239 9 10.5V13.5C9 13.7761 9.22386 14 9.5 14H10V12.5H9.5" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13 10V14" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 10H17.5" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 12H17" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 10V14" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`,
debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
},
isFav(url) {
return this.favGifs.includes(url);
},
toggleFav(url) {
const idx = this.favGifs.indexOf(url);
if (idx === -1) {
this.favGifs.push(url);
} else {
this.favGifs.splice(idx, 1);
}
localStorage.setItem('tbd_fav_gifs', JSON.stringify(this.favGifs));
},
showFavs() {
this.favMode = true;
this.activeRequests.forEach(r => r && r.abort && r.abort());
this.activeRequests.clear();
this.resultsArea.innerHTML = '';
if (this.favGifs.length === 0) {
this.resultsArea.innerHTML = '<div style="grid-column:1/-1;text-align:center;color:#888;padding:30px 10px;font-size:12px;">No favourites yet.<br>Hover any GIF and click ♥ to save it.</div>';
return;
}
const fragment = document.createDocumentFragment();
this.favGifs.forEach(url => {
const wrap = this._makeGifTile(url, url);
fragment.appendChild(wrap);
});
this.resultsArea.appendChild(fragment);
},
_makeGifTile(thumb, direct) {
const wrap = document.createElement('div');
wrap.style.cssText = 'position:relative;border-radius:4px;overflow:visible;';
const img = document.createElement('img');
img.src = thumb;
img.dataset.direct = direct;
img.style.cssText = 'width:100%;height:95px;object-fit:cover;cursor:pointer;border-radius:4px;transition:all .12s;border:2px solid transparent;background:#202225;opacity:0;display:block;';
img.onload = () => img.style.opacity = '1';
img.onerror = () => wrap.remove();
img.onclick = (e) => {
e.stopPropagation();
this.insertGif(img.dataset.direct);
};
const heart = document.createElement('button');
const alreadyFav = this.isFav(direct);
heart.title = alreadyFav ? 'Remove from favourites' : 'Add to favourites';
heart.textContent = alreadyFav ? '♥' : '♡';
heart.style.cssText = `position:absolute;top:3px;right:3px;background:rgba(0,0,0,0.65);border:none;color:${alreadyFav ? '#ff4d6d' : '#ccc'};font-size:14px;cursor:pointer;border-radius:3px;width:22px;height:22px;display:flex;align-items:center;justify-content:center;padding:0;opacity:0;transition:opacity .15s;line-height:1;`;
heart.onclick = (e) => {
e.stopPropagation();
this.toggleFav(direct);
const nowFav = this.isFav(direct);
heart.textContent = nowFav ? '♥' : '♡';
heart.style.color = nowFav ? '#ff4d6d' : '#ccc';
heart.title = nowFav ? 'Remove from favourites' : 'Add to favourites';
if (this.favMode && !nowFav) {
wrap.remove();
if (this.resultsArea.children.length === 0) {
this.resultsArea.innerHTML = '<div style="grid-column:1/-1;text-align:center;color:#888;padding:30px 10px;font-size:12px;">No favourites yet.<br>Hover any GIF and click ♥ to save it.</div>';
}
}
};
wrap.addEventListener('mouseenter', () => heart.style.opacity = '1');
wrap.addEventListener('mouseleave', () => heart.style.opacity = '0');
wrap.appendChild(img);
wrap.appendChild(heart);
return wrap;
},
createModal() {
const html = `
<div id="gif-tool-modal" role="dialog" style="position:fixed;width:440px;max-width:90vw;background:#2f3136;border:1px solid #444;border-radius:8px;z-index:2147483647;display:none;box-shadow:0 10px 30px rgba(0,0,0,0.8);overflow:hidden;flex-direction:column;top:auto;">
<div id="gif-tool-header" style="height:24px;background:#202225;cursor:grab;display:flex;align-items:center;justify-content:space-between;padding:0 8px;border-bottom:1px solid #36393f;flex-shrink:0;">
<span id="gif-tool-title" style="font-size:11px;color:#aaa;font-weight:bold;user-select:none;text-transform:uppercase;letter-spacing:0.5px;">Gif Picker</span>
<button id="gif-tool-close" aria-label="Close" style="background:none;border:none;color:#aaa;font-size:18px;cursor:pointer;line-height:1;padding:0;">×</button>
</div>
<div id="gif-tool-content" style="padding:8px;display:flex;flex-direction:column;gap:8px;">
<div id="gif-tool-results" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(80px,1fr));gap:6px;max-height:320px;min-height:100px;overflow-y:auto;"></div>
<div id="gif-tool-controls" style="display:flex;gap:6px;align-items:center;flex-shrink:0;">
<input id="gif-tool-search" placeholder="Search GIFs..." autocomplete="off" style="flex:1;padding:6px 10px;background:#40444b;border:1px solid #555;color:#fff;border-radius:6px;font-size:13px;outline:none;"/>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', html);
this.modal = document.getElementById('gif-tool-modal');
this.headerBar = document.getElementById('gif-tool-header');
this.searchBox = document.getElementById('gif-tool-search');
this.resultsArea = document.getElementById('gif-tool-results');
const debouncedSearch = this.debounce((val) => this.handleSearch(val), 300);
this.searchBox.addEventListener('input', () => {
const val = this.searchBox.value.trim();
this.favMode = false;
debouncedSearch(val);
});
document.getElementById('gif-tool-close').addEventListener('click', () => this.closeModal());
this.resultsArea.addEventListener('scroll', () => {
if (this.favMode) return;
if (this.resultsArea.scrollTop + this.resultsArea.clientHeight >= this.resultsArea.scrollHeight - 50) {
if (this.lastQuery.length > 0 && this.activeRequests.size === 0) this.fetchGifs(this.lastQuery, false);
}
});
this.modal.addEventListener('mouseenter', () => {
if (this.modal.style.display === 'flex' && !this.isDragging) {
this.searchBox.focus();
}
});
this.modal.addEventListener('mouseover', (e) => {
if (this.modal.style.display === 'flex' && !this.isDragging &&
document.activeElement !== this.searchBox &&
!e.target.closest('#gif-tool-results img')) {
this.searchBox.focus();
}
});
this.setupDraggable();
},
setupDraggable() {
let startX, startY, startLeft, startBottom;
this.headerBar.addEventListener('mousedown', (e) => {
if (e.target.id === 'gif-tool-close') return;
e.preventDefault();
this.isDragging = true;
startX = e.clientX;
startY = e.clientY;
const rect = this.modal.getBoundingClientRect();
startLeft = rect.left;
const computedStyle = window.getComputedStyle(this.modal);
const cssBottom = parseInt(computedStyle.bottom);
if (isNaN(cssBottom)) {
startBottom = window.innerHeight - rect.bottom;
} else {
startBottom = cssBottom;
}
const onMouseMove = (e) => {
if (!this.isDragging) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
this.modal.style.left = `${startLeft + dx}px`;
this.modal.style.bottom = `${startBottom - dy}px`;
this.modal.style.top = 'auto';
};
const onMouseUp = () => {
this.isDragging = false;
this.hasMoved = true;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
this.constrainToViewport();
this.savePosition();
};
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
},
savePosition() {
if (!this.modal) return;
const rect = this.modal.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const pos = {
rightDist: viewportWidth - rect.right,
bottomDist: viewportHeight - rect.bottom,
width: viewportWidth,
height: viewportHeight
};
localStorage.setItem('tbd_gif_pos_v2', JSON.stringify(pos));
},
constrainToViewport() {
if (!this.modal || this.modal.style.display !== 'flex') return;
const rect = this.modal.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const margin = 10;
let left = parseInt(this.modal.style.left) || rect.left;
let bottom = parseInt(this.modal.style.bottom);
if (isNaN(bottom)) {
bottom = viewportHeight - rect.bottom;
}
const modalHeight = rect.height;
const top = viewportHeight - bottom - modalHeight;
const maxLeft = viewportWidth - rect.width - margin;
if (left < margin) left = margin;
if (left > maxLeft) left = maxLeft;
if (top < margin) {
bottom = viewportHeight - modalHeight - margin;
}
if (bottom < margin) {
bottom = margin;
}
this.modal.style.left = `${left}px`;
this.modal.style.bottom = `${bottom}px`;
this.modal.style.top = 'auto';
},
restorePosition(anchor) {
const saved = localStorage.getItem('tbd_gif_pos_v2');
if (saved) {
try {
const pos = JSON.parse(saved);
if (pos.rightDist !== undefined && pos.bottomDist !== undefined) {
const viewportWidth = window.innerWidth;
const rect = this.modal.getBoundingClientRect();
const left = viewportWidth - pos.rightDist - rect.width;
const bottom = pos.bottomDist;
this.modal.style.left = `${left}px`;
this.modal.style.bottom = `${bottom}px`;
this.modal.style.top = 'auto';
this.hasMoved = true;
setTimeout(() => this.constrainToViewport(), 0);
return;
}
} catch(e) { console.error('Invalid saved pos'); }
}
if (anchor && !this.hasMoved) {
const rect = anchor.getBoundingClientRect();
let left = rect.right - 440;
if (left < 10) left = 10;
const bottom = window.innerHeight - rect.top + 10;
this.modal.style.left = `${left}px`;
this.modal.style.bottom = `${bottom}px`;
this.modal.style.top = 'auto';
setTimeout(() => this.constrainToViewport(), 0);
}
},
addGifButton() {
const tray = document.querySelector('#shout-ibb-container');
if (!tray || document.getElementById('tbdm-gif-tool-btn')) return;
this.gifButton = document.createElement('span');
this.gifButton.id = 'tbdm-gif-tool-btn';
this.gifButton.className = 'inline-submit-btn';
this.gifButton.title = 'Insert GIF';
this.gifButton.innerHTML = this.CUSTOM_GIF_SVG;
const isSpotlight = document.body.classList.contains('spotlight-mode') ||
window.location.search.includes('spotlight');
const topValue = isSpotlight ? '17px' : '6px';
this.gifButton.style.cssText = `display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:#ccc;position:relative;top:${topValue};height:24px;width:28px;margin-left:4px;margin-right:2px;`;
this.gifButton.addEventListener('click', (ev) => {
ev.stopPropagation();
ev.preventDefault();
const input = document.querySelector('#shout_text');
if (input) {
if(this.modal.style.display === 'flex') this.closeModal();
else this.openModal(input, 'shoutbox', this.gifButton);
}
});
const targetBtn = tray.querySelector('#tbd-uploader-button')
|| tray.querySelector('#imgbd-uploader-btn')
|| tray.querySelector('#urlBtn')
|| tray.querySelector('#tbdm-image-upload-btn');
if (targetBtn) {
tray.insertBefore(this.gifButton, targetBtn);
} else {
tray.appendChild(this.gifButton);
}
},
handleSearch(q) {
this.activeRequests.forEach(r => r && r.abort && r.abort());
this.activeRequests.clear();
if (!q || q.length < 1) {
this.resultsArea.innerHTML = '';
this.lastQuery = '';
this.tenorPos = '';
this.showFavs();
return;
}
if (q !== this.lastQuery) {
this.lastQuery = q;
this.currentPage = 1;
this.tenorPos = '';
this.seenGifUrls.clear();
this.resultsArea.innerHTML = '';
}
this.fetchGifs(q, true);
},
async fetchGiphyKey() {
const now = Date.now();
if (this.currentGiphyKey && now < this.keyExpiryTime) return this.currentGiphyKey;
try {
const resp = await fetch(this.GIPHY_KEY_POOL_URL);
if (!resp.ok) throw new Error();
const d = await resp.json();
if (d.api_key) {
this.currentGiphyKey = d.api_key;
this.keyExpiryTime = Date.now() + 3600000;
return this.currentGiphyKey;
}
} catch (e) { return null; }
},
showLoading() {
if (!this.resultsArea.querySelector('.gif-loading')) {
const loader = document.createElement('div');
loader.className = 'gif-loading';
loader.style.cssText = 'text-align:center;color:#888;padding:20px;grid-column:1/-1;';
loader.textContent = 'Loading...';
this.resultsArea.appendChild(loader);
}
},
hideLoading() {
const el = this.resultsArea.querySelector('.gif-loading');
if (el) el.remove();
},
async fetchGifs(query, isNewSearch) {
if (!query) return;
if (isNewSearch) this.showLoading();
const myRequestId = ++this.requestId;
await this.fetchGiphyKey();
const tenorParams = new URLSearchParams({
q: query,
key: this.TENOR_API_KEY,
limit: this.GIFS_PER_REQUEST,
media_filter: 'minimal'
});
if (this.tenorPos) tenorParams.set('pos', this.tenorPos);
const tenorReq = GM_xmlhttpRequest({
method: 'GET',
url: `https://tenor.googleapis.com/v2/search?${tenorParams}`,
onload: (res) => {
this.activeRequests.delete(tenorReq);
if (myRequestId !== this.requestId || res.status !== 200) return this.checkRequestsComplete();
try {
const d = JSON.parse(res.responseText);
this.renderGifs(d.results, 'tenor');
this.tenorPos = d.next || '';
} catch (e) {}
this.checkRequestsComplete();
},
onerror: () => {
this.activeRequests.delete(tenorReq);
this.checkRequestsComplete();
}
});
this.activeRequests.add(tenorReq);
if (this.currentGiphyKey) {
const offset = (this.currentPage - 1) * this.GIFS_PER_REQUEST;
const giphyReq = GM_xmlhttpRequest({
method: 'GET',
url: `https://api.giphy.com/v1/gifs/search?api_key=${this.currentGiphyKey}&q=${encodeURIComponent(query)}&limit=${this.GIFS_PER_REQUEST}&offset=${offset}`,
onload: (res) => {
this.activeRequests.delete(giphyReq);
if (myRequestId !== this.requestId) return this.checkRequestsComplete();
if (res.status === 200) {
try {
this.renderGifs(JSON.parse(res.responseText).data, 'giphy');
} catch (e) {}
}
this.checkRequestsComplete();
},
onerror: () => {
this.activeRequests.delete(giphyReq);
this.checkRequestsComplete();
}
});
this.activeRequests.add(giphyReq);
}
},
checkRequestsComplete() {
if (this.activeRequests.size === 0) {
this.hideLoading();
this.currentPage++;
}
},
renderGifs(results, source) {
if (!results || results.length === 0) return;
this.hideLoading();
this.favMode = false;
const fragment = document.createDocumentFragment();
results.forEach(gif => {
let thumb, direct;
if (source === 'tenor') {
if (!gif.media_formats) return;
thumb = gif.media_formats.tinygif?.url || gif.media_formats.gif?.url;
direct = gif.media_formats.gif?.url || gif.url;
} else {
thumb = gif.images?.fixed_height_small?.url || gif.images?.fixed_height?.url;
direct = gif.images?.original?.url;
}
if (!thumb || this.seenGifUrls.has(direct)) return;
this.seenGifUrls.add(direct);
const wrap = this._makeGifTile(thumb, direct);
fragment.appendChild(wrap);
});
this.resultsArea.appendChild(fragment);
},
getShortenedUrl(direct) {
const gmatch = direct.match(/giphy\.com\/media\/[^/]+\/([^/]+)\//);
if (gmatch) return `https://i.giphy.com/${gmatch[1]}.webp`;
const tmatch = direct.match(/media\.tenor\.com\/images\/([^/]+)\//) || direct.match(/media\.tenor\.com\/([^/]+)/);
if (tmatch) return `https://c.tenor.com/${tmatch[1]}`;
return direct;
},
insertGif(directUrl) {
if (!this.activeInput) return;
const text = (this.currentInputType === 'shoutbox')
? this.getShortenedUrl(directUrl)
: `[img]${directUrl}[/img]`;
const cur = this.activeInput.value;
const before = cur.substring(0, this.commandStartPos);
const after = cur.substring(this.activeInput.selectionStart || cur.length);
const toInsert = text + ' ';
this.activeInput.value = before + toInsert + after;
const newCursorPos = before.length + toInsert.length;
this.closeModal();
setTimeout(() => {
this.activeInput.focus();
this.activeInput.setSelectionRange(newCursorPos, newCursorPos);
this.activeInput = null;
}, 50);
},
openModal(inputEl, inputType, anchorEl) {
this.activeInput = inputEl;
this.currentInputType = inputType || 'shoutbox';
this.commandStartPos = inputEl.selectionStart || inputEl.value.length;
this.restorePosition(anchorEl);
this.modal.style.display = 'flex';
this.searchBox.value = '';
this.currentPage = 1;
this.lastQuery = '';
this.tenorPos = '';
this.seenGifUrls.clear();
this.showFavs();
setTimeout(() => this.searchBox.focus(), 50);
},
closeModal() {
this.modal.style.display = 'none';
this.searchBox.value = '';
this.resultsArea.innerHTML = '';
this.currentPage = 1;
this.lastQuery = '';
this.tenorPos = '';
this.seenGifUrls.clear();
this.activeRequests.forEach(r => r && r.abort && r.abort());
this.activeRequests.clear();
this.favMode = false;
},
setupInputListener() {
const el = document.querySelector('#shout_text');
if (!el) return;
el.addEventListener('input', () => {
const pos = el.selectionStart || 0;
const before = el.value.substring(0, pos);
const m = before.match(/\/gif\s*(.*)$/);
if (m) {
this.commandStartPos = pos - m[0].length;
this.activeInput = el;
this.currentInputType = 'shoutbox';
if (this.modal.style.display !== 'flex') {
this.restorePosition(this.gifButton);
this.modal.style.display = 'flex';
this.searchBox.focus();
}
if (m[1].trim()) {
this.handleSearch(m[1].trim());
}
} else if (this.modal.style.display === 'flex' && this.activeInput === el) {
this.closeModal();
}
});
},
init() {
if (!CONFIG.gif_picker_enabled) return;
this.TENOR_API_KEY = CONFIG.gif_picker_tenor_key;
this.createModal();
this.addGifButton();
this.setupInputListener();
const resizeHandler = this.debounce(() => {
if (this.modal && this.modal.style.display === 'flex') {
this.constrainToViewport();
this.savePosition();
}
}, 150);
window.addEventListener('resize', resizeHandler);
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.modal.style.display === 'flex') {
e.preventDefault();
this.closeModal();
if (this.activeInput) this.activeInput.focus();
}
});
},
stop() {
const btn = document.getElementById('tbdm-gif-tool-btn');
if (btn) btn.remove();
if (this.modal) this.closeModal();
}
};
// ============================================================================
// MODULE 10: IMAGE VIEWER
// ============================================================================
const ImageViewerModule = {
URL_RE: /https?:\/\/(?:[^\s'"><]+\.(?:png|jpe?g|webp|gif)(?:\?[^\s'">]*)?(?=[^\w\-]|$)|(?:c|media)\.tenor\.com\/[^\s'"><]+|prnt\.sc\/[\w\-]+|ibb\.co(?:\.com)?\/[\w\-]+|gifyu\.com\/image\/[\w\-]+|imgbox\.com\/[\w\-]+|postimg\.cc\/[\w\-]+)/gi,
_isDirect(url) {
return /\.(png|jpe?g|webp|gif)(\?.*)?$/i.test(url)
|| /^https?:\/\/(?:c|media)\.tenor\.com\//i.test(url)
|| /^https?:\/\/i\.giphy\.com\//i.test(url)
|| /^https?:\/\/timepass\.fpureit\.top\//i.test(url);
},
resolvePageImage(url) {
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: 'GET',
url: url,
headers: { 'User-Agent': 'Mozilla/5.0' },
onload(res) {
const m = res.responseText.match(
/<meta[^>]+(?:name|property)=["'](?:og:image|twitter:image:src)["'][^>]+content=["']([^"']+)["']/i
);
resolve(m?.[1] ?? null);
},
onerror() { resolve(null); }
});
});
},
createImageElement(src, fullSrc) {
const img = document.createElement('img');
if (src) img.src = src;
img.dataset.fullsrc = fullSrc || src;
img.referrerPolicy = 'no-referrer';
img.style.cssText = 'max-width:100px;max-height:100px;vertical-align:middle;display:inline;margin:0 4px;cursor:pointer;border-radius:4px;';
img.alt = 'image';
return img;
},
createOverlay(img) {
const existing = document.querySelector('.tbdm-image-overlay');
if (existing) existing.remove();
const overlay = document.createElement('div');
overlay.className = 'tbdm-image-overlay';
overlay.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.9);z-index:10000;display:flex;align-items:center;justify-content:center;cursor:pointer;';
const fullImg = document.createElement('img');
fullImg.src = img.dataset.fullsrc || img.src;
fullImg.referrerPolicy = 'no-referrer';
fullImg.style.cssText = 'max-width:90vw;max-height:90vh;border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,0.5);';
overlay.appendChild(fullImg);
overlay.onclick = () => overlay.remove();
fullImg.onclick = e => e.stopPropagation();
document.addEventListener('keydown', function escClose(e) {
if (e.key === 'Escape') overlay.remove();
}, { once: true });
document.body.appendChild(overlay);
},
_processTextNodes(field) {
const walker = document.createTreeWalker(field, NodeFilter.SHOW_TEXT);
const nodes = [];
let n;
while ((n = walker.nextNode())) nodes.push(n);
for (const textNode of nodes) {
const text = textNode.nodeValue;
this.URL_RE.lastIndex = 0;
if (!this.URL_RE.test(text)) continue;
const frag = document.createDocumentFragment();
let last = 0;
this.URL_RE.lastIndex = 0;
let m;
while ((m = this.URL_RE.exec(text)) !== null) {
if (m.index > last) frag.append(document.createTextNode(text.slice(last, m.index)));
const url = m[0];
frag.append(document.createTextNode(' '));
if (this._isDirect(url)) {
frag.append(this.createImageElement(url, url));
} else {
const img = this.createImageElement(null, url);
img.alt = 'loading...';
const span = document.createElement('span');
span.append(img);
this.resolvePageImage(url).then(real => {
if (real) {
img.src = img.dataset.fullsrc = real;
img.alt = 'image';
} else {
span.replaceWith(document.createTextNode(url + ' '));
}
});
frag.append(span);
}
frag.append(document.createTextNode(' '));
last = this.URL_RE.lastIndex = m.index + url.length;
}
if (last < text.length) frag.append(document.createTextNode(text.slice(last)));
textNode.replaceWith(frag);
}
},
_processAnchors(field) {
for (const a of field.querySelectorAll('a')) {
if (a.dataset.gifProcessed) continue;
const href = a.href;
const isDirect = this._isDirect(href);
const needsResolve = !isDirect && (
/prnt\.sc\/|ibb\.co(?:\.com)?\/|gifyu\.com\/image\/|imgbox\.com\/|postimg\.cc\//i.test(href) ||
/^https?:\/\/(?:www\.)?tenor\.com\//i.test(href)
);
if (!isDirect && !needsResolve) continue;
a.dataset.gifProcessed = 'true';
a.onclick = e => e.preventDefault();
const img = this.createImageElement(null, href);
a.innerHTML = '';
a.append(img);
if (isDirect) {
img.src = href;
} else {
img.alt = 'loading...';
this.resolvePageImage(href).then(real => {
if (real) {
img.src = img.dataset.fullsrc = real;
img.alt = 'image';
} else {
a.replaceWith(document.createTextNode(href));
}
});
}
}
},
addClickHandlers(scope) {
const root = scope || document;
root.querySelectorAll('.shout-text img[data-fullsrc]').forEach(img => {
if (img.dataset.bound) return;
img.dataset.bound = '1';
img.addEventListener('click', e => {
e.preventDefault();
e.stopPropagation();
this.createOverlay(img);
});
if (img.parentElement?.tagName === 'A') {
img.parentElement.addEventListener('click', e => e.preventDefault());
}
});
},
convertLinksInNode(node) {
const field = node.querySelector('.shout-text');
if (!field) return;
this._processTextNodes(field);
this._processAnchors(field);
this.addClickHandlers(field);
},
scanAllShouts() {
document.querySelectorAll('.shout-item').forEach(node => this.convertLinksInNode(node));
},
observeChat() {
const shoutContainer = document.querySelector('#shouts-container');
if (shoutContainer) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((m) => {
m.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE &&
(node.classList?.contains('shout-item') || node.querySelector?.('.shout-item'))) {
const shoutItem = node.classList?.contains('shout-item') ? node : node.querySelector('.shout-item');
setTimeout(() => this.convertLinksInNode(shoutItem), 50);
}
});
});
});
observer.observe(shoutContainer, { childList: true, subtree: true });
}
},
init() {
if (!CONFIG.image_viewer_enabled) return;
this.scanAllShouts();
this.observeChat();
},
stop() {
document.querySelectorAll('.shout-text img[data-fullsrc]').forEach(img => {
const parent = img.parentNode;
if (parent) parent.replaceChild(document.createTextNode(img.dataset.fullsrc + ' '), img);
});
}
};
// ============================================================================
// MODULE 11: UNICODE EMOJI PICKER (EXACT COPY FROM SPOTLIGHT)
// ============================================================================
const smileys = {"name":"Smileys & People","key":"smileys","mob":"😃","emojis":[{"obj":"😀","alt":"Grinning Face"},{"obj":"😃","alt":"Grinning Face with Big Eyes"},{"obj":"😄","alt":"Grinning Face with Smiling Eyes"},{"obj":"😁","alt":"Beaming Face with Smiling Eyes"},{"obj":"😆","alt":"Grinning Squinting Face"},{"obj":"😅","alt":"Grinning Face with Sweat"},{"obj":"🤣","alt":"Rolling on the Floor Laughing"},{"obj":"😂","alt":"Face with Tears of Joy"},{"obj":"🙂","alt":"Slightly Smiling Face"},{"obj":"🙃","alt":"Upside-Down Face"},{"obj":"🫠","alt":"Melting Face"},{"obj":"😉","alt":"Winking Face"},{"obj":"😊","alt":"Smiling Face with Smiling Eyes"},{"obj":"😇","alt":"Smiling Face with Halo"},{"obj":"🥰","alt":"Smiling Face with Hearts"},{"obj":"😍","alt":"Smiling Face with Heart-Eyes"},{"obj":"🤩","alt":"Star-Struck"},{"obj":"😘","alt":"Face Blowing a Kiss"},{"obj":"😗","alt":"Kissing Face"},{"obj":"☺️","alt":"Smiling Face"},{"obj":"😚","alt":"Kissing Face with Closed Eyes"},{"obj":"😙","alt":"Kissing Face with Smiling Eyes"},{"obj":"🥲","alt":"Smiling Face with Tear"},{"obj":"😋","alt":"Face Savoring Food"},{"obj":"😛","alt":"Face with Tongue"},{"obj":"😜","alt":"Winking Face with Tongue"},{"obj":"🤪","alt":"Zany Face"},{"obj":"😝","alt":"Squinting Face with Tongue"},{"obj":"🤑","alt":"Money-Mouth Face"},{"obj":"🤗","alt":"Smiling Face with Open Hands"},{"obj":"🤭","alt":"Face with Hand Over Mouth"},{"obj":"🫢","alt":"Face with Open Eyes and Hand Over Mouth"},{"obj":"🫣","alt":"Face with Peeking Eye"},{"obj":"🤫","alt":"Shushing Face"},{"obj":"🤔","alt":"Thinking Face"},{"obj":"🫡","alt":"Saluting Face"},{"obj":"🤐","alt":"Zipper-Mouth Face"},{"obj":"🤨","alt":"Face with Raised Eyebrow"},{"obj":"😐","alt":"Neutral Face"},{"obj":"😑","alt":"Expressionless Face"},{"obj":"😶","alt":"Face Without Mouth"},{"obj":"🫥","alt":"Dotted Line Face"},{"obj":"😶🌫️","alt":"Face in Clouds"},{"obj":"😏","alt":"Smirking Face"},{"obj":"😒","alt":"Unamused Face"},{"obj":"🙄","alt":"Face with Rolling Eyes"},{"obj":"😬","alt":"Grimacing Face"},{"obj":"😮💨","alt":"Face Exhaling"},{"obj":"🤥","alt":"Lying Face"},{"obj":"😌","alt":"Relieved Face"},{"obj":"😔","alt":"Pensive Face"},{"obj":"😪","alt":"Sleepy Face"},{"obj":"🤤","alt":"Drooling Face"},{"obj":"😴","alt":"Sleeping Face"},{"obj":"😷","alt":"Face with Medical Mask"},{"obj":"🤒","alt":"Face with Thermometer"},{"obj":"🤕","alt":"Face with Head-Bandage"},{"obj":"🤢","alt":"Nauseated Face"},{"obj":"🤮","alt":"Face Vomiting"},{"obj":"🤧","alt":"Sneezing Face"},{"obj":"🥵","alt":"Hot Face"},{"obj":"🥶","alt":"Cold Face"},{"obj":"🥴","alt":"Woozy Face"},{"obj":"😵","alt":"Face with Crossed-Out Eyes"},{"obj":"😵💫","alt":"Face with Spiral Eyes"},{"obj":"🤯","alt":"Exploding Head"},{"obj":"🤠","alt":"Cowboy Hat Face"},{"obj":"🥳","alt":"Partying Face"},{"obj":"🥸","alt":"Disguised Face"},{"obj":"😎","alt":"Smiling Face with Sunglasses"},{"obj":"🤓","alt":"Nerd Face"},{"obj":"🧐","alt":"Face with Monocle"},{"obj":"😕","alt":"Confused Face"},{"obj":"🫤","alt":"Face with Diagonal Mouth"},{"obj":"😟","alt":"Worried Face"},{"obj":"🙁","alt":"Slightly Frowning Face"},{"obj":"☹️","alt":"Frowning Face"},{"obj":"😮","alt":"Face with Open Mouth"},{"obj":"😯","alt":"Hushed Face"},{"obj":"😲","alt":"Astonished Face"},{"obj":"😳","alt":"Flushed Face"},{"obj":"🥺","alt":"Pleading Face"},{"obj":"🥹","alt":"Face Holding Back Tears"},{"obj":"😦","alt":"Frowning Face with Open Mouth"},{"obj":"😧","alt":"Anguished Face"},{"obj":"😨","alt":"Fearful Face"},{"obj":"😰","alt":"Anxious Face with Sweat"},{"obj":"😥","alt":"Sad but Relieved Face"},{"obj":"😢","alt":"Crying Face"},{"obj":"😭","alt":"Loudly Crying Face"},{"obj":"😱","alt":"Face Screaming in Fear"},{"obj":"😖","alt":"Confounded Face"},{"obj":"😣","alt":"Persevering Face"},{"obj":"😞","alt":"Disappointed Face"},{"obj":"😓","alt":"Downcast Face with Sweat"},{"obj":"😩","alt":"Weary Face"},{"obj":"😫","alt":"Tired Face"},{"obj":"🥱","alt":"Yawning Face"},{"obj":"😤","alt":"Face with Steam From Nose"},{"obj":"😡","alt":"Enraged Face"},{"obj":"😠","alt":"Angry Face"},{"obj":"🤬","alt":"Face with Symbols on Mouth"},{"obj":"😈","alt":"Smiling Face with Horns"},{"obj":"👿","alt":"Angry Face with Horns"},{"obj":"💀","alt":"Skull"},{"obj":"☠️","alt":"Skull and Crossbones"},{"obj":"💩","alt":"Pile of Poo"},{"obj":"🤡","alt":"Clown Face"},{"obj":"👹","alt":"Ogre"},{"obj":"👺","alt":"Goblin"},{"obj":"👻","alt":"Ghost"},{"obj":"👽","alt":"Alien"},{"obj":"👾","alt":"Alien Monster"},{"obj":"🤖","alt":"Robot"},{"obj":"😺","alt":"Grinning Cat"},{"obj":"😸","alt":"Grinning Cat with Smiling Eyes"},{"obj":"😹","alt":"Cat with Tears of Joy"},{"obj":"😻","alt":"Smiling Cat with Heart-Eyes"},{"obj":"😼","alt":"Cat with Wry Smile"},{"obj":"😽","alt":"Kissing Cat"},{"obj":"🙀","alt":"Weary Cat"},{"obj":"😿","alt":"Crying Cat"},{"obj":"😾","alt":"Pouting Cat"},{"obj":"💋","alt":"Kiss Mark"},{"obj":"👋","alt":"Waving Hand"},{"obj":"🤚","alt":"Raised Back of Hand"},{"obj":"🖐️","alt":"Hand with Fingers Splayed"},{"obj":"✋","alt":"Raised Hand"},{"obj":"🖖","alt":"Vulcan Salute"},{"obj":"🫱","alt":"Rightwards Hand"},{"obj":"🫲","alt":"Leftwards Hand"},{"obj":"🫳","alt":"Palm Down Hand"},{"obj":"🫴","alt":"Palm Up Hand"},{"obj":"👌","alt":"OK Hand"},{"obj":"🤌","alt":"Pinched Fingers"},{"obj":"🤏","alt":"Pinching Hand"},{"obj":"✌️","alt":"Victory Hand"},{"obj":"🤞","alt":"Crossed Fingers"},{"obj":"🫰","alt":"Hand with Index Finger and Thumb Crossed"},{"obj":"🤟","alt":"Love-You Gesture"},{"obj":"🤘","alt":"Sign of the Horns"},{"obj":"🤙","alt":"Call Me Hand"},{"obj":"👈","alt":"Backhand Index Pointing Left"},{"obj":"👉","alt":"Backhand Index Pointing Right"},{"obj":"👆","alt":"Backhand Index Pointing Up"},{"obj":"🖕","alt":"Middle Finger"},{"obj":"👇","alt":"Backhand Index Pointing Down"},{"obj":"☝️","alt":"Index Pointing Up"},{"obj":"🫵","alt":"Index Pointing at the Viewer"},{"obj":"👍","alt":"Thumbs Up"},{"obj":"👎","alt":"Thumbs Down"},{"obj":"✊","alt":"Raised Fist"},{"obj":"👊","alt":"Oncoming Fist"},{"obj":"🤛","alt":"Left-Facing Fist"},{"obj":"🤜","alt":"Right-Facing Fist"},{"obj":"👏","alt":"Clapping Hands"},{"obj":"🙌","alt":"Raising Hands"},{"obj":"🫶","alt":"Heart Hands"},{"obj":"👐","alt":"Open Hands"},{"obj":"🤲","alt":"Palms Up Together"},{"obj":"🤝","alt":"Handshake"},{"obj":"🙏","alt":"Folded Hands"},{"obj":"✍️","alt":"Writing Hand"},{"obj":"💅","alt":"Nail Polish"},{"obj":"🤳","alt":"Selfie"},{"obj":"💪","alt":"Flexed Biceps"},{"obj":"🦾","alt":"Mechanical Arm"},{"obj":"🦿","alt":"Mechanical Leg"},{"obj":"🦵","alt":"Leg"},{"obj":"🦶","alt":"Foot"},{"obj":"👂","alt":"Ear"},{"obj":"🦻","alt":"Ear with Hearing Aid"},{"obj":"👃","alt":"Nose"},{"obj":"🧠","alt":"Brain"},{"obj":"🫀","alt":"Anatomical Heart"},{"obj":"🫁","alt":"Lungs"},{"obj":"🦷","alt":"Tooth"},{"obj":"🦴","alt":"Bone"},{"obj":"👀","alt":"Eyes"},{"obj":"👁️","alt":"Eye"},{"obj":"👅","alt":"Tongue"},{"obj":"👄","alt":"Mouth"},{"obj":"🫦","alt":"Biting Lip"},{"obj":"👶","alt":"Baby"},{"obj":"🧒","alt":"Child"},{"obj":"👦","alt":"Boy"},{"obj":"👧","alt":"Girl"},{"obj":"🧑","alt":"Person"},{"obj":"👱","alt":"Person: Blond Hair"},{"obj":"👨","alt":"Man"},{"obj":"🧔","alt":"Person: Beard"},{"obj":"👨🦰","alt":"Man: Red Hair"},{"obj":"👨🦱","alt":"Man: Curly Hair"},{"obj":"👨🦳","alt":"Man: White Hair"},{"obj":"👨🦲","alt":"Man: Bald"},{"obj":"👩","alt":"Woman"},{"obj":"👩🦰","alt":"Woman: Red Hair"},{"obj":"🧑🦰","alt":"Person: Red Hair"},{"obj":"👩🦱","alt":"Woman: Curly Hair"},{"obj":"🧑🦱","alt":"Person: Curly Hair"},{"obj":"👩🦳","alt":"Woman: White Hair"},{"obj":"🧑🦳","alt":"Person: White Hair"},{"obj":"👩🦲","alt":"Woman: Bald"},{"obj":"🧑🦲","alt":"Person: Bald"},{"obj":"👱♀️","alt":"Woman: Blond Hair"},{"obj":"👱♂️","alt":"Man: Blond Hair"},{"obj":"🧓","alt":"Older Person"},{"obj":"👴","alt":"Old Man"},{"obj":"👵","alt":"Old Woman"},{"obj":"🙍","alt":"Person Frowning"},{"obj":"🙍♂️","alt":"Man Frowning"},{"obj":"🙍♀️","alt":"Woman Frowning"},{"obj":"🙎","alt":"Person Pouting"},{"obj":"🙎♂️","alt":"Man Pouting"},{"obj":"🙎♀️","alt":"Woman Pouting"},{"obj":"🙅","alt":"Person Gesturing No"},{"obj":"🙅♂️","alt":"Man Gesturing No"},{"obj":"🙅♀️","alt":"Woman Gesturing No"},{"obj":"🙆","alt":"Person Gesturing OK"},{"obj":"🙆♂️","alt":"Man Gesturing OK"},{"obj":"🙆♀️","alt":"Woman Gesturing OK"},{"obj":"💁","alt":"Person Tipping Hand"},{"obj":"💁♂️","alt":"Man Tipping Hand"},{"obj":"💁♀️","alt":"Woman Tipping Hand"},{"obj":"🙋","alt":"Person Raising Hand"},{"obj":"🙋♂️","alt":"Man Raising Hand"},{"obj":"🙋♀️","alt":"Woman Raising Hand"},{"obj":"🧏","alt":"Deaf Person"},{"obj":"🧏♂️","alt":"Deaf Man"},{"obj":"🧏♀️","alt":"Deaf Woman"},{"obj":"🙇","alt":"Person Bowing"},{"obj":"🙇♂️","alt":"Man Bowing"},{"obj":"🙇♀️","alt":"Woman Bowing"},{"obj":"🤦","alt":"Person Facepalming"},{"obj":"🤦♂️","alt":"Man Facepalming"},{"obj":"🤦♀️","alt":"Woman Facepalming"},{"obj":"🤷","alt":"Person Shrugging"},{"obj":"🤷♂️","alt":"Man Shrugging"},{"obj":"🤷♀️","alt":"Woman Shrugging"},{"obj":"🧑⚕️","alt":"Health Worker"},{"obj":"👨⚕️","alt":"Man Health Worker"},{"obj":"👩⚕️","alt":"Woman Health Worker"},{"obj":"🧑🎓","alt":"Student"},{"obj":"👨🎓","alt":"Man Student"},{"obj":"👩🎓","alt":"Woman Student"},{"obj":"🧑🏫","alt":"Teacher"},{"obj":"👨🏫","alt":"Man Teacher"},{"obj":"👩🏫","alt":"Woman Teacher"},{"obj":"🧑⚖️","alt":"Judge"},{"obj":"👨⚖️","alt":"Man Judge"},{"obj":"👩⚖️","alt":"Woman Judge"},{"obj":"🧑🌾","alt":"Farmer"},{"obj":"👨🌾","alt":"Man Farmer"},{"obj":"👩🌾","alt":"Woman Farmer"},{"obj":"🧑🍳","alt":"Cook"},{"obj":"👨🍳","alt":"Man Cook"},{"obj":"👩🍳","alt":"Woman Cook"},{"obj":"🧑🔧","alt":"Mechanic"},{"obj":"👨🔧","alt":"Man Mechanic"},{"obj":"👩🔧","alt":"Woman Mechanic"},{"obj":"🧑🏭","alt":"Factory Worker"},{"obj":"👨🏭","alt":"Man Factory Worker"},{"obj":"👩🏭","alt":"Woman Factory Worker"},{"obj":"🧑💼","alt":"Office Worker"},{"obj":"👨💼","alt":"Man Office Worker"},{"obj":"👩💼","alt":"Woman Office Worker"},{"obj":"🧑🔬","alt":"Scientist"},{"obj":"👨🔬","alt":"Man Scientist"},{"obj":"👩🔬","alt":"Woman Scientist"},{"obj":"🧑💻","alt":"Technologist"},{"obj":"👨💻","alt":"Man Technologist"},{"obj":"👩💻","alt":"Woman Technologist"},{"obj":"🧑🎤","alt":"Singer"},{"obj":"👨🎤","alt":"Man Singer"},{"obj":"👩🎤","alt":"Woman Singer"},{"obj":"🧑🎨","alt":"Artist"},{"obj":"👨🎨","alt":"Man Artist"},{"obj":"👩🎨","alt":"Woman Artist"},{"obj":"🧑✈️","alt":"Pilot"},{"obj":"👨✈️","alt":"Man Pilot"},{"obj":"👩✈️","alt":"Woman Pilot"},{"obj":"🧑🚀","alt":"Astronaut"},{"obj":"👨🚀","alt":"Man Astronaut"},{"obj":"👩🚀","alt":"Woman Astronaut"},{"obj":"🧑🚒","alt":"Firefighter"},{"obj":"👨🚒","alt":"Man Firefighter"},{"obj":"👩🚒","alt":"Woman Firefighter"},{"obj":"👮","alt":"Police Officer"},{"obj":"👮♂️","alt":"Man Police Officer"},{"obj":"👮♀️","alt":"Woman Police Officer"},{"obj":"🕵️","alt":"Detective"},{"obj":"🕵️♂️","alt":"Man Detective"},{"obj":"🕵️♀️","alt":"Woman Detective"},{"obj":"💂","alt":"Guard"},{"obj":"💂♂️","alt":"Man Guard"},{"obj":"💂♀️","alt":"Woman Guard"},{"obj":"🥷","alt":"Ninja"},{"obj":"👷","alt":"Construction Worker"},{"obj":"👷♂️","alt":"Man Construction Worker"},{"obj":"👷♀️","alt":"Woman Construction Worker"},{"obj":"🫅","alt":"Person with Crown"},{"obj":"🤴","alt":"Prince"},{"obj":"👸","alt":"Princess"},{"obj":"👳","alt":"Person Wearing Turban"},{"obj":"👳♂️","alt":"Man Wearing Turban"},{"obj":"👳♀️","alt":"Woman Wearing Turban"},{"obj":"👲","alt":"Person with Skullcap"},{"obj":"🧕","alt":"Woman with Headscarf"},{"obj":"🤵","alt":"Person in Tuxedo"},{"obj":"🤵♂️","alt":"Man in Tuxedo"},{"obj":"🤵♀️","alt":"Woman in Tuxedo"},{"obj":"👰","alt":"Person with Veil"},{"obj":"👰♂️","alt":"Man with Veil"},{"obj":"👰♀️","alt":"Woman with Veil"},{"obj":"🤰","alt":"Pregnant Woman"},{"obj":"🫃","alt":"Pregnant Man"},{"obj":"🫄","alt":"Pregnant Person"},{"obj":"🤱","alt":"Breast-Feeding"},{"obj":"👩🍼","alt":"Woman Feeding Baby"},{"obj":"👨🍼","alt":"Man Feeding Baby"},{"obj":"🧑🍼","alt":"Person Feeding Baby"},{"obj":"👼","alt":"Baby Angel"},{"obj":"🎅","alt":"Santa Claus"},{"obj":"🤶","alt":"Mrs. Claus"},{"obj":"🧑🎄","alt":"Mx Claus"},{"obj":"🦸","alt":"Superhero"},{"obj":"🦸♂️","alt":"Man Superhero"},{"obj":"🦸♀️","alt":"Woman Superhero"},{"obj":"🦹","alt":"Supervillain"},{"obj":"🦹♂️","alt":"Man Supervillain"},{"obj":"🦹♀️","alt":"Woman Supervillain"},{"obj":"🧙","alt":"Mage"},{"obj":"🧙♂️","alt":"Man Mage"},{"obj":"🧙♀️","alt":"Woman Mage"},{"obj":"🧚","alt":"Fairy"},{"obj":"🧚♂️","alt":"Man Fairy"},{"obj":"🧚♀️","alt":"Woman Fairy"},{"obj":"🧛","alt":"Vampire"},{"obj":"🧛♂️","alt":"Man Vampire"},{"obj":"🧛♀️","alt":"Woman Vampire"},{"obj":"🧜","alt":"Merperson"},{"obj":"🧜♂️","alt":"Merman"},{"obj":"🧜♀️","alt":"Mermaid"},{"obj":"🧝","alt":"Elf"},{"obj":"🧝♂️","alt":"Man Elf"},{"obj":"🧝♀️","alt":"Woman Elf"},{"obj":"🧞","alt":"Genie"},{"obj":"🧞♂️","alt":"Man Genie"},{"obj":"🧞♀️","alt":"Woman Genie"},{"obj":"🧟","alt":"Zombie"},{"obj":"🧟♂️","alt":"Man Zombie"},{"obj":"🧟♀️","alt":"Woman Zombie"},{"obj":"🧌","alt":"Troll"},{"obj":"💆","alt":"Person Getting Massage"},{"obj":"💆♂️","alt":"Man Getting Massage"},{"obj":"💆♀️","alt":"Woman Getting Massage"},{"obj":"💇","alt":"Person Getting Haircut"},{"obj":"💇♂️","alt":"Man Getting Haircut"},{"obj":"💇♀️","alt":"Woman Getting Haircut"},{"obj":"🚶","alt":"Person Walking"},{"obj":"🚶♂️","alt":"Man Walking"},{"obj":"🚶♀️","alt":"Woman Walking"},{"obj":"🧍","alt":"Person Standing"},{"obj":"🧍♂️","alt":"Man Standing"},{"obj":"🧍♀️","alt":"Woman Standing"},{"obj":"🧎","alt":"Person Kneeling"},{"obj":"🧎♂️","alt":"Man Kneeling"},{"obj":"🧎♀️","alt":"Woman Kneeling"},{"obj":"🧑🦯","alt":"Person with White Cane"},{"obj":"👨🦯","alt":"Man with White Cane"},{"obj":"👩🦯","alt":"Woman with White Cane"},{"obj":"🧑🦼","alt":"Person in Motorized Wheelchair"},{"obj":"👨🦼","alt":"Man in Motorized Wheelchair"},{"obj":"👩🦼","alt":"Woman in Motorized Wheelchair"},{"obj":"🧑🦽","alt":"Person in Manual Wheelchair"},{"obj":"👨🦽","alt":"Man in Manual Wheelchair"},{"obj":"👩🦽","alt":"Woman in Manual Wheelchair"},{"obj":"🏃","alt":"Person Running"},{"obj":"🏃♂️","alt":"Man Running"},{"obj":"🏃♀️","alt":"Woman Running"},{"obj":"💃","alt":"Woman Dancing"},{"obj":"🕺","alt":"Man Dancing"},{"obj":"🕴️","alt":"Person in Suit Levitating"},{"obj":"👯","alt":"People with Bunny Ears"},{"obj":"👯♂️","alt":"Men with Bunny Ears"},{"obj":"👯♀️","alt":"Women with Bunny Ears"},{"obj":"🧖","alt":"Person in Steamy Room"},{"obj":"🧖♂️","alt":"Man in Steamy Room"},{"obj":"🧖♀️","alt":"Woman in Steamy Room"},{"obj":"🧘","alt":"Person in Lotus Position"},{"obj":"🧑🤝🧑","alt":"People Holding Hands"},{"obj":"👭","alt":"Women Holding Hands"},{"obj":"👫","alt":"Woman and Man Holding Hands"},{"obj":"👬","alt":"Men Holding Hands"},{"obj":"💏","alt":"Kiss"},{"obj":"👩❤️💋👨","alt":"Kiss: Woman, Man"},{"obj":"👨❤️💋👨","alt":"Kiss: Man, Man"},{"obj":"👩❤️💋👩","alt":"Kiss: Woman, Woman"},{"obj":"💑","alt":"Couple with Heart"},{"obj":"👩❤️👨","alt":"Couple with Heart: Woman, Man"},{"obj":"👨❤️👨","alt":"Couple with Heart: Man, Man"},{"obj":"👩❤️👩","alt":"Couple with Heart: Woman, Woman"},{"obj":"👪","alt":"Family"},{"obj":"👨👩👦","alt":"Family: Man, Woman, Boy"},{"obj":"👨👩👧","alt":"Family: Man, Woman, Girl"},{"obj":"👨👩👧👦","alt":"Family: Man, Woman, Girl, Boy"},{"obj":"👨👩👦👦","alt":"Family: Man, Woman, Boy, Boy"},{"obj":"👨👩👧👧","alt":"Family: Man, Woman, Girl, Girl"},{"obj":"👨👨👦","alt":"Family: Man, Man, Boy"},{"obj":"👨👨👧","alt":"Family: Man, Man, Girl"},{"obj":"👨👨👧👦","alt":"Family: Man, Man, Girl, Boy"},{"obj":"👨👨👦👦","alt":"Family: Man, Man, Boy, Boy"},{"obj":"👨👨👧👧","alt":"Family: Man, Man, Girl, Girl"},{"obj":"👩👩👦","alt":"Family: Woman, Woman, Boy"},{"obj":"👩👩👧","alt":"Family: Woman, Woman, Girl"},{"obj":"👩👩👧👦","alt":"Family: Woman, Woman, Girl, Boy"},{"obj":"👩👩👦👦","alt":"Family: Woman, Woman, Boy, Boy"},{"obj":"👩👩👧👧","alt":"Family: Woman, Woman, Girl, Girl"},{"obj":"👨👦","alt":"Family: Man, Boy"},{"obj":"👨👦👦","alt":"Family: Man, Boy, Boy"},{"obj":"👨👧","alt":"Family: Man, Girl"},{"obj":"👨👧👦","alt":"Family: Man, Girl, Boy"},{"obj":"👨👧👧","alt":"Family: Man, Girl, Girl"},{"obj":"👩👦","alt":"Family: Woman, Boy"},{"obj":"👩👦👦","alt":"Family: Woman, Boy, Boy"},{"obj":"👩👧","alt":"Family: Woman, Girl"},{"obj":"👩👧👦","alt":"Family: Woman, Girl, Boy"},{"obj":"👩👧👧","alt":"Family: Woman, Girl, Girl"},{"obj":"🗣️","alt":"Speaking Head"},{"obj":"👤","alt":"Bust in Silhouette"},{"obj":"👥","alt":"Busts in Silhouette"},{"obj":"🫂","alt":"People Hugging"},{"obj":"👣","alt":"Footprints"},{"obj":"🧳","alt":"Luggage"},{"obj":"🌂","alt":"Closed Umbrella"},{"obj":"☂️","alt":"Umbrella"},{"obj":"🎃","alt":"Jack-O-Lantern"},{"obj":"🧵","alt":"Thread"},{"obj":"🧶","alt":"Yarn"},{"obj":"👓","alt":"Glasses"},{"obj":"🕶️","alt":"Sunglasses"},{"obj":"🥽","alt":"Goggles"},{"obj":"🥼","alt":"Lab Coat"},{"obj":"🦺","alt":"Safety Vest"},{"obj":"👔","alt":"Necktie"},{"obj":"👕","alt":"T-Shirt"},{"obj":"👖","alt":"Jeans"},{"obj":"🧣","alt":"Scarf"},{"obj":"🧤","alt":"Gloves"},{"obj":"🧥","alt":"Coat"},{"obj":"🧦","alt":"Socks"},{"obj":"👗","alt":"Dress"},{"obj":"👘","alt":"Kimono"},{"obj":"🥻","alt":"Sari"},{"obj":"🩱","alt":"One-Piece Swimsuit"},{"obj":"🩲","alt":"Briefs"},{"obj":"🩳","alt":"Shorts"},{"obj":"👙","alt":"Bikini"},{"obj":"👚","alt":"Woman’s Clothes"},{"obj":"👛","alt":"Purse"},{"obj":"👜","alt":"Handbag"},{"obj":"👝","alt":"Clutch Bag"},{"obj":"🎒","alt":"Backpack"},{"obj":"🩴","alt":"Thong Sandal"},{"obj":"👞","alt":"Man’s Shoe"},{"obj":"👟","alt":"Running Shoe"},{"obj":"🥾","alt":"Hiking Boot"},{"obj":"🥿","alt":"Flat Shoe"},{"obj":"👠","alt":"High-Heeled Shoe"},{"obj":"👡","alt":"Woman’s Sandal"},{"obj":"🩰","alt":"Ballet Shoes"},{"obj":"👢","alt":"Woman’s Boot"},{"obj":"👑","alt":"Crown"},{"obj":"👒","alt":"Woman’s Hat"},{"obj":"🎩","alt":"Top Hat"},{"obj":"🎓","alt":"Graduation Cap"},{"obj":"🧢","alt":"Billed Cap"},{"obj":"🪖","alt":"Military Helmet"},{"obj":"⛑️","alt":"Rescue Worker’s Helmet"},{"obj":"💄","alt":"Lipstick"},{"obj":"💍","alt":"Ring"},{"obj":"💼","alt":"Briefcase"},{"obj":"🩸","alt":"Drop of Blood"}]},
nature = {"name":"Animals & Nature","key":"nature","mob":"🐻","emojis":[{"obj":"🙈","alt":"See-No-Evil Monkey"},{"obj":"🙉","alt":"Hear-No-Evil Monkey"},{"obj":"🙊","alt":"Speak-No-Evil Monkey"},{"obj":"💥","alt":"Collision"},{"obj":"💫","alt":"Dizzy"},{"obj":"💦","alt":"Sweat Droplets"},{"obj":"💨","alt":"Dashing Away"},{"obj":"🐵","alt":"Monkey Face"},{"obj":"🐒","alt":"Monkey"},{"obj":"🦍","alt":"Gorilla"},{"obj":"🦧","alt":"Orangutan"},{"obj":"🐶","alt":"Dog Face"},{"obj":"🐕","alt":"Dog"},{"obj":"🦮","alt":"Guide Dog"},{"obj":"🐕🦺","alt":"Service Dog"},{"obj":"🐩","alt":"Poodle"},{"obj":"🐺","alt":"Wolf"},{"obj":"🦊","alt":"Fox"},{"obj":"🦝","alt":"Raccoon"},{"obj":"🐱","alt":"Cat Face"},{"obj":"🐈","alt":"Cat"},{"obj":"🐈⬛","alt":"Black Cat"},{"obj":"🦁","alt":"Lion"},{"obj":"🐯","alt":"Tiger Face"},{"obj":"🐅","alt":"Tiger"},{"obj":"🐆","alt":"Leopard"},{"obj":"🐴","alt":"Horse Face"},{"obj":"🐎","alt":"Horse"},{"obj":"🦄","alt":"Unicorn"},{"obj":"🦓","alt":"Zebra"},{"obj":"🦌","alt":"Deer"},{"obj":"🦬","alt":"Bison"},{"obj":"🐮","alt":"Cow Face"},{"obj":"🐂","alt":"Ox"},{"obj":"🐃","alt":"Water Buffalo"},{"obj":"🐄","alt":"Cow"},{"obj":"🐷","alt":"Pig Face"},{"obj":"🐖","alt":"Pig"},{"obj":"🐗","alt":"Boar"},{"obj":"🐽","alt":"Pig Nose"},{"obj":"🐏","alt":"Ram"},{"obj":"🐑","alt":"Ewe"},{"obj":"🐐","alt":"Goat"},{"obj":"🐪","alt":"Camel"},{"obj":"🐫","alt":"Two-Hump Camel"},{"obj":"🦙","alt":"Llama"},{"obj":"🦒","alt":"Giraffe"},{"obj":"🐘","alt":"Elephant"},{"obj":"🦣","alt":"Mammoth"},{"obj":"🦏","alt":"Rhinoceros"},{"obj":"🦛","alt":"Hippopotamus"},{"obj":"🐭","alt":"Mouse Face"},{"obj":"🐁","alt":"Mouse"},{"obj":"🐀","alt":"Rat"},{"obj":"🐹","alt":"Hamster"},{"obj":"🐰","alt":"Rabbit Face"},{"obj":"🐇","alt":"Rabbit"},{"obj":"🐿️","alt":"Chipmunk"},{"obj":"🦫","alt":"Beaver"},{"obj":"🦔","alt":"Hedgehog"},{"obj":"🦇","alt":"Bat"},{"obj":"🐻","alt":"Bear"},{"obj":"🐻❄️","alt":"Polar Bear"},{"obj":"🐨","alt":"Koala"},{"obj":"🐼","alt":"Panda"},{"obj":"🦥","alt":"Sloth"},{"obj":"🦦","alt":"Otter"},{"obj":"🦨","alt":"Skunk"},{"obj":"🦘","alt":"Kangaroo"},{"obj":"🦡","alt":"Badger"},{"obj":"🐾","alt":"Paw Prints"},{"obj":"🦃","alt":"Turkey"},{"obj":"🐔","alt":"Chicken"},{"obj":"🐓","alt":"Rooster"},{"obj":"🐣","alt":"Hatching Chick"},{"obj":"🐤","alt":"Baby Chick"},{"obj":"🐥","alt":"Front-Facing Baby Chick"},{"obj":"🐦","alt":"Bird"},{"obj":"🐧","alt":"Penguin"},{"obj":"🕊️","alt":"Dove"},{"obj":"🦅","alt":"Eagle"},{"obj":"🦆","alt":"Duck"},{"obj":"🦢","alt":"Swan"},{"obj":"🦉","alt":"Owl"},{"obj":"🦤","alt":"Dodo"},{"obj":"🪶","alt":"Feather"},{"obj":"🦩","alt":"Flamingo"},{"obj":"🦚","alt":"Peacock"},{"obj":"🦜","alt":"Parrot"},{"obj":"🐸","alt":"Frog"},{"obj":"🐊","alt":"Crocodile"},{"obj":"🐢","alt":"Turtle"},{"obj":"🦎","alt":"Lizard"},{"obj":"🐍","alt":"Snake"},{"obj":"🐲","alt":"Dragon Face"},{"obj":"🐉","alt":"Dragon"},{"obj":"🦕","alt":"Sauropod"},{"obj":"🦖","alt":"T-Rex"},{"obj":"🐳","alt":"Spouting Whale"},{"obj":"🐋","alt":"Whale"},{"obj":"🐬","alt":"Dolphin"},{"obj":"🦭","alt":"Seal"},{"obj":"🐟","alt":"Fish"},{"obj":"🐠","alt":"Tropical Fish"},{"obj":"🐡","alt":"Blowfish"},{"obj":"🦈","alt":"Shark"},{"obj":"🐙","alt":"Octopus"},{"obj":"🐚","alt":"Spiral Shell"},{"obj":"🪸","alt":"Coral"},{"obj":"🐌","alt":"Snail"},{"obj":"🦋","alt":"Butterfly"},{"obj":"🐛","alt":"Bug"},{"obj":"🐜","alt":"Ant"},{"obj":"🐝","alt":"Honeybee"},{"obj":"🪲","alt":"Beetle"},{"obj":"🐞","alt":"Lady Beetle"},{"obj":"🦗","alt":"Cricket"},{"obj":"🪳","alt":"Cockroach"},{"obj":"🕷️","alt":"Spider"},{"obj":"🕸️","alt":"Spider Web"},{"obj":"🦂","alt":"Scorpion"},{"obj":"🦟","alt":"Mosquito"},{"obj":"🪰","alt":"Fly"},{"obj":"🪱","alt":"Worm"},{"obj":"🦠","alt":"Microbe"},{"obj":"💐","alt":"Bouquet"},{"obj":"🌸","alt":"Cherry Blossom"},{"obj":"💮","alt":"White Flower"},{"obj":"🪷","alt":"Lotus"},{"obj":"🏵️","alt":"Rosette"},{"obj":"🌹","alt":"Rose"},{"obj":"🥀","alt":"Wilted Flower"},{"obj":"🌺","alt":"Hibiscus"},{"obj":"🌻","alt":"Sunflower"},{"obj":"🌼","alt":"Blossom"},{"obj":"🌷","alt":"Tulip"},{"obj":"🌱","alt":"Seedling"},{"obj":"🪴","alt":"Potted Plant"},{"obj":"🌲","alt":"Evergreen Tree"},{"obj":"🌳","alt":"Deciduous Tree"},{"obj":"🌴","alt":"Palm Tree"},{"obj":"🌵","alt":"Cactus"},{"obj":"🌾","alt":"Sheaf of Rice"},{"obj":"🌿","alt":"Herb"},{"obj":"☘️","alt":"Shamrock"},{"obj":"🍀","alt":"Four Leaf Clover"},{"obj":"🍁","alt":"Maple Leaf"},{"obj":"🍂","alt":"Fallen Leaf"},{"obj":"🍃","alt":"Leaf Fluttering in Wind"},{"obj":"🪹","alt":"Empty Nest"},{"obj":"🪺","alt":"Nest with Eggs"},{"obj":"🍄","alt":"Mushroom"},{"obj":"🌰","alt":"Chestnut"},{"obj":"🦀","alt":"Crab"},{"obj":"🦞","alt":"Lobster"},{"obj":"🦐","alt":"Shrimp"},{"obj":"🦑","alt":"Squid"},{"obj":"🌍","alt":"Globe Showing Europe-Africa"},{"obj":"🌎","alt":"Globe Showing Americas"},{"obj":"🌏","alt":"Globe Showing Asia-Australia"},{"obj":"🌐","alt":"Globe with Meridians"},{"obj":"🪨","alt":"Rock"},{"obj":"🌑","alt":"New Moon"},{"obj":"🌒","alt":"Waxing Crescent Moon"},{"obj":"🌓","alt":"First Quarter Moon"},{"obj":"🌔","alt":"Waxing Gibbous Moon"},{"obj":"🌕","alt":"Full Moon"},{"obj":"🌖","alt":"Waning Gibbous Moon"},{"obj":"🌗","alt":"Last Quarter Moon"},{"obj":"🌘","alt":"Waning Crescent Moon"},{"obj":"🌙","alt":"Crescent Moon"},{"obj":"🌚","alt":"New Moon Face"},{"obj":"🌛","alt":"First Quarter Moon Face"},{"obj":"🌜","alt":"Last Quarter Moon Face"},{"obj":"☀️","alt":"Sun"},{"obj":"🌝","alt":"Full Moon Face"},{"obj":"🌞","alt":"Sun with Face"},{"obj":"⭐","alt":"Star"},{"obj":"🌟","alt":"Glowing Star"},{"obj":"🌠","alt":"Shooting Star"},{"obj":"☁️","alt":"Cloud"},{"obj":"⛅","alt":"Sun Behind Cloud"},{"obj":"⛈️","alt":"Cloud with Lightning and Rain"},{"obj":"🌤️","alt":"Sun Behind Small Cloud"},{"obj":"🌥️","alt":"Sun Behind Large Cloud"},{"obj":"🌦️","alt":"Sun Behind Rain Cloud"},{"obj":"🌧️","alt":"Cloud with Rain"},{"obj":"🌨️","alt":"Cloud with Snow"},{"obj":"🌩️","alt":"Cloud with Lightning"},{"obj":"🌪️","alt":"Tornado"},{"obj":"🌫️","alt":"Fog"},{"obj":"🌬️","alt":"Wind Face"},{"obj":"🌈","alt":"Rainbow"},{"obj":"☂️","alt":"Umbrella"},{"obj":"☔","alt":"Umbrella with Rain Drops"},{"obj":"⚡","alt":"High Voltage"},{"obj":"❄️","alt":"Snowflake"},{"obj":"☃️","alt":"Snowman"},{"obj":"⛄","alt":"Snowman Without Snow"},{"obj":"☄️","alt":"Comet"},{"obj":"🔥","alt":"Fire"},{"obj":"💧","alt":"Droplet"},{"obj":"🌊","alt":"Water Wave"},{"obj":"🎄","alt":"Christmas Tree"},{"obj":"✨","alt":"Sparkles"},{"obj":"🎋","alt":"Tanabata Tree"},{"obj":"🎍","alt":"Pine Decoration"},{"obj":"🫧","alt":"Bubbles"}]},
food = {"name":"Food & Drink","key":"food","mob":"🍔","emojis":[{"obj":"🍇","alt":"Grapes"},{"obj":"🍈","alt":"Melon"},{"obj":"🍉","alt":"Watermelon"},{"obj":"🍊","alt":"Tangerine"},{"obj":"🍋","alt":"Lemon"},{"obj":"🍌","alt":"Banana"},{"obj":"🍍","alt":"Pineapple"},{"obj":"🥭","alt":"Mango"},{"obj":"🍎","alt":"Red Apple"},{"obj":"🍏","alt":"Green Apple"},{"obj":"🍐","alt":"Pear"},{"obj":"🍑","alt":"Peach"},{"obj":"🍒","alt":"Cherries"},{"obj":"🍓","alt":"Strawberry"},{"obj":"🫐","alt":"Blueberries"},{"obj":"🥝","alt":"Kiwi Fruit"},{"obj":"🍅","alt":"Tomato"},{"obj":"🫒","alt":"Olive"},{"obj":"🥥","alt":"Coconut"},{"obj":"🥑","alt":"Avocado"},{"obj":"🍆","alt":"Eggplant"},{"obj":"🥔","alt":"Potato"},{"obj":"🥕","alt":"Carrot"},{"obj":"🌽","alt":"Ear of Corn"},{"obj":"🌶️","alt":"Hot Pepper"},{"obj":"🫑","alt":"Bell Pepper"},{"obj":"🥒","alt":"Cucumber"},{"obj":"🥬","alt":"Leafy Green"},{"obj":"🥦","alt":"Broccoli"},{"obj":"🧄","alt":"Garlic"},{"obj":"🧅","alt":"Onion"},{"obj":"🍄","alt":"Mushroom"},{"obj":"🥜","alt":"Peanuts"},{"obj":"🫘","alt":"Beans"},{"obj":"🌰","alt":"Chestnut"},{"obj":"🍞","alt":"Bread"},{"obj":"🥐","alt":"Croissant"},{"obj":"🥖","alt":"Baguette Bread"},{"obj":"🫓","alt":"Flatbread"},{"obj":"🥨","alt":"Pretzel"},{"obj":"🥯","alt":"Bagel"},{"obj":"🥞","alt":"Pancakes"},{"obj":"🧇","alt":"Waffle"},{"obj":"🧀","alt":"Cheese Wedge"},{"obj":"🍖","alt":"Meat on Bone"},{"obj":"🍗","alt":"Poultry Leg"},{"obj":"🥩","alt":"Cut of Meat"},{"obj":"🥓","alt":"Bacon"},{"obj":"🍔","alt":"Hamburger"},{"obj":"🍟","alt":"French Fries"},{"obj":"🍕","alt":"Pizza"},{"obj":"🌭","alt":"Hot Dog"},{"obj":"🥪","alt":"Sandwich"},{"obj":"🌮","alt":"Taco"},{"obj":"🌯","alt":"Burrito"},{"obj":"🫔","alt":"Tamale"},{"obj":"🥙","alt":"Stuffed Flatbread"},{"obj":"🧆","alt":"Falafel"},{"obj":"🥚","alt":"Egg"},{"obj":"🍳","alt":"Cooking"},{"obj":"🥘","alt":"Shallow Pan of Food"},{"obj":"🍲","alt":"Pot of Food"},{"obj":"🫕","alt":"Fondue"},{"obj":"🥣","alt":"Bowl with Spoon"},{"obj":"🥗","alt":"Green Salad"},{"obj":"🍿","alt":"Popcorn"},{"obj":"🧈","alt":"Butter"},{"obj":"🧂","alt":"Salt"},{"obj":"🥫","alt":"Canned Food"},{"obj":"🍱","alt":"Bento Box"},{"obj":"🍘","alt":"Rice Cracker"},{"obj":"🍙","alt":"Rice Ball"},{"obj":"🍚","alt":"Cooked Rice"},{"obj":"🍛","alt":"Curry Rice"},{"obj":"🍜","alt":"Steaming Bowl"},{"obj":"🍝","alt":"Spaghetti"},{"obj":"🍠","alt":"Roasted Sweet Potato"},{"obj":"🍢","alt":"Oden"},{"obj":"🍣","alt":"Sushi"},{"obj":"🍤","alt":"Fried Shrimp"},{"obj":"🍥","alt":"Fish Cake with Swirl"},{"obj":"🥮","alt":"Moon Cake"},{"obj":"🍡","alt":"Dango"},{"obj":"🥟","alt":"Dumpling"},{"obj":"🥠","alt":"Fortune Cookie"},{"obj":"🥡","alt":"Takeout Box"},{"obj":"🦪","alt":"Oyster"},{"obj":"🍦","alt":"Soft Ice Cream"},{"obj":"🍧","alt":"Shaved Ice"},{"obj":"🍨","alt":"Ice Cream"},{"obj":"🍩","alt":"Doughnut"},{"obj":"🍪","alt":"Cookie"},{"obj":"🎂","alt":"Birthday Cake"},{"obj":"🍰","alt":"Shortcake"},{"obj":"🧁","alt":"Cupcake"},{"obj":"🥧","alt":"Pie"},{"obj":"🍫","alt":"Chocolate Bar"},{"obj":"🍬","alt":"Candy"},{"obj":"🍭","alt":"Lollipop"},{"obj":"🍮","alt":"Custard"},{"obj":"🍯","alt":"Honey Pot"},{"obj":"🍼","alt":"Baby Bottle"},{"obj":"🥛","alt":"Glass of Milk"},{"obj":"☕","alt":"Hot Beverage"},{"obj":"🫖","alt":"Teapot"},{"obj":"🍵","alt":"Teacup Without Handle"},{"obj":"🍶","alt":"Sake"},{"obj":"🍾","alt":"Bottle with Popping Cork"},{"obj":"🍷","alt":"Wine Glass"},{"obj":"🍸","alt":"Cocktail Glass"},{"obj":"🍹","alt":"Tropical Drink"},{"obj":"🍺","alt":"Beer Mug"},{"obj":"🍻","alt":"Clinking Beer Mugs"},{"obj":"🥂","alt":"Clinking Glasses"},{"obj":"🥃","alt":"Tumbler Glass"},{"obj":"🫗","alt":"Pouring Liquid"},{"obj":"🥤","alt":"Cup with Straw"},{"obj":"🧋","alt":"Bubble Tea"},{"obj":"🧃","alt":"Beverage Box"},{"obj":"🧉","alt":"Mate"},{"obj":"🧊","alt":"Ice"},{"obj":"🥢","alt":"Chopsticks"},{"obj":"🍽️","alt":"Fork and Knife with Plate"},{"obj":"🍴","alt":"Fork and Knife"},{"obj":"🥄","alt":"Spoon"},{"obj":"🫙","alt":"Jar"}]},
activity = {"name":"Activity","key":"activity","mob":"⚽","emojis":[{"obj":"🕴️","alt":"Person in Suit Levitating"},{"obj":"🧗","alt":"Person Climbing"},{"obj":"🧗♂️","alt":"Man Climbing"},{"obj":"🧗♀️","alt":"Woman Climbing"},{"obj":"🤺","alt":"Person Fencing"},{"obj":"🏇","alt":"Horse Racing"},{"obj":"⛷️","alt":"Skier"},{"obj":"🏂","alt":"Snowboarder"},{"obj":"🏌️","alt":"Person Golfing"},{"obj":"🏌️♂️","alt":"Man Golfing"},{"obj":"🏌️♀️","alt":"Woman Golfing"},{"obj":"🏄","alt":"Person Surfing"},{"obj":"🏄♂️","alt":"Man Surfing"},{"obj":"🏄♀️","alt":"Woman Surfing"},{"obj":"🚣","alt":"Person Rowing Boat"},{"obj":"🚣♂️","alt":"Man Rowing Boat"},{"obj":"🚣♀️","alt":"Woman Rowing Boat"},{"obj":"🏊","alt":"Person Swimming"},{"obj":"🏊♂️","alt":"Man Swimming"},{"obj":"🏊♀️","alt":"Woman Swimming"},{"obj":"⛹️","alt":"Person Bouncing Ball"},{"obj":"⛹️♂️","alt":"Man Bouncing Ball"},{"obj":"⛹️♀️","alt":"Woman Bouncing Ball"},{"obj":"🏋️","alt":"Person Lifting Weights"},{"obj":"🏋️♂️","alt":"Man Lifting Weights"},{"obj":"🏋️♀️","alt":"Woman Lifting Weights"},{"obj":"🚴","alt":"Person Biking"},{"obj":"🚴♂️","alt":"Man Biking"},{"obj":"🚴♀️","alt":"Woman Biking"},{"obj":"🚵","alt":"Person Mountain Biking"},{"obj":"🚵♂️","alt":"Man Mountain Biking"},{"obj":"🚵♀️","alt":"Woman Mountain Biking"},{"obj":"🤸","alt":"Person Cartwheeling"},{"obj":"🤸♂️","alt":"Man Cartwheeling"},{"obj":"🤸♀️","alt":"Woman Cartwheeling"},{"obj":"🤼","alt":"People Wrestling"},{"obj":"🤼♂️","alt":"Men Wrestling"},{"obj":"🤼♀️","alt":"Women Wrestling"},{"obj":"🤽","alt":"Person Playing Water Polo"},{"obj":"🤽♂️","alt":"Man Playing Water Polo"},{"obj":"🤽♀️","alt":"Woman Playing Water Polo"},{"obj":"🤾","alt":"Person Playing Handball"},{"obj":"🤾♂️","alt":"Man Playing Handball"},{"obj":"🤾♀️","alt":"Woman Playing Handball"},{"obj":"🤹","alt":"Person Juggling"},{"obj":"🤹♂️","alt":"Man Juggling"},{"obj":"🤹♀️","alt":"Woman Juggling"},{"obj":"🧘","alt":"Person in Lotus Position"},{"obj":"🧘♂️","alt":"Man in Lotus Position"},{"obj":"🧘♀️","alt":"Woman in Lotus Position"},{"obj":"🎪","alt":"Circus Tent"},{"obj":"🛹","alt":"Skateboard"},{"obj":"🛼","alt":"Roller Skate"},{"obj":"🛶","alt":"Canoe"},{"obj":"🎗️","alt":"Reminder Ribbon"},{"obj":"🎟️","alt":"Admission Tickets"},{"obj":"🎫","alt":"Ticket"},{"obj":"🎖️","alt":"Military Medal"},{"obj":"🏆","alt":"Trophy"},{"obj":"🏅","alt":"Sports Medal"},{"obj":"🥇","alt":"1st Place Medal"},{"obj":"🥈","alt":"2nd Place Medal"},{"obj":"🥉","alt":"3rd Place Medal"},{"obj":"⚽","alt":"Soccer Ball"},{"obj":"⚾","alt":"Baseball"},{"obj":"🥎","alt":"Softball"},{"obj":"🏀","alt":"Basketball"},{"obj":"🏐","alt":"Volleyball"},{"obj":"🏈","alt":"American Football"},{"obj":"🏉","alt":"Rugby Football"},{"obj":"🎾","alt":"Tennis"},{"obj":"🥏","alt":"Flying Disc"},{"obj":"🎳","alt":"Bowling"},{"obj":"🏏","alt":"Cricket Game"},{"obj":"🏑","alt":"Field Hockey"},{"obj":"🏒","alt":"Ice Hockey"},{"obj":"🥍","alt":"Lacrosse"},{"obj":"🏓","alt":"Ping Pong"},{"obj":"🏸","alt":"Badminton"},{"obj":"🥊","alt":"Boxing Glove"},{"obj":"🥋","alt":"Martial Arts Uniform"},{"obj":"🥅","alt":"Goal Net"},{"obj":"⛳","alt":"Flag in Hole"},{"obj":"⛸️","alt":"Ice Skate"},{"obj":"🎣","alt":"Fishing Pole"},{"obj":"🎽","alt":"Running Shirt"},{"obj":"🎿","alt":"Skis"},{"obj":"🛷","alt":"Sled"},{"obj":"🥌","alt":"Curling Stone"},{"obj":"🎯","alt":"Bullseye"},{"obj":"🎱","alt":"Pool 8 Ball"},{"obj":"🎮","alt":"Video Game"},{"obj":"🎰","alt":"Slot Machine"},{"obj":"🎲","alt":"Game Die"},{"obj":"🧩","alt":"Puzzle Piece"},{"obj":"🪩","alt":"Mirror Ball"},{"obj":"♟️","alt":"Chess Pawn"},{"obj":"🎭","alt":"Performing Arts"},{"obj":"🎨","alt":"Artist Palette"},{"obj":"🧵","alt":"Thread"},{"obj":"🧶","alt":"Yarn"},{"obj":"🎼","alt":"Musical Score"},{"obj":"🎤","alt":"Microphone"},{"obj":"🎧","alt":"Headphone"},{"obj":"🎷","alt":"Saxophone"},{"obj":"🪗","alt":"Accordion"},{"obj":"🎸","alt":"Guitar"},{"obj":"🎹","alt":"Musical Keyboard"},{"obj":"🎺","alt":"Trumpet"},{"obj":"🎻","alt":"Violin"},{"obj":"🥁","alt":"Drum"},{"obj":"🪘","alt":"Long Drum"},{"obj":"🎬","alt":"Clapper Board"},{"obj":"🏹","alt":"Bow and Arrow"}]},
places = {"name":"Travel & Places","key":"places","mob":"🚀","emojis":[{"obj":"🚣","alt":"Person Rowing Boat"},{"obj":"🗾","alt":"Map of Japan"},{"obj":"🏔️","alt":"Snow-Capped Mountain"},{"obj":"⛰️","alt":"Mountain"},{"obj":"🌋","alt":"Volcano"},{"obj":"🗻","alt":"Mount Fuji"},{"obj":"🏕️","alt":"Camping"},{"obj":"🏖️","alt":"Beach with Umbrella"},{"obj":"🏜️","alt":"Desert"},{"obj":"🏝️","alt":"Desert Island"},{"obj":"🏞️","alt":"National Park"},{"obj":"🏟️","alt":"Stadium"},{"obj":"🏛️","alt":"Classical Building"},{"obj":"🏗️","alt":"Building Construction"},{"obj":"🛖","alt":"Hut"},{"obj":"🏘️","alt":"Houses"},{"obj":"🏚️","alt":"Derelict House"},{"obj":"🏠","alt":"House"},{"obj":"🏡","alt":"House with Garden"},{"obj":"🏢","alt":"Office Building"},{"obj":"🏣","alt":"Japanese Post Office"},{"obj":"🏤","alt":"Post Office"},{"obj":"🏥","alt":"Hospital"},{"obj":"🏦","alt":"Bank"},{"obj":"🏨","alt":"Hotel"},{"obj":"🏩","alt":"Love Hotel"},{"obj":"🏪","alt":"Convenience Store"},{"obj":"🏫","alt":"School"},{"obj":"🏬","alt":"Department Store"},{"obj":"🏭","alt":"Factory"},{"obj":"🏯","alt":"Japanese Castle"},{"obj":"🏰","alt":"Castle"},{"obj":"💒","alt":"Wedding"},{"obj":"🗼","alt":"Tokyo Tower"},{"obj":"🗽","alt":"Statue of Liberty"},{"obj":"⛪","alt":"Church"},{"obj":"🕌","alt":"Mosque"},{"obj":"🛕","alt":"Hindu Temple"},{"obj":"🕍","alt":"Synagogue"},{"obj":"⛩️","alt":"Shinto Shrine"},{"obj":"🕋","alt":"Kaaba"},{"obj":"⛲","alt":"Fountain"},{"obj":"⛺","alt":"Tent"},{"obj":"🌁","alt":"Foggy"},{"obj":"🌃","alt":"Night with Stars"},{"obj":"🏙️","alt":"Cityscape"},{"obj":"🌄","alt":"Sunrise Over Mountains"},{"obj":"🌅","alt":"Sunrise"},{"obj":"🌆","alt":"Cityscape at Dusk"},{"obj":"🌇","alt":"Sunset"},{"obj":"🌉","alt":"Bridge at Night"},{"obj":"🎠","alt":"Carousel Horse"},{"obj":"🛝","alt":"Playground Slide"},{"obj":"🎡","alt":"Ferris Wheel"},{"obj":"🎢","alt":"Roller Coaster"},{"obj":"🚂","alt":"Locomotive"},{"obj":"🚃","alt":"Railway Car"},{"obj":"🚄","alt":"High-Speed Train"},{"obj":"🚅","alt":"Bullet Train"},{"obj":"🚆","alt":"Train"},{"obj":"🚇","alt":"Metro"},{"obj":"🚈","alt":"Light Rail"},{"obj":"🚉","alt":"Station"},{"obj":"🚊","alt":"Tram"},{"obj":"🚝","alt":"Monorail"},{"obj":"🚞","alt":"Mountain Railway"},{"obj":"🚋","alt":"Tram Car"},{"obj":"🚌","alt":"Bus"},{"obj":"🚍","alt":"Oncoming Bus"},{"obj":"🚎","alt":"Trolleybus"},{"obj":"🚐","alt":"Minibus"},{"obj":"🚑","alt":"Ambulance"},{"obj":"🚒","alt":"Fire Engine"},{"obj":"🚓","alt":"Police Car"},{"obj":"🚔","alt":"Oncoming Police Car"},{"obj":"🚕","alt":"Taxi"},{"obj":"🚖","alt":"Oncoming Taxi"},{"obj":"🚗","alt":"Automobile"},{"obj":"🚘","alt":"Oncoming Automobile"},{"obj":"🚙","alt":"Sport Utility Vehicle"},{"obj":"🛻","alt":"Pickup Truck"},{"obj":"🚚","alt":"Delivery Truck"},{"obj":"🚛","alt":"Articulated Lorry"},{"obj":"🚜","alt":"Tractor"},{"obj":"🏎️","alt":"Racing Car"},{"obj":"🏍️","alt":"Motorcycle"},{"obj":"🛵","alt":"Motor Scooter"},{"obj":"🛺","alt":"Auto Rickshaw"},{"obj":"🚲","alt":"Bicycle"},{"obj":"🛴","alt":"Kick Scooter"},{"obj":"🚏","alt":"Bus Stop"},{"obj":"🛣️","alt":"Motorway"},{"obj":"🛤️","alt":"Railway Track"},{"obj":"⛽","alt":"Fuel Pump"},{"obj":"🛞","alt":"Wheel"},{"obj":"🚨","alt":"Police Car Light"},{"obj":"🚥","alt":"Horizontal Traffic Light"},{"obj":"🚦","alt":"Vertical Traffic Light"},{"obj":"🚧","alt":"Construction"},{"obj":"⚓","alt":"Anchor"},{"obj":"🛟","alt":"Ring Buoy"},{"obj":"⛵","alt":"Sailboat"},{"obj":"🚤","alt":"Speedboat"},{"obj":"🛳️","alt":"Passenger Ship"},{"obj":"⛴️","alt":"Ferry"},{"obj":"🛥️","alt":"Motor Boat"},{"obj":"🚢","alt":"Ship"},{"obj":"✈️","alt":"Airplane"},{"obj":"🛩️","alt":"Small Airplane"},{"obj":"🛫","alt":"Airplane Departure"},{"obj":"🛬","alt":"Airplane Arrival"},{"obj":"🪂","alt":"Parachute"},{"obj":"💺","alt":"Seat"},{"obj":"🚁","alt":"Helicopter"},{"obj":"🚟","alt":"Suspension Railway"},{"obj":"🚠","alt":"Mountain Cableway"},{"obj":"🚡","alt":"Aerial Tramway"},{"obj":"🛰️","alt":"Satellite"},{"obj":"🚀","alt":"Rocket"},{"obj":"🛸","alt":"Flying Saucer"},{"obj":"🪐","alt":"Ringed Planet"},{"obj":"🌠","alt":"Shooting Star"},{"obj":"🌌","alt":"Milky Way"},{"obj":"⛱️","alt":"Umbrella on Ground"},{"obj":"🎆","alt":"Fireworks"},{"obj":"🎇","alt":"Sparkler"},{"obj":"🎑","alt":"Moon Viewing Ceremony"},{"obj":"💴","alt":"Yen Banknote"},{"obj":"💵","alt":"Dollar Banknote"},{"obj":"💶","alt":"Euro Banknote"},{"obj":"💷","alt":"Pound Banknote"},{"obj":"🗿","alt":"Moai"},{"obj":"🛂","alt":"Passport Control"},{"obj":"🛃","alt":"Customs"},{"obj":"🛄","alt":"Baggage Claim"},{"obj":"🛅","alt":"Left Luggage"}]},
objects = {"name":"Objects","key":"objects","mob":"💿","emojis":[{"obj":"💌","alt":"Love Letter"},{"obj":"🕳️","alt":"Hole"},{"obj":"💣","alt":"Bomb"},{"obj":"🛀","alt":"Person Taking Bath"},{"obj":"🛌","alt":"Person in Bed"},{"obj":"🔪","alt":"Kitchen Knife"},{"obj":"🏺","alt":"Amphora"},{"obj":"🗺️","alt":"World Map"},{"obj":"🧭","alt":"Compass"},{"obj":"🧱","alt":"Brick"},{"obj":"💈","alt":"Barber Pole"},{"obj":"🦽","alt":"Manual Wheelchair"},{"obj":"🦼","alt":"Motorized Wheelchair"},{"obj":"🛢️","alt":"Oil Drum"},{"obj":"🛎️","alt":"Bellhop Bell"},{"obj":"🧳","alt":"Luggage"},{"obj":"⌛","alt":"Hourglass Done"},{"obj":"⏳","alt":"Hourglass Not Done"},{"obj":"⌚","alt":"Watch"},{"obj":"⏰","alt":"Alarm Clock"},{"obj":"⏱️","alt":"Stopwatch"},{"obj":"⏲️","alt":"Timer Clock"},{"obj":"🕰️","alt":"Mantelpiece Clock"},{"obj":"🌡️","alt":"Thermometer"},{"obj":"⛱️","alt":"Umbrella on Ground"},{"obj":"🧨","alt":"Firecracker"},{"obj":"🎈","alt":"Balloon"},{"obj":"🎉","alt":"Party Popper"},{"obj":"🎊","alt":"Confetti Ball"},{"obj":"🎎","alt":"Japanese Dolls"},{"obj":"🎏","alt":"Carp Streamer"},{"obj":"🎐","alt":"Wind Chime"},{"obj":"🧧","alt":"Red Envelope"},{"obj":"🎀","alt":"Ribbon"},{"obj":"🎁","alt":"Wrapped Gift"},{"obj":"🤿","alt":"Diving Mask"},{"obj":"🪀","alt":"Yo-Yo"},{"obj":"🪁","alt":"Kite"},{"obj":"🔮","alt":"Crystal Ball"},{"obj":"🪄","alt":"Magic Wand"},{"obj":"🧿","alt":"Nazar Amulet"},{"obj":"🪬","alt":"Hamsa"},{"obj":"🕹️","alt":"Joystick"},{"obj":"🧸","alt":"Teddy Bear"},{"obj":"🪅","alt":"Piñata"},{"obj":"🪆","alt":"Nesting Dolls"},{"obj":"🖼️","alt":"Framed Picture"},{"obj":"🧵","alt":"Thread"},{"obj":"🪡","alt":"Sewing Needle"},{"obj":"🧶","alt":"Yarn"},{"obj":"🪢","alt":"Knot"},{"obj":"🛍️","alt":"Shopping Bags"},{"obj":"📿","alt":"Prayer Beads"},{"obj":"💎","alt":"Gem Stone"},{"obj":"📯","alt":"Postal Horn"},{"obj":"🎙️","alt":"Studio Microphone"},{"obj":"🎚️","alt":"Level Slider"},{"obj":"🎛️","alt":"Control Knobs"},{"obj":"📻","alt":"Radio"},{"obj":"🪕","alt":"Banjo"},{"obj":"📱","alt":"Mobile Phone"},{"obj":"📲","alt":"Mobile Phone with Arrow"},{"obj":"☎️","alt":"Telephone"},{"obj":"📞","alt":"Telephone Receiver"},{"obj":"📟","alt":"Pager"},{"obj":"📠","alt":"Fax Machine"},{"obj":"🔋","alt":"Battery"},{"obj":"🔌","alt":"Electric Plug"},{"obj":"💻","alt":"Laptop"},{"obj":"🖥️","alt":"Desktop Computer"},{"obj":"🖨️","alt":"Printer"},{"obj":"⌨️","alt":"Keyboard"},{"obj":"🖱️","alt":"Computer Mouse"},{"obj":"🖲️","alt":"Trackball"},{"obj":"💽","alt":"Computer Disk"},{"obj":"💾","alt":"Floppy Disk"},{"obj":"💿","alt":"Optical Disk"},{"obj":"📀","alt":"DVD"},{"obj":"🧮","alt":"Abacus"},{"obj":"🎥","alt":"Movie Camera"},{"obj":"🎞️","alt":"Film Frames"},{"obj":"📽️","alt":"Film Projector"},{"obj":"📺","alt":"Television"},{"obj":"📷","alt":"Camera"},{"obj":"📸","alt":"Camera with Flash"},{"obj":"📹","alt":"Video Camera"},{"obj":"📼","alt":"Videocassette"},{"obj":"🔍","alt":"Magnifying Glass Tilted Left"},{"obj":"🔎","alt":"Magnifying Glass Tilted Right"},{"obj":"🕯️","alt":"Candle"},{"obj":"💡","alt":"Light Bulb"},{"obj":"🔦","alt":"Flashlight"},{"obj":"🏮","alt":"Red Paper Lantern"},{"obj":"🪔","alt":"Diya Lamp"},{"obj":"📔","alt":"Notebook with Decorative Cover"},{"obj":"📕","alt":"Closed Book"},{"obj":"📖","alt":"Open Book"},{"obj":"📗","alt":"Green Book"},{"obj":"📘","alt":"Blue Book"},{"obj":"📙","alt":"Orange Book"},{"obj":"📚","alt":"Books"},{"obj":"📓","alt":"Notebook"},{"obj":"📒","alt":"Ledger"},{"obj":"📃","alt":"Page with Curl"},{"obj":"📜","alt":"Scroll"},{"obj":"📄","alt":"Page Facing Up"},{"obj":"📰","alt":"Newspaper"},{"obj":"🗞️","alt":"Rolled-Up Newspaper"},{"obj":"📑","alt":"Bookmark Tabs"},{"obj":"🔖","alt":"Bookmark"},{"obj":"🏷️","alt":"Label"},{"obj":"💰","alt":"Money Bag"},{"obj":"🪙","alt":"Coin"},{"obj":"💴","alt":"Yen Banknote"},{"obj":"💵","alt":"Dollar Banknote"},{"obj":"💶","alt":"Euro Banknote"},{"obj":"💷","alt":"Pound Banknote"},{"obj":"💸","alt":"Money with Wings"},{"obj":"💳","alt":"Credit Card"},{"obj":"🧾","alt":"Receipt"},{"obj":"✉️","alt":"Envelope"},{"obj":"📧","alt":"E-Mail"},{"obj":"📨","alt":"Incoming Envelope"},{"obj":"📩","alt":"Envelope with Arrow"},{"obj":"📤","alt":"Outbox Tray"},{"obj":"📥","alt":"Inbox Tray"},{"obj":"📦","alt":"Package"},{"obj":"📫","alt":"Closed Mailbox with Raised Flag"},{"obj":"📪","alt":"Closed Mailbox with Lowered Flag"},{"obj":"📬","alt":"Open Mailbox with Raised Flag"},{"obj":"📭","alt":"Open Mailbox with Lowered Flag"},{"obj":"📮","alt":"Postbox"},{"obj":"🗳️","alt":"Ballot Box with Ballot"},{"obj":"✏️","alt":"Pencil"},{"obj":"✒️","alt":"Black Nib"},{"obj":"🖋️","alt":"Fountain Pen"},{"obj":"🖊️","alt":"Pen"},{"obj":"🖌️","alt":"Paintbrush"},{"obj":"🖍️","alt":"Crayon"},{"obj":"📝","alt":"Memo"},{"obj":"📁","alt":"File Folder"},{"obj":"📂","alt":"Open File Folder"},{"obj":"🗂️","alt":"Card Index Dividers"},{"obj":"📅","alt":"Calendar"},{"obj":"📆","alt":"Tear-Off Calendar"},{"obj":"🗒️","alt":"Spiral Notepad"},{"obj":"🗓️","alt":"Spiral Calendar"},{"obj":"📇","alt":"Card Index"},{"obj":"📈","alt":"Chart Increasing"},{"obj":"📉","alt":"Chart Decreasing"},{"obj":"📊","alt":"Bar Chart"},{"obj":"📋","alt":"Clipboard"},{"obj":"📌","alt":"Pushpin"},{"obj":"📍","alt":"Round Pushpin"},{"obj":"📎","alt":"Paperclip"},{"obj":"🖇️","alt":"Linked Paperclips"},{"obj":"📏","alt":"Straight Ruler"},{"obj":"📐","alt":"Triangular Ruler"},{"obj":"✂️","alt":"Scissors"},{"obj":"🗃️","alt":"Card File Box"},{"obj":"🗄️","alt":"File Cabinet"},{"obj":"🗑️","alt":"Wastebasket"},{"obj":"🔒","alt":"Locked"},{"obj":"🔓","alt":"Unlocked"},{"obj":"🔏","alt":"Locked with Pen"},{"obj":"🔐","alt":"Locked with Key"},{"obj":"🔑","alt":"Key"},{"obj":"🗝️","alt":"Old Key"},{"obj":"🔨","alt":"Hammer"},{"obj":"🪓","alt":"Axe"},{"obj":"⛏️","alt":"Pick"},{"obj":"⚒️","alt":"Hammer and Pick"},{"obj":"🛠️","alt":"Hammer and Wrench"},{"obj":"🗡️","alt":"Dagger"},{"obj":"⚔️","alt":"Crossed Swords"},{"obj":"🔫","alt":"Water Pistol"},{"obj":"🪃","alt":"Boomerang"},{"obj":"🛡️","alt":"Shield"},{"obj":"🪚","alt":"Carpentry Saw"},{"obj":"🔧","alt":"Wrench"},{"obj":"🪛","alt":"Screwdriver"},{"obj":"🔩","alt":"Nut and Bolt"},{"obj":"⚙️","alt":"Gear"},{"obj":"🗜️","alt":"Clamp"},{"obj":"⚖️","alt":"Balance Scale"},{"obj":"🦯","alt":"White Cane"},{"obj":"🔗","alt":"Link"},{"obj":"⛓️","alt":"Chains"},{"obj":"🪝","alt":"Hook"},{"obj":"🧰","alt":"Toolbox"},{"obj":"🧲","alt":"Magnet"},{"obj":"🪜","alt":"Ladder"},{"obj":"⚗️","alt":"Alembic"},{"obj":"🧪","alt":"Test Tube"},{"obj":"🧫","alt":"Petri Dish"},{"obj":"🧬","alt":"DNA"},{"obj":"🔬","alt":"Microscope"},{"obj":"🔭","alt":"Telescope"},{"obj":"📡","alt":"Satellite Antenna"},{"obj":"💉","alt":"Syringe"},{"obj":"🩸","alt":"Drop of Blood"},{"obj":"💊","alt":"Pill"},{"obj":"🩹","alt":"Adhesive Bandage"},{"obj":"🩼","alt":"Crutch"},{"obj":"🩺","alt":"Stethoscope"},{"obj":"🚪","alt":"Door"},{"obj":"🪞","alt":"Mirror"},{"obj":"🪟","alt":"Window"},{"obj":"🛏️","alt":"Bed"},{"obj":"🛋️","alt":"Couch and Lamp"},{"obj":"🪑","alt":"Chair"},{"obj":"🚽","alt":"Toilet"},{"obj":"🪠","alt":"Plunger"},{"obj":"🚿","alt":"Shower"},{"obj":"🛁","alt":"Bathtub"},{"obj":"🪤","alt":"Mouse Trap"},{"obj":"🪒","alt":"Razor"},{"obj":"🧴","alt":"Lotion Bottle"},{"obj":"🧷","alt":"Safety Pin"},{"obj":"🧹","alt":"Broom"},{"obj":"🧺","alt":"Basket"},{"obj":"🧻","alt":"Roll of Paper"},{"obj":"🪣","alt":"Bucket"},{"obj":"🧼","alt":"Soap"},{"obj":"🪥","alt":"Toothbrush"},{"obj":"🧽","alt":"Sponge"},{"obj":"🧯","alt":"Fire Extinguisher"},{"obj":"🛒","alt":"Shopping Cart"},{"obj":"🚬","alt":"Cigarette"},{"obj":"⚰️","alt":"Coffin"},{"obj":"🪦","alt":"Headstone"},{"obj":"⚱️","alt":"Funeral Urn"},{"obj":"🗿","alt":"Moai"},{"obj":"🪧","alt":"Placard"},{"obj":"🪪","alt":"Identification Card"},{"obj":"🚰","alt":"Potable Water"}]},
symbols = {"name":"Symbols","key":"symbols","mob":"💗","emojis":[{"obj":"💘","alt":"Heart with Arrow"},{"obj":"💝","alt":"Heart with Ribbon"},{"obj":"💖","alt":"Sparkling Heart"},{"obj":"💗","alt":"Growing Heart"},{"obj":"💓","alt":"Beating Heart"},{"obj":"💞","alt":"Revolving Hearts"},{"obj":"💕","alt":"Two Hearts"},{"obj":"💟","alt":"Heart Decoration"},{"obj":"❣️","alt":"Heart Exclamation"},{"obj":"💔","alt":"Broken Heart"},{"obj":"❤️🔥","alt":"Heart on Fire"},{"obj":"❤️🩹","alt":"Mending Heart"},{"obj":"❤️","alt":"Red Heart"},{"obj":"🧡","alt":"Orange Heart"},{"obj":"💛","alt":"Yellow Heart"},{"obj":"💚","alt":"Green Heart"},{"obj":"💙","alt":"Blue Heart"},{"obj":"💜","alt":"Purple Heart"},{"obj":"🤎","alt":"Brown Heart"},{"obj":"🖤","alt":"Black Heart"},{"obj":"🤍","alt":"White Heart"},{"obj":"💯","alt":"Hundred Points"},{"obj":"💢","alt":"Anger Symbol"},{"obj":"💬","alt":"Speech Balloon"},{"obj":"👁️🗨️","alt":"Eye in Speech Bubble"},{"obj":"🗨️","alt":"Left Speech Bubble"},{"obj":"🗯️","alt":"Right Anger Bubble"},{"obj":"💭","alt":"Thought Balloon"},{"obj":"💤","alt":"Zzz"},{"obj":"💮","alt":"White Flower"},{"obj":"♨️","alt":"Hot Springs"},{"obj":"💈","alt":"Barber Pole"},{"obj":"🛑","alt":"Stop Sign"},{"obj":"🕛","alt":"Twelve O’Clock"},{"obj":"🕧","alt":"Twelve-Thirty"},{"obj":"🕐","alt":"One O’Clock"},{"obj":"🕜","alt":"One-Thirty"},{"obj":"🕑","alt":"Two O’Clock"},{"obj":"🕝","alt":"Two-Thirty"},{"obj":"🕒","alt":"Three O’Clock"},{"obj":"🕞","alt":"Three-Thirty"},{"obj":"🕓","alt":"Four O’Clock"},{"obj":"🕟","alt":"Four-Thirty"},{"obj":"🕔","alt":"Five O’Clock"},{"obj":"🕠","alt":"Five-Thirty"},{"obj":"🕕","alt":"Six O’Clock"},{"obj":"🕡","alt":"Six-Thirty"},{"obj":"🕖","alt":"Seven O’Clock"},{"obj":"🕢","alt":"Seven-Thirty"},{"obj":"🕗","alt":"Eight O’Clock"},{"obj":"🕣","alt":"Eight-Thirty"},{"obj":"🕘","alt":"Nine O’Clock"},{"obj":"🕤","alt":"Nine-Thirty"},{"obj":"🕙","alt":"Ten O’Clock"},{"obj":"🕥","alt":"Ten-Thirty"},{"obj":"🕚","alt":"Eleven O’Clock"},{"obj":"🕦","alt":"Eleven-Thirty"},{"obj":"🌀","alt":"Cyclone"},{"obj":"♠️","alt":"Spade Suit"},{"obj":"♥️","alt":"Heart Suit"},{"obj":"♦️","alt":"Diamond Suit"},{"obj":"♣️","alt":"Club Suit"},{"obj":"🃏","alt":"Joker"},{"obj":"🀄","alt":"Mahjong Red Dragon"},{"obj":"🎴","alt":"Flower Playing Cards"},{"obj":"🔇","alt":"Muted Speaker"},{"obj":"🔈","alt":"Speaker Low Volume"},{"obj":"🔉","alt":"Speaker Medium Volume"},{"obj":"🔊","alt":"Speaker High Volume"},{"obj":"📢","alt":"Loudspeaker"},{"obj":"📣","alt":"Megaphone"},{"obj":"📯","alt":"Postal Horn"},{"obj":"🔔","alt":"Bell"},{"obj":"🔕","alt":"Bell with Slash"},{"obj":"🎵","alt":"Musical Note"},{"obj":"🎶","alt":"Musical Notes"},{"obj":"💹","alt":"Chart Increasing with Yen"},{"obj":"🛗","alt":"Elevator"},{"obj":"🏧","alt":"ATM Sign"},{"obj":"🚮","alt":"Litter in Bin Sign"},{"obj":"🚰","alt":"Potable Water"},{"obj":"♿","alt":"Wheelchair Symbol"},{"obj":"🚹","alt":"Men’s Room"},{"obj":"🚺","alt":"Women’s Room"},{"obj":"🚻","alt":"Restroom"},{"obj":"🚼","alt":"Baby Symbol"},{"obj":"🚾","alt":"Water Closet"},{"obj":"⚠️","alt":"Warning"},{"obj":"🚸","alt":"Children Crossing"},{"obj":"⛔","alt":"No Entry"},{"obj":"🚫","alt":"Prohibited"},{"obj":"🚳","alt":"No Bicycles"},{"obj":"🚭","alt":"No Smoking"},{"obj":"🚯","alt":"No Littering"},{"obj":"🚱","alt":"Non-Potable Water"},{"obj":"🚷","alt":"No Pedestrians"},{"obj":"📵","alt":"No Mobile Phones"},{"obj":"🔞","alt":"No One Under Eighteen"},{"obj":"☢️","alt":"Radioactive"},{"obj":"☣️","alt":"Biohazard"},{"obj":"⬆️","alt":"Up Arrow"},{"obj":"↗️","alt":"Up-Right Arrow"},{"obj":"➡️","alt":"Right Arrow"},{"obj":"↘️","alt":"Down-Right Arrow"},{"obj":"⬇️","alt":"Down Arrow"},{"obj":"↙️","alt":"Down-Left Arrow"},{"obj":"⬅️","alt":"Left Arrow"},{"obj":"↖️","alt":"Up-Left Arrow"},{"obj":"↕️","alt":"Up-Down Arrow"},{"obj":"↔️","alt":"Left-Right Arrow"},{"obj":"↩️","alt":"Right Arrow Curving Left"},{"obj":"↪️","alt":"Left Arrow Curving Right"},{"obj":"⤴️","alt":"Right Arrow Curving Up"},{"obj":"⤵️","alt":"Right Arrow Curving Down"},{"obj":"🔃","alt":"Clockwise Vertical Arrows"},{"obj":"🔄","alt":"Counterclockwise Arrows Button"},{"obj":"🔙","alt":"Back Arrow"},{"obj":"🔚","alt":"End Arrow"},{"obj":"🔛","alt":"On! Arrow"},{"obj":"🔜","alt":"Soon Arrow"},{"obj":"🔝","alt":"Top Arrow"},{"obj":"🛐","alt":"Place of Worship"},{"obj":"⚛️","alt":"Atom Symbol"},{"obj":"🕉️","alt":"Om"},{"obj":"✡️","alt":"Star of David"},{"obj":"☸️","alt":"Wheel of Dharma"},{"obj":"☯️","alt":"Yin Yang"},{"obj":"✝️","alt":"Latin Cross"},{"obj":"☦️","alt":"Orthodox Cross"},{"obj":"☪️","alt":"Star and Crescent"},{"obj":"☮️","alt":"Peace Symbol"},{"obj":"🕎","alt":"Menorah"},{"obj":"🔯","alt":"Dotted Six-Pointed Star"},{"obj":"♈","alt":"Aries"},{"obj":"♉","alt":"Taurus"},{"obj":"♊","alt":"Gemini"},{"obj":"♋","alt":"Cancer"},{"obj":"♌","alt":"Leo"},{"obj":"♍","alt":"Virgo"},{"obj":"♎","alt":"Libra"},{"obj":"♏","alt":"Scorpio"},{"obj":"♐","alt":"Sagittarius"},{"obj":"♑","alt":"Capricorn"},{"obj":"♒","alt":"Aquarius"},{"obj":"♓","alt":"Pisces"},{"obj":"⛎","alt":"Ophiuchus"},{"obj":"🔀","alt":"Shuffle Tracks Button"},{"obj":"🔁","alt":"Repeat Button"},{"obj":"🔂","alt":"Repeat Single Button"},{"obj":"▶️","alt":"Play Button"},{"obj":"⏩","alt":"Fast-Forward Button"},{"obj":"⏭️","alt":"Next Track Button"},{"obj":"⏯️","alt":"Play or Pause Button"},{"obj":"◀️","alt":"Reverse Button"},{"obj":"⏪","alt":"Fast Reverse Button"},{"obj":"⏮️","alt":"Last Track Button"},{"obj":"🔼","alt":"Upwards Button"},{"obj":"⏫","alt":"Fast Up Button"},{"obj":"🔽","alt":"Downwards Button"},{"obj":"⏬","alt":"Fast Down Button"},{"obj":"⏸️","alt":"Pause Button"},{"obj":"⏹️","alt":"Stop Button"},{"obj":"⏺️","alt":"Record Button"},{"obj":"⏏️","alt":"Eject Button"},{"obj":"🎦","alt":"Cinema"},{"obj":"🔅","alt":"Dim Button"},{"obj":"🔆","alt":"Bright Button"},{"obj":"📶","alt":"Antenna Bars"},{"obj":"📳","alt":"Vibration Mode"},{"obj":"📴","alt":"Mobile Phone Off"},{"obj":"♀️","alt":"Female Sign"},{"obj":"♂️","alt":"Male Sign"},{"obj":"✖️","alt":"Multiply"},{"obj":"➕","alt":"Plus"},{"obj":"➖","alt":"Minus"},{"obj":"➗","alt":"Divide"},{"obj":"🟰","alt":"Heavy Equals Sign"},{"obj":"♾️","alt":"Infinity"},{"obj":"‼️","alt":"Double Exclamation Mark"},{"obj":"⁉️","alt":"Exclamation Question Mark"},{"obj":"❓","alt":"Red Question Mark"},{"obj":"❔","alt":"White Question Mark"},{"obj":"❕","alt":"White Exclamation Mark"},{"obj":"❗","alt":"Red Exclamation Mark"},{"obj":"〰️","alt":"Wavy Dash"},{"obj":"💱","alt":"Currency Exchange"},{"obj":"💲","alt":"Heavy Dollar Sign"},{"obj":"⚕️","alt":"Medical Symbol"},{"obj":"♻️","alt":"Recycling Symbol"},{"obj":"⚜️","alt":"Fleur-de-lis"},{"obj":"🔱","alt":"Trident Emblem"},{"obj":"📛","alt":"Name Badge"},{"obj":"🔰","alt":"Japanese Symbol for Beginner"},{"obj":"⭕","alt":"Hollow Red Circle"},{"obj":"✅","alt":"Check Mark Button"},{"obj":"☑️","alt":"Check Box with Check"},{"obj":"✔️","alt":"Check Mark"},{"obj":"❌","alt":"Cross Mark"},{"obj":"❎","alt":"Cross Mark Button"},{"obj":"➰","alt":"Curly Loop"},{"obj":"➿","alt":"Double Curly Loop"},{"obj":"〽️","alt":"Part Alternation Mark"},{"obj":"✳️","alt":"Eight-Spoked Asterisk"},{"obj":"✴️","alt":"Eight-Pointed Star"},{"obj":"❇️","alt":"Sparkle"},{"obj":"©️","alt":"Copyright"},{"obj":"®️","alt":"Registered"},{"obj":"™️","alt":"Trade Mark"},{"obj":"#️⃣","alt":"Keycap Number Sign"},{"obj":"*️⃣","alt":"Keycap Asterisk"},{"obj":"0️⃣","alt":"Keycap Digit Zero"},{"obj":"1️⃣","alt":"Keycap Digit One"},{"obj":"2️⃣","alt":"Keycap Digit Two"},{"obj":"3️⃣","alt":"Keycap Digit Three"},{"obj":"4️⃣","alt":"Keycap Digit Four"},{"obj":"5️⃣","alt":"Keycap Digit Five"},{"obj":"6️⃣","alt":"Keycap Digit Six"},{"obj":"7️⃣","alt":"Keycap Digit Seven"},{"obj":"8️⃣","alt":"Keycap Digit Eight"},{"obj":"9️⃣","alt":"Keycap Digit Nine"},{"obj":"🔟","alt":"Keycap: 10"},{"obj":"🔠","alt":"Input Latin Uppercase"},{"obj":"🔡","alt":"Input Latin Lowercase"},{"obj":"🔢","alt":"Input Numbers"},{"obj":"🔣","alt":"Input Symbols"},{"obj":"🔤","alt":"Input Latin Letters"},{"obj":"🅰️","alt":"A Button (Blood Type)"},{"obj":"🆎","alt":"AB Button (Blood Type)"},{"obj":"🅱️","alt":"B Button (Blood Type)"},{"obj":"🆑","alt":"CL Button"},{"obj":"🆒","alt":"Cool Button"},{"obj":"🆓","alt":"Free Button"},{"obj":"ℹ️","alt":"Information"},{"obj":"🆔","alt":"ID Button"},{"obj":"Ⓜ️","alt":"Circled M"},{"obj":"🆕","alt":"New Button"},{"obj":"🆖","alt":"NG Button"},{"obj":"🅾️","alt":"O Button (Blood Type)"},{"obj":"🆗","alt":"OK Button"},{"obj":"🅿️","alt":"P Button"},{"obj":"🆘","alt":"SOS Button"},{"obj":"🆙","alt":"Up! Button"},{"obj":"🆚","alt":"Vs Button"},{"obj":"🈁","alt":"Japanese “Here” Button"},{"obj":"🈂️","alt":"Japanese “Service Charge” Button"},{"obj":"🈷️","alt":"Japanese “Monthly Amount” Button"},{"obj":"🈶","alt":"Japanese “Not Free of Charge” Button"},{"obj":"🈯","alt":"Japanese “Reserved” Button"},{"obj":"🉐","alt":"Japanese “Bargain” Button"},{"obj":"🈹","alt":"Japanese “Discount” Button"},{"obj":"🈚","alt":"Japanese “Free of Charge” Button"},{"obj":"🈲","alt":"Japanese “Prohibited” Button"},{"obj":"🉑","alt":"Japanese “Acceptable” Button"},{"obj":"🈸","alt":"Japanese “Application” Button"},{"obj":"🈴","alt":"Japanese “Passing Grade” Button"},{"obj":"🈳","alt":"Japanese “Vacancy” Button"},{"obj":"㊗️","alt":"Japanese “Congratulations” Button"},{"obj":"㊙️","alt":"Japanese “Secret” Button"},{"obj":"🈺","alt":"Japanese “Open for Business” Button"},{"obj":"🈵","alt":"Japanese “No Vacancy” Button"},{"obj":"🔴","alt":"Red Circle"},{"obj":"🟠","alt":"Orange Circle"},{"obj":"🟡","alt":"Yellow Circle"},{"obj":"🟢","alt":"Green Circle"},{"obj":"🔵","alt":"Blue Circle"},{"obj":"🟣","alt":"Purple Circle"},{"obj":"🟤","alt":"Brown Circle"},{"obj":"⚫","alt":"Black Circle"},{"obj":"⚪","alt":"White Circle"},{"obj":"🟥","alt":"Red Square"},{"obj":"🟧","alt":"Orange Square"},{"obj":"🟨","alt":"Yellow Square"},{"obj":"🟩","alt":"Green Square"},{"obj":"🟦","alt":"Blue Square"},{"obj":"🟪","alt":"Purple Square"},{"obj":"🟫","alt":"Brown Square"},{"obj":"⬛","alt":"Black Large Square"},{"obj":"⬜","alt":"White Large Square"},{"obj":"◼️","alt":"Black Medium Square"},{"obj":"◻️","alt":"White Medium Square"},{"obj":"◾","alt":"Black Medium-Small Square"},{"obj":"◽","alt":"White Medium-Small Square"},{"obj":"▪️","alt":"Black Small Square"},{"obj":"▫️","alt":"White Small Square"},{"obj":"🔶","alt":"Large Orange Diamond"},{"obj":"🔷","alt":"Large Blue Diamond"},{"obj":"🔸","alt":"Small Orange Diamond"},{"obj":"🔹","alt":"Small Blue Diamond"},{"obj":"🔺","alt":"Red Triangle Pointed Up"},{"obj":"🔻","alt":"Red Triangle Pointed Down"},{"obj":"💠","alt":"Diamond with a Dot"},{"obj":"🔘","alt":"Radio Button"},{"obj":"🔳","alt":"White Square Button"},{"obj":"🔲","alt":"Black Square Button"},]},
flags = {"name":"Flags","key":"flags","mob":"🎌","emojis":[{"obj":"🏁","alt":"Chequered Flag"},{"obj":"🚩","alt":"Triangular Flag"},{"obj":"🎌","alt":"Crossed Flags"},{"obj":"🏴","alt":"Black Flag"},{"obj":"🏳️","alt":"White Flag"},{"obj":"🏳️🌈","alt":"Rainbow Flag"},{"obj":"🏳️⚧️","alt":"Transgender Flag"},{"obj":"🏴☠️","alt":"Pirate Flag"},{"obj":"🇦🇨","alt":"Flag: Ascension Island"},{"obj":"🇦🇩","alt":"Flag: Andorra"},{"obj":"🇦🇪","alt":"Flag: United Arab Emirates"},{"obj":"🇦🇫","alt":"Flag: Afghanistan"},{"obj":"🇦🇬","alt":"Flag: Antigua & Barbuda"},{"obj":"🇦🇮","alt":"Flag: Anguilla"},{"obj":"🇦🇱","alt":"Flag: Albania"},{"obj":"🇦🇲","alt":"Flag: Armenia"},{"obj":"🇦🇴","alt":"Flag: Angola"},{"obj":"🇦🇶","alt":"Flag: Antarctica"},{"obj":"🇦🇷","alt":"Flag: Argentina"},{"obj":"🇦🇸","alt":"Flag: American Samoa"},{"obj":"🇦🇹","alt":"Flag: Austria"},{"obj":"🇦🇺","alt":"Flag: Australia"},{"obj":"🇦🇼","alt":"Flag: Aruba"},{"obj":"🇦🇽","alt":"Flag: Åland Islands"},{"obj":"🇦🇿","alt":"Flag: Azerbaijan"},{"obj":"🇧🇦","alt":"Flag: Bosnia & Herzegovina"},{"obj":"🇧🇧","alt":"Flag: Barbados"},{"obj":"🇧🇩","alt":"Flag: Bangladesh"},{"obj":"🇧🇪","alt":"Flag: Belgium"},{"obj":"🇧🇫","alt":"Flag: Burkina Faso"},{"obj":"🇧🇬","alt":"Flag: Bulgaria"},{"obj":"🇧🇭","alt":"Flag: Bahrain"},{"obj":"🇧🇮","alt":"Flag: Burundi"},{"obj":"🇧🇯","alt":"Flag: Benin"},{"obj":"🇧🇱","alt":"Flag: St. Barthélemy"},{"obj":"🇧🇲","alt":"Flag: Bermuda"},{"obj":"🇧🇳","alt":"Flag: Brunei"},{"obj":"🇧🇴","alt":"Flag: Bolivia"},{"obj":"🇧🇶","alt":"Flag: Caribbean Netherlands"},{"obj":"🇧🇷","alt":"Flag: Brazil"},{"obj":"🇧🇸","alt":"Flag: Bahamas"},{"obj":"🇧🇹","alt":"Flag: Bhutan"},{"obj":"🇧🇻","alt":"Flag: Bouvet Island"},{"obj":"🇧🇼","alt":"Flag: Botswana"},{"obj":"🇧🇾","alt":"Flag: Belarus"},{"obj":"🇧🇿","alt":"Flag: Belize"},{"obj":"🇨🇦","alt":"Flag: Canada"},{"obj":"🇨🇨","alt":"Flag: Cocos (Keeling) Islands"},{"obj":"🇨🇩","alt":"Flag: Congo - Kinshasa"},{"obj":"🇨🇫","alt":"Flag: Central African Republic"},{"obj":"🇨🇬","alt":"Flag: Congo - Brazzaville"},{"obj":"🇨🇭","alt":"Flag: Switzerland"},{"obj":"🇨🇮","alt":"Flag: Côte d’Ivoire"},{"obj":"🇨🇰","alt":"Flag: Cook Islands"},{"obj":"🇨🇱","alt":"Flag: Chile"},{"obj":"🇨🇲","alt":"Flag: Cameroon"},{"obj":"🇨🇳","alt":"Flag: China"},{"obj":"🇨🇴","alt":"Flag: Colombia"},{"obj":"🇨🇵","alt":"Flag: Clipperton Island"},{"obj":"🇨🇷","alt":"Flag: Costa Rica"},{"obj":"🇨🇺","alt":"Flag: Cuba"},{"obj":"🇨🇻","alt":"Flag: Cape Verde"},{"obj":"🇨🇼","alt":"Flag: Curaçao"},{"obj":"🇨🇽","alt":"Flag: Christmas Island"},{"obj":"🇨🇾","alt":"Flag: Cyprus"},{"obj":"🇨🇿","alt":"Flag: Czechia"},{"obj":"🇩🇪","alt":"Flag: Germany"},{"obj":"🇩🇬","alt":"Flag: Diego Garcia"},{"obj":"🇩🇯","alt":"Flag: Djibouti"},{"obj":"🇩🇰","alt":"Flag: Denmark"},{"obj":"🇩🇲","alt":"Flag: Dominica"},{"obj":"🇩🇴","alt":"Flag: Dominican Republic"},{"obj":"🇩🇿","alt":"Flag: Algeria"},{"obj":"🇪🇦","alt":"Flag: Ceuta & Melilla"},{"obj":"🇪🇨","alt":"Flag: Ecuador"},{"obj":"🇪🇪","alt":"Flag: Estonia"},{"obj":"🇪🇬","alt":"Flag: Egypt"},{"obj":"🇪🇭","alt":"Flag: Western Sahara"},{"obj":"🇪🇷","alt":"Flag: Eritrea"},{"obj":"🇪🇸","alt":"Flag: Spain"},{"obj":"🇪🇹","alt":"Flag: Ethiopia"},{"obj":"🇪🇺","alt":"Flag: European Union"},{"obj":"🇫🇮","alt":"Flag: Finland"},{"obj":"🇫🇯","alt":"Flag: Fiji"},{"obj":"🇫🇰","alt":"Flag: Falkland Islands"},{"obj":"🇫🇲","alt":"Flag: Micronesia"},{"obj":"🇫🇴","alt":"Flag: Faroe Islands"},{"obj":"🇫🇷","alt":"Flag: France"},{"obj":"🇬🇦","alt":"Flag: Gabon"},{"obj":"🇬🇧","alt":"Flag: United Kingdom"},{"obj":"🇬🇩","alt":"Flag: Grenada"},{"obj":"🇬🇪","alt":"Flag: Georgia"},{"obj":"🇬🇫","alt":"Flag: French Guiana"},{"obj":"🇬🇬","alt":"Flag: Guernsey"},{"obj":"🇬🇭","alt":"Flag: Ghana"},{"obj":"🇬🇮","alt":"Flag: Gibraltar"},{"obj":"🇬🇱","alt":"Flag: Greenland"},{"obj":"🇬🇲","alt":"Flag: Gambia"},{"obj":"🇬🇳","alt":"Flag: Guinea"},{"obj":"🇬🇵","alt":"Flag: Guadeloupe"},{"obj":"🇬🇶","alt":"Flag: Equatorial Guinea"},{"obj":"🇬🇷","alt":"Flag: Greece"},{"obj":"🇬🇸","alt":"Flag: South Georgia & South Sandwich Islands"},{"obj":"🇬🇹","alt":"Flag: Guatemala"},{"obj":"🇬🇺","alt":"Flag: Guam"},{"obj":"🇬🇼","alt":"Flag: Guinea-Bissau"},{"obj":"🇬🇾","alt":"Flag: Guyana"},{"obj":"🇭🇰","alt":"Flag: Hong Kong SAR China"},{"obj":"🇭🇲","alt":"Flag: Heard & McDonald Islands"},{"obj":"🇭🇳","alt":"Flag: Honduras"},{"obj":"🇭🇷","alt":"Flag: Croatia"},{"obj":"🇭🇹","alt":"Flag: Haiti"},{"obj":"🇭🇺","alt":"Flag: Hungary"},{"obj":"🇮🇨","alt":"Flag: Canary Islands"},{"obj":"🇮🇩","alt":"Flag: Indonesia"},{"obj":"🇮🇪","alt":"Flag: Ireland"},{"obj":"🇮🇱","alt":"Flag: Israel"},{"obj":"🇮🇲","alt":"Flag: Isle of Man"},{"obj":"🇮🇳","alt":"Flag: India"},{"obj":"🇮🇴","alt":"Flag: British Indian Ocean Territory"},{"obj":"🇮🇶","alt":"Flag: Iraq"},{"obj":"🇮🇷","alt":"Flag: Iran"},{"obj":"🇮🇸","alt":"Flag: Iceland"},{"obj":"🇮🇹","alt":"Flag: Italy"},{"obj":"🇯🇪","alt":"Flag: Jersey"},{"obj":"🇯🇲","alt":"Flag: Jamaica"},{"obj":"🇯🇴","alt":"Flag: Jordan"},{"obj":"🇯🇵","alt":"Flag: Japan"},{"obj":"🇰🇪","alt":"Flag: Kenya"},{"obj":"🇰🇬","alt":"Flag: Kyrgyzstan"},{"obj":"🇰🇭","alt":"Flag: Cambodia"},{"obj":"🇰🇮","alt":"Flag: Kiribati"},{"obj":"🇰🇲","alt":"Flag: Comoros"},{"obj":"🇰🇳","alt":"Flag: St. Kitts & Nevis"},{"obj":"🇰🇵","alt":"Flag: North Korea"},{"obj":"🇰🇷","alt":"Flag: South Korea"},{"obj":"🇰🇼","alt":"Flag: Kuwait"},{"obj":"🇰🇾","alt":"Flag: Cayman Islands"},{"obj":"🇰🇿","alt":"Flag: Kazakhstan"},{"obj":"🇱🇦","alt":"Flag: Laos"},{"obj":"🇱🇧","alt":"Flag: Lebanon"},{"obj":"🇱🇨","alt":"Flag: St. Lucia"},{"obj":"🇱🇮","alt":"Flag: Liechtenstein"},{"obj":"🇱🇰","alt":"Flag: Sri Lanka"},{"obj":"🇱🇷","alt":"Flag: Liberia"},{"obj":"🇱🇸","alt":"Flag: Lesotho"},{"obj":"🇱🇹","alt":"Flag: Lithuania"},{"obj":"🇱🇺","alt":"Flag: Luxembourg"},{"obj":"🇱🇻","alt":"Flag: Latvia"},{"obj":"🇱🇾","alt":"Flag: Libya"},{"obj":"🇲🇦","alt":"Flag: Morocco"},{"obj":"🇲🇨","alt":"Flag: Monaco"},{"obj":"🇲🇩","alt":"Flag: Moldova"},{"obj":"🇲🇪","alt":"Flag: Montenegro"},{"obj":"🇲🇫","alt":"Flag: St. Martin"},{"obj":"🇲🇬","alt":"Flag: Madagascar"},{"obj":"🇲🇭","alt":"Flag: Marshall Islands"},{"obj":"🇲🇰","alt":"Flag: North Macedonia"},{"obj":"🇲🇱","alt":"Flag: Mali"},{"obj":"🇲🇲","alt":"Flag: Myanmar (Burma)"},{"obj":"🇲🇳","alt":"Flag: Mongolia"},{"obj":"🇲🇴","alt":"Flag: Macao Sar China"},{"obj":"🇲🇵","alt":"Flag: Northern Mariana Islands"},{"obj":"🇲🇶","alt":"Flag: Martinique"},{"obj":"🇲🇷","alt":"Flag: Mauritania"},{"obj":"🇲🇸","alt":"Flag: Montserrat"},{"obj":"🇲🇹","alt":"Flag: Malta"},{"obj":"🇲🇺","alt":"Flag: Mauritius"},{"obj":"🇲🇻","alt":"Flag: Maldives"},{"obj":"🇲🇼","alt":"Flag: Malawi"},{"obj":"🇲🇽","alt":"Flag: Mexico"},{"obj":"🇲🇾","alt":"Flag: Malaysia"},{"obj":"🇲🇿","alt":"Flag: Mozambique"},{"obj":"🇳🇦","alt":"Flag: Namibia"},{"obj":"🇳🇨","alt":"Flag: New Caledonia"},{"obj":"🇳🇪","alt":"Flag: Niger"},{"obj":"🇳🇫","alt":"Flag: Norfolk Island"},{"obj":"🇳🇬","alt":"Flag: Nigeria"},{"obj":"🇳🇮","alt":"Flag: Nicaragua"},{"obj":"🇳🇱","alt":"Flag: Netherlands"},{"obj":"🇳🇴","alt":"Flag: Norway"},{"obj":"🇳🇵","alt":"Flag: Nepal"},{"obj":"🇳🇷","alt":"Flag: Nauru"},{"obj":"🇳🇺","alt":"Flag: Niue"},{"obj":"🇳🇿","alt":"Flag: New Zealand"},{"obj":"🇴🇲","alt":"Flag: Oman"},{"obj":"🇵🇦","alt":"Flag: Panama"},{"obj":"🇵🇪","alt":"Flag: Peru"},{"obj":"🇵🇫","alt":"Flag: French Polynesia"},{"obj":"🇵🇬","alt":"Flag: Papua New Guinea"},{"obj":"🇵🇭","alt":"Flag: Philippines"},{"obj":"🇵🇰","alt":"Flag: Pakistan"},{"obj":"🇵🇱","alt":"Flag: Poland"},{"obj":"🇵🇲","alt":"Flag: St. Pierre & Miquelon"},{"obj":"🇵🇳","alt":"Flag: Pitcairn Islands"},{"obj":"🇵🇷","alt":"Flag: Puerto Rico"},{"obj":"🇵🇸","alt":"Flag: Palestinian Territories"},{"obj":"🇵🇹","alt":"Flag: Portugal"},{"obj":"🇵🇼","alt":"Flag: Palau"},{"obj":"🇵🇾","alt":"Flag: Paraguay"},{"obj":"🇶🇦","alt":"Flag: Qatar"},{"obj":"🇷🇪","alt":"Flag: Réunion"},{"obj":"🇷🇴","alt":"Flag: Romania"},{"obj":"🇷🇸","alt":"Flag: Serbia"},{"obj":"🇷🇺","alt":"Flag: Russia"},{"obj":"🇷🇼","alt":"Flag: Rwanda"},{"obj":"🇸🇦","alt":"Flag: Saudi Arabia"},{"obj":"🇸🇧","alt":"Flag: Solomon Islands"},{"obj":"🇸🇨","alt":"Flag: Seychelles"},{"obj":"🇸🇩","alt":"Flag: Sudan"},{"obj":"🇸🇪","alt":"Flag: Sweden"},{"obj":"🇸🇬","alt":"Flag: Singapore"},{"obj":"🇸🇭","alt":"Flag: St. Helena"},{"obj":"🇸🇮","alt":"Flag: Slovenia"},{"obj":"🇸🇯","alt":"Flag: Svalbard & Jan Mayen"},{"obj":"🇸🇰","alt":"Flag: Slovakia"},{"obj":"🇸🇱","alt":"Flag: Sierra Leone"},{"obj":"🇸🇲","alt":"Flag: San Marino"},{"obj":"🇸🇳","alt":"Flag: Senegal"},{"obj":"🇸🇴","alt":"Flag: Somalia"},{"obj":"🇸🇷","alt":"Flag: Suriname"},{"obj":"🇸🇸","alt":"Flag: South Sudan"},{"obj":"🇸🇹","alt":"Flag: São Tomé & Príncipe"},{"obj":"🇸🇻","alt":"Flag: El Salvador"},{"obj":"🇸🇽","alt":"Flag: Sint Maarten"},{"obj":"🇸🇾","alt":"Flag: Syria"},{"obj":"🇸🇿","alt":"Flag: Eswatini"},{"obj":"🇹🇦","alt":"Flag: Tristan Da Cunha"},{"obj":"🇹🇨","alt":"Flag: Turks & Caicos Islands"},{"obj":"🇹🇩","alt":"Flag: Chad"},{"obj":"🇹🇫","alt":"Flag: French Southern Territories"},{"obj":"🇹🇬","alt":"Flag: Togo"},{"obj":"🇹🇭","alt":"Flag: Thailand"},{"obj":"🇹🇯","alt":"Flag: Tajikistan"},{"obj":"🇹🇰","alt":"Flag: Tokelau"},{"obj":"🇹🇱","alt":"Flag: Timor-Leste"},{"obj":"🇹🇲","alt":"Flag: Turkmenistan"},{"obj":"🇹🇳","alt":"Flag: Tunisia"},{"obj":"🇹🇴","alt":"Flag: Tonga"},{"obj":"🇹🇷","alt":"Flag: Turkey"},{"obj":"🇹🇹","alt":"Flag: Trinidad & Tobago"},{"obj":"🇹🇻","alt":"Flag: Tuvalu"},{"obj":"🇹🇼","alt":"Flag: Taiwan"},{"obj":"🇹🇿","alt":"Flag: Tanzania"},{"obj":"🇺🇦","alt":"Flag: Ukraine"},{"obj":"🇺🇬","alt":"Flag: Uganda"},{"obj":"🇺🇲","alt":"Flag: U.S. Outlying Islands"},{"obj":"🇺🇳","alt":"Flag: United Nations"},{"obj":"🇺🇸","alt":"Flag: United States"},{"obj":"🇺🇾","alt":"Flag: Uruguay"},{"obj":"🇺🇿","alt":"Flag: Uzbekistan"},{"obj":"🇻🇦","alt":"Flag: Vatican City"},{"obj":"🇻🇨","alt":"Flag: St. Vincent & Grenadines"},{"obj":"🇻🇪","alt":"Flag: Venezuela"},{"obj":"🇻🇬","alt":"Flag: British Virgin Islands"},{"obj":"🇻🇮","alt":"Flag: U.S. Virgin Islands"},{"obj":"🇻🇳","alt":"Flag: Vietnam"},{"obj":"🇻🇺","alt":"Flag: Vanuatu"},{"obj":"🇼🇫","alt":"Flag: Wallis & Futuna"},{"obj":"🇼🇸","alt":"Flag: Samoa"},{"obj":"🇽🇰","alt":"Flag: Kosovo"},{"obj":"🇾🇪","alt":"Flag: Yemen"},{"obj":"🇾🇹","alt":"Flag: Mayotte"},{"obj":"🇿🇦","alt":"Flag: South Africa"},{"obj":"🇿🇲","alt":"Flag: Zambia"},{"obj":"🇿🇼","alt":"Flag: Zimbabwe"},{"obj":"🏴","alt":"Flag: England"},{"obj":"🏴","alt":"Flag: Scotland"},{"obj":"🏴","alt":"Flag: Wales"},{"obj":"🏴","alt":"Flag for Texas (US-TX)"}]};
const UnicodeEmojiModule = {
// Determine active theme to set button color
color: (() => {
if (localStorage.themeDark && localStorage.themeLight) {
return "var(--engine-content-title)";
}
return "ghostwhite";
})(),
// CSS for emoji picker
emojiCSS: `
#spotlight-emojis {
display: flex;
flex-direction: column;
gap: 1.5rem;
background: var(--main-bg);
width: 100%;
height: 0;
opacity: 0;
overflow: hidden;
padding: 0 1rem;
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
transition: height 0.2s linear, opacity 0.1s linear, padding 0.2s linear;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
body.spotlight-mode #spotlight-emojis {
position: absolute;
bottom: 0;
left: 0.7%;
right: 0.7%;
width: auto;
margin-bottom: calc(4rem + 8px);
overflow-y: auto;
}
#spotlight-emojis.active {
opacity: 1;
height: 20rem;
max-height: 20rem;
padding: 1rem;
}
body.spotlight-mode #spotlight-emojis.active {
height: 30vh;
}
#spotlight-emojis spotfilter {
display: flex;
gap: 1rem;
flex-flow: row wrap;
padding: 0 4px;
}
#spotlight-emojis spotfilter key {
cursor: pointer;
padding: 0.6rem 0.8rem 0.8rem 0.8rem;
word-break: keep-all;
color: #00838f;
background: transparent;
border-radius: 4px;
border: 1px solid teal;
transition: transform 0.1s linear;
}
#spotlight-emojis spotfilter key:active {
transform: scale(0.9);
}
#spotlight-emojis spotfilter key.active {
cursor: default;
transform: scale(1) !important;
background: teal;
color: ghostwhite;
}
#spotlight-emojis spotfilter key:hover,
#spotlight-emojis spotfilter key:focus {
background: teal;
color: ghostwhite;
}
#spotlight-emojis spotfilter key span:last-of-type {
margin-left: 0.25rem;
margin-right: 3px;
}
@media screen and (max-width: 1274px) {
#spotlight-emojis spotfilter key {
padding: 0.4rem 0.6rem 0.6rem 0.6rem;
}
#spotlight-emojis spotfilter key span {
font-size: 1.2rem;
}
#spotlight-emojis spotfilter key span:last-of-type {
display: none;
}
}
@media screen and (max-width: 509px) {
#spotlight-emojis spotfilter key {
padding: 0.3rem 0.5rem 0.5rem 0.5rem;
}
}
@media screen and (max-width: 485px) {
#spotlight-emojis spotfilter {
gap: 0.75rem;
}
#spotlight-emojis spotfilter key {
padding: 0.3rem 0.4rem 0.4rem 0.4rem;
}
}
@media screen and (max-width: 435px) {
#spotlight-emojis spotfilter {
gap: 0.65rem;
}
#spotlight-emojis spotfilter key {
padding: 0.2rem 0.3rem 0.3rem 0.3rem;
}
}
@media screen and (max-width: 400px) {
#spotlight-emojis spotfilter key span {
font-size: 1rem;
}
}
#spotlight-emojis spotmoji {
display: flex;
flex-flow: row wrap;
gap: 0 0.75rem;
font-size: 1.4rem;
overflow-y: auto;
overflow-x: hidden;
}
#spotlight-emojis spotmoji x.emoji {
display: flex;
align-items: center;
justify-content: center;
height: 2.4rem;
width: 2.4rem;
margin-bottom: 0.5rem;
cursor: pointer;
transition: transform 0.1s linear;
}
#spotlight-emojis spotmoji x.emoji:hover,
#spotlight-emojis spotmoji x.emoji:focus {
transform: scale(1.4);
}
#spotlight-emojis spotmoji x.emoji:active {
transform: scale(1.1);
}
#emoji-toggle-btn {
cursor: pointer;
color: ${(() => {
if (localStorage.themeDark && localStorage.themeLight) {
return "var(--engine-content-title)";
}
return "ghostwhite";
})()};
}
#emoji-toggle-btn:hover {
color: var(--link-color);
}
`,
// JavaScript for emoji functionality
emojiJS: `
// Replace custom css to filter visible emojis
function emojistyle(key) {
let style = "#spotlight-emojis spotmoji x.emoji:not(." + key + ") {display: none;}";
return style;
}
// Show activity and emojis when a filter key is clicked
function filterKey(type, num) {
let emojiCSS = document.getElementById("emoji-filter");
document.querySelector("#spotlight-emojis spotfilter key.active").removeAttribute("class");
document.querySelectorAll("#spotlight-emojis spotfilter key")[num].classList.add("active");
emojiCSS.innerHTML = emojistyle(type);
document.querySelector("#spotlight-emojis spotmoji").scrollTo({ top: 0, behavior: 'smooth' });
}
// Toggle emoji picker
function toggleEmojiPicker() {
let picker = document.getElementById("spotlight-emojis");
// Close spotlight smileys if open
let spotlightSmileys = document.getElementById("spotlight-smileys");
if (spotlightSmileys && spotlightSmileys.classList.contains("shiner")) {
spotlightSmileys.classList.remove("shiner");
setTimeout(function() {
spotlightSmileys.classList.add("fader");
}, 200);
}
// Close smilies-outline if open (for homepage)
let sOutline = document.getElementById("smilies-outline");
if (sOutline && sOutline.style.display !== "none") {
$('#smilies-outline').slideToggle(200);
setTimeout(function() {
sOutline.classList.remove("smileys");
sOutline.classList.remove("emojis");
}, 200);
}
if (picker.classList.contains("active")) {
picker.classList.remove("active");
} else {
picker.classList.add("active");
document.querySelector("#spotlight-emojis spotmoji").scrollTo({ top: 0, behavior: 'smooth' });
}
}
`,
// Style base for filtering emojis
emojistyle(key) {
let style = "#spotlight-emojis spotmoji x.emoji:not(." + key + ") {display: none;}";
return style;
},
// Function for printing the Emojiboard
print: '',
echo(key) {
this.print = this.print + key;
},
// Function for Pushing Filter Keys
setKey(input, num, keyclass) {
this.echo(`<key `);
if (keyclass) {
this.echo(`class="${keyclass}" `);
}
this.echo(`onclick="filterKey('${input.key}',${num})">`);
this.echo(`<span>${input.mob}</span><span>${input.name}</span>`);
this.echo(`</key>`);
},
// Function for Pushing Emojis
setEmoji(input) {
for (let x = 0; x < input.emojis.length; x++) {
this.echo(`<x class="emoji ${input.key}" `);
this.echo(`title="${input.emojis[x].alt}">`);
this.echo(input.emojis[x].obj);
this.echo(`</x>`);
}
},
// Build emoji picker HTML
buildEmojiHTML() {
this.print = '';
this.echo(`<spotfilter>`);
this.setKey(smileys, 0, "active");
this.setKey(nature, 1);
this.setKey(food, 2);
this.setKey(activity, 3);
this.setKey(places, 4);
this.setKey(objects, 5);
this.setKey(symbols, 6);
this.setKey(flags, 7);
this.echo(`</spotfilter>`);
this.echo(`<spotmoji>`);
this.setEmoji(smileys);
this.setEmoji(nature);
this.setEmoji(food);
this.setEmoji(activity);
this.setEmoji(places);
this.setEmoji(objects);
this.setEmoji(symbols);
this.setEmoji(flags);
this.echo(`</spotmoji>`);
return this.print;
},
// Function for adding styles
addStyle(css, id) {
let head = document.getElementsByTagName("head")[0];
if (!head) {window.location.reload();}
let style = document.createElement("style");
if (id) {style.id = id;}
style.setAttribute("type", "text/css");
style.innerHTML = css;
head.appendChild(style);
},
// Function for adding scripts
addScript(js) {
let body = document.getElementsByTagName("body")[0];
if (!body) {window.location.reload();}
let script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.innerHTML = js;
body.appendChild(script);
},
// Add emoji button to shoutbox
addEmojiButton() {
let ibbc = document.getElementById("shout-ibb-container");
let befr = document.getElementById("shoutHistoryBtn");
if (!ibbc || !befr) return;
let face = document.createElement("span");
face.id = "emoji-toggle-btn";
face.classList.add("inline-submit-btn");
face.title = "Unicode Emojis";
face.setAttribute("onclick", "toggleEmojiPicker()");
face.innerHTML = "<i class='material-icons'>face</i>";
ibbc.insertBefore(face, befr);
},
// Add emoji picker container
addEmojiPicker() {
let container = document.getElementById("smilies-outline");
if (!container) return;
let div = document.createElement("div");
div.id = "spotlight-emojis";
div.innerHTML = this.buildEmojiHTML();
container.parentNode.insertBefore(div, container.nextSibling);
},
init() {
if (!CONFIG.unicode_emoji_enabled) return;
// Detect if in spotlight mode and add class to body
if (location.href.includes("?spotlight")) {
document.body.classList.add("spotlight-mode");
}
// Add CSS
this.addStyle(this.emojiCSS);
this.addStyle(this.emojistyle("smileys"), "emoji-filter");
// Add JS
this.addScript(this.emojiJS);
// Add UI elements
this.addEmojiButton();
this.addEmojiPicker();
// Fix the smilies-outline container for homepage
if (location.href === location.origin + "/") {
const smiliesOutline = document.getElementById('smilies-outline');
if (smiliesOutline && !smiliesOutline.querySelector('#spotlight-smileys')) {
const smileyDiv = document.createElement('div');
smileyDiv.id = 'spotlight-smileys';
smiliesOutline.insertBefore(smileyDiv, smiliesOutline.firstChild);
}
}
// Add click handler for emojis
document.addEventListener("click", function(event) {
if (!event.target.tagName.match(/x/i)) return;
if (!event.target.className.match(/emoji/i)) return;
if (!event.target.parentNode.tagName.match(/spotmoji/i)) return;
let shoutText = document.getElementById("shout_text");
if (!shoutText) return;
let thisEmoji = event.target.innerHTML;
shoutText.value = shoutText.value + thisEmoji;
shoutText.focus();
});
// Close picker when sending message
let sendBtn = document.getElementById("sendShoutBtn");
if (sendBtn) {
sendBtn.addEventListener("click", function() {
let picker = document.getElementById("spotlight-emojis");
if (picker && picker.classList.contains("active")) {
picker.classList.remove("active");
}
});
}
// Close picker on Enter key
let shoutText = document.getElementById("shout_text");
if (shoutText) {
shoutText.addEventListener("keydown", function(e) {
if (e.key !== "Enter") return;
let picker = document.getElementById("spotlight-emojis");
if (picker && picker.classList.contains("active")) {
picker.classList.remove("active");
}
});
}
},
stop() {
const btn = document.getElementById('emoji-toggle-btn');
if (btn) btn.remove();
const picker = document.getElementById('spotlight-emojis');
if (picker) picker.remove();
const filterStyle = document.getElementById('emoji-filter');
if (filterStyle) filterStyle.remove();
document.body.classList.remove("spotlight-mode");
}
};
// ============================================================================
// MODULE 12: SPOTLIGHT MODE
// ============================================================================
const UIImprovementsModule = {
// Determine active theme to set button color
color: (() => {
if (localStorage.themeDark && localStorage.themeLight) {
return "var(--engine-content-title)";
}
return "ghostwhite";
})(),
// CSS for shoutbox
shoutboxCSS: `
#shout-expander {
display: none;
}
.shoutbox-container .content-title {
position: relative;
}
.shoutbox-container span.toggle-indicator,
.shoutbox-container .content-title > div.right {
position: absolute;
right: 16px;
}
.shoutbox-container h6 {
padding: 0.5rem 0 0.4rem 0;
margin: 0;
width: 100%;
}
#spotbtns {
display: flex;
position: absolute;
right: 48px;
gap: 12px;
margin-top: 1.475px;
}
#spotlight, #greasylight, #forumlight {
cursor: pointer;
padding-top: 4px;
color: ${(() => {
if (localStorage.themeDark && localStorage.themeLight) {
return "var(--engine-content-title)";
}
return "ghostwhite";
})()};
transition: transform 0.1s;
}
#spotlight:hover, #spotlight:focus,
#greasylight:hover, #greasylight:focus,
#forumlight:hover, #forumlight:focus {
color: var(--link-color);
transform: scale(1);
}
#spotlight:active,
#greasylight:active,
#forumlight:active {
transform: scale(0.8);
}
#spotlight {
order: 3;
}
#shout-ibb-container {
display: flex;
gap: 0 0.5rem;
}
#shouts-container {
display: flex;
flex-direction: column;
}
`,
// Spotlight mode CSS
spotlightCSS: `
header, #kuddus-trigger-container, footer, .drag-target, #left-block, #middle-block .row.card-panel:not(.shoutbox-container), br, .shoutbox-container .toggle-indicator, #smilies-outline {
display: none !important;
}
html, body, main, #parent-block, #middle-block, #middle-block div.shoutbox-container {
min-width: 100%;
min-height: 100%;
max-width: 100%;
max-height: 100%;
width: 100%;
height: 100%;
overflow: hidden;
font-size: 16px !important;
}
.shouts, .shouts i {
font-size: 1rem !important;
}
@media screen and (max-width: 743px) {
html, body {
font-size: 15px !important;
}
}
main {
margin-top: 0;
padding-top: 0 !important;
}
#middle-block {
margin: 0;
padding: 0.5rem;
}
#spotbtns {
right: 1rem;
}
#greasylight {
order: 4;
}
#forumlight {
order: 5;
}
.shoutbox-container {
margin: auto;
}
.shoutbox-container .content-title {
padding: 0 1rem;
height: 4.375rem;
display: flex;
align-items: center;
}
.shoutbox-container .toggle-indicator {
cursor: pointer;
}
#shoutbox-outline {
width: 100%;
height: 100%;
display: flex !important;
flex-direction: column;
}
#shouts-container {
min-height: 100px;
height: calc(calc(100% - 8.375rem) - 15px);
padding: 0 0.7%;
}
#shout-send-container {
height: 4rem;
padding: 0;
position: relative;
}
#shout_text {
height: 100%;
padding-left: 1.5rem;
padding-right: calc(140px + 2.5rem);
}
#shout-ibb-container {
margin-right: 0;
padding-right: 1.5rem;
position: absolute;
right: 0;
bottom: 0;
}
#shout-ibb-container .inline-submit-btn i {
line-height: calc(4rem - 2px);
}
#spotlight-smileys {
display: flex;
flex-flow: row wrap;
gap: 1rem;
position: absolute;
bottom: 1%;
padding: 1rem;
margin-bottom: calc(4rem - 2px);
background: var(--main-bg);
width: 100%;
height: 0;
opacity: 0;
transition: height 0.2s linear, opacity 0.1s linear;
border-top: 1px solid;
border-bottom: 1px solid;
border-color: var(--border-color);
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
overflow-y: auto;
}
#spotlight-smileys.shiner {
opacity: 1;
height: 30vh;
}
#spotlight-smileys.fader {
display: none;
}
#spotlight-smileys a {
display: flex;
align-items: center;
justify-content: center;
height: 2.4rem;
width: 2.4rem;
margin-bottom: 0.5rem;
cursor: pointer;
transition: transform 0.1s linear;
}
#spotlight-smileys a:hover,
#spotlight-smileys a:focus {
transform: scale(1.4);
}
#spotlight-smileys a:active {
transform: scale(1.1);
}
`,
// Homepage CSS
shouthomeCSS: `
.shoutbox-container > .content-title {
padding: 2px;
}
.shoutbox-container h6 {
padding-left: 14px;
padding-right: 14px;
}
#spotlight-smileys, #spotlight-emojis {
display: none;
}
#smilies-outline.smileys #spotlight-smileys {
display: flex;
}
#smilies-outline.emojis #spotlight-emojis {
display: flex;
}
#smilies-outline {
padding: 0 !important;
margin: 0 !important;
}
#smilies-outline.smileys {
padding: 1rem !important;
}
#smilies-outline.emojis {
padding: 1rem !important;
}
#smilies-outline.emojis #spotlight-smileys {
display: none !important;
height: 0 !important;
padding: 0 !important;
margin: 0 !important;
min-height: 0 !important;
}
#smilies-outline.smileys #spotlight-emojis {
display: none !important;
height: 0 !important;
padding: 0 !important;
margin: 0 !important;
min-height: 0 !important;
}
#spotlight-smileys:empty {
display: none !important;
height: 0 !important;
padding: 0 !important;
margin: 0 !important;
}
#spotlight-smileys, #spotlight-emojis {
flex-direction: column;
gap: 1.5rem;
background: var(--main-bg);
width: 103%;
height: 100%;
max-height: 20rem;
transition: height 0.2s linear, opacity 0.1s linear;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
padding: 0;
margin: 0;
}
#smilies-outline.emojis {
padding: 1rem;
}
#smilies-outline.emojis #spotlight-smileys {
display: none !important;
}
#smilies-outline.smileys #spotlight-emojis {
display: none !important;
}
#smilies-outline {
max-width: 98%;
margin: 0 auto;
}
#spotlight-smileys {
flex-flow: row wrap;
gap: 1rem 0.75rem;
overflow-y: auto;
}
#spotlight-smileys a {
display: flex;
align-items: center;
justify-content: center;
height: 2.4rem;
width: 2.4rem;
margin-bottom: 0.5rem;
cursor: pointer;
transition: transform 0.1s linear;
}
#spotlight-smileys a:hover,
#spotlight-smileys a:focus {
transform: scale(1.4);
}
#spotlight-smileys a:active {
transform: scale(1.1);
}
#shout-send-container {
padding: 0;
position: relative;
}
#shout_text {
padding-left: 0.5rem;
padding-right: 140px;
}
#shout-ibb-container {
margin-right: 0;
padding-right: 0.5rem;
position: absolute;
right: 0;
}
`,
// Spotlight mode JS for emojis
spotlightJS_emojis: `
// Toggle Smileys
function toggleSmilemoji(id, alt) {
let emojibox = document.getElementById(id);
// Close unicode emoji picker if open
let unicodePicker = document.getElementById("spotlight-emojis");
if (unicodePicker && unicodePicker.classList.contains("active")) {
unicodePicker.classList.remove("active");
}
if (emojibox.className.match(/shiner/i)) {
emojibox.classList.remove("shiner");
setTimeout(function() {
emojibox.classList.add("fader");
}, 200);
} else {
emojibox.classList.remove("fader");
setTimeout(function() {
emojibox.classList.add("shiner");
}, 100);
}
}
// Toggle button for smileys
function toggleSmileys() {
toggleSmilemoji("spotlight-smileys");
}
// Load the smileys ahead of time in new container
(function() {
if (!smiliesLoaded) {
$.post(
siteUrl + "ajscripts.php", {
task: 'getSmilies'
},
function(data) {
$('#spotlight-smileys').html(data);
smiliesLoaded = true;
}
);
}
})();
`,
// Homepage JS for emojis
homepageJS_emojis: `
// Quick Toggle for Smileys
function toggleQuickES(main, sub) {
let sOutline = document.getElementById("smilies-outline");
sOutline.classList.add(main);
sOutline.classList.remove(sub);
// If outline is hidden, show it
if (sOutline.style.display === "none" || !sOutline.style.display) {
$('#smilies-outline').slideDown(150);
} else {
// If outline is visible, hide it with easeOutQuad for smoother closing
$('#smilies-outline').slideUp(150, 'linear', function() {
sOutline.classList.remove(main);
sOutline.classList.remove(sub);
});
}
}
// Handle Opposite Clicks on Smileys
function toggleSmilemoji(one, alt) {
let sOutline = document.getElementById("smilies-outline"),
oneRE = new RegExp(one, "i"),
altRE = new RegExp(alt, "i");
// Close unicode emoji picker if open
let unicodePicker = document.getElementById("spotlight-emojis");
if (unicodePicker && unicodePicker.classList.contains("active")) {
unicodePicker.classList.remove("active");
// Force open the smiley outline after closing unicode picker
setTimeout(function() {
sOutline.classList.add(one);
sOutline.classList.remove(alt);
if (sOutline.style.display === "none" || !sOutline.style.display) {
$('#smilies-outline').slideDown(150);
}
}, 150);
return;
}
// Normal toggle logic when unicode picker is not open
if (altRE.test(sOutline.className)) {
// If switching from emoji to smiley or vice versa, close then open
$('#smilies-outline').slideUp(150, 'linear', function() {
sOutline.classList.remove(alt);
sOutline.classList.add(one);
$('#smilies-outline').slideDown(150);
});
} else {
toggleQuickES(one, alt);
}
}
// Make room for Smileys in the same container
(function() {
let sOutline = document.getElementById("smilies-outline");
sOutline.innerHTML = "<div id='spotlight-smileys'></div>";
})();
// Load the smileys ahead of time in new container
(function() {
if (!smiliesLoaded) {
$.post(
siteUrl + "ajscripts.php", {
task: 'getSmilies'
},
function(data) {
$('#spotlight-smileys').html(data);
smiliesLoaded = true;
}
);
}
})();
`,
addStyle(css, id) {
let head = document.getElementsByTagName("head")[0];
if (!head) {window.location.reload();}
let style = document.createElement("style");
if (id) {style.id = id;}
style.setAttribute("type", "text/css");
style.innerHTML = css;
head.appendChild(style);
},
addScript(js) {
let body = document.getElementsByTagName("body")[0];
if (!body) {window.location.reload();}
let script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.innerHTML = js;
body.appendChild(script);
},
spotlightJS(key, link, icon) {
let spotHead = document.querySelector(".shoutbox-container .content-title.toggle");
if (!spotHead) return;
spotHead.removeAttribute("onclick");
if (key.match(/enter/i)) {
spotHead.children[0].setAttribute("onclick","toggleShoutbox()");
spotHead.children[1].setAttribute("onclick","toggleShoutbox()");
} else {
if (spotHead.children[1]) spotHead.children[1].remove();
spotHead.classList.remove("toggle");
}
let newCon = document.createElement("div");
newCon.id = "spotbtns";
spotHead.appendChild(newCon);
newCon = document.getElementById("spotbtns");
let anchor = document.createElement("a");
anchor.id = "spotlight";
anchor.title = key + " Spotlight";
anchor.href = link;
anchor.innerHTML = `<i class="material-icons">${icon}</i>`;
newCon.appendChild(anchor);
},
whiteNull() {
const shoutsContainer = document.getElementById("shouts-container");
if (shoutsContainer) {
shoutsContainer.innerHTML = shoutsContainer.innerHTML.replace(/>\s+</g,'><');
}
},
smileyJS() {
let container = document.getElementById("shout-send-container");
let bottom = document.getElementById("shout_text");
let toggle = document.querySelector("#shout-ibb-container span[onclick*='toggleSmilies']");
if (!container || !bottom) return;
let div = document.createElement("div");
div.id = "spotlight-smileys";
div.classList.add("fader");
container.insertBefore(div, bottom);
if (toggle) {
toggle.setAttribute("onclick","toggleSmileys()");
}
},
initSpotlightMode() {
// Update material icons
try {
const iconLink = document.querySelector("link[href*='material-icons.css']");
if (iconLink) {
iconLink.href = "https://fonts.googleapis.com/icon?family=Material+Icons";
}
} catch(e) {}
// Set new document title
document.title = "Shoutbox - TorrentBD";
// Set cookie for expanded Shoutbox
document.cookie = "hideShoutbox=0";
// Push the styles in head
this.addStyle(this.shoutboxCSS + this.spotlightCSS, "spotlight");
// Change stuff when page loads
const initSpotlight = () => {
this.addScript(this.spotlightJS_emojis);
this.spotlightJS("Exit", location.origin, "fullscreen_exit");
this.whiteNull();
this.smileyJS();
// Auto-close smileys after picking one
document.addEventListener("click", function(event) {
if (!event.target.closest("#spotlight-smileys a")) {return;}
let emojibox = document.getElementById("spotlight-smileys");
if (emojibox && emojibox.className.match(/shiner/i)) {
emojibox.classList.remove("shiner");
setTimeout(function() {
emojibox.classList.add("fader");
}, 200);
}
});
// Add ESC key handler to exit spotlight mode
document.addEventListener("keydown", function(event) {
if (event.key === "Escape") {
window.location.href = location.origin;
}
});
// Observe shoutbox
let observer = new MutationObserver(function() {
const shoutsContainer = document.getElementById("shouts-container");
if (!shoutsContainer || !shoutsContainer.children[0]) {return;}
let shoutID = parseInt(shoutsContainer.children[0].id.replace(/.*-/,''));
if (!window.lastShoutID) {
window.lastShoutID = shoutID;
return;
}
if (window.lastShoutID >= shoutID) {return;}
window.lastShoutID = shoutID;
});
const shoutsContainer = document.getElementById("shouts-container");
if (shoutsContainer) {
observer.observe(shoutsContainer, {childList: true});
}
};
if (document.readyState === 'loading') {
window.addEventListener('load', initSpotlight);
} else {
initSpotlight();
}
},
initHomepage() {
// Update material icons
try {
const iconLink = document.querySelector("link[href*='material-icons.css']");
if (iconLink) {
iconLink.href = "https://fonts.googleapis.com/icon?family=Material+Icons";
}
} catch(e) {}
// Push the styles in head
this.addStyle(this.shoutboxCSS + this.shouthomeCSS, "shouthome");
// Change stuff when page loads
const initHome = () => {
this.addScript(this.homepageJS_emojis);
this.spotlightJS("Enter", "?spotlight", "fullscreen");
this.whiteNull();
// Change button to use new toggle function
let emojiOrigin = document.querySelector("#shout-ibb-container span[onclick='toggleSmilies()']");
if (emojiOrigin) {
emojiOrigin.setAttribute("onclick","toggleSmilemoji('smileys','emojis')");
}
};
if (document.readyState === 'loading') {
window.addEventListener('load', initHome);
} else {
initHome();
}
},
init() {
// Check if spotlight URL matches
if (location.href === location.origin + "/?spotlight") {
this.initSpotlightMode();
}
// Check if user is on the homepage
else if (location.href === location.origin + "/") {
this.initHomepage();
}
}
};
// ============================================================================
// MODULE 13: MEME CREATOR
// ============================================================================
const MemeCreatorModule = {
IMGFLIP_CONFIG: {
username: 'puls33',
password: '12345678@',
apiUrl: 'https://api.imgflip.com'
},
modal: null,
templatesArea: null,
editorArea: null,
memeButton: null,
allTemplates: [],
currentTemplate: null,
activeInput: null,
isDragging: false,
hasMoved: false,
debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
},
addStyles() {
const css = `
#meme-tool-modal {
position: fixed;
width: 520px;
max-width: 90vw;
max-height: 90vh;
background: #2f3136;
border: 1px solid #444;
border-radius: 8px;
z-index: 2147483647;
display: none;
box-shadow: 0 10px 30px rgba(0,0,0,0.8);
overflow: hidden;
flex-direction: column;
top: auto;
}
#meme-tool-header {
height: 24px;
background: #202225;
cursor: grab;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8px;
border-bottom: 1px solid #36393f;
flex-shrink: 0;
}
#meme-tool-header:active { cursor: grabbing; background: #18191c; }
#meme-tool-title { font-size: 11px; color: #aaa; font-weight: bold; user-select: none; text-transform: uppercase; letter-spacing: 0.5px; }
#meme-tool-content { padding: 10px; display: flex; flex-direction: column; gap: 10px; overflow-y: auto; flex: 1; }
#meme-tool-templates {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px,1fr));
gap: 8px;
max-height: 400px;
min-height: 200px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #202225 #2f3136;
}
#meme-tool-templates::-webkit-scrollbar { width: 8px; }
#meme-tool-templates::-webkit-scrollbar-track { background: #2f3136; }
#meme-tool-templates::-webkit-scrollbar-thumb { background-color: #202225; border-radius: 4px; }
.meme-template {
cursor: pointer;
border: 2px solid transparent;
border-radius: 6px;
overflow: hidden;
transition: all 0.15s;
position: relative;
background: #202225;
min-height: 115px;
display: flex;
flex-direction: column;
}
.meme-template:hover { border-color: #00bfff; transform: scale(1.03); }
.meme-template.selected { border-color: #00ff00; box-shadow: 0 0 0 2px #00ff00; }
.meme-template img { width: 100%; height: 100px; object-fit: contain; display: block; background: #18191c; }
.meme-template-name {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.85);
color: white;
padding: 4px 3px;
font-size: 10px;
text-align: center;
line-height: 1.1;
}
#meme-tool-editor { display: none; }
#meme-tool-editor.active { display: flex; flex-direction: column; overflow-y: auto; flex: 1; }
.meme-preview {
text-align: center;
margin-bottom: 10px;
background: #202225;
padding: 12px;
border-radius: 6px;
flex-shrink: 0;
}
.meme-preview img {
max-width: 100%;
max-height: 200px;
border-radius: 6px;
object-fit: contain;
}
.meme-template-info {
text-align: center;
color: #aaa;
font-size: 11px;
margin-top: 6px;
}
.meme-input {
margin-bottom: 8px;
}
.meme-input label {
display: block;
margin-bottom: 3px;
font-weight: 600;
color: #ccc;
font-size: 12px;
}
.meme-input input {
width: 100%;
padding: 8px;
background: #40444b;
border: 1px solid #555;
border-radius: 4px;
font-size: 13px;
box-sizing: border-box;
color: #fff;
}
.meme-input input:focus {
outline: none;
border-color: #00bfff;
}
#meme-generation-controls {
flex-shrink: 0;
}
.meme-btn {
width: 100%;
padding: 10px;
background: #5865f2;
color: white;
border: none;
border-radius: 4px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.15s;
margin-bottom: 6px;
}
.meme-btn:hover { background: #4752c4; }
.meme-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.meme-btn-secondary {
background: #4f545c;
}
.meme-btn-secondary:hover {
background: #686d73;
}
.meme-status {
text-align: center;
padding: 8px;
border-radius: 4px;
margin-bottom: 10px;
font-size: 12px;
display: none;
}
.meme-status.info {
background: #2c3e50;
color: #3498db;
}
.meme-status.success {
background: #1e4620;
color: #4caf50;
}
.meme-status.error {
background: #4a1c1c;
color: #f44336;
}
.meme-result {
margin-top: 10px;
text-align: center;
flex-shrink: 0;
}
.meme-result img {
max-width: 100%;
max-height: 50vh;
border-radius: 6px;
margin-bottom: 8px;
object-fit: contain;
}
.meme-loading {
text-align: center;
padding: 15px;
color: #aaa;
font-size: 12px;
}
#meme-tool-close { background:none; border:none; color:#aaa; font-size: 18px; cursor:pointer; line-height: 1; padding: 0; }
#meme-tool-close:hover { color:#fff; }
`;
GM_addStyle(css);
},
createModal() {
const html = `
<div id="meme-tool-modal" role="dialog">
<div id="meme-tool-header">
<span id="meme-tool-title">Meme Generator</span>
<button id="meme-tool-close" aria-label="Close">×</button>
</div>
<div id="meme-tool-content">
<div id="meme-tool-templates"></div>
<div id="meme-tool-editor">
<div class="meme-preview" id="meme-preview"></div>
<div id="meme-inputs-container"></div>
<div class="meme-status info" id="meme-status"></div>
<div id="meme-generation-controls">
<button class="meme-btn" id="meme-generate">Create Meme</button>
<button class="meme-btn meme-btn-secondary" id="meme-back">← Back to Templates</button>
</div>
<div class="meme-result" id="meme-result"></div>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', html);
this.modal = document.getElementById('meme-tool-modal');
const headerBar = document.getElementById('meme-tool-header');
this.templatesArea = document.getElementById('meme-tool-templates');
this.editorArea = document.getElementById('meme-tool-editor');
document.getElementById('meme-tool-close').addEventListener('click', () => this.closeModal());
document.getElementById('meme-back').addEventListener('click', () => {
this.editorArea.classList.remove('active');
this.templatesArea.style.display = 'grid';
document.getElementById('meme-preview').style.display = 'block';
document.getElementById('meme-inputs-container').style.display = 'block';
document.getElementById('meme-generation-controls').style.display = 'block';
document.getElementById('meme-status').style.display = 'none';
document.getElementById('meme-result').innerHTML = '';
});
this.setupDraggable(headerBar);
},
setupDraggable(handle) {
let startX, startY, startLeft, startBottom;
handle.addEventListener('mousedown', (e) => {
if (e.target.id === 'meme-tool-close') return;
e.preventDefault();
this.isDragging = true;
startX = e.clientX;
startY = e.clientY;
const rect = this.modal.getBoundingClientRect();
startLeft = rect.left;
const computedStyle = window.getComputedStyle(this.modal);
const cssBottom = parseInt(computedStyle.bottom);
if (isNaN(cssBottom)) {
startBottom = window.innerHeight - rect.bottom;
} else {
startBottom = cssBottom;
}
const onMouseMove = (e) => {
if (!this.isDragging) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
this.modal.style.left = `${startLeft + dx}px`;
this.modal.style.bottom = `${startBottom - dy}px`;
this.modal.style.top = 'auto';
};
const onMouseUp = () => {
this.isDragging = false;
this.hasMoved = true;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
this.constrainToViewport();
this.savePosition();
};
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
},
savePosition() {
if (!this.modal) return;
const rect = this.modal.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const pos = {
rightDist: viewportWidth - rect.right,
bottomDist: viewportHeight - rect.bottom,
width: viewportWidth,
height: viewportHeight
};
localStorage.setItem('tbd_meme_pos_v1', JSON.stringify(pos));
},
constrainToViewport() {
if (!this.modal || this.modal.style.display !== 'flex') return;
const rect = this.modal.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const margin = 10;
let left = parseInt(this.modal.style.left) || rect.left;
let bottom = parseInt(this.modal.style.bottom);
if (isNaN(bottom)) {
bottom = viewportHeight - rect.bottom;
}
const modalHeight = rect.height;
const top = viewportHeight - bottom - modalHeight;
const maxLeft = viewportWidth - rect.width - margin;
if (left < margin) left = margin;
if (left > maxLeft) left = maxLeft;
if (top < margin) {
bottom = viewportHeight - modalHeight - margin;
}
if (bottom < margin) {
bottom = margin;
}
this.modal.style.left = `${left}px`;
this.modal.style.bottom = `${bottom}px`;
this.modal.style.top = 'auto';
},
restorePosition(anchor) {
const saved = localStorage.getItem('tbd_meme_pos_v1');
if (saved) {
try {
const pos = JSON.parse(saved);
if (pos.rightDist !== undefined && pos.bottomDist !== undefined) {
const viewportWidth = window.innerWidth;
const rect = this.modal.getBoundingClientRect();
const left = viewportWidth - pos.rightDist - rect.width;
const bottom = pos.bottomDist;
this.modal.style.left = `${left}px`;
this.modal.style.bottom = `${bottom}px`;
this.modal.style.top = 'auto';
this.hasMoved = true;
setTimeout(() => this.constrainToViewport(), 0);
return;
}
} catch(e) { console.error('Invalid saved pos'); }
}
if (anchor && !this.hasMoved) {
const rect = anchor.getBoundingClientRect();
let left = rect.right - 520;
if (left < 10) left = 10;
const bottom = window.innerHeight - rect.top + 10;
this.modal.style.left = `${left}px`;
this.modal.style.bottom = `${bottom}px`;
this.modal.style.top = 'auto';
setTimeout(() => this.constrainToViewport(), 0);
}
},
addMemeButton() {
const tray = document.querySelector('#shout-ibb-container');
if (!tray || document.getElementById('meme-tool-btn')) return;
this.memeButton = document.createElement('span');
this.memeButton.id = 'meme-tool-btn';
this.memeButton.className = 'inline-submit-btn';
this.memeButton.title = 'Create Meme';
this.memeButton.innerHTML = '<i class="material-icons">theater_comedy</i>';
const isSpotlight = document.body.classList.contains('spotlight-mode') ||
window.location.search.includes('spotlight');
const topValue = isSpotlight ? '17px' : '6px';
this.memeButton.style.cssText = `display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:#ccc;position:relative;top:${topValue};height:24px;width:28px;margin-left:4px;margin-right:2px;`;
this.memeButton.addEventListener('click', (ev) => {
ev.stopPropagation();
ev.preventDefault();
const input = document.querySelector('#shout_text');
if (input) {
if(this.modal.style.display === 'flex') this.closeModal();
else this.openModal(input, this.memeButton);
}
});
const targetBtn = tray.querySelector('#tbd-uploader-button')
|| tray.querySelector('#imgbd-uploader-btn')
|| tray.querySelector('#urlBtn')
|| tray.querySelector('#tbdm-image-upload-btn')
|| tray.querySelector('#tbdm-gif-tool-btn');
if (targetBtn) {
tray.insertBefore(this.memeButton, targetBtn);
} else {
tray.appendChild(this.memeButton);
}
},
loadTemplates() {
const CUSTOM_TEMPLATES_URL = 'https://imgflip.mushi53566.workers.dev/';
// Fetch Imgflip templates
GM_xmlhttpRequest({
method: 'GET',
url: this.IMGFLIP_CONFIG.apiUrl + '/get_memes',
onload: (res) => {
try {
const data = JSON.parse(res.responseText);
if (data.success) {
const apiTemplates = data.data.memes;
// Fetch custom templates
GM_xmlhttpRequest({
method: 'GET',
url: CUSTOM_TEMPLATES_URL,
onload: (customRes) => {
try {
const customData = JSON.parse(customRes.responseText);
if (customData.success) {
const customTemplates = customData.data.memes;
const merged = [];
const interval = Math.floor(apiTemplates.length / customTemplates.length);
let customIndex = 0;
for (let i = 0; i < apiTemplates.length; i++) {
merged.push(apiTemplates[i]);
if ((i + 1) % interval === 0 && customIndex < customTemplates.length) {
merged.push(customTemplates[customIndex]);
customIndex++;
}
}
while (customIndex < customTemplates.length) {
merged.push(customTemplates[customIndex]);
customIndex++;
}
this.allTemplates = merged;
this.renderTemplates();
}
} catch (e) {
// If custom templates fail, just use API templates
this.allTemplates = apiTemplates;
this.renderTemplates();
}
},
onerror: () => {
// Fallback to API templates only
this.allTemplates = apiTemplates;
this.renderTemplates();
}
});
}
} catch (e) {
console.error('Error loading templates');
}
},
onerror: () => console.error('Network error')
});
},
renderTemplates() {
this.templatesArea.innerHTML = this.allTemplates.map(t => `
<div class="meme-template" data-id="${t.id}" data-url="${t.url}" data-boxes="${t.box_count}" data-name="${t.name}">
<img src="${t.url}" alt="${t.name}" loading="lazy">
<div class="meme-template-name">${t.name}</div>
</div>
`).join('');
this.templatesArea.querySelectorAll('.meme-template').forEach(el => {
el.onclick = () => this.selectTemplate(el);
});
},
selectTemplate(el) {
document.querySelectorAll('.meme-template').forEach(t => t.classList.remove('selected'));
el.classList.add('selected');
const id = el.dataset.id;
const url = el.dataset.url;
const boxes = parseInt(el.dataset.boxes) || 2;
const name = el.dataset.name;
this.currentTemplate = { id, url, boxes, name };
document.getElementById('meme-preview').innerHTML = `
<img src="${url}">
<div class="meme-template-info">
${name} - ${boxes} text ${boxes === 1 ? 'box' : 'boxes'}
</div>
`;
this.createTextInputs(boxes);
this.templatesArea.style.display = 'none';
this.editorArea.classList.add('active');
document.getElementById('meme-result').innerHTML = '';
setTimeout(() => {
this.constrainToViewport();
}, 0);
document.getElementById('meme-generate').onclick = () => this.generateMeme();
},
createTextInputs(count) {
const container = document.getElementById('meme-inputs-container');
container.innerHTML = '';
for (let i = 0; i < count; i++) {
container.innerHTML += `
<div class="meme-input">
<label>Text Box ${i + 1}</label>
<input type="text" id="meme-text${i}" placeholder="Enter text for box ${i + 1}">
</div>
`;
}
},
generateMeme() {
if (!this.currentTemplate) return;
const boxes = [];
for (let i = 0; i < this.currentTemplate.boxes; i++) {
const text = document.getElementById(`meme-text${i}`)?.value || '';
boxes.push({ text });
}
const hasText = boxes.some(box => box.text.trim() !== '');
if (!hasText) {
this.showStatus('Enter at least one text', 'error');
return;
}
const btn = document.getElementById('meme-generate');
btn.disabled = true;
this.showStatus('Creating meme...', 'info');
let formData = `template_id=${this.currentTemplate.id}&username=${this.IMGFLIP_CONFIG.username}&password=${this.IMGFLIP_CONFIG.password}`;
boxes.forEach((box, index) => {
formData += `&boxes[${index}][text]=${encodeURIComponent(box.text)}`;
});
GM_xmlhttpRequest({
method: 'POST',
url: this.IMGFLIP_CONFIG.apiUrl + '/caption_image',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: formData,
onload: (res) => {
btn.disabled = false;
btn.textContent = 'Create Meme';
try {
const result = JSON.parse(res.responseText);
if (result.success) {
this.showStatus('Meme created! Click to insert.', 'success');
const memeUrl = result.data.url;
document.getElementById('meme-preview').style.display = 'none';
document.getElementById('meme-inputs-container').style.display = 'none';
document.getElementById('meme-generation-controls').style.display = 'none';
document.getElementById('meme-status').style.display = 'none';
document.getElementById('meme-result').innerHTML = `
<img src="${memeUrl}">
<button class="meme-btn" id="meme-insert-action">Insert Meme</button>
`;
document.getElementById('meme-insert-action').onclick = () => {
this.insertMeme(memeUrl);
};
} else {
this.showStatus('Error: ' + result.error_message, 'error');
}
} catch (e) {
this.showStatus('Failed to create meme', 'error');
}
},
onerror: () => {
btn.disabled = false;
btn.textContent = 'Create Meme';
this.showStatus('Network error', 'error');
}
});
},
insertMeme(url) {
if (!this.activeInput) return;
const cur = this.activeInput.value;
const cursorPos = this.activeInput.selectionStart || cur.length;
const before = cur.substring(0, cursorPos);
const after = cur.substring(cursorPos);
const toInsert = url + ' ';
this.activeInput.value = before + toInsert + after;
const newCursorPos = before.length + toInsert.length;
this.closeModal();
setTimeout(() => {
this.activeInput.focus();
this.activeInput.setSelectionRange(newCursorPos, newCursorPos);
}, 50);
},
showStatus(msg, type) {
const status = document.getElementById('meme-status');
if (status) {
status.textContent = msg;
status.className = 'meme-status ' + type;
status.style.display = 'block';
}
},
openModal(inputEl, anchorEl) {
this.activeInput = inputEl;
this.restorePosition(anchorEl);
this.modal.style.display = 'flex';
if (this.allTemplates.length === 0) {
this.loadTemplates();
}
},
closeModal() {
this.modal.style.display = 'none';
this.editorArea.classList.remove('active');
this.templatesArea.style.display = 'grid';
document.getElementById('meme-preview').style.display = 'block';
document.getElementById('meme-inputs-container').style.display = 'block';
document.getElementById('meme-generation-controls').style.display = 'block';
document.getElementById('meme-status').style.display = 'none';
document.getElementById('meme-result').innerHTML = '';
},
init() {
if (!CONFIG.meme_creator_enabled) return;
this.addStyles();
this.createModal();
this.addMemeButton();
const resizeHandler = this.debounce(() => {
if (this.modal && this.modal.style.display === 'flex') {
this.constrainToViewport();
this.savePosition();
}
}, 150);
window.addEventListener('resize', resizeHandler);
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.modal.style.display === 'flex') {
e.preventDefault();
this.closeModal();
if (this.activeInput) this.activeInput.focus();
}
});
},
stop() {
const btn = document.getElementById('meme-tool-btn');
if (btn) btn.remove();
if (this.modal) this.closeModal();
}
};
// ============================================================================
// MODULE 14: BBCODE VALIDATOR (Built-in - Always Active)
// ============================================================================
const BBCodeValidatorModule = {
warningElement: null,
checkTimeout: null,
DISALLOWED_BBCODES: [
'img', 'color', 'size', 'font', 'b', 'i', 'u', 's',
'spoiler', 'center', 'quote', 'df', 'imgw'
],
createWarningElement() {
if (this.warningElement) return;
this.warningElement = document.createElement('div');
this.warningElement.id = 'bbcode-warning';
this.warningElement.style.cssText = `
display: none;
position: absolute;
top: 45px;
right: 12px;
background: rgba(220, 38, 38, 0.15);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1.5px solid rgba(239, 68, 68, 0.4);
color: #ef4444;
padding: 10px 16px;
border-radius: 10px;
font-size: 13px;
font-weight: 600;
box-shadow: 0 4px 16px rgba(220, 38, 38, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.05);
animation: slideInRight 0.3s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 10000;
pointer-events: none;
letter-spacing: 0.3px;
`;
this.warningElement.innerHTML = `
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="display: inline-block; vertical-align: middle; margin-right: 8px;">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>
<span style="vertical-align: middle;">BBCode is not allowed. Read rules.</span>
<img src="https://www.torrentbd.net/images/smilies/an_slap.gif"
alt="slap"
style="display: inline-block; vertical-align: middle; margin-left: 8px; height: 24px; width: auto;">
`;
const container = document.getElementById('shoutbox-container');
if (container) {
container.style.position = 'relative';
container.appendChild(this.warningElement);
}
},
checkForBBCode(text) {
// Check for disallowed BBCode patterns
const pattern = new RegExp(`\\[(${this.DISALLOWED_BBCODES.join('|')})(=.*?)?\\]`, 'i');
return pattern.test(text);
},
showWarning() {
if (this.warningElement) {
this.warningElement.style.display = 'flex';
this.warningElement.style.alignItems = 'center';
}
},
hideWarning() {
if (this.warningElement) {
this.warningElement.style.display = 'none';
}
},
handleInput(e) {
clearTimeout(this.checkTimeout);
this.checkTimeout = setTimeout(() => {
const text = e.target.value;
if (this.checkForBBCode(text)) {
this.showWarning();
} else {
this.hideWarning();
}
}, 300);
},
init() {
const shoutInput = document.querySelector('#shout_text');
if (!shoutInput) return;
this.createWarningElement();
shoutInput.addEventListener('input', (e) => this.handleInput(e));
// Hide warning when sending message
const sendBtn = document.getElementById('sendShoutBtn');
if (sendBtn) {
sendBtn.addEventListener('click', () => this.hideWarning());
}
}
};
// ============================================================================
// MODULE 15: INFINITE SCROLL
// ============================================================================
const InfiniteScrollModule = {
isLoading: false,
init() {
const container = document.querySelector('#shouts-container');
if (!container) return;
container.addEventListener('scroll', () => {
if (this.isLoading) return;
const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
if (distanceFromBottom > 80) return;
this.isLoading = true;
if (typeof getShoutHistory === 'function') {
getShoutHistory();
}
// Watch for new shouts being added, then unlock
const before = container.children.length;
let checks = 0;
const poll = setInterval(() => {
checks++;
const after = container.children.length;
if (after > before || checks > 20) {
clearInterval(poll);
this.isLoading = false;
}
}, 200);
});
}
};
// ============================================================================
// SETTINGS UI
// ============================================================================
function createSettingsUI() {
if (document.querySelector("#tbdm-modal-wrapper")) return;
const uiWrapper = document.createElement("div");
uiWrapper.id = "tbdm-modal-wrapper";
uiWrapper.style.display = "none";
uiWrapper.innerHTML = `
<div id="tbdm-container">
<div id="tbdm-header">
<h1>🛠️ Shoutbox Manager</h1>
<p>Complete control over your TorrentBD shoutbox experience</p>
<button id="tbdm-close-btn" title="Close">×</button>
</div>
<div id="tbdm-tabs">
<button class="tbdm-tab active" data-tab="features">⚡ Features</button>
<button class="tbdm-tab" data-tab="cleaner">🧹 Cleaner</button>
<button class="tbdm-tab" data-tab="notifier">🔔 Notifier</button>
<button class="tbdm-tab" data-tab="autocomplete">⌨️ Autocomplete</button>
</div>
<div id="tbdm-content">
<!-- Features Tab -->
<div class="tbdm-tab-content active" data-tab="features">
<div class="tbdm-section">
<h2 class="tbdm-section-header">🎨 Available Features</h2>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">📸</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">Image Upload</div>
<div class="tbdm-feature-desc">Paste, drag & drop, or click to upload</div>
</div>
</div>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-image-upload"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">@</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">Easy Mention</div>
<div class="tbdm-feature-desc">Click timestamp to mention users</div>
</div>
</div>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-easy-mention"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">🔗</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">URL Sender</div>
<div class="tbdm-feature-desc">Easy URL formatting with labels</div>
</div>
</div>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-url-sender"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">🎯</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">Shoutbox Input Lock</div>
<div class="tbdm-feature-desc">Put mouse anywhere on the shoutbox</div>
</div>
</div>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-focus-lock"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">⏰</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">Idle Prevention</div>
<div class="tbdm-feature-desc">Prevent auto-pause on idle</div>
</div>
</div>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-idle-prevention"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">🎬</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">GIF Picker</div>
<div class="tbdm-feature-desc">Search and insert GIFs (Tenor/Giphy)</div>
</div>
</div>
<label class="tbdm-switch">
<input type="checkbox" id="tbdm-gif-picker">
<span class="tbdm-slider"></span>
</label>
</div>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">🖼️</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">Image Viewer</div>
<div class="tbdm-feature-desc">Display image links as thumbnails</div>
</div>
</div>
<label class="tbdm-switch">
<input type="checkbox" id="tbdm-image-viewer">
<span class="tbdm-slider"></span>
</label>
</div>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">😊</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">Unicode Emoji</div>
<div class="tbdm-feature-desc">Pick and insert unicode emojis</div>
</div>
</div>
<label class="tbdm-switch">
<input type="checkbox" id="tbdm-unicode-emoji">
<span class="tbdm-slider"></span>
</label>
</div>
<div class="tbdm-feature-card">
<div class="tbdm-feature-header">
<div class="tbdm-feature-icon">🎭</div>
<div class="tbdm-feature-info">
<div class="tbdm-feature-title">Meme Creator</div>
<div class="tbdm-feature-desc">Create and insert memes</div>
</div>
</div>
<label class="tbdm-switch">
<input type="checkbox" id="tbdm-meme-creator">
<span class="tbdm-slider"></span>
</label>
</div>
</div>
</div>
<!-- Cleaner Tab -->
<div class="tbdm-tab-content" data-tab="cleaner">
<div class="tbdm-setting-row tbdm-master-switch">
<label for="tbdm-cleaner-enabled">Enable Cleaner</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-cleaner-enabled"><span class="tbdm-slider"></span></label>
</div>
<hr class="tbdm-divider">
<div class="tbdm-section">
<h2 class="tbdm-section-header">Hide System Messages</h2>
<div class="tbdm-setting-row">
<label for="tbdm-hide-all-users">Hide All User Messages</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-hide-all-users"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-setting-row">
<label for="tbdm-filter-torrent">New Torrents</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-filter-torrent"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-setting-row">
<label for="tbdm-filter-forumPost">New Forum Posts</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-filter-forumPost"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-setting-row">
<label for="tbdm-filter-forumTopic">New Forum Topics</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-filter-forumTopic"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-setting-row">
<label for="tbdm-filter-request">New Requests</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-filter-request"><span class="tbdm-slider"></span></label>
</div>
</div>
<hr class="tbdm-divider">
<div class="tbdm-section">
<h2 class="tbdm-section-header">Block Users</h2>
<div class="tbdm-setting-row">
<label for="tbdm-block-users">Block by User ID</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-block-users"><span class="tbdm-slider"></span></label>
</div>
<textarea id="tbdm-blocked-user-ids" placeholder="Enter User IDs (one per line)"></textarea>
</div>
<hr class="tbdm-divider">
<div class="tbdm-section">
<h2 class="tbdm-section-header">Block Keywords</h2>
<div class="tbdm-setting-row">
<label for="tbdm-block-keywords">Block by Keyword</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-block-keywords"><span class="tbdm-slider"></span></label>
</div>
<textarea id="tbdm-blocked-keywords" placeholder="Enter keywords to block (one per line)"></textarea>
</div>
</div>
<!-- Notifier Tab -->
<div class="tbdm-tab-content" data-tab="notifier">
<div class="tbdm-setting-row tbdm-master-switch">
<label for="tbdm-notifier-enabled">Enable Notifier</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-notifier-enabled"><span class="tbdm-slider"></span></label>
</div>
<hr class="tbdm-divider">
<div class="tbdm-section">
<div class="tbdm-form-group">
<label>Your Username</label>
<div id="tbdm-username-display">Detecting...</div>
</div>
<div class="tbdm-form-group">
<div class="tbdm-label-row">
<label for="tbdm-keywords">Additional Keywords (One per line)</label>
<button id="tbdm-reset-keywords">Reset</button>
</div>
<textarea id="tbdm-keywords" placeholder="Add keywords to be notified about..."></textarea>
</div>
<div class="tbdm-controls-row">
<div class="tbdm-form-group">
<label>Highlight Color</label>
<div id="tbdm-color-picker-wrapper">
<input type="color" id="tbdm-highlight-color">
</div>
</div>
<div class="tbdm-form-group">
<label for="tbdm-volume">Notification Volume</label>
<div class="tbdm-volume-control">
<div id="tbdm-volume-icon"></div>
<input type="range" id="tbdm-volume" min="0" max="100" step="1">
</div>
</div>
</div>
</div>
</div>
<!-- Autocomplete Tab -->
<div class="tbdm-tab-content" data-tab="autocomplete">
<div class="tbdm-section">
<h2 class="tbdm-section-header">Autocomplete Options (Press Tab)</h2>
<div class="tbdm-setting-row">
<label for="tbdm-autocomplete-username">Username Autocomplete</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-autocomplete-username"><span class="tbdm-slider"></span></label>
</div>
<div class="tbdm-setting-row">
<label for="tbdm-autocomplete-sticker">Sticker/Emoji Autocomplete</label>
<label class="tbdm-switch"><input type="checkbox" id="tbdm-autocomplete-sticker"><span class="tbdm-slider"></span></label>
</div>
</div>
<hr class="tbdm-divider">
<div class="tbdm-section">
<h2 class="tbdm-section-header">Custom Mappings</h2>
<div class="tbdm-mapping-header">
<p class="tbdm-help-text">Format: keyword = emoji/sticker (one per line)</p>
<button id="tbdm-reset-mappings" class="tbdm-secondary-btn">Reset to Defaults</button>
</div>
<div id="tbdm-mappings-container"></div>
<button id="tbdm-add-mapping" class="tbdm-primary-btn">+ Add New Mapping</button>
</div>
</div>
</div>
</div>
`;
document.body.appendChild(uiWrapper);
// Tab switching
const tabs = uiWrapper.querySelectorAll('.tbdm-tab');
const tabContents = uiWrapper.querySelectorAll('.tbdm-tab-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const tabName = tab.dataset.tab;
tabs.forEach(t => t.classList.remove('active'));
tabContents.forEach(tc => tc.classList.remove('active'));
tab.classList.add('active');
uiWrapper.querySelector(`.tbdm-tab-content[data-tab="${tabName}"]`).classList.add('active');
});
});
// Close button
document.getElementById('tbdm-close-btn').addEventListener('click', () => {
uiWrapper.style.display = 'none';
});
uiWrapper.addEventListener('click', (e) => {
if (e.target.id === 'tbdm-modal-wrapper') {
uiWrapper.style.display = 'none';
}
});
// Load and setup all settings
setupCleanerSettings();
setupNotifierSettings();
setupFeaturesSettings();
setupAutocompleteSettings();
return uiWrapper;
}
function setupCleanerSettings() {
const enabledSwitch = document.getElementById('tbdm-cleaner-enabled');
const hideAllUsers = document.getElementById('tbdm-hide-all-users');
const blockUsers = document.getElementById('tbdm-block-users');
const blockedUserIds = document.getElementById('tbdm-blocked-user-ids');
const blockKeywords = document.getElementById('tbdm-block-keywords');
const blockedKeywords = document.getElementById('tbdm-blocked-keywords');
// Load settings
enabledSwitch.checked = CONFIG.cleaner_enabled;
hideAllUsers.checked = CONFIG.cleaner_hide_all_users;
blockUsers.checked = CONFIG.cleaner_block_users_enabled;
blockedUserIds.value = CONFIG.cleaner_blocked_user_ids.join('\n');
blockedUserIds.disabled = !CONFIG.cleaner_block_users_enabled;
blockKeywords.checked = CONFIG.cleaner_block_keywords_enabled;
blockedKeywords.value = CONFIG.cleaner_blocked_keywords.join('\n');
blockedKeywords.disabled = !CONFIG.cleaner_block_keywords_enabled;
for (const key in CleanerModule.systemFilters) {
const checkbox = document.getElementById(`tbdm-filter-${key}`);
checkbox.checked = CONFIG.cleaner_filters[key];
checkbox.addEventListener('change', (e) => {
CONFIG.cleaner_filters[key] = e.target.checked;
saveConfig('cleaner_filters', CONFIG.cleaner_filters);
CleanerModule.check();
});
}
// Event listeners
enabledSwitch.addEventListener('change', (e) => {
saveConfig('cleaner_enabled', e.target.checked);
if (!e.target.checked) {
// When disabling, show all hidden messages immediately
const shoutItems = document.querySelectorAll(".shout-item");
shoutItems.forEach((item) => {
item.style.display = "";
});
} else {
// When enabling, apply filters immediately
CleanerModule.check();
}
});
hideAllUsers.addEventListener('change', (e) => {
saveConfig('cleaner_hide_all_users', e.target.checked);
CleanerModule.check();
});
blockUsers.addEventListener('change', (e) => {
saveConfig('cleaner_block_users_enabled', e.target.checked);
blockedUserIds.disabled = !e.target.checked;
CleanerModule.check();
});
blockedUserIds.addEventListener('input', () => {
const ids = blockedUserIds.value.split('\n').map(u => u.trim()).filter(Boolean);
saveConfig('cleaner_blocked_user_ids', ids);
CleanerModule.check();
});
blockKeywords.addEventListener('change', (e) => {
saveConfig('cleaner_block_keywords_enabled', e.target.checked);
blockedKeywords.disabled = !e.target.checked;
CleanerModule.check();
});
blockedKeywords.addEventListener('input', () => {
const keywords = blockedKeywords.value.split('\n').map(k => k.trim()).filter(Boolean);
saveConfig('cleaner_blocked_keywords', keywords);
CleanerModule.check();
});
// Make setting rows clickable
document.querySelectorAll('.tbdm-tab-content[data-tab="cleaner"] .tbdm-setting-row').forEach(row => {
row.style.cursor = 'pointer';
// Remove the for attribute from labels so they don't interfere
const label = row.querySelector('label:first-child');
if (label) label.removeAttribute('for');
row.addEventListener('click', (e) => {
if (e.target.closest('.tbdm-switch')) return;
const checkbox = row.querySelector('input[type="checkbox"]');
if (checkbox) checkbox.click();
});
});
}
function setupNotifierSettings() {
const enabledSwitch = document.getElementById('tbdm-notifier-enabled');
const usernameDisplay = document.getElementById('tbdm-username-display');
const keywords = document.getElementById('tbdm-keywords');
const resetKeywords = document.getElementById('tbdm-reset-keywords');
const highlightColor = document.getElementById('tbdm-highlight-color');
const colorWrapper = document.getElementById('tbdm-color-picker-wrapper');
const volumeSlider = document.getElementById('tbdm-volume');
const volumeIcon = document.getElementById('tbdm-volume-icon');
// Detect and load username
const detectedUsername = detectUsername();
if (!CONFIG.notifier_username || CONFIG.notifier_username === 'Not Found') {
saveConfig('notifier_username', detectedUsername);
}
usernameDisplay.textContent = CONFIG.notifier_username;
// Load settings
enabledSwitch.checked = CONFIG.notifier_enabled;
keywords.value = CONFIG.notifier_keywords.join('\n');
highlightColor.value = CONFIG.notifier_highlight_color;
colorWrapper.style.backgroundColor = CONFIG.notifier_highlight_color;
volumeSlider.value = CONFIG.notifier_sound_volume * 100;
const volumeIcons = {
mute: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13.5 4.06c0-1.336-1.616-2.005-2.56-1.06l-4.5 4.5H4.508c-1.141 0-2.318.664-2.66 1.905A9.76 9.76 0 0 0 1.5 12c0 .898.121 1.768.35 2.595.341 1.24 1.518 1.905 2.659 1.905h1.93l4.5 4.5c.945.945 2.561.276 2.561-1.06V4.06ZM17.78 9.22a.75.75 0 1 0-1.06 1.06L18.44 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06l1.72-1.72 1.72 1.72a.75.75 0 1 0 1.06-1.06L20.56 12l1.72-1.72a.75.75 0 1 0-1.06-1.06l-1.72 1.72-1.72-1.72Z" /></svg>`,
on: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13.5 4.06c0-1.336-1.616-2.005-2.56-1.06l-4.5 4.5H4.508c-1.141 0-2.318.664-2.66 1.905A9.76 9.76 0 0 0 1.5 12c0 .898.121 1.768.35 2.595.341 1.24 1.518 1.905 2.659 1.905h1.93l4.5 4.5c.945.945 2.561.276 2.561-1.06V4.06ZM18.584 5.106a.75.75 0 0 1 1.06 0c3.808 3.807 3.808 9.98 0 13.788a.75.75 0 0 1-1.06-1.06 8.25 8.25 0 0 0 0-11.668.75.75 0 0 1 0-1.06Z" /><path d="M15.932 7.757a.75.75 0 0 1 1.061 0 6 6 0 0 1 0 8.486.75.75 0 0 1-1.06-1.061 4.5 4.5 0 0 0 0-6.364.75.75 0 0 1 0-1.06Z" /></svg>`
};
function updateVolumeIcon() {
volumeIcon.innerHTML = volumeSlider.value == 0 ? volumeIcons.mute : volumeIcons.on;
}
updateVolumeIcon();
// Event listeners
enabledSwitch.addEventListener('change', (e) => {
saveConfig('notifier_enabled', e.target.checked);
if (e.target.checked) NotifierModule.init();
});
keywords.addEventListener('input', () => {
const keywordList = keywords.value.split('\n').map(k => k.trim()).filter(k => k);
saveConfig('notifier_keywords', keywordList);
});
resetKeywords.addEventListener('click', () => {
keywords.value = '';
saveConfig('notifier_keywords', []);
});
highlightColor.addEventListener('input', () => {
saveConfig('notifier_highlight_color', highlightColor.value);
colorWrapper.style.backgroundColor = highlightColor.value;
});
volumeSlider.addEventListener('input', () => {
saveConfig('notifier_sound_volume', parseFloat(volumeSlider.value) / 100);
updateVolumeIcon();
});
// Make notifier setting rows clickable
document.querySelectorAll('.tbdm-tab-content[data-tab="notifier"] .tbdm-setting-row').forEach(row => {
row.style.cursor = 'pointer';
// Remove the for attribute from labels so they don't interfere
const label = row.querySelector('label:first-child');
if (label) label.removeAttribute('for');
row.addEventListener('click', (e) => {
if (e.target.closest('.tbdm-switch')) return;
const checkbox = row.querySelector('input[type="checkbox"]');
if (checkbox) checkbox.click();
});
});
}
function setupFeaturesSettings() {
const imageUpload = document.getElementById('tbdm-image-upload');
const easyMention = document.getElementById('tbdm-easy-mention');
const urlSender = document.getElementById('tbdm-url-sender');
const focusLock = document.getElementById('tbdm-focus-lock');
const idlePrevention = document.getElementById('tbdm-idle-prevention');
const gifPicker = document.getElementById('tbdm-gif-picker');
const imageViewer = document.getElementById('tbdm-image-viewer');
const unicodeEmoji = document.getElementById('tbdm-unicode-emoji');
// Load settings
unicodeEmoji.checked = CONFIG.unicode_emoji_enabled;
// Event listener
unicodeEmoji.addEventListener('change', (e) => {
saveConfig('unicode_emoji_enabled', e.target.checked);
if (e.target.checked) {
UnicodeEmojiModule.init();
} else {
UnicodeEmojiModule.stop();
}
});
//
const memeCreator = document.getElementById('tbdm-meme-creator');
// Load settings
memeCreator.checked = CONFIG.meme_creator_enabled;
// Event listener
memeCreator.addEventListener('change', (e) => {
saveConfig('meme_creator_enabled', e.target.checked);
if (e.target.checked) {
MemeCreatorModule.init();
} else {
MemeCreatorModule.stop();
}
});
// Load settings
gifPicker.checked = CONFIG.gif_picker_enabled;
imageViewer.checked = CONFIG.image_viewer_enabled;
// Event listeners
gifPicker.addEventListener('change', (e) => {
saveConfig('gif_picker_enabled', e.target.checked);
if (e.target.checked) {
GifPickerModule.init();
} else {
GifPickerModule.stop();
}
});
imageViewer.addEventListener('change', (e) => {
saveConfig('image_viewer_enabled', e.target.checked);
if (e.target.checked) {
ImageViewerModule.init();
} else {
ImageViewerModule.stop();
}
});
// Load settings
imageUpload.checked = CONFIG.image_upload_enabled;
easyMention.checked = CONFIG.easy_mention_enabled;
urlSender.checked = CONFIG.url_sender_enabled;
focusLock.checked = CONFIG.focus_lock_enabled;
idlePrevention.checked = CONFIG.idle_prevention_enabled;
// Make feature cards clickable
document.querySelectorAll('.tbdm-feature-card').forEach(card => {
card.style.cursor = 'pointer';
card.addEventListener('click', (e) => {
if (e.target.closest('.tbdm-switch')) return;
const checkbox = card.querySelector('input[type="checkbox"]');
if (checkbox) checkbox.click();
});
});
// Event listeners
imageUpload.addEventListener('change', (e) => {
saveConfig('image_upload_enabled', e.target.checked);
if (e.target.checked) {
ImageUploadModule.init();
} else {
// Remove upload button if exists
const uploadBtn = document.getElementById('tbdm-image-upload-btn');
if (uploadBtn) uploadBtn.remove();
}
});
easyMention.addEventListener('change', (e) => {
saveConfig('easy_mention_enabled', e.target.checked);
if (e.target.checked) {
EasyMentionModule.init();
} else {
EasyMentionModule.stop();
}
});
urlSender.addEventListener('change', (e) => {
saveConfig('url_sender_enabled', e.target.checked);
if (e.target.checked) {
URLSenderModule.init();
} else {
// Remove URL button and window if exists
const urlBtn = document.getElementById('urlBtn');
const urlWindow = document.getElementById('urlWindow');
if (urlBtn) urlBtn.remove();
if (urlWindow) urlWindow.remove();
}
});
focusLock.addEventListener('change', (e) => {
saveConfig('focus_lock_enabled', e.target.checked);
if (e.target.checked) {
FocusLockModule.init();
} else {
FocusLockModule.deactivateFocusLock();
}
});
idlePrevention.addEventListener('change', (e) => {
saveConfig('idle_prevention_enabled', e.target.checked);
if (e.target.checked) {
IdlePreventionModule.init();
} else {
IdlePreventionModule.stop();
}
});
}
function setupAutocompleteSettings() {
const usernameAutocomplete = document.getElementById('tbdm-autocomplete-username');
const stickerAutocomplete = document.getElementById('tbdm-autocomplete-sticker');
const mappingsContainer = document.getElementById('tbdm-mappings-container');
const addMappingBtn = document.getElementById('tbdm-add-mapping');
const resetMappingsBtn = document.getElementById('tbdm-reset-mappings');
// Load settings
usernameAutocomplete.checked = CONFIG.autocomplete_username_enabled;
stickerAutocomplete.checked = CONFIG.autocomplete_sticker_enabled;
// Event listeners
usernameAutocomplete.addEventListener('change', (e) => {
saveConfig('autocomplete_username_enabled', e.target.checked);
if (e.target.checked || CONFIG.autocomplete_sticker_enabled) {
AutocompleteModule.init();
} else if (!e.target.checked && !CONFIG.autocomplete_sticker_enabled) {
AutocompleteModule.stop();
}
});
stickerAutocomplete.addEventListener('change', (e) => {
saveConfig('autocomplete_sticker_enabled', e.target.checked);
if (e.target.checked || CONFIG.autocomplete_username_enabled) {
AutocompleteModule.init();
} else if (!e.target.checked && !CONFIG.autocomplete_username_enabled) {
AutocompleteModule.stop();
}
});
// Render mappings
function renderMappings() {
mappingsContainer.innerHTML = '';
const mappings = CONFIG.autocomplete_mappings;
Object.keys(mappings).forEach(key => {
const row = document.createElement('div');
row.className = 'tbdm-mapping-row';
row.innerHTML = `
<input type="text" class="tbdm-mapping-key" value="${key}" placeholder="keyword" data-original-key="${key}">
<span>=</span>
<input type="text" class="tbdm-mapping-value" value="${mappings[key]}" placeholder=":emoji" data-original-key="${key}">
<button class="tbdm-delete-mapping" title="Delete" data-key="${key}">×</button>
`;
mappingsContainer.appendChild(row);
const keyInput = row.querySelector('.tbdm-mapping-key');
const valueInput = row.querySelector('.tbdm-mapping-value');
const deleteBtn = row.querySelector('.tbdm-delete-mapping');
keyInput.addEventListener('blur', () => {
const originalKey = keyInput.getAttribute('data-original-key');
const newKey = keyInput.value.trim();
if (newKey && newKey !== originalKey) {
updateMappingKey(originalKey, newKey);
keyInput.setAttribute('data-original-key', newKey);
// Update the data attribute for valueInput as well
valueInput.setAttribute('data-original-key', newKey);
// Update the delete button's data-key
deleteBtn.setAttribute('data-key', newKey);
}
});
valueInput.addEventListener('blur', () => {
const currentKey = keyInput.getAttribute('data-original-key');
const newValue = valueInput.value.trim();
const mappings = CONFIG.autocomplete_mappings;
if (newValue && newValue !== mappings[currentKey]) {
updateMappingValue(currentKey, newValue);
}
});
deleteBtn.addEventListener('click', () => {
const keyToDelete = deleteBtn.getAttribute('data-key');
deleteMapping(keyToDelete);
});
});
}
function updateMappingKey(oldKey, newKey) {
const mappings = CONFIG.autocomplete_mappings;
if (mappings.hasOwnProperty(oldKey)) {
const value = mappings[oldKey];
delete mappings[oldKey];
mappings[newKey] = value;
saveConfig('autocomplete_mappings', mappings);
}
}
function updateMappingValue(key, newValue) {
const mappings = CONFIG.autocomplete_mappings;
if (mappings.hasOwnProperty(key)) {
mappings[key] = newValue;
saveConfig('autocomplete_mappings', mappings);
}
}
function deleteMapping(key) {
const mappings = CONFIG.autocomplete_mappings;
delete mappings[key];
saveConfig('autocomplete_mappings', mappings);
renderMappings();
}
addMappingBtn.addEventListener('click', () => {
const mappings = CONFIG.autocomplete_mappings;
let newKey = 'new';
let counter = 1;
while (mappings.hasOwnProperty(newKey)) {
newKey = `new${counter++}`;
}
mappings[newKey] = ':emoji';
saveConfig('autocomplete_mappings', mappings);
renderMappings();
});
resetMappingsBtn.addEventListener('click', () => {
if (confirm('Reset all mappings to defaults?')) {
const defaultMappings = {
'hi': ':hello',
'hello': ':hello',
'no': ':negative',
'nahi': ':sticker-mb-no',
'sad': ':sticker-pepe-face',
'lmao': ':lmao',
'justsaid': ':justsaid',
'wow': ':sticker-omg-wow',
'lol': ':sticker-jjj-laugh',
'why': ':sticker-cat-why',
'ty': ':thankyou',
'slap': ':slap',
'wont': ':sticker-sr-no',
'bruh': ':sticker-facepalm',
'yay': ':yepdance',
'aww': ':sticker-pepe-aw'
};
saveConfig('autocomplete_mappings', defaultMappings);
renderMappings();
}
});
renderMappings();
// Make autocomplete setting rows clickable
document.querySelectorAll('.tbdm-tab-content[data-tab="autocomplete"] .tbdm-setting-row').forEach(row => {
row.style.cursor = 'pointer';
// Remove the for attribute from labels so they don't interfere
const label = row.querySelector('label:first-child');
if (label) label.removeAttribute('for');
row.addEventListener('click', (e) => {
if (e.target.closest('.tbdm-switch')) return;
const checkbox = row.querySelector('input[type="checkbox"]');
if (checkbox) checkbox.click();
});
});
}
function addSettingsButton() {
const titleElement = document.querySelector('#shoutbox-container .content-title h6.left');
if (titleElement && !document.querySelector("#tbdm-settings-btn")) {
const btn = document.createElement("span");
btn.id = "tbdm-settings-btn";
btn.title = "Shoutbox Manager Settings";
btn.textContent = "⚙️";
btn.addEventListener("click", (e) => {
e.stopPropagation();
const modal = document.getElementById('tbdm-modal-wrapper');
modal.style.display = "flex";
});
titleElement.style.display = 'flex';
titleElement.style.alignItems = 'center';
titleElement.appendChild(btn);
}
}
// ============================================================================
// STYLES
// ============================================================================
GM_addStyle(`
/* Modal Wrapper with Glass Effect */
#tbdm-modal-wrapper {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
position: fixed;
z-index: 99999;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.75);
display: none;
justify-content: center;
align-items: center;
backdrop-filter: blur(8px);
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
#tbdm-container {
background: linear-gradient(145deg, #1a1a2e 0%, #16213e 100%);
color: #e0e0e0;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 16px;
width: 90%;
max-width: 450px;
box-shadow: 0 20px 60px rgba(0,0,0,0.6), 0 0 100px rgba(20,167,108,0.15);
display: flex;
flex-direction: column;
max-height: 80vh;
overflow: hidden;
animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}
/* Header with Gradient */
#tbdm-header {
padding: 20px 20px 16px;
text-align: center;
position: relative;
background: linear-gradient(135deg, rgba(20,167,108,0.15) 0%, rgba(59,130,246,0.1) 100%);
border-bottom: 1px solid rgba(255,255,255,0.1);
}
#tbdm-header h1 {
font-size: 22px;
font-weight: 800;
background: linear-gradient(135deg, #14a76c 0%, #3b82f6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0 0 4px 0;
letter-spacing: -0.5px;
}
#tbdm-header p {
font-size: 12px;
color: #9ca3af;
margin: 0;
font-weight: 400;
}
#tbdm-close-btn {
position: absolute;
top: 10px;
right: 10px;
border: none;
background: transparent;
color: #6b7280;
cursor: pointer;
font-size: 24px;
font-weight: 400;
transition: color .2s;
padding: 0;
margin: 0;
line-height: 24px;
width: 24px;
height: 24px;
display: inline-block;
text-align: center;
}
#tbdm-close-btn:hover {
color: #ef4444;
}
/* Modern Tabs */
#tbdm-tabs {
display: flex;
padding: 0 14px;
gap: 4px;
background: rgba(0,0,0,0.2);
border-bottom: 1px solid rgba(255,255,255,0.05);
overflow-x: auto;
flex-shrink: 0;
}
#tbdm-tabs::-webkit-scrollbar { height: 3px; }
#tbdm-tabs::-webkit-scrollbar-track { background: transparent; }
#tbdm-tabs::-webkit-scrollbar-thumb { background: rgba(20,167,108,0.3); border-radius: 10px; }
.tbdm-tab {
background: none;
border: none;
color: #6b7280;
padding: 10px 14px;
cursor: pointer;
font-size: 12px;
font-weight: 600;
border-bottom: 3px solid transparent;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
white-space: nowrap;
position: relative;
}
.tbdm-tab:hover {
color: #d1d5db;
background: rgba(255,255,255,0.03);
}
.tbdm-tab.active {
color: #14a76c;
border-bottom-color: #14a76c;
background: rgba(20,167,108,0.08);
}
.tbdm-tab.active::after {
content: '';
position: absolute;
bottom: -3px;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #14a76c, #3b82f6);
box-shadow: 0 0 10px rgba(20,167,108,0.5);
}
/* Content Area */
#tbdm-content {
padding: 16px;
overflow-y: auto;
flex: 1;
background: linear-gradient(to bottom, rgba(0,0,0,0.1) 0%, transparent 100%);
min-height: 0;
}
#tbdm-content::-webkit-scrollbar { width: 6px; }
#tbdm-content::-webkit-scrollbar-track { background: rgba(0,0,0,0.2); border-radius: 10px; }
#tbdm-content::-webkit-scrollbar-thumb { background: rgba(20,167,108,0.3); border-radius: 10px; }
#tbdm-content::-webkit-scrollbar-thumb:hover { background: rgba(20,167,108,0.5); }
.tbdm-tab-content { display: none; animation: fadeInContent 0.3s ease; }
.tbdm-tab-content.active { display: block; }
@keyframes fadeInContent { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
/* Feature Cards - New Modern Style */
.tbdm-feature-card {
background: linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 10px;
padding: 12px 14px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.tbdm-feature-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(20,167,108,0.1) 0%, rgba(59,130,246,0.1) 100%);
opacity: 0;
transition: opacity 0.3s;
}
.tbdm-feature-card:hover {
background: linear-gradient(135deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.04) 100%);
border-color: rgba(20,167,108,0.3);
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(0,0,0,0.3), 0 0 25px rgba(20,167,108,0.12);
}
.tbdm-feature-card:hover::before { opacity: 1; }
.tbdm-feature-header {
display: flex;
align-items: center;
gap: 10px;
flex: 1;
position: relative;
z-index: 1;
}
.tbdm-feature-icon {
width: 36px;
height: 36px;
border-radius: 8px;
background: linear-gradient(135deg, rgba(20,167,108,0.2) 0%, rgba(59,130,246,0.2) 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
box-shadow: 0 3px 10px rgba(0,0,0,0.2);
}
.tbdm-feature-info {
flex: 1;
}
.tbdm-feature-title {
font-size: 13px;
font-weight: 600;
color: #f3f4f6;
margin-bottom: 2px;
}
.tbdm-feature-desc {
font-size: 11px;
color: #9ca3af;
font-weight: 400;
}
/* Sections */
.tbdm-section {
margin-bottom: 16px;
}
.tbdm-section-header {
font-size: 11px;
text-transform: uppercase;
color: #14a76c;
margin: 0 0 10px 0;
letter-spacing: 0.8px;
font-weight: 700;
display: flex;
align-items: center;
gap: 6px;
}
.tbdm-divider {
border: none;
border-top: 1px solid rgba(255,255,255,0.08);
margin: 14px 0;
box-shadow: 0 1px 0 rgba(0,0,0,0.2);
}
/* Settings Row - Cleaner Style */
.tbdm-setting-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: rgba(255,255,255,0.03);
border-radius: 8px;
margin-bottom: 6px;
border: 1px solid rgba(255,255,255,0.05);
transition: all 0.2s;
}
.tbdm-setting-row:hover {
background: rgba(255,255,255,0.05);
border-color: rgba(20,167,108,0.2);
}
.tbdm-setting-row label {
font-size: 13px !important;
margin-bottom: 0 !important;
}
.tbdm-master-switch {
padding: 12px 16px;
background: linear-gradient(135deg, rgba(20,167,108,0.12) 0%, rgba(59,130,246,0.08) 100%);
border: 1px solid rgba(20,167,108,0.3);
margin-bottom: 10px;
}
.tbdm-master-switch label:first-child {
font-size: 15px !important;
font-weight: 700;
color: #fff;
}
/* Compact Switch for Settings Rows */
.tbdm-setting-row .tbdm-switch {
position: relative;
display: inline-block;
width: 42px;
height: 24px;
flex-shrink: 0;
cursor: pointer;
}
.tbdm-setting-row .tbdm-switch input { opacity: 0; width: 0; height: 0; }
.tbdm-setting-row .tbdm-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #374151 0%, #1f2937 100%);
transition: .4s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 24px;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.3);
}
.tbdm-setting-row .tbdm-slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background: linear-gradient(135deg, #f3f4f6 0%, #d1d5db 100%);
transition: .4s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
}
.tbdm-setting-row input:checked + .tbdm-slider {
background: linear-gradient(135deg, #14a76c 0%, #10b981 100%);
box-shadow: 0 0 15px rgba(20,167,108,0.4), inset 0 1px 3px rgba(255,255,255,0.2);
}
.tbdm-setting-row input:checked + .tbdm-slider:before {
transform: translateX(18px);
background: linear-gradient(135deg, #ffffff 0%, #f3f4f6 100%);
}
/* Modern Switch for Master and Feature Cards */
.tbdm-master-switch .tbdm-switch,
.tbdm-feature-card .tbdm-switch {
position: relative;
display: inline-block;
width: 50px;
height: 28px;
flex-shrink: 0;
cursor: pointer;
}
.tbdm-master-switch .tbdm-switch input,
.tbdm-feature-card .tbdm-switch input { opacity: 0; width: 0; height: 0; }
.tbdm-master-switch .tbdm-slider,
.tbdm-feature-card .tbdm-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #374151 0%, #1f2937 100%);
transition: .4s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 28px;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.3);
}
.tbdm-master-switch .tbdm-slider:before,
.tbdm-feature-card .tbdm-slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 3px;
bottom: 3px;
background: linear-gradient(135deg, #f3f4f6 0%, #d1d5db 100%);
transition: .4s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
}
.tbdm-master-switch input:checked + .tbdm-slider,
.tbdm-feature-card input:checked + .tbdm-slider {
background: linear-gradient(135deg, #14a76c 0%, #10b981 100%);
box-shadow: 0 0 20px rgba(20,167,108,0.4), inset 0 1px 3px rgba(255,255,255,0.2);
}
.tbdm-master-switch input:checked + .tbdm-slider:before,
.tbdm-feature-card input:checked + .tbdm-slider:before {
transform: translateX(22px);
background: linear-gradient(135deg, #ffffff 0%, #f3f4f6 100%);
}
/* Form Elements with Glow */
.tbdm-form-group { margin-bottom: 16px; }
#tbdm-content label {
font-weight: 600;
color: #d1d5db;
font-size: 12px;
display: block;
margin-bottom: 6px;
letter-spacing: 0.3px;
}
.tbdm-label-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
}
textarea {
width: 100%;
height: 80px;
resize: vertical;
background: rgba(0,0,0,0.3);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 10px;
padding: 10px 12px;
font-size: 13px;
box-sizing: border-box;
color: #f3f4f6;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
transition: all 0.3s;
}
textarea:focus {
outline: none;
border-color: #14a76c;
background: rgba(0,0,0,0.4);
box-shadow: 0 0 0 3px rgba(20,167,108,0.15), 0 0 20px rgba(20,167,108,0.1);
}
textarea:disabled { opacity: 0.4; cursor: not-allowed; }
#tbdm-username-display {
background: rgba(20,167,108,0.12);
border: 1px solid rgba(20,167,108,0.3);
border-radius: 10px;
color: #14a76c;
padding: 12px 14px;
font-size: 14px;
font-weight: 600;
box-sizing: border-box;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
}
#tbdm-keywords { height: 100px; }
#tbdm-blocked-user-ids { height: 80px; }
#tbdm-blocked-keywords { height: 80px; }
/* Color Picker with Preview */
.tbdm-controls-row { display: grid; grid-template-columns: 1fr 1.5fr; gap: 12px; }
#tbdm-color-picker-wrapper {
position: relative;
width: 100%;
height: 44px;
border: 2px solid rgba(255,255,255,0.1);
border-radius: 10px;
overflow: hidden;
box-shadow: inset 0 2px 8px rgba(0,0,0,0.3), 0 4px 12px rgba(0,0,0,0.2);
transition: all 0.3s;
}
#tbdm-color-picker-wrapper:hover {
border-color: rgba(20,167,108,0.4);
box-shadow: inset 0 2px 8px rgba(0,0,0,0.3), 0 0 20px rgba(20,167,108,0.2);
}
#tbdm-highlight-color {
position: absolute;
top: -5px;
left: -5px;
width: calc(100% + 10px);
height: calc(100% + 10px);
border: none;
padding: 0;
cursor: pointer;
}
/* Volume Control */
.tbdm-volume-control {
display: flex;
align-items: center;
gap: 10px;
height: 44px;
background: rgba(0,0,0,0.3);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 10px;
padding: 0 14px;
box-sizing: border-box;
transition: all 0.3s;
}
.tbdm-volume-control:hover {
border-color: rgba(20,167,108,0.3);
background: rgba(0,0,0,0.4);
}
#tbdm-volume-icon { color: #14a76c; width: 20px; height: 20px; flex-shrink: 0; }
#tbdm-volume {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 5px;
background: linear-gradient(to right, #14a76c 0%, #374151 0%);
border-radius: 5px;
outline: none;
transition: all 0.3s;
}
#tbdm-volume::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
background: linear-gradient(135deg, #14a76c 0%, #10b981 100%);
border-radius: 50%;
cursor: pointer;
box-shadow: 0 2px 6px rgba(20,167,108,0.4), 0 0 0 3px rgba(20,167,108,0.15);
transition: all 0.2s;
margin-top: -5.5px;
}
#tbdm-volume::-webkit-slider-thumb:hover {
transform: scale(1.2);
box-shadow: 0 3px 10px rgba(20,167,108,0.6), 0 0 0 5px rgba(20,167,108,0.2);
}
#tbdm-volume::-moz-range-thumb {
width: 16px;
height: 16px;
background: linear-gradient(135deg, #14a76c 0%, #10b981 100%);
border-radius: 50%;
cursor: pointer;
border: none;
box-shadow: 0 2px 6px rgba(20,167,108,0.4);
}
#tbdm-volume::-webkit-slider-runnable-track {
height: 5px;
border-radius: 5px;
}
#tbdm-volume::-moz-range-track {
height: 5px;
border-radius: 5px;
}
/* Buttons with Gradients */
button { font-family: inherit; }
#tbdm-reset-keywords, .tbdm-secondary-btn {
background: linear-gradient(135deg, rgba(239,68,68,0.15) 0%, rgba(220,38,38,0.1) 100%);
border: 1px solid rgba(239,68,68,0.3);
color: #f87171;
font-size: 11px;
font-weight: 600;
cursor: pointer;
padding: 6px 10px;
border-radius: 6px;
transition: all 0.3s;
white-space: nowrap;
}
#tbdm-reset-keywords:hover, .tbdm-secondary-btn:hover {
background: linear-gradient(135deg, rgba(239,68,68,0.25) 0%, rgba(220,38,38,0.15) 100%);
border-color: rgba(239,68,68,0.5);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(239,68,68,0.2);
}
.tbdm-primary-btn {
background: linear-gradient(135deg, #14a76c 0%, #10b981 100%);
border: none;
color: #fff;
padding: 10px 16px;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
width: 100%;
margin-top: 10px;
transition: all 0.3s;
box-shadow: 0 4px 12px rgba(20,167,108,0.3);
}
.tbdm-primary-btn:hover {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(20,167,108,0.4);
}
.tbdm-primary-btn:active {
transform: translateY(0);
}
/* BBCode Warning Animation */
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
/* Mappings */
.tbdm-mapping-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
gap: 10px;
}
.tbdm-help-text {
font-size: 12px;
color: #9ca3af;
margin: 0;
padding: 8px 12px;
background: rgba(59,130,246,0.08);
border-left: 3px solid #3b82f6;
border-radius: 6px;
flex: 1;
}
#tbdm-mappings-container {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 10px;
max-height: 250px;
overflow-y: auto;
padding: 2px;
}
#tbdm-mappings-container::-webkit-scrollbar { width: 5px; }
#tbdm-mappings-container::-webkit-scrollbar-track { background: rgba(0,0,0,0.2); border-radius: 10px; }
#tbdm-mappings-container::-webkit-scrollbar-thumb { background: rgba(20,167,108,0.3); border-radius: 10px; }
.tbdm-mapping-row {
display: flex;
align-items: center;
gap: 6px;
background: rgba(255,255,255,0.03);
padding: 4px 6px;
border-radius: 6px;
border: 1px solid rgba(255,255,255,0.05);
transition: all 0.2s;
}
.tbdm-mapping-row:hover {
background: rgba(255,255,255,0.05);
border-color: rgba(20,167,108,0.2);
}
.tbdm-mapping-key, .tbdm-mapping-value {
flex: 1;
background: rgba(0,0,0,0.3);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 5px;
padding: 6px 8px;
font-size: 12px;
color: #f3f4f6;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
transition: all 0.2s;
}
.tbdm-mapping-key:focus, .tbdm-mapping-value:focus {
outline: none;
border-color: #14a76c;
background: rgba(0,0,0,0.4);
box-shadow: 0 0 0 2px rgba(20,167,108,0.1);
}
.tbdm-mapping-row span {
color: #6b7280;
font-weight: bold;
font-size: 12px;
}
.tbdm-delete-mapping {
background: rgba(239,68,68,0.1);
border: 1px solid rgba(239,68,68,0.2);
color: #f87171;
font-size: 16px;
font-weight: bold;
cursor: pointer;
width: 26px;
height: 26px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
transition: all 0.3s;
flex-shrink: 0;
}
.tbdm-delete-mapping:hover {
background: rgba(239,68,68,0.2);
border-color: rgba(239,68,68,0.4);
transform: scale(1.1) rotate(90deg);
}
/* GIF Tool Modal Styles */
#gif-tool-modal {
position: fixed;
width: 440px;
max-width: 90vw;
background: #2f3136;
border: 1px solid #444;
border-radius: 8px;
z-index: 2147483647;
display: none;
box-shadow: 0 10px 30px rgba(0,0,0,0.8);
overflow: hidden;
flex-direction: column;
top: auto;
}
#gif-tool-header {
height: 24px;
background: #202225;
cursor: grab;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8px;
border-bottom: 1px solid #36393f;
flex-shrink: 0;
}
#gif-tool-header:active { cursor: grabbing; background: #18191c; }
#gif-tool-title {
font-size: 11px;
color: #aaa;
font-weight: bold;
user-select: none;
text-transform: uppercase;
letter-spacing: 0.5px;
}
#gif-tool-content {
padding: 8px;
display: flex;
flex-direction: column;
gap: 8px;
}
#gif-tool-results {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px,1fr));
gap: 6px;
max-height: 320px;
min-height: 100px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #202225 #2f3136;
}
#gif-tool-results::-webkit-scrollbar { width: 8px; }
#gif-tool-results::-webkit-scrollbar-track { background: #2f3136; }
#gif-tool-results::-webkit-scrollbar-thumb { background-color: #202225; border-radius: 4px; }
#gif-tool-results img {
width:100%;
height:80px;
object-fit:cover;
cursor:pointer;
border-radius:4px;
transition:all .12s;
border:2px solid transparent;
background: #202225;
}
#gif-tool-results img:hover {
transform:scale(1.04);
border-color:#00bfff;
z-index: 1;
}
#gif-tool-controls {
display:flex;
gap:8px;
align-items:center;
flex-shrink: 0;
}
#gif-tool-search {
flex:1;
padding:6px 10px;
background:#40444b;
border:1px solid #555;
color:#fff;
border-radius:6px;
font-size:13px;
outline:none;
}
#gif-tool-search:focus { border-color: #00bfff; }
#gif-tool-close {
background:none;
border:none;
color:#aaa;
font-size: 18px;
cursor:pointer;
line-height: 1;
padding: 0;
}
#gif-tool-close:hover { color:#fff; }
#tbdm-gif-tool-btn {
display: inline-flex !important;
align-items: center;
justify-content: center;
height: 28px;
width: 28px;
margin-left: 4px;
margin-right: 2px;
cursor: pointer;
color: #ccc;
transition: color 0.1s, transform 0.1s;
}
#tbdm-gif-tool-btn svg {
position: relative;
top: 1px;
}
#tbdm-gif-tool-btn svg { display: block; }
#tbdm-gif-tool-btn:hover { color: #fff; transform: translateY(-1px); }
/* Settings Button */
#tbdm-settings-btn {
cursor: pointer;
margin-left: 10px;
font-size: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
transition: all .3s ease;
opacity: 0.7;
}
#tbdm-settings-btn:hover {
opacity: 1;
transform: rotate(90deg) scale(1.1);
}
/* Easy Mention Styles */
.shout-user { user-select: none; }
.chromium span.shout-time:has(+ .shout-user [href^="account"]) { cursor: pointer; position: relative; }
.chromium span.shout-time:has(+ .shout-user [href^="account"]):hover { margin-left: -1.5em; }
.chromium span.shout-time:has(+ .shout-user [href^="account"])::after { content: ""; margin-left: .5em; height: 1em; rotate: 180deg; display: none; width: 1em; }
.chromium span.shout-time:has(+ .shout-user [href^="account"]):hover::after { display: inline-block; }
.firefox span.shout-time { cursor: pointer; position: relative; }
.firefox span.shout-time:hover { margin-left: -1.5em; }
.firefox span.shout-time::after { content: ""; margin-left: .5em; height: 1em; rotate: 180deg; display: none; width: 1em; }
.firefox span.shout-time:hover::after { display: inline-block; }
.dark-scheme.chromium span.shout-time:has(+ .shout-user [href^="account"])::after { background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M205 34.8c11.5 5.1 19 16.6 19 29.2v64H336c97.2 0 176 78.8 176 176c0 113.3-81.5 163.9-100.2 174.1c-2.5 1.4-5.3 1.9-8.1 1.9c-10.9 0-19.7-8.9-19.7-19.7c0-7.5 4.3-14.4 9.8-19.5c9.4-8.8 22.2-26.4 22.2-56.7c0-53-43-96-96-96H224v64c0 12.6-7.4 24.1-19 29.2s-25 3-34.4-5.4l-160-144C3.9 225.7 0 217.1 0 208s3.9-17.7 10.6-23.8l160-144c9.4-8.5 22.9-10.6 34.4-5.4z" style="fill: rgb(238, 238, 238);"/></svg>'); }
.light-scheme.chromium span.shout-time:has(+ .shout-user [href^="account"])::after { background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M205 34.8c11.5 5.1 19 16.6 19 29.2v64H336c97.2 0 176 78.8 176 176c0 113.3-81.5 163.9-100.2 174.1c-2.5 1.4-5.3 1.9-8.1 1.9c-10.9 0-19.7-8.9-19.7-19.7c0-7.5 4.3-14.4 9.8-19.5c9.4-8.8 22.2-26.4 22.2-56.7c0-53-43-96-96-96H224v64c0 12.6-7.4 24.1-19 29.2s-25 3-34.4-5.4l-160-144C3.9 225.7 0 217.1 0 208s3.9-17.7 10.6-23.8l160-144c9.4-8.5 22.9-10.6 34.4-5.4z" style="fill: rgb(68 ,68, 68);"/></svg>'); }
.dark-scheme.firefox span.shout-time::after { background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M205 34.8c11.5 5.1 19 16.6 19 29.2v64H336c97.2 0 176 78.8 176 176c0 113.3-81.5 163.9-100.2 174.1c-2.5 1.4-5.3 1.9-8.1 1.9c-10.9 0-19.7-8.9-19.7-19.7c0-7.5 4.3-14.4 9.8-19.5c9.4-8.8 22.2-26.4 22.2-56.7c0-53-43-96-96-96H224v64c0 12.6-7.4 24.1-19 29.2s-25 3-34.4-5.4l-160-144C3.9 225.7 0 217.1 0 208s3.9-17.7 10.6-23.8l160-144c9.4-8.5 22.9-10.6 34.4-5.4z" style="fill: rgb(238, 238, 238);"/></svg>'); }
.light-scheme.firefox span.shout-time::after { background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M205 34.8c11.5 5.1 19 16.6 19 29.2v64H336c97.2 0 176 78.8 176 176c0 113.3-81.5 163.9-100.2 174.1c-2.5 1.4-5.3 1.9-8.1 1.9c-10.9 0-19.7-8.9-19.7-19.7c0-7.5 4.3-14.4 9.8-19.5c9.4-8.8 22.2-26.4 22.2-56.7c0-53-43-96-96-96H224v64c0 12.6-7.4 24.1-19 29.2s-25 3-34.4-5.4l-160-144C3.9 225.7 0 217.1 0 208s3.9-17.7 10.6-23.8l160-144c9.4-8.5 22.9-10.6 34.4-5.4z" style="fill: rgb(68 ,68, 68);"/></svg>'); }
/* URL Sender Styles */
#shout-ibb-container {
display: flex;
gap: 0 .5rem;
margin-right: 0;
padding-right: .5rem;
right: 0;
position: absolute;
}
/* Keep history button always on the right */
#shoutHistoryBtn {
margin-left: auto !important;
order: 999 !important;
}
#shout-send-container { position: relative; }
#urlWindow { position: absolute; width: 100%; left: 0; flex-flow: wrap; bottom: 7px; gap: 0.5rem; display: flex; transition: visibility 0s linear .2s, opacity .1s linear .1s, translate .2s linear; visibility: hidden; background: var(--main-bg); border-top: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); padding-top: 0.5rem; padding-bottom: 0.5rem; opacity: 0; }
.spotlight #urlWindow { bottom: 28px; }
#urlWindow.show { visibility: visible; translate: 0 -30px; transition-delay: 0s; opacity: 1; }
.url-inputs { height: 37px; color: var(--text-color) !important; background: var(--main-bg); padding: 0px .5rem; border-top:1px solid var(--border-color); border-bottom:1px solid var(--border-color); border-left: none; border-right: none; outline: 0; }
.spotlight .url-inputs { height: 60px; }
#urlField { flex: 1 1 100%; }
#labelField { flex: 4 1 auto; border-right: 1px solid var(--border-color); }
#submitURL { flex: 1 1 auto; background: transparent; border: 1px solid var(--border-color); border-right: none; color: var(--text-color); font-weight: 600; font-size: 0.9rem; cursor: pointer; }
#urlBtn i { line-height: 37px; font-size: 0.9rem; font-weight: 600; font-family: inherit; user-select: none; }
input.shoutbox-text { width: auto !important; }
.spotlight input#shout_text { padding-left: .5rem; padding-right: calc(220px - .5rem); }
@media(max-width: 767px) {
.spotlight input#shout_text { padding-right: calc(200px - .5rem); }
.spotlight #shout-ibb-container { padding-right: .5rem; }
#tbdm-container { width: 95%; max-width: 95%; border-radius: 16px; }
#tbdm-tabs { padding: 0 12px; gap: 4px; }
.tbdm-tab { padding: 12px 14px; font-size: 13px; }
.tbdm-controls-row { grid-template-columns: 1fr; }
}
/* Mobile Improvements */
input#shout_text.shoutbox-text { -webkit-text-size-adjust: 100%; }
input#shout_text { autocapitalize: on; }
`);
// ============================================================================
// INITIALIZATION
// ============================================================================
let scriptInitialized = false;
function updateShoutInputPadding() {
const input = document.querySelector('#shout_text');
const tray = document.querySelector('#shout-ibb-container');
if (!input || !tray) return;
const buttonIds = [
'tbdm-image-upload-btn',
'tbdm-gif-tool-btn',
'meme-tool-btn',
'emoji-toggle-btn',
'urlBtn'
];
const paddingMap = { 1: 125, 2: 160, 3: 195, 4: 230, 5: 260 };
const count = buttonIds.filter(id => document.getElementById(id)).length;
const padding = paddingMap[count] || 125;
input.style.paddingRight = padding + 'px';
}
function watchTrayForPaddingUpdates() {
const tray = document.querySelector('#shout-ibb-container');
if (!tray) return;
const observer = new MutationObserver(() => updateShoutInputPadding());
observer.observe(tray, { childList: true, subtree: true });
}
function waitForElement(selector, callback, maxRetries = 100) {
let retries = 0;
const checkInterval = setInterval(() => {
const element = document.querySelector(selector);
if (element) {
clearInterval(checkInterval);
callback(element);
}
retries++;
if (retries > maxRetries) clearInterval(checkInterval);
}, 100);
}
function initializeScript() {
if (scriptInitialized) return;
const shoutboxContainer = document.querySelector('#shoutbox-container');
if (!shoutboxContainer) {
setTimeout(initializeScript, 500);
return;
}
scriptInitialized = true;
if (CONFIG.cleaner_enabled) CleanerModule.init();
if (CONFIG.notifier_enabled) NotifierModule.init();
if (CONFIG.image_upload_enabled) ImageUploadModule.init();
if (CONFIG.easy_mention_enabled) EasyMentionModule.init();
if (CONFIG.url_sender_enabled) URLSenderModule.init();
if (CONFIG.autocomplete_username_enabled || CONFIG.autocomplete_sticker_enabled) AutocompleteModule.init();
if (CONFIG.focus_lock_enabled) FocusLockModule.init();
if (CONFIG.idle_prevention_enabled) IdlePreventionModule.init();
if (CONFIG.gif_picker_enabled) GifPickerModule.init();
if (CONFIG.image_viewer_enabled) ImageViewerModule.init();
if (CONFIG.meme_creator_enabled) MemeCreatorModule.init();
BBCodeValidatorModule.init();
InfiniteScrollModule.init();
setTimeout(() => {
updateShoutInputPadding();
watchTrayForPaddingUpdates();
}, 200);
UIImprovementsModule.init();
if (CONFIG.unicode_emoji_enabled) UnicodeEmojiModule.init();
createSettingsUI();
waitForElement('#shoutbox-container .content-title h6.left', addSettingsButton);
const shoutInput = document.querySelector("#shout_text");
if (shoutInput) shoutInput.setAttribute("autocapitalize", "on");
const viewport = document.querySelector("meta[name='viewport']");
if (viewport) {
viewport.setAttribute("content", "width=device-width, height=device-height, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no");
}
}
setTimeout(initializeScript, 1000);
setTimeout(initializeScript, 3000);
if (document.readyState !== 'loading') {
initializeScript();
} else {
document.addEventListener('DOMContentLoaded', initializeScript);
}
})();