SpringerLink – Zugriffsfilter

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

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Advertisement:

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

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();
    }

})();