// ==UserScript==
// @name Reddit Emoji Picker
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Reddit Emoji Picker for the baj
// @author Baj
// @license MIT
// @match https://*.reddit.com/*
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// List emojis and codes
const emojiList = [
{ code: '[emote:t5_33td5:59888](http://img)', name: 'Clueless', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Clueless.png' },
{ code: '[emote:t5_33td5:59889](http://img)', name: 'Okayeg', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Okayeg.png' },
{ code: '[emote:t5_33td5:59890](http://img)', name: 'cmonBruh', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/cmonBruh.png' },
{ code: '[emote:t5_33td5:59891](http://img)', name: 'Copesen', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Copesen.png' },
{ code: '[emote:t5_33td5:59892](http://img)', name: 'forsenCD', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/forsenCD.png' },
{ code: '[emote:t5_33td5:59893](http://img)', name: 'forsenE', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/forsenE.png' },
{ code: '[emote:t5_33td5:59894](http://img)', name: 'tf', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/tf.png' },
{ code: '[emote:t5_33td5:59895](http://img)', name: 'FeelsOkayMan', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/FeelsOkayMan.png' },
{ code: '[emote:t5_33td5:59896](http://img)', name: 'gachiGASM', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/gachiGASM.png' },
{ code: '[emote:t5_33td5:59897](http://img)', name: 'monkaOMEGA', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/monkaOMEGA.png' },
{ code: '[emote:t5_33td5:59898](http://img)', name: 'LULE', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/LULE.png' },
{ code: '[emote:t5_33td5:59899](http://img)', name: 'OMEGALUL', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/OMEGALUL.png' },
{ code: '[emote:t5_33td5:59900](http://img)', name: 'PagMan', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/PagMan.png' },
{ code: '[emote:t5_33td5:59901](http://img)', name: 'forsenBased', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/forsenBased.png' },
{ code: '[emote:t5_33td5:59902](http://img)', name: 'Sadeg', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Sadeg.png' },
{ code: '[emote:t5_33td5:59903](http://img)', name: 'forsenAlright', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/forsenAlright.png' },
{ code: '[emote:t5_33td5:59904](http://img)', name: 'forsenLevel', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/forsenLevel.png' },
{ code: '[emote:t5_33td5:59905](http://img)', name: 'pepeLaugh', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/pepeLaugh.png' },
{ code: '[emote:t5_33td5:59906](http://img)', name: 'forsenDespair', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/forsenDespair.png' },
{ code: '[emote:t5_33td5:53915](http://img)', name: 'sadE', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/sadE.png' },
{ code: '[emote:t5_33td5:53373](http://img)', name: 'forsenMaxLevel', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/forsenMaxLevel.png' },
{ code: '[emote:t5_33td5:9486](http://img)', name: 'maoE', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/maoE.png' },
{ code: '[emote:t5_33td5:9683](http://img)', name: 'Wutface', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Wutface.png' },
{ code: '[emote:t5_33td5:5322](http://img)', name: 'MegaLUL', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/MegaLUL.png' },
{ code: '[emote:t5_33td5:5359](http://img)', name: 'Docsen', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Docsen.png' },
{ code: '[emote:t5_33td5:10257](http://img)', name: 'amongE', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/amongE.png' },
{ code: '[emote:t5_33td5:9671](http://img)', name: 'Batchest', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Batchest.png' },
{ code: '[emote:t5_33td5:53371](http://img)', name: 'forsenNugget', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/forsenNugget.png' },
{ code: '[emote:t5_33td5:53390](http://img)', name: 'LongHairMaxLevel', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/LongHairMaxLevel.png' },
{ code: '[emote:t5_33td5:54526](http://img)', name: 'Pewds', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Pewds.png' },
{ code: '[emote:t5_33td5:53563](http://img)', name: 'monkaLaugh', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/monkaLaugh.png' },
{ code: '[emote:t5_33td5:55418](http://img)', name: 'muv', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/muv.png' },
{ code: '[emote:t5_33td5:55894](http://img)', name: 'snowman', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/snowman.png' },
{ code: '[emote:t5_33td5:55905](http://img)', name: 'bbangrE', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/bbangrE.png' },
{ code: '[emote:t5_33td5:56633](http://img)', name: 'WeebsOut', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/WeebsOut.png' },
{ code: '[emote:t5_33td5:56664](http://img)', name: 'freakE', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/freakE.png' },
{ code: '[emote:t5_33td5:57649](http://img)', name: 'emiru', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/emiru.png' },
{ code: '[emote:t5_33td5:58897](http://img)', name: 'wahhabi', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/wahhabi.png' },
{ code: '[emote:t5_33td5:58898](http://img)', name: 'thisIsYouOnIslam', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/thisIsYouOnIslam.png' },
{ code: '[emote:t5_33td5:58899](http://img)', name: 'familyPhoto', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/familyPhoto.png' },
{ code: '[emote:t5_33td5:58900](http://img)', name: 'sheikh', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/sheikh.png' },
{ code: '[emote:t5_33td5:58901](http://img)', name: 'Stoning', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Stoning.png' },
{ code: '[emote:t5_33td5:58913](http://img)', name: 'turban', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/turban.png' },
{ code: '[emote:t5_33td5:58914](http://img)', name: 'quranPepe', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/quranPepe.png' },
{ code: '[emote:t5_33td5:58948](http://img)', name: 'desertLevel', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/desertLevel.png' },
{ code: '[emote:t5_33td5:58949](http://img)', name: 'hesHalal', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/hesHalal.png' },
{ code: '[emote:t5_33td5:59909](http://img)', name: 'Tomfoolery', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/Tomfoolery.png' },
{ code: '[emote:t5_33td5:60326](http://img)', name: 'salute', url: 'https://raw.githubusercontent.com/rarestemotes/emotes/main/images/salute.png' }
];
const defaultConfig = { autoSwitchToMarkdown: true };
let config = GM_getValue('redditEmojiPickerConfig', defaultConfig);
let showFormattingClicked = false;
let switchToMarkdownClicked = false;
let markdownObserver = null;
const processedEditors = new Map();
const styles = `
.emoji-picker-button {
background-color: transparent;
color: #606060;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
margin-right: 8px;
transition: background-color 0.2s;
}
.emoji-picker-button:hover { background-color: rgba(0, 0, 0, 0.1); }
.emoji-picker-button svg { width: 24px; height: 24px; fill: currentColor; }
.emoji-picker-container {
position: absolute;
background-color: #1a1a1b;
border: 1px solid #343536;
border-radius: 8px;
padding: 12px;
max-width: 400px;
max-height: 300px;
overflow-y: auto;
z-index: 1001;
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
.emoji-item {
width: 32px;
height: 32px;
cursor: pointer;
border-radius: 4px;
padding: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
}
.emoji-item:hover { background-color: #343536; }
.emoji-item img { width: 100%; height: 100%; object-fit: contain; }
.emoji-config-panel {
position: absolute;
background-color: #1a1a1b;
border: 1px solid #343536;
border-radius: 8px;
padding: 12px;
width: 250px;
z-index: 1001;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
display: none;
}
.emoji-config-title { font-size: 16px; font-weight: bold; margin-bottom: 12px; color: #d7dadc; }
.emoji-config-option { display: flex; align-items: center; margin-bottom: 8px; }
.emoji-config-option label { margin-left: 8px; color: #d7dadc; flex-grow: 1; }
.emoji-config-button {
background-color: #0079d3;
color: white;
border: none;
border-radius: 4px;
padding: 6px 12px;
margin-top: 8px;
cursor: pointer;
font-size: 14px;
width: 100%;
}
.emoji-config-button:hover { background-color: #0061a9; }
.emoji-config-toggle {
background-color: transparent;
color: #606060;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
position: absolute;
top: 8px;
right: 8px;
transition: background-color 0.2s;
}
.emoji-config-toggle:hover { background-color: rgba(0, 0, 0, 0.1); }
.emoji-config-toggle svg { width: 16px; height: 16px; fill: currentColor; }
.emoji-picker-wrapper { position: relative; display: flex; align-items: center; margin: 8px 0; z-index: 999; }
.emoji-status-message {
position: absolute;
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
bottom: 100%;
left: 0;
margin-bottom: 5px;
display: none;
z-index: 1002;
}
`;
function injectStyles() {
const styleElement = document.createElement('style');
styleElement.textContent = styles;
document.head.appendChild(styleElement);
}
function createEmojiPickerButton() {
const button = document.createElement('button');
button.className = 'emoji-picker-button';
button.title = 'Show emoji picker';
button.setAttribute('aria-label', 'Show emoji picker');
button.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24" focusable="false" aria-hidden="true">
<path d="M15.83 15c-.52 1.38-2.19 2-3.79 2-1.59 0-3.28-.62-3.85-2h7.64m.69-1H7.49c-.27 0-.49.22-.46.47C7.34 16.83 9.7 18 12.05 18c2.35 0 4.69-1.18 4.93-3.54.03-.25-.2-.46-.46-.46zM12 3c4.96 0 9 4.04 9 9s-4.04 9-9 9-9-4.04-9-9 4.04-9 9-9m0-1C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM6.94 9.73C7.19 9.25 7.72 9 8.5 9c.75 0 1.28.25 1.57.75.14.24.45.32.68.18.24-.14.32-.44.18-.68C10.6 8.68 9.91 8 8.5 8c-1.48 0-2.15.69-2.44 1.27-.13.25-.03.55.21.67.07.04.15.06.23.06.18 0 .36-.1.44-.27zm7 0c.25-.48.78-.73 1.56-.73.75 0 1.28.25 1.57.75.14.24.45.32.68.18.24-.14.32-.44.18-.68C17.6 8.68 16.91 8 15.5 8c-1.48 0-2.15.69-2.44 1.27-.13.25-.03.55.21.67.07.04.15.06.23.06.18 0 .36-.1.44-.27z"></path>
</svg>
`;
button.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
toggleEmojiPicker(e);
}, true);
return button;
}
function createConfigButton() {
const button = document.createElement('button');
button.className = 'emoji-config-toggle';
button.title = 'Settings';
button.setAttribute('aria-label', 'Settings');
button.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"></path>
</svg>
`;
button.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
toggleConfigPanel(e);
}, true);
return button;
}
function createStatusMessage() {
const message = document.createElement('div');
message.className = 'emoji-status-message';
return message;
}
function showStatusMessage(wrapper, text, duration = 3000) {
const statusMessage = wrapper.querySelector('.emoji-status-message');
if (statusMessage) {
statusMessage.textContent = text;
statusMessage.style.display = 'block';
setTimeout(() => statusMessage.style.display = 'none', duration);
}
}
function createConfigPanel() {
const panel = document.createElement('div');
panel.className = 'emoji-config-panel';
panel.innerHTML = `
<div class="emoji-config-title">Emoji Picker Settings</div>
<div class="emoji-config-option">
<input type="checkbox" id="auto-switch-markdown" ${config.autoSwitchToMarkdown ? 'checked' : ''}>
<label for="auto-switch-markdown">Auto-switch to Markdown editor</label>
</div>
<button class="emoji-config-button" type="button">Save Settings</button>
`;
const saveButton = panel.querySelector('.emoji-config-button');
saveButton.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const checkbox = panel.querySelector('#auto-switch-markdown');
const oldConfig = { ...config };
config.autoSwitchToMarkdown = checkbox.checked;
GM_setValue('redditEmojiPickerConfig', config);
if (config.autoSwitchToMarkdown && !oldConfig.autoSwitchToMarkdown) startMarkdownObserver();
else if (!config.autoSwitchToMarkdown && oldConfig.autoSwitchToMarkdown) stopMarkdownObserver();
panel.style.display = 'none';
if (panel.parentNode?.classList.contains('emoji-picker-wrapper')) {
showStatusMessage(panel.parentNode, 'Settings saved!', 2000);
}
});
return panel;
}
function toggleConfigPanel(event) {
const button = event.currentTarget;
const panel = button.parentNode.querySelector('.emoji-config-panel');
const isHidden = panel.style.display === 'none' || panel.style.display === '';
document.querySelectorAll('.emoji-config-panel').forEach(p => p.style.display = 'none');
if (isHidden) panel.style.display = 'block';
}
function createEmojiPickerContainer(composerElement, wrapper) {
const container = document.createElement('div');
container.className = 'emoji-picker-container';
container.style.display = 'none';
emojiList.forEach(emoji => {
const item = document.createElement('div');
item.className = 'emoji-item';
item.title = emoji.name;
item.innerHTML = `<img src="${emoji.url}" alt="${emoji.name}">`;
item.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const form = composerElement.closest('faceplate-form');
const submitButton = form?.querySelector('shreddit-composer button[slot="submit-button"][type="submit"]') ||
composerElement.querySelector('button[slot="submit-button"][type="submit"]');
const wasDisabled = submitButton?.disabled ?? false;
if (submitButton) submitButton.disabled = true;
const finalize = (error) => {
if (submitButton && !wasDisabled) {
setTimeout(() => submitButton.disabled = false, error ? 100 : 300);
}
container.style.display = 'none';
};
(config.autoSwitchToMarkdown ? activateEditor(composerElement).then(() => new Promise(r => setTimeout(r, 300))) : activateEditor(composerElement))
.then(() => {
const target = composerElement.shadowRoot?.querySelector('shreddit-markdown-composer textarea') ||
composerElement.shadowRoot?.querySelector('[data-lexical-editor="true"]');
if (target) target.focus();
return new Promise(r => setTimeout(r, 50));
})
.then(() => insertEmoji(emoji.code, composerElement, wrapper))
.then(() => finalize(false))
.catch(() => {
showStatusMessage(wrapper, 'Error inserting emoji', 2000);
finalize(true);
});
}, true);
container.appendChild(item);
});
document.addEventListener('click', (e) => {
if (!container.contains(e.target) && !wrapper.querySelector('.emoji-picker-button').contains(e.target)) {
container.style.display = 'none';
}
}, false);
return container;
}
function toggleEmojiPicker(event) {
const button = event.currentTarget;
const container = button.parentNode.querySelector('.emoji-picker-container');
const isHidden = container.style.display === 'none' || container.style.display === '';
document.querySelectorAll('.emoji-picker-container').forEach(c => c.style.display = 'none');
document.querySelectorAll('.emoji-config-panel').forEach(p => p.style.display = 'none');
if (isHidden) container.style.display = 'grid';
}
function activateEditor(composerElement) {
return new Promise(resolve => {
if (!composerElement?.shadowRoot) return resolve();
const lexicalEditor = composerElement.shadowRoot.querySelector('[data-lexical-editor="true"]');
if (lexicalEditor) {
lexicalEditor.click();
lexicalEditor.focus();
return setTimeout(resolve, 100);
}
const markdownComposer = composerElement.shadowRoot.querySelector('shreddit-markdown-composer');
const textarea = markdownComposer?.shadowRoot?.querySelector('textarea');
if (textarea) {
textarea.click();
textarea.focus();
return setTimeout(resolve, 100);
}
const triggerButton = composerElement.shadowRoot.querySelector('[data-testid="trigger-button"]');
if (triggerButton) {
triggerButton.click();
return setTimeout(resolve, 300);
}
resolve();
});
}
function querySelectorAllDeep(selector, root = document) {
const results = Array.from(root.querySelectorAll(selector));
root.querySelectorAll('*').forEach(host => {
if (host.shadowRoot) results.push(...querySelectorAllDeep(selector, host.shadowRoot));
});
return results;
}
function checkAndClickMarkdownButtons(composerContext = document.body) {
if (!config.autoSwitchToMarkdown) return;
const searchRoot = composerContext.shadowRoot || composerContext;
if (!showFormattingClicked) {
const formattingBarConfigs = searchRoot === document.body ?
querySelectorAllDeep('data[data-key="formattingBar"][data-show-formatting-bar="false"]') :
Array.from(searchRoot.querySelectorAll('data[data-key="formattingBar"][data-show-formatting-bar="false"]'));
if (formattingBarConfigs.length) {
const buttons = searchRoot === document.body ?
querySelectorAllDeep('rte-toolbar-button') :
Array.from(searchRoot.querySelectorAll('rte-toolbar-button'));
for (const button of buttons) {
if (button.offsetParent && button.getAttribute('screenreadercontent') === 'Show formatting options') {
button.click();
showFormattingClicked = true;
setTimeout(() => checkMarkdownSwitch(searchRoot), 300);
break;
}
}
} else {
checkMarkdownSwitch(searchRoot);
}
}
}
function checkMarkdownSwitch(searchRoot) {
if (switchToMarkdownClicked) return;
const markdownButtons = searchRoot === document.body ?
querySelectorAllDeep('button[aria-label="Switch to Markdown Editor"]') :
Array.from(searchRoot.querySelectorAll('button[aria-label="Switch to Markdown Editor"]'));
if (markdownButtons.length && markdownButtons[0].offsetParent) {
markdownButtons[0].click();
switchToMarkdownClicked = true;
}
}
function detectAndProcessNewEditors() {
querySelectorAllDeep('shreddit-composer').forEach(composer => {
if (!processedEditors.has(composer)) {
processedEditors.set(composer, true);
activateEditor(composer).then(() => {
setTimeout(() => {
if (config.autoSwitchToMarkdown) {
showFormattingClicked = false;
switchToMarkdownClicked = false;
checkAndClickMarkdownButtons(composer);
}
}, 750);
});
}
});
processedEditors.forEach((_, key) => !document.contains(key) && processedEditors.delete(key));
}
function startMarkdownObserver() {
if (markdownObserver) return;
markdownObserver = new MutationObserver(() => {
checkAndClickMarkdownButtons();
detectAndProcessNewEditors();
});
markdownObserver.observe(document.body, { childList: true, subtree: true });
detectAndProcessNewEditors();
}
function stopMarkdownObserver() {
if (markdownObserver) {
markdownObserver.disconnect();
markdownObserver = null;
showFormattingClicked = false;
switchToMarkdownClicked = false;
processedEditors.clear();
}
}
function insertEmoji(emojiCode, composerElement, wrapper) {
if (!composerElement?.shadowRoot) return;
const markdownComposer = composerElement.shadowRoot.querySelector('shreddit-markdown-composer');
const textarea = markdownComposer?.shadowRoot?.querySelector('textarea');
if (textarea) {
textarea.focus();
const start = textarea.selectionStart;
textarea.value = textarea.value.substring(0, start) + emojiCode + ' ' + textarea.value.substring(textarea.selectionEnd);
const newPos = start + emojiCode.length + 1;
textarea.setSelectionRange(newPos, newPos);
textarea.dispatchEvent(new Event('input', { bubbles: true }));
textarea.dispatchEvent(new Event('change', { bubbles: true }));
showStatusMessage(wrapper, 'Emoji inserted!', 1000);
return;
}
const lexicalEditor = composerElement.shadowRoot.querySelector('[data-lexical-editor="true"]');
if (lexicalEditor) {
lexicalEditor.focus();
try {
document.execCommand('insertText', false, emojiCode + ' ');
showStatusMessage(wrapper, 'Emoji inserted!', 1000);
} catch {
const selection = window.getSelection();
const textNode = document.createTextNode(emojiCode + ' ');
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(textNode);
range.setStartAfter(textNode);
selection.removeAllRanges();
selection.addRange(range);
} else {
lexicalEditor.appendChild(textNode);
}
showStatusMessage(wrapper, 'Emoji inserted!', 1000);
}
}
}
function addEmojiPickerToCommentAreas() {
document.querySelectorAll('shreddit-composer:not([data-emoji-picker-added="true"])').forEach(composer => {
composer.setAttribute('data-emoji-picker-added', 'true');
const wrapper = document.createElement('div');
wrapper.className = 'emoji-picker-wrapper';
wrapper.appendChild(createEmojiPickerButton());
wrapper.appendChild(createStatusMessage());
wrapper.appendChild(createEmojiPickerContainer(composer, wrapper));
wrapper.appendChild(createConfigButton());
wrapper.appendChild(createConfigPanel());
composer.parentNode?.insertBefore(wrapper, composer);
});
}
function observeDOM() {
new MutationObserver((mutations) => {
if (mutations.some(m => Array.from(m.addedNodes).some(n => n.nodeType === Node.ELEMENT_NODE &&
(n.tagName === 'SHREDDIT-COMPOSER' || n.querySelector('shreddit-composer'))))) {
setTimeout(addEmojiPickerToCommentAreas, 500);
}
}).observe(document.body, { childList: true, subtree: true });
}
function init() {
injectStyles();
if (config.autoSwitchToMarkdown) startMarkdownObserver();
setTimeout(() => {
addEmojiPickerToCommentAreas();
observeDOM();
}, 1000);
setInterval(addEmojiPickerToCommentAreas, 3000);
}
document.readyState === 'complete' ? init() : window.addEventListener('load', init);
})();