novelupdates Cover Preview

Previews covers in novelupdates.com when hovering over hyperlinks that lead to novel pages.

当前为 2020-11-05 提交的版本,查看 最新版本

// ==UserScript==
// https://gf.qytechs.cn/scripts/26439-novelupdates-cover-preview/
// @name        novelupdates Cover Preview
// @namespace   somethingthatshouldnotclashwithotherscripts
// @include     https://www.novelupdates.com/*
// @include     http://www.novelupdates.com/*
// @include     https://forum.novelupdates.com/*
// @include     http://forum.novelupdates.com/*
// @version     1.3.5
// @description Previews covers in novelupdates.com when hovering over hyperlinks that lead to novel pages.
// @inject-into content
// @grant       GM_xmlhttpRequest
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_deleteValue
// @run-at   	document-end
// @license     http://creativecommons.org/licenses/by-nc-sa/4.0/

// ==/UserScript==
const MAXCACHEAGE = 7 * 24 * 60 * 60 * 1000; // Max Age before Cached data gets overridden with current data. Max Age is 7 days in milliseconds  //days * h * min  * sec * ms
var STYLESHEETHIJACKFORBACKGROUND = ".l-canvas"; //if unknown set empty ""; classname with leading dot
var STYLESHEETHIJACKFORTITLE = '.widgettitle_nuf'; //if unknown set empty ""; classname with leading dot
const DEFAULTTITLEBACKGROUNDCOLOR = '#aac'; //if no hijack class style available use plain color
const DEFAULTBACKGROUNDCOLOR = '#ccc'; //if no hijack class style available use plain color

const SELECTOR1 = 'td a'; //index/group/readinglist pages , forum
const SELECTOR2 = '.wpb_wrapper > a, .messageContent a'; //individual serie pages recommendation titles
const PREDIFINEDNATIVTITLE = "^Recommended by"; //in case native title is used to display something different
const INDIVIDUALPAGETEST = "novelupdates.com/series/";
const IMAGELINKCONTAINERS = '.serieseditimg, .seriesimg'; //instead of single element class name with dot
const IMAGELINKCONTAINERSnonJquery = 'serieseditimg seriesimg'; //instead of single element class name with dot
const IMAGEBLOCKER = "www.novelupdates.com/img/noimagefound.jpg"; //tested with string.match(). no need for prefixed http https in url. Can even be just the file name
const CONTAINERNUMBER = 0;
const preloadUrlRequests = true;
const preloadImages = false;
//^^^^	frontend settings over this line	^^^^

const RE = /\s*,\s*/; //Regex for split and remove empty spaces
var defaultHeight = "400px";
var IMAGEBLOCKERARRAY = IMAGEBLOCKER.split(RE);
var onHover = false;
var currentTitelHover;
//var windowCached = $(window);
//var concatenatedSelectors = $(concatSelector());
var style = 1;
var popover;
var concatenatedSelectorAll = document.querySelectorAll(concatSelector());

//get value from key. Decide if timestamp is older than MAXCACHEAGE than look for new image
function GM_getCachedValue(key) {
    const DEBUG = false;
    var currentTime = Date.now();
    var rawCover = GM_getValue(key, null);
    DEBUG && console.group("GM_getCachedValue")
    DEBUG && console.log("rawCover: " + rawCover)
    let result = null;
    if (!rawCover) {
        result = null;
    }
    else {
        var coverData;
        try { //is json parseable data? if not delete for refreshing
            coverData = JSON.parse(rawCover);
            DEBUG && console.log("coverData: " + coverData)
            if (!(coverData.url && coverData.cachedTime)) //has same variable definitions?
            {
                GM_deleteValue(key);
                result = null;
            }
        } catch (e) {
            GM_deleteValue(key);
            result = null;
        }


        var measuredTimedifference = currentTime - coverData.cachedTime;
        if (measuredTimedifference < MAXCACHEAGE)
            result = coverData.url;
        else {
            GM_deleteValue(key);
            result = null;
        }
    }
    DEBUG && console.groupEnd("GM_getCachedValue")
    return result;
}

//set value and currenttime for key
function GM_setCachedValue(key, value) {
    var coverData = {
        url: value,
        cachedTime: Date.now()
    };
    GM_setValue(key, JSON.stringify(coverData));
}

function inBlocklist(link) {
    if (IMAGEBLOCKERARRAY)
        if (IMAGEBLOCKERARRAY.length > 0)
            for (let i = 0; i < IMAGEBLOCKERARRAY.length; i++)
                if (IMAGEBLOCKERARRAY[i] !== "")
                    if (link.match(IMAGEBLOCKERARRAY[i]))
                        return true;
    return false;
}

function concatSelector() {
    var result;
    if (SELECTOR1)
        result = SELECTOR1;
    if (SELECTOR2) {
        if (SELECTOR1) //in case selector1 is missing
            result += ', ';
        result += SELECTOR2;
    }

    return result;
}
//https://medium.com/@alexcambose/js-offsettop-property-is-not-great-and-here-is-why-b79842ef7582
const getOffset = (element, horizontal = false) => {
    if (!element) return 0;
    return getOffset(element.offsetParent, horizontal) + (horizontal ? element.offsetLeft : element.offsetTop);
}

// popupPositioning function
popupPos = function (event, element, style, nativElement) {
    const DEBUG = false;
    DEBUG && console.group("popupPos style:" + style)

    var offsetToBottomBorderY = 5; //offset to bottom border
    var offsetToRightBorderX = 5; //offset to right border
    var X, Y;
    var hoveredSelectedPosX, hoveredSelectedPosY;
    var distanceToBottom, distanceToRight;
    //console.log(element.parents()[0])
    DEBUG && console.log(nativElement)
    //console.log(nativElement.parentElement)
    let parentElement = nativElement.parentElement;
    //let computedFontSizeJquery = parseInt(window.getComputedStyle(element.parents()[0]).fontSize);
    let computedFontSize = parseInt(window.getComputedStyle(parentElement).fontSize);
    //console.log(computedFontSize);

    //Initialising variables (multiple usages)
    let scrollPosY = window.scrollY || window.scrollTop || document.getElementsByTagName("html")[0].scrollTop;
    let scrollPosX = window.scrollX || window.scrollLeft || document.getElementsByTagName("html")[0].scrollLeft;
    // console.log(scrollPosX)
    var scrollTop = scrollPosY; // windowCached.scrollTop();
    var scrollLeft = scrollPosX; //windowCached.scrollLeft();
    //var elementPopup = this[0]; //this = ontop of jquery object
    let elementPopup = popover;
    let elementImg = elementPopup.getElementsByTagName("img");

    DEBUG && console.log(popover)
    DEBUG && console.log(elementPopup)
    DEBUG && console.log("elementPopup.offsetHeight: " + elementPopup.offsetHeight)
    DEBUG && console.log("popover[0].offsetHeight: " + popover.offsetHeight)
    DEBUG && console.log(elementImg)
    if (elementImg) {
        DEBUG && console.log(elementImg)
    }
    //var elementParentOffset = element.parents().offset();
    let elementParentOffset = {
        top: getOffset(parentElement),
        left: getOffset(parentElement, true)
    }
    DEBUG && console.log(elementParentOffset)

    //var elementParentOuterHeight = element.parents().outerHeight();
    //var elementParentOuterWidth = element.parents().outerWidth();
    let elementParentOuterHeight = parentElement.offsetHeight;
    let elementParentOuterWidth = parentElement.offsetWidth;

    //var elementOffset = element.offset();
    let elementOffset = {
        top: getOffset(nativElement),
        left: getOffset(nativElement, true)
    }

    DEBUG && console.log(elementOffset)
    //var elementOuterHeight = element.outerHeight();
    let elementOuterHeight = nativElement.offsetHeight;
    //var elementOuterWidth = element.outerWidth();
    //var elementRect = element[0].getBoundingClientRect();
    //var elementParentRect = element.parents()[0].getBoundingClientRect();
    if (style == 1) //index: position next to parent table cell (SELECTOR1)
    {
        hoveredSelectedPosX = elementParentOffset.left + elementParentOuterWidth; //link position + tablecell width; + elementOuterWidth;
        hoveredSelectedPosY = elementParentOffset.top + computedFontSize; //link position + tablecell height; + elementOuterHeight;
        //console.log("height " + elementParentOuterHeight  + ' - ' + elementOuterHeight);
    } else if (style == 2) //recommendations: position next to link height and parent (SELECTOR2) width
    {
        hoveredSelectedPosX = elementOffset.left + elementParentOuterWidth; //elementOffset.left + elementOuterWidth;
        hoveredSelectedPosY = elementOffset.top + elementOuterHeight;
    } else { //position to mouse hover position
        hoveredSelectedPosX = event.pageX; // + offsetToRightBorderX;
        hoveredSelectedPosY = event.pageY; // + offsetToBottomBorderY;
    }

    X = hoveredSelectedPosX;
    Y = hoveredSelectedPosY;
    DEBUG && console.log("hoveredSelectedPos x: " + X + ", y: " + Y)

    //windowCached.height() = window.innerHeight 
    //document.body.clientWidth
    //window.innerWidth == document.body.clientWidth + scrollbarWidth
    // Distance to the right
    distanceToRight = window.innerWidth - (X - scrollLeft);
    DEBUG && console.log("distanceToRight: " + distanceToRight + ", elementPopup.outerWidth: " + elementPopup.offsetWidth)
    // Tooltip too close to the right?
    if (distanceToRight < elementPopup.offsetWidth - offsetToRightBorderX)
        X += distanceToRight - elementPopup.offsetWidth - offsetToRightBorderX; //elementPopup

    //console.log(document.body.clientHeight)
    //console.log("windowCached.height(): " + windowCached.height())
    DEBUG && console.log("window.innerHeight: " + window.innerHeight)
    // Distance to the bottom
    distanceToBottom = window.innerHeight - (Y - scrollTop);
    DEBUG && console.log("distanceToBottom: " + distanceToBottom)
    DEBUG && console.log("elementPopup.offsetHeight: " + elementPopup.offsetHeight)
    DEBUG && console.log("popover[0].offsetHeight: " + popover.offsetHeight)
    DEBUG && console.log("parentElement.offsetHeight: " + parentElement.offsetHeight)
    let imgElement = elementPopup.getElementsByTagName("img")[0]
    DEBUG && console.log(imgElement)
    //console.log(elementPopup)
    // Tooltip too close to the bottom?
    if (distanceToBottom < elementPopup.offsetHeight - offsetToBottomBorderY) //(offsetToBottomBorderY + elementPopup.outerHeight())
        Y += distanceToBottom - elementPopup.offsetHeight - offsetToBottomBorderY;
    //offsetToRightBorderX
    //offsetToBottomBorderY

    //console.log("Distance to the bottom " + distanceToBottom+" elementPopupHeight " +elementPopup.outerHeight()+ "\nDistance to the right " + distanceToRight+ " elementPopupouterWidth " +elementPopup.outerWidth());
    //Tooltip over top border?
    //if(Y + offsetToBottomBorderY < scrollTop) Y = scrollTop + offsetToBottomBorderY;
    //if(X + offsetToRightBorderX < scrollLeft) X = scrollLeft + offsetToRightBorderX;

    /*
    console.log(elementPopup.style)
    this.css('top', Y + 'px');
    this.css('left', X + 'px');
    console.log(elementPopup.style)
    */
    elementPopup.style.top = Y + 'px';
    elementPopup.style.left = X + 'px';
    //popover.show();
    showPopOver();
    DEBUG && console.groupEnd("popupPos")
    //console.log("final popup position "+X+' # '+Y);
    return this;
};

//.wpb_wrapper a = title links on individual seriepage

//http://youmightnotneedjquery.com/
//https://latteandcode.medium.com/vanilla-js-addeventlistener-queryselector-and-closest-e7e5503b6418
function changeStyleType(styleType) {
    //if (!e.target.matches(selectorType)) return;
    style = styleType;
}
function mouseenterAll(selectorType, styleType = 1) {
    let selectorAll = document.querySelectorAll(selectorType);
    //console.log("mouseenterAll for selectors: " + selectorType)
    selectorAll.forEach(function (selector) {
        selector.addEventListener("mouseenter", changeStyleType(styleType))
    })
}
mouseenterAll(SELECTOR1, 1);
mouseenterAll(SELECTOR2, 2);


function loadImgUrl(elementUrl) {
    const DEBUG = false;
    DEBUG && console.group("loadImgUrl: " + elementUrl)


    let PromiseResult = new Promise(async function (resolve, reject) {
        let retrievedImgLink = GM_getCachedValue(elementUrl);
        DEBUG && console.log("elementUrl: " + elementUrl);
        DEBUG && console.log("retrievedImgLink: " + retrievedImgLink)
        if (retrievedImgLink !== null) {
            DEBUG && console.log("loadImgUrl has retrievedImgLink: " + retrievedImgLink)
            resolve(retrievedImgLink);
        } else {
            DEBUG && console.log("make ajax request try to save image of page into cache: " + elementUrl);
            /*
            $.ajax({
                url: elementUrl,
                type: "GET",
                dataType: 'text'
            }).done(function (data) {
                let html = data.replace(/src=/g, 'data-src='); //block resources loading when DOM gets opened
                DEBUG && console.log("html: " + html);
                try {
                    var imagelinks = $(html).find(IMAGELINKCONTAINERS).find('img');

                    var imagelink = imagelinks[CONTAINERNUMBER].getAttribute("data-src");
                    DEBUG && console.log('init ' + imagelink);
                    GM_setCachedValue(elementUrl, imagelink); //cache imageurl link
                    resolve(imagelink);
                } catch (error) {
                    DEBUG && console.log(error);
                    showPopupLoadingSpinner(serieTitle, 1);
                    reject(elementUrl);
                }

            })
                .fail(function (xhr) {
                    DEBUG && console.log('error', xhr);

                });*/


            GM_xmlhttpRequest({
                method: "GET",
                responseType: 'document',
                url: elementUrl,
                onload: function (xhr) {
                    DEBUG && console.log(response.responseText);
                    try {

                        let temp = xhr.response.querySelectorAll(IMAGELINKCONTAINERS);
                        let imageLinkByTag = temp[0].getElementsByTagName("img");
                        var imagelink = imageLinkByTag[CONTAINERNUMBER].getAttribute("src");

                        DEBUG && console.log('init ' + imagelink);
                        GM_setCachedValue(elementUrl, imagelink); //cache imageurl link
                        resolve(imagelink);
                    } catch (error) {
                        DEBUG && console.log(error);
                        showPopupLoadingSpinner(serieTitle, 1);
                        reject(elementUrl);
                    }
                }
            });
        }
    });
    DEBUG && console.groupEnd("loadImgUrl: " + elementUrl)
    return PromiseResult;
}

main();

function main() {
    const DEBUG = false;
    function uniq(a) {
        return Array.from(new Set(a));
    }

    if (preloadUrlRequests) {
        var getNovelLinks = function () {
            const links = Array.from(
                document.querySelectorAll('a[href*="' + INDIVIDUALPAGETEST + '"]')
            );

            return links;
        };
        const links = getNovelLinks();
        const novelUrlList = links.map(function (el) {
            // console.log('novelUrlList ' + el.href);
            return el.href;
        });

        const uniqueNovelUrlList = uniq(novelUrlList);
        /*
        console.group("uniqueNovelUrlList")
        console.log(uniqueNovelUrlList);
        console.groupEnd()
        */
        const imageUrlList = uniqueNovelUrlList.map(function (elementUrl) {
            loadImgUrl(elementUrl)
                .then(function (imgUrl) {
                    if (preloadImages) {
                        let img = document.createElement("img"); //put img into dom. Let the image preload in background
                        img.src = imgUrl;
                        DEBUG && console.log("onpageload cache init " + imgUrl);
                    }
                }, function (Error) {
                    DEBUG && console.log(Error + ' failed to fetch ' + elementUrl);
                });
            // console.log("imageUrlList " + elementUrl);
        });
    }
    //circle spinner from http://codepen.io/Beaugust/pen/DByiE
    //add additional stylesheet for "@keyframe spin" into head after document finishes loading
    //@keyframes spin is used for the loading spinner

    var style = document.createElement('style');
    //style.type = 'text/css';
    style.innerHTML = 'body {}';
    document.getElementsByTagName('head')[0].appendChild(style);
    this.stylesheet = document.styleSheets[document.styleSheets.length - 1];

    try {
        this.stylesheet.insertRule(`
@keyframes spin {
100% {
transform: rotate(360deg);
}
}`, this.stylesheet.cssRules.length);
    } catch (e) {
        alert('error');
    }
}
function loadStyleSheets() {

    //windowCached.on('load', function () {
    function styleSheetContainsClass(f) {
        // const DEBUG = true;
        var localDomainCheck = '^http://' + document.domain;
        var localDomainCheckHttps = '^https://' + document.domain;
        // DEBUG && console.log("Domain check with: " + localDomainCheck);
        var hasStyle = false;
        var stylename = f;
        var fullStyleSheets = document.styleSheets;
        // DEBUG && console.log("start styleSheetContainsClass " + stylename);
        if (fullStyleSheets) {
            for (let i = 0; i < fullStyleSheets.length - 1; i++) {
                //DEBUG && console.log("loop fullStyleSheets " + stylename);
                let styleSheet = fullStyleSheets[i];
                if (styleSheet != null) {
                    if (styleSheet.href !== null) //https://gold.xitu.io/entry/586c67c4ac502e12d631836b "However since FF 3.5 (or thereabouts) you don't have access to cssRules collection when the file is hosted on a different domain" -> Access error for Firefox based browser. script error not continuing
                        if (styleSheet.href.match(localDomainCheck) || styleSheet.href.match(localDomainCheckHttps)) {
                            if (styleSheet.cssRules) {
                                //DEBUG && console.log("styleSheet.cssRules.length: " + styleSheet.cssRules.length)
                                for (let rulePos = 0; rulePos < styleSheet.cssRules.length - 1; rulePos++) {
                                    if (styleSheet.cssRules[rulePos] !== undefined) {
                                        // DEBUG && console.log("styleSheet.cssRules[rulePos] "+ stylename);
                                        if (styleSheet.cssRules[rulePos].selectorText) {
                                            //  console.log(styleSheet.cssRules[rulePos].selectorText)
                                            if (styleSheet.cssRules[rulePos].selectorText == stylename) {
                                                console.log('styleSheet class has been found - class: ' + stylename);
                                                hasStyle = true; //break;
                                                break; //return hasStyle;
                                            }
                                        }  //else DEBUG && console.log("undefined styleSheet.cssRules[rulePos] "+rulePos +" - "+ stylename);
                                    }
                                    //else DEBUG && console.log("loop undefined styleSheet.cssRules[rulePos] "+ stylename);
                                }
                            } //else DEBUG && console.log("undefined styleSheet.cssRules "+ stylename);
                        }
                    //   DEBUG && console.log("stylesheet url " + styleSheet.href);
                } //else DEBUG && console.log("undefined styleSheet "+ stylename);
                if (hasStyle) break;
            }
        } //else console.log("undefined fullStyleSheets=document.styleSheets "+ stylename);
        if (!hasStyle)
            console.log("styleSheet class has not been found - style: " + stylename);
        return hasStyle;
    }

    if (STYLESHEETHIJACKFORBACKGROUND !== "")
        if (!styleSheetContainsClass(STYLESHEETHIJACKFORBACKGROUND))
            STYLESHEETHIJACKFORBACKGROUND = "";
        else {
            STYLESHEETHIJACKFORBACKGROUND = STYLESHEETHIJACKFORBACKGROUND.replaceAll(".", " ").trim()
        }
    if (STYLESHEETHIJACKFORTITLE !== "")
        if (!styleSheetContainsClass(STYLESHEETHIJACKFORTITLE))
            STYLESHEETHIJACKFORTITLE = "";
        else {
            STYLESHEETHIJACKFORTITLE = STYLESHEETHIJACKFORTITLE.replaceAll(".", " ").trim()
        }


    // $('body').append('<div ID="popover" ' + stylesheetForBackground() + '></div>');
    let bodyElement = document.getElementsByTagName("BODY")[0];
    bodyElement.insertAdjacentHTML("beforeend", '<div ID="popover" ' + stylesheetForBackground() + '></div>');
    //console.log(bodyElement.lastChild)
    popover = document.getElementById('popover');

    popover.style['position'] = "absolute";
    popover.style['z-index'] = '10';
    popover.style['box-shadow'] = '0px 0px 5px #7A7A7A';

    //console.log(popover)
}
window.addEventListener("load", loadStyleSheets);


function stylesheetForTitle() {
    if (STYLESHEETHIJACKFORTITLE !== "")
        return 'class="' + STYLESHEETHIJACKFORTITLE + '" style="display:inline-block;width:100%;max-width:auto;text-align:center !important"';
    else
        return 'style="background-color:' + DEFAULTTITLEBACKGROUNDCOLOR + ';display:inline-block;width:100%;max-width:auto;text-align:center !important"';
}

function stylesheetForBackground() {
    if (STYLESHEETHIJACKFORBACKGROUND !== "")
        return 'class="' + STYLESHEETHIJACKFORBACKGROUND + '" style="display:flex !important;flex:1;flex-direction: column; align-items:center;pointer-events:none; width:auto; height:auto; max-width:100%; max-height:100%;"';
    else
        return 'style="background-color:' + DEFAULTBACKGROUNDCOLOR + ';display:flex !important;flex-direction: column; align-items:center;pointer-events:none; width:auto; height:auto; max-width:100%; max-height:100%;"';
}

function mouseEnterPopup(e) {

    if (!e.target.matches(concatSelector())) return;
    let element = undefined;//$(this);
    let nativElement = this;
    let Href = this.href;// element.attr('href');
    if (Href.search(INDIVIDUALPAGETEST) != -1) //only trigger for links that point to serie pages
    {
        let refreshPopover = function (title, link) {
            const DEBUG = false;
            /*	clear popup
             *	append title and image into popup
             *	when img loading is finished reposition (popupPos) to element/border
             */
            if (currentTitelHover == title) //popup only gets refreshed when currentTitelHover == title
            {
                DEBUG && console.group("refreshPopover");

                //popover.empty();
                popover.innerHTML = "";
                if (inBlocklist(link)) {
                    // popover.append('<div ' + stylesheetForTitle() + '>' + title + '</div>Blocked Image<br />No Cover Image<br />Unwanted Image');
                    popover.insertAdjacentHTML("beforeend", '<div ' + stylesheetForTitle() + '>' + title + '</div>Blocked Image<br />No Cover Image<br />Unwanted Image');
                    //   popupPos(e, element, style, nativElement);

                } else {
                    let imgElement = new Image();//document.createElement("img");
                    imgElement.src = link;
                    let aspectRatio = imgElement.naturalWidth / imgElement.naturalHeight;
                    DEBUG && console.log("aspectRatio: " + aspectRatio)

                    //popover.append('<div ' + stylesheetForTitle() + '>' + title + '</div><img src="' + link + '" style="margin:5px; width:auto; height:' + defaultHeight + ' !important; max-width:100%;min-height:0; max-height:100% !important; align-items: stretch;align-self: stretch;object-fit: contain;"></img>');
                    if (aspectRatio <= 1)
                        popover.insertAdjacentHTML("beforeend", '<div ' + stylesheetForTitle() + '>' + title + '</div><img src="' + link + '" style="margin:5px; width:auto; height:' + defaultHeight + ' !important; max-width:100%;min-height:0; max-height:100% !important; align-items: stretch;align-self: stretch;object-fit: contain;"></img>');
                    //popover.insertAdjacentHTML("beforeend", '<div style="display:flex;flex-direction:column"><div ' + stylesheetForTitle() + '>' + title + '</div><img src="' + link + '" style="margin:5px; width:auto; height:' + defaultHeight + ' !important; max-width:100%;min-height:0; max-height:100% !important; align-items: stretch;align-self: stretch;object-fit: contain;"></img></div>');
                    else
                        popover.insertAdjacentHTML("beforeend", '<div ' + stylesheetForTitle() + '>' + title + '</div><img src="' + link + '" style="margin:5px; height:auto; width:' + defaultHeight + ' !important; max-height:100%;min-width:0; max-width:100% !important; align-items: stretch;align-self: stretch;object-fit: contain;"></img>');
                    // popover[0].insertAdjacentHTML("beforeend", '<div ' + stylesheetForTitle() + '>' + title + '</div>');
                    //<img src="" style="margin:5px; width:auto; height:' + defaultHeight + ' !important; max-width:100%;min-height:0; max-height:100% !important; align-items: stretch;align-self: stretch;object-fit: contain;"></img>



                    //  popover[0].insertAdjacentElement("beforeend",imgElement);
                    //  let popoverElement = popover;    //document.getElementById("popover")
                    //console.log($('#popover img'))
                    //console.log(popoverElement)
                    //  let popOverImg = popoverElement.getElementsByTagName("img")[0];
                    //console.log("popoverElement.offsetHeight: " + popover[0].offsetHeight)
                    //console.log(popOverImg)

                    //   if (popOverImg) {
                    /*
                     popoverImg.onload = function () {
                         //console.log(Href + "onload is executed"); // for testing purposes

                         if (onHover) //is mouse still hovering over same title after loading finishes?
                             requestAnimationFrame(() => {
                                 console.log("popoverElement.offsetHeight: " + popoverElement.offsetHeight)
                                 popoverElement.popupPos(e, element, style, nativElement);
                             });
                     };
                     */


                    // imgElement.onload = () => {
                    //    DEBUG && console.log("onload is executed"); // for testing purposes
                    // console.log("imgElement.addEventListener load");
                    //DEBUG && console.log("popoverElement.offsetHeight: " + popoverElement.offsetHeight);
                    // console.log("popoverImg: " + popoverImg);
                    // if (onHover) //is mouse still hovering over same title after loading finishes?
                    //popoverElement.popupPos(e, element, style, nativElement);
                    //   popupPos(e, element, style, nativElement);
                    //   }

                    //   }




                    /*

                    $('#popover img').on('load', function () {
                        //console.log(Href + "onload is executed"); // for testing purposes
                        if (onHover) //is mouse still hovering over same title after loading finishes?
                            popover.popupPos(e, element, style, nativElement);
                    });
                     */
                }
                DEBUG && console.groupEnd("refreshPopover");
                if (onHover)
                    popupPos(e, element, style, nativElement);
            }


        };

        //popup loading spinner
        var showPopupLoadingSpinner = function (title, error = false) {
            const DEBUG = false;
            // console.group("showPopupLoadingSpinner")
            //popover.empty();
            popover.innerHTML = "";
            DEBUG && console.log("popover.offsetHeight: " + popover.offsetHeight);
            if (error)
                popover.insertAdjacentHTML("beforeend", '<div ' + stylesheetForTitle() + '>' + title + '</div><div style="position: relative;width:150px; height:150px;color:#000;display: flex; justify-content: center; flex-direction: column; text-align: center;">imagecontainer setting is invalid</div>');
            else {
                popover.insertAdjacentHTML("beforeend", '<div ' + stylesheetForTitle() + '>' + title + '</div><div style="position: relative;width:150px; height:150px;color:#fff;display: flex; justify-content: center; flex-direction: column; text-align: center;">Loading image<div style="z-index: -100;position:absolute;top:0;left:0;background-color:#000; box-sizing: border-box; width: 150px; height: 150px; border-radius: 100%; border: 10px solid rgba(255, 255, 255, 0.2); border-top-color: #FFF; animation: spin 1s infinite linear;"></div></div>');
            }
            DEBUG && console.log(popover)
            DEBUG && console.log("popover.offsetHeight: " + popover.offsetHeight);
            popupPos(e, element, style, nativElement);
            //  console.groupEnd("showPopupLoadingSpinner")
        }

        //async wait until image is loaded before refreshing popup
        var ajaxLoadImageUrlAndShowPopup = function (elementUrl, title) {
            const DEBUG = false;
            // console.group("ajaxLoadImageUrlAndShowPopup")
            loadImgUrl(elementUrl)
                .then(function (imgUrl) {
                    let img = document.createElement("img"); //put img into dom. Let the image preload in background                        
                    img.onload = () => {
                        DEBUG && console.log("imgurl " + imgUrl);
                        DEBUG && console.log("popover[0].offsetHeight: " + popover.offsetHeight);
                        refreshPopover(title, imgUrl); //popup only gets refreshed when currentTitelHover == serieTitle      
                        DEBUG && console.log("popover[0].offsetHeight: " + popover.offsetHeight);
                        // GM_setCachedValue(elementUrl, imgUrl); //cache imageurl link
                        DEBUG && console.log(elementUrl + " url has been found and is written to temporary cache.\n" + imgUrl + ' successfully cached.'); // for testing purposes                      
                    };
                    img.src = imgUrl;
                }, function (Error) {
                    DEBUG && console.log(Error + ' failed to fetch ' + elementUrl);
                });
            // console.groupEnd("ajaxLoadImageUrlAndShowPopup")
        };


        onHover = true;
        //console.log(this)
        // console.log(this.text) //shortTitle
        //  console.log(this.title) //LongTitle
        let shortSerieTitle = this.text; //element.text(); //get linkname


        var serieTitle = this.title;//element.attr('datatitle'); //try to get nativ title if available from datatitle
        if (!serieTitle) //has no set nativ long title -> use (available shortend) linkname
            serieTitle = shortSerieTitle;
        else //no need to run check if it is already shortSerieTitle
            if (serieTitle.match(PREDIFINEDNATIVTITLE)) //catch on individual serie page nativ title begins with "Recommended by" x people -> use linkname
                serieTitle = shortSerieTitle;

        currentTitelHover = serieTitle; //mark which titel is currently hovered
        //console.log("serieTitle: " + serieTitle)
        //console.log("Href: " + Href)
        /* var retrievedImgLink = GM_getCachedValue(Href); //was imageurl cached?
         if (retrievedImgLink) {
             refreshPopover(serieTitle, retrievedImgLink); //popup only gets refreshed when currentTitelHover == serieTitle
             console.log(retrievedImgLink + ' on the page ' + Href + " has been found and retrieved from the cache."); // for testing purposes
         }
         else*/

        showPopupLoadingSpinner(serieTitle);
        ajaxLoadImageUrlAndShowPopup(Href, serieTitle);

    }
}

//when selected link is entered load imageurl and write popover content
concatenatedSelectorAll.forEach(function (selector) {
    selector.addEventListener("mouseenter", mouseEnterPopup)
})

function hidePopOver() {
    popover.style.visibility = "hidden";
    onHover = false;
}
function showPopOver() {
    popover.style.display = "flex"
    popover.style.visibility = "visible";
}
function hideOnMouseLeave(e) {
    if (!e.target.matches(concatSelector())) return;
    //popover.hide();
    hidePopOver();
}
//hide and empty popup when mouse is not over title
concatenatedSelectorAll.forEach(function (selector) {
    selector.addEventListener("mouseleave", hideOnMouseLeave)
})
/*
$(document).mouseleave(function () { //force close when mouse is outside window and previous mouseleave does not get called
    popover.hide();
    onHover = false;
});
*/

window.addEventListener("blur", hidePopOver);

function mouseenterAllRemoveListener(selectorType, styleType = 1) {
    let selectorAll = document.querySelectorAll(selectorType);
    //console.log("mouseenterAll for selectors: " + selectorType)
    selectorAll.forEach(function (selector) {
        selector.removeEventListener("mouseenter", changeStyleType)
    })
}


window.onunload = function () {
    window.removeEventListener("blur", hidePopOver);
    window.removeEventListener("load", loadStyleSheets);


    //possible memoryleaks?
    concatenatedSelectorAll.forEach(function (selector) {
        selector.removeEventListener("mouseleave", hideOnMouseLeave)
    })
    concatenatedSelectorAll.forEach(function (selector) {
        selector.removeEventListener("mouseenter", mouseEnterPopup)
    })

    mouseenterAllRemoveListener(SELECTOR1, 1);
    mouseenterAllRemoveListener(SELECTOR2, 2);


}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址