AQW Wiki Link Preview

Adds image previews for links on the official AQW Wiki

  1. // ==UserScript==
  2. // @name AQW Wiki Link Preview
  3. // @description Adds image previews for links on the official AQW Wiki
  4. // @namespace Lamp
  5. // @license GNU GPLv3
  6. // @include *aqwwiki.wikidot.com/*
  7. // @include *wqa.wikidot.com/*
  8. // @version 2.0.1
  9. // ==/UserScript==
  10.  
  11.  
  12.  
  13.  
  14. let currentMousePos = { x: -1, y: -1 };
  15. let hoverTimeout = null;
  16. let lastHoveredElement = null;
  17.  
  18. document.addEventListener("mousemove", (event) => {
  19. currentMousePos.x = event.pageX;
  20. currentMousePos.y = event.pageY;
  21. });
  22.  
  23. const excludedTerms = [
  24. "image-tags",
  25. "classes-skills",
  26. "AQWPassive",
  27. "quantserve",
  28. "statcounter",
  29. "avatar.php"
  30. ];
  31.  
  32. function isNotHidden(img) {
  33. let currentElement = img.parentElement;
  34. while (currentElement) {
  35. const inlineStyle = currentElement.getAttribute("style");
  36. if (inlineStyle && inlineStyle.includes("display:none")) {
  37. return false;
  38. }
  39. currentElement = currentElement.parentElement;
  40. }
  41. return true;
  42. }
  43.  
  44. function isValidImageElement(img) {
  45. const src = img.getAttribute("src");
  46.  
  47. if (!src || excludedTerms.some(term => src.includes(term))) {
  48. return false;
  49. }
  50.  
  51. return true;
  52. }
  53.  
  54. async function fetchAndParse(url) {
  55. const response = await fetch(url);
  56. const text = await response.text();
  57. const parser = new DOMParser();
  58. const doc = parser.parseFromString(text, "text/html");
  59.  
  60. return doc;
  61. }
  62.  
  63. function extractImages(doc) {
  64. const images = [];
  65. const hasFemale = Array.from(doc.querySelectorAll("em")).some(em => em.textContent.trim() === "Female");
  66.  
  67. if (hasFemale) {
  68. const maleContainers = doc.querySelectorAll("#wiki-tab-0-0");
  69. const femaleContainers = doc.querySelectorAll("#wiki-tab-0-1");
  70.  
  71. let maleImage = null;
  72. let femaleImage = null;
  73.  
  74. for (const maleContainer of maleContainers) {
  75. const img = maleContainer.querySelector("img");
  76. if (isValidImageElement(img)) {
  77. maleImage = img;
  78. break;
  79. }
  80. }
  81.  
  82. for (const femaleContainer of femaleContainers) {
  83. const img = femaleContainer.querySelector("img");
  84. if (isValidImageElement(img)) {
  85. femaleImage = img;
  86. break;
  87. }
  88. }
  89.  
  90. if (maleImage) {
  91. images.push(maleImage.getAttribute("src"));
  92. }
  93. if (femaleImage) {
  94. images.push(femaleImage.getAttribute("src"));
  95. }
  96. }
  97.  
  98. if (images.length === 0) {
  99. const allImages = Array.from(doc.querySelectorAll("img"));
  100. for (const img of allImages) {
  101. if (isValidImageElement(img) && isNotHidden(img)) {
  102. images.push(img.getAttribute("src"));
  103. break;
  104. }
  105. }
  106. }
  107.  
  108. return images;
  109. }
  110.  
  111. function createPreviewElement(images) {
  112. const preview = document.createElement("div");
  113. preview.id = "preview";
  114. preview.style.position = "absolute";
  115. preview.style.zIndex = "9999";
  116. preview.style.backgroundColor = "transparent";
  117. preview.style.display = "flex";
  118.  
  119. let maxHeight = "400px";
  120. if (images.length === 2) {
  121. maxHeight = "600px";
  122. preview.style.width = "auto";
  123. }
  124.  
  125. images.forEach((src) => {
  126. const img = document.createElement("img");
  127. img.src = src;
  128. img.style.maxHeight = maxHeight;
  129. img.style.display = "block";
  130. preview.appendChild(img);
  131. });
  132.  
  133. return preview;
  134. }
  135.  
  136. function removeAllPreviews() {
  137. document.querySelectorAll("#preview").forEach(preview => preview.remove());
  138. if (hoverTimeout) {
  139. clearTimeout(hoverTimeout);
  140. hoverTimeout = null;
  141. }
  142. lastHoveredElement = null;
  143. }
  144.  
  145. document.addEventListener("mouseenter", (event) => {
  146. if (event.target.matches("a.item, div.item > div > a, div.title > a, div.list-pages-item > p > a, div.collapsible-block-content > a, div.collapsible-block-content > p > a, div.yui-content > div > a, div.yui-content > div > p > a, div.list-pages-box > p > a, #page-content > ul > li > a, #page-content > ul > li > span > a, #page-content > ul > li > ul > li > a, div.yui-content > div > ul > li > ul > li > a, #page-content > a, #page-content > p > a, tr > td > a, .yui-content > div > ul > li > a")) {
  147. removeAllPreviews();
  148.  
  149. const url = event.target.href;
  150. lastHoveredElement = event.target;
  151.  
  152. hoverTimeout = setTimeout(async () => {
  153. if (lastHoveredElement !== event.target) return;
  154.  
  155. const doc = await fetchAndParse(url);
  156. const images = extractImages(doc);
  157. const preview = createPreviewElement(images);
  158.  
  159. document.body.appendChild(preview);
  160. preview.style.top = `${currentMousePos.y - 200}px`;
  161. preview.style.left = `${currentMousePos.x + 100}px`;
  162.  
  163. const removePreview = () => removeAllPreviews();
  164. event.target.addEventListener("mouseleave", removePreview, { once: true });
  165. preview.addEventListener("mouseleave", removePreview, { once: true });
  166. }, 250);
  167. }
  168. }, true);
  169.  
  170.  
  171.  
  172.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址