Sleazy Fork is available in English.

Image Board Enhancer (Rule34, Gelbooru, e621, and more)

Auto Resize images and video on multiple image boards and enlarges thumbnails on mouse hover and adds content type icons to them.

Versão de: 06/07/2020. Veja: a última versão.

// ==UserScript==
// @name         Image Board Enhancer (Rule34, Gelbooru, e621, and more)
// @namespace    ImageBoardEnhancer
// @version      1.2
// @description  Auto Resize images and video on multiple image boards and enlarges thumbnails on mouse hover and adds content type icons to them.
// @author       DanDanDan
// @match        *://rule34.xxx/*
// @match        *://chan.sankakucomplex.com/*
// @match        *://idol.sankakucomplex.com/*
// @match        *://gelbooru.com/*
// @match        *://danbooru.donmai.us/*
// @match        *://konachan.com/*
// @match        *://yande.re/*
// @match        *://safebooru.org/*
// @match        *://rule34.paheal.net/*
// @match        *://rule34hentai.net/*
// @match        *://e621.net/*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @grant       GM.setValue
// @grant       GM.getValue
// ==/UserScript==

(async () => {
  'use strict';

  // vanilla-js-wheel-zoom https://github.com/worka/vanilla-js-wheel-zoom
  (function (a, b) { "object" == typeof exports && "undefined" != typeof module ? module.exports = b() : "function" == typeof define && define.amd ? define(b) : (a = a || self, a.WZoom = b()) })(this, function () { 'use strict'; var k = Math.abs; function a(a) { var b = a.getBoundingClientRect(), c = document, d = c.body, e = c.documentElement, f = window.pageYOffset || e.scrollTop || d.scrollTop, g = window.pageXOffset || e.scrollLeft || d.scrollLeft, h = e.clientTop || d.clientTop || 0, i = e.clientLeft || d.clientLeft || 0, j = b.top + f - h, k = b.left + g - i; return { top: j, left: k } } function b(a, b) { if (a && b) for (var c in b) b.hasOwnProperty(c) && (a[c] = b[c]); return a } function c(a, b, c) { var d = !!(3 < arguments.length && void 0 !== arguments[3]) && arguments[3]; a.addEventListener(b, c, d) } function d(a, b, c) { var d = !!(3 < arguments.length && void 0 !== arguments[3]) && arguments[3]; a.removeEventListener(b, c, d) } function e() { return "ontouchstart" in window || 0 < navigator.MaxTouchPoints || 0 < navigator.msMaxTouchPoints } function f(a) { return "wheel" === a.type || "mousedown" === a.type || "mousemove" === a.type || "mouseup" === a.type ? a.clientX : a.changedTouches[0].clientX } function g(a) { return "wheel" === a.type || "mousedown" === a.type || "mousemove" === a.type || "mouseup" === a.type ? a.clientY : a.changedTouches[0].clientY } function h(a, d) { var f = this, g = 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : {}; this._dropHandler = this._dropHandler.bind(this), this._grabHandler = this._grabHandler.bind(this), this._moveHandler = this._moveHandler.bind(this), this.options = b({ smoothExtinction: !1, onGrab: null, onMove: null, onDrop: null }, g), this.isTouch = e(), this.events = this.isTouch ? { grab: "touchstart", move: "touchmove", drop: "touchend" } : { grab: "mousedown", move: "mousemove", drop: "mouseup" }, this.events.options = !!this.isTouch && { passive: !0 }, this.window = a, this.content = d, c(this.content.$element, this.events.grab, function (a) { (f.isTouch && 1 === a.touches.length || 1 === a.buttons) && f._grabHandler(a) }, this.events.options) } function i(a, b) { var c = b.left, d = b.top, e = b.scale; a.style.transform = "translate3d(".concat(c, "px, ").concat(d, "px, 0px) scale(").concat(e, ")") } function j(a) { var c = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {}; this._init = this._init.bind(this), this._prepare = this._prepare.bind(this), this._computeNewScale = this._computeNewScale.bind(this), this._computeNewPosition = this._computeNewPosition.bind(this), this._transform = this._transform.bind(this); this.content.$element = document.querySelector(a), this.isTouch = e(), this.events = this.isTouch ? { down: "touchstart", up: "touchend" } : { down: "mousedown", up: "mouseup" }, this.events.options = !!this.isTouch && { passive: !0 }, this.content.$element && (this.options = b({ type: "image", width: null, height: null, dragScrollable: !0, dragScrollableOptions: {}, maxScale: 1, speed: 50 }, c), this.window.$element = this.content.$element.parentNode, "image" === this.options.type ? this.content.$element.complete ? this._init() : this.content.$element.onload = this._init : this._init()) } return h.prototype = { constructor: h, window: null, content: null, isTouch: !1, isGrab: !1, events: null, moveTimer: null, options: {}, coordinates: null, speed: null, _grabHandler: function (a) { this.isTouch || a.preventDefault(), this.isGrab = !0, this.coordinates = { left: f(a), top: g(a) }, this.speed = { x: 0, y: 0 }, c(document, this.events.drop, this._dropHandler, this.events.options), c(document, this.events.move, this._moveHandler, this.events.options), "function" == typeof this.options.onGrab && this.options.onGrab() }, _dropHandler: function (a) { this.isTouch || a.preventDefault(), this.isGrab = !1, d(document, this.events.drop, this._dropHandler), d(document, this.events.move, this._moveHandler), "function" == typeof this.options.onDrop && this.options.onDrop() }, _moveHandler: function (a) { this.isTouch || a.preventDefault(); var b = this.window, c = this.content, d = this.speed, e = this.coordinates, h = this.options; d.x = f(a) - e.left, d.y = g(a) - e.top, clearTimeout(this.moveTimer), this.moveTimer = setTimeout(function () { d.x = 0, d.y = 0 }, 50); var j = c.currentLeft + d.x, l = c.currentTop + d.y, m = (c.currentWidth - b.originalWidth) / 2 + c.correctX, n = (c.currentHeight - b.originalHeight) / 2 + c.correctY; k(j) <= m && (c.currentLeft = j), k(l) <= n && (c.currentTop = l), i(c.$element, { left: c.currentLeft, top: c.currentTop, scale: c.currentScale }), e.left = f(a), e.top = g(a), "function" == typeof h.onMove && h.onMove() } }, j.prototype = { constructor: j, isTouch: !1, events: null, content: {}, window: {}, direction: 1, options: null, stack: [], _init: function () { var a = this; this._prepare(), !0 === this.options.dragScrollable && new h(this.window, this.content, this.options.dragScrollableOptions), c(this.window.$element, "wheel", function (b) { b.preventDefault(), a._transform(a._computeNewPosition(a._computeNewScale(b.deltaY), { x: f(b), y: g(b) })) }); var b = !0; c(this.window.$element, this.events.down, function (c) { (a.isTouch && 1 === c.touches.length || 1 === c.buttons) && (b = !1, setTimeout(function () { return b = !0 }, 150)) }, this.events.options), c(this.window.$element, this.events.up, function (c) { b || (a._transform(a._computeNewPosition(1 === a.direction ? a.content.maxScale : a.content.minScale, { x: f(c), y: g(c) })), a.direction *= -1) }, this.events.options) }, _prepare: function () { var b = Math.max, c = Math.min, d = a(this.window.$element); this.window.originalWidth = this.window.$element.offsetWidth, this.window.originalHeight = this.window.$element.offsetHeight, this.window.positionLeft = d.left, this.window.positionTop = d.top, "image" === this.options.type ? (this.content.originalWidth = this.options.width || this.content.$element.naturalWidth, this.content.originalHeight = this.options.height || this.content.$element.naturalHeight) : (this.content.originalWidth = this.options.width || this.content.$element.offsetWidth, this.content.originalHeight = this.options.height || this.content.$element.offsetHeight), this.content.minScale = c(this.window.originalWidth / this.content.originalWidth, this.window.originalHeight / this.content.originalHeight), this.content.maxScale = this.options.maxScale, this.content.currentWidth = this.content.originalWidth * this.content.minScale, this.content.currentHeight = this.content.originalHeight * this.content.minScale, this.content.currentLeft = 0, this.content.currentTop = 0, this.content.currentScale = this.content.minScale, this.content.correctX = b(0, (this.window.originalWidth - this.content.currentWidth) / 2), this.content.correctY = b(0, (this.window.originalHeight - this.content.currentHeight) / 2), this.content.$element.style.transform = "translate3d(0px, 0px, 0px) scale(".concat(this.content.minScale, ")"), "function" == typeof this.options.prepare && this.options.prepare() }, _computeNewScale: function (a) { this.direction = 0 > a ? 1 : -1; var b = this.content, c = b.minScale, d = b.maxScale, e = b.currentScale, f = e + this.direction / this.options.speed; return f < c ? c : f > d ? d : f }, _computeNewPosition: function (a, b) { var c = b.x, d = b.y, e = this.window, f = this.content, g = f.originalWidth * a, h = f.originalHeight * a, i = document, j = i.body, l = i.documentElement, m = e.pageXOffset || l.scrollLeft || j.scrollLeft, n = e.pageYOffset || l.scrollTop || j.scrollTop, o = c + m - e.positionLeft, p = e.originalWidth / 2 - o, q = p + f.currentLeft, r = q * (g / f.currentWidth) - q + f.currentLeft; if (-1 === this.direction && (g - e.originalWidth) / 2 + f.correctX < k(r)) { var s = 0 > r ? -1 : 1; r = ((g - e.originalWidth) / 2 + f.correctX) * s } var t = d + n - e.positionTop, u = e.originalHeight / 2 - t, v = u + f.currentTop, w = v * (h / f.currentHeight) - v + f.currentTop; if (-1 === this.direction && (h - e.originalHeight) / 2 + f.correctY < k(w)) { var x = 0 > w ? -1 : 1; w = ((h - e.originalHeight) / 2 + f.correctY) * x } a === this.content.minScale && (r = w = 0); var y = { currentLeft: f.currentLeft, newLeft: r, currentTop: f.currentTop, newTop: w, currentScale: f.currentScale, newScale: a }; return f.currentWidth = g, f.currentHeight = h, f.currentLeft = r, f.currentTop = w, f.currentScale = a, y }, _transform: function (a) { var b = a.currentLeft, c = a.newLeft, d = a.currentTop, e = a.newTop, f = a.currentScale, g = a.newScale; this.content.$element.style.transform = "translate3d(".concat(c, "px, ").concat(e, "px, 0px) scale(").concat(g, ")"), "function" == typeof this.options.rescale && this.options.rescale() }, _zoom: function (b) { var c = a(this.window.$element); this._transform(this._computeNewPosition(this._computeNewScale(b), { x: c.left + this.window.originalWidth / 2, y: c.top + this.window.originalHeight / 2 })) }, prepare: function () { this._prepare() }, zoomUp: function () { this._zoom(-1) }, zoomDown: function () { this._zoom(1) } }, j.create = function (a, b) { return new j(a, b) }, j });

  try {
    var resizeImageToFit = await GM.getValue('resizeImageToFit', true);
    var resizeVideoToFit = await GM.getValue('resizeVideoToFit', true);
    var autoplayVideos = await GM.getValue('autoplayVideos', true);
    var autoScrollToContent = await GM.getValue('autoScrollToContent', true);
    var updateWithWindowResize = await GM.getValue('updateWithWindowResize', true);
    var updateScrollOnWindowResize = await GM.getValue('updateScrollOnWindowResize', true);
    var showFitButton = await GM.getValue('showFitButton', true);
    var showScrollButton = await GM.getValue('showScrollButton', true);
    var showR34XXXLikeAndFavoriteButtons = await GM.getValue('showR34XXXLikeAndFavoriteButtons', true);
    var removeFluid = await GM.getValue('removeFluid', false);
    var videoVolume = await GM.getValue('videoVolume', 0);
    var enableEnhancedThumbnails = await GM.getValue('enableEnhancedThumbnails', true);
    var alwaysShowScrollbars = await GM.getValue('alwaysShowScrollbars', false);
    var enableZoomableImage = await GM.getValue('enableZoomableImage', true);
    var maxZoom = await GM.getValue('maxZoom', 1);
    var zoomSpeed = await GM.getValue('zoomSpeed', 7);
    var resizeButton = await GM.getValue('resizeButton', 'BracketLeft');
    var scrollButton = await GM.getValue('scrollButton', 'BracketRight');

    var customSites = await GM.getValue('customSites', '{}');

    // Create variables.
    var currentWindowWidth = 0;
    var currentWindowHeight = 0;
    var currentWindowAspect = 0;
    var contentTrueWidth = 0;
    var contentTrueHeight = 0;
    var contentTrueAspect = 0;
    var resizeReady = false;
    var debugMode = false;
    var r34buttons = false;
    var toolbarDOM = '.sidebar form';
    var containerDOM = '#content';
    var imageDOM = '#image';
    var playerDOM;
    var changeKeyboardShortcut = false;
    var containerAlignment = '';
    var thumbnailDOM = '.thumb';
    var thumbnails = [];
    var animationTagIsGif = false;
    var urlParams = new URLSearchParams(window.location.search);
    var unsupportedThumbnailPage = (document.location.hostname.toLowerCase() == 'chan.sankakucomplex.com' || document.location.hostname.toLowerCase() == 'idol.sankakucomplex.com');

    // Per-site DOM selection.
    if (document.location.hostname.toLowerCase() == 'rule34.xxx') { toolbarDOM = '.space'; r34buttons = showR34XXXLikeAndFavoriteButtons; playerDOM = '#gelcomVideoContainer'; animationTagIsGif = true; }
    else if (document.location.hostname.toLowerCase() == 'chan.sankakucomplex.com') { toolbarDOM = '#search-form'; }
    else if (document.location.hostname.toLowerCase() == 'idol.sankakucomplex.com') { toolbarDOM = '#search-form'; }
    else if (document.location.hostname.toLowerCase() == 'gelbooru.com') { toolbarDOM = '#tag-list form'; containerDOM = '.contain-push'; thumbnailDOM = '.thumbnail-preview'; }
    else if (document.location.hostname.toLowerCase() == 'danbooru.donmai.us') { toolbarDOM = '#search-box'; thumbnailDOM = '.post-preview'; }
    else if (document.location.hostname.toLowerCase() == 'konachan.com') { animationTagIsGif = true; }
    else if (document.location.hostname.toLowerCase() == 'yande.re') { }
    else if (document.location.hostname.toLowerCase() == 'safebooru.org') { }
    else if (document.location.hostname.toLowerCase() == 'rule34.paheal.net') { toolbarDOM = '#Navigationleft'; containerDOM = 'article'; imageDOM = '#main_image'; containerAlignment = 'margin-left: auto;'; }
    else if (document.location.hostname.toLowerCase() == 'rule34hentai.net') { toolbarDOM = '#Navigationleft'; containerDOM = 'article'; imageDOM = '#main_image'; playerDOM = '#fluid_video_wrapper_video-id'; containerAlignment = 'margin-left: auto;'; }
    else if (document.location.hostname.toLowerCase() == 'e621.net') { toolbarDOM = '#search-box'; thumbnailDOM = '.post-preview'; animationTagIsGif = true; }
    // Custom site DOM selection.
    else if (customSites[document.location.hostname.toLowerCase()]) {
      var obj = customSites[document.location.hostname.toLowerCase()];
      if (debugMode) console.log(obj, 'obj')
      if (obj.toolbarDOM) toolbarDOM = obj.toolbarDOM;
      if (obj.containerDOM) containerDOM = obj.containerDOM;
      if (obj.imageDOM) imageDOM = obj.imageDOM;
      if (obj.thumbnailDOM) thumbnailDOM = obj.thumbnailDOM
      $("body").append("<button id='ibenhancerDeleteConfigButton' style='position: absolute; top: 0px; right: 0px; z-index: 9999999; color: black; background-color: whitesmoke; font-size: 12px;'>Delete site config.</button>");
      $("#ibenhancerDeleteConfigButton").click(deleteSiteConfig);
    }
    // Default site DOM and add setup button.
    else {
      // Add setup button to websites wihtout a config.
      $("body").append("<button id='ibenhancerSetupButton' style='position: absolute; top: 0px; right: 0px; z-index: 9999999; color: black; background-color: whitesmoke; font-size: 12px;'>Setup Image Board Enhancer</button>");
      $("#ibenhancerSetupButton").click(addSiteConfig);
      console.warn('This site is not supported, but may still work.');
    }

    // Get Thumbnails
    if (enableEnhancedThumbnails) thumbnails = $(thumbnailDOM);

    // Add site config.
    function addSiteConfig() {
      var config = JSON.parse(prompt("Enter config in JSON format.", '{"toolbarDOM": ".sidebar form", "containerDOM": "#content", "imageDOM": "#image", "thumbnailDOM": ".thumb" }'));

      if (config === null || !config || config == {}) {
        alert('Config not valid.');
      } else {
        customSites[document.location.hostname.toLowerCase()] = config;
        if (debugMode) console.log(customSites);
        GM.setValue('customSites', customSites);
        alert('Config saved.')
        location.reload();
      }
    }

    // Delete site config.
    function deleteSiteConfig() {
      delete customSites[document.location.hostname.toLowerCase()];
      GM.setValue('customSites', customSites);
      alert('Config deleted.')
      location.reload();
    }

    // Remove the Gelcom Video player.
    function removeFluidPlayer() {
      if (debugMode) console.log('removeFluidPlayer');
      $(playerDOM).replaceWith($(containerDOM + ' video'));
      $(containerDOM + ' video').attr('id', 'image');
      document.querySelector(imageDOM).outerHTML = document.querySelector(imageDOM).outerHTML; // This removes all event listeners, it seems jquery tries to maintain  them.
      $(containerDOM + ' video').removeAttr('style');
      $(containerDOM + ' video').removeAttr('playsinline');
      $(containerDOM + ' video').removeAttr('webkit-playsinline');
      $(containerDOM + ' video').attr('controls', 'true');
      $(containerDOM + ' video').attr('autoplay', autoplayVideos);
    }

    // Get window size and aspect ratio.
    function getWindowProps() {
      if (debugMode) console.log('getWindowProps');
      currentWindowWidth = document.documentElement.clientWidth;
      currentWindowHeight = document.documentElement.clientHeight;
      if (currentWindowWidth !== 0 && currentWindowHeight !== 0)
        currentWindowAspect = currentWindowWidth / currentWindowHeight;
    }

    // Get the real size of the video or image.
    function getContentProps() {
      if (debugMode) console.log('getContentProps');

      if ($(containerDOM + ' video').length) {
        contentTrueWidth = $(containerDOM + ' video')[0].videoWidth;
        contentTrueHeight = $(containerDOM + ' video')[0].videoHeight;
      }

      else if ($(containerDOM + ' ' + imageDOM).length) {
        var screenImage = $(containerDOM + ' ' + imageDOM);
        var theImage = new Image();
        theImage.src = screenImage.attr("src");
        contentTrueWidth = theImage.width;
        contentTrueHeight = theImage.height;
      }

      if (contentTrueWidth !== 0 && contentTrueHeight !== 0)
        contentTrueAspect = contentTrueWidth / contentTrueHeight;
      resizeReady = true;
    }

    // Resize the image (This resizes the video on some sites eg. sankakucomplex.com)
    function resizeImage() {
      if (debugMode) console.log('resizeImage');

      $(containerDOM + ' ' + imageDOM).css('max-width', '');

      if (currentWindowAspect > contentTrueAspect) {
        $(containerDOM + ' ' + imageDOM)[0].width = currentWindowHeight * contentTrueAspect;
        $(containerDOM + ' ' + imageDOM)[0].height = currentWindowHeight;
      }

      else {
        $(containerDOM + ' ' + imageDOM)[0].width = currentWindowWidth;
        $(containerDOM + ' ' + imageDOM)[0].height = currentWindowWidth / contentTrueAspect;
      }

      // Remove css from images.
      $(containerDOM + ' ' + imageDOM).removeAttr('style');
      if (enableZoomableImage && !$(containerDOM + ' video').length) $(containerDOM + ' ' + imageDOM).css({ cursor: 'zoom-in' });
    }

    // Resize Fluid video player.
    function resizeFluidVideo() {
      if (debugMode) console.log('resizeFluidVideo');

      $(containerDOM + ' ' + playerDOM).css('max-width', '');

      if (currentWindowAspect > contentTrueAspect) {
        $(containerDOM + ' ' + playerDOM).css('width', currentWindowHeight * contentTrueAspect);
        $(containerDOM + ' ' + playerDOM).css('height', currentWindowHeight);
      }

      else {
        $(containerDOM + ' ' + playerDOM).css('width', currentWindowWidth);
        $(containerDOM + ' ' + playerDOM).css('height', currentWindowWidth / contentTrueAspect);
      }
    }

    // Resize default video.
    function resizeVideo() {
      if (debugMode) console.log('resizeVideo');

      $(containerDOM + ' video').css('max-width', '');

      if (currentWindowAspect > contentTrueAspect) {
        $(containerDOM + ' video')[0].width = currentWindowHeight * contentTrueAspect;
        $(containerDOM + ' video')[0].height = currentWindowHeight;
      }

      else {
        $(containerDOM + ' video')[0].width = currentWindowWidth;
        $(containerDOM + ' video').height = currentWindowWidth / contentTrueAspect;
      }
    }

    // Scroll the window to the video or image.
    function scrollToContent(delay) {
      if (debugMode) console.log('scrollToContent');

      setTimeout(function () {
        var contentID;

        if ($(containerDOM + ' ' + imageDOM).length) contentID = containerDOM + ' ' + imageDOM;
        else if ($(containerDOM + ' ' + playerDOM).length) contentID = containerDOM + ' ' + playerDOM;
        else if ($(containerDOM + ' video').length) contentID = containerDOM + ' video';

        $([document.documentElement, document.body]).animate({
          scrollTop: $(contentID).offset().top + 1
        }, 0);
        $([document.documentElement, document.body]).animate({
          scrollLeft: $(contentID).offset().left + 1
        }, 0);
      }, delay);

    }

    // Check if resize is ready and what type of content to resize. 
    function fitContent(delay) {
      if (debugMode) console.log('fitContent');
      setTimeout(function () {
        if (resizeReady) {
          getWindowProps();
          if ($(containerDOM + ' ' + imageDOM).length) {
            resizeImage();
          }

          else if ($(containerDOM + ' ' + playerDOM).length) {
            resizeFluidVideo();
          }

          else if ($(containerDOM + ' video').length) {
            resizeVideo();
          }
        }
      }, delay);


    }

    // Set the video auto play and volume settings.
    function videoSettings() {
      if (debugMode) console.log('videoSettings');

      $(containerDOM + ' video').prop('autoplay', autoplayVideos);
      $(containerDOM + ' video').prop('volume', videoVolume);
      $(containerDOM + ' video').prop('loop', true);
      if (autoplayVideos) $(containerDOM + ' video')[0].play(); else $(containerDOM + ' video')[0].pause();
    }

    // Remove the Gelcom player if present.
    if (removeFluid && $(playerDOM).length) removeFluidPlayer();

    // Get the image properties, resize, and scroll as the page is loading. 
    // If the image loads too quickly it wont fire the event.
    if ($(containerDOM + ' video').length || $(containerDOM + ' ' + imageDOM).length) {

      // Show Scrollbars
      if (alwaysShowScrollbars) $('html').css({ overflow: 'scroll' });

      getContentProps();
      if (resizeImageToFit) fitContent(200);
      if (autoScrollToContent) scrollToContent(200);
    }

    // Add event listener to the image or video.
    if ($(containerDOM + ' video').length) {
      if (debugMode) console.log('Create video event listener');

      videoSettings();
      $(containerDOM + ' video').on('loadedmetadata', function () { //NOTE: replaced 'loadedmetadata' with 'canplay'
        getContentProps();
        if (resizeVideoToFit) fitContent(200);
        if (autoScrollToContent) scrollToContent(200);
        $(containerDOM + ' video').play();

      });
    }

    else if ($(containerDOM + ' ' + imageDOM).length) {
      if (debugMode) console.log('Create image event listener');

      $(containerDOM + ' ' + imageDOM).on('load', function () {
        getContentProps();
        if (resizeImageToFit) fitContent(200);
        if (autoScrollToContent) scrollToContent(200);
      });
    }

    // Add the event listener to the window.
    if (updateWithWindowResize) {
      $(window).resize(function () {
        fitContent(0);
        if (updateScrollOnWindowResize) scrollToContent(0);
      });
    }

    // Setting Functions
    function showSettings() {
      $("#ibenhancerSettings").addClass('show');
    }
    function hideSettings() {
      $("#ibenhancerSettings").removeClass('show');
      location.reload();
    }
    function changeResizeButtonClicked() {
      $('#resizeButton').html('?');
      changeKeyboardShortcut = 'resizeButton';
    }
    function changeScrollButtonClicked() {
      $('#scrollButton').html('?');
      changeKeyboardShortcut = 'scrollButton';
    }
    function saveSettings() {
      GM.setValue('resizeImageToFit', $('#resizeImageToFitCheckbox').is(':checked'));

      GM.setValue('resizeVideoToFit', $('#resizeVideoToFitCheckbox').is(':checked'));

      GM.setValue('autoplayVideos', $('#autoplayVideosCheckbox').is(':checked'));

      GM.setValue('autoScrollToContent', $('#autoScrollToContentCheckbox').is(':checked'));

      GM.setValue('updateScrollOnWindowResize', $('#updateScrollOnWindowResizeCheckbox').is(':checked'));

      GM.setValue('updateWithWindowResize', $('#updateWithWindowResizeCheckbox').is(':checked'));

      GM.setValue('showFitButton', $('#showFitButtonCheckbox').is(':checked'));

      GM.setValue('showScrollButton', $('#showScrollButtonCheckbox').is(':checked'));

      GM.setValue('showR34XXXLikeAndFavoriteButtons', $('#showR34XXXLikeAndFavoriteButtonsCheckbox').is(':checked'));

      GM.setValue('removeFluid', $('#removeFluidCheckbox').is(':checked'));

      GM.setValue('videoVolume', $('#videoVolumeInput').val());

      GM.setValue('enableEnhancedThumbnails', $('#enableEnhancedThumbnailsCheckbox').is(':checked'));

      GM.setValue('alwaysShowScrollbars', $('#alwaysShowScrollbarsCheckbox').is(':checked'));

      GM.setValue('enableZoomableImage', $('#enableZoomableImageCheckbox').is(':checked'));

      GM.setValue('maxZoom', $('#maxZoomInput').val());
      GM.setValue('zoomSpeed', $('#zoomSpeedInput').val());

      GM.setValue('resizeButton', resizeButton);
      GM.setValue('scrollButton', scrollButton);

      hideSettings();

      location.reload();
    }

    // Create the toolbar.
    if ($(containerDOM + ' ' + imageDOM).length || $(containerDOM + ' video').length) {
      if (debugMode) console.log('Create toolbar');

      $(toolbarDOM).after("<div id='ibenhancer'>Image Board Enhancer<br></div>");

      if (showFitButton) {
        $("#ibenhancer").append("<button id='fitContentButton' style='margin-top: 3px;'>Fit</button>");
        $("#fitContentButton").click(function () { getContentProps(); fitContent(0); });
      }

      if (showScrollButton) {
        $("#ibenhancer").append("<button id='scrollContentButton' style='margin-top: 3px;'>Scroll</button>");
        $("#scrollContentButton").click(scrollToContent);
      }

      // Create settings.
      $("#ibenhancer").append("<br><button id='ibenhancerSettingsButton' style='margin-top: 3px;'>Settings</button>");
      $("#ibenhancerSettingsButton").click(showSettings);
      $("#ibenhancer").append(`
        <div id="ibenhancerSettings">
          <label><input id="resizeImageToFitCheckbox" type="checkbox" ` + (resizeImageToFit ? `checked` : ``) + `>resizeImageToFit</label>
          <br>
          <label><input id="resizeVideoToFitCheckbox" type="checkbox" ` + (resizeVideoToFit ? `checked` : ``) + `>resizeVideoToFit</label>
          <br>
          <label><input id="autoplayVideosCheckbox" type="checkbox" ` + (autoplayVideos ? `checked` : ``) + `>autoplayVideos</label>
          <br>
          <label><input id="autoScrollToContentCheckbox" type="checkbox" ` + (autoScrollToContent ? `checked` : ``) + `>autoScrollToContent</label>
          <br>
          <label><input id="updateWithWindowResizeCheckbox" type="checkbox" ` + (updateWithWindowResize ? `checked` : ``) + `>updateWithWindowResize</label>
          <br>
          <label><input id="updateScrollOnWindowResizeCheckbox" type="checkbox" ` + (updateScrollOnWindowResize ? `checked` : ``) + `>updateScrollOnWindowResize</label>
          <br>
          <label><input id="showFitButtonCheckbox" type="checkbox" ` + (showFitButton ? `checked` : ``) + `>showFitButton</label>
          <br>
          <label><input id="showScrollButtonCheckbox" type="checkbox" ` + (showScrollButton ? `checked` : ``) + `>showScrollButton</label>
          <br>
          <label><input id="showR34XXXLikeAndFavoriteButtonsCheckbox" type="checkbox" ` + (showR34XXXLikeAndFavoriteButtons ? `checked` : ``) + `>showR34XXXLikeAndFavoriteButtons</label>
          <br>
          <label><input id="removeFluidCheckbox" type="checkbox" ` + (removeFluid ? `checked` : ``) + `>removeFluid</label>
          <br>
          <label><input id="enableEnhancedThumbnailsCheckbox" type="checkbox" ` + (enableEnhancedThumbnails ? `checked` : ``) + `>enableEnhancedThumbnails</label>
          <br>
          <label><input id="alwaysShowScrollbarsCheckbox" type="checkbox" ` + (alwaysShowScrollbars ? `checked` : ``) + `>alwaysShowScrollbars</label>
          <br>
          <label><input id="enableZoomableImageCheckbox" type="checkbox" ` + (enableZoomableImage ? `checked` : ``) + `>enableZoomableImage</label>
          <br>
          <label>maxZoom<input id="maxZoomInput" type="number" min="1" max="5" step="1" value="` + maxZoom + `" style="width:60px;">1 - 5</label>
          <br>
          <label>zoomSpeed<input id="zoomSpeedInput" type="number" min="1" max="15" step="1" value="` + zoomSpeed + `" style="width:60px;">1 - 15</label>
          <br>
          <label>videoVolume<input id="videoVolumeInput" type="number" min="0" max="1" step="0.01" value="` + videoVolume + `" style="width:60px;">0 - 1</label>
          <br>
          <label><button id="resizeButton">` + resizeButton + `</button> resizeButton</label>
          <br>
          <label><button id="scrollButton">` + scrollButton + `</button> scrollButton</label>
          <br>
          <button id="ibenhancerSettingsSave">Save</button><button id="ibenhancerSettingsCancel">Cancel</button>
          <a href="https://icons8.com/">Icons by Icons8</a>
        </div>
      ` );

      $("#ibenhancerSettingsSave").click(saveSettings);
      $("#ibenhancerSettingsCancel").click(hideSettings);
      $("#resizeButton").click(changeResizeButtonClicked);
      $("#scrollButton").click(changeScrollButtonClicked);

      addGlobalStyle(`
        #ibenhancer {
          background: white !important;
          color: black !important;
          border: solid 1px grey;
          border-radius: 4px;
          padding: 5px;
          width: 170px;
          text-align: center !important;
          margin-top: 5px;
          margin-bottom: 5px;
          margin-right: auto;
          ` + containerAlignment + `
        }
        #ibenhancer, #ibenhancer label {
          font-size: 15px !important;
          font-family: Arial, Helvetica, sans-serif !important;
          font-style: normal !important;
          font-weight: normal !important;
        }
        #ibenhancerSettings {
          position: fixed;
          width: 300px;
          height: 320px;
          left: calc(50vw - 155px);
          top: calc(50vh - 165px);
          background-color: white;
          border: 2px solid black;
          border-radius: 3px;
          overflow-y: auto;
          overflow-x: hidden;
          padding: 10px;
          text-align: left;
          z-index: 999999;
          display: none;
          -webkit-box-sizing: unset;
          -moz-box-sizing: unset;
          box-sizing: unset;
          color: black;
        }
        #ibenhancerSettings input {
          margin: 5px;
          width: auto;
        }
        #ibenhancerSettings input[type=number] {
          border: solid 1px darkgrey;
        }
        #ibenhancer button {
          padding: 3px !important;
          width: auto;
          border: solid 1px darkgrey !important;
          margin: 2px !important;
          border-radius 2px !important;
          background: WhiteSmoke !important;
          cursor: pointer;
        }
        #ibenhancerSettings.show {
          display: block;
        }
        #fitContentButton {
          width: 73px !important;
        }
        #scrollContentButton {
          width: 73px !important;
        }
        #ibenhancerSettingsButton {
          width: 150px !important;
        }
			`);

      // Add the like and favorite button to rule34.xxx
      if (r34buttons) {
        if (debugMode) console.log('r34buttons');

        var likeButtonImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAAC7ElEQVRoge2Zz0sUYRjHP8/MbotkUF462MlTYKnZ9kuKDgYFRdBBqC4R3YLUjS5aVy+VWXnofwjqFHTITpsikpmSh7Bgu3iIpSArtZx5O+iss7M7OLv7zm4D+z3NO+8zz/N8eOd9n/edgbrqCkUSluP26dS4UnRtNP+gSIuib/bwyHwY8YwwnAK4IAC2IXQrg3Tru5vtYcQLDcRHu0xbPW+bvbVdt+NqgwC0qL9WSrfTWoAgkDo2kWrQ6bMmIEDT74Sc1+mwViAo1Fmd/moGguKITnehgPQ87TEDmDXrjBkKyEJL85kAZms6Y4YCYiHXtrIRyOiMqR1k32TfboFzW9kpmNIZVzuImHIFiAcwHdcZVz+IyNUAZso04q90xtUKsn8qdRLYG8B0Zqbz7qLO2FpBxGTLSb5uqF7qjAuu80jb2/40yPHSHldv5pIPTwB0zPTvtC1ZBLTuoYpoScGYKHtg7tCjj85N14hIR+k+N8FtSy4TPgTADoELiDHRNnljj3PTBaIyFQYI9lrpU5PEYg+chntEMhU67qzw+ZJlw2nnOgciorfSVkPi2ubkQJRSX2qTTvkSeO1cb75aKnIj8n1NGHQaORDbrHiOVFUCF+cPjnxy2jmQhG1kapJRmVJC3lYoBzKdHM4CS1XPqFwput1NzxYlUhM+7xTqAYnOPHGvWOABiVAtyVuxwAMSlVriXbHA+2pFpJZ4VyzwgESmlnhWLPCARK2WuJUHslFLftYol+CS/BULih51Kz6XhK2vtord8d4sAuI3T2SQuNmIcFtzYsEljNnEuj4k7332dhWA+NUSq2FldK79/q/V5fhoCCkGVbYYBBQB8asl5kqit3X+emMisdarO7vAUpzy64oVMc4U/derGDKXE0OI0plaqfL9glkwIv91LRHG/LoKQCqoJS/KfC6ovhmGNeDXWQAynRzOCirod9m0c2E1rF4C9QTIlp6jvxT8QHhmmNbR9wceL+j0XVdddUVQ/wDifsCLkXFX6QAAAABJRU5ErkJggg==';
        var favoriteButtonImage = 'data:image/png;base64,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';
        
        $("#ibenhancer").append('<br><img id="like-butt" class="custom-button" src="' + likeButtonImage + '" alt="like"><img id="favorite-butt" class="custom-button" src="' + favoriteButtonImage + '" alt="favorite">');
        $("#like-butt").click(function () {
          $("#stats > ul > li:contains('(vote up)') > a:contains('up')").click();
        });
        $("#favorite-butt").click(function () {
          $("#stats + div > ul > li > a:contains('Add to favorites')").click();
        });

        addGlobalStyle(`
          img.custom-button {
            cursor: pointer;
            width: 35px;
            padding: 3px;
            margin: 0;
            border-radius: 20px;
          }
          .custom-button:hover {
            background-color: rgba(255,255,255,.5);
          }
          .custom-button:active {
            background-color: rgba(255,255,255,1);
          }
			  `);
      }
    }

    function addGlobalStyle(css) {
      if (debugMode) console.log('addGlobalStyle');
      var head, style;
      head = document.getElementsByTagName('head')[0];
      if (!head) { return; }
      style = document.createElement('style');
      style.type = 'text/css';
      style.innerHTML = css;
      head.appendChild(style);
    }

    // Keyboard shortcuts
    document.addEventListener('keyup', (e) => {
      if (debugMode) console.log(e.code)
      if (!changeKeyboardShortcut) {
        if (e.code === resizeButton) { getContentProps(0); fitContent(0); }
        else if (e.code === scrollButton) scrollToContent(0);
      }
      else if (changeKeyboardShortcut == 'resizeButton') {
        resizeButton = e.code;
        $('#resizeButton').html(e.code);
        changeKeyboardShortcut = false;
      }
      else if (changeKeyboardShortcut == 'scrollButton') {
        scrollButton = e.code;
        $('#scrollButton').html(e.code);
        changeKeyboardShortcut = false;
      }
    });

    // Thumbnails

    var gifIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAACGklEQVRoge3YzYtOURwH8A8pMkJempHyLhssyMrCWkqW/gAbG3sLlJ2yQlnYKBvRNAslCy8bb8UwTZFMTF4SylqkHot7NCPPPM99nvmd2zXdb93Oveee8/3+vvec85zze2jwf6GV6bqBhRX6yGakhZtYVLWRHJwt3MOSYP6Oojk4P6fyLgaCNWYUzcG5DR/S/X0sDdZpK5qLcwPepOenWBms1VY0F+c6TKS651gVrNdWNBfnEF6k+pdYE6xZmREYxHh69wprqxDNxbkCT9L7SWysQjQX53I8Tm3eYXMVork4l+FhavceW6sQzcU5gDumNs/tVYj2w9nr9aUT6bwSomXa9YLZfJgZ41gwC9J+0c9H6Wp+fh+ktURjpG5ojNQN0b9a63EQqxVH8yvYif0YxW0cUiRVfzCJa8Fx/INeNsQj+DGtz/i0+hbOpefr/t7obkXEETUiu3Ax3Z9WHPp+delzIrX7FhFAlJFjiessTpXsM6aYaiGIWux7UzncQ58hbBKU2kYZGUzlR5w3Nae3dOhzSfHHw8mIAKKm1s9ULsYDHNA9u7usSGlHIwKIGpHXqdyBq3hWos8wzghaJ1FGRlJ5XJGqVo6oqXUBh7EbX8UnY10RZeQ79uEo9ijWyoTC1FvFJjiW2j5SGP0UpF0KOVLdftA1jjlzaGyM1A2NkbqhMVI3NEbqhsZI3VD20FiH81ZHzJkRaVA3/AZ83criPw8nKgAAAABJRU5ErkJggg=="/>';
    var videoIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAABEElEQVRoge2ZOw6CQBCGP4yPOxgTr0Jj4m1sLT2Lpa2FpQV6Aq08htEaGzAbAkjiLOxO5ksIMJB/+NlndsEwjC5cgTzSIwNICiO55F8ZgGRcDTjXeU0stPi3AEYoQY0RNW1ETYm0NfYYsMYeLGYkNMxIaPgwcgJuwMqD9k/KKbGkVg4cgaWQbluu5oCA+Ms5b4GpkH5druaAgPgc2Dv3D2AtlKOaqzkgKJ4Cd/xUt16NAEyADfAsnr2BHTCTzuXbSMkCODjvXKRyqRlHSqxqdRBPibyxq+l+1QyI0U9RzvQ3afRqpE/0jSNmJDTMSGioMVJd+42xCwYUl4htvQ2NGiPqtt7+XQQYkmzoDzCMGPgA6oe4sKj4htcAAAAASUVORK5CYII="/>';
    var soundIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAADUUlEQVRoge2ay28NcRTHP0ouTURaifQRbERrIUJSm2o3XsEGGyQNrQV2UhW0C+y8/gLBP+AdIULtStKEpGwRjZASj0pb8Wi5HYtzJjN+nblzZ+ZOp0O/yWRuzjm/me/3/h5zfmcGpvH/oRK4AowAj4Cl6dKJhrlAL2C5joepMoqA2UA3Qv4VUK+/f6VJKixmApcR4h+AOrXbvZIJzAAuIISHgJUuX6aEnEPIfgOaDF9mhHQhRMeAzR7+TAhpA8aBPLDTJ2bKC9kB/EaE7C8QF1bIWqAmBq9Q2ASMIgQ7A2LDCKkFvgCfge2R2RWJRmRSW8gkD0IYIVXAbY3PA+1RCBaDFcg/ZgEXkWU3CFHmyGFEiAV0hGwbiCXAe734VeQBWAyiTvYWREyeEg6zWqBfCXUjqUixCBJSBuzzuWaHth1UDr7IAWeBd64bFjp6kaQwDIKEtKv/GbDYw39L/TcL3eRMAHHzmB9GgSJISBPwXGPeMlFMNbIdsIDVfhcZ0IDGmGTitq0AenB6xhxmp9R3Iy7BpIUAzANeaOxJw1eFbAVGkU1b5JuUWshyYL1HbJPGjjCR8D31tcYhWEohOeCp2k54xNsbswOG3V4UzschWEoh5cAxZKhYwEYjvg3vVcrurSdxCCYxR46o/b5hr1P7a8Neq/aBOASTELJA7Z8Me7nafxj2Oaa9LCKhUsMvxbHzt3HDXmbap4qQPXruM+wL9fzRsNur2JDXxdKa7F04k32DEb8X78nerPbHtiHtHskju8tZwHHggeFv0fNdw96gZ7MHgXQfiOs8Yu1/fRhJW9yYcg9EP1QAL/FOUaqRofgTnxTFThrXlIhM1LaVSLHbQoZOzvCfVt91vwvYAWmm8c04PfEGWGT4a4CvyLLbgA9yiBi7Z4KOJDdWfR4iwClG+KbwYTAZW11zOIGTxgxSwlqXu/hwjeSLD7txig/bIrQvCHc56BLJlYOOInPCAg6FbFs0ki7Q3dH4PHAwCsEwmIyS6dbI7ELinyhi22jFea2wyycmzsN0UtGJEB0Dtnj4MyMEpFppAd+RJ7UbmRJivgxd5fJlSghMfD1dr/bMCYG/PxjoB5bp70x9MGDD6xOOnlQZxUAlMsyGERGZ/KhmGmHxB25kbiUBv+xoAAAAAElFTkSuQmCC"/>'
    var flashIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAAA5UlEQVRoge2aQQrCMBREp+I1vE+P1Ru48XwumgO4dqWbBCW0JJUf/5DMg9BSPuEPM/BLWkAIcZQZQADwIl1r7LHIStBsjZgiqZiVzf5ODo00QULYkBA2JIQNKyFPlIfYN8vO85/pxpGz8X5XALeKOvM3ByshU7w+ANyN9jyEV7TMHbEWMpVL2mAlxE1AQtHaYbho0TuyoDzVm9DNZLeO1hLv85VDHy23QwsvIbSOpPgM54g5ilbGsNGidSTRjSO1/EWwTuM9kRA2JIQNCWFj68g0ALiAe5aEmqIZ3N/aq38YEEJ8eAOEJqp6Qxx7zgAAAABJRU5ErkJggg=="/>';

    var straightIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAB+0lEQVRYhe2VvU4bQRSFPyMai7+IGC+Si+QlIqXEdCBh06dza/MEEMlAQ8ET5DHiUPEKSfgRBhnyBogEAW4sLKeYM5phWRbHuysUiSOtZu7snXvO3Ds/8IoXRi7D2INheMcyFDAUshSQ0xcA7Qx5YmHJB8Cp2rjS/BMKwBZwANwBXeBIYwFQBE5E2Abm0xRQBa69gOHvD9BR/0SCSEtAFegr0FegDEzoKwMtj+gckwmLxAIKuJWvx/htyOd3SEBibOFWDlCTkCiSb/LdTIO4pPZQQReA90APt9Ll0JxF/fuZlLwK7Kh/q6CTQB5YBfY11gcq3rwpjd8lIbc1340QYJEDmrjd/1bjsxq7iSN47iZcA2aAadm/1H7wfAYSsAe80RyAj2ovnuGIha35kuxN2a0I33DNv3jiRoZN+azsAibNNrD/mtqa25Tv8rAkI6HL45pXcBfRPmYj5iME7AArScjBlaAcGl8CrvSvB7zDleCHfEqkAHvxRNW8iLmEarL3SKHmAONeP8DVfCNmzmceH8OREGCe1E+y53Cv2gBzvS5i9sSk+nblfVKoeV3B7oEGcCy7g8vEU89vYnKLZij4MSYTRWAbszG7mCP6Xf6J0h7GPHDpCWikGXwY8jMRWxH3uD2RORoiPZOYpuxDHp6OTFEXuW8HT/i+4v/HXxZ7pgDQ+AB1AAAAAElFTkSuQmCC"/>';
    var gayIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABR0lEQVRIidXUuUoEQRgE4G9lI49kA1ERD8zFGxRTfTuDVbxeQgWPFzEQM48HkNXAI9FgerAde9adEQMLmoauv6t+pv5p/jsaFevfq+r0VTSojKoGDQzj8g96IRJ/j/ayz1aKKRziHi+4RhsLBfHhOgabeIwuplYu7oe6b5iOxE+xjH7M4ii6uFy417PBYSReRAMngd9P8HE2pQb3gVhKkWhhBs0u4l2Dfw3EYIIbxW5o4lXN4O8CsVg4X8eT7oGmgv+GA58Z5L/+ODoFsTZWcRadzacEi5jyOUUXWMN2QfwBY6G+EZrJTXvCRqLjfHVC5zGWAnfVqwFMykbxFm9h38ZIonYoGDxXMYjRlI1mq4RfCQY3dQ22gsCx9Mt7Hvi9ugbjsmBzkzkMyDrPxTuYqGtANk1lD2BHNhi/xgh2fA1+zy87/x/4AGsUjtWIwZ1aAAAAAElFTkSuQmCC"/>';
    var lesbianIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAABmklEQVRYhe3VzWoUQRSG4Sciiegsso8guFDJBIxZZPwBb0SvRO9AJUyMczEJBrJNFm4muhVF14KKJDAxLuqEaYZJVUUHImE+KKrofr9zTnV3nWaq/1xL6GIfP2Psx7WlBncDr/ABvzJctWbRwxGOTxkDbOAqdiu42bMk3w7zAdbRwbUYnQh4GMxbLOAlHmI+w1UV0QvDF9zNcMvBHOP1BDik9zWQdp5LfhvPcE/a4QDtDF/L6Ual64VC1/Abj/EmPGsFTxX3PqDVQrB3wXVwP9b9gqeK+xFQqxDsW4Nrxfp7wTOWu1QwnabLjfVMzIOCZ2yu0YufY85+KCPcYqw/FjwnMT/lCtiO+UkhWJO7Ij3anYLn6Yh3rNpS9zuQzm8t9xy3MvyK4TG8UyjUhmEjyhVRy63ga7DdUnKYw1YYDiPRA+krnscjqe1ez3CtWPcMW/FmxK7SnNSMcj+jvUhU4o5i59U/o6baUv/uNwLu4gVuFrh+FLdoQjoJPCkOf9+IJqZpATOZe9Xv8V9ynfsTOIump+BiFjDVuesPLa+Y7uSJITcAAAAASUVORK5CYII="/>';
    var transIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAABQElEQVRYhe2WzWrCQBSFP93W7jT2QbpX+wBiwQepiyKlXdUHstiXyM4/EATfwKKl6q6tXeQOCalNZuwNguTAZSCcOefcmWEykOPEKCjr7V29isoBnKEdoCDlAVNlbWtUgAnBdpgxaWsyM58BV5oBPKAHDIEtsAPG8q3K784rMk8lQBv4iIjFaw3MD5irBGgD3yLSB2rAhVQdeImYzGPm/4ZH2Pl9Aq9LuBJVzQA9ws7TMBDus2aAkYjWLLgN4Q41A2xEtGTBvRTuzsUg7SZ0+VcY7pdmgIWM1xZahrNIZDkGeJXxzkKrE5ujAg94J9jbbgLvQTgroKwZAOAW+BSDAcFpL0ndEHS8F05L29ygBbzx91W8BJpZmRuUgSfAjxj7wCMZLHsa1H6xZ/ckc4bLTZe25Ee9sE++AsciP4Q51PADChhdsZzcLLgAAAAASUVORK5CYII="/>';
    var trapIcon = '<img width="36px" height="36px" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAABw0lEQVRoge2aTU7DMBCFPyrogp4AIXGKcojeA4mLUCgHgHbFKdggRFcIbgGrFhYtC2AFErCIU0WOaZz4p1Pwk6xk0rHnvYwnsatAGR1gBMyBb2FtDgwVx0qMBBCuauc2QvJMdG2cI6NLxm1m45yrlgojv5ZFx1XWTK2a0KErllAzxZqwnjG6o2vNuExVU000FuJaM777G8fbdAhgi1tT4BjwnRFXWPGxeWqtBZIQaUhCpCEJkYYkRBr+tZAWcAfcR7Ibo2pt01P2QyQ72FrrUB0vItmNsewO7AAfwCewG8Gu4rNA3YwcAFvAJTCJYDth2R14VOe9SHYVnwVsdojFHd6zaleRbD1+Lax6R1iFtENcC/j4F8XXNNxw6ZwyYkDTO+olo6Ey0gZOgSnZi22grkVF3cevyX9QuJ63QcB4wQaeUhbyFDBesKllIval2XvAdaD4CxIhptaJ5jPWfvfOz8fAbTIxE34v9ncECzlWBPsW/W4QLORNnb9a9MtrRKSQPpmYoxDE6voHGzhkPJ9LlJXuYdKisQCn5bcv/JmMmIS8qKPEj2r21XFu4zykvLyQ1s5shHTIvv2YCSCst5kSsa2T/gH6y/OoeKJUAQAAAABJRU5ErkJggg=="/>';
    var threeDIcon = '<img width="36px" height="36px" src=" data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TtSIVBTuIiGSoThb8QhylikWwUNoKrTqYXPohNGlIUlwcBdeCgx+LVQcXZ10dXAVB8APEydFJ0UVK/F9SaBHjwXE/3t173L0DhFqJqWbbGKBqlpGMRcVMdkUMvKIDQwB6MS4xU4+nFtLwHF/38PH1LsKzvM/9ObqVnMkAn0g8y3TDIl4nnt60dM77xCFWlBTic+JRgy5I/Mh12eU3zgWHBZ4ZMtLJOeIQsVhoYbmFWdFQiaeIw4qqUb6QcVnhvMVZLVVY4578hcGctpziOs1BxLCIOBIQIaOCDZRgIUKrRoqJJO1HPfwDjj9BLplcG2DkmEcZKiTHD/4Hv7s185MTblIwCrS/2PbHMBDYBepV2/4+tu36CeB/Bq60pr9cA2Y+Sa82tfAR0LMNXFw3NXkPuNwB+p90yZAcyU9TyOeB9zP6pizQdwt0rbq9NfZx+gCkqaulG+DgEBgpUPaax7s7W3v790yjvx+GhXKv1Px9cwAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+QGHgATDBbeK4EAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAB30lEQVRYw+3Wz4tNYRzH8RcuMkXM+JH8Wih2sjJKSX6nlLKwuZQoCzuxIzU7/4IysaCUSBZufmSjqJmtH1MjC8KQ8auIabo2z9Sdp+fcc87cuzyferrd5znn0/s8z/l+vodKlSpVqjRNs8JvD/ZgBzZjI5ZgDn7hLZ7hOp5neD3FtsR8E7/xAyMYwp02PuBluLHIuIZaBlCzxHiCtVlAoyXNLnQBqIkx9LcDGsLxQD4XK3ECPyOjkQJAj8J8DUtxEPcTUB/QF5sNo97mSAcik48lgGJdTEDdKPvyX4oMGh0AwePo2kmsy4OYjzU4GR3ZZEY1lQE6nNil01kX7895CQ+1KfuiQH0J78swu8SxfcF5POxC/o1jIppbXhZoWXiK19jSIVAzayEF1AgJPg8rsA+vWtZX4zYWdQDUG2KlVWN5OzSBz3iAM9HaKuzsAGh7Ym64zJGlcmdxB0CnEg9/bwqoHhK0XQ7sTsy9myHMuVDJrRrEp6k/9fCS/cUV7AqdfgHW4yz+RSX6NeRU0dbRi724myj3USxsNarPoCke7VJzfY9NsdGREgbfcaxgMOaNRmje01TDzbBtB7AVG0JI9YQ2MY4XodquhoAsmzl/8A1vwofZrfBlUalSpUqV8vQfDaH2dxUuD8sAAAAASUVORK5CYII="/>';
    
    if(urlParams.get('page') && urlParams.get('page').toLowerCase() == 'favorites') unsupportedThumbnailPage = true; // Add method to remove favorite.

    if (!unsupportedThumbnailPage && thumbnails.length > 0) {
      $('body').append(`
        <a href="#" id="thumbPlusPreviewLink" style="">
          <div id="thumbPlusPreview" class="">
            <div id="thumbPlusPreviewImage"></div>
            <div style="position: relative;">
              <span id="thumbPlusPreviewGif" class="thumbPlusPreviewIcon">` + gifIcon + `</span>
              <span id="thumbPlusPreviewVideo" class="thumbPlusPreviewIcon">` + videoIcon + `</span>
              <span id="thumbPlusPreviewFlash" class="thumbPlusPreviewIcon">` + flashIcon + `</span>
              <span id="thumbPlusPreviewSound" class="thumbPlusPreviewIcon">` + soundIcon + `</span>
              <span id="thumbPlusPreview3D" class="thumbPlusPreviewIcon">` + threeDIcon + `</span>
              <span id="thumbPlusPreviewStraight" class="thumbPlusPreviewIcon">` + straightIcon + `</span>
              <span id="thumbPlusPreviewGay" class="thumbPlusPreviewIcon">` + gayIcon + `</span>
              <span id="thumbPlusPreviewLesbian" class="thumbPlusPreviewIcon">` + lesbianIcon + `</span>
              <span id="thumbPlusPreviewTrans" class="thumbPlusPreviewIcon">` + transIcon + `</span>
              <span id="thumbPlusPreviewTrap" class="thumbPlusPreviewIcon">` + trapIcon + `</span>
            </div>
          </div>
        </a>
      `);
      $('#thumbPlusPreview').css({ backgroundColor: $('body').css("background-color"), backgroundImage: $('body').css("background-image") });
      $('#thumbPlusPreviewContextMenuRemoveFavorite').click(function(){alert('Clicked')});
      $('#thumbPlusPreview').mouseout(function () {
        $('#thumbPlusPreview').removeClass('show gif video sound flash straight gay lesbian trans threed trap');
      })

      thumbnails.each(function () {
        $(this).mouseover(function () {
          thumbnailMouseOver(this);
        })
      });

      function thumbnailMouseOver(thumb) {
        if ($('img', thumb).attr('oldtitle') && $('img', thumb).attr('oldtitle') != '') // oldtitle is used for gelbooru
          var title = $('img', thumb).attr('oldtitle');
        else
          var title = $('img', thumb).attr('title');

        var tags = title.split(' ').filter(function (el) {
          if (el) return el.toLowerCase();
        });

        if (tags.includes("webm")) $('#thumbPlusPreview').addClass('video');
        else if (tags.includes("mp4")) $('#thumbPlusPreview').addClass('video');
        else if (tags.includes("animated_gif")) $('#thumbPlusPreview').addClass('gif');
        else if (tags.includes("flash")) $('#thumbPlusPreview').addClass('flash');
        else if (tags.includes("flash_animation")) $('#thumbPlusPreview').addClass('flash');
        else if (tags.includes("video")) $('#thumbPlusPreview').addClass('video');
        else if (tags.includes("animated") && animationTagIsGif && !tags.includes("sound")) $('#thumbPlusPreview').addClass('gif');
        else if (tags.includes("animated")) $('#thumbPlusPreview').addClass('video');

        if (tags.includes("sound")) $('#thumbPlusPreview').addClass('sound');
        else if (tags.includes("audio")) $('#thumbPlusPreview').addClass('sound');
        else if (tags.includes("has_sound")) $('#thumbPlusPreview').addClass('sound');
        else if (tags.includes("has_audio")) $('#thumbPlusPreview').addClass('sound');
        else if (tags.includes("video_with_sound")) $('#thumbPlusPreview').addClass('sound');

        if (tags.includes("hetero")) $('#thumbPlusPreview').addClass('straight');
        else if (tags.includes("straight")) $('#thumbPlusPreview').addClass('straight');
        else if (tags.includes("male/female")) $('#thumbPlusPreview').addClass('straight');

        if (tags.includes("yaoi")) $('#thumbPlusPreview').addClass('gay');
        else if (tags.includes("gay")) $('#thumbPlusPreview').addClass('gay');
        else if (tags.includes("male/male")) $('#thumbPlusPreview').addClass('gay');

        if (tags.includes("yuri")) $('#thumbPlusPreview').addClass('lesbian');
        else if (tags.includes("lesbian")) $('#thumbPlusPreview').addClass('lesbian');
        else if (tags.includes("female/female")) $('#thumbPlusPreview').addClass('lesbian');

        if (tags.includes("futanari")) $('#thumbPlusPreview').addClass('trans');
        else if (tags.includes("newhalf")) $('#thumbPlusPreview').addClass('trans');
        else if (tags.includes("dickgirl")) $('#thumbPlusPreview').addClass('trans');
        else if (tags.includes("shemale")) $('#thumbPlusPreview').addClass('trans');
        else if (tags.includes("intersex")) $('#thumbPlusPreview').addClass('trans');

        if (tags.includes("trap")) $('#thumbPlusPreview').addClass('trap');
        else if (tags.includes("otoko no ko")) $('#thumbPlusPreview').addClass('trap');

        if (tags.includes("3d")) $('#thumbPlusPreview').addClass('threed');

        $('#thumbPlusPreview').css({ top: thumb.offsetTop, left: thumb.offsetLeft });
        $('#thumbPlusPreviewImage').css({ backgroundImage: 'url(' + $('img', thumb).attr('src') + ')' });
        $('#thumbPlusPreviewImage').attr('title', title);

        var height = $('img', thumb).height() * 1.555;
        if (height) $('#thumbPlusPreviewImage').css({ height: $('img', thumb).height() * 1.555 });

        if ($(thumb).attr('href'))
          $('#thumbPlusPreviewLink').attr('href', $(thumb).attr('href'));
        else
          $('#thumbPlusPreviewLink').attr('href', $('a', thumb).attr('href'));

        $('#thumbPlusPreview').addClass('show');
      }
      addGlobalStyle(`
        #thumbPlusPreview {
          display: none;
          position: absolute;
          z-index: 9999;
          top: 0;
          left: 0;
          border: 2px solid black;
          border-radius: 5px;
          flex-direction: column !important;
          align-content: center !important;
          justify-content: center !important;
          align-items: center !important;
          flex-grow: 4 !important; 
          margin-left: -50px;
          margin-top: -50px;
          width: 280px;
          min-height: 280px;
          max-height: 380px;
        }
        #thumbPlusPreview.show {
          display: flex;
        }
        #thumbPlusPreviewImage {
          width: calc(100% * 0.833);
          height: calc(100% * 0.833);
          margin-left: auto;
          margin-right: auto;
          background-repeat: no-repeat;
          background-position: center top;
          background-size: contain;
        }
        .thumbPlusPreviewIcon {
          display: none;
          height: 36px;
          width: 36px;
          background-color: #fff5;
          border-radius: 3px;
          padding: 2px;
        }
        #thumbPlusPreview.gif #thumbPlusPreviewGif,
        #thumbPlusPreview.video #thumbPlusPreviewVideo,
        #thumbPlusPreview.sound #thumbPlusPreviewSound,
        #thumbPlusPreview.flash #thumbPlusPreviewFlash,
        #thumbPlusPreview.straight #thumbPlusPreviewStraight,
        #thumbPlusPreview.gay #thumbPlusPreviewGay,
        #thumbPlusPreview.lesbian #thumbPlusPreviewLesbian,
        #thumbPlusPreview.trans #thumbPlusPreviewTrans,
        #thumbPlusPreview.trap #thumbPlusPreviewTrap,
        #thumbPlusPreview.threed #thumbPlusPreview3D  {
          display: inline-block;
        }
	    `);
    }

    var xIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24"><path d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z"/></svg>';

    function closeZoom() {
      $("#IBEZoomableImageContainer").remove();
      if (alwaysShowScrollbars)
        $("html").css({ overflowX: 'scroll', overflowY: 'scroll' });
      else
        $("html").css({ overflowX: 'auto', overflowY: 'auto' });
    }

    function addZoomable() {
      if ($(containerDOM + ' ' + imageDOM)[0].tagName == "IMG") {
        $('body').append(`
          <div id="IBEZoomableImageContainer" class="show">
            <div id="IBEZoomableImage">
              <div id="IBEZoomableContent">
                <img src="` + $(containerDOM + ' ' + imageDOM).attr('src') + `"/>
              </div>
            </div>
            <div id="IBEZoomableImageClose">`+ xIcon + `</div>
          </div>
        `);
        $("#IBEZoomableImageClose").click(function(){history.back();});
        addGlobalStyle(` 
          #IBEZoomableImageContainer {
            position: fixed;
            margin: 0px;
            width: 100vw;
            height: 100vh;
            left: 0px;
            top: 0px;
            background-color: #0009;
            z-index: 99999;
            display: none;
          }
          #IBEZoomableImageContainer.show {
            display: block;
          }
          #IBEZoomableImage {
            position: absolute;
            width: 100%;
            height: 100%;
            background-color:blue;
            cursor: grab;
            display: flex;
            align-items: center;
            justify-content: center;
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 100%;
            border: 0;
            background-color: gray;
            overflow: hidden;
          }
          #IBEZoomableContent {
            width: `+ contentTrueWidth + `px;
            hieght: `+ contentTrueHeight + `px;
          }
          #IBEZoomableImageContainer img {
            display: block;
            height: auto;
            margin: auto;
        }
          #IBEZoomableImageClose {
            position: absolute;
            width: 30px;
            height: 30px;
            background-color: #0005;
            color: white;
            cursor: pointer;
            fill: white;
          }
          #IBEZoomableImageClose:hover {
            background-color: black;
          }
          #IBEZoomableImageClose svg {
            margin: 5px;
          }
        `);

        var imageElement = document.getElementById('IBEZoomableContent').querySelector('img');

        if (imageElement.complete) {
          init();
        } else {
          imageElement.onload = init;
        }
        
        var wzoom;
        
        function init() {
          var maxScale = parseInt(maxZoom);
          wzoom = WZoom.create('#IBEZoomableContent', {
            type: 'html',
            width: imageElement.naturalWidth,
            height: imageElement.naturalHeight,
            maxScale: maxScale,
            speed: zoomSpeed,
            dragScrollableOptions: {
              onGrab: function () {
                document.getElementById('IBEZoomableImage').style.cursor = 'grabbing';
              },
              onDrop: function () {
                document.getElementById('IBEZoomableImage').style.cursor = 'grab';
              }
            }
          });

          window.addEventListener('resize', function () {
            wzoom.prepare();
          });

        }

      }
    }
    if (enableZoomableImage && $(containerDOM + ' ' + imageDOM).length && !$(containerDOM + ' video').length) {

      window.addEventListener('popstate', function (event) {
        if (event.state && event.state.id && event.state.id == 'default') closeZoom();
        else if (event.state && event.state.id && event.state.id == 'zoom') openZoom();
      });

      function openZoom() {
        addZoomable();
        $("html").css({ overflowX: 'hidden', overflowY: 'hidden' });
      }

      $(containerDOM + ' ' + imageDOM).click(function(){
        history.replaceState({ id: "default" }, 'title');
        history.pushState({ id: "zoom" }, '');
        openZoom();
      })

    }

  }
  catch (e) {
    console.error(e);
  }
  if (debugMode) console.log('End of script.');
})();