Pixiv View Util

change the layout of caption. open comments automatically. add popup tool.

目前為 2018-08-19 提交的版本,檢視 最新版本

// ==UserScript==
// @name            Pixiv View Util
// @namespace       Pixiv View Util
// @description     change the layout of caption. open comments automatically. add popup tool.
// @author          sotoba
// @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/bookmark_add.php?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*
// @version         0.0.2.20180819
// @homepageURL     https://github.com/SotobatoNihu/PixivViewUtil
// @license         MIT License
// @require         https://code.jquery.com/jquery-3.2.1.min.js
// @grant           GM.getValue
// @grant           GM.setValue
// ==/UserScript==

/*
this script is based on "Pixiv Arts Preview & Followed Atrists Coloring"(MIT license).
for more details,please visit folows:
https://gf.qytechs.cn/ja/scripts/39387-pixiv-arts-preview-followed-atrists-coloring
https://github.com/NightLancer/PixivPreview
*/

(function () {
    'use strict';
    /*
    設定画面やsave & load 機能を実装予定
     */
    async function init() {
        const default_setting = {
            changeLayout: true,
            openComment: true,
            popup: true
        }
        return await GM.getValue("pixiv_viewutil_setting", JSON.stringify(default_setting));
    }

    const prop = {
        changeLayout: Symbol("changeLayout"),
        popup_typeA: Symbol("popup_typeA"),
        popup_typeB: Symbol("popup_typeB"),
        openComment: Symbol("openComment")
    }


    if (window.top == window.self && window.jQuery) {
        jQuery(function ($) {
            class Setting {
                constructor(jsonString) {
                    this.changeLayout = true;
                    this.openComment = true;
                    this.popup = true;
                }

                set setData(value) {
                    /*
                    const jsonData = JSON.parse(value);
                    this.isChangeLayout = (jsonData.this.isChangeLayout == null) ? true : jsonData.this.isChangeLayout;
                    this.isOpenComment = (jsonData.this.isOpenComment == null) ? true : jsonData.this.isOpenComment;
                    */
                    this.changeLayout = true;
                    this.openComment = true;
                    this.popup = true;
                }
            }

            const pagetype = {
                top: Symbol("top"),                    // my top page
                bookmark_new_illust: Symbol("bookmark_new_illust"), //Works from favourite artists
                discovery: Symbol("discovery"),              //Discovery page
                member_illust: Symbol("member_illust"),          //Artist works page
                member: Symbol("member"),                //Artist "top" page
                bookmark_detail: Symbol("bookmark_detail"),         //Bookmark information
                bookmark_add: Symbol("bookmark_add"),           //Added new bookmarks
                ranking: Symbol("ranking"),                 //Daily rankings
                bookmark_id: Symbol("bookmark_id"),             //Someone's bookmarks page
                search: Symbol("search"),                 //Search page
                bookmark: Symbol("bookmark"),                //Your bookmarks page
                other: Symbol("other"),
            };


            //   function checkPageType(url) {
            const checkPageType = (url) => {
                if (url.match('https://www.pixiv.net/bookmark_new_illust.php?')) return pagetype.bookmark_new_illust;
                if (url.match('https://www.pixiv.net/discovery?')) return pagetype.discovery;
                if (url.match('https://www.pixiv.net/member_illust.php?')) return pagetype.member_illust;
                if (url.match('https://www.pixiv.net/member.php?')) return pagetype.member;
                if (url.match('https://www.pixiv.net/bookmark_detail.php?')) return pagetype.bookmark_detail;
                if (url.match('https://www.pixiv.net/bookmark_add.php?')) return pagetype.bookmark_add;
                if (url.match('https://www.pixiv.net/ranking.php?')) return pagetype.ranking;
                if (url.match(/https:\/\/www\.pixiv\.net\/bookmark\.php\?id/)) return pagetype.bookmark_id;
                if (url.match('https://www.pixiv.net/search.php')) return pagetype.search;
                if (url.match('https://www.pixiv.net/bookmark.php?')) return pagetype.bookmark;
                if (url.match('https://www.pixiv.net/')) return pagetype.top;
                else return pagetype.other;
            }

            const getAllowedFuncList = (type) => {
                switch (type) {
                    case pagetype.top:
                        return [prop.popup_typeB];
                        break;
                    case pagetype.bookmark_new_illust:
                        return [prop.popup_typeA];
                        break;
                    case pagetype.discovery:
                        return [prop.popup_typeA];
                        break;
                    case pagetype.member_illust:
                        return [prop.popup_typeB, prop.changeLayout, prop.openComment];
                        break;
                    case pagetype.member:
                        return [prop.popup_typeB];
                        break;
                    case pagetype.bookmark_detail:
                        return [prop.popup_typeB];
                        break;
                    case pagetype.bookmark_add:
                        return [prop.popup_typeB];
                        break;
                    case pagetype.bookmark_id:
                        return [prop.popup_typeB];
                        break;
                    case pagetype.search:
                        return [prop.popup_typeA];
                        break;
                    case pagetype.ranking:
                        return [prop.popup_typeB];
                        break;
                    case pagetype.bookmark:
                        return [prop.popup_typeB];
                        break;
                        return [];
                }
            }

            class Util {
                changeLayout() {
                    const figure = document.getElementsByTagName("figure");
                    $('figure').before($('figcaption'));
                }

                openComment() {
                    let elem = $("article");
                    elem.find("[aria-expanded='false']").click();
                    var observer = new MutationObserver(function (MutationRecords, MutationObserver) {
                        elem.find("[aria-expanded='false']").click();
                    });
                    observer.observe(document, {
                        childList: true,
                        subtree: true,
                    });
                }

                setPopup(type) {
                    let imgContainer = document.createElement('div');
                    imgContainer.id = 'imgContainer';
                    imgContainer.style = 'position:absolute; display:block; z-index:1000; background:#222; padding:5px; margin:-5px;';

                    let popupImg = document.createElement('img');
                    popupImg.id = 'popupImg';
                    imgContainer.appendChild(popupImg);
                    let captionContainer = document.createElement('div');
                    captionContainer.id = 'captionContainer';

                    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; z-index:1000; padding:5px; background:#111; maxWidth:1200px; marginY:-5px; marginX: auto;';
                    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,
                        Checked = false,
                        artsContainers,
                        artsLoaded = 0,
                        hits = 0,
                        isRunning = false,
                        lastImgId = " ",
                        siteImgMaxWidth = 150, //for now it is used for pagetype==7
                        mangaWidth = 1200,
                        bookmarkObj,
                        DELTASCALE = ('mozInnerScreenX' in window) ? 70 : 4,
                        PAGETYPE = checkPageType(document.URL);

                    mangaWidth = document.body.clientWidth - 80;
                    mangaContainer.style.maxWidth = mangaOuterContainer.style.maxWidth = mangaWidth + 'px';
                    document.body.appendChild(imgContainer);
                    document.body.appendChild(captionContainer);
                    document.body.appendChild(mangaOuterContainer);

                    function setCaption(imageID, x, y) {
                        if (imageID === undefined || imageID.length === 0) return; //just in case
                        const url = "https://www.pixiv.net/ajax/illust/" + imageID;
                        console.log("url:" + url);

                        fetch(url).then(function (response) {

                            return response.json();
                        }).then(function (json) {

                            //captionContainer.style="position: absolute; display: block; z-index: 1001; background: rgb(255, 255, 255);  top: 0px; left: 0px; color:black "
                            captionContainer.innerHTML = json.body.description;
                            x = x - parseInt($(captionContainer).height());
                            captionContainer.style = "position: absolute; display: block; z-index: 1001; background: rgb(255, 255, 255); padding: 5px; margin: -5px; top: " + x + "px; left: " + y + "px; color:black "
                        });
                    }

                    function hoverImg(thisObj) {
                        mangaOuterContainer.style.display = 'none';
                        popupImg.src = parseImgUrl(thisObj);
                        imgContainer.style.top = getOffsetRect(thisObj.parentNode.parentNode).top + 'px';
                        //adjusting preview position considering expected image width
                        let l = getOffsetRect(thisObj.parentNode.parentNode).left;
                        let w = 600 * (((PAGETYPE == pagetype.ranking) ? thisObj.clientWidth : thisObj.parentNode.parentNode.clientWidth) / siteImgMaxWidth) + 5;
                        imgContainer.style.left = (document.body.clientWidth - l < w) ? document.body.clientWidth - w + 'px' : l + 'px';

                        if ($(bookmarkObj).hasClass("on")) {
                            $(imgContainer).css("background", "rgb(255, 64, 96)");
                        }
                        else {
                            $(imgContainer).css("background", "rgb(34, 34, 34)");
                        }
                        imgContainer.style.display = 'block';
                    }

                    function hoverManga(thisObj, count) {

                        imgContainer.style.display = 'none'; //just in case
                        mangaOuterContainer.style.top = getOffsetRect(thisObj.parentNode.parentNode).top + 'px';
                        mangaOuterContainer.style.left = '30px';
                        if ($(bookmarkObj).hasClass("on")) {
                            $(mangaOuterContainer).css("background", "rgb(255, 64, 96)");
                        }
                        else {
                            $(mangaOuterContainer).css("background", "rgb(34, 34, 34)");
                        }
                        imgsArrInit(parseImgUrl(thisObj), +count);
                        //  const imageId = thisObj.getAttribute('data-id');
                        //   setCaption(imageId,parseInt($(mangaOuterContainer).css('top')),parseInt($(mangaOuterContainer).css('left')));
                    }

                    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)};
                    };

                    //-----------------------------------------------------------------------------------
                    function imgsArrInit(primaryLink, l) {
                        let margins = document.body.clientWidth - l * 600; //some blind frame adjusting
                        if (margins > 0) mangaOuterContainer.style.left = margins / 2 - 10 + 'px';

                        let currentImgId = getImgId(primaryLink);
                        //---------------------------------------------------------------------------------
                        if (currentImgId != lastImgId) {
                            for (let j = 0; j < imgsArr.length; j++) {
                                imgsArr[j].src = '';
                            }
                            mangaOuterContainer.style.display = 'block';
                            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);
                            }
                        }
                        //---------------------------------------------------------------------------------
                        else mangaOuterContainer.style.display = 'block';
                    };

                    //-----------------------------------------------------------------------------------
                    function parseImgUrl(thisObj) {
                        let url = (thisObj.src) ? thisObj.src : thisObj.style.backgroundImage.slice(5, -2); //pixiv changes layout randomly
                        url = url.replace(/\/...x...\//, '/600x600/'); //both feed and artist works case | TODO: '1200x1200' variant
                        return url;
                    };

                    //-----------------------------------------------------------------------------------
                    function getImgId(str) {
                        return str.substring(str.lastIndexOf("/") + 1, str.indexOf("_"));
                    }


                    //-----------------------------------------------------------------------------------
                    //**************************************Hide*****************************************
                    //-----------------------------------------------------------------------------------
                    imgContainer.onmouseleave = function () {

                            // if caption don't have URL, simply erase caption
                            if ($(captionContainer).find('a').length == 0) {
                                captionContainer.style.display = 'none';
                                imgContainer.style.display = 'none';
                                popupImg.src = '';
                            }else {
                                setTimeout(function(){
                                    captionContainer.style.display = 'none';
                                    imgContainer.style.display = 'none';
                                    popupImg.src = '';
                                },1000);
                            }

                    };
                    //-----------------------------------------------------------------------------------
                    mangaOuterContainer.onmouseleave = function () {

                        if ($(captionContainer).find('a').length == 0) {
                            captionContainer.style.display = 'none';
                            mangaOuterContainer.style.display = 'none';
                        }else{
                            setTimeout(function(){
                            captionContainer.style.display = 'none';
                            mangaOuterContainer.style.display = 'none';
                            },500);
                        }
                    };
                    captionContainer.onmouseleave = function () {
                        captionContainer.style.display = 'none';
                    };
                    //-----------------------------------------------------------------------------------
                    //*************************************Clicks****************************************
                    //-----------------------------------------------------------------------------------
                    popupImg.onmouseup = function (event) //single arts onclick actions
                    {
                        onClickActions(this, event, false);
                    };
                    //-----------------------------------------------------------------------------------
                    $('body').on('mouseup', 'div#mangaContainer > img', function (event) //manga arts onclick actions
                    {
                        onClickActions(this, event, true);
                    });

                    //-----------------------------------------------------------------------------------
                    async function onClickActions(imgContainerObj, event, isManga) {
                        event.preventDefault();
                        let strId = getImgId(imgContainerObj.src);
                        let illustPageUrl = 'https://www.pixiv.net/member_illust.php?mode=medium&illust_id=' + strId;

                        //----------------------------Middle Mouse Button click----------------------------
                        if (event.button == 1) {
                            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
                                if (!isManga) //single art original
                                {
                                    getOriginalUrl(illustPageUrl, event, false, toSave);
                                }
                                else //manga art original
                                {
                                    let src = imgContainerObj.src;
                                    let pageNum = src.substring(src.indexOf("_") + 2, src.lastIndexOf("_"));
                                    let singleIllustPageUrl = 'https://www.pixiv.net/member_illust.php?mode=manga_big&illust_id=' + strId + '&page=' + pageNum;
                                    getOriginalUrl(singleIllustPageUrl, event, true, 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)");
                            }
                            //-------------------------------------------------------------------------------
                        }
                        //---------------------------------------------------------------------------------
                    };

                    //-----------------------------------------------------------------------------------
                    async function getOriginalUrl(illustPageUrl, event, isManga, toSave) {
                        let xhr = new XMLHttpRequest();
                        xhr.open("GET", illustPageUrl, true);
                        xhr.onreadystatechange = function () {
                            if (xhr.readyState == 4 && xhr.status == 200) {
                                let originalArtUrl = "";
                                if (!isManga) {
                                    let scripts = xhr.responseXML.head.getElementsByTagName("script");
                                    for (let i = 0; i < scripts.length; i++) {
                                        let originalURL = scripts[i].textContent.match(/"original":"(http[^"]+)"/); //thanks to Mango
                                        if (originalURL) originalArtUrl = originalURL[1].replace(/\\\//g, '/');
                                    }
                                }
                                else {
                                    originalArtUrl = xhr.responseXML.querySelectorAll('img')[0].src;
                                }
                                if (toSave) window.open(originalArtUrl + '?s=1', '_blank');
                                else window.open(originalArtUrl, '_blank');
                            }
                        };
                        xhr.responseType = "document";
                        xhr.send();
                    }

                    //-----------------------------------------------------------------------------------
                    //**************************************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 ((scrlLft > 0 && e.deltaY < 0) || ((scrlLft < (mangaContainer.scrollWidth - mangaContainer.clientWidth)) && e.deltaY > 0)) {
                            e.preventDefault();
                            mangaContainer.scrollLeft += e.deltaY * DELTASCALE; // TODO - find better value for opera/chrome
                        }
                    };
                    //-----------------------------------------------------------------------------------
                    window.onresize = function () {
                        mangaWidth = document.body.clientWidth - 80;
                        mangaContainer.style.maxWidth = mangaOuterContainer.style.maxWidth = mangaWidth + 'px';
                    };

                    //-----------------------------------------------------------------------------------
                    //**************************************Hover****************************************
                    //-----------------------------------------------------------------------------------
                    //feed, discovery and search-------------------------------------------------------
                    if (type === prop.popup_typeA) {
                        //single art hover
                        siteImgMaxWidth = 200;
                        $('body').on('mouseenter', 'a[href*="member_illust.php?mode=medium&illust_id="] > div:only-child', function () {
                            bookmarkObj = $(this).parent().parent().children(".thumbnail-menu").children("._one-click-bookmark");
                            var elem = this;
                            $(this).attr('onclick', 'console.log();return false;');
                            $(this).on('click', function () {
                                hoverImg(elem);
                                var x = parseInt($(imgContainer).css('top'));
                                var y = parseInt($(imgContainer).css('left'));
                                const href = $(this).parent().parent().find('a').attr('href');
                                const imageID = href.substring(href.indexOf('illust_id=') + 'illust_id='.length);
                                console.log("id:" + imageID)
                                setCaption(imageID, x, y);
                            });
                        });

                        //manga-style arts hover
                        $('body').on('mouseenter', 'a[href*="member_illust.php?mode=medium&illust_id="] > div:nth-child(2) ', function () {
                            bookmarkObj = $(this).parent().parent().children(".thumbnail-menu").children("._one-click-bookmark");
                            if (this.parentNode.firstChild.childNodes.length) {
                                $(this).attr('onclick', 'console.log();return false;');
                                var elem1 = this;
                                var elem2 = this.parentNode.firstChild.firstChild.textContent;
                                $(this).on('click', function () {
                                    hoverManga(elem1, elem2);
                                    var x = parseInt($(mangaOuterContainer).css('top'));
                                    var y = parseInt($(mangaOuterContainer).css('left'));
                                    const href = $(this).parent().parent().find('a').attr('href');
                                    const imageID = href.slice(href.substring('illust_id=') + 'illust_id='.length);
                                    console.log("id:" + imageID)
                                    setCaption(imageID, x, y);
                                });
                            }
                        });

                        //clearing loaded arts count when switching on tabs
                        if (PAGETYPE === pagetype.discovery) $('body').on('mouseup', 'a[href="/discovery/users"]', function () //todo:make into single event handler
                        {
                            console.log('leaving works page...');
                            artsLoaded = hits = 0;
                        });
                    }
                    //artist works page and daily rankings & rest of pages-----------------------------
                    if (type === prop.popup_typeB) {
                        {
                            $('body').on('mouseenter', 'a[href*="member_illust.php?mode=medium&illust_id="]', function () //direct div selector works badly with "::before"
                            {
                                if (this.childNodes.length == 1 && this.childNodes[0].nodeName == "DIV") //single art
                                {
                                    bookmarkObj = $(this.firstChild.firstChild).parent().children("._one-click-bookmark");
                                    //setHover(this.firstChild.firstChild);
                                    var elem = this.firstChild.firstChild;
                                    var parent = this;
                                    $(this).attr('onclick', 'console.log();return false;');
                                    $(captionContainer).insertBefore(imgContainer);
                                    $(this).on('click', function () {
                                        hoverImg(elem);
                                        var x = parseInt($(imgContainer).css('top'));
                                        var y = parseInt($(imgContainer).css('left'));
                                        const imageId = elem.getAttribute('data-id');
                                        setCaption(imageId, x, y);
                                    });
                                }
                                else if (this.children[1] && this.children[1].className == 'page-count') //manga
                                {
                                    bookmarkObj = $(this.firstChild.firstChild).parent().children("._one-click-bookmark");
                                    //setMangaHover(this.firstChild.firstChild, this.children[1].children[1].textContent);
                                    var elem1 = this.firstChild.firstChild;
                                    var elem2 = this.children[1].children[1].textContent;
                                    $(this).attr('onclick', 'console.log();return false;');

                                    $(this).on('click', function () {
                                        hoverManga(elem1, elem2);
                                        var x = parseInt($(mangaOuterContainer).css('top'));
                                        var y = parseInt($(mangaOuterContainer).css('left'));
                                        const imageId = elem1.getAttribute('data-id');
                                        setCaption(imageId, x, y);
                                    });
                                }
                                ;
                            });
                        }
                    }
                }
            }

            class Page {
                constructor(url) {
                    this.URL = url;
                    this.pagetype = checkPageType(url);
                    this.alloedFunclist = getAllowedFuncList(this.pagetype);
                }

                set setURL(url) {
                    this.URL = url;
                    this.pagetype = checkPageType(url);
                }

                get getPagetype() {
                    return this.pagetype;
                }

                get getURL() {
                    return this.URL;
                }

                get getFunclist() {
                    return this.alloedFunclist;
                }

                isEnable(symbol) {
                    return this.alloedFunclist.includes(symbol);
                }
            }

            /*
            * main function
            */
            let page = new Page(document.URL);
            let util = new Util();

            let setting;
            init().then(result => setting = new Setting(result));

            $(document).ready(function(){

                if (setting.popup && page.isEnable(prop.popup_typeA)) {
                    util.setPopup(prop.popup_typeA);
                    console.log("popup  A is enable");
                } else if (setting.popup && page.isEnable(prop.popup_typeB)) {
                    util.setPopup(prop.popup_typeB);
                    console.log("popup  B is enable");
                }
            });

            window.onload = function () {
                if (setting.openComment && page.isEnable(prop.openComment)) {
                    util.openComment();
                    console.log("comment opend");
                }
                console.log("pagetype:" + page.pagetype.toString());
                if (setting.changeLayout && page.isEnable(prop.changeLayout)) {
                    util.changeLayout();
                    console.log("layout chainged");
                }
            };
        })
    }
})();

QingJ © 2025

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