Twitter Home - clickable links to images

Linkifies all images in the Twitter Home stream. These links point to the :orig version while the stream content is modified to use the :small variant to increase performance.

当前为 2018-12-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Twitter Home - clickable links to images
  3. // @namespace twitter_home_linkify
  4. // @version 1.0.0
  5. // @license GNU AGPLv3
  6. // @description Linkifies all images in the Twitter Home stream. These links point to the :orig version while the stream content is modified to use the :small variant to increase performance.
  7. // @author marp
  8. // hhhhomepageURL
  9. // @include https://twitter.com/
  10. // @include https://twitter.com/*
  11. // @include https://pbs.twimg.com/media/*
  12. // @exclude https://twitter.com/settings
  13. // @exclude https://twitter.com/settings/*
  14. // @run-at document-end
  15. // ==/UserScript==
  16.  
  17. function createImageLinks(myDoc, myContext) {
  18.  
  19. //console.info("createImageLinks: ", myContext);
  20. if (myDoc===null) myDoc= myContext;
  21. if (myDoc===null) return;
  22. if (myContext===null) myContext= myDoc;
  23. var matches;
  24. var tmpstr;
  25.  
  26. matches=myDoc.evaluate("//div[contains(@class,'AdaptiveMedia-photoContainer')]/img",
  27. myContext, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  28. for(var i=0, el; (i<matches.snapshotLength); i++) {
  29. el=matches.snapshotItem(i);
  30. if (el) {
  31. try {
  32. // console.info("matched-element: ", el);
  33. tmpstr=getCleanImageURL(el.getAttribute("src"), false);
  34. // console.info("cleanurl: ", tmpstr);
  35. // only need ":small" variant for tream/thumbnail display (save bandwidth and increase performance)
  36. el.setAttribute("src", tmpstr+":small");
  37. // create dorect link to ":orig" image - best way to access by opening in new tab via "middle-click")
  38. insertLinkElement(myDoc, el, tmpstr+":orig", getFilename(tmpstr));
  39. } catch (e) { console.warn("error: ", e); }
  40. }
  41. }
  42. }
  43.  
  44.  
  45. function insertLinkElement(myDoc, wrapElement, linkTarget, downloadName) {
  46. var newnode;
  47. var parentnode;
  48. newnode = myDoc.createElement("a");
  49. newnode.setAttribute("href", linkTarget);
  50. newnode.setAttribute("target", "_blank");
  51. newnode.setAttribute("download", downloadName);
  52. parentnode = wrapElement.parentNode;
  53. parentnode.replaceChild(newnode, wrapElement);
  54. newnode.appendChild(wrapElement);
  55. }
  56.  
  57.  
  58. function getCleanImageURL(imageurl) {
  59. var pos;
  60. var pos2;
  61.  
  62. pos = imageurl.toLowerCase().lastIndexOf(":");
  63. pos2 = imageurl.toLowerCase().indexOf("/");
  64. if (pos >= 0 && pos > pos2) {
  65. return imageurl.substring(0, pos);
  66. } else {
  67. return imageurl;
  68. }
  69. }
  70.  
  71.  
  72. function getFilename(imageurl) {
  73. return getCleanImageURL(imageurl).substring(imageurl.toLowerCase().lastIndexOf("/")+1);
  74. }
  75.  
  76.  
  77. // Two very different actions depending on if this is on twitter.com or twing.com
  78. if (window.location.href.includes('pbs.twimg.com/media')){
  79.  
  80. var image;
  81. var url;
  82. image = document.querySelector('img');
  83. url = image.src;
  84. insertLinkElement(document, image, getCleanImageURL(url)+":orig", getFilename(url));
  85.  
  86. }
  87. else
  88. {
  89.  
  90. // create an observer instance and iterate through each individual new node
  91. var observer = new MutationObserver(function(mutations) {
  92. mutations.forEach(function(mutation) {
  93. mutation.addedNodes.forEach(function(addedNode) {
  94. createImageLinks(mutation.target.ownerDocument, addedNode);
  95. });
  96. });
  97. });
  98.  
  99. // configuration of the observer
  100. // NOTE: subtree is false as the wanted nodes are direct children of <ol id="posts"> -> notable performance improvement
  101. var config = { attributes: false, childList: true, characterData: false, subtree: false };
  102.  
  103. // pass in the target node (<ol> element contains all dashboard posts), as well as the observer options
  104. var postsmatch = document.evaluate("//ol[@id='stream-items-id']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
  105. //console.info("postsmatch: ", postsmatch);
  106. var postsnode = postsmatch.singleNodeValue;
  107. //console.info("postsnode: ", postsnode);
  108.  
  109. //process already loaded nodes (the initial posts before scrolling down for the first time)
  110. createImageLinks(document, postsnode);
  111.  
  112. //start the observer for new nodes
  113. observer.observe(postsnode, config);
  114. }