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.

Stan na 30-12-2018. Zobacz najnowsza wersja.

// ==UserScript==
// @name         Twitter Home - clickable links to images 
// @namespace    twitter_home_linkify
// @version      1.0.0
// @license      GNU AGPLv3
// @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.
// @author       marp
// @homepageURL  https://greasyfork.org/en/users/204542-marp
// @include      https://twitter.com/
// @include      https://twitter.com/*
// @include      https://pbs.twimg.com/media/*
// @exclude      https://twitter.com/settings
// @exclude      https://twitter.com/settings/*
// @run-at document-end
// ==/UserScript==

function createImageLinks(myDoc, myContext) {

//console.info("createImageLinks: ", myContext);
  
  if (myDoc===null) myDoc= myContext;
  if (myDoc===null) return;
  if (myContext===null) myContext= myDoc;
  
  var matches;
  var tmpstr;

  matches=myDoc.evaluate("//div[contains(@class,'AdaptiveMedia-photoContainer')]/img", 
                         myContext, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  for(var i=0, el; (i<matches.snapshotLength); i++) {
    el=matches.snapshotItem(i);
    if (el) {
      try {
//        console.info("matched-element: ", el);
        tmpstr=getCleanImageURL(el.getAttribute("src"), false);
//        console.info("cleanurl: ", tmpstr);
        // only need ":small" variant for tream/thumbnail display (save bandwidth and increase performance)
        el.setAttribute("src", tmpstr+":small");
        // create dorect link to ":orig" image - best way to access by opening in new tab via "middle-click")
        insertLinkElement(myDoc, el, tmpstr+":orig", getFilename(tmpstr));
      } catch (e) { console.warn("error: ", e); }
    }
	}
}


function insertLinkElement(myDoc, wrapElement, linkTarget, downloadName) {
	var newnode;
  var parentnode;
  
  newnode = myDoc.createElement("a");
  newnode.setAttribute("href", linkTarget);
  newnode.setAttribute("target", "_blank");
  newnode.setAttribute("download", downloadName);
  parentnode = wrapElement.parentNode;
  parentnode.replaceChild(newnode, wrapElement);
  newnode.appendChild(wrapElement);
}


function getCleanImageURL(imageurl) {
  var pos;
  var pos2;

  pos = imageurl.toLowerCase().lastIndexOf(":");
  pos2 = imageurl.toLowerCase().indexOf("/");
  if (pos >= 0 && pos > pos2) {
    return imageurl.substring(0, pos);
  } else {
    return imageurl; 
  }
}


function getFilename(imageurl) {
  return getCleanImageURL(imageurl).substring(imageurl.toLowerCase().lastIndexOf("/")+1);
}


// Two very different actions depending on if this is on twitter.com or twing.com
if (window.location.href.includes('pbs.twimg.com/media')){

  var image;
  var url;
  
  image = document.querySelector('img');
  url = image.src;
  insertLinkElement(document, image, getCleanImageURL(url)+":orig", getFilename(url));

}
else 
{

  // create an observer instance and iterate through each individual new node
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      mutation.addedNodes.forEach(function(addedNode) {
        createImageLinks(mutation.target.ownerDocument, addedNode);
      });
    });    
  });

  // configuration of the observer
  // NOTE: subtree is false as the wanted nodes are direct children of <ol id="posts"> -> notable performance improvement
  var config = { attributes: false, childList: true, characterData: false, subtree: false };

  // pass in the target node (<ol> element contains all dashboard posts), as well as the observer options
  var postsmatch = document.evaluate("//ol[@id='stream-items-id']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
  //console.info("postsmatch: ", postsmatch);
  var postsnode = postsmatch.singleNodeValue;
  //console.info("postsnode: ", postsnode);

  //process already loaded nodes (the initial posts before scrolling down for the first time)
  createImageLinks(document, postsnode);

  //start the observer for new nodes
  observer.observe(postsnode, config);
 
}