Newspaper (Syndication Feed Reader)
This software renders syndication feeds as XHTML; It supports Atom Activity Streams (Friendica, Nostr, OStatus), Atom Over XMPP (Blasta, Libervia, Movim, Rivista), BitTorrent RSS, JSON Feed, OPML, RDF (DOAP, FOAF, RSS, XMPP), RSS-in-JSON, Simple Machines Forum (SMF), Sitemap, The Atom Syndication Format, and Twtxt; and it also supports navigation (RFC 5005).
Old: v25.11.01 - 2025-10-24 - Fix variables and declaruon thereof.
New: v25.11.02 - 2025-10-26 - Add support of FOAF https://planet.jabber.org/foafroll.xml;
Utilize XPath instead of CSS Selectors;
Further improve code structure; and
Add a special emergency message.
Please, help.
- @@ -1,7 +1,7 @@
- // ==UserScript==
- // @name Newspaper (Syndication Feed Reader)
- // @namespace i2p.schimon.newspaper
-// @description Native Feed Viewer. Render syndication feeds (supports ActivityStreams, Atom Syndication Format, JSON Feed, Nostr, OPML, OStatus, RDF, RSS, RSS-in-JSON, Sitemap, SMF, and Twtxt).
- +// @description Native Feed Viewer. Render syndication feeds (supports ActivityStreams, Atom Syndication Format, FOAF, JSON Feed, Nostr, OPML, OStatus, RDF, RSS, RSS-in-JSON, Sitemap, SMF, and Twtxt).
- // @author Schimon Jehudah
- // @collaborator CY Fung
- // @collaborator NotYou
- @@ -14,15 +14,18 @@
- // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48dGV4dCB5PSIuOWVtIiBmb250LXNpemU9IjkwIj7wn5OwPC90ZXh0Pjwvc3ZnPgo=
- // @match file:///*
- // @match *://*/*
- +// @noframes
- // @exclude *?streamburner=0*
- // @exclude *&streamburner=0*
-// @version 25.11.01
- +// @version 25.11.02
- // @run-at document-start
- // @grant GM.setValue
- // @grant GM.getValue
- // @grant GM.registerMenuCommand
- // ==/UserScript==
-
- +"use strict";
- +
- /*
-
- TODO
- @@ -30,11 +33,6 @@
- Friendica Atom OStatus Feeds
- https://my-place.social/display/feed-item/55851418.atom
-
-ADD AN XSLT TO THE OPML!
-
-XPath: Migrate to XPath so that it would be easier to
-select and possible to select elements with colon.
-
- json
- https://timburton.com/news?format=json
- https://pentagon-mushroom-xbey.squarespace.com/news?format=json
- @@ -75,123 +73,29 @@
-
- */
-
-const
- namespace = "i2p.schimon.newspaper",
- defaultTitle = "Streamburner",
- // This news feed is brought to you by Streamburner News Reader
- defaultSubtitle = "News feed rendered with Streamburner",
- defaultAbout = "No description was provided.",
- rtlLocales = ["ar", "fa", "he", "ji", "ku", "ur", "yi"],
- svgGraphics = '<?xml version="1.0"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="128px" height="128px" viewBox="0 0 256 256"><defs><linearGradient x1="0.085" y1="0.085" x2="0.915" y2="0.915" id="syndication"><stop offset="0.0" stop-color="#E3702D"/><stop offset="0.1071" stop-color="#EA7D31"/><stop offset="0.3503" stop-color="#F69537"/><stop offset="0.5" stop-color="#FB9E3A"/><stop offset="0.7016" stop-color="#EA7C31"/><stop offset="0.8866" stop-color="#DE642B"/><stop offset="1.0" stop-color="#D95B29"/></linearGradient></defs><rect width="256" height="256" rx="55" ry="55" x="0" y="0" fill="#CC5D15"/><rect width="246" height="246" rx="50" ry="50" x="5" y="5" fill="#F49C52"/><rect width="236" height="236" rx="47" ry="47" x="10" y="10" fill="url(#syndication)"/><circle cx="68" cy="189" r="24" fill="#FFF"/><path d="M160 213h-34a82 82 0 0 0 -82 -82v-34a116 116 0 0 1 116 116z" fill="#FFF"/><path d="M184 213A140 140 0 0 0 44 73 V 38a175 175 0 0 1 175 175z" fill="#FFF"/></svg>',
- atomRules = {
- "feedLanguage" : "feed", // NOTE @xml:lang
- "feedTitlePage" : "feed > title",
- "feedSubtitle" : "feed > subtitle",
- "feedIcon" : "feed > icon",
- "feedLogo" : "feed > logo",
- "feedLink" : "feed > link",
- "feedDate" : "updated",
- "feedGenerated" : "feed > generator", // TODO extract attributes version and uri
- "feedItem" : "entry",
- "feedItemTitle" : "title",
- "feedItemLink" : "link", // NOTE varies and does not always contain rel='alternate'
- "feedItemPublished" : "published",
- "feedItemDate" : "updated",
- "feedItemAuthor" : "author",
- "feedItemContent" : "content",
- "feedItemSummary" : "summary",
- "feedItemEnclosure" : "link[rel='enclosure']",
- "feedItemRelated" : "link[rel='related']",
- "feedItemNext" : "link[rel='next']",
- "feedItemPrevious" : "link[rel='prev']"
- },
- foafRules = { // TODO
- "feedTitlePage" : "foaf\\:name",
- "feedSubtitle" : "head > description",
- "feedLink" : "foaf\\:homepage",
- "feedDate" : "head > dateModified",
- "feedItem" : "foaf\\:member",
- "feedItemTitle" : "dc\\:title",
- "feedItemLink" : "rss\\:channel",
- "feedItemAuthor" : "foaf\\:name",
- },
- opmlRules = {
- "feedLanguage" : "head > language", // NOTE Does not exist
- "feedTitlePage" : "head > title",
- "feedSubtitle" : "head > description",
- "feedLink" : "head > urlPublic",
- "feedDate" : "head > dateModified", // dateCreated
- "feedItem" : "body outline",
- "feedItemTitle" : "title",
- "feedItemLink" : "htmlUrl",
- "feedItemPublished" : "published", // NOTE Does not exist
- "feedItemDate" : "created",
- "feedItemContent" : "description",
- "feedItemSummary" : "text",
- "feedItemEnclosure" : "xmlUrl"
- },
- rdfRules = {
- "feedLanguage" : "channel > language", // TODO Test
- "feedTitlePage" : "channel > title",
- "feedSubtitle" : "channel > description",
- "feedIcon" : "channel > image > url", // TODO Test
- "feedLink" : "channel > link",
- "feedDate" : "date",
- "feedGenerated" : "channel > generator", // TODO Test
- "feedItem" : "item",
- "feedItemTitle" : "title",
- "feedItemLink" : "link",
- "feedItemPublished" : "published", // NOTE Exist?
- "feedItemDate" : "date",
- "feedItemAuthor" : "creator", // dc:publisher
- "feedItemContent" : "description",
- "feedItemEnclosure" : "resource" // TODO Test
- },
- rssRules = {
- "feedLanguage" : "channel > language",
- "feedTitlePage" : "channel > title",
- "feedSubtitle" : "channel > description",
- "feedIcon" : "channel > image > url",
- "feedLink" : "channel > link",
- "feedDate" : "lastBuildDate",
- "feedGenerated" : "channel > generator",
- "feedItem" : "item",
- "feedItemTitle" : "title",
- "feedItemLink" : "link",
- "feedItemPublished" : "published", // NOTE Exist?
- "feedItemDate" : "pubDate",
- "feedItemAuthor" : "dc:creator", // Discourse uses dc:creator
- "feedItemContent" : "description",
- // NOTE prefer content:encoded
- // https://agovernmentofwolves.com/feed/
- //"feedItemSummary" : "content\\:encoded",
- "feedItemEnclosure" : "enclosure",
- "feedItemMedia" : "media:content" // CSS Selectors do not work (not even with CSS.escape `media\\:content`. Move to XPath
- },
- sitemapRules = { // TODO
- "feedItem" : "sitemap",
- "feedItemLink" : "loc",
- "feedItemDate" : "lastmod",
- },
- smfRules = {
- // FIXME "smf\\:xml-feed" does not appear to work
- // or at least not when it is originated from json
- "feedLanguage" : "smf\\:xml-feed", // NOTE @xml:lang
- "feedTitlePage" : "smf\\:xml-feed", // NOTE @forum-name
- "feedSubtitle" : "smf\\:xml-feed", // NOTE @description
- "feedIcon" : "channel > image > url", // TODO Test
- "feedLink" : "smf\\:xml-feed",
- "feedDate" : "time", // NOTE @generated-date-UTC @generated-date-localized
- "feedGenerated" : "generator",
- "feedItem" : "recent-post",
- "feedItemTitle" : "subject",
- "feedItemLink" : "topic > link",
- "feedItemPublished" : "published", // NOTE Exist?
- "feedItemDate" : "time",
- "feedItemAuthor" : "poster",
- "feedItemContent" : "body",
- "feedItemEnclosure" : "enclosure" // NOTE Does not exist
- },
-
- +const namespace = "i2p.schimon.newspaper",
- + xmlns = {
- + "atom" : "http://www.w3.org/2005/Atom",
- + "content" : "http://purl.org/rss/1.0/modules/content/",
- + "dc" : "http://purl.org/dc/elements/1.1/",
- + "foaf" : "http://xmlns.com/foaf/0.1/",
- + "media" : "http://search.yahoo.com/mrss/",
- + "owl" : "http://www.w3.org/2002/07/owl#",
- + "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
- + "rdfs" : "http://www.w3.org/2000/01/rdf-schema#",
- + "rss" : "http://purl.org/rss/1.0/",
- + "rss09" : "http://my.netscape.com/rdf/simple/0.9/",
- + "rss10" : "https://web.resource.org/rss/1.0/",
- + "smf" : "http://www.simplemachines.org/xml/recent",
- + "syn" : "http://purl.org/rss/1.0/modules/syndication/",
- + "webfeeds" : "http://webfeeds.org/rss/1.0"
- + },
- + defaultTitle = "Streamburner",
- + // This news feed is brought to you by Streamburner News Reader
- + defaultSubtitle = "Syndicated document feed rendered with Streamburner",
- + defaultAbout = "No description was provided.",
- + rtlLocales = ["ar", "fa", "he", "ji", "ku", "ur", "yi"],
- + svgGraphics = '<?xml version="1.0"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="128px" height="128px" viewBox="0 0 256 256"><defs><linearGradient x1="0.085" y1="0.085" x2="0.915" y2="0.915" id="syndication"><stop offset="0.0" stop-color="#E3702D"/><stop offset="0.1071" stop-color="#EA7D31"/><stop offset="0.3503" stop-color="#F69537"/><stop offset="0.5" stop-color="#FB9E3A"/><stop offset="0.7016" stop-color="#EA7C31"/><stop offset="0.8866" stop-color="#DE642B"/><stop offset="1.0" stop-color="#D95B29"/></linearGradient></defs><rect width="256" height="256" rx="55" ry="55" x="0" y="0" fill="#CC5D15"/><rect width="246" height="246" rx="50" ry="50" x="5" y="5" fill="#F49C52"/><rect width="236" height="236" rx="47" ry="47" x="10" y="10" fill="url(#syndication)"/><circle cx="68" cy="189" r="24" fill="#FFF"/><path d="M160 213h-34a82 82 0 0 0 -82 -82v-34a116 116 0 0 1 116 116z" fill="#FFF"/><path d="M184 213A140 140 0 0 0 44 73 V 38a175 175 0 0 1 175 175z" fill="#FFF"/></svg>',
- banner = `<svg width="256" height="100" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient x1="77.1180071%" y1="12.3268731%" x2="3.36110907%" y2="118.781335%" id="a"><stop stop-color="#D446FF" offset="0%"/><stop stop-color="#A0D8FF" offset="100%"/></linearGradient><linearGradient x1="50.7818321%" y1="-17.173918%" x2="76.3448843%" y2="77.2144178%" id="b"><stop stop-color="#3C3C3C" offset="0%"/><stop stop-color="#191919" offset="100%"/></linearGradient><linearGradient x1="148.794275%" y1="-26.5643443%" x2="-21.1415871%" y2="99.3029307%" id="c"><stop stop-color="#D446FF" offset="0%"/><stop stop-color="#A0D8FF" offset="100%"/></linearGradient><linearGradient x1="41.8083357%" y1="20.866645%" x2="95.5956597%" y2="-8.31097281%" id="d"><stop stop-color="#FFF" offset="0%"/><stop stop-color="#DADADA" offset="100%"/></linearGradient><linearGradient x1="52.2801818%" y1="70.5577815%" x2="2.53678786%" y2="8.97706744%" id="e"><stop stop-color="#FFF" offset="0%"/><stop stop-color="#DADADA" offset="100%"/></linearGradient><linearGradient x1="98.684398%" y1="12.9995489%" x2="35.2678133%" y2="40.863838%" id="f"><stop stop-color="#D0D0D0" offset="0%"/><stop stop-color="#FFF" offset="100%"/></linearGradient><linearGradient x1="34.2841787%" y1="31.6476155%" x2="-40.2132134%" y2="123.398162%" id="g"><stop stop-color="#FFDC68" offset="0%"/><stop stop-color="#CE4300" offset="100%"/></linearGradient><linearGradient x1="95.7086811%" y1="2.33776624%" x2="-10.5474304%" y2="34.7418529%" id="h"><stop stop-color="#FFDC68" offset="0%"/><stop stop-color="#CE4300" offset="100%"/></linearGradient><linearGradient x1="55.2222258%" y1="39.6484627%" x2="-63.5655829%" y2="222.055577%" id="i"><stop stop-color="#FFDC68" offset="0%"/><stop stop-color="#CE4300" offset="100%"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><path fill="#252525" fill-rule="nonzero" d="M68.3766216 33.24450169V46.6414437h17.1215335v4.3309176H68.3766216v18.9693703h-4.9083597V28.91359256h23.7622535v4.33090913H68.3766216zm31.2822046-4.92645583h5.5724368L119.81199 69.3461849h-4.966111l-4.157673-11.9244357H93.7687843l-4.1576731 11.9244357H85.078099l14.5807272-41.02813904Zm2.5696738 4.7928662L95.241299 53.1197155h13.945526L102.2285 33.11091206Zm20.879509-4.1973195h4.90836V65.6397065h18.420795v4.3020251h-23.329155zm27.650382 0h4.908369V69.9417316h-4.908369zm48.41257-.4253905c2.714031 0 5.245206.48121024 7.593526 1.44363071 2.367565.9431758 4.407905 2.28094667 6.121021 4.01331259 1.732366 1.73235454 3.089381 3.89780774 4.071046 6.49635944.981671 2.5792957 1.472507 5.4184455 1.472507 8.5174493 0 4.1961709-.837308 7.9303791-2.511923 11.2026246-1.655364 3.2529952-3.965184 5.7745455-6.929458 7.5646511-2.945012 1.770861-6.284623 2.6562914-10.018831 2.6562914-2.463811 0-4.82175-.43309-7.073818-1.2992702-2.252074-.8854361-4.282792-2.1654616-6.092154-3.8400765-1.80935-1.6746149-3.252981-3.8593184-4.330892-6.5541105-1.077922-2.7140367-1.616884-5.7649203-1.616884-9.1526508 0-3.1759995.490836-6.0825226 1.472507-8.7195693.981676-2.6370411 2.329072-4.8506144 4.042188-6.64072001 1.713115-1.8093616 3.753455-3.20487781 6.12102-4.18654862 2.367559-1.00091547 4.927607-1.50137321 7.680145-1.50137321Zm-.259854 4.30203363c-1.751611 0-3.435857.32722172-5.052738.98166514-1.616863.65445477-3.108617 1.61688087-4.475261 2.88727847-1.347399 1.2511471-2.434941 2.9450124-3.262626 5.0815957-.808429 2.117333-1.212643 4.5137703-1.212643 7.1893121 0 2.560051.375344 4.9276129 1.126034 7.1026855.750689 2.1558337 1.770858 3.9651924 3.060506 5.4280763 1.289648 1.4628896 2.810277 2.5985489 4.561887 3.406978 1.770867.8084405 3.666844 1.2126607 5.687931 1.2126607 1.366643 0 2.704411-.2021115 4.013304-.6063346 1.328148-.4042174 2.598552-1.0394161 3.811209-1.9055963 1.212647-.8854361 2.271313-1.9633529 3.176-3.2337505.923925-1.2703975 1.655367-2.8391469 2.194326-4.7062481.558203-1.867107.837304-3.9170723.837304-6.149896 0-2.2713186-.288726-4.3501538-.86618-6.2365054-.577453-1.8863516-1.347393-3.4647261-2.309819-4.7351237-.943176-1.2704032-2.049963-2.3386948-3.32036-3.2048749-1.251159-.88543618-2.550435-1.52063777-3.897828-1.90560483-1.328143-.40421172-2.685158-.60631758-4.071046-.60631758Zm23.397966-3.87664313h6.207638l22.347481 34.32967654V28.91359256h4.908369V69.9417316h-6.20763l-22.34749-34.3874106v34.3874106h-4.908368V28.91359256z"/><path fill="url(#a)" fill-rule="nonzero" d="M174.265411 4.50913925h6.14987L163.062778 24.0848526l-6.178763.4042146z" transform="translate(0 25)"/><path fill="url(#b)" fill-rule="nonzero" d="M162.552309 23.4815553 181.00198 44.933981h-6.323132l-18.305302-21.0482111z" transform="translate(0 25)"/><g transform="translate(0 25)"><ellipse stroke="#A3A3A3" stroke-width=".5" fill="url(#c)" fill-rule="nonzero" cx="26.8134179" cy="26.3507547" rx="26.805321" ry="26.3202814"/><path d="M9.37216085 52.0819111S17.7489335 22.7696899 50.3566889 26.4969018c0 0-20.723434-12.3020208-40.84161386 4.2828996-5.94153114 13.7625736-.14291419 21.3021097-.14291419 21.3021097Z" fill="url(#d)"/><path d="M11.8660211 48.3743096s8.7405326-21.0730284 32.0717549-21.4433648c-17.7350232 4.3205976.0290967 19.6378374.0290967 19.6378374s-14.0287234 12.7111189-32.1008516 1.8055274Z" fill="url(#e)"/><path d="M6.99959641 32.2202162S24.4574437 13.0165918 50.527832 26.2845469c-.4655559-3.607941-3.5497731-7.6814378-9.0780884-8.8452977-1.2220448-.5819257-10.1837464-14.43182348-29.4455641-6.459393 5.9256811.461387 8.4682656 1.291426 8.612541 1.9203488C10.369106 11.1390373 7.33148608 25.722572 6.99959641 32.2202162Z" fill="url(#f)"/><path d="M32.03706 15.9299212s4.4665322-1.5996384 5.0482196 3.1577417c-4.4042039.4155129-5.0482196-3.1577417-5.0482196-3.1577417Z" fill="#202020"/><path d="M9.2551104 52.1772666s7.6361082-24.9093105 33.1663186-25.8730411c0 0-20.7166787-3.7798413-35.34817197 19.3977044-.08112197 3.2790716 2.18185337 6.4753367 2.18185337 6.4753367Z" fill="url(#g)"/><path d="M12.0256021 10.9822724s6.3677384.3703449 8.6411954 1.9339868c.9492333-.1952797 1.7552122-.8451998 1.892222-1.7303694-.8145463-.337105-8.2702464-.9442903-10.5334174-.2036174Z" fill="url(#h)"/><path d="M7.05701562 32.2533371s7.97220928-9.5753699 24.06662038-10.719313c0 0-21.9094907.9901814-23.7487686 7.1768481-.5113449 1.4876671-.31785178 3.5424649-.31785178 3.5424649Z" fill="url(#i)"/></g></g></svg>`,
- quote = `
- <p>"The technology that big organizations want you to forget".</p>
- @@ -2659,7 +2563,7 @@
- <p>Google Chrome once had <a href="https://chromium.org/user-experience/feed-subscriptions/">a built-in RSS button</a> in the desktop version of it, and also in the source code of Chromium, the browser upon which it is based.</p>
- <p>However, the company has since removed the feature from the browser and no reason was given for its removal.</p>
- <ul>
- <li><a href="https://jwz.org/xscreensaver/google.html">XScreenSaver: Google Store Privacy Policy</a> (Unlike Google,...)</li>
- + <li><a href="https://jwz.org/xscreensaver/google.html">XScreenSaver: Boogle Store Privacy Policy</a> (Unlike Boogle,…)</li>
- <li><a href="https://jwz.org/blog/2024/06/your-personal-information-is-very-important-to-us/">"Your private information is very important to us."</a> (June 8, 2024)</li>
- <li><a href="https://openrss.org/blog/browsers-should-bring-back-the-rss-button">Browsers removed the RSS Button and they should bring it back</a> (May 30, 2023)</li>
- <li><a href="https://theverge.com/2021/10/8/22716813/google-chrome-follow-button-rss-reader">Google Reader is still defunct, but now you can ‘follow’ RSS feeds in Chrome on Android</a> (October 8, 2021)</li>
- @@ -3525,7 +3429,7 @@
- <p>The Ladybird browser came to life on July 4th; it was originally a headless mode of LibWeb (previously LibHTML) and is also featuring a Qt GUI for the LibWeb browser engine. Ladybird was intended to be a debugging tool for people to remain in Linux while working on LibWeb if they wanted to. Two months later, Ladybird has became a browser in its own right.</p>
- <p>At this point, we might as well tweak the scope from “browser engine for SerenityOS” to “cross-platform browser engine” and build something that many more people could potentially have use for some day. :^)</p>
- <h3>🌆 <a href="https://gmi.skyjake.fi/lagrange/">Lagrange</a></h3>
- <h3>Browser for people who talk business... securely.</h3>
- + <h3>Browser for people who talk business… securely.</h3>
- <!-- h3>Browser For People Who Talk <sup>Secured</sup> Business.</h3 -->
- <p>Lagrange is a GUI client for browsing Geminispace. It offers modern conveniences familiar from browsers, such as smooth scrolling, inline image viewing, multiple tabs, visual themes, Unicode fonts, bookmarks, history, and page outlines.</p>
- <p>Like Gemini, Lagrange has been designed with minimalism in mind. It depends on a small number of essential libraries. It is written in C and uses SDL for hardware-accelerated graphics. OpenSSL is utilized for secure communications.</p>
- @@ -3560,7 +3464,7 @@
- <h1><span class="redice">🧊</span> Even <i>"they"</i> have syndication feeds</h1>
- <h2>Then why should not you too?</h2>
- <p>This is a list of sources who are infamous by government regulated media (<span title="Corporate Lame-Ass Propaganda">CLAP</span>), which does not mean that these sources are necessarily bad; it only means that some governments hate them, which is fine, because most people hate most of the political imposters (i.e. politicians) either, so that makes it all even.</p>
- <p>Call them <span title="Are they?">silly</span>; call them <span title="It seems that they are pretty large!">small</span>; call them <span title="Are they?">stupid</span>; call them <span title="Is being racist a bad thing?">racists</span>; call them <span title="Are they?">incompetent</span>; call them <span title="Are they?">extremists</span>; even call them <span title="Are they?">dangerous</span>, <span title="Are they?">terrorists</span> and <span title="Are they?">saboteurs</span>; call them <a href="http://newworldorderreport.com/Articles/tabid/266/ID/980/33-Conspiracy-Theories-That-Turned-Out-To-Be-True-What-Every-Person-Should-Know.aspx" title="33 Conspiracy Theories That Turned Out To Be True, What Every Person Should Know...">conspiracy</a> <a href="https://blacklistednews.com/article/83721/5-crazy-conspiracy-theories-that-actually-turned-out-to-be.html" title="5 “CRAZY” CONSPIRACY THEORIES THAT ACTUALLY TURNED OUT TO BE TRUE">theorists</a>; call them <span title="Are they?">bad</span>!</p>
- + <p>Call them <span title="Are they?">silly</span>; call them <span title="It seems that they are pretty large!">small</span>; call them <span title="Are they?">stupid</span>; call them <span title="Is being racist a bad thing?">racists</span>; call them <span title="Are they?">incompetent</span>; call them <span title="Are they?">extremists</span>; even call them <span title="Are they?">dangerous</span>, <span title="Are they?">terrorists</span> and <span title="Are they?">saboteurs</span>; call them <a href="http://newworldorderreport.com/Articles/tabid/266/ID/980/33-Conspiracy-Theories-That-Turned-Out-To-Be-True-What-Every-Person-Should-Know.aspx" title="33 Conspiracy Theories That Turned Out To Be True, What Every Person Should Know…">conspiracy</a> <a href="https://blacklistednews.com/article/83721/5-crazy-conspiracy-theories-that-actually-turned-out-to-be.html" title="5 “CRAZY” CONSPIRACY THEORIES THAT ACTUALLY TURNED OUT TO BE TRUE">theorists</a>; call them <span title="Are they?">bad</span>!</p>
- <p>You can call them anything you want, and yet, they are indeed wiser when it regards to be easy to reach.</p>
- <h3>Asia</h3>
- <ul>
- @@ -3775,7 +3679,7 @@
- <li><a href="https://wired.com/story/rss-readers-feedly-inoreader-old-reader/">It is time for an RSS revival</a> (March 30, 2018)</li>
- <li><a href="https://jwz.org/blog/2013/07/this-week-in-rss-apocalypse/">This Week in RSS Apocalypse</a> (July 5, 2013)</li>
- <li><a href="https://camendesign.com/rss_a_reply">RSS: A Reply</a> (January 14, 2011)</li>
- <li><a href="http://scripting.com/stories/2011/01/08/youCanGetAnythingYouWant.html#p4214">You can get anything you want...</a> (January 8, 2011)</li>
- + <li><a href="http://scripting.com/stories/2011/01/08/youCanGetAnythingYouWant.html#p4214">You can get anything you want…</a> (January 8, 2011)</li>
- <li><a href="https://spiegel.de/netzwelt/web/streit-um-internet-nutzung-komfort-schlaegt-freiheit-a-737748.html">Streit um Internet-Nutzung: Komfort schlägt Freiheit - DER SPIEGEL</a> (January 7, 2011)</li>
- <li><a href="https://web.archive.org/web/20110108063442/http://buddycloud.com/cms/content/we-are-aol-days-social-networking">We are in the AOL days of Social Networking</a> (January 6, 2011)</li>
- <li><a href="http://scripting.com/stories/2011/01/05/upcomingTheMinimalBlogging.html">Upcoming: The minimal journaling tool</a> (January 5, 2011)</li>
- @@ -4014,7 +3918,7 @@
- "receiveBody": []
- }`,
- htmlBar = `
-<a id="ace-link-prev" title="Navigate to previous page" href="javascript:alert('ACE directives were not found.')">Previous</a>
- +<a id="ace-link-prev" title="Navigate to previous page" href="javascript:alert('RFC 5005 directives were not found.')">Previous</a>
- <a id="subscribe-link" class="subscribe-link" title="Subscribe to get the latest updates and news">Subscribe</a>
- <!-- a class="cursor-pointer" id="service" title="Subscribe online">Handler</a -->
- <!-- a id="service" title="Subscribe online" href="https://subtome.com/#/subscribe?feeds=${location.href}">SubToMe</a -->
- @@ -4022,14 +3926,14 @@
- <span id="previous" title="Previous item (Ctrl + Shift + Key Up)">❰</span>
- <span id="next" title="Next item (Ctrl + Shift + Key Down)">❱</span>
- <span id="mode" title="Dark mode">💡</span>
-<a id="about-help" class="about-help" title="About Newspaper and StreamBurner and more...">🛟</a>
- +<a id="about-help" class="about-help" title="About Newspaper and StreamBurner and more…">🛟</a>
- <span id="about-settings" title="Newspaper settings">⚙️</span>
- <span id="direction" title="Change text direction">𝐓</span>
- <span id="increase" title="Increase text size">+ 𝐀</span>
- <span id="decrease" title="Decrease text size">- 𝐚</span>
- <!-- span id="about-help" title="Learn about syndication feed and how you can help">⁝⁝⁝⁝⁝</span -->
-<a class="homepage-link" id="homepage-link" title="Visit homepage" href="javascript:location.href = location.protocol + "//" + location.hostname">Home Page</a>
-<a id="ace-link-next" title="Navigate to next page" href="javascript:alert('ACE directives were not found.')">Proceed</a>`,
- +<a class="homepage-link" id="homepage-link" title="Navigate to alternate document" href="javascript:location.href = location.protocol + "//" + location.hostname">Alternate</a>
- +<a id="ace-link-next" title="Navigate to next page" href="javascript:alert('RFC 5005 directives were not found.')">Proceed</a>`,
- htmlEmpty = `
- <div class="notice no-entry" id="empty-feed">
- <h3>This news feed is empty</h3>
- @@ -4077,7 +3981,7 @@
- direction: ltr;
- display: block;
- font-family: system-ui;
- /* font-size: 90%; */
- + font-size: 90%;
- height: 50px;
- margin: auto;
- max-width: 1200px;
- @@ -5131,7 +5035,8 @@
- }
-
- #email-link {
- margin-top: 25px;
- + display: block;
- + margin-top: 3em;
- text-decoration: overline;
- outline: none;
- }
- @@ -5346,6 +5251,7 @@
- `;
-
- var articlesFiltered = [],
- + autoDiscovery,
- contentMode,
- cssFileBase,
- enableEnclosure,
- @@ -5359,6 +5265,8 @@
- gmSetValue,
- handlerInstance,
- handlerUrl,
- + hueDegreeValue,
- + iconPosition,
- ignoreMinimumItemNumber = false,
- init,
- keywords,
- @@ -5367,28 +5275,9 @@
- minimumItemNumber,
- playEnclosure = false,
- subscriptionHandler,
- wellFormed,
- xmlStylesheet = false,
- viewMode,
- autoDiscovery,
- iconPosition,
- hueDegreeValue,
- contentMode,
- enableIcon,
- enableEnclosure,
- enclosureView,
- fontSize,
- fontType,
- filterBlacklist,
- filterWhitelist,
- handlerInstance,
- handlerUrl,
- keywordsBlacklist,
- keywordsWhitelist,
- minimumItemNumber,
- playEnclosure,
- subscriptionHandler,
- viewMode;
- + wellFormed,
- + xmlStylesheet = false;
-
- // Check availability of Greasemonkey APIs
-
- @@ -5434,7 +5323,6 @@
- init = checkContentType();
- }
-
-
- function checkContentType() {
- let myPromise = new Promise(function(myResolve, myReject) {
- let request = new XMLHttpRequest();
- @@ -5444,13 +5332,13 @@
- //request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
- //request.setRequestHeader("Content-Type", "text/plain");
- request.onload = function() {
- let mb = messageBar("📰 Greasemonkey Newspaper: Retrieving data...", "#f5f5f5", "#5e5e5e");
- + let mb = messageBar("📰 Greasemonkey Newspaper: Retrieving data…", "#f5f5f5", "#5e5e5e");
- if (document.URL.startsWith("file:") || request.status == 200) {
- myResolve(request);
- } else {
- myReject("File not Found.");
- }
- mb.remove()
- + mb.remove();
- };
- request.send();
-
- @@ -5482,7 +5370,7 @@
-
- myPromise.then(
- async function(request) {
- let mb = messageBar("📰 Greasemonkey Newspaper: Analyzing content...", "#f5f5f5", "#5e5e5e");
- + let mb = messageBar("📰 Greasemonkey Newspaper: Analyzing content…", "#f5f5f5", "#5e5e5e");
- //delay(1000);
- // Read settings
- if (gmGetValue) {
- @@ -5516,7 +5404,7 @@
- }
- */
-
- let jsonFile, xmlFile;
- + let jsonFile, xmlFile, newDocument, feed = {};
- let isHtml = false, isJson = false; //isXml = false isTxt = false
- let requestResponse = request.response;
- console.log(request.getResponseHeader("Content-Type"));
- @@ -5556,16 +5444,24 @@
- }
- }
-
- + /*
- + try {
- +
- + } catch (e) {
- + pageLoader(e, "error");
- + }
- + */
- +
- if (isJson) { // Attempt to parse JSON.
- + pageLoader("Collecting data");
- if (jsonFile.version) {
- // FIXME TODO Handle empty feed https://jblevins.org/index.json
- if (jsonFile.version.toLowerCase().includes("jsonfeed.org")) {
- pageLoader("Collecting data");
- //setTimeout(function(){renderJSONFeed(jsonFile)}, 1500);
- let jsonFeed = extractJsonFeed(jsonFile);
- pageLoader("Generating document");
- newDocument = renderDocument(jsonFeed);
- newDocument = feedInfoJSON(
- + extractJsonFeed(jsonFile, feed);
- + feed.type = "JSONFeed";
- + newDocument = renderDocument(feed);
- + feedInfoJSON(
- newDocument,
- jsonFile,
- jsonFile.home_page_url,
- @@ -5573,7 +5469,7 @@
- jsonFile.version,
- jsonFile.items[0].date_published,
- "JSON Feed");
- newDocument = await preProcess(newDocument);
- + await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- }
- @@ -5581,19 +5477,18 @@
- if (jsonFile["@context"]) {
- if (jsonFile["@context"][0] == "https://www.w3.org/ns/activitystreams" &&
- jsonFile["@context"][1] == "https://w3id.org/security/v1") {
- pageLoader("Collecting data");
- let nostrFeed = extractNostr(jsonFile);
- pageLoader("Generating document");
- newDocument = renderDocument(nostrFeed);
- newDocument = feedInfoJSON(
- + extractNostr(jsonFile, feed);
- + feed.type = "Nostr";
- + newDocument = renderDocument(feed);
- + feedInfoJSON(
- newDocument,
- jsonFile,
- "nostr:" + jsonFile["preferredUsername"],
- + "nostr:" + jsonFile.preferredUsername,
- location.protocol + "//" + location.hostname,
- jsonFile["@context"][2]["nostr"],
- + jsonFile["@context"][2].nostr,
- null,
- "Nostr (ActivityStream)");
- newDocument = await preProcess(newDocument);
- + await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- }
- @@ -5601,11 +5496,10 @@
- if (jsonFile.generator) {
- if (jsonFile.generator.toLowerCase().includes("statusnet") || // TODO Case insensitive
- jsonFile.generator.toLowerCase().includes("gnu social")) {
- pageLoader("Collecting data");
- let avctivityStreamFeed = extractOStatus(jsonFile);
- pageLoader("Generating document");
- newDocument = renderDocument(avctivityStreamFeed);
- newDocument = feedInfoJSON(
- + extractOStatus(jsonFile, feed);
- + feed.type = "OStatus";
- + newDocument = renderDocument(feed);
- + feedInfoJSON(
- newDocument,
- jsonFile,
- //jsonFile.items[0].id, // NOTE Not good. This is done so that infoSquare will load
- @@ -5614,18 +5508,17 @@
- null,
- jsonFile.items[0].published,
- "OStatus (ActivityStream)");
- newDocument = await preProcess(newDocument);
- + await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- }
- } else
- if (jsonFile.rss) {
- if (jsonFile.rss.version.toLowerCase().includes("2.0")) {
- pageLoader("Collecting data");
- let rssInJsonFeed = extractRssInJson(jsonFile.rss.channel);
- pageLoader("Generating document");
- newDocument = renderDocument(rssInJsonFeed);
- newDocument = feedInfoJSON(
- + extractRssInJson(jsonFile.rss.channel, feed);
- + feed.type = "RSS-In-JSON";
- + newDocument = renderDocument(feed);
- + feedInfoJSON(
- newDocument,
- jsonFile,
- jsonFile.rss.channel.link,
- @@ -5633,7 +5526,7 @@
- jsonFile.rss.version,
- jsonFile.rss.channel.pubDate,
- "RSS-in-JSON");
- newDocument = await preProcess(newDocument);
- + await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- }
- @@ -5878,10 +5771,10 @@
- ) { // Attempt to parse twtxt.
- wellFormed = true;
- pageLoader("Collecting data");
- let twtxtFeed = extractTwtxt(requestResponse);
- pageLoader("Generating document");
- newDocument = renderDocument(twtxtFeed);
- newDocument = feedInfoTXT(
- + extractTwtxt(requestResponse, feed);
- + feed.type = "Twtxt";
- + newDocument = renderDocument(feed);
- + feedInfoTXT(
- newDocument,
- requestResponse,
- null,
- @@ -5889,7 +5782,7 @@
- null,
- null,
- "Twtxt");
- newDocument = await preProcess(newDocument);
- + await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- }
- @@ -5915,7 +5808,7 @@
- mb.remove();
-
- //let xmlStylesheet;
- for (childNode of xmlFile.childNodes) {
- + for (let childNode of xmlFile.childNodes) {
- if (childNode.target == "xml-stylesheet") {
- childNode.remove();
- xmlStylesheet = true;
- @@ -5962,55 +5855,36 @@
- }
- */
-
- switch (xmlFile.firstElementChild) {
- + pageLoader("Collecting data");
- + let nodeFeed = xmlFile.queryPath(xmlns.atom, "atom:feed"),
- + nodeOpml = xmlFile.queryPath(null, "opml"),
- + nodeRdf = xmlFile.queryPath(xmlns.rdf, "rdf:RDF"),
- + nodeRss = xmlFile.queryPath(null, "rss"),
- + nodeSmf = xmlFile.queryPath(xmlns.smf, "smf:xml-feed");
- + if (nodeFeed) {
- // <feed xmlns="http://www.w3.org/2005/Atom">
- // xmlFile.getElementsByTagNameNS("http://www.w3.org/2005/Atom","feed")
- case xmlFile.querySelector("feed"):
- pageLoader("Collecting data");
- let atomFeed = extractAtom(xmlFile);
- pageLoader("Generating document");
- newDocument = renderDocument(atomFeed);
- //aboutInfo(xmlFile, rdfRules);
- newDocument = feedInfoXML(
- newDocument,
- xmlFile,
- atomRules,
- "The Atom Syndication Format 1.0");
- newDocument = await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess(atomFeed.next, atomFeed.prev);
- break;
- + extractAtom(xmlFile, feed);
- + feed.type = "The Atom Syndication Format 1.0";
- + } else
- + if (nodeRss) {
- // Netscape RSS 0.91 <!DOCTYPE rss SYSTEM "http://my.netscape.com/publish/formats/rss-0.91.dtd">
- // Userland RSS 0.91 <rss version="0.91">
- // RSS 0.92 <rss version="0.92">
- // RSS 0.93 <rss version="0.93">
- // RSS 0.94 <rss version="0.94">
- // RSS 2.0 <rss version="2.0">
- case xmlFile.querySelector("rss"):
- pageLoader("Collecting data");
- let rssFeed = extractRss(xmlFile);
- pageLoader("Generating document");
- newDocument = renderDocument(rssFeed);
-
- //aboutInfo(xmlFile, rdfRules);
- // FIXME https://elegislation.gov.hk/verified-chapters!en.rss.xml
- if (rssVersion = xmlFile.firstElementChild.getAttribute("version")) {
- newDocument = feedInfoXML(
- newDocument,
- xmlFile,
- rssRules,
- `RDF Site Summary ${rssVersion}`);
- } else {
- newDocument = feedInfoXML(
- newDocument,
- xmlFile,
- rssRules,
- `RDF Site Summary`); // RSS Syndication Feed 2.0
- }
- newDocument = await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- break;
- + extractRss(xmlFile, feed);
- +
- + //aboutInfo(xmlFile, rdfRules);
- + // FIXME https://elegislation.gov.hk/verified-chapters!en.rss.xml
- + if (rssVersion = xmlFile.firstElementChild.getAttribute("version")) {
- + feed.type = `RSS ${rssVersion}`;
- + } else {
- + feed.type = "RSS"; // RSS Syndication Feed 2.0
- + }
- + } else
- + if (nodeRdf && nodeRdf.getAttribute("xmlns:foaf")) {
- // TODO Check by namespace xmlns
- // https://yaxim.org/doap/yaxim.rdf.xml
- // https://wiki.gnome.org/action/rss_rc/Home?action=rss_rc&unique=1&ddiffs=1
- @@ -6019,77 +5893,27 @@
- // RSS 1.0 <rdf:RDF xmlns="http://purl.org/rss/1.0/">
- // NOTE firstElementChild test page https://web.resource.org/rss/1.0/
- // xmlFile.getElementsByTagNameNS("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "RDF");
- case xmlFile.getElementsByTagName("rdf:RDF")[0]: // RDF Vocabulary
- switch (xmlFile.firstElementChild.getAttribute("xmlns")) {
- case "http://xmlns.com/foaf/0.1/":
- case "http://my.netscape.com/rdf/simple/0.9/":
- case "https://web.resource.org/rss/1.0/": // TODO TEST
- case "http://purl.org/rss/1.0/":
- pageLoader("Collecting data");
- let rdfFeed = extractRdf(xmlFile);
- pageLoader("Generating document");
- newDocument = renderDocument(rdfFeed);
- //aboutInfo(xmlFile, rdfRules);
- switch (xmlFile.firstElementChild.getAttribute("xmlns")) {
- case "http://xmlns.com/foaf/0.1/": // TODO
- newDocument = feedInfoXML(
- newDocument,
- xmlFile,
- foafRules,
- "FOAF - Friend Of A Friend 0.1");
- break;
- case "https://web.resource.org/rss/1.0/":
- case "http://purl.org/rss/1.0/":
- newDocument = feedInfoXML(
- newDocument,
- xmlFile,
- rdfRules,
- "RSS - RDF Site Summary 1.0");
- break;
- case "http://my.netscape.com/rdf/simple/0.9/":
- newDocument = feedInfoXML(
- newDocument,
- xmlFile,
- rdfRules,
- "RDF - RDF Site Summary 0.9");
- break;
- }
- newDocument = await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- }
- break;
- case xmlFile.querySelector("opml"):
- pageLoader("Collecting data");
- // dateCreated http://source.scripting.com/?format=opml&streamburner=0
- let feedOpml = extractOpml(xmlFile);
- pageLoader("Generating document");
- newDocument = renderDocument(feedOpml);
- //aboutInfo(xmlFile, rdfRules);
- newDocument = feedInfoXML(
- newDocument,
- xmlFile,
- opmlRules,
- "OPML Collection"); // OPML
- newDocument = await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- break;
- case xmlFile.getElementsByTagName("smf:xml-feed")[0]:
- pageLoader("Collecting data");
- let smfFeed = extractSmf(xmlFile);
- pageLoader("Generating document");
- newDocument = renderDocument(smfFeed);
- //aboutInfo(xmlFile, rdfRules);
- newDocument = feedInfoXML(
- newDocument,
- xmlFile,
- smfRules,
- "Simple Machines Forum"); // SMF
- newDocument = await preProcess(newDocument);
- placeNewDocument(newDocument);
- await postProcess("", "");
- break;
- + extractFoaf(xmlFile, feed);
- + feed.type = "FOAF - Friend Of A Friend 0.1";
- + } else
- + if (nodeRdf) { // RDF Vocabulary
- + switch (nodeRdf.getAttribute("xmlns")) {
- + case "http://my.netscape.com/rdf/simple/0.9/": // RDF - RDF Site Summary 0.9
- + case "https://web.resource.org/rss/1.0/": // TODO TEST
- + case "http://purl.org/rss/1.0/": // RSS - RDF Site Summary 1.0
- + extractRdf(xmlFile, feed);
- + feed.type = "RDF - RDF Site Summary";
- + break;
- + }
- + } else
- + if (nodeOpml) {
- + // dateCreated http://source.scripting.com/?format=opml&streamburner=0
- + extractOpml(xmlFile, feed);
- + feed.type = "OPML Collection";
- + } else
- + if (nodeSmf) {
- + extractSmf(xmlFile, feed);
- + feed.type = "SMF - Simple Machines Forum";
- }
- // FIXME
- // This appears to be a not usuable Atom feed
- @@ -6099,8 +5923,17 @@
- // https://libranet.de/display/feed-item/171774921.atom
- // FIXME Parse Friendica entry
- // https://venera.social/display/feed-item/99550546.atom
- + newDocument = renderDocument(feed);
- + //aboutInfo(xmlFile, rdfRules);
- + urgentMessage(newDocument);
- + feedInfoXml(newDocument, feed);
- + await preProcess(newDocument);
- + placeNewDocument(newDocument);
- + await postProcess(feed.next, feed.prev);
- }
-
- + //pageLoader("Generating document");
- +
- // Information of request
- //console.info(xmlFile);
- //console.info(document);
- @@ -6127,7 +5960,7 @@
- // `//a[ends-with(text(), ".${array[i]}")]/@href`
- results = executeQuery(query, "xpath");
- if (results.length) {
- for (result of results) {
- + for (let result of results) {
- protocol = location.protocol;
- hostname = location.hostname;
- //console.log(result)
- @@ -6167,7 +6000,7 @@
- ]/@href`];
- results = executeQuery(query, "xpath");
- if (results.length) {
- for (result of results) {
- + for (let result of results) {
- links.push(result);
- }
- }
- @@ -6182,7 +6015,7 @@
- `//a[starts-with(@href, "${i}:")]/@href`];
- results = executeQuery(query, "xpath");
- if (results.length) {
- for (result of results) {
- + for (let result of results) {
- let url = new URL(result);
- let bol = url.protocol.match(i);
- if (bol) {
- @@ -6196,7 +6029,7 @@
-
- function executeQuery(queries, method) {
- let i = 0, links = [], nodes;
- for (query of queries) {
- + for (let query of queries) {
- switch (method) {
- case "css":
- result = document.querySelector(queries[i]);
- @@ -6320,7 +6153,7 @@
- */
-
- // TODO Markdown https://uninformativ.de/twtxt.txt
-function extractTwtxt(txtFile) {
- +function extractTwtxt(txtFile, feed) {
- let lines = txtFile.trim().split("\n");
- let metadata = {};
- for (const line of lines) {
- @@ -6332,12 +6165,11 @@
- metadata[key] = value;
- }
- }
- let feed = {};
- feed.count = lines.length;
- feed.icon = metadata["avatar"];
- feed.language = metadata["lang"];
- feed.subtitle = metadata["description"]
- feed.title = metadata["nick"];
- + feed.icon = metadata.avatar;
- + feed.language = metadata.lang;
- + feed.subtitle = metadata.description;
- + feed.title = metadata.nick;
- feed.entries = [];
- for (const line of lines.reverse()) {
- if (line && !line.startsWith("#")) {
- @@ -6345,17 +6177,14 @@
- let twtEntry = line.split(" ");
- let date = twtEntry[0], text = twtEntry[1];
- entry.date = date;
- entry.title = `${date.slice(0,10)} ${text.slice(0,50)}...`;
- entry.text = text;
- feed.entries.push(entry);
- }
- }
- return feed;
- }
-
-// TODO Test with a feed.
-function extractNostr(jsonFile) {
- let feed = {};
- +// TODO Test with a Nostr feed.
- +function extractNostr(jsonFile, feed) {
- feed.title = jsonFile.name;
- // NOTE It appears that Nostr has no attribute language.
- feed.language = jsonFile.language;
- @@ -6373,9 +6202,6 @@
- let entry = {};
- entry.date = item.published;
- emtry.author = item.actor.portablecontacts_net.preferredUsername;
- let dateAsTitle = new Date(entry.date);
- entry.title = `${dateAsTitle.toDateString()} ${entry.text}`;
- //titleToc.textContent = item.content.replace(/(<([^>]+)>)/gi, "");
- entry.link = item.url;
- //entry.id = item.id;
- if (item.actor && item.actor.image) {
- @@ -6385,24 +6211,21 @@
- feed.entries.push(entry);
- }
- }
- return feed;
- }
-
-function extractOStatus(jsonFile) {
- let feed = {};
- +function extractOStatus(jsonFile, feed) {
- feed.title = jsonFile.title;
- feed.language = jsonFile.language
- feed.subtitle = jsonFile.description
- feed.count = jsonFile.items.length
- + feed.language = jsonFile.language;
- + feed.subtitle = jsonFile.description;
- + feed.count = jsonFile.items.length;
- if (feed.count) {
- feed.entries = [];
- for (const item of jsonFile.items) {
- //for (let i = 0; i < jsonFile.items.length; i++) {
- let entry = {}
- + let entry = {};
- entry.date = item.published;
- let dateAsTitle = new Date(entry.date);
- entry.author = item.actor.portablecontacts_net.preferredUsername;
- entry.title = `${entry.author} at ${dateAsTitle.toDateString()}`.trim();
- //titleToc.textContent = item.content.replace(/(<([^>]+)>)/gi, "");
- //titleToc.textContent = item.actor.portablecontacts_net.preferredUsername;
- entry.link = item.url;
- @@ -6412,11 +6235,9 @@
- feed.entries.push(entry);
- }
- }
- return feed;
- }
-
-function extractRssInJson(jsonFile) {
- let feed = {};
- +function extractRssInJson(jsonFile, feed) {
- feed.title = jsonFile.title;
- feed.language = jsonFile.language;
- feed.title = jsonFile.title;
- @@ -6429,12 +6250,7 @@
- entry.date = item.pubDate;
- entry.text = item.description;
- entry.html = true;
- if (item.title) {
- entry.title = item.title;
- } else
- if (entry.date || entry.text) {
- entry.title = `${entry.date.slice(0,16)} ${entry.text}`.replace(/(<([^>]+)>)/gi, "").slice(0,50);
- }
- + //entry.title =
- entry.link = item.link; // item["source:outline"].permalink
- //entry.id = item.id;
-
- @@ -6452,10 +6268,9 @@
- feed.entries.push(entry);
- }
- }
- return feed;
- }
-
-function extractJsonFeed(jsonFile) {
- +function extractJsonFeed(jsonFile, feed) {
-
- let feedMap = {
- "title": "title",
- @@ -6494,7 +6309,6 @@
- }
- */
-
- let feed = {};
- feed.count = jsonFile.items.length;
- feed.icon = jsonFile.icon;
- feed.link = jsonFile.home_page_url;
- @@ -6533,12 +6347,12 @@
- //for (let i = 0; i < jsonFile.items.length; i++) {
- let entry = {};
- entry.title = item.title;
- entry.date = item.date_published
- + entry.date = item.date_published;
- entry.link = item.url;
- entry.id = item.id;
- //entry.updated = date_modified;
- // TODO Set it as enclosure unless content is not html (i.e. is text)
- entry.image = item.image
- + entry.image = item.image;
- if (item.content_html) {
- entry.text = item.content_html;
- entry.html = true;
- @@ -6549,7 +6363,8 @@
- // TODO Test
- if (item.authors) {
- entry.authors = [];
- for (let item of item.authors) { // TODO Check whether this is singular: item.author
- + // TODO Check whether this is singular: item.author
- + for (let item of item.authors) {
- +
- author = {};
- if (item.name) {
- author.name = item.name;
- @@ -6563,102 +6378,92 @@
- feed.entries.push(entry);
- }
- }
- return feed;
- }
-
-function extractAtom(xmlFile) {
- const xmlRules = {
- "feedLanguage" : "feed", // NOTE @xml:lang
- "feedTitlePage" : "feed > title",
- "feedSubtitle" : "feed > subtitle",
- "feedIcon" : "feed > icon",
- "feedLogo" : "feed > logo",
- "feedLink" : "feed > link",
- "feedUpdated" : "updated",
- "feedGenerator" : "feed > generator",
- "feedItem" : "entry",
- "feedItemTitle" : "title",
- "feedItemLink" : "link", // NOTE varies and does not always contain rel='alternate'
- "feedItemPublished" : "published",
- "feedItemUpdated" : "updated",
- "feedItemAuthor" : "author",
- "feedItemContent" : "content",
- "feedItemSummary" : "summary",
- "feedItemLinkEnclosure" : "link[rel='enclosure']",
- "feedItemLinkRelated" : "link[rel='related']",
- "feedItemLinkNext" : "link[rel='next']",
- "feedItemLinkPrevious" : "link[rel='prev']"
- };
- let feed = {};
- if (xmlFile.querySelector(xmlRules.feedTitlePage)) {
- feed.title = xmlFile.querySelector(xmlRules.feedTitlePage).textContent;
- }
- if (xmlFile.querySelector(xmlRules.feedLanguage) &&
- xmlFile.querySelector(xmlRules.feedLanguage).getAttribute("xml:lang")) {
- feed.language = xmlFile.querySelector(xmlRules.feedLanguage).getAttribute("xml:lang");
-
- +function extractAtom(xmlFile, feed) {
- + let nodeFeed = xmlFile.queryPath(xmlns.atom, "atom:feed");
- + feed.next = nodeFeed.queryPath(xmlns.atom, "atom:link[@rel='next']");
- + feed.prev = nodeFeed.queryPath(xmlns.atom, "atom:link[@rel='previous']");
- + if (enableIcon) {
- + let iconPath = nodeFeed.queryPath(xmlns.atom, "atom:icon");
- + let logoPath = nodeFeed.queryPath(xmlns.atom, "atom:logo");
- + if (logoPath || iconPath) {
- + if (logoPath) {
- + feed.icon = logoPath.textContent;
- + } else
- + if (iconPath) {
- + feed.icon = iconPath.textContent;
- + }
- + }
- +
- }
- if (xmlFile.querySelector(xmlRules.feedSubtitle)) {
- feed.subtitle = xmlFile.querySelector(xmlRules.feedSubtitle).textContent.replace(/(<([^>]+)>)/gi, "");
- + let nodeTitle = nodeFeed.queryPath(xmlns.atom, "atom:title");
- + if (nodeTitle) {
- + feed.title = nodeTitle.textContent;
- + }
- + if (nodeFeed.getAttribute("xml:lang")) {
- + feed.language = nodeFeed.getAttribute("xml:lang");
- + }
- + let nodeSubtitle = nodeFeed.queryPath(xmlns.atom, "atom:subtitle");
- + if (nodeSubtitle) {
- + feed.subtitle = nodeSubtitle.textContent.replace(/(<([^>]+)>)/gi, "");
- }
- feed.count = xmlFile.querySelectorAll("entry").length;
- + let nodesEntry = nodeFeed.queryPathAll(xmlns.atom, "atom:entry");
- + feed.count = nodesEntry.length;
- if (feed.count) {
- feed.entries = [];
- let numberOfArticles = 0, numberOfArticlesBlacklisted = 0;
- for (const item of xmlFile.querySelectorAll("entry")) {
- + for (let nodeEntry of nodesEntry) {
- let entry = {};
- // Entry date
- if (item.querySelector("updated") &&
- item.querySelector("updated").textContent.length) {
- entry.date = item.querySelector("updated").textContent;
- + let nodeUpdated = nodeEntry.queryPath(xmlns.atom, "atom:updated");
- + let nodePublished = nodeEntry.queryPath(xmlns.atom, "atom:published");
- + if (nodeUpdated && nodeUpdated.textContent.length) {
- + entry.date = nodeUpdated.textContent;
- } else
- if (item.querySelector("published") &&
- item.querySelector("published").textContent.length) {
- entry.date = item.querySelector("published").textContent;
- + if (nodePublished && nodePublished.textContent.length) {
- + entry.date = nodePublished.textContent;
- }
- // Entry title
- if (item.querySelector(xmlRules.feedItemTitle) &&
- item.querySelector(xmlRules.feedItemTitle).textContent.trim().length) {
- entry.title = item.querySelector(xmlRules.feedItemTitle).textContent.replace(/(<([^>]+)>)/gi, "");
- + let nodeTitle = nodeEntry.queryPath(xmlns.atom, "atom:title");
- + if (nodeTitle && nodeTitle.textContent.trim().length) {
- + entry.title = nodeTitle.textContent.replace(/(<([^>]+)>)/gi, "");
- } else
- if (entry.date) {
- let dateAsTitle = new Date(entry.date);
- entry.title = dateAsTitle.toDateString();
- }
- // Entry link
- if (item.querySelector(xmlRules.feedItemLink) &&
- item.querySelector(xmlRules.feedItemLink).getAttribute("href")) {
- + let nodeLink = nodeEntry.queryPath(xmlns.atom, "atom:link");
- + if (nodeLink && nodeLink.getAttribute("href")) {
- // TODO Add an option to configure preferred type (e.g. Atom) and href (e.g. XMPP).
- if (item.querySelector(xmlRules.feedItemLink + "[rel='alternate'][href^='http']")) {
- entry.link = item.querySelector(xmlRules.feedItemLink + "[rel='alternate'][href^='http']").getAttribute("href");
- + if (nodeEntry.queryPath(xmlns.atom, "atom:link[@rel='alternate' and contains(@href,'http')]")) {
- + entry.link = nodeEntry.queryPath(xmlns.atom, "atom:link[@rel='alternate' and contains(@href,'http')]").getAttribute("href");
- } else {
- entry.link = item.querySelector(xmlRules.feedItemLink).getAttribute("href");
- + entry.link = nodeEntry.queryPath(xmlns.atom, "atom:link").getAttribute("href");
- }
- } else
- if (getHomeLink(xmlFile, xmlRules)) {
- entry.link = getHomeLink(xmlFile, xmlRules);
- }
- + //if (getHomeLink(xmlFile, xmlRules)) {
- + // entry.link = getHomeLink(xmlFile, xmlRules);
- + //}
- // Entry content
- +// if (contentMode == "content-complete" || contentMode == "content-summary") {
- if (contentMode != "content-title") {
- let contentExist = item.querySelector(xmlRules.feedItemContent);
- let summaryExist = item.querySelector(xmlRules.feedItemSummary);
- if ((!summaryExist && contentExist) || (contentExist && contentMode == "content-complete")) {
- entry.text = item.querySelector(xmlRules.feedItemContent).textContent;
- if (/<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- entry.html = true;
- }
- }
- if ((!contentExist && summaryExist) || (summaryExist && contentMode == "content-summary")) {
- entry.text = item.querySelector(xmlRules.feedItemSummary).textContent;
- if (/<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- entry.html = true;
- }
-
- + let nodeContent = nodeEntry.queryPath(xmlns.atom, "atom:content");
- + let nodeSummary = nodeEntry.queryPath(xmlns.atom, "atom:summary");
- +// if (nodeContent) {
- + if (contentMode == "content-complete" && nodeContent) {
- + entry.text = nodeContent.textContent;
- + } else // contentMode == "content-summary"
- + if (nodeSummary) {
- + entry.text = nodeSummary.textContent;
- +
- }
- }
- // Entry enclosures
- entry.enclosures = [];
- if (enableEnclosure && item.querySelector(xmlRules.feedItemLinkEnclosure)) {
- let related_enclosures = item.querySelectorAll(xmlRules.feedItemLinkEnclosure);
- for (const enclosure of related_enclosures) {
- + if (enableEnclosure) {
- + entry.enclosures = [];
- + // FIXME XPath
- + let nodesLinkEnclosure = nodeEntry.queryPathAll(xmlns.atom, "atom:link[@rel='enclosure']");
- + for (const enclosure of nodesLinkEnclosure) {
- let entryEnclosure = {};
- if (enclosure.getAttribute("type")) {
- entryEnclosure.type = enclosure.getAttribute("type");
- @@ -6681,39 +6486,39 @@
- }
- // Entry related
- entry.resources = [];
- if (item.querySelector(xmlRules.feedItemLinkRelated)) {
- let related_resources = item.querySelectorAll(xmlRules.feedItemLinkRelated);
- for (const related of related_resources) {
- let entryResource = {};
- if (related.getAttribute("type")) {
- entryResource.type = related.getAttribute("type");
- } else {
- entryResource.type = "";
- }
- if (related.getAttribute("title")) {
- entryResource.title = related.getAttribute("title");
- }
- if (related.getAttribute("href")) {
- entryResource.uri = related.getAttribute("href");
- }
- entry.resources.push(entryResource);
-
- + // FIXME XPath
- + let nodesLinkRelated = nodeEntry.queryPathAll(xmlns.atom, "atom:link[@rel='related']");
- + for (const related of nodesLinkRelated) {
- + let entryResource = {};
- + if (related.getAttribute("type")) {
- + entryResource.type = related.getAttribute("type");
- + } else {
- + entryResource.type = "";
- }
- + if (related.getAttribute("title")) {
- + entryResource.title = related.getAttribute("title");
- + }
- + if (related.getAttribute("href")) {
- + entryResource.uri = related.getAttribute("href");
- + }
- + entry.resources.push(entryResource);
- }
- // Entry author
- let authorItem = item.querySelector(xmlRules.feedItemAuthor);
- if (authorItem) {
- if (authorItem.querySelector("name")) {
- entry.authors = [];
- let authors = item.querySelectorAll(xmlRules.feedItemAuthor);
- for (const author of authors) {
- let entryAuthor = {};
- entryAuthor.name = author.querySelector("name").textContent;
- if (author.querySelector("uri")) {
- entryAuthor.uri = author.querySelector("uri").textContent;
- }
- entry.authors.push(entryAuthor);
- }
- + entry.authors = [];
- + let nodesAuthor = nodeEntry.queryPathAll(xmlns.atom, "atom:author");
- + for (const nodeAuthor of nodesAuthor) {
- + let entryAuthor = {};
- + let nodeAuthorName = nodeAuthor.queryPath(xmlns.atom, "atom:name");
- + let nodeAuthorUri = nodeAuthor.queryPath(xmlns.atom, "atom:uri");
- + if (nodeAuthorName && nodeAuthorName.textContent) {
- + entryAuthor.name = nodeAuthorName.textContent;
- + } else {
- + continue;
- }
- + if (nodeAuthorUri) {
- + entryAuthor.uri = nodeAuthorUri.textContent;
- + }
- + entry.authors.push(entryAuthor);
- }
- let entry_content;
- entry_content = entry.text + " " + entry.title + " " + entry.subtitle;
- @@ -6755,131 +6560,171 @@
- }
- }
- }
- feed.next = xmlFile.querySelector(xmlRules.feedItemLinkNext);
- feed.prev = xmlFile.querySelector(xmlRules.feedItemLinkPrevious);
- if (enableIcon) {
- let iconPath = xmlFile.querySelector(xmlRules.feedIcon);
- let logoPath = xmlFile.querySelector(xmlRules.feedLogo);
- if (logoPath || iconPath) {
- if (logoPath) {
- feed.icon = logoPath.textContent;
- +}
- +
- +function extractFoaf(xmlFile, feed) {
- + let nodeRdf = xmlFile.queryPath(xmlns.rdf, "rdf:RDF");
- + let nodeGroup = nodeRdf.queryPath(xmlns.foaf, "foaf:Group");
- + let nodeTitle = nodeGroup.queryPath(xmlns.foaf, "foaf:name");
- + if (nodeTitle) {
- + feed.title = nodeTitle.textContent;
- + }
- + let nodeLink = nodeGroup.queryPath(xmlns.foaf, "foaf:homepage");
- + if (nodeLink) {
- + feed.link = nodeLink.textContent;
- + }
- + let nodeSeeAlso = nodeGroup.queryPath(xmlns.rdfs, "rdfs:seeAlso");
- + if (nodeSeeAlso) {
- + feed.alternate = nodeSeeAlso.getAttribute("rdf:resource");;
- + }
- + let nodesMember = nodeGroup.queryPathAll(xmlns.foaf, "foaf:member");
- + feed.count = nodesMember.length;
- + if (feed.count) {
- + feed.entries = [];
- + let numberOfArticles = 0, numberOfArticlesBlacklisted = 0;
- + for (const nodeMember of nodesMember) {
- + let entry = {};
- + let nodeAgent = nodeMember.queryPath(xmlns.foaf, "foaf:Agent");
- + let nodeWeblog = nodeAgent.queryPath(xmlns.foaf, "foaf:weblog");
- + let nodeDocument = nodeWeblog.queryPath(xmlns.foaf, "foaf:Document");
- + // Entry title
- + let nodeName = nodeAgent.queryPath(xmlns.foaf, "foaf:name");
- + if (nodeName && nodeName.textContent.trim().length) {
- + entry.title = nodeName.textContent.replace(/(<([^>]+)>)/gi, "");
- + }
- + // Entry link
- + let nodeSeeAlso = nodeDocument.queryPath(xmlns.rdfs, "rdfs:seeAlso");
- + let nodeChannel = nodeSeeAlso.queryPath(xmlns.rss, "rss:channel");
- + if (nodeChannel && nodeChannel.getAttribute("rdf:about")) {
- + entry.link = nodeChannel.getAttribute("rdf:about");
- } else
- if (iconPath) {
- feed.icon = iconPath.textContent;
- + if (nodeDocument && nodeDocument.getAttribute("rdf:about")) {
- + entry.link = nodeDocument.getAttribute("rdf:about");
- + }
- + // Entry content
- + let nodeTitle = nodeDocument.queryPath(xmlns.dc, "dc:title");
- + if (nodeTitle && nodeTitle.textContent.trim().length) {
- + entry.text = nodeTitle.textContent.replace(/(<([^>]+)>)/gi, "");
- + }
- + // TODO Entry attachments
- + // TODO Entry author
- + let entry_content;
- + entry_content = entry.text + " " + entry.title + " " + entry.link;
- + entry_content = entry_content.toLowerCase();
- + entry.blacklisted = false;
- + entry.whitelisted = false;
- + if (filterBlacklist && keywordsBlacklist) {
- + if (filterWhitelist) {
- + for (let keyword of keywordsWhitelist.split(",")) {
- + if (keyword.length && entry_content.includes(keyword)) {
- + entry.whitelisted = true;
- + break;
- + }
- + }
- + }
- + if (!entry.whitelisted) {
- + for (let keyword of keywordsBlacklist.split(",")) {
- + for (let character of [" ", "’", "'", "-", '"', ":", ";", ",", "."]) {
- + if (keyword.length && entry_content.includes(" " + keyword + character)) {
- + entry.blacklisted = true;
- + break;
- + }
- + }
- + }
- + }
- + }
- + feed.entries.push(entry);
- + if (entry.whitelisted) {
- + numberOfArticlesBlacklisted += 1;
- + }
- + numberOfArticles += 1;
- + let numberOfArticlesToDisplay = numberOfArticles - numberOfArticlesBlacklisted;
- + if (
- + numberOfArticlesToDisplay > minimumItemNumber &&
- + numberOfArticlesToDisplay < feed.count &&
- + !ignoreMinimumItemNumber
- + ) {
- + break;
- }
- }
- }
- return feed;
- }
-
-function extractRdf(xmlFile) {
- const xmlRules = {
- "feedLanguage" : "channel > language", // TODO Test
- "feedTitlePage" : "channel > title",
- "feedSubtitle" : "channel > description",
- "feedIcon" : "channel > image", // TODO Test
- "feedLink" : "channel > link",
- "feedDate" : "date",
- "feedItem" : "item",
- "feedItemTitle" : "title",
- "feedItemLink" : "link",
- "feedItemDate" : "date", // dc:date
- "feedItemCreator" : "dc:creator",
- "feedItemPublisher" : "dc:publisher",
- "feedItemRights" : "dc:rights",
- "feedItemContent" : "description",
- "feedItemEnclosure" : "resource" // TODO Test
- };
- let feed = {};
- if (xmlFile.querySelector(xmlRules.feedTitlePage)) {
- feed.title = xmlFile.querySelector(xmlRules.feedTitlePage).textContent;
- }
- if (xmlFile.querySelector(xmlRules.feedLanguage)) {
- feed.language = xmlFile.querySelector(xmlRules.feedLanguage).textContent;
-
- +function extractRdf(xmlFile, feed) {
- + let nodeRdf = xmlFile.queryPath(xmlns.rdf, "rdf:RDF");
- + let nodeChannel = nodeRdf.queryPath(xmlns.rss, "rss:channel");
- + if (enableIcon) {
- + let nodeImage = nodeChannel.queryPath(xmlns.rss, "rss:image");
- + if (nodeImage) {
- + feed.icon = nodeImage.getAttribute("rdf:resource");
- + }
- +
- }
- if (xmlFile.querySelector(xmlRules.feedSubtitle)) {
- feed.subtitle = xmlFile.querySelector(xmlRules.feedSubtitle).textContent.replace(/(<([^>]+)>)/gi, "");
- + let nodeTitle = nodeChannel.queryPath(xmlns.rss, "rss:title");
- + if (nodeTitle) {
- + feed.title = nodeTitle.textContent;
- + }
- + //let nodeLanguage = nodeChannel.queryPath(xmlns.rss, "rss:language");
- + //if (nodeLanguage) {
- + // feed.language = nodeLanguage.textContent;
- + //}
- + let nodeDescription = nodeChannel.queryPath(xmlns.rss, "rss:description");
- + if (nodeDescription) {
- + feed.subtitle = nodeDescription.textContent.replace(/(<([^>]+)>)/gi, "");
- }
- feed.count = xmlFile.querySelectorAll(xmlRules.feedItem).length;
- + let nodesItem = nodeRdf.queryPathAll(xmlns.rss, "rss:item");
- + feed.count = nodesItem.length;
- if (feed.count) {
- feed.entries = [];
- let numberOfArticles = 0, numberOfArticlesBlacklisted = 0;
- for (const item of xmlFile.querySelectorAll(xmlRules.feedItem)) {
- + for (const nodeItem of nodesItem) {
- let entry = {};
- // Entry date
- if (item.querySelector(xmlRules.feedItemDate)) {
- entry.date = item.querySelector(xmlRules.feedItemDate).textContent;
- + let nodeDate = nodeItem.queryPath(xmlns.dc, "dc:date");
- + if (nodeDate) {
- + entry.date = nodeDate.textContent;
- }
- // Entry title
- if (item.querySelector(xmlRules.feedItemTitle) &&
- item.querySelector(xmlRules.feedItemTitle).textContent.trim().length) {
- entry.title = item.querySelector(xmlRules.feedItemTitle).textContent.replace(/(<([^>]+)>)/gi, "");
- + let nodeTitle = nodeItem.queryPath(xmlns.rss, "rss:title");
- + if (nodeTitle && nodeTitle.textContent.trim().length) {
- + entry.title = nodeTitle.textContent.replace(/(<([^>]+)>)/gi, "");
- } else
- if (entry.date) {
- let dateAsTitle = new Date(entry.date);
- entry.title = dateAsTitle.toDateString();
- }
- // Entry link
- if (item.querySelector(xmlRules.feedItemLink) &&
- item.querySelector(xmlRules.feedItemLink).textContent.length) {
- + let nodeLink = nodeItem.queryPath(xmlns.rss, "rss:link");
- + if (nodeLink && nodeLink.textContent.length) {
- // FIXME Ignore whitespace
- // https://handheldgameconsoles.com/f.atom
- entry.link = item.querySelector(xmlRules.feedItemLink).textContent;
- } else
- if (getHomeLink(xmlFile, xmlRules)) {
- entry.link = getHomeLink(xmlFile, xmlRules);
-
- + entry.link = nodeLink.textContent;
- }
- + //if (getHomeLink(xmlFile, xmlRules)) {
- + // entry.link = getHomeLink(xmlFile, xmlRules);
- + //}
- // Entry content
- +// if (contentMode == "content-complete" || contentMode == "content-summary") {
- if (contentMode != "content-title") {
- let contentExist = item.querySelector(xmlRules.feedItemContent);
- let summaryExist = item.querySelector(xmlRules.feedItemSummary);
- if ((!summaryExist && contentExist) || (contentExist && contentMode == "content-complete")) {
- entry.text = item.querySelector(xmlRules.feedItemContent).textContent;
- if (/<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- entry.html = true;
- }
- }
- if ((!contentExist && summaryExist) || (summaryExist && contentMode == "content-summary")) {
- entry.text = item.querySelector(xmlRules.feedItemSummary).textContent;
- if (/<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- entry.html = true;
- }
-
- + let nodeDescription = nodeItem.queryPath(xmlns.rss, "rss:description");
- + if (nodeDescription) {
- + entry.text = nodeDescription.textContent;
- }
- }
- // TODO Entry attachments
- entry.enclosures = [];
- if (enableEnclosure && item.querySelector(xmlRules.feedItemAttachment)) {
- let related_enclosures = item.querySelectorAll(xmlRules.feedItemAttachment);
- for (const enclosure of related_enclosures) {
- let entryEnclosure = {};
- if (enclosure.getAttribute("type")) {
- entryEnclosure.type = enclosure.getAttribute("type");
- } else {
- entryEnclosure.type = "";
- }
- if (enclosure.getAttribute("title")) {
- entryEnclosure.title = enclosure.getAttribute("title");
- }
- if (enclosure.getAttribute("length")) {
- entryEnclosure.length = enclosure.getAttribute("length");
- }
- if (enclosure.getAttribute("href")) {
- entryEnclosure.uri = enclosure.getAttribute("href");
- entryEnclosure.filename = extractEnclosureFilename(entryEnclosure.uri.trim());
- }
- entry.enclosures.push(entryEnclosure);
- }
- }
- // Entry author
- let authorItem = item.getElementsByTagName(xmlRules.feedItemCreator)[0];
- if (authorItem) {
- entry.authors = [];
- let authors = item.getElementsByTagName(xmlRules.feedItemCreator);
- for (const author of authors) {
- let entryAuthor = {};
- entryAuthor.name = author.textContent;
- entry.authors.push(entryAuthor);
- }
-
- + entry.authors = [];
- + let nodeAuthor = nodeItem.queryPath(xmlns.dc, "dc:creator");
- + let nodePublisher = nodeItem.queryPath(xmlns.dc, "dc:publisher");
- + if (nodeAuthor.textContent) {
- + let entryAuthor = {};
- + entryAuthor.name = nodeAuthor.textContent;
- + entry.authors.push(entryAuthor);
- + } else
- + if (nodePublisher.textContent) {
- + let entryAuthor = {};
- + entryAuthor.name = nodePublisher.textContent;
- + entry.authors.push(entryAuthor);
- }
- let entry_content;
- entry_content = entry.text + " " + entry.title + " " + entry.subtitle;
- @@ -6921,158 +6766,132 @@
- }
- }
- }
- if (enableIcon) {
- let iconPath = xmlFile.querySelector(xmlRules.feedIcon);
- if (iconPath) {
- feed.icon = iconPath.getAttribute("rdf:resource");
- }
- }
- return feed;
-}
-
-function extractRss(xmlFile) {
- const xmlRules = {
- "feedDate" : "lastBuildDate",
- "feedGenerated" : "channel > generator",
- "feedIcon" : "channel > image > url",
- "feedLanguage" : "channel > language",
- "feedLink" : "channel > link",
- "feedRights" : "channel > copyright",
- "feedSubtitle" : "channel > description",
- "feedTitlePage" : "channel > title",
- "feedItem" : "item",
- "feedItemTitle" : "title",
- "feedItemLink" : "link",
- "feedItemDate" : "pubDate",
- "feedItemAuthor" : "dc:creator",
- "feedItemContent" : "description",
- // NOTE prefer content:encoded
- // https://agovernmentofwolves.com/feed/
- //"feedItemSummary" : "content\\:encoded",
- "feedItemEnclosure" : "enclosure",
- "feedItemMedia" : "media:content"
- // NOTE CSS Selectors do not work (not even with CSS.escape `media\\:content`.
- // TODO Utilize a proper query language such as XPath.
- };
- let feed = {};
- if (xmlFile.querySelector(xmlRules.feedTitlePage)) {
- feed.title = xmlFile.querySelector(xmlRules.feedTitlePage).textContent;
- }
- if (xmlFile.querySelector(xmlRules.feedLanguage)) {
- feed.language = xmlFile.querySelector(xmlRules.feedLanguage).textContent;
- +}
- +
- +function extractRss(xmlFile, feed) {
- + let nodeChannel = xmlFile.queryPath(null, "rss/channel");
- + let nodeTitle = nodeChannel.queryPath(null, "title");
- + if (nodeTitle) {
- + feed.title = nodeTitle.textContent;
- + }
- + let nodeLanguage = nodeChannel.queryPath(null, "language");
- + if (nodeLanguage) {
- + feed.language = nodeLanguage.textContent;
- + }
- + let nodeDescription = nodeChannel.queryPath(null, "description");
- + if (nodeDescription) {
- + feed.subtitle = nodeDescription.textContent.replace(/(<([^>]+)>)/gi, "");
- }
- if (xmlFile.querySelector(xmlRules.feedSubtitle)) {
- feed.subtitle = xmlFile.querySelector(xmlRules.feedSubtitle).textContent.replace(/(<([^>]+)>)/gi, "");
-
- + if (enableIcon) {
- + let nodeImageUrl = nodeChannel.queryPath(null, "image/url");
- + if (nodeImageUrl) {
- + feed.icon = nodeImageUrl.textContent;
- + }
- + if (!feed.icon) {
- + let nodeIcon = nodeChannel.queryPath(xmlns.webfeeds, "webfeeds:icon");
- + if (nodeIcon) {
- + feed.icon = nodeIcon.textContent;
- + }
- + }
- +
- }
- feed.count = xmlFile.querySelectorAll(xmlRules.feedItem).length;
- + let nodesItem = nodeChannel.queryPathAll(null, "item");
- + feed.count = nodesItem.length;
- if (feed.count) {
- feed.entries = [];
- let numberOfArticles = 0, numberOfArticlesBlacklisted = 0;
- for (const item of xmlFile.querySelectorAll(xmlRules.feedItem)) {
- + for (const nodeItem of nodesItem) {
- let entry = {};
- // Entry date
- if (item.querySelector(xmlRules.feedItemDate)) {
- entry.date = item.querySelector(xmlRules.feedItemDate).textContent;
- + let nodeDate = nodeItem.queryPath(xmlns.dc, "dc:date");
- + let nodePubDate = nodeItem.queryPath(null, "pubDate");
- + if (nodePubDate) {
- + entry.date = nodePubDate.textContent;
- + } else
- + if (nodeDate) {
- + entry.date = nodeDate.textContent;
- }
- // Entry title
- if (item.querySelector(xmlRules.feedItemTitle) &&
- item.querySelector(xmlRules.feedItemTitle).textContent.trim().length) {
- entry.title = item.querySelector(xmlRules.feedItemTitle).textContent.replace(/(<([^>]+)>)/gi, "");
- } else
- if (entry.date) {
- let dateAsTitle = new Date(entry.date);
- entry.title = dateAsTitle.toDateString();
-
- + let nodeTitle = nodeItem.queryPath(null, "title");
- + if (nodeTitle && nodeTitle.textContent.trim().length) {
- + entry.title = nodeTitle.textContent.replace(/(<([^>]+)>)/gi, "");
- }
- // Entry link
- if (item.querySelector(xmlRules.feedItemLink) &&
- item.querySelector(xmlRules.feedItemLink).textContent.length) {
- + let nodeLink = nodeItem.queryPath(null, "link");
- + if (nodeLink && nodeLink.textContent.length) {
- // FIXME Ignore whitespace
- // https://handheldgameconsoles.com/f.atom
- entry.link = item.querySelector(xmlRules.feedItemLink).textContent;
- } else
- if (getHomeLink(xmlFile, xmlRules)) {
- entry.link = getHomeLink(xmlFile, xmlRules);
-
- + entry.link = nodeLink.textContent;
- }
- + //if (getHomeLink(xmlFile, xmlRules)) {
- + // entry.link = getHomeLink(xmlFile, xmlRules);
- + //}
- // Entry content
- + // NOTE prefer content:encoded
- + // https://agovernmentofwolves.com/feed/
- +// if (contentMode == "content-complete" || contentMode == "content-summary") {
- if (contentMode != "content-title") {
- let contentExist = item.querySelector(xmlRules.feedItemContent);
- let summaryExist = item.querySelector(xmlRules.feedItemSummary);
- if ((!summaryExist && contentExist) || (contentExist && contentMode == "content-complete")) {
- entry.text = item.querySelector(xmlRules.feedItemContent).textContent;
- if (/<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- entry.html = true;
- }
- }
- if ((!contentExist && summaryExist) || (summaryExist && contentMode == "content-summary")) {
- entry.text = item.querySelector(xmlRules.feedItemSummary).textContent;
- if (/<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- entry.html = true;
- }
-
- + let nodeDescription = nodeItem.queryPath(null, "description");
- + let nodeContentEncoded = nodeItem.queryPath(xmlns.content, "encoded");
- +// if (nodeContentEncoded) {
- + if (contentMode == "content-complete" && nodeContentEncoded) {
- + entry.text = nodeContentEncoded.textContent;
- + } else // contentMode == "content-summary"
- + if (nodeDescription && nodeDescription.textContent) {
- + entry.text = nodeDescription.textContent;
- +
- }
- }
- // Entry enclosures
- if (enableEnclosure) {
- entry.enclosures = [];
- if (item.querySelector(xmlRules.feedItemEnclosure)) {
- let related_enclosures = item.querySelectorAll(xmlRules.feedItemEnclosure);
- for (const enclosure of related_enclosures) {
- let entryEnclosure = {};
- if (enclosure.getAttribute("type")) {
- entryEnclosure.type = enclosure.getAttribute("type");
- } else {
- entryEnclosure.type = "";
- }
- // NOTE Check if there is an element enclosure with attribute title for RSS.
- if (enclosure.getAttribute("title")) {
- entryEnclosure.title = enclosure.getAttribute("title");
- }
- if (enclosure.getAttribute("length")) {
- entryEnclosure.length = enclosure.getAttribute("length");
- }
- if (enclosure.getAttribute("url")) {
- entryEnclosure.uri = enclosure.getAttribute("url");
- entryEnclosure.filename = extractEnclosureFilename(entryEnclosure.uri.trim());
- }
- entry.enclosures.push(entryEnclosure);
-
- + let nodesEnclosure = nodeItem.queryPathAll(null, "enclosure");
- + for (const nodeEnclosure of nodesEnclosure) {
- + let entryEnclosure = {};
- + if (nodeEnclosure.getAttribute("type")) {
- + entryEnclosure.type = nodeEnclosure.getAttribute("type");
- + } else {
- + entryEnclosure.type = "";
- + }
- + // NOTE Check if there is an element enclosure with attribute title for RSS.
- + if (nodeEnclosure.getAttribute("title")) {
- + entryEnclosure.title = nodeEnclosure.getAttribute("title");
- + }
- + if (nodeEnclosure.getAttribute("length")) {
- + entryEnclosure.length = nodeEnclosure.getAttribute("length");
- + }
- + if (nodeEnclosure.getAttribute("url")) {
- + entryEnclosure.uri = nodeEnclosure.getAttribute("url");
- + entryEnclosure.filename = extractEnclosureFilename(entryEnclosure.uri.trim());
- }
- + entry.enclosures.push(entryEnclosure);
- }
- // /questions/45110893/select-elements-by-attributes-with-colon
- // const feedItemMediaQuery = CSS.escape(feedItemMedia)
- // console.log(document.querySelectorAll(`[${feedItemMediaQuery}]`))
- // Neither work. Utilize XPath.
- //console.log(item.querySelectorAll(`[media\\:content]`))
- //console.log(item)
- //console.log(item.querySelectorAll(`[media\\3A content]`))
- // Media RSS
- if (item.getElementsByTagName(xmlRules.feedItemMedia).length) {
- let related_media = item.getElementsByTagName(xmlRules.feedItemMedia);
- for (const media of related_media) {
- let entryMedia = {};
- if (media.getAttribute("medium")) {
- entryMedia.type = media.getAttribute("medium");
- }
- if (media.getAttribute("url")) {
- mediaUrl = media.getAttribute("url");
- entryMedia.uri = mediaUrl;
- entryMedia.filename = mediaUrl.split("/").pop();
- }
- if (media.getAttribute("length")) {
- entryMedia.length = media.getAttribute("length");
- }
- entry.enclosures.push(entryMedia);
-
- + let nodesMedia = nodeItem.queryPathAll(xmlns.media, "content");
- + for (const media of nodesMedia) {
- + let entryMedia = {};
- + if (media.getAttribute("type")) {
- + entryMedia.type = media.getAttribute("type");
- + } else
- + if (media.getAttribute("medium")) {
- + entryMedia.type = media.getAttribute("medium");
- }
- }
- }
- // Entry author
- let authorItem = item.getElementsByTagName(xmlRules.feedItemAuthor);
- if (authorItem.length) {
- entry.authors = [];
- let authors = item.getElementsByTagName(xmlRules.feedItemAuthor);
- for (const author of authors) {
- let entryAuthor = {};
- entryAuthor.name = author.textContent;
- entry.authors.push(entryAuthor);
- }
-
- + if (media.getAttribute("url")) {
- + mediaUrl = media.getAttribute("url");
- + entryMedia.uri = mediaUrl;
- + entryMedia.filename = mediaUrl.split("/").pop();
- + }
- + if (media.getAttribute("length")) {
- + entryMedia.length = media.getAttribute("length");
- + }
- + entry.enclosures.push(entryMedia);
- + }
- + }
- + // Entry creator
- + entry.authors = [];
- + let nodesCreator = nodeItem.queryPathAll(xmlns.dc, "dc:creator");
- + for (const author of nodesCreator) {
- + let entryAuthor = {};
- + entryAuthor.name = author.textContent;
- + entry.authors.push(entryAuthor);
- }
- let entry_content;
- entry_content = entry.text + " " + entry.title + " " + entry.subtitle;
- @@ -7114,132 +6933,77 @@
- }
- }
- }
- if (enableIcon) {
- let iconPath = xmlFile.querySelector(xmlRules.feedIcon);
- let logoPath = xmlFile.querySelector(xmlRules.feedLogo);
- if (logoPath || iconPath) {
- if (logoPath) {
- feed.icon = logoPath.textContent;
- } else
- if (iconPath) {
- feed.icon = iconPath.textContent;
- }
- }
- }
- return feed;
- }
-
-function extractSmf(xmlFile) {
- const xmlRules = {
- // FIXME "smf\\:xml-feed" does not appear to work
- // or at least not when it is originated from json
- "feedLanguage" : "smf\\:xml-feed", // NOTE @xml:lang
- "feedTitlePage" : "smf\\:xml-feed", // NOTE @forum-name
- "feedSubtitle" : "smf\\:xml-feed", // NOTE @description
- "feedLink" : "smf\\:xml-feed",
- "feedDate" : "time", // NOTE @generated-date-UTC @generated-date-localized
- "feedGenerated" : "generator",
- "feedItem" : "recent-post",
- "feedItemTitle" : "subject",
- "feedItemLink" : "topic > link",
- "feedItemTime" : "time",
- "feedItemPoster" : "poster",
- "feedItemStarter" : "starter",
- "feedItemContent" : "body",
- "feedItemAttachment" : "attachments" // TODO
- };
- let feed = {};
- if (xmlFile.querySelector(xmlRules.feedTitlePage)) {
- feed.title = xmlFile.querySelector(xmlRules.feedTitlePage).getAttribute("forum-name");
- feed.subtitle = xmlFile.querySelector(xmlRules.feedTitlePage).getAttribute("description");
- //feed.subtitle = xmlFile.querySelector(xmlRules.feedTitlePage).getAttribute("about");
- }
- if (xmlFile.getElementsByTagName("smf:xml-feed") &&
- xmlFile.getElementsByTagName("smf:xml-feed").length) {
- feed.language = xmlFile.getElementsByTagName("smf:xml-feed")[0].getAttribute("xml:lang");
- }
- feed.count = xmlFile.querySelectorAll(xmlRules.feedItem).length;
- +function extractSmf(xmlFile, feed) {
- + let nodeFeed = xmlFile.queryPath(xmlns.smf, "smf:xml-feed");
- + feed.date = nodeFeed.getAttribute("generated-date-UTC"); // generated-date-localized
- + feed.subtitle = nodeFeed.getAttribute("description");
- + //feed.subtitle = nodeFeed.getAttribute("about");
- + feed.title = nodeFeed.getAttribute("forum-name");
- + feed.language = nodeFeed.getAttribute("xml:lang");
- + //feed.link = nodeFeed.getAttribute("forum-url");
- + feed.version = nodeFeed.getAttribute("version");
- + let nodesRecentPost = nodeFeed.queryPathAll(null, "recent-post");
- + feed.count = nodesRecentPost.length;
- if (feed.count) {
- feed.entries = [];
- let numberOfArticles = 0, numberOfArticlesBlacklisted = 0;
- for (const item of xmlFile.querySelectorAll(xmlRules.feedItem)) {
- + for (const nodeRecentPost of nodesRecentPost) {
- let entry = {};
- // Entry date
- if (item.querySelector(xmlRules.feedItemTime)) {
- entry.date = item.querySelector(xmlRules.feedItemTime).textContent;
- + let nodeTime = nodeRecentPost.queryPath(null, "time");
- + if (nodeTime) {
- + //entry.date = nodeTime.textContent;
- + entry.date = nodeTime.getAttribute("UTC");
- + } else {
- + entry.date = nodeTime.textContent;
- }
- // Entry title
- if (item.querySelector(xmlRules.feedItemTitle) &&
- item.querySelector(xmlRules.feedItemTitle).textContent.trim().length) {
- entry.title = item.querySelector(xmlRules.feedItemTitle).textContent.replace(/(<([^>]+)>)/gi, "");
- } else
- if (entry.date) {
- let dateAsTitle = new Date(entry.date);
- entry.title = dateAsTitle.toDateString();
-
- + let nodeSubject = nodeRecentPost.queryPath(null, "subject");
- + if (nodeSubject && nodeSubject.textContent.trim().length) {
- + // NOTE Perhaps HTML should be parsed.
- + entry.title = nodeSubject.textContent.replace(/(<([^>]+)>)/gi, "");
- }
- // Entry link
- if (item.querySelector(xmlRules.feedItemLink) &&
- item.querySelector(xmlRules.feedItemLink).textContent.length) {
- + let nodeLink = nodeRecentPost.queryPath(null, "link");
- + if (nodeLink && nodeLink.textContent.length) {
- // FIXME Ignore whitespace
- // https://handheldgameconsoles.com/f.atom
- entry.link = item.querySelector(xmlRules.feedItemLink).textContent;
- } else
- if (getHomeLink(xmlFile, xmlRules)) {
- entry.link = getHomeLink(xmlFile, xmlRules);
-
- + entry.link = nodeLink.textContent;
- }
- + //if (getHomeLink(xmlFile, xmlRules)) {
- + // entry.link = getHomeLink(xmlFile, xmlRules);
- + //}
- // Entry content
- +// if (contentMode == "content-complete" || contentMode == "content-summary") {
- if (contentMode != "content-title") {
- let contentExist = item.querySelector(xmlRules.feedItemContent);
- let summaryExist = item.querySelector(xmlRules.feedItemSummary);
- if ((!summaryExist && contentExist) || (contentExist && contentMode == "content-complete")) {
- entry.text = item.querySelector(xmlRules.feedItemContent).textContent;
- if (/<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- entry.html = true;
- }
- }
- if ((!contentExist && summaryExist) || (summaryExist && contentMode == "content-summary")) {
- entry.text = item.querySelector(xmlRules.feedItemSummary).textContent;
- if (/<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- entry.html = true;
- }
-
- + let nodeBody = nodeRecentPost.queryPath(null, "body");
- + if (nodeBody) {
- + entry.text = nodeBody.textContent;
- }
- }
- // TODO Entry attachments
- entry.enclosures = [];
- if (enableEnclosure && item.querySelector(xmlRules.feedItemAttachment)) {
- let related_enclosures = item.querySelectorAll(xmlRules.feedItemAttachment);
- for (const enclosure of related_enclosures) {
- let entryEnclosure = {};
- if (enclosure.getAttribute("type")) {
- entryEnclosure.type = enclosure.getAttribute("type");
- } else {
- entryEnclosure.type = "";
- }
- if (enclosure.getAttribute("title")) {
- entryEnclosure.title = enclosure.getAttribute("title");
- }
- if (enclosure.getAttribute("length")) {
- entryEnclosure.length = enclosure.getAttribute("length");
- }
- if (enclosure.getAttribute("href")) {
- entryEnclosure.uri = enclosure.getAttribute("href");
- entryEnclosure.filename = extractEnclosureFilename(entryEnclosure.uri.trim());
- }
- entry.enclosures.push(entryEnclosure);
- + // Entry author
- + let nodePoster = nodeRecentPost.queryPath(null, "poster");
- + let nodeStarter = nodeRecentPost.queryPath(null, "starter");
- + if (nodePoster) {
- + if (nodePoster.queryPath(null, "name")) {
- + entry.authors = [];
- + let entryAuthor = {};
- + entryAuthor.name = nodePoster.queryPath(null, "name").textContent;
- + entryAuthor.uri = nodePoster.queryPath(null, "link").textContent;
- + entry.authors.push(entryAuthor);
- }
- }
- // Entry author
- let authorItem = item.querySelector(xmlRules.feedItemPoster);
- if (authorItem) {
- if (authorItem.querySelector("name")) {
- + if (nodeStarter) {
- + if (nodeStarter.queryPath(null, "name")) {
- entry.authors = [];
- let authors = item.querySelectorAll(xmlRules.feedItemPoster);
- for (const author of authors) {
- let entryAuthor = {};
- entryAuthor.name = author.querySelector("name").textContent;
- entryAuthor.uri = author.querySelector("link").textContent;
- entry.authors.push(entryAuthor);
- }
-
- + let entryAuthor = {};
- + entryAuthor.name = nodeStarter.queryPath(null, "name").textContent;
- + entryAuthor.uri = nodeStarter.queryPath(null, "link").textContent;
- + entry.authors.push(entryAuthor);
- }
- }
- let entry_content;
- @@ -7282,7 +7046,6 @@
- }
- }
- }
- return feed;
- }
-
- function renderDocument(feed) {
- @@ -7317,13 +7080,27 @@
- let index = Array.from(feed.entries).indexOf(entry) + 1;
- let titleToc = newDocument.createElement("a");
- if (!entry.title) {
- + //if (!entry.title || entry.title && !entry.title.length) {
- if (entry.date) {
- entry.title = `${entry.date.slice(0,10)} ${entry.text.replace(/(<([^>]+)>)/gi, "").slice(0,50)}...`;
- + let plainDate, plainText;
- + if (entry.text) {
- + plainText = entry.text.replace(/(<([^>]+)>)/gi, "");
- + } else {
- + plainText = "No title";
- + }
- + if (feed.type.toLowerCase().includes("rdf") ||
- + feed.type.toLowerCase().includes("rss")) {
- + plainDate = entry.date.slice(0,16);
- + } else {
- + plainDate = entry.date.slice(0,10);
- + }
- + let dateText = `${plainDate} ${plainText}…`;
- + entry.title = dateText.slice(0,50);
- } else {
- entry.title = "No title";
- }
- }
- titleToc.textContent = entry.title.replace(/(<([^>]+)>)/gi, "");
- + titleToc.textContent = entry.title;
- titleToc.href = `#newspaper-oujs-${index}`;
- titleToc.title = titleToc.textContent.trim();
- let liElement = newDocument.createElement("li");
- @@ -7344,7 +7121,7 @@
- date.textContent = entry.date;
- article.append(date);
- let text = newDocument.createElement("div");
- if (entry.html) {
- + if (entry.html || /<\/?[a-z][\s\S]*>/i.test(entry.text)) {
- text.className = "content";
- text.innerHTML = entry.text;
- } else {
- @@ -7481,7 +7258,7 @@
- let authors = newDocument.createElement("div");
- authors.className = "authors";
- article.append(authors);
- for (entryAuthor of entry.authors) {
- + for (let entryAuthor of entry.authors) {
- let author = newDocument.createElement("a");
- author.className = "author";
- if (entryAuthor.uri) {
- @@ -7550,37 +7327,47 @@
- return newDocument;
- }
-
- +// TODO Support categories of element "outline".
- // NOTE http://east-village.org/subs.opml
-function extractOpml(xmlFile) {
- let feed = {};
- if (xmlFile.querySelector("head > title")) {
- feed.title = xmlFile.querySelector("head > title").textContent;
- }
- if (xmlFile.querySelector("head > description")) {
- feed.subtitle = xmlFile.querySelector("head > description").textContent;
- }
- feed.count = xmlFile.querySelectorAll("body outline").length;
- if (feed.count) {
- +function extractOpml(xmlFile, feed) {
- + let nodeOpml = xmlFile.queryPath(null, "opml");
- + let nodeHead = nodeOpml.queryPath(null, "head");
- + let nodeTitle = nodeHead.queryPath(null, "title");
- + if (nodeTitle) {
- + feed.title = nodeTitle.textContent;
- + }
- + let nodeDescription = nodeHead.queryPath(null, "description");
- + if (nodeDescription) {
- + feed.title = nodeDescription.textContent;
- + }
- + let nodeBody = nodeOpml.queryPath(null, "body");
- + let nodesOutlineOutline = nodeBody.queryPathAll(null, "outline/outline");
- + feed.count = nodesOutlineOutline.length;
- + let nodesOutline = nodeBody.queryPathAll(null, "outline");
- + let outline_count = nodesOutline.length;
- + if (outline_count) {
- feed.entries = [];
- for (const item of xmlFile.querySelectorAll("body outline")) {
- let entry = {};
- if (!item.children.length) {
- entry.text = item.getAttribute("description");
- entry.title = item.getAttribute("text");
- entry.link = item.getAttribute("xmlUrl");
- if (entry.link && !entry.link.length) {
- entry.link = item.getAttribute("htmlUrl");
- }
- if (entry.link && !entry.link.length) {
- entry.link = item.getAttribute("href");
- + for (const nodeOutline of nodesOutline) {
- + if (nodeOutline.children.length) {
- + let nodesOutlineOutline = nodeOutline.queryPathAll(null, "outline");
- + for (const nodeOutlineOutline of nodesOutlineOutline) {
- + let entry = {};
- + entry.text = nodeOutlineOutline.getAttribute("description");
- + entry.title = nodeOutlineOutline.getAttribute("text");
- + entry.link = nodeOutlineOutline.getAttribute("xmlUrl");
- + if (entry.link && !entry.link.length) {
- + entry.link = nodeOutlineOutline.getAttribute("htmlUrl");
- + }
- + if (entry.link && !entry.link.length) {
- + entry.link = nodeOutlineOutline.getAttribute("href");
- + }
- + if (entry.link && entry.link.length) {
- + feed.entries.push(entry);
- + }
- +
- }
- }
- if (entry.link && entry.link.length) {
- feed.entries.push(entry);
- }
- }
- }
- return feed;
- }
-
- function createPage() {
- @@ -7590,7 +7377,7 @@
- }
-
- async function getFontSize() {
- return fontSize = await GM.getValue("font-size", 20);
- + return await GM.getValue("font-size", 20);
- }
-
- async function setSettingValue(key, message) {
- @@ -7638,7 +7425,7 @@
- }
-
- async function getContentMode() {
- return await GM.getValue("content-mode", "content-summary");
- + return await GM.getValue("content-mode", "content-complete");
- }
-
- async function getFilterBlacklist() {
- @@ -8542,8 +8329,8 @@
- "a", "body", "code", ".enclosures", ".resources",
- "#control-bar-container", "#empty-feed", "#info-square"];
- if (viewMode == "dark") {
- for (cssSelector of cssSelectors) {
- for (element of newDocument.querySelectorAll(cssSelector)) {
- + for (let cssSelector of cssSelectors) {
- + for (let element of newDocument.querySelectorAll(cssSelector)) {
- element.classList.add("dark");
- let mode = newDocument.querySelector("#mode");
- //mode.textContent = "💡"; // 🌓
- @@ -8625,8 +8412,6 @@
- infoSquare.textContent = "Welcome to StreamBurner and enjoy syndicated content. Your truely, The Syndication Task Force.";
- newDocument.body.append(infoSquare);
-
- return newDocument;
-
- }
-
- async function postProcess(next, prev) {
- @@ -8685,7 +8470,7 @@
- }
-
- // Iterate through all stylesheets in the document
- for (sheet of cssFileBase) { //cssFileRTL
- + for (let sheet of cssFileBase) { //cssFileRTL
-
- // Remove comments from the CSS string
- sheet_clean = sheet.replace(/\/\*[\s\S]*?\*\//g, "");
- @@ -8742,7 +8527,7 @@
-
- for (let titleToc of document.querySelectorAll(".expand")) {
- titleToc.onclick = () => {
- titleToc.textContent = "Loading all of the items. Please wait...";
- + titleToc.textContent = "Loading all of the items. Please wait…";
- ignoreMinimumItemNumber = true;
- checkContentType();
- init;
- @@ -9434,7 +9219,7 @@
- .addEventListener ("mouseover", function() {
- document
- .querySelector("#torrents h3")
- //.innerHTML = `<span class="text-icon orange">RSS</span> & BitTorrent. It Is A Neverending Love Story...`;
- + //.innerHTML = `<span class="text-icon orange">RSS</span> & BitTorrent. It Is A Neverending Love Story…`;
- .innerHTML = `<span class="text-icon orange">Feeds</span> & BitTorrent. The NeverEnding Story.`;
- // TODO Add animated effect which will be activated upon each event mouseover
- });
- @@ -9515,13 +9300,13 @@
- // url = link.getAttribute("href");
- // }
- //})
- for (link of links) {
- + for (let link of links) {
- if (link.getAttribute("rel") == "alternate") {
- url = link.getAttribute("href");
- break;
- }
- }
- for (link of links) {
- + for (let link of links) {
- if (url) { break; }
- let rel = link.getAttribute("rel");
- if (rel && rel != "alternate") { continue; }
- @@ -9530,7 +9315,7 @@
- break;
- }
- }
- for (link of links) {
- + for (let link of links) {
- if (url) { break; }
- let rel = link.getAttribute("rel");
- if (rel && rel != "alternate") { continue; }
- @@ -9601,7 +9386,34 @@
- return date;
- }
-
-function feedInfoXML(newDocument, xmlFile, xmlRules, type) {
- +function urgentMessage(newDocument) {
- + let divElement = newDocument.createElement("div");
- + newDocument.body.append(divElement);
- + divElement.id = "urgent-message";
- + divElement.innerHTML = `
- +<p><b>Our daughter has been kidnapped by Germany.</b></p>
- +<p>We do not know whether she receives her anemia medicine since they claim that we "invented the anemia".</p>
- +<p>Please help us by downloading and sharing these resources.</p>
- +<ol>
- +<li><a href="https://metalhead.club/@error">@[email protected]</a>
- +<li><a href="https://metalhead.club/@error.rss">@[email protected] (RSS feed)</a>
- +<li><a href="https://youtube.com/@kidmafiaComedy">@kidmafiaComedy (Channel)</a></li>
- +<li><a href="https://youtube.com/@kidmafiaComedy/videos">@kidmafiaComedy (Videos)</a></li>
- +<li><a href="https://youtube.com/@kidmafiaComedy/shorts">@kidmafiaComedy (Short videos)</a></li>
- +</ol>
- +<p>Last updated at October 26, 2025</p>
- +`;
- + divElement.style.background = "#96cdd9";
- + divElement.style.borderRadius = "15px";
- + divElement.style.color = "#000";
- + divElement.style.fontSize = "90%";
- + divElement.style.margin = "auto";
- + divElement.style.marginTop = "3em";
- + divElement.style.padding = "1em";
- + divElement.style.width = "50%";
- +}
- +
- +function feedInfoXml(newDocument, feed) {
- // metadata
- let keys = [
- "language",
- @@ -9611,31 +9423,20 @@
- "generator",
- "updated",
- "type"],
- rules = [
- null,
- null,
- xmlRules.feedSubtitle,
- null,
- xmlRules.feedGenerated,
- null,
- null];
- values = [
- newDocument.querySelector("html").lang,
- newDocument.contentType,
- null,
- getHomeLink(xmlFile, xmlRules),
- null,
- getDateXML(xmlFile, xmlRules),
- type];
- + feed.subtitle,,
- + "",//getHomeLink(xmlFile, xmlRules),
- + feed.generator,
- + "",//getDateXML(xmlFile, xmlRules),
- + feed.type];
- for (let i = 0; i < keys.length; i++) {
- let meta = newDocument.createElement("meta");
- let name = keys[i];
- let content;
- if (values[i]) {
- content = values[i];
- } else
- if (xmlFile.querySelector(rules[i])) {
- content = xmlFile.querySelector(rules[i]).textContent;
- }
- if (content) {
- meta.setAttribute("name", name);
- @@ -9647,15 +9448,14 @@
- let divElement = newDocument.createElement("div");
- divElement.id = "feed-info";
- let spanElement = newDocument.createElement("span");
- spanElement.textContent = `${type}`;
- + spanElement.textContent = feed.type;
- divElement.append(spanElement);
- let generator = xmlFile.querySelector(xmlRules.feedGenerated);
- if (generator) {
- + if (feed.generator) {
- // NOTE Adding colons should probably be done by CSS.
- spanElement.textContent = `${type} : `;
- let text = generator.textContent;
- let uri = generator.getAttribute("uri");
- let version = generator.getAttribute("version");
- + spanElement.textContent = `${feed.type} : `;
- + let text = feed.generator.name;
- + let uri = feed.generator.uri;
- + let version = feed.generator.version;
- if (uri) {
- let elementGenerator = newDocument.createElement("a");
- elementGenerator.href = uri;
- @@ -9672,14 +9472,13 @@
- divElement.append(elementGenerator);
- }
- }
- if (date = xmlFile.querySelector(xmlRules.feedDate)) {
- + if (feed.date) {
- // FIXME No date for https://parlament.mt/en/rss/?t=calendar
- let spanElement = newDocument.createElement("span");
- spanElement.innerHTML = ` : Updated at <span class="published">${date.textContent}</span>`;
- + spanElement.innerHTML = ` : Updated at <span class="published">${feed.date}</span>`;
- divElement.append(spanElement);
- }
- newDocument.body.append(divElement);
- return newDocument;
- }
-
- function feedInfoTXT(newDocument, txtFile, homeRule, generatedRule, versionRule, dateRule, type) {
- @@ -9740,7 +9539,6 @@
- divElement.append(spanElement);
- }
- newDocument.body.append(divElement);
- return newDocument;
- }
-
- function feedInfoJSON(newDocument, jsonFile, homeRule, generatedRule, versionRule, dateRule, type) {
- @@ -9801,7 +9599,6 @@
- divElement.append(spanElement);
- }
- newDocument.body.append(divElement);
- return newDocument;
- }
-
- // FIXME Falkon
- @@ -9829,6 +9626,7 @@
- }
- }
-
- +// NOTE Is this "for" loop required?
- function subToMe(instance) {
- for (const service of document.querySelectorAll(".subscribe-link")) {
- if (document.contentType.endsWith("xml")) {
- @@ -9862,8 +9660,8 @@
- mode.style.filter = "saturate(7)"; // revert
- mode.title = "Switch to dark mode";
- infoSquare("Bright mode");
- for (cssSelector of cssSelectors) {
- for (element of document.querySelectorAll(cssSelector)) {
- + for (let cssSelector of cssSelectors) {
- + for (let element of document.querySelectorAll(cssSelector)) {
- element.classList.remove("dark");
- }
- }
- @@ -9875,8 +9673,8 @@
- mode.style.filter = "hue-rotate(300deg)"; // invert
- mode.title = "Switch to bright mode";
- infoSquare("Dark mode");
- for (cssSelector of cssSelectors) {
- for (element of document.querySelectorAll(cssSelector)) {
- + for (let cssSelector of cssSelectors) {
- + for (let element of document.querySelectorAll(cssSelector)) {
- element.classList.add("dark");
- }
- }
- @@ -9886,8 +9684,8 @@
- mode.style.filter = "saturate(7)"; // revert
- mode.title = "Switch to bright mode";
- infoSquare("Dark mode");
- for (cssSelector of cssSelectors) {
- for (element of document.querySelectorAll(cssSelector)) {
- + for (let cssSelector of cssSelectors) {
- + for (let element of document.querySelectorAll(cssSelector)) {
- element.classList.remove("dark");
- }
- }
- @@ -9895,8 +9693,8 @@
- mode.style.filter = "hue-rotate(300deg)"; // invert
- mode.title = "Switch to dark mode";
- infoSquare("Bright mode");
- for (cssSelector of cssSelectors) {
- for (element of document.querySelectorAll(cssSelector)) {
- + for (let cssSelector of cssSelectors) {
- + for (let element of document.querySelectorAll(cssSelector)) {
- element.classList.add("dark");
- }
- }
- @@ -9910,8 +9708,8 @@
- mode.addEventListener ("click", function() {
- cssSelectors = [
- "body", "code", "a", ".enclosures", ".resources", "#empty-feed"];
- for (cssSelector of cssSelectors) {
- for (element of document.querySelectorAll(cssSelector)) {
- + for (let cssSelector of cssSelectors) {
- + for (let element of document.querySelectorAll(cssSelector)) {
- //element.style.color = "#f5f5f5";
- element.classList.toggle("dark");
- }
- @@ -10077,26 +9875,6 @@
- }
- */
-
-function getNodeByXPath(node, query) { // FIXME
- res = document.evaluate(
- query, node, null,
- XPathResult.ANY_UNORDERED_NODE_TYPE);
- nRes = result.singleNodeValue;
- return nRes;
-}
-
-function queryByXPath(node, queries) {
- let result, i = 0;
- do {
- result = document.evaluate(
- queries[i], document,
- null, XPathResult.STRING_TYPE);
- i = i + 1;
- result = result.stringValue;
- } while (!result && i < queries.length);
- return result;
-}
-
- function messageBar(message, color, background) {
- let mb = document.createElement("span");
- try {
- @@ -10124,10 +9902,17 @@
- return mb;
- }
-
-function pageLoader(message) {
- +function pageLoader(message, type) {
-
- let newDocument, insertDocument, removeDocument;
- + let newDocument, insertDocument, removeDocument, loading, please;
-
- + if (type == "error") {
- + loading = "An error has occured during attempt to parse this document.";
- + please = "Please report of this concern to the developer.";
- + } else {
- + loading = "Loading syndication feed document.";
- + please = "Please wait…";
- + }
- // /questions/6464592/how-to-align-entire-html-body-to-the-center
- const domParser = new DOMParser(),
- loadPage = `
- @@ -10148,7 +9933,7 @@
- background-color: #f1f1f1;
- font-family: system-ui;
- cursor: default;
- user-select: none;
- + /* user-select: none; */
- max-height: 100%;
- max-width: 100%; }
-
- @@ -10171,8 +9956,8 @@
- <body>
- <dl>
- <dt>📰 <b>Newspaper</b></dt>
- <dd>Loading syndication news feed document.</dd>
- <dd>${message}. Please wait…</dd>
- + <dd>${loading}</dd>
- + <dd>${message}. ${please}</dd>
- </dl>
- </body>
- </html>`;
- @@ -10241,7 +10026,7 @@
- }
-
- function extractEnclosureFilename(enclosureUrl) {
- for (protocol of ["ftp", "gemini", "gopher", "http"]) {
- + for (let protocol of ["ftp", "gemini", "gopher", "http"]) {
- if (enclosureUrl.startsWith(protocol)) {
- return enclosureUrl.split("/").pop();
- }
- @@ -10253,7 +10038,7 @@
- if (enclosureUrl.startsWith("ed2k")) {
- let sections = enclosureUrl.split("|");
- let isSectionFile = false;
- for (section of sections) {
- + for (let section of sections) {
- if (!isSectionFile) {
- if (section.toLowerCase() == "file") {
- isSectionFile = true;
- @@ -10274,3 +10059,77 @@
- currentDate = Date.now();
- } while (currentDate - anchorDate < milliseconds);
- }
- +
- +function getNodeByXPath(node, query) { // FIXME
- + res = document.evaluate(
- + query, node, null,
- + XPathResult.ANY_UNORDERED_NODE_TYPE);
- + nRes = result.singleNodeValue;
- + return nRes;
- +}
- +
- +function queryByXPath(node, queries) {
- + let result, i = 0;
- + do {
- + result = document.evaluate(
- + queries[i], document,
- + null, XPathResult.STRING_TYPE);
- + i = i + 1;
- + result = result.stringValue;
- + } while (!result && i < queries.length);
- + return result;
- +}
- +
- +/*
- +function queryPathAll(data, node, namespace, expression) {
- + nodes = data.evaluate(
- + expression, node, () => namespace, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
- + let results = [];
- + if (nodes) {
- + let node = nodes.iterateNext();
- + while (node) {
- + // Add the link to the array
- + results.push(nodes.nodeValue);
- + // Get the next node
- + node = nodes.iterateNext();
- + }
- + }
- + return results;
- +}
- +
- +function queryPath(data, node, namespace, expression) {
- + return data.evaluate(
- + expression, node, () => namespace, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
- + .singleNodeValue;
- +}
- +*/
- +
- +Node.prototype.queryPathAll = function (namespace, expression) {
- + data = this.ownerDocument || this;
- + nodes = data.evaluate(
- + expression,
- + this,
- + () => namespace,
- + XPathResult.ORDERED_NODE_ITERATOR_TYPE,
- + null);
- + let results = [];
- + let node = nodes.iterateNext();
- + while (node) {
- + // Add the link to the array
- + results.push(node);
- + // Get the next node
- + node = nodes.iterateNext();
- + }
- + return results;
- +};
- +
- +Node.prototype.queryPath = function (namespace, expression) {
- + data = this.ownerDocument || this;
- + return data.evaluate(
- + expression,
- + this,
- + () => namespace,
- + XPathResult.FIRST_ORDERED_NODE_TYPE,
- + null)
- + .singleNodeValue;
- +};