yande.re refine

yande.re refining

Od 09.03.2020.. Pogledajte najnovija verzija.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name           yande.re refine
// @namespace      https://greasyfork.org/scripts/5250
// @description    yande.re refining
// @include        *://behoimi.org/*
// @include        *://www.behoimi.org/*
// @include        *://*.donmai.us/*
// @include        *://konachan.tld/*
// @include        *://yande.re/*
// @include        *://chan.sankakucomplex.com/*
// @version        2020.03.09
// @grant          none
// ==/UserScript==

//If true, each added page retains its paginator.  If false, elements are smoothly joined together.
var pageBreak = false

//Minimum amount of window left to scroll, maintained by loading more pages.
var scrollBuffer = 600

//Time (in ms) the script will wait for a response from the next page before attempting to fetch the page again.  If the script gets trapped in a loop trying to load the next page, increase this value.
var timeToFailure = 15000

var previewLocked = false

//============================================================================
//=========================Script initialization==============================
//============================================================================

var nextPage, mainTable, mainParent, pending, timeout, iframe

if (typeof customF != 'undefined') customF()

injectGlobalStyle()
initialize()

function initialize() {
  //Stop if inside an iframe
  if (window != window.top || scrollBuffer == 0) return

  //Stop if no "table"
  mainTable = getMainTable(document)
  if (!mainTable) return

  //Stop if no paginator
  var paginator = getPaginator(document)
  if (!paginator) return

  //Stop if no more pages
  nextPage = getNextPage(paginator)
  if (!nextPage) return

  injectStyle()
  hijackClick()
  processItems()

  //Hide the blacklist sidebar, since this script breaks the tag totals and post unhiding.
  var sidebar = document.getElementById('blacklisted-sidebar')
  if (sidebar) sidebar.style.display = 'none'

  //Other important variables:
  scrollBuffer += window.innerHeight
  mainParent = mainTable.parentNode
  pending = false

  iframe = document.createElement('iframe')
  iframe.width = iframe.height = 0
  iframe.style.visibility = 'hidden'
  document.body.appendChild(iframe)

  //Slight delay so that Danbooru's initialize_edit_links() has time to hide all the edit boxes on the Comment index
  iframe.addEventListener(
    'load',
    function(e) {
      setTimeout(appendNewContent, 100)
    },
    false
  )

  //Stop if empty page
  if (
    /<p>(Nothing to display.|Nobody here but us chickens!)<.p>/.test(
      mainTable.innerHTML
    )
  )
    return

  //Add copy of paginator to the top
  mainParent.insertBefore(paginator.cloneNode(true), mainParent.firstChild)

  if (!pageBreak) paginator.style.display = 'none'
  //Hide bottom paginator
  else {
    //Reposition bottom paginator and add horizontal break
    mainTable.parentNode.insertBefore(
      document.createElement('hr'),
      mainTable.nextSibling
    )
    mainTable.parentNode.insertBefore(paginator, mainTable.nextSibling)
  }

  //Listen for scroll events
  window.addEventListener('scroll', testScrollPosition, false)
  testScrollPosition()
}

//============================================================================
//============================Script functions================================
//============================================================================

//Some pages match multiple "tables", so order is important.
function getMainTable(source) {
  //Special case: Sankaku post index with Auto Paging enabled
  if (
    /sankaku/.test(location.host) &&
    /auto_page=1/.test(document.cookie) &&
    /^(post(\/|\/index\/?)?|\/)$/.test(location.pathname)
  )
    return null

  var xpath = [
    ".//div[@id='c-favorites']//div[@id='posts']", // Danbooru (/favorites)
    ".//div[@id='posts']/div", // Danbooru; don't want to fall through to the wrong xpath if no posts ("<article>") on first page.
    ".//div[@id='c-pools']//section/article/..", // Danbooru (/pools/####)

    ".//div[@id='a-index']/table[not(contains(@class,'search'))]", // Danbooru (/forum_topics, ...), take care that this doesn't catch comments containing tables
    ".//div[@id='a-index']", // Danbooru (/comments, ...)

    ".//table[contains(@class,'highlight')]", // large number of pages
    ".//div[@id='content']/div/div/div/div/span[@class='author']/../../../..", // Sankaku: note search
    ".//div[contains(@id,'comment-list')]/div/..", // comment index
    ".//*[not(contains(@id,'popular'))]/span[contains(@class,'thumb')]/a/../..", // post/index, pool/show, note/index
    ".//li/div/a[contains(@class,'thumb')]/../../..", // post/index, note/index
    ".//div[@id='content']//table/tbody/tr[@class='even']/../..", // user/index, wiki/history
    ".//div[@id='content']/div/table", // 3dbooru user records
    ".//div[@id='forum']" // forum/show
  ]

  for (var i = 0; i < xpath.length; i++) {
    getMainTable = (function(query) {
      return function(source) {
        var mTable = new XPathEvaluator().evaluate(
          query,
          source,
          null,
          XPathResult.FIRST_ORDERED_NODE_TYPE,
          null
        ).singleNodeValue
        if (!mTable || !pageBreak) return mTable

        //Special case: Danbooru's /favorites lacks the extra DIV that /posts has, which causes issues with the paginator/page break.
        var xDiv = document.createElement('div')
        xDiv.style.overflow = 'hidden'
        mTable.parentNode.insertBefore(xDiv, mTable)
        xDiv.appendChild(mTable)
        return xDiv
      }
    })(xpath[i])

    var result = getMainTable(source)
    if (result) {
      //alert("UPW main table query: "+xpath[i]+"\n\n"+location.pathname);
      return result
    }
  }

  return null
}

function getPaginator(source) {
  var pager = new XPathEvaluator().evaluate(
    "descendant-or-self::div[@id='paginator' or @class='paginator' or @id='paginater']",
    source,
    null,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
  ).singleNodeValue

  // Need clear:none to prevent the 2nd page from being pushed to below the sidebar on the Post index... but we don't want this when viewing a specific pool,
  // because then the paginator is shoved to the right of the last images on a page.  Other sites have issues with clear:none as well, like //yande.re/post.
  if (
    pager &&
    location.host.indexOf('donmai.') >= 0 &&
    document.getElementById('sidebar')
  )
    pager.style.clear = 'none'

  return pager
}

function getNextPage(source) {
  let page = getPaginator(source)
  if (page)
    page = new XPathEvaluator().evaluate(
      ".//a[@alt='next' or @rel='next' or contains(text(),'>') or contains(text(),'Next')]",
      page,
      null,
      XPathResult.FIRST_ORDERED_NODE_TYPE,
      null
    ).singleNodeValue

  return page && page.href
}

function testScrollPosition() {
  if (!nextPage) testScrollPosition = function() {}
  //Take the max of the two heights for browser compatibility
  else if (
    !pending &&
    window.pageYOffset + scrollBuffer >
      Math.max(
        document.documentElement.scrollHeight,
        document.documentElement.offsetHeight
      )
  ) {
    pending = true
    timeout = setTimeout(function() {
      pending = false
      testScrollPosition()
    }, timeToFailure)
    iframe.contentDocument.location.replace(nextPage)
  }
}

function appendNewContent() {
  //Make sure page is correct.  Using 'indexOf' instead of '!=' because links like "https://danbooru.donmai.us/pools?page=2&search%5Border%5D=" become "https://danbooru.donmai.us/pools?page=2" in the iframe href.
  clearTimeout(timeout)
  if (nextPage.indexOf(iframe.contentDocument.location.href) < 0) {
    setTimeout(function() {
      pending = false
    }, 1000)
    return
  }

  //Copy content from retrived page to current page, but leave off certain headers, labels, etc...
  var sourcePaginator = document.adoptNode(getPaginator(iframe.contentDocument))
  var nextElem,
    deleteMe,
    source = document.adoptNode(getMainTable(iframe.contentDocument))

  if (
    /<p>(Nothing to display.|Nobody here but us chickens!)<.p>/.test(
      source.innerHTML
    )
  )
    nextPage = null
  else {
    nextPage = getNextPage(sourcePaginator)

    if (pageBreak) mainParent.appendChild(source)
    else {
      //Hide elements separating one table from the next (h1 is used for user names on comment index)
      var rems = source.querySelectorAll('h2, h3, h4, thead, tfood')
      for (var i = 0; i < rems.length; i++) rems[i].style.display = 'none'

      //Move contents of next table into current one
      var fragment = document.createDocumentFragment()
      while ((nextElem = source.firstChild)) fragment.appendChild(nextElem)
      mainTable.appendChild(fragment)
    }
  }

  //Add the paginator at the bottom if needed.
  if (!nextPage || pageBreak) mainParent.appendChild(sourcePaginator)
  if (pageBreak && nextPage)
    mainParent.appendChild(document.createElement('hr'))

  //Clear the pending request marker and check position again
  processItems()
  pending = false
  testScrollPosition()
}

function injectGlobalStyle() {
  const s = document.createElement('style')
  s.innerHTML = `
#title { display: none !important; }
#header { margin: 0 !important; text-align: center; }
#header ul { float: none !important; display: inline-block;}
`
  document.body.appendChild(s)
}

function injectStyle() {
  const s = document.createElement('style')
  s.innerHTML = `
a.directlink { display: none !important; }
.sidebar { position: absolute; top: 10px; right: 10px; }
.sidebar #tag-sidebar, .sidebar h5 { display: none !important; }
div.content { width: 100vw; }
ul#post-list-posts { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; }
ul#post-list-posts li { width: auto !important; margin: auto; padding: 5px; transition: .3s ease-out; overflow: visible; }
ul#post-list-posts li img, ul#post-list-posts li .inner { width: auto !important; height: auto !important; }
ul#post-list-posts li img { border-radius: 5px; }
ul#post-list-posts li a { transition: .3s ease-out; }
ul#post-list-posts li a.liked img { border: 2px solid pink; }
ul#post-list-posts li:hover { transform: scale(1.3); z-index: 1;  }
ul#post-list-posts li:hover a { opacity: 1; box-shadow: 6px 6px 20px 0px rgba(50, 50, 50, 0.75); }
.hidden { display: none !important; }
.preivew-dialog { position: fixed; top: 0; left: 0; height: 100vh; width: 100vw; background: rgba(0,0,0,0.7); z-index: 100; }
.preivew-dialog iframe { position: absolute; height: 90vh; width: 80vw; top: 50%; left: 50%; transform: translate(-50%, -50%); background: grey; border: none; border-radius: 5px; overflow: hidden; }
    `
  document.body.appendChild(s)
}

function hijackClick() {}

let previewIframe, previewIframeDialog

function initPreviewIframe() {
  previewIframeDialog = document.createElement('div')
  previewIframeDialog.addClassName('preivew-dialog hidden')
  previewIframe = document.createElement('iframe')
  previewIframeDialog.appendChild(previewIframe)
  previewIframeDialog.onclick = e => {
    if (e.target === previewIframeDialog)
      previewIframeDialog.classList.toggle('hidden', true)
  }
  document.body.appendChild(previewIframeDialog)
}

function processItems() {
  document
    .querySelectorAll('ul#post-list-posts li a.thumb:not([_init])')
    .forEach(a => {
      a.setAttribute('_init', true)
      const url = a.href
      a.href = 'javascript:;'
      if (!url) return
      const id = url.split('/').slice(-1)[0]
      let lastClicked = -Infinity
      let timer = null
      let liked = false
      a.onclick = e => {
        e.preventDefault()
        if (Date.now() - lastClicked < 500) {
          if (liked) vote(id, 2)
          else vote(id, 3)
          liked = !liked
          a.classList.toggle('liked', liked)
          clearTimeout(timer)
        } else {
          lastClicked = +Date.now()
          timer = setTimeout(() => openUrl(url), 600)
        }
        return false
      }
    })
}

function openUrl(url) {
  console.log(url)
  if (!previewIframe) initPreviewIframe()
  previewIframe.onload = () => {
    if (previewIframe.contentWindow.location.href !== url) {
      location.href = previewIframe.contentWindow.location.href
      previewIframeDialog.classList.toggle('hidden', true)
      previewIframe.onload = null
    }
  }
  previewIframe.src = url
  previewIframeDialog.classList.toggle('hidden', false)
}

async function vote(id, score) {
  let body = new FormData()
  body.append('id', id)
  body.append('score', score)
  const rawResponse = await fetch('https://yande.re/post/vote.json', {
    method: 'POST',
    headers: {
      'X-CSRF-Token': document.querySelector('meta[name=csrf-token]').attributes
        .content.value
    },
    body
  })
  const content = await rawResponse.json()

  console.log(content)
}