Facebook Enhancer v3.11

Hide Reels, Stories, Suggestions, Marketplace, Events, Shortcuts. Pause/mute videos, unwrap links, remove ads. Smart settings menu. No layout breakage. Reels removal fixed (June 2025 version). Persistent UI toggle with logging option and structure-safe removal strategy.

// ==UserScript==
// @name         Facebook Enhancer v3.11
// @namespace    https://github.com/TamperMonkeyDevelopment/TamperMonkeyScripts
// @version      3.11
// @description  Hide Reels, Stories, Suggestions, Marketplace, Events, Shortcuts. Pause/mute videos, unwrap links, remove ads. Smart settings menu. No layout breakage. Reels removal fixed (June 2025 version). Persistent UI toggle with logging option and structure-safe removal strategy.
// @author       Eliminater74
// @match        *://www.facebook.com/*
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const STORAGE_KEY = 'fb-enhancer-settings';
  const defaultSettings = {
    blockSponsored: true,
    blockSuggested: true,
    disableAutoplay: true,
    muteVideos: true,
    hideReels: true,
    hideStories: true,
    hidePeopleYouMayKnow: true,
    unwrapLinks: true,
    autoExpandComments: true,
    toggleMarketplace: false,
    toggleEvents: false,
    toggleShortcuts: false,
    keywordFilter: 'kardashian,tiktok,reaction',
    forceMostRecentFeed: true,
    forceDarkMode: false,
    customCSS: '',
    debugMode: false
  };

  let settings = loadSettings();

  function loadSettings() {
    try {
      const saved = JSON.parse(localStorage.getItem(STORAGE_KEY));
      return Object.assign({}, defaultSettings, saved || {});
    } catch {
      return { ...defaultSettings };
    }
  }

  function saveSettings() {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
  }

  function applyCustomCSS() {
    const id = 'fb-enhancer-custom-style';
    document.getElementById(id)?.remove();
    if (settings.customCSS) {
      const style = document.createElement('style');
      style.id = id;
      style.textContent = settings.customCSS;
      document.head.appendChild(style);
    }
  }

  function createSettingsMenu() {
    const button = document.createElement('div');
    button.textContent = '⚙ Enhancer';
    button.style.cssText = 'position:fixed;top:60px;right:10px;background:#4267B2;color:#fff;padding:5px 10px;border-radius:5px;z-index:99999;cursor:pointer;font-weight:bold;';
    button.id = 'fb-enhancer-toggle';

    const panel = document.createElement('div');
    panel.id = 'fb-enhancer-panel';
    panel.style.cssText = 'position:fixed;top:100px;right:10px;z-index:99999;background:#fff;padding:10px;width:280px;max-height:70vh;overflow:auto;border:1px solid #ccc;border-radius:6px;font-size:14px;display:none;';

    const html = ['<h3>Facebook Enhancer</h3>'];
    for (let key in settings) {
      const val = settings[key];
      if (typeof val === 'boolean') {
        html.push(`<label><input type="checkbox" id="${key}" ${val ? 'checked' : ''}/> ${key}</label>`);
      } else {
        html.push(`<label>${key}<input type="text" id="${key}" value="${val}" style="width:100%"/></label>`);
      }
    }
    html.push('<button id="fb-save">Save</button> <button id="fb-reset">Reset</button>');
    panel.innerHTML = html.join('<br>');

    button.onclick = () => {
      panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
    };

    document.body.appendChild(button);
    document.body.appendChild(panel);
    makeDraggable(button);
    makeDraggable(panel);

    document.getElementById('fb-save').onclick = () => {
      for (let key in settings) {
        const input = document.getElementById(key);
        if (!input) continue;
        settings[key] = input.type === 'checkbox' ? input.checked : input.value.trim();
      }
      saveSettings();
      alert('Settings saved. Reloading...');
      location.reload();
    };

    document.getElementById('fb-reset').onclick = () => {
      localStorage.removeItem(STORAGE_KEY);
      alert('Settings reset. Reloading...');
      location.reload();
    };
  }

  function makeDraggable(el) {
    let offsetX = 0,
      offsetY = 0,
      isDragging = false;
    el.addEventListener('mousedown', e => {
      isDragging = true;
      offsetX = e.clientX - el.getBoundingClientRect().left;
      offsetY = e.clientY - el.getBoundingClientRect().top;
    });
    document.addEventListener('mousemove', e => {
      if (isDragging) {
        el.style.left = `${e.clientX - offsetX}px`;
        el.style.top = `${e.clientY - offsetY}px`;
        el.style.right = 'auto';
        el.style.bottom = 'auto';
      }
    });
    document.addEventListener('mouseup', () => (isDragging = false));
  }
  function unwrapLinks() {
    document.querySelectorAll('a[href*="l.facebook.com/l.php"]').forEach(a => {
      try {
        const url = new URL(a.href);
        const real = decodeURIComponent(url.searchParams.get('u'));
        if (real) a.href = real;
      } catch {}
    });
  }

  function muteAndPauseVideos() {
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        const video = entry.target;
        if (entry.isIntersecting && !video.dataset.fbEnhanced) {
          video.removeAttribute('autoplay');
          if (settings.muteVideos) video.muted = true;
          if (settings.disableAutoplay && !video.paused) video.pause();
          video.dataset.fbEnhanced = 'true';
          if (settings.debugMode) console.log('[FB Enhancer] Muted & paused video');
        }
      });
    }, { threshold: 0.5 });

    document.querySelectorAll('video:not([data-fb-enhanced])').forEach(video => observer.observe(video));
  }

  function pauseVideosOnScroll() {
    let ticking = false;
    window.addEventListener('scroll', () => {
      if (!ticking) {
        window.requestAnimationFrame(() => {
          document.querySelectorAll('video').forEach(video => {
            const rect = video.getBoundingClientRect();
            const inView = rect.top >= 0 && rect.bottom <= window.innerHeight;
            if (!inView && !video.paused && settings.disableAutoplay) {
              video.pause();
              if (settings.debugMode) console.log('[FB Enhancer] Paused on scroll');
            }
          });
          ticking = false;
        });
        ticking = true;
      }
    });
  }

  function autoExpandComments(post) {
    post.querySelectorAll('div[role="button"]').forEach(btn => {
      if (/view (more )?comments|replies/i.test(btn.textContent)) btn.click();
    });
  }

  function runEnhancer(post) {
    try {
      const text = post.innerText.toLowerCase();
      if (settings.blockSponsored && /sponsored/i.test(text)) return post.remove();
      if (settings.blockSuggested && /suggested for you/i.test(text)) return post.remove();
      if (settings.keywordFilter && settings.keywordFilter.split(',').some(k => text.includes(k.trim()))) return post.remove();
      if (settings.autoExpandComments) autoExpandComments(post);
    } catch (err) {
      if (settings.debugMode) console.warn('[FB Enhancer] Post error:', err);
    }
  }

  function observeFeed() {
    const observer = new MutationObserver(mutations => {
      mutations.forEach(m => {
        m.addedNodes.forEach(node => {
          if (node.nodeType === 1 && node.getAttribute('role') === 'article') runEnhancer(node);
        });
      });
    });
    const feed = document.querySelector('[role="feed"]');
    if (feed) observer.observe(feed, { childList: true, subtree: true });
    document.querySelectorAll('[role="article"]').forEach(runEnhancer);
  }

  // ✅ Reels blocking with multi-method detection
  function hideReels() {
    const matches = [
      'div[aria-label="Reels"]',
      'div[role="complementary"] h3',
      'h3 span',
      'div[data-pagelet]'
    ];

    matches.forEach(selector => {
      document.querySelectorAll(selector).forEach(el => {
        const label = el.innerText?.toLowerCase();
        const container = el.closest('div[data-pagelet]') || el.closest('div[role="complementary"]') || el.closest('div');

        if (!container || container.dataset.fbEnhanced === '1') return;
        const hasVideos = container.querySelectorAll('video').length >= 3;
        const looksLikeReels = label?.includes('reels') && hasVideos;

        if (looksLikeReels) {
          container.remove();
          container.dataset.fbEnhanced = '1';
          if (settings.debugMode) console.log('[FB Enhancer] ✅ Reels block removed:', container);
        }
      });
    });
  }

  function hideStories() {
    document.querySelectorAll('div[aria-label="Stories"], div[data-pagelet*="Stories"]').forEach(el => {
      el.remove();
      if (settings.debugMode) console.log('[FB Enhancer] Removed Stories');
    });
  }

  function hidePeopleYouMayKnow() {
    document.querySelectorAll('[role="feed"] div').forEach(block => {
      const text = block.innerText?.toLowerCase();
      const isHeader = text?.includes('people you may know');
      const hasButtons = [...block.querySelectorAll('button')].some(b => b.innerText.toLowerCase().includes('add friend'));
      if (isHeader && hasButtons) {
        block.remove();
        if (settings.debugMode) console.log('[FB Enhancer] Removed People You May Know');
      }
    });
  }

  function collapseSidebarSections() {
    const map = {
      toggleMarketplace: 'marketplace',
      toggleEvents: 'events',
      toggleShortcuts: 'your shortcuts'
    };

    for (let key in map) {
      if (!settings[key]) continue;
      const matchText = map[key];
      document.querySelectorAll('span, div').forEach(el => {
        const txt = el.textContent?.toLowerCase();
        if (!txt || !txt.includes(matchText)) return;
        const container = el.closest('ul') || el.closest('li') || el.closest('div[role="navigation"]');
        if (container && !container.dataset.fbEnhanced) {
          container.style.display = 'none';
          container.dataset.fbEnhanced = '1';
          if (settings.debugMode) console.log(`[FB Enhancer] Collapsed sidebar: ${matchText}`);
        }
      });
    }
  }

  function forceMostRecent() {
    const link = document.querySelector('a[href*="sk=h_chr"]');
    if (link) link.click();
  }

  function applyEnhancements() {
    if (settings.unwrapLinks) unwrapLinks();
    if (settings.hideReels) hideReels();
    if (settings.hideStories) hideStories();
    if (settings.hidePeopleYouMayKnow) hidePeopleYouMayKnow();
    if (settings.toggleMarketplace || settings.toggleEvents || settings.toggleShortcuts) collapseSidebarSections();
    if (settings.forceMostRecentFeed) forceMostRecent();
    if (settings.forceDarkMode) document.documentElement.classList.add('fb-dark-mode');
    muteAndPauseVideos();
    if (settings.disableAutoplay) pauseVideosOnScroll();
  }

  // 🚀 Run Everything
  applyCustomCSS();
  createSettingsMenu();
  applyEnhancements();
  observeFeed();
})();