Medium Freedium Reader

Adds "Read Free" button to Medium articles to open them in Freedium

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Medium Freedium Reader
// @namespace    https://github.com/DiyRex/UserScripts/Medium-Freedium-Reader
// @version      1.1.0
// @description  Adds "Read Free" button to Medium articles to open them in Freedium
// @author       DiyRex
// @match        https://medium.com/*
// @match        https://*.medium.com/*
// @match        https://towardsdatascience.com/*
// @match        https://levelup.gitconnected.com/*
// @match        https://betterprogramming.pub/*
// @match        https://javascript.plainenglish.io/*
// @match        https://aws.plainenglish.io/*
// @match        https://python.plainenglish.io/*
// @match        https://blog.devops.dev/*
// @match        https://itnext.io/*
// @match        https://awstip.com/*
// @match        https://betterhumans.pub/*
// @match        https://uxdesign.cc/*
// @match        https://entrepreneurshandbook.co/*
// @icon         https://freedium.cfd/favicon.ico
// @grant        GM_addStyle
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

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

    // Inject styles using GM_addStyle or fallback
    function injectStyles() {
        const css = `
            .freedium-btn {
                background: linear-gradient(135deg, #667eea, #764ba2) !important;
                color: #fff !important;
                border: none !important;
                border-radius: 16px !important;
                padding: 4px 10px !important;
                font-size: 11px !important;
                font-weight: 500 !important;
                cursor: pointer !important;
                text-decoration: none !important;
                font-family: system-ui, -apple-system, sans-serif !important;
                display: inline-flex !important;
                align-items: center !important;
                gap: 4px !important;
                margin-left: 12px !important;
                vertical-align: middle !important;
                transition: all 0.2s ease !important;
                line-height: 1.4 !important;
            }
            .freedium-btn:hover {
                opacity: 0.9 !important;
                transform: translateY(-1px) !important;
                box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4) !important;
            }
            .freedium-float {
                position: fixed !important;
                bottom: 24px !important;
                right: 24px !important;
                z-index: 999999 !important;
                padding: 10px 18px !important;
                font-size: 14px !important;
                border-radius: 24px !important;
                box-shadow: 0 4px 15px rgba(102, 126, 234, 0.5) !important;
            }
            .freedium-float:hover {
                transform: translateY(-2px) !important;
                box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6) !important;
            }
        `;

        if (typeof GM_addStyle !== 'undefined') {
            GM_addStyle(css);
        } else {
            const style = document.createElement('style');
            style.textContent = css;
            (document.head || document.documentElement).appendChild(style);
        }
    }

    // Get base domain for building URLs
    function getBaseDomain() {
        const host = window.location.hostname;
        if (host.includes('medium.com')) {
            return 'https://medium.com';
        }
        return window.location.origin;
    }

    // Check if current page is an article
    function isArticlePage() {
        const path = window.location.pathname;
        return path.match(/-[a-f0-9]{8,}$/i) || path.match(/\/p\/[a-f0-9]+/i);
    }

    // Add floating button on article pages
    function addFloatingButton() {
        if (!isArticlePage()) return;
        if (document.querySelector('.freedium-float')) return;

        const currentUrl = window.location.href.split('?')[0];
        const btn = document.createElement('a');
        btn.className = 'freedium-btn freedium-float';
        btn.href = 'https://freedium.cfd/' + currentUrl;
        btn.target = '_blank';
        btn.rel = 'noopener noreferrer';
        btn.innerHTML = '🔓 Read Free';
        btn.title = 'Open in Freedium';
        document.body.appendChild(btn);
    }

    // Add buttons to article cards in feed
    function addFeedButtons() {
        // Find article title links (h2 inside anchor)
        const titleLinks = document.querySelectorAll('a[href*="-"] h2, a[href*="-"] h3');

        titleLinks.forEach(heading => {
            const link = heading.closest('a');
            if (!link) return;

            let href = link.getAttribute('href');
            if (!href || !href.match(/-[a-f0-9]{8,}/i)) return;

            // Build full URL
            if (href.startsWith('/')) {
                href = getBaseDomain() + href;
            }
            href = href.split('?')[0];

            // Find the article container
            const article = link.closest('article') ||
                           link.closest('div[class]')?.parentElement?.parentElement;
            if (!article || article.dataset.freediumDone === '1') return;
            article.dataset.freediumDone = '1';

            // Find the action bar with buttons/icons
            const actionBar = article.querySelector('button[aria-label]')?.closest('div')?.parentElement ||
                             article.querySelector('svg')?.closest('div')?.parentElement?.parentElement;

            if (!actionBar || actionBar.querySelector('.freedium-btn')) return;

            // Create button
            const btn = document.createElement('a');
            btn.className = 'freedium-btn';
            btn.href = 'https://freedium.cfd/' + href;
            btn.target = '_blank';
            btn.rel = 'noopener noreferrer';
            btn.innerHTML = '🔓 Free';
            btn.title = 'Read on Freedium';
            btn.onclick = e => e.stopPropagation();

            actionBar.appendChild(btn);
        });
    }

    // Main function
    function init() {
        addFloatingButton();
        addFeedButtons();
    }

    // Start the script
    function start() {
        injectStyles();

        // Initial run with delays to catch dynamic content
        setTimeout(init, 1000);
        setTimeout(init, 2000);
        setTimeout(init, 3500);

        // Watch for dynamic content (Medium is SPA)
        const observer = new MutationObserver(() => {
            clearTimeout(window._freediumTimer);
            window._freediumTimer = setTimeout(init, 600);
        });

        // Start observing when body is available
        function startObserver() {
            if (document.body) {
                observer.observe(document.body, { childList: true, subtree: true });
            } else {
                setTimeout(startObserver, 100);
            }
        }
        startObserver();

        // Handle SPA navigation
        let lastUrl = location.href;
        setInterval(() => {
            if (location.href !== lastUrl) {
                lastUrl = location.href;
                document.querySelector('.freedium-float')?.remove();
                // Reset processed flags for new page
                document.querySelectorAll('[data-freedium-done]').forEach(el => {
                    el.removeAttribute('data-freedium-done');
                });
                setTimeout(init, 500);
                setTimeout(init, 1500);
            }
        }, 500);

        console.log('🔓 Medium Freedium Reader v1.1 loaded!');
    }

    // Run when ready
    onReady(start);

})();