Adds search bar and filters to FlatMMO market page
// ==UserScript==
// @name FlatMMO Market Search + Filters
// @namespace http://tampermonkey.net/
// @version 1.7
// @description Adds search bar and filters to FlatMMO market page
// @author Carlos
// @license MIT
// @match https://flatmmo.com/market/
// @grant none
// ==/UserScript==
(function() {
'use strict';
function addSearchFilters() {
const table = document.getElementById('postingsTable');
if (!table) return false; // Table not found yet
// Prevent adding filters multiple times
if (document.getElementById('market-search-container')) return true;
// Create container for controls
const container = document.createElement('div');
container.id = 'market-search-container'; // Mark container so we don't add twice
container.style.display = 'flex';
container.style.flexWrap = 'wrap';
container.style.justifyContent = 'center';
container.style.gap = '10px';
container.style.margin = '15px 0';
// Search input
const searchInput = document.createElement('input');
searchInput.type = 'text';
searchInput.placeholder = 'Search items or usernames...';
searchInput.style.padding = '10px';
searchInput.style.fontSize = '16px';
searchInput.style.borderRadius = '5pt';
searchInput.style.border = '1px solid black';
searchInput.style.minWidth = '250px';
container.appendChild(searchInput);
// Buying/Selling filter dropdown
const typeFilter = document.createElement('select');
typeFilter.style.padding = '10px';
typeFilter.style.fontSize = '16px';
typeFilter.style.borderRadius = '5pt';
typeFilter.style.border = '1px solid black';
typeFilter.innerHTML = `
<option value="all">All Types</option>
<option value="BUYING">Buying</option>
<option value="SELLING">Selling</option>
`;
container.appendChild(typeFilter);
// Online status filter dropdown
const onlineFilter = document.createElement('select');
onlineFilter.style.padding = '10px';
onlineFilter.style.fontSize = '16px';
onlineFilter.style.borderRadius = '5pt';
onlineFilter.style.border = '1px solid black';
onlineFilter.innerHTML = `
<option value="all">All Online Status</option>
<option value="online">Online Only</option>
<option value="offline">Offline Only</option>
`;
container.appendChild(onlineFilter);
// Min price input
const priceMin = document.createElement('input');
priceMin.type = 'number';
priceMin.min = 0;
priceMin.placeholder = 'Min price';
priceMin.style.padding = '10px';
priceMin.style.fontSize = '16px';
priceMin.style.borderRadius = '5pt';
priceMin.style.border = '1px solid black';
priceMin.style.width = '100px';
container.appendChild(priceMin);
// Max price input
const priceMax = document.createElement('input');
priceMax.type = 'number';
priceMax.min = 0;
priceMax.placeholder = 'Max price';
priceMax.style.padding = '10px';
priceMax.style.fontSize = '16px';
priceMax.style.borderRadius = '5pt';
priceMax.style.border = '1px solid black';
priceMax.style.width = '100px';
container.appendChild(priceMax);
// Insert controls above table
table.parentNode.insertBefore(container, table);
// Filtering logic
function filterRows() {
const searchText = searchInput.value.toLowerCase();
const typeValue = typeFilter.value;
const onlineValue = onlineFilter.value;
const minPrice = priceMin.value ? parseFloat(priceMin.value) : null;
const maxPrice = priceMax.value ? parseFloat(priceMax.value) : null;
for (const row of table.tBodies[0].rows) {
const username = row.cells[0].textContent.toLowerCase();
const onlineStatus = row.cells[1].textContent.includes('✓') ? 'online' : 'offline';
const type = row.cells[4].textContent.toUpperCase();
let priceText = row.cells[6].textContent.trim();
let priceNum = parseFloat(priceText.match(/[\d,.]+/));
if (isNaN(priceNum)) priceNum = 0;
const itemName = row.cells[3].getAttribute('title').toLowerCase();
const matchesSearch = username.includes(searchText) || itemName.includes(searchText);
const matchesType = (typeValue === 'all') || (type === typeValue);
const matchesOnline = (onlineValue === 'all') || (onlineValue === onlineStatus);
const matchesMin = (minPrice === null) || (priceNum >= minPrice);
const matchesMax = (maxPrice === null) || (priceNum <= maxPrice);
if (matchesSearch && matchesType && matchesOnline && matchesMin && matchesMax) {
row.style.display = '';
} else {
row.style.display = 'none';
}
}
}
// Add listeners
searchInput.addEventListener('input', filterRows);
typeFilter.addEventListener('change', filterRows);
onlineFilter.addEventListener('change', filterRows);
priceMin.addEventListener('input', filterRows);
priceMax.addEventListener('input', filterRows);
// Run initially
filterRows();
return true; // Filters added successfully
}
// Wait for the table to exist, check every 500ms
function waitForTable() {
if (!addSearchFilters()) {
setTimeout(waitForTable, 500);
}
}
waitForTable();
})();