Hentai Heroes image viewer

Allows you to display any stage image of any harem girl, owned ones or not. Works also in event display and Places of Power. Includes zoom-in feature to display full-size girl images gallery (lightbox).

// ==UserScript==
// @name         Hentai Heroes image viewer
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Allows you to display any stage image of any harem girl, owned ones or not. Works also in event display and Places of Power. Includes zoom-in feature to display full-size girl images gallery (lightbox).
// @author       randomfapper34
// @match        http*://nutaku.haremheroes.com/*
// @match        http*://*.hentaiheroes.com/*
// @match        http*://*.gayharem.com/*
// @match        http*://*.comixharem.com/*
// @require      https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js
// @grant        none
// @license      MIT
// ==/UserScript==

// gayharem image link head:    gh1
// hentaiharem image link head: hh2
// comixharem image link head:  ch

var $ = window.jQuery;
var haremHead = (function() {
    var haremType = ($('body#hh_gay').length > 0) ?
                    'gh1' :
                    ($('body#hh_comix').length > 0) ? 'ch' : 'hh2';
    return 'https://' + haremType;
})();
var wikiLink = (function() {
    var haremType = ($('body#hh_gay').length > 0) ?
                    'harem-battle.club/wiki/Gay-Harem/GH:' :
                    ($('body#hh_comix').length > 0) ? '' : 'harem-battle.club/wiki/Harem-Heroes/HH:';
    return haremType;
})();
var CurrentPage = window.location.pathname;
var sheet = (function() {
	var style = document.createElement('style');
	document.head.appendChild(style);
	return style.sheet;
})();
var imageExt = '-1200x.webp'; //old ext: '.png';
var icoExt = '-300x.webp';

$(document).ready(function() {
    //include lightbox css
    $(document.head).append(
        '<link href="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.css" rel="stylesheet" type="text/css">'
    );
    //define own css
    defineCss();
});

// current page: Activities (PoP)
if (CurrentPage.indexOf('activities') != -1)
{
    if ($('.pop_list').css('display') != 'none') return;

    setTimeout(async function () {
        var popElement = $('#activities #pop.canvas');
        var popImage = popElement.find('.pop_left_part img');
        var popRewardInfo = popElement.find('.pop_rewards_display.reward_wrap').attr('data-reward-display');
        var popImageIcon = popElement.find('.pop_rewards_display .shards_girl_ico img');
        //if girl is won, there is no shards data in popRewardInfo, and therefore no id. Use regex to get girl id from image link?
        var jsonReward = JSON.parse(popRewardInfo);
        if (!jsonReward.hasOwnProperty('shards')) return;
        var girlInfo = jsonReward.shards[0];
        var girlId = girlInfo.id_girl;
        var girlGrades = girlInfo.graded2.split('<g').length - 1;

        //check for image existance with high grades (always work no matter the webpage display chages)
        if (girlGrades == 0) {
            girlGrades = 5;
            var checkImageLink = haremHead + '.hh-content.com/pictures/girls/' + girlId + '/ava5' + imageExt;
            if (await checkUrlResponse(checkImageLink) === false) girlGrades = 3;
        }

        //create diamonds on the top part
        popElement.find('.diamond-bar').remove();
        var allDiamonds = '';
        for (var i = 0; i <= girlGrades; i++) {
            var diamondToAdd = '<div class="diamond unlocked" grade="' + i + '"></div>';
            allDiamonds += diamondToAdd;
        }
        popImage.before('<div class="diamond-bar-container"><div class="diamond-bar">' + allDiamonds + '</div></div>');

        //connect diamonds to image links
        var allLinks = popElement.find('.diamond');
        var linksArray = [];
        for (i = 0; i <= girlGrades; i++) {
            var imgLink = haremHead + '.hh-content.com/pictures/girls/' + girlId + '/ava' + i + imageExt;
            var icoLink = haremHead + '.hh-content.com/pictures/girls/' + girlId + '/ico' + i + icoExt;
            linksArray.push(imgLink);
            $(allLinks.get(i)).attr("link", imgLink);
            $(allLinks.get(i)).attr("icoLink", icoLink);
        }

        $( ".pop_left_part .diamond-bar .diamond" ).on('mouseenter', function() {
            var girlAvatarLink = $(this).attr('link');
            var girlIconLink = $(this).attr('icoLink');
            popImage.attr('src', girlAvatarLink);
            popImageIcon.attr('src', girlIconLink);
        });

        //create zooming event
        $(popImage).removeData('allImages');
        $(popImage).data('allImages', linksArray);
        $(popImage).on('mouseup', zoomIntoImage);
    }, 50);
}

// current page: Event box
if (CurrentPage.indexOf('event') != -1)
{
    var eventGirlElementSelector = ".nc-event-list-rewards-container .nc-event-list-reward-container"
    var rewardBox = ".nc-event-reward-container.selected ";
    var eventGirlImageSelector = ".canvas " + rewardBox + " .nc-event-reward-preview";
    var eventGirlInfoSelector = ".canvas " + rewardBox + " .nc-event-reward-info";

    setTimeout(function () {
        $(eventGirlElementSelector + ".selected").click();
    }, 50);

    $(eventGirlElementSelector).on('click', function() {
        setTimeout(async function () {
            var girlImageDiv = $(eventGirlImageSelector);
            var girlInfoDiv = $(eventGirlInfoSelector);
            var girlInfo = girlInfoDiv.find('.new_girl_info .girl_tooltip_grade');
            var girlGrades = girlInfo.find('g').length;
            var girlIconImage = $(".nc-event-list-rewards-container > .nc-event-list-reward-container.selected img");
            var girlImage = girlImageDiv.children('img');
            girlImageDiv.find('.diamond-bar').remove();

            //find girl id from image src
            var girlImageSrc = girlImage.attr('src');
            var startPosition = girlImageSrc.indexOf('pictures/girls/') + 'pictures/girls/'.length;
            var girlIdStr = girlImageSrc.substring(startPosition, girlImageSrc.lastIndexOf('/ava'));
            if (isNaN(girlIdStr))
                return;
            var girlId = parseInt(girlIdStr);

            //check for image existance with high grades (always work no matter the webpage display chages)
            if (girlGrades == 0) {
                girlGrades = 5;
                var checkImageLink = haremHead + '.hh-content.com/pictures/girls/' + girlId + '/ava' + girlGrades + imageExt;
                if (await checkUrlResponse(checkImageLink) === false) girlGrades = 3;
            }

            //create diamonds on the top part
            var allDiamonds = '';
            for (var i = 0; i <= girlGrades; i++) {
                var diamondToAdd = '<div class="diamond unlocked" grade="' + i + '"></div>';
                allDiamonds += diamondToAdd;
            }
            girlImage.before('<div class="diamond-bar">' + allDiamonds + '</div>');

            //connect diamonds to image links
            var allLinks = girlImageDiv.find('.diamond');
            var linksArray = [];
            for (i = 0; i <= girlGrades; i++) {
                var imgLink = haremHead + '.hh-content.com/pictures/girls/' + girlId + '/ava' + i + imageExt;
                var icoLink = haremHead + '.hh-content.com/pictures/girls/' + girlId + '/ico' + i + icoExt;
                linksArray.push(imgLink);
                $(allLinks.get(i)).attr("link", imgLink);
                $(allLinks.get(i)).attr("icoLink", icoLink);
            }

            $( rewardBox + " .diamond-bar .diamond" ).on('mouseenter', function() {
                var girlAvatarLink = $(this).attr('link');
                var girlIconLink = $(this).attr('icoLink');
                girlImage.attr('src', girlAvatarLink);
                girlIconImage.attr('src', girlIconLink);
            });

            //create zooming event
            $(girlImage).removeData('allImages');
            $(girlImage).data('allImages', linksArray);
            $(girlImage).off('mouseup', zoomIntoImage);
            $(girlImage).on('mouseup', zoomIntoImage);
        }, 10);
    });
}

// current page: Harem
if (CurrentPage.indexOf('harem') != -1)
{
    var callback = function(mutationsList) {
        for (let mutation of mutationsList) {
            if (mutation.type === 'childList') {
                mutation.addedNodes.forEach(node => {
                    if (node.outerHTML) {
                        node.addEventListener('click', onGirlClick, false);
                    }
                });
            }
        }
    };

    const targetNode = document.querySelector('#harem_left div.girls_list');
    const config = { childList: true };
    const observer = new MutationObserver(callback);
    observer.observe(targetNode, config);
    $( ".girls_list div[id_girl]" ).on('click', onGirlClick);

    function onGirlClick(event) {
        var girlId = $(this).children('[girl]').attr('girl');
        var girlGrades = $(this).find('.graded').children().length;
        var girlName = $(this).find('div.right h4')[0].innerText;
        updateInfo(girlId, girlGrades, girlName);
    }

    setTimeout(function () {
        //update view of girl currently selected when loading the harem
        $("#harem_left div.girls_list div[girl].opened").click();
    }, 200);

    function updateInfo(girlId, girlGrades, girlName)
    {
        setTimeout(function () {
            var haremRight = $('#harem_right');
            haremRight.children('[girl]').each( function() {
                if (girlId == 0) girlId = $(this).attr('girl');

                var notOwned = $(this).children('.missing_girl');
                var girlImageDiv = $(this).find('.avatar-box');
                var girlIconDiv = $("#harem_left div.girls_list div[girl].opened div.left img");

                if (notOwned.length > 0) {
                    //create diamonds on the bottom part
                    var allDiamonds = '';
                    for (var i = 0; i <= girlGrades; i++) {
                        var diamondToAdd = '<div class="diamond locked" grade="' + i + '"></div>';
                        allDiamonds += diamondToAdd;
                    }

                    $(this).find('.middle_part').css('margin', '0');
                    $(this).find('.dialog-box').after('<h3>' + girlName + '</h3>');
                    $(this).find('img.avatar').wrap('<div class="avatar-box"></div>');
                    $(this).find('.avatar-box').css('margin-top', '23px');
                    $(this).find('.avatar-box').after('<div class="diamond-bar">' + allDiamonds + '</div>');
                }

                //update for any girl (owned or not)
                var wikiBase = wikiLink;
                if (wikiBase != '') {
                    $(this).find('h3').wrap('<div class="WikiLink"></div>').wrap('<a href="https://' + wikiBase + girlName + '" target="_blank"></a>');
                }
                var allLinks = $(this).find('.diamond');
                var linksArray = [];
                for (i = 0; i <= girlGrades; i++) {
                    var imgLink = haremHead + '.hh-content.com/pictures/girls/' + girlId + '/ava' + i + imageExt;
                    var icoLink = haremHead + '.hh-content.com/pictures/girls/' + girlId + '/ico' + i + icoExt;
                    linksArray.push(imgLink);
                    $(allLinks.get(i)).attr("link", imgLink);
                    $(allLinks.get(i)).attr("icoLink", icoLink);
                }
                $('.avatar-box img.avatar').removeData('allImages');
                $('.avatar-box img.avatar').data('allImages', linksArray);
                if (notOwned) $('.avatar-box img.avatar').attr('src', linksArray[0]);

                $('.variation_block .big_border').on('click', function() {
                    var girlId = $(this).children('[girl]').attr('girl');
                    var girlGrades = $(this).find('.graded').children().length;
                    setTimeout(function() {
                        updateInfo(girlId, girlGrades, girlName);
                    }, 50);
                });

                $( ".diamond-bar .diamond" ).on('mouseenter', function() {
                    var mainParent = $(this).closest('.middle_part');
                    var girlAvatar = mainParent.find('img.avatar');
                    var girlAvatarLink = $(this).attr('link');
                    var girlIconLink = $(this).attr('icoLink');
                    girlIconDiv.attr('src', girlIconLink);
                    girlAvatar.attr('src', girlAvatarLink);
                });

                $('.avatar-box img.avatar').on('mouseup', zoomIntoImage);
            });
        }, 0);
    }
}

//zoom into image with lightbox, event only on left click
function zoomIntoImage(e)
{
    if (e.which != 1) return;

    var linksArray = $(this).data('allImages');
    var girlAvatarLink = $(this).attr('src');
    var indexOfQuestion = girlAvatarLink.lastIndexOf('?');
    if (indexOfQuestion >= 0) girlAvatarLink = girlAvatarLink.slice(0, indexOfQuestion);
    var indexOfCurrent = linksArray.indexOf(girlAvatarLink);

    var allImages = [];
    for (var i = 0; i < linksArray.length; i++) {
        allImages.push({
            src  : linksArray[i].toString(),
            type : 'image',
            opts : {
                caption : i == 0 ? 'Default' : 'Stage ' + i
            }
        });
    }

    $.fancybox.open(allImages, {
        loop : true,
        keyboard: true,
        transitionEffect: "tube"
    }, indexOfCurrent);
}

//checks for any errors in url (like image 404)
async function checkUrlResponse(url)
{
    let result = false;

    await fetch(url.toString())
    .then(function(response) {
        if (response.status >= 200 && response.status <= 299) {
            return response;
        } else {
            throw Error(response.statusText);
        }
    }).then(function(response) {
        result = true;
    }).catch(function(error) {
    });

    return result;
}

function defineCss()
{
    sheet.insertRule('#harem_left div[girl]>.left>img, #harem_right>div[girl] .middle_part div.avatar-box img.avatar, #shops #girls_list .g1 .girl-ico>img {'
                     + 'image-rendering: initial; }');

    sheet.insertRule('#harem_right .WikiLink a {'
                     + 'text-decoration: none; }');

    sheet.insertRule('#harem_right .diamond-bar {'
                     + 'margin-top: 4px; }');

    sheet.insertRule('.rewards-stats .diamond-bar {'
                     + 'position: static;'
                     + 'justify-content: center;'
                     + 'margin-top: 42px;'
                     + 'margin-bottom: -40px; }');

    sheet.insertRule('.generic-girl-image .diamond-bar, .nc-event-reward-preview .diamond-bar {'
                     + 'justify-content: center;'
                     + 'z-index: 1;'
                     + 'width: 100%; }');

    sheet.insertRule('.rewards-stats .avatars-drawn-bottom-part .diamond-bar {'
                     + 'margin-top: 275px; }');

    sheet.insertRule('.rewards-stats .avatars-drawn-bottom-part img {'
                     + 'margin-top: -275px; }');

    sheet.insertRule('.nc-event-reward-preview .diamond-bar {'
                     + 'margin-top: -25px; }');

    sheet.insertRule('.rewards-stats .diamond-bar .diamond.unlocked, .pop_left_part .diamond-bar .diamond.unlocked, .generic-girl-image .diamond-bar .diamond.unlocked {'
                     + 'cursor: default; }');

    sheet.insertRule('.pop_left_part .diamond-bar-container {'
                     + 'z-index: 5;'
                     + 'position: absolute; }');

    sheet.insertRule('.pop_left_part .diamond-bar {'
                     + 'position: relative;'
                     + 'left: 50%; }');

    sheet.insertRule('#harem_right .diamond-bar .diamond:hover, .rewards-stats .diamond-bar .diamond:hover, .pop_left_part .diamond-bar .diamond:hover, .generic-girl-image .diamond-bar .diamond:hover, .nc-event-reward-preview .diamond-bar .diamond:hover {'
                     + 'border: 2px solid #FE00FE; }');

    sheet.insertRule('.avatar-box img, .event-widget.special-fullscreen-view .widget .rewards-stats .reward img, .generic-girl-image img, .nc-event-reward-preview img {'
                     + 'cursor: zoom-in; }');

    sheet.insertRule('#pop.canvas .pop_left_part img.pop_left_fade_page {'
                     + 'margin-bottom: 10px;'
                     + 'cursor: zoom-in; }');
}