Greasy Fork

来自缓存

Greasy Fork is available in English.

Tumblr Dashboard - clickable links to images and display time-stamps

NEW Tumblr UI - READ DESCRIPTION. Linkifies all images in the tumblr dashboard and side-view streams. The script also displays the time-stamp of each post in the upper right corner.

当前为 2020-04-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Tumblr Dashboard - clickable links to images and display time-stamps
// @namespace    tumblr_dashboard_linkify
// @version      2.0.0
// @license      GNU AGPLv3
// @description  NEW Tumblr UI - READ DESCRIPTION. Linkifies all images in the tumblr dashboard and side-view streams. The script also displays the time-stamp of each post in the upper right corner.
// @author       marp
// @homepageURL  http://greasyfork.icu/en/users/204542-marp
// @include      https://www.tumblr.com/dashboard
// @include      https://www.tumblr.com/dashboard/*
// @include      https://www.tumblr.com/likes
// @include      https://www.tumblr.com/likes/*
// @run-at document-end
// ==/UserScript==



function nsResolver(prefix) {
  if (prefix === 'svg') { 
    return 'http://www.w3.org/2000/svg';
  } else {
    return null;
  }
}


function doNothing_tumblr_dashboard_linkify(event) {
  e.preventDefault();
  console.info("JS Tescht");
  return false;
}

function insertLinkElement(myDoc, wrapElement, linkTarget) {
	var newnode;
  var parentnode;
  
  console.info("myDoc - wrapE - linkT: ", myDoc, " - ", wrapElement, " - ", linkTarget);
  newnode = myDoc.createElement("a");
  newnode.setAttribute("href", linkTarget);
  newnode.setAttribute("target", "_blank");
//  newnode.setAttribute("displaytimestampscript", "1"); // flag that this node was added my this script
  newnode.addEventListener("click", doNothing_tumblr_dashboard_linkify, true);
  parentnode = wrapElement.parentNode;
  parentnode.replaceChild(newnode, wrapElement);
  newnode.appendChild(wrapElement);
  console.info("newnode: ", newnode);
}

function getHighResImageURL(imageElement) {
  var srcarray;
  var tmpstr;
  
  srcarray = imageElement.getAttribute("srcset").split(",");
  // QUICK AND DIRTY - assume largest image is the last in array... seems to be true for Tumblr... but might change...
  tmpstr = srcarray[srcarray.length-1].trim();
  return tmpstr.substring(0, tmpstr.indexOf(" "));
}



function createImageLinks(myDoc, myContext) {

  if (myDoc===null) myDoc= myContext;
  if (myDoc===null) return;
  if (myContext===null) myContext= myDoc;
  
  var matches;
  var tmpstr;
  
  // the img might be added as part of a whole pos (first expr) - or just the img or the div/img, in which case we need to check if the image is part of the correct hierarchy (second expr)
  matches=myDoc.evaluate(".//article[@aria-label]//button[@aria-label]//figure/div/img[@role='img' and @srcset and @sizes]"
                         + " | " +
                         "self::img[@role='img' and @srcset and @sizes and parent::div/parent::figure/ancestor::button[@aria-label]/ancestor::article[@aria-label]]"
                         + " | " +
                         "self::div/img[@role='img' and @srcset and @sizes and parent::div/parent::figure/ancestor::button[@aria-label]/ancestor::article[@aria-label]]",
                         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=getHighResImageURL(el);
        console.info("highresurl: ", tmpstr);
        console.info("el.parent: ", el.parentNode);
        insertLinkElement(myDoc, el.parentNode, tmpstr);
      } catch (e) { console.warn("error: ", e); }
    }
	}
}


function displayDateTime(myDoc, myContext) {
  if (myDoc===null) myDoc= myContext;
  if (myDoc===null) return;
  if (myContext===null) myContext= myDoc;
  
  var matches;
  var singlematch;
  var singlenode;
  var moreoptionsnode;
  var permalinknode;
  var tmpstr;
  var newnode;
  
  matches=myDoc.evaluate(".//article[@aria-label]//header[@role='banner']", 
                         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);
        // find the grandchild div that contains the "More Options" Ellipsis button
        // make sure that this div is the last child (if it is not then this script might have run already)
        singlematch = myDoc.evaluate("((./div/div)[last()])[.//button//svg:svg]", 
                                     el, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
        singlenode = singlematch.singleNodeValue; 
        if (singlenode) {
          moreoptionsnode = singlenode;
          // find the permalink element - this has the timestamp as the title attribute (mouseover popup)
          singlematch = myDoc.evaluate("./a[@role='button' and @target='_blank' and @title]", 
                                       el, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
          singlenode = singlematch.singleNodeValue; 
          if (singlenode) {
            permalinknode = singlenode;
            // get the class attrib of the "more options" div - we'll create a second div with same class
            tmpstr = moreoptionsnode.getAttribute("class");
            // create new node with the timestamp text
            newnode = myDoc.createElement("div");
            newnode.setAttribute("class", tmpstr);
            newnode.setAttribute("style", "font-size: 85%");
            newnode.setAttribute("displaytimestampscript", "1"); // flag that this node was added my this script
            newnode.textContent = permalinknode.getAttribute("title");
            moreoptionsnode.parentNode.appendChild(newnode);
          } 
        }
      } catch (e) { console.warn("error: ", e) }
    }
	}
}




// 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) {
      console.info("ADDED node: ", addedNode);
      createImageLinks(mutation.target.ownerDocument, addedNode);
      displayDateTime(mutation.target.ownerDocument, addedNode);
    });
    mutation.removedNodes.forEach(function(removedNode) {
      console.info("REMOVED node: ", removedNode);
      // TUMBLR - OH NO YOU DON'T !!
      if (removedNode.hasAttribute("displaytimestampscript")) {
        mutation.target.insertBefore(removedNode, mutation.nextSibling)
        console.info("  READDED node: ", removedNode);
      }
    });
  });    
});
 
// configuration of the observer
var config = { attributes: false, childList: true, characterData: false, subtree: true };
 
// new twitter UI has few stable IDs - need to start very high with "root" node
var singlematch = document.evaluate("//body[@id='tumblr']/div[@id='root']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
//console.info("singlematch: ", singlematch);
var rootnode = singlematch.singleNodeValue;

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

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