Pinboard - Sort Visible Links(modified)

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

  1. // ==UserScript==
  2. // @name Pinboard - Sort Visible Links(modified)
  3. // @namespace http://murklins.talkoncorners.net
  4. // @description A modified version of Pinboard - Sort Visible Links, compatible with Pinboard.in bookmarks with favicons
  5. // @version 0.1
  6. // @include http://pinboard.in/*
  7. // @include http://www.pinboard.in/*
  8. // @include https://pinboard.in/*
  9. // @include https://www.pinboard.in/*
  10. // @exclude http://pinboard.in/popular/*
  11. // @exclude http://www.pinboard.in/popular/*
  12. // @exclude https://pinboard.in/popular/*
  13. // @exclude https://www.pinboard.in/popular/*
  14. // @exclude http://pinboard.in/add*
  15. // @exclude http://www.pinboard.in/add*
  16. // @exclude https://pinboard.in/add*
  17. // @exclude https://www.pinboard.in/add*
  18. // @exclude http://pinboard.in/url:*
  19. // @exclude http://www.pinboard.in/url:*
  20. // @exclude https://pinboard.in/url:*
  21. // @exclude https://www.pinboard.in/url:*
  22. // ==/UserScript==
  23.  
  24. var main_node;
  25. var postArr = [];
  26.  
  27. // if can't find pinboard element, exit
  28. main_node = document.getElementById("pinboard");
  29. if (!main_node) {
  30. return;
  31. }
  32.  
  33. // gather up all the bookmarks and fill the postArr
  34. var bookmarks = fillPostArr();
  35. if (bookmarks.snapshotLength === 0) {
  36. // no bookmarks, so exit
  37. return;
  38. }
  39.  
  40. // add the sorting options div just above the first bookmark
  41. var firstBookmark = getBookmarkContainer(bookmarks.snapshotItem(0));
  42. var sortVisDiv = document.createElement("div");
  43. sortVisDiv.id = "gm_sortVisDiv";
  44. firstBookmark.parentNode.insertBefore(sortVisDiv, firstBookmark);
  45.  
  46. // add the sort links
  47. sortVisDiv.appendChild(document.createTextNode("Visible Sorted By ("));
  48. createSortLink("title", postSortTitle, sortVisDiv);
  49. sortVisDiv.appendChild(document.createTextNode(" | "));
  50. createSortLink("url", postSortUrl, sortVisDiv);
  51. sortVisDiv.appendChild(document.createTextNode(" | "));
  52. createSortLink("pop", postSortPop, sortVisDiv);
  53. sortVisDiv.appendChild(document.createTextNode(" | "));
  54. createSortLink("default", postSortDefault, sortVisDiv);
  55. sortVisDiv.appendChild(document.createTextNode(")"));
  56.  
  57. function fillPostArr() {
  58. // clear array
  59. postArr = [];
  60. // get all the bookmarks
  61. var bookmarks = document.evaluate("//div[contains(@class, 'bookmark ')]", main_node, null,
  62. XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  63. var p;
  64. for (var i = 0; i < bookmarks.snapshotLength; i++) {
  65. var b = bookmarks.snapshotItem(i);
  66. // get the number of people who saved the link
  67. var people = document.evaluate("./div[contains(@class, 'display')]/a[contains(@class, 'url_link')]",
  68. b, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  69. var popularity = 0;
  70. if (people.snapshotLength > 0) {
  71. popularity = people.snapshotItem(0).innerHTML;
  72. // format is "3 others" so just take the piece before the space
  73. popularity = popularity.split(" ")[0];
  74. }
  75. // get the link and title
  76. var postLink = document.evaluate("./div[contains(@class, 'display')]/a[contains(@class, 'bookmark_title')]",
  77. b, null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  78. var t = "";
  79. var l = "";
  80. if (postLink.snapshotLength > 0) {
  81. //Using along with Pinboard.in bookmarks with favicons (http://userscripts.org/scripts/show/94151)
  82. //would cause sorting by title unavailable.
  83. //Use text rather than innerHTML to get bookmark title can fix this.
  84.  
  85. //t = postLink.snapshotItem(0).innerHTML;
  86. t = postLink.snapshotItem(0).text;
  87. l = postLink.snapshotItem(0).getAttribute("href");
  88. }
  89. p = getBookmarkContainer(b);
  90. // add the post to the array
  91. postArr[postArr.length] = new PopPost(p, i, t, l, popularity);
  92. }
  93. return bookmarks;
  94. }
  95.  
  96. function createSortLink(linkText, sortFunction, parentDiv) {
  97. var a = document.createElement("a");
  98. a.innerHTML = linkText;
  99. a.className = "gm_sortVisLink";
  100. a.href = "";
  101. if (linkText == "default") {
  102. a.className = a.className + " gm_sortVisLinkOn";
  103. }
  104. parentDiv.appendChild(a);
  105. a.addEventListener("click", function(event) {
  106. event.stopPropagation();
  107. event.preventDefault();
  108. // get all the bookmarks
  109. var bookmarks = document.evaluate("//div[contains(@class, 'bookmark ')]", main_node, null,
  110. XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  111. if (bookmarks.snapshotLength != postArr.length) {
  112. // woops! so the postArr no longer has the same number of posts in it as there are posts on the page
  113. // usually this is because another GM script, like Autopagerize, has messed with the page since
  114. // we first grabbed all the posts. No problem, though, just re-fill the array.
  115. // But first, before we refill the post array, make sure we restore the default sort order to the
  116. // posts that may have already benn sorted one or more times, otherwise our new default order
  117. // will be... not very default.
  118. doSort(bookmarks, postArr.length, postSortDefault, "default");
  119. fillPostArr();
  120. }
  121. // do the sort!
  122. doSort(bookmarks, bookmarks.snapshotLength, sortFunction, this.innerHTML);
  123. }, false);
  124. return a;
  125. }
  126.  
  127. function doSort(bookmarks, numRemove, sortFunction, currentSortText) {
  128. // first remove all the posts from the DOM
  129. var list;
  130. var next;
  131. for (var i = 0; i < numRemove; i++) {
  132. var post = getBookmarkContainer(bookmarks.snapshotItem(i));
  133. list = post.parentNode;
  134. if (post.nextSibling) {
  135. next = post.nextSibling;
  136. }
  137. list.removeChild(post);
  138. }
  139. // why are autopagerize links getting added to the top of the list if a sort is triggered before the pagerize?
  140. // WHYYYYYYYYYYYYYYYYYYY. I don't really want to read the autopagerize code, so let's assume that the next page's
  141. // posts are pre-loaded into the bookmark list and we need to append our sorted links BEFORE them.
  142. //var remainingChild;
  143. //if (list.firstChild) {
  144. //remainingChild = list.firstChild;
  145. //}
  146.  
  147. // sort the post array using the passed in sort function and then add the posts in their new order as list children
  148. postArr.sort(sortFunction);
  149. /*
  150. for (var i = 0; i < postArr.length; i++) {
  151. //if (remainingChild) {
  152. //list.insertBefore(postArr[i].post, remainingChild);
  153. //}
  154. if (next) {
  155. list.insertBefore(postArr[i].post, next);
  156. }
  157. else {
  158. list.appendChild(postArr[i].post);
  159. }
  160. }
  161. */
  162. // insert the nodes back by finding the next sibling of the sort option div
  163. // if no sibling exists, we'll just append to the sort option div's parent
  164. var parent = sortVisDiv.parentNode;
  165. var insertBefore = null;
  166. if (sortVisDiv.nextSibling) {
  167. insertBefore = sortVisDiv.nextSibling;
  168. }
  169. // add the posts in their new order
  170. for (var i = 0; i < postArr.length; i++) {
  171. if (insertBefore) {
  172. parent.insertBefore(postArr[i].post, insertBefore);
  173. }
  174. else {
  175. parent.appendChild(postArr[i].post);
  176. }
  177. }
  178. // for compatibility with my own Pinboard - Date Lines GM script,
  179. // hide date lines on all but default sort order
  180. var dateLineDivs = document.evaluate("//div[contains(@class, 'gm_datelines_newdate')]", main_node, null,
  181. XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
  182. null);
  183. for (var i = 0; i < dateLineDivs.snapshotLength; i++) {
  184. if (currentSortText == "default") {
  185. dateLineDivs.snapshotItem(i).style.display = "block";
  186. }
  187. else {
  188. dateLineDivs.snapshotItem(i).style.display = "none";
  189. }
  190. }
  191. // highlight this link to indicate that it is on, deselect the other links
  192. var sortLinks = document.evaluate("//a[contains(@class, 'gm_sortVisLink')]", main_node, null,
  193. XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
  194. null);
  195. for (var i = 0; i < sortLinks.snapshotLength; i++) {
  196. sortLinks.snapshotItem(i).className = "gm_sortVisLink";
  197. if (sortLinks.snapshotItem(i).innerHTML == currentSortText) {
  198. sortLinks.snapshotItem(i).className = "gm_sortVisLinkOn";
  199. }
  200. }
  201. }
  202.  
  203. function PopPost(p, d, t, h, pop) {
  204. this.post = p;
  205. this.defaultOrder = d;
  206. this.title = t;
  207. this.link = h;
  208. this.pop = pop;
  209. }
  210. function postSortDefault(a, b) {
  211. return a.defaultOrder - b.defaultOrder;
  212. }
  213. function postSortTitle(a, b) {
  214. var x = a.title.toLowerCase();
  215. var y = b.title.toLowerCase();
  216. return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  217. }
  218. function postSortUrl(a, b) {
  219. var x = a.link;
  220. var y = b.link;
  221. return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  222. }
  223. function postSortPop(a, b) {
  224. return b.pop - a.pop;
  225. }
  226.  
  227. // bookmarks you can edit have an extra containing div, so look for it
  228. // and use it as the main post node
  229. function getBookmarkContainer(node) {
  230. var container = node;
  231. var parent = node.parentNode;
  232. if (parent.id != "bookmarks") {
  233. var node = parent.firstChild;
  234. while (node) {
  235. if (node.className == "edit_checkbox") {
  236. container = parent;
  237. break;
  238. }
  239. node = node.nextSibling;
  240. }
  241. }
  242. return container;
  243. }
  244.  
  245. GM_addStyle(
  246. 'div#gm_sortVisDiv { margin: 7px 0; }' +
  247. 'div#gm_sortVisDiv a.gm_sortVisLinkOn { color: blue; }' +
  248. 'div#gm_sortVisDiv a { color: #999; }'
  249. );