Greasy Fork is available in English.

Pinboard - Sort Visible Links(modified)

A modified version of Pinboard - Sort Visible Links, compatible with Pinboard.in bookmarks with favicons

// ==UserScript==
// @name Pinboard - Sort Visible Links(modified)
// @namespace http://murklins.talkoncorners.net
// @description A modified version of Pinboard - Sort Visible Links, compatible with Pinboard.in bookmarks with favicons
// @version 0.1
// @include http://pinboard.in/*
// @include http://www.pinboard.in/*
// @include https://pinboard.in/*
// @include https://www.pinboard.in/*
// @exclude http://pinboard.in/popular/*
// @exclude http://www.pinboard.in/popular/*
// @exclude https://pinboard.in/popular/*
// @exclude https://www.pinboard.in/popular/*
// @exclude http://pinboard.in/add*
// @exclude http://www.pinboard.in/add*
// @exclude https://pinboard.in/add*
// @exclude https://www.pinboard.in/add*
// @exclude http://pinboard.in/url:*
// @exclude http://www.pinboard.in/url:*
// @exclude https://pinboard.in/url:*
// @exclude https://www.pinboard.in/url:*
// ==/UserScript==

var main_node;
var postArr = [];

// if can't find pinboard element, exit
main_node = document.getElementById("pinboard");
if (!main_node) {
  return;
}

// gather up all the bookmarks and fill the postArr
var bookmarks = fillPostArr();
if (bookmarks.snapshotLength === 0) {
  // no bookmarks, so exit
  return;
}

// add the sorting options div just above the first bookmark
var firstBookmark =  getBookmarkContainer(bookmarks.snapshotItem(0));
var sortVisDiv = document.createElement("div");
sortVisDiv.id = "gm_sortVisDiv";
firstBookmark.parentNode.insertBefore(sortVisDiv, firstBookmark);

// add the sort links	
sortVisDiv.appendChild(document.createTextNode("Visible Sorted By ("));
createSortLink("title", postSortTitle, sortVisDiv);
sortVisDiv.appendChild(document.createTextNode(" | "));
createSortLink("url", postSortUrl, sortVisDiv);
sortVisDiv.appendChild(document.createTextNode(" | "));
createSortLink("pop", postSortPop, sortVisDiv);
sortVisDiv.appendChild(document.createTextNode(" | "));
createSortLink("default", postSortDefault, sortVisDiv);	
sortVisDiv.appendChild(document.createTextNode(")"));

function fillPostArr() {
  // clear array
  postArr = [];
  
  // get all the bookmarks
  var bookmarks = document.evaluate("//div[contains(@class, 'bookmark ')]", main_node, null,
                                      XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  
  var p;
  for (var i = 0; i < bookmarks.snapshotLength; i++) {      
    var b = bookmarks.snapshotItem(i); 
    
    // get the number of people who saved the link
    var people = document.evaluate("./div[contains(@class, 'display')]/a[contains(@class, 'url_link')]", 
                                    b, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);                  
    var popularity = 0;
    if (people.snapshotLength > 0) {
      popularity = people.snapshotItem(0).innerHTML;
      // format is "3 others" so just take the piece before the space
      popularity = popularity.split(" ")[0];
    }				
    
    // get the link and title
    var postLink = document.evaluate("./div[contains(@class, 'display')]/a[contains(@class, 'bookmark_title')]", 
                                      b, null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    var t = "";
    var l = "";
    if (postLink.snapshotLength > 0) {
      //Using along with Pinboard.in bookmarks with favicons (http://userscripts.org/scripts/show/94151)
      //would cause sorting by title unavailable.
      //Use text rather than innerHTML to get bookmark title can fix this.

      //t = postLink.snapshotItem(0).innerHTML;
      t = postLink.snapshotItem(0).text;
      l = postLink.snapshotItem(0).getAttribute("href");
    }
    
    p = getBookmarkContainer(b);
    
    // add the post to the array
    postArr[postArr.length] = new PopPost(p, i, t, l, popularity);					
  }
  
  return bookmarks;
}

function createSortLink(linkText, sortFunction, parentDiv) {
	var a = document.createElement("a");
	a.innerHTML = linkText;
	a.className = "gm_sortVisLink";
	a.href = "";
	if (linkText == "default") {	
		a.className = a.className + " gm_sortVisLinkOn";
	}
	parentDiv.appendChild(a);
	
	a.addEventListener("click", function(event) {
		event.stopPropagation();		
		event.preventDefault();
		
		// get all the bookmarks
    var bookmarks = document.evaluate("//div[contains(@class, 'bookmark ')]", main_node, null,
                                      XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    
    if (bookmarks.snapshotLength != postArr.length) {
			// woops! so the postArr no longer has the same number of posts in it as there are posts on the page
			// usually this is because another GM script, like Autopagerize, has messed with the page since
			// we first grabbed all the posts. No problem, though, just re-fill the array.
			
			// But first, before we refill the post array, make sure we restore the default sort order to the 
			// posts that may have already benn sorted one or more times, otherwise our new default order 
			// will be... not very default.
			doSort(bookmarks, postArr.length, postSortDefault, "default");
			
			fillPostArr();
		}
		
		// do the sort!
		doSort(bookmarks, bookmarks.snapshotLength, sortFunction, this.innerHTML);
		
	}, false);		
	
	return a;
}

function doSort(bookmarks, numRemove, sortFunction, currentSortText) {
	// first remove all the posts from the DOM
	var list;
	var next;
	for (var i = 0; i < numRemove; i++) {
	  var post = getBookmarkContainer(bookmarks.snapshotItem(i));
	  list = post.parentNode;
	  if (post.nextSibling) {
	    next = post.nextSibling;
	  }
		list.removeChild(post);
	}
	
	// why are autopagerize links getting added to the top of the list if a sort is triggered before the pagerize?
	// WHYYYYYYYYYYYYYYYYYYY. I don't really want to read the autopagerize code, so let's assume that the next page's
	// posts are pre-loaded into the bookmark list and we need to append our sorted links BEFORE them.
	//var remainingChild;
	//if (list.firstChild) {
		//remainingChild = list.firstChild;
	//}
	

	// sort the post array using the passed in sort function and then add the posts in their new order as list children
	postArr.sort(sortFunction);
	
	/*
	for (var i = 0; i < postArr.length; i++) {
		//if (remainingChild) {
			//list.insertBefore(postArr[i].post, remainingChild);
		//}
		if (next) {
			list.insertBefore(postArr[i].post, next);
		}
		else {
			list.appendChild(postArr[i].post);
		}
	}
	*/
	
  // insert the nodes back by finding the next sibling of the sort option div
  // if no sibling exists, we'll just append to the sort option div's parent
  var parent = sortVisDiv.parentNode;
  var insertBefore = null;
  if (sortVisDiv.nextSibling) {
    insertBefore = sortVisDiv.nextSibling;
  }
  
  // add the posts in their new order
  for (var i = 0; i < postArr.length; i++) {
    if (insertBefore) {
      parent.insertBefore(postArr[i].post, insertBefore);
    }
    else {
      parent.appendChild(postArr[i].post);
    }
  }
		
  // for compatibility with my own Pinboard - Date Lines GM script, 
  // hide date lines on all but default sort order
  var dateLineDivs = document.evaluate("//div[contains(@class, 'gm_datelines_newdate')]", main_node, null,
                                    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                                    null);
  for (var i = 0; i < dateLineDivs.snapshotLength; i++) {
    if (currentSortText == "default") {
      dateLineDivs.snapshotItem(i).style.display = "block";
    }
    else {
      dateLineDivs.snapshotItem(i).style.display = "none";      
    }
  }
	
	// highlight this link to indicate that it is on, deselect the other links
	var sortLinks = document.evaluate("//a[contains(@class, 'gm_sortVisLink')]", main_node, null,
	                            						XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
	                            						null);
	for (var i = 0; i < sortLinks.snapshotLength; i++) {			
		sortLinks.snapshotItem(i).className = "gm_sortVisLink";
		if (sortLinks.snapshotItem(i).innerHTML == currentSortText) {
			sortLinks.snapshotItem(i).className = "gm_sortVisLinkOn";
		}
	}
}

function PopPost(p, d, t, h, pop) {
	this.post = p;
	this.defaultOrder = d;
	this.title = t;
	this.link = h;
	this.pop = pop;
}
function postSortDefault(a, b) {
	return a.defaultOrder - b.defaultOrder;
}
function postSortTitle(a, b) {
	var x = a.title.toLowerCase();
	var y = b.title.toLowerCase();
	return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}
function postSortUrl(a, b) {
	var x = a.link;
	var y = b.link;
	return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}
function postSortPop(a, b) {
	return b.pop - a.pop;
}

// bookmarks you can edit have an extra containing div, so look for it
// and use it as the main post node
function getBookmarkContainer(node) {
  var container = node;
  var parent = node.parentNode;
  if (parent.id != "bookmarks") {
    var node = parent.firstChild;
    while (node) {
      if (node.className == "edit_checkbox") {
        container = parent;
        break;
      }
      node = node.nextSibling;
    }
  }
  return container;
}

GM_addStyle(
  'div#gm_sortVisDiv { margin: 7px 0; }' +
	'div#gm_sortVisDiv a.gm_sortVisLinkOn { color: blue; }' +
	'div#gm_sortVisDiv a { color: #999; }'
);