// ==UserScript==
// @name GC Giveaway Search/Filters
// @version 1.2
// @description Adds search bar and pre-made filters, including full relic list. Adds checkbox to hide entered giveaways. Persists through reload
// @author spiderpool1855
// @match https://www.grundos.cafe/giveaways/
// @license MIT
// @grant none
// @namespace https://greasyfork.org/users/1295622
// ==/UserScript==
(function() {
'use strict';
let page = 2; // Start from the first page
let loading = false;
// Function to disable the page's loadMoreItems function
function disablePageLoader() {
if (window.loadMoreItems) {
console.log('Disabling the page\'s loadMoreItems function.');
window.loadMoreItems = function() {
console.log('loadMoreItems function has been disabled.');
};
} else {
console.log('loadMoreItems function not found.');
}
}
function loadAllItems() {
if (loading) return; // Prevent overlapping fetches
loading = true;
const url = `/giveaways/?page=${page}&active=true`;
fetch(url, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
}
})
.then(response => response.json())
.then(data => {
const container = document.querySelector('.giveaway-grid-container');
const initialItemCount = container.children.length;
container.insertAdjacentHTML('beforeend', data.items_html);
const newItemCount = container.children.length;
if (newItemCount > initialItemCount) {
// Continue loading items
if (data.has_next) {
page += 1;
setTimeout(loadAllItems, 100); // Delay before loading the next page
} else {
console.log('All items have been loaded.');
disablePageLoader(); // Disable further loading after the last page
}
} else {
console.log('No new items found, stopping.');
disablePageLoader(); // Disable further loading if no new items found
}
})
.catch(error => {
console.error('Error loading more items:', error);
})
.finally(() => {
loading = false; // Allow the next load to proceed
});
}
// Start loading all items from the first page
loadAllItems();
})();
(function() {
'use strict';
// Pre-made queries - You can add more here and it will add checkboxes for them
const queries = ['Paint', 'Potion', 'Stamp', 'Map', 'Negg'];
// Relic query list
const relicList = ["Air Faerie Crown" , "Air Faerie Token" , "Ancient Lupe Wand" , "Aquatic Gem" , "Army Math Tools" , "Attack Cape" , "Attack Fork" , "Bag of Occult Jelly" , "Battle Plunger" , "Battle Quill" , "Bit of Evil Clown" , "Blizzard Ring" , "Blood Grub" , "Boom Sticks" , "Brain Tree Branch" , "Brain Tree Knife" , "Brain Tree Mace" , "Brain Tree Root" , "Brain Tree Splinters" , "Cabbage of Mystery" , "Candy Club" , "Castle Defenders Shield" , "Castle Defenders Sword" , "Caustic Potion" , "Charles' Torch" , "Cobrall Wand" , "Dark Faerie Dagger" , "Dark Faerie Token" , "Donny's Mallet" , "Earth Faerie Dagger" , "Earth Faerie Token" , "Earth Stone Gem" , "Elephante Lamp" , "Eraser of the Dark Faerie" , "Exploding Space Bugs" , "Faerie Eraser" , "Fat Red Pen" , "Fire Faerie Token" , "Fire Stone Gem" , "Frostbite Dart" , "Fumpu Leaf Medallion" , "Garin's Sword" , "Genie Orb" , "Ghost Lupe Sword" , "Golden Aisha Wand" , "Golden Meepit Statue" , "Golden Pirate Amulet" , "Good Snowball" , "Grarrg Tooth" , "Great Snowball" , "Grundo Gavel" , "Halloween Aisha Bucket" , "Happy Anniversary Negg" , "Happy Negg Eraser" , "Hawk Bracelet" , "Hawk Wand" , "Iced Wand" , "Iceray Bracelet" , "Irregulation Chainmail" , "Jar of Spiders" , "King Kelpbeards Blessing" , "Lava Rock" , "Legendary von Roo Ring" , "Light Faerie Dagger" , "Light Faerie Token" , "Magic Branch" , "Magnus Club" , "Malice Potion" , "Melting Mirror" , "Monotonous Dial" , "Mystical Fish Lobber" , "Mystic Guitar" , "Mystic Jelly Bean Necklace" , "Neutron Wand" , "Nimmo Finger" , "Official Prissy Miss Hair Brush" , "Patched Magic Hat" , "Pear of Disintegration" , "Petpet Bone" , "Platinum Dubloon" , "Poké Ball" , "Power Negg Eraser" , "Pumpkin Stick" , "Radish Bow" , "Rainbow Cybunny Wand" , "Rainbow Kacheek Pendant" , "Rainbow Negg Eraser" , "Rainbow Pteri Feather" , "Reinvented Wheel" , "Ring of the Lost" , "Robo Sloth Fist of Power" , "Royal Wedding Ring" , "Rusty Garden Pitchfork" , "Rutabaga Lance" , "Scarab Amulet" , "Scroll of Ultimate Knowledge" , "Snowager Pendant" , "Snowager Sleep Ray" , "Snow Beast Horn" , "Snowflake Pendant" , "Snowglobe Staff" , "Soul Stone" , "Space Amulet" , "Space Faeries Shield" , "Space Faerie Token" , "Spider Grundo Sword" , "Spirited Fiddle" , "Spooky Slime" , "Squash Club" , "Staff of Brain" , "Starry Scorchio Wand" , "Superior Battle Plunger" , "Trident of Chiazilla" , "Trusty Hand Cannon" , "Tyrannian Amulet" , "Ultra Fire Gem" , "Wand of the Snow Faerie" , "Water Faerie Dagger" , "Water Faerie Token" , "Wind Up Rat" , "Witches Orb" , "Wooden Compass" , "Zucchini Bat"];
// Create the filter div
const filterContainer = document.createElement('div');
filterContainer.style.margin = '20px 0';
// Creates searchbar
const searchBar = document.createElement('input');
searchBar.setAttribute('type', 'text');
searchBar.setAttribute('placeholder', 'Search giveaways...');
searchBar.style.marginRight = '10px';
searchBar.style.width = '25%';
// Load previous search from localstorage
searchBar.value = localStorage.getItem('searchQuery') || '';
// Save current query to local storage and initiates filter
searchBar.addEventListener('input', () => {
localStorage.setItem('searchQuery', searchBar.value);
filterGiveaways();
});
filterContainer.appendChild(searchBar);
// Create the "Open" checkbox - Open means that it hides "Entered" giveaways
const hideEnteredCheckbox = document.createElement('input');
hideEnteredCheckbox.setAttribute('type', 'checkbox');
hideEnteredCheckbox.setAttribute('id', 'hideEntered');
hideEnteredCheckbox.checked = JSON.parse(localStorage.getItem('hideEntered')) || false;
const hideEnteredLabel = document.createElement('label');
hideEnteredLabel.setAttribute('for', 'hideEntered');
hideEnteredLabel.textContent = 'Open';
filterContainer.appendChild(hideEnteredCheckbox);
filterContainer.appendChild(hideEnteredLabel);
// Save "Open" checkbox status to local storage
hideEnteredCheckbox.addEventListener('change', () => {
localStorage.setItem('hideEntered', hideEnteredCheckbox.checked);
filterGiveaways();
});
// Create checkboxes for pre-made queries
queries.forEach(query => {
const checkbox = document.createElement('input');
checkbox.setAttribute('type', 'checkbox');
checkbox.setAttribute('id', `filter${query.replace(/\s+/g, '')}`);
checkbox.checked = JSON.parse(localStorage.getItem(`filter${query.replace(/\s+/g, '')}`)) || false;
const label = document.createElement('label');
label.setAttribute('for', `filter${query.replace(/\s+/g, '')}`);
label.textContent = query;
filterContainer.appendChild(checkbox);
filterContainer.appendChild(label);
// Save pre-made query checkbox statuses to local storage
checkbox.addEventListener('change', () => {
localStorage.setItem(`filter${query.replace(/\s+/g, '')}`, checkbox.checked);
filterGiveaways();
});
});
// Create the "Relic" checkbox
const relicCheckbox = document.createElement('input');
relicCheckbox.setAttribute('type', 'checkbox');
relicCheckbox.setAttribute('id', 'filterRelic');
relicCheckbox.checked = JSON.parse(localStorage.getItem('filterRelic')) || false;
const relicLabel = document.createElement('label');
relicLabel.setAttribute('for', 'filterRelic');
relicLabel.textContent = 'Relic';
filterContainer.appendChild(relicCheckbox);
filterContainer.appendChild(relicLabel);
// Save "Relic" checkbox status to local storage
relicCheckbox.addEventListener('change', () => {
localStorage.setItem('filterRelic', relicCheckbox.checked);
filterGiveaways();
});
// Insert the filter container
const parentElement = document.querySelector('.giveaway-grid-container').parentNode;
parentElement.insertBefore(filterContainer, document.querySelector('.giveaway-grid-container'));
// Filter function
searchBar.addEventListener('input', filterGiveaways);
function filterGiveaways() {
const query = searchBar.value.toLowerCase();
const hideEntered = hideEnteredCheckbox.checked;
const relicCheckboxChecked = relicCheckbox.checked;
document.querySelectorAll('.giveaway-item').forEach(item => {
let text = item.querySelector('h3')?.textContent.toLowerCase() || '';
const status = item.querySelector('p.state')?.textContent.toLowerCase() || '';
let showItem = true;
// Filter by "Open" checkbox
if (hideEntered && status.includes('entered')) {
showItem = false;
}
// Filter by pre-made queries
if (queries.some(query => {
const checkbox = document.getElementById(`filter${query.replace(/\s+/g, '')}`);
return checkbox.checked && !text.includes(query.toLowerCase());
})) {
showItem = false;
}
// Filter by "Relic" checkbox
const relicMatches = relicList.some(relic => text.includes(relic.toLowerCase()));
if (relicCheckboxChecked && !relicMatches) {
showItem = false;
}
// Filter by search query
if (query && !text.includes(query)) {
showItem = false;
}
// Show all items if no filters are checked
const anyCheckboxChecked = queries.some(q => document.getElementById(`filter${q.replace(/\s+/g, '')}`).checked) || relicCheckboxChecked || hideEntered;
if (!anyCheckboxChecked && !query) {
showItem = true;
}
item.style.display = showItem ? '' : 'none';
});
}
// Initial filter on page load
filterGiveaways();
})();