Greasy Fork is available in English.
Enlarged preview of arts and manga on mouse hovering on most pages. Click on image preview to open original art in new tab, or MMB-click to open art illustration page, Alt+LMB-click to to add art to bookmarks, Ctrl+LMB-click for saving originals of artworks. The names of the authors you are already subscribed to are highlighted with green.
// ==UserScript== // @name Pixiv Arts Preview & Followed Atrists Coloring // @name:ru Pixiv Arts Preview & Followed Atrists Coloring // @namespace Pixiv // @description Enlarged preview of arts and manga on mouse hovering on most pages. Click on image preview to open original art in new tab, or MMB-click to open art illustration page, Alt+LMB-click to to add art to bookmarks, Ctrl+LMB-click for saving originals of artworks. The names of the authors you are already subscribed to are highlighted with green. // @description:ru Увеличённый предпросмотр артов и манги по наведению мышки на большинстве страниц. Клик ЛКМ по превью арта для открытия исходника в новой вкладке, СКМ для открытия страницы с артом, Alt + клик ЛКМ для добавления в закладки, Ctrl + клик ЛКМ для сохранения оригиналов артов. Имена авторов, на которых вы уже подписаны, подсвечиваются зелёным цветом. // @author NightLancerX // @version 1.43.1 // @match https://www.pixiv.net/bookmark_new_illust.php* // @match https://www.pixiv.net/discovery* // @match https://www.pixiv.net/bookmark_detail.php?illust_id=* // @match https://www.pixiv.net/member_illust.php* // @match https://www.pixiv.net/ranking.php?mode=* // @match https://www.pixiv.net/member.php?id=* // @match https://www.pixiv.net/bookmark.php* // @match https://www.pixiv.net/search.php* // @match https://www.pixiv.net/ // @match https://www.pixiv.net/stacc* // @match https://www.pixiv.net/*artworks/* // @match https://www.pixiv.net/*tags/* // @connect i.pximg.net // @homepageURL https://github.com/NightLancer/PixivPreview // @license MIT License // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @require https://code.jquery.com/jquery-3.3.1.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js // ==/UserScript== //======================================================================================= (function () { 'use strict'; if (window.top == window.self && window.jQuery) jQuery(function($) { console.log('MyPixivJS'); //---------------------------***CUSTOM PREFERENCES***-------------------------------- const PREVIEW_ON_CLICK = false; //if "true" — showing arts preview after LMB-click on art instead of hovering over it const DELAY_BEFORE_PREVIEW = 0; //if you need delay before showing art preview, set it here (1000 = 1 second) const previewSize = 0; //you can manually set size of preview to 1200 or 600 (pixels); Default value (0) means it will be calculated automatically with "live" dependence of current screen size const ACCURATE_MANGA_PREVIEW = false; //if `true` - increases time before manga preview appearing(to 1sec) but shows it at more accurate position considering width(for case of few arts) const DISABLE_MANGA_PREVIEW_SCROLLLING_PROPAGATION = false; //defines whether to keep on scrolling propagation when reaching end of manga preview container const SCROLL_INTO_VIEW_FOR_SINGLE_IMAGE = true; //apply scrollIntoView for single preview const DISABLE_SINGLE_PREVIEW_BACKGROUND_SCROLLING = false; //defines background scrolling for single preview when `SCROLL_INTO_VIEW_FOR_SINGLE_IMAGE` set to `true` //----------------------------------------------------------------------------------- //const EXPERIMENTAL_WORKSPAGE_RESTYLE = false; //currently raw style adjustment on illust page; may broke at any time, so turn it on your risk (yet) --> currently broken //----------------------------------------------------------------------------------- let hoverImg = document.createElement('img'); let imgContainer = document.createElement('div'); imgContainer.style = 'position:absolute; display:block; z-index:1000; background:#222; padding:5px; margin:-5px;'; imgContainer.appendChild(hoverImg); let mangaContainer = document.createElement('div'); mangaContainer.id = 'mangaContainer'; mangaContainer.style = 'display:block; z-index:1500; background:#111; overflow-x:auto; maxWidth:1200px; white-space:nowrap;'; let mangaOuterContainer = document.createElement('div'); mangaOuterContainer.style = 'position:absolute; display:block; visibility:hidden; z-index:1000; padding:5px; background:#111; maxWidth:1200px; marginY:-5px; marginX: auto; left: 30px;'; mangaOuterContainer.appendChild(mangaContainer); let imgsArr = [], //for manga-style image packs... followedUsersId = {}, //storing followed users pixiv ID BOOKMARK_URL = 'https://www.pixiv.net/bookmark.php', CheckedPublic = false, CheckedPrivate = false, artsLoaded = 0, lastHits = 0, lastImgId = -1, PREVIEW_SIZE, siteImgMaxWidth = 184, //2,3,7,12 [NEW]| quite useless on this pages because of square previews... mangaWidth = 1200, maxRequestTime = 30000, bookmarkObj, isBookmarked = false, //rework or delete. Arts can be bookmarked on art page. DELTASCALE = ('mozInnerScreenX' in window)?70:4, previewEventType = (PREVIEW_ON_CLICK)?'click':'mouseenter', PAGETYPE = checkPageType(); var timerId, tInt; //----------------------------------------------------------------------------------- Storage.prototype.setObj = function(key, obj){ return this.setItem(key, JSON.stringify(obj)) } Storage.prototype.getObj = function(key){ return JSON.parse(this.getItem(key)) } //=================================================================================== //************************************PageType*************************************** //=================================================================================== function checkPageType() { if (document.URL.match('https://www.pixiv.net/bookmark_new_illust.php?')) return 0; //New illustrations - Old + if (document.URL==='https://www.pixiv.net/discovery') return 1; //Discovery page(works) - Old + if (document.URL.match('https://www.pixiv.net/member_illust.php?')) return 2; //Artist works page - New + | Todo: merge 2 and 3 pages?... if (document.URL.match('https://www.pixiv.net/member.php?')) return 3; //Artist "Home" page - New + if (document.URL.match('https://www.pixiv.net/bookmark_detail.php?')) return 4; //Bookmark information - Old + if (document.URL.match('https://www.pixiv.net/ranking.php?')) return 6; //Daily rankings - Old + if (document.URL.match(/https:\/\/www\.pixiv\.net\/bookmark\.php\?id/)) return 7; //Someone's bookmarks page - New + if (document.URL.match(/https:\/\/www\.pixiv\.net\/(?:en\/)?tags/)) return 8; //Search page - New + if (document.URL.match('https://www.pixiv.net/bookmark.php?')) return 9; //Your bookmarks page - Old + //if (document.URL==='https://www.pixiv.net/') return 10; //Home page - //todo: settings menu? if (document.URL.match('https://www.pixiv.net/stacc?')) return 11; //Feed ('stacc') Old + if (document.URL.match(/https:\/\/www\.pixiv\.net\/(?:en\/)?artworks/)) return 12; //Illust page - New* + if (document.URL.match('https://www.pixiv.net/discovery/users?')) return 13; //Discovery page(users) New + return -1; } console.log('PAGETYPE: '+ PAGETYPE); //Old: 0,1,4,6,9,11 //New: 2,3,7,12,13,8 //----------------------------------------------------------------------------------- function resetPreviewSize(){PREVIEW_SIZE = (previewSize)?previewSize:(window.innerHeight>1200 & document.body.clientWidth>1200)?1200:600} //=================================================================================== //**********************************ColorFollowed************************************ //=================================================================================== if ([1,4,6,7].includes(PAGETYPE)) //+12 in initMutationParentObject -> user may not scroll to bottom, so it is better to stay in mutaionObserver { checkFollowedArtistsInit(); } function checkFollowedArtistsInit() { if ((Date.now()-23*60*60*1000)>localStorage.getItem('followedCheckDate') && !localStorage.getObj('followedCheckStarted')) //forcing update followed list(in case of errors) at least every 23 hours { console.log('followedCheckStarted'); localStorage.setObj('followedCheckCompleted', false); localStorage.setObj('followedCheckStarted', true); checkFollowedArtists(BOOKMARK_URL+'?type=user'); //public checkFollowedArtists(BOOKMARK_URL+'?type=user&rest=hide'); //private } else if ([6].includes(PAGETYPE)) colorFollowed(); } //----------------------------------------------------------------------------------- async function checkFollowedArtists(url) { if (url === undefined || url.length === 0) return; //just in case let xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.timeout = 15000; xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log("XHR done"); let doc = document.implementation.createHTMLDocument("Followed"); doc.documentElement.innerHTML = xhr.responseText; let followedProfiles = doc.querySelectorAll('div>a.ui-profile-popup'); for(let i = 0; i < followedProfiles.length; i++) { //followedUsersId.push(followedProfiles[i].getAttribute("data-user_id")); followedUsersId[followedProfiles[i].getAttribute("data-user_id")] = true; } console.log(Object.keys(followedUsersId).length); let urlTail = $(doc).find('a[rel="next"]').attr('href'); if (urlTail !== undefined && urlTail.length) { console.log(urlTail); checkFollowedArtists(BOOKMARK_URL+urlTail); } else { if (doc.querySelectorAll('li.current')[0].textContent==='Public') CheckedPublic = true; else if (doc.querySelectorAll('li.current')[0].textContent==='Private') CheckedPrivate = true; if (CheckedPublic && CheckedPrivate) { localStorage.setObj('followedCheckCompleted', true); localStorage.setObj('followedCheckStarted', false); localStorage.setObj('followedUsersId', followedUsersId); localStorage.setObj('followedCheckDate', Date.now()); localStorage.setObj('followedCheckError', false); console.log('Followed check completed'); if (PAGETYPE===6) colorFollowed(); //only for daily rankings? | for loading on same page case } } doc = followedProfiles = null; } }; xhr.onerror = function() { console.error('ERROR while GETTING subscriptions list!'); localStorage.setObj('followedCheckError', true); localStorage.setObj('followedCheckCompleted', false); localStorage.setObj('followedCheckStarted', false); }; xhr.send(); } //----------------------------------------------------------------------------------- async function colorFollowed(artsContainers) { let c = 0, d = 0; while (!artsContainers || artsContainers.length === 0) //first call -> daily rankings, illust page { console.log('waiting for arts...'); await sleep(2000); artsContainers = getArtsContainers(); ++c; if (c>5) { console.error('Error while waiting for atrs loading! [Timeout 10s]'); break; } } let artsContainersLength = artsContainers.length; //console.log(artsContainersLength); //wait until last XHR completed if it is not--------------------------------------- if (localStorage.getObj('followedCheckCompleted') === null || localStorage.getObj('followedCheckCompleted') === false) { if (localStorage.getObj('followedCheckStarted')) { while (!localStorage.getObj('followedCheckCompleted')) { console.log("waiting for followed users..."); //this could happen in case of huge followed users amount await sleep(2000); if (localStorage.getObj('followedCheckError')) { console.error('ERROR while RECEIVING subscriptions list!'); break; } ++d; if (d*2000>maxRequestTime) { console.error('ERROR while EXPECTING for subscriptions list!'); localStorage.setObj('followedCheckError', true); localStorage.setObj('followedCheckCompleted', false); localStorage.setObj('followedCheckStarted', false); break; } } followedUsersId = localStorage.getObj('followedUsersId'); console.log('Succesfully received followedUsersId: '+ Object.keys(followedUsersId).length); } else console.error('Subscriptions check was not STARTED for some reason!'); } else { followedUsersId = localStorage.getObj('followedUsersId'); console.log('Succesfully loaded cached followedUsersId: '+ Object.keys(followedUsersId).length); } //--------------------------------------------------------------------------------- artsLoaded = (PAGETYPE===12)?$('.gtm-illust-recommend-user-name').length:$('.ui-profile-popup').length; //if it brokes - get info from getArtsContainers() console.log('arts loaded: '+artsContainersLength + ' (Total: '+(artsLoaded)+')'); let currentHits = 0; let userId = 0; //console.dir(artsContainers); for(let i = 0; i < artsContainersLength; i++) { userId = getUserId(artsContainers[i]); //console.log(userId); //if (followedUsersId.indexOf(userId)>=0) if (followedUsersId[userId]==true) { ++currentHits; artsContainers[i].setAttribute("style", "background-color: green; !important"); } } lastHits += currentHits; console.log('hits: '+currentHits + ' (Total: '+(lastHits)+')'); } //----------------------------------------------------------------------------------- function getArtsContainers() { switch (PAGETYPE){ case 1,4: return document.querySelectorAll('.gtm-illust-recommend-user-name'); case 12: return document.querySelectorAll('.gtm-illust-recommend-title'); case 6: return document.querySelectorAll('.ui-profile-popup'); case 7: return document.querySelectorAll('a[href*="/member.php?id="]'); default: console.error('Unprocessed PAGETYPE in getArtsContainers()!'); } return null; } //----------------------------------------------------------------------------------- let getUserId = (artContainer) => { let userId = -1; if (typeof artContainer.hasAttribute !== 'function'){ console.log(artContainer,'has been filtered out.'); } else if (PAGETYPE===1 || PAGETYPE===6){ userId = (artContainer.hasAttribute('data-user_id')) ?artContainer.getAttribute('data-user_id') :artContainer.querySelectorAll('.ui-profile-popup')[0].getAttribute('data-user_id'); } else if (PAGETYPE===4 || PAGETYPE===12){ artContainer = artContainer.querySelectorAll('.gtm-illust-recommend-title')[0] || artContainer; // -_-' userId = artContainer.parentNode.querySelectorAll('[href*="/member.php?id="]')[0].getAttribute('href').split('=').pop(); } else if (PAGETYPE===7){ userId = artContainer.getAttribute('href').split('=').pop(); } else{ console.error('UNPROCESSED getUserId() call!'); } return userId; } //----------------------------------------------------------------------------------- function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } //----------------------------------------------------------------------------------- function getElementByXpath(path) { return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } //----------------------------------------------------------------------------------- let getArtSectionContainers = ([1,4,12].includes(PAGETYPE))? () => $('.gtm-illust-recommend-zone')[0] : () => $('.ranking-items')[0]; //6 //----------------------------------------------------------------------------------- function createObserver(mainDiv) { let observer = new MutationObserver(function(mutations) { let arr = []; mutations.forEach(function(mutation) { mutation.addedNodes.forEach(function(node) { //console.log(mutation); arr.push(node); }); }); colorFollowed(arr); }); let options = { 'childList': true }; observer.observe(mainDiv, options); console.log('Observer has been set'); } //----------------------------------------------------------------------------------- async function initMutationObject() { let mainDiv = getArtSectionContainers(); while(!mainDiv) { console.log('Waiting for arts container...'); await sleep(1000); mainDiv = getArtSectionContainers(); } console.log(mainDiv); createObserver(mainDiv); } //----------------------------------------------------------------------------------- async function initMutationParentObject(parentSelector, nodesSelector) { let observerParent = new MutationObserver(function(mutations) { let mainDiv; mutations.forEach(function(mutation) { mainDiv = mutation.addedNodes[0].getElementsByClassName(nodesSelector); console.log(mainDiv); if (mainDiv.length>0) { checkFollowedArtistsInit(); createObserver(mainDiv[0]); observerParent.disconnect(); observerParent = null; } }); }); let options2 = { 'childList': true }; let parentDiv = getElementByXpath(parentSelector); while(!parentDiv) { console.log('Waiting for getElementByXpath'); await sleep(1000); parentDiv = getElementByXpath(parentSelector); } console.log(parentDiv); observerParent.observe(parentDiv, options2); console.log('observerParent set'); } //----------------------------------------------------------------------------------- /* function initGallery() { let _ttt = $('figure > div[role="presentation"]'); let _sss = _ttt[0].textContent.split("⧸").pop(); //NOT slash! charCodeAt(0) == 10744 console.log(_ttt); console.log(_sss); } */ //----------------------------------------------------------------------------------- function followage(thisObj, toFollow, isNew) //In case of followed check lasting too long, async queue may be a solution { console.log('toFollow: '+ toFollow); let userId; switch (isNew){ case 0: userId = thisObj.parentNode.parentNode.querySelectorAll('a.user-name')[0].getAttribute('href').split('&')[0].split('=')[1]; break; //OLD case 1: userId = document.querySelectorAll('[href*="/member.php?id="]')[0].getAttribute('href').split('=').pop(); break; //NEW case 2: userId = thisObj.getAttribute('data-user-id'); break; //recommended users } //console.log(userId); if (localStorage.getObj('followedCheckCompleted')) //at least basic check until queue is developed { let followedUsersId = localStorage.getObj('followedUsersId'); //local if (toFollow){ followedUsersId[userId] = true; if ([2,3,7,12].includes(PAGETYPE)) initFollowagePreview(); } else delete followedUsersId[userId]; localStorage.setObj('followedUsersId', followedUsersId); console.log('userId ' + userId + [(toFollow)?' added to':' deleted from'] + ' localStorage. Followed: '+ Object.keys(followedUsersId).length); } else console.error('Slow down! You have subscribed too many to handle this by now! Wait for the next updates'); } //=================================================================================== if (PAGETYPE===0 || PAGETYPE===1 || PAGETYPE===8) siteImgMaxWidth = 198; else if (PAGETYPE===4 || PAGETYPE===9 || PAGETYPE===10) siteImgMaxWidth = 150; else if (PAGETYPE===6 || PAGETYPE===11) siteImgMaxWidth = 240; //----------------------------------------------------------------------------------- $(document).ready(function () { console.log('$(document).ready'); mangaWidth = document.body.clientWidth - 80; mangaContainer.style.maxWidth = mangaOuterContainer.style.maxWidth = mangaWidth+'px'; document.body.appendChild(imgContainer); document.body.appendChild(mangaOuterContainer); resetPreviewSize(); //-------------------------------Follow onclick------------------------------------ let toFollow, isNew, followSelector; if ([2,3,7,12].includes(PAGETYPE)){ followSelector = '[data-click-label*="follow"]'; isNew = 1; } else if([13].includes(PAGETYPE)){ followSelector = '.follow-button'; isNew = 2; } else { followSelector = '.follow-button'; isNew = 0; } $('body').on('mouseup', followSelector, function(){ toFollow = (this.textContent == 'Follow'); followage(this, toFollow, isNew); }); //----------------------------Bookmark detail page cleaning------------------------ if (PAGETYPE===4) { let _bkmrklst = $('.bookmark-list-unit')[0]; _bkmrklst.parentNode.removeChild(_bkmrklst); _bkmrklst = null; } //------------------------------Dayli rankings ad cleaning------------------------- if (PAGETYPE===6) { $('.ad-printservice').remove(); } //-------------------------------Illust page extra check--------------------------- if (PAGETYPE===12) { let parentSelector = '/html/body/div[1]/div[1]/div/aside[2]/div', //replace with selector if possible zoneSelector = 'gtm-illust-recommend-zone', zone = document.getElementsByClassName(zoneSelector)[0]; if (zone === undefined) initMutationParentObject(parentSelector, zoneSelector); else { console.log(zone); checkFollowedArtistsInit(); colorFollowed(); //process missed arts containers createObserver(zone); } } //-----------------------------Init illust fetch listener-------------------------- if (PAGETYPE===1 || PAGETYPE===4 || PAGETYPE===6) { initMutationObject(); } //---------------------------------Pixiv Member pages------------------------------ if (PAGETYPE===2|| PAGETYPE===3 || PAGETYPE===7) { $('body').on('mouseup', 'a[href*="&rest=show"]', function(){ console.log('PAGETYPE: '+ PAGETYPE+' -> 7'); PAGETYPE = 7; bookmarksInit(); }); // $('body').on('mouseup', 'a[href*="/member.php?id="]', function(){ // console.log('PAGETYPE: '+ PAGETYPE+' -> 3'); // PAGETYPE = 3; // }); // // $('body').on('mouseup', 'a[href*="&type=illust"]', function(){ // console.log('PAGETYPE: '+ PAGETYPE+' -> 2'); // PAGETYPE = 2; // }); } //------------------------------- async function bookmarksInit() { checkFollowedArtistsInit(); let mainSelector = '/html/body/div[1]/div[1]/div/div[2]/div[1]/section/ul'; let mainDiv = getElementByXpath(mainSelector); while(!mainDiv) { console.log('Waiting for getElementByXpath'); await sleep(1000); mainDiv = getElementByXpath(mainSelector); } colorFollowed(); } //------------------------------------Bookmarks------------------------------------ if (PAGETYPE===7) { bookmarksInit(); } //================================================================================= //***************************************HOVER************************************* //================================================================================= //------------------------------------Profile card--------------------------------- //0,1,4,6,8,9,11 if ([0,1,4,6,9,11].includes(PAGETYPE)) { $('body').on(previewEventType, 'a[href*="/artworks/"]', function(e) { e.preventDefault(); if (this.childNodes.length === 0) //for preventing issues with 4 and 6 pages { setHover(this); imgContainer.style.top = getOffsetRect(this).top+200+'px'; } }); } //-------------------------------------Followage----------------------------------- //2,3,7,12 function initFollowagePreview() { $('body').on(previewEventType, '.gtm-recommend-user-thumbnail', function(e) { e.preventDefault(); let top = window.innerHeight - PREVIEW_SIZE - 5 + window.scrollY + 'px'; setHover(this.firstChild.firstChild, top); let scroll = getElementByXpath('/html/body/div[6]/div/div/div/div/ul'); scroll.onwheel = function(ev) { ev.preventDefault(); scroll.scrollLeft += ev.deltaY*DELTASCALE; }; }); } //--------------------NEW ILLUSTRATIONS, DISCOVERY[ARTWORKS] AND SEARCH------------ //0,1,8 function setPreviewEventListeners() { //single art hover--------------------------------------------------------------- $('body').on(previewEventType, 'a[href*="/artworks/"] > div:only-child', function(e) { e.preventDefault(); bookmarkObj = $(this).parent().parent().children(".thumbnail-menu").children("._one-click-bookmark"); checkBookmark(this); setHover(this); }); //manga-style arts hover--------------------------------------------------------- $('body').on(previewEventType, 'a[href*="/artworks/"] > div:nth-child(2) ', function(e) { e.preventDefault(); if (this.parentNode.firstChild.childNodes.length) { bookmarkObj = $(this).parent().parent().children(".thumbnail-menu").children("._one-click-bookmark"); checkBookmark(this); setMangaHover(this, this.parentNode.firstChild.firstChild.textContent); } }); } //-----------------------------------DISCOVERY[USERS]------------------------------ //13 function setDiscoveryUsersPreviewEventListeners() { $('body').on(previewEventType, 'a[href*="/artworks/"]', function(e) { e.preventDefault(); if (this.childNodes.length == 0) setHover(this); //single art else if (this.childNodes.length == 1) setMangaHover(this, this.firstChild.textContent); //manga }); } //--------------------------------------------------------------------------------- //1->13 function setTabSwitchingListenerW_U() { $('body').on('mouseup','a[href="/discovery/users"]', function() { console.log('Works -> Users'); PAGETYPE = 13; artsLoaded = lastHits = 0; //clearing loaded arts count when switching on tabs $('body').off(); //may cause some removal of timeout events? setDiscoveryUsersPreviewEventListeners(); setTabSwitchingListenerU_W(); }); } //--------------------------------------------------------------------------------- //13->1 function setTabSwitchingListenerU_W() { $('body').on('mouseup','a[href="/discovery"]', function() { console.log('Users -> Works'); PAGETYPE = 1; artsLoaded = lastHits = 0; //not necessary checkFollowedArtistsInit(); initMutationObject(); $('body').off();//(previewEventType, 'a[href*="member_illust.php?mode=medium&illust_id="]', discoveryUsersPreview); setPreviewEventListeners(); setTabSwitchingListenerW_U(); }); } //--------------------------------DISCOVERY[Artworks]------------------------------ //1 if (PAGETYPE === 1) //Works { setTabSwitchingListenerW_U(); setPreviewEventListeners(); } //----------------------------------DISCOVERY[Users]------------------------------- //13 else if (PAGETYPE === 13) //Users { setTabSwitchingListenerU_W(); setDiscoveryUsersPreviewEventListeners(); } //----------------------------NEW ILLUSTRATIONS AND SEARCH------------------------- //0,8 else if (PAGETYPE === 0) { setPreviewEventListeners(); } //--------------------ARTIST WORKS, "TOP" PAGES, Someone's Bookmarks--------------- //2,3,7,12 else if (PAGETYPE === 2 || PAGETYPE === 3 || PAGETYPE === 7 || PAGETYPE === 12 || PAGETYPE === 8) //TODO!!! do smthng with that amorphous pixiv styleshit! { /* $('body').on(previewEventType, 'a[href*="member_illust.php?mode=medium&illust_id="] > div:only-child', function() { //single art hover------------------------------------------------------------- if (this.firstChild.childNodes.length===1) //single { //console.log('single'); bookmarkObj = this.parentNode.parentNode.childNodes[1].childNodes[0].childNodes[0]; //checkBookmark_NewLayout(this); setHover(this.childNodes[1]); } //manga-style arts hover------------------------------------------------------- else { //console.log('manga'); bookmarkObj = this.parentNode.parentNode.childNodes[1].childNodes[0].childNodes[0]; //checkBookmark_NewLayout(this); setMangaHover(this.childNodes[1], this.firstChild.childNodes[1].textContent); } }); */ $('body').on(previewEventType, 'a[href*="/artworks/"] > div:nth-child(2) ', function(e) { e.preventDefault(); //single art hover------------------------------------------------------------- if (this.parentNode.firstChild.childNodes.length===1) //single { bookmarkObj = this.parentNode.parentNode.childNodes[1].childNodes[0].childNodes[0]; setHover(this.childNodes[0]); //todo? - zero child-node trying? } //manga-style arts hover------------------------------------------------------- else { bookmarkObj = this.parentNode.parentNode.childNodes[1].childNodes[0].childNodes[0]; setMangaHover(this.childNodes[0], this.parentNode.firstChild.childNodes[1].textContent); } }); } //----------------------DAILY RANKINGS & BOOKMARK INFORMATION PAGES---------------- //4,6 [10] else if (PAGETYPE === 4 || PAGETYPE === 6) { $('body').on(previewEventType, 'a[href*="/artworks/"]', function(e) //direct div selector works badly with "::before" { e.preventDefault(); if (this.childNodes.length == 1 && this.childNodes[0].nodeName=="DIV") //single art { bookmarkObj = $(this.firstChild.firstChild).parent().children("._one-click-bookmark"); checkBookmark(this); setHover(this.firstChild.firstChild); } else if (this.children[1] && this.children[1].className == 'page-count') //manga { bookmarkObj = $(this.firstChild.firstChild).parent().children("._one-click-bookmark"); checkBookmark(this); setMangaHover(this.firstChild.firstChild, this.children[1].children[1].textContent); } }); } //--------------------------------------BOOKMARKS---------------------------------- //9 else if (PAGETYPE === 9) { $('body').on(previewEventType, 'a[href*="member_illust.php?mode=medium&illust_id="]', function(e) //direct div selector works badly with "::before" { e.preventDefault(); if (this.childNodes.length == 1 && this.childNodes[0].nodeName=="DIV") //single art { bookmarkObj = $(this.firstChild.firstChild).parent().children("._one-click-bookmark"); checkBookmark(this); setHover(this.firstChild.firstChild); } else if (this.children[1] && this.children[1].className == 'page-count') //manga { bookmarkObj = $(this.firstChild.firstChild).parent().children("._one-click-bookmark"); checkBookmark(this); setMangaHover(this.firstChild.firstChild, this.children[1].children[1].textContent); } }); } //------------------------------------Feed('stacc')-------------------------------- //11 else if (PAGETYPE === 11) { $('body').on(previewEventType, 'a[href*="member_illust.php?mode=medium&illust_id="]', function(e) { e.preventDefault(); if (this.childNodes.length == 1 && this.childNodes[0].nodeName=="DIV") { if ($(this).hasClass('multiple')) //manga { let link = 'https://www.pixiv.net/ajax/illust/' + this.href.split('&')[1].split('=')[1]; let that = this; let xhr = new XMLHttpRequest(); xhr.responseType = 'json'; xhr.open("GET", link, true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { let count = this.response.body.pageCount; setMangaHover(that.firstChild.firstChild, count); if (!(that.parentNode.parentNode.parentNode.parentNode.getElementsByClassName('imageCount').length>0)) //todo?.. { let s = document.createElement('span'); s.className = 'imageCount'; s.style = 'position:relative; display: inline-block; float: right; top:-240px;' s.textContent = count; that.parentNode.parentNode.parentNode.parentNode.appendChild(s); } } }; xhr.send(); } else setHover(this.firstChild.firstChild); //single art } }); } //--------------------------------------------------------------------------------- if (DELAY_BEFORE_PREVIEW>0) $('body').on('mouseleave', 'a[href*="member_illust.php?mode=medium&illust_id="]', function() { clearTimeout(timerId); }); }); //=================================================================================== //----------------------------------------------------------------------------------- function checkDelay(callback) { if (DELAY_BEFORE_PREVIEW>0){ clearTimeout(timerId); timerId = setTimeout(callback, DELAY_BEFORE_PREVIEW); } else callback(); } //----------------------------------------------------------------------------------- function setHover(thisObj, top) { clearTimeout(timerId); clearTimeout(tInt); mangaOuterContainer.style.visibility = 'hidden'; hoverImg.src = parseImgUrl(thisObj, PREVIEW_SIZE); imgContainer.style.top = top || getOffsetRect(thisObj.parentNode.parentNode).top+'px'; //adjusting preview position considering expected image width //--------------------------------------------------------------------------------- let l = (![2,3,7,12,13].includes(PAGETYPE)) //more accurate on discovery users ?getOffsetRect(thisObj.parentNode.parentNode).left :getOffsetRect(thisObj).left; let dcw = document.body.clientWidth; let previewWidth = 0; if (hoverImg.naturalWidth>0){ //cached (previously viewed) adjustSinglePreview(dcw, l, hoverImg.naturalWidth); } else{ if (![2,3,7,12,13].includes(PAGETYPE)){ let previewWidth = PREVIEW_SIZE*(((PAGETYPE==6)?thisObj.clientWidth:thisObj.parentNode.parentNode.clientWidth)/siteImgMaxWidth)+5; //if not on NEW layout - width is predictable adjustSinglePreview(dcw, l, previewWidth); } else{ if (dcw - l - PREVIEW_SIZE - 5 > 0){ //if it is obvious that preview will fit on the screen then there is no need in setInterval(trying to use as minimun setInterval`s as possible) imgContainer.style.left = l+'px'; checkDelay(function(){imgContainer.style.display='block';}); }else{ //when on NEW layout - need to wait until image width is received let tLimit; tInt = setInterval(function(){ if (hoverImg.naturalWidth>0){ clearTimeout(tInt); //elementMouseIsOver = document.elementFromPoint(x, y); if (thisObj != elementMouseIsOver) break; adjustSinglePreview(dcw, l, hoverImg.naturalWidth); //position mismatching due to old `thisObj` => clearing in hoverImg.mouseleave ++tLimit; } if (tLimit*20>3000){ //in case of loading errors clearTimeout(tInt); hoverImg.src=''; console.error('setInterval error'); } }, 20); } } } //--------------------------------------------------------------------------------- if (isBookmarked) $(imgContainer).css("background", "rgb(255, 64, 96)"); else $(imgContainer).css("background", "rgb(34, 34, 34)"); } //----------------------------------------------------------------------------------- function adjustSinglePreview(dcw, l, contentWidth) { let d = dcw - l - contentWidth - 5; //5 - padding - todo... imgContainer.style.left = (d>=0)?l+'px':l+d+'px'; checkDelay(function(){imgContainer.style.display='block';}); } //----------------------------------------------------------------------------------- function setMangaHover(thisObj, count) { clearTimeout(timerId); clearTimeout(tInt); imgContainer.style.display='none'; //just in case mangaOuterContainer.style.top = getOffsetRect(thisObj.parentNode.parentNode).top+'px'; if (!ACCURATE_MANGA_PREVIEW) mangaOuterContainer.style.left = '30px'; if (isBookmarked) $(mangaOuterContainer).css("background", "rgb(255, 64, 96)"); else $(mangaOuterContainer).css("background", "rgb(34, 34, 34)"); imgsArrInit(parseImgUrl(thisObj, PREVIEW_SIZE), +count); } //----------------------------------------------------------------------------------- function imgsArrInit(primaryLink, l) { let currentImgId = getImgId(primaryLink); //console.log('lastImgId: ' + lastImgId); //console.log('currentImgId: ' + currentImgId); //--------------------------------------------------------------------------------- if (currentImgId != lastImgId) { for(let j=0; j<imgsArr.length; j++) { imgsArr[j].src = ''; } lastImgId = currentImgId; for(let i=0; i<l; i++) { if (!(!!imgsArr[i])) //if [i] img element doesn't exist { imgsArr[i] = document.createElement('img'); mangaContainer.appendChild(imgsArr[i]); } imgsArr[i].src = primaryLink.replace('p0','p'+i); } if (ACCURATE_MANGA_PREVIEW == true || DELAY_BEFORE_PREVIEW > 1000) //more accurate frame adjusting { setTimeout(function() { adjustMargins(mangaOuterContainer.scrollWidth); checkDelay(function(){mangaOuterContainer.style.visibility='visible';}); }, 1000); } else //some blind frame adjusting { adjustMargins(l*PREVIEW_SIZE); checkDelay(function(){mangaOuterContainer.style.visibility='visible';}); } } //--------------------------------------------------------------------------------- else { adjustMargins(mangaOuterContainer.scrollWidth); checkDelay(function(){mangaOuterContainer.style.visibility='visible';}); } } //----------------------------------------------------------------------------------- function parseImgUrl(thisObj, PREVIEW_SIZE) { let url = (thisObj.src)? thisObj.src: thisObj.style.backgroundImage.slice(5,-2); url = url.replace(/\/...x..[0|8]/, '/'+PREVIEW_SIZE+'x'+PREVIEW_SIZE).replace('_80_a2','').replace('_square1200','_master1200').replace('_70',''); return url; } //----------------------------------------------------------------------------------- function adjustMargins(width) { let margins = document.body.clientWidth - width; if (margins > 0) mangaOuterContainer.style.left = margins/2-10+'px'; else mangaOuterContainer.style.left = '30px'; } //----------------------------------------------------------------------------------- function checkBookmark(thisObj) //let this be until it works { isBookmarked = ($(bookmarkObj).hasClass("on")); } //----------------------------------------------------------------------------------- function getImgId(str) { return str.substring(str.lastIndexOf("/")+1, str.indexOf("_")); } //----------------------------------------------------------------------------------- function getOffsetRect(elem) { // (1) let box = elem.getBoundingClientRect(); // (2) let body = document.body; let docElem = document.documentElement; // (3) let scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop; let scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft; // (4) let clientTop = docElem.clientTop || body.clientTop || 0; let clientLeft = docElem.clientLeft || body.clientLeft || 0; // (5) let top = box.top + scrollTop - clientTop; let left = box.left + scrollLeft - clientLeft; return { top: Math.round(top), left: Math.round(left) }; } //=================================================================================== //**************************************Hide***************************************** //=================================================================================== imgContainer.onmouseleave = function() { imgContainer.style.display='none'; hoverImg.src=''; clearTimeout(timerId); clearTimeout(tInt); }; //----------------------------------------------------------------------------------- mangaOuterContainer.onmouseleave = function() { mangaOuterContainer.style.visibility='hidden'; clearTimeout(timerId); }; //=================================================================================== //***********************************Art Clicks************************************** //=================================================================================== //-----------------------------Single arts onclick actions--------------------------- hoverImg.onmouseup = function (event) { onClickActions(this, event, false); }; //-----------------------------Manga arts onclick actions---------------------------- $('body').on('mouseup', 'div#mangaContainer > img', function(event) { onClickActions(this, event, true); }); //---------------------------------onClickActions------------------------------------ async function onClickActions(imgContainerObj, event, isManga) { event.preventDefault(); let illustId = getImgId(imgContainerObj.src); //----------------------------Middle Mouse Button click---------------------------- if (event.button == 1) { let illustPageUrl = 'https://www.pixiv.net/artworks/' + illustId; window.open(illustPageUrl,'_blank'); //open illust page in new tab(in background — with FF pref "browser.tabs.loadDivertedInBackground" set to "true") } //----------------------------Left Mouse Button clicks...-------------------------- else if (event.button == 0) { //----------------------------Single LMB-click----------------------------------- if (!event.altKey) { let toSave = event.ctrlKey;// Ctrl + LMB-click -> saving image let pageNum = 0; //Single (general url) let ajaxIllustUrl = 'https://www.pixiv.net/ajax/illust/' + illustId; //Manga if (isManga) { let src = imgContainerObj.src; pageNum = src.substring(src.indexOf("_")+2, src.lastIndexOf("_")); } getOriginalUrl(ajaxIllustUrl, event, pageNum, toSave); } //-----------------------------Alt + LMB-click----------------------------------- else if (event.altKey) { $(bookmarkObj).click(); if (!isManga) $(imgContainerObj).parent().css("background", "rgb(255, 64, 96)"); else $(mangaOuterContainer).css("background", "rgb(255, 64, 96)"); } //------------------------------------------------------------------------------- } //--------------------------------------------------------------------------------- } //---------------------------------getOriginalUrl------------------------------------ async function getOriginalUrl(illustPageUrl, event, pageNum, toSave) { let xhr = new XMLHttpRequest(); xhr.responseType = 'json'; xhr.open("GET", illustPageUrl, true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { let originalArtUrl = this.response.body.urls.original; if (pageNum>0) originalArtUrl = originalArtUrl.replace('p0','p'+pageNum); if (toSave) saveImgByUrl(originalArtUrl); else window.open(originalArtUrl, '_blank'); } }; xhr.send(); } //----------------------------------------------------------------------------------- async function saveImgByUrl(sourceUrl) { const filename = sourceUrl.split('/').pop(); const illustId = filename.split('_')[0]; const ext = filename.split('.').pop().toLowerCase(); const GMR = (typeof(GM_xmlhttpRequest)==='function')?GM_xmlhttpRequest:GM.xmlHttpRequest; //Thanx to FlandreKawaii(c) GMR({ method: 'GET', url: sourceUrl, responseType: 'arraybuffer', //TM binary: true, //GM headers: { Referer: `https://www.pixiv.net/en/artworks/${illustId}`, }, onload: function(response) { if (ext === 'jpg' || ext === 'jpeg') saveAs(new File([response.response], filename, { type: 'image/jpeg' })); else if (ext === 'png') saveAs(new File([response.response], filename, { type: 'image/png' })); } }); } //=================================================================================== //**************************************Other**************************************** //=================================================================================== mangaContainer.onwheel = function(e) { if (e.deltaY<0 && (mangaOuterContainer.getBoundingClientRect().top < 0)) { mangaOuterContainer.scrollIntoView({block: "start", behavior: "smooth"}); //aligning to top screen side on scrollUp if needed } else if (e.deltaY>0 && (mangaOuterContainer.getBoundingClientRect().bottom > document.documentElement.clientHeight)) { mangaOuterContainer.scrollIntoView({block: "end", behavior: "smooth"}); //aligning to bottom screen side on scrollDown if needed } let scrlLft = mangaContainer.scrollLeft; if ((DISABLE_MANGA_PREVIEW_SCROLLLING_PROPAGATION) || ((scrlLft>0 && e.deltaY<0) || ((scrlLft<(mangaContainer.scrollWidth-mangaContainer.clientWidth)) && e.deltaY>0))) { e.preventDefault(); mangaContainer.scrollLeft += e.deltaY*DELTASCALE; } }; //----------------------------------------------------------------------------------- if (SCROLL_INTO_VIEW_FOR_SINGLE_IMAGE) imgContainer.onwheel = function(e) { if (DISABLE_SINGLE_PREVIEW_BACKGROUND_SCROLLING) e.preventDefault(); if (e.deltaY<0 && (imgContainer.getBoundingClientRect().top < 0)) { imgContainer.scrollIntoView({block: "start", behavior: "smooth"}); //aligning to top screen side on scrollUp if needed } else if (e.deltaY>0 && (imgContainer.getBoundingClientRect().bottom > document.documentElement.clientHeight)) { imgContainer.scrollIntoView({block: "end", behavior: "smooth"}); //aligning to bottom screen side on scrollDown if needed } }; //----------------------------------------------------------------------------------- window.onresize = function() { mangaWidth = document.body.clientWidth - 80; mangaContainer.style.maxWidth = mangaOuterContainer.style.maxWidth = mangaWidth+'px'; resetPreviewSize(); }; //----------------------------------------------------------------------------------- document.onkeyup = function(e) //Enlarge with shift { //console.log(e.keyCode); if (e.keyCode == 16 && hoverImg.src && PREVIEW_SIZE<1200) { let l = getOffsetRect(imgContainer).left; let w = hoverImg.naturalWidth*2+5; hoverImg.src = hoverImg.src.replace(/\/...x..[0|8]/, '/1200x1200'); imgContainer.style.left = (document.body.clientWidth-l < w)? document.body.clientWidth-w +'px': l +'px'; } } //=================================================================================== //*********************************************************************************** //=================================================================================== }); }) (); //function