Newspaper (Native RSS Reader)

Native Feed Viewer. Render syndication web feeds (supports ActivityStreams, Atom, JSON, RDF and RSS)

当前为 2023-06-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Newspaper (Native RSS Reader)
  3. // @namespace i2p.schimon.newspaper
  4. // @description Native Feed Viewer. Render syndication web feeds (supports ActivityStreams, Atom, JSON, RDF and RSS)
  5. // @homepageURL https://sjehuda.github.io/newspaper.html
  6. // @supportURL https://greasyfork.org/en/scripts/465932-newspaper/feedback
  7. // @copyright 2023, Schimon Jehudah (http://schimon.i2p)
  8. // @license MIT; https://opensource.org/licenses/MIT
  9. // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48dGV4dCB5PSIuOWVtIiBmb250LXNpemU9IjkwIj7wn5OwPC90ZXh0Pjwvc3ZnPgo=
  10. // @exclude *?streamburner=0
  11. // @exclude *?*streamburner=0
  12. // @match *://*/*
  13. // @version 23.06.01
  14. // @run-at document-start
  15. // ==/UserScript==
  16.  
  17. //window.addEventListener("securitypolicyviolation", (e) => {
  18. // console.info(e.originalPolicy);
  19. //}, { passive : true, });
  20.  
  21. /*
  22.  
  23. NOTE
  24.  
  25. RDF
  26. https://web.archive.org/web/20070504072733/http://www.svg.org/feeds/725.rss
  27. https://web.archive.org/web/20070402123623if_/http://blogs.forum.nokia.com/rss_entry_feed.rss
  28.  
  29. OPML
  30. http://opml.org/?format=opml
  31.  
  32. SML
  33. https://www.pclinuxos.com/forum/index.php?action=.xml
  34.  
  35.  
  36. TODO
  37.  
  38. 0) Skim pages (10 entries per page)
  39.  
  40. 1) "Lazy" Content media loader
  41. https://www.merixstudio.com/blog/lazy-loading-pure-javascript/
  42. https://css-tricks.com/the-complete-guide-to-lazy-loading-images/
  43.  
  44. 2) Dark mode
  45. body { color: WhiteSmoke; background: #333; }
  46. a { color: WhiteSmoke; }
  47. #top-navigation-button { background: '#555'; border: '2px solid WhiteSmoke'; }
  48. Example: https://alligator.io/feed.xml (Color schemes)
  49. Note: Waiting for Falkon https://bugs.kde.org/show_bug.cgi?id=468046
  50.  
  51. 3) Support OPML http://opml.org/?format=opml
  52. Support SML https://www.pclinuxos.com/forum/index.php?action=.xml
  53.  
  54. 4) Use JSON to store and apply inline CSS style per element
  55.  
  56. 5) Replace <xsl:text>#newspaper-oujs-</xsl:text> by <xsl:text>#</xsl:text><xsl:value-of select="title"/>
  57.  
  58. 6) Add instructions to
  59. navigator.userAgent.toLowerCase().includes('firefox')
  60. https://openuserjs.org/scripts/sjehuda/Newspaper#gecko
  61.  
  62. NOTE
  63.  
  64. 1) <p class="explanation">This is a podcast RSS feed generated by <a href="https://castos.com/seriously-simple-podcasting/">Seriously Simple Podcasting</a>. It is meant for consumption by podcast feed readers using the URL in the address bar.</p>
  65.  
  66. Design HTML as JSON
  67.  
  68.  
  69.  
  70. NOTE Handling attributes
  71. "attributes": {
  72. "style" : {
  73. "direction" : "language"
  74. }
  75. }
  76.  
  77. feedItems = {
  78. "div": "feed",
  79. "attributes": {
  80. "style" : "language",
  81. },
  82. "children" : {
  83. "div": "title",
  84. "div": "subtitle",
  85. "div": "links-bar",
  86. "div": "entry", // multiple alike
  87. "attributes": {
  88. "style" : "language",
  89. },
  90. "children" : {
  91. "div" : "title",
  92. "attributes": {
  93. "href" : ["url", "id"],
  94. "id" : "id",
  95. },
  96. "div" : ["authors", "author"],
  97. "div" : ["date_published", "date_modified"],
  98. "div" : ["content_html", "content_text"],
  99. "div" : "image",
  100. "div" : "tags",
  101. },
  102. }
  103. };
  104.  
  105. FIXME
  106.  
  107. 1) Event listeners don't work on every page, yet.
  108. This is due to document.contentType (read only) determined as xml (Mainstream issue)
  109.  
  110. 2) https://imslp.org/wiki/Special:IMSLPRecordingsFeed/atom
  111. https://artemislena.eu/feed.json
  112.  
  113. */
  114.  
  115.  
  116. /*
  117.  
  118. <!-- _The very simple technology that Fortune 500 and Web Giants are jealously trying to oppress and hide from you. -- Alex James Anderson_ -->
  119.  
  120. > _The technology that Fortune 500 and Web Giants don't want you to know about. -- Alex James Anderson_
  121.  
  122. ## 📰 View news feeds inside the browser
  123. ### _Render syndication web feeds_
  124.  
  125. This programs renders web feeds into readable and printable HTML file. It supports ActivityStream, Atom, JSON, RDF and RSS syndications.
  126.  
  127. If you are using LibreWolf, Waterfox, Firefox or SeaMonkey, please [follow instructions](#gecko) in order to make the best use of this program.
  128.  
  129. ---
  130.  
  131. #### What is a Web Syndication News Feed?
  132.  
  133. Syndication Feed is a mean for content and media publishers to reach a wider audience easily. It allows you to receive information directly without the going from site to site.
  134.  
  135. Essentially, a feed is a function that allows “Feed Readers” to access multiple websites automatically looking for new content and then posting the information about new content and updates to another website, mobile app or desktop software at the office.
  136.  
  137. Feeds provide a way for users to keep up with the latest news, events, package and delivery status information posted on different websites such as news sites, music sites, content sites (i.e. social networks), torrent indexers and podcasts in one spot.
  138.  
  139. ---
  140. <!--
  141. #### Preview of [The IFERS Forum](https://ifers.forumotion.com/?ref=org.openuserjs.sjehuda.newspaper) ([XML](https://ifers.forumotion.com/feed/?type=atom&ref=org.openuserjs.sjehuda.newspaper)) ([Static Preview](https://github.com/greasemonkey/greasemonkey/files/11321118/newspaper.pdf))
  142.  
  143. | Before | | After |
  144. | ------- | --- | ------- |
  145. | [<img src="https://user-images.githubusercontent.com/19249089/234231176-1fe660fb-46ce-4009-a167-341f7d6d1c99.png" alt="Before" title="This is how a web feed file really looks like, an XML."/>](https://user-images.githubusercontent.com/19249089/234231176-1fe660fb-46ce-4009-a167-341f7d6d1c99.png) | > | [<img src="https://user-images.githubusercontent.com/19249089/234228487-2351fc25-a84e-473c-b2cf-559c5db7145e.gif" alt="After" title="This is how a web feed looks like when using the Newspaper userscript."/>](https://user-images.githubusercontent.com/19249089/234228487-2351fc25-a84e-473c-b2cf-559c5db7145e.gif) |
  146.  
  147. #### More Samples
  148. -->
  149. #### [Gajim XMPP Client](https://gajim.org/?ref=org.openuserjs.sjehuda.newspaper) ([XML](https://gajim.org/index.xml?ref=org.openuserjs.sjehuda.newspaper)); view as [PNG](https://user-images.githubusercontent.com/19249089/167972159-54a4040c-b56f-46c1-a581-2c8d24930d93.jpeg) or [PDF](https://github.com/mastodon/mastodon/files/8674313/Gajim.pdf) (with images)
  150.  
  151. #### [BlackListed News](https://blacklistednews.com/?ref=org.openuserjs.sjehuda.newspaper) ([XML](https://blacklistednews.com/rss.php?ref=org.openuserjs.sjehuda.newspaper)); view as [PDF](https://github.com/yacy/yacy_search_server/files/11240710/blacklistednews.com.pdf) (text only)
  152.  
  153. <!-- [![BLN](https://user-images.githubusercontent.com/19249089/232254656-b7d63d99-797a-46e0-aa6c-1413b81fad00.png)](https://github.com/yacy/yacy_search_server/files/11240710/blacklistednews.com.pdf) -->
  154.  
  155. ---
  156.  
  157. #### Please visit our counterparts
  158.  
  159. #### Falkon Browser
  160.  
  161. #### [RSS Finder](https://store.falkon.org/p/1689113/)
  162. Find RSS feeds on the website. This extension a button to toolbar and statusbar on which when you click the popup window with available feed will show up.
  163.  
  164. #### LibreWolf Browser
  165.  
  166. #### [Feed Preview](https://code.guido-berhoerster.org/addons/firefox-addons/feed-preview/)
  167. Native handling of RSS and Atom feed reading using live bookmarks.
  168.  
  169. #### [Livemarks](https://addons.mozilla.org/en-US/firefox/addon/livemarks/)
  170. Restore RSS Feed Livemarks in Firefox.
  171.  
  172. #### [rss.html](https://addons.mozilla.org/en-US/firefox/addon/rss-html/)
  173. Renders some rss feeds as html.
  174.  
  175. #### [RSSPreview](https://github.com/aureliendavid/rsspreview#rsspreview)
  176. Preview RSS feeds in the browser.
  177.  
  178. #### [Want My RSS](https://github.com/Reeywhaar/want-my-rss#-want-my-rss)
  179. Restore some of RSS functionality which Firefox abandoned.
  180.  
  181. #### Chromium Browser
  182.  
  183. #### [Foxish](http://davidhampgonsalves.com/foxish/)
  184. Indicate the availability of RSS or Atom feeds in the browser's URL bar and render a previews of feeds.
  185.  
  186. #### [The RSS Aggregator](https://chrome.google.com/webstore/detail/the-rss-aggregator/ffhafkagcdhnhamiaecajogjcfgienom)
  187. RSS reader like in the Opera browser.
  188.  
  189. <!--
  190.  
  191. ---
  192.  
  193. #### <span style='color:darkred;'> HELP WANTED </span> (CSS DESIGNER)
  194. #### If you are good at CSS, you're more than welcome to help refining the [CSS stylesheet](https://openuserjs.org/scripts/sjehuda/Newspaper/issues/Improve_CSS) used by this Userscript.
  195. #### Thank you!
  196. -->
  197.  
  198. ---
  199.  
  200. #### Upcoming changes:
  201. * Dark mode;
  202. * ToC: Toggle button/link;
  203. * ToC: Auto collaps when length exceeds N entries;
  204. * Provident Media Load "Lazy" (so-called) content media loader;
  205. * Load stylesheet upon error of existing stylesheet (i.e. 404 etc.).
  206.  
  207. <!--
  208. ### Done Tasks
  209.  
  210. #### XSLT
  211. * Atom support;
  212. * RDF support;
  213. * RSS support;
  214. * Set maximum amount entries to display;
  215. * Table of contents.
  216.  
  217. #### CSS
  218. * Newspaper style.
  219.  
  220. #### JS
  221. * Subresource Integrity (SRI);
  222. * Use DOMParser to parse XML;
  223. * Migrate to ~~[XSLT-processor](https://github.com/fiduswriter/xslt-processor) or [SaxonJS](https://www.saxonica.com/saxon-js/index.xml) or to~~ any other XSLT processor, because some web browsers provide broken XSLT processors built-in (shenanigans resulted by pressure from the advertising industry to oppress RSS);
  224. * Help button;
  225. * Respect existing stylesheets;
  226. * SubToMe button;
  227. * JSON support;
  228. * First check HEADER, then GET content [@7nik](https://openuserjs.org/scripts/sjehuda/Newspaper/issues/First_check_HEADER,_then_GET_content).
  229. -->
  230.  
  231. <!--
  232. ### Please visit out counterparts
  233. [FeedPress](https://feedpress.me/) (Online Service)
  234. FeedPress manage your podcast hosting and keep watching your podcast and RSS feed stats in single web application FeedPress also styling your Feeds into a readable HTML page with a slick design.
  235. -->
  236.  
  237. ---
  238.  
  239. #### <img src="https://raw.githubusercontent.com/KDE/falkon/master/logo.png" height="26" alt="Falkon" title="Download Falkon Browser"/> Designed for <span style='color:MediumPurple'>[Falkon](https://www.falkon.org/?ref=org.openuserjs.sjehuda.newspaper)</span>‬<!-- ![Falkon Web Browser](https://raw.githubusercontent.com/KDE/falkon/master/logo.png "Falkon Web Browser") -->
  240.  
  241. #### <img src="https://raw.githubusercontent.com/midori-browser/core/master/icons/scalable/apps/org.midori_browser.Midori.svg" height="26" alt="Midori Browser" title="Download Midori Browser"/> Designed for <span style='color:DarkGreen'>[Midori](https://github.com/midori-browser/core)
  242.  
  243. #### <img src="https://raw.githubusercontent.com/OtterBrowser/otter-browser/8b5ac96c1d36a1ffee6828969514a4f2745c88be/resources/icons/otter-browser.svg" height="26" alt="Otter Browser" title="Download Otter Browser"/> Designed for <span style='color:DeepSkyBlue'>[Otter](https://otter-browser.org/?ref=org.openuserjs.sjehuda.newspaper)
  244.  
  245. #### <!-- img src="https://www.qt.io/hubfs/qt-design-system/assets/logos/qt-logo.svg?ref=org.openuserjs.sjehuda.newspaper" width="26" alt="QtWebEngine" title="Support QtWebEngine"/> Designed for <span style='color:MediumPurple'>[QtWebEngine](https://wiki.qt.io/QtWebEngine?ref=org.openuserjs.sjehuda.newspaper)</span -->
  246.  
  247. ---
  248.  
  249. #### Gecko
  250. #### Enable JSON
  251. - Navigate to `about:config`.
  252. - Set `devtools.jsonview.enabled` to `false`.
  253.  
  254. #### Enable Atom & RSS
  255. - Install [Open in Browser](https://addons.mozilla.org/firefox/addon/open-in-browser/)
  256. - Import the following rules via extension preferences.
  257. ```json
  258. {
  259. "mime-mappings": {
  260. "application/atom+xml": "1text/plain",
  261. "application/rss+xml": "1text/plain",
  262. "application/feed+json": "1text/plain"
  263. },
  264. "sniffed-mime-mappings": {},
  265. "text-nosniff": false,
  266. "octet-sniff-mime": true,
  267. "override-download-type": false
  268. }
  269. ```
  270.  
  271. ---
  272.  
  273. ### Member of <span style='color:orange'>[RSS Task Force](http://rss.task.force)</span>
  274.  
  275. */
  276.  
  277. const
  278. namespace = 'i2p.schimon.newspaper',
  279. defaultTitle = 'Streamburner',
  280. // This news feed is brought to you by Streamburner News Reader
  281. defaultSubtitle = 'News feed rendered by Streamburner',
  282. rtlLocales = ['ar', 'fa', 'he', 'ji', 'ku', 'ur', 'yi'],
  283. atomRules = {
  284. "feedLanguage" : "feed", // NOTE @xml:lang
  285. "feedTitlePage" : "feed > title",
  286. "feedSubtitle" : "feed > subtitle",
  287. "feedDate" : "updated",
  288. "feedItem" : "entry",
  289. "feedItemTitle" : "title",
  290. "feedItemLink" : "link", // NOTE varies and doesn't always contain rel='alternate'
  291. "feedItemDate" : "updated",
  292. "feedItemContent" : "content",
  293. "feedItemSummary" : "summary",
  294. "feedItemEnclosure" : "link[rel='enclosure']"
  295. },
  296. rdfRules = {
  297. "feedLanguage" : "channel > language", // TODO Test
  298. "feedTitlePage" : "channel > title",
  299. "feedSubtitle" : "channel > description",
  300. "feedDate" : "date",
  301. "feedItem" : "item",
  302. "feedItemTitle" : "title",
  303. "feedItemLink" : "link",
  304. "feedItemDate" : "date",
  305. "feedItemContent" : "description",
  306. "feedItemEnclosure" : "resource" // TODO Test
  307. },
  308. rssRules = {
  309. "feedLanguage" : "channel > language",
  310. "feedTitlePage" : "channel > title",
  311. "feedSubtitle" : "channel > description",
  312. "feedDate" : "lastBuildDate",
  313. "feedItem" : "item",
  314. "feedItemTitle" : "title",
  315. "feedItemLink" : "link",
  316. "feedItemDate" : "pubDate",
  317. "feedItemContent" : "description",
  318. "feedItemEnclosure" : "enclosure"
  319. },
  320. 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>`,
  321. htmlDonate =
  322. `<div class="about-newspaper" id="about-donate">
  323. <div><big><b>Donations</b></big></div>
  324. <p>No, thank you. Yet, I do appreciate your concern.</p>
  325. <p><big><b>Here are some things you can do</b></big></p>
  326. <p>In no particular order …</p>
  327. <ul>
  328. <li>Talk with your friends about the benefits of RSS (i.e. Web Feeds). That would be a good table talk.</li>
  329. <li>Use an RSS reader. (See list of programs in Help menu)</li>
  330. <li>Teach other people to use RSS readers. Blog about RSS readers. And about other open web technologies and apps.</li>
  331. <li>Write a blog instead of posting to social networks”. (You can always re-post to those places if you want to extend your reach.) <a href="https://micro.blog/">Micro.blog</a> is one good place to get going, and it’s not the only one.</li>
  332. <li>Contact <a href="http://unicode.org/pending/proposals.html"><u>unicode.org</u></a> and promote the initiative for the <a href="https://github.com/vhf/unicode-syndication-proposal"><u>Proposal to Include Web Syndication Symbol</u></a>.</li>
  333. <li>Donate to charities that promote literacy.</li>
  334. <li>Tell other people about cool blogs and feeds youve found.</li>
  335. <li>Support independent podcast apps and desktop programs.</li>
  336. <li>Support your local library.</li>
  337. <li>Be bold and do your best work.</li>
  338. <li>Support indie developers. Even though software like Falkon, Newspaper, postmarketOS etc. are free, software are most definitely not free to make, and it costs time and effort to keep improving them. Its worth it.</li>
  339. <li>Finally: report bugs and make feature requests on our Issues tracker. We also need testers, writers, and, especially, people who are willing to talk things over. Most of software development is just making decisions, and we appreciate all the help we can get!.</li>
  340. <li>Or: skip helping us, and, instead, help people who need help more than we do. Those people should not be hard to find.</li>
  341. <li>Buy a meal to a person in need, or, even better, get a job for him or her.</li>
  342. <li>Establish a family, or if you already are a parent, bring a new healthy child to the world.</li>
  343. <li>Get more ideas from <a href="https://github.com/Ranchero-Software/NetNewsWire/blob/main/Technotes/HowToSupportNetNewsWire.markdown#here-are-some-things-you-can-do">Ranchero-Software/NetNewsWire</a>.</li>
  344. </ul>
  345. <p>If you happen to visit in the Middle East, reach me out and we can meet for a café or tea.</p>
  346. <p>Sharing is caring, and is exactly what makes us humans. It's "all of us for all of us" or we're on our own.</p>
  347. <p>Schimon</p>
  348. <div class="decor"></div>
  349. <div class="quote">
  350. <p>(The syndication technology behind ActivityPub, Atom and RSS is)</p>
  351. <p>"The very simple technology that Big Corps, Fortune 500, Mozilla et al. are jealously trying to oppress and conceal from you";</p>
  352. <p>"Because it unleashes the embodiment of what open web should really be, a truely free-speech-driven web."</p>
  353. <p>"The problem, for them, is that if true openness would flourish, it might have the "dire" potential, at least for them, to put many of them off the market".</p>
  354. <p>-- Alex James Anderson</p>
  355. </div>
  356. </div>`,
  357. htmlAbout =
  358. `<div class="about-newspaper" id="about-feed">
  359. <div class="quote content">
  360. <p>"The technology that Big Corps, Fortune 500 and Mozilla et al. want you to forget". -- Alex James Anderson</p>
  361. </div>
  362. <div><big><b>Table of contents</b></big></div>
  363. <ul class="content" id="about-toc">
  364. <li><a href="#intro">About news feed</a></li>
  365. <li><a href="#feeds">List of feeds sorted by subjects</a></li>
  366. <li><a href="#software">List of <abbr title="feed readers">apps</abbr> for desktop and mobile</a></li>
  367. <li><a href="#services">List of online services with syndication</a></li>
  368. <li><a href="#learn">Historical review about web feeds</a></li>
  369. <li><a href="#plea">An appeal from the author</a></li>
  370. </ul>
  371. <div><big>📰 <b>Web syndication news feed</b></big></div>
  372. <div class="content" id="intro">
  373. <p>Syndication feed is a mean for content and media publishers to reach a wider audience easily. It allows you to receive information directly without the going from site to site.</p>
  374. <p>Essentially, a feed embodies a function that allows Feed Readers to access multiple websites, automatically looking for new contents and then posting the information about new contents and updates to another website, mobile app or desktop software at your office.</p>
  375. <p>Feeds provide a simple way to keep up with the latest news, events, package and delivery status information posted on different websites such as news sites, music sites, content sites (aka social networks”), torrent indexers, podcasts and <a href="#feeds"><u>more</u></a>; all, in one single spot.</p>
  376. <p>In the hope that you would find this program useful; and in the hope that you would enjoy and get the most out of this program!</p>
  377. <p>Read more on: <a href="http://rss.userland.com/howUseRSS">How You Can Use RSS</a> and <a href="http://rss.userland.com/whyImportant">Why is RSS Important?</a> and <a href="https://marcus.io/blog/making-rss-more-visible-again-with-slash-feeds">Making RSS more accessible with a /feeds page</a>.</p>
  378. </div>
  379. <div class="decor"></div>
  380. <!-- div><big>📗 <b>Recommended feeds</b></big></div -->
  381. <!-- div><big>{ } <b>&lt;rss&gt; is everywhere</b></big></div -->
  382. <div><big><span class="text-icon">RSS</span> <b>is everywhere</b></big></div>
  383. <div class="content" id="feeds">
  384. <p>This is a list of feeds that should get you started with your news reader <a href="#software"><u>app or software</u></a>.</p>
  385. <p>The format of the feeds below is vary, from ActivityStreams and JSON to Atom and RDF, not only RSS.</p>
  386. <div class="category">
  387. <div>Art, Literature &amp; Nature</div>
  388. <a href="https://4columns.org/feed">4Columns</a>
  389. <a href="https://annas-blog.org/rss.xml">Annas Blog</a>
  390. <a href="https://audiobookbay.is/feed/atom/">AudioBook Bay</a>
  391. <a href="https://barnesreview.org/feed/">Barnes Review</a>
  392. <a href="https://www.kusc.org/feed/">Classical KUSC</a>
  393. <a href="https://darksitefinder.com/feed/">Dark Site Finder</a>
  394. <a href="https://tpb.party/rss/new/601">E-books (TPB)</a>
  395. <a href="http://freedif.org/blog.atom">Freedif</a>
  396. <a href="https://sacred-texts.com/rss/new.xml">ISTA - Internet Sacred Text Archive</a>
  397. <a href="https://librivox.org/feed/">LibriVox</a>
  398. <a href="https://www.music-scores.com/blog/feed/">Music Scores Blog</a>
  399. <a href="https://www.nioc.eu/rss">Nioc Photos</a>
  400. <a href="https://www.transformativeworks.org/feed/">Organization for Transformative Works</a>
  401. <a href="https://www.brainyquote.com/link/quotebr.rss">Quotes</a>
  402. <a href="https://roaring.earth/feed/">Roaring Earth</a>
  403. <a href="https://www.rockandice.com/feed/">Rock and Ice Magazine</a>
  404. <a href="https://zenfolio.com/feed">Ron Reyes Photography</a>
  405. <a href="https://publicdomainreview.org/rss.xml">The Public Domain Review</a>
  406. <a href="https://www.torrent911.me/rss/ebooks">Torrent911</a>
  407. </div>
  408. <div class="category">
  409. <div>Blogroll &amp; Videos</div>
  410. <a href="https://nerdy.dev/rss.xml">Adam Argyle</a>
  411. <a href="https://arantius.com/feed.rss">arantius.com</a>
  412. <a href="https://denshi.live/feeds/videos.atom">denshi.live</a>
  413. <a href="https://denshi.org/index.xml">DenshiSite</a>
  414. <a href="https://tilde.town/~dustin/index.xml">~dustin</a>
  415. <a href="https://nu.federati.net/api/statuses/user_timeline/16.as">GeniusMusing (@geniusmusing)</a>
  416. <a href="https://nu.federati.net/api/statuses/user_timeline/2.as">LinuxWalt (@lnxw48a1)</a>
  417. <a href="https://problogger.com/feed/">ProBlogger</a>
  418. <a href="https://singpolyma.net/feed/action_stream/?full">Stephen Paul Weber</a>
  419. <a href="http://thedarnedestthing.com/atom.xml">the darnedest thing</a>
  420. <a href="https://geniusmusing.com/feed/rss">The Random Thoughts of GeniusMusing</a>
  421. <a href="https://unixsheikh.com/feed.rss">unixsheikh.com</a>
  422. <a href="https://webring.xxiivv.com/#rss">Webring (index)</a>
  423. <a href="https://willnorris.com/atom.xml">willnorris.com</a>
  424. </div>
  425. <div class="category">
  426. <div>Comic, Entertainment &amp; Games</div>
  427. <a href="https://abstrusegoose.com/atomfeed.xml">Abstruse Goose</a>
  428. <a href="https://www.basicinstructions.net/basic-instructions?format=rss">Basic Instructions</a>
  429. <a href="https://www.crossfire.nu/feed/journals">Crossfire Journals</a>
  430. <a href="https://www.crossfire.nu/feed/news">Crossfire News</a>
  431. <a href="https://dieselsweeties.com/ds-unifeed.xml">Diesel Sweeties</a>
  432. <a href="https://www.dsogaming.com/feed/">DSOGaming</a>
  433. <a href="https://www.gamingonlinux.com/article_rss.php">GamingOnLinux</a>
  434. <a href="https://lichess.org/blog.atom">Lichess</a>
  435. <a href="https://mindblur.thecomicseries.com/rss/">Mindblur</a>
  436. <a href="https://pikabu.ru/xmlfeeds.php?cmd=popular">pikabu.ru</a>
  437. <a href="https://revive.thecomicseries.com/rss/">Revive</a>
  438. <a href="https://pbfcomics.com/feed/">The Perry Bible Fellowship</a>
  439. <a href="https://toothpastefordinner.com/rss/rss.php">Toothpaste For Dinner</a>
  440. <a href="https://xkcd.com/atom.xml">xkcd</a>
  441. </div>
  442. <div class="category">
  443. <div>Cybersecurity, IT &amp; Privacy</div>
  444. <a href="https://www.bleepingcomputer.com/feed/">Bleeping Computer</a>
  445. <a href="https://www.comparitech.com/feed/">Comparitech</a>
  446. <a href="https://cyberscoop.com/feed/">CyberScoop</a>
  447. <a href="https://dataoverhaulers.com/feed/">Data Overhaulers</a>
  448. <a href="https://decrypt.fail/feed/">decrypt[.]fail</a>
  449. <a href="https://www.hackread.com/feed/">HackRead</a>
  450. <a href="https://nakedsecurity.sophos.com/feed/">Naked Security</a>
  451. <a href="https://privacysavvy.com/feed/">PrivacySavvy</a>
  452. <a href="https://www.rapidseedbox.com/feed">RapidSeedbox</a>
  453. <a href="https://reclaimthenet.org/feed/">Reclaim The Net</a>
  454. <a href="https://restoreprivacy.com/feed/">Restore Privacy</a>
  455. <a href="https://fosstodon.org/@RTP.rss">(RTP) Privacy and Tech Tips</a>
  456. <a href="https://www.schneier.com/feed/">Schneier on Security</a>
  457. <a href="https://securityintelligence.com/feed/">Security Intelligence</a>
  458. <a href="https://takebackourtech.org/rss/">Take Back Our Tech</a>
  459. <a href="https://torrentfreak.com/feed/">TorrentFreak</a>
  460. <a href="https://venturebeat.com/feed/">VentureBeat</a>
  461. </div>
  462. <div class="category">
  463. <div>Discussions, Forums &amp; Message Boards</div>
  464. <a href="https://www.antixforum.com/feed/">antiX Linux</a>
  465. <a href="https://bbs.archlinux.org/extern.php?action=feed&amp;type=atom&amp;limit=20">Arch Linux</a>
  466. <a href="https://forum.artixlinux.org/index.php?action=.xml&amp;type=atom&amp;limit=20">Artix Linux</a>
  467. <a href="https://community.bitwarden.com/posts.rss">Bitwarden Community Forums</a>
  468. <a href="https://www.climate-debate.com/feeds/threads.php">Climate Debate</a>
  469. <a href="https://www.crossfire.nu/feed/threads">Crossfire</a>
  470. <a href="https://www.diychatroom.com/forums/-/index.rss">DIY Home Improvement Forum</a>
  471. <a href="https://www.doityourself.com/forum/external.php?type=RSS2">DoItYourself.com</a>
  472. <a href="https://forum.endeavouros.com/posts.rss">EndeavourOS</a>
  473. <a href="https://forums.freebsd.org/forums/-/index.rss">FreeBSD</a>
  474. <a href="https://www.gamingonlinux.com/forum_rss.php">GamingOnLinux</a>
  475. <a href="https://forum.garudalinux.org/posts.rss">Garuda Linux</a>
  476. <a href="https://www.happiness.com/rss/1-happinesscom-magazine.xml/">Happiness and Meditation</a>
  477. <a href="https://houserepairtalk.com/forums/-/index.rss">Home Improvement, Remodeling &amp; Repair</a>
  478. <a href="http://i2pforum.i2p/app.php/feed">I2P support</a>
  479. <a href="https://ifers.forumotion.com/feed/?type=atom">IFERS (Horizontal Earth)</a>
  480. <a href="https://forum.invoiceninja.com/posts.rss">Invoice Ninja</a>
  481. <a href="https://forum.jami.net/posts.rss">Jami Forum</a>
  482. <a href="https://forums.kali.org/external.php?type=RSS2">Kali Linux</a>
  483. <a href="http://board.kolibrios.org/app.php/feed">KolibriOS board</a>
  484. <a href="https://forum.linux-hardware.org/index.php?action=.xml&amp;type=atom&amp;limit=20">Linux Hardware Review</a>
  485. <a href="https://community.metabrainz.org/posts.rss">MusicBrainz</a>
  486. <a href="https://forum.mxlinux.org/feed">MX Linux</a>
  487. <a href="https://discourse.nixos.org/posts.rss">NixOS</a>
  488. <a href="https://www.pclinuxos.com/forum/index.php?type=atom&amp;action=.xml">PCLinuxOS</a>
  489. <a href="https://forum.pine64.org/syndication.php?type=atom1.0">PINE64</a>
  490. <a href="https://www.solveforum.com/forums/forums/-/index.rss">solveForum</a>
  491. <a href="https://sqlite.org/forum/timeline.rss">SQLite Forum</a>
  492. <a href="https://forum.tribler.org/latest.rss">Tribler BitTorrent</a>
  493. <a href="https://www.usingenglish.com/forum/forums/-/index.rss">UsingEnglish.com</a>
  494. <a href="https://www.vogons.org/feed">VOGONS</a>
  495. <a href="https://forum.xda-developers.com/f/-/index.rss">XDA Forums</a>
  496. <a href="https://forum.zdoom.org/app.php/feed">ZDoom</a>
  497. <a href="https://zigforums.com/xml/feed.rss">Zig Forums</a>
  498. </div>
  499. <div class="category">
  500. <div>DIY (3D Modeling &amp; Printing, Architecture and Crafting)</div>
  501. <a href="http://tracker2.postman.i2p/?view=RSS&amp;mapset=85701">Cad/3D Printing</a>
  502. <a href="https://designoptimal.com/feed/">DesignOptimal</a>
  503. <a href="https://www.doityourself.com/feed">Doityourself.com</a>
  504. <a href="https://www.elementalchile.cl/en/feed/">Elemental</a>
  505. <a href="https://www.familyhandyman.com/feed/">Family Handyman</a>
  506. <a href="https://www.opensourceecology.org/feed/">Open Source Ecology</a>
  507. <a href="https://tpb.party/rss/new/605">Physibles (TPB)</a>
  508. <a href="http://yorik.uncreated.net/feed">Yorik's blog</a>
  509. </div>
  510. <div class="category">
  511. <div>Electronics, Hacking &amp; Robotics</div>
  512. <a href="https://crlf.link/feed.xml">&amp;Cr&#59; &amp;Lf&#59;</a>
  513. <a href="http://dangerousprototypes.com/blog/feed/">Dangerous Prototypes</a>
  514. <a href="https://electro.pizza/feed.xml">electro·pizza</a>
  515. <a href="https://blog.flipperzero.one/rss/">Flipper Zero Blog</a>
  516. <a href="https://knuxify.github.io/blog/feed.xml">knuxifys blog</a>
  517. <a href="https://mansfield-devine.com/speculatrix/feed/">Machina Speculatrix</a>
  518. <a href="http://moderntoil.com/?feed=rss2">Modern Toil</a>
  519. <a href="https://n-o-d-e.net/rss/rss.xml">N O D E</a>
  520. <a href="https://www.open-electronics.org/feed/atom/">Open Electronics</a>
  521. <a href="https://rfidresearchgroup.com/feed/">RFID Research Group</a>
  522. <a href="https://smartbuilds.io/feed/">SmartBuilds</a>
  523. <a href="https://webring.xxiivv.com/#rss">Webring (index)</a>
  524. </div>
  525. <div class="category">
  526. <div>Family, Leisure &amp; Travel</div>
  527. <a href="https://www.baldandbeards.com/feed/">Bald &amp; Beards</a>
  528. <a href="https://blastaloud.com/feed/atom/">BlastAloud</a>
  529. <a href="https://dailyurbanista.com/feed/atom/">Daily Urbanista</a>
  530. <a href="https://divinelifestyle.com/feed/">Divine Lifestyle</a>
  531. <a href="https://expertvagabond.com/feed/">Expert Vagabond</a>
  532. <a href="https://www.girlschase.com/rss.xml">Girls Chase</a>
  533. <a href="https://latest-fashion-tips.com/feed/">Latest Fashion Tips</a>
  534. <a href="https://www.mom-on-a-mission.blog/all-posts?format=rss">Mom on a Mission</a>
  535. <a href="https://rebelliousdevelopment.com/feed">Rebellious Development</a>
  536. <a href="https://www.artofmanliness.com/feed/">The Art of Manliness</a>
  537. <a href="https://thebaldbrothers.com/feed/">The Bald Brothers</a>
  538. <a href="https://theeverygirl.com/feed/">The Everygirl</a>
  539. <a href="https://thefrugalgirls.com/feed">The Frugal Girls</a>
  540. </div>
  541. <div class="category">
  542. <div>Government, Politics &amp; World Affairs News</div>
  543. <a href="https://www.blacklistednews.com/rss.php">BlackListed News</a>
  544. <a href="https://www.cryptogon.com/?feed=atom">cryptogon</a>
  545. <a href="https://mastodon.social/@Cryptome.rss">Cryptome</a>
  546. <a href="https://fakeologist.com/feed/">Fakeologist</a>
  547. <a href="https://leohohmann.com/feed/">LeoHohmann.com</a>
  548. <a href="https://www.elegislation.gov.hk/verified-chapters!en.rss.xml">List of Verified Legislation (Hong Kong e-Legislation)</a>
  549. <a href="https://off-guardian.org/feed/">OffGuardian</a>
  550. <a href="https://www.presstv.ir/rss.xml">Press TV</a>
  551. <a href="https://redice.tv/rss/news">Red Ice News</a>
  552. <a href="https://www.rt.com/rss/">RT (Russia Today)</a>
  553. <a href="https://seymourhersh.substack.com/feed">Seymour Hersh</a>
  554. <a href="https://stevekirsch.substack.com/feed">Steve Kirsch</a>
  555. <a href="https://stewpeters.com/feed/">Stew Peters</a>
  556. <a href="https://strategicinvestment.com/page/str/rssfeed.xml">Strategic Investment</a>
  557. <a href="https://canadianpatriot.org/feed/">the Canadian patriot</a>
  558. <a href="https://dailysceptic.org/feed/">The Daily Sceptic</a>
  559. <a href="https://www.dailywire.com/feeds/rss.xml">The Daily Wire</a>
  560. <a href="https://www.thegatewaypundit.com/feed/">The Gateway Pundit</a>
  561. <a href="http://themostimportantnews.com/feed">The Most Important News</a>
  562. <a href="https://www.theorganicprepper.com/feed/">The Organic Prepper</a>
  563. <a href="https://unlimitedhangout.com/feed/">Unlimited Hangout</a>
  564. </div>
  565. <div class="category">
  566. <div>Health, Nutrition &amp; Recipes</div>
  567. <a href="https://www.101cookbooks.com/feed">101 Cookbooks</a>
  568. <a href="https://www.asweetpeachef.com/feed/">A Sweet Pea Chef</a>
  569. <a href="https://www.annalenashearthbeat.com/feed/">Annalena's Heart(h)beat</a>
  570. <a href="https://askannamoseley.com/feed/">Ask Anna</a>
  571. <a href="https://cooknourishbliss.com/feed/">Cook Nourish Bliss</a>
  572. <a href="https://www.easycookingwithmolly.com/feed/">Easy Cooking with Molly</a>
  573. <a href="http://www.easypeasyjapanesey.com/blogeasypeasyjapanesey?format=rss">Easy Peasy Japanesey</a>
  574. <a href="https://farmersforum.com/feed/">Farmers Forum</a>
  575. <!--a href="https://fathub.org/feed/">FatHub</a-->
  576. <a href="http://foodly.com/feed/">Foodly</a>
  577. <a href="https://freezedryguy.com/feed/">Freeze Dry Guy</a>
  578. <a href="https://heathenherbs.com/feed/">Heathen Herbs</a>
  579. <a href="https://www.healthyandnaturalworld.com/feed/">Healthy and Natural World</a>
  580. <a href="https://www.jamieoliver.com/feed/">Jamie Oliver</a>
  581. <a href="https://juicing-for-health.com/feed">Juicing for Health</a>
  582. <a href="https://www.loveandlemons.com/feed/">Love &amp; Lemons</a>
  583. <a href="https://articles.mercola.com/sites/articles/rss.aspx">Mercola.com</a>
  584. <a href="https://www.mindful.org/feed/">Mindful</a>
  585. <a href="https://nutritionaustralia.org/category/recipes/feed/">Nutrition Australia</a>
  586. <a href="https://pinchofyum.com/feed">Pinch of Yum</a>
  587. <a href="https://plantbasedwithamy.com/feed/">Plant Based with Amy</a>
  588. <a href="https://punchdrink.com/feed/">PUNCH</a>
  589. <a href="https://recipeswitholiveoil.com/feed/">Recipes With Olive Oil</a>
  590. <a href="https://www.sheknows.com/food-and-recipes/feed/">SheKnows</a>
  591. <a href="https://steptohealth.com/feed/">Step To Health</a>
  592. <a href="https://stevekirsch.substack.com/feed">Steve Kirsch</a>
  593. <a href="https://thegreenloot.com/feed/">The Green Loot</a>
  594. <a href="https://theprettybee.com/feed/">The Pretty Bee</a>
  595. <a href="https://traditionalcookingschool.com/feed/">Traditional Cooking School</a>
  596. <a href="https://wonderfulcook.com/feed/">Wonderful Cook</a>
  597. </div>
  598. <div class="category">
  599. <div>Music, Scores &amp; Sound</div>
  600. <a href="https://320kbpshouse.net/feed">320KBPSHOUSE</a>
  601. <a href="https://acidstag.com/feed/">Acid Stag</a>
  602. <a href="https://www.free-scores.com/rss/fluxrss-uk.xml">Free-scores.com</a>
  603. <a href="https://www.frostclick.com/wp/index.php/feed/">FrostClick</a>
  604. <a href="https://intmusic.net/feed">IntMusic</a>
  605. <a href="https://itopmusicx.com/feed/">iTOPMUSICX</a>
  606. <a href="https://losslessclub.com/atom.php">LosslessClub</a>
  607. <a href="https://nfodb.ru/rss.php">MP3 NFO Database</a>
  608. <a href="https://tpb.party/rss/new/101">Music (TPB)</a>
  609. <a href="https://musicrider.org/feed/">Music Rider</a>
  610. <a href="https://losslessalbums.club/rss.xml">New lossless albums</a>
  611. <a href="https://rss.ngfiles.com/latestsubmissions.xml">Newgrounds</a>
  612. <a href="https://rss.ngfiles.com/weeklyaudiotop5.xml">Newgrounds (Weekly Top 5)</a>
  613. <a href="https://phish.in/feeds/rss">Phish.in</a>
  614. <a href="https://secondhandsongs.com/rss/new.xml">Second Hand Songs</a>
  615. </div>
  616. <div class="category">
  617. <div>Podcasts &amp; Radio</div>
  618. <a href="https://files.manager-tools.com/files/public/feeds/career_tools_podcasts.xml">Career Tools</a>
  619. <a href="https://fakeologist.com/blog/category/audio/fakeologistshow/feed/">Fakeologist Show</a>
  620. <a href="http://hackerpublicradio.org/hpr_spx_rss.php">Hacker Public Radio</a>
  621. <a href="https://www.iceagefarmer.com/feed/">Ice Age Farmer</a>
  622. <a href="https://jameshfetzer.org/feed/">James H. Fetzer</a>
  623. <a href="http://larkenrose.com/?format=feed">Larken Rose</a>
  624. <a href="https://files.manager-tools.com/files/public/feeds/manager-tools-podcasts.xml">Manager Tools</a>
  625. <a href="https://mediamonarchy.com/feed/podcast/">Media Monarchy</a>
  626. <a href="https://feed.podbean.com/ediviney/feed.xml">Midwest Vegan Radio</a>
  627. <a href="http://www.opensourcetruth.com/feed/">Open Source Truth</a>
  628. <a href="https://optoutpod.com/index.xml">Opt Out</a>
  629. <a href="https://rss.podomatic.net/rss/peacerevolution.podomatic.com/rss2.xml">Peace Revolution</a>
  630. <a href="https://www.pine64.org/feed/mp3/">PineTalk</a>
  631. <a href="https://cast.postmarketos.org/feed.rss">postmarketOS</a>
  632. <a href="https://redice.tv/rss/radio-3fourteen">Radio 3Fourteen</a>
  633. <a href="https://www.reallibertymedia.com/category/podcasts/feed/?redirect=no">Real Liberty Media</a>
  634. <a href="https://redice.tv/rss/red-ice-radio">Red Ice Radio</a>
  635. <a href="https://redice.tv/rss/red-ice-tv">Red Ice TV</a>
  636. <a href="http://revolutionradio.org/feed/">Revolution Radio</a>
  637. <a href="https://feed.podbean.com/rightonradio/feed.xml">Right on Radio</a>
  638. <a href="https://roaring.earth/category/podcast/feed/">Roaring Earth</a>
  639. <a href="https://speakfreeradio.com/feed/">Speak Free Radio</a>
  640. <a href="https://www.corbettreport.com/feed/">The Corbett Report</a>
  641. <a href="https://www.thehighersidechats.com/feed/">The Higherside Chats</a>
  642. </div>
  643. <div class="category">
  644. <div>Product Shopping Reviews &amp; Stores</div>
  645. <a href="https://www.geartaker.com/feed/">Gear Taker</a>
  646. <a href="https://lab401.com/collections/flipper-zero.atom">Lab401</a>
  647. <a href="https://liliputing.com/feed/">Liliputing</a>
  648. <a href="https://www.megabites.com.ph/feed/">MegaBites</a>
  649. <a href="https://newatlas.com/index.rss">New Atlas</a>
  650. <a href="https://www.producthunt.com/feed">Product Hunt</a>
  651. <a href="https://www.sheknows.com/feed/">SheKnows</a>
  652. <a href="https://www.trustedreviews.com/feed">Trusted Reviews</a>
  653. </div>
  654. <div class="category">
  655. <div>Social Action (Activism)</div>
  656. <a href="https://campaignforliberty.org/feed/">Campaign for Liberty</a>
  657. <a href="https://fluoridealert.org/feed/">Fluoride Action Network</a>
  658. <a href="https://www.geoengineeringwatch.org/feed/atom/">Geoengineering Watch</a>
  659. <a href="https://stop5g.cz/us/feed/">Stop 5G</a>
  660. </div>
  661. <div class="category">
  662. <div>Software, Guides &amp; Technology</div>
  663. <a href="https://www.comparitech.com/feed/">Comparitech</a>
  664. <a href="https://ddos-guard.net/rss">DDoS-GUARD</a>
  665. <a href="https://www.dedoimedo.com/rss_feed.xml">Dedoimedo</a>
  666. <a href="https://distrowatch.com/news/dw.xml">DistroWatch</a>
  667. <a href="https://www.evilsocket.net/atom.xml">evilsocket</a>
  668. <a href="https://blog.front-matter.io/atom/">Front Matter</a>
  669. <a href="https://www.gamingonlinux.com/article_rss.php">GamingOnLinux</a>
  670. <a href="https://www.ghacks.net/feed/">gHacks</a>
  671. <a href="https://guides.lw1.at/index.xml">guides.lw1.at</a>
  672. <a href="https://i2p.rocks/blog/feeds/all.atom.xml">i2p.rocks</a>
  673. <a href="https://planet.jabber.org/atom.xml">Jabber World</a>
  674. <a href="https://cyber.dabamos.de/blog/feed.rss">Lazy Reading | The Cyber Vanguard</a>
  675. <a href="https://leimao.github.io/atom.xml">Lei Mao's Log Book</a>
  676. <a href="https://linuxgameconsortium.com/feed/">Linux Game Consortium</a>
  677. <a href="https://linmob.net/feed.xml">LINux on MOBile</a>
  678. <a href="https://www.linuxuprising.com/feeds/posts/default">Linux Uprising Blog</a>
  679. <a href="https://www.mfitzp.com/feeds/all.atom.xml">Martin Fitzpatrick
  680. </a>
  681. <a href="https://nerdstuff.org/index.xml">nerdstuff.org</a>
  682. <a href="https://www.nngroup.com/feed/rss/">NN/g latest articles and announcements</a>
  683. <a href="https://singpolyma.net/feed/">Singpolyma</a>
  684. <a href="https://sourceforge.net/blog/feed/">SourceForge Community Blog</a>
  685. <a href="https://www.sqlservercentral.com/articles/feed">SQLServerCentral</a>
  686. <a href="https://tuxphones.com/rss/">TuxPhones</a>
  687. </div>
  688. <div class="category">
  689. <div>Software Project Updates</div>
  690. <a href="https://artixlinux.org/feed.php">Artix Linux</a>
  691. <a href="https://news.agpt.co/feed/">Auto-GPT</a>
  692. <a href="https://bitwarden.com/blog/feed.xml">Bitwarden</a>
  693. <a href="https://sourceforge.net/p/butt/activity/feed">Broadcast Using This Tool</a>
  694. <a href="https://blog.discourse.org/rss/">Discourse</a>
  695. <a href="https://www.dragonflydigest.com/feed/">DragonFly BSD Digest</a>
  696. <a href="https://www.falkon.org/atom.xml">Falkon Browser</a>
  697. <a href="https://f-droid.org/feed.xml">F-Droid Store</a>
  698. <a href="https://flatassembler.net/atom.php">FASM (flat assembler)</a>
  699. <a href="https://sourceforge.net/p/freedos/news/feed.rss">FreeDOS</a>
  700. <a href="https://blog.funkwhale.audio/feeds/all.atom.xml">Funkwhale</a>
  701. <a href="https://blogs.gnome.org/shell-dev/">GNOME Shell &amp; Mutter</a>
  702. <a href="https://blog.gtk.org/feed/">GTK Development Blog</a>
  703. <a href="https://greasyfork.org/en/scripts.atom?sort=updated">Greasy Fork (Recent Updates)</a>
  704. <a href="https://jami.net/feed/">Jami</a>
  705. <a href="https://krita.org/en/feed/">Krita</a>
  706. <a href="https://leafletjs.com/atom.xml">Leaflet Dev Blog</a>
  707. <a href="https://libreboot.org/feed.xml">Libreboot</a>
  708. <a href="https://mobile.nixos.org/index.xml">Mobile NixOS</a>
  709. <a href="https://blogs.gnome.org/thaller/feed/">NetworkManager</a>
  710. <a href="https://otter-browser.org/feed/">Otter Browser</a>
  711. <a href="https://obsproject.com/blog/rss">Open Broadcaster Software</a>
  712. <a href="https://joinpeertube.org/rss-en.xml">PeerTube</a>
  713. <a href="https://www.pine64.org/feed/">PINE64</a>
  714. <a href="https://postmarketos.org/blog/feed.atom">postmarketOS</a>
  715. <a href="https://plausible.io/blog/feed.xml">Plausible Analytics</a>
  716. <a href="https://www.qemu.org/feed.xml">QEMU</a>
  717. <a href="https://reactos.org/index.xml">ReactOS</a>
  718. <a href="https://blog.replicant.us/feed/">Replicant</a>
  719. <a href="https://servo.org/blog/feed.xml">Servo</a>
  720. <a href="https://spidermonkey.dev/feed.xml">SpiderMonkey</a>
  721. <a href="https://translatelocally.com/rss/">Translate Locally</a>
  722. <a href="https://typo3.org/rss">TYPO3</a>
  723. <a href="https://ubports.com/blog/ubports-news-1/feed">UBports</a>
  724. <a href="https://www.uzbl.org/atom.xml">Uzbl Browser</a>
  725. </div>
  726. <div class="category">
  727. <div>Standards &amp; Protocols</div>
  728. <a href="https://gemini.circumlunar.space/news/atom.xml">Gemini Project</a>
  729. <a href="https://gopher.zone/index.xml">Highway to the Gopher Zone</a>
  730. <a href="https://geti2p.net/en/feed/blog/atom">I2P Blog</a>
  731. <a href="https://blog.ipfs.io/index.xml">IPFS Blog &amp; News</a>
  732. <a href="https://oxen.io/feed/atom">Oxen (Session &amp; Lokinet)</a>
  733. <a href="https://blog.torproject.org/feed.xml">Tor Project</a>
  734. <a href="https://www.w3.org/blog/news/feed/atom">W3C</a>
  735. <a href="https://xmpp.org/feeds/all.atom.xml">XMPP Blog</a>
  736. <a href="https://yggdrasil-network.github.io/feed.xml">Yggdrasil Network</a>
  737. </div>
  738. <div class="category">
  739. <div>Syndication &amp; XML</div>
  740. <a href="https://www.dublincore.org/index.xml">Dublin Core</a>
  741. <a href="https://www.jsonfeed.org/feed.xml">JSON Feed</a>
  742. <a href="https://microformats.org/feed">Microformats</a>
  743. <a href="https://openrss.org/rss">Open RSS</a>
  744. <a href="http://opml.org/?format=opml">OPML</a>
  745. <a href="http://feeds.rssboard.org/rssboard">RSS Advisory Board</a>
  746. <a href="https://blog.saxonica.com/atom.xml">Saxonica</a>
  747. <a href="http://docs.subtome.com/feed.xml">SubToMe</a>
  748. <a href="https://sword.cottagelabs.com/feed/">SWORD</a>
  749. <a href="http://rss.userland.com/xml/rss.xml">UserLand RSS Central</a>
  750. <a href="http://xml.coverpages.org/covernews.xml">XML Cover Pages</a>
  751. <a href="https://www.xml.com/feed/all/">XML.com</a>
  752. </div>
  753. <div class="category">
  754. <div>Torrents</div>
  755. <a href="https://androidkino.net/rss.xml">AndroidKino (RU)</a>
  756. <a href="https://angietorrents.cc/rss.php?custom=1">AngieTorrents</a>
  757. <a href="https://anidex.info/rss/">AniDex Tracker (JA)</a>
  758. <a href="https://www.anirena.com/rss.php">AniRena (JA)</a>
  759. <a href="https://audiobookbay.is/feed/atom/">AudioBook Bay</a>
  760. <a href="https://bangumi.moe/rss/latest">Bangumi Moe</a>
  761. <a href="https://eztv.re/ezrss.xml">EZTV</a>
  762. <a href="http://firebit.org/rss.xml">FireBit</a>
  763. <a href="https://igg-games.com/feed">Install Guide Games</a>
  764. <a href="https://www.limetorrents.lol/rss/">Lime Torrents</a>
  765. <a href="https://nyaa.si/?page=rss">Nyaa</a>
  766. <a href="https://pcgamestorrents.com/feed">PCGamesTorrents</a>
  767. <a href="http://tracker2.postman.i2p/?view=AddRSSMap">Postman</a>
  768. <a href="https://rarbg.to/rss.php">RARBG</a>
  769. <a href="http://rutor.info/rss.php">RUTOR (EN/RU)</a>
  770. <a href="https://sktorrent.org/feed_rss.xml">SkTorrent</a>
  771. <a href="https://tpb.party/rss">The Pirate Bay</a>
  772. <a href="https://tokyo-tosho.net/rss.php">Tokyo Toshokan</a>
  773. <!--a href="https://www.tokyotosho.info/rss.php">Tokyo Toshokan</a-->
  774. <a href="https://www.torlock.com/rss.xml">Torlock</a>
  775. <a href="https://www.torrent911.me/rss">Torrent911 (ES)</a>
  776. <a href="https://www.torrentdownload.info/feed_latest">Torrent Download</a>
  777. <a href="https://www.torrentdownloads.pro/rss.xml">Torrent Downloads</a>
  778. <a href="https://booktracker.org/rss.php">Книжный трекер (RU)</a>
  779. <a href="https://gamestracker.org/torrents/rss/">Торрент игры (RU)</a>
  780. </div>
  781. </div>
  782. <div class="decor"></div>
  783. <div><big>💿 <b>Install a feed reader</b></big></div>
  784. <div class="content" id="software">
  785. <p>This is a list of desktop applications, mobile apps and online services for you to choose from.</p>
  786. <p>This list includes news readers, podcast managers, torrent clients and web browsers which support web feeds.</p>
  787. <p>Recommended programs are marked with 🔖</p>
  788. <div id="filter">
  789. <span class="filter" id="torrent">BitTorrent</span>
  790. <span class="filter" id="news">News</span>
  791. <span class="filter" id="music">Podcast</span>
  792. <span class="filter" id="web">Web Browser</span>
  793. </div>
  794. <div class="category">
  795. <div>Desktop</div>
  796. <div class="subcategory">
  797. <div>Linux</div>
  798. <a class="news" href="https://apps.kde.org/akregator/">Akregator</a>
  799. <a class="music" href="https://amarok.kde.org/">Amarok</a>
  800. <a class="web" href="https://brave.com/">Brave</a>
  801. <a class="torrent" href="https://deluge-torrent.org/">Deluge</a>
  802. <a class="news" href="https://github.com/jeena/FeedTheMonkey">Feed The Monkey</a>
  803. <a class="music" href="https://gpodder.github.io/">gPodder</a>
  804. <a class="news recom" href="https://leechcraft.org/">LeechCraft</a>
  805. <a class="news recom" href="https://lzone.de/liferea/">Liferea</a>
  806. <a class="news recom" href="https://gitlab.com/news-flash/news_flash_gtk/">NewsFlash</a>
  807. <a class="web" href="https://otter-browser.org/">Otter Browser</a>
  808. <a class="torrent" href="https://www.qbittorrent.org/">qBittorrent</a>
  809. <a class="news" href="https://quiterss.org/">QuiteRSS</a>
  810. <a class="news" href="https://github.com/martinrotter/rssguard">RSS Guard</a>
  811. <a class="news" href="http://www.rssowl.org/">RSSOwl</a>
  812. <a class="music" href="https://wiki.gnome.org/Apps/Rhythmbox">Rhythmbox</a>
  813. <a class="music" href="https://strawberrymusicplayer.org/">Strawberry Music Player</a>
  814. <a class="news" href="https://www.open-tickr.net/">TICKR</a>
  815. <a class="torrent recom" href="https://www.tribler.org/">Tribler</a>
  816. <a class="web" href="https://vivaldi.com/features/feed-reader/">Vivaldi</a>
  817. </div>
  818. <div class="subcategory">
  819. <div>macOS</div>
  820. <a class="music" href="https://amarok.kde.org/">Amarok</a>
  821. <a class="web" href="https://brave.com/">Brave</a>
  822. <a class="torrent" href="https://deluge-torrent.org/">Deluge</a>
  823. <a class="music" href="https://gpodder.github.io/">gPodder</a>
  824. <a class="news recom" href="https://leechcraft.org/">LeechCraft</a>
  825. <a class="news recom" href="https://netnewswire.com/">NetNewsWire</a>
  826. <a class="web" href="https://otter-browser.org/">Otter Browser</a>
  827. <a class="torrent" href="https://www.qbittorrent.org/">qBittorrent</a>
  828. <a class="news" href="https://quiterss.org/">QuiteRSS</a>
  829. <a class="news" href="https://github.com/martinrotter/rssguard">RSS Guard</a>
  830. <a class="news" href="http://www.rssowl.org/">RSSOwl</a>
  831. <a class="music" href="https://strawberrymusicplayer.org/">Strawberry Music Player</a>
  832. <a class="torrent recom" href="https://www.tribler.org/">Tribler</a>
  833. <a class="web" href="https://vivaldi.com/features/feed-reader/">Vivaldi</a>
  834. </div>
  835. <div class="subcategory">
  836. <div>React OS and Windows</div>
  837. <a class="music" href="https://amarok.kde.org/">Amarok</a>
  838. <a class="web" href="https://brave.com/">Brave</a>
  839. <a class="torrent" href="https://deluge-torrent.org/">Deluge</a>
  840. <a class="music" href="https://gpodder.github.io/">gPodder</a>
  841. <a class="web" href="http://kmeleonbrowser.org/">K-Meleon</a>
  842. <a class="news recom" href="https://leechcraft.org/">LeechCraft</a>
  843. <a class="web" href="https://otter-browser.org/">Otter Browser</a>
  844. <a class="torrent" href="https://www.qbittorrent.org/">qBittorrent</a>
  845. <a class="news" href="https://quiterss.org/">QuiteRSS</a>
  846. <a class="news" href="http://rssbandit.org/">RSS Bandit</a>
  847. <a class="news" href="https://github.com/martinrotter/rssguard">RSS Guard</a>
  848. <a class="news" href="http://www.rssowl.org/">RSSOwl</a>
  849. <a class="news" href="http://sharpreader.net/">SharpReader</a>
  850. <a class="music" href="https://strawberrymusicplayer.org/">Strawberry Music Player</a>
  851. <a class="torrent recom" href="https://www.tribler.org/">Tribler</a>
  852. <a class="web" href="https://vivaldi.com/features/feed-reader/">Vivaldi</a>
  853. </div>
  854. </div>
  855. <div class="category">
  856. <div>Mobile</div>
  857. <div class="subcategory">
  858. <div>Android</div>
  859. <a class="web" href="https://brave.com/">Brave</a>
  860. <a class="news" href="https://f-droid.org/en/packages/com.nononsenseapps.feeder/">Feeder</a>
  861. <a class="torrent" href="https://f-droid.org/en/packages/org.proninyaroslav.libretorrent/">LibreTorrent</a>
  862. <a class="music" href="https://f-droid.org/en/packages/io.gitlab.listentogether">ListenTogether</a>
  863. <a class="news" href="https://f-droid.org/en/packages/co.appreactor.news/">News</a>
  864. <a class="news" href="https://f-droid.org/en/packages/com.nunti/">Nunti</a>
  865. <a class="music" href="https://f-droid.org/en/packages/com.podverse.fdroid/">Podverse</a>
  866. <a class="news" href="https://f-droid.org/en/packages/me.ash.reader/">Read You</a>
  867. <a class="news recom" href="https://f-droid.org/en/packages/com.aerotoad.thud/">Thud</a>
  868. <a class="web" href="https://vivaldi.com/features/feed-reader/">Vivaldi</a>
  869. </div>
  870. <div class="subcategory">
  871. <div>GerdaOS / KaiOS</div>
  872. <a class="news" href="https://store.bananahackers.net/#feedolin">feedolin</a>
  873. <a class="music" href="https://store.bananahackers.net/#FoxCastLite">FoxCast Lite</a>
  874. <a class="music" href="https://store.bananahackers.net/#Mica">Mica</a>
  875. <a class="music" href="https://store.bananahackers.net/#PodKast">PodKast</a>
  876. <a class="music" href="https://store.bananahackers.net/#podlp">PodLP</a>
  877. <a class="news" href="https://store.bananahackers.net/#n4no.com.rss-reader">RSS Reader</a>
  878. </div>
  879. <div class="subcategory">
  880. <div>iOS</div>
  881. <a class="web" href="https://brave.com/">Brave</a>
  882. <a class="news" href="https://netnewswire.com/">NetNewsWire</a>
  883. <a class="music" href="https://github.com/guumeyer/Podcast">Podcast</a>
  884. <a class="music" href="https://github.com/rafaelclaycon/PodcastApp">PodcastApp</a>
  885. </div>
  886. <div class="subcategory">
  887. <div>postmarketOS</div>
  888. <a class="news" href="https://apps.kde.org/alligator/">Alligator</a>
  889. <a class="news" href="https://gfeeds.gabmus.org/">Feeds</a>
  890. </div>
  891. <div class="subcategory">
  892. <div>Sailfish OS</div>
  893. <a class="news" href="https://github.com/mkiol/kaktus">Kaktus</a>
  894. <a class="news" href="https://github.com/donaggio/harbour-feedhaven">Feed Haven</a>
  895. <a class="news" href="http://gitlab.unique-conception.org/apps-4-sailfish/feed-me">Feed Me</a>
  896. <a class="music" href="https://gpodder.github.io/">gPodder</a>
  897. <a class="news" href="https://github.com/walokra/haikala">Haikala</a>
  898. <a class="news" href="https://github.com/pycage/tidings">Tidings</a>
  899. </div>
  900. <div class="subcategory">
  901. <div>Tizen</div>
  902. <a class="news" href="https://github.com/CESARBR/tizenreader">Tizen Reader</a>
  903. </div>
  904. <div class="subcategory">
  905. <div>Ubuntu Touch</div>
  906. <a class="music" href="https://open-store.io/app/com.mikeasoft.podbird">Podbird</a>
  907. <a class="music" href="https://open-store.io/app/soy.iko.podphoenix">Podphoenix</a>
  908. <a class="news" href="https://open-store.io/app/rssreader.florisluiten">RSSreader</a>
  909. <a class="news" href="https://open-store.io/app/simplestrss.kazord">SimplestRSS</a>
  910. <!-- a class="torrent" href="https://open-store.io/app/transmission.pavelprosto">Transmission</a -->
  911. <!-- a class="torrent" href="https://open-store.io/app/tr.davidv.dev">Transmission Remote</a -->
  912. <a class="news" href="https://open-store.io/app/darkeye.ursses">uRsses</a>
  913. </div>
  914. </div>
  915. <div class="category">
  916. <div>Terminal</div>
  917. <a class="news" href="https://codezen.org/canto-ng/">Canto</a>
  918. <a class="news" href="https://newsboat.org/">Newsboat</a>
  919. <a class="news" href="https://sr.ht/~ghost08/photon/">Photon</a>
  920. <a class="torrent" href="https://github.com/rakshasa/rtorrent">RTorrent</a>
  921. <a class="torrent" href="https://github.com/dpsenner/bridge-from-torrent-rss-feed-to-rtorrent">bridge-from-torrent-rss-feed-to-rtorrent</a>
  922. <a class="news" href="https://codemadness.org/sfeed_curses-ui.html">Sfeed</a>
  923. </div>
  924. <div class="category">
  925. <div>Web (self hosted)</div>
  926. <a class="news" href="https://feedbin.com/">Feedbin</a>
  927. <a class="news" href="http://feedonfeeds.com/">Feeds on Feeds</a>
  928. <a class="news" href="https://freshrss.org/">FreshRSS</a>
  929. <a class="news" href="https://miniflux.app/">Miniflux</a>
  930. <a class="news" href="https://offog.org/code/rawdog/">rawdog</a>
  931. <a class="news" href="https://github.com/acavalin/rrss">RRSS</a>
  932. <a class="torrent" href="https://github.com/Novik/ruTorrent">ruTorrent</a>
  933. <a class="news" href="https://tt-rss.org/">Tiny Tiny RSS</a>
  934. </div>
  935. <div class="category">
  936. <div>Web (service)</div>
  937. <a class="news" href="https://feedbin.com/">Feedbin</a>
  938. <a class="news" href="https://feeder.co/">Feeder</a>
  939. <a class="news" href="https://feedly.com/">Feedly</a>
  940. <a class="news" href="https://goodnews.click/">Good News</a>
  941. <a class="news" href="https://www.inoreader.com/">Inoreader</a>
  942. <a class="news" href="http://www.netvibes.com/en">Netvibes</a>
  943. <a class="news" href="https://newsblur.com/">NewsBlur</a>
  944. <a class="news" href="https://www.reedah.com/">Reedah</a>
  945. <a class="news" href="https://theoldreader.com/">The Old Reader</a>
  946. </div>
  947. </div>
  948. <div class="decor"></div>
  949. <div><big>📢 <b>Speak your mind!</b></big></div>
  950. <div class="content">
  951. <p>Do you want to start a syndication-enabled podcast?</p>
  952. <p>The following blog and podcast services provide access to syndication.</p>
  953. <p>Recommended service providers are marked with 🔖</p>
  954. <div id="services">
  955. <div class="category">
  956. <div>Decentralized (ActivityPub)</div>
  957. <p><i>Decentralized services are <u>very and mostly</u> encouraged; Use one account to communicate with all services and platforms.</i></p>
  958. <a href="https://diaspora.fediverse.observer/list">diaspora*</a>
  959. <a class="recom" href="https://funkwhale.fediverse.observer/list">Funkwhale</a>
  960. <a href="https://instances.social/">Mastodon</a>
  961. <a class="recom" href="https://instances.joinpeertube.org/">PeerTube</a>
  962. </div>
  963. <div class="category">
  964. <div>No-charge services</div>
  965. <p><i>Whilst all of the listed services support syndication, free of monetary charge services are generally <u>not</u> encouraged.</i></p>
  966. <a href="https://www.acast.com/">Acast</a>
  967. <a href="https://www.blogtalkradio.com/">Blog Talk Radio</a>
  968. <a class="recom" href="https://www.chatons.org/search/by-service?service_type_target_id=154">Chatons (list of external services)</a>
  969. <a href="https://castos.com/">Castos</a>
  970. <a href="https://feedpress.com/">FeedPress</a>
  971. <a href="http://libsyn.com/">libsyn</a>
  972. <a class="recom" href="https://neocities.org/">Neocities</a>
  973. <a class="recom" href="https://noblogs.org/">NoBlogs</a>
  974. <a href="https://podbean.com/">PodBean</a>
  975. <a href="https://podomatic.com/">Podomatic</a>
  976. <a href="https://rawvoice.com/">RawVoice</a>
  977. <a class="recom" href="https://tedomum.net/service/">TeDomum (list of services)</a>
  978. </div>
  979. <div class="category">
  980. <div>Pay services</div>
  981. <a href="https://www.cloudaccess.net/">CloudAccess</a>
  982. <a href="https://www.hetzner.com/">Hetzner</a>
  983. <a href="https://www.hostinger.co.uk/">Hostinger</a>
  984. <a class="recom" href="https://micro.blog/">Micro.blog</a>
  985. <a class="recom" href="https://monocles.eu/more/">monocles</a>
  986. <a href="https://www.rochen.com/">Rochen</a>
  987. <a href="https://www.strato.de/">STRATO</a>
  988. <a class="recom" href="https://zourit.net/">Zourit</a>
  989. </div>
  990. <div class="category">
  991. <div>Self host</div>
  992. <p><i>You are encouraged to host <b>your own</b> server connected to <a href="https://i2pd.readthedocs.io/en/latest/tutorials/http/#host-anonymous-website"><u>the I2P network</u></a>.</i></p>
  993. <a href="https://www.11ty.dev/">Eleventy</a>
  994. <a href="https://dthompson.us/projects/haunt.html">Haunt</a>
  995. <a href="https://gohugo.io/">Hugo</a>
  996. <a href="https://jekyllrb.com/">Jekyll</a>
  997. <a href="https://picocms.org/">Pico</a>
  998. <a href="https://wordpress.org/">WordPress</a>
  999. <a href="http://getzola.org/">Zola</a>
  1000. </div>
  1001. <div class="category">
  1002. <div>Of note</div>
  1003. <p>Whatever is your medium you choose to publish your podcast, best ways to make your files available are via BitTorrent, I2P and IPFS.</p>
  1004. <p>* PeerTube has built-in BitTorrent support.</p>
  1005. </div>
  1006. </div>
  1007. </div>
  1008. <div class="decor"></div>
  1009. <div><big>📚 <b>Learn more about syndication</b></big></div>
  1010. <div class="content" id="learn">
  1011. <p>This is a short history and reference guide to web feeds.</p>
  1012. <div><b>Newspaper (2021)</b></div>
  1013. <p>Newspaper is a Userscript that renders web feeds to web pages with the sole use of Javascript. <a href="https://openuserjs.org/scripts/sjehuda/Newspaper">Continue reading…</a></p>
  1014. <div><b>JSON Feed (2017)</b></div>
  1015. <p>The JSON Feed format is a pragmatic syndication format, like RSS and Atom, but with one big difference: its JSON instead of XML. <a href="https://www.jsonfeed.org/version/1.1/">Continue reading…</a></p>
  1016. <div><b>StreamBurner (2013)</b></div>
  1017. <p>StreamBurner is a set of XSLT stylesheets that allow the conversion of web feeds into printable and readable web pages. <a href="https://gitgud.io/sjehuda/streamburner">Continue reading…</a></p>
  1018. <div><b>Activity Streams (2008)</b></div>
  1019. <p>An extension to the Atom feed format to express what people are doing around web. The stream in ActivityStreams is a feed of related activities for a given person or social object.
  1020. <a href="https://wiki.activitystrea.ms/w/page/1359261/FrontPage">Continue reading…</a></p>
  1021. <div><b>h-entry and hAtom 0.1 (2006)</b></div>
  1022. <p>h-entry is a simple, open format for episodic or datestamped content on the web. h-entry is often used with content intended to be syndicated, e.g. blog posts. h-entry is one of several open microformat standards suitable for embedding data in HTML. <a href="http://microformats.org/wiki/h-entry">Continue reading…</a></p>
  1023. <div><b>Atom and AtomPub (2003)</b></div>
  1024. <p>Atom is the name of an XML-based Web content and metadata syndication format, and an application-level protocol for publishing and editing Web resources belonging to periodically updated websites. <a href="https://web.archive.org/web/20120105062737if_/http://www.atomenabled.org:80/developers/syndication/#whatIsAtom">Continue reading…</a></p>
  1025. <div><b>OPML (2000)</b></div>
  1026. <p>OPML is a text-based format designed to store and exchange outlines with attributes. It has been around since the early 2000s, and is widely used in the RSS world to exchange subscription lists. It is an established standard for interop among outliners. <a href="http://opml.org/spec2.opml">Continue reading…</a></p>
  1027. <div><b>RSS (1999)</b></div>
  1028. <p>RSS is a Web content syndication format. Its name is an acronym for Really Simple Syndication. RSS is a dialect of XML. <a href="https://www.rssboard.org/rss-specification#whatIsRss">Continue reading…</a></p>
  1029. <div><b>XSL and XSLT (1998)</b></div>
  1030. <p>XSL (Extensible Stylesheet Language) is designed for use as part of XSL, which is a stylesheet language for XML that has document manipulation capabilities beyond styling. <a href="http://xml.coverpages.org/xsl.html">Continue reading…</a></p>
  1031. </div>
  1032. <div class="decor"></div>
  1033. <div><big>✒️ <b>An appeal from the author</b></big></div>
  1034. <div class="content" id="plea">
  1035. <p>Greetings,</p>
  1036. <p>My name is Schimon Jehudah, Im an Attorney at Law and Crypto Analyst, and author of StreamBurner News Reader (also Newspaper Userscript).</p>
  1037. <p>For many years, the technology generally known as RSS has been serving me and the companies Ive been working with (financial houses and law firms) in both corporate and individual life.</p>
  1038. <p>Since its inception, advertising and media companies have been working together to eliminate this technology; mostly, by paying off software companies (namely, web browser makers) to ignore, neglect and even remove support for this technology.</p>
  1039. <p>This vital technology, which exists for over 20 years, is being oppressed for over a decade, particularly by advertising companies, news publishers, western governments and data mining websites (aka social networks”) who want to turn more control of data flow to them and much less control to individuals, like you.</p>
  1040. <p>I advise you to share this technology with your friends and any acquaintance of yours, while at the same time, boycott media and news outlets that refuse to provide web feeds.</p>
  1041. <p><b><i>I <u>dont</u> ask you for financial support nor monetary donations;</i></b></p>
  1042. <p><b><i>I only ask YOU to share this technology with people you know.</i></b></p>
  1043. <p>Respectfully,</p>
  1044. <p>Schimon</p>
  1045. </div>
  1046. <div><big>📜 <b>Postscript</b></big></div>
  1047. <div class="content" id="postscript">
  1048. <p>This program was made by a person who is a Lawyer and has no formal technical trainings nor technical qualifications, so-called, neither in CSS, ECMA (JavaScript), HTML nor XSLT technologies.</p>
  1049. <p>Since a Lawyer can make this program from scratch in 28 the days (4 hours each day), then ask yourselves Why do web browser makers look for excuses to actively ignore this important web technology, if not because of payoffs?”</p>
  1050. </div>
  1051. <div>
  1052. StreamBurner project and source code:
  1053. <a href="https://gitgud.io/sjehuda/streamburner">https://gitgud.io/sjehuda/streamburner</a>
  1054. </div>
  1055. </div>`,
  1056. htmlBar =
  1057. `<div id="links-bar">
  1058. <a id="follow" title="Subscribe to get the latest updates and news">Follow</a>
  1059. <a class="cursor-pointer" id="subtome" title="Subscribe via SubToMe">SubToMe</a>
  1060. <a id="direction" title="Switch direction">Direction</a>
  1061. <a class="cursor-help" id="donate-newspaper" title="Learn how you can support">Support</a>
  1062. <a class="cursor-help" id="about-newspaper" title="Learn about syndication feed">Help</a>
  1063. </div>`,
  1064. htmlEmpty =
  1065. `<div class="notice no-entry" id="empty-feed">
  1066. <div><big><b>This news feed is empty</b></big></div>
  1067. <p>You are advised to contact the web administrators</p>
  1068. <p>and ask them to maintain Atom or RSS feeds.</p>
  1069. <!-- div>You might want to address them to <a href="https://aboutfeeds.com">aboutfeeds.com</a>.</div -->
  1070. <div class="decor"></div>
  1071. <p>Below is a contact link with possible emails;</p>
  1072. <p>Use it only in case there is no contact</p>
  1073. <p>address or form on this website.</p>
  1074. <div class="decor"></div>
  1075. </div>`,
  1076. cssFileBar = `
  1077. #links-bar {
  1078. margin: auto;
  1079. outline: .1em solid;
  1080. width: 100%;
  1081. /* max-width: 70%; */
  1082. direction: ltr;
  1083. text-align: center;
  1084. position: fixed;
  1085. top: 0;
  1086. left: 0;
  1087. right: 0;
  1088. color: WhiteSmoke;
  1089. background: #555; /* #eee */
  1090. /* white-space: nowrap; */
  1091. z-index: 1; }
  1092.  
  1093. #links-bar > a {
  1094. color: WhiteSmoke;
  1095. /* font-style: italic; */
  1096. font-family: system-ui; }
  1097.  
  1098. #links-bar > a:nth-child(1) {
  1099. /* border-left-style: unset; */
  1100. border-right-style: solid;
  1101. /* border-radius: 2em; */
  1102. color: #555;
  1103. background: WhiteSmoke;
  1104. font-weight: bold; }`,
  1105. cssFileLTR = `
  1106. html, body {
  1107. padding: 0;
  1108. margin: 0;
  1109. overflow-x: hidden; }
  1110.  
  1111. body {
  1112. background: WhiteSmoke;
  1113. color: #333;
  1114. hyphens: auto; }
  1115.  
  1116. #feed {
  1117. /*
  1118. width: 98%;
  1119. margin: auto;
  1120. position: relative;
  1121. overflow-x: hidden;
  1122. */
  1123. margin: 0 -1em 1em -1em;
  1124. margin-bottom: 1em;
  1125. padding: 1em 1em 0 1em; }
  1126.  
  1127. #feed a {
  1128. color: #333;
  1129. display: inline-block; }
  1130.  
  1131. #logo {
  1132. display: inline-block;
  1133. float: left;
  1134. overflow: hidden;
  1135. position: relative;
  1136. height: 60px;
  1137. width: 60px;
  1138. margin-right: 9;
  1139. padding-top: 12; }
  1140.  
  1141. #logo > a > img {
  1142. margin: auto;
  1143. max-width: 100%;
  1144. position: absolute;
  1145. width: 5em;
  1146. bottom: 0;
  1147. right: 0;
  1148. left: 0;
  1149. top: 0; }
  1150.  
  1151. #title { /* TODO tag </title-page> */
  1152. font-variant: small-caps;
  1153. text-align: center;
  1154. font-weight: bold;
  1155. font-size: 3em;
  1156. margin-bottom: 0;
  1157. overflow: hidden;
  1158. -webkit-line-clamp: 2;
  1159. margin: 0; }
  1160.  
  1161. #title .empty:before {
  1162. font-variant: small-caps;
  1163. content: 'Streamburner News Dashboard';
  1164. text-align: center; }
  1165.  
  1166. #subtitle {
  1167. border-top: 1px solid;
  1168. width: 90%;
  1169. margin: auto;
  1170. overflow: hidden;
  1171. -webkit-line-clamp: 2;
  1172. white-space: wrap;
  1173. text-align: center;
  1174. font-variant: all-small-caps;
  1175. font-weight: normal;
  1176. font-size: 1.5em; }
  1177.  
  1178. .container {
  1179. display: flex; }
  1180.  
  1181. #links-bar {
  1182. /* cursor: default; */
  1183. user-select: none;
  1184. display: block;
  1185. margin: auto;
  1186. margin-bottom: 1em;
  1187. margin-top: 1em;
  1188. width: 90%;
  1189. text-align: center;
  1190. direction: ltr;
  1191. /* font-size: 90%; */ }
  1192.  
  1193. #links-bar > a {
  1194. padding: 10px;
  1195. text-decoration: none;
  1196. /* font-size: 70%; */
  1197. user-select: none;
  1198. outline: none;
  1199. min-width: 12%;
  1200. padding: 6px;
  1201. margin: 6px; }
  1202.  
  1203. #links-bar > a:nth-child(1) {
  1204. cursor: pointer;
  1205. background: lavender; /* honeydew */
  1206. border-color: grey;
  1207. border-left-style: solid;
  1208. border-radius: 2em; /* 40% */
  1209. /*
  1210. border-top-left-radius: 2em;
  1211. border-bottom-left-radius: 2em;
  1212. */ }
  1213.  
  1214. .cursor-pointer {
  1215. cursor: pointer; }
  1216.  
  1217. .cursor-help {
  1218. cursor: help; }
  1219.  
  1220. #toc {
  1221. margin-left: 3%;
  1222. padding: 5px; }
  1223.  
  1224. #toc:before {
  1225. content: 'Latest Headlines';
  1226. /* font-size: 76%; */
  1227. font-weight: bold; }
  1228.  
  1229. #toc > li:first-child {
  1230. margin-top: 1em; }
  1231.  
  1232. #toc > li > a {
  1233. text-decoration: none;
  1234. /* font-size: 66%; */
  1235. display: block;
  1236. outline: none;
  1237. padding: 5px 0;
  1238. margin-left: 1%; }
  1239.  
  1240. #toc > li > a:hover {
  1241. text-decoration: underline; }
  1242.  
  1243. #toc > li > a:visited {
  1244. text-decoration: line-through; }
  1245.  
  1246. /*
  1247. #toc > a:first-child {
  1248. margin-top: 1em; }
  1249.  
  1250. #toc > a {
  1251. text-decoration: none;
  1252. display: list-item;
  1253. outline: none;
  1254. padding: 5px 0;
  1255. margin-left: 1%; }
  1256.  
  1257. #toc > a:hover {
  1258. text-decoration: underline; }
  1259.  
  1260. #toc > a:visited {
  1261. text-decoration: line-through; }
  1262. */
  1263.  
  1264. .about-newspaper { /* overlay */
  1265. font-family: system-ui;
  1266. font-style: initial;
  1267. position: fixed;
  1268. display: none;
  1269. top: 0;
  1270. left: 0;
  1271. right: 0;
  1272. bottom: 0;
  1273. color: WhiteSmoke;
  1274. background-color: #555;
  1275. /* background-color: #ffff9b; */
  1276. z-index: 2;
  1277. overflow-y: auto;
  1278. text-align: left; /* justify */
  1279. direction: ltr;
  1280. padding: 5%;
  1281. line-height: 1.8;
  1282. font-size: 110%;
  1283. cursor: unset; }
  1284.  
  1285. .about-newspaper div {
  1286. margin-bottom: 1em; }
  1287.  
  1288. .about-newspaper a,
  1289. .category > a,
  1290. .subcategory > a {
  1291. text-decoration: none;
  1292. color: WhiteSmoke; }
  1293.  
  1294. .about-newspaper a:hover,
  1295. .category > a:hover,
  1296. .subcategory > a:hover {
  1297. text-decoration: underline; }
  1298.  
  1299. .quote {
  1300. text-align: center; /* right */
  1301. font-size: 70%;
  1302. font-style: italic;
  1303. }
  1304.  
  1305. .text-icon {
  1306. font-weight: bold;
  1307. background: darkorange;
  1308. border-radius: 11px;
  1309. padding-top: 3px;
  1310. padding-bottom: 3px;
  1311. padding-right: 5px;
  1312. padding-left: 5px;
  1313. user-select: none;
  1314. }
  1315.  
  1316. #services > a:after,
  1317. .category > a:after,
  1318. .subcategory > a:after {
  1319. content: ', '; }
  1320.  
  1321. #services > a:last-child:after,
  1322. .category > a:last-child:after,
  1323. .subcategory > a:last-child:after {
  1324. content: '.'; }
  1325.  
  1326. #filter {
  1327. /* margin-right: auto;
  1328. margin-left: auto; */
  1329. margin-top: 25px; }
  1330.  
  1331. .filter {
  1332. font-weight: bold;
  1333. outline: none;
  1334. user-select: none;
  1335. border-bottom: 2px solid WhiteSmoke;
  1336. background: WhiteSmoke;
  1337. color: #555;
  1338. border-radius: 5%;
  1339. /* border-bottom-right-radius: unset; */
  1340. /* border-bottom-left-radius: unset; */
  1341. margin-right: 15px;
  1342. padding: 5px;
  1343. width: 10%;
  1344. cursor: pointer; }
  1345.  
  1346. .hide {
  1347. display: none; }
  1348.  
  1349. .grey {
  1350. background: inherit;
  1351. color: inherit; }
  1352.  
  1353. /*
  1354. .recom {
  1355. filter: drop-shadow(2px 4px 6px pink); }
  1356. */
  1357.  
  1358. .recom:before {
  1359. font-size: 80%;
  1360. content : '🔖 '; }
  1361.  
  1362. .category > div:first-child {
  1363. font-size: 110%;
  1364. font-weight: bold;
  1365. margin-top: 25px; }
  1366.  
  1367. .subcategory > div {
  1368. font-weight: bold;
  1369. /* text-decoration: underline; */ }
  1370.  
  1371. #postscript {
  1372. font-style: italic; }
  1373.  
  1374. /*
  1375. #feeds > .category > a:link {
  1376. text-decoration: none; }
  1377. */
  1378.  
  1379. #feeds > .category > a:before {
  1380. font-size: 80%;
  1381. content: '🏷️ '; } /* 🧧 🔗 */
  1382.  
  1383. #articles {
  1384. justify-content: space-between;
  1385. max-width: 90%;
  1386. margin: 0 auto;
  1387. padding: 10px 0; }
  1388.  
  1389. #articles > * {
  1390. margin: .5em;
  1391. white-space: normal;
  1392. vertical-align: top;
  1393. margin-bottom: 50px; }
  1394.  
  1395. .entry {
  1396. /*border-bottom: inset;
  1397. border-bottom: groove; */
  1398. margin-left: auto;
  1399. margin-right: auto;
  1400. overflow: auto;
  1401. line-height: 1.6;
  1402. /* font-size: 85%; */
  1403. /* overflow-x: hidden; */
  1404. max-width: 98%;
  1405. /* outline: auto; */
  1406. outline: none;
  1407. padding: 4px; }
  1408.  
  1409. .entry:last-child {
  1410. border-bottom: unset; }
  1411.  
  1412. .entry:hover {
  1413. /* background: #f8f9fa; */
  1414. /* outline: none; */ }
  1415.  
  1416. .entry > a {
  1417. white-space: normal; }
  1418.  
  1419. .decor {
  1420. /* border-top: inset; */
  1421. /* border-top: groove;
  1422. width: 30%; */
  1423. /* padding-right: 30%;
  1424. padding-left: 30%; */
  1425. margin-right: 30% !important;
  1426. margin-left: 30% !important;
  1427. padding-bottom: 1.5em !important;
  1428. text-align: center;
  1429. /* text-decoration: overline; */
  1430. user-select: none; }
  1431.  
  1432. .decor:after {
  1433. /* content: '∽ ✦ ∼' */
  1434. /* content: '· · ✦ · ·'; */
  1435. content: '· · • · ·'; } /* ✦ ✧ ۞ ⍟ ⍣ ✹ ✸ ✴ ✶ ✵ ✷ */
  1436.  
  1437. .title {
  1438. cursor: pointer;
  1439. display: inline-block;
  1440. font-size: 150%;
  1441. font-weight: bold;
  1442. text-decoration: underline;
  1443. overflow-wrap: anywhere;
  1444. /* overflow: visible;
  1445. text-overflow: ellipsis; */
  1446. font-variant: small-caps; }
  1447.  
  1448. /*
  1449. .title > a {
  1450. text-decoration: none; }
  1451.  
  1452. .title > a:hover {
  1453. text-decoration: underline; }
  1454. */
  1455.  
  1456. .geolocation > a {
  1457. text-decoration: none;
  1458. padding-left: 6px; }
  1459.  
  1460. .author {
  1461. font-size: 75%;
  1462. margin: 0 auto 0 auto; }
  1463.  
  1464. .author:before {
  1465. content: 'By '; }
  1466.  
  1467. .author:after {
  1468. content: ' / '; }
  1469.  
  1470. .published, .updated {
  1471. /* font-size: 75%; */
  1472. margin: 0 auto 0 auto;
  1473. /* direction: ltr; */ }
  1474.  
  1475. .content {
  1476. margin: 15px auto 15px 1%;
  1477. inline-size: 95%;
  1478. text-indent: 3px; }
  1479.  
  1480. .content-text {
  1481. white-space: pre-wrap; }
  1482.  
  1483. .content[type='text'] {
  1484. font-family: monospace; }
  1485.  
  1486. .content * {
  1487. /* max-width: 96%; */
  1488. object-fit: contain;
  1489. height: auto; }
  1490.  
  1491. img, svg {
  1492. margin: 1em !important;
  1493. margin-left: 0 !important;
  1494. margin-top: 0 !important;
  1495. display: block;
  1496. /* border: 4px solid #555; */
  1497. border-radius: 0.5em;
  1498. /* min-width: 96%; */
  1499. max-width: 96%;
  1500. }
  1501.  
  1502. video {
  1503. border-radius: 0.5em;
  1504. outline: none;
  1505. }
  1506.  
  1507. iframe {
  1508. display: block;
  1509. border-radius: 0.5em;
  1510. width: 96%;
  1511. min-height: 70vw;
  1512. }
  1513.  
  1514. /* TODO Test <pre> */
  1515. code {
  1516. color: WhiteSmoke;
  1517. background: #555;
  1518. overflow: auto;
  1519. display: inline-flex;
  1520. max-height: 300px;
  1521. border-radius: 4px; }
  1522.  
  1523. .enclosures {
  1524. background: #eee;
  1525. border: 1px solid GrayText;
  1526. border-radius: 4px;
  1527. clear: both;
  1528. color: #525c66;
  1529. cursor: help;
  1530. direction: ltr;
  1531. font-size: .8em;
  1532. margin: 5px auto 15px 1%;
  1533. padding: 15px;
  1534. vertical-align: middle;
  1535. /* border: 1px solid #aaa; */
  1536. border-radius: .5em;
  1537. max-width: 100%;
  1538. border-left: double;
  1539. padding: 1em; }
  1540.  
  1541. .enclosure > * {
  1542. margin: 3px; }
  1543.  
  1544. .enclosure > span:after {
  1545. content: ' (Document file) '; }
  1546.  
  1547. .enclosure > span[icon]:after {
  1548. content: '📄️';
  1549. margin: 3px; }
  1550.  
  1551. .enclosure > span.executable:after{
  1552. content: ' (Executable file) '; }
  1553.  
  1554. .enclosure > span[icon='executable']:after {
  1555. content: '📦️';
  1556. margin: 3px; }
  1557.  
  1558. .enclosure > span.image:after {
  1559. content: ' (Image file) '; }
  1560.  
  1561. .enclosure > span[icon='image']:after {
  1562. content: '🖼️';
  1563. margin: 3px; }
  1564.  
  1565. .enclosure > span.audio:after {
  1566. content: ' (Audio file) '; }
  1567.  
  1568. .enclosure > span[icon='audio']:after{
  1569. content: '🎼️';
  1570. margin: 3px; }
  1571.  
  1572. .enclosure > span.video:after {
  1573. content: ' (Video file) '; }
  1574.  
  1575. .enclosure > span[icon='video']:after {
  1576. content: '📽️';
  1577. margin:3px; }
  1578.  
  1579. .notice {
  1580. text-align: center;
  1581. display: block;
  1582. font-size: 130%;
  1583. font-weight: lighter;
  1584. font-variant-caps: all-small-caps;
  1585. color: FireBrick; }
  1586.  
  1587. .warning {
  1588. display: block;
  1589. font-size: 85%; /* 60 */
  1590. font-weight: bold;
  1591. color: DarkRed; }
  1592.  
  1593. .atom1.author:after {
  1594. content: 'Atom 1.0 Warning: Element </author> is missing'; }
  1595.  
  1596. .atom1.id:after {
  1597. content: 'Atom 1.0 Warning: Element </id> is missing'; }
  1598.  
  1599. .atom1.link:after {
  1600. content: 'Atom 1.0 Warning: Element </link> is missing'; }
  1601.  
  1602. .atom1.published:after {
  1603. content: 'Atom 1.0 Warning: Element </published> is missing'; }
  1604.  
  1605. .atom1.title:after {
  1606. content: 'Atom 1.0 Warning: Element </title> is missing'; }
  1607.  
  1608. .rss2.description:after {
  1609. content: 'RSS 2.0 Warning: Element </description> is missing'; }
  1610.  
  1611. .rss2.link:after {
  1612. content: 'RSS 2.0 Warning: Element </link> is missing'; }
  1613.  
  1614. .rss2.title:after {
  1615. content: 'RSS 2.0 Warning: Element </title> is missing'; }
  1616.  
  1617. abbr,acronym {
  1618. border-bottom: 1px dotted #c30; }
  1619.  
  1620. dt {
  1621. font-weight: bold; }
  1622.  
  1623. #about-toc {
  1624. display: grid;
  1625. /* border-bottom: inset;
  1626. text-align: right;
  1627. width: 70%;
  1628. margin-left: 30%; */ }
  1629.  
  1630.  
  1631. #about-toc > li > a {
  1632. /* display: list-item; */
  1633. display: block; }
  1634.  
  1635.  
  1636. .about-newspaper > .content {
  1637. margin-bottom: 3em;
  1638. white-space: unset; }
  1639.  
  1640. #info-square {
  1641. position: fixed;
  1642. margin: auto;
  1643. bottom: 0;
  1644. right: 0;
  1645. left: 0;
  1646. /* top: 33px; */
  1647. padding: 3px;
  1648. color: WhiteSmoke;
  1649. background: #555;
  1650. /* border-radius: 50px;
  1651. width: 50%;
  1652. font-size: 70%; */
  1653. /* font-style: italic; */
  1654. font-family: system-ui;
  1655. /* justify-content: center; */
  1656. align-items: center;
  1657. display: flex;
  1658. text-overflow: ellipsis;
  1659. overflow: hidden;
  1660. /* white-space: pre; in case we have html tags */
  1661. white-space: nowrap; }
  1662.  
  1663. #info-square > * {
  1664. color: WhiteSmoke;
  1665. margin-left : 0.5em;
  1666. margin-right : 0.5em; }
  1667.  
  1668. #top-navigation-button {
  1669. /* set position */
  1670. position : fixed;
  1671. bottom : 10px;
  1672. right : 20px;
  1673. z-index : 1;
  1674. /* set appearance */
  1675. background : WhiteSmoke;
  1676. border : 2px solid #555;
  1677. border-radius : 50px;
  1678. /* margin : 10px; */
  1679. min-width : 30px;
  1680. min-height : 30px;
  1681. font-size : 20px;
  1682. /* opacity : 0.3; */
  1683. /* center character */
  1684. justify-content : center;
  1685. align-items : center;
  1686. display : flex;
  1687. /* disable selection marks */
  1688. outline : none;
  1689. user-select : none;
  1690. cursor : default; }
  1691.  
  1692. #email-link {
  1693. margin-top: 25px;
  1694. text-decoration: overline;
  1695. outline: none; }
  1696.  
  1697. #feed-info {
  1698. font-size : 70%;
  1699. margin-bottom : 35px;
  1700. text-align : center; }
  1701.  
  1702. #feed-banner {
  1703. outline: none;
  1704. display: table;
  1705. margin: auto;
  1706. /* filter: drop-shadow(2px 4px 6px black); */ }
  1707.  
  1708.  
  1709. #xslt-message {
  1710. background: indianred; /* #2c3e50 coral */
  1711. font-family: system-ui;
  1712. color: white; /* #eee navajowhite */
  1713. padding: 6px; /* 13px //15px //11px //9px //3px //1px */
  1714. display: block;
  1715. text-align: center; /* justify */
  1716. text-decoration: none;
  1717. direction: ltr; }`,
  1718. cssFileRTL = `
  1719. html, body {
  1720. text-align: right; }
  1721.  
  1722. #feed {
  1723. direction: rtl; }
  1724.  
  1725. #logo {
  1726. float: right;
  1727. margin-left: 9; }
  1728.  
  1729. .geolocation > a {
  1730. padding-right: 6px; }
  1731.  
  1732. .image {
  1733. float: right;
  1734. margin-left: 40px;
  1735. margin-right: auto; }`;
  1736.  
  1737. var
  1738. cssFileBase, xmlStylesheet;
  1739.  
  1740. (function checkContentType() {
  1741. let myPromise = new Promise(function(myResolve, myReject) {
  1742. let request = new XMLHttpRequest();
  1743. //request.overrideMimeType('text/plain');
  1744. //request.responseType = 'text'; // ms-stream also works but both don't make a difference
  1745. request.open('GET', document.documentURI);
  1746. //request.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
  1747. //request.setRequestHeader('Content-Type', 'text/plain');
  1748. request.onload = function() {
  1749. if (document.URL.startsWith('file:') ||
  1750. request.status == 200) {
  1751. myResolve(request);
  1752. }
  1753. else {
  1754. myReject("File not Found");
  1755. }
  1756. };
  1757. request.send();
  1758.  
  1759. /* gmXmlhttpRequest({
  1760. method: 'GET',
  1761. url: document.documentURI,
  1762. headers: {
  1763. "Content-Type": "text/plain",
  1764. "Accept": "text/plain"
  1765. },
  1766. onprogress: function(request) {
  1767. request.responseType = 'text';
  1768. },
  1769. onload: function(request) {
  1770. request.overrideMimeType = 'text/plain';
  1771. if (document.URL.startsWith('file:') ||
  1772. request.status == 200) {
  1773. myResolve(request);
  1774. }
  1775. else {
  1776. myReject("File not Found");
  1777. }
  1778. },
  1779. onerror: function(request) {
  1780. myReject('File not Found')
  1781. }
  1782. }) */
  1783. });
  1784.  
  1785. myPromise.then(
  1786. function(request) {
  1787.  
  1788. /*
  1789. if (request.response.toLowerCase().includes('<?xml-stylesheet')) {
  1790. // Apparently, this program doesn't influence server stylesheet
  1791. // This if statement is useful to save CPU and RAM resources.
  1792. // NOTE We can remove it using DOMParser.
  1793. return; // exit
  1794. }
  1795. */
  1796. let xmlFile
  1797. let domParser = new DOMParser();
  1798. xmlFile = domParser.parseFromString(request.response.trim(), 'text/xml');
  1799. // TODO Preference to respect or override stylesheet
  1800. // TODO Ignore all stylesheets if all are CSS
  1801. // TODO Infobar suggesting to render with Streamburner ?streamburner=1
  1802. // TODO Infobar suggesting to disable (watch without) Streamburner ?streamburner=0 <-- do this
  1803. if (xmlFile) {
  1804.  
  1805. //let xmlStylesheet;
  1806. for (childNode of xmlFile.childNodes) {
  1807. if (childNode.target == 'xml-stylesheet') {
  1808. childNode.remove();
  1809. xmlStylesheet = true;
  1810. //return; // exit
  1811. }
  1812. }
  1813.  
  1814. /*
  1815. // TODO Configuration to override existing stylesheet
  1816. if (override) {
  1817. if (xmlFile.firstChild.nodeName == "xml-stylesheet") {
  1818. console.log(xmlFile.firstChild)
  1819. xmlFile.firstChild.remove();
  1820. }
  1821. } else {
  1822. return; // exit
  1823. }
  1824. */
  1825.  
  1826. /*
  1827. // Remove node of type comment
  1828. // Because of this code below
  1829. if (xmlFile.childNodes[0] == xmlFile.querySelector('feed')) {
  1830. while (xmlFile.firstChild.nodeName == '#comment') {
  1831. xmlFile.firstChild.remove(); // xmlFile.childNodes[0]
  1832. }
  1833. }
  1834. */
  1835.  
  1836. /*
  1837. // Remove all nodes of type comment
  1838. nodeIterator = xmlDoc.createNodeIterator(
  1839. xmlDoc, // Starting node, usually the document body
  1840. NodeFilter.SHOW_ALL, // NodeFilter to show all node types
  1841. null,
  1842. false
  1843. );
  1844.  
  1845. let currentNode;
  1846. // Loop through each node in the node iterator
  1847. while (currentNode = nodeIterator.nextNode()) {
  1848. // Do something with each node
  1849. console.log(currentNode.nodeName);
  1850. }
  1851. */
  1852.  
  1853. // <feed xmlns="http://www.w3.org/2005/Atom">
  1854. // xmlFile.getElementsByTagNameNS('http://www.w3.org/2005/Atom','feed')
  1855. if (xmlFile.firstElementChild == xmlFile.querySelector('feed')) {
  1856. pageLoader();
  1857. renderXML(xmlFile, atomRules);
  1858. extensionLoader(
  1859. // NOTE This && is an if statement
  1860. xmlFile.querySelector('feed > link') && xmlFile.querySelector('feed > link').href,
  1861. getDateXML(xmlFile, atomRules),
  1862. 'Atom' // Atom Web Feed 1.0
  1863. );
  1864. } else
  1865. // Netscape RSS 0.91 <!DOCTYPE rss SYSTEM "http://my.netscape.com/publish/formats/rss-0.91.dtd">
  1866. // Userland RSS 0.91 <rss version="0.91">
  1867. // RSS 0.92 <rss version="0.92">
  1868. // RSS 0.93 <rss version="0.93">
  1869. // RSS 0.94 <rss version="0.94">
  1870. // RSS 2.0 <rss version="2.0">
  1871. if (xmlFile.firstElementChild == xmlFile.querySelector('rss')) {
  1872. pageLoader();
  1873. renderXML(xmlFile, rssRules);
  1874. extensionLoader(
  1875. // NOTE This && is an if statement
  1876. xmlFile.querySelector('channel > link') && xmlFile.querySelector('channel > link').href,
  1877. // FIXME
  1878. // https://www.elegislation.gov.hk/verified-chapters!en.rss.xml
  1879. getDateXML(xmlFile, rssRules),
  1880. 'RSS' // RSS Web Feed 2.0
  1881. );
  1882. } else
  1883. // TODO Check by namespace xmlns
  1884. // https://wiki.gnome.org/action/rss_rc/Home?action=rss_rc&unique=1&ddiffs=1
  1885. // http://purl.org/rss/1.0/
  1886. // https://web.resource.org/rss/1.0/schema.rdf
  1887. // RSS 0.90 <rdf:RDF xmlns="http://my.netscape.com/rdf/simple/0.9/">
  1888. // RSS 1.0 <rdf:RDF xmlns="http://purl.org/rss/1.0/">
  1889. // NOTE firstElementChild test page https://web.resource.org/rss/1.0/
  1890. // xmlFile.getElementsByTagNameNS('http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'RDF');
  1891. if (xmlFile.firstElementChild == xmlFile.getElementsByTagName("rdf:RDF")[0]) {
  1892. pageLoader();
  1893. renderXML(xmlFile, rdfRules);
  1894. extensionLoader(
  1895. null,
  1896. getDateXML(xmlFile, rdfRules),
  1897. 'RDF Vocabulary' // RSS Web Feed 1.0
  1898. );
  1899. }
  1900. // TODO
  1901. // This appears to be a not usuable Atom feed
  1902. // Either make it usable or invalidate it
  1903. // https://dbpedia.org/data/Searx.atom
  1904. }
  1905.  
  1906. // Information of request
  1907. //console.info(xmlFile);
  1908. //console.info(document);
  1909. //console.info(`all headers: ${request.getAllResponseHeaders()}`);
  1910. //console.info(`content-type header: ${request.getResponseHeader('content-type')}`);
  1911. //console.info(`content-type document: ${document.contentType}`);
  1912.  
  1913. // errorPage is a good idea to promote Falkon
  1914. //setTimeout(function(){renderFeed(request.response)}, 1500); // timeout for testing
  1915.  
  1916. try {
  1917. if (JSON.parse(request.response)) {
  1918. let jsonFile = JSON.parse(request.response);
  1919. if (jsonFile.version) {
  1920. if (jsonFile.version.toLowerCase().includes('jsonfeed.org')) {
  1921. pageLoader();
  1922. //setTimeout(function(){renderJSONFeed(jsonFile)}, 1500);
  1923. renderJSONFeed(jsonFile);
  1924. extensionLoader(
  1925. jsonFile.feed_url,
  1926. jsonFile.items[0].date_published,
  1927. // jsonFile.items[0].date_modified,
  1928. 'JSON Feed'
  1929. );
  1930. }
  1931. } else
  1932. if (jsonFile.generator) {
  1933. if (jsonFile.generator.toLowerCase().includes('statusnet') || // TODO Case insensitive
  1934. jsonFile.generator.toLowerCase().includes('gnu social')) {
  1935. pageLoader();
  1936. renderActivityStream(jsonFile);
  1937. extensionLoader(
  1938. null,
  1939. jsonFile.items[0].published,
  1940. 'ActivityStream'
  1941. );
  1942. }
  1943. }
  1944. // TODO ActivityStream
  1945. // https://nu.federati.net/api/statuses/show/3424682.json
  1946. }
  1947. } catch {
  1948. // Not JSON
  1949. }
  1950. }
  1951. );
  1952. })();
  1953.  
  1954. /*
  1955.  
  1956. Test code (attempting to modify content type):
  1957.  
  1958. console.info(document);
  1959. request = new XMLHttpRequest();
  1960. // "false" is only used for this test
  1961. request.open('GET', document.documentURI, false);
  1962. request.overrideMimeType('text/plain');
  1963. request.setRequestHeader('content-type', 'text/plain');
  1964. request.send();
  1965. //console.info(request.response);
  1966. console.info(`all headers: ${request.getAllResponseHeaders()}`);
  1967. console.info(`content-type header:
  1968. ${request.getResponseHeader('content-type')}`);
  1969. console.info(`content-type document: ${document.contentType}`);
  1970.  
  1971. */
  1972.  
  1973. /*
  1974. (function checkContentType() {
  1975.  
  1976. fetch(
  1977. document.documentURI,
  1978. {
  1979. method: 'GET',
  1980. headers: {
  1981. "Content-Type" : "text/plain",
  1982. },
  1983. }
  1984. )
  1985.  
  1986. .then((response) => {
  1987. console.info(response.headers.get('content-type'))
  1988. //console.info(response.arrayBuffer())
  1989. return response.arrayBuffer();
  1990. })
  1991.  
  1992. .then((data) => {
  1993. let decoder = new TextDecoder(document.characterSet);
  1994. let text = decoder.decode(data);
  1995.  
  1996. domParser = domParser = new DOMParser();
  1997.  
  1998. try {
  1999. if (JSON.parse(text)) {
  2000. jsonFile = JSON.parse(text);
  2001. if (jsonFile.version) {
  2002. if (jsonFile.version.toLowerCase().includes('jsonfeed.org')) {
  2003. renderJSONFeed(jsonFile);
  2004. extensionLoader(jsonFile.feed_url); // , jsonFile, 'JSON'
  2005. }
  2006. } else
  2007. if (jsonFile.generator) {
  2008. if (jsonFile.generator.toLowerCase().includes('statusnet') || // TODO Case insensitive
  2009. jsonFile.generator.toLowerCase().includes('gnu social')) {
  2010. renderActivityStream(jsonFile);
  2011. extensionLoader(); // null, jsonFile, 'ActivityStream'
  2012. }
  2013. }
  2014. }
  2015. } catch {
  2016. if (domParser.parseFromString(text, 'text/xml')) {
  2017. xmlFile = domParser.parseFromString(text, 'text/xml');
  2018. // errorPage is a good idea to promote Falkon
  2019. if (xmlFile.querySelector('feed')) {
  2020. renderXML(xmlFile, atomRules);
  2021. extensionLoader(xmlFile.querySelector('feed > link').href); // , xmlFile, 'Atom'
  2022. } else
  2023. if (xmlFile.querySelector('rss')) {
  2024. renderXML(xmlFile, rssRules);
  2025. extensionLoader(xmlFile.querySelector('channel > link').href); // , xmlFile, 'RSS'
  2026. }
  2027. }
  2028. }
  2029. });
  2030. })();
  2031. */
  2032.  
  2033. function renderActivityStream(jsonFile) {
  2034.  
  2035. let newDocument = createPage();
  2036.  
  2037. newDocument.title = jsonFile.title;
  2038. if (jsonFile.language) {
  2039. newDocument
  2040. .documentElement
  2041. .setAttribute('lang', jsonFile.language);
  2042. }
  2043.  
  2044. let feed = newDocument.createElement('div');
  2045. feed.id = 'feed';
  2046.  
  2047. let title = newDocument.createElement('div');
  2048. if (jsonFile.title) {
  2049. title.textContent = jsonFile.title;
  2050. } else {
  2051. title.textContent = defaultTitle;
  2052. }
  2053. title.id = 'title';
  2054. feed.append(title);
  2055.  
  2056. let subtitle = newDocument.createElement('div');
  2057. if (jsonFile.description) {
  2058. subtitle.textContent = jsonFile.description;
  2059. } else {
  2060. subtitle.textContent = defaultSubtitle;
  2061. }
  2062. subtitle.id = 'subtitle';
  2063. feed.append(subtitle);
  2064.  
  2065. let toc = newDocument.createElement('div');
  2066. toc.id = 'toc';
  2067. feed.append(toc);
  2068.  
  2069. let articles = newDocument.createElement('div');
  2070. articles.id = 'articles';
  2071. feed.append(articles);
  2072.  
  2073. if (jsonFile.items.length) {
  2074. for (const item of jsonFile.items) {
  2075. //for (let i = 0; i < jsonFile.items.length; i++) {
  2076. let index = jsonFile.items.indexOf(item) + 1;
  2077. let titleToc = newDocument.createElement('a');
  2078. // /questions/5002111/how-to-strip-html-tags-from-string-in-javascript
  2079. titleToc.textContent =
  2080. item
  2081. .content
  2082. .replace(/(<([^>]+)>)/gi, "");
  2083. //titleToc.textContent = item.actor.portablecontacts_net.preferredUsername;
  2084. titleToc.href = `#newspaper-oujs-${index}`;
  2085. toc.append(titleToc);
  2086.  
  2087. let entry = newDocument.createElement('div');
  2088. entry.className = 'entry';
  2089.  
  2090. let link = newDocument.createElement('a');
  2091. link.textContent =
  2092. item
  2093. .actor
  2094. .portablecontacts_net.preferredUsername;
  2095. link.href = item.url;
  2096. //link.id = item.id;
  2097. link.id = `newspaper-oujs-${index}`;
  2098.  
  2099. title = newDocument.createElement('div');
  2100. title.className = 'title';
  2101. title.append(link);
  2102. entry.append(title);
  2103.  
  2104. let date = newDocument.createElement('div');
  2105. date.className = 'published';
  2106. date.textContent = item.published;
  2107. entry.append(date);
  2108.  
  2109. if (item.image) {
  2110. let image = newDocument.createElement('img');
  2111. image.src = item.actor.image;
  2112. entry.append(image);
  2113. }
  2114.  
  2115. let text = newDocument.createElement('div');
  2116. text.className = 'content';
  2117. text.innerHTML = item.content;
  2118. entry.append(text);
  2119.  
  2120. articles.append(entry);
  2121.  
  2122. if (index > 19) {
  2123. break;
  2124. }
  2125. }
  2126. } else {
  2127. toc.remove();
  2128. articles
  2129. .insertAdjacentHTML('beforeend', htmlEmpty);
  2130. }
  2131.  
  2132. newDocument.body.append(feed);
  2133. newDocument = checkContentEmptiness(newDocument);
  2134. placeNewDocument(newDocument);
  2135.  
  2136. }
  2137.  
  2138. function renderJSONFeed(jsonFile) {
  2139.  
  2140. let feedMap = {
  2141. "title": "title",
  2142. "subtitle": "description",
  2143. "language" : "language",
  2144. "item": [{
  2145. "title" : "title",
  2146. "url" : ["url", "id"],
  2147. "content" : ["content_html", "content_text"],
  2148. "image" : "image",
  2149. "date" : ["date_published", "date_modified"],
  2150. "authors" : ["authors", "author"],
  2151. "tags" : "tags",
  2152. "language" : "language",
  2153. "id" : "id",
  2154. }],
  2155. "attachments": [{
  2156. "url" : "url",
  2157. "mime_type" : "mime_type",
  2158. "title" : "title",
  2159. "size_in_bytes" : "size_in_bytes",
  2160. "duration_in_seconds" : "duration_in_seconds"
  2161. }],
  2162. "homepage": "home_page_url",
  2163. "version": "version",
  2164. "url": "feed_url"
  2165. };
  2166.  
  2167. let newDocument = createPage();
  2168.  
  2169. /*
  2170. newDocument = domParser.parseFromString('<html></html>', 'text/html');
  2171. elements = ['html', 'head', 'body'];
  2172. //for (const element of elements) {
  2173. for (let i = 1; i < elements.length; i++) {
  2174. element = newDocument.createElement(elements[i]);
  2175. newDocument.documentElement.append(element);
  2176. }
  2177. */
  2178.  
  2179. newDocument.title = jsonFile.title;
  2180. if (jsonFile.language) {
  2181. newDocument
  2182. .documentElement
  2183. .setAttribute('lang', jsonFile.language);
  2184. }
  2185.  
  2186. let feed = newDocument.createElement('div');
  2187. feed.id = 'feed';
  2188.  
  2189. let title = newDocument.createElement('div');
  2190. if (jsonFile.title) {
  2191. title.textContent = jsonFile.title;
  2192. } else {
  2193. title.textContent = defaultTitle;
  2194. }
  2195. title.id = 'title';
  2196. feed.append(title);
  2197.  
  2198. let subtitle = newDocument.createElement('div');
  2199. if (jsonFile.description) {
  2200. subtitle.textContent = jsonFile.description;
  2201. } else {
  2202. subtitle.textContent = defaultSubtitle;
  2203. }
  2204. subtitle.id = 'subtitle';
  2205. feed.append(subtitle);
  2206.  
  2207. let toc = newDocument.createElement('div');
  2208. toc.id = 'toc';
  2209. feed.append(toc);
  2210.  
  2211. let articles = newDocument.createElement('div');
  2212. articles.id = 'articles';
  2213. feed.append(articles);
  2214.  
  2215. /* FIXME
  2216. These couple of for-loops don't work
  2217. Failing part: jsonFile.cellOfArray
  2218. Uncaught (in promise) TypeError: Cannot read property '0' of undefined
  2219.  
  2220. tags = ['title', 'description'];
  2221. for (const tag of tags) {
  2222. element = newDocument.createElement('div');
  2223. element.textContent = jsonFile.tag;
  2224. element.id = tag;
  2225. feed.append(element);
  2226. }
  2227.  
  2228. elements = ['title', 'description'];
  2229. for (let i = 0; i < elements.length; i++) {
  2230.  
  2231. element = newDocument.createElement('div');
  2232. element.textContent = jsonFile.elements[i];
  2233. element.id = elements[i];
  2234. feed.append(element);
  2235.  
  2236. }
  2237. */
  2238.  
  2239. if (jsonFile.items.length) {
  2240. for (const item of jsonFile.items) {
  2241. //for (let i = 0; i < jsonFile.items.length; i++) {
  2242. let index = jsonFile.items.indexOf(item) + 1;
  2243. let titleToc = newDocument.createElement('a');
  2244. if (item.title) {
  2245. titleToc.textContent = item.title;
  2246. } else if (item.content_text) {
  2247. titleToc.textContent = item.content_text;
  2248. } else if (item.content_html) {
  2249. titleToc.textContent =
  2250. item
  2251. .content_html
  2252. .replace(/(<([^>]+)>)/gi, "");
  2253. } else {
  2254. titleToc.textContent = '*** No Title ***';
  2255. }
  2256. titleToc.href = `#newspaper-oujs-${index}`;
  2257. toc.append(titleToc);
  2258.  
  2259. let entry = newDocument.createElement('div');
  2260. entry.className = 'entry';
  2261.  
  2262. let link = newDocument.createElement('a');
  2263. if (item.title) {
  2264. link.textContent = item.title;
  2265. } else {
  2266. link.textContent = 'No Title';
  2267. }
  2268. link.href = item.url;
  2269. //link.id = item.id;
  2270. link.id = `newspaper-oujs-${index}`;
  2271.  
  2272. title = newDocument.createElement('div');
  2273. title.className = 'title';
  2274. title.append(link);
  2275. entry.append(title);
  2276.  
  2277. let date = newDocument.createElement('div');
  2278. date.className = 'published';
  2279. date.textContent = item.date_published; // date_modified
  2280. entry.append(date);
  2281.  
  2282. // TODO Set it as enclosure unless content is not html (i.e. is text)
  2283. if (item.image) {
  2284. let image = newDocument.createElement('img');
  2285. image.src = item.image;
  2286. entry.append(image);
  2287. }
  2288.  
  2289. let text = newDocument.createElement('div');
  2290. text.className = 'content';
  2291. text.innerHTML = item.content_html; // content_text
  2292. entry.append(text);
  2293.  
  2294. articles.append(entry);
  2295.  
  2296. if (index > 19) {
  2297. break;
  2298. }
  2299. }
  2300. } else {
  2301. toc.remove();
  2302. articles.insertAdjacentHTML('beforeend', htmlEmpty);
  2303. }
  2304.  
  2305. newDocument.body.append(feed);
  2306. newDocument = checkContentEmptiness(newDocument);
  2307. placeNewDocument(newDocument);
  2308.  
  2309. }
  2310.  
  2311. function renderXML(xmlFile, xmlRules) {
  2312.  
  2313. let newDocument = createPage();
  2314.  
  2315. newDocument.title =
  2316. xmlFile
  2317. .querySelector(xmlRules.feedTitlePage)
  2318. .textContent;
  2319. if (xmlFile.querySelector(xmlRules.feedLanguage)) {
  2320. let language;
  2321. if (xmlFile.querySelector(xmlRules.feedLanguage).getAttribute('xml:lang')) { // Atom
  2322. language =
  2323. xmlFile
  2324. .querySelector(xmlRules.feedLanguage)
  2325. .getAttribute('xml:lang');
  2326. } else {
  2327. language =
  2328. xmlFile
  2329. .querySelector(xmlRules.feedLanguage)
  2330. .textContent;
  2331. }
  2332. newDocument.documentElement.setAttribute('lang', language);
  2333. }
  2334.  
  2335. let feed = newDocument.createElement('div');
  2336. feed.id = 'feed';
  2337.  
  2338. let title = newDocument.createElement('div');
  2339. if (xmlFile.querySelector(xmlRules.feedTitlePage)) {
  2340. title.textContent =
  2341. xmlFile
  2342. .querySelector(xmlRules.feedTitlePage)
  2343. .textContent;
  2344. }
  2345. if (!title.textContent) {
  2346. title.textContent = defaultTitle;
  2347. }
  2348. title.id = 'title';
  2349. feed.append(title);
  2350.  
  2351. let subtitle = newDocument.createElement('div');
  2352. if (xmlFile.querySelector(xmlRules.feedSubtitle)) {
  2353. subtitle.textContent =
  2354. xmlFile
  2355. .querySelector(xmlRules.feedSubtitle)
  2356. .textContent
  2357. .replace(/(<([^>]+)>)/gi, "");
  2358. }
  2359. if (!subtitle.textContent) {
  2360. subtitle.textContent = defaultSubtitle;
  2361. }
  2362. subtitle.id = 'subtitle';
  2363. feed.append(subtitle);
  2364.  
  2365. let toc = newDocument.createElement('ol');
  2366. toc.id = 'toc';
  2367. feed.append(toc);
  2368.  
  2369. let articles = newDocument.createElement('div');
  2370. articles.id = 'articles';
  2371. feed.append(articles);
  2372.  
  2373. if (xmlFile.querySelectorAll(xmlRules.feedItem).length) {
  2374. for (const item of xmlFile.querySelectorAll(xmlRules.feedItem)) {
  2375. let index = Array.from(xmlFile.querySelectorAll(xmlRules.feedItem)).indexOf(item) + 1;
  2376. let titleToc = newDocument.createElement('a');
  2377. // /questions/5002111/how-to-strip-html-tags-from-string-in-javascript
  2378. if (item.querySelector(xmlRules.feedItemTitle) &&
  2379. item.querySelector(xmlRules.feedItemTitle).textContent.length) { // FIXME there are two of the same
  2380. titleToc.textContent =
  2381. item
  2382. .querySelector(xmlRules.feedItemTitle)
  2383. .textContent;
  2384. //titleToc.textContent =
  2385. titleToc.innerHTML =
  2386. titleToc
  2387. .textContent
  2388. .replace(/(<([^>]+)>)/gi, "");
  2389. } else
  2390. if (item.querySelector(xmlRules.feedItemContent) &&
  2391. item.querySelector(xmlRules.feedItemContent).textContent) {
  2392. titleToc.textContent =
  2393. item
  2394. .querySelector(xmlRules.feedItemContent)
  2395. .textContent
  2396. .replace(/(<([^>]+)>)/gi, "");
  2397. } else {
  2398. titleToc.textContent = '*** No Title ***';
  2399. }
  2400. titleToc.href = `#newspaper-oujs-${index}`;
  2401. let liElement = newDocument.createElement('li');
  2402. liElement.append(titleToc)
  2403. toc.append(liElement);
  2404.  
  2405. let entry = newDocument.createElement('div');
  2406. entry.className = 'entry';
  2407.  
  2408. let link = newDocument.createElement('a');
  2409. link.id = `newspaper-oujs-${index}`;
  2410. if (item.querySelector(xmlRules.feedItemTitle) &&
  2411. item.querySelector(xmlRules.feedItemTitle).textContent.length) { // FIXME there are two of the same
  2412. //link.textContent =
  2413. link.innerHTML =
  2414. item
  2415. .querySelector(xmlRules.feedItemTitle)
  2416. .textContent
  2417. .replace(/(<([^>]+)>)/gi, "");
  2418. } else {
  2419. link.textContent = 'No Title';
  2420. }
  2421.  
  2422. if (item.querySelector(xmlRules.feedItemLink) &&
  2423. item.querySelector(xmlRules.feedItemLink).textContent.length) {
  2424. // rss
  2425. link.href =
  2426. item
  2427. .querySelector(xmlRules.feedItemLink)
  2428. .textContent;
  2429. } else
  2430. if (item.querySelector(xmlRules.feedItemLink) &&
  2431. item.querySelector(xmlRules.feedItemLink).getAttribute('href')) {
  2432. // atom
  2433. if (item.querySelector(xmlRules.feedItemLink + "[rel='alternate']")) {
  2434. link.href =
  2435. item
  2436. .querySelector(xmlRules.feedItemLink + "[rel='alternate']")
  2437. .getAttribute('href');
  2438. } else {
  2439. link.href =
  2440. item
  2441. .querySelector(xmlRules.feedItemLink)
  2442. .getAttribute('href');
  2443. }
  2444. } else {
  2445. link.href = './?ref=feed';
  2446. }
  2447.  
  2448. title = newDocument.createElement('div');
  2449. title.className = 'title';
  2450. title.append(link);
  2451. entry.append(title);
  2452.  
  2453. if (item.querySelector(xmlRules.feedItemDate)) {
  2454. let date = newDocument.createElement('div');
  2455. date.className = 'published';
  2456. date.textContent =
  2457. item
  2458. .querySelector(xmlRules.feedItemDate)
  2459. .textContent;
  2460. entry.append(date);
  2461. }
  2462.  
  2463. if (item.querySelector(xmlRules.feedItemContent)) {
  2464. let text = newDocument.createElement('div');
  2465. text.className = 'content content-text';
  2466. text.innerHTML =
  2467. item
  2468. .querySelector(xmlRules.feedItemContent)
  2469. .textContent;
  2470. if (/<\/?[a-z][\s\S]*>/i.test(text.innerHTML)) {
  2471. text.className = 'content';
  2472. }
  2473. entry.append(text);
  2474. }
  2475.  
  2476. if (item.querySelector(xmlRules.feedItemSummary)) {
  2477. let text = newDocument.createElement('div');
  2478. text.className = 'content content-text';
  2479. text.innerHTML =
  2480. item
  2481. .querySelector(xmlRules.feedItemSummary)
  2482. .textContent;
  2483. if (/<\/?[a-z][\s\S]*>/i.test(text.innerHTML)) {
  2484. text.className = 'content';
  2485. }
  2486. entry.append(text);
  2487. }
  2488.  
  2489. if (item.querySelector(xmlRules.feedItemEnclosure)) {
  2490. let enclosures = newDocument.createElement('div');
  2491. enclosures.className = 'enclosures';
  2492. enclosures.title = 'Right-click and Save link as…';
  2493. for (const enclosure of item.querySelectorAll(xmlRules.feedItemEnclosure)) {
  2494. let file = newDocument.createElement('div');
  2495. file.className = 'enclosure';
  2496. enclosures.append(file);
  2497. let icon = newDocument.createElement('span');
  2498. let documentType;
  2499. if (enclosure.getAttribute('type')) {
  2500. documentType = enclosure.getAttribute('type').split('/')[0];
  2501. } else {
  2502. documentType = '';
  2503. }
  2504. icon.setAttribute('icon', documentType);
  2505. file.append(icon);
  2506. let link = newDocument.createElement('a');
  2507. let enclosureBase, enclosureUrl;
  2508. if (enclosure.getAttribute('url')) { // rss
  2509. enclosureUrl = enclosure.getAttribute('url');
  2510. enclosureBase = enclosureUrl.split('/').pop();
  2511. } else
  2512. if (enclosure.getAttribute('href')) { // atom https://tomosnowbug.hatenablog.com/feed
  2513. enclosureUrl = enclosure.getAttribute('href');
  2514. enclosureBase = enclosureUrl.split('/').pop();
  2515. }
  2516. link.textContent = enclosureBase;
  2517. link.download = enclosureBase;
  2518. link.href = enclosureUrl;
  2519. file.append(link);
  2520. let size = newDocument.createElement('span');
  2521. // class="size" is needed for function transformFileSize
  2522. size.className = `size ${documentType}`;
  2523. size.textContent = `${enclosure.getAttribute('length')}`;
  2524. file.append(size);
  2525. }
  2526. entry.append(enclosures);
  2527. }
  2528.  
  2529. articles.append(entry);
  2530.  
  2531. if (index > 19) {
  2532. break;
  2533. }
  2534. }
  2535. }
  2536.  
  2537. newDocument.body.append(feed);
  2538. newDocument = checkContentEmptiness(newDocument);
  2539. placeNewDocument(newDocument);
  2540.  
  2541. }
  2542.  
  2543. function createPage() {
  2544. let domParser = new DOMParser();
  2545. let newDocument =
  2546. domParser
  2547. .parseFromString('', 'text/html');
  2548. return newDocument;
  2549. }
  2550.  
  2551. function checkContentEmptiness(newDocument) {
  2552. if (newDocument.getElementsByClassName('entry').length == 1) {
  2553. newDocument.getElementById('toc').remove();
  2554. // NOTE https://dbpedia.org/data/Searx.atom
  2555. if (!newDocument.getElementById('articles').outerText) {
  2556. newDocument.getElementsByClassName('entry')[0].remove();
  2557. // Should removed data be added to the htmlEmpty message?
  2558. // newDocument.getElementsByClassName('entry')[0].outerHTML;
  2559. newDocument.getElementById('articles')
  2560. .insertAdjacentHTML('beforeend', htmlEmpty);
  2561. }
  2562. } else
  2563. if (newDocument.getElementsByClassName('entry').length == 0) {
  2564. newDocument.getElementById('toc').remove();
  2565. newDocument.getElementById('articles')
  2566. .insertAdjacentHTML('beforeend', htmlEmpty);
  2567. }
  2568. return newDocument;
  2569. }
  2570.  
  2571. // Possible solution for the document.contentType issue
  2572. // /questions/40201137/i-need-to-read-a-text-file-from-a-javascript
  2573. // https://openuserjs.org/garage/Loading_functions_after_document_is_replaced_by_new_document
  2574. function placeNewDocument(newDocument) {
  2575.  
  2576. // Failed attempt to force web browser to treat file as HTML
  2577. var meta = newDocument.createElement('meta');
  2578. meta.setAttribute('http-equiv', 'Content-Type');
  2579. meta.setAttribute('content', 'text/html; charset=utf-8');
  2580.  
  2581. // Append the <meta> element to the <head> element
  2582. newDocument.head.appendChild(meta);
  2583. // Failed attempt to force web browser to treat file as HTML
  2584.  
  2585. newDocument = purgeStylesheets(newDocument); // FIXME
  2586. newDocument = setCssStylesheet(newDocument); // FIXME
  2587. if (xmlStylesheet) {
  2588. stylesheetMessage(newDocument)
  2589. }
  2590. //var newDoc = document.adoptNode(newDoc.documentElement, true);
  2591. let insertDocument =
  2592. document
  2593. .importNode(
  2594. newDocument
  2595. .documentElement,
  2596. true
  2597. );
  2598.  
  2599. let removeDocument =
  2600. document
  2601. .documentElement;
  2602.  
  2603. document
  2604. .replaceChild(
  2605. insertDocument,
  2606. removeDocument
  2607. );
  2608. }
  2609.  
  2610. // TODO
  2611. // The following events don't work on some pages: https://momi.ca/feed.xml
  2612. // Perhaps, confine them to some type of window.onload = (event) => { CODE }
  2613. function extensionLoader(url, date, type) {
  2614. // /questions/381744/is-there-anyway-to-change-the-content-type-of-an-xml-document-in-the-xml-docume
  2615. // /questions/23034283/is-it-possible-to-use-htmls-queryselector-to-select-by-xlink-attribute-in-an
  2616. //purgeStylesheets(); // FIXME
  2617. //setCssStylesheet(); // FIXME
  2618. scrollDown();
  2619. decorateEntry();
  2620. linksBar();
  2621. follow(url);
  2622. subToMe(url);
  2623. direction();
  2624. //setBanner();
  2625. truncateToc();
  2626. feedInfo(date, type);
  2627. formatDate();
  2628. aboutPage();
  2629. donatePage();
  2630. settleFilters();
  2631. statusBar();
  2632. mailTo();
  2633. //geckoHelp();
  2634. setNonceUponCSP();
  2635. transformFileSize();
  2636. trimEnclosureFilename();
  2637. }
  2638.  
  2639. function trimEnclosureFilename() {
  2640. for (const fileName of document.querySelectorAll('.enclosure > a')) {
  2641. if (fileName.textContent.includes('?')) {
  2642. //let newfileName;
  2643. //newfileName = fileName.href.split('/').pop();
  2644. //newfileName = newfileName.substring(0, newfileName.indexOf('?'));
  2645. let newfileName = fileName.textContent.substring(0, fileName.textContent.indexOf('?'));
  2646. fileName.textContent = newfileName;
  2647. fileName.download = newfileName;
  2648. fileName.href = newfileName;
  2649. }
  2650. }
  2651. }
  2652.  
  2653. // questions/10420352/converting-file-size-in-bytes-to-human-readable-string
  2654. function transformFileSize() {
  2655. for (const item of document.querySelectorAll('.size')) {
  2656. size = item.textContent;
  2657. var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
  2658. item.textContent = (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
  2659. if (item.textContent == '0 B' ||
  2660. item.textContent == 'NaN undefined') {
  2661. item.textContent = null;
  2662. }
  2663. }
  2664. }
  2665.  
  2666. function truncateToc() {
  2667. for (const titleToc of document.querySelectorAll('#toc > li > a')) {
  2668. if (titleToc.textContent.length > 70) {
  2669. titleToc.title = titleToc.textContent;
  2670. titleToc.textContent =
  2671. titleToc
  2672. .textContent
  2673. .substring(0, 70) + ' […]';
  2674. }
  2675. }
  2676. }
  2677.  
  2678. function stylesheetMessage(document) {
  2679. let aElement = document.createElement('a');
  2680. document.body.prepend(aElement);
  2681. //aElement.href = location.href.substring(0, location.href.indexOf('#')) + location.search + '?streamburner=0';
  2682. let url = new URL(location.href);
  2683. url.searchParams.set('streamburner','0');
  2684. aElement.href = url.href;
  2685. aElement.innerHTML = 'View this feed with its own stylesheet'; // This feed has its own stylesheet
  2686. aElement.id = 'xslt-message';
  2687. }
  2688.  
  2689. // FIXME https://lw1.at/en/postfeed.xml
  2690. function getDateXML(xmlFile, xmlRules) {
  2691. if (xmlFile.querySelector(xmlRules.feedDate)) {
  2692. date = xmlFile.querySelector(xmlRules.feedDate).textContent;
  2693. } else
  2694. if (xmlFile.querySelector(xmlRules.feedItemDate)) {
  2695. date = xmlFile.querySelector(xmlRules.feedItemDate).textContent;
  2696. } else {
  2697. return null;
  2698. }
  2699. return date;
  2700. }
  2701.  
  2702. function setBanner() {
  2703. let aElement = document.createElement('a');
  2704. aElement.href = 'https://www.falkon.org/?ref=newspaper';
  2705. aElement.id = 'feed-banner';
  2706. document.body.append(aElement);
  2707. aElement.insertAdjacentHTML('beforeend', banner);
  2708. }
  2709.  
  2710. function feedInfo(date, type) {
  2711. let divElement = document.createElement('div');
  2712. divElement.id = 'feed-info';
  2713. // TODO Add "generated by" and also add to meta
  2714. if (date) {
  2715. divElement.innerHTML = `${type} syndication updated at <span class="published">${date}</span>`;
  2716. } else {
  2717. divElement.innerHTML = `${type} syndication`;
  2718. }
  2719. document.body.append(divElement);
  2720. }
  2721.  
  2722. function decorateEntry() {
  2723. for (const entry of document.querySelectorAll('.entry')) {
  2724. let divElement = document.createElement('div');
  2725. divElement.className = 'decor';
  2726. entry.parentNode.insertBefore(divElement, entry.nextSibling);
  2727. }
  2728. }
  2729.  
  2730. function formatDate() {
  2731. let elements = ['.published', '.updated'];
  2732. for (let i = 0; i < elements.length; i++) {
  2733. for (const element of document.querySelectorAll(elements[i])) {
  2734. let date = new Date(element.textContent);
  2735. //element.textContent = date.toDateString();
  2736. element.textContent = date.toLocaleString();
  2737. }
  2738. }
  2739. }
  2740.  
  2741. function setCssStylesheet(document) {
  2742. let cssStylesheet = document.createElement('style');
  2743. document.head.append(cssStylesheet);
  2744. //stylesheet.setAttribute('crossorigin', 'anonymous');
  2745. cssStylesheet.type = 'text/css';
  2746. cssStylesheet.id = namespace;
  2747.  
  2748. // TODO
  2749. // Set direction by the letters of the initial set of words
  2750. // if no language specified.
  2751. if (rtlLocales.includes(document.documentElement.getAttribute('lang'))) {
  2752. cssFileBase = cssFileLTR + cssFileRTL;
  2753. } else {
  2754. cssFileBase = cssFileLTR;
  2755. }
  2756.  
  2757. cssStylesheet.textContent = cssFileBase;
  2758. //cssStylesheet.setAttribute('unsafe-hashes', null);
  2759. return document; // FIXME
  2760. }
  2761.  
  2762. // FIXME
  2763. // Blocked due to server policy
  2764. // https://archlinux.org/feeds/packages/
  2765. // https://www.openstreetmap.org/traces/rss
  2766.  
  2767. // NOTE
  2768. // Consider https://openuserjs.org/libs/BigTSDMB/setStyle
  2769. function setNonceUponCSP() {
  2770. window.addEventListener("securitypolicyviolation", (e) => {
  2771. //let message = e.originalPolicy;
  2772. //messageTruncated = message.substring(message.indexOf("'nonce-") + 7);
  2773. //let nonceValue = messageTruncated.substring(0, messageTruncated.indexOf("'"));
  2774. cssStylesheet = document.getElementById(namespace);
  2775. let nonceValue = e.originalPolicy.match(/'nonce-(.*?)'/)[1];
  2776. cssStylesheet.setAttribute('nonce', nonceValue);
  2777. //let hashValue = e.originalPolicy.match(/sha256-[A-Za-z0-9+/=]+/)[0];
  2778. //cssStylesheet.setAttribute('unsafe-inline', hashValue);
  2779.  
  2780. // Reload stylesheet
  2781. textContent = cssStylesheet.textContent;
  2782. cssStylesheet.textContent = null;
  2783. cssStylesheet.textContent = textContent;
  2784. }, { passive : true, });
  2785. }
  2786.  
  2787. // NOTE
  2788. // https://momi.ca/feed.xml
  2789. // https://momi.ca/css/base.css
  2790. // https://momi.ca/css/dark.css
  2791. function purgeStylesheets(document) {
  2792. for (const style of document.querySelectorAll('link[rel="stylesheet"]')) {
  2793. style.remove();
  2794. }
  2795. return document; // FIXME
  2796. }
  2797.  
  2798. function follow(url) {
  2799. let flw = document.querySelector('#follow');
  2800. if (url) {
  2801. flw.href = `feed:${url}`;
  2802. } else {
  2803. flw.href = `feed:${location.href}`;
  2804. }
  2805. /*
  2806. flw.addEventListener ("click", function() {
  2807. if (url) {
  2808. location.href = `feed:${url}`;
  2809. } else {
  2810. location.href = `feed:${location.href}`;
  2811. }
  2812. });
  2813. */
  2814. }
  2815.  
  2816. function subToMe(url) {
  2817. let stm = document.querySelector('#subtome');
  2818. //stm.href = `https://www.subtome.com/#/subscribe?feeds=${url}`;
  2819. stm.addEventListener ("click", function() {
  2820. (function(btn){
  2821. let z = document.createElement("script");
  2822. document.subtomeBtn = btn;
  2823. z.src = "https://www.subtome.com/load.js";
  2824. document.body.appendChild(z);
  2825. })(stm);
  2826. return false;
  2827. });
  2828. }
  2829.  
  2830. function direction() {
  2831. let dir = document.querySelector('#direction');
  2832. dir.addEventListener ("click", function() {
  2833. if (document.dir == 'ltr' || !document.dir) {
  2834. document.dir = 'rtl';
  2835. dir.title = 'Set Left-to-right direction';
  2836. } else {
  2837. document.dir = 'ltr';
  2838. dir.title = 'Set Right-to-left direction';
  2839. }
  2840. });
  2841. }
  2842.  
  2843. function linksBar() {
  2844. let divElement = document.createElement('div');
  2845. divElement.innerHTML = htmlBar;
  2846. let subtitle = document.querySelector('#subtitle');
  2847. subtitle.parentNode.insertBefore(divElement, subtitle.nextSibling);
  2848. }
  2849.  
  2850. function aboutPage() {
  2851. document.body.insertAdjacentHTML('beforeend', htmlAbout);
  2852. let btn = document.querySelector('#about-newspaper');
  2853. btn.addEventListener ("click", function() {
  2854. document
  2855. .querySelector('#about-feed')
  2856. .style
  2857. .display = 'block';
  2858. });
  2859. // freedos at sourceforge won't allow inline ondblclick
  2860. // ondblclick="this.style.display = &quot;none&quot;"
  2861. let dia = document.querySelector('#about-feed');
  2862. dia.addEventListener ("dblclick", function() {
  2863. document
  2864. .querySelector('#about-feed')
  2865. .style
  2866. .display = 'none';
  2867. });
  2868. }
  2869.  
  2870. function donatePage() {
  2871. document.body.insertAdjacentHTML('beforeend', htmlDonate);
  2872. let btn = document.querySelector('#donate-newspaper');
  2873. btn.addEventListener ("click", function() {
  2874. document
  2875. .querySelector('#about-donate')
  2876. .style
  2877. .display = 'block';
  2878. });
  2879. // freedos at sourceforge won't allow inline ondblclick
  2880. // ondblclick="this.style.display = &quot;none&quot;"
  2881. let dia = document.querySelector('#about-donate');
  2882. dia.addEventListener ("dblclick", function() {
  2883. document
  2884. .querySelector('#about-donate')
  2885. .style
  2886. .display = 'none';
  2887. });
  2888. }
  2889.  
  2890. // TODO Use this function and incorporate it with function mailTo()
  2891. function emptyPage() {
  2892. document
  2893. .querySelector('#articles')
  2894. .insertAdjacentHTML('beforeend', htmlEmpty);
  2895. }
  2896.  
  2897. function mailTo() {
  2898. // Add link with emails
  2899. if (document.querySelector('#empty-feed')) {
  2900. let ele, eml, hyl;
  2901. ele = document.querySelector('#empty-feed');
  2902. aElement = document.createElement('a');
  2903. aElement.id = 'email-link';
  2904. aElement.textContent = 'Contact Webmaster';
  2905. let una = ['admin', 'contact', 'form', 'hello',
  2906. 'hi', 'info', 'office', 'pr', 'press',
  2907. 'support', 'web', 'webmaster',];
  2908. hyl = `${una[0]}@${location.hostname},`
  2909. for (let i = 1; i < una.length; i++) {
  2910. hyl += `${una[i]}@${location.hostname},`;
  2911. }
  2912. //hyl = hyl.slice(0. -1);
  2913. aElement.href = `mailto:?subject=Web Feeds for ${location.hostname}&body=Hello,%0D%0A%0D%0AThis is an automated message.%0D%0A%0D%0APlease add Web Feeds (aka RSS) to your website, so that people can easily receive updates from your website.%0D%0A%0D%0AIf you do not know what a Web Feed is or how you can benefit from it, visit aboutfeeds.com to read about it.%0D%0A%0D%0AKind regards,%0D%0A&bcc=${hyl}`
  2914. ele.append(aElement);
  2915. }
  2916. }
  2917.  
  2918. function settleFilters() {
  2919. // Hide all links to software that are not of news
  2920. for (const element of document.querySelectorAll('#software a')) {
  2921. //if (element.className != 'news') {
  2922. if (!element.className.includes('news')) {
  2923. element.classList.toggle('hide');
  2924. }
  2925. }
  2926.  
  2927. // Create toggle mechanism
  2928. for (const element of document.querySelectorAll('.filter')) { // #filter > span
  2929. if (element.id != 'news') {
  2930. element.classList.toggle('grey');
  2931. }
  2932. element.addEventListener ("click", function() {
  2933. element.classList.toggle('grey');
  2934. for (const span of document.querySelectorAll(`.${element.id}`)) {
  2935. span.classList.toggle('hide');
  2936. }
  2937. });
  2938. }
  2939. }
  2940.  
  2941. function statusBar() {
  2942. // Display entry title in status bar
  2943. for (const element of document.querySelectorAll('.entry')) {
  2944. element.addEventListener ("mouseover", function() {
  2945. infoSquare(`${this.querySelector('.title > a').textContent}`);
  2946. });
  2947. }
  2948. // Prepare links to be used with status bar
  2949. for (const element of document.querySelectorAll('#links-bar > a')) {
  2950. element.addEventListener ("mouseover", function() {
  2951. //infoSquare(this.title);
  2952. if (this.title) {
  2953. this.setAttribute('info', this.title);
  2954. this.removeAttribute('title');
  2955. }
  2956. //infoSquare(`<b>Info:&nbsp</b> ${this.getAttribute('info')}`);
  2957. infoSquare(`${this.getAttribute('info')}`);
  2958. });
  2959. }
  2960. // Remove status bar
  2961. for (const element of document.querySelectorAll('#links-bar')) {
  2962. //for (const element of document.querySelectorAll('#links-bar > a')) {
  2963. element.addEventListener ("mouseleave", function() { // mouseout
  2964. if (document.querySelector('#info-square')) {
  2965. document.querySelector('#info-square').remove();
  2966. }
  2967. });
  2968. }
  2969. }
  2970.  
  2971. function floatBar() {
  2972. const cssStylesheet = document.getElementById(namespace);
  2973. if (document.querySelector('#links-bar')) {
  2974. document.querySelector('#links-bar').style.display = 'auto';
  2975. }
  2976. if (window.pageYOffset > 300) { // TODO when first entry is focused
  2977. cssStylesheet.textContent = cssFileBase + cssFileBar;
  2978. topButton();
  2979. } else {
  2980. cssStylesheet.textContent = cssFileBase;
  2981. document.querySelector('#links-bar').removeAttribute('style');
  2982. let elements = ['#top-navigation-button']; // '#close-bar'
  2983. for (let i = 0; i < elements.length; i++) {
  2984. if (document.querySelector(elements[i])) {
  2985. document.querySelector(elements[i]).remove();
  2986. }
  2987. }
  2988. }
  2989. }
  2990.  
  2991. function scrollDown() {
  2992. let url;
  2993. // Create toolbar and a button when scrolling down
  2994. document.addEventListener ("scroll", function() {
  2995. if (location.href == url ||
  2996. window.pageYOffset < 300) {
  2997. floatBar();
  2998. }
  2999. if (location.href != url) {
  3000. url = location.href;
  3001. }
  3002. });
  3003. }
  3004. /*
  3005. window.addEventListener ("hashchange", function() {
  3006. document.querySelector('#links-bar').style.display = 'none';
  3007. });
  3008. */
  3009.  
  3010. /*
  3011. window.addEventListener ("scroll", function() {
  3012. const elementTitle = document.querySelector('#title');
  3013. const elementStyle = document.getElementById(namespace);
  3014. if (isInViewport(elementTitle)) {
  3015. elementStyle.textContent = stylesheetBase;
  3016. } else {
  3017. elementStyle.textContent = stylesheetBase + stylesheetBar;
  3018. }
  3019. });
  3020. */
  3021.  
  3022. /*
  3023. // FIXME This is a safer fashion to do this task,
  3024. // specifically against server policy.
  3025. // NOTE Element is created, albeit without type="text/css",
  3026. // but no change applied, not even with !important
  3027. window.addEventListener ("scroll", function() {
  3028. const elementTitle = document.querySelector('#subtitle');
  3029. let elementStyle = document.querySelector('#bar');
  3030. if (isInViewport(elementTitle) && elementStyle) {
  3031. if (elementStyle) {
  3032. elementStyle.remove();
  3033. }
  3034. } else if (!elementStyle) {
  3035. elementStyle = document.createElement('style');
  3036. document.head.append(elementStyle);
  3037. elementStyle.textContent = stylesheetBar;
  3038. elementStyle.type = 'text/css';
  3039. elementStyle.id = 'bar';
  3040. }
  3041. });
  3042. */
  3043.  
  3044. function imageData() {
  3045. /* TODO handle loading of images by saving bandwidth
  3046. document.body.addEventListener ("mouseover", function(e) {
  3047. if (e.target && e.target.nodeName == "IMG" && !e.target.src) {
  3048. console.log('DELEGATED');
  3049. source = e.target.getAttribute('src-data');
  3050. e.target.removeAttribute('src-data');
  3051. e.target.src = source;
  3052. }
  3053. });
  3054.  
  3055. for (const image of document.querySelectorAll('img')) {
  3056. image.addEventListener('mouseover', loadImage(image));
  3057. //image.onmouseover = () => {
  3058. // image.removeEventListener('mouseover',loadImage(image));
  3059. //}
  3060. }
  3061. */
  3062.  
  3063. /*
  3064. for (const image of document.querySelectorAll('img')) {
  3065. image.addEventListener('focus',event => {
  3066. //image.addEventListener('mouseover',event => {
  3067. if (image.getAttribute('src-data')) {
  3068. //toggleAttribute(image);
  3069. source = image.getAttribute('src-data');
  3070. image.removeAttribute('src-data');
  3071. image.src = source;
  3072. }
  3073. }, {passive: true});
  3074. image.onmouseover = () => {
  3075. if (image.getAttribute('src-data')) {
  3076. //toggleAttribute(image);
  3077. source = image.getAttribute('src-data');
  3078. image.removeAttribute('src-data');
  3079. image.src = source;
  3080. }
  3081. };
  3082. }
  3083. */
  3084. }
  3085.  
  3086. // Create status bar
  3087. function infoSquare(text) {
  3088. if (document.querySelector('#info-square')) {
  3089. document.querySelector('#info-square').remove();
  3090. }
  3091. let divElement = document.createElement('div');
  3092. divElement.id = 'info-square';
  3093. divElement.innerHTML = text; // codeberg feeds
  3094. //divElement.textContent = text;
  3095. document.body.append(divElement);
  3096. }
  3097.  
  3098. function topButton() {
  3099. if (document.querySelector('#top-navigation-button')) return;
  3100. let divElement = document.createElement('div');
  3101. divElement.id = 'top-navigation-button';
  3102. divElement.innerHTML = '^'; // Better than ⬆️
  3103. // behaviour
  3104. divElement.onclick = () => {
  3105. window.scrollTo({ top: 0 });
  3106. };
  3107. document.body.append(divElement);
  3108. }
  3109.  
  3110. // /questions/123999/how-can-i-tell-if-a-dom-element-is-visible-in-the-current-viewport
  3111. // https://www.javascripttutorial.net/dom/css/check-if-an-element-is-visible-in-the-viewport/
  3112. function isInViewport(element) {
  3113. const rect = element.getBoundingClientRect();
  3114. return (
  3115. rect.top >= 0 &&
  3116. rect.left >= 0 &&
  3117. rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
  3118. rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  3119. );
  3120. }
  3121.  
  3122. function loadImage(image) {
  3123. //image.removeEventListener('mouseover',loadImage(image));
  3124. console.log('onmouseover');
  3125. let source = image.getAttribute('src-data');
  3126. image.removeAttribute('src-data');
  3127. image.src = source;
  3128. }
  3129.  
  3130. /* TODO handle loading of images by saving bandwidth
  3131. function toggleAttribute(image) {
  3132. source = image.getAttribute('src-data');
  3133. image.removeAttribute('src-data');
  3134. image.src = source;
  3135. }
  3136. */
  3137.  
  3138. /* TODO handle loading of images by saving bandwidth
  3139. for (const image of document.querySelectorAll('img')) {
  3140. //source = image.src;
  3141. //image.removeAttribute('src');
  3142. //image.setAttribute('src-data', source);
  3143. image.setAttribute('src-data', image.src);
  3144. image.removeAttribute('src');
  3145. }
  3146. */
  3147.  
  3148. /* TODO remove or parse <aside> or refer to XSLT to parse it as HTML
  3149. // https://web.dev/feed.xml
  3150. // /questions/9848465/js-remove-a-tag-without-deleting-content
  3151. for (const aside of document.querySelectorAll('aside')) {
  3152. aside.remove();
  3153. }
  3154. */
  3155.  
  3156. function queryByXPath(queries) {
  3157. let result, i = 0;
  3158. do {
  3159. result = document.evaluate(
  3160. queries[i], document,
  3161. null, XPathResult.STRING_TYPE);
  3162. i = i + 1;
  3163. result = result.stringValue;
  3164. } while (!result && i < queries.length);
  3165. return result;
  3166. }
  3167.  
  3168. function pageLoader() {
  3169.  
  3170. let
  3171. newDocument, insertDocument, removeDocument;
  3172.  
  3173. // /questions/6464592/how-to-align-entire-html-body-to-the-center
  3174. const
  3175. loadPage = `
  3176. <html>
  3177. <head>
  3178. <title>Newspaper</title>
  3179. <style>
  3180. html, body {
  3181. height: 100%; }
  3182.  
  3183. html {
  3184. display: table;
  3185. margin: auto; }
  3186.  
  3187. body {
  3188. display: table-cell;
  3189. vertical-align:middle;
  3190. background-color: #f1f1f1;
  3191. font-family: "Helvetica Neue", Helvetica,Arial, sans-serif;
  3192. cursor:default;
  3193. user-select: none;
  3194. max-height: 100%;
  3195. max-width: 100%; }
  3196.  
  3197. div {
  3198. font-size: 6vw;
  3199. font-weight: bold; }
  3200. </style>
  3201. </head>
  3202. <body>
  3203. <div id="loader">📰 Newspaper</div>
  3204. </body>
  3205. </html>`,
  3206. domParser = new DOMParser();
  3207.  
  3208. newDocument = domParser.parseFromString(loadPage, 'text/html');
  3209. insertDocument = document.importNode(newDocument.documentElement, true);
  3210. removeDocument = document.documentElement;
  3211. document.replaceChild(insertDocument, removeDocument);
  3212. }