Dev_Multi_Open2

Easier Opening in Deviantart Notification Center

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Dev_Multi_Open2
// @namespace    http://phi.pf-control.de/apps/userscripts
// @version      1.5
// @description  Easier Opening in Deviantart Notification Center
// @author       Dediggefedde
// @match        https://www.deviantart.com/notifications/*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require      http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js
// @grant        GM.xmlHttpRequest
// ==/UserScript==
/* globals $*/
// make IDE recognise $ from jquery

(function() {
    "use strict";

    //class names, may need updates when template changes
    let selectees = "" + //things that drag can select
        "section._3fxzN._3q1dq ," + //watch thumbs
        "div._1AEwc"; //notification entries
    let topBar = "div._1MpFZ._3DiMC._65VTy._2Jm2O > div._1MpFZ._3DiMC";
    let selectedEl = "._1S1FT"; //selected entries
    let viewAllBut = "a._25AwR._2gSAL._2aAuV._31fTQ._1dZ7P"; //._3xVcb._1KcL_._21wpm"; //identify view-all-button
    let stackCount = "div.paUD_._1dZ7P"; //display of "# deviations" in a stack

    let selfTrg = false; //prevent self triggering
    let selContainer; //selection container

    //load JqueryUI style and
    //activate dynamic insertion (check every second for GUI changes)
    function prepareSite() {
        let scr = $("<link rel=\"stylesheet\" href=\"//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css\">");
        scr.appendTo(document.head);
        let sty = $("<style type=\"text/css\"></style>");
        sty.append(".ui-selectable-helper{background-image:linear-gradient(rgb(0,255,0),rgb(0,100,0));opacity:0.3;}");
        sty.append(".dmo_openAll{top: 68%;}");
        sty.appendTo(document.head);

        scr.ready(function() {
            setInterval(dynamicInsertion, 1000);
        });
    }

    //selection/unselection event
    function selecting(ev, ui) {
        let target = $(ui.selecting);
        let select = true; //selecting or unselecting
        if (target.length == 0) {
            target = $(ui.unselecting);
            select = false;
        }

        selfTrg = true; //self trigger prevention by click on checkmark
        let el = target.find("input[type=checkbox]").parent(); //checkmark event trigger source: Container
        if (el.length > 0) { //trigger event wrong
            if (!select) {
                el.click(); //unselecting DOM (this causes self-trigger)
                if (ev == 0) {
                    target.removeClass("ui-selected").addClass("ui-unselecting");
                }
            } else {
                el.click(); //selecting DOM
                if (ev == 0) {
                    target.addClass("ui-selecting");
                }
            }
        }
        selfTrg = false;
    }

    //insert Open all Button
    function insertOpenButton() {
        if ($("button.dmo2_openTab").length > 0) {
            return; //identify top bar
        }
        //copy last button and change itto "open in tab"
        let btnOpen = $(topBar).find("button").last().clone().html("Open in new Tab").css("margin", "0px 15px").appendTo(topBar);
        $("button.eNvqE").addClass("_1BMgL"); //add space between buttons
        btnOpen.click(function() { //use url for normal case and script openALL button for stacks
            let els = $(selectees).filter(selectedEl);
            if (els.length > 5) {
                if (!confirm(`You are about to open ${els.length} Tabs. Proceed?`)) {
                    return;
                }
            }
            els.each(function() {
                if ($(this).attr("url") != undefined) {
                    window.open($(this).attr("url"));
                } else {
                    $(this).find("button.dmo_openAll").click();
                }
            });
        });
    }

    //recursive call to get all deviations in stack
    //results in open a new window for each element
    //called by "open all" button for stacks
    function getStackURLs(offset, userid, type) {
        return new Promise(function(resolve, reject) {
            GM.xmlHttpRequest({
                method: "GET",
                url: "https://www.deviantart.com/_napi/da-messagecentre/api/stack?stackId=uq:devwatch:tg%3D" + type + ",sender%3D" + userid + "&type=deviations&offset=" + offset + "&limit=24",
                onerror: function(response) {
                    reject(response);
                },
                onload: function(response) {
                    let resp = JSON.parse(response.responseText);
                    //response.results[0].deviation.url;

                    for (const el of resp.results) {
                        window.open(el.deviation.url);
                    }
                    if (resp.hasMore) {
                        resolve(getStackURLs(24, userid, type));
                    } else {
                        resolve(1);
                    }
                }
            });
        });
    }


    //called by "open all" button. calls getStackURL depending on type (deviation, polls etc.)
    function requestAllOpen(event) {
        event.preventDefault(); //prevent bubbling
        event.stopPropagation();

        let sender = $(event.target);
        let userid = sender.closest(selectees).find("a.user-link").attr("data-userid");

        switch (sender.attr("type")) {
            case "1":
                getStackURLs(0, userid, "deviations");
                getStackURLs(0, userid, "groupdeviations");
                break;
            case "2":
                getStackURLs(0, userid, "journals");
                break;
            case "3":
                getStackURLs(0, userid, "polls");
                break;
        }
    }

    //inserts button "Open all" calling requestAllOpen and sets its "type".
    function insertOpenAllButton() {
        let el = $("<button>Open All</button>").attr("dmo2_openAll", true).click(requestAllOpen);
        el.attr("class", $(viewAllBut).attr("class")).addClass("dmo_openAll");
        $(viewAllBut).parent().not("[dmo2_openAll]").attr("dmo2_openAll", true).append(el);

        $("button.dmo_openAll:not([type])").each(function() {
            let type = $(this).closest(selectees).find(stackCount).text();
            if (type.indexOf("Deviations") != -1) {
                $(this).attr("type", 1);
            } else if (type.indexOf("Journals") != -1) {
                $(this).attr("type", 2);
            } else if (type.indexOf("Polls") != -1) {
                $(this).attr("type", 3);
            } else {
                $(this).attr("type", 0);
            }
        })
    }

    //calls JQUERY UI to make things selectable
    //also inserts open-buttons
    function makeSelectable() {
        selContainer.selectable({ //jquery ui selectable
            filter: selectees,
            distance: 10, //allows clicking. deprecated, hopefully stays a while
            selecting: selecting, //during selection; select/unselect only regarding mark-area
            unselecting: selecting,
            cancel: "[contenteditable]"
        });

        $(selectees).find("label input").change(function(event) { //change selection by hand
            if (!selfTrg) {
                event.stopPropagation();
                let target = $(this).closest(selectees);
                let el = {};
                if (!target.hasClass(selectedEl)) {
                    el.unselecting = target;
                } else {
                    el.selecting = target;
                }
                selecting(0, el);
                selContainer.selectable("refresh");
                selContainer.data("ui-selectable")._mouseStop(null);
            }
        });
        insertOpenButton();
    }

    //called every second to check if UI has changed and selectable needs to be updated.
    //necessary for javascript navigation DA is using and endless pages.
    function dynamicInsertion() {
        $(selectees).not("[url]").each(function() { //href disappears for selected items. copy it beforehand
            $(this).attr("url", $(this).find("a[data-hook=\"deviation_link\"]").attr("href"));
        });
        insertOpenAllButton();
        if ($(selectees).not("[dmo2]").length == 0) {
            return; //only do if you have selectees without attribute
        }
        $(selectees).attr("dmo2", true);
        selContainer = $(selectees).parents("section").parent(); // $(selcont);
        makeSelectable();
    }

    //start call of script.
    prepareSite();
})();

/* deviantart API
https://www.deviantart.com/_napi/da-messagecentre/api/stack?stackId=uq:devwatch:tg%3Ddeviations,sender%3D4165994&type=deviations&offset=0&limit=24
stackId	uq:devwatch:tg=deviations,sender=4165994
type	deviations
offset	0
limit	24

response
response.results[0].deviation.url
response.counts.total
response.hasMore*/