RSS+Atom Feed Subscribe Button Generator

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

当前为 2014-11-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @id RSS+Atom Feed Subscribe Button Generator
  3. // @name RSS+Atom Feed Subscribe Button Generator
  4. // @description Finds RSS and/or Atom links on a page and inserts feed subscription links for use by aggregators
  5. // @namespace http://loonyone.livejournal.com
  6. // @include *
  7. // @creator Manpreet Singh [junkblocker@yahoo.com]
  8. // @copyright Manpreet Singh [junkblocker@yahoo.com]
  9. // @author Manpreet Singh [junkblocker@yahoo.com]
  10. // @version 2.5
  11. // @date 2014-11-04
  12. // ==/UserScript==
  13.  
  14. /*
  15. * Copyright (c) 2006-2014, Manpreet Singh [junkblocker@yahoo.com]
  16. *
  17. * Permission is hereby granted, free of charge, to any person
  18. * obtaining a copy of this software and associated documentation
  19. * files (the "Software"), to deal in the Software without
  20. * restriction, including without limitation the rights to use,
  21. * copy, modify, merge, publish, distribute, sublicense, and/or sell
  22. * copies of the Software, and to permit persons to whom the
  23. * Software is furnished to do so, subject to the following
  24. * conditions:
  25. *
  26. * The above copyright notice and this permission notice shall be
  27. * included in all copies or substantial portions of the Software.
  28. *
  29. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  30. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  31. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  32. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  33. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  34. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  35. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  36. * OTHER DEALINGS IN THE SOFTWARE.
  37. */
  38.  
  39. // ******
  40. // Opera, Chrome, Chromium, Safari and IE7Pro compatible except
  41. //
  42. // 1) link tags injection does not work in IE7Pro
  43. //
  44. // due to non-firefox platform limitations/differences.
  45. //
  46. // IE7Pro bundles an older version of this script by default.
  47. // ******
  48.  
  49. // Version 2.5 - New home at greasyfork.org
  50. // Removed auto-update code
  51. // Version 2.4 - Constrained container background and border
  52. // Version 2.3 - Constrained button dimensions some more
  53. // Version 2.2 - Constrained button dimensions
  54. // Version 2.1 - Fixed Content Security Policy issues
  55. // Version 2.0 - 'more' button was broken for me
  56. // some javascript lint cleanup
  57. // Lighter icons by simon!
  58. // Version 1.9 - Fixed invalid update metadata
  59. // Version 1.8 - Layout fix on apple.com/macupdate.com etc.
  60. // Version 1.7 - Minor layout fix on arstechnica.com
  61. // Version 1.6 - Insert discovered link tags in header for the browser's detection to list them too
  62. // Version 1.5 - Added license info
  63. // Added checks for feeds like &feed=rss or &feed=atom and some case insensitivity
  64. // Version 1.4 - Made the feed check slightly more stringent by excluding javascript: mailto: etc. urls
  65. // Version 1.3 - Reduce the number of button shown initially to a sane minimum
  66. // Version 1.2 - Fixed partially broken auto update.
  67. // Don't increase the install count on userscripts.org while checking for updates
  68. // Version 1.1 - Misc code cleanup
  69. // Version 1.0 - Added auto update without requiring an external script
  70. // Version 0.9 - Added a close button
  71. // Version 0.8 - Checks some wiki feeds with ?action=rss_rc and feeds with /rss/$ or /atom/$
  72. // Version 0.7 - Check for feedburner feeds
  73. // Version 0.6 - Fixed a major issue with using snapshotLength when it was not available
  74. // Version 0.5 - Updated contact info
  75. // Version 0.4 - Added optional auto-update facility
  76. // Version 0.3 - Added some speed optimizations
  77. // Version 0.2 - Merged Iain Brodfoot's floating div behaviour and removed purl.org links
  78. // - Allow revert to old behaviour by hand editing this script
  79. // Version 0.1 - First release
  80.  
  81. /*
  82. * Options contribution by Iain Broadfoot (ibroadfo@geeksoc.org)
  83. * - remove link to purl
  84. * (Manually edit this script to restore old behaviour. See TWEAK NOTE 1 below)
  85. * - Float the buttons to enable better behaviour on blogger/blogspot.com
  86. * (Manually edit this script to restore old behaviour. See TWEAK NOTE 2 below)
  87. *
  88. * Initially lifted mostly from
  89. * Generate RSS and ATOM tags: http://highbyte.bounceme.net/greasemonkey/genrsslinkrel.user.js
  90. * Amazon Atom Injector: http://docs.g-blog.net/code/greasemonkey/amazon_xml_feeds.user.js
  91. * Add RSS Index: http://www.xs4all.nl/~jlpoutre/BoT/Javascript/RSSpanel/rsspanel.user.js
  92. *
  93. * Original buttons from the excellent: http://kalsey.com/tools/buttonmaker/
  94. */
  95.  
  96. (function() {
  97. function addEventHandler(target, eventName, eventHandler, scope) {
  98. var f = scope ? function(){ eventHandler.apply( scope, arguments ); } : eventHandler;
  99. if (target.addEventListener) {
  100. target.addEventListener(eventName, f, true);
  101. } else if (target.attachEvent) {
  102. target.attachEvent('on' + eventName, f);
  103. }
  104. return f;
  105. }
  106.  
  107. var head = document.getElementsByTagName('head');
  108. if (head && head[0]) head = head[0];
  109. var INITIAL_COUNT = 5;
  110. var ATOM_ICON = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPBAMAAACB51W8AAAAAXNSR0IArs4c6QAAACRQTFRFAAEAAxx+JCYjYWNgho53kJKPy6Gio63Ys7Wysb3lyMnG/f/8AJKv4wAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfcBhsNKDWiRqoQAAAA2UlEQVQoz42SoQ7CMBCGL10w7CWWEAwzNRiwOEzD5DwvgSGoJVUoBAZFSGeWQZPSeznabutYtiX709b0y3/X/wowVSFW0vZ4iSMOq2hAGVgux2d98dl15UFmQMVr4zHwAAsFsMXMAJdltKKzYVACgTVAbA2RucYdaLZfDtSJhJgGPMPvHRk5QwKpvWvXvi5NjZ3pkeP1hDQoiaxAb9eAb0gtqDneULMW7DmWplb8gI19rOiC3R4VkCgsYW5T539gPx7lhmITFH4So4G7qM1kpoBC5DiqYvLn+QGXavuS25r9yAAAAABJRU5ErkJggg==" style="border: none !important; margin: 0px !important; width: 80px !important; height: 15px !important; display: inline !important;" />';
  111. var RSS_ICON = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPAgMAAAAOp6AcAAAAAXNSR0IArs4c6QAAAAxQTFRFAAAA/2YAiY55////S8IQBgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFRMROauDVnkAAAB7SURBVBjTpc8xDoAgDEBRLuXsKToaT0FMungfMLgz4CmMdygzC1G0yGIii/EPDM0LaYWo1Z6vdtFADtWj6ctwhKNfcTE26ehm8nHhodxAApKx3iifX3vL0AM6Y09NxDJlOUAHK2o2ihkVKbcwAiZNaSb25c9/e1Zvr3UBWOasaPHYnJwAAAAASUVORK5CYII=" style="border: none !important; margin: 0px !important; width: 80px !important; height: 15px !important; display: inline !important;" />';
  112. var RDF_ICON = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPAgMAAAAOp6AcAAAAAXNSR0IArs4c6QAAAAxQTFRFAAAA/2YAiY55////S8IQBgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFRMKD80PCHoAAAB3SURBVBjTpc8xDsAQGIZhl+rcU4jJMaSJxX1o2A0cwx1+s0Ua+muXJrU0fQfDlycCIbPW/uogCx1J/Wj/MipWFJXR+mZqcJBrxFEkLqgE67PVeZz+khxlsL4bAJRtyI0NadBoZHBLkQqOzUBzgP6+8987p3+fdQKJXassgfh4CAAAAABJRU5ErkJggg==" style="border: none !important; margin: 0px !important; width: 80px !important; height: 15px !important; display: inline !important;" />';
  113. var XML_ICON = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPAgMAAAAOp6AcAAAAAXNSR0IArs4c6QAAAAxQTFRFAAAA/2YAiY55////S8IQBgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFRMRFO5cCgwAAAB3SURBVBjTY2DABuz/Y4A/DKahIFC1CgmsIUWwtPTq9dCq/et2/Vv9a9/6V69/7QcKxl79DhR8tW7X63WrXoPIXUDB8NC7QMF963b9X/3qFVDlP4hKkPbVQDWrgMpeQVSCzPxa9W/1q3/rXwHVQ8ykzJ1Y/Y4NAADEZ67GIr3R+wAAAABJRU5ErkJggg==" style="border: none !important; margin: 0px !important; width: 80px !important; height: 15px !important; display: inline !important;" />';
  114. var CLOSE_ICON = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPAgMAAABGuH3ZAAAAAXNSR0IArs4c6QAAAAlQTFRF/2YA////AAAALA0mBgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAEnMAABJzAYwiuQcAAAAHdElNRQfcCxUTCRv8+I/EAAAAMklEQVQI12NYtWrVCoapoaERDBMYGCSghAMLkBAQABIsDkCCkQHGAouBZeGKwXpBpgAAhtMRBKRIycIAAAAASUVORK5CYII=" style="border: none !important; margin: 0px !important; width: 15px !important; height: 15px !important; display: inline !important;" />';
  115. var MORE_ICON = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAPAgMAAADrOWVwAAAAAXNSR0IArs4c6QAAAAxQTFRFAAEA/mcAh494/v/8doDPMwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wLFRMNODrzO7IAAABNSURBVAjXY2BAAPv/UPCHwTQUDKpWrcHGfAVklpbWxl/9W/ULyKz9ej229GrVPyCzvPQ6SPQ/WMH12KsQUaDa2FKIWiQTsFqB5AYEAABsXUkpSEP5/AAAAABJRU5ErkJggg==" style="border: none !important; margin: 0px !important; width: 40px !important; height: 15px !important; display: inline !important;" />';
  116. // array to keep track of 'seen' feed links.
  117. var seen = [];
  118.  
  119. var fsbdiv = document.createElement("div");
  120. fsbdiv.setAttribute("id", "XmlButtons");
  121. fsbdiv.style.textAlign = "left";
  122. fsbdiv.style.margin = "0px";
  123. // top right bottom left
  124. fsbdiv.style.padding = "0px 3px 0px 3px";
  125. fsbdiv.style.border = "none" ;
  126. fsbdiv.style.background = "none";
  127. // TWEAK NOTE 1 START: Comment out the next three lines to disable the buttons from floating
  128. fsbdiv.style.position = "fixed";
  129. fsbdiv.style.top = "0px";
  130. fsbdiv.style.left = "0px";
  131. fsbdiv.style.zIndex = "99999";
  132. fsbdiv.style.display = 'inline';
  133. // TWEAK NOTE 1 END
  134.  
  135. // Get links already declared properly in <link> elements in <head>
  136. function getKnownTags() {
  137. var linkRelElems = document.getElementsByTagName("link");
  138. var tlink;
  139.  
  140. for (var i = 0, l = linkRelElems.length; i < l; i++) {
  141. tlink = linkRelElems[i];
  142. if (!tlink) continue;
  143. try {
  144. var thref = tlink.href;
  145. var type = tlink.type;
  146.  
  147. if (type && type.match(/.*\/rs[sd](\+xml)?$/i)) {
  148. addRssLinkTag(thref, RSS_ICON);
  149. } else if (type && type.match(/.*\/atom\+xml$/i)) {
  150. addRssLinkTag(thref, ATOM_ICON);
  151. } else if (type && type.match(/^text\/xml$/i)) {
  152. addRssLinkTag(thref, RSS_ICON);
  153. }
  154. } catch (e) {
  155. }
  156. }
  157. }
  158.  
  159. function discoverUnknownFeeds() {
  160. if( !document.links ) {
  161. document.links = document.getElementsByTagName("a");
  162. }
  163. var links = document.links;
  164. for (var a = 0, len = links.length; a < len ; a++) {
  165. var link = links[a];
  166. linkhref = link.href;
  167.  
  168. if (linkhref.match(/^rss:/) ||
  169. linkhref.match(/^(http|ftp|feed).*([\.\/]rss([\.\/]xml|\.aspx|\.jsp|\/)?$|\/node\/feed$|\/rss\/[a-z0-9]+$|[?&;](rss|xml|rdf)=|[?&;]feed=rss[0-9.]*$|[?&;]action=rss_rc$|feeds\.feedburner\.com\/[a-z0-9]+$)/i)) {
  170. addRssLinkTag(linkhref, RSS_ICON, "rss");
  171. } else if (linkhref.match(/^(http|ftp|feed).*\/atom(\.xml|\.aspx|\.jsp|\/)?$|[?&;]feed=atom[0-9.]*$/i)) {
  172. addRssLinkTag(linkhref, ATOM_ICON, "atom");
  173. } else if (linkhref.match(/^(http|ftp|feed).*(\/feeds?\/[^.\/]*\.xml$|.*\/index\.xml$|feed\/msgs\.xml(\?num=\d+)?$)/i)) {
  174. addRssLinkTag(linkhref, XML_ICON, "rss");
  175. } else if (linkhref.match(/^(http|ftp|feed).*\.rdf$/i)) {
  176. addRssLinkTag(linkhref, RDF_ICON, "rss");
  177. } else if (linkhref.match(/^feed:\/\//i)) {
  178. addRssLinkTag(linkhref, RSS_ICON, "rss");
  179. }
  180. }
  181. }
  182.  
  183. function beenThere(linkhref) {
  184. if (seen.length <= 0) return false;
  185.  
  186. var href = linkhref.toLowerCase();
  187. for (var i = seen.length-1; i >=0; --i) {
  188. if (seen[i].toLowerCase() == href) return true;
  189. }
  190.  
  191. return false;
  192. }
  193.  
  194. function moreFSB() {
  195. var elem;
  196. for (var i = 1; ; i++) {
  197. elem = document.getElementById("XmlButton" + i);
  198. if (!elem) break;
  199. try {elem.style.display = "inline";} catch(e) {}
  200. }
  201. document.getElementById("MoreFSBButton").style.display = 'none';
  202. return false;
  203. }
  204.  
  205. function addRssLinkTag(linkhref, icon, addheadtype) {
  206. if (beenThere(linkhref)) {
  207. return;
  208. } else {
  209. seen.push(linkhref);
  210. }
  211.  
  212. if (seen.length == (INITIAL_COUNT + 1)) {
  213. var moreButton = document.createElement('a');
  214. moreButton.innerHTML = MORE_ICON;
  215. moreButton.style.display = 'inline';
  216. moreButton.style.cursor = 'pointer';
  217. moreButton.id = 'MoreFSBButton';
  218. addEventHandler(moreButton, 'click', moreFSB);
  219. fsbdiv.appendChild(moreButton);
  220. fsbdiv.appendChild(document.createTextNode(" "));
  221. }
  222. var flink = document.createElement("a");
  223. flink.innerHTML = icon;
  224. flink.href = linkhref;
  225. // TWEAK NOTE 2 START : Uncomment the href line below to retore linking
  226. // to purl.org pages for easy subscription in feed readers. This service
  227. // creates link for all known aggregators which support RSS subscription
  228. // service via a URL.
  229.  
  230. // href = "http://purl.org/net/syndication/subscribe/?rss=" + linkhref;
  231.  
  232. // TWEAK NOTE 2 END
  233. flink.alt = linkhref;
  234. flink.title = linkhref;
  235. flink.id = 'XmlButton' + seen.length;
  236. if (seen.length >= (INITIAL_COUNT + 1)) {
  237. flink.style.display = 'none';
  238. }
  239. fsbdiv.appendChild(flink);
  240. fsbdiv.appendChild(document.createTextNode(" "));
  241. // <link href="url" rel="alternate" title="desc" type="application/rss+xml" />
  242. if (addheadtype && head) {
  243. var link = document.createElement("link");
  244. link.href=linkhref;
  245. link.rel="alternate";
  246. link.title=linkhref + " (discovered by RSS+Atom Feed Subscribe Button Generator)";
  247. link.type="application/" + addheadtype + "+xml";
  248. head.appendChild(link);
  249. }
  250.  
  251. }
  252.  
  253. getKnownTags();
  254. discoverUnknownFeeds();
  255.  
  256. var body = document.getElementsByTagName("body")[0];
  257.  
  258. function closeFSB() {
  259. try {
  260. document.getElementById("XmlButtons").style.display = 'none';
  261. } catch(e) {}
  262. }
  263.  
  264. // insert the div only if something was found
  265. if (seen.length > 0) {
  266. if (head) {
  267. var closeButton = document.createElement('a');
  268. closeButton.innerHTML = CLOSE_ICON;
  269. addEventHandler(closeButton, 'click', closeFSB);
  270. closeButton.style.display = 'inline';
  271. closeButton.style.cursor = 'pointer';
  272. }
  273. fsbdiv.insertBefore(closeButton, fsbdiv.firstChild);
  274. setTimeout(function() {
  275. body.insertBefore(fsbdiv, body.firstChild);
  276. }, 333); // hack to workaround the duplicated content problem
  277. }
  278. })();