infiniteScroll.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // @license http://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
  2. // SPDX-License-Identifier: AGPL-3.0-only
  3. function insertBeforeLast(node, elem) {
  4. node.insertBefore(elem, node.childNodes[node.childNodes.length - 2]);
  5. }
  6. function getLoadMore(doc) {
  7. return doc.querySelector(".show-more:not(.timeline-item)");
  8. }
  9. function isDuplicate(item, itemClass) {
  10. const tweet = item.querySelector(".tweet-link");
  11. if (tweet == null) return false;
  12. const href = tweet.getAttribute("href");
  13. return (
  14. document.querySelector(itemClass + " .tweet-link[href='" + href + "']") !=
  15. null
  16. );
  17. }
  18. window.onload = function () {
  19. const url = window.location.pathname;
  20. const isTweet = url.indexOf("/status/") !== -1;
  21. const containerClass = isTweet ? ".replies" : ".timeline";
  22. const itemClass = containerClass + " > div:not(.top-ref)";
  23. var html = document.querySelector("html");
  24. var container = document.querySelector(containerClass);
  25. var loading = false;
  26. function handleScroll(failed) {
  27. if (loading) return;
  28. if (html.scrollTop + html.clientHeight >= html.scrollHeight - 3000) {
  29. loading = true;
  30. var loadMore = getLoadMore(document);
  31. if (loadMore == null) return;
  32. loadMore.children[0].text = "Loading...";
  33. var url = new URL(loadMore.children[0].href);
  34. url.searchParams.append("scroll", "true");
  35. fetch(url.toString())
  36. .then(function (response) {
  37. if (response.status > 299) throw "error";
  38. return response.text();
  39. })
  40. .then(function (html) {
  41. var parser = new DOMParser();
  42. var doc = parser.parseFromString(html, "text/html");
  43. loadMore.remove();
  44. for (var item of doc.querySelectorAll(itemClass)) {
  45. if (item.className == "timeline-item show-more") continue;
  46. if (isDuplicate(item, itemClass)) continue;
  47. if (isTweet) container.appendChild(item);
  48. else insertBeforeLast(container, item);
  49. }
  50. loading = false;
  51. const newLoadMore = getLoadMore(doc);
  52. if (newLoadMore == null) return;
  53. if (isTweet) container.appendChild(newLoadMore);
  54. else insertBeforeLast(container, newLoadMore);
  55. })
  56. .catch(function (err) {
  57. console.warn("Something went wrong.", err);
  58. if (failed > 3) {
  59. loadMore.children[0].text = "Error";
  60. return;
  61. }
  62. loading = false;
  63. handleScroll((failed || 0) + 1);
  64. });
  65. }
  66. }
  67. window.addEventListener("scroll", () => handleScroll());
  68. };
  69. // @license-end