SpringerLink – Zugriffsfilter

Blendet Suchergebnisse ohne "Vollen Zugriff" aus und zeigt ein Steuerfeld

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

Advertisement:

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

Advertisement:

// ==UserScript==
// @name         SpringerLink – Zugriffsfilter
// @namespace    https://link.springer.com/
// @version      1.3
// @description  Blendet Suchergebnisse ohne "Vollen Zugriff" aus und zeigt ein Steuerfeld
// @author       Nader Rupiani
// @match        https://link.springer.com/*
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ── Styles ────────────────────────────────────────────────────────────────
    GM_addStyle(`
        #sl-access-panel {
            position: fixed;
            bottom: 24px;
            right: 24px;
            z-index: 99999;
            background: #fff;
            border: 1.5px solid #006EAF;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.15);
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
            font-size: 13px;
            color: #1a1a1a;
            min-width: 220px;
            user-select: none;
            overflow: hidden;
        }
        #sl-panel-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 11px 14px;
            cursor: pointer;
            background: #f5f9fd;
            border-bottom: 1.5px solid #d0e4f0;
            gap: 8px;
        }
        #sl-panel-header:hover { background: #e8f1f9; }
        #sl-panel-title {
            font-size: 13px;
            font-weight: 700;
            color: #006EAF;
            letter-spacing: 0.3px;
            display: flex;
            align-items: center;
            gap: 6px;
            margin: 0;
        }
        #sl-panel-title::before { content: "🔒"; font-size: 14px; }
        #sl-collapse-icon {
            font-size: 11px;
            color: #006EAF;
            transition: transform 0.2s;
            flex-shrink: 0;
        }
        #sl-collapse-icon.collapsed { transform: rotate(-90deg); }
        #sl-panel-body {
            padding: 14px 18px;
            transition: max-height 0.25s ease, opacity 0.2s ease, padding 0.2s ease;
            max-height: 300px;
            opacity: 1;
            overflow: hidden;
        }
        #sl-panel-body.collapsed {
            max-height: 0;
            opacity: 0;
            padding-top: 0;
            padding-bottom: 0;
        }
        #sl-stats {
            margin-bottom: 12px;
            line-height: 1.6;
        }
        #sl-stats .stat-row {
            display: flex;
            justify-content: space-between;
            gap: 12px;
        }
        #sl-stats .stat-value {
            font-weight: 700;
        }
        #sl-stats .stat-value.full { color: #2e7d32; }
        #sl-stats .stat-value.no   { color: #c62828; }
        .sl-toggle-btn {
            width: 100%;
            padding: 7px 10px;
            border: none;
            border-radius: 7px;
            cursor: pointer;
            font-size: 12.5px;
            font-weight: 600;
            transition: background 0.2s, opacity 0.2s;
        }
        #sl-btn-hide {
            background: #006EAF;
            color: #fff;
            margin-bottom: 6px;
        }
        #sl-btn-hide:hover { background: #005a8e; }
        #sl-btn-dim {
            background: #f0f4f8;
            color: #444;
            margin-bottom: 6px;
        }
        #sl-btn-dim:hover  { background: #dce6ef; }
        #sl-btn-show {
            background: #f0f4f8;
            color: #444;
        }
        #sl-btn-show:hover { background: #dce6ef; }

        /* States applied to result cards */
        .sl-no-access--hidden {
            display: none !important;
        }
        .sl-no-access--dimmed {
            opacity: 0.25 !important;
            filter: grayscale(60%) !important;
            pointer-events: none;
        }
        .sl-no-access-badge {
            display: inline-block;
            background: #c62828;
            color: #fff;
            font-size: 10px;
            font-weight: 700;
            padding: 2px 6px;
            border-radius: 4px;
            margin-left: 6px;
            vertical-align: middle;
            letter-spacing: 0.5px;
        }
    `);

    // ── State ─────────────────────────────────────────────────────────────────
    let mode = 'badge'; // 'badge' | 'dim' | 'hide'

    // ── Panel ─────────────────────────────────────────────────────────────────
    function buildPanel() {
        if (document.getElementById('sl-access-panel')) return;

        const panel = document.createElement('div');
        panel.id = 'sl-access-panel';
        panel.innerHTML = `
            <div id="sl-panel-header">
                <h4 id="sl-panel-title">Zugriffsfilter</h4>
                <span id="sl-collapse-icon">▼</span>
            </div>
            <div id="sl-panel-body">
                <div id="sl-stats">
                    <div class="stat-row">
                        <span>✅ Voller Zugriff</span>
                        <span class="stat-value full" id="sl-count-full">–</span>
                    </div>
                    <div class="stat-row">
                        <span>🔒 Kein Zugriff</span>
                        <span class="stat-value no" id="sl-count-no">–</span>
                    </div>
                </div>
                <button class="sl-toggle-btn" id="sl-btn-hide">Ausblenden</button>
                <button class="sl-toggle-btn" id="sl-btn-dim">Abdunkeln</button>
                <button class="sl-toggle-btn" id="sl-btn-show">Alle einblenden</button>
            </div>
        `;
        document.body.appendChild(panel);

        // Collapse toggle
        let collapsed = false;
        document.getElementById('sl-panel-header').addEventListener('click', () => {
            collapsed = !collapsed;
            document.getElementById('sl-panel-body').classList.toggle('collapsed', collapsed);
            document.getElementById('sl-collapse-icon').classList.toggle('collapsed', collapsed);
        });

        document.getElementById('sl-btn-hide').addEventListener('click', () => setMode('hide'));
        document.getElementById('sl-btn-dim').addEventListener('click',  () => setMode('dim'));
        document.getElementById('sl-btn-show').addEventListener('click', () => setMode('badge'));
    }

    function setMode(newMode) {
        mode = newMode;
        applyMode();
    }

    // ── Core logic ────────────────────────────────────────────────────────────
    function scanAndTag() {
        const cards = document.querySelectorAll('li.app-card-open');
        let countFull = 0;
        let countNo   = 0;

        cards.forEach(card => {
            // Remove previous badges to avoid duplicates on re-scan
            card.querySelectorAll('.sl-no-access-badge').forEach(b => b.remove());
            card.classList.remove('sl-no-access', 'sl-no-access--hidden', 'sl-no-access--dimmed');

            const hasFullAccess = !!card.querySelector(
                '.app-entitlement__icon--full-access, [data-test="entitlements"]'
            ) && card.querySelector('[data-test="entitlements"]')?.textContent.trim().toLowerCase().includes('full access');

            if (hasFullAccess) {
                countFull++;
            } else {
                countNo++;
                card.classList.add('sl-no-access');

                // Add badge next to title
                const title = card.querySelector('.app-card-open__heading, h3');
                if (title && !title.querySelector('.sl-no-access-badge')) {
                    const badge = document.createElement('span');
                    badge.className = 'sl-no-access-badge';
                    badge.textContent = 'KEIN ZUGRIFF';
                    title.appendChild(badge);
                }
            }
        });

        // Update counters
        const elFull = document.getElementById('sl-count-full');
        const elNo   = document.getElementById('sl-count-no');
        if (elFull) elFull.textContent = countFull;
        if (elNo)   elNo.textContent   = countNo;

        applyMode();
    }

    function applyMode() {
        const noAccessCards = document.querySelectorAll('.sl-no-access');
        noAccessCards.forEach(card => {
            card.classList.remove('sl-no-access--hidden', 'sl-no-access--dimmed');
            if (mode === 'hide') {
                card.classList.add('sl-no-access--hidden');
            } else if (mode === 'dim') {
                card.classList.add('sl-no-access--dimmed');
            }
            // 'badge' mode: leave visible, badge already attached
        });

        // Update button active state
        ['sl-btn-hide', 'sl-btn-dim', 'sl-btn-show'].forEach(id => {
            const btn = document.getElementById(id);
            if (btn) btn.style.outline = '';
        });
        const activeMap = { hide: 'sl-btn-hide', dim: 'sl-btn-dim', badge: 'sl-btn-show' };
        const activeBtn = document.getElementById(activeMap[mode]);
        if (activeBtn) activeBtn.style.outline = '2px solid #006EAF';
    }

    // ── MutationObserver – handles pagination / dynamic content ───────────────
    let debounceTimer = null;
    const observer = new MutationObserver(() => {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => {
            buildPanel();
            scanAndTag();
        }, 400);
    });

    function init() {
        buildPanel();
        scanAndTag();

        // Watch the search results container (or body as fallback)
        const target = document.querySelector('#results-list, [data-test="results"], main') || document.body;
        observer.observe(target, { childList: true, subtree: true });
    }

    // Wait for DOM ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();