您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Previews covers in novelupdates.com when hovering over hyperlinks that lead to novel pages.
当前为
// ==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/* // @version 1.2 // @description Previews covers in novelupdates.com when hovering over hyperlinks that lead to novel pages. // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @run-at document-end // @require http://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js // @license http://creativecommons.org/licenses/by-nc-sa/4.0/ // ==/UserScript== // article a; http://forum.novelupdates.com/ ; crossDomain: true, xhrFields to delete before upload const MAXCACHEAGE = 7 * 24 * 60 * 60 * 1000; // Max Age before Cached data gets overridden with current data. Max Age is 24 hour in milliseconds //days * h * min * sec * ms const SELECTOR1 = 'td a'; //index/group/readinglist pages const SELECTOR2 = '.wpb_wrapper > 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/"; var STYLESHEETHIJACKFORBACKGROUND = "breadcrumb_nu"; //if unknown set empty ""; classname without leading dot var STYLESHEETHIJACKFORTITLE = "tbl_sort"; //if unknown set empty ""; classname without 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 IMAGELINKCONTAINER = 'serieseditimg'; const IMAGELINKCONTAINERANONYM = 'seriesimg'; //sContainer const CONTAINERNUMBER = 0; const IMAGEBLOCKER = "http://www.novelupdates.com/img/noimagefound.jpg"; const HASERROR = true; const RE = /\s*,\s*/; //Regex for split and remove empty spaces const IMAGEBLOCKERARRAY = IMAGEBLOCKER.split(RE); function concatSelector() { var result; if(SELECTOR1) result = SELECTOR1; if(SELECTOR2) { if(SELECTOR1) //in case selector1 is missing result += ', '; result += SELECTOR2; } return result; } //get value from key. Decide if timestamp is older than MAXCACHEAGE than look for new image function GM_getCachedValue(key) { var currentTime = Date.now(); var keyCache = key+'cachedTime'; if(GM_getValue(keyCache)) //has previous timestamp for key { var measuredTimedifference = currentTime-parseInt(GM_getValue(keyCache)); if(measuredTimedifference < MAXCACHEAGE ) { if(GM_getValue(key)) { return GM_getValue(key); //when time difference smaller MAXCACHEAGE and value available return value } else GM_deleteValue(keyCache); //if no value available delete previous key with timestamp } else { //when time difference bigger than MAXCACHEAGE delete timestamp and value; GM_deleteValue(keyCache); GM_deleteValue(key); } } else //when no timestamp available return false return false; } //set value and currenttime for key function GM_setCachedValue(key, value) { var currentTime = Date.now(); // current datetime in milliseconds GM_setValue(key, value); GM_setValue(key+'cachedTime', currentTime); } var onHover = false; var currentTitelHover; // popupPositioning function jQuery.fn.popupPos = function (event, element, style) { var offsetToBottomBorderY = 5; //offset to bottom border var offsetToRightBorderX = 5; //offset to right border var X,Y; var hoveredSelectedPosX,hoveredSelectedPosY; var distanceToBottom, distanceToRight; //Initialising variables (multiple usages) var scrollTop = $(window).scrollTop(); var scrollLeft = $(window).scrollLeft(); var elementPopup = $(this); var elementParentOffset = element.parents().offset(); var elementParentOuterHeight = element.parents().outerHeight(); var elementParentOuterWidth = element.parents().outerWidth(); var elementOffset = element.offset(); var elementOuterHeight = element.outerHeight(); var elementOuterWidth = element.outerWidth(); if(style==1) //index: position next to parent table cell (SELECTOR1) { hoveredSelectedPosX = elementParentOffset.left + elementParentOuterWidth; //link position + tablecell width; + elementOuterWidth;; hoveredSelectedPosY = elementParentOffset.top + elementParentOuterHeight; //link position + tablecell height; + elementOuterHeight; } else if(style==2) //recommendations: position next to link height and parent (SELECTOR2) width { hoveredSelectedPosX = elementParentOffset.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; // Distance to the right distanceToRight = $(window).width() - (X + offsetToRightBorderX - scrollLeft); // Tooltip too close to the right? if(distanceToRight < elementPopup.outerWidth() + offsetToRightBorderX) X += distanceToRight - elementPopup.outerWidth(); // Distance to the bottom distanceToBottom = $(window).height() - (Y + offsetToBottomBorderY - scrollTop); // Tooltip too close to the bottom? if(distanceToBottom < elementPopup.outerHeight() + offsetToBottomBorderY) //(offsetToBottomBorderY + elementPopup.outerHeight()) Y += distanceToBottom - elementPopup.outerHeight(); //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; this.css('top', Y + 'px'); this.css('left', X + 'px'); //console.log("final popup position "+X+' # '+Y); return this; }; var style=1; //.wpb_wrapper a = title links on individual seriepage $(SELECTOR2).mouseenter(function (e) { style=2; }); //td a = links in table cells (index and group page) $(SELECTOR1).mouseenter(function (e) { style=1; }); $(window).on('load', function () { function styleSheetContainsClass(f) { var localDomainCheck = '^http://'+document.domain; //console.log("Domain check with: " + localDomainCheck); var hasStyle = false; var stylename = '.'+f; var fullStyleSheets = document.styleSheets; // console.log("start styleSheetContainsClass " + stylename); if(fullStyleSheets) { for (var i = 0; i < fullStyleSheets.length-1; i++){ //console.log("loop fullStyleSheets " + stylename); //hier im bereich firefox bug? var styleSheet = fullStyleSheets[i]; //var stylehref = ""+styleSheet.href; //console.log("style test : " + styleSheet.href); //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) ) { if(styleSheet.cssRules) { for(var rulePos = 0; rulePos < styleSheet.cssRules.length-1; rulePos++) { if(styleSheet.cssRules[rulePos] !== undefined) { // console.log("styleSheet.cssRules[rulePos] "+ stylename); if(styleSheet.cssRules[rulePos].selectorText) { if(styleSheet.cssRules[rulePos].selectorText == stylename) { console.log('styleSheet class has been found - style: ' + stylename); hasStyle = true; //break; return hasStyle; } }// else console.log("undefined styleSheet.cssRules[rulePos] "+ stylename); } // else console.log("loop undefined styleSheet.cssRules[rulePos] "+ stylename); } } //else console.log("undefined styleSheet.cssRules "+ stylename); } //console.log("stylesheet url " + styleSheet.href); } //else console.log("undefined styleSheet "+ stylename); if(hasStyle) break; } } //else console.log("undefined fullStyleSheets "+ stylename); console.log("styleSheet class has not been found - style: " + stylename); return hasStyle; } if(STYLESHEETHIJACKFORBACKGROUND !="") if (!styleSheetContainsClass(STYLESHEETHIJACKFORBACKGROUND) ) STYLESHEETHIJACKFORBACKGROUND = ""; if(STYLESHEETHIJACKFORTITLE !="") if (!styleSheetContainsClass(STYLESHEETHIJACKFORTITLE) ) STYLESHEETHIJACKFORTITLE = ""; $('body').append('<div ID="popover" '+stylesheetForBackground()+'></div>'); $('#popover').css('position', 'absolute'); $('#popover').css('z-index', '10'); $('#popover').css('box-shadow', '0px 0px 5px #7A7A7A'); //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 die 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 stylesheetForTitle() { if(STYLESHEETHIJACKFORTITLE != "") return 'class="' + STYLESHEETHIJACKFORTITLE + '" style="display:inline-block;width:100%;text-align:center !important"'; else return 'style="background-color:' + DEFAULTTITLEBACKGROUNDCOLOR+';display:inline-block;width:100%;text-align:center !important"'; } function stylesheetForBackground() { if(STYLESHEETHIJACKFORBACKGROUND != "") return 'class="'+ STYLESHEETHIJACKFORBACKGROUND+'" style="display:flex !important;flex-direction: column; align-items:center;pointer-events:none; width:auto; height:auto; max-width:500px; max-height:500px;"'; else return 'style="background-color:'+DEFAULTBACKGROUNDCOLOR+';display:flex !important;flex-direction: column; align-items:center;pointer-events:none; width:auto; height:auto; max-width:500px; max-height:500px;"'; } //when selected link is entered load imageurl and write popover content $(concatSelector()).mouseenter(function (e) { var element = $(this); var Href = element.attr('href'); if (Href.search(INDIVIDUALPAGETEST) != -1) //only trigger for links that point to serie pages { function inBlocklist(link) { if(IMAGEBLOCKERARRAY) if(IMAGEBLOCKERARRAY.length > 0) for(var i=0; i < IMAGEBLOCKERARRAY.length; i++) if( link == IMAGEBLOCKERARRAY[i] ) return true; return false; } function refreshPopover(title, link) { /* 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 { $('#popover').empty(); if( inBlocklist(link) ) { $('#popover').append('<div '+stylesheetForTitle()+'>'+title+'</div>Blocked Image / No Cover Image / Unwanted Image'); $('#popover').popupPos(e, element, style); } else { $('#popover').append('<div '+stylesheetForTitle()+'>'+title+'</div><img src="' + link + '" style="margin:5px; width:auto; height:400px !important; max-width:100%; max-height:100%; align-items: stretch;align-self: stretch;object-fit: contain;"></img>'); $('#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); }); } } } //popup loading spinner function showPopupLoadingSpinner(title, error = false) { $('#popover').empty(); if(error) $('#popover').append('<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;">iamgelink getElementsByClassName is invalid</div>'); else $('#popover').append('<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;">Getting image url<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>'); $('#popover').popupPos(e, element, style); } //async wait until image is loaded before refreshing popup function ajaxLoadImageUrlAndShowPopup(url, title) { $.ajax({ url: Href, type: "GET", dataType: 'text', crossDomain: true, xhrFields: { withCredentials: true }, success: function (data) { if (!data && data == " ") { console.log('response to ' + Href + ' was empty'); showPopupLoadingSpinner(serieTitle,1); } else { //vvv get imageurl link vvv var htmlData = $('<div>').html(data)[0]; var imagelinkLoggedIn = htmlData.getElementsByClassName(IMAGELINKCONTAINER)[CONTAINERNUMBER]; var imagelink; if(imagelinkLoggedIn) //need to consider different img classes for logged in and anonymous users imagelink = imagelinkLoggedIn.getElementsByTagName('img')[0].src; else imagelink = htmlData.getElementsByClassName(IMAGELINKCONTAINERANONYM)[CONTAINERNUMBER].getElementsByTagName('img')[0].src; //console.log("imagelink: " + imagelink); // ^^^ get imageurl link ^^^ refreshPopover(serieTitle, imagelink); //popup only gets refreshed when currentTitelHover == serieTitle // cache info GM_setCachedValue(Href, imagelink); //cache imageurl link console.log(Href + " url has been found and is written to temporary cache.\n"+ imagelink + ' successfully cached.'); // for testing purposes } } }); } onHover = true; $('#popover').show(); var shortSerieTitle = element.text(); //get linkname //move native title to custom data attribute. Suppress nativ title if(!element.attr('datatitle')) { element.attr('datatitle', element.attr('title')); element.removeAttr('title'); } var serieTitle = 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 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); } } }); //hide and empty popup when mouse is not over title $(concatSelector()).mouseleave(function () { //close popup when mouse leaves titlelink //$('#popover').empty(); $('#popover').hide(); onHover = false; }); $(document).mouseleave(function () { //force close when mouse is outside window and previous mouseleave does not get called $('#popover').hide(); onHover = false; }); $(window).blur(function () { //chrome fix -> force close when mouse is outside window alt + tab $('#popover').hide(); onHover = false; });
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址