dev_group_list2

Better Submit-to-group dialog

Από την 01/01/2020. Δείτε την τελευταία έκδοση.

// ==UserScript==
// @name         dev_group_list2
// @namespace    http://www.deviantart.com/
// @version      2.1
// @description  Better Submit-to-group dialog
// @author       Dediggefedde
// @match        https://www.deviantart.com/*
// @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
// @grant        GM.setValue
// @grant        GM.getValue

// ==/UserScript==
/* globals $*/

//dgl2=dgl2 identifiert/classname
(function () {
    'use strict';
    var userName = "";
    var moduleID = 0;
    var grPerReq = 24;
    var groups = [];
    var groupN = 0;
    var listedGroups = [];
    var devID;
    var collections = [{
        id: 0,
        name: "all",
        groups: []
    }];
    var collectionOrder = [];
    var colMode = 0; //0 show, 1 add, 2 remove, 3 delete
    var colModeTarget = 0;
    var lastFilter = 0;

    //API calls getting data
    function fillGroups(offset) { //async+callback //load all groups
        return new Promise(function (resolve, reject) {
            GM.xmlHttpRequest({
                method: "GET",
                url: "https://www.deviantart.com/_napi/da-user-profile/api/module/groups/members?username=" + userName + "&moduleid=" + moduleID + "&offset=" + offset + "&limit=" + grPerReq,
                onerror: function (response) {
                    reject(response);
                },
                onload: function (response) {
                    var resp = JSON.parse(response.responseText);


                    groups = groups.concat(resp.results);
                    groupN = resp.total;

                    var frac = 0;
                    if (groupN > 0) frac = groups.length / groupN * 100;
                    frac = Math.round(frac);

                    $("#dgl2_refresh rect").css("fill", "url(#dgl2_grad1)");
                    $("#dgl2_grad1_stop1").attr("offset", frac + "%");
                    $("#dgl2_grad1_stop2").attr("offset", (frac + 1) + "%");
                    $("#dgl2_refresh").attr("title", frac + "%");
                    $("span.dgl2_descr").text("Loading List of Groups... " + frac + "%");

                    if (resp.hasMore) {
                        resolve(fillGroups(resp.nextOffset));
                    } else {
                        GM.setValue("groups", JSON.stringify(groups));
                        insertGroups();
                        $("#dgl2_refresh rect").css("fill", "");
                        $("#dgl2_refresh").css("cursor", "pointer");

                        resolve(groups);
                    }
                }
            });
        });
    }

    function fillSubFolder(groupID, type) { //async+callback //type =[collection,gallery]
        return new Promise(function (resolve, reject) {
            GM.xmlHttpRequest({
                method: "GET",
                url: "https://www.deviantart.com/_napi/shared_api/deviation/group_folders?groupid=" + groupID + "&type=" + type,
                onerror: function (response) {
                    reject(response);
                },
                onload: async function (response) {
                    var resp;
                    try{
                        resp = JSON.parse(response.responseText);
                    }catch(ex){
                        alert("Page problems. Please try again later!\n"+ex);
                    }
                    insertSubFolders(resp.results);
                    resolve(resp.results);
                }
            });
        });
    }

    function fillModuleID() { //async+callback //get Module ID for group submission
        return new Promise(function (resolve, reject) {
            GM.xmlHttpRequest({
                method: "GET",
                url: "https://www.deviantart.com/" + userName + "/about",
                onerror: function (response) {
                    reject(response);
                },
                onload: async function (response) {
                    var resp = (response.responseText);
                    resp = resp.match(/{\\\"id\\\":(\d+),\\\"type\\\":\\\"group_list_members/i);
                    if (resp.length > 1) {
                        moduleID = resp[1];
                        resolve(moduleID);
                    } else {
                        reject(response);
                    }
                }
            });
        });
    }

    function fillListedGroups(devID) {
        return new Promise(function (resolve, reject) {
            GM.xmlHttpRequest({
                method: "GET",
                url: "https://www.deviantart.com/deviation/" + devID + "/groups",
                onerror: function (response) {
                    reject(response);
                },
                onload: async function (response) {
                    var tex = response.responseText;
                    var res = tex.matchAll(/gmi-groupid="(\d+)"/gi);

                    for (var entr of res) {
                        listedGroups.push(parseInt(entr[1]));
                    }
                    resolve(listedGroups);
                }
            });
        });

    }

    function requestAddSubmission(token, devID, folderID, groupID, type) { //async+callback //type =[collection,gallery]
        return new Promise(function (resolve, reject) {
            var dat = {
                "groupid": parseInt(groupID),
                "type": type.toString(),
                "folderid": parseInt(folderID),
                "deviationid": parseInt(devID),
                "csrf_token": token.toString()
            };
            GM.xmlHttpRequest({
                method: "POST",
                url: "https://www.deviantart.com/_napi/shared_api/deviation/group_add",
                headers: {
                    "accept": 'application/json, text/plain, */*',
                    "content-type": 'application/json;charset=UTF-8'
                },
                dataType: 'json',
                data: JSON.stringify(dat),
                onerror: function (response) {
                    reject(response);
                },
                onload: async function (response) {
                    var resp = JSON.parse(response.responseText);
                    resolve(resp);
                }
            });
        });
    }

    //event handlers
    function Ev_groupClick(event) { //event propagation
        event.stopPropagation();

        var targetBut = $(event.target).closest("button.dgl2_groupButton");
        var groupID = targetBut.attr("groupID");
        var elInd;

        switch (colMode) {
            case 1: //add
                elInd = collections[colModeTarget].groups.indexOf(groupID);
                if (elInd == -1) {
                    collections[colModeTarget].groups.push(groupID);
                    GM.setValue("collections", JSON.stringify(collections));
                    targetBut.addClass("dgl2_inCollection");
                }
                break;
            case 2: //remove
                elInd = collections[colModeTarget].groups.indexOf(groupID);
                if (elInd > -1) {
                    collections[colModeTarget].groups.splice(elInd, 1);
                    GM.setValue("collections", JSON.stringify(collections));
                }
                break;
            case 0:
            default:
                if (targetBut.attr("type") == "group") {
                    fillSubFolder(groupID, "gallery");
                    $("span.dgl2_titleText").text("< Add to " + targetBut.attr("groupName"));
                    $("span.dgl2_descr").text("Add this deviation to a group folder");

                    //Add this deviation to a group folder
                } else if (targetBut.attr("type") == "folder") {
                    var token = $("input[name=validate_token]").val();
                    requestAddSubmission(token, devID, targetBut.attr("folderID"), targetBut.attr("groupID"), targetBut.attr("folderType")).then(function (arg) {
                        if (arg.success == true) {
                            if (arg.needsVote) {
                                alert("Success! Submission pending group's vote");
                            } else {
                                alert("Success! Submission added to group");
                            }
                        } else {
                            alert("Error! Something went wrong:\n" + JSON.stringify(arg));
                        }
                        /*
                        deviationGroupCount: 1
                        needsVote: true
                        success: true
                        */
                    }).catch(function (arg) {
                        alert("Error! Something went wrong:\n" + JSON.stringify(arg));
                    });
                }
        }
    }

    function Ev_colListClick(event) {
        event.stopPropagation();

        var id = $(event.target).closest("li").first().attr("colID");
        if (id == undefined && $(event.target).closest("button").hasClass("dgl2_topBut")) id = 0;
        else if (id == undefined) return;
        var clasNam = $(event.target).closest("button").attr("class");
        var index = colIndexById(id);
        $("div.dgl2_groupWrapper").removeClass("dgl2_addGroup").removeClass("dgl2_remGroup");
        $("button.dgl2_inCollection").removeClass("dgl2_inCollection");
        var el;

        if (clasNam) clasNam = clasNam.replace(" dgl2_topBut", "");
        switch (clasNam) {
            case "dgl2_export":
                var obj = {
                    collections: collections,
                    collectionOrder: collectionOrder
                };
                var d = new Date();
                var dat = d.getFullYear() + ("0" + d.getMonth()).slice(-2) + ("0" + d.getDate()).slice(-2) + "-" + ("0" + d.getHours()).slice(-2) + ("0" + d.getMinutes()).slice(-2) + ("0" + d.getSeconds()).slice(-2);
                download(JSON.stringify(obj), "dev_group_list2_data_" + dat + ".txt");
                break;
            case "dgl2_import":
                upload().then(function (imp) {
                    try {
                        var obj = JSON.parse(imp);
                        if (obj.collections && obj.collectionOrder) {
                            collections = obj.collections;
                            collectionOrder = obj.collectionOrder;
                        } else if (obj[0] && obj[0][0].indexOf("_grouplist") != -1) { //v1 compatibility mode
                            collections = [{
                                id: 0,
                                name: "all",
                                groups: []
                            }];
                            var oldList = obj[1][1].split("\u0002");
                            for (var lists of oldList) {
                                var oldCol = lists.split("\u0001");
                                var newCol = [];
                                for (var nams of oldCol) {
                                    el = $("button[groupName='" + nams + "']").attr("groupID");
                                    if(el != undefined){
                                        newCol.push(el);
                                    }
                                }
                                collections.push({
                                    id: getLowestFree(collections),
                                    name: oldCol[0],
                                    groups: newCol
                                });
                            }
                        } else {
                            throw "No collections found!";
                        }
                    } catch (ex) {
                        alert("Not a valid dev_group_list2 file!\n" + ex);
                        return;
                    }

                    GM.setValue("collectionOrder", JSON.stringify(collectionOrder));
                    GM.setValue("collections", JSON.stringify(collections));
                    alert("Import successfull!");
                    insertGroups();
                    insertCollections();
                });
                break;
            case "dgl2_add":
                insertGroups();
                $("span.dgl2_descr").text("Add groups to the collection " + collections[index].name);
                colMode = 1;
                colModeTarget = index;
                $("div.dgl2_groupWrapper").addClass("dgl2_addGroup");
                for (el of collections[index].groups) {
                    $("button.dgl2_groupButton[groupID=" + el + "]").addClass("dgl2_inCollection");
                }
                break;
            case "dgl2_sub":
                insertFilteredGroups(index);
                $("span.dgl2_descr").text("Remove groups from the collection " + collections[index].name);
                colMode = 2;
                colModeTarget = index;
                $("div.dgl2_groupWrapper").addClass("dgl2_remGroup");
                break;
            case "dgl2_del":
                var con = confirm("Delete Collection " + collections[index].name + " ?");
                if (con) {
                    collections.splice(index, 1);
                    collectionOrder.splice(collectionOrder.indexOf("dgl2item-" + id), 1);

                    GM.setValue("collectionOrder", JSON.stringify(collectionOrder));
                    GM.setValue("collections", JSON.stringify(collections));

                    insertGroups();
                    insertCollections();
                }
                break;
            case "dgl2_new":
                el = {
                    name: "New Collection",
                    groups: []
                };
                el.id = getLowestFree(collections);
                collections.push(el);
                collectionOrder.push("dgl2item-" + el.id);

                GM.setValue("collectionOrder", JSON.stringify(collectionOrder));
                GM.setValue("collections", JSON.stringify(collections));

                insertGroups();
                insertCollections();
                break;
            case "dgl2_edit":
                var nam = prompt("Please enter a new collection name!", collections[index].name);
                if (nam == "" || nam == null) return;
                collections[index].name = nam;
                GM.setValue("collections", JSON.stringify(collections));
                insertCollections();
                break;
            case undefined:
            default:
                colMode = 0;
                insertFilteredGroups(index);
        }
    }

    function Ev_getGroupClick() {
        groups = [];

        $("span.dgl2_descr").text("Loading Module ID...");
        $("#dgl2_refresh").css("cursor", "pointer");
        fillModuleID().then(function () {
            $("span.dgl2_descr").text("Loading List of Groups...");
            $("#dgl2_refresh").css("cursor", "wait");
            return fillGroups(0);
        }).then(function () {
            $("span.dgl2_descr").text("Add this deviation to one of your groups");
        }).catch(function (e) {
            console.log("error:", e);
        });
    }
    //templates
    function getGroupTemplate(name, img, id) { //return HTML string
        //return "<button groupID="+id+" type='group' groupName="+name+" class='_3UhBt _3PBqc _3PBqc dgl2_groupButton'><div class='_1cFkE _11Rtb _3Lbo4 dgl2_groupButDiv'><div class='_3_Bqs _1lUlZ'><div class='pqF_I 3tZow'><span class='_1X7Yj _14i4i _23Ekg _3q2EJ'><img data-hook='user_avatar' alt='"+name+"'s avatar' class='_19ZLc dIDzJ' src='"+img+"'></span></div><div class='_3po1a _3_Nxk'><span class='_3g6BC _2PY8v _3YKkm _1sm3t _3HH04 _3y1P2 _3kEx1 _32s2-'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'><path d='M4.75,3.25 L7,3.25 L7,4.75 L4.75,4.75 L4.75,7 L3.25,7 L3.25,4.75 L1,4.75 L1,3.25 L3.25,3.25 L3.25,1 L4.75,1 L4.75,3.25 Z'></path></svg></span></div></div><div class='_3j_sQ _22Jp8'>"+name+"</div></div></button>";
        return "<button class='dgl2_groupButton' groupID=" + id + " type='group' groupName=" + name + " >" +
            "  <div class='dgl2_imgwrap'>" +
            "    <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' class='dgl2_hover'>" +
            "      <path d='M4.75,3.25 L7,3.25 L7,4.75 L4.75,4.75 L4.75,7 L3.25,7 L3.25,4.75 L1,4.75 L1,3.25 L3.25,3.25 L3.25,1 L4.75,1 L4.75,3.25 Z'></path>" +
            "    </svg>" +
            "    <img class='dgl2_group_image' title='" + name + "' src='" + img + "'/>" +
            "  </div>" +
            "  <div class='dgl2_groupName'>" + name + "</div>" +
            "</button>";
    }

    function getSubFolderTemplate(name, devCnt, grID, foID, foType, img) { //return HTML string
        //return "<button class='_3UhBt _3PBqc _3PBqc dgl2_groupButton' groupID="+grID+" folderName="+name+" folderID="+foID+" folderType='"+foType+"' type='folder'><div class='_26MiR _2yz_F'><div class='_3Q4xp'><div class='_1PQmd'><div role='img' aria-label='Suggestions unwatch fav' class='_2i5F4 _2m25r' style='width: 112px; height: 48px; background-position: center center; background-size: cover; background-repeat: no-repeat; background-image: url(&quot;"+img+"&quot;);'><noscript></noscript></div></div><div class='_2ghXZ'><span class='_3g6BC _2PY8v _3YKkm _1AjcI'><svg width='0' height='0' viewBox='0 0 8 8' xmlns='http://www.w3.org/2000/svg'><path d='M1.237 6.187L0 4.95l1.237-1.238L2.475 4.95l3.712-3.713 1.238 1.238-4.95 4.95-1.238-1.238z' fill-rule='evenodd'></path></svg></span></div></div><div class='_3a6pU'><div class='_1lEBY'>"+name+"</div><span class='_3_DY3'>"+devCnt+" deviations</span></div></div></button>";

        var imgstring=img.baseUri +"/"+ img.types[0].c.replace("<prettyName>",img.prettyName) +"?token=" + img.token[0];
        return "<button class='dgl2_groupButton' groupID=" + grID + " folderName=" + name + " folderID=" + foID + " folderType='" + foType + "' type='folder'>" +
            "  <div class='dgl2_imgwrap'>" +
            "    <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' class='dgl2_hover'>" +
            "      <path d='M4.75,3.25 L7,3.25 L7,4.75 L4.75,4.75 L4.75,7 L3.25,7 L3.25,4.75 L1,4.75 L1,3.25 L3.25,3.25 L3.25,1 L4.75,1 L4.75,3.25 Z'></path>" +
            "    </svg>" +
            "    <img class='dgl2_group_image' title='" + name + "' src='" + imgstring + "'/>" +
            "  </div>" +
            "  <div class='dgl2_groupName'>" + name + "</div>" +
            "  <div class='dgl2_devCnt'>" + devCnt + "</div>" +
            "</button>";
    }

    function getSearchBarTemplate() {
        return "<input id='dgl2_searchbar' type='text' placeholder='Search'/>";
    }

    function getCollectionColTemplate() {
        return "<div id='dgl2_CollTab'><span>Collections</span><div class='buttons'></div><ul class='sortableList'></ul></div>";
    }

    function getAddButTemplate() {
        var sty = getComputedStyle(document.body);
        return "<button class='dgl2_add'><svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='24' height='24' viewBox='0 0 172 172'>" +
            "	<g style='stroke:" + sty.color + ";stroke-width:15;'>" +
            "		<rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
            "		<line x1='86' y1='30' x2='86' y2='142' />" +
            "		<line x1='30' y1='86' x2='142' y2='86' />" +
            "	</g>" +
            "</svg></button>";
    }

    function getNewColTemplate() {
        var sty = getComputedStyle(document.body);
        return "<button class='dgl2_new dgl2_topBut'><svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='24' height='24' viewBox='0 0 172 172'>" +
            "	<g style='stroke:" + sty.color + ";stroke-width:15;'>" +
            "		<rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
            "		<line x1='86' y1='30' x2='86' y2='142' />" +
            "		<line x1='30' y1='86' x2='142' y2='86' />" +
            "	</g>" +
            "</svg></button>";
    }

    function getSubButTemplate() {
        var sty = getComputedStyle(document.body);
        return "<button class='dgl2_sub'><svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='24' height='24' viewBox='0 0 172 172'>" +
            "	<g style='stroke:" + sty.color + ";stroke-width:15;'>" +
            "		<rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
            "		<line x1='30' y1='86' x2='142' y2='86' />" +
            "	</g>" +
            "</svg></button>";
    }

    function getRefreshButTemplate() {
        var sty = getComputedStyle(document.body);
        return "<button id='dgl2_refresh'><svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='24' height='24' viewBox='0 0 172 172' style=' fill:#000000;'><g fill='none' fill-rule='nonzero' stroke='none' stroke-width='1' stroke-linecap='butt' stroke-linejoin='miter' stroke-miterlimit='10' stroke-dasharray='' stroke-dashoffset='0' font-family='none' font-weight='none' font-size='none' text-anchor='none' style='mix-blend-mode: normal'><path d='M0,172v-172h172v172z' fill='none'></path>" +
            " <linearGradient id='dgl2_grad1' x1='0%' y1='100%' x2='0%' y2='0%'><stop id='dgl2_grad1_stop1' offset='0%' style='stop-color:rgb(0,255,0);stop-opacity:1' /><stop id='dgl2_grad1_stop2' offset='100%' style='stop-color:rgb(255,0,0);stop-opacity:1' /></linearGradient>" +
            "<rect x='00' y='00' style='stroke:" + sty.color + ";stroke-width:5;opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
            "<g fill='" + sty.color + "'><path d='M62.00062,36.98l7.99979,10.89333h21.44625c18.10591,0 32.68,14.57409 32.68,32.68v21.78667h-16.34l21.78667,29.78646l21.78667,-29.78646h-16.34v-21.78667c0,-23.99937 -19.57396,-43.57333 -43.57333,-43.57333zM42.42667,39.87354l-21.78667,29.78646h16.34v21.78667c0,23.99938 19.57396,43.57333 43.57333,43.57333h29.44604l-7.99979,-10.89333h-21.44625c-18.10591,0 -32.68,-14.57409 -32.68,-32.68v-21.78667h16.34z'></path></g><path d='M43.86,172c-24.22321,0 -43.86,-19.63679 -43.86,-43.86v-84.28c0,-24.22321 19.63679,-43.86 43.86,-43.86h84.28c24.22321,0 43.86,19.63679 43.86,43.86v84.28c0,24.22321 -19.63679,43.86 -43.86,43.86z' fill='none'></path><path d='M47.3,168.56c-24.22321,0 -43.86,-19.63679 -43.86,-43.86v-77.4c0,-24.22321 19.63679,-43.86 43.86,-43.86h77.4c24.22321,0 43.86,19.63679 43.86,43.86v77.4c0,24.22321 -19.63679,43.86 -43.86,43.86z' fill='none'></path></g></svg></button";
    }

    function getDelButTemplate() {
        var sty = getComputedStyle(document.body);
        return "<button class='dgl2_del'><svg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='24' height='24' viewBox='0 0 172 172'>" +
            "<g style='stroke-width:5;stroke:" + sty.color + ";fill:none'>" +
            "	<rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
            "	<rect x='50' y='50' rx='5' ry='5' width='72' height='92'></rect>" +
            "	<rect x='65' y='35' rx='5' ry='5' width='42' height='15'></rect>" +
            "  <line x1='40' y1='50' x2='132' y2='50'/>" +
            "  <line x1='70' y1='132' x2='70' y2='60'/>" +
            "  <line x1='86' y1='132' x2='86' y2='60'  />" +
            "  <line x1='104' y1='132' x2='104' y2='60' />" +
            "  </g>" +
            "</svg></button>";
    }

    function getExportButTemplate() {
        return '<button class="dgl2_export dgl2_topBut"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 5.2916665 5.2916668">' +
            '   <g transform="translate(0,-291.70832)">' +
            '      <path style="fill:#008000;"' +
            '          d="M 0.26458332,291.9729 H 5.0270831 v 4.7625 H 0.79345641 l -0.52887309,-0.51217 z" />' +
            '      <rect style="fill:#ffffff;" width="3.7041667" height="1.8520833" x="0.79374999" y="292.23749" />' +
            '      <rect style="fill:#ffffff;" width="2.6458333" height="1.3229259" x="1.3229166" y="295.41248" />' +
            '      <rect style="fill:#008000;" width="0.52916676" height="0.79375702" x="2.9104166" y="295.67706" />' +
            '   </g>' +
            '</svg></button>';
    }

    function getImportButTemplate() {
        return '<button class="dgl2_import dgl2_topBut"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 5.2916665 5.2916668">' +
            '	<g transform="translate(0,-291.70832)">' +
            '		<rect style="fill:#806600;" width="3.96875" height="2.9104137" x="0.52916664" y="293.03125" />' +
            '		<path style="fill:#ffcc00;" d="m 0.52916666,295.94165 0.79375004,-2.11666 h 3.96875 l -0.7937501,2.11666 z" />' +
            '		<rect style="fill:#00DD00;" width="0.52916664" height="1.0583333" x="3.4395833" y="292.50208" />' +
            '		<path style="fill:#00DD00;" d="m 3.175,292.50207 0.5291667,-0.52917 0.5291667,0.52917 z" />' +
            '	</g>' +
            '</svg></button>';
    }

    function getTitleBarTemplate() {
        return '<span class="dgl2_titleText">Add to Group</span>' +
            '<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path fill-rule="evenodd" d="M8.84210526,13 L8.84210526,21.1578947 L2,21.1578947 L2,9.57894737 L12,3 L22,9.57894737 L22,21.1578947 L15.1578947,21.1578947 L15.1578947,13 L8.84210526,13 Z"></path></svg>' +
            '<span class="dgl2_descr">Add this deviation to one of your groups</span>'
    }

    function getEditButTemplate() {
        return '<button class="dgl2_edit"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20.389 6.4503l-3-3-1.46-1.45-1.41 1.42-11.52 11.58-1 .97v6.03h5.987l1.013-1 11.41-11.76 1.39-1.41-1.41-1.38zm-4.45-1.62l3 3-.88.87-3-3 .88-.87zm.74 5.33l-8.21 8.2-2.801-3.0118 8.0028-8.099 3.0083 2.9108zm-12.68 9.84v-3.17l3.0433 3.17H3.9991z"></path></svg></button>';
    }

    function addCss() {
        if ($("#dgl2_style").length > 0) return;
        var style = $("<style type='text/css' id='dgl2_style'></style>");

        //searchbar
        style.append("#dgl2_searchbar{background: var(--L3);box-shadow: inset 0 1px 4px 0 rgba(0,0,0,.25);padding: 5px;width: 200px;}");

        //right collection column
        style.append("#dgl2_CollTab{flex:1;padding: 40px 0 0 20px;font-family: CalibreSemiBold,sans-serif;font-weight: 600;font-size: 20px;font-display: swap;line-height: 24px;letter-spacing: .3px;margin-bottom: 28px;}");
        style.append("#dgl2_CollTab ul{height: 200px;overflow: auto;list-style: none;padding-left: 10px;margin-top: 20px;}");
        style.append("#dgl2_CollTab ul li{cursor:pointer;padding:2px;display:grid;grid-template: auto/7px auto 16px 16px 16px 16px;}");
        style.append("#dgl2_CollTab ul li:hover{background:linear-gradient(to right, rgba(255,0,0,0.1), rgba(255,0,0,0));}");
        style.append("#dgl2_CollTab button{cursor:pointer;border-width: 0;padding: 0;margin: 0;background-color: transparent;}");
        style.append("#dgl2_CollTab button:hover rect{fill:red;user-select: none; }");
        style.append("#dgl2_refresh{margin-left:auto;border-width:0px;background:transparent;cursor:pointer}");
        style.append("#dgl2_CollTab div.buttons{display: inline-block;vertical-align: middle;margin: 0 5px;}");
        style.append("div.dgl2_groupCol{width:700px;flex:3;margin-left:100px;}");
        style.append("div.dgl2_groupdialog{display:flex;}");
        style.append("div.dgl2_titlebar{display:flex;justify-content:space-between;}");
        style.append("#dgl2_refresh:hover rect{fill:red;}");
        style.append("#dgl2_refresh rect{fill:rgba(255,0,0,0.1);}");
        style.append("#dgl2_CollTab ul li span.handle{vertical-align:middle; display:inline-block; width:5px; height:100%; cursor:move; text-align:center; background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABZJREFUeNpi2r9//38gYGAEESAAEGAAasgJOgzOKCoAAAAASUVORK5CYII=);}");
        style.append("button.dgl2_groupButton{background-color:rgba(255,255,255,0.5);margin:5px; padding:5px; width:120px; border-width:0px; overflow:hidden; position:relative; cursor:pointer; }");
        style.append("div.dgl2_imgwrap{ position: relative;}");
        style.append("svg.dgl2_hover{ position: absolute; left: 50%; width:50%; height:50%; transform: translate(-50%,50%); opacity:0; transition: ease 0.25s; }");
        style.append("img.dgl2_group_image{ opacity:1; width:100px; height:50px; transition: ease 0.25s; border-radius:2px; }");
        style.append("div.dgl2_groupName{ font-family: CalibreSemiBold; font-size: 15px; line-height: 15px; font-weight: 600; letter-spacing: 0.3px; word-break: break-word; }");
        style.append("button.dgl2_groupButton:hover{background-color:rgba(255,255,255,0.8);}");
        style.append("button.dgl2_groupButton:hover svg.dgl2_hover{opacity:1;}");
        style.append("button.dgl2_groupButton:hover img.dgl2_group_image{opacity:0.3;}");
        style.append("button.dgl2_groupButton:active{background-color:rgba(255,255,255,0.3);}");
        style.append("button.dgl2_groupButton.dgl2_inGroup{background-image:linear-gradient(red, transparent);}");
        style.append("span.dgl2_titleText{cursor:pointer;}");
        style.append("span.dgl2_descr{font-family: CalibreRegular,sans-serif; font-weight: 400; font-size: 13px; font-display: swap; letter-spacing: 1.3px; margin-left: 32px; text-transform: uppercase;}");
        style.append("button.dgl2_edit{height:0.5em;}");
        style.append("button.dgl2_edit:hover path{fill:red;}");
        style.append("#dgl2_CollTab button svg{width: 90%;}");
        style.append("div.dgl2_addGroup{background-color: rgba(0, 255, 0, 0.3);}");
        style.append("div.dgl2_remGroup{background-color: rgba(255, 0, 0, 0.3);}");
        style.append("button.dgl2_inCollection{background-color: rgba(15, 104, 5, 0.7);}");
        style.append("button.dgl2_export:hover ,button.dgl2_import:hover {opacity:0.8}");
        style.append("button.dgl2_export:active ,button.dgl2_import:active {opacity:1}");

        $("head").append(style);

        $("head").append(
            '<link ' +
            'href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/themes/le-frog/jquery-ui.min.css" ' +
            'rel="stylesheet" type="text/css">'
        );

    }
    //filling GUI DOM
    function insertFilteredGroups(id) {
        insertGroups();
        lastFilter = id;
        var allButs = $("button[type='group']");
        if (collections[id].groups.length == 0) {
            allButs.show();
        } else {
            allButs.hide();
            for (var grID of collections[id].groups) {
                $("button[groupID='" + grID + "']").show();
            }
        }
    }

    function insertCollections() {
        var coltab = 0;
        var toAffect = $("div.dgl2_groupdialog").not("[dgl2]").attr("dgl2", 1); //group submission container
        if (toAffect.length > 0) {
            coltab = $(getCollectionColTemplate());
            toAffect.append(coltab);
            coltab.find("div.buttons")
                .append(getNewColTemplate())
                .append(getExportButTemplate())
                .append(getImportButTemplate());

            coltab.click(Ev_colListClick);
        } else {
            coltab = $("#dgl2_CollTab");
        }


        var colList = coltab.find("ul");
        colList.empty();
        var el;
        for (var col of collections) {
            el = "<li colid=" + col.id + " id='dgl2item-" + col.id + "'><span class='handle'></span><span>" + col.name + "</span>" + getEditButTemplate();
            if (col.id > 0) el += getAddButTemplate() + getSubButTemplate() + getDelButTemplate();
            el += "</li>";
            colList.append(el);
        }
        if (collectionOrder.length > 0) {
            $.each(collectionOrder, function (i, position) {
                var $target = colList.find('#' + position);
                $target.appendTo(colList); // or prependTo for reverse
            });
        }
        $(".sortableList").sortable({
            revert: true,
            cursor: 'move',
            axis: 'y',
            handle: 'span.handle',
            update: function (event, ui) {
                collectionOrder = $(this).sortable('toArray');
                GM.setValue("collectionOrder", JSON.stringify(collectionOrder));
            }
        });
    }

    function insertSearchBar() {
        var bar = $(getSearchBarTemplate());
        var refrBut = $(getRefreshButTemplate());

        $("div.dgl2_titlebar").append(refrBut).append(bar);
        bar.keyup(function () {
            var search = $(this).val();
            var allButs = $("button[type='group']").show();
            allButs.filter(function () {
                if (lastFilter > 1 && collections[lastFilter].groups.indexOf($(this).attr("groupID")) == -1) return 1;
                if (search == "") return 0;
                var words = search.split(" ");
                for (var word of words) {
                    if ($(this).attr('groupName').toLowerCase().indexOf(word) == -1) return 1;
                }
                return 0;
            }).hide();
        });

        refrBut.click(Ev_getGroupClick);

        $("span.dgl2_titleText").click(insertGroups);
    }

    function insertSubFolders(subfolders) { //fill view with subfolders //subfolders not stored, request when needed
        var buts = $("button.dgl2_groupButton"); //button wrapper
        if (buts.length > 0) {
            var par = buts.first().parent();
            var newBut;
            par.empty();
            for (var i = 0; i < subfolders.length; ++i) {
                if (subfolders[i].thumb == null) continue;
                newBut = $(getSubFolderTemplate(subfolders[i].name, subfolders[i].size, subfolders[i].owner.userId, subfolders[i].folderId, subfolders[i].type, subfolders[i].thumb.media));
                par.append(newBut);

            }
            par.not("[dgl2]").attr("dgl2", 1).click(Ev_groupClick);
        }
    }

    function insertGroups() { //fill view with groups //groups are stored
        lastFilter = 0;
        groups.sort(function (l, u) {
            return l.username.toLowerCase().localeCompare(u.username.toLowerCase());
        });

        $("span.dgl2_titleText").text("Add to Group");
        $("span.dgl2_descr").text("Add this deviation to one of your groups");

        var par = $("div.dgl2_groupWrapper"); //group list wrapper
        var newBut;
        par.empty();
        for (var i = 0; i < groups.length; ++i) {
            newBut = $(getGroupTemplate(groups[i].username, groups[i].usericon, groups[i].userId));
            if (listedGroups.includes(groups[i].userId)) {
                newBut.addClass("dgl2_inGroup"); //button div inside wrapper; used in template
            }
            par.append(newBut);
        }
        par.not("[dgl2]").attr("dgl2", 1).click(Ev_groupClick);
    }

    //in case classNames (hopefully) change sometime. Also necessary since browsing=other class names than refreshing site.
    function classNormalizer() {//_2n5r3 _3Isw- _3yw1l _1F3vX _34hvg
        if($("div._38jjU").length>1)$("div._38jjU").hide().last().show();

        $("span.u1km9, span._3M1Kg").addClass("dgl2_descr"); //refr u1km9 browse _3M1Kg //top description
        $("button._3PBqc, button._3UhBt").addClass("dgl2_groupButton"); //refr _3UhBt _3PBqc, browse _3PBqc //button to add to group
        $("div._1cFkE, div._3Lbo4").addClass("dgl2_groupButDiv"); //refr _1cFkE _11Rtb, browse _3Lbo4 _3EbZ8 //div inside button
        $("div.A9uFL, div.JcCFR").addClass("dgl2_groupWrapper"); //refr A9uFL _2IeoY, browse JcCFR _159Ra //div parent of group buttons
        $("div._1HNK0, div._1ezbx").addClass("dgl2_titlebar"); //refr _1HNK0, browse _1ezbx //title bar
        $("div._2n5r3, div._2qgZz").addClass("dgl2_groupdialog"); //refr _2n5r3, browse _2qgZz // dialog(opening div) for group submission
        $("div._2UK-E, div._2kU4L").addClass("dgl2_groupCol"); //refr _2UK, browse _2kU4L//left column (containing groups)

        $("div.dgl2_groupdialog div._13bQf").hide();
        $("div.dgl2_groupdialog button._2-StF").hide();

        if($("div.dgl2_titlebar").length==0 && $("div.dgl2_groupCol:not([nogroups])").length>0){
            $("div.dgl2_groupCol").attr("nogroups",1).html('<div class="_38jjU"><div class="_1HNK0 dgl2_titlebar">Add to Group<span class="_3g6BC _1Re00 _3YKkm _1d2qE"><svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path fill-rule="evenodd" d="M8.84210526,13 L8.84210526,21.1578947 L2,21.1578947 L2,9.57894737 L12,3 L22,9.57894737 L22,21.1578947 L15.1578947,21.1578947 L15.1578947,13 L8.84210526,13 Z"></path></svg></span><span class="u1km9 dgl2_descr">Add this deviation to one of your groups</span></div><div class="_2ZROG"><div class="A9uFL _2IeoY dgl2_groupWrapper"><button class="_3UhBt dgl2_groupButton"><div class="_1cFkE _11Rtb dgl2_groupButDiv"><div class="_3_Bqs"><div class="pqF_I"><span class="_1X7Yj _14i4i"><img alt="NatureNation\'s avatar" data-hook="user_avatar" class="_19ZLc" src="https://a.deviantart.net/avatars-big/n/a/naturenation.jpg?2" loading="lazy"></span></div><div class="_3po1a"><span class="_3g6BC _2PY8v _3YKkm _1sm3t"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path d="M4.75,3.25 L7,3.25 L7,4.75 L4.75,4.75 L4.75,7 L3.25,7 L3.25,4.75 L1,4.75 L1,3.25 L3.25,3.25 L3.25,1 L4.75,1 L4.75,3.25 Z"></path></svg></span></div></div><div class="_3j_sQ">NatureNation</div></div></button></div></div></div>');
        }

    }

    function getLowestFree(collection) {
        collection.sort(function (a, b) {
            return a.id - b.id;
        }); //changing order does not matter thanks to index/order array
        var lowest = -1;
        var i;
        for (i = 0; i < collection.length; ++i) {
            if (collection[i].id != i) {
                lowest = i;
                break;
            }
        }
        if (lowest == -1) {
            lowest = collection[collection.length - 1].id + 1;
        }
        return lowest;

    }

    function download(data, filename) {
        var file = new Blob([data], {
            type: "application/json"
        });
        var a = document.createElement("a"),
            url = URL.createObjectURL(file);
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        setTimeout(function () {
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
        }, 0);
    }

    function upload() {
        return new Promise(function (resolve, reject) {
            var inp = $('<input type="file" id="input">').appendTo("body").click()
            inp.change(function () {
                var reader = new FileReader();
                reader.onload = function (evt) {
                    resolve(evt.target.result);
                };
                reader.readAsBinaryString($(this).prop("files")[0]);
            });
            return "";
        });
    }

    function colIndexById(id) {
        for (var i = 0; i < collections.length; ++i) {
            if (collections[i].id == id) return i;
        }
        return -1;
    }

    //bind script to buttons. dynamic browsing compatible
    function addListener() {

        classNormalizer();
        var el = $("span.u1km9, span._3M1Kg").not("[dgl2]"); //span containing group title description
        var but = $("button._3PBqc, button._3UhBt"); //group button


        if (el.length > 0 && but.length > 0) {
            el.first().attr("dgl2", 1);
            $("div.dgl2_titlebar").html(getTitleBarTemplate());

            devID = location.href.match(/\d+$/)[0];
            userName = $("a.user-link").attr("data-username"); // "dediggefedde";

            addCss();
            insertSearchBar();

            GM.getValue("collections", "").then(function (cols) {
                if (collections != ""){
                    collections = JSON.parse(cols);
                }
                return GM.getValue("collectionOrder", "[]");
            }).then(function (order) {
                collectionOrder = JSON.parse(order);
                return insertCollections();
            }).catch(function () {
                return insertCollections();
            });

            fillListedGroups(devID).then(function () {
                return GM.getValue("groups", "");
            }).then(function (grps) {
                if (grps == "") {
                    Ev_getGroupClick();
                } else {
                    groups = JSON.parse(grps);
                    insertGroups();
                }
            }).catch(function (e) {
                console.log("error:", e);
            });

            ; //can run in parallel

        }
    }

    setInterval(addListener, 1000);
})();

/* resources:
group template
<button class="_3PBqc"> <!--_3UhBt-->
 <div class="_3Lbo4 _3EbZ8">
  <div class="_1lUlZ">
   <div class="_3tZow">
    <span class="_23Ekg _3q2EJ">
     <img data-hook="user_avatar" alt="iterators's avatar" class="dIDzJ" src="https://a.deviantart.net/avatars-big/i/t/iterators.png?3">
    </span>
   </div>
   <div class="_3_Nxk">
    <span class="_3HH04 _3y1P2 _3kEx1 _32s2-">
     <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8">
      <path d="M4.75,3.25 L7,3.25 L7,4.75 L4.75,4.75 L4.75,7 L3.25,7 L3.25,4.75 L1,4.75 L1,3.25 L3.25,3.25 L3.25,1 L4.75,1 L4.75,3.25 Z"></path>
     </svg>
    </span>
   </div>
  </div>
  <div class="_22Jp8">iterators</div>
 </div>
</button>

group_request_response_structure
    userId	14471598
    useridUuid	55b0c0d3-fed7-45ff-8951-0d8554eb01b1
    username	deviantARTSupporters
    usericon	https://a.deviantart.net/avatars-big/d/e/deviantartsupporters.gif?12
    type	group
    isNewDeviant	false

subfolder request response structure
    commentCount: 0
    description: ""
    folderId: 13361368
    name: "Featured"
    owner: Object { userId: 11209451, useridUuid: "294e75e1-94ec-4009-883a-b13eff743164", username: "iterators", … }
    parentId: null
    size: 5
    thumb:
       author: Object { userId: 7514199, useridUuid: "56613871-de84-49fc-b1c1-f9fd0f796461", username: "Championx91", … }
       blockReason: null
       deviationId: 710763111
       files: Array(9) [ {…}, {…}, {…}, … ]
          0: Object { type: "150", src: "https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/56613871-de84-49fc-b1c1-f9fd0f796461/dbr64br-58702ee6-5e4e-4bab-b2ea-c5ca2563bb7f.png/v1/fit/w_150,h_150,strp/suggestions_unwatch_fav_by_championx91_dbr64br-150.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7ImhlaWdodCI6Ijw9ODIxIiwicGF0aCI6IlwvZlwvNTY2MTM4NzEtZGU4NC00OWZjLWIxYzEtZjlmZDBmNzk2NDYxXC9kYnI2NGJyLTU4NzAyZWU2LTVlNGUtNGJhYi1iMmVhLWM1Y2EyNTYzYmI3Zi5wbmciLCJ3aWR0aCI6Ijw9NjQxIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmltYWdlLm9wZXJhdGlvbnMiXX0.vyFqJs1s5cyralkRXS31gJbHrwFsBm72c2q9Hb0uJDs", height: 150, … }
       isAntisocial: true
       isBlocked: false
       isCommentable: true
       isDeleted: false
       isDownloadable: false
       isFavourited: false
       isJournal: false
       isMature: false
       isPublished: true
       isPurchasable: false
       isShareable: false
       isTextEditable: false
       legacyTextEditUrl: null
       printId: null
       publishedTime: "2017-10-20T10:39:40-0700"
       stats: Object { comments: 17, favourites: 19 }
       title: "Suggestions unwatch fav"
       typeId: 1
       url: "https://www.deviantart.com/championx91/art/Suggestions-unwatch-fav-710763111"
    type: "gallery"

required:
	groupid =  groups request
	folderid = subfolders/favourites requests
	deviationid = url \d+$
	csrf_token = $("input[name=validate_token]").value; //deviation page
	moduleID = window.document.body.innerHTML.match(/{\\\"id\\\":(\d+),\\\"type\\\":\\\"group_list_members/i) //https://www.deviantart.com/dediggefedde/about

groups GET
	https://www.deviantart.com/_napi/da-user-profile/api/module/groups/members?username=Dediggefedde&moduleid=1725444339&offset=30&limit=24
	Limit max 60
	finished if hasMore==false
	needs moduleID, username

subfolders GET
	https://www.deviantart.com/_napi/shared_api/deviation/group_folders?groupid=13042771&type=gallery
	needs groupID

favourites GET
	https://www.deviantart.com/_napi/shared_api/deviation/group_folders?groupid=28209298&type=collection
	needs groupID

add to subfolder POST
  {"groupid":11209451,"type":"gallery","folderid":23881334,"deviationid":501848540,"csrf_token":"wPPvYvd4br1JpNjT.pyuz1q.lVPn5jT3ohUO8uD0QLruZb-V9d5NDb1FOyl7OcHe7w"}
	https://www.deviantart.com/_napi/shared_api/deviation/group_add
	needs:
		csrf_token	THjPWGH1SRH_-epZ.py75wu.VTYjdFg4YApQBqsZBS5ISBHG1W4aWv-oTkk5WDA4t-w
		deviationid	298766207
		folderid	23881334
		groupid	11209451
		type	gallery
*/