RSS+Atom Feed Subscribe Button Generator

Finds RSS and/or Atom links on a page and inserts feed subscription links for use by aggregators

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @id          RSS+Atom Feed Subscribe Button Generator
// @name        RSS+Atom Feed Subscribe Button Generator
// @description Finds RSS and/or Atom links on a page and inserts feed subscription links for use by aggregators
// @namespace   http://loonyone.livejournal.com
// @include     *
// @creator     Manpreet Singh [[email protected]]
// @copyright   Manpreet Singh [[email protected]]
// @author      Manpreet Singh [[email protected]]
// @version     2.7
// @date        2022-04-20
// @inject-into content
// ==/UserScript==

/*
 * Copyright (c) 2006-2022, Manpreet Singh [[email protected]]
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

// ******
// Opera, Chrome, Chromium, Safari and IE7Pro compatible except
//
// 1) link tags injection does not work in IE7Pro
//
// due to non-firefox platform limitations/differences.
//
// IE7Pro bundles an older version of this script by default.
// ******

// Version 2.7 - Further improve display CSS enforcement
// Version 2.6 - Better injection of icons on some strict CSP sites
// Version 2.5 - New home at greasyfork.org
//               Removed auto-update code
// Version 2.4 - Constrained container background and border
// Version 2.3 - Constrained button dimensions some more
// Version 2.2 - Constrained button dimensions
// Version 2.1 - Fixed Content Security Policy issues
// Version 2.0 - 'more' button was broken for me
//               some javascript lint cleanup
//               Lighter icons by simon!
// Version 1.9 - Fixed invalid update metadata
// Version 1.8 - Layout fix on apple.com/macupdate.com etc.
// Version 1.7 - Minor layout fix on arstechnica.com
// Version 1.6 - Insert discovered link tags in header for the browser's detection to list them too
// Version 1.5 - Added license info
//               Added checks for feeds like &feed=rss or &feed=atom and some case insensitivity
// Version 1.4 - Made the feed check slightly more stringent by excluding javascript: mailto: etc. urls
// Version 1.3 - Reduce the number of button shown initially to a sane minimum
// Version 1.2 - Fixed partially broken auto update.
//               Don't increase the install count on userscripts.org while checking for updates
// Version 1.1 - Misc code cleanup
// Version 1.0 - Added auto update without requiring an external script
// Version 0.9 - Added a close button
// Version 0.8 - Checks some wiki feeds with ?action=rss_rc and feeds with /rss/$ or /atom/$
// Version 0.7 - Check for feedburner feeds
// Version 0.6 - Fixed a major issue with using snapshotLength when it was not available
// Version 0.5 - Updated contact info
// Version 0.4 - Added optional auto-update facility
// Version 0.3 - Added some speed optimizations
// Version 0.2 - Merged Iain Brodfoot's floating div behaviour and removed purl.org links
//             - Allow revert to old behaviour by hand editing this script
// Version 0.1 - First release

/*
 * Options contribution by Iain Broadfoot ([email protected])
 * - remove link to purl
 *   (Manually edit this script to restore old behaviour. See TWEAK NOTE 1 below)
 * - Float the buttons to enable better behaviour on blogger/blogspot.com
 *   (Manually edit this script to restore old behaviour. See TWEAK NOTE 2 below)
 *
 * Initially lifted mostly from
 *   Generate RSS and ATOM tags: http://highbyte.bounceme.net/greasemonkey/genrsslinkrel.user.js
 *   Amazon Atom Injector:       http://docs.g-blog.net/code/greasemonkey/amazon_xml_feeds.user.js
 *   Add RSS Index:              http://www.xs4all.nl/~jlpoutre/BoT/Javascript/RSSpanel/rsspanel.user.js
 *
 * Original buttons from the excellent: http://kalsey.com/tools/buttonmaker/
 */

console.log('rssatom_feed_subscribe_b.user.js starting');
try {
    const head = document.head;
    const INITIAL_COUNT = 5;
    const ATOM_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPBAMAAACB51W8AAAAAXNSR0IArs4c6QAAACRQTFRFAAEAAxx+JCYjYWNgho53kJKPy6Gio63Ys7Wysb3lyMnG/f/8AJKv4wAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfcBhsNKDWiRqoQAAAA2UlEQVQoz42SoQ7CMBCGL10w7CWWEAwzNRiwOEzD5DwvgSGoJVUoBAZFSGeWQZPSeznabutYtiX709b0y3/X/wowVSFW0vZ4iSMOq2hAGVgux2d98dl15UFmQMVr4zHwAAsFsMXMAJdltKKzYVACgTVAbA2RucYdaLZfDtSJhJgGPMPvHRk5QwKpvWvXvi5NjZ3pkeP1hDQoiaxAb9eAb0gtqDneULMW7DmWplb8gI19rOiC3R4VkCgsYW5T539gPx7lhmITFH4So4G7qM1kpoBC5DiqYvLn+QGXavuS25r9yAAAAABJRU5ErkJggg==";
    const RSS_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPAgMAAAAOp6AcAAAAAXNSR0IArs4c6QAAAAxQTFRFAAAA/2YAiY55////S8IQBgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFRMROauDVnkAAAB7SURBVBjTpc8xDoAgDEBRLuXsKToaT0FMungfMLgz4CmMdygzC1G0yGIii/EPDM0LaYWo1Z6vdtFADtWj6ctwhKNfcTE26ehm8nHhodxAApKx3iifX3vL0AM6Y09NxDJlOUAHK2o2ihkVKbcwAiZNaSb25c9/e1Zvr3UBWOasaPHYnJwAAAAASUVORK5CYII=";
    const RDF_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPAgMAAAAOp6AcAAAAAXNSR0IArs4c6QAAAAxQTFRFAAAA/2YAiY55////S8IQBgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFRMKD80PCHoAAAB3SURBVBjTpc8xDsAQGIZhl+rcU4jJMaSJxX1o2A0cwx1+s0Ua+muXJrU0fQfDlycCIbPW/uogCx1J/Wj/MipWFJXR+mZqcJBrxFEkLqgE67PVeZz+khxlsL4bAJRtyI0NadBoZHBLkQqOzUBzgP6+8987p3+fdQKJXassgfh4CAAAAABJRU5ErkJggg==";
    const XML_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPAgMAAAAOp6AcAAAAAXNSR0IArs4c6QAAAAxQTFRFAAAA/2YAiY55////S8IQBgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFRMRFO5cCgwAAAB3SURBVBjTY2DABuz/Y4A/DKahIFC1CgmsIUWwtPTq9dCq/et2/Vv9a9/6V69/7QcKxl79DhR8tW7X63WrXoPIXUDB8NC7QMF963b9X/3qFVDlP4hKkPbVQDWrgMpeQVSCzPxa9W/1q3/rXwHVQ8ykzJ1Y/Y4NAADEZ67GIr3R+wAAAABJRU5ErkJggg==";
    const CLOSE_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPAgMAAABGuH3ZAAAAAXNSR0IArs4c6QAAAAlQTFRF/2YA////AAAALA0mBgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAEnMAABJzAYwiuQcAAAAHdElNRQfcCxUTCRv8+I/EAAAAMklEQVQI12NYtWrVCoapoaERDBMYGCSghAMLkBAQABIsDkCCkQHGAouBZeGKwXpBpgAAhtMRBKRIycIAAAAASUVORK5CYII=";
    const MORE_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAPAgMAAADrOWVwAAAAAXNSR0IArs4c6QAAAAxQTFRFAAEA/mcAh494/v/8doDPMwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFRMNODrzO7IAAABNSURBVAjXY2BAAPv/UPCHwTQUDKpWrcHGfAVklpbWxl/9W/ULyKz9ej229GrVPyCzvPQ6SPQ/WMH12KsQUaDa2FKIWiQTsFqB5AYEAABsXUkpSEP5/AAAAABJRU5ErkJggg==";
    
    const seen = [];
    const fsbdiv = document.createElement("div");
    fsbdiv.setAttribute("id", "XmlButtons");
    fsbdiv.style.cssText = "text-align: left !important; margin: 0px !important; padding: 0px 3px 0px 3px !important; border: none !important; background: none !important; position: fixed !important; top: 0px !important; left: 0px !important; z-index: 99999 !important; display: inline !important;";

    function getKnownTags() {
        try {
            const linkRelElems = document.getElementsByTagName("link");
            
            for (const tlink of linkRelElems) {
                if (!tlink) continue;
                try {
                    const thref = tlink.href;
                    const type = tlink.type;

                    if (type?.match(/.*\/rs[sd](\+xml)?$/i)) {
                        addRssLinkTag(thref, RSS_ICON);
                    } else if (type?.match(/.*\/atom\+xml$/i)) {
                        addRssLinkTag(thref, ATOM_ICON);
                    } else if (type?.match(/^text\/xml$/i)) {
                        addRssLinkTag(thref, RSS_ICON);
                    }
                } catch (e) {
                    console.log(e);
                }
            }
        } catch (ex) {
            console.log(ex);
        }
    }

    function discoverUnknownFeeds() {
        try {
            const links = document.links || document.getElementsByTagName("a");
            
            for (const link of links) {
                const linkhref = link.href;

                if (linkhref.match(/^rss:/) ||
                    linkhref.match(/^(http|ftp|feed).*([\.\/]rss([\.\/]xml|\.aspx|\.jsp|\/)?$|\/node\/feed$|\/rss\/[a-z0-9]+$|[?&;](rss|xml|rdf)=|[?&;]feed=rss[0-9.]*$|[?&;]action=rss_rc$|feeds\.feedburner\.com\/[a-z0-9]+$)/i)) {
                    addRssLinkTag(linkhref, RSS_ICON, "rss");
                } else if (linkhref.match(/^(http|ftp|feed).*\/atom(\.xml|\.aspx|\.jsp|\/)?$|[?&;]feed=atom[0-9.]*$/i)) {
                    addRssLinkTag(linkhref, ATOM_ICON, "atom");
                } else if (linkhref.match(/^(http|ftp|feed).*(\/feeds?\/[^.\/]*\.xml$|.*\/index\.xml$|feed\/msgs\.xml(\?num=\d+)?$)/i)) {
                    addRssLinkTag(linkhref, XML_ICON, "rss");
                } else if (linkhref.match(/^(http|ftp|feed).*\.rdf$/i)) {
                    addRssLinkTag(linkhref, RDF_ICON, "rss");
                } else if (linkhref.match(/^feed:\/\//i)) {
                    addRssLinkTag(linkhref, RSS_ICON, "rss");
                }
            }
        } catch (e) {
            console.log(e);
        }
    }

    function beenThere(linkhref) {
        if (seen.length <= 0) return false;
        const href = linkhref.toLowerCase();
        return seen.some(s => s.toLowerCase() === href);
    }

    function moreFSB() {
        for (let i = 1;; i++) {
            const elem = document.getElementById(`XmlButton${i}`);
            if (!elem) break;
            try {
                elem.style.cssText = "display: inline !important;";
            } catch (e) {}
        }
        document.getElementById("MoreFSBButton").style.cssText = "display: none !important;";
        return false;
    }

    function addRssLinkTag(linkhref, icon, addheadtype) {
        try {
            if (beenThere(linkhref)) return;
            seen.push(linkhref);

            if (seen.length === (INITIAL_COUNT + 1)) {
                const moreButton = document.createElement('a');
                const moreButton_img = document.createElement('img');
                moreButton_img.src = MORE_ICON;
                moreButton_img.style.cssText = "border: none !important; margin: 0px !important; width: 40px !important; height: 15px !important; display: inline !important";
                moreButton.appendChild(moreButton_img);
                moreButton.style.cssText = "display: inline !important; cursor: pointer !important;";
                moreButton.id = 'MoreFSBButton';
                moreButton.addEventListener('click', moreFSB, true);
                fsbdiv.appendChild(moreButton);
                fsbdiv.appendChild(document.createTextNode(" "));
            }
            
            const flink = document.createElement("a");
            const flink_img = document.createElement("img");
            flink_img.src = icon;
            flink_img.style.cssText = "border: none !important; margin: 0px !important; width: 80px !important; height: 15px !important; display: inline !important";
            flink.appendChild(flink_img);
            flink.href = linkhref;
            flink.alt = linkhref;
            flink.title = linkhref;
            flink.id = `XmlButton${seen.length}`;
            
            if (seen.length >= (INITIAL_COUNT + 1)) {
                flink.style.cssText = "display: none !important";
            }
            fsbdiv.appendChild(flink);
            fsbdiv.appendChild(document.createTextNode(" "));
            
            if (addheadtype && head) {
                const link = document.createElement("link");
                link.href = linkhref;
                link.rel = "alternate";
                link.title = `${linkhref} (discovered by RSS+Atom Feed Subscribe Button Generator)`;
                link.type = `application/${addheadtype}+xml`;
                head.appendChild(link);
            }
        } catch (e) {
            console.log(e);
        }
    }

    try {
        getKnownTags();
        discoverUnknownFeeds();
    } catch (e) {}

    const body = document.body;

    function closeFSB() {
        try {
            document.getElementById("XmlButtons").style.cssText = "display: none !important";
        } catch (e) {}
    }
    
    try {
        if (seen.length > 0 && head) {
            const closeButton = document.createElement('a');
            const closeButton_img = document.createElement('img');
            closeButton_img.src = CLOSE_ICON;
            closeButton_img.style.cssText = "border: none !important; margin: 0px !important; width: 15px !important; height: 15px !important; display: inline !important";
            closeButton.appendChild(closeButton_img);
            closeButton.addEventListener('click', closeFSB, true);
            closeButton.style.cssText = "display: inline !important; cursor: pointer !important;";
            
            fsbdiv.insertBefore(closeButton, fsbdiv.firstChild);
            setTimeout(() => {
                body.insertBefore(fsbdiv, body.firstChild);
            }, 333);
        }
    } catch (e) {}
} catch (outere) {
    console.log(outere);
}
console.log('rssatom_feed_subscribe_b.user.js ended');
// vim: set et fdm=indent fenc=utf-8 ff=unix ft=javascript sts=0 sw=4 ts=4 tw=0 nowrap :