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, XPathR###lt.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, XPathR###lt.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,XPathR###lt.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, XPathR###lt.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, XPathR###lt.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, XPathR###lt.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; }' );