Derpibooru Comment Preview

Hover preview for links to other comments

נכון ליום 20-09-2016. ראה הגרסה האחרונה.

  1. // ==UserScript==
  2. // @name Derpibooru Comment Preview
  3. // @description Hover preview for links to other comments
  4. // @version 1.0.17
  5. // @author Marker
  6. // @namespace https://github.com/marktaiwan/
  7. // @homepageURL https://github.com/marktaiwan/Derpibooru-Quote-Preview
  8. // @supportURL https://github.com/marktaiwan/Derpibooru-Quote-Preview/issues
  9. // @include /^https?://(www\.)?(derpiboo\.ru|derpibooru\.org|trixiebooru\.org)/\d{1,}(\?.{1,}|/|\.html)?/
  10. // @include /^https?://(www\.)?(derpiboo\.ru|derpibooru\.org|trixiebooru\.org)/lists/user_comments/\d{1,}(\?.{1,}|/|\.html)?/
  11. // @include /^https?://(www\.)?(derpiboo\.ru|derpibooru\.org|trixiebooru\.org)/lists/my_comments(\?.{1,}/|\.html)?$/
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17. function displayHover(comment, link) {
  18. const PADDING = 5; // px
  19.  
  20. if (!hover) return;
  21.  
  22. var preview = document.querySelectorAll('#hover_preview');
  23. if (preview !== null) {
  24. for (i = 0; i < preview.length; i++) {
  25. preview[i].parentNode.removeChild(preview[i]);
  26. }
  27. }
  28.  
  29. comment.id = 'hover_preview';
  30. comment.style.position = 'absolute';
  31. comment.style.maxWidth = '980px';
  32. comment.style.minWidth = '490px';
  33.  
  34. // Make spoiler visible
  35. var i;
  36. var list = comment.querySelectorAll('span.spoiler, span.imgspoiler');
  37. if (list !== null) {
  38. for (i = 0; i < list.length; i++) {
  39. list[i].style.color = '#333';
  40. list[i].style.backgroundColor = '#f7d4d4';
  41. }
  42. }
  43. // img spoiler
  44. list = comment.querySelectorAll('span.imgspoiler img');
  45. if (list !== null) {
  46. for (i = 0; i < list.length; i++) {
  47. list[i].style.visibility = 'visible';
  48. }
  49. }
  50.  
  51. var container = document.getElementById('image_comments') || document.getElementById('content');
  52. if (container) {
  53. container.appendChild(comment);
  54. }
  55.  
  56. // calculate link position
  57. var linkRect = link.getBoundingClientRect();
  58. var linkTop = linkRect.top + viewportPos().top;
  59. var linkLeft = linkRect.left + viewportPos().left;
  60.  
  61. var commentRect = comment.getBoundingClientRect();
  62. var commentTop;
  63. var commentLeft;
  64.  
  65. // When there is room, place the preview above the link
  66. // otherwise place it to the right and aligns it to the top of viewport
  67. if (linkRect.top > commentRect.height + PADDING) {
  68.  
  69. commentTop = linkTop - commentRect.height - PADDING;
  70. commentLeft = linkLeft;
  71.  
  72. } else {
  73.  
  74. commentTop = viewportPos().top + PADDING;
  75. commentLeft = linkLeft + linkRect.width + PADDING;
  76.  
  77. }
  78.  
  79. comment.style.top = commentTop + 'px';
  80. comment.style.left = commentLeft + 'px';
  81. }
  82.  
  83. function linkEnter(e) {
  84.  
  85. // filtering
  86. if (!e.target.matches('.communication__body__text a[href*="#comment_"], .communication__body__text a[href*="#comment_"]>*')) return;
  87.  
  88. hover = true;
  89.  
  90. // Example: e.target.hash == "#comment_5430424"
  91. var sourceLink = e.target.parentNode.matches('a[href*="#comment_"]') ? e.target.parentNode : e.target;
  92. var targetID = sourceLink.hash.slice(9);
  93. var targetComment = document.getElementById('comment_' + targetID);
  94.  
  95. // ignore quotes
  96. // this is terrible
  97. if (sourceLink.nextElementSibling &&
  98. sourceLink.nextElementSibling.nextElementSibling &&
  99. sourceLink.nextElementSibling.nextElementSibling.matches('blockquote')) return;
  100.  
  101.  
  102. if (targetComment !== null) {
  103. // Post exist on current page
  104.  
  105. if (!elementInViewport(targetComment)) {
  106. displayHover(targetComment.cloneNode(true), sourceLink);
  107. }
  108.  
  109. // Highlight linked post
  110. targetComment.firstChild.style.backgroundColor = 'rgba(230,230,30,0.3)';
  111.  
  112. } else {
  113. // External post, display from cached response if possible
  114. if (fetchCache[targetID] !== undefined) {
  115.  
  116. displayHover(fetchCache[targetID], sourceLink);
  117.  
  118. } else {
  119.  
  120. fetch(window.location.origin + '/comment/' + targetID + '.html')
  121. .then((response) => response.text())
  122. .then((text) => {
  123. var d = document.createElement('div');
  124. d.innerHTML = text;
  125. fetchCache[targetID] = d.firstChild;
  126. displayHover(d.firstChild, sourceLink);
  127. });
  128.  
  129. }
  130.  
  131. }
  132. }
  133.  
  134. function linkLeave(e) {
  135.  
  136. // filtering
  137. if (!e.target.matches('.communication__body__text a[href*="#comment_"], .communication__body__text a[href*="#comment_"]>*')) return;
  138.  
  139. hover = false;
  140.  
  141. var i;
  142. var sourceLink = e.target.parentNode.matches('a[href*="#comment_"]') ? e.target.parentNode : e.target;
  143. var targetID = sourceLink.hash.slice(9);
  144. var targetComment = document.getElementById('comment_' + targetID);
  145. var preview = document.getElementById('hover_preview');
  146.  
  147. // ignore quotes
  148. // this is terrible
  149. if (sourceLink.nextElementSibling &&
  150. sourceLink.nextElementSibling.nextElementSibling &&
  151. sourceLink.nextElementSibling.nextElementSibling.matches('blockquote')) return;
  152.  
  153. //remove highlight
  154. if (targetComment !== null) {
  155. targetComment.firstChild.style.backgroundColor = '';
  156. }
  157. if (preview !== null) preview.parentNode.removeChild(preview);
  158.  
  159. }
  160.  
  161. // Chrome/Firefox compatibility hack for getting viewport position
  162. function viewportPos() {
  163. return {
  164. top: (document.documentElement.scrollTop || document.body.scrollTop),
  165. left: (document.documentElement.scrollLeft || document.body.scrollLeft)
  166. };
  167. }
  168.  
  169. function elementInViewport(el) {
  170. var rect = el.getBoundingClientRect();
  171.  
  172. return (
  173. rect.top >= 0 &&
  174. rect.left >= 0 &&
  175. rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
  176. rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  177. );
  178. }
  179.  
  180. var hover = false;
  181. var fetchCache = {};
  182. var container = document.getElementById('image_comments') || document.getElementById('content');
  183.  
  184. if (container) {
  185. container.addEventListener('mouseover', linkEnter);
  186. container.addEventListener('mouseout', linkLeave);
  187. }
  188. })();