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

})();