Raw Source
aolko / PH toolbox

// ==UserScript==
// @name         PH toolbox
// @namespace    http://tampermonkey.net/
// @version      0.6.5
// @description  PornHub toolbox (https://codeberg.org/aolko/userscripts)
// @author       aolko
// @license      GPL-3.0-or-later
// @match        *://*.pornhub.com/*
// @match        *pornhub.com*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=pornhub.com
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_openInTab
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_listValues
// @grant        GM_registerMenuCommand
// @resource     PH_CSS https://codeberg.org/aolko/userscripts/raw/branch/master/ph_toolbox/ph.style.css?v1
// @require      https://code.jquery.com/jquery-3.6.1.min.js
// @require      https://openuserjs.org/src/libs/sizzle/GM_config.js
// @updateURL    https://codeberg.org/aolko/userscripts/raw/branch/master/ph_toolbox/PH%20toolbox.user.js
// @downloadURL  https://codeberg.org/aolko/userscripts/raw/branch/master/ph_toolbox/PH%20toolbox.user.js
// ==/UserScript==

/*
Check the official repo: https://codeberg.org/aolko/userscripts
*/

/*! instant.page v5.1.1 - (C) 2019-2020 Alexandre Dieulot - https://instant.page/license */
let t,e;const n=new Set,o=document.createElement("link"),i=o.relList&&o.relList.supports&&o.relList.supports("prefetch")&&window.IntersectionObserver&&"isIntersecting"in IntersectionObserverEntry.prototype,s="instantAllowQueryString"in document.body.dataset,a="instantAllowExternalLinks"in document.body.dataset,r="instantWhitelist"in document.body.dataset,c="instantMousedownShortcut"in document.body.dataset,d=1111;let l=65,u=!1,f=!1,m=!1;if("instantIntensity"in document.body.dataset){const t=document.body.dataset.instantIntensity;if("mousedown"==t.substr(0,"mousedown".length))u=!0,"mousedown-only"==t&&(f=!0);else if("viewport"==t.substr(0,"viewport".length))navigator.connection&&(navigator.connection.saveData||navigator.connection.effectiveType&&navigator.connection.effectiveType.includes("2g"))||("viewport"==t?document.documentElement.clientWidth*document.documentElement.clientHeight<45e4&&(m=!0):"viewport-all"==t&&(m=!0));else{const e=parseInt(t);isNaN(e)||(l=e)}}if(i){const n={capture:!0,passive:!0};if(f||document.addEventListener("touchstart",function(t){e=performance.now();const n=t.target.closest("a");if(!h(n))return;v(n.href)},n),u?c||document.addEventListener("mousedown",function(t){const e=t.target.closest("a");if(!h(e))return;v(e.href)},n):document.addEventListener("mouseover",function(n){if(performance.now()-e<d)return;if(!("closest"in n.target))return;const o=n.target.closest("a");if(!h(o))return;o.addEventListener("mouseout",p,{passive:!0}),t=setTimeout(()=>{v(o.href),t=void 0},l)},n),c&&document.addEventListener("mousedown",function(t){if(performance.now()-e<d)return;const n=t.target.closest("a");if(t.which>1||t.metaKey||t.ctrlKey)return;if(!n)return;n.addEventListener("click",function(t){1337!=t.detail&&t.preventDefault()},{capture:!0,passive:!1,once:!0});const o=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!1,detail:1337});n.dispatchEvent(o)},n),m){let t;(t=window.requestIdleCallback?t=>{requestIdleCallback(t,{timeout:1500})}:t=>{t()})(()=>{const t=new IntersectionObserver(e=>{e.forEach(e=>{if(e.isIntersecting){const n=e.target;t.unobserve(n),v(n.href)}})});document.querySelectorAll("a").forEach(e=>{h(e)&&t.observe(e)})})}}function p(e){e.relatedTarget&&e.target.closest("a")==e.relatedTarget.closest("a")||t&&(clearTimeout(t),t=void 0)}function h(t){if(t&&t.href&&(!r||"instant"in t.dataset)&&(a||t.origin==location.origin||"instant"in t.dataset)&&["http:","https:"].includes(t.protocol)&&("http:"!=t.protocol||"https:"!=location.protocol)&&(s||!t.search||"instant"in t.dataset)&&!(t.hash&&t.pathname+t.search==location.pathname+location.search||"noInstant"in t.dataset))return!0}function v(t){if(n.has(t))return;const e=document.createElement("link");e.rel="prefetch",e.href=t,document.head.appendChild(e),n.add(t)}

/* PageAutomator */
function PageAutomator(){this.hover=function(t){return document.querySelector(t).dispatchEvent(new MouseEvent("mouseover")),this},this.click=function(t,e="left"){var n=document.querySelector(t);return n?("left"===e?n.dispatchEvent(new MouseEvent("click")):"right"===e&&n.dispatchEvent(new MouseEvent("contextmenu")),this):(console.log("Error: element not found"),this)},this.scroll=function(t){return window.scrollBy(0,t),this},this.scrollTo=function(t){return t.scrollIntoView({behavior:"smooth",block:"start",inline:"nearest"}),this},this.hold=function(t,e){var n=document.querySelector(t);return"left"===e?n.dispatchEvent(new MouseEvent("mousedown")):"right"===e&&n.dispatchEvent(new MouseEvent("mousedown",{button:2})),this},this.moveToPosition=function(t,e){return window.dispatchEvent(new MouseEvent("mousemove",{clientX:t,clientY:e})),this},this.moveToElement=function(t){var e=document.querySelector(t);if(!e)return console.log("Error: element not found"),this;var n=e.getBoundingClientRect(),o=n.left+window.pageXOffset+n.width/2,i=n.top+window.pageYOffset+n.height/2;return window.dispatchEvent(new MouseEvent("mousemove",{clientX:o,clientY:i})),this},this.getPosition=function(){var t={x:0,y:0};return document.addEventListener("mousemove",(function(e){t.x=e.clientX,t.y=e.clientY})),t},this.keyPress=function(t){var e=new KeyboardEvent("keypress",{key:t});return document.dispatchEvent(e),this},this.keyUp=function(t){var e=new KeyboardEvent("keyup",{key:t});return document.dispatchEvent(e),this},this.keyDown=function(t){var e=new KeyboardEvent("keydown",{key:t});return document.dispatchEvent(e),this},this.holdKey=function(t,e){var n={ctrl:17,shift:16,alt:18,win:91},o=new KeyboardEvent("keydown",{keyCode:n[t],which:n[t]});document.dispatchEvent(o),e();o=new KeyboardEvent("keyup",{keyCode:n[t],which:n[t]});return document.dispatchEvent(o),this},this.holdKeySequence=function(t,e){return Mousetrap.bind(t,(function(){e(),Mousetrap.unbind(t)}),"keydown"),this},this.setKeyState=function(t,e){if("numlock"===t){var n=new KeyboardEvent("keydown",{key:"NumLock",code:"NumLock"});document.dispatchEvent(n)}else if("scrolllock"===t){n=new KeyboardEvent("keydown",{key:"ScrollLock",code:"ScrollLock"});document.dispatchEvent(n)}else if("capslock"===t){n=new KeyboardEvent("keydown",{key:"CapsLock",code:"CapsLock"});document.dispatchEvent(n)}return this},this.blockInput=function(){return document.addEventListener("keydown",(function(t){t.preventDefault()})),document.addEventListener("mousedown",(function(t){t.preventDefault()})),this},this.wait=function(t){for(var e=(new Date).getTime(),n=e;n<e+t;)n=(new Date).getTime();return this},this.waitForElement=function(t){for(var e=document.querySelector(t);!e;)e=document.querySelector(t);return this},this.waitForMouse=function(t){for(var e=document.body.style.cursor;e!==t;)e=document.body.style.cursor;return this},this.ifElement=function(t,e,n){var o=document.querySelector(t);return"contains"===e?!!o.innerHTML.includes(n):"does not contain"===e?!o.innerHTML.includes(n):"is"===e?o.innerHTML===n:"is not"===e?o.innerHTML!==n:this},this.onElement=function(t,e){var n=document.querySelector(t);return new MutationObserver((function(t){return e.call(n),this})).observe(n,{attributes:!0,childList:!0,characterData:!0}),this},this.hasText=function(t,e){return t.textContent.trim().includes(e)},this.hasElement=function(t,e){return null!==t.querySelector(e)},this.showNotification=function(t,e){new Notification(t,{body:e});return this},this.showDialog=function(t,e){var n=document.createElement("dialog"),o=document.createElement("strong");o.innerHTML=t;var i=document.createElement("p");return i.innerHTML=e,n.appendChild(o),n.appendChild(i),document.body.appendChild(n),n.show(),this},this.showCustomDialog=function(t){var e=document.createElement("dialog");return e.innerHTML=t,document.body.appendChild(e),e.show(),this},this.getClipboardText=function(){return navigator.clipboard.readText().then((t=>t))},this.setClipboardText=function(t){return navigator.clipboard.writeText(t),this},this.clearClipboard=function(){return navigator.clipboard.writeText(""),this},this.ifUrl=function(t,e){(t.startsWith("/")&&window.location.pathname===t||"/"===t&&"/"===window.location.pathname||window.location.href===t)&&e()},this.navigate=function(t){window.location.href=t},this.get_domain=function(){return window.location.hostname},this.get_protocol=function(){return window.location.protocol},this.get_page=function(){return window.location.pathname},this.get_query=function(){return window.location.search}}

/* globals $ */
var $ = window.jQuery;

var frame = document.createElement('div');
document.body.appendChild(frame);

GM_config.init(
{
  'id': 'ph__config', // The id used for this instance of GM_config
  'title': "⚙ PH toolbox settings",
  'fields': // Fields object
  {
    'labsFrontend': {
      'label': 'Experimental frontend',
      'section': 'Experimental',
      'type': 'checkbox',
      'default': false
    },
    'Layout': {
      'label': 'Layout',
      'section': 'General',
      'type': 'radio',
      'options': ['Default','Basic'],
      'default': 'Default'
    },
    'HideShortVids': {
      'label': 'Hide short videos',
      'type': 'checkbox',
      'default': false
    },
    'ShortVidMin': {
      'label': 'Minimum video length (in seconds)',
      'type': 'int',
      'min': 0,
      'max': 600,
      'default': 60
    }
  },
  'events':
  {
    'open': function(){
      GM_config.frame.setAttribute('style', `
        position: fixed; /* Stay in place */
        display: flex !important;
        flex-direction: column;
        align-items: center;
        z-index: 9999; /* Sit on top */
        left: 0;
        top: 0;
        width: 100%; /* Full width */
        height: 100%; /* Full height */
        overflow: auto; /* Enable scroll if needed */
      `);
    }
  },
  'frame': frame,
  'css': `
  #ph__config{background: hsl(0 0% 0% / .8);}
  #ph__config_wrapper{
    position: relative;
    top: 20%;
    margin:auto !important;
    background: #000;
    border-radius: 8px;
    max-width: 500px;
    border: 2px solid #ff9000;
    transition: all .5s ease-in-out;
  }
  #ph__config_header,.config_var{padding: 10px;}
  `
});

if (typeof GM_registerMenuCommand === 'function') {
    GM_registerMenuCommand('Settings', function() {
        GM_config.open();
    });
}

function getElementByXpath(path) {
  return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

function match(string, cases) {
  var matched = false;
  for (var key in cases) {
    if (key.includes("||")) {
      var orKeys = key.split("||");
      orKeys.forEach(function(orKey) {
        if (orKey === string) {
          cases[key]();
          matched = true;
        }
      });
    } else if (key === string) {
      cases[key]();
      matched = true;
    }
  }
  if (!matched && cases.hasOwnProperty("default")) {
    cases["default"]();
  } else if (!matched) {
    throw new Error(`No case found for ${string}`);
  }
}


function showVids(url){
  var full_url = ph__domain + url+"/videos";
  console.log(full_url);
  $(`body`).append(`
             <dialog class="ph_dialog ph_modelVideos__window">
                <div class="heading">
                    <div class="left"><h2><i class="fa-solid fa-puzzle-piece-simple"></i> Model's videos</h2></div>
                    <div class="right"><a id="ph_modal-close" class="button"><i class="fa-solid fa-times"></i></a></div>
                </div>
                <div class="ph_modelVideos__container"></div>
             </dialog>
         `)
  //$( "dialog.phmodelVideos__window" ).load( "${full_url}/videos #videosTab > div > div > div:nth-child(2)" );
  $.ajax({
    url:full_url,
    type: 'GET',
    cache: false,
    success: function(data){
      $('dialog.ph_modelVideos__window > .ph_modelVideos__container').html($(data).find('#videosTab > .sectionWrapper > .profileVids').html());
    }
  });
};

function parseURL(url){
  var getQueryParams = function (query) {
    var params = {};
    new URLSearchParams(query).forEach(function (value, key) {
      var decodedKey = decodeURIComponent(key);
      var decodedValue = decodeURIComponent(value);
      // This key is part of an array
      if (decodedKey.endsWith("[]")) {
        decodedKey = decodedKey.replace("[]", "");
        params[decodedKey] || (params[decodedKey] = []);
        params[decodedKey].push(decodedValue);
        // Just a regular parameter
      } else {
        params[decodedKey] = decodedValue;
      }
    });

    return params;
  };

  var _url = new URL(url);
  _url._path = _url.pathname.split( '/' ).join(`#/#`).split(`#`);
  _url._path[0] = _url.origin;
  _url._params = getQueryParams(_url.searchParams);
  return _url;
}

function secondsToTime(e) {
  var h = Math.floor(e / 3600).toString().padStart(2, '0'),
      m = Math.floor(e % 3600 / 60).toString().padStart(2, '0'),
      s = Math.floor(e % 60).toString().padStart(2, '0');
  
  if (h > 0) {
    // Include the hours value in the output string if it is greater than 0
    return h + ':' + m + ':' + s;
  } else {
    // Omit the hours value if it is 0 or less
    return m + ':' + s;
  }
}


function timeToSeconds(e) {
  // Split the input time string into an array of hours, minutes, and seconds
  var [m, s] = e.split(":");
  
  // Convert the hours, minutes, and seconds to integers and return the total number of seconds
  return parseInt(m, 10) * 60 + parseInt(s, 10);
}

function shuffleMessages(messages) {
  for (let i = messages.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1));
    [messages[i], messages[j]] = [messages[j], messages[i]];
  }
  return messages;
}

function getRandomMessage(dict) {
  let messages = shuffleMessages(dict);
  return messages[0];
}

const dict__messages = {
  video: {
    malicious: [
      "This video is a scam and contains malicious content",
      "This video contains ads and is trying to sell you something",
      "This video is promoting premium accounts or external links",
      "This video contains external links that could be harmful"
    ],
    tease: [
      "This video might contain no climax or might contain external links",
      "This video might be edited to cut out content and might contain external links",
      "This video might contain purposely cut content and might contain external links"
    ],
    wrong_classification: [
      "This video is not in the correct category",
      "This video is not using the correct tags"
    ]
  },
  profile: {
    malicious: [
      "This page is a scam and contains malicious content",
      "This page contains ads and is trying to sell you something",
      "This page is promoting premium accounts or external links",
      "This page contains external links that could be harmful"
    ],
    tease: [
      "This page might contain no climax or might contain external links",
      "This page might be edited to cut out content and might contain external links",
      "This page might contain purposely cut content and might contain external links"
    ],
    wrong_classification: [
      "This page is not in the correct category",
      "This page is not using the correct tags"
    ],
    inactive_profile: [
      "This profile is fake or no longer active.",
      "This user has not been active on the site in some time or this page is fake.",
      "We're sorry, but this profile is no longer available or this page is fake.",
      "Unfortunately, the user associated with this profile is no longer active on the platform.",
    ]
  },
  something_went_wrong: [
    "Whoops, something broke here, try to reload the page",
    "There was an error with loading the data, please try again later",
    "There was a script error, please try again later"
  ]
  
}



const ph__domain = window.location.origin;

// global namespace for placing cleanup functions in
var cleanup = cleanup || {};

const ph__url = parseURL(window.location.href);

window.addEventListener("load", function(){
  'use strict';

  console.log("[PageAutomator] Automator started");

  var automator = new PageAutomator();

  console.log(`[PH toolbox] Started`);

  // TODO: Update elements for theme refresh in PH

  cleanup.page = function(){
    $(`#age-verification-container,#age-verification-wrapper,.abAlertShown,#js-abContainterMain`).remove();
  }
  cleanup.header = function(){
    var elements = [
      ".networkBarWrapper",
      "#countryRedirectMessage",
      "#welcome",
      "#leftMenu a.menuLink.customUpgradeBtn.removeAdLink",
      "#leftMenu a.menuLink.halfWidth.js-menuAnalytics[href*='https://www.modelhub.com/']",
      "#leftMenu a.menuLink.halfWidth.js-menuAnalytics[href*='https://www.pornhubapparel.com/']",
      "#leftMenu a.menuLink.halfWidth.js-menuAnalytics[href*='https://www.uviu.com/']",
      "#leftMenu a.menuLink.halfWidth.leftItem.js-menuAnalytics[href*='https://www.pornhub.com/sex/']",
      "#leftMenu button.menuLink.removeAdLink",
      //"#profileMenuDropdownScroll > div.menuSection > .menuLink:has(a[href*='/user/orders'])",
      "#profileMenuDropdownScroll > div.menuSection > .menuLink:has(a[href*='/user/verification'])",
      "#profileMenuDropdownScroll > div.menuSection > .menuLink > a[href*='/user/orders']",
      "header > #headerMenuContainer",
      "body > div.wrapper > div.container > div.frontListingWrapper > div:nth-child(1) > div",
    ];
    elements.forEach(function(el) { 
      $(el).remove();
    });
    $(`#coummunityMenuItems > li:nth-child(3)`).remove();
    $(getElementByXpath(`/html/body/div[4]/div[3]/div[5]/div[2]/comment()[3]`)).next().remove();
    $(`#menuItem2 > div > div > div.leftPanel.videos > ul > li:nth-child(10)`).remove();
    $(`#dropdownHeaderSubMenu > div.innerHeaderSubMenu.trendingWrapper`).remove();
    $(`#menuItem3 > div > div > div.rightPanel`).remove();
    $(`#menuItem3 > div > div > div.leftPanel > div.pornInLang`).remove();
  }
  cleanup.body = function(){
    $(`#player`).removeClass(`original`);
    $(`#player`).addClass(`wide`);
    $(`#hd-rightColVideoPage`).removeClass(`original`);
    $(`#hd-rightColVideoPage`).addClass(`wide`);
    $(`div.mgp_controlBar > div.mgp_front > div.mgp_cinema`).remove();
    $(`#recommendedVideos`).attr('class','videos user-playlist allRecommendedVideos');
    $(`#recommendedVideosVPage > a`).remove();
    $(`#hd-leftColVideoPage > div.video-wrapper.js-relatedRecommended.js-relatedVideos.relatedVideos`).addClass('allRelatedVideos');
    $(`#loadMoreRelatedVideosCenter`).remove();
  }

  $("#profileMenuDropdownScroll > div.menuSection > .menuLink > a[href*='/feeds']").removeClass("halfWidth");
  $("#profileMenuDropdownScroll > div.menuSection > .menuLink > a[href*='/feeds'] > span").addClass("inline");
  $("#profileMenuDropdownScroll > div.menuSection > .menuLink > a[href*='/feeds'] > .iconHolder").remove();
  $(`<i class="ph-icon-rss-feed inline"></i>`).insertBefore("#profileMenuDropdownScroll > div.menuSection > .menuLink > a[href*='/feeds'] > span");
  $("#leftMenu a.menuLink[href*='https://help.pornhub.com/hc/en-us/categories/4419836212499']").removeClass("halfWidth");
//
  var flags = JSON.parse(localStorage.getItem('flags')) || {"videos":[],"models":[]};

  var favCategories = [
    { "name": "Uncategorized", "id": "0" },
    { "name": "General", "id": "1" }
  ];

  // Global styles
  GM_addStyle(`
        /* https://sleazyfork.org/ru/scripts/368777-pornhub-crack-for-russia */
        #age-verification-container,#age-verification-wrapper,.abAlertShown,#js-abContainterMain{display:none!important}

        @supports (display: grid) {
            html.supportsGridLayout #header #headerMenuContainer #headerMainMenuInner ul#headerMainMenu>li .wideDropdown.categories .innerDropdown {
                grid-template-columns: .75fr auto !important;
            }
            html.supportsGridLayout #header {
                grid-template-rows: auto !important;
            }
        }

        #header #headerMenuContainer #headerMainMenuInner ul#headerMainMenu>li .wideDropdown.categories .middlePanel{
            width: 100%;
        }

        .ph_dialog{
            margin:auto !important;
            background: #000;
            border-radius: 8px;
            min-width: 500px;
            max-width: 800px;
            border: 2px solid #ff9000;
            transition: all .5s ease-in-out;
        }

        .ph_dialog > .heading{
            display: flex;
            align-content: center;
            padding: 10px;
            background: hsl(0,0%,10%);
        }

        .ph_dialog > .heading > a{
            text-decoration: unset;
            color: #ff9000;
        }

        .ph_dialog > .heading > *{
            display: flex;
            align-content: center;
        }

        .ph_dialog > .heading > .left > .button,.ph_dialog > .heading > .right > .button{
            display: flex;
            justify-content: center;
            align-items: center;
            width: 24px;
            height: 24px;
            font-size: 16pt;
            padding: 0;
            margin: 0;
        }

        .ph_dialog > .heading > .left{
            flex: 1;
        }

        .ph_dialog > .heading > .right{
            margin-left: auto;
        }

        .ph_dialog > .body{
            color: var(--ph__white);
            padding: 10px;
            overflow-y: auto;
            max-height: 800px;
        }

        .ph_dialog > .body >*:not(:last-child){
          margin: 0 0 10px 0;
        }

        .ph_comments__window > .ph_comments__container{
            padding: 10px;
        }

        .ph_comments__window > .ph_comments__container .cmtHeader{
            padding: 1em !important;
        }

        .ph_comments__window > .ph_comments__container #cmtContent{
            overflow-y: auto;
            max-height: 500px;
        }

        .ph_recommended__window > .ph_recommended__container #recommendedVideos{
            overflow-y: auto;
            max-height: 500px;
        }

        .ph_related__window > .ph_related__container #relatedVideosCenter{
            overflow-y: auto;
            max-height: 500px;
        }

        .ph_modelVideos__window > .ph_modelVideos__container{
            overflow-y: auto;
            max-height: 500px;
        }

        .ph_dialog::backdrop{
            z-index: 1;
            background: rgba(0,0,0,.8);
        }

        .ph_video-actions button{
            padding: 4px 24px;
            display: inline-block;
            margin-bottom: 5px;
            border-radius: 8px;
            padding: 8px 18px;
            background: #1b1b1b;
            font-weight: 400;
            font-size: 14px;
            color: #fff;
            text-transform: capitalize;
            white-space: nowrap;
            border: 1px solid #ff9000;
        }
        .ph_video-actions button:hover{
            text-decoration: none;
            background-color: #2f2f2f;
        }
        .ph_videoShortcuts1{
          display: inline-flex;
          gap: 4px;
        }
        #ph_flag__msgbox{
          display: flex;
          gap: 8px;
          align-items: flex-start;
          padding: 8px;
          boder-radius: 8px;
          background: #ff9000;
          color: black;
        }
        #ph_flag__msgbox .icon{
          display: flex;
          flex: 0 0 32px;
          align-items: center;
          justify-content: center;
          height: 24px;
          width: 24px;
          font-size: 16px;
        }
        #ph_flag__msgbox .text{
          flex: 1;
        }
        .video-action-sub-tab.addToStream li .wrap .thumbnail-info-wrapper span.title, ul.videos li .wrap .thumbnail-info-wrapper span.title {
            max-height: min-content;
        }
        @media only screen and (min-width: 1350px){
            .video-action-sub-tab.addToStream li .wrap .thumbnail-info-wrapper span.title, ul.videos li .wrap .thumbnail-info-wrapper span.title {
                max-height: min-content;
            }
        }
    `);

  GM_addStyle(GM_getResourceText("PH_CSS"));

  $(`header`).append(`
  <link href="//cdn.jsdelivr.net/npm/@sweetalert2/theme-dark@4/dark.css" rel="stylesheet">
  <script src="https://github.com/lofcz/sweetalert2-neutral/releases/download/v11.6.15-NEUTRAL/sweetalert2.all.min.js" defer></script>
  `);

  $(`body`).append(`
        <script src="https://cdn.jsdelivr.net/gh/aolko/fontawesome-pro@master/fontawesome-pro-6.1.2-web/js/all.min.js" data-auto-replace-svg="nest"></script>
        <script src="https://cdn.jsdelivr.net/gh/aolko/fontawesome-pro@master/fontawesome-pro-6.1.2-web/js/v4-shims.min.js"></script>
    `);

  console.info("[PH Toolbox] Cleaning up the page...");
  cleanup.page();
  cleanup.header();
  cleanup.body();

  console.info("[PH Toolbox] Guessing your location...");

  // LOCAL: specific pages

  if(window.location.href.indexOf("/video/search") > -1 || window.location.href.indexOf("/transgender") > -1) {
    // PH search page
    console.info("[PH Toolbox] You are currently searching for a video");
    if(GM_config.get("HideShortVids") === true){
      var hiddenEl = 0;
      $(`ul.videos > .videoBox`).each(function(){
        var videoDuration = $(`.linkVideoThumb > .marker-overlays > var.duration`,this).text();
        if(timeToSeconds(videoDuration) < GM_config.get("ShortVidMin")){
          $(this).hide();
          hiddenEl++;
        }
      });
      console.log(`[PH Toolbox] Hidden ${hiddenEl} videos shorter than ${secondsToTime(GM_config.get("ShortVidMin"))}`);
    }
  }
  else if(window.location.href.indexOf("/view_video.php") > -1) {
    // PH video page
    console.info("[PH Toolbox] You are currently watching a video");
    var modelURL = $(`#hd-leftColVideoPage > div:nth-child(1) > div.video-actions-container > div.video-actions-tabs > div.video-action-tab.about-tab.active > div.video-detailed-info > div.video-info-row.userRow > div.userInfo > div > span > a`).attr('href');

    $(`#hd-leftColVideoPage > div:nth-child(1) > div.video-actions-container > div.video-actions-tabs > div.video-action-tab.about-tab.active > div.video-detailed-info > div.video-info-row.userRow > div.userInfo > div.usernameWrap > span.usernameBadgesWrapper`).append(`
            <div class="ph_quickActions" style="display: flex; align-items: center; gap: 5px; margin: 0 5px 0 0;">
                <a href="${modelURL}/photos" data-popup><i class="fa-solid fa-images-user"></i> images</a>
                <a href="${modelURL}/videos" data-popup><i class="fa-solid fa-film"></i> videos</a>
            </div>
        `);

    $(`#ph_loadVids`).click(function(){
      var modelURL = $(`#hd-leftColVideoPage > div:nth-child(1) > div.video-actions-container > div.video-actions-tabs > div.video-action-tab.about-tab.active > div.video-detailed-info > div.video-info-row.userRow > div.userInfo > div > span > a`).attr('href');
      showVids(modelURL);
      document.querySelector(`.ph_modelVideos__window`).showModal();
    });

    $(`#relatedVideosCenter > .pcVideoListItem`).each(function(){
      var related_modelURL = $(`.wrap > .thumbnail-info-wrapper > .videoUploaderBlock > .usernameWrap > a`,this).attr('href');
      var related_vidURL = $(`.wrap > .thumbnail-info-wrapper > .title > a`,this).attr('href');
      $(`.wrap > .thumbnail-info-wrapper > .title`,this).append(`
                <div class="ph_quickActions">
                    <a href="https://9xbud.com/https://www.pornhub.com/${related_vidURL}" class="tooltipTrig" data-title="Download"><i class="fa-solid fa-download"></i></a>
                </div>
            `);
    });

    $(`#recommendedVideos > .pcVideoListItem`).each(function(){
      var recommended_modelURL = $(`.wrap > .thumbnail-info-wrapper > .videoUploaderBlock > .usernameWrap > a`,this).attr('href');
      var recommended_vidURL = $(`.wrap > .thumbnail-info-wrapper > .title > a`,this).attr('href');
      $(`.wrap > .thumbnail-info-wrapper > .title`,this).append(`
                <div class="ph_quickActions">
                    <a href="https://9xbud.com/https://www.pornhub.com/${recommended_vidURL}" class="tooltipTrig" data-title="Download"><i class="fa-solid fa-download"></i></a>
                </div>
            `);
    });

    $(`body`).append(`
            <dialog class="ph_dialog ph_comments__window">
                <div class="heading">
                    <div class="left"><h2><i class="fa-solid fa-puzzle-piece-simple"></i> Comments</h2></div>
                    <div class="right"><a id="ph_modal-close" class="button"><i class="fa-solid fa-times"></i></a></div>
                </div>
                <div class="ph_comments__container"></div>
            </dialog>
        `);

    $(`body`).append(`
            <dialog class="ph_dialog ph_recommended__window">
                <div class="heading">
                    <div class="left"><h2><i class="fa-solid fa-puzzle-piece-simple"></i> Recommended videos</h2></div>
                    <div class="right"><a id="ph_modal-close" class="button"><i class="fa-solid fa-times"></i></a></div>
                </div>
                <div class="ph_recommended__container"></div>
            </dialog>
        `);

    $(`body`).append(`
            <dialog class="ph_dialog ph_related__window">
                <div class="heading">
                    <div class="left"><h2><i class="fa-solid fa-puzzle-piece-simple"></i> Related videos</h2></div>
                    <div class="right"><a id="ph_modal-close" class="button"><i class="fa-solid fa-times"></i></a></div>
                </div>
                <div class="ph_related__container"></div>
            </dialog>
        `);

    function isFlagged(){
      var cur_url = ph__url.href;
      cur_url = cur_url.replace(/#+$/, ''); // Remove # character from the end of the URL
      if(localStorage.flags){
        var flags = JSON.parse(localStorage.flags);
        // check against current url
        var video = $.grep(flags.videos, function(v) {
          return v.url === cur_url;
        })[0];
        if (video) {
          return {
            flagged: true,
            reason: video.reason,
            url: video.url
          };
        }
      }
      return { flagged: false };
    }
             
    $(`body`).append(`
        <dialog class="ph_dialog ph_flagVideo__window">
            <div class="heading">
                <div class="left"><h2><i class="fa-solid fa-flag"></i> Flag video</h2></div>
                <div class="right"><a id="ph_modal-close" class="button"><i class="fa-solid fa-times"></i></a></div>
            </div>
            <div class="body">
              <div style="display:flex; gap: 4px;">
                <select id="ph_videoFlagReason" style="flex:1;">
                  <option value="scam">Scam</option>
                  <option value="ad">Ad</option>
                  <option value="low_quality">Low quality</option>
                  <option value="no_finisher">No orgasm/cumshot/creampie/climax</option>
                  <option value="teaser">Teaser</option>
                  <option value="wrong_tags">Wrong tags</option>
                  <option value="wrong_category">Wrong category</option>
                </select>
                <button id="ph_flagVideo_btn">Flag video</button>
              </div>
              <div style="display:flex; gap: 4px;">
                <span style="flex:1;font-weight:600;" id="ph_flagVideo_status"></span>
                <button id="ph_flagVideo_clear_btn">Clear flags</button>
              </div>
            </div>
        </dialog>
    `);
    $(`<div id="ph_flag__msgbox"><span class="icon"><i class="fa-regular fa-flag"></i></span><span class="text"></span></div>`).insertBefore(`#player`);
    if(isFlagged().flagged){
      $(`#ph_flagVideo_status`).html(`<i class="fa-solid fa-warning fa-fw"></i> This video was flagged as "${isFlagged().reason}"`);
      $(`#ph_videoFlagReason`).prop("disabled",true);
      $(`#ph_flagVideo_btn`).prop("disabled",true);
      match(isFlagged().reason, {
        "scam||ad||low_quality": function() { 
            $(`#ph_flag__msgbox>.text`).text(`
              ${getRandomMessage(dict__messages.video.malicious)}
            `);
         },
        "no_finisher||teaser": function() { 
            $(`#ph_flag__msgbox>.text`).text(`
              ${getRandomMessage(dict__messages.video.tease)}
            `);
         },
         "wrong_tags||wrong_category": function(){
            $(`#ph_flag__msgbox>.text`).text(`
              ${getRandomMessage(dict__messages.video.wrong_classification)}
            `);
         }
      });
      
    } else {
      $(`#ph_flag__msgbox`).remove();
      $(`#ph_flagVideo_status`).text(`This video wasn't flagged`);
    }

    
    $(`#under-player-comments`).appendTo(`.ph_comments__container`);

    $(`#hd-leftColVideoPage > div:nth-child(1) > div.video-actions-container > div.video-actions-tabs > div.video-action-tab.about-tab.active > div.video-detailed-info > div.video-info-row.userRow > div.userActions`).append(`
            <div class="ph_commentsButton videoSubscribeButton">
                <button type="button">
                    <i class="buttonIcon"></i>
                    <span class="buttonLabel">Comments</span>
                </button>
            </div>
        `);

    $(`#hd-leftColVideoPage > div:nth-child(1) > div.title-container.translate > h1`).append(`<div class="ph_videoShortcuts1"></div>`);
    $(`#hd-leftColVideoPage > div:nth-child(1) > div.title-container.translate > h1 > .ph_videoShortcuts1`).append(`<a id="ph_incognitoTab" href="#"><i class="fa-solid fa-user-secret"></i></a>`);
    $(`#hd-leftColVideoPage > div:nth-child(1) > div.title-container.translate > h1 > .ph_videoShortcuts1`).append(`<a id="ph_flag_btn" href="#"><i class="fa-solid fa-flag"></i></a>`);

    $(`#ph_incognitoTab`).click(function(){
      GM_openInTab(window.location.href, {"incognito":true});
    });

    $(`.ph_commentsButton>button`).click(function(){
      document.querySelector(`.ph_comments__window`).showModal();
    });

    $(`<div class="video-wrapper ph_video-actions" style="margin:20px 0; padding: 10px;"></div>`).insertAfter(`#hd-leftColVideoPage > div:nth-child(1)`);
    $(`.ph_video-actions`).html(`
            <button id="ph_videoOnly_btn">Basic layout</button>
            <button id="ph_videoDownload_btn">Video download</button>
        `);

    if(GM_config.get("Layout") === "Basic"){
      $(`#ph_videoOnly_btn`).hide();
      $(`.ph_video-actions`).append(`
            <button id="ph_relatedVids_btn">Related</button>
            <button id="ph_recommendedVids_btn">Recommended</button>
        `);

      $(`#ph_recommendedVids_btn`).click(function(){
        document.querySelector(`.ph_recommended__window`).showModal();
      });

      $(`#ph_relatedVids_btn`).click(function(){
        document.querySelector(`.ph_related__window`).showModal();
      });

      $(`#ph_flag_btn`).click(function(){
        document.querySelector(`.ph_flagVideo__window`).showModal();
      });

      $(`#recommendedVideos`).appendTo(`.ph_recommended__container`);
      $(`#relatedVideosCenter`).appendTo(`.ph_related__container`);
      $(`#hd-rightColVideoPage`).hide();
      $(`#hd-leftColVideoPage > div.video-wrapper.js-relatedRecommended.js-relatedVideos.relatedVideos.allRelatedVideos`).hide();
      $(`#under-player-playlists`).hide();

      GM_addStyle(`
        html.supportsGridLayout.fluidContainer #main-container #vpContentContainer:not(.premiumLocked) {
            grid-template-columns: 1fr;
        }
      `);
    } else {

    }

    $(`#ph_flagVideo_btn`).click(function(){
      var url = ph__url.toString();
      var videoFlag = {"url":url.replace("#", ""), "reason":$(`#ph_videoFlagReason`).val()}
      flags.videos.push(videoFlag);
      localStorage.setItem('flags', JSON.stringify(flags));

      document.querySelector(`.ph_flagVideo__window`).close();
      Swal.fire({
        icon: 'success',
        title: 'Flagged',
        text: `Video was flagged with the reason "${$(`#ph_videoFlagReason`).val()}"`,
      }).then((result) => {
        if (result.isConfirmed) {
          document.location.reload(true);
        }
      });
    });

    $(`#ph_videoOnly_btn`).click(function(){
      $(`.ph_video-actions`).append(`
            <button id="ph_relatedVids_btn">Related</button>
            <button id="ph_recommendedVids_btn">Recommended</button>
        `);

      $(`#ph_recommendedVids_btn`).click(function(){
        document.querySelector(`.ph_recommended__window`).showModal();
      });

      $(`#ph_relatedVids_btn`).click(function(){
        document.querySelector(`.ph_related__window`).showModal();
      });

      $(`#recommendedVideos`).appendTo(`.ph_recommended__container`);
      $(`#relatedVideosCenter`).appendTo(`.ph_related__container`);
      $(`#hd-rightColVideoPage`).hide();
      $(`#hd-leftColVideoPage > div.video-wrapper.js-relatedRecommended.js-relatedVideos.relatedVideos.allRelatedVideos`).hide();
      $(`#under-player-playlists`).hide();

      GM_addStyle(`
            html.supportsGridLayout.fluidContainer #main-container #vpContentContainer:not(.premiumLocked) {
                grid-template-columns: 1fr;
            }
        `);
    });


    $(`#ph_videoDownload_btn`).click(function(){
      var url = window.location;
      var newUrl = window.location.href.replace(/(https?:\/\/)/, "$19xbud.com/");
      window.location = newUrl;
        });

    $(`.allActionsContainer .CTAs #ctaBox`).append(`
      <li><span style="display:inline-block;width:100%;height:5px;"></span></li>
      <li><a href="#"><span>Favorite</span></a></li>
    `);

    automator.onElement("#player",function(){
      if (automator.hasText(this, "No valid sources are available for this video.")) {
        Swal.fire("PH Toolbox","Your player went stale. Refresh the page.","info");
      }
    })
  }
  else if(window.location.href.indexOf("/model/") > -1 
  || window.location.href.indexOf("/pornstar/") > -1 
  || window.location.href.indexOf("/channels/") > -1 
  || window.location.href.indexOf("/users/") > -1) {
    // PH profile
    console.info("[PH Toolbox] You are currently watching a model/channel/user page");

    // Get the cache from local storage
    var cache = JSON.parse(localStorage.getItem('favCache')) || {};

    // Function to cache the response for a request
    function cacheResponse(url, response) {
      cache[url] = response;
      localStorage.setItem('favCache', JSON.stringify(cache));
    }

    // Favorites check
    function isFavorite(url){
      if(localStorage.favorites){
        // get the favorites value from the localstorage
        var _fav = JSON.parse(localStorage.favorites);
        // check against current url
        if($.inArray(url, _fav.profiles) !== -1){
          return true;
        }
      }
      return false;
    }

    function isFlagged(){
      var cur_url = ph__url.href;
      cur_url = cur_url.replace(/#+$/, ''); // Remove # character from the end of the URL
      if(localStorage.flags){
        var flags = JSON.parse(localStorage.flags);
        // check against current url
        var model = $.grep(flags.models, function(m) {
          return m.url === cur_url;
        })[0];
        if (model) {
          return {
            flagged: true,
            reason: model.reason,
            url: model.url
          };
        }
      }
      return { flagged: false };
    }

    if(isFavorite(ph__url.href)){
      $(`#mainMenuProfile > div.userButtonsMenu > div.userButtons`).append(`
                <div class="float-left mainButton">
                    <button id="ph_unfav_btn" class="buttonBase" type="button">
                        <i class="fa-regular fa-star fa-fw" style="background:unset;"></i>
                        <span class="buttonLabel">Unfavorite</span>
                    </button>
                </div>
            `);
    } else {
      $(`#mainMenuProfile > div.userButtonsMenu > div.userButtons`).append(`
                <div class="float-left mainButton">
                    <button id="ph_fav_btn" class="buttonBase" type="button">
                        <i class="fa-solid fa-star fa-fw" style="background:unset;"></i>
                        <span class="buttonLabel">Favorite</span>
                    </button>
                </div>
            `);
    };
    $(`#mainMenuProfile > div.userButtonsMenu > div.userButtons`).append(`
      <div class="float-left mainButton">
          <button id="ph_flag_btn" class="buttonBase" type="button">
              <i class="fa-solid fa-flag fa-fw" style="background:unset;"></i>
              <span class="buttonLabel">Flag</span>
          </button>
      </div>
    `);

    $(`#ph_fav_btn`).click(function(e){
      //e.preventDefault();
      var info = {};
      if (localStorage.favorites){
        var favorites = JSON.parse(localStorage.favorites);
        if (favorites.profiles.length === 0){
          var _fav = {"profiles":[ph__url],"vids":[],"playlists":[]};
          localStorage.favorites = JSON.stringify(_fav);
        } else {
          favorites.profiles.push(ph__url);
          localStorage.favorites = JSON.stringify(favorites);

          info = {
            "name":$(".topProfileHeader > div.coverImage div.name > h1").text().trim(),
            "avatar":$(".topProfileHeader > #avatarPicture #getAvatar").attr("src")
          };

          // Add the favorite item to the cache
          cacheResponse(ph__url, {"name": info.name, "avatar": info.avatar});

          Swal.fire({
            icon: 'success',
            title: 'Added to favorites',
            text: 'Model was added to favorites',
          }).then((result) => {
            if (result.isConfirmed) {
              document.location.reload(true);
            }
          });
        }
      } else {
        console.log("[PH toolbox] no localstorage");
        var _fav = {"profiles":[ph__url],"vids":[],"playlists":[]};
        localStorage.favorites = JSON.stringify(_fav);
      }
    });
    $(`#ph_unfav_btn`).click(function(e){
      //e.preventDefault();
      if (localStorage.favorites){
        var favorites = JSON.parse(localStorage.favorites);
        if (favorites.profiles.length === 0){
          var _fav = {"profiles":[ph__url],"vids":[],"playlists":[]};
          localStorage.favorites = JSON.stringify(_fav);
        } else {
          favorites.profiles = favorites.profiles.filter(url => url !== ph__url.href);
          localStorage.favorites = JSON.stringify(favorites);

          // Remove the favorite item from the cache
          delete cache[ph__url];
          localStorage.setItem('favCache', JSON.stringify(cache));

          Swal.fire({
            icon: 'success',
            title: 'Removed from favorites',
            text: 'Model was removed from favorites',
          }).then((result) => {
            if (result.isConfirmed) {
              document.location.reload(true);
            }
          });
        }
      } else {
        console.log("[PH toolbox] no localstorage");
        var _fav = {"profiles":[],"vids":[],"playlists":[]};
        localStorage.favorites = JSON.stringify(_fav);
      }
    });

    if(GM_config.get("HideShortVids") === true){
      var hiddenEl = 0;
      $(`ul.videos > .videoBox`).each(function(){
        var videoDuration = $(`.linkVideoThumb > .marker-overlays > var.duration`,this).text();
        if(timeToSeconds(videoDuration) < GM_config.get("ShortVidMin")){
          $(this).hide();
          hiddenEl++;
        }
      });
      console.log(`[PH Toolbox] Hidden ${hiddenEl} videos shorter than ${secondsToTime(GM_config.get("ShortVidMin"))}`);
    }

    $(`<div id="ph_flag__msgbox"><span class="icon"><i class="fa-regular fa-flag"></i></span><span class="text"></span></div>`).insertAfter(`#svgProfileElements`);

    $(`body`).append(`
        <dialog class="ph_dialog ph_flagModel__window">
            <div class="heading">
                <div class="left"><h2><i class="fa-solid fa-flag"></i> Flag model</h2></div>
                <div class="right"><a id="ph_modal-close" class="button"><i class="fa-solid fa-times"></i></a></div>
            </div>
            <div class="body">
            <div style="display:flex; gap: 4px;">
              <select id="ph_modelFlagReason" style="flex:1;">
                <optgroup label="Malicious activity">
                  <option value="scam">Scam</option>
                  <option value="ad">Ads</option>
                  <option value="premium">Premium account</option>
                </optgroup>
                <optgroup label="Pleasure denial">
                  <option value="no_finisher">Showoff/poser</option>
                  <option value="teaser">Teaser</option>
                  <option value="turnoff">Turn-off</option>
                </optgroup>
                <optgroup label="Misclassification">
                  <option value="dead_profile">Dead profile</option>
                  <option value="fake_profile">Fake profile</option>
                  <option value="wrong_gender">Wrong gender</option>
                </optgroup>
              </select>
              <button id="ph_flagModel_btn">Flag model</button>
              </div>
              <div style="display:flex; gap: 4px;">
                <span style="flex:1;" id="ph_flagModel_status">This model wasn't flagged</span>
                <button id="ph_flagModel_clear_btn">Clear flags</button>
              </div>
            </div>
        </dialog>
    `);

    if(isFlagged().flagged){
      $(`#ph_flagModel_status`).html(`<i class="fa-solid fa-warning fa-fw"></i> This profile was flagged as "${isFlagged().reason}"`);
      $(`#ph_modelFlagReason`).prop("disabled",true);
      $(`#ph_flagModel_btn`).prop("disabled",true);
      match(isFlagged().reason, {
        "scam||ad||premium": function() { 
            $(`#ph_flag__msgbox>.text`).text(`
              ${getRandomMessage(dict__messages.profile.malicious)}
            `);
         },
        "no_finisher||teaser||turnoff": function() { 
            $(`#ph_flag__msgbox>.text`).text(`
              ${getRandomMessage(dict__messages.profile.tease)}
            `);
         },
         "dead_profile||fake_profile": function(){
            $(`#ph_flag__msgbox>.text`).text(`
              ${getRandomMessage(dict__messages.profile.inactive_profile)}
            `);
         },
         "wrong_gender": function(){
          $(`#ph_flag__msgbox>.text`).text(`
            ${getRandomMessage(dict__messages.profile.wrong_classification)}
          `);
         }
      });
    } else {
      $(`#ph_flag__msgbox`).remove();
      $(`#ph_flagModel_status`).text(`This profile wasn't flagged`);
    }

    $(`#ph_flag_btn`).click(function(){
      document.querySelector(`.ph_flagModel__window`).showModal();
    });

    $(`#ph_flagModel_btn`).click(function(){
      var url = ph__url.toString();
      var modelFlag = {"url":url.replace("#", ""), "reason":$(`#ph_modelFlagReason`).val()}
      flags.models.push(modelFlag);
      localStorage.setItem('flags', JSON.stringify(flags));

      document.querySelector(`.ph_flagModel__window`).close();
      Swal.fire({
        icon: 'success',
        title: 'Flagged',
        text: `Profile was flagged with the reason "${$(`#ph_modelFlagReason`).val()}"`,
      }).then((result) => {
        if (result.isConfirmed) {
          document.location.reload(true);
        }
      });
    });


  }
  else if(window.location.href.indexOf("/playlist/") > -1) {
    // PH playlist
    console.info("[PH Toolbox] You are currently watching a playlist");
  }
  else if(window.location.href.indexOf("/user/friend_requests") > -1){

    $(`
      <button id="ph_massFriendSpam" class="orangeButton clearfix" style="margin: 10px 0;" type="button">Mark all as spam</button>
    `).insertBefore(`.showingInfo.vgf`)
    var spamBtnClicks = 0;

    $(`#ph_massFriendSpam`).click(function(){
      $.wait = function(ms) {
        var defer = $.Deferred();
        setTimeout(function() { defer.resolve(); }, ms);
        return defer;
      };
      spamBtnClicks++;
      if ($("#profileFeaturedVideo > .sectionWrapper > #moreData").children().length) {
        var _el = $("#profileFeaturedVideo > .sectionWrapper > #moreData");
        console.warn("[PH Toolbox] Mass marking friend requests as spam...");
        $(`li.requestBox`,_el).each(function(){
          //$(this).css("background","red");
          $("a.spam",this).trigger("click");
          automator.click("#modalWrapMTubes > div > div > div.modal-body > div:nth-child(3) > button.spriteProfileIcons.orangeButton.bigButton.yesBtn");
        });
      } else {
        console.info("[PH Toolbox] Can't do anything, chief. No friend requests.");
        if(spamBtnClicks < 3){
        } else if(spamBtnClicks > 3 && spamBtnClicks < 5){
          Swal.fire('Hi, buddy','Calm down please, you have no friend requests right now.','info');
        } else if(spamBtnClicks > 10 && spamBtnClicks < 30){
          Swal.fire('Hey','I said calm down.','warning');
        } else if(spamBtnClicks > 30){
          Swal.fire({
            title: 'HEY!!!',
            html: 'STOP. CLICKING. THE. BUTTON.',
            icon: 'error',
            timer: 10000,
            timerProgressBar: true,
            allowEscapeKey: false,
            allowEnterKey: false,
            allowOutsideClick: false,
            showConfirmButton: false,
          });
          $(`#ph_massFriendSpam`).prop('disabled', true);
          $(`#ph_massFriendSpam`).addClass('disabled');
          $.wait(15000).then(function(){
            Swal.fire({
              title: 'Haha',
              html: 'I have disabled the button. Who\'s laughing now?',
              timer: 10000,
              timerProgressBar: true,
              allowEscapeKey: false,
              allowEnterKey: false,
              allowOutsideClick: false,
              showConfirmButton: false,
            });
          });
        }
      }
    });
    
  }
  else{
    // Other cases
  }

  // GLOBAL: All pages

  $(`dialog.ph_dialog #ph_modal-close`).click(function(){
      $(this).closest(`.ph_dialog`)[0].close();
      console.log($(this));
    });

  // Add profile shortcuts on pages with video thumbs
  $(`div.thumbnail-info-wrapper.clearfix > div.videoUploaderBlock.clearfix`).each(function(){
    var modelUrl_list = $(`.usernameWrap a`,this).attr('href');
    $(this).append(`
            <div class="ph_quickActions" style="display: inline-flex; align-items: center; gap: 5px; margin: 0 5px 0 0;">
                <a href="${modelUrl_list}/photos" data-popup><i class="fa-solid fa-images-user"></i> images</a>
                <a href="${modelUrl_list}/videos" data-popup><i class="fa-solid fa-film"></i> videos</a>
            </div>
        `);
  });

  $(`
    <div class="menuLink">
      <a href="/toolbox" class="halfWidth">
        <i class="fa-solid fa-toolbox fa-fw inline" style="color:var(--ph__brand--primary);"></i>
        <span class="inline" style="color:var(--ph__brand--primary);">PH Toolbox</span>
      </a>
      <a href="/toolbox/favorites" class="halfWidth">
        <i class="fa-solid fa-heart fa-fw inline" style="color:var(--ph__brand--primary);"></i>
        <span class="inline" style="color:var(--ph__brand--primary);">[PH Toolbox] Favorites</span>
      </a>
    </div>
  `).insertAfter(`#profileMenuDropdownScroll > div.menuSection > .menuLink:has(a[href*='/translate/sign_up'])`);


  // Userscript pages
  if(window.location.href.indexOf("/toolbox") > -1) {
    console.info("[PH Toolbox] Page: Toolbox");
    $(`head>title`).text(`PH toolbox`);
    $(`.wrapper > .container`).html(`
      <div class="sectionWrapper">
        <div class="section" style="padding: 20px;">
          <h1>PH Toolbox</h1>
          <h2>Links</h2>
          <p>
            <ul>
              <li><a href="/toolbox/favorites/">My favorites</a></li>
            </ul>
          </p>
        </div>
      </div>
    `);
    if(window.location.href.indexOf("/favorites") > -1) {

      var favorites = JSON.parse(localStorage.favorites);
      console.info("[PH Toolbox] Page: Toolbox » Favorites");
      $(`head>title`).text(`PH toolbox: Favorites`);
      $(`head`).append(`
        <link rel="stylesheet" href="https://di.phncdn.com/www-static/css/./pornstar-beforeaction-pc.css?cache=2022121401" type="text/css" />
        <link rel="stylesheet" href="https://di.phncdn.com/www-static/css/./pornstar-pornstars-pc.css?cache=2022121401" type="text/css" />
      `);
      $(`.wrapper > .container`).addClass(`ph__root`);
      $(`.wrapper > .container`).html(`
        <div class="sectionWrapper">
          <div class="section" style="padding: 20px;">
            <a id="ph_getFavorites" href="#" class="buttonBase orangeButton big">Get favorites</a>
          </div>
        </div>
        <div class="sectionWrapper">
          <div class="section" style="padding: 20px;">
            <a href="/toolbox"><h1>PH Toolbox</h1></a>
            <h2>Favorites</h2>
            <p><i class="fa-regular fa-warning"></i> If your images or text appear to be broken or missing - wait a few moments and refresh the page.</p>
          </div>
        </div>
        <div class="pornstar_container">
          <div class="sectionWrapper">
            <!-- Model list -->
            <ul id="ph_favModels" class="videos row-5-thumbs popular-pornstar">
            </ul>
            <!-- / -->
          </div>
        </div>
      `);

      $(`#ph_favModels`).append(`<div class="ph__loader">Loading the favorites...</div>`);
      //$(`.ph__loader`).show();

      // Set the rate limit in milliseconds
      var rateLimit = 5000;

      // Set the maximum number of concurrent promises
      var maxConcurrentPromises = 2;

      var promises = [];

      function jsonInfo(url){
        var Url = new URL(url);
        var relUrl = Url.pathname + Url.search;
        return new Promise(function(resolve, reject) {
          var info = {};
          $.ajax({
            type: "GET",
            url: relUrl,
            success: function (response) {
              info = {
                "name":$(".topProfileHeader > div.coverImage div.name > h1", response).text().trim(),
                "avatar":$(".topProfileHeader > #avatarPicture #getAvatar", response).attr("src")
              };
              resolve(info);
            },
            error: function(jqxhr, status, exception) {
                // Group the error message in the console
                console.groupCollapsed(`Error: ${exception.message}`);
                //console.error(exception);
                //alert('Exception:', exception);
                reject(exception);
                console.groupEnd();
            }
          });
        });
      }

      // Get the cache from local storage
      var cache = JSON.parse(localStorage.getItem('favCache')) || {};

      // Function to check if a request is cached
      function isCached(url) {
        return cache.hasOwnProperty(url);
      }

      // Function to get the cached response for a request
      function getCachedResponse(url) {
        return cache[url];
      }

      // Function to cache the response for a request
      function cacheResponse(url, response) {
        cache[url] = response;
        localStorage.setItem('favCache', JSON.stringify(cache));
      }

      // Create a function to process the promises
      function processPromises() {
        // Check if there are more promises in the array
        if (promises.length > 0) {
          // Get the next batch of promises
          const batch = promises.splice(0, maxConcurrentPromises);

          // Wait for the batch to complete
          Promise.all(batch)
            .then(function() {
              // Process the next batch of promises
              setTimeout(processPromises(),rateLimit);
            })
            .catch(function(error) {
              // Group the error message in the console
              console.groupCollapsed(`Error: ${error.message}`);
              console.error(error);
              console.groupEnd();

              // Process the next batch of promises
              setTimeout(processPromises(),rateLimit);
            });
        } else {
          // Hide the loader element
          $(`.ph__loader`).hide();
        }
      }
      
      function sendRequests() {
        // Initialize a flag to track if all items are cached
        var allCached = true;

        $.each(favorites.profiles, function(index, profileUrl) {
          // Set the retry count to 0
          var retryCount = 0;
          var info = jsonInfo(profileUrl);
        
          // Function to send the request and handle the response
          function sendRequest() {
            // Check if the request is already cached
            if (isCached(profileUrl)) {
              // Get the cached response
              var cachedResponse = getCachedResponse(profileUrl);

              // Check if the cached response is valid
              var isValid = true;
              for (var prop in cachedResponse) {
                /*if (!cachedResponse.hasOwnProperty(prop) && !cachedResponse[prop]) {
                  isValid = false;
                  break;
                }*/
                if (cachedResponse.hasOwnProperty(prop) && cachedResponse[prop]) {
                  // prop is not empty, so continue to the next iteration
                  continue;
                } else {
                  // prop is empty or does not exist, so set isValid to false
                  isValid = false;
                  break;
                }
              }

              if (isValid) {
                // Do something with the cached response
                var htmlItem = `
                <li class="modelLi">
                  <div class="wrap">
                      <a href="${profileUrl}">
                          <span class="pornstar_label">
                              <span class="title-album">
                                  <span>Model</span>
                              </span>
                          </span>
                          <img class="lazy"
                              src="${cachedResponse.avatar}"
                              alt="${cachedResponse.name}">
                      </a>
                      <div class="thumbnail-info-wrapper">
                          <a href="${profileUrl}" class="title">
                              <span class="modelName">${cachedResponse.name}</span>
                          </a>
                      </div>
                  </div>
                </li>
                `;
                // Append the HTML to the DOM and fade it in
                $(`#ph_favModels`).append(htmlItem).find('.modelLi').fadeIn();
                
                console.log(cachedResponse);
              } else {
                // Set the allCached flag to false
                allCached = false;

                // Add the request promise to the array
                promises.push(
                  jsonInfo(profileUrl)
                    .then(function(info) {
                      // Cache the response
                      cacheResponse(profileUrl, info);

                      // Do something with the response
                      //console.log(info);
                      var htmlItem = `
                      <li class="modelLi">
                        <div class="wrap">
                            <a href="${profileUrl}">
                                <span class="pornstar_label">
                                    <span class="title-album">
                                        <span>Model</span>
                                    </span>
                                </span>
                                <img class="lazy"
                                    src="${info.avatar}"
                                    alt="${info.name}">
                            </a>
                            <div class="thumbnail-info-wrapper">
                                <a href="${profileUrl}" class="title">
                                    <span class="modelName">${info.name}</span>
                                </a>
                            </div>
                        </div>
                      </li>
                      `;
                      // Append the HTML to the DOM and fade it in
                      $(`#ph_favModels`).append(htmlItem).find('.modelLi').fadeIn();
                      console.log(info);
                    })
                    .catch(function(error) {
                      // Increment the retry count
                      retryCount++;

                      // If the retry count is less than the maximum number of retries, retry the request
                      if (retryCount < 5) {
                        console.warn(`[PH Toolbox] Can't get the model info, retrying... (${retryCount}/5)`)
                        sendRequest();
                      } else {
                        // Group the error message in the console
                        console.groupCollapsed(`[PH toolbox] Error: ${error.message}`);
                        console.error(error);
                        console.groupEnd();
                      }
                    })
                );
              }
            } else {
              // Set the allCached flag to false
              allCached = false;

              // Add the request promise to the array
              promises.push(
                jsonInfo(profileUrl)
                  .then(function(info) {
                    // Cache the response
                    cacheResponse(profileUrl, info);

                    // Do something with the response
                    //console.log(info);
                    var htmlItem = `
                    <li class="modelLi">
                      <div class="wrap">
                          <a href="${profileUrl}">
                              <span class="pornstar_label">
                                  <span class="title-album">
                                      <span>Model</span>
                                  </span>
                              </span>
                              <img class="lazy"
                                  src="${info.avatar}"
                                  alt="${info.name}">
                          </a>
                          <div class="thumbnail-info-wrapper">
                              <a href="${profileUrl}" class="title">
                                  <span class="modelName">${info.name}</span>
                              </a>
                          </div>
                      </div>
                    </li>
                    `;
                    // Append the HTML to the DOM and fade it in
                    $(`#ph_favModels`).append(htmlItem).find('.modelLi').fadeIn();
                    console.log(info);
                  })
                  .catch(function(error) {
                    // Increment the retry count
                    retryCount++;

                    // If the retry count is less than the maximum number of retries, retry the request
                    if (retryCount < 5) {
                      console.warn(`[PH Toolbox] Can't get the model info, retrying... (${retryCount}/5)`)
                      sendRequest();
                    } else {
                      // Group the error message in the console
                      console.groupCollapsed(`[PH toolbox] Error: ${error.message}`);
                      console.error(error);
                      console.groupEnd();
                    }
                  })
              );
            }
          }

          // Send the request in the background
          setTimeout(sendRequest, 0);
        });

        // Check if all items are cached
        if (allCached) {
          // Hide the loader element
          $(`.ph__loader`).hide();
        } else {
          // Process the promises
          processPromises();
        }
      };

      $(`#ph_getFavorites`).click(function(){
        Swal.fire({
          toast: true,
          position: 'top-end',
          showConfirmButton: false,
          showCloseButton: false,
          title: "Getting favorites",
          timer: 5000,
          timerProgressBar: true
        });
        if($(`#ph_favModels`).length){
          $(`#ph_favModels`).html("");
        }
        
        setTimeout(sendRequests,rateLimit);

        // Wait for all the promises to complete
        setTimeout(processPromises(),rateLimit);
      });
    }
  }

  //---

  $(`body > div.footerContentWrapper > h2`).html(`Running scripts`);
  $(`body > div.footerContentWrapper > p`).html(`
        <p><i class="fa-solid fa-puzzle-piece-simple"></i> <span>PH Toolbox</span>,
        <i class="fa-solid fa-puzzle-piece-simple"></i> <span>pornhub crack for Russia</span>
    `);
});