' } ] }
],
onConfirm: async ({ text, color, tag }) => {
await GM_setValue(LAST_TAG_TYPE_KEY, tag);
let newElement;
if (tag === 'div') {
newElement = `\n
\n ${text}\n
\n`;
} else {
newElement = `
${text}`;
}
textarea.setRangeText(newElement, start, end, selectedText ? 'end' : 'select');
textarea.focus();
}
});
}
});
const hrStyleBtn = createToolbarButton({
title: getTranslation('horizontal_line_style'),
label: '
',
action: () => {
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selectedText = textarea.value.substring(start, end).trim();
let currentSize = '1';
let currentColor = input.value;
const hrRegex = /
{
const newHr = `
`;
textarea.setRangeText(newHr, start, end, 'select');
textarea.focus();
}
});
}
});
const borderStyleBtn = createToolbarButton({
title: getTranslation('border_style'),
label: '
',
action: async () => {
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selectedText = textarea.value.substring(start, end);
const lastTag = await GM_getValue(LAST_TAG_TYPE_KEY, 'span');
showCustomPrompt({
inputs: [
{ id: 'text', label: getTranslation('prompt_border_text'), type: 'text', value: selectedText || getTranslation('border_text_placeholder') },
{ id: 'size', label: getTranslation('prompt_border_size'), type: 'number', value: '1' },
{ id: 'color', label: getTranslation('prompt_border_color'), type: 'text', value: input.value },
{ id: 'tag', label: getTranslation('prompt_border_tag_type'), type: 'select', value: lastTag, options: [ { value: 'span', text: '
' }, { value: 'div', text: '' } ] }
],
onConfirm: async ({ text, size, color, tag }) => {
await GM_setValue(LAST_TAG_TYPE_KEY, tag);
let newElement;
if (tag === 'div') {
newElement = `\n
\n ${text}\n
`;
} else {
newElement = `
${text}`;
}
textarea.setRangeText(newElement, start, end, selectedText ? 'end' : 'select');
textarea.focus();
}
});
}
});
colorContainer.append(input, colorBtn, bgBtn, hrStyleBtn, borderStyleBtn);
toolbar.appendChild(colorContainer);
} else {
toolbar.appendChild(createToolbarButton(tool));
}
}
const infoButton = createToolbarButton({
title: getTranslation('info_tooltip'),
icon: '
',
action: showInfoModal
});
infoButton.style.marginLeft = 'auto';
toolbar.appendChild(infoButton);
textarea.parentNode.insertBefore(container, textarea);
container.append(toolbar, textarea);
}
function applyToAllTextareas() {
const textareas = document.querySelectorAll('textarea:not(#script_version_code):not([data-editor-applied])');
textareas.forEach(createTextStyleEditor);
}
function enableSourceEditorCheckbox() {
const enableCheckbox = () => {
const checkbox = document.getElementById('enable-source-editor-code');
if (checkbox && !checkbox.checked) {
checkbox.checked = true;
const event = new Event('change', {
bubbles: true
});
checkbox.dispatchEvent(event);
}
};
enableCheckbox();
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
const checkbox = document.getElementById('enable-source-editor-code');
if (checkbox) {
enableCheckbox();
observer.disconnect();
break;
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
function isMarkdownPage() {
const path = window.location.pathname;
const markdownSegments = [ '/new', '/edit', '/feedback', '/discussions', '/conversations' ];
if (path.includes('/sets/')) {
return false;
}
return markdownSegments.some(segment => path.includes(segment));
}
// #endregion
// ================
// #region DOWNLOAD
// ================
function isCodePage() {
return /^\/([a-z]{2}(-[A-Z]{2})?\/)?scripts\/\d+-.+\/code/.test(window.location.pathname);
}
function initializeDownloadButton() {
const waitFor = (sel) =>
new Promise((resolve) => {
const el = document.querySelector(sel);
if (el) return resolve(el);
const obs = new MutationObserver(() => {
const el = document.querySelector(sel);
if (el) {
obs.disconnect();
resolve(el);
}
});
obs.observe(document, { childList: true, subtree: true });
});
waitFor('label[for="wrap-lines"]').then((label) => {
const wrapLinesCheckbox = document.getElementById('wrap-lines');
if (wrapLinesCheckbox) {
wrapLinesCheckbox.checked = false;
}
const toolbar = label.parentElement;
const btn = document.createElement('button');
btn.className = 'btn';
btn.textContent = getTranslation('download');
btn.style.marginLeft = '12px';
btn.style.backgroundColor = '#005200';
btn.style.color = 'white';
btn.style.border = 'none';
btn.style.padding = '6px 16px';
btn.style.borderRadius = '4px';
btn.style.cursor = 'pointer';
btn.addEventListener('mouseenter', () => btn.style.backgroundColor = '#1e971e');
btn.addEventListener('mouseleave', () => btn.style.backgroundColor = '#005200');
btn.addEventListener('click', () => {
const normalizedPath = normalizeScriptPath(window.location.pathname);
const scriptId = extractScriptIdFromNormalizedPath(normalizedPath);
if (!scriptId) {
alert(getTranslation('scriptIdNotFound'));
return;
}
const scriptUrl = `https://update.greasyfork.icu/scripts/${scriptId}.js`;
btn.disabled = true;
btn.textContent = getTranslation('downloading');
GM_xmlhttpRequest({
method: 'GET',
url: scriptUrl,
onload: function (res) {
const code = res.responseText;
if (!code) {
alert(getTranslation('notFound'));
return;
}
const nameMatch = code.match(/\/\/\s*@name\s+(.+)/i);
const fileName = nameMatch ? `${nameMatch[1].trim()}.user.js` : 'script.user.js';
const blob = new Blob([code], { type: 'application/javascript;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
},
onerror: function (res) {
alert(getTranslation('downloadError'));
},
ontimeout: function () {
alert(getTranslation('downloadTimeout'));
},
onloadend: function () {
btn.disabled = false;
btn.textContent = getTranslation('download');
}
});
});
toolbar.appendChild(btn);
const spacer = document.createElement('div');
spacer.style.height = '12px';
toolbar.appendChild(spacer);
});
}
// #endregion
// ================
// #region INICIALIZAR
// ================
async function start() {
iconCache = await GM_getValue(CACHE_KEY, {});
await determineLanguage();
languageModal = createLanguageModal();
document.body.appendChild(languageModal);
registerLanguageMenu();
registerForceUpdateMenu();
setupThemeChangeListener();
if (isMarkdownPage()) {
applyToAllTextareas();
enableSourceEditorCheckbox();
}
if (isCodePage()){
initializeDownloadButton();
}
processIconElements();
highlightScriptDescription();
if (isScriptPage()) {
addAdditionalInfoSeparator();
}
makeDiscussionClickable();
applySyntaxHighlighting();
const observer = new MutationObserver(() => {
processIconElements();
highlightScriptDescription();
if (isScriptPage()) {
addAdditionalInfoSeparator();
}
if (isMarkdownPage()) {
applyToAllTextareas();
}
makeDiscussionClickable();
applySyntaxHighlighting();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
start();
// #endregion
})();