This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/514423/1473491/GC%20-%20Universal%20Userscripts%20Settings.js
async function addTextInput(configuration) {
let {
categoryName,
settingName,
labelText,
lableTooltip = undefined,
currentSetting = undefined,
defaultSetting = '',
callbackFunction = undefined,
} = configuration;
const header = await _addCategoryHeader(categoryName);
if (currentSetting === undefined) {
currentSetting = await GM.getValue(settingName, defaultSetting);
}
const textInput = document.createElement('input');
textInput.type = 'text';
textInput.name = settingName;
textInput.value = currentSetting;
const label = _createLabel(labelText);
_formatLabel(label, lableTooltip);
_addBelowHeader(header, label);
const inputElement = _createInput(textInput);
label.insertAdjacentElement('afterend', inputElement);
if (callbackFunction) {
_addCallback(() => callbackFunction(settingName, textInput.value));
} else {
_addCallback(async () => await GM.setValue(settingName, checkbox.checked));
}
}
async function addNumberInput(configuration) {
let {
categoryName,
settingName,
labelText,
lableTooltip = undefined,
min = 0,
max = 100,
step = 1,
currentSetting = undefined,
defaultSetting = 0,
callbackFunction = undefined
} = configuration;
const header = await _addCategoryHeader(categoryName);
if (currentSetting === undefined) {
currentSetting = await GM.getValue(settingName, defaultSetting);
}
const numberInput = document.createElement('input');
numberInput.type = 'number';
numberInput.name = settingName;
numberInput.value = currentSetting;
numberInput.min = min;
numberInput.max = max;
numberInput.step = step;
const label = _createLabel(labelText);
_formatLabel(label, lableTooltip);
_addBelowHeader(header, label);
const inputElement = _createInput(numberInput);
label.insertAdjacentElement('afterend', inputElement);
if (callbackFunction) {
_addCallback(() => callbackFunction(settingName, numberInput.value));
} else {
_addCallback(async () => await GM.setValue(settingName, checkbox.checked));
}
}
async function addCheckboxInput(configuration) {
let {
categoryName,
settingName,
labelText,
lableTooltip = undefined,
currentSetting = undefined,
defaultSetting = false,
callbackFunction = undefined
} = configuration;
const header = await _addCategoryHeader(categoryName);
if (currentSetting === undefined) {
currentSetting = await GM.getValue(settingName, defaultSetting);
}
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.name = settingName;
checkbox.checked = currentSetting;
const label = _createLabel(labelText);
_formatLabel(label, lableTooltip);
_addBelowHeader(header, label);
const inputElement = _createInput(checkbox);
label.insertAdjacentElement('afterend', inputElement);
if (callbackFunction) {
_addCallback(() => callbackFunction(settingName, checkbox.checked));
} else {
_addCallback(async () => await GM.setValue(settingName, checkbox.checked));
}
}
async function addDropdown(configuration) {
let {
categoryName,
settingName,
labelText,
lableTooltip = undefined,
options, // [{ value: 'value', text: 'text', ... }]
currentSetting = undefined,
defaultSetting = undefined,
callbackFunction = undefined
} = configuration;
const header = await _addCategoryHeader(categoryName);
if (currentSetting === undefined) {
currentSetting = await GM.getValue(settingName, defaultSetting);
}
const select = document.createElement('select');
select.name = settingName;
select.classList.add('form-control');
options.forEach(option => {
const optionElement = document.createElement('option');
optionElement.value = option.value;
optionElement.textContent = option.text;
if (option.value === currentSetting) {
optionElement.selected = true;
}
select.appendChild(optionElement);
});
const label = _createLabel(labelText);
_formatLabel(label, lableTooltip);
_addBelowHeader(header, label);
const inputElement = _createInput(select);
label.insertAdjacentElement('afterend', inputElement);
if (callbackFunction) {
_addCallback(() => callbackFunction(settingName, select.value));
} else {
_addCallback(async () => await GM.setValue(settingName, checkbox.checked));
}
}
function _formatLabel(label, lableTooltip) {
if (lableTooltip) {
const info = document.createElement('sup');
info.textContent = ' ⓘ';
label.appendChild(info);
const showTooltip = () => {
const tooltip = document.getElementById('universal-userscript-tooltip');
const hideTooltip = () => {
tooltip.style.display = 'None';
};
const closeButton = `<a href="#" id="tooltip-close" style="display: flex; justify-content: center; color: var(--link_color); margin-top: 0.5rem;">Close Tooltip</a>`;
tooltip.innerHTML = lableTooltip + closeButton;
tooltip.style.display = 'block';
tooltip.querySelector('#tooltip-close').addEventListener('click', (event) => {
event.preventDefault();
hideTooltip();
});
};
label.addEventListener('click', (event) => {
showTooltip(event);
});
}
const colon = document.createTextNode('\u00A0:');
label.appendChild(colon);
}
function _addBelowHeader(header, label) {
let nextHeader = header.nextElementSibling;
while (nextHeader && !nextHeader.matches('.header')) {
nextHeader = nextHeader.nextElementSibling;
}
if (!nextHeader) {
nextHeader = document.querySelector('#universal-userscript-preferences>.market_grid.profile.prefs.margin-auto>.footer.small-gap');
}
nextHeader.insertAdjacentElement('beforebegin', label);
}
function _addSettingsLink() {
const existingNav = document.querySelector('nav.center.margin-1');
if (existingNav) {
const newNav = document.createElement('nav');
newNav.classList.add('center', 'margin-1');
newNav.id = 'universal-userscript-navigation';
const newLink = document.createElement('a');
newLink.href = '/help/userscripts/';
newLink.textContent = 'Userscript Preferences';
newNav.appendChild(newLink);
existingNav.after(newNav);
} else {
console.error('Existing navigation element not found.');
}
}
function _createLabel(labelText) {
const labelContainer = document.createElement('div');
labelContainer.classList.add('data', 'left');
const label = document.createElement('span');
label.textContent = labelText;
labelContainer.appendChild(label);
return labelContainer;
}
function _createInput(input) {
const inputContainer = document.createElement('div');
inputContainer.classList.add('data', 'flex-column', 'right');
inputContainer.appendChild(input);
return inputContainer;
}
function _addCallback(callbackFunction) {
document.getElementById('universal-userscript-update').addEventListener('click', callbackFunction);
}
async function _addCategoryHeader(categoryName) {
if (!window.location.href.includes('/help/userscripts/')) throw Error('Attempted to add setting outside of settings page.');
await _checkSettingsSetup();
const settings = document.querySelector('.market_grid.profile.prefs.margin-auto');
const footer = document.querySelector('#universal-userscript-preferences>.market_grid.profile.prefs.margin-auto>.footer.small-gap');
if (!settings) {
console.error('Settings not found.');
return;
}
const headers = Array.from(settings.querySelectorAll('.header'));
let header = headers.find(header => header.textContent.trim() === categoryName);
if (!header) {
header = document.createElement('div');
header.classList.add('header');
header.innerHTML = `<strong>${categoryName}</strong>`;
const insertionPoint = headers.find(existingHeader => existingHeader.textContent.trim().localeCompare(categoryName) > 0);
if (insertionPoint) {
insertionPoint.insertAdjacentElement('beforebegin', header);
} else {
footer.insertAdjacentElement('beforebegin', header);
}
}
return header;
}
function _replaceBannerImage() {
const bannerImage = document.getElementById('top-header-image');
if (bannerImage) {
bannerImage.src = 'https://grundoscafe.b-cdn.net/misc/banners/userinfo.gif';
} else {
console.error('Banner image not found.');
}
}
function _setupUniversalSettings() {
const helpPages = /\/help\/(?:profile|siteprefs|sidebar|randomevents)\/|\/discord\//;
const settings = window.location.href.includes('/help/userscripts/');
const help = helpPages.test(window.location.href);
if (help) {
_addSettingsLink();
} else if (settings) {
_replaceBannerImage();
const element = document.querySelector('main');
if (element) {
element.id = 'universal-userscript-preferences';
element.innerHTML = `
<h1>Userscript Preferences</h1>
<nav class="center margin-1">
<a href="/help/profile/">Edit Profile</a> |
<a href="/help/siteprefs/">Site Preferences</a> |
<a href="/help/sidebar/">Edit Sidebar</a> |
<a href="/discord/">Discord</a> |
<a href="/help/randomevents/">Random Event Log</a>
</nav>
<nav class="center margin-1" id="universal-userscript-navigation"><a href="/help/userscripts/">Userscript Preferences</a></nav>
<p>Here you can adjust settings for participating userscripts.</p>
<div id="universal-userscript-tooltip" style="display: none; background-color: var(--grid_odd); padding: 10px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); z-index: 1000; margin-bottom: 1rem; border: 1px solid var(--grid_head);"></div>
<div class="market_grid profile prefs margin-auto">
<div class="footer small-gap">
<input class="form-control half-width" type="submit" value="Update Preferences" id="universal-userscript-update">
</div>
</div>
`;
document.getElementById('universal-userscript-update').addEventListener('click', () => {
const button = document.getElementById('universal-userscript-update');
button.disabled = true;
const originalText = button.value;
button.value = 'Preferences Updated!';
setTimeout(() => {
button.disabled = false;
button.value = originalText;
}, 2000);
});
}
}
}
async function _checkSettingsSetup() {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (document.getElementById('universal-userscript-preferences') !== null) {
clearInterval(interval);
clearTimeout(timeout);
resolve(true);
}
}, 50);
const timeout = setTimeout(() => {
clearInterval(interval);
reject(new Error('Timeout: universal-userscript-preferences element not found within 10 seconds'));
}, 10000);
});
}
if (!document.getElementById('universal-userscript-navigation')) {
_setupUniversalSettings();
}