nascent / OnTheMarket - Remove Non-Freehold Properties

// ==UserScript==
// @name         OnTheMarket - Remove Non-Freehold Properties
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Remove properties that aren't freehold from OnTheMarket search results
// @author       nascent
// @updateURL    https://openuserjs.org/meta/nascent/OnTheMarket_-_Remove_Non-Freehold_Properties.meta.js
// @downloadURL  https://openuserjs.org/install/nascent/OnTheMarket_-_Remove_Non-Freehold_Properties.user.js
// @copyright    2025, nascent (https://openuserjs.org/users/nascent)
// @license      MIT
// @match        https://www.onthemarket.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function removeNonFreeholdProperties() {
        const propertyCards = document.querySelectorAll('li[id^="result-"]');
        let removedCount = 0;

        propertyCards.forEach(card => {
            const propertyInfo = card.querySelector('[data-component="property-info"]');
            if (!propertyInfo) return;

            const textContent = propertyInfo.textContent;

            // Check if the property contains "Tenure: Freehold"
            const isFreehold = /Tenure:\s*Freehold(?!\s*\()/i.test(textContent);

            // If not freehold (or tenure info missing), remove it
            if (!isFreehold) {
                card.style.display = 'none';
                removedCount++;
            }
        });

        if (removedCount > 0) {
            console.log(`Removed ${removedCount} non-freehold properties`);
        }
    }

    // Run initially
    removeNonFreeholdProperties();

    // Set up MutationObserver to watch for dynamically loaded content
    const observer = new MutationObserver((mutations) => {
        let shouldCheck = false;

        mutations.forEach((mutation) => {
            if (mutation.addedNodes.length > 0) {
                mutation.addedNodes.forEach((node) => {
                    if (node.nodeType === 1) { // Element node
                        if (node.id && node.id.startsWith('result-')) {
                            shouldCheck = true;
                        } else if (node.querySelector && node.querySelector('li[id^="result-"]')) {
                            shouldCheck = true;
                        }
                    }
                });
            }
        });

        if (shouldCheck) {
            removeNonFreeholdProperties();
        }
    });

    // Start observing the document body for changes
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    console.log('OnTheMarket Freehold Filter: Active');
})();