Medium Freedium Reader

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

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==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);

})();