Sankaku Previewer

try to take over the world!

  1. // ==UserScript==
  2. // @name Sankaku Previewer
  3. // @namespace https://greasyfork.org/ja/scripts/370717-sankaku-previewer
  4. // @version 1.3
  5. // @description try to take over the world!
  6. // @author You
  7. // @match https://chan.sankakucomplex.com/*
  8. // @grant GM_xmlhttpRequest
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. const body = document.getElementsByTagName('body')[0]
  13.  
  14. createPreview()
  15. addEvent()
  16. body.onmousemove = () => { addEvent() }
  17.  
  18. function xpath(path, d){
  19. if(!d) d = document
  20. return document.evaluate(path, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
  21. }
  22.  
  23. function xpathList(nodes, path) {
  24. const xpathObj = xpath(path)
  25. for (let i = 0; i < xpathObj.snapshotLength; i++){
  26. nodes.push(xpathObj.snapshotItem(i))
  27. }
  28. return nodes
  29. }
  30.  
  31. let timer
  32. function addEvent() {
  33. let nodes = []
  34. nodes = xpathList(nodes, "//div[@class='content']//img[not(@preview)]")
  35. for(let i = 0; i < nodes.length; i++){
  36. const imgEle = nodes[i]
  37.  
  38.  
  39. imgEle.setAttribute('preview', '')
  40. imgEle.setAttribute('flag', '0')
  41. const tags = imgEle.getAttribute('title')
  42.  
  43. if (tags == null || tags.match(/animated/)) {continue}
  44.  
  45. imgEle.removeAttribute('title')
  46.  
  47. //もっとエレガントに
  48. imgEle.onmouseover = () => {
  49. imgEle.setAttribute('flag', '0')
  50. if (imgEle.getAttribute('preview')) {
  51. openPreview(imgEle.getAttribute('preview'))
  52. }
  53. else if (!imgEle.getAttribute('preview')) {
  54. const parent = imgEle.parentNode
  55. const req = new GM_xmlhttpRequest({
  56. method: "GET",
  57. url: parent.href,
  58. responseType: 'text',
  59. onload: (res) => {
  60. const raw_xml = res.response
  61. const parser = new DOMParser()
  62. const html = parser.parseFromString(raw_xml, "text/html")
  63. const img = html.getElementById('lowres') || html.getElementById('highres')
  64. if (imgEle.getAttribute('flag') != 0) return
  65. const blob_req = new GM_xmlhttpRequest({
  66. method: "GET",
  67. url: img.href,
  68. responseType: 'blob',
  69. onload: (res) => {
  70. if (imgEle.getAttribute('flag') != 0) return
  71. const img_src = window.URL.createObjectURL(res.response)
  72. imgEle.setAttribute('preview', img_src)
  73. openPreview(imgEle.getAttribute('preview'))
  74. }
  75. })
  76. }
  77. })
  78. }
  79. }
  80.  
  81. imgEle.onmouseout = () => {
  82. imgEle.setAttribute('flag', '1')
  83. closePreview()
  84. }
  85. imgEle.setAttribute('preview', '')
  86. }
  87. }
  88.  
  89. function createPreview() {
  90. const preview = document.createElement('img')
  91. const content = document.getElementById('content')
  92.  
  93. preview.setAttribute('id', 'sankaku-preview')
  94. preview.height = document.documentElement.clientHeight
  95. preview.style.pointerEvents = 'none'
  96. preview.style.zIndex = '99999'
  97. preview.style.opacity = '0.9'
  98. preview.style.left = '0px'
  99. preview.style.top = '0px'
  100. preview.style.position = 'fixed'
  101. preview.style.display = 'none'
  102. content.appendChild(preview)
  103. }
  104.  
  105. function openPreview(src) {
  106. const preview = document.getElementById('sankaku-preview')
  107.  
  108. preview.setAttribute('src', src)
  109. preview.style.display = 'inline'
  110. }
  111.  
  112. function closePreview() {
  113. const preview = document.getElementById('sankaku-preview')
  114.  
  115. preview.style.display = 'none'
  116. }
  117. })();