// ==UserScript==
// @name Replace Dollar Signs in Flarum
// @namespace https://beiduofen.top/
// @version 0.2
// @description Replaces all $ with $ in textareas on beiduofen.top, fixing Flarum's editor issues
// @author Steper Lin
// @match *://*/*
// ==/UserScript==
(function() {
'use strict';
// Function to replace regular dollar signs with full-width dollar signs
function replaceDollarSigns(element) {
// Save cursor position
const isTextarea = element.tagName === 'TEXTAREA';
const cursorPosition = isTextarea ? element.selectionStart : null;
// Replace content
const text = isTextarea ? element.value : element.textContent;
const newText = text.replace(/$/g, '$');
// Only update if changes were made
if (text !== newText) {
if (isTextarea) {
element.value = newText;
// Dispatch input event to trigger Flarum's update mechanism
element.dispatchEvent(new Event('input', { bubbles: true }));
// Also dispatch change event for good measure
element.dispatchEvent(new Event('change', { bubbles: true }));
// Restore cursor position if needed
if (cursorPosition !== null) {
element.selectionStart = cursorPosition;
element.selectionEnd = cursorPosition;
}
} else {
element.textContent = newText;
}
}
}
// Handle key presses in the document to catch paste operations
document.addEventListener('keydown', function(e) {
// Check for paste operation (Ctrl+V or Command+V. if ((e.ctrlKey || e.metaKey) && e.key === 'v') {✅
// Give time for the paste to complete
setTimeout(function() {
processAllElements();
}, 100);
}
});
// Intercept paste event on textareas
function setupPasteListener(element) {
element.addEventListener('paste', function() {
// Process after paste completes
setTimeout(() => {
replaceDollarSigns(element);
// Also look for preview areas that might have been updated
processPreviewAreas();
}, 100);
});
}
// Setup input listener for textareas
function setupInputListener(element) {
element.addEventListener('input', function() {
replaceDollarSigns(this);
// Also check for preview areas on input
setTimeout(processPreviewAreas, 100);
});
// Also handle blur event (when user clicks away)
element.addEventListener('blur', function() {
replaceDollarSigns(this);
// Final check of preview areas
setTimeout(processPreviewAreas, 100);
});
// Set up paste listener
setupPasteListener(element);
}
// Process preview areas - these are likely where formatted content appears
function processPreviewAreas() {
// Target common preview area selectors in Flarum
const previewSelectors = [
'.Post-body', '.PostPreview', '.FormattedContent',
'.Post-preview', '.ComposerBody-preview', '.item-preview',
'[data-preview]', '.Preview', '.PostPreview-content',
// Add code block related selectors
'pre', 'code', '.hljs', '.CodeMirror', '.CodeMirror-code',
// Try to catch any element with "preview" in its class or id
'[class*="preview"]', '[id*="preview"]'
];
previewSelectors.forEach(selector => {
try {
const previewElements = document.querySelectorAll(selector);
previewElements.forEach(element => {
// For code blocks, we need to be more thorough
if (element.tagName === 'PRE' || element.tagName === 'CODE') {
replaceDollarSigns(element);
} else {
// For other preview elements, look for code blocks inside them
const codeBlocks = element.querySelectorAll('pre, code');
codeBlocks.forEach(codeBlock => {
replaceDollarSigns(codeBlock);
});
// Also check the text nodes directly
replaceDollarSigns(element);
}
});
} catch (e) {
// Ignore errors from invalid selectors
}
});
}
// Process all elements that might contain dollar signs
function processAllElements() {
// Handle textareas
const textareas = document.querySelectorAll('textarea');
textareas.forEach(textarea => {
replaceDollarSigns(textarea);
// Make sure the listener is set up
if (!textarea.hasProcessedDollarSigns) {
setupInputListener(textarea);
textarea.hasProcessedDollarSigns = true;
}
});
// Process preview areas
processPreviewAreas();
}
// Run initial processing
processAllElements();
// Also look for submit buttons and process everything before submission
document.addEventListener('click', function(e) {
if (e.target && (
e.target.type === 'submit' ||
e.target.classList.contains('Button--primary') ||
e.target.classList.contains('PostStream-reply')
)) {
// Process all elements one last time before form submission
processAllElements();
// Give it a moment to process before actual submission
setTimeout(() => {}, 100);
}
});
// Observe DOM for new elements
const observer = new MutationObserver(mutations => {
let shouldProcess = false;
mutations.forEach(mutation => {
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
shouldProcess = true;
// Specifically look for added textareas
mutation.addedNodes.forEach(node => {
if (node.nodeName === 'TEXTAREA' && !node.hasProcessedDollarSigns) {
setupInputListener(node);
node.hasProcessedDollarSigns = true;
replaceDollarSigns(node);
}
// Check for textareas within added nodes
if (node.querySelectorAll) {
const textareas = node.querySelectorAll('textarea');
textareas.forEach(textarea => {
if (!textarea.hasProcessedDollarSigns) {
setupInputListener(textarea);
textarea.hasProcessedDollarSigns = true;
replaceDollarSigns(textarea);
}
});
}
});
}
});
if (shouldProcess) {
// Process everything after DOM changes
setTimeout(processAllElements, 200);
}
});
// Start observing the document
observer.observe(document.body, { childList: true, subtree: true });
// Also set a periodic check for complex editors
setInterval(processAllElements, 2000);
})();
在很多论坛系统中,美元符号不能直接使用,尤其是开启了 latex语法的论坛。
那么最简单的就是通过油猴脚本,在发帖时,把 美元符号换成 全角美元符号。代码如下: