Endless Pixiv Pages

Loads more pixiv pages as you scroll down.

Od 26.01.2018.. Pogledajte najnovija verzija.

  1. // ==UserScript==
  2. // @name Endless Pixiv Pages
  3. // @namespace https://greasyfork.org/scripts/3254
  4. // @include *://www.pixiv.net/*
  5. // @description Loads more pixiv pages as you scroll down.
  6. // @version 2018.01.25
  7. // @grant none
  8. // ==/UserScript==
  9.  
  10. var iframeLoader = false;//If true, force all pages to be loaded using a hidden iframe instead of XMLHttpRequest.
  11. var scrollBuffer = 500;//Minimum height remaining to scroll before loading the next page.
  12. var timeToFailure = 30;//Seconds to wait for a response from the next page before attempting to fetch the page again.
  13.  
  14. //////////////////////////////////////////////////////////////////////////////////////
  15.  
  16. var mainTable, bottomDoc, nextPage, timeout = 0, pending = false, totalPages = 0, loadAll = false, loadAllLink = null, itemsPerPage = 20;
  17.  
  18. //Hide the ad for pixiv premium inserted into the middle of tag search results; its thumbnails are broken in the loaded pages anyway.
  19. var pStyle = document.createElement("style");
  20. pStyle.innerHTML = "._premium-lead-popular-d-body{display:none !important}";
  21. document.head.appendChild(pStyle);
  22.  
  23. if( window != window.top )
  24. ;//console.debug('Endless Pixiv Pages: Ignoring inner frame');
  25. else if( !(mainTable = getMainTable(document)) )
  26. console.debug('Endless Pixiv Pages: No main table');
  27. else if( !(bottomDoc = getBottomPager(document)) )
  28. console.debug('Endless Pixiv Pages: No bottom pager');
  29. else if( !(nextPage = getNextPage(bottomDoc)) )
  30. console.debug('Endless Pixiv Pages: Next page not found in bottom pager');
  31. else
  32. {
  33. //Move bottom pager out of where additional content will be loaded
  34. bottomDoc.parentNode.removeChild(bottomDoc);
  35. mainTable.parentNode.parentNode.appendChild(bottomDoc);
  36. if( iframeLoader || (mainTable.id || "").indexOf("js-") == 0 )
  37. {
  38. //iframe to load pages; required for the 'js-' pages
  39. console.debug('Endless Pixiv Pages: Loading with iframe');
  40. iframeLoader = document.createElement("iframe");
  41. iframeLoader.addEventListener("load", function(){ processPage( iframeLoader.contentDocument ); }, false);
  42. iframeLoader.width = iframeLoader.height = 0;
  43. iframeLoader.style.visibility = "hidden";
  44. document.body.appendChild(iframeLoader);
  45. }
  46. else console.debug('Endless Pixiv Pages: Loading with XMLHttpRequest');
  47.  
  48. //Calculate total number of items per page. Only four page types seem to deviate from the default (20), though:
  49. // 48: https://www.pixiv.net/bookmark.php?id=...&type=user
  50. // 40: https://www.pixiv.net/search.php?...
  51. // 12: https://www.pixiv.net/novel/tags.php?...
  52. // 12: https://www.pixiv.net/group/search_group.php?...
  53. if( mainTable.parentNode.querySelector('[data-items]') )
  54. itemsPerPage = JSON.parse( mainTable.parentNode.querySelector('[data-items]').getAttribute('data-items').replace(/"/g,'"') ).length;
  55. else if( mainTable.tagName == "UL" )
  56. {
  57. let i = 0;
  58. for( itemsPerPage = 0; i < mainTable.childNodes.length; i++ )
  59. if( mainTable.childNodes[i].tagName == "LI" )
  60. itemsPerPage++;
  61. }
  62. if( itemsPerPage <= 0 )
  63. itemsPerPage = 20;
  64.  
  65. //console.debug('items per page = '+itemsPerPage);
  66.  
  67. let resultBadge = document.querySelector(".count-badge");
  68. let resultMatcher = resultBadge && resultBadge.textContent.match(/^(\d+) *(.*)/);
  69. if( resultMatcher )
  70. {
  71. totalPages = Math.floor( ( parseInt(resultMatcher[1]) + itemsPerPage - 1 ) / itemsPerPage );
  72. resultBadge.textContent = resultMatcher[1]+" "+resultMatcher[2];//Making sure there's a space between the number and (if present) "results"
  73. if( totalPages > 1 )
  74. {
  75. resultBadge.textContent += " ("+totalPages+" pages)";
  76. loadAllLink = resultBadge.appendChild( document.createElement("a") );
  77. loadAllLink.style = 'margin-left:10px; cursor:pointer';
  78. loadAllLink.textContent = "Load All Pages";
  79. loadAllLink.onclick = function()
  80. {
  81. loadAll = !loadAll;
  82. loadAllLink.textContent = loadAll ? "Stop Loading Pages" : "Load All Pages";
  83. testScrollPosition();
  84. };
  85. }
  86. }
  87. //Adjust buffer height
  88. scrollBuffer += window.innerHeight;
  89.  
  90. //Watch scrolling
  91. window.addEventListener("scroll", testScrollPosition, false);
  92. testScrollPosition();
  93. }
  94.  
  95. function getMainTable(source)
  96. {
  97. let result = null, tableFun =
  98. [
  99. //bookmark.php?type=user
  100. function(src){ src = src.getElementById("search-result"); return src ? src.getElementsByTagName("ul")[0] : null; },
  101. //search.php
  102. function(src){ return src.getElementById("js-react-search-mid"); },
  103.  
  104. //bookmark_new_illust.php
  105. function(src){ return src.getElementById("js-mount-point-latest-following"); },
  106.  
  107. //member_illust.php
  108. //bookmark.php (except type=user)
  109. //new_illust.php
  110. function(src){ src = src.getElementsByClassName("image-item")[0]; return src ? src.parentNode : null; },
  111. //novel/tags.php
  112. //group/search_group.php
  113. function(src){ return src.getElementsByClassName("autopagerize_page_element")[0]; },
  114. //search: users
  115. ////function(src){ return src.getElementsByClassName("user-recommendation-items")[0]; },//issue with lazy-loading
  116. //response.php
  117. function(src){ return src.getElementsByClassName("linkStyleWorks")[0]; }//returning inner list element breaks page separation on https://www.pixiv.net/response.php?mode=all&id=...
  118. ];
  119. for( let i = 0; i < tableFun.length; i++ )
  120. {
  121. getMainTable = tableFun[i];
  122. if( (result = getMainTable(source)) != null )
  123. {
  124. //console.debug('Endless Pixiv Pages: main['+i+']');
  125. return result;
  126. }
  127. }
  128. return null;
  129. }
  130.  
  131. function getBottomPager(source)
  132. {
  133. let result = null, pagerFun =
  134. [
  135. //most things
  136. function(src){ src = src.getElementsByClassName("pager-container"); return src.length ? src[src.length - 1].parentNode : null; },
  137. //image responses, bookmarked users
  138. function(src){ src = src.getElementsByClassName("_pager-complex"); return src.length ? src[src.length - 1] : null; }
  139. ];
  140. for( let i = 0; i < pagerFun.length; i++ )
  141. {
  142. getBottomPager = pagerFun[i];
  143. if( (result = getBottomPager(source)) != null )
  144. return result;
  145. }
  146. return null;
  147. }
  148.  
  149. function getNextPage(pager)
  150. {
  151. let links = pager.getElementsByTagName("a");
  152. if( links.length == 0 || links[links.length-1].getAttribute("rel") != "next" )
  153. return null;//No more pages
  154. else if( links[links.length-1].href.indexOf("//www.pixiv.net/") < 0 )
  155. return location.protocol+"//www.pixiv.net/"+links[links.length-1].href;
  156. else
  157. return links[links.length-1].href;
  158. }
  159.  
  160. function testScrollPosition()
  161. {
  162. if( !pending && ( loadAll || window.pageYOffset + scrollBuffer > bottomDoc.offsetTop ) )
  163. {
  164. pending = true;
  165. timeout = setTimeout( function(){ pending = false; testScrollPosition(); }, timeToFailure*1000 );
  166. //If the page about to be loaded is the last, hide the "Load All Pages" link.
  167. if( loadAllLink && nextPage.replace(/.*(\?|&)p=([0-9]+).*/,'$2') == totalPages )
  168. loadAllLink.style.display = "none";
  169. if( iframeLoader )
  170. iframeLoader.contentDocument.location.replace(nextPage);
  171. else
  172. {
  173. let xhr = new XMLHttpRequest();
  174. xhr.open( "GET", nextPage );
  175. xhr.onabort = xhr.onabort = xhr.onerror = function(){ clearTimeout(timeout); pending = false; };
  176. xhr.onload = function(){ processPage( xhr.responseXML ); };
  177. xhr.responseType = "document";
  178. xhr.send();
  179. }
  180. }
  181. }
  182.  
  183. function processPage( newDoc )
  184. {
  185. clearTimeout(timeout);
  186. let newTable = getMainTable(newDoc);
  187. //Make sure page loaded properly
  188. if( !newTable )
  189. {
  190. pending = false;
  191. return;
  192. }
  193.  
  194. //Pixiv needs a little time to dynamically populate the 'main table' of tag searches, so if the table is empty, pause for a moment and try again.
  195. if( newTable.innerHTML.length == 0 )
  196. {
  197. setTimeout( function(){ processPage(newDoc); }, 100 );
  198. return;
  199. }
  200.  
  201. //search.php and bookmark_new_illust.php store image info in JSON string and set the thumbnails as background images
  202. let rawItems = newTable.getAttribute("data-items") || ( newTable.nextSibling && newTable.nextSibling.getAttribute("data-items") );
  203. if( rawItems ) try
  204. {
  205. let lazyDivs = newTable.querySelectorAll('div > a > div.js-lazyload, div > a > div.lazyloaded');//Only actually expecting 'js-lazyload'; only the initial page uses 'lazyloaded'.
  206. let jsonItems = JSON.parse(rawItems);
  207. if( lazyDivs.length == jsonItems.length )
  208. for( let i = 0; i < lazyDivs.length; i++ )
  209. lazyDivs[i].style.backgroundImage = 'url("'+jsonItems[i].url+'")';
  210. } catch(e) {
  211. console.log('Endless Pixiv Pages - failed to parse raw items: '+e);
  212. }
  213.  
  214. //Disable lazy loading to fix pages like /new_illust.php
  215. let image = newTable.getElementsByTagName("img");
  216. for( let i = 0; i < image.length; i++ )
  217. if( image[i].getAttribute("data-src") )
  218. image[i].src = image[i].getAttribute("data-src");
  219. //Add page link
  220. mainTable.parentNode.appendChild( document.createElement("div") ).innerHTML = '<hr style="background-color:#e4e7ee; padding:10px;"><hr style = "margin:10px;"><a style="font-size:large; text-align:left; margin: 12px" href="'+nextPage+'">Page '+nextPage.replace(/.*(\?|&)p=([0-9]+).*/,'$2')+( totalPages > 0 ? " of "+totalPages : "")+'</a>';
  221. //Update the visible bottom paginator.
  222. let bottomPage = getBottomPager( newDoc );
  223. bottomDoc.innerHTML = bottomPage.innerHTML;
  224. //Append new page
  225. mainTable.parentNode.appendChild( document.adoptNode(newTable) );
  226. //Check for the next page, and disable the script if there isn't one.
  227. if( !(nextPage = getNextPage(bottomPage)) )
  228. mainTable.parentNode.appendChild( document.createElement("div") ).setAttribute("class","clear");
  229. else
  230. {
  231. pending = false;
  232. testScrollPosition();
  233. }
  234. }
  235.  
  236. //So as I pray, Unlimited Pixiv Works.