Greasy Fork

Greasy Fork is available in English.

Pixiv View Util

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

目前为 2018-09-07 提交的版本。查看 最新版本

// ==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.3.20180908
// @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:
http://greasyfork.icu/ja/scripts/39387-pixiv-arts-preview-followed-atrists-coloring
https://github.com/NightLancer/PixivPreview
*/

(() => {
    'use strict';
    /*
    設定画面やsave & load 機能を実装予定
     */
    const init = async () => {
        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(($) => {
            class Setting {
                constructor(jsonString) {
                    const jsonData = JSON.parse(jsonString);
                    this.changeLayout = (jsonData.changeLayout == null) ? true : jsonData.changeLayout;
                    this.openComment = (jsonData.openComment == null) ? true : jsonData.openComment;
                    this.popup = (jsonData.popup == null) ? true : jsonData.popup;
                }

                set setData(jsonData) {
                    this.changeLayout = (jsonData.changeLayout == null) ? true : jsonData.changeLayout;
                    this.openComment = (jsonData.openComment == null) ? true : jsonData.openComment;
                    this.popup = (jsonData.popup == null) ? true : jsonData.popup;
                }
            }

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

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

                //-----------------------------------------------------------------------------------
                //**************************************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(() => {
                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 = () => {
                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");
                }
                $('a').on('click', () => {
                    let page = new Page(document.URL);
                    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.");
                    }
                })

            };
        })
    }
})();