R34 Favorites Stats and Search

Adds a search bar to the Favorites page, and provides statistics on tag frequency and similarity between users

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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

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

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

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

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

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

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         R34 Favorites Stats and Search
// @version      1.0.3
// @description  Adds a search bar to the Favorites page, and provides statistics on tag frequency and similarity between users
// @author       Librake & BaraBowser
// @match        https://rule34.xxx/index.php?page=favorites&s=view&id=*
// @match        https://rule34.xxx/index.php?page=post&s=list*
// @icon         https://i.imgur.com/EnHAGt0.png
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @license      MIT
// @namespace Discord: nanamicow
// ==/UserScript==

// === About the project (version 1.0) ===
// - This project is a modification of Librake's Rule34 Favorites Search version 1.2. This is the first version of it.
// - It allows you to extract a tag list and frequency from any user's favorites. You can do this on just one page or their whole favorites.

(function () {
    'use strict';


    var inlineLZString = function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}return t[o][r]}var r=String.fromCharCode,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",t={},i={compressToBase64:function(o){if(null==o)return"";var r=i._compress(o,6,function(o){return n.charAt(o)});switch(r.length%4){default:case 0:return r;case 1:return r+"===";case 2:return r+"==";case 3:return r+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(e){return o(n,r.charAt(e))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(o){return null==o?"":""==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compressToUint8Array:function(o){for(var r=i.compress(o),n=new Uint8Array(2*r.length),e=0,t=r.length;t>e;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;i<o.length;i+=1)if(u=o.charAt(i),Object.prototype.hasOwnProperty.call(s,u)||(s[u]=f++,p[u]=!0),c=a+u,Object.prototype.hasOwnProperty.call(s,c))a=c;else{if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();
    /* lz-string.js - JavaScript compression and decompression using LZ-based algorithms. (c) 2013-2015 Pieroxy, MIT License */
    var localLZString;

    function onLZStringReady(event) {
         try {
            if (event && event.detail && event.detail.LZS) {
                localLZString = event.detail.LZS;
            } else {
                localLZString = inlineLZString;
            }
        } catch (error) {
            localLZString = inlineLZString;
        }
    }

    document.addEventListener('LZStringReady', onLZStringReady);

    const scriptElement = document.createElement('script');
    scriptElement.textContent = `var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}return t[o][r]}var r=String.fromCharCode,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",t={},i={compressToBase64:function(o){if(null==o)return"";var r=i._compress(o,6,function(o){return n.charAt(o)});switch(r.length%4){default:case 0:return r;case 1:return r+"===";case 2:return r+"==";case 3:return r+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(e){return o(n,r.charAt(e))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(o){return null==o?"":""==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compressToUint8Array:function(o){for(var r=i.compress(o),n=new Uint8Array(2*r.length),e=0,t=r.length;t>e;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;i<o.length;i+=1)if(u=o.charAt(i),Object.prototype.hasOwnProperty.call(s,u)||(s[u]=f++,p[u]=!0),c=a+u,Object.prototype.hasOwnProperty.call(s,c))a=c;else{if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();

    var event = new CustomEvent('LZStringReady', {
        detail: {
            LZS: LZString
        }
    });
    document.dispatchEvent(event);
    `;

    document.head.appendChild(scriptElement);


    const discordLink = "https://discord.gg/jZzYFNeCTw"

    const scriptVersion = '1.2';
    let allImages = [];
    let loadedImages = [];
    let images;
    let results = [];
    let searchTag = "";
    let searchTags = [];
    let negativeTags = [];
    let hardSearch = false;
    let orMode = false;
    let inputTags = [];
    let fromBack = false;
    let needScan = false;
    let fullScan = false;
    let actualFavCount;
    let prevFavCount;
    let loadedTags = [];
    let appendLoadedSave = false;
    let prevId;
    let lastImageId;
    let textColor;
    let darkMode = false;
    let userId;
    let isMobile;
    let customIcon = true;
    let borderFavs = true;

    const onFavPage = isOnFavPage();


    function isOnFavPage() {
        const url = window.location.href;

        if (url.includes('page=favorites&s=view&id=')) {
            return true;
        } else if (url.includes('page=post&s=list')) {
            return false;
        } else {
            return null;
        }
    }

    function getBgColor() {
        const bodyElement = document.querySelector('body');
        const computedStyle = window.getComputedStyle(bodyElement);
        const backgroundColor = computedStyle.backgroundColor;

        return backgroundColor;
    }

    function isMobileVersion() {
        const cssLinks = document.querySelectorAll('link[rel="stylesheet"][type="text/css"][media="screen"]');

        for (let i = 0; i < cssLinks.length; i++) {
            const href = cssLinks[i].getAttribute('href');
            if (href && (href.includes('mobile.css') || href.includes('mobile-dark.css'))) {
                return true;
            }
        }

        return false;
    }

    function isDarkMode() {
        const cssLinks = document.querySelectorAll('link[rel="stylesheet"][type="text/css"][media="screen"]');

        for (let i = 0; i < cssLinks.length; i++) {
            const href = cssLinks[i].getAttribute('href');
            if (href && (href.includes('dark.css'))) {
                return true;
            }
        }

        return false;
    }

    function loadSavedData() {
        const savedInputTags = localStorage.getItem('inputTags');
        loadedTags = savedInputTags ? JSON.parse(savedInputTags) : [];

        const savedHardSearch = localStorage.getItem('hardSearch');
        hardSearch = savedHardSearch ? JSON.parse(savedHardSearch) : false;
        const savedOrMode = localStorage.getItem('orMode');
        orMode = savedOrMode ? JSON.parse(savedOrMode) : false;

        const savedCustomIcon = localStorage.getItem('customIcon');
        customIcon = savedCustomIcon ? JSON.parse(savedCustomIcon) : true;
        const savedborderFavs = localStorage.getItem('borderFavs');
        borderFavs = savedborderFavs ? JSON.parse(savedborderFavs) : true;

        const savedFromBack = localStorage.getItem('fromBack');
        fromBack = savedFromBack ? JSON.parse(savedFromBack) : false;
        localStorage.removeItem('fromBack');

        const savedPrevFavCount = localStorage.getItem('prevFavCount');
        prevFavCount = (savedPrevFavCount && savedPrevFavCount != 'undefined') ? JSON.parse(savedPrevFavCount) : 0;

        const savedPrevId = localStorage.getItem('prevId');
        prevId = (savedPrevId && savedPrevId != 'undefined') ? JSON.parse(savedPrevId) : 0;
    }

    function reset() {

    // Save the dictionary first
    const savedDictionary = localStorage.getItem('tagTypeDictionary');

    // Clear everything
    localStorage.clear();

    // Restore the dictionary
    if (savedDictionary) {
        localStorage.setItem('tagTypeDictionary', savedDictionary);
    }


        location.reload();
    }

    function updateIcon(newIconUrl) {
        const existingFavicons = document.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]');
        existingFavicons.forEach(favicon => favicon.parentNode.removeChild(favicon));

        const link = document.createElement('link');
        link.rel = 'icon';
        link.href = newIconUrl;

        document.head.appendChild(link);

        const shortcutIconLink = document.createElement('link');
        shortcutIconLink.rel = 'shortcut icon';
        shortcutIconLink.href = newIconUrl;

        document.head.appendChild(shortcutIconLink);
    }

    function loadAllImagesFromLocalStorage(callback) {
        try {
            const storedData = localStorage.getItem('allImages');
            if (!storedData) {
                callback([]);
                return;
            }

            const decompressedData = localLZString.decompressFromUTF16(storedData);

            const loadedAllImages = decompressedData ? JSON.parse(decompressedData) : [];

            callback(loadedAllImages);
        }
        catch (e) {
            callback([]);
        }
    }
//NEW CODE############################################################################################
// Create the help button
const helpButton = document.createElement('button');
helpButton.textContent = '?';
helpButton.style.backgroundColor = 'backgroundColor';
helpButton.style.color = 'white';
helpButton.style.border = '1px solid #d6d6d6';
helpButton.style.borderRadius = '50%';
helpButton.style.width = '20px';
helpButton.style.height = '20px';
helpButton.style.cursor = 'pointer';
helpButton.style.marginLeft = '10px';
helpButton.style.padding = '0';
helpButton.style.fontSize = '12px';
helpButton.style.position = 'fixed';
helpButton.style.top = '110px';
helpButton.style.right = '125px';

        // Create the help panel
        const helpPanel = document.createElement('div');
        helpPanel.style.display = 'none';
        helpPanel.style.position = 'fixed';
        helpPanel.style.top = '135px';
        helpPanel.style.right = '5px'
        helpPanel.style.width = '400px';
        helpPanel.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
        helpPanel.style.color = 'white';
        helpPanel.style.padding = '15px';
        helpPanel.style.borderRadius = '10px';
        helpPanel.style.zIndex = '10000';
        helpPanel.style.height = '400px';
        helpPanel.style.overflowY = 'auto';

helpPanel.innerHTML = `
<div style="font-weight: bold; font-size: 18px; color: #58b85d; margin-bottom: 20px">
    Tag Stats v:1.0.3 - Preview Mode
</div>

<div style="margin-bottom: 20px">
    <div style="font-style: italic; color: white; margin-bottom: 10px">
        What is it?
    </div>
    <div style="color: #cccccc">
        A modification of Librake's Favorites Search v:1.2, designed to analyze tag patterns in your favorites and those of others.
    </div>
</div>

<div style="margin-bottom: 20px">
    <div style="font-style: italic; color: white; margin-bottom: 10px">
        What can I do with it?
    </div>
    <div style="color: #cccccc">
        <ul style="list-style-type: none; padding-left: 0; margin-top: 5px">
            <li style="margin-bottom: 8px">• This is a <b>preview mode</b>. For access to all features, use Librake's search bar and wait for the results. An empty search returns all of the user's favorites.</li>
            <li style="margin-bottom: 8px">• Tag types are <b>not fetched</b> or added to the dictionary in this mode.</li>
            <li style="margin-bottom: 8px">• Analyze tag patterns on the current page, including absolute and relative frequencies.</li>
            <li style="margin-bottom: 8px">• The "suggested sample pages" is a list of random pages that the script, based on the total number of favorite pages of the current user, suggests you to visit in order to get a better idea of how similar you and the user truly are. It's a middle ground between viewing a single page and doing a full scan. It just keeps track of what pages you visited, but doesn't do any extra math: the similarity is still calculated based just on the current page and saved data.</li>
            <li style="margin-bottom: 8px">• Filter tags using the panel's search tools. For example:
                <ul style="padding-left: 15px; margin-top: 5px">
                    <li><code>tag</code>: Regular search.</li>
                    <li><code>"tag"</code>: Literal search.</li>
                    <li><code>-tag</code> or <code>-"tag"</code>: Exclude specific terms or tags.</li>
                </ul>
            </li>
            <li style="margin-bottom: 8px">• Filter by tag type: (use type:...)
                <ul style="padding-left: 15px; margin-top: 5px">
                    <li><span style="color: #bd0404;">artist</span></li>
                    <li><span style="color: #dbb700;">character</span></li>
                    <li><span style="color: #9712c7;">copyright</span></li>
                    <li><span style="color: #52b8d1;">meta</span></li>
                    <li><span style="color: #f5f5f5;">general</span></li>
                    <li><span style="color: grey;">null</span></li>
                </ul>
            </li>
            <li style="margin-bottom: 8px">• Filter by tag status (<code>status:shared</code> or <code>status:exclusive</code>).</li>
            <li style="margin-bottom: 8px">• Tags in <span style="color: #32a852;">green</span> are the ones not present in your dictionary, while <span style="color: grey;">grey</span> ones are knowingly type null.</li>
        </ul>
    </div>
</div>
`;


helpButton.addEventListener('click', () => {
    // Toggle the help panel display
    helpPanel.style.display = helpPanel.style.display === 'none' ? 'block' : 'none';
});

document.body.appendChild(helpButton);
document.body.appendChild(helpPanel);

const statPanel = document.createElement('div');
statPanel.textContent = 'Most frequent tags:';
statPanel.style.position = 'fixed';
statPanel.style.top = '135px';
statPanel.style.right = '5px';
statPanel.style.width = '400px';
statPanel.style.height = '400px';
statPanel.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
statPanel.style.color = 'white';
statPanel.style.padding = '10px';
statPanel.style.borderRadius = '10px';
statPanel.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
statPanel.style.overflowY = 'auto';
statPanel.style.zIndex = '9999';
statPanel.style.display = 'none';

const toggleButton = document.createElement('button');
toggleButton.textContent = 'Stats Preview';
toggleButton.style.backgroundColor = '#00bdb0';
toggleButton.style.position = 'fixed';
toggleButton.style.color = 'white';
toggleButton.style.border = 'none';
toggleButton.style.padding = '6px';
toggleButton.style.cursor = 'pointer';
toggleButton.style.borderRadius = '3px';
toggleButton.style.fontSize = '15px';
toggleButton.style.top = '105px';
toggleButton.style.right = '5px';
toggleButton.onmouseover = () => {
    toggleButton.style.backgroundColor = '#02ada2';
};
toggleButton.onmouseout = () => {
    toggleButton.style.backgroundColor = '#00bdb0';
};

document.body.appendChild(toggleButton);
document.body.appendChild(statPanel);

const currentUrl = window.location.href;

const pageListContainer = document.createElement('div');
pageListContainer.style.marginBottom = '10px';
pageListContainer.style.color = 'white';
pageListContainer.style.fontSize = '10px';

const buttonsContainer = document.createElement('div');
buttonsContainer.style.marginBottom = '5px';
buttonsContainer.style.display = 'flex';
buttonsContainer.style.justifyContent = 'flex-start';
buttonsContainer.appendChild(pageListContainer);

const infoText = document.createElement('div');
infoText.textContent = ' ';
infoText.style.color = 'white';
infoText.style.marginBottom = '10px';
infoText.style.fontSize = '10px';

const userInfoText = document.createElement('div');
userInfoText.textContent = '';
userInfoText.style.color = 'white';
userInfoText.style.marginBottom = '10px';
userInfoText.style.fontSize = '10px';

let localTagDictionary = JSON.parse(localStorage.getItem('tagTypeDictionary') || '{}');

function getDictionarySize() {
    const dictString = JSON.stringify(localTagDictionary);
    const bytes = new Blob([dictString]).size;

    if (bytes > 1024 * 1024) {
        return `(${(bytes / (1024 * 1024)).toFixed(2)} MB)`;
    } else if (bytes > 1024) {
        return `(${(bytes / 1024).toFixed(2)} KB)`;
    }
    return `(${bytes} bytes)`;
}

const dictionaryInfoText = document.createElement('div');
dictionaryInfoText.textContent = `Tags in dictionary: ${Object.keys(localTagDictionary).length} ${getDictionarySize()}`;
dictionaryInfoText.style.color = '#52b8d1';
dictionaryInfoText.style.marginBottom = '10px';
dictionaryInfoText.style.fontSize = '10px';
dictionaryInfoText.style.fontWeight = 'normal';

const secondInfoText = document.createElement('div');
secondInfoText.textContent = '';
secondInfoText.style.color = 'white';
secondInfoText.style.marginBottom = '10px';
secondInfoText.style.fontSize = '15px';
secondInfoText.style.fontWeight = 'normal';

let currentTagFrequencies = {};
let mainUserId = localStorage.getItem('mainUserId') ? localStorage.getItem('mainUserId') : 'defaultUser';
let mainUserTags = localStorage.getItem('mainUserTags') ? JSON.parse(localStorage.getItem('mainUserTags')) : {};
let diaEHora = localStorage.getItem('diaEHora') || 'No date saved';
let mainUsername = localStorage.getItem('mainUsername') || 'No username';
let currentTagTypes = {};
let fetchedTagTypes = {};
let originalTags = [];

function getMainUserTagsSize(){
    const tagsString = JSON.stringify(mainUserTags);
    const bytes = new Blob([tagsString]).size;

    if (bytes > 1024 * 1024) {
        return `(${(bytes / (1024 * 1024)).toFixed(2)} MB)`;
    } else if (bytes > 1024) {
        return `(${(bytes / 1024).toFixed(2)} KB)`;
    }
    return `(${bytes} bytes)`;
}

function getSampleSize(totalPages) {
    // Always count page 1, then calculate additional pages needed
    if (totalPages <= 60) {
        return Math.ceil((totalPages) * 0.2); // 20% of remaining pages + page 1
    }

    // Modified Cochran's formula for remaining pages
    const remainingPages = totalPages - 1;
    return 1 + Math.ceil(
        (remainingPages * 1.28 ** 2 * 0.5 * (1 - 0.5)) /
        ((0.1 ** 2 * (remainingPages - 1)) + (1.28 ** 2 * 0.5 * (1 - 0.5)))
    );
}

const currentUserId = currentUrl.match(/id=(\d+)/)?.[1];
let visitedPages = new Map();

function generatePageList() {
    const lastPageLink = document.evaluate(
        '/html/body/div[5]/div[2]/a[6]',
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
    ).singleNodeValue;

    const pidMatch = lastPageLink?.getAttribute('onclick')?.match(/pid=(\d+)/);
    const totalPages = pidMatch ? (parseInt(pidMatch[1]) / 50) + 1 : 0;
    const sampleSize = getSampleSize(totalPages);

    const pageNumbers = Array.from({length: totalPages - 1}, (_, i) => i + 2);
    const selectedPages = pageNumbers
        .sort(() => Math.random() - 0.5)
        .slice(0, sampleSize - 1);

    selectedPages.unshift(1);
    selectedPages.sort((a, b) => a - b);

    const pageUrls = selectedPages.map(page =>
        `https://rule34.xxx/index.php?page=favorites&s=view&id=${currentUserId}&pid=${(page-1) * 50}`
    );

    if (!visitedPages.has(currentUserId)) {
        visitedPages.set(currentUserId, new Set());
    }
    visitedPages.get(currentUserId).add(pageUrls[0]);

    return {
        sampleSize,
        pages: pageUrls
    };
}

function storeSampleData(userId, pages) {
    const sampleData = {
        userId: userId,
        suggestedPages: pages,
        visitedPages: []
    };
    localStorage.setItem('sampleData', JSON.stringify(sampleData));
    return sampleData;
}

// Get or create sample data
function getSampleData() {
    const stored = localStorage.getItem('sampleData');
    if (stored) {
        const data = JSON.parse(stored);
        if (data.userId === currentUserId) {
            return data;
        }
    }
    // If userId changed or no data exists, generate new sample
    const {sampleSize, pages} = generatePageList();
    return storeSampleData(currentUserId, pages);
}

// Mark a page as visited
function markVisited(url) {
    const data = getSampleData();
    if (!data.visitedPages.includes(url)) {
        data.visitedPages.push(url);
        localStorage.setItem('sampleData', JSON.stringify(data));
        // Update the visitedPages Map as well
        if (!visitedPages.has(currentUserId)) {
            visitedPages.set(currentUserId, new Set());
        }
        visitedPages.get(currentUserId).add(url);
    }
}

// Mark current page as visited immediately when loading
const currentPid = window.location.href.match(/pid=(\d+)/)?.[1] || '0';
const currentPageUrl = `https://rule34.xxx/index.php?page=favorites&s=view&id=${currentUserId}&pid=${currentPid}`;

// Display the pages
const sampleData = getSampleData();
pageListContainer.innerHTML = `
    <div style="margin-bottom: 5px">Suggested sample pages: ${sampleData.suggestedPages.length} pages</div>
    <div style="display: flex; gap: 1px; flex-wrap: wrap;">
        ${sampleData.suggestedPages.map((url) => `
            <a href="${url}"
               onclick="(function(event) {
                   markVisited('${url}');
                   this.style.color = '#0e04cf';
               }).call(this, event)"
               style="color: ${sampleData.visitedPages.includes(url) || url === currentPageUrl ? '#0e04cf' : '#e68e00'};
                      text-decoration: none;">
               [${Math.floor(url.match(/pid=(\d+)/)[1] / 50) + 1}]
            </a>
        `).join('')}
    </div>
`;

if (sampleData.suggestedPages.includes(currentPageUrl)) {
    markVisited(currentPageUrl);
}

if (Object.keys(mainUserTags).length === 0) {
    infoText.textContent = 'No saved data!';
} else {
    infoText.innerHTML = `Loaded data for user <span style="color: #dbb700;">${mainUsername}</span> on <span style="color: #ff3df2;">${diaEHora}</span> - ${getMainUserTagsSize()}`;
}

let lastFetchTime = 0;
const FETCH_COOLDOWN = 5000; // 5 seconds

async function fetchUsername(userId) {
    const currentTime = Date.now();
    if (currentTime - lastFetchTime < FETCH_COOLDOWN) {
        return null;
    }

    lastFetchTime = currentTime;
    const response = await fetch(`index.php?page=account&s=profile&id=${userId}`);
    const text = await response.text();
    const parser = new DOMParser();
    const doc = parser.parseFromString(text, 'text/html');
    const username = doc.querySelector("#content > h2")?.textContent || null;

    return username;
}

function updateCurrentUserInfo() {
    const cachedUserId = sessionStorage.getItem('currentUserId');
    const cachedUsername = sessionStorage.getItem('currentUsername');

    // Check if we're viewing a different user or if cache is empty
    if (!cachedUsername || cachedUserId !== currentUserId) {
        fetchUsername(currentUserId).then(username => {
            if (username) {
                sessionStorage.setItem('currentUserId', currentUserId);
                sessionStorage.setItem('currentUsername', username);
                userInfoText.innerHTML = `Current user: <span style="color: #dbb700;">${username}</span>`;
            }
        });
    } else {
        userInfoText.innerHTML = `Current user: <span style="color: #dbb700;">${cachedUsername}</span>`;
    }
}


function createTagTypeMap(tags) {
    if (Object.keys(currentTagTypes).length > 0) {
        return currentTagTypes;
    }
    tags.forEach(tag => {
        if (tag in localTagDictionary) {
            fetchedTagTypes[tag] = localTagDictionary[tag];
            currentTagTypes[tag] = localTagDictionary[tag];
        }
    });
    return fetchedTagTypes;
}

function getTagColor(tagType) {
    switch (tagType) {
        case 0: return '#f5f5f5';
        case 1: return '#bd0404';
        case 3: return '#9712c7';
        case 4: return '#dbb700';
        case 5: return '#52b8d1';
        case null: return 'grey';
        default: return '#32a852';
    }
}

function displayFilteredTags(filteredTags, totalImages) {
    statPanel.innerHTML = '';

    const title = document.createElement('div');
    title.textContent = 'Most frequent tags:';
    title.style.marginBottom = '10px';
    title.style.color = 'white';
    title.style.fontSize = '16px';

    const searchInput = document.createElement('input');
    searchInput.type = 'text';
    searchInput.placeholder = 'Search for tags...';
    searchInput.style.width = 'calc(100% - 20px)';
    searchInput.style.padding = '6px';
    searchInput.style.marginBottom = '10px';
    searchInput.style.borderRadius = '4px';
    searchInput.style.border = '1px solid #ccc';
    searchInput.style.boxSizing = 'border-box';

    const typeNames = {
        'general': 0,
        'artist': 1,
        'copyright': 3,
        'character': 4,
        'meta': 5,
        'null': null
    };

searchInput.addEventListener('keydown', (event) => {
    if (event.key === 'Enter') {
        let query = searchInput.value.toLowerCase();
        let filteredResults = [...originalTags];

        if (query.trim() !== "") {
            const typeFilters = query.match(/type:(general|artist|copyright|character|meta|null)/g);
            if (typeFilters) {
                const types = typeFilters.map(t => typeNames[t.split(':')[1]]);
                filteredResults = filteredResults.filter(([tag]) => {
                    const tagType = currentTagTypes[tag];
                    if (types.includes(null)) {
                        return tagType === null || tagType === undefined;
                    }
                    return types.includes(tagType);
                });
                query = query.replace(/type:(general|artist|copyright|character|meta|null)\s*/g, '');
            }

            const statusFilter = query.match(/status:(shared|exclusive)/g);
            if (statusFilter) {
                const status = statusFilter[0].split(':')[1];
                filteredResults = filteredResults.filter(([tag]) => {
                    const isShared = tag in mainUserTags;
                    return status === 'shared' ? isShared : !isShared;
                });
                query = query.replace(/status:(shared|exclusive)\s*/g, '');
            }
            const excludedTerms = query.split(" ").filter(term => term.startsWith("-") && term.length > 1);
            if (excludedTerms.length > 0) {
                const cleanExcludedTerms = excludedTerms.map(term => {
                    if (term.startsWith(`-"`) && term.endsWith(`"`)) {
                        return {term: term.substring(2, term.length - 1), exact: true}
                    } else {
                        return {term: term.substring(1), exact: false}
                    }
                });

                filteredResults = filteredResults.filter(([tag]) =>
                    !cleanExcludedTerms.some(excludedTerm => {
                        if (excludedTerm.exact) {
                            return tag.toLowerCase() === excludedTerm.term;
                        } else {
                            return tag.toLowerCase().includes(excludedTerm.term);
                        }
                    })
                );
                query = query.split(" ").filter(term => !(term.startsWith("-") && term.length > 1)).join(" ");
            }

            const exactSearchTerms = query.split(" ").filter(term => term.startsWith('"') && term.endsWith('"') && term.length > 1);
            if (exactSearchTerms.length > 0) {
                const cleanExactSearchTerms = exactSearchTerms.map(term => term.substring(1, term.length - 1));
                filteredResults = filteredResults.filter(([tag]) =>
                    cleanExactSearchTerms.some(searchTerm => tag.toLowerCase() === searchTerm)
                );
                query = query.split(" ").filter(term => !(term.startsWith('"') && term.endsWith('"') && term.length > 1)).join(" ");
            }
            if (query.trim() !== "") {
                filteredResults = filteredResults.filter(([tag]) => tag.toLowerCase().includes(query.trim()));
            }
        }

        displayFilteredTags(filteredResults, totalImages);

        const newSearchInput = statPanel.querySelector('input');
        if (newSearchInput) {
            newSearchInput.value = searchInput.value;
        }
    }
});

    statPanel.appendChild(buttonsContainer);
    statPanel.appendChild(infoText);
    statPanel.appendChild(userInfoText);
    statPanel.appendChild(dictionaryInfoText);
    statPanel.appendChild(secondInfoText);
    statPanel.appendChild(title);
    statPanel.appendChild(searchInput);

    if (filteredTags.length === 0) {
        const noTagsMessage = document.createElement('div');
        noTagsMessage.textContent = 'No tags found.';
        statPanel.appendChild(noTagsMessage);
    } else {
        const tagList = document.createElement('ul');
        tagList.style.listStyleType = 'none';
        tagList.style.padding = '0';
        tagList.style.margin = '0';
        filteredTags.forEach(([tag, frequency], index) => {
            const excludeButton = document.createElement('span');
            excludeButton.innerText = '[-]';
            excludeButton.style.padding = '3px';
            excludeButton.style.color = '#b858a5';
            excludeButton.style.cursor = 'pointer';
            excludeButton.style.userSelect = 'text';
            excludeButton.style.fontSize = '12px';

            const tagItem = document.createElement('li');
            tagItem.style.display = 'flex';
            tagItem.style.alignItems = 'flex-start';
            tagItem.style.flexWrap = 'wrap';
            tagItem.style.gap = '5px';

            const tagText = document.createElement('span');
            tagText.textContent = `${tag}: `;
            tagText.style.color = getTagColor(fetchedTagTypes[tag]);

            const frequencyText = document.createElement('span');
            frequencyText.textContent = frequency;
            frequencyText.style.color = 'white';

            const lineNumber = document.createElement('span');
            lineNumber.textContent = `${index + 1}.`;
            lineNumber.style.color = 'gray';
            lineNumber.style.minWidth = '20px';
            tagItem.appendChild(lineNumber);
            tagItem.appendChild(excludeButton);
            tagItem.appendChild(tagText);
            tagItem.appendChild(frequencyText);

            const relativeFrequency = ((frequency / totalImages) * 100).toFixed(2);
            const relativeFrequencyText = document.createElement('span');
            relativeFrequencyText.textContent = ` (${relativeFrequency}%)`;
            relativeFrequencyText.style.color = '#a65614';
            tagItem.appendChild(relativeFrequencyText);

            currentTagFrequencies[tag] = relativeFrequency;

            const tagStatus = tag in mainUserTags ? "shared" : "exclusive";
            const tagStatusText = document.createElement('span');
            tagStatusText.textContent = ` ${tagStatus}`;
            tagStatusText.style.color = tagStatus === "shared" ? "#0b358f" : "#9712c7";
            tagItem.appendChild(tagStatusText);

            excludeButton.addEventListener('click', () => {
                let currentSearch = searchInput.value;
                if (currentSearch.includes(`-"${tag}"`)) {
                    currentSearch = currentSearch.replace(`-"${tag}"`, "");
                } else {
                    currentSearch += `-"${tag}" `;
                }
                searchInput.value = currentSearch;
                searchInput.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Enter'}));
            });

            tagList.appendChild(tagItem);
        });

        statPanel.appendChild(tagList);
    }
}
function displayTagFrequencies() {
    const images = document.querySelectorAll('img');
    const allTags = [];

    images.forEach(img => {
        if (img.title) {
            const tags = img.title.split(' ').filter(tag => tag.trim() !== '');
            allTags.push(...tags);
        }
    });

    const tagFrequencies = {};
    allTags.forEach(tag => {
        tagFrequencies[tag] = (tagFrequencies[tag] || 0) + 1;
    });

    const totalImages = images.length;
    originalTags = Object.entries(tagFrequencies)
        .sort(([, frequencyA], [, frequencyB]) => frequencyB - frequencyA);

    if (Object.keys(currentTagTypes).length === 0) {
        fetchedTagTypes = createTagTypeMap(Object.keys(tagFrequencies));
        currentTagTypes = {...fetchedTagTypes};
    }

    displayFilteredTags(originalTags, totalImages);
}


function calcularSimilaridadeTags(obj1, obj2) {
    const todasAsTags = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
    const vetorA = [];
    const vetorB = [];
    todasAsTags.forEach(tag => {
        vetorA.push(obj1[tag] || 0);
        vetorB.push(obj2[tag] || 0);
    });

    const arredondar = (numero) => Math.round(numero * 10000) / 10000;
    const magnitudeA = Math.sqrt(arredondar(vetorA.reduce((sum, val) => sum + val * val, 0)));
    const magnitudeB = Math.sqrt(arredondar(vetorB.reduce((sum, val) => sum + val * val, 0)));
    const produtoEscalar = arredondar(vetorA.reduce((sum, val, i) => sum + val * vetorB[i], 0));

    let similaridade = 0;
    if (magnitudeA !== 0 && magnitudeB !== 0) {
        similaridade = produtoEscalar / (magnitudeA * magnitudeB);
    }

    return similaridade;
}

toggleButton.addEventListener('click', async () => {
    if (statPanel.style.display === 'none') {
        statPanel.style.display = 'block';
        statPanel.innerHTML = 'Loading tag information...';
        displayTagFrequencies();

        setTimeout(async () => {
            updateCurrentUserInfo();
        }, 500);
    } else {
        statPanel.style.display = 'none';
        toggleButton.innerText = 'Stats Preview';
    }

    const similaridadePercentage = calcularSimilaridadeTags(mainUserTags, currentTagFrequencies);
    const porcentagemExibida = similaridadePercentage * 100;

    let cor = "";
    if (porcentagemExibida <= 20) {
        cor = "#bd0404"; // Vermelho
    } else if (porcentagemExibida <= 40) {
        cor = "#e68e00"; // Laranja
    } else if (porcentagemExibida <= 60) {
        cor = "#dbb700"; // Amarelo
    } else if (porcentagemExibida <= 80) {
        cor = "#1c9c13"; // Verde
    } else {
        cor = "#8cff66"; // Verde-limão brilhante
    }

    function countCommonTags(currenttagfrequencies, mainusertags) {
        let commonCount = 0;
        for (let tag in currentTagFrequencies) {
            if (mainUserTags.hasOwnProperty(tag)) {
                commonCount++;
            }
        }
        return commonCount;
    }

    let commonTags = countCommonTags(currentTagFrequencies, mainUserTags);
    const uniqueTags = new Set([...Object.keys(mainUserTags), ...Object.keys(currentTagFrequencies)]);
    const totalTags = uniqueTags.size;
    const porcentagemTagsComuns = (commonTags / totalTags) * 100;
    let cor2 = "";
    if (porcentagemTagsComuns <= 10) {
        cor2 = "#bd0404";
    } else if (porcentagemExibida <= 25) {
        cor2 = '#dbb700';
    } else {
        cor2 = "#1c9c13";
    }

    secondInfoText.innerHTML = `Similarity: <span style="color: ${cor};">${porcentagemExibida.toFixed(2)}%</span>  <span style="margin-left: 20px;">Common Tags: <span style="color: ${cor2};">${porcentagemTagsComuns.toFixed(2)}%</span></span>`;
});

//END OF NEW CODE##############################################################################################


//IMPORTANT####################################################################################################
    function saveAllImagesToLocalStorage() {
        const allImagesData = [];
        allImagesData.push(...allImages.map(img => ({
            src: img.getAttribute('src'),
            title: img.getAttribute('title'),
            link: `index.php?page=post&s=view&id=${img.src.split('?')[1]}`,
            id: img.src.split('?')[1]
        })));

        if (appendLoadedSave) {
            allImagesData.push(...loadedImages.map(img => ({
                src: img.src,
                title: img.title,
                link: img.link,
                id: img.id
            })));
        }
//IMPORTANT####################################################################################################

        const jsonStr = JSON.stringify(allImagesData);

        const compressedData = localLZString.compressToUTF16(jsonStr);
        try {
            localStorage.setItem('allImages', compressedData);
        } catch (e) {
            console.error("Storage limit exceeded: ", e);
        }
    }

    function getIdFromUrl() {
        var url = window.location.href;
        var idIndex = url.indexOf("id=");
        if (idIndex !== -1) {
            var idStartIndex = idIndex + 3;
            var idEndIndex = url.indexOf("&", idStartIndex);
            if (idEndIndex === -1) {
                idEndIndex = url.length;
            }
            var id = url.substring(idStartIndex, idEndIndex);
            return id;
        }
        return null;
    }

    async function getFavoritesCount(userId) {
        const url = `https://rule34.xxx/index.php?page=account&s=profile&id=${userId}`;
        try {
            const response = await fetch(url);
            if (!response.ok) {
                throw new Error(`HTTP error! Status: ${response.status}`);
            }
            const text = await response.text();
            const parser = new DOMParser();
            const doc = parser.parseFromString(text, 'text/html');

            const favoritesRow = Array.from(doc.querySelectorAll('tr')).find(row =>
                row.querySelector('td strong')?.textContent.trim() === 'Favorites'
            );

            if (!favoritesRow) {
                throw new Error('Favorites row not found');
            }

            const favoritesCount = favoritesRow.querySelector('td a')?.textContent.trim();

            return favoritesCount ? parseInt(favoritesCount, 10) : null
        } catch (error) {
            console.error('Error fetching favorites count:', error);
            return null;
        }
    }


    const SearchInputModule = (() => {

        function createSearchInput() {
            const header = document.getElementById('header');
            const navbar = document.getElementById('navbar');

            const inputContainer = document.createElement('div');
            inputContainer.style.marginLeft = '20px';

            const helpContainer = createHelpTooltip(isMobile ? -20 : 0);
            const settingsContainer = createSettings();

            const createToggleElement = isMobile ? createToggleButton : createCheckbox;
            const verbatimModeContainer = createToggleElement('verbatimModeCheckbox', 'Verbatim mode', hardSearch, (checked) => {
                hardSearch = checked;
                localStorage.setItem('hardSearch', JSON.stringify(checked));
            });

            const orModeContainer = createToggleElement('orMode', 'Or mode', orMode, (checked) => {
                orMode = checked;
                localStorage.setItem('orMode', JSON.stringify(checked));
            });

            const inputWrapper = createSearchInputField();

            const searchButton = createSearchButton(() => {
                searchTags.length = 0;
                negativeTags.length = 0;
                inputTags = inputWrapper.querySelector('input').value.trim().split(' ');
                inputTags = inputTags.map(tag => tag.toLowerCase());
                inputTags.forEach(tag => {
                    if (tag.startsWith('-')) {
                        negativeTags.push(tag.substring(1));
                    } else if (tag.length > 0) {
                        searchTags.push(tag);
                    }
                });
                scan();
            });

            const progress = document.createElement('span');
            progress.id = 'progress';
            progress.style.marginLeft = '10px';

            if (isMobile) {
                const container1 = document.createElement('div');
                container1.style.display = 'flex';
                container1.style.position = 'relative';
                container1.style.width = 'calc(100% - 10px - 20px)';
                container1.style.maxWidth = '430px';
                container1.style.marginLeft = '10px';
                container1.style.alignItems = 'center';

                progress.style.position = 'absolute';
                progress.style.right = '20px';

                settingsContainer.style.marginRight = '20px';

                container1.appendChild(settingsContainer);
                container1.appendChild(helpContainer);
                container1.appendChild(progress);

                inputContainer.appendChild(container1);

                const container2 = document.createElement('div');
                container2.appendChild(inputWrapper);
                container2.style.marginTop = '10px';

                inputContainer.appendChild(container2);

                const container3 = document.createElement('div');
                container3.style.display = 'flex';
                container3.style.justifyContent = 'space-between';
                container3.style.width = 'calc(100% - 20px)';
                container3.style.maxWidth = '450px';
                container3.style.alignItems = 'center';
                container3.style.marginTop = '10px';


                container3.appendChild(verbatimModeContainer);
                container3.appendChild(orModeContainer);
                container3.appendChild(searchButton);
                inputContainer.appendChild(container3);

                const spacer = document.createElement('div');
                spacer.style.height = '10px';
                inputContainer.appendChild(spacer);

            } else {
                inputContainer.style.marginTop = '10px';
                inputContainer.appendChild(helpContainer);
                inputContainer.appendChild(verbatimModeContainer);
                inputContainer.appendChild(orModeContainer);
                inputContainer.appendChild(inputWrapper);
                inputContainer.appendChild(searchButton);
                inputContainer.appendChild(settingsContainer);
                inputContainer.appendChild(progress);
            }

            header.insertBefore(inputContainer, navbar.nextSibling);

            inputWrapper.querySelector('input').addEventListener('keyup', function (event) {
                if (event.key === 'Enter') {
                    event.preventDefault();
                    searchButton.click();
                }
            });

            if (loadedTags.length > 0) {
                inputWrapper.querySelector('input').value = loadedTags.join(' ');
            }

            const spans = document.querySelectorAll('span');
            spans.forEach(span => {
                if (span.textContent.trim() === 'Help & Info') {
                    const computedStyle = getComputedStyle(span);
                    const currentColor = computedStyle.color;
                    textColor = currentColor;
                    if (currentColor !== 'rgb(0, 0, 0)') {
                        darkMode = true;
                    }
                }
            });
        }

        function createSettings() {
            const settingsContainer = document.createElement('div');
            settingsContainer.style.display = 'inline-block';
            settingsContainer.style.marginRight = '10px';
            settingsContainer.style.position = 'relative';

            const label = document.createElement('span');
            const img = document.createElement('img');
            const icon = 'https://raw.githubusercontent.com/Librake/Favorites-Search/main/res/settings.svg';
            const iconDark = 'https://raw.githubusercontent.com/Librake/Favorites-Search/main/res/settings_dark.svg';
            img.src = darkMode ? iconDark : icon;
            img.alt = 'S';

            const labelSize = isMobile ? 30 : 22;
            img.style.width = `${labelSize}px`;
            img.style.height = `${labelSize}px`;
            img.style.fill = 'red';
            label.appendChild(img);
            label.style.fontWeight = 'bold';
            label.style.textDecoration = 'underline';
            label.style.cursor = 'pointer';

            const tooltip = document.createElement('div');
            tooltip.style.position = 'absolute';
            tooltip.style.top = '100%';
            tooltip.style.left = '0';
            tooltip.style.paddingTop = '10px';
            tooltip.style.paddingRight = '30px';
            tooltip.style.paddingBottom = '15px';
            tooltip.style.paddingLeft = '20px';
            tooltip.style.borderRadius = '5px';
            tooltip.style.backgroundColor = '#333';
            tooltip.style.color = '#fff';
            tooltip.style.whiteSpace = 'normal';
            tooltip.style.visibility = 'hidden';
            tooltip.style.opacity = '0';
            tooltip.style.transition = 'opacity 0.2s';
            tooltip.style.maxWidth = 'calc(100vw - 30px)';
            tooltip.style.height = 'auto';
            tooltip.style.maxHeight = '400px';
            tooltip.style.marginTop = '9px';
            tooltip.style.zIndex = '9999';
            tooltip.style.overflowX = 'auto';

            if (darkMode) {
                tooltip.style.border = '1px solid #fff';
            }

            function adjustTooltipPosition() {
                const rect = tooltip.getBoundingClientRect();
                const cont = settingsContainer.getBoundingClientRect();

                const offset = -150;
                const width = 500;
                let addOffset = 0;

                if (isMobile) {
                    tooltip.style.width = 'calc(100vw - 30px)';
                    tooltip.style.maxWidth = '450px';

                    addOffset = -(cont.left + offset) + 20;
                }
                else {
                    tooltip.style.width = `${width-50}px`;

                    if (cont.left + offset + width + 30 > window.innerWidth) {
                        addOffset = -(cont.left + offset + width - window.innerWidth) - 30;
                    }

                    if (cont.left + offset < 0) {
                        addOffset = -(cont.left + offset) + 20;
                    }

                }
                tooltip.style.marginLeft = `${offset + addOffset}px`;

            }

            const closeButton = document.createElement('button');
            closeButton.textContent = '\u00D7';
            closeButton.style.position = 'absolute';
            closeButton.style.top = '10px';
            closeButton.style.right = '10px';
            closeButton.style.background = 'none';
            closeButton.style.border = 'none';
            closeButton.style.color = isMobile ? '#E08B82' : '#fff';
            closeButton.style.fontSize = '20px';
            closeButton.style.cursor = 'pointer';

            closeButton.onclick = (event) => {
                event.stopPropagation();
                hideTooltip();
            };

            function createCheckboxWithDescription(labelText, descriptionText, initialState, action) {
                const container = document.createElement('div');
                container.style.marginBottom = '10px';


                const checkbox = createCheckbox(labelText, labelText, initialState, (checked) => {
                    action(checked);
                });

                const description = document.createElement('span');
                description.innerHTML = descriptionText;
                description.style.display = 'block';
                description.style.fontSize = '14px';
                description.style.color = '#999';
                description.style.marginLeft = '30px';

                container.appendChild(checkbox);
                container.appendChild(description);

                return container;
            }

            const checkboxContainer1 = createCheckboxWithDescription(
                'Custom icon',
                'Use custom red icon for the Favorites tab.',
                customIcon,
                (checked) => {
                    customIcon = checked;
                    localStorage.setItem('customIcon', JSON.stringify(checked));
                    location.reload();
                }
            );

            const checkboxContainer2 = createCheckboxWithDescription(
                'Favorites detection',
                'Highlights images with a red border on other pages of the site if they are already in your favorites.<br>(requires scanned)',
                borderFavs,
                (checked) => {
                    borderFavs = checked;
                    localStorage.setItem('borderFavs', JSON.stringify(checked));
                }
            );

            const tooltipText = document.createElement('div');
            tooltipText.innerHTML = "If something doesn't work properly, try to...";

            function updateTooltipMaxHeight() {
                const rect = tooltip.getBoundingClientRect();
                const availableHeight = window.innerHeight - rect.top - 10;
                tooltip.style.maxHeight = `${availableHeight}px`;
            }
            function updateSize() {
                updateTooltipMaxHeight();
                adjustTooltipPosition();
            }
            updateTooltipMaxHeight();
            window.addEventListener('resize', updateSize);

            tooltip.appendChild(closeButton);
            if(!isMobile) {
                tooltip.appendChild(checkboxContainer1);
            }
            tooltip.appendChild(checkboxContainer2);

            let tooltipVisible = false;

            label.onclick = (event) => {
                event.stopPropagation();
                if (tooltipVisible) {
                    hideTooltip();
                } else {
                    showTooltip();
                }
            };

            setTimeout(function() {
                const spans = document.querySelectorAll('span');
                spans.forEach(span => {
                    if (span.textContent.trim() === 'Help & Info') {
                        span.addEventListener('mouseover', () => {
                            hideTooltip();
                        });
                    }
                });
            }, 300);


            document.addEventListener('click', (event) => {
                if (tooltipVisible && !tooltip.contains(event.target) && event.target !== label) {
                    hideTooltip();
                }
            });

            settingsContainer.appendChild(label);
            settingsContainer.appendChild(tooltip);

            return settingsContainer;

            function showTooltip() {
                adjustTooltipPosition();
                label.style.color = '#CC0000';
                tooltip.style.visibility = 'visible';
                tooltip.style.opacity = '1';
                tooltipVisible = true;

                img.style.transform = 'rotate(10deg)';
            }

            function hideTooltip() {
                tooltip.style.visibility = 'hidden';
                tooltip.style.opacity = '0';
                label.style.color = '';
                tooltipVisible = false;

                img.style.transform = 'rotate(0deg)';
            }
        }

        function createHelpTooltip(offset = 0) {
            const helpContainer = document.createElement('div');
            helpContainer.style.display = 'inline-block';
            helpContainer.style.marginRight = '50px';
            helpContainer.style.position = 'relative';

            const helpText = document.createElement('span');
            helpText.textContent = 'Help & Info';
            helpText.style.fontWeight = 'bold';
            helpText.style.textDecoration = 'underline';

            const tooltip = document.createElement('div');
            tooltip.style.position = 'absolute';
            tooltip.style.top = '100%';
            tooltip.style.left = '0';
            tooltip.style.paddingTop = '10px';
            tooltip.style.paddingRight = '30px';
            tooltip.style.paddingBottom = '15px';
            tooltip.style.paddingLeft = '20px';
            tooltip.style.borderRadius = '5px';
            tooltip.style.backgroundColor = '#333';
            tooltip.style.color = '#fff';
            tooltip.style.whiteSpace = 'normal';
            tooltip.style.visibility = 'hidden';
            tooltip.style.opacity = '0';
            tooltip.style.transition = 'opacity 0.2s';
            tooltip.style.width = '550px';
            tooltip.style.maxWidth = 'calc(100vw - 30px)';
            tooltip.style.height = 'auto';
            tooltip.style.maxHeight = '400px';
            tooltip.style.marginTop = '15px';
            tooltip.style.marginLeft = `${offset}px`;
            tooltip.style.zIndex = '9999';
            tooltip.style.overflowX = 'auto';

            if (darkMode) {
                tooltip.style.border = '1px solid #fff';
            }

            function adjustTooltipPosition() {
                const cont = helpContainer.getBoundingClientRect();

                const offset = 0;
                let addOffset = 0;

                if (isMobile) {
                    tooltip.style.width = 'calc(100vw - 30px)';
                    tooltip.style.maxWidth = '450px';

                    addOffset = -(cont.left + offset) + 20;
                }

                tooltip.style.marginLeft = `${offset + addOffset}px`;

            }



            const closeButton = document.createElement('button');
            closeButton.textContent = '\u00D7';
            closeButton.style.position = 'absolute';
            closeButton.style.top = '10px';
            closeButton.style.right = '10px';
            closeButton.style.background = 'none';
            closeButton.style.border = 'none';
            closeButton.style.color = isMobile ? '#E08B82' : '#fff';;
            closeButton.style.fontSize = '20px';
            closeButton.style.cursor = 'pointer';

            closeButton.onclick = () => {
                hideTooltip();
            };


            const content = [
                `Script's search uses keywords instead of tags, you can search by any part of the tag, such as '<span style="color: #EC91FF;">gahara</span>' instead of 'senjou<span style="color: #EC91FF;">gahara</span>_hitagi'.`,
                "Verbatim mode - switch to standard search by full tags instead of keywords.",
                "Or mode - should search result contain any or all of tags/keywords.",
                "Use '-' to exclude tags/keywords.",
                "A full scan takes a while, but it's only needed for the first search or after a reset.",
                "On the search results page, use the Back button on the screen or Esc instead of the Back button on your browser."
            ];

            const list = document.createElement('ul');
            list.style.paddingLeft = '10px';
            list.style.margin = '0';
            list.style.listStyleType = 'disc';

            content.forEach(text => {
                const listItem = document.createElement('li');
                listItem.innerHTML = text;
                listItem.style.padding = '3px';
                listItem.style.margin = '5px 0';
                list.appendChild(listItem);
            });

            tooltip.appendChild(list);

            const tooltipText = document.createElement('div');
            tooltipText.innerHTML = "If smth doesn't work properly try to";

            const discordText = document.createElement('div');
            discordText.innerHTML = `
            <p><p>If you still have some questions or suggestions, visit the project's
            <a href="${discordLink}" target="_blank" style="color: #7289DA; text-decoration: underline; font-size: 1.2em;">Discord</a>.</p>
            <p>You can also find some other useful scripts for rule34.xxx there.</p>`;

            function updateTooltipMaxHeight() {
                const rect = tooltip.getBoundingClientRect();
                const availableHeight = window.innerHeight - rect.top - 10;
                tooltip.style.maxHeight = `${availableHeight}px`;
            }

            function updateSize() {
                updateTooltipMaxHeight();
                adjustTooltipPosition();
            }
            updateTooltipMaxHeight();

            window.addEventListener('resize', updateSize);


            const resetButton = document.createElement('button');
            resetButton.textContent = 'Reset';
            resetButton.style.backgroundColor = '#e26c5e';
            resetButton.style.color = '#fff';
            resetButton.style.border = 'none';
            resetButton.style.padding = '5px 10px';
            resetButton.style.borderRadius = '5px';
            resetButton.style.cursor = 'pointer';
            resetButton.style.marginTop = '10px';
            resetButton.style.marginLeft = '10px';
            resetButton.style.zIndex = '10000';
            resetButton.onmouseover = () => {
                resetButton.style.backgroundColor = '#c45a4b';
            };
            resetButton.onmouseout = () => {
                resetButton.style.backgroundColor = '#e26c5e';
            };
            resetButton.onclick = reset;

            tooltipText.appendChild(resetButton);
            tooltipText.appendChild(discordText);
            tooltip.appendChild(closeButton);
            tooltip.appendChild(tooltipText);

            let hideTimeout;
            helpText.onmouseover = () => {
                adjustTooltipPosition();

                helpText.style.color = '#CC0000';
                clearTimeout(hideTimeout);
                tooltip.style.visibility = 'visible';
                tooltip.style.opacity = '1';
            };

            const timeToHide = isMobile ? 0 : 300;
            helpText.onmouseout = (event) => {
                hideTimeout = setTimeout(() => {
                    if (!tooltip.contains(event.relatedTarget)) {
                        hideTooltip();
                    }
                }, timeToHide);
            };

            tooltip.onmouseover = () => {
                clearTimeout(hideTimeout);
                tooltip.style.visibility = 'visible';
                tooltip.style.opacity = '1';
            };
            tooltip.onmouseout = (event) => {
                hideTimeout = setTimeout(() => {
                    if (!helpText.contains(event.relatedTarget)) {
                        hideTooltip();
                    }
                }, timeToHide);
            };

            helpContainer.appendChild(helpText);
            helpContainer.appendChild(tooltip);

            return helpContainer;

            function hideTooltip() {
                tooltip.style.visibility = 'hidden';
                tooltip.style.opacity = '0';
                helpText.style.color = textColor;
            }
        }

        function createCheckbox(id, labelText, isChecked, onChange) {
            const container = document.createElement('div');
            container.style.display = 'inline-block';
            container.style.marginRight = '50px';

            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.id = id;
            checkbox.style.marginRight = isMobile ? '15px' : '5px';
            checkbox.style.verticalAlign = 'middle';
            checkbox.checked = isChecked;
            checkbox.onchange = () => onChange(checkbox.checked);

            const label = document.createElement('label');
            label.htmlFor = id;
            label.textContent = labelText;
            label.style.verticalAlign = 'middle';

            container.appendChild(checkbox);
            container.appendChild(label);

            return container;
        }

        function createToggleButton(id, labelText, isActive, onChange) {
            const container = document.createElement('div');
            container.style.display = 'inline-block';

            const toggleButton = document.createElement('button');
            toggleButton.textContent = labelText;
            toggleButton.style.backgroundColor = isActive ? '#2196F3' : '#e0e0e0';
            toggleButton.style.color = isActive ? '#fff' : '#555';
            toggleButton.style.border = 'none';
            toggleButton.style.padding = '5px 8px';
            toggleButton.style.borderRadius = '4px';
            toggleButton.style.cursor = 'pointer';

            toggleButton.onmouseover = () => {
                toggleButton.style.backgroundColor = isActive ? '#1976D2' : '#bdbdbd';
            };
            toggleButton.onmouseout = () => {
                toggleButton.style.backgroundColor = isActive ? '#2196F3' : '#e0e0e0';
            };

            toggleButton.onclick = () => {
                isActive = !isActive;
                toggleButton.style.backgroundColor = isActive ? '#2196F3' : '#e0e0e0';
                toggleButton.style.color = isActive ? '#fff' : '#555';
                onChange(isActive);
            };

            container.appendChild(toggleButton);

            return container;
        }

        function createSearchInputField() {
            const inputWrapper = document.createElement('div');
            inputWrapper.style.position = 'relative';
            inputWrapper.style.display = 'inline-block';
            inputWrapper.style.width = 'calc(100% - 20px)';
            inputWrapper.style.maxWidth = '450px';
            inputWrapper.style.boxSizing = 'border-box';
            inputWrapper.style.marginRight = '10px';


            const input = document.createElement('input');
            input.type = 'text';
            input.placeholder = 'Enter tags or parts of tags...';
            input.id = 'searchTagInput';
            input.style.padding = '5px';
            input.style.border = '1px solid #ccc';
            input.style.borderRadius = '3px';
            input.style.width = '100%';
            input.style.boxSizing = 'border-box';


            const clearButton = document.createElement('button');
            clearButton.textContent = '\u00D7';
            clearButton.style.border = 'none';
            clearButton.style.background = 'none';
            clearButton.style.fontSize = '25px';
            clearButton.style.cursor = 'pointer';
            clearButton.style.position = 'absolute';
            clearButton.style.right = '7px';
            clearButton.style.top = '50%';
            clearButton.style.transform = 'translateY(-50%)';
            clearButton.style.color = '#ccc';
            clearButton.style.padding = '0';

            clearButton.onclick = () => {
                input.value = '';
                input.focus();
                localStorage.removeItem('inputTags');
            };

            inputWrapper.appendChild(input);
            inputWrapper.appendChild(clearButton);

            return inputWrapper;
        }

        function createSearchButton(onClick) {
            const button = document.createElement('button');
            button.textContent = 'Search';

            button.onclick = onClick;

            button.style.color = 'white';
            button.style.backgroundColor = '#e26c5e';

            if (isMobile) {
                button.style.border = 'none';
                button.style.padding = '10px 20px';
                button.style.borderRadius = '5px';
            } else {
                button.style.marginRight = '10px';
                button.style.padding = '5px 10px';
                button.style.border = '1px solid #ccc';
                button.style.borderRadius = '3px';
                button.style.cursor = 'pointer';
                button.onmouseover = () => {
                    button.style.backgroundColor = '#c45a4b';
                };
                button.onmouseout = () => {
                    button.style.backgroundColor = '#e26c5e';
                };
            }

            return button;
        }

        return {
            createSearchInput
        };
    })();


    function scan() {
        if (needScan) {

            if (fullScan) {
                searchAllPages();
            }
            else {
                searchNewPages();
            }
        }
        else {
            loadAllPages();
        }
    }

    async function fetchFavoritesPage(page) {

        const url = `https://rule34.xxx/index.php?page=favorites&s=view&id=${userId}&pid=${page * 50}`;
        try {
            const response = await fetch(url);
            const text = await response.text();
            const parser = new DOMParser();
            const doc = parser.parseFromString(text, 'text/html');
            return doc;
        } catch (error) {
            console.error(`Error fetching page ${page}:`, error);
            return null;
        }
    }

    async function extractImagesAndTags(doc, loadedImgs) {
        if (loadedImgs) {
            images = loadedImgs;
        }
        else {
            images = doc.querySelectorAll('.thumb img[src]');
        }

        images.forEach(image => {
            if (!loadedImgs) {
                allImages.push(image);
            }

            let tags = '';
            if (hardSearch) {
                tags = image.title.trim().split(' ');
            } else {
                tags = image.title;
            }

            var positived;

            if (orMode) {
                positived = false;
                searchTags.forEach(tag => {
                    if (tags.includes(tag)) {
                        positived = true;
                    }
                });
            }
            else {
                positived = true;
                searchTags.forEach(tag => {
                    if (!tags.includes(tag)) {
                        positived = false;
                    }
                });
            }

            if (searchTags.length == 0) {
                positived = true;
            }

            if (positived) {
                var negatived = false;
                negativeTags.forEach(ntag => {
                    if (tags.includes(ntag)) {
                        negatived = true;
                    }
                });
//IMPORTANT####################################################################################################
                if (!negatived) {

                    if (loadedImgs) {
                        results.push({
                            link: image.link,
                            src: image.src,
                            id: image.link.split('id=')[1],
                            title: image.title,
                            video: image.title.trim().split(' ').includes('video')
                        });
                    }
                    else {
                        const imageId = image.src.split('?')[1];
                        const postLink = `index.php?page=post&s=view&id=${imageId}`
                        results.push({
                            link: postLink,
                            src: image.src,
                            id: imageId,
                            title: image.title,
                            video: image.title.trim().split(' ').includes('video')
                        });
                    }

                }
            }
        });
//IMPORTANT####################################################################################################
        if (!loadedImgs && images.length) {
            lastImageId = images[images.length - 1].src.split('?')[1];
        }
    }

    function loadAllPages() {
        extractImagesAndTags(false, loadedImages);
        displayResultsInModal();
    }

    async function searchAllPages(startPage = 0) {

        const totalPages = Math.ceil(actualFavCount / 50);
        const fetchPromises = [];
        let loadedPages = startPage;

        const maxConcurrentRequests = 9;
        let activeRequests = 0;

        function delay(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }

        async function fetchAndExtract(page, attempt = 0) {
            while (activeRequests >= maxConcurrentRequests) {
                await delay(100);
            }

            activeRequests++;
            const delayTime = attempt > 0 ? Math.pow(2, attempt) * 100 : page * 50;
            await delay(delayTime);
            const doc = await fetchFavoritesPage(page);

            if (doc) {
                await extractImagesAndTags(doc);
            }

            let retryCount = 0;
            while (images.length === 0 && retryCount < 5) {
                const retryDelay = Math.pow(2, retryCount) * 100;
                await delay(retryDelay);
                const retryDoc = await fetchFavoritesPage(page);
                if (retryDoc) {
                    await extractImagesAndTags(retryDoc);
                }
                retryCount++;
            }

            if (images.length > 0) {
                loadedPages++;
            }

            document.getElementById('progress').innerHTML = `scanning: ${loadedPages} / ${totalPages}&nbsp;&nbsp;(full)`;

            activeRequests--;
        }

        for (let page = startPage; page < totalPages; page++) {
            fetchPromises.push(fetchAndExtract(page));
        }

        await Promise.all(fetchPromises);

        displayResultsInModal();
    }

    async function searchNewPages() {

        const totalPages = Math.min(10, Math.ceil(actualFavCount / 50));
        const fetchPromises = [];
        let loadedPages = 0;

        const maxConcurrentRequests = 9;
        let activeRequests = 0;

        function delay(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }

        async function fetchAndExtract(page, attempt = 0) {
            while (activeRequests >= maxConcurrentRequests) {
                await delay(100);
            }

            activeRequests++;
            const delayTime = attempt > 0 ? Math.pow(2, attempt) * 100 : page * 50;
            await delay(delayTime);
            const doc = await fetchFavoritesPage(page);

            if (doc) {
                await extractImagesAndTags(doc);
            }

            let retryCount = 0;
            while (images.length === 0 && retryCount < 5) {
                const retryDelay = Math.pow(2, retryCount) * 100;
                await delay(retryDelay);
                const retryDoc = await fetchFavoritesPage(page);
                if (retryDoc) {
                    await extractImagesAndTags(retryDoc);
                }
                retryCount++;
            }

            if (images.length > 0) {
                loadedPages++;
            }

            document.getElementById('progress').textContent = `scanning: ${loadedPages} / ${totalPages}`;
            activeRequests--;
        }

        for (let page = 0; page < totalPages; page++) {
            fetchPromises.push(fetchAndExtract(page));
        }

        await Promise.all(fetchPromises);


        const lastResultId = lastImageId;
        const indexToDelete = loadedImages.findIndex(item => item.link.split('id=')[1] === lastResultId);
        if (indexToDelete !== -1) {
            loadedImages.splice(0, indexToDelete + 1);
            extractImagesAndTags(false, loadedImages);
            appendLoadedSave = true;
            displayResultsInModal();
        }
        else {
            await delay(100);
            searchAllPages(10);
        }
    }


    function displayResultsInModal(tabTitle = 'Fav Search', columnWidth = 250) {
        const bgColor = getBgColor();

        document.body.innerHTML = '';

        localStorage.setItem('inputTags', JSON.stringify(inputTags));
        localStorage.setItem('prevFavCount', JSON.stringify(actualFavCount));
        localStorage.setItem('prevId', JSON.stringify(userId));
        if (needScan) {
            try {
            saveAllImagesToLocalStorage();
            }
            catch (e) {
                setTimeout(() => {
                    alert(`Error: Scan results cannot be cached, so each search requires a fresh scan.\nHowever, you can still view the search results.\nTry using the Tampermonkey and updating or changing your browser to resolve the issue.\nPlease report the bug if this issue persists.`);
                }, 1000);
            }
        }

        const existingResultContainer = document.querySelector('#resultContainer');
        if (existingResultContainer) {
            existingResultContainer.remove();
        }

        let removeLabelsShown = false;

        const resultContainer = document.createElement('div');
        resultContainer.id = 'resultContainer';
        resultContainer.style.width = '100%';
        resultContainer.style.height = '100vh';
        resultContainer.style.backgroundColor = bgColor;
        resultContainer.style.color = 'white';
        resultContainer.style.overflowY = 'auto';
        resultContainer.style.zIndex = '10000';
        resultContainer.style.padding = '20px';
        resultContainer.style.boxSizing = 'border-box';
        resultContainer.style.display = 'flex';
        resultContainer.style.flexWrap = 'wrap';
        resultContainer.style.justifyContent = 'flex-start';
        resultContainer.style.alignContent = 'flex-start';
        resultContainer.style.alignItems = 'flex-start';


        function createHeaderContainer() {
            const headerContainer = document.createElement('div');
            headerContainer.style.width = '100%';
            headerContainer.style.display = 'flex';
            headerContainer.style.flexDirection = 'column';
            headerContainer.style.alignItems = 'flex-start';

            const imageCount = document.createElement('div');
            imageCount.textContent = `Number of images: ${results.length}`;
            imageCount.style.fontFamily = 'Verdana, sans-serif';
            imageCount.style.fontSize = '20px';
            imageCount.style.fontWeight = 'bold';
            imageCount.style.color = textColor;
            imageCount.style.textAlign = 'left';
            imageCount.style.marginBottom = '10px';

            const controlsContainer = document.createElement('div');
            controlsContainer.style.width = '100%';
            controlsContainer.style.display = 'flex';
            controlsContainer.style.alignItems = 'center';

            const toggleRemoveLabelContainer = document.createElement('div');
            toggleRemoveLabelContainer.style.display = 'flex';
            toggleRemoveLabelContainer.style.alignItems = 'center';

            const toggleRemoveLabelCheckbox = document.createElement('input');
            toggleRemoveLabelCheckbox.type = 'checkbox';
            toggleRemoveLabelCheckbox.id = 'toggleRemoveLabelCheckbox';
            toggleRemoveLabelCheckbox.style.marginRight = '10px';
            toggleRemoveLabelCheckbox.onchange = () => {
                removeLabelsShown = !removeLabelsShown;
                const removeLabels = document.querySelectorAll('.removeLabel');
                removeLabels.forEach(label => {
                    label.style.display = toggleRemoveLabelCheckbox.checked ? 'inline' : 'none';
                });
            };

            const toggleRemoveLabelText = document.createElement('label');
            toggleRemoveLabelText.htmlFor = 'toggleRemoveLabelCheckbox';
            toggleRemoveLabelText.textContent = 'Show Remove Labels';
            toggleRemoveLabelText.style.color = textColor;
            toggleRemoveLabelText.style.fontFamily = 'Verdana, sans-serif';
            toggleRemoveLabelText.style.fontSize = '16px';
            toggleRemoveLabelText.style.fontWeight = 'bold';

            toggleRemoveLabelContainer.appendChild(toggleRemoveLabelCheckbox);
            toggleRemoveLabelContainer.appendChild(toggleRemoveLabelText);

            const randomizeButton = document.createElement('button');
            randomizeButton.textContent = 'Randomize';
            randomizeButton.style.backgroundColor = '#e26c5e';
            randomizeButton.style.color = 'white';
            randomizeButton.style.border = 'none';
            randomizeButton.style.padding = '5px 10px 3px 10px';
            randomizeButton.style.cursor = 'pointer';
            randomizeButton.style.borderRadius = '3px';
            randomizeButton.style.fontSize = '18px';
            randomizeButton.style.marginLeft = '20px';
            randomizeButton.onmouseover = () => {
                randomizeButton.style.backgroundColor = '#c45a4b';
            };
            randomizeButton.onmouseout = () => {
                randomizeButton.style.backgroundColor = '#e26c5e';
            };
            randomizeButton.onclick = () => {
                const shuffledResults = results.sort(() => 0.5 - Math.random());
                updateResults(shuffledResults, columnWidth);
            };

            controlsContainer.appendChild(toggleRemoveLabelContainer);
            controlsContainer.appendChild(randomizeButton);

            function updateLayout() {
                const screenWidth = window.innerWidth || document.documentElement.clientWidth;
                if (screenWidth >= 1010) {
                    randomizeButton.style.marginLeft = '50px';
                    headerContainer.style.flexDirection = 'row';
                    headerContainer.style.justifyContent = 'space-between';
                    headerContainer.style.alignItems = 'center';

                    imageCount.style.marginBottom = '0';
                    imageCount.style.marginLeft = '285px';
                    imageCount.style.transform = 'translateX(-50%)';

                    controlsContainer.style.flexDirection = 'row';
                    controlsContainer.style.justifyContent = 'flex-start';
                    controlsContainer.appendChild(imageCount);
                    toggleRemoveLabelText.textContent = 'Show Remove Labels';
                    toggleRemoveLabelContainer.style.marginLeft = '10px';

                    headerContainer.appendChild(controlsContainer);

                } else {
                    randomizeButton.style.marginLeft = '20px';
                    headerContainer.style.flexDirection = 'column';
                    headerContainer.style.alignItems = 'flex-start';

                    imageCount.style.marginBottom = '10px';
                    imageCount.style.transform = 'translateX(0%)';
                    imageCount.style.marginLeft = '0px';

                    controlsContainer.style.flexDirection = 'row';
                    controlsContainer.style.justifyContent = 'flex-start';
                    toggleRemoveLabelText.textContent = 'Remove Labels';
                    toggleRemoveLabelContainer.style.marginLeft = '0px';


                    headerContainer.appendChild(imageCount);
                    headerContainer.appendChild(controlsContainer);
                }
            }

            updateLayout();

            window.addEventListener('resize', updateLayout);

            return headerContainer;
        }

        const headerContainer = createHeaderContainer();
        resultContainer.appendChild(headerContainer);

        const spacer = document.createElement('div');
        spacer.style.width = '100%';
        spacer.style.height = '20px';
        resultContainer.appendChild(spacer);

        results.forEach(result => {
            appendResult(result);
        });

        const backButton = document.createElement('button');
        backButton.textContent = 'Back';
        backButton.style.position = 'fixed';
        backButton.style.top = '10px';
        backButton.style.right = '20px';
        backButton.style.backgroundColor = 'transparent';
        backButton.style.border = 'none';
        backButton.style.color = darkMode ? textColor : '#0b3d91';
        backButton.style.fontSize = '24px';
        backButton.style.fontWeight = 'bold';
        backButton.style.cursor = 'pointer';
        backButton.style.zIndex = '9999';
        backButton.addEventListener('mouseover', () => {
            backButton.style.color = '#660000';
        });
        backButton.addEventListener('mouseout', () => {
            backButton.style.color = darkMode ? textColor : '#0b3d91';

        });
        backButton.addEventListener('click', () => {
            localStorage.setItem('fromBack', JSON.stringify(true));
            location.reload();
        });
        document.body.appendChild(backButton);

        document.addEventListener('keydown', (event) => {
            if (event.key === 'Escape') {
                backButton.click();
            }
        });

//NEW CODE##############################################################
// Create the help button
        const helpButton = document.createElement('button');
        helpButton.textContent = '?';
        helpButton.style.backgroundColor = 'backgroundColor';
        helpButton.style.color = '#d6d6d6';
        helpButton.style.border = '1px solid #d6d6d6';
        helpButton.style.borderRadius = '50%';
        helpButton.style.width = '20px';
        helpButton.style.height = '20px';
        helpButton.style.cursor = 'pointer';
        helpButton.style.marginLeft = '10px';
        helpButton.style.padding = '0';
        helpButton.style.fontSize = '12px';
        helpButton.style.position = 'fixed';
        helpButton.style.top = '55px';
        helpButton.style.right = '110px';


        // Create the help panel
        const helpPanel = document.createElement('div');
        helpPanel.style.display = 'none';
        helpPanel.style.position = 'fixed';
        helpPanel.style.top = '80px';
        helpPanel.style.right = '20px';
        helpPanel.style.width = '400px';
        helpPanel.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
        helpPanel.style.color = 'white';
        helpPanel.style.padding = '15px';
        helpPanel.style.borderRadius = '10px';
        helpPanel.style.zIndex = '10000';
        helpPanel.style.maxHeight = '80vh';
        helpPanel.style.overflowY = 'auto';

helpPanel.innerHTML = `
<div style="font-weight: bold; font-size: 18px; color: #58b85d; margin-bottom: 20px">
    Tag Stats v:1.0.3
</div>

<div style="margin-bottom: 20px">
    <div style="font-style: italic; color: white; margin-bottom: 10px">
        What is it?
    </div>
    <div style="color: #cccccc">
        A modification of Librake's Favorites Search v:1.2, built to analyze tag patterns in user favorites.
    </div>
</div>

<div style="margin-bottom: 20px">
    <div style="font-style: italic; color: white; margin-bottom: 10px">
        What can I do with it?
    </div>
    <div style="color: #cccccc">
        <ul style="list-style-type: none; padding-left: 0; margin-top: 5px">
            <li style="margin-bottom: 8px">• View tag statistics for a user's favorites, including absolute and relative frequencies.</li>
            <li style="margin-bottom: 8px">• Save tag data for the current user to compare with others or refine results later.</li>
            <li style="margin-bottom: 8px">• Filter tags directly using the panel's tools. For instance:
                <ul style="padding-left: 15px; margin-top: 5px">
                    <li><code>tag</code>: Regular search.</li>
                    <li><code>"tag"</code>: Literal search.</li>
                    <li><code>-tag</code> or <code>-"tag"</code>: Exclude specific terms or tags.</li>
                </ul>
            </li>
              <li style="margin-bottom: 8px">• Filter by tag type: (use type:...)
                <ul style="padding-left: 15px; margin-top: 5px">
                    <li><span style="color: #bd0404;">artist</span></li>
                    <li><span style="color: #dbb700;">character</span></li>
                    <li><span style="color: #9712c7;">copyright</span></li>
                    <li><span style="color: #52b8d1;">meta</span></li>
                    <li><span style="color: #f5f5f5;">general</span></li>
                    <li><span style="color: grey;">null</span></li>
                </ul>
            </li>
            <li style="margin-bottom: 8px">• Use <code>status:</code> to filter by tag status (shared or exclusive).</li>
        </ul>
    </div>
</div>

<div style="margin-bottom: 20px">
    <div style="font-style: italic; color: white; margin-bottom: 10px">
        How does it work?
    </div>
    <div style="color: #cccccc">
        <ul style="list-style-type: none; padding-left: 0; margin-top: 5px">
            <li style="margin-bottom: 8px">• Tags are not fetched or processed unless you click the "Tag Stats" button.</li>
            <li style="margin-bottom: 8px">• Results are based on your current search in Librake's Favorites Search. To analyze all favorites, leave the search bar empty and press Enter.</li>
            <li style="margin-bottom: 8px">• Tag types are fetched from the R34 API and saved in a local dictionary. Use the "Clear Dictionary" button to reset this data.</li>
            <li style="margin-bottom: 8px">• Save and clear user data using the respective buttons for easier comparison.</li>
            <li style="margin-bottom: 8px">• Similarity percentages are calculated using cosine similarity, factoring in both tag presence and frequency.</li>
            <li style="margin-bottom: 8px">• Shared tag percentages are calculated as: (shared tags / total combined tags) × 100.</li>
            <li style="margin-bottom: 8px">• Tags, along with their absolute and relative frequencies, are displayed in the panel.</li>
            <li style="margin-bottom: 8px">• Shared tags appear in both lists, while exclusive tags are unique to the current user's list.</li>
            <li style="margin-bottom: 8px">• Use [-] to hide tags from view.</li>
        </ul>
    </div>
</div>

<div style="font-size: 12px; color: #cccccc">
    <div style="margin-bottom: 10px">
        Tips:
    </div>
    <ul style="list-style-type: none; padding-left: 0; margin-top: 5px">
        <li style="margin-bottom: 8px">• Loading large favorites lists (e.g., 10,000+ favorites) can be slow due to the number of tags processed.</li>
        <li style="margin-bottom: 8px">• Saved dates use DD/MM/YYYY for clarity and consistency.</li>
        <li style="margin-bottom: 8px">• Similarity percentages may be less reliable for users with very few favorites.</li>
        <li style="margin-bottom: 8px">• Use Librake's search to focus on specific tags and their associations by excluding or prioritizing them in the results (by setting their frequency to 100%).</li>
    </ul>
</div>

<div style="font-style: italic; color: #cccccc; margin-top: 20px">
    Have fun analyzing!
</div>
`;


helpButton.addEventListener('click', () => {
    // Toggle the help panel display
    helpPanel.style.display = helpPanel.style.display === 'none' ? 'block' : 'none';
});

document.body.appendChild(helpButton);
document.body.appendChild(helpPanel);

const statPanel = document.createElement('div');
statPanel.textContent = 'Most frequent tags:';
statPanel.style.position = 'fixed';
statPanel.style.top = '80px';
statPanel.style.right = '20px';
statPanel.style.width = '400px';
statPanel.style.height = '450px';
statPanel.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
statPanel.style.color = 'white';
statPanel.style.padding = '10px';
statPanel.style.borderRadius = '10px';
statPanel.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
statPanel.style.overflowY = 'auto';
statPanel.style.zIndex = '9999';
statPanel.style.display = 'none';

const toggleButton = document.createElement('button');
toggleButton.textContent = 'Tag Stats';
toggleButton.style.backgroundColor = '#00bdb0';
toggleButton.style.position = 'fixed';
toggleButton.style.color = 'white';
toggleButton.style.border = 'none';
toggleButton.style.padding = '6px';
toggleButton.style.cursor = 'pointer';
toggleButton.style.borderRadius = '3px';
toggleButton.style.fontSize = '15px';
toggleButton.style.top = '50px';
toggleButton.style.right = '20px';
toggleButton.onmouseover = () => {
    toggleButton.style.backgroundColor = '#02ada2';
};
toggleButton.onmouseout = () => {
    toggleButton.style.backgroundColor = '#00bdb0';
};

document.body.appendChild(statPanel);
document.body.appendChild(toggleButton);

const saveButton = document.createElement('span');
saveButton.innerText = '| Save my data |';
saveButton.style.padding = '3px';
saveButton.style.color = '#1c9c13';
saveButton.style.cursor = 'pointer';
saveButton.style.userSelect = 'text';
saveButton.style.marginRight = '5px';

const clearButton = document.createElement('span');
clearButton.innerText = '| Clear my data |';
clearButton.style.padding = '3px';
clearButton.style.color = '#bd0404';
clearButton.style.cursor = 'pointer';
clearButton.style.userSelect = 'text';
clearButton.style.marginLeft = '5px';

const clearDictionaryButton = document.createElement('span');
clearDictionaryButton.innerText = '| Clear dictionary |';
clearDictionaryButton.style.padding = '3px';
clearDictionaryButton.style.color = '#0b358f';
clearDictionaryButton.style.cursor = 'pointer';
clearDictionaryButton.style.userSelect = 'text';
clearDictionaryButton.style.marginLeft = '5px';

const buttonsContainer = document.createElement('div');
buttonsContainer.style.marginBottom = '10px';
buttonsContainer.style.display = 'flex';
buttonsContainer.style.justifyContent = 'flex-start';
buttonsContainer.appendChild(saveButton);
buttonsContainer.appendChild(clearButton);
buttonsContainer.appendChild(clearDictionaryButton);

const infoText = document.createElement('div');
infoText.textContent = ' ';
infoText.style.color = 'white';
infoText.style.marginBottom = '10px';
infoText.style.fontSize = '10px';

const userInfoText = document.createElement('div');
userInfoText.textContent = '';
userInfoText.style.color = 'white';
userInfoText.style.marginBottom = '10px';
userInfoText.style.fontSize = '10px';

let localTagDictionary = JSON.parse(localStorage.getItem('tagTypeDictionary') || '{}');

function getDictionarySize() {
    const dictString = JSON.stringify(localTagDictionary);
    const bytes = new Blob([dictString]).size;

    if (bytes > 1024 * 1024) {
        return `(${(bytes / (1024 * 1024)).toFixed(2)} MB)`;
    } else if (bytes > 1024) {
        return `(${(bytes / 1024).toFixed(2)} KB)`;
    }
    return `(${bytes} bytes)`;
}

const dictionaryInfoText = document.createElement('div');
dictionaryInfoText.textContent = `Tags in dictionary: ${Object.keys(localTagDictionary).length} ${getDictionarySize()}`;
dictionaryInfoText.style.color = '#52b8d1';
dictionaryInfoText.style.marginBottom = '10px';
dictionaryInfoText.style.fontSize = '10px';
dictionaryInfoText.style.fontWeight = 'normal';

const secondInfoText = document.createElement('div');
secondInfoText.textContent = '';
secondInfoText.style.color = 'white';
secondInfoText.style.marginBottom = '10px';
secondInfoText.style.fontSize = '15px';
secondInfoText.style.fontWeight = 'normal';

let currentTagFrequencies = {};
let mainUserId = localStorage.getItem('mainUserId') ? localStorage.getItem('mainUserId') : 'defaultUser';
let mainUserTags = localStorage.getItem('mainUserTags') ? JSON.parse(localStorage.getItem('mainUserTags')) : {};
let diaEHora = localStorage.getItem('diaEHora') || 'No date saved';
let mainUsername = localStorage.getItem('mainUsername') || 'No username';
let currentTagTypes = {};
let fetchedTagTypes = {};
let originalTags = [];

function getMainUserTagsSize(){
    const tagsString = JSON.stringify(mainUserTags);
    const bytes = new Blob([tagsString]).size;

    if (bytes > 1024 * 1024) {
        return `(${(bytes / (1024 * 1024)).toFixed(2)} MB)`;
    } else if (bytes > 1024) {
        return `(${(bytes / 1024).toFixed(2)} KB)`;
    }
    return `(${bytes} bytes)`;
}

if (Object.keys(mainUserTags).length === 0) {
    infoText.textContent = 'No saved data!';
} else {
    infoText.innerHTML = `Loaded data for user <span style="color: #dbb700;">${mainUsername}</span> on <span style="color: #ff3df2;">${diaEHora}</span> - ${getMainUserTagsSize()}`;
}

let lastFetchTime = 0;
const FETCH_COOLDOWN = 5000; // 5 seconds

async function fetchUsername(userId) {
    const currentTime = Date.now();
    if (currentTime - lastFetchTime < FETCH_COOLDOWN) {
        return null;
    }

    lastFetchTime = currentTime;
    const response = await fetch(`index.php?page=account&s=profile&id=${userId}`);
    const text = await response.text();
    const parser = new DOMParser();
    const doc = parser.parseFromString(text, 'text/html');
    const username = doc.querySelector("#content > h2")?.textContent || null;

    return username;
}

const currentUrl = window.location.href;

function updateCurrentUserInfo() {
    const cachedUserId = sessionStorage.getItem('currentUserId');
    const cachedUsername = sessionStorage.getItem('currentUsername');

    // Check if we're viewing a different user or if cache is empty
    if (!cachedUsername || cachedUserId !== currentUserId) {
        fetchUsername(currentUserId).then(username => {
            if (username) {
                sessionStorage.setItem('currentUserId', currentUserId);
                sessionStorage.setItem('currentUsername', username);
                userInfoText.innerHTML = `Current user: <span style="color: #dbb700;">${username}</span>`;
            }
        });
    } else {
        userInfoText.innerHTML = `Current user: <span style="color: #dbb700;">${cachedUsername}</span>`;
    }
}

async function createTagTypeMap(tags) {
    if (Object.keys(currentTagTypes).length > 0) {
        return currentTagTypes;
    }

    const uncachedTags = tags.filter(tag => !(tag in localTagDictionary));

    tags.forEach(tag => {
        if (tag in localTagDictionary) {
            fetchedTagTypes[tag] = localTagDictionary[tag];
            currentTagTypes[tag] = localTagDictionary[tag];
        }
    });

    if (uncachedTags.length > 0) {
        const maxConcurrentRequests = 100;
        let activeRequests = 0;

        async function fetchWithConcurrency(tag) {
            while (activeRequests >= maxConcurrentRequests) {
                await new Promise(resolve => setTimeout(resolve, 10));
            }

            activeRequests++;
            try {
                const url = `index.php?page=dapi&s=tag&q=index&name=${encodeURIComponent(tag)}&json=1`;
                const response = await fetch(url);

                if (!response.ok) {
                    return { tag, type: null };
                }

                const contentType = response.headers.get('content-type');
                if (contentType && contentType.includes('application/json')) {
                    const data = await response.json();
                    if (data && data.tags && data.tags.tag) {
                        const tagData = Array.isArray(data.tags.tag) ? data.tags.tag[0] : data.tags.tag;
                        if (tagData.type !== undefined) {
                            return { tag, type: parseInt(tagData.type) };
                        }
                    }
                } else if (contentType && contentType.includes('text/xml')) {
                    const text = await response.text();
                    const parser = new DOMParser();
                    const xmlDoc = parser.parseFromString(text, "application/xml");
                    const tagElement = xmlDoc.querySelector("tag");

                    if (tagElement && tagElement.getAttribute("type")) {
                        return { tag, type: parseInt(tagElement.getAttribute("type"), 10) };
                    }
                }

                return { tag, type: null };
            } catch (error) {
                return { tag, type: null };
            } finally {
                activeRequests--;
            }
        }

        const promises = uncachedTags.map(tag => fetchWithConcurrency(tag));
        const results = await Promise.all(promises);

        results.forEach(({ tag, type }) => {
            if (type !== 2) {
                localTagDictionary[tag] = type;
                fetchedTagTypes[tag] = type;
                currentTagTypes[tag] = type;
            }
        });
        localStorage.setItem('tagTypeDictionary', JSON.stringify(localTagDictionary));
        dictionaryInfoText.textContent = `Tags in dictionary: ${Object.keys(localTagDictionary).length} ${getDictionarySize()}`;
    }

    return fetchedTagTypes;
}

function getTagColor(tagType) {
    switch (tagType) {
        case 0: return '#f5f5f5';
        case 1: return '#bd0404';
        case 3: return '#9712c7';
        case 4: return '#dbb700';
        case 5: return '#52b8d1';
        default: return 'grey';
    }
}

function displayFilteredTags(filteredTags, totalImages) {
    statPanel.innerHTML = '';

    const title = document.createElement('div');
    title.textContent = 'Most frequent tags:';
    title.style.marginBottom = '10px';
    title.style.color = 'white';
    title.style.fontSize = '16px';

    const searchInput = document.createElement('input');
    searchInput.type = 'text';
    searchInput.placeholder = 'Search for tags...';
    searchInput.style.width = 'calc(100% - 20px)';
    searchInput.style.padding = '6px';
    searchInput.style.marginBottom = '10px';
    searchInput.style.borderRadius = '4px';
    searchInput.style.border = '1px solid #ccc';
    searchInput.style.boxSizing = 'border-box';

    const typeNames = {
        'general': 0,
        'artist': 1,
        'copyright': 3,
        'character': 4,
        'meta': 5,
        'null': null
    };

searchInput.addEventListener('keydown', (event) => {
    if (event.key === 'Enter') {
        let query = searchInput.value.toLowerCase();
        let filteredResults = [...originalTags];

        if (query.trim() !== "") {
            const typeFilters = query.match(/type:(general|artist|copyright|character|meta|null)/g);
            if (typeFilters) {
                const types = typeFilters.map(t => typeNames[t.split(':')[1]]);
                filteredResults = filteredResults.filter(([tag]) => {
                    const tagType = currentTagTypes[tag];
                    if (types.includes(null)) {
                        return tagType === null || tagType === undefined;
                    }
                    return types.includes(tagType);
                });
                query = query.replace(/type:(general|artist|copyright|character|meta|null)\s*/g, '');
            }

            const statusFilter = query.match(/status:(shared|exclusive)/g);
            if (statusFilter) {
                const status = statusFilter[0].split(':')[1];
                filteredResults = filteredResults.filter(([tag]) => {
                    const isShared = tag in mainUserTags;
                    return status === 'shared' ? isShared : !isShared;
                });
                query = query.replace(/status:(shared|exclusive)\s*/g, '');
            }
            const excludedTerms = query.split(" ").filter(term => term.startsWith("-") && term.length > 1);
            if (excludedTerms.length > 0) {
                const cleanExcludedTerms = excludedTerms.map(term => {
                    if (term.startsWith(`-"`) && term.endsWith(`"`)) {
                        return {term: term.substring(2, term.length - 1), exact: true}
                    } else {
                        return {term: term.substring(1), exact: false}
                    }
                });

                filteredResults = filteredResults.filter(([tag]) =>
                    !cleanExcludedTerms.some(excludedTerm => {
                        if (excludedTerm.exact) {
                            return tag.toLowerCase() === excludedTerm.term;
                        } else {
                            return tag.toLowerCase().includes(excludedTerm.term);
                        }
                    })
                );
                query = query.split(" ").filter(term => !(term.startsWith("-") && term.length > 1)).join(" ");
            }

            const exactSearchTerms = query.split(" ").filter(term => term.startsWith('"') && term.endsWith('"') && term.length > 1);
            if (exactSearchTerms.length > 0) {
                const cleanExactSearchTerms = exactSearchTerms.map(term => term.substring(1, term.length - 1));
                filteredResults = filteredResults.filter(([tag]) =>
                    cleanExactSearchTerms.some(searchTerm => tag.toLowerCase() === searchTerm)
                );
                query = query.split(" ").filter(term => !(term.startsWith('"') && term.endsWith('"') && term.length > 1)).join(" ");
            }
            if (query.trim() !== "") {
                filteredResults = filteredResults.filter(([tag]) => tag.toLowerCase().includes(query.trim()));
            }
        }

        displayFilteredTags(filteredResults, totalImages);

        const newSearchInput = statPanel.querySelector('input');
        if (newSearchInput) {
            newSearchInput.value = searchInput.value;
        }
    }
});

    statPanel.appendChild(buttonsContainer);
    statPanel.appendChild(infoText);
    statPanel.appendChild(userInfoText);
    statPanel.appendChild(dictionaryInfoText);
    statPanel.appendChild(secondInfoText);
    statPanel.appendChild(title);
    statPanel.appendChild(searchInput);

    if (filteredTags.length === 0) {
        const noTagsMessage = document.createElement('div');
        noTagsMessage.textContent = 'No tags found.';
        statPanel.appendChild(noTagsMessage);
    } else {
        const tagList = document.createElement('ul');
        tagList.style.listStyleType = 'none';
        tagList.style.padding = '0';
        tagList.style.margin = '0';
        filteredTags.forEach(([tag, frequency], index) => {
            const excludeButton = document.createElement('span');
            excludeButton.innerText = '[-]';
            excludeButton.style.padding = '3px';
            excludeButton.style.color = '#b858a5';
            excludeButton.style.cursor = 'pointer';
            excludeButton.style.userSelect = 'text';
            excludeButton.style.fontSize = '12px';

            const tagItem = document.createElement('li');
            tagItem.style.display = 'flex';
            tagItem.style.alignItems = 'flex-start';
            tagItem.style.flexWrap = 'wrap';
            tagItem.style.gap = '5px';

            const tagText = document.createElement('span');
            tagText.textContent = `${tag}: `;
            tagText.style.color = getTagColor(fetchedTagTypes[tag]);

            const frequencyText = document.createElement('span');
            frequencyText.textContent = frequency;
            frequencyText.style.color = 'white';

            const lineNumber = document.createElement('span');
            lineNumber.textContent = `${index + 1}.`;
            lineNumber.style.color = 'gray';
            lineNumber.style.minWidth = '20px';
            tagItem.appendChild(lineNumber);
            tagItem.appendChild(excludeButton);
            tagItem.appendChild(tagText);
            tagItem.appendChild(frequencyText);

            const relativeFrequency = ((frequency / totalImages) * 100).toFixed(2);
            const relativeFrequencyText = document.createElement('span');
            relativeFrequencyText.textContent = ` (${relativeFrequency}%)`;
            relativeFrequencyText.style.color = '#a65614';
            tagItem.appendChild(relativeFrequencyText);

            currentTagFrequencies[tag] = relativeFrequency;

            const tagStatus = tag in mainUserTags ? "shared" : "exclusive";
            const tagStatusText = document.createElement('span');
            tagStatusText.textContent = ` ${tagStatus}`;
            tagStatusText.style.color = tagStatus === "shared" ? "#0b358f" : "#9712c7";
            tagItem.appendChild(tagStatusText);

            excludeButton.addEventListener('click', () => {
                let currentSearch = searchInput.value;
                if (currentSearch.includes(`-"${tag}"`)) {
                    currentSearch = currentSearch.replace(`-"${tag}"`, "");
                } else {
                    currentSearch += `-"${tag}" `;
                }
                searchInput.value = currentSearch;
                searchInput.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Enter'}));
            });

            tagList.appendChild(tagItem);
        });

        statPanel.appendChild(tagList);
    }
}
async function displayTagFrequencies() {
    const images = document.querySelectorAll('#resultContainer .resultItem img');
    const allTags = [];

    images.forEach(img => {
        if (img.title) {
            const tags = img.title.split(' ').filter(tag => tag.trim() !== '');
            allTags.push(...tags);
        }
    });

    const tagFrequencies = {};
    allTags.forEach(tag => {
        tagFrequencies[tag] = (tagFrequencies[tag] || 0) + 1;
    });

    const totalImages = images.length;
    originalTags = Object.entries(tagFrequencies)
        .sort(([, frequencyA], [, frequencyB]) => frequencyB - frequencyA);

    if (Object.keys(currentTagTypes).length === 0) {
        fetchedTagTypes = await createTagTypeMap(Object.keys(tagFrequencies));
        currentTagTypes = {...fetchedTagTypes};
    }

    displayFilteredTags(originalTags, totalImages);
}

function calcularSimilaridadeTags(obj1, obj2) {
    const todasAsTags = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
    const vetorA = [];
    const vetorB = [];
    todasAsTags.forEach(tag => {
        vetorA.push(obj1[tag] || 0);
        vetorB.push(obj2[tag] || 0);
    });

    const arredondar = (numero) => Math.round(numero * 10000) / 10000;
    const magnitudeA = Math.sqrt(arredondar(vetorA.reduce((sum, val) => sum + val * val, 0)));
    const magnitudeB = Math.sqrt(arredondar(vetorB.reduce((sum, val) => sum + val * val, 0)));
    const produtoEscalar = arredondar(vetorA.reduce((sum, val, i) => sum + val * vetorB[i], 0));

    let similaridade = 0;
    if (magnitudeA !== 0 && magnitudeB !== 0) {
        similaridade = produtoEscalar / (magnitudeA * magnitudeB);
    }

    return similaridade;
}

toggleButton.addEventListener('click', async () => {
    if (statPanel.style.display === 'none') {
        statPanel.style.display = 'block';
        statPanel.innerHTML = 'Loading tag information...';
        await displayTagFrequencies();

        setTimeout(async () => {
            updateCurrentUserInfo();
        }, 500);
    } else {
        statPanel.style.display = 'none';
        toggleButton.innerText = 'Tag Stats';
    }

    const similaridadePercentage = calcularSimilaridadeTags(mainUserTags, currentTagFrequencies);
    const porcentagemExibida = similaridadePercentage * 100;

    let cor = "";
    if (porcentagemExibida <= 20) {
        cor = "#bd0404"; // Vermelho
    } else if (porcentagemExibida <= 40) {
        cor = "#e68e00"; // Laranja
    } else if (porcentagemExibida <= 60) {
        cor = "#dbb700"; // Amarelo
    } else if (porcentagemExibida <= 80) {
        cor = "#1c9c13"; // Verde
    } else {
        cor = "#8cff66"; // Verde-limão brilhante
    }

    function countCommonTags(currenttagfrequencies, mainusertags) {
        let commonCount = 0;
        for (let tag in currentTagFrequencies) {
            if (mainUserTags.hasOwnProperty(tag)) {
                commonCount++;
            }
        }
        return commonCount;
    }

    let commonTags = countCommonTags(currentTagFrequencies, mainUserTags);
    const uniqueTags = new Set([...Object.keys(mainUserTags), ...Object.keys(currentTagFrequencies)]);
    const totalTags = uniqueTags.size;
    const porcentagemTagsComuns = (commonTags / totalTags) * 100;
    let cor2 = "";
    if (porcentagemTagsComuns <= 10) {
        cor2 = "#bd0404";
    } else if (porcentagemExibida <= 25) {
        cor2 = '#dbb700';
    } else {
        cor2 = "#1c9c13";
    }

    secondInfoText.innerHTML = `Similarity: <span style="color: ${cor};">${porcentagemExibida.toFixed(2)}%</span>  <span style="margin-left: 20px;">Common Tags: <span style="color: ${cor2};">${porcentagemTagsComuns.toFixed(2)}%</span></span>`;
});

saveButton.addEventListener('click', async () => {
    mainUserTags = currentTagFrequencies;
    mainUserId = userId;
    const username = await fetchUsername(userId);
    if (username) {
        mainUsername = username;
        localStorage.setItem('mainUsername', mainUsername);
    }

    let now = new Date();
    let formattedDate = now.toLocaleDateString('pt-BR');
    let formattedTime = now.toLocaleTimeString('pt-BR');
    diaEHora = `${formattedDate} - ${formattedTime}`;

    localStorage.setItem('mainUserTags', JSON.stringify(mainUserTags));
    localStorage.setItem('mainUserId', mainUserId);
    localStorage.setItem('diaEHora', diaEHora);

    infoText.innerHTML = `Saved data for user <span style="color: #dbb700;">${mainUsername}</span> on <span style="color: #ff3df2;">${diaEHora}</span> - ${getMainUserTagsSize()}`;
    console.log('mainUserTags:', mainUserTags);
    console.log('DdiaEHora:', diaEHora);

    secondInfoText.innerHTML = `Similarity: <span style="color: #8cff66;">${'100.00%'}</span>  <span style="margin-left: 20px;">Common Tags: <span style="color: #1c9c13;">${'100.00%'}</span></span>`;
});

clearButton.addEventListener('click', () => {
    mainUserTags = {};
    mainUserId = 'defaultUser';
    diaEHora = 'No date saved';
    mainUsername = 'No username';

    localStorage.setItem('mainUserTags', JSON.stringify(mainUserTags));
    localStorage.setItem('mainUserId', mainUserId);
    localStorage.setItem('diaEHora', diaEHora);
    localStorage.setItem('mainUsername', mainUsername);
    infoText.innerHTML = `Data cleared!`;

    console.log('mainUserTags:', mainUserTags);
    console.log('mainUserId:', mainUserId);
    console.log('diaEHora:', diaEHora);

    secondInfoText.innerHTML = `Similarity: <span style="color: #bd0404;">${'0.00%'}</span>  <span style="margin-left: 20px;">Common Tags: <span style="color: #bd0404;">${'0.00%'}</span></span>`;
});

clearDictionaryButton.addEventListener('click', () => {
    const confirmClear = confirm('Are you sure you want to delete dictionary information?');

    if (confirmClear) {
        localStorage.removeItem('tagTypeDictionary');
        localTagDictionary = {};
        dictionaryInfoText.textContent = `Tags in dictionary: ${Object.keys(localTagDictionary).length} ${getDictionarySize()}`;
    }
});

//END OF NEW CODE################################################################


        document.title = tabTitle || 'Results';
        document.body.style.margin = '0';
        document.body.style.padding = '0';
        document.body.style.height = '100vh';
        document.body.style.overflow = 'hidden';

        document.body.appendChild(resultContainer);

        function updateResults(shuffledResults, columnWidth) {
            resultContainer.querySelectorAll('.resultItem').forEach(item => item.remove());

            shuffledResults.forEach(result => {
                appendResult(result);

            });
        }

        function appendResult(result) {
            const resultItem = document.createElement('div');
            resultItem.className = 'resultItem';
            resultItem.style.textAlign = 'center';
            resultItem.style.width = `${columnWidth}px`;
            resultItem.style.margin = '10px';
            resultItem.style.alignSelf = 'flex-start';

            const removeLabel = document.createElement('a');
            removeLabel.href = '#';
            removeLabel.className = 'removeLabel';
            removeLabel.style.color = darkMode ? textColor : '#009';
            removeLabel.style.fontWeight = 'bold';
            removeLabel.style.textDecoration = 'none';
            removeLabel.style.fontFamily = 'Verdana, sans-serif';
            removeLabel.style.fontSize = '100%';
            removeLabel.style.display = removeLabelsShown ? 'inline' : 'none';
            removeLabel.onclick = () => {
                document.location = `index.php?page=favorites&s=delete&id=${result.id}`;
                return false;
            };
            removeLabel.textContent = 'Remove';

            const borderStyle = result.video ? 'border: 3px solid rgb(0, 0, 255);' : '';
//IMPORTANT####################################################################################################

            resultItem.innerHTML = `
        <a href="index.php?page=post&s=view&id=${result.id}" id="p${result.id}" target="_blank">
            <img src="${result.src}" title="${result.title}" border="0" alt="" style="max-width: 100%; max-height: 100%; ${borderStyle}">
        </a><br>`
//IMPORTANT####################################################################################################

            resultItem.appendChild(removeLabel);
            resultContainer.appendChild(resultItem);
        }

      }

    function init() {
        if (customIcon) {
            updateIcon('https://i.imgur.com/EnHAGt0.png');
        }

        localStorage.setItem('scriptVersion', scriptVersion);

        SearchInputModule.createSearchInput();


        getFavoritesCount(userId).then(favoritesCount => {
            actualFavCount = favoritesCount;

            loadAllImagesFromLocalStorage(function(loadedImgs) {
            loadedImages = loadedImgs;

            if (prevFavCount > 0 && loadedImages.length > 0) {


                let loadedFirstId = loadedImages[0].link.split('id=')[1];

                fetchFavoritesPage(0).then(pageDoc => {
                    let actualFirstId = pageDoc.querySelectorAll('.thumb img')[0].parentElement.href.split('id=')[1];
                    if ((loadedFirstId != actualFirstId) && !fromBack || (userId != prevId) || (favoritesCount != prevFavCount)) {
                        needScan = true;
                    }
                    if (!needScan) {
                        allImages = loadedImages;
                        document.getElementById('progress').textContent = 'scanned';
                    }
                    else {
                        document.getElementById('progress').textContent = 'scanned (new)';
                    }
                });
            }
            else {
                needScan = true;
            }


        });
        });

    }

    const exploreModule = (() => {
        const BORDER_COLOR = '#DB1C32';
        const BORDER_THICKNESS = 3;

        function highlightFavs() {
            const savedborderFavs = localStorage.getItem('borderFavs');
            borderFavs = savedborderFavs ? JSON.parse(savedborderFavs) : true;

            if (borderFavs) {
                loadAllImagesFromLocalStorage(function(loadedImgs) {

                    const idSet = new Set();
                    loadedImgs.forEach(img => {
                        idSet.add(img.id);
                    });

                    if (loadedImgs.length > 0) {
                        const imageListDivs = document.querySelectorAll('div.image-list');
                        imageListDivs.forEach(imageListDiv => {
                            const thumbs = imageListDiv.querySelectorAll('span.thumb');

                            thumbs.forEach(thumb => {
                                const images = thumb.querySelectorAll('img');

                                images.forEach(img => {
                                    const imgId = img.src.split('?')[1];
                                    if (idSet.has(imgId)) {
                                        img.style.border = `${BORDER_THICKNESS}px solid ${BORDER_COLOR}`;
                                    }
                                });
                            });
                        });
                    }
                });
            }
        }
        return {
            highlightFavs
        };
    })();


    if(onFavPage) {
        userId = getIdFromUrl();
        isMobile = isMobileVersion();
        darkMode = isDarkMode();
        loadSavedData();
        init();
    }
    else {
        exploreModule.highlightFavs();
    }


})();