Code: Select all
// ==UserScript==
// @name RPGHQ - BBCode Highlighter
// @namespace http://rpghq.org/
// @version 4.0
// @description Highlight BBCode tags in the text editor on RPGHQ forum with consistent colors for matching tags
// @author loregamer
// @match https://rpghq.org/forums/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=rpghq.org
// @grant none
// ==/UserScript==
(function () {
"use strict";
// Simplified customSmileys array
const customSmileys = [
"📥",
"https://f.rpghq.org/knxzaIDkcOuq.png?n=pasted-file.png",
"https://f.rpghq.org/t2n0MlWR5h8K.png?n=pasted-file.png",
"https://f.rpghq.org/5Niz8ii3t0cN.png?n=HQ%20Official.png",
"https://f.rpghq.org/ZgRYx3ztDLyD.png?n=cancel_forum.png",
"https://f.rpghq.org/W5kvLDYCwg8G.png",
"https://f.rpghq.org/zjCPqqYUdj1J.png?n=sadmonke.png",
"https://f.rpghq.org/b6dhUkX2kwTu.png?n=lore.png",
// Add more custom smiley URLs here
];
console.log("Userscript is running!");
// Add custom CSS to highlight BBCode tags with multiple colors
const style = document.createElement("style");
style.textContent = `
.bbcode-bracket {
color: #D4D4D4; /* Light grey */
}
.bbcode-tag-0 {
color: #569CD6; /* Blue */
}
.bbcode-tag-1 {
color: #CE9178; /* Light orange */
}
.bbcode-tag-2 {
color: #DCDCAA; /* Yellow */
}
.bbcode-tag-3 {
color: #C586C0; /* Light purple */
}
.bbcode-tag-4 {
color: #4EC9B0; /* Light green */
}
.bbcode-attribute {
color: #9CDCFE; /* Light blue */
}
.bbcode-list-item {
color: #FFD700; /* Gold color for list items */
}
.bbcode-smiley {
color: #FFD700; /* Gold color for smileys */
}
#bbcode-highlight {
white-space: pre-wrap;
word-wrap: break-word;
position: absolute;
top: 0;
left: 0;
z-index: 3; /* Ensure it is above the textarea */
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none; /* Allow interaction with textarea */
box-sizing: border-box;
padding: 3px;
font-family: Verdana, Helvetica, Arial, sans-serif;
font-size: 11px;
font-style: normal;
font-variant-caps: normal;
font-variant-east-asian: normal;
font-variant-ligatures: normal;
font-variant-numeric: normal;
font-weight: 400;
line-height: 15.4px;
background-color: transparent; /* Make background transparent */
color: transparent; /* Make text color transparent */
transition: all 0.5s ease, height 0.001s linear;
}
#message {
position: relative;
z-index: 2;
background: transparent;
color: rgb(204, 204, 204);
caret-color: white; /* Change cursor color to white */
width: 100%;
height: 100%;
padding: 3px;
box-sizing: border-box;
resize: none;
overflow: auto;
font-family: Verdana, Helvetica, Arial, sans-serif;
font-size: 11px;
font-style: normal;
font-variant-caps: normal;
font-variant-east-asian: normal;
font-variant-ligatures: normal;
font-variant-numeric: normal;
font-weight: 400;
line-height: 15.4px;
}
.editor-container {
position: relative;
width: 100%;
height: auto; /* Allow dynamic height */
}
.bbcode-link {
color: #5D8FBD; /* Light blue */
}
.custom-smiley-container {
margin-top: 5px;
margin-bottom: 5px;
padding-left: 5px;
padding-right: 5px;
}
.custom-smiley-button {
display: inline-block;
margin-right: 5px;
margin-bottom: 5px;
width: 20px;
height: 20px;
text-decoration: none;
vertical-align: middle;
line-height: 1;
}
.custom-smiley-button:hover {
text-decoration: none;
}
.emoji-smiley {
font-size: 20px;
display: inline-block;
width: 20px;
height: 20px;
text-align: center;
vertical-align: middle;
}
#smiley-box {
position: absolute;
max-height: 80vh; /* Adjust this value as needed */
width: 17%;
overflow-y: auto;
padding: 10px;
border-radius: 5px;
z-index: 1000;
}
#smiley-box a {
text-decoration: none;
}
#smiley-box a:hover {
text-decoration: underline;
}
#abbc3_buttons.fixed {
position: fixed;
top: 0;
z-index: 1000;
margin-top: 0;
padding-top: 0;
}
.abbc3_buttons_row {
margin: 0;
padding: 0;
}
.abbc3_buttons_row.fixed {
position: fixed;
top: 0;
z-index: 1000;
margin-top: 0;
padding-top: 0;
}
.custom-button {
color: white;
border: none;
padding: 5px 10px;
margin: 5px;
border-radius: 3px;
cursor: pointer;
}
.custom-button:hover {
}
#smiley_tabmenu {
text-align: center;
}
.tabcontent {
padding: 10px;
border-radius: 0 0 5px 5px;
text-align: left;
}
.smiley-button {
display: inline-block;
width: 30px;
height: 30px;
margin: 2px;
text-decoration: none;
vertical-align: middle;
line-height: 30px;
text-align: center;
border-radius: 3px;
transition: background-color 0.2s;
overflow: hidden; /* Ensure content doesn't overflow */
}
.smiley-button:hover {
}
.smiley-button img {
max-width: 80%;
max-height: 80%;
width: auto;
height: auto;
object-fit: contain;
vertical-align: middle;
}
.emoji-smiley {
font-size: 18px;
display: inline-block;
width: 100%;
height: 100%;
text-align: center;
vertical-align: middle;
}
#smiley-box {
border-radius: 5px;
padding: 10px;
margin-top: 10px;
}
`;
document.head.appendChild(style);
// Function to get a consistent color index for each tag name
const tagColorMap = {
img: 1,
url: 4,
color: 3,
// Add more tag-color mappings as needed
};
function getColorIndex(tagName) {
if (tagName === "*") {
return "list-item";
}
if (!(tagName in tagColorMap)) {
const colorIndex = Object.keys(tagColorMap).length % 5;
tagColorMap[tagName] = colorIndex;
}
return tagColorMap[tagName];
}
function getContrastColor(hexColor) {
const r = parseInt(hexColor.slice(1, 3), 16);
const g = parseInt(hexColor.slice(3, 5), 16);
const b = parseInt(hexColor.slice(5, 7), 16);
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
return yiq >= 128 ? "black" : "white";
}
function highlightBBCode(text) {
// First, let's separate code blocks and replace them with placeholders
const codeBlocks = [];
text = text.replace(
/\[code\]([\s\S]*?)\[\/code\]/gi,
function (match, code) {
codeBlocks.push(code);
return `[CODE_PLACEHOLDER_${codeBlocks.length - 1}]`;
}
);
const escapeHTML = (str) =>
str.replace(
/[&<>"']/g,
(m) =>
({
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
}[m])
);
// Handle smileys
text = text.replace(/:([a-z0-9-]+):/gi, function (match, smileyName) {
const customSmiley = customSmileys.find((url) =>
url.includes(smileyName)
);
if (customSmiley) {
return `<span class="bbcode-smiley" title="${match}"><img src="${customSmiley}" alt="${match}" /></span>`;
}
return `<span class="bbcode-smiley">${escapeHTML(match)}</span>`;
});
// Handle [img] and [media] tags separately
text = text.replace(
/\[(img|media)\](.*?)\[\/\1\]/gi,
function (match, tagName, url) {
const colorIndex = getColorIndex(tagName);
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-${colorIndex}">${tagName}</span><span class="bbcode-bracket">]</span><span class="bbcode-link">${escapeHTML(
url
)}</span><span class="bbcode-bracket">[</span><span class="bbcode-tag-${colorIndex}">/${tagName}</span><span class="bbcode-bracket">]</span>`;
}
);
// Handle [img] and [media] tags with attributes
text = text.replace(
/\[(img|media)(\s+[a-z]+=[^\]]+)+\](.*?)\[\/\1\]/gi,
function (match, tagName, attributes, url) {
const colorIndex = getColorIndex(tagName);
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-${colorIndex}">${tagName}</span><span class="bbcode-attribute">${escapeHTML(
attributes
)}</span><span class="bbcode-bracket">]</span><span class="bbcode-link">${escapeHTML(
url
)}</span><span class="bbcode-bracket">[</span><span class="bbcode-tag-${colorIndex}">/${tagName}</span><span class="bbcode-bracket">]</span>`;
}
);
// Then, handle the rest of the BBCode tags
text = text.replace(
/\[([a-zA-Z0-9*]+)((?:=[^\]]+)?|(?:\s+[a-z]+=[^\]]+)+)?\]|\[\/([a-zA-Z0-9*]+)\]|(https?:\/\/[^\s\]]+)/g,
function (match, openTag, attribute, closeTag) {
if (openTag) {
const colorIndex = getColorIndex(openTag);
if (openTag.toLowerCase() === "color" && attribute) {
const colorMatch = attribute.match(/=(#[0-9A-Fa-f]{6})/i);
if (colorMatch) {
const hexColor = colorMatch[1];
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-${colorIndex}">${escapeHTML(
openTag
)}</span><span class="bbcode-attribute">=</span><span class="bbcode-color-preview" style="background-color:${hexColor}; color: ${getContrastColor(
hexColor
)};">${escapeHTML(
hexColor
)}</span><span class="bbcode-bracket">]</span>`;
}
}
if (openTag === "*") {
return `<span class="bbcode-bracket">[</span><span class="bbcode-list-item">*</span><span class="bbcode-bracket">]</span>`;
}
if (openTag.toLowerCase() === "url" && attribute) {
const urlMatch = attribute.match(/=(https?:\/\/[^\]]+)/);
if (urlMatch) {
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-${colorIndex}">${escapeHTML(
openTag
)}</span><span class="bbcode-attribute">=<span class="bbcode-link">${escapeHTML(
urlMatch[1]
)}</span></span><span class="bbcode-bracket">]</span>`;
}
}
if (openTag.toLowerCase() === "smention") {
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-smention" style="color: #FFC107;">${escapeHTML(
openTag
)}</span>${
attribute
? `<span class="bbcode-attribute">${escapeHTML(
attribute
)}</span>`
: ""
}<span class="bbcode-bracket">]</span>`;
}
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-${colorIndex}">${escapeHTML(
openTag
)}</span>${
attribute
? `<span class="bbcode-attribute">${escapeHTML(attribute)}</span>`
: ""
}<span class="bbcode-bracket">]</span>`;
}
if (closeTag) {
const colorIndex = getColorIndex(closeTag);
if (closeTag.toLowerCase() === "smention") {
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-smention" style="color: #FFC107;">/${escapeHTML(
closeTag
)}</span><span class="bbcode-bracket">]</span>`;
}
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-${colorIndex}">/${escapeHTML(
closeTag
)}</span><span class="bbcode-bracket">]</span>`;
}
return match;
}
);
// Finally, replace code placeholders with non-highlighted code blocks
text = text.replace(/\[CODE_PLACEHOLDER_(\d+)\]/g, function (match, index) {
return `<span class="bbcode-bracket">[</span><span class="bbcode-tag-0">code</span><span class="bbcode-bracket">]</span><span style="color: #2E8B57;">${escapeHTML(
codeBlocks[index]
)}</span><span class="bbcode-bracket">[</span><span class="bbcode-tag-0">/code</span><span class="bbcode-bracket">]</span>`;
});
return text;
}
// Function to adjust textarea and highlight div
function adjustTextareaAndHighlight() {
const newTextarea = document.getElementById("message");
const highlightDiv = document.getElementById("bbcode-highlight");
// Adjust textarea height
newTextarea.style.height = "auto";
newTextarea.style.height = newTextarea.scrollHeight + "px";
// Match highlight div to textarea
highlightDiv.style.width = newTextarea.offsetWidth + "px";
highlightDiv.style.height = newTextarea.offsetHeight + "px";
highlightDiv.style.padding = window.getComputedStyle(newTextarea).padding;
highlightDiv.style.borderWidth =
window.getComputedStyle(newTextarea).borderWidth;
highlightDiv.style.borderStyle =
window.getComputedStyle(newTextarea).borderStyle;
highlightDiv.style.borderColor = "transparent";
highlightDiv.style.fontFamily =
window.getComputedStyle(newTextarea).fontFamily;
highlightDiv.style.fontSize = window.getComputedStyle(newTextarea).fontSize;
highlightDiv.style.lineHeight =
window.getComputedStyle(newTextarea).lineHeight;
// Reposition smiley box
positionSmileyBox();
positionEditorHeader();
}
function updateHighlight() {
const textarea = document.getElementById("message");
const highlightDiv = document.getElementById("bbcode-highlight");
const content = textarea.value;
highlightDiv.innerHTML = highlightBBCode(content);
}
function wrapSelectedText(textarea, tag) {
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selectedText = textarea.value.substring(start, end);
const replacement = `[${tag}]${selectedText}[/${tag}]`;
textarea.value =
textarea.value.substring(0, start) +
replacement +
textarea.value.substring(end);
textarea.setSelectionRange(
start + tag.length + 2,
start + replacement.length - tag.length - 3
);
}
function positionSmileyBox() {
const smileyBox = document.getElementById("smiley-box");
const textarea = document.getElementById("message");
if (smileyBox && textarea) {
const textareaRect = textarea.getBoundingClientRect();
const windowWidth = window.innerWidth;
const scrollTop =
window.pageYOffset || document.documentElement.scrollTop;
// Define the point at which the smiley box should start scrolling
const scrollStartPoint = textareaRect.top + scrollTop;
// Calculate the left position to avoid overlap
const smileyBoxWidth = 220; // Width of the smiley box plus some padding
const leftPosition = Math.min(
textareaRect.right + 10,
windowWidth - smileyBoxWidth
);
if (scrollTop >= scrollStartPoint) {
const scrollDistance = scrollTop - scrollStartPoint;
const maxScroll = textarea.offsetHeight - smileyBox.offsetHeight;
const newTop = Math.min(scrollDistance, maxScroll);
smileyBox.style.position = "absolute";
smileyBox.style.top = scrollStartPoint + newTop + "px";
smileyBox.style.left = leftPosition + "px";
} else {
smileyBox.style.position = "absolute";
smileyBox.style.top = scrollStartPoint + "px";
smileyBox.style.left = leftPosition + "px";
}
}
}
function positionEditorHeader() {
const editorHeader = document.getElementById("abbc3_buttons");
const textarea = document.getElementById("message");
if (editorHeader && textarea) {
const textareaRect = textarea.getBoundingClientRect();
const headerRect = editorHeader.getBoundingClientRect();
const scrollTop =
window.pageYOffset || document.documentElement.scrollTop;
// Calculate the offset between the header and the textarea
const offset = headerRect.top - textareaRect.top;
// Define the point at which the header should start scrolling
const scrollStartPoint = textareaRect.top + scrollTop - offset;
if (scrollTop >= scrollStartPoint) {
if (!editorHeader.classList.contains("fixed")) {
editorHeader.classList.add("fixed");
// Add a placeholder to prevent content jump
const placeholder = document.createElement("div");
placeholder.style.height = editorHeader.offsetHeight + "px";
placeholder.id = "abbc3_buttons_placeholder";
editorHeader.parentNode.insertBefore(placeholder, editorHeader);
}
// Match the width of the textarea for the header
editorHeader.style.width = textarea.offsetWidth + "px";
editorHeader.style.left = textareaRect.left + "px";
editorHeader.style.top = "0px"; // Ensure it's at the very top
// Set the width and position for each row
const editorRows = editorHeader.querySelectorAll(".abbc3_buttons_row");
let cumulativeHeight = 0;
editorRows.forEach((row, index) => {
row.style.width = textarea.offsetWidth + "px";
row.classList.add("fixed");
row.style.top = cumulativeHeight + "px";
row.style.position = "fixed";
cumulativeHeight += row.offsetHeight;
});
} else {
if (editorHeader.classList.contains("fixed")) {
editorHeader.classList.remove("fixed");
editorHeader.style.width = "";
editorHeader.style.left = "";
editorHeader.style.top = "";
// Remove the placeholder
const placeholder = document.getElementById(
"abbc3_buttons_placeholder"
);
if (placeholder) {
placeholder.parentNode.removeChild(placeholder);
}
// Reset the styles for each row
const editorRows =
editorHeader.querySelectorAll(".abbc3_buttons_row");
editorRows.forEach((row) => {
row.style.width = "";
row.style.top = "";
row.style.position = "";
row.classList.remove("fixed");
});
}
}
}
}
function insertSmiley(smiley) {
const textarea = document.getElementById("message");
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const scrollTop = textarea.scrollTop; // Store the current scroll position
const text = textarea.value;
const before = text.substring(0, start);
const after = text.substring(end);
const insertText = smiley.startsWith("http")
? `[img]${smiley}[/img]`
: smiley;
// Insert the smiley
textarea.value = before + insertText + after;
// Set the cursor position after the inserted smiley
const newCursorPos = start + insertText.length;
textarea.setSelectionRange(newCursorPos, newCursorPos);
// Restore the scroll position
textarea.scrollTop = scrollTop;
// Maintain focus on the textarea
textarea.focus();
// Update the highlight and adjust textarea size
updateHighlight();
adjustTextareaAndHighlight();
}
function addCustomSmileyButtons() {
const smileyBox = document.getElementById("smiley-box");
if (smileyBox) {
// Find the "View more smilies" link
const viewMoreLink = smileyBox.querySelector('a[href*="mode=smilies"]');
// Group default smileys by directory
const defaultSmileys = {};
const smileyLinks = smileyBox.querySelectorAll(
'a[onclick^="insert_text"]'
);
smileyLinks.forEach((link) => {
const imgSrc = link.querySelector("img").src;
const directory = imgSrc.split("/").slice(-2, -1)[0]; // Get the second-to-last part of the path
if (!defaultSmileys[directory]) {
defaultSmileys[directory] = [];
}
defaultSmileys[directory].push(link);
});
// Add additional smilies from "View more smilies"
const additionalSmilies = [
{
code: ":turtle dance:",
src: "./images/smilies/animated/turtle-dance.gif",
alt: ":turtle dance:",
title: "Turtle Dance",
},
{
code: ":autism:",
src: "./images/smilies/large/cat-autism.webp",
alt: ":autism:",
title: "I have autism",
},
{
code: ":0of5:",
src: "./images/smilies/ratings/0of5.png",
alt: ":0of5:",
title: "Zero Watermelons!",
},
{
code: ":1of5:",
src: "./images/smilies/ratings/1of5.png",
alt: ":1of5:",
title: "One Watermelon.",
},
{
code: ":2of5:",
src: "./images/smilies/ratings/2of5.png",
alt: ":2of5:",
title: "Two Watermelons.",
},
{
code: ":3of5:",
src: "./images/smilies/ratings/3of5.png",
alt: ":3of5:",
title: "Three Watermelons",
},
{
code: ":4of5:",
src: "./images/smilies/ratings/4of5.png",
alt: ":4of5:",
title: "Four Watermelons",
},
{
code: ":5of5:",
src: "./images/smilies/ratings/5of5.png",
alt: ":5of5:",
title: "Five Whole Watermelons!",
},
{
code: ":kill it with fire:",
src: "./images/smilies/animated/scorpio.webp",
alt: ":kill it with fire:",
title: "Kill it with fire",
},
{
code: ":trash:",
src: "./images/smilies/animated/trash.webp",
alt: ":trash:",
title: "Into the trash it goes",
},
{
code: ":waiting:",
src: "./images/smilies/large/Waiting-Skeleton.jpg",
alt: ":waiting:",
title: "This will take a while",
},
{
code: ":yells at clouds:",
src: "./images/smilies/cloud.webp",
alt: ":yells at clouds:",
title: "Cloud Yelling",
},
{
code: ":tip hat:",
src: "./images/smilies/tip_hat.jpg",
alt: ":tip hat:",
title: "Hat Tipping Intensifies",
},
{
code: ":trolling:",
src: "./images/smilies/trolling.webp",
alt: ":trolling:",
title: "Trolling",
},
{
code: ":what:",
src: "./images/smilies/what.jpg",
alt: ":what:",
title: "What",
},
{
code: ":disgust:",
src: "./images/smilies/test/disgust.jpg",
alt: ":disgust:",
title: "Disgust",
},
{
code: ":popcorn:",
src: "./images/smilies/popcorn_b.webp",
alt: ":popcorn:",
title: "Popcorn",
},
{
code: ":soy:",
src: "./images/smilies/soy.webp",
alt: ":soy:",
title: "Soy",
},
{
code: ":fnv:",
src: "./images/smilies/large/fnv.webp",
alt: ":fnv:",
title: "FNV is for ********",
},
];
additionalSmilies.forEach((smiley) => {
const directory = smiley.src.split("/").slice(-2, -1)[0];
if (!defaultSmileys[directory]) {
defaultSmileys[directory] = [];
}
const link = document.createElement("a");
link.href = "#";
link.onclick = function (e) {
e.preventDefault();
initInsertions();
insert_text(smiley.code, true, true);
return false;
};
const img = document.createElement("img");
img.src = smiley.src;
img.alt = smiley.alt;
img.title = smiley.title;
link.appendChild(img);
defaultSmileys[directory].push(link);
});
// Remove existing smiley links
smileyLinks.forEach((link) => link.remove());
// Create tabmenu structure
const tabmenu = document.createElement("div");
tabmenu.className = "tabmenu_live";
tabmenu.id = "smiley_tabmenu";
tabmenu.style.width = "340px";
tabmenu.style.maxWidth = "100%";
const tabmenuUl = document.createElement("ul");
tabmenuUl.className = "tabmenu_ul";
const tabcontentWrapper = document.createElement("div");
tabcontentWrapper.className = "tabcontent_wrapper";
tabmenu.appendChild(tabmenuUl);
tabmenu.appendChild(tabcontentWrapper);
smileyBox.insertBefore(tabmenu, viewMoreLink);
// Add tabs and content for default smileys
Object.entries(defaultSmileys).forEach(([category, links], index) => {
// Create tab
const tabLi = document.createElement("li");
const tabA = document.createElement("a");
tabA.href = "javascript:void(null)";
tabA.className = `t${index}${index === 0 ? " current" : ""}`;
tabA.textContent =
category === "default"
? "Default"
: category.charAt(0).toUpperCase() + category.slice(1);
tabLi.appendChild(tabA);
tabmenuUl.appendChild(tabLi);
// Create content
const content = document.createElement("div");
content.className = "tabcontent";
content.style.display = index === 0 ? "block" : "none";
links
.sort((a, b) => {
const aText = a.querySelector("img").alt;
const bText = b.querySelector("img").alt;
return aText.localeCompare(bText);
})
.forEach((link) => {
link.className = "smiley-button";
content.appendChild(link.cloneNode(true));
});
tabcontentWrapper.appendChild(content);
});
// Add tab and content for custom smileys
const customTabLi = document.createElement("li");
const customTabA = document.createElement("a");
customTabA.href = "javascript:void(null)";
customTabA.className = `t${Object.keys(defaultSmileys).length}`;
customTabA.textContent = "Custom";
customTabLi.appendChild(customTabA);
tabmenuUl.appendChild(customTabLi);
const customContent = document.createElement("div");
customContent.className = "tabcontent";
customContent.style.display = "none";
// Sort and add custom smileys
customSmileys
.sort((a, b) => {
const aText = a.startsWith("http") ? a.split("/").pop() : a;
const bText = b.startsWith("http") ? b.split("/").pop() : b;
return aText.localeCompare(bText);
})
.forEach((smiley) => {
const smileyButton = document.createElement("a");
smileyButton.href = "#";
smileyButton.className = "smiley-button custom-smiley-button";
if (smiley.startsWith("http")) {
smileyButton.innerHTML = `<img src="${smiley}" alt="Custom Smiley" title="Custom Smiley">`;
} else {
smileyButton.innerHTML = `<span class="emoji-smiley">${smiley}</span>`;
}
smileyButton.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
insertSmiley(smiley);
});
customContent.appendChild(smileyButton);
});
tabcontentWrapper.appendChild(customContent);
// Add click event listeners to tabs
tabmenuUl.querySelectorAll("a").forEach((tab, index) => {
tab.addEventListener("click", function () {
tabmenuUl
.querySelectorAll("a")
.forEach((t) => t.classList.remove("current"));
this.classList.add("current");
tabcontentWrapper
.querySelectorAll(".tabcontent")
.forEach((content, i) => {
content.style.display = i === index ? "block" : "none";
});
});
});
}
}
function addCustomButtons() {
const smileyBox = document.getElementById("smiley-box");
if (smileyBox) {
const bbcodeStatus = smileyBox.querySelector(".bbcode-status");
if (bbcodeStatus) {
bbcodeStatus.innerHTML = `
<hr>
<button type="button" class="button button-secondary" id="insert-mod-template">Insert Mod Template</button>
<button type="button" class="button button-secondary" id="insert-table">Insert Table</button>
`;
document
.getElementById("insert-mod-template")
.addEventListener("click", function (e) {
e.preventDefault();
insertModTemplate();
});
document
.getElementById("insert-table")
.addEventListener("click", function (e) {
e.preventDefault();
insertTable();
});
}
}
}
function insertModTemplate() {
const template = `[align=center][img] MOD IMAGE URL HERE [/img][/align]
[hr]
[size=150][b][color=#FE545D] Overview [/color][/b][/size]
MOD DESCRIPTION HERE
[hr]
[size=150][b][color=#FE545D] Downloads [/color][/b][/size]
| Files | Version | Type | Description |
|-------|-----------|-------|---------------|
|[url=URL HERE] 📥 HYPERLINK TEXT HERE [/url] | FILE VERSION HERE | Main/Optional/Add-on | FILE DESCRIPTION HERE |
|[url=URL HERE] 📥 HYPERLINK TEXT HERE [/url] | FILE VERSION HERE | Main/Optional/Add-on | FILE DESCRIPTION HERE |
|[url=URL HERE] 📥 HYPERLINK TEXT HERE [/url] | FILE VERSION HERE | Main/Optional/Add-on | FILE DESCRIPTION HERE |
|[url=URL HERE] 📥 HYPERLINK TEXT HERE [/url] | FILE VERSION HERE | Main/Optional/Add-on | FILE DESCRIPTION HERE |
[hr]
[size=150][b][color=#FE545D] Installation Instructions [/color][/b][/size]
[list=1]
[*] Instruction Number 1
[*] Instruction Number 2
[*] Instruction Number 3
[/list]
[hr]
[size=150][b][color=#FE545D] Changelog [/color][/b][/size]
[spoiler]
[b]VERSION NUMBER HERE[/b]
[list]
[*] CHANGE HERE
[*] CHANGE HERE
[*] CHANGE HERE
[/list]
[b]VERSION NUMBER HERE[/b]
[list]
[*] CHANGE HERE
[*] CHANGE HERE
[*] CHANGE HERE
[/list]
[/spoiler]
[hr]
[size=150][b][color=#FE545D] To Do [/color][/b][/size]
[list]
[*] TO DO
[*] TO DO
[*] TO DO
[/list]
[hr]
[size=150][b][color=#FE545D] Reporting Bugs [/color][/b][/size]
To report any bugs, please submit a post in the [url=https://rpghq.org/forums/posting.php?mode=post&f=40]Mod Support section[/url] and mention my username.
[hr]
[size=150][b][color=#FE545D]Credits[/color][/b][/size]
[list]
[*] CREDIT
[*] CREDIT
[*] CREDIT
[/list]
[hr]
[size=150][b][color=#FE545D] My Other Mods [/color][/b][/size]
[list]
[*] [url=MOD URL] MOD NAME [/url]
[*] [url=MOD URL] MOD NAME [/url]
[*] [url=MOD URL] MOD NAME [/url]
[*] [url=MOD URL] MOD NAME [/url]
[*] [url=MOD URL] MOD NAME [/url]
[/list]
[hr]
`;
insertTextAtCursor(template);
}
function insertTable() {
const table = `| Files | Version | Type | Description |
|-------|-----------|-------|---------------|
|[url=URL HERE] 📥 HYPERLINK TEXT HERE [/url] | FILE VERSION HERE | Main/Optional/Add-on | FILE DESCRIPTION HERE |
|[url=URL HERE] 📥 HYPERLINK TEXT HERE [/url] | FILE VERSION HERE | Main/Optional/Add-on | FILE DESCRIPTION HERE |
|[url=URL HERE] 📥 HYPERLINK TEXT HERE [/url] | FILE VERSION HERE | Main/Optional/Add-on | FILE DESCRIPTION HERE |
|[url=URL HERE] 📥 HYPERLINK TEXT HERE [/url] | FILE VERSION HERE | Main/Optional/Add-on | FILE DESCRIPTION HERE |
`;
insertTextAtCursor(table);
}
function insertTextAtCursor(text) {
const textarea = document.getElementById("message");
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const scrollTop = textarea.scrollTop; // Store the current scroll position
const textBefore = textarea.value.substring(0, start);
const textAfter = textarea.value.substring(end);
// Insert the text
textarea.value = textBefore + text + textAfter;
// Set the cursor position after the inserted text
const newCursorPos = start + text.length;
textarea.setSelectionRange(newCursorPos, newCursorPos);
// Restore the scroll position
textarea.scrollTop = scrollTop;
// Maintain focus on the textarea
textarea.focus();
// Update the highlight and adjust textarea size
updateHighlight();
adjustTextareaAndHighlight();
}
function initialize() {
console.log("Initialize function called");
const originalTextarea = document.getElementById("message");
if (originalTextarea) {
console.log("Textarea found");
// Create a new textarea element
const newTextarea = document.createElement("textarea");
newTextarea.id = "message";
newTextarea.name = "message";
newTextarea.className = originalTextarea.className;
newTextarea.value = originalTextarea.value;
newTextarea.setAttribute(
"tabindex",
originalTextarea.getAttribute("tabindex")
);
newTextarea.setAttribute(
"onselect",
originalTextarea.getAttribute("onselect")
);
newTextarea.setAttribute(
"onclick",
originalTextarea.getAttribute("onclick")
);
newTextarea.setAttribute(
"onkeyup",
originalTextarea.getAttribute("onkeyup")
);
newTextarea.setAttribute(
"onfocus",
originalTextarea.getAttribute("onfocus")
);
// Set the style for the new textarea
newTextarea.style.overflow = "hidden";
newTextarea.style.resize = "none";
newTextarea.style.minHeight = "500px"; // Set a minimum height
newTextarea.addEventListener("keydown", function (e) {
if (e.ctrlKey) {
let tag = "";
if (e.key === "b") tag = "b";
else if (e.key === "i") tag = "i";
else if (e.key === "u") tag = "u";
if (tag) {
e.preventDefault();
wrapSelectedText(this, tag);
updateHighlight();
adjustTextareaAndHighlight();
}
}
});
const container = document.createElement("div");
container.className = "editor-container";
const highlightDiv = document.createElement("div");
highlightDiv.id = "bbcode-highlight";
container.appendChild(highlightDiv);
originalTextarea.parentNode.replaceChild(container, originalTextarea);
container.appendChild(newTextarea);
// Function to adjust textarea height
function adjustTextareaHeight() {
newTextarea.style.height = "auto";
newTextarea.style.height = newTextarea.scrollHeight + "px";
highlightDiv.style.height = newTextarea.style.height;
}
let lastContent = "";
let updateTimer = null;
function checkForUpdates() {
const currentContent = newTextarea.value;
if (currentContent !== lastContent) {
updateHighlight();
adjustTextareaAndHighlight();
lastContent = currentContent;
}
updateTimer = setTimeout(checkForUpdates, 100); // Check every 100ms
}
newTextarea.addEventListener("input", function () {
clearTimeout(updateTimer);
checkForUpdates();
});
window.addEventListener("resize", adjustTextareaAndHighlight);
// Initial adjustment and highlight
adjustTextareaAndHighlight();
updateHighlight();
lastContent = newTextarea.value;
// Start checking for updates
checkForUpdates();
// Add custom smiley buttons
addCustomSmileyButtons();
// Add custom buttons
addCustomButtons();
// Position the smiley box and editor header initially
positionSmileyBox();
positionEditorHeader();
// Add event listeners for repositioning
window.addEventListener("resize", function () {
positionSmileyBox();
positionEditorHeader();
});
window.addEventListener("scroll", function () {
positionSmileyBox();
positionEditorHeader();
});
} else {
console.log("Textarea not found, checking again in 500ms");
setTimeout(initialize, 500);
}
}
// Run the initialize function on page load
window.addEventListener("load", function () {
console.log("Window load event triggered");
initialize();
});
})();