您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Better Submit-to-group dialog
当前为
// ==UserScript== // @name dev_group_list2 // @namespace http://www.deviantart.com/ // @version 4.9 // @description Better Submit-to-group dialog // @author Dediggefedde // @match https://www.deviantart.com/* // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js // @grant GM.xmlHttpRequest // @grant GM.setValue // @grant GM.getValue // ==/UserScript== /* globals $*/ // @require http://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js //dgl2=dgl2 identifiert/classname (function() { 'use strict'; let userName = ""; let moduleID = 0; let grPerReq = 24; let groups = []; //{userId,useridUuid,username,usericon,type,isNewDeviant} // isNewDeviant not needed. // type=oneof{group, super group} // usericon always starts with https://a.deviantart.net/avatars-big // useridUuid specific to the group, contains submission rights // userid of the group let groupN = 0; let listedGroups = []; let devID; let collections = [{ id: 0, name: "all", groups: [], showing: 1 }]; let collectionOrder = []; let colMode = 0; //0 show, 1 add, 2 remove, 3 delete let colModeTarget = 0; let macros = []; //{id, name, data:[{folderName, folderID, groupID, type}]} let macroOrder = []; let macroMode = 0; //0 idle, 1 record, 2 play, 3 remove let macroModeTarget = 0; //for recording let lastFilter = 0; var dragSrcEl = null; let colListMode = 0; //0 collection, 1 macro; let svgTurnArrow = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1.507856 1.2692268" style="height:0.5em;"> <defs id="defs2"> <marker id="Arrow2Send" style="overflow:visible"> <path id="path8454" style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" transform="matrix(-0.3,0,0,-0.3,0.69,0)" /> </marker> </defs> <g id="layer1" transform="translate(-1.2565625,-0.79875001)"> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Send)" d="m 2.38125,0.93125 c -1.3229167,0 -1.3229166,0.79375 0,0.79375" id="path8419"/> </g> </svg> '; let fetchingGroups = false; //prevent refresh button requesting multiple times at once let entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }; let loadedFolders = new Map(); let lastGroupClickID; let notScanThisInstance = false; let targetName = ""; //target group or macro name const errtyps = Object.freeze({ Connection_Error: "Connection Error", No_User_ID: "No User ID", Unknown_Error: "Unkown Error", Parse_Error: "Parse Error", Wrong_Setting: "Wrong Profile setting" }); //error protocol convention: ErrType of errtyps, ErrDescr as text, ErrDetail with exception object //alert of type/descr, console log with detail function err(type, descr, detail) { return { ErrType: type, ErrDescr: descr, ErrDetail: detail }; } function errorHndl(err) { myMsgBox("<strong>Type</strong>: " + err.ErrType + "<br /><strong>Description</strong>: " + err.ErrDescr + "<br /><br />See the log (F12 in Chrome/Firefox) for more details.<br />If the error persist, feel free to write me a <a style=\"text-decoration: underline;\" href=\"https://www.deviantart.com/dediggefedde/art/dev-group-list2-817465905#comments\">comment</a>.", "Error"); console.log("dev_group_list2 error:", err); } //API calls getting data function fillGroups(offset) { //async+callback //load all groups let murl = "https://www.deviantart.com/_napi/da-user-profile/api/module/groups/members?username=" + userName + "&moduleid=" + moduleID + "&offset=" + offset + "&limit=" + grPerReq; return new Promise(function(resolve, reject) { GM.xmlHttpRequest({ method: "GET", url: murl, onerror: function(response) { reject(err(errtyps.Connection_Error, "Connection to " + murl + " failed", response)); }, onload: function(response) { if (response.status == 500) { resolve(oldFillGroups(offset)); return } let resp = JSON.parse(response.responseText); if (offset == 0) groups = []; let newnams = resp.results.map(x => x.userId); if (groups.map(x => x.userId).filter(x => newnams.includes(x)).length > 0) { reject(err(errtyps.Wrong_Setting, "Double group entries detected. Probably wrong sorting at profile/about's group-member section. Please choose asc or desc, not random.", groups)); $("#dgl2_refresh").css("cursor", "pointer"); return; } groups = groups.concat(resp.results); groupN = resp.total; let 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); } } }); }); } let ngroups = []; function oldFillGroups(offset) { //only fills names and icons let murl = "https://www.deviantart.com/" + userName + "/modals/mygroups/?offset=" + offset + "&limit=100"; return new Promise(function(resolve, reject) { GM.xmlHttpRequest({ method: "GET", url: murl, onerror: function(response) { reject(err(errtyps.Connection_Error, "Connection to " + murl + " failed", response)); }, onload: function(response) { let tgroups = response.responseText.split("<div class=\"mygroup") let rex = /<img.*?class="avatar".*?src="(.*?)".*?title="(.*?)"/i; let rex2 = /data-gruser-type="(.*?)"/i; tgroups = tgroups.map(str => { //parse websites to group data let ret = {}; let mat = str.match(rex); if (mat == null) return null; ret.usericon = mat[1]; ret.username = mat[2]; ret.type = str.match(rex2)[1]; return ret; }).filter(el => el != null); ngroups = ngroups.concat(tgroups); $("#dgl2_refresh rect").css("fill", "url(#dgl2_grad1)"); $("span.dgl2_descr").text(`Loading List of Groups... ${ngroups.length}/?`); if (response.responseText.indexOf("class=\"next disabled") == -1) { //next not disabled = new page available resolve(oldFillGroups(offset + 100)); } else { ngroups.forEach(el => { //new groups not previously in list if (el == null) return; for (let gr of groups) { if (gr.username == el.username) { Object.assign(el, gr); //el=gr; //copy old information to prevent id/uuid loss } } }); groups = ngroups; ngroups = []; GM.setValue("groups", JSON.stringify(groups)); insertGroups(); $("#dgl2_refresh rect").css("fill", ""); $("#dgl2_refresh").css("cursor", "pointer"); resolve(groups); } } }); }); } function grabIDfromPage(name) { return new Promise(function(resolve, reject) { GM.xmlHttpRequest({ method: "GET", url: "https://www.deviantart.com/" + name, onerror: function(response) { reject(err(errtyps.Connection_Error, "Connection to https://www.deviantart.com/" + name + " failed", response)); }, onload: async function(response) { $("#dgl2_refresh rect").css("fill", "url(#dgl2_grad1)"); ngrpCnt += 1; $("span.dgl2_descr").text(`Loading List of Groups IDs... ${ngrpCnt}/${ngrpleft}`); let rex = /itemid":(.*?),"friendid":"(.*?)"/i; let mat = response.responseText.match(rex); if (mat == null) { reject(err(errtyps.No_User_ID, "Request of " + name + "-id failed", response)); return; } groups.forEach(gr => { if (gr.username == name) { gr.userId = mat[1]; gr.useridUuid = mat[2]; } }); GM.setValue("groups", JSON.stringify(groups)); resolve(mat[1]); } }); }); } function fillSubFolder(groupID, type, name) { //async+callback //type =[collection,gallery] if (groupID == "undefined") { $(".dgl2_groupdialog ").css("cursor", "wait"); $(".dgl2_groupButton").css("cursor", "wait"); return grabIDfromPage(name).then(id => { groupID = id; lastGroupClickID = id; return fillSubFolder(groupID, type, name); }).catch(erg => { errorHndl(erg); $(".dgl2_groupdialog ").css("cursor", "pointer"); return null; }); } return new Promise(function(resolve, reject) { let murl = "https://www.deviantart.com/_napi/shared_api/deviation/group_folders?groupid=" + groupID + "&type=" + type; GM.xmlHttpRequest({ method: "GET", url: murl, onerror: function(response) { reject(err(errtyps.Connection_Error, "Connection to " + murl + " failed", response)); }, onload: async function(response) { let resp; let errg; try { resp = JSON.parse(response.responseText); } catch (ex) { errg = err(errtyps.Connection_Error, "Page problems. Please try again later!", ex) reject(errg) } if (resp.results == undefined) { errg = err(errtyps.Parse_Error, "Error parsing website response", response); errorHndl(errg); } else { insertSubFolders(resp.results); if (macroMode == 1) { for (let gr of macros[macroModeTarget].data) { if (gr.groupID == groupID) { $("button.dgl2_groupButton[folderID='" + gr.folderID + "']").addClass("folderInMacro"); break; } } } } $(".dgl2_groupdialog").css("cursor", ""); $(".dgl2_groupButton").css("cursor", "pointer"); $("div.groupPopup").focus(); resolve(resp.results); } }); }); } function fillModuleID() { //async+callback //get Module ID for group submission let murl = "https://www.deviantart.com/" + userName + "/about"; return new Promise(function(resolve, reject) { GM.xmlHttpRequest({ method: "GET", url: murl, onerror: function(response) { reject(err(errtyps.Connection_Error, "Connection to " + murl + " failed", response)); }, onload: async function(response) { try { let resp = (response.responseText); resp = resp.match(/{\\\"id\\\":(\d+),\\\"type\\\":\\\"group_list_members/i); if (resp != null && resp.length > 1) { moduleID = resp[1]; resolve(moduleID); } else { reject(err(errtyps.Wrong_Setting, "No group-member section in /about page", resp)); } } catch (ex) { reject(err(errtyps.Unknown_Error, "Something went wrong while accessing groups", ex)); } } }); }); } function fillListedGroups(devID) { let murl = "https://www.deviantart.com/deviation/" + devID + "/groups"; return new Promise(function(resolve, reject) { GM.xmlHttpRequest({ method: "GET", url: murl, onerror: function(response) { reject(err(errtyps.Connection_Error, "Connection to " + murl + " failed", response)); }, onload: async function(response) { let tex = response.responseText; let res = tex.matchAll(/gmi-groupid="(\d+)"/gi); for (let entr of res) { listedGroups.push(parseInt(entr[1])); } resolve(listedGroups); } }); }); } function requestAddSubmission(token, devID, folderID, groupID, type) { //async+callback //type =[collection,gallery] let macroFchanged = false; if (macroMode == 1) { if (macros[macroModeTarget].data.some(e => e.groupID === groupID)) { macros[macroModeTarget].data.forEach(function(el) { if (el.groupID === groupID) { el.folderID = folderID; el.folderName = loadedFolders.get(folderID); } }); //change folder of present group macroFchanged = true; } else { //don't add if included already macros[macroModeTarget].data.push({ folderName: loadedFolders.get(folderID), folderID: folderID, groupID: groupID, type: type }); } GM.setValue("macros", JSON.stringify(macros)); } return new Promise(function(resolve, reject) { let dat = { "groupid": parseInt(groupID), "type": type.toString(), "folderid": parseInt(folderID), "deviationid": parseInt(devID), "csrf_token": token.toString() }; if (macroMode == 1) { //don't submit while adding to macros resolve({ success: true, gname: groupNameById(groupID), fname: loadedFolders.get(folderID), fchanged: macroFchanged }); } else { 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) { response.gname = groupNameById(groupID); reject(err(errtyps.Connection_Error, "Connection to https://www.deviantart.com/_napi/shared_api/deviation/group_add failed", response)); }, onload: async function(response) { let resp = JSON.parse(response.responseText); resp.gname = groupNameById(groupID); resolve(resp); } }); } }); } function playMacro(index) { macroMode = 2; let promises = []; let token = $("input[name=validate_token]").val(); for (let d of macros[index].data) { promises.push(requestAddSubmission(token, devID, d.folderID, d.groupID, d.type)); } Promise.all(promises).catch(err => { alert(macros[index].name + " Error!<br/>" + JSON.stringify(macros[index].data) + " " + JSON.stringify(err), "Error"); }).then(res => { myMsgBox( res.map(obj => { let retval = "<strong>" + obj.gname + "</strong>: " if (obj.success) { retval += "Success! "; if (obj.needsVote == true) retval += " Vote pending"; } else { retval += "Error! "; if (obj.errorDetails) retval += obj.errorDetails; } return retval; }).join("<br/>"), "Play Macro " + macros[macroModeTarget].name); }) macroMode = 0; } //event handlers function Ev_groupClick(event) { //event propagation event.stopPropagation(); let targetBut = $(event.target).closest(".dgl2_groupButton"); let groupID = targetBut.attr("groupID"); let groupNam = targetBut.attr("groupname"); //targetName=""; if (groupID == "undefined") { grabIDfromPage(groupNam).then(id => { $(event.target).closest(".dgl2_groupButton").attr("groupID", id); Ev_groupClick(event); }); return; } let 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"); targetName = targetBut.attr("groupName"); } break; case 2: //remove targetName = collections[colModeTarget].name; switch (colListMode) { case 0: //collection elInd = collections[colModeTarget].groups.indexOf(groupID); if (elInd > -1) { collections[colModeTarget].groups.splice(elInd, 1); GM.setValue("collections", JSON.stringify(collections)); } insertFilteredGroups(colModeTarget); break; case 1: //macro for (elInd = 0; elInd < macros[macroModeTarget].data.length; ++elInd) { if (macros[macroModeTarget].data[elInd].groupID == groupID) break; } if (elInd < macros[macroModeTarget].data.length) { macros[macroModeTarget].data.splice(elInd, 1); GM.setValue("macros", JSON.stringify(macros)); } insertMacroGroups(macroModeTarget); break; } break; case 0: default: if (targetBut.attr("type") == "group") { lastGroupClickID = targetBut.attr("groupID"); fillSubFolder(groupID, "gallery", groupNam); if (macroMode != 1) { targetName = targetBut.attr("groupName"); } else { } displayModeText(); //Add this deviation to a group folder } else if (targetBut.attr("type") == "folder") { let 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 (macroMode == 1) { if (arg.fchanged) { myMsgBox(arg.gname + " target folder changed to " + arg.fname, "Info"); } else { myMsgBox(arg.gname + "/" + arg.fname + " added to macro", "Info"); } insertMacros(); //update titles insertGroups(); //go back to groups view $("div.dgl2_groupWrapper").addClass("dgl2_addGroup"); displayModeText(); //$("span.dgl2_descr").text("macro " + macros[macroModeTarget].name + " is recording."); for (let el of macros[macroModeTarget].data) { $("button.dgl2_groupButton[groupID=" + el.groupID + "]").addClass("dgl2_inCollection"); } macroMode = 1; let lastgrBut = $("button[groupid=" + lastGroupClickID + "]") lastgrBut[0].scrollIntoView(); lastgrBut.addClass("shadow-pulse"); } else { let retfun = function() { $("span.dgl2_titleText").click(); let lastgrBut = $("button[groupid=" + lastGroupClickID + "]") lastgrBut[0].scrollIntoView(); lastgrBut.addClass("shadow-pulse"); }; if (arg.needsVote) { myMsgBox("Success! Submission pending group's vote", "Info").then(retfun); } else { myMsgBox("Success! Submission added to group", "Info").then(retfun); } } } else { throw arg; } /* deviationGroupCount: 1 needsVote: true */ }).catch(function(arg) { let tx = "deviation-ID: " + devID + "<br/>" + "Group-Name: " + (arg.gname ? arg.gname : "Unknown") + "<br/>" + (arg.errorDescription ? arg.errorDescription : "Unexpected error.") + "<br/>" + (arg.errorDetails ? arg.errorDetails : JSON.stringify(arg)) let errg = err(errtyps.Unknown_Error, tx, arg); errorHndl(errg); }); } } displayModeText(); } function Ev_ContextSubmit(event) { event.stopPropagation(); event.preventDefault(); event.target = $("#dgl2_grContext select option:selected").get(0); Ev_groupClick(event); $("#dgl2_grContext").hide().find("select").empty(); } function Ev_groupContext(event) { event.stopPropagation(); event.preventDefault(); let el = $("#dgl2_grContext"); if (el.length == 0) { el = $("<div id='dgl2_grContext'><span class='desc'>Submit to a Folder</span><br /><select size=5></select><br/><button>Submit</button></div>").appendTo(document.body); el.find("button").click(Ev_ContextSubmit); } el.find("select").hide(); el.finish().show().css({ top: event.pageY + "px", left: event.pageX + "px" }); let groupID = $(event.target).closest("button.dgl2_groupButton").attr("groupid") fillSubFolder(groupID, "gallery", $(event.target).closest("button.dgl2_groupButton").attr("groupname")).then(function() { el.find("select").show().focus().get(0).selectedIndex = 0; }); } function switchColList() { switch (colListMode) { case 0: colListMode = 1; $("span.dgl2_CollTitle").html("Macros"); insertMacros(); break; case 1: $("span.dgl2_CollTitle").html("Collections"); colListMode = 0; insertCollections(); } } function highlightLetter(which) { $(".dgl2_letterfound").removeClass("dgl2_letterfound"); $(".dgl2_groupdialog button.dgl2_groupButton[groupName^='" + which + "' i]").addClass("dgl2_letterfound").focus(); $(".dgl2_groupdialog button.dgl2_groupButton[folderName^='" + which + "' i]").addClass("dgl2_letterfound").focus(); } function Ev_colListClick(event) { event.stopPropagation(); if ($(event.target).closest(".dgl2_CollTitleBut").length > 0) { switchColList(); } let 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; let clasNam = $(event.target).closest("button").attr("class"); let index; if (colListMode == 0) index = colIndexById(id); else if (colListMode == 1) index = makIndexById(id); $("div.dgl2_groupWrapper").removeClass("dgl2_addGroup").removeClass("dgl2_remGroup"); $("button.dgl2_inCollection").removeClass("dgl2_inCollection"); let el; let obj, dat; let d = new Date(); let con; let nam; targetName = ""; if (clasNam) clasNam = clasNam.replace(" dgl2_topBut", ""); switch (clasNam) { case "dgl2_export": obj = { collections: collections, collectionOrder: collectionOrder, macros: macros, macroOrder: macroOrder }; 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 { let i; let obj = JSON.parse(imp); if (obj.macros && obj.macroOrder) { macros = obj.macros; macroOrder = obj.macroOrder; } if (obj.collections && obj.collectionOrder) { collections = obj.collections; collectionOrder = obj.collectionOrder; } else if (obj[0] != undefined && (obj[0][0].indexOf("_collist") != -1 || obj[1][0].indexOf("_collist") != -1)) { //v1 compatibility mode collections = [{ id: 0, name: "all", groups: [], showing: 1 }]; let ind = (obj[0][0].indexOf("_collist") != -1) ? 0 : ((obj[1][0].indexOf("_collist") != -1) ? 1 : -1) let oldList = obj[ind][1].split("\u0002"); let coll = oldList.map((list, ind) => { let entries = list.split("\u0001"); let nam = entries.shift(); return { id: ind + 1, name: nam, groups: entries.map(el => { return $("button[groupname='" + el + "']").attr("groupid"); }).filter(el => el != undefined), showing: 1 } }); collections = collections.concat(coll).sort((a, b) => a.id > b.id); collectionOrder = collections.map(col => "dgl2item-" + col.id); } else { throw "No collections found!"; } //clean up old groups not beeing a member of anymore for (i in macros) { macros[i].data = macros[i].data.filter(el => { return groupNameById(el.groupID) != "" }); } for (i in collections) { collections[i].groups = collections[i].groups.filter(el => { return groupNameById(el) != "" }); } } catch (ex) { errorHndl(err(errtyps.Parse_Error, "Not a valid dev_group_list2 file", ex)); return; } GM.setValue("collectionOrder", JSON.stringify(collectionOrder)); GM.setValue("collections", JSON.stringify(collections)); GM.setValue("macroOrder", JSON.stringify(macroOrder)); GM.setValue("macros", JSON.stringify(macros)); myMsgBox("Import successfull!", "Info"); insertGroups(); insertCollections(); if (colListMode != 0) switchColList(); }); break; case "dgl2_add": insertGroups(); switch (colListMode) { case 0: //collection targetName = collections[index].name; //$("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"); } displayModeText(); break; case 1: colMode = 0; insertGroups(); $("div.dgl2_groupWrapper").addClass("dgl2_addGroup"); targetName = macros[index].name; //$("span.dgl2_descr").text("macro " + macros[index].name + " is recording."); for (el of macros[index].data) { $("button.dgl2_groupButton[groupID=" + el.groupID + "]").addClass("dgl2_inCollection"); } macroMode = 1; macroModeTarget = index; displayModeText(); break; } break; case "dgl2_sub": switch (colListMode) { case 0: //collection insertFilteredGroups(index); targetName = collections[index].name; //$("span.dgl2_descr").text("Remove groups from the collection " + collections[index].name); colMode = 2; colModeTarget = index; $("div.dgl2_groupWrapper").addClass("dgl2_remGroup"); displayModeText(); break; case 1: //macro insertMacroGroups(index); targetName = macros[index].name; // $("span.dgl2_descr").text("Remove groups from the macro " + macros[index].name); colMode = 2; macroMode = 3; macroModeTarget = index; $("div.dgl2_groupWrapper").addClass("dgl2_remGroup"); displayModeText(); break; } break; case "dgl2_del": switch (colListMode) { case 0: //collection myMsgBox("Delete Collection " + collections[index].name + " ?", "Collection", 1).then(con => { if (!con) return; 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 1: //macro myMsgBox("Delete Macro " + macros[index].name + " ?", "Macro", 1).then(con => { if (!con) return; macros.splice(index, 1); macroOrder.splice(macroOrder.indexOf("dgl2item-" + id), 1); GM.setValue("macroOrder", JSON.stringify(macroOrder)); GM.setValue("macros", JSON.stringify(macros)); insertGroups(); insertMacros(); }); break; } break; case "dgl2_new": switch (colListMode) { case 0: //collection el = { name: "New Collection", groups: [], showing: 1 }; 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 1: //macros el = { name: "New Macro", data: [] }; el.id = getLowestFree(macros); macros.push(el); macroOrder.push("dgl2item-" + el.id); GM.setValue("macroOrder", JSON.stringify(macroOrder)); GM.setValue("macros", JSON.stringify(macros)); insertGroups(); insertMacros(); break; } break; case "dgl2_visible": switch (colListMode) { case 0: //collection if (!collections[index].hasOwnProperty("showing")) collections[index].showing = 0; else collections[index].showing = 1 - collections[index].showing; //toggle 0 and 1 GM.setValue("collections", JSON.stringify(collections)); $(event.target).closest("li").attr("active", collections[index].showing); insertGroups(); break; case 1: //macro //donothing break; } break; case "dgl2_edit": switch (colListMode) { case 0: //collection myMsgBox("Please enter a new collection name!", "Change Collection Name", 2, collections[index].name).then(nam => { if (!nam) return; collections[index].name = nam; GM.setValue("collections", JSON.stringify(collections)); insertCollections(); }); break; case 1: //macro myMsgBox("Please enter a new macro name!", "Change Macro Name", 2, macros[index].name).then(nam => { if (!nam) return; macros[index].name = nam; GM.setValue("macros", JSON.stringify(macros)); insertMacros(); }); break; } break; case undefined: default: switch (colListMode) { case 0: //collection colMode = 0; insertFilteredGroups(index); break; case 1: //macro //$("span.dgl2_descr").text("Add this deviation to a group folder"); myMsgBox("Do you want to add this to the following groups?<br/>" + macros[index].data.map(obj => { return groupNameById(obj.groupID); }).join(", "), "Submit to Groups", 1).then(con => { if (!con) {} else { macroMode = 2; playMacro(index); } displayModeText(); }); break; } } displayModeText(); } function Ev_getGroupClick() { if (fetchingGroups) return; fetchingGroups = true; groups.forEach((el) => { if (el.userId == 0) { el.userId = "undefined" el.useridUuid = "undefined" } }); $("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"); displayModeText(); }).catch(function(e) { if (e.ErrType != null) errorHndl(e); else errorHndl(err(errtyps.Unknown_Error, "fillGroups error", e)); }).finally(function() { fetchingGroups = false; }); } //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='" + escapeHtml(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='" + escapeHtml(name) + "' src='" + img + "'/>" + " </div>" + " <div class='dgl2_groupName'>" + escapeHtml(name) + "</div>" + "</button>"; } function getSubFolderOptionTemplate(name, devCnt, grID, foID, foType, img) { //text option only needs name,IDs and type return "<option class='dgl2_groupButton' groupID=" + grID + " folderName='" + escapeHtml(name) + "' folderID=" + foID + " folderType='" + foType + "' type='folder'>" + escapeHtml(name) + "</option>"; } function getSubFolderTemplate(name, devCnt, grID, foID, foType, img) { //return HTML string loadedFolders.set("" + foID, name); //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(""+img+"");'><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>"; let imgstring; if (img.textContent) { //journal imgstring = "<p class='dgl2_journalSubF'>" + img.textContent.excerpt + "</p>"; } else { let i; let cstr = ""; img = img.media; for (i of img.types) { if (i.c != undefined) { cstr = i.c; break; } } if (cstr == "") { for (i of img.types) { if (i.s != undefined) { cstr = i.s; break; } } } if (img.baseUri) imgstring = img.baseUri + "/"; if (img.prettyName) imgstring += cstr.replace("<prettyName>", img.prettyName); if (img.token) imgstring += "?token=" + img.token[0]; imgstring = "<img class='dgl2_group_image' title='" + escapeHtml(name) + "' src='" + imgstring + "'/>"; } return "<button class='dgl2_groupButton' groupID=" + grID + " folderName='" + escapeHtml(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>" + imgstring + " </div>" + " <div class='dgl2_groupName'>" + escapeHtml(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'><div class='dgl2_CollTitleBut'>" + svgTurnArrow + "<span class='dgl2_CollTitle'>" + "Collections</span></div><div class='buttons'></div><ul class='sortableList'></ul></div>"; } function getAddButTemplate() { let sty = getComputedStyle(document.body); return "<button title='Add group to collection/macro' 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 getRecButTemplate() { let sty = getComputedStyle(document.body); return "<button title='Add groups to macro' 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>" + " <ellipse cx='86' cy='86' rx='40' ry='40'></ellipse>" + " </g>" + "</svg></button>"; } function getNewColTemplate() { let sty = getComputedStyle(document.body); return "<button title='New collection/macro' 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() { let sty = getComputedStyle(document.body); return "<button title='Remove groups from collection/macro' 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() { let sty = getComputedStyle(document.body); return "<button title='refresh list of groups' 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() { let sty = getComputedStyle(document.body); return "<button title='Delete collection/macro' 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 title="Export collection/macro list to file" 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" title="Import collection/macro list from file" ><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 title="Change collection/macro name" 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 getVisibleButTemplate() { return `<button title="Hide/show groups within collection" class="dgl2_visible"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 250" stroke-width="20"> <defs> <radialGradient id="c1" cx="0.5" cy="0.5" r="0.5"> <stop offset="0" stop-color="#ffffff" /> <stop offset=".5" stop-color="hsl(40, 60%, 60%)" /> <stop offset="1" stop-color="#3dff3d" /> </radialGradient> </defs> <ellipse role="sclera" cx="250" cy="125" rx="200" ry="100" fill="white"/> <ellipse role="iris" cx="250" cy="125" rx="95" ry="95" stroke="black" fill="url(#c1)"/> <ellipse role="pupil" cx="250" cy="125" rx="50" ry="50" stroke="none" fill="black"/> <ellipse role="light" cx="200" cy="80" rx="50" ry="50" stroke="none" fill="#fffffaee"/> <ellipse role="outline" cx="250" cy="125" rx="200" ry="100" stroke="black" fill="none"/> </svg> </button>`; } function addCss() { if ($("#dgl2_style").length > 0) return; let 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: 50%;}"); //right collection column style.append(` #dgl2_CollTab{padding-left:15px; overflow-y: auto;font-family: CalibreSemiBold,sans-serif;font-weight: 600;font-size: 20px;font-display: swap;line-height: 24px;letter-spacing: .3px;margin-bottom: 28px;grid-row: 2;} #dgl2_CollTab ul{overflow-wrap: anywhere;overflow: auto;list-style: none;padding-left: 10px;margin-top: 20px;} #dgl2_CollTab ul li{cursor:pointer;padding:2px;display:grid;grid-template: auto/7px auto 16px 16px 16px 16px 16px;} #dgl2_CollTab ul li:hover{background:linear-gradient(to right, rgba(255,0,0,0.1), rgba(255,0,0,0));} #dgl2_CollTab button{cursor:pointer;border-width: 0;padding: 0;margin: 0;background-color: transparent;} #dgl2_CollTab button:hover rect{fill:red;user-select: none; } #dgl2_refresh{margin-left:auto;border-width:0px;background:transparent;cursor:pointer} #dgl2_CollTab div.buttons{display: inline-block;vertical-align: middle;margin: 0 5px;} div.dgl2_groupCol{overflow-y:auto;grid-row: 2;} div.dgl2_groupdialog{display: grid;position: fixed;top: 50%;left: 50%;transform: translate(-50%,-50%);height: 80%;background-color:#afcaa9;grid-template-columns: auto 300px;grid-template-rows: 50px auto;width: 80%;border: 2px solid #2a5d00;border-radius: 10px;box-shadow: 1px 1px 2px ;} div.dgl2_titlebar{cursor:move;display:flex;justify-content:space-between;grid-row: 1;grid-column: 1/3;background: linear-gradient(#619c32,#378201);color: white;align-items: center;} div.dgl2_titlebar > * {margin: 7px;} #dgl2_refresh:hover rect{fill:red;} div.dgl2_closeDiag{border-radius: 50px;padding: 7px;cursor: pointer;} #dgl2_refresh rect{fill:rgba(255,0,0,0.1);} #dgl2_CollTab ul li span.handle{vertical-align:middle; display:inline-block; width:5px; height:100%; cursor:move; text-align:center;background-color:#363; background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABZJREFUeNpi2r9//38gYGAEESAAEGAAasgJOgzOKCoAAAAASUVORK5CYII=);} button.dgl2_groupButton{border-radius:15px; background-color:rgba(255,255,255,0.5);margin:5px; padding:5px; width:120px; border-width:0px; overflow:hidden; position:relative; cursor:pointer; } div.dgl2_imgwrap{ position: relative;} svg.dgl2_hover{ position: absolute; left: 50%; width:50%; height:50%; transform: translate(-50%,50%); opacity:0; transition: ease 0.25s; } img.dgl2_group_image{ opacity:1; width:100px; height:50px; transition: ease 0.25s; border-radius:2px; } div.dgl2_groupName{ font-family: CalibreSemiBold; font-size: 15px; line-height: 15px; font-weight: 600; letter-spacing: 0.3px; word-break: break-word; } button.dgl2_groupButton:hover{background-color:rgba(255,255,255,0.8);} button.dgl2_groupButton:hover svg.dgl2_hover{opacity:1;} button.dgl2_groupButton:hover img.dgl2_group_image{opacity:0.3;} button.dgl2_groupButton:active{background-color:rgba(255,255,255,0.3);} button.dgl2_groupButton.dgl2_inGroup{background-image:linear-gradient(red, transparent);} span.dgl2_titleText{cursor:pointer;} 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;} button.dgl2_edit{height:0.5em;} button.dgl2_edit:hover path{fill:red;} #dgl2_CollTab button svg{width: 90%;} #dgl2_CollTab button.dgl2_visible:hover ellipse{stroke: red;} div.dgl2_addGroup{background-color: rgba(0, 255, 0, 0.3);} div.dgl2_remGroup{background-color: rgba(255, 0, 0, 0.3);} button.dgl2_inCollection{background-color: rgba(15, 104, 5, 0.7);} button.dgl2_export:hover ,button.dgl2_import:hover {opacity:0.8} button.dgl2_export:active ,button.dgl2_import:active {opacity:1} div.dgl2_CollTitleBut{cursor:pointer;display:inline-block;} div.dgl2_CollTitleBut:hover span{color:red;} .dgl2_journalSubF { overflow: hidden; height: 50px; font-size: xx-small; text-align: left; margin-bottom: 5px;} .groupPopup .ui-widget-content{background-color:#afcaa9 !important;color:black;} button.dgl2_letterfound {background-color: rgba(105, 14, 5, 0.7);} .folderInMacro {background-color:rgba(205, 24, 25, 0.6)!important;} @keyframes shadowPulse {0% {box-shadow: 0px 0px 50px 20px #f00;} 100% {box-shadow: 0px 0px 50px 20px #ff000000;}} .shadow-pulse {animation-name: shadowPulse;animation-duration: 0.5s;animation-iteration-count: infinite;animation-timing-function: linear; animation-direction:alternate;} #dgl2_grContext{display: none;z-index: 1000;position: absolute;overflow: hidden;white-space: nowrap;padding: 5px;background-color: #afcaa9;border-radius: 5px;border: 2px solid green;} #dgl2_grContext select{background: none; border: none;width:100%;margin:5px 0;} #dgl2_grContext select option{background-color: #ddffd8;} #dgl2_grContext select option:nth-child(even) {background-color: #6fd061;} #dgl2_grContext select option::selection {color: red;background: yellow;} #dgl2_grContext button {cursor:pointer; width: 100%;background-color: #408706;color: white;border: 1px outset black;border-radius: 5px;} #dgl2_grContext button:hover { background-color: #608706;} #dgl2_CollTab li[active='0'] button.dgl2_visible ellipse[role='iris'] { fill: lightgray;stroke:lightgray} #dgl2_CollTab li[active='0'] button.dgl2_visible ellipse { fill: lightgray;} #dgl2_CollTab li button{display: flex; height: 100%;align-items: center;} #dgl2_CollTab li.dgl2_drgover{border-top: 2px solid blue;} #dgl2_alertBox {z-index:7777;box-shadow: 1px 1px 2px black;position: fixed;top: 50%;left: 50%;background-color: #afcaa9;border-radius: 5px;border: 2px solid #285c00;transform: translate(-50%, -50%);} #dgl2_alertBox .dgl2_alertTitle{cursor:move;background-color:#5d982d;color:white;font-weight: bold;} #dgl2_alertBox div.dgl2_alertButtons div{padding: 5px;display: inline-block;border-radius: 5px;border: 1px solid;cursor: pointer;margin:5px;} #dgl2_alertBox .dgl2_alertOKBut{background-color: #d0e8cb;} #dgl2_alertBox .dgl2_alertCancelBut{background-color: #e8e3cb;} #dgl2_alertBox>div{padding:5px;} #dgl2_alertBox>div{padding:5px;} #dgl2_alertBox div.dgl2_alertButtons{display: flex;flex-direction: row-reverse;} #dgl2_promptVal{display: block;margin: 5px;border-radius: 5px;width: 90%;} `); // style.append(".noTitleDialog .ui-dialog-titlebar {display:none}"); $("head").append(style); // $("head").append( // '<link ' + // 'href="//ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/le-frog/jquery-ui.min.css" ' + // 'rel="stylesheet" type="text/css">' // ); } //function from https://www.w3schools.com/howto/howto_js_draggable.asp //makes elements draggable function dragElement(elmnt) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; if (elmnt.querySelector(".dgl2_alertTitle")) { // if present, the header is where you move the DIV from: elmnt.querySelector(".dgl2_alertTitle").onmousedown = dragMouseDown; } if (elmnt.querySelector(".dgl2_titlebar")) { // if present, the header is where you move the DIV from: elmnt.querySelector(".dgl2_titlebar").onmousedown = dragMouseDown; } else { // otherwise, move the DIV from anywhere inside the DIV: elmnt.onmousedown = dragMouseDown; } function dragMouseDown(e) { e = e || window.event; if (e.target.tagName == "INPUT") return; e.preventDefault(); // get the mouse cursor position at startup: pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; // call a function whenever the cursor moves: document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); // calculate the new cursor position: pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; // set the element's new position: elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; } function closeDragElement() { // stop moving when mouse button is released: document.onmouseup = null; document.onmousemove = null; } } //filling GUI DOM function hideHiddenGroups() { collections.forEach(col => { if (col.showing == 0) { col.groups.forEach(grID => { $("button[groupID='" + grID + "']").hide(); }); } }); } function insertFilteredGroups(id) { insertGroups(); lastFilter = id; let allButs = $("button[type='group']"); if (collections[id].groups.length == 0) { allButs.show(); } else { allButs.hide(); for (let grID of collections[id].groups) { $("button[groupID='" + grID + "']").show(); } } hideHiddenGroups() } function insertMacroGroups(id) { insertGroups(); lastFilter = id; let allButs = $("button[type='group']"); if (macros[id].data.length == 0) { allButs.show(); } else { allButs.hide(); for (let grID of macros[id].data) { $("button[groupID='" + grID.groupID + "']").show(); } } } function insertMacros() { let coltab = 0; let toAffect = $("div.dgl2_groupdialog").not("[dgl2]").attr("dgl2", 1); 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"); } let colList = coltab.find("ul"); colList.empty(); let el, descr; for (let col of macros) { descr = ""; for (let gr of col.data) { descr += groupNameById(gr.groupID) + "/" + gr.folderName + "\n"; } el = "<li colid=" + col.id + " title='" + escapeHtml(descr) + "' id='dgl2item-" + col.id + "'><span class='handle'></span><span>" + col.name + "</span>" + getEditButTemplate(); el += getRecButTemplate() + getSubButTemplate() + getDelButTemplate(); el += "</li>"; colList.append(el); } if (macroOrder.length > 0) { $.each(macroOrder, function(i, position) { let $target = colList.find('#' + position); $target.appendTo(colList); // or prependTo for reverse }); } makesortable(); } function insertCollections() { let coltab = 0; let 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"); } let colList = coltab.find("ul"); colList.empty(); let el; for (let col of collections) { el = `<li colid=${col.id} active='${col.showing}' id='dgl2item-${col.id}'><span class='handle'></span><span>${col.name}</span>${getEditButTemplate()}`; if (col.id > 0) el += getVisibleButTemplate() + getAddButTemplate() + getSubButTemplate() + getDelButTemplate(); el += "</li>"; colList.append(el); } if (collectionOrder.length > 0) { $.each(collectionOrder, function(i, position) { let $target = colList.find('#' + position); $target.appendTo(colList); // or prependTo for reverse }); } makesortable(); } function makesortable() { //ul.sortableList //li colid=col.id id='dgl2item-" + col.id, //<span class='handle'> let lists = document.querySelectorAll("ul.sortableList li"); for (let i = 0; i < lists.length; ++i) { lists[i].draggable = "true"; addDragHandler(lists[i]); } } //drag handler for drag-sortable lists function addDragHandler(entry) { entry.addEventListener('dragstart', function(e) { e.dataTransfer.setData('text/plain', this.id); e.dataTransfer.effectAllowed = 'move'; }, false); // entry.addEventListener('dragenter', function(e){}, false) entry.addEventListener('dragover', function(e) { if (e.preventDefault) { e.preventDefault(); } this.classList.add('dgl2_drgover'); e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object. return false; }, false); entry.addEventListener('dragleave', function(e) { this.classList.remove('dgl2_drgover'); }, false); entry.addEventListener('drop', function(e) { var dropHTML = e.dataTransfer.getData('text/plain'); this.parentNode.insertBefore(document.querySelector("#" + dropHTML), this); this.classList.remove('dgl2_drgover'); if (colListMode == 0) { collectionOrder = [...this.parentNode.querySelectorAll("[draggable]")].map(el => el.id); GM.setValue("collectionOrder", JSON.stringify(collectionOrder)); } else if (colListMode == 1) { macroOrder = [...this.parentNode.querySelectorAll("[draggable]")].map(el => el.id); GM.setValue("macroOrder", JSON.stringify(macroOrder)); } return false; }, false); entry.addEventListener('dragend', function(e) { this.classList.remove('dgl2_drgover'); }, false); } function insertSearchBar() { let bar = $(getSearchBarTemplate()); let refrBut = $(getRefreshButTemplate()); $("div.dgl2_titlebar").append(refrBut).append(bar).append( $("<div class='dgl2_closeDiag'>X</div>").click(function() { $("div.dgl2_groupdialog").hide(); }) ); bar.keyup(function() { let search = $(this).val(); let 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; let words = search.split(" "); for (let word of words) { if ($(this).attr('groupName').toLowerCase().indexOf(word) == -1) return 1; } return 0; }).hide(); }); //bar.mousedown(function(event){event.stopPropagation(); event.preventDefault();event.target.focus();}); refrBut.click(Ev_getGroupClick); $("span.dgl2_titleText").click(function() { if (colListMode == 0) { insertGroups(); $("li[colid=" + lastFilter + "]").click(); } else { macroMode = 0; //abort macro mode add/remove insertGroups(); displayModeText(); $("div.dgl2_groupWrapper").removeClass("dgl2_addGroup").removeClass("dgl2_remGroup"); $("button.dgl2_inCollection").removeClass("dgl2_inCollection"); } }); } function pulsing(element) { element.animate({ opacity: 0 }, 250, function() { $(this).animate({ opacity: 1 }, 250, pulsing); }); } function insertSubFolders(subfolders) { //fill view with subfolders //subfolders not stored, request when needed let buts = $("button.dgl2_groupButton"); //button wrapper subfolders.sort(function(l, u) { return l.name.toLowerCase().localeCompare(u.name.toLowerCase()); }); if (buts.length > 0) { let subf; let newBut; if ($("#dgl2_grContext").is(":visible")) { let cont = $("#dgl2_grContext select"); let newopt; cont.empty(); for (subf of subfolders) { if (subf.thumb == null) continue; //no thumb=db problem, so also no text entry newBut = $(getSubFolderOptionTemplate(subf.name, subf.size, subf.owner.userId, subf.folderId, subf.type, subf.thumb)); cont.append(newBut); } if (subfolders.length == 0) { $("#dgl2_grContext span.desc").html("This group does<br/>not allow submissions<br/>using the gallery<br/>system!"); } else { $("#dgl2_grContext span.desc").text("Submit to a Folder"); } } else { let par = buts.first().parent(); par.empty(); for (subf of subfolders) { if (subf.thumb == null) continue; newBut = $(getSubFolderTemplate(subf.name, subf.size, subf.owner.userId, subf.folderId, subf.type, subf.thumb)); par.append(newBut); } if (subfolders.length == 0) { par.append("This group does not allow submissions using the gallery system!"); } par.not("[dgl2]").attr("dgl2", 1).click(Ev_groupClick); } } } function displayModeText() { let titl = "Add to Group"; let descr = "Add this deviation to one of your groups"; if (targetName != "") { if (colListMode == 0) { //colection if (colMode == 0) { //show titl = "< Add to " + targetName; descr = "Add this deviation to Group " + targetName; } else if (colMode == 1) { //add titl = "< Add to " + targetName; descr = "Add groups to Collection " + targetName; } else if (colMode == 2) { //remove titl = "< Remove from " + targetName; descr = "Remove groups from Collection " + targetName; } } else { //macros if (macroMode == 0) {} else if (macroMode == 1) { titl = "< Add to Macro"; descr = "macro " + targetName + " is recording."; } else if (macroMode == 3) { //remove titl = "< Remove from Macro"; descr = "remove groups from " + targetName; } } } $("span.dgl2_titleText").text(titl); $("span.dgl2_descr").text(descr); } function insertGroups() { //fill view with groups //groups are stored lastFilter = 0; groups.sort(function(l, u) { return l.username.toLowerCase().localeCompare(u.username.toLowerCase()); }); displayModeText(); let par = $("div.dgl2_groupWrapper"); //group list wrapper let newBut; let hasEmptyId = false; par.empty(); for (let gr of groups) { newBut = $(getGroupTemplate(gr.username, gr.usericon, gr.userId)); newBut.contextmenu(Ev_groupContext); if (typeof gr.userId == "undefined") hasEmptyId = true; if (listedGroups.includes(parseInt(gr.userId))) { newBut.addClass("dgl2_inGroup"); //button div inside wrapper; used in template } if (par.find("div[groupName='" + gr.username + "']").length == 0) { par.append(newBut); } } par.not("[dgl2]").attr("dgl2", 1).click(Ev_groupClick); hideHiddenGroups(); if (hasEmptyId && !notScanThisInstance) { requestAllGroupIDs(); } } function uniqBy(a, key) { let seen = new Set(); return a.filter(item => { let k = key(item); return seen.has(k) ? false : seen.add(k); }); } let ngrpCnt = 0; let fetchItMsg = "" let ngrpleft = 0; function delayIteratefetchGrId(groups, index, delay) { if (index < groups.length) { grabIDfromPage(groups[index].username).catch(() => { if (fetchItMsg != "") fetchItMsg += ", "; fetchItMsg += groups[index].username; groups[index].userId = 0; groups[index].useridUuid = 0; GM.setValue("groups", JSON.stringify(groups)); }).finally(() => { setTimeout(function() { delayIteratefetchGrId(groups, index + 1, delay); }, delay); }); } else { if (fetchItMsg != "") myMsgBox("failed fetching IDs for groups: " + fetchItMsg + "<br/>They might be deleted.") $("#dgl2_refresh rect").css("fill", ""); $("#dgl2_refresh").css("cursor", "pointer"); //$("span.dgl2_descr").text("Add this deviation to a group folder"); displayModeText(); } } function requestAllGroupIDs() { let nullgr = groups.filter(gr => typeof gr.userId == "undefined"); let remtim = nullgr.length * 1.1; let remtex = "" ngrpleft = nullgr.length; if (remtim < 60) { remtex = "seconds" } else if (remtim >= 60 && remtim < 60 * 60) { remtex = "minutes"; remtim /= 60.0; } else if (remtim >= 60 * 60) { remtex = "hours"; remtim /= 60.0 * 60.0; } myMsgBox(`${ngrpleft} group-IDs are not fetched.<br/>Fetching all group IDs now might take a while (est. ${Math.round((remtim + Number.EPSILON) * 100) / 100} ${remtex}).<br/>If you press "cancel" IDs are fetched dynamically when group-buttons are clicked.<br/>Collections, macros and list of already submitted deviations can not display groups without ID.<br/><br/>Fetch all remaining group-IDs now?`, "Fetch Group-IDs", 1).then((choice) => { if (choice) { ngrpCnt = 0; fetchItMsg = ""; delayIteratefetchGrId(nullgr, 0, 250); } }); notScanThisInstance = true; } function insertHTML() { if ($("div.dgl2_groupdialog").length > 0) return; addCss(); $("<div class='dgl2_groupdialog'><div class='dgl2_titlebar'></div><div class='dgl2_groupCol'><div class='dgl2_groupWrapper'></div></div></div>").appendTo($("body")); $("div.dgl2_titlebar").html(getTitleBarTemplate()); insertSearchBar(); let devInd = location.href.indexOf("?"); if (devInd == -1) { devID = location.href.match(/(\d+)\D*$/)[1]; } else { devID = location.href.substring(0, devInd).match(/(\d+)\D*$/)[1]; } userName = $("a.user-link").attr("data-username"); // "dediggefedde"; let proms = [ GM.getValue("collections", ""), GM.getValue("collectionOrder", ""), GM.getValue("macros", ""), GM.getValue("macroOrder", "") ]; Promise.all(proms).then(([cols, colOrder, macs, macOrder]) => { if (cols != "") { collections = JSON.parse(cols); } collections.forEach(el => { if (!el.hasOwnProperty("showing")) { el.showing = 1; }; }); //backward-compatibility for collection-showing attribute before v3.0 if (colOrder != "") { collectionOrder = JSON.parse(colOrder); } if (macs != "") { macros = JSON.parse(macs); macros.forEach(function(el) { el.data = uniqBy(el.data, JSON.stringify); }); //unique macros } if (macOrder != "") { macroOrder = JSON.parse(macOrder); } insertCollections(); }).catch(function(e) { errorHndl(err(errtyps.Unknown_Error, "Error Loading Database", e)); 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) { errorHndl(err(errtyps.Unknown_Error, "fillListedGroups error", e)); }); } //outdated, now I'm using my own template //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.dgl2_groupdialog").length>0)return; // $("<div class='dgl2_groupdialog'><div class='dgl2_groupCol'><div class='dgl2_titlebar'></div><div class='dgl2_groupWrapper'></div></div></div>").appendTo($("body")); // // $("div.dgl2_groupdialog div._13bQf").hide(); // // $("div.dgl2_groupdialog button._2-StF").hide(); // // $("span.u1km9, span._3M1Kg").addClass("dgl2_descr"); //refr u1km9 browse _3M1Kg //top description //added by template // // $("button._3PBqc, button._3UhBt").addClass("dgl2_groupButton"); //refr _3UhBt _3PBqc, browse _3PBqc //button to add to group //added by template // // $("div._1cFkE, div._3Lbo4").addClass("dgl2_groupButDiv"); //refr _1cFkE _11Rtb, browse _3Lbo4 _3EbZ8 //div inside button //outdated // // $("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) // // if($("div.dgl2_titlebar").length==0 && $("div.dgl2_groupCol:not([nogroups])").length>0){ // // $("div.dgl2_groupCol").attr("nogroups",1).html('<div class="_2UK-E"><div class="_38jjU"><div class="_1HNK0">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">Add this deviation to one of your groups</span></div><div class="_2ZROG"><div class="A9uFL _2IeoY"><button class="_3UhBt"><div class="_1cFkE _11Rtb"><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></div>'); // // classNormalizer(); // // } // } function getLowestFree(collection) { collection.sort(function(a, b) { return a.id - b.id; }); //changing order does not matter thanks to index/order array let lowest = -1; let i; for (i = 0; i < collection.length; ++i) { if (collection[i].id != i) { lowest = i; break; } } if (lowest == -1 && collection.length > 0) { lowest = collection[collection.length - 1].id + 1; } else if (collection.length == 0) lowest = 0; return lowest; } function escapeHtml(string) { return String(string).replace(/[&<>"'`=\/]/g, function(s) { return entityMap[s]; }); } function download(data, filename) { let file = new Blob([data], { type: "application/json" }); let 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) { let inp = $('<input type="file" id="input">').appendTo("body").click() inp.change(function() { let reader = new FileReader(); reader.onload = function(evt) { resolve(evt.target.result); }; reader.readAsBinaryString($(this).prop("files")[0]); }); return ""; }); } function colIndexById(id) { for (let i = 0; i < collections.length; ++i) { if (collections[i].id == id) return i; } return -1; } function makIndexById(id) { for (let i = 0; i < macros.length; ++i) { if (macros[i].id == id) return i; } return -1; } //shows an alert box with text //mode 0:alert, 1:confirm, 2 prompt function myMsgBox(tex, titl = "Notification", mode = 0, defText = "") { let dfd = new $.Deferred(); let box = $("#dgl2_alertBox"); if (box.length == 0) { box = $("<div id='dgl2_alertBox'></div>").appendTo("body"); } box.html("<div class='dgl2_alertTitle'></div><div class='dgl2_alertText'></div><div class='dgl2_alertButtons'></div>"); box.find("div.dgl2_alertText").html(tex); box.find("div.dgl2_alertTitle").html(titl); if (mode == 0) { $("<div class='dgl2_alertOKBut'>OK</div>").click(function() { dfd.resolve(true); $("#dgl2_alertBox").hide(); }).appendTo(box.find("div.dgl2_alertButtons")); } else if (mode == 1) { $("<div class='dgl2_alertOKBut'>OK</div>").click(function() { dfd.resolve(true); $("#dgl2_alertBox").hide(); }).appendTo(box.find("div.dgl2_alertButtons")); $("<div class='dgl2_alertCancelBut'>Cancel</div>").click(function() { dfd.resolve(false); $("#dgl2_alertBox").hide(); }).appendTo(box.find("div.dgl2_alertButtons")); } else if (mode == 2) { $("<input type='text' id='dgl2_promptVal' value='" + defText + "' class='text ui-widget-content ui-corner-all'>").appendTo(box.find("div.dgl2_alertText")); $("<div class='dgl2_alertOKBut'>OK</div>").click(function() { dfd.resolve($("#dgl2_promptVal").val()); $("#dgl2_alertBox").hide(); }).appendTo(box.find("div.dgl2_alertButtons")); $("<div class='dgl2_alertCancelBut'>Cancel</div>").click(function() { dfd.resolve(false); $("#dgl2_alertBox").hide(); }).appendTo(box.find("div.dgl2_alertButtons")); } box.show(); dragElement(box[0]); return dfd.promise(); } function showPopup(event) { event.preventDefault(); event.stopPropagation(); insertHTML(); //does nothing if already inserted let el = $("div.dgl2_groupdialog"); el.show(); dragElement(el[0]); el.attr("tabindex", "0"); el.keydown(function(event) { if (event.target.tagName != "INPUT") { highlightLetter(String.fromCharCode(event.which)); } }); $("div.dgl2_groupdialog, div.dgl2_groupdialog>div").click(function(event) { event.stopPropagation(); if (event.target.tagName != "INPUT") $("div.dgl2_groupdialog").focus(); }); // $("div.dgl2_groupdialog").dialog({ // width: "80%", // height: (window.innerHeight * 0.8), // draggable: false, // resizable: false, // dialogClass: 'groupPopup', // create: function(event) { // $(event.target).parent().css({ // 'position': 'fixed', // "left": '10%', // "top": '10%' // }); // $(event.target).parent().find("div.ui-dialog-titlebar").prepend($("div.dgl2_titlebar")); // $(event.target).parent().find("span.ui-dialog-title").hide(); // } // }); // $("div.groupPopup").keydown(function(event) { // if (event.target.tagName != "INPUT" && !$("#dgl2_grContext").is(":visible")) { // highlightLetter(String.fromCharCode(event.which)); // } // }); } function groupNameById(id) { for (let g of groups) { if (g.userId == id) return g.username; } return ""; } //bind script to buttons. dynamic browsing compatible function addListener() { let els = $('*[datahook="group_counter"]:not([dgl2=1]),*[data-hook="group_counter"]:not([dgl2=1])'); els.attr("dgl2", 1).find("svg").html( '<path d="M18.63 17l1.89 5h2l-2.53-7h-6.67l.64 2zM4.04 15l-2.52 7h2l1.88-5h4.23l1.89 5h2l-2.53-7zM7.52 4.33c1.9304.011 3.4873 1.5829 3.48 3.5133-.0074 1.9303-1.5762 3.4903-3.5066 3.4866C5.563 11.3263 4 9.7604 4 7.83c0-1.933 1.567-3.5 3.5-3.5h.02zm-.02-2C4.4624 2.33 2 4.7924 2 7.83s2.4624 5.5 5.5 5.5 5.5-2.4624 5.5-5.5-2.4624-5.5-5.5-5.5zM13 3.37a5.59 5.59 0 0 1 1.5 1.45 3.41 3.41 0 0 1 1.5-.35c1.933 0 3.5 1.567 3.5 3.5s-1.567 3.5-3.5 3.5a3.41 3.41 0 0 1-1.5-.35 5.63 5.63 0 0 1-1.5 1.46c1.968 1.2806 4.532 1.1706 6.3831-.2738 1.8511-1.4445 2.5812-3.9047 1.8175-6.125C20.437 3.9608 18.348 2.4702 16 2.47a5.4102 5.4102 0 0 0-3 .9z"/>' + '<path stroke="#0A0" stroke-width="4" stroke-opacity="0.8" d="M12 18H24M18 12V24"/>' ); els.parent().parent().click(showPopup); } $(document).ready(function() { $(document).mousedown(function(event) { if ($(event.target).closest("#dgl2_grContext").length == 0) { $("#dgl2_grContext").hide().find("select").empty(); } }); }); 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 */