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.

اعتبارا من 23-02-2021. شاهد أحدث إصدار.

// ==UserScript==
// @name        Image Board Enhancer (Rule34, Gelbooru, e621, and more)
// @namespace   ImageBoardEnhancer
// @version     1.3.5
// @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       *://konachan.net/*
// @match       *://yande.re/*
// @match       *://safebooru.org/*
// @match       *://rule34.paheal.net/*
// @match       *://rule34hentai.net/*
// @match       *://e621.net/*
// @match       *://e926.net/*
// @match       *://tbib.org/*
// @match       *://behoimi.org/*
// @require     https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @require     https://greasyfork.org/scripts/420841-image-board-enhancer-icons/code/Image%20Board%20Enhancer%20Icons.js?version=901260
// @require     https://greasyfork.org/scripts/420842-vanilla-js-wheel-zoom/code/vanilla-js-wheel-zoom.js?version=895198
// @grant       GM.setValue
// @grant       GM.getValue
// ==/UserScript==
(async () => {
  'use strict';

  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 iconSize = await GM.getValue('iconSize', 36);

    var hiddenIcons = await GM.getValue('hiddenIcons', []);

    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 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 = false;
    var waitingForZoom = false;
    var wzoom;
    var placeSettingsAfter = true;

    var debugMode = false;
    var keepThumbOpen = false;

    // Per-site DOM selection.
    if (document.location.hostname.toLowerCase() == 'rule34.xxx') { toolbarDOM = '.sidebar > div'; r34buttons = showR34XXXLikeAndFavoriteButtons; playerDOM = '#gelcomVideoContainer'; animationTagIsGif = true; }
    else if (document.location.hostname.toLowerCase() == 'chan.sankakucomplex.com' || document.location.hostname.toLowerCase() == 'idol.sankakucomplex.com') { toolbarDOM = '#search-form'; }
    else if (document.location.hostname.toLowerCase() == 'gelbooru.com') { toolbarDOM = 'section.aside > *'; placeSettingsAfter = false; containerDOM = '#container'; 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' || document.location.hostname.toLowerCase() == 'konachan.net') { animationTagIsGif = true; }
    else if (document.location.hostname.toLowerCase() == 'yande.re') { }
    else if (document.location.hostname.toLowerCase() == 'safebooru.org') { }
    else if (document.location.hostname.toLowerCase() == 'tbib.org') { }
    else if (document.location.hostname.toLowerCase() == 'behoimi.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' || document.location.hostname.toLowerCase() == 'e926.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.');
    }

    // 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');
      // Create a new video player with the source of the original. This will break blob content if any sites start using DRM.
      $(playerDOM).replaceWith("<video src='" + ($(containerDOM + ' video').attr('src') || $(containerDOM + ' video source').attr('src')) + "' controls='true' />");

      // Old method.
      // $(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 () {
        if (waitingForZoom) {
          console.log('Waited for zoom');
          setTimeout(openZoom, 16);
          waitingForZoom = false;
        }
        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');
      $("#ibenhancerSettings-blocker").addClass('show');
      $("html").addClass('ibenhancerSettingVisible');
    }
    function hideSettings() {
      $("#ibenhancerSettings").removeClass('show');
      $("#ibenhancerSettings-blocker").removeClass('show');
      $("html").removeClass('ibenhancerSettingVisible');

      // Reset the form to original state.
      $("#resizeImageToFitCheckbox").prop('checked', resizeImageToFit);
      $("#resizeVideoToFitCheckbox").prop('checked', resizeVideoToFit);
      $("#autoplayVideosCheckbox").prop('checked', autoplayVideos);
      $("#autoScrollToContentCheckbox").prop('checked', autoScrollToContent);
      $("#updateWithWindowResizeCheckbox").prop('checked', updateWithWindowResize);
      $("#updateScrollOnWindowResizeCheckbox").prop('checked', updateScrollOnWindowResize);
      $("#showFitButtonCheckbox").prop('checked', showFitButton);
      $("#showScrollButtonCheckbox").prop('checked', showScrollButton);
      $("#showR34XXXLikeAndFavoriteButtonsCheckbox").prop('checked', showR34XXXLikeAndFavoriteButtons);
      $("#removeFluidCheckbox").prop('checked', removeFluid);
      $("#enableEnhancedThumbnailsCheckbox").prop('checked', enableEnhancedThumbnails);
      $("#alwaysShowScrollbarsCheckbox").prop('checked', alwaysShowScrollbars);
      $("#enableZoomableImageCheckbox").prop('checked', enableZoomableImage);

      $("#maxZoomInput").val(maxZoom);
      $("#zoomSpeedInput").val(zoomSpeed);
      $("#videoVolumeInput").val(videoVolume);

      $("#iconSizeInput").val(iconSize);

      $("#hideIconGifCheckbox").prop('checked', hiddenIcons.includes("gif"));
      $("#hideIconVideoCheckbox").prop('checked', hiddenIcons.includes("video"));
      $("#hideIconSoundCheckbox").prop('checked', hiddenIcons.includes("sound"));
      $("#hideIconFlashCheckbox").prop('checked', hiddenIcons.includes("flash"));
      $("#hideIconStraightCheckbox").prop('checked', hiddenIcons.includes("straight"));
      $("#hideIconGayCheckbox").prop('checked', hiddenIcons.includes("gay"));
      $("#hideIconLesbianCheckbox").prop('checked', hiddenIcons.includes("lesbian"));
      $("#hideIconTransCheckbox").prop('checked', hiddenIcons.includes("trans"));
      $("#hideIconTrapCheckbox").prop('checked', hiddenIcons.includes("trap"));
      $("#hideIconThreeDCheckbox").prop('checked', hiddenIcons.includes("threeD"));
      $("#hideIconLoliCheckbox").prop('checked', hiddenIcons.includes("loli"));
      $("#hideIconShotaCheckbox").prop('checked', hiddenIcons.includes("shota"));
      $("#hideIconGoreDeathCheckbox").prop('checked', hiddenIcons.includes("goreDeath"));
      $("#hideIconPregnantCheckbox").prop('checked', hiddenIcons.includes("pregnant"));
      $("#hideIconBestialityCheckbox").prop('checked', hiddenIcons.includes("bestiality"));
      $("#hideIconFeetCheckbox").prop('checked', hiddenIcons.includes("feet"));
      $("#hideIconBondageCheckbox").prop('checked', hiddenIcons.includes("bondage"));
      $("#hideIconPoopCheckbox").prop('checked', hiddenIcons.includes("poop"));
      $("#hideIconPissCheckbox").prop('checked', hiddenIcons.includes("piss"));
      $("#hideIconGroupCheckbox").prop('checked', hiddenIcons.includes("group"));
      $("#hideIconIncestCheckbox").prop('checked', hiddenIcons.includes("incest"));
      $("#hideIconSafeCheckbox").prop('checked', hiddenIcons.includes("safe"));
      $("#hideIconQuestionableCheckbox").prop('checked', hiddenIcons.includes("questionable"));
      $("#hideIconExplicitCheckbox").prop('checked', hiddenIcons.includes("explicit"));
      $("#hideIconBukkakeCheckbox").prop('checked', hiddenIcons.includes("bukkake"));
      $("#hideIconTentaclesCheckbox").prop('checked', hiddenIcons.includes("tentacles"));
      $("#hideIconRapeCheckbox").prop('checked', hiddenIcons.includes("rape"));
      $("#hideIconPublicCheckbox").prop('checked', hiddenIcons.includes("public"));
      $("#hideIconFurryCheckbox").prop('checked', hiddenIcons.includes("furry"));
      $("#hideIconFatCheckbox").prop('checked', hiddenIcons.includes("fat"));
      $("#hideIconHypnosisCheckbox").prop('checked', hiddenIcons.includes("hypnosis"));
      $("#hideIconNtrCheckbox").prop('checked', hiddenIcons.includes("ntr"));
      $("#hideIconFemdomCheckbox").prop('checked', hiddenIcons.includes("femdom"));
    }
    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('iconSize', $('#iconSizeInput').val());

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

      var newHiddenIcons = [];

      if ($('#hideIconGifCheckbox').is(':checked')) newHiddenIcons.push('gif');
      if ($('#hideIconVideoCheckbox').is(':checked')) newHiddenIcons.push('video');
      if ($('#hideIconSoundCheckbox').is(':checked')) newHiddenIcons.push('sound');
      if ($('#hideIconFlashCheckbox').is(':checked')) newHiddenIcons.push('flash');
      if ($('#hideIconStraightCheckbox').is(':checked')) newHiddenIcons.push('straight');
      if ($('#hideIconGayCheckbox').is(':checked')) newHiddenIcons.push('gay');
      if ($('#hideIconLesbianCheckbox').is(':checked')) newHiddenIcons.push('lesbian');
      if ($('#hideIconTransCheckbox').is(':checked')) newHiddenIcons.push('trans');
      if ($('#hideIconTrapCheckbox').is(':checked')) newHiddenIcons.push('trap');
      if ($('#hideIconThreeDCheckbox').is(':checked')) newHiddenIcons.push('threeD');
      if ($('#hideIconLoliCheckbox').is(':checked')) newHiddenIcons.push('loli');
      if ($('#hideIconShotaCheckbox').is(':checked')) newHiddenIcons.push('shota');
      if ($('#hideIconGoreDeathCheckbox').is(':checked')) newHiddenIcons.push('goreDeath');
      if ($('#hideIconPregnantCheckbox').is(':checked')) newHiddenIcons.push('pregnant');
      if ($('#hideIconBestialityCheckbox').is(':checked')) newHiddenIcons.push('bestiality');
      if ($('#hideIconFeetCheckbox').is(':checked')) newHiddenIcons.push('feet');
      if ($('#hideIconBondageCheckbox').is(':checked')) newHiddenIcons.push('bondage');
      if ($('#hideIconPoopCheckbox').is(':checked')) newHiddenIcons.push('poop');
      if ($('#hideIconPissCheckbox').is(':checked')) newHiddenIcons.push('piss');
      if ($('#hideIconGroupCheckbox').is(':checked')) newHiddenIcons.push('group');
      if ($('#hideIconIncestCheckbox').is(':checked')) newHiddenIcons.push('incest');
      if ($('#hideIconBukkakeCheckbox').is(':checked')) newHiddenIcons.push('bukkake');
      if ($('#hideIconTentaclesCheckbox').is(':checked')) newHiddenIcons.push('tentacles');
      if ($('#hideIconRapeCheckbox').is(':checked')) newHiddenIcons.push('rape');
      if ($('#hideIconPublicCheckbox').is(':checked')) newHiddenIcons.push('public');
      if ($('#hideIconFurryCheckbox').is(':checked')) newHiddenIcons.push('furry');
      if ($('#hideIconFatCheckbox').is(':checked')) newHiddenIcons.push('fat');
      if ($('#hideIconHypnosisCheckbox').is(':checked')) newHiddenIcons.push('hypnosis');
      if ($('#hideIconNtrCheckbox').is(':checked')) newHiddenIcons.push('ntr');
      if ($('#hideIconFemdomCheckbox').is(':checked')) newHiddenIcons.push('femdom');
      if ($('#hideIconSafeCheckbox').is(':checked')) newHiddenIcons.push('safe');
      if ($('#hideIconQuestionableCheckbox').is(':checked')) newHiddenIcons.push('questionable');
      if ($('#hideIconExplicitCheckbox').is(':checked')) newHiddenIcons.push('explicit');

      GM.setValue('hiddenIcons', newHiddenIcons);

      location.reload();
    }

    // Create the toolbar. Only create if there isn't one already and if there is a image, video or thumbnails.
    if ($('#ibenhancer').length < 1 && ($(containerDOM + ' ' + imageDOM).length || $(containerDOM + ' video').length || $(thumbnailDOM).length)) {
      if (debugMode) console.log('Create toolbar');

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

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

      if (showScrollButton && ($(containerDOM + ' ' + imageDOM).length || $(containerDOM + ' video').length)) {
        $("#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-blocker"></div>
        <div id="ibenhancerSettings">
          <div id="ibenhancerSettings-options">
            <label><input id="resizeImageToFitCheckbox" type="checkbox" ` + (resizeImageToFit ? `checked` : ``) + `>Resize images to fit screen.</label>
            <br>
            <label><input id="resizeVideoToFitCheckbox" type="checkbox" ` + (resizeVideoToFit ? `checked` : ``) + `>Resize videos to fit screen.</label>
            <br>
            <label><input id="autoplayVideosCheckbox" type="checkbox" ` + (autoplayVideos ? `checked` : ``) + `>Autoplay videos.</label>
            <br>
            <label><input id="autoScrollToContentCheckbox" type="checkbox" ` + (autoScrollToContent ? `checked` : ``) + `>Scroll to content.</label>
            <br>
            <label><input id="updateWithWindowResizeCheckbox" type="checkbox" ` + (updateWithWindowResize ? `checked` : ``) + `>Resize content with window.</label>
            <br>
            <label><input id="updateScrollOnWindowResizeCheckbox" type="checkbox" ` + (updateScrollOnWindowResize ? `checked` : ``) + `>Scroll to content on window resize.</label>
            <br>
            <label><input id="showFitButtonCheckbox" type="checkbox" ` + (showFitButton ? `checked` : ``) + `>Show fit button.</label>
            <br>
            <label><input id="showScrollButtonCheckbox" type="checkbox" ` + (showScrollButton ? `checked` : ``) + `>Show scroll button.</label>
            <br>
            <label><input id="showR34XXXLikeAndFavoriteButtonsCheckbox" type="checkbox" ` + (showR34XXXLikeAndFavoriteButtons ? `checked` : ``) + `>Show like and favorite buttons on rule34.xxx.</label>
            <br>
            <label><input id="removeFluidCheckbox" type="checkbox" ` + (removeFluid ? `checked` : ``) + `>Remove fluid video player.</label>
            <br>
            <label><input id="enableEnhancedThumbnailsCheckbox" type="checkbox" ` + (enableEnhancedThumbnails ? `checked` : ``) + `>Enable enhanced thumbnails.</label>
            <br>
            <label><input id="alwaysShowScrollbarsCheckbox" type="checkbox" ` + (alwaysShowScrollbars ? `checked` : ``) + `>Always show scrollbars. (Fixes resize issue.)</label>
            <br>
            <label><input id="enableZoomableImageCheckbox" type="checkbox" ` + (enableZoomableImage ? `checked` : ``) + `>Enable zoomable image.</label>
            <br>
            <label>Max zoom: <input id="maxZoomInput" type="number" min="1" max="5" step="1" value="` + maxZoom + `" style="width:60px;">1 - 5</label>
            <br>
            <label>Zoom speed: <input id="zoomSpeedInput" type="number" min="1" max="15" step="1" value="` + zoomSpeed + `" style="width:60px;">1 - 15</label>
            <br>
            <label>Video volume: <input id="videoVolumeInput" type="number" min="0" max="1" step="0.01" value="` + videoVolume + `" style="width:60px;">0 - 1</label>
            <br>
            <label>Icon Size: <input id="iconSizeInput" type="number" min="16" max="50" step="1" value="` + iconSize + `" style="width:60px;">16 - 50</label>
            <br>
            <label>Resize keyboard shurtcut: <button id="resizeButton">` + resizeButton + `</button></label>
            <br>
            <label>Scroll keyboard shurtcut: <button id="scrollButton">` + scrollButton + `</button></label>
            <br>
            Hide icons: <button id="hideAllIconsButton">Hide All</button> <button id="unhideAllIconsButton">Unhide All</button>
            <br>
            <span id="iconCheckboxes">
              <label><input id="hideIconGifCheckbox" type="checkbox" ` + (hiddenIcons.includes("gif") ? `checked` : ``) + `>GIF</label>
              <label><input id="hideIconVideoCheckbox" type="checkbox" ` + (hiddenIcons.includes("video") ? `checked` : ``) + `>Video</label>
              <label><input id="hideIconSoundCheckbox" type="checkbox" ` + (hiddenIcons.includes("sound") ? `checked` : ``) + `>Sound</label>
              <label><input id="hideIconFlashCheckbox" type="checkbox" ` + (hiddenIcons.includes("flash") ? `checked` : ``) + `>Flash</label>
              <label><input id="hideIconStraightCheckbox" type="checkbox" ` + (hiddenIcons.includes("straight") ? `checked` : ``) + `>Straight</label>
              <label><input id="hideIconGayCheckbox" type="checkbox" ` + (hiddenIcons.includes("gay") ? `checked` : ``) + `>Yaoi/Gay</label>
              <label><input id="hideIconLesbianCheckbox" type="checkbox" ` + (hiddenIcons.includes("lesbian") ? `checked` : ``) + `>Yuri/Lesbian</label>
              <label><input id="hideIconTransCheckbox" type="checkbox" ` + (hiddenIcons.includes("trans") ? `checked` : ``) + `>Futanari/Transsexual</label>
              <label><input id="hideIconTrapCheckbox" type="checkbox" ` + (hiddenIcons.includes("trap") ? `checked` : ``) + `>Trap</label>
              <label><input id="hideIconThreeDCheckbox" type="checkbox" ` + (hiddenIcons.includes("threeD") ? `checked` : ``) + `>3D</label>
              <label><input id="hideIconLoliCheckbox" type="checkbox" ` + (hiddenIcons.includes("loli") ? `checked` : ``) + `>Loli</label>
              <label><input id="hideIconShotaCheckbox" type="checkbox" ` + (hiddenIcons.includes("shota") ? `checked` : ``) + `>Shota</label>
              <label><input id="hideIconGoreDeathCheckbox" type="checkbox" ` + (hiddenIcons.includes("goreDeath") ? `checked` : ``) + `>Gore/Death</label>
              <label><input id="hideIconPregnantCheckbox" type="checkbox" ` + (hiddenIcons.includes("pregnant") ? `checked` : ``) + `>Pregnant</label>
              <label><input id="hideIconBestialityCheckbox" type="checkbox" ` + (hiddenIcons.includes("bestiality") ? `checked` : ``) + `>Bestiality</label>
              <label><input id="hideIconFeetCheckbox" type="checkbox" ` + (hiddenIcons.includes("feet") ? `checked` : ``) + `>Feet/Footjob</label>
              <label><input id="hideIconBondageCheckbox" type="checkbox" ` + (hiddenIcons.includes("bondage") ? `checked` : ``) + `>Bondage/BDSM</label>
              <label><input id="hideIconPoopCheckbox" type="checkbox" ` + (hiddenIcons.includes("poop") ? `checked` : ``) + `>Scat</label>
              <label><input id="hideIconPissCheckbox" type="checkbox" ` + (hiddenIcons.includes("piss") ? `checked` : ``) + `>Piss/Golden Shower</label>
              <label><input id="hideIconGroupCheckbox" type="checkbox" ` + (hiddenIcons.includes("group") ? `checked` : ``) + `>Group Sex/Gangbang</label>
              <label><input id="hideIconIncestCheckbox" type="checkbox" ` + (hiddenIcons.includes("incest") ? `checked` : ``) + `>Incest</label>

              <label><input id="hideIconBukkakeCheckbox" type="checkbox" ` + (hiddenIcons.includes("bukkake") ? `checked` : ``) + `>Bukkake</label>
              <label><input id="hideIconTentaclesCheckbox" type="checkbox" ` + (hiddenIcons.includes("tentacles") ? `checked` : ``) + `>Tentacles</label>
              <label><input id="hideIconRapeCheckbox" type="checkbox" ` + (hiddenIcons.includes("rape") ? `checked` : ``) + `>Rape</label>
              <label><input id="hideIconPublicCheckbox" type="checkbox" ` + (hiddenIcons.includes("public") ? `checked` : ``) + `>Public</label>
              <label><input id="hideIconFurryCheckbox" type="checkbox" ` + (hiddenIcons.includes("furry") ? `checked` : ``) + `>Furry</label>
              <label><input id="hideIconFatCheckbox" type="checkbox" ` + (hiddenIcons.includes("fat") ? `checked` : ``) + `>BBW</label>
              <label><input id="hideIconHypnosisCheckbox" type="checkbox" ` + (hiddenIcons.includes("hypnosis") ? `checked` : ``) + `>Hypnosis</label>
              <label><input id="hideIconNtrCheckbox" type="checkbox" ` + (hiddenIcons.includes("ntr") ? `checked` : ``) + `>NTR</label>
              <label><input id="hideIconFemdomCheckbox" type="checkbox" ` + (hiddenIcons.includes("femdom") ? `checked` : ``) + `>Femdom/Dominatrix</label>


              <label><input id="hideIconSafeCheckbox" type="checkbox" ` + (hiddenIcons.includes("safe") ? `checked` : ``) + `>Rating: Safe</label>
              <label><input id="hideIconQuestionableCheckbox" type="checkbox" ` + (hiddenIcons.includes("questionable") ? `checked` : ``) + `>Rating: Questionable</label>
              <label><input id="hideIconExplicitCheckbox" type="checkbox" ` + (hiddenIcons.includes("explicit") ? `checked` : ``) + `>Rating: Explicit</label>
            </span>
          </div>
          <button id="ibenhancerSettingsSave">Save</button><button id="ibenhancerSettingsCancel">Cancel</button>
        </div>
      ` );

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

      $("#hideAllIconsButton").click(() => $('#iconCheckboxes input[type=checkbox]').prop('checked', true));
      $("#unhideAllIconsButton").click(() => $('#iconCheckboxes input[type=checkbox]').prop('checked', false));


      addGlobalStyle(`
        html.ibenhancerSettingVisible {
          overflow: hidden !important;
        }
        #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, #ibenhancerSettings label {
          font-size: 15px !important;
          font-family: Arial, Helvetica, sans-serif !important;
          font-style: normal !important;
          font-weight: normal !important;
          color: black;
        }
        #ibenhancerSettings-blocker {
          position: fixed;
          content: '';
          background-color: rgba(0, 0, 0, .5);
          width: 100vw;
          height: 100vh;
          top: 0;
          left: 0;
          z-index: 1;
          display: none;
        }
        #ibenhancerSettings-blocker.show {
          display: block;
        }
        #ibenhancerSettings {
          position: fixed;
          width: 400px;
          height: 400px;
          left: calc(50vw - 155px);
          top: calc(50vh - 165px);
          background-color: white;
          border: 2px solid black;
          border-radius: 3px;
          padding: 10px;
          text-align: left;
          z-index: 999999;
          display: none;
          -webkit-box-sizing: unset;
          -moz-box-sizing: unset;
          box-sizing: unset;
          color: black;
          padding-bottom: 0px;
        }
        #ibenhancerSettings-options {
          overflow-y: auto;
          overflow-x: hidden;
          width: 100%;
          height: calc(100% - 32px);
        }
        #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 && ($(containerDOM + ' ' + imageDOM).length || $(containerDOM + ' video').length)) {
        if (debugMode) console.log('r34buttons');

        var likeGelbooru = () => $('#container .aside #tag-list a').filter(function(index) { return $(this).text() === "Up"; }).trigger("click");
        var favoriteGelbooru = () => $('#container .aside #tag-list a').filter(function(index) { return $(this).text() === "Add to favorites"; }).trigger("click");

        // UNKNOWN var likeDanbooru = () => $('#post-options').filter(function(index) { return $(this).text() === "Up"; }).trigger("click");
        var favoriteDanbooru = () => $('#add-to-favorites').trigger("click");

        $("#ibenhancer").append('<br>' + likeButtonSvg + favoriteButtonSvg);
        $("#like-button").click(function () {
          $("#stats > ul > li:contains('(vote up)') > a:contains('up')").click();
        });
        $("#favorite-button").click(function () {
          $("#stats + div > ul > li > a:contains('Add to favorites')").click();
        });

        addGlobalStyle(`
          .ibenhancer-button {
            cursor: pointer;
            width: 32px;
            padding: 3px;
            margin: 0;
            border: none;
            vertical-align: middle;
          }
          #favorite-button {
            fill: #E04F5F;
          }
          #like-button {
            fill: #2ECC71;
          }
          .ibenhancer-button:hover {
            background-color: rgba(255,255,255,.5);
          }
          .ibenhancer-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;
      }
    });

    if (urlParams.get('page') && urlParams.get('page').toLowerCase() == 'favorites') unsupportedThumbnailPage = true; // Add method to remove favorite.

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

    function checkForMoreThumbnails() {
      var oldNumber = thumbnails.length;
      thumbnails = $(thumbnailDOM);
      return (thumbnails.length > oldNumber);
    }

    if (!unsupportedThumbnailPage && thumbnails.length > 0) {
      $('body').append(`
        <a href="#" id="thumbPlusPreviewLink" style="">
          <div id="thumbPlusPreview" class="">
            <div id="thumbPlusPreviewImage"></div>
            <div id="thumbPlusIcons" style="position: relative;">
              <span id="thumbPlusPreviewGif" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("gif") ? `` : `show`) + `" title="GIF">` + icons['gif'] + `</span>
              <span id="thumbPlusPreviewVideo" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("video") ? `` : `show`) + `" title="Video">` + icons['video'] + `</span>
              <span id="thumbPlusPreviewFlash" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("flash") ? `` : `show`) + `" title="Flash Animation">` + icons['flash'] + `</span>
              <span id="thumbPlusPreviewSound" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("sound") ? `` : `show`) + `" title="Has Sound">` + icons['sound'] + `</span>
              <span id="thumbPlusPreview3D" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("threeD") ? `` : `show`) + `" title="3D">` + icons['threeD'] + `</span>
              <span id="thumbPlusPreviewStraight" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("straight") ? `` : `show`) + `" title="Straight">` + icons['straight'] + `</span>
              <span id="thumbPlusPreviewGay" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("gay") ? `` : `show`) + `" title="Yaoi/Gay">` + icons['gay'] + `</span>
              <span id="thumbPlusPreviewLesbian" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("lesbian") ? `` : `show`) + `" title="Yuri/Lesbian">` + icons['lesbian'] + `</span>
              <span id="thumbPlusPreviewTrans" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("trans") ? `` : `show`) + `" title="Futanari/Transsexual">` + icons['trans'] + `</span>
              <span id="thumbPlusPreviewTrap" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("trap") ? `` : `show`) + `" title="Trap">` + icons['trap'] + `</span>
              <span id="thumbPlusPreviewLoli" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("loli") ? `` : `show`) + `" title="Loli">` + icons['loli'] + `</span>
              <span id="thumbPlusPreviewShota" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("shota") ? `` : `show`) + `" title="Shota">` + icons['shota'] + `</span>
              <span id="thumbPlusPreviewGoreDeath" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("goreDeath") ? `` : `show`) + `" title="Gore/Death">` + icons['goreDeath'] + `</span>
              <span id="thumbPlusPreviewPregnant" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("pregnant") ? `` : `show`) + `" title="Pregnant">` + icons['pregnant'] + `</span>
              <span id="thumbPlusPreviewBestiality" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("bestiality") ? `` : `show`) + `" title="Bestiality">` + icons['bestiality'] + `</span>
              <span id="thumbPlusPreviewFeet" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("feet") ? `` : `show`) + `" title="Feet/Footjob">` + icons['feet'] + `</span>
              <span id="thumbPlusPreviewBondage" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("bondage") ? `` : `show`) + `" title="Bondage/BDSM">` + icons['bondage'] + `</span>
              <span id="thumbPlusPreviewPoop" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("poop") ? `` : `show`) + `" title="Scat">` + icons['poop'] + `</span>
              <span id="thumbPlusPreviewPiss" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("piss") ? `` : `show`) + `" title="Piss/Golden Shower">` + icons['piss'] + `</span>
              <span id="thumbPlusPreviewGroup" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("group") ? `` : `show`) + `" title="Group Sex/Gangbang">` + icons['group'] + `</span>
              <span id="thumbPlusPreviewIncest" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("incest") ? `` : `show`) + `" title="Incest">` + icons['incest'] + `</span>
              <span id="thumbPlusPreviewBukkake" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("bukkake") ? `` : `show`) + `" title="Bukkake">` + icons['bukkake'] + `</span>
              <span id="thumbPlusPreviewTentacles" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("tentacles") ? `` : `show`) + `" title="Tentacles">` + icons['tentacles'] + `</span>
              <span id="thumbPlusPreviewRape" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("rape") ? `` : `show`) + `" title="Rape">` + icons['rape'] + `</span>
              <span id="thumbPlusPreviewPublic" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("public") ? `` : `show`) + `" title="Public Sex/Exhibitionism">` + icons['public'] + `</span>
              <span id="thumbPlusPreviewFurry" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("furry") ? `` : `show`) + `" title="Furry">` + icons['furry'] + `</span>
              <span id="thumbPlusPreviewFat" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("fat") ? `` : `show`) + `" title="Chubby/BBW">` + icons['fat'] + `</span>
              <span id="thumbPlusPreviewHypnosis" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("hypnosis") ? `` : `show`) + `" title="Hypnosis/Mind Control">` + icons['hypnosis'] + `</span>
              <span id="thumbPlusPreviewNtr" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("ntr") ? `` : `show`) + `" title="Netorare (NTR)/Cuckold">` + icons['ntr'] + `</span>
              <span id="thumbPlusPreviewFemdom" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("femdom") ? `` : `show`) + `" title="Femdom">` + icons['femdom'] + `</span>
              <span id="thumbPlusPreviewSafe" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("safe") ? `` : `show`) + `" title="Rating: Safe">` + icons['safe'] + `</span>
              <span id="thumbPlusPreviewQuestionable" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("questionable") ? `` : `show`) + `" title="Rating: Questionable">` + icons['questionable'] + `</span>
              <span id="thumbPlusPreviewExplicit" class="thumbPlusPreviewIcon ` + (hiddenIcons.includes("explicit") ? `` : `show`) + `" title="Rating: Explicit">` + icons['explicit'] + `</span>
            </div>
          </div>
        </a>
      `);
      $('#thumbPlusPreview').css({ backgroundColor: $('body').css("background-color") != "rgba(0, 0, 0, 0)" ? $('body').css("background-color") : 'white' });
      $('#thumbPlusPreviewContextMenuRemoveFavorite').click(function () { alert('Clicked') });
      $('#thumbPlusPreview').mouseout(function () {
        if (!debugMode || ( !keepThumbOpen && debugMode )) {
          $('#thumbPlusPreviewLink').removeClass('show');
          $('#thumbPlusPreview').removeClass('show gif video sound flash straight gay lesbian trans threed trap loli shota gore pregnant bestiality feet bondage poop piss group incest bukkake tentacles rape public furry fat hypnosis ntr femdom safe questionable explicit');
        }
      })

      function addThumbnailEnhancement() {
        if (debugMode) console.log('Add thumbnails');
        if (enableEnhancedThumbnails) thumbnails = $(thumbnailDOM);

        var tmp = 0;
        thumbnails.each(function () {
          if (!$(this).hasClass('ibeThumbnail')) {
            $(this).mouseover(function (e) {
              thumbnailMouseOver(this);
            })
            $(this).addClass('ibeThumbnail');
            tmp++;
          }
        });
        console.log('Added', tmp, 'thumbnails.');
      }

      addThumbnailEnhancement();
      setInterval(function () {
        if (checkForMoreThumbnails()) addThumbnailEnhancement()
      }, 100);

      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(' ');

        if ($('img', thumb).attr('rating')) tags.push($('img', thumb).attr('rating'));

        for (let i = 0; i < tags.length; i++) {
          tags[i] = tags[i].toLowerCase().split(',').join('');
        }

        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("intersex")) $('#thumbPlusPreview').addClass('trans')
        else 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');

        if (tags.includes("trap")) $('#thumbPlusPreview').addClass('trap');
        else if (tags.includes("otoko_no_ko")) $('#thumbPlusPreview').addClass('trap');

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

        if (tags.includes("loli")) $('#thumbPlusPreview').addClass('loli');

        if (tags.includes("shota")) $('#thumbPlusPreview').addClass('shota');

        if (tags.includes("guro")) $('#thumbPlusPreview').addClass('gore');
        else if (tags.includes("gore")) $('#thumbPlusPreview').addClass('gore');
        else if (tags.includes("death")) $('#thumbPlusPreview').addClass('gore');
        else if (tags.includes("snuf")) $('#thumbPlusPreview').addClass('gore');

        if (tags.includes("pregnant")) $('#thumbPlusPreview').addClass('pregnant');
        else if (tags.includes("pregnant_futa")) $('#thumbPlusPreview').addClass('pregnant');
        else if (tags.includes("pregnant_loli")) $('#thumbPlusPreview').addClass('pregnant');

        if (tags.includes("bestiality")) $('#thumbPlusPreview').addClass('bestiality');
        else if (tags.includes("zoophilia")) $('#thumbPlusPreview').addClass('bestiality');

        // A lot seem to be tagged with feet when it is not the main focus and many foot fetish posts are only tagged as feet.
        // if (tags.includes("feet")) $('#thumbPlusPreview').addClass('feet');
        // else
        if (tags.includes("ashikoki")) $('#thumbPlusPreview').addClass('feet');
        else if (tags.includes("footjob")) $('#thumbPlusPreview').addClass('feet');
        else if (tags.includes("foot_fetish")) $('#thumbPlusPreview').addClass('feet');
        else if (tags.includes("foot_focus")) $('#thumbPlusPreview').addClass('feet');
        else if (tags.includes("foot_worship")) $('#thumbPlusPreview').addClass('feet');
        else if (tags.includes("pov_feet")) $('#thumbPlusPreview').addClass('feet');


        if (tags.includes("bondage")) $('#thumbPlusPreview').addClass('bondage');
        else if (tags.includes("bdsm")) $('#thumbPlusPreview').addClass('bondage');

        if (tags.includes("scat")) $('#thumbPlusPreview').addClass('poop');
        else if (tags.includes("defecating")) $('#thumbPlusPreview').addClass('poop');
        else if (tags.includes("feces")) $('#thumbPlusPreview').addClass('poop');
        else if (tags.includes("coprophagia")) $('#thumbPlusPreview').addClass('poop');
        else if (tags.includes("soiling")) $('#thumbPlusPreview').addClass('poop');

        if (tags.includes("urine")) $('#thumbPlusPreview').addClass('piss');
        else if (tags.includes("urinating")) $('#thumbPlusPreview').addClass('piss');
        else if (tags.includes("peeing")) $('#thumbPlusPreview').addClass('piss');
        else if (tags.includes("golden_shower")) $('#thumbPlusPreview').addClass('piss');
        else if (tags.includes("watersports")) $('#thumbPlusPreview').addClass('piss');

        if (tags.includes("group_sex")) $('#thumbPlusPreview').addClass('group');
        else if (tags.includes("gangbang")) $('#thumbPlusPreview').addClass('group');
        else if (tags.includes("threesome")) $('#thumbPlusPreview').addClass('group');
        else if (tags.includes("orgy")) $('#thumbPlusPreview').addClass('group');
        else if (tags.includes("foursome")) $('#thumbPlusPreview').addClass('group');

        if (tags.includes("incest")) $('#thumbPlusPreview').addClass('incest');

        if (tags.includes("bukkake")) $('#thumbPlusPreview').addClass('bukkake');

        if (tags.includes("tentacles")) $('#thumbPlusPreview').addClass('tentacles');
        else if (tags.includes("tentacle")) $('#thumbPlusPreview').addClass('tentacles');
        else if (tags.includes("tentacle_sex")) $('#thumbPlusPreview').addClass('tentacles');

        if (tags.includes("rape")) $('#thumbPlusPreview').addClass('rape');

        if (tags.includes("exhibitionism")) $('#thumbPlusPreview').addClass('public');
        else if (tags.includes("public_nudity")) $('#thumbPlusPreview').addClass('public');
        else if (tags.includes("public_sex")) $('#thumbPlusPreview').addClass('public');
        else if (tags.includes("public_masturbation")) $('#thumbPlusPreview').addClass('public');
        else if (tags.includes("public_display")) $('#thumbPlusPreview').addClass('public');
        else if (tags.includes("public") && (tags.includes("sex") || tags.includes("nude"))) $('#thumbPlusPreview').addClass('public');

        if (tags.includes("furry")) $('#thumbPlusPreview').addClass('furry');
        else if (tags.includes("anthro")) $('#thumbPlusPreview').addClass('furry');

        if (tags.includes("bbw")) $('#thumbPlusPreview').addClass('fat');
        else if (tags.includes("ssbbw")) $('#thumbPlusPreview').addClass('fat');
        else if (tags.includes("chubby")) $('#thumbPlusPreview').addClass('fat');
        else if (tags.includes("obese")) $('#thumbPlusPreview').addClass('fat');
        else if (tags.includes("overweight")) $('#thumbPlusPreview').addClass('fat');

        if (tags.includes("hypnosis")) $('#thumbPlusPreview').addClass('hypnosis');
        else if (tags.includes("mind_control")) $('#thumbPlusPreview').addClass('hypnosis');

        if (tags.includes("ntr")) $('#thumbPlusPreview').addClass('ntr');
        else if (tags.includes("netorare")) $('#thumbPlusPreview').addClass('ntr');
        else if (tags.includes("cuckold")) $('#thumbPlusPreview').addClass('ntr');

        if (tags.includes("femdom")) $('#thumbPlusPreview').addClass('femdom');
        else if (tags.includes("dominatrix")) $('#thumbPlusPreview').addClass('femdom');

        if (tags.includes("rating:safe")) $('#thumbPlusPreview').addClass('safe');
        else if (tags.includes("safe")) $('#thumbPlusPreview').addClass('safe');
        else if (tags.includes("rating:questionable")) $('#thumbPlusPreview').addClass('questionable');
        else if (tags.includes("questionable")) $('#thumbPlusPreview').addClass('questionable');
        else if (tags.includes("rating:explicit")) $('#thumbPlusPreview').addClass('explicit');
        else if (tags.includes("explicit")) $('#thumbPlusPreview').addClass('explicit');

        $('#thumbPlusPreviewLink').css({ top: cumulativeOffset(thumb).top, left: cumulativeOffset(thumb).left });
        $('#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'));

        $('#thumbPlusPreviewLink').addClass('show');
      }
      addGlobalStyle(`
        #thumbPlusPreviewLink {
          background-color: white;
          position: absolute;
          top: 0px;
          left: 0px;
          display: none;
          z-index: 999999;
        }
        #thumbPlusPreviewLink.show {
          display: block;
        }
        #thumbPlusPreview {
          display: flex;
          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;
        }
        #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;
        }
        #thumbPlusIcons {
          display: flex;
          flex-wrap: wrap;
          justify-content: center;
          margin-top: 4px;
        }
        .thumbPlusPreviewIcon {
          display: none;
          margin: 2px;
          background-color: #fff5;
          border-radius: 3px;
          padding: 0px;
        }
        .thumbPlusPreviewIcon svg {
          border: none;
          vertical-align: middle;
        }
        .thumbPlusPreviewIcon, .thumbPlusPreviewIcon svg {
          width: ` + iconSize + `px;
          height: ` + iconSize + `px;
        }
        #thumbPlusPreview.gif #thumbPlusPreviewGif.show,
        #thumbPlusPreview.video #thumbPlusPreviewVideo.show,
        #thumbPlusPreview.sound #thumbPlusPreviewSound.show,
        #thumbPlusPreview.flash #thumbPlusPreviewFlash.show,
        #thumbPlusPreview.straight #thumbPlusPreviewStraight.show,
        #thumbPlusPreview.gay #thumbPlusPreviewGay.show,
        #thumbPlusPreview.lesbian #thumbPlusPreviewLesbian.show,
        #thumbPlusPreview.trans #thumbPlusPreviewTrans.show,
        #thumbPlusPreview.trap #thumbPlusPreviewTrap.show,
        #thumbPlusPreview.threed #thumbPlusPreview3D.show,
        #thumbPlusPreview.loli #thumbPlusPreviewLoli.show,
        #thumbPlusPreview.shota #thumbPlusPreviewShota.show,
        #thumbPlusPreview.gore #thumbPlusPreviewGoreDeath.show,
        #thumbPlusPreview.pregnant #thumbPlusPreviewPregnant.show,
        #thumbPlusPreview.bestiality #thumbPlusPreviewBestiality.show,
        #thumbPlusPreview.feet #thumbPlusPreviewFeet.show,
        #thumbPlusPreview.bondage #thumbPlusPreviewBondage.show,
        #thumbPlusPreview.poop #thumbPlusPreviewPoop.show,
        #thumbPlusPreview.piss #thumbPlusPreviewPiss.show,
        #thumbPlusPreview.group #thumbPlusPreviewGroup.show,
        #thumbPlusPreview.incest #thumbPlusPreviewIncest.show,
        #thumbPlusPreview.bukkake #thumbPlusPreviewBukkake.show,
        #thumbPlusPreview.tentacles #thumbPlusPreviewTentacles.show,
        #thumbPlusPreview.rape #thumbPlusPreviewRape.show,
        #thumbPlusPreview.public #thumbPlusPreviewPublic.show,
        #thumbPlusPreview.furry #thumbPlusPreviewFurry.show,
        #thumbPlusPreview.fat #thumbPlusPreviewFat.show,
        #thumbPlusPreview.hypnosis #thumbPlusPreviewHypnosis.show,
        #thumbPlusPreview.ntr #thumbPlusPreviewNtr.show,
        #thumbPlusPreview.femdom #thumbPlusPreviewFemdom.show,
        #thumbPlusPreview.safe #thumbPlusPreviewSafe.show,
        #thumbPlusPreview.questionable #thumbPlusPreviewQuestionable.show,
        #thumbPlusPreview.explicit #thumbPlusPreviewExplicit.show {
          display: inline-block;
        }
	    `);
    }

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

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

    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"><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></div>
          </div>
        `);
        $("#IBEZoomableImageClose").click(function () { history.back(); });
        $('#IBEZoomableContent img').on('load', function () {
          if (wzoom) setTimeout(function () {
            getWindowProps();
            $('#IBEZoomableContent').css({ width: contentTrueWidth + 'px', height: contentTrueHeight + 'px' });
            wzoom.prepare();
          }, 0);
        });
        addGlobalStyle(` 
          #IBEZoomableImageContainer {
            position: fixed;
            margin: 0px;
            width: 100vw;
            height: 100vh;
            left: 0px;
            top: 0px;
            background-color: #0009;
            z-index: 999999;
            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;
            //height: `+ 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;
        }

        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();
      });



      $(containerDOM + ' ' + imageDOM).click(function () {
        var currentSrc = $(containerDOM + ' ' + imageDOM).attr('src');
        history.replaceState({ id: "default" }, 'title');
        history.pushState({ id: "zoom" }, '');

        setTimeout(function () {
          if (currentSrc == $(containerDOM + ' ' + imageDOM).attr('src')) openZoom();
          else {
            waitingForZoom = true;
          }
        }, 0)
      })

    }

    var cumulativeOffset = function (element) {
      var top = 0, left = 0;
      do {
        top += element.offsetTop || 0;
        left += element.offsetLeft || 0;
        element = element.offsetParent;
      } while (element);

      return {
        top: top,
        left: left
      };
    };

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