dev_group_list2

Better Submit-to-group dialog

当前为 2020-08-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name dev_group_list2
  3. // @namespace http://www.deviantart.com/
  4. // @version 3.4
  5. // @description Better Submit-to-group dialog
  6. // @author Dediggefedde
  7. // @match https://www.deviantart.com/*
  8. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
  9. // @require http://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
  10. // @grant GM.xmlHttpRequest
  11. // @grant GM.setValue
  12. // @grant GM.getValue
  13.  
  14. // ==/UserScript==
  15. /* globals $*/
  16.  
  17. //dgl2=dgl2 identifiert/classname
  18. (function () {
  19. 'use strict';
  20. var userName = "";
  21. var moduleID = 0;
  22. var grPerReq = 24;
  23. var groups = []; //{userId,useridUuid,username,usericon,type,isNewDeviant}
  24. // isNewDeviant not needed.
  25. // type=oneof{group, super group}
  26. // usericon always starts with https://a.deviantart.net/avatars-big
  27. // useridUuid specific to the group, contains submission rights
  28. // userid of the group
  29. var groupN = 0;
  30. var listedGroups = [];
  31. var devID;
  32. var collections = [{
  33. id: 0,
  34. name: "all",
  35. groups: [],
  36. showing: 1
  37. }];
  38. var collectionOrder = [];
  39. var colMode = 0; //0 show, 1 add, 2 remove, 3 delete
  40. var colModeTarget = 0;
  41. var macros = []; //{id, name, data:[{folderName, folderID, groupID, type}]}
  42. var macroOrder = [];
  43. var macroMode = 0; //0 idle, 1 record, 2 play, 3 remove
  44. var macroModeTarget = 0; //for recording
  45. var lastFilter = 0;
  46. var colListMode = 0; //0 collection, 1 macro;
  47. var 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> ';
  48. var fetchingGroups=false; //prevent refresh button requesting multiple times at once
  49. var entityMap = {
  50. '&': '&amp;',
  51. '<': '&lt;',
  52. '>': '&gt;',
  53. '"': '&quot;',
  54. "'": '&#39;',
  55. '/': '&#x2F;',
  56. '`': '&#x60;',
  57. '=': '&#x3D;'
  58. };
  59. var loadedFolders=new Map();
  60. var lastGroupClickID;
  61. var notScanThisInstance=false;
  62.  
  63. //API calls getting data
  64. function fillGroups(offset) { //async+callback //load all groups
  65. //console.log("https://www.deviantart.com/_napi/da-user-profile/api/module/groups/members?username=" + userName + "&moduleid=" + moduleID + "&offset=" + offset + "&limit=" + grPerReq);
  66. return new Promise(function (resolve, reject) {
  67. GM.xmlHttpRequest({
  68. method: "GET",
  69. url: "https://www.deviantart.com/_napi/da-user-profile/api/module/groups/members?username=" + userName + "&moduleid=" + moduleID + "&offset=" + offset + "&limit=" + grPerReq,
  70. onerror: function (response) {
  71. reject(response);
  72. },
  73. onload: function (response) {
  74. if(response.status==500){
  75. resolve(oldFillGroups(offset));
  76. //reject(response.status);
  77. //console.log(response);
  78. return
  79. }
  80. var resp = JSON.parse(response.responseText);
  81.  
  82. if(offset==0)groups = [];
  83. groups = groups.concat(resp.results);
  84. groupN = resp.total;
  85.  
  86. var frac = 0;
  87. if (groupN > 0) frac = groups.length / groupN * 100;
  88. frac = Math.round(frac);
  89.  
  90. $("#dgl2_refresh rect").css("fill", "url(#dgl2_grad1)");
  91. $("#dgl2_grad1_stop1").attr("offset", frac + " %");
  92. $("#dgl2_grad1_stop2").attr("offset", (frac + 1) + " %");
  93. $("#dgl2_refresh").attr("title", frac + " %");
  94. $("span.dgl2_descr").text("Loading List of Groups... " + frac + " %");
  95.  
  96. if (resp.hasMore) {
  97. resolve(fillGroups(resp.nextOffset));
  98. } else {
  99. GM.setValue("groups", JSON.stringify(groups));
  100. insertGroups();
  101. $("#dgl2_refresh rect").css("fill", "");
  102. $("#dgl2_refresh").css("cursor", "pointer");
  103.  
  104. resolve(groups);
  105. }
  106. }
  107. });
  108. });
  109. }
  110.  
  111. var ngroups=[];
  112. function oldFillGroups(offset){ //only fills names and icons
  113. return new Promise(function(resolve,reject){
  114. GM.xmlHttpRequest({
  115. method: "GET",
  116. url: "https://www.deviantart.com/"+userName+"/modals/mygroups/?offset=" + offset + "&limit=100",
  117. onerror: function (response) {
  118. reject(response);
  119. },
  120. onload: function (response) {
  121.  
  122. var tgroups=response.responseText.split("<div class=\"mygroup")
  123. var rex=/<img.*?class="avatar".*?src="(.*?)".*?title="(.*?)"/i
  124. var rex2=/data-gruser-type="(.*?)"/i
  125.  
  126. tgroups=tgroups.map(str=>{ //parse websites to group data
  127. var ret={};
  128. var mat=str.match(rex);
  129. if(mat==null)return null;
  130. ret.usericon=mat[1];
  131. ret.username=mat[2];
  132. ret.type=str.match(rex2)[1];
  133. return ret;
  134. }).filter(el=>el!=null);
  135. ngroups = ngroups.concat(tgroups);
  136.  
  137. $("#dgl2_refresh rect").css("fill", "url(#dgl2_grad1)");
  138. $("span.dgl2_descr").text(`Loading List of Groups... ${ngroups.length}/?`);
  139.  
  140. if(response.responseText.indexOf("class=\"next disabled")==-1){ //next not disabled = new page available
  141. resolve(oldFillGroups(offset+100));
  142. }else{
  143. ngroups.forEach(el=>{ //new groups not previously in list
  144. if(el==null)return;
  145. for(var gr of groups){
  146. if(gr.username==el.username){
  147. Object.assign(el,gr);
  148. //el=gr;
  149. //copy old information to prevent id/uuid loss
  150. }
  151. }
  152. });
  153. groups=ngroups;
  154. ngroups=[];
  155.  
  156. GM.setValue("groups", JSON.stringify(groups));
  157. insertGroups();
  158. $("#dgl2_refresh rect").css("fill", "");
  159. $("#dgl2_refresh").css("cursor", "pointer");
  160. resolve(groups);
  161. }
  162. }
  163. });
  164. });
  165. }
  166. function grabIDfromPage(name){
  167. return new Promise(function (resolve, reject) {
  168. GM.xmlHttpRequest({
  169. method: "GET",
  170. url: "https://www.deviantart.com/" + name,
  171. onerror: function (response) {
  172. reject(response);
  173. },
  174. onload: async function (response) {
  175. //console.log(response);
  176. // console.log(response.responseText);
  177.  
  178. $("#dgl2_refresh rect").css("fill", "url(#dgl2_grad1)");
  179. ngrpCnt+=1;
  180. $("span.dgl2_descr").text(`Loading List of Groups IDs... ${ngrpCnt}`);
  181.  
  182. var rex=/itemid":(.*?),"friendid":"(.*?)"/i;
  183. var mat = response.responseText.match(rex);
  184. if(mat==null){reject(response);return;}
  185.  
  186. groups.forEach(gr=>{
  187. if(gr.username==name){
  188. gr.userId=mat[1];
  189. gr.useridUuid=mat[2];
  190. }
  191. });
  192. GM.setValue("groups", JSON.stringify(groups));
  193. resolve(mat[1]);
  194. }
  195. });
  196. });
  197. }
  198. function fillSubFolder(groupID, type,name) { //async+callback //type =[collection,gallery]
  199.  
  200. if(groupID == "undefined"){
  201. $(".dgl2_groupdialog ").css("cursor", "wait");
  202. $(".dgl2_groupButton").css("cursor", "wait");
  203. return grabIDfromPage(name).then(id=>{
  204. groupID=id;
  205. lastGroupClickID=id;
  206. return fillSubFolder(groupID, type,name);
  207. }).catch(erg=>{
  208. console.log("Error at folder ID fetch "+name + " " +erg);
  209. $(".dgl2_groupdialog ").css("cursor", "pointer");
  210. return null;
  211. });
  212. }
  213.  
  214. return new Promise(function (resolve, reject) {
  215. GM.xmlHttpRequest({
  216. method: "GET",
  217. url: "https://www.deviantart.com/_napi/shared_api/deviation/group_folders?groupid=" + groupID + "&type=" + type,
  218. onerror: function (response) {
  219. reject(response);
  220. },
  221. onload: async function (response) {
  222. //console.log(response);
  223. var resp;
  224. try {
  225. resp = JSON.parse(response.responseText);
  226. } catch (ex) {
  227. myAlert("Page problems. Please try again later!<br/>" + ex, "Error");
  228. }
  229.  
  230. insertSubFolders(resp.results);
  231. if(macroMode==1){
  232. for(var gr of macros[macroModeTarget].data){
  233. if(gr.groupID==groupID){
  234. $("button.dgl2_groupButton[folderID='"+gr.folderID+"']").addClass("folderInMacro");
  235. break;
  236. }
  237. }
  238. }
  239. $(".dgl2_groupdialog").css("cursor", "");
  240. $(".dgl2_groupButton").css("cursor", "pointer");
  241. $("div.groupPopup").focus();
  242. resolve(resp.results);
  243.  
  244. }
  245. });
  246. });
  247. }
  248.  
  249. function fillModuleID() { //async+callback //get Module ID for group submission
  250. return new Promise(function (resolve, reject) {
  251. GM.xmlHttpRequest({
  252. method: "GET",
  253. url: "https://www.deviantart.com/" + userName + "/about",
  254. onerror: function (response) {
  255. reject(response);
  256. },
  257. onload: async function (response) {
  258. try {
  259. var resp = (response.responseText);
  260. resp = resp.match(/{\\\"id\\\":(\d+),\\\"type\\\":\\\"group_list_members/i);
  261. if (resp.length > 1) {
  262. moduleID = resp[1];
  263. resolve(moduleID);
  264. } else {
  265. reject(response);
  266. }
  267. } catch (ex) {
  268. reject([ex, response]);
  269. }
  270. }
  271. });
  272. });
  273. }
  274.  
  275. function fillListedGroups(devID) {
  276. return new Promise(function (resolve, reject) {
  277. GM.xmlHttpRequest({
  278. method: "GET",
  279. url: "https://www.deviantart.com/deviation/" + devID + "/groups",
  280. onerror: function (response) {
  281. reject(response);
  282. },
  283. onload: async function (response) {
  284. var tex = response.responseText;
  285. var res = tex.matchAll(/gmi-groupid="(\d+)"/gi);
  286.  
  287. for (var entr of res) {
  288. listedGroups.push(parseInt(entr[1]));
  289. }
  290. // console.log(listedGroups);
  291. resolve(listedGroups);
  292. }
  293. });
  294. });
  295.  
  296. }
  297.  
  298. function requestAddSubmission(token, devID, folderID, groupID, type) { //async+callback //type =[collection,gallery]
  299. var macroFchanged=false;
  300. if (macroMode == 1) {
  301. if(macros[macroModeTarget].data.some(e => e.groupID === groupID)){
  302. macros[macroModeTarget].data.forEach(function(el){
  303. if(el.groupID===groupID){
  304. el.folderID=folderID;
  305. el.folderName=loadedFolders.get(folderID);
  306. }
  307. });//change folder of present group
  308. macroFchanged=true;
  309. }else{//don't add if included already
  310. macros[macroModeTarget].data.push({
  311. folderName:loadedFolders.get(folderID),
  312. folderID: folderID,
  313. groupID: groupID,
  314. type: type
  315. });
  316. }
  317. GM.setValue("macros", JSON.stringify(macros));
  318. }
  319.  
  320. return new Promise(function (resolve, reject) {
  321. var dat = {
  322. "groupid": parseInt(groupID),
  323. "type": type.toString(),
  324. "folderid": parseInt(folderID),
  325. "deviationid": parseInt(devID),
  326. "csrf_token": token.toString()
  327. };
  328. if (macroMode == 1){ //don't submit while adding to macros
  329. resolve({success:true, gname:groupById(groupID).username, fname:loadedFolders.get(folderID),fchanged:macroFchanged});
  330. }else{
  331. GM.xmlHttpRequest({
  332. method: "POST",
  333. url: "https://www.deviantart.com/_napi/shared_api/deviation/group_add",
  334. headers: {
  335. "accept": 'application/json, text/plain, */*',
  336. "content-type": 'application/json;charset=UTF-8'
  337. },
  338. dataType: 'json',
  339. data: JSON.stringify(dat),
  340. onerror: function (response) {
  341. response.gname = groupById(groupID).username;
  342. reject(response);
  343. },
  344. onload: async function (response) {
  345. var resp = JSON.parse(response.responseText);
  346. resp.gname = groupById(groupID).username;
  347. resolve(resp);
  348. }
  349. });
  350. }
  351. });
  352. }
  353.  
  354.  
  355. function playMacro(index) {
  356. macroMode = 2;
  357. var promises = [];
  358. var token = $("input[name=validate_token]").val();
  359. for (var d of macros[index].data) {
  360. promises.push(requestAddSubmission(token,devID, d.folderID, d.groupID, d.type));
  361. }
  362. Promise.all(promises).catch(err => {
  363. alert(macros[index].name + " Error!<br/>" + JSON.stringify(macros[index].data) + " " + JSON.stringify(err), "Error");
  364. }).then(res => {
  365. myAlert(
  366. res.map(obj => {
  367. var retval = "<strong>" + obj.gname + "</strong>: "
  368. if (obj.success) {
  369. retval += "Success! ";
  370. if (obj.needsVote == true) retval += " Vote pending";
  371. } else {
  372. retval += "Error! ";
  373. if (obj.errorDetails) retval += obj.errorDetails;
  374. }
  375. return retval;
  376. }).join("<br/>"), "Play Macro " + macros[macroModeTarget].name);
  377. })
  378. macroMode = 0;
  379. }
  380. //event handlers
  381. function Ev_groupClick(event) { //event propagation
  382. event.stopPropagation();
  383.  
  384. var targetBut = $(event.target).closest(".dgl2_groupButton");
  385. var groupID = targetBut.attr("groupID");
  386. var groupNam = targetBut.attr("groupname");
  387.  
  388. if(groupID=="undefined"){
  389. grabIDfromPage(groupNam).then(id=>{
  390. $(event.target).closest(".dgl2_groupButton").attr("groupID",id);
  391. Ev_groupClick(event);
  392. });
  393. return;
  394. }
  395. var elInd;
  396. switch (colMode) {
  397. case 1: //add
  398. elInd = collections[colModeTarget].groups.indexOf(groupID);
  399. if (elInd == -1) {
  400. collections[colModeTarget].groups.push(groupID);
  401. GM.setValue("collections", JSON.stringify(collections));
  402. targetBut.addClass("dgl2_inCollection");
  403. }
  404. break;
  405. case 2: //remove
  406. switch (colListMode) {
  407. case 0: //collection
  408. elInd = collections[colModeTarget].groups.indexOf(groupID);
  409. if (elInd > -1) {
  410. collections[colModeTarget].groups.splice(elInd, 1);
  411. GM.setValue("collections", JSON.stringify(collections));
  412. }
  413. insertFilteredGroups(colModeTarget);
  414. break;
  415. case 1: //macro
  416. for (elInd = 0; elInd < macros[macroModeTarget].data.length; ++elInd) {
  417. if (macros[macroModeTarget].data[elInd].groupID == groupID) break;
  418. }
  419.  
  420. if (elInd < macros[macroModeTarget].data.length) {
  421. macros[macroModeTarget].data.splice(elInd, 1);
  422. GM.setValue("macros", JSON.stringify(macros));
  423. }
  424. insertMacroGroups(macroModeTarget);
  425. break;
  426. }
  427. break;
  428. case 0:
  429. default:
  430. if (targetBut.attr("type") == "group") {
  431. lastGroupClickID=targetBut.attr("groupID");
  432. fillSubFolder(groupID, "gallery",groupNam);
  433. $("span.dgl2_titleText").text("< Add to " + targetBut.attr("groupName"));
  434. $("span.dgl2_descr").text("Add this deviation to a group folder");
  435. //Add this deviation to a group folder
  436. } else if (targetBut.attr("type") == "folder") {
  437. var token = $("input[name=validate_token]").val();
  438. requestAddSubmission(token, devID, targetBut.attr("folderID"), targetBut.attr("groupID"), targetBut.attr("folderType")).then(function (arg) {
  439. if (arg.success == true) {
  440. if(macroMode==1){
  441. if(arg.fchanged){
  442. myAlert(arg.gname+" target folder changed to "+arg.fname, "Info");
  443. }else{
  444. myAlert(arg.gname+"/"+arg.fname+" added to macro", "Info");
  445. }
  446. insertMacros();//update titles
  447. insertGroups();//go back to groups view
  448. }else{
  449. if (arg.needsVote) {
  450. myAlert("Success! Submission pending group's vote", "Info");
  451. } else {
  452. myAlert("Success! Submission added to group", "Info");
  453. }
  454. }
  455. } else {
  456. // myAlert("Error! Something went wrong:<br/>devID: "+devID+"<br/>" + JSON.stringify(arg), "Error");
  457. throw arg;
  458. }
  459. /*
  460. deviationGroupCount: 1
  461. needsVote: true
  462. */
  463. }).catch(function (arg) {
  464. console.log("dev_group_list_2 Error: ",arg);
  465. myAlert("deviation-ID: "+devID+"<br/>"+
  466. "Group-Name: "+(arg.gname?arg.gname:"Unknown")+"<br/>"+
  467. (arg.errorDescription?arg.errorDescription:"Unexpected error.")+"<br/>"+
  468. (arg.errorDetails?arg.errorDetails:JSON.stringify(arg)),
  469. (arg.error?arg.error:"Error"));
  470. });
  471. }
  472. }
  473. }
  474. function Ev_ContextSubmit(event){
  475. event.stopPropagation();
  476. event.preventDefault();
  477. event.target=$("#dgl2_grContext select option:selected").get(0);
  478. Ev_groupClick(event);
  479. $("#dgl2_grContext").hide().find("select").empty();
  480. }
  481. function Ev_groupContext(event){
  482. event.stopPropagation();
  483. event.preventDefault();
  484. var el=$("#dgl2_grContext");
  485. if(el.length==0){
  486. 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);
  487. el.find("button").click(Ev_ContextSubmit);
  488. }
  489. el.find("select").hide();
  490. el.finish().show().css({
  491. top: event.pageY + "px",
  492. left: event.pageX + "px"
  493. });
  494. var groupID=$(event.target).closest("button.dgl2_groupButton").attr("groupid")
  495. fillSubFolder(groupID, "gallery",$(event.target).closest("button.dgl2_groupButton").attr("groupname")).then(function(){
  496. el.find("select").show().focus().get(0).selectedIndex = 0;
  497. });
  498.  
  499. }
  500.  
  501. function switchColList() {
  502. switch (colListMode) {
  503. case 0:
  504. colListMode = 1;
  505. $("span.dgl2_CollTitle").html("Macros");
  506. insertMacros();
  507. break;
  508. case 1:
  509. $("span.dgl2_CollTitle").html("Collections");
  510. colListMode = 0;
  511. insertCollections();
  512. }
  513. }
  514.  
  515. function highlightLetter(which){
  516.  
  517. $(".dgl2_letterfound").removeClass("dgl2_letterfound");
  518. $(".dgl2_groupdialog button.dgl2_groupButton[groupName^='"+which+"' i]").addClass("dgl2_letterfound").focus();
  519. $(".dgl2_groupdialog button.dgl2_groupButton[folderName^='"+which+"' i]").addClass("dgl2_letterfound").focus();
  520. }
  521.  
  522. function Ev_colListClick(event) {
  523. event.stopPropagation();
  524.  
  525. if ($(event.target).closest(".dgl2_CollTitleBut").length > 0) {
  526. switchColList();
  527. }
  528. var id = $(event.target).closest("li").first().attr("colID");
  529. if (id == undefined && $(event.target).closest("button").hasClass("dgl2_topBut")) id = 0;
  530. else if (id == undefined) return;
  531. var clasNam = $(event.target).closest("button").attr("class");
  532. var index;
  533. if (colListMode == 0) index = colIndexById(id);
  534. else if (colListMode == 1) index = makIndexById(id);
  535. $("div.dgl2_groupWrapper").removeClass("dgl2_addGroup").removeClass("dgl2_remGroup");
  536. $("button.dgl2_inCollection").removeClass("dgl2_inCollection");
  537. var el;
  538.  
  539. if (clasNam) clasNam = clasNam.replace(" dgl2_topBut", "");
  540. switch (clasNam) {
  541. case "dgl2_export":
  542. var obj = {
  543. collections: collections,
  544. collectionOrder: collectionOrder,
  545. macros: macros,
  546. macroOrder: macroOrder
  547. };
  548. var d = new Date();
  549. 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);
  550. download(JSON.stringify(obj), "dev_group_list2_data_" + dat + ".txt");
  551. break;
  552. case "dgl2_import":
  553. upload().then(function (imp) {
  554. try {
  555. var obj = JSON.parse(imp);
  556. if (obj.macros && obj.macroOrder) {
  557. macros = obj.macros;
  558. macroOrder = obj.macroOrder;
  559. }
  560. if (obj.collections && obj.collectionOrder) {
  561. collections = obj.collections;
  562. collectionOrder = obj.collectionOrder;
  563. } else if (obj[0] && obj[0][0].indexOf("_grouplist") != -1) { //v1 compatibility mode
  564. collections = [{
  565. id: 0,
  566. name: "all",
  567. groups: [],
  568. showing:1
  569. }];
  570. var oldList = obj[1][1].split("\u0002");
  571. for (var lists of oldList) {
  572. var oldCol = lists.split("\u0001");
  573. var newCol = [];
  574. for (var nams of oldCol) {
  575. el = $("button[groupName='" + nams + "']").attr("groupID");
  576. if (el != undefined) {
  577. newCol.push(el);
  578. }
  579. }
  580. collections.push({
  581. id: getLowestFree(collections),
  582. name: oldCol[0],
  583. groups: newCol,
  584. showing:1
  585. });
  586. }
  587. } else {
  588. throw "No collections found!";
  589. }
  590. } catch (ex) {
  591. myAlert("Not a valid dev_group_list2 file!<br/>" + ex, "Error");
  592. return;
  593. }
  594.  
  595. GM.setValue("collectionOrder", JSON.stringify(collectionOrder));
  596. GM.setValue("collections", JSON.stringify(collections));
  597. GM.setValue("macroOrder", JSON.stringify(macroOrder));
  598. GM.setValue("macros", JSON.stringify(macros));
  599. myAlert("Import successfull!", "Info");
  600. insertGroups();
  601. insertCollections();
  602. if (colListMode != 0) switchColList();
  603. });
  604. break;
  605. case "dgl2_add":
  606. insertGroups();
  607. switch (colListMode) {
  608. case 0: //collection
  609. $("span.dgl2_descr").text("Add groups to the collection " + collections[index].name);
  610. colMode = 1;
  611. colModeTarget = index;
  612. $("div.dgl2_groupWrapper").addClass("dgl2_addGroup");
  613. for (el of collections[index].groups) {
  614. $("button.dgl2_groupButton[groupID=" + el + "]").addClass("dgl2_inCollection");
  615. }
  616. break;
  617. case 1:
  618. colMode = 0;
  619. insertGroups();
  620. $("div.dgl2_groupWrapper").addClass("dgl2_addGroup");
  621. $("span.dgl2_descr").text("macro " + macros[index].name + " is recording.");
  622. macroMode = 1;
  623. macroModeTarget = index;
  624. break;
  625. }
  626. break;
  627. case "dgl2_sub":
  628. switch (colListMode) {
  629. case 0: //collection
  630. insertFilteredGroups(index);
  631. $("span.dgl2_descr").text("Remove groups from the collection " + collections[index].name);
  632. colMode = 2;
  633. colModeTarget = index;
  634. $("div.dgl2_groupWrapper").addClass("dgl2_remGroup");
  635. break;
  636. case 1: //macro
  637. insertMacroGroups(index);
  638. $("span.dgl2_descr").text("Remove groups from the macro " + macros[index].name);
  639. colMode = 2;
  640. macroModeTarget = index;
  641. $("div.dgl2_groupWrapper").addClass("dgl2_remGroup");
  642. break;
  643. }
  644. break;
  645. case "dgl2_del":
  646. var con;
  647. switch (colListMode) {
  648. case 0: //collection
  649. myConfirm("Delete Collection " + collections[index].name + " ?", "Collection").then(con => {
  650. if (!con) return;
  651. collections.splice(index, 1);
  652. collectionOrder.splice(collectionOrder.indexOf("dgl2item-" + id), 1);
  653.  
  654. GM.setValue("collectionOrder", JSON.stringify(collectionOrder));
  655. GM.setValue("collections", JSON.stringify(collections));
  656.  
  657. insertGroups();
  658. insertCollections();
  659. });
  660. break;
  661. case 1: //macro
  662. myConfirm("Delete Macro " + collections[index].name + " ?", "Macro").then(con => {
  663. if (!con) return;
  664. macros.splice(index, 1);
  665. macroOrder.splice(macroOrder.indexOf("dgl2item-" + id), 1);
  666.  
  667. GM.setValue("macroOrder", JSON.stringify(macroOrder));
  668. GM.setValue("macros", JSON.stringify(macros));
  669.  
  670. insertGroups();
  671. insertMacros();
  672. });
  673. break;
  674. }
  675. break;
  676. case "dgl2_new":
  677. switch (colListMode) {
  678. case 0: //collection
  679. el = {
  680. name: "New Collection",
  681. groups: [],
  682. showing:1
  683. };
  684. el.id = getLowestFree(collections);
  685. collections.push(el);
  686. collectionOrder.push("dgl2item-" + el.id);
  687.  
  688. GM.setValue("collectionOrder", JSON.stringify(collectionOrder));
  689. GM.setValue("collections", JSON.stringify(collections));
  690.  
  691. insertGroups();
  692. insertCollections();
  693. break;
  694. case 1: //macros
  695. el = {
  696. name: "New Macro",
  697. data: []
  698. };
  699. el.id = getLowestFree(macros);
  700. macros.push(el);
  701. macroOrder.push("dgl2item-" + el.id);
  702.  
  703. GM.setValue("macroOrder", JSON.stringify(macroOrder));
  704. GM.setValue("macros", JSON.stringify(macros));
  705.  
  706. insertGroups();
  707. insertMacros();
  708. break;
  709. }
  710. break;
  711. case "dgl2_visible":
  712.  
  713. switch (colListMode) {
  714. case 0: //collection
  715. if(!collections[index].hasOwnProperty("showing"))collections[index].showing=0;
  716. else collections[index].showing=1-collections[index].showing; //toggle 0 and 1
  717. GM.setValue("collections", JSON.stringify(collections));
  718.  
  719. $(event.target).closest("li").attr("active",collections[index].showing);
  720. insertGroups();
  721. break;
  722. case 1: //macro
  723. //donothing
  724. break;
  725. }
  726. break;
  727. case "dgl2_edit":
  728. var nam;
  729. switch (colListMode) {
  730. case 0: //collection
  731. myPrompt("Please enter a new collection name!", "Change Collection Name", collections[index].name).then(nam => {
  732. if (!nam) return;
  733. collections[index].name = nam;
  734. GM.setValue("collections", JSON.stringify(collections));
  735. insertCollections();
  736. });
  737. break;
  738. case 1: //macro
  739. myPrompt("Please enter a new macro name!", "Change Macro Name", macros[index].name).then(nam => {
  740. if (!nam) return;
  741. macros[index].name = nam;
  742. GM.setValue("macros", JSON.stringify(macros));
  743. insertMacros();
  744. });
  745. break;
  746. }
  747. break;
  748. case undefined:
  749. default:
  750. switch (colListMode) {
  751. case 0: //collection
  752. colMode = 0;
  753. insertFilteredGroups(index);
  754. break;
  755.  
  756. case 1: //macro
  757. myConfirm("Do you want to add this to the following groups?<br/>" + macros[index].data.map(obj => {
  758. return groupById(obj.groupID).username
  759. }).join(", "), "Submit to Groups").then(con => {
  760. if (!con) return;
  761. macroMode = 2;
  762. playMacro(index);
  763. });
  764. break;
  765. }
  766.  
  767. }
  768. }
  769.  
  770. function Ev_getGroupClick() {
  771.  
  772. if(fetchingGroups)return;
  773. fetchingGroups=true;
  774.  
  775. $("span.dgl2_descr").text("Loading Module ID...");
  776. $("#dgl2_refresh").css("cursor", "pointer");
  777. fillModuleID().then(function () {
  778. $("span.dgl2_descr").text("Loading List of Groups...");
  779. $("#dgl2_refresh").css("cursor", "wait");
  780. return fillGroups(0);
  781. }).then(function () {
  782. $("span.dgl2_descr").text("Add this deviation to one of your groups");
  783. }).catch(function (e) {
  784. console.log("dev_group_list_2 error:", e);
  785. }).finally(function(){
  786. fetchingGroups=false;
  787. });
  788. }
  789. //templates
  790. function getGroupTemplate(name, img, id) { //return HTML string
  791. //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>";
  792. return "<button class='dgl2_groupButton' groupID=" + id + " type='group' groupName='" + escapeHtml(name) + "' >" +
  793. " <div class='dgl2_imgwrap'>" +
  794. " <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' class='dgl2_hover'>" +
  795. " <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>" +
  796. " </svg>" +
  797. " <img class='dgl2_group_image' title='" + escapeHtml(name) + "' src='" + img + "'/>" +
  798. " </div>" +
  799. " <div class='dgl2_groupName'>" + escapeHtml(name) + "</div>" +
  800. "</button>";
  801. }
  802.  
  803. function getSubFolderOptionTemplate(name, devCnt, grID, foID, foType, img){
  804. //text option only needs name,IDs and type
  805. return "<option class='dgl2_groupButton' groupID="+grID+" folderName='" + escapeHtml(name) + "' folderID=" + foID + " folderType='" + foType + "' type='folder'>"+escapeHtml(name)+"</option>";
  806. }
  807. function getSubFolderTemplate(name, devCnt, grID, foID, foType, img) { //return HTML string
  808. loadedFolders.set(""+foID,name);
  809. //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>";
  810. var imgstring;
  811. if (img.textContent) { //journal
  812. imgstring = "<p class='dgl2_journalSubF'>" + img.textContent.excerpt + "</p>";
  813. } else {
  814. var i;
  815. var cstr = "";
  816. img = img.media;
  817. for (i of img.types) {
  818. if (i.c != undefined) {
  819. cstr = i.c;
  820. break;
  821. }
  822. }
  823. if (cstr == "") {
  824. for (i of img.types) {
  825. if (i.s != undefined) {
  826. cstr = i.s;
  827. break;
  828. }
  829. }
  830. }
  831. if (img.baseUri) imgstring = img.baseUri + "/";
  832. if (img.prettyName) imgstring += cstr.replace("<prettyName>", img.prettyName);
  833. if (img.token) imgstring += "?token=" + img.token[0];
  834. imgstring = "<img class='dgl2_group_image' title='" + escapeHtml(name) + "' src='" + imgstring + "'/>";
  835. }
  836.  
  837. return "<button class='dgl2_groupButton' groupID=" + grID + " folderName='" + escapeHtml(name) + "' folderID=" + foID + " folderType='" + foType + "' type='folder'>" +
  838. " <div class='dgl2_imgwrap'>" +
  839. " <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' class='dgl2_hover'>" +
  840. " <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>" +
  841. " </svg>" +
  842. imgstring +
  843. " </div>" +
  844. " <div class='dgl2_groupName'>" + escapeHtml(name) + "</div>" +
  845. " <div class='dgl2_devCnt'>" + devCnt + "</div>" +
  846. "</button>";
  847. }
  848.  
  849. function getSearchBarTemplate() {
  850. return "<input id='dgl2_searchbar' type='text' placeholder='Search'/>";
  851. }
  852.  
  853. function getCollectionColTemplate() {
  854. return "<div id='dgl2_CollTab'><div class='dgl2_CollTitleBut'>" + svgTurnArrow + "<span class='dgl2_CollTitle'>" +
  855. "Collections</span></div><div class='buttons'></div><ul class='sortableList'></ul></div>";
  856. }
  857.  
  858. function getAddButTemplate() {
  859. var sty = getComputedStyle(document.body);
  860. 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'>" +
  861. " <g style='stroke:" + sty.color + ";stroke-width:15;'>" +
  862. " <rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
  863. " <line x1='86' y1='30' x2='86' y2='142' />" +
  864. " <line x1='30' y1='86' x2='142' y2='86' />" +
  865. " </g>" +
  866. "</svg></button>";
  867. }
  868.  
  869. function getRecButTemplate() {
  870. var sty = getComputedStyle(document.body);
  871. 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'>" +
  872. " <g style='stroke:" + sty.color + ";stroke-width:15;'>" +
  873. " <rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
  874. " <ellipse cx='86' cy='86' rx='40' ry='40'></ellipse>" +
  875. " </g>" +
  876. "</svg></button>";
  877. }
  878.  
  879. function getNewColTemplate() {
  880. var sty = getComputedStyle(document.body);
  881. 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'>" +
  882. " <g style='stroke:" + sty.color + ";stroke-width:15;'>" +
  883. " <rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
  884. " <line x1='86' y1='30' x2='86' y2='142' />" +
  885. " <line x1='30' y1='86' x2='142' y2='86' />" +
  886. " </g>" +
  887. "</svg></button>";
  888. }
  889.  
  890. function getSubButTemplate() {
  891. var sty = getComputedStyle(document.body);
  892. 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'>" +
  893. " <g style='stroke:" + sty.color + ";stroke-width:15;'>" +
  894. " <rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
  895. " <line x1='30' y1='86' x2='142' y2='86' />" +
  896. " </g>" +
  897. "</svg></button>";
  898. }
  899.  
  900. function getRefreshButTemplate() {
  901. var sty = getComputedStyle(document.body);
  902. 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>" +
  903. " <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>" +
  904. "<rect x='00' y='00' style='stroke:" + sty.color + ";stroke-width:5;opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
  905. "<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";
  906. }
  907.  
  908. function getDelButTemplate() {
  909. var sty = getComputedStyle(document.body);
  910. 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'>" +
  911. "<g style='stroke-width:5;stroke:" + sty.color + ";fill:none'>" +
  912. " <rect x='00' y='00' style='opacity:0.1' rx='50' ry='50' width='172' height='172'></rect>" +
  913. " <rect x='50' y='50' rx='5' ry='5' width='72' height='92'></rect>" +
  914. " <rect x='65' y='35' rx='5' ry='5' width='42' height='15'></rect>" +
  915. " <line x1='40' y1='50' x2='132' y2='50'/>" +
  916. " <line x1='70' y1='132' x2='70' y2='60'/>" +
  917. " <line x1='86' y1='132' x2='86' y2='60' />" +
  918. " <line x1='104' y1='132' x2='104' y2='60' />" +
  919. " </g>" +
  920. "</svg></button>";
  921. }
  922.  
  923. function getExportButTemplate() {
  924. 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">' +
  925. ' <g transform="translate(0,-291.70832)">' +
  926. ' <path style="fill:#008000;"' +
  927. ' d="M 0.26458332,291.9729 H 5.0270831 v 4.7625 H 0.79345641 l -0.52887309,-0.51217 z" />' +
  928. ' <rect style="fill:#ffffff;" width="3.7041667" height="1.8520833" x="0.79374999" y="292.23749" />' +
  929. ' <rect style="fill:#ffffff;" width="2.6458333" height="1.3229259" x="1.3229166" y="295.41248" />' +
  930. ' <rect style="fill:#008000;" width="0.52916676" height="0.79375702" x="2.9104166" y="295.67706" />' +
  931. ' </g>' +
  932. '</svg></button>';
  933. }
  934.  
  935. function getImportButTemplate() {
  936. 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">' +
  937. ' <g transform="translate(0,-291.70832)">' +
  938. ' <rect style="fill:#806600;" width="3.96875" height="2.9104137" x="0.52916664" y="293.03125" />' +
  939. ' <path style="fill:#ffcc00;" d="m 0.52916666,295.94165 0.79375004,-2.11666 h 3.96875 l -0.7937501,2.11666 z" />' +
  940. ' <rect style="fill:#00DD00;" width="0.52916664" height="1.0583333" x="3.4395833" y="292.50208" />' +
  941. ' <path style="fill:#00DD00;" d="m 3.175,292.50207 0.5291667,-0.52917 0.5291667,0.52917 z" />' +
  942. ' </g>' +
  943. '</svg></button>';
  944. }
  945.  
  946. function getTitleBarTemplate() {
  947. return '<span class="dgl2_titleText">Add to Group</span>' +
  948. '<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>' +
  949. '<span class="dgl2_descr">Add this deviation to one of your groups</span>'
  950. }
  951.  
  952. function getEditButTemplate() {
  953. 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>';
  954. }
  955.  
  956. function getVisibleButTemplate() {
  957. return `<button title="Hide/show groups within collection" class="dgl2_visible">
  958. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 250" stroke-width="20">
  959. <defs>
  960. <radialGradient id="c1" cx="0.5" cy="0.5" r="0.5">
  961. <stop offset="0" stop-color="#ffffff" />
  962. <stop offset=".5" stop-color="hsl(40, 60%, 60%)" />
  963. <stop offset="1" stop-color="#3dff3d" />
  964. </radialGradient>
  965. </defs>
  966. <ellipse role="sclera" cx="250" cy="125" rx="200" ry="100" fill="white"/>
  967. <ellipse role="iris" cx="250" cy="125" rx="95" ry="95" stroke="black" fill="url(#c1)"/>
  968. <ellipse role="pupil" cx="250" cy="125" rx="50" ry="50" stroke="none" fill="black"/>
  969. <ellipse role="light" cx="200" cy="80" rx="50" ry="50" stroke="none" fill="#fffffaee"/>
  970. <ellipse role="outline" cx="250" cy="125" rx="200" ry="100" stroke="black" fill="none"/>
  971. </svg>
  972. </button>`;
  973. }
  974.  
  975. function addCss() {
  976. if ($("#dgl2_style").length > 0) return;
  977. var style = $("<style type='text/css' id='dgl2_style'></style>");
  978.  
  979. //searchbar
  980. style.append("#dgl2_searchbar{background: var(--L3);box-shadow: inset 0 1px 4px 0 rgba(0,0,0,.25);padding: 5px;width: 50%;}");
  981.  
  982. //right collection column
  983. style.append(`
  984. #dgl2_CollTab{padding-left:15px; flex:1;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;}
  985. #dgl2_CollTab ul{overflow: auto;list-style: none;padding-left: 10px;margin-top: 20px;}
  986. #dgl2_CollTab ul li{cursor:pointer;padding:2px;display:grid;grid-template: auto/7px auto 16px 16px 16px 16px 16px;}
  987. #dgl2_CollTab ul li:hover{background:linear-gradient(to right, rgba(255,0,0,0.1), rgba(255,0,0,0));}
  988. #dgl2_CollTab button{cursor:pointer;border-width: 0;padding: 0;margin: 0;background-color: transparent;}
  989. #dgl2_CollTab button:hover rect{fill:red;user-select: none; }
  990. #dgl2_refresh{margin-left:auto;border-width:0px;background:transparent;cursor:pointer}
  991. #dgl2_CollTab div.buttons{display: inline-block;vertical-align: middle;margin: 0 5px;}
  992. div.dgl2_groupCol{width:700px;flex:3;overflow-y:auto}
  993. div.dgl2_groupdialog{display:flex;}
  994. div.dgl2_titlebar{display:flex;justify-content:space-between;margin: 0 20px;}
  995. #dgl2_refresh:hover rect{fill:red;}
  996. #dgl2_refresh rect{fill:rgba(255,0,0,0.1);}
  997. #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=);}
  998. 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; }
  999. div.dgl2_imgwrap{ position: relative;}
  1000. svg.dgl2_hover{ position: absolute; left: 50%; width:50%; height:50%; transform: translate(-50%,50%); opacity:0; transition: ease 0.25s; }
  1001. img.dgl2_group_image{ opacity:1; width:100px; height:50px; transition: ease 0.25s; border-radius:2px; }
  1002. div.dgl2_groupName{ font-family: CalibreSemiBold; font-size: 15px; line-height: 15px; font-weight: 600; letter-spacing: 0.3px; word-break: break-word; }
  1003. button.dgl2_groupButton:hover{background-color:rgba(255,255,255,0.8);}
  1004. button.dgl2_groupButton:hover svg.dgl2_hover{opacity:1;}
  1005. button.dgl2_groupButton:hover img.dgl2_group_image{opacity:0.3;}
  1006. button.dgl2_groupButton:active{background-color:rgba(255,255,255,0.3);}
  1007. button.dgl2_groupButton.dgl2_inGroup{background-image:linear-gradient(red, transparent);}
  1008. span.dgl2_titleText{cursor:pointer;}
  1009. 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;}
  1010. button.dgl2_edit{height:0.5em;}
  1011. button.dgl2_edit:hover path{fill:red;}
  1012. #dgl2_CollTab button svg{width: 90%;}
  1013. #dgl2_CollTab button.dgl2_visible:hover ellipse{stroke: red;}
  1014. div.dgl2_addGroup{background-color: rgba(0, 255, 0, 0.3);}
  1015. div.dgl2_remGroup{background-color: rgba(255, 0, 0, 0.3);}
  1016. button.dgl2_inCollection{background-color: rgba(15, 104, 5, 0.7);}
  1017. button.dgl2_export:hover ,button.dgl2_import:hover {opacity:0.8}
  1018. button.dgl2_export:active ,button.dgl2_import:active {opacity:1}
  1019. div.dgl2_CollTitleBut{cursor:pointer;display:inline-block;}
  1020. div.dgl2_CollTitleBut:hover span{color:red;}
  1021. .dgl2_journalSubF { overflow: hidden; height: 50px; font-size: xx-small; text-align: left; margin-bottom: 5px;}
  1022. .groupPopup .ui-widget-content{background-color:#afcaa9 !important;color:black;}
  1023. button.dgl2_letterfound {background-color: rgba(105, 14, 5, 0.7);}
  1024. .folderInMacro {background-color:rgba(205, 24, 25, 0.6)!important;}
  1025. @keyframes shadowPulse {0% {box-shadow: 0px 0px 50px 20px #f00;} 100% {box-shadow: 0px 0px 50px 20px #ff000000;}}
  1026. .shadow-pulse {animation-name: shadowPulse;animation-duration: 0.5s;animation-iteration-count: infinite;animation-timing-function: linear; animation-direction:alternate;}
  1027. #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;}
  1028. #dgl2_grContext select{background: none; border: none;width:100%;margin:5px 0;}
  1029. #dgl2_grContext select option{background-color: #ddffd8;}
  1030. #dgl2_grContext select option:nth-child(even) {background-color: #6fd061;}
  1031. #dgl2_grContext select option::selection {color: red;background: yellow;}
  1032. #dgl2_grContext button {cursor:pointer; width: 100%;background-color: #408706;color: white;border: 1px outset black;border-radius: 5px;}
  1033. #dgl2_grContext button:hover { background-color: #608706;}
  1034. #dgl2_CollTab li[active='0'] button.dgl2_visible ellipse[role='iris'] { fill: lightgray;stroke:lightgray}
  1035. #dgl2_CollTab li[active='0'] button.dgl2_visible ellipse { fill: lightgray;}
  1036. #dgl2_CollTab li button{display: flex; height: 100%;align-items: center;}
  1037. `);
  1038.  
  1039. // style.append(".noTitleDialog .ui-dialog-titlebar {display:none}");
  1040.  
  1041.  
  1042. $("head").append(style);
  1043.  
  1044. $("head").append(
  1045. '<link ' +
  1046. 'href="//ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/le-frog/jquery-ui.min.css" ' +
  1047. 'rel="stylesheet" type="text/css">'
  1048. );
  1049.  
  1050. }
  1051. //filling GUI DOM
  1052. function hideHiddenGroups(){
  1053. collections.forEach(col=>{
  1054. if(col.showing==0){
  1055. col.groups.forEach(grID=>{
  1056. $("button[groupID='" + grID + "']").hide();
  1057. });
  1058. }
  1059. });
  1060. }
  1061.  
  1062. function insertFilteredGroups(id) {
  1063. insertGroups();
  1064. lastFilter = id;
  1065. var allButs = $("button[type='group']");
  1066. if (collections[id].groups.length == 0) {
  1067. allButs.show();
  1068. } else {
  1069. allButs.hide();
  1070. for (var grID of collections[id].groups) {
  1071. $("button[groupID='" + grID + "']").show();
  1072. }
  1073. }
  1074. hideHiddenGroups()
  1075. }
  1076.  
  1077. function insertMacroGroups(id) {
  1078. insertGroups();
  1079. lastFilter = id;
  1080. var allButs = $("button[type='group']");
  1081. if (macros[id].data.length == 0) {
  1082. allButs.show();
  1083. } else {
  1084. allButs.hide();
  1085. for (var grID of macros[id].data) {
  1086. $("button[groupID='" + grID.groupID + "']").show();
  1087. }
  1088. }
  1089. }
  1090.  
  1091. function insertMacros() {
  1092. var coltab = 0;
  1093. var toAffect = $("div.dgl2_groupdialog").not("[dgl2]").attr("dgl2", 1);
  1094. if (toAffect.length > 0) {
  1095. coltab = $(getCollectionColTemplate());
  1096. toAffect.append(coltab);
  1097. coltab.find("div.buttons")
  1098. .append(getNewColTemplate())
  1099. .append(getExportButTemplate())
  1100. .append(getImportButTemplate());
  1101.  
  1102. coltab.click(Ev_colListClick);
  1103. } else {
  1104. coltab = $("#dgl2_CollTab");
  1105. }
  1106. var colList = coltab.find("ul");
  1107. colList.empty();
  1108. var el,descr;
  1109. for (var col of macros) {
  1110. descr="";
  1111. for(var gr of col.data){
  1112. descr+=groupById(gr.groupID).username+"/"+gr.folderName+"\n";
  1113. }
  1114. el = "<li colid=" + col.id + " title='"+escapeHtml(descr)+"' id='dgl2item-" + col.id + "'><span class='handle'></span><span>" + col.name + "</span>" + getEditButTemplate();
  1115. el += getRecButTemplate() + getSubButTemplate() + getDelButTemplate();
  1116. el += "</li>";
  1117. colList.append(el);
  1118. }
  1119. if (macroOrder.length > 0) {
  1120. $.each(macroOrder, function (i, position) {
  1121. var $target = colList.find('#' + position);
  1122. $target.appendTo(colList); // or prependTo for reverse
  1123. });
  1124. }
  1125. $(".sortableList").sortable({
  1126. revert: true,
  1127. cursor: 'move',
  1128. axis: 'y',
  1129. handle: 'span.handle',
  1130. update: function (event, ui) {
  1131. macroOrder = $(this).sortable('toArray');
  1132. GM.setValue("macroOrder", JSON.stringify(macroOrder));
  1133. }
  1134. });
  1135. }
  1136.  
  1137. function insertCollections() {
  1138. var coltab = 0;
  1139. var toAffect = $("div.dgl2_groupdialog").not("[dgl2]").attr("dgl2", 1); //group submission container
  1140. if (toAffect.length > 0) {
  1141. coltab = $(getCollectionColTemplate());
  1142. toAffect.append(coltab);
  1143. coltab.find("div.buttons")
  1144. .append(getNewColTemplate())
  1145. .append(getExportButTemplate())
  1146. .append(getImportButTemplate());
  1147.  
  1148. coltab.click(Ev_colListClick);
  1149. } else {
  1150. coltab = $("#dgl2_CollTab");
  1151. }
  1152.  
  1153.  
  1154. var colList = coltab.find("ul");
  1155. colList.empty();
  1156. var el;
  1157. for (var col of collections) {
  1158. el=`<li colid=${col.id} active='${col.showing}' id='dgl2item-${col.id}'><span class='handle'></span><span>${col.name}</span>${getEditButTemplate()}`;
  1159. if (col.id > 0) el += getVisibleButTemplate()+ getAddButTemplate() + getSubButTemplate() + getDelButTemplate();
  1160. el += "</li>";
  1161. colList.append(el);
  1162. }
  1163. if (collectionOrder.length > 0) {
  1164. $.each(collectionOrder, function (i, position) {
  1165. var $target = colList.find('#' + position);
  1166. $target.appendTo(colList); // or prependTo for reverse
  1167. });
  1168. }
  1169. $(".sortableList").sortable({
  1170. revert: true,
  1171. cursor: 'move',
  1172. axis: 'y',
  1173. handle: 'span.handle',
  1174. update: function (event, ui) {
  1175. collectionOrder = $(this).sortable('toArray');
  1176. GM.setValue("collectionOrder", JSON.stringify(collectionOrder));
  1177. }
  1178. });
  1179. }
  1180.  
  1181. function insertSearchBar() {
  1182. var bar = $(getSearchBarTemplate());
  1183. var refrBut = $(getRefreshButTemplate());
  1184.  
  1185. $("div.dgl2_titlebar").append(refrBut).append(bar);
  1186. bar.keyup(function () {
  1187. var search = $(this).val();
  1188. var allButs = $("button[type='group']").show();
  1189. allButs.filter(function () {
  1190. if (lastFilter > 1 && collections[lastFilter].groups.indexOf($(this).attr("groupID")) == -1) return 1;
  1191. if (search == "") return 0;
  1192. var words = search.split(" ");
  1193. for (var word of words) {
  1194. if ($(this).attr('groupName').toLowerCase().indexOf(word) == -1) return 1;
  1195. }
  1196. return 0;
  1197. }).hide();
  1198. });
  1199. //bar.mousedown(function(event){event.stopPropagation(); event.preventDefault();event.target.focus();});
  1200.  
  1201.  
  1202. refrBut.click(Ev_getGroupClick);
  1203.  
  1204. $("span.dgl2_titleText").click(function(){
  1205. //insertGroups();
  1206. $("li[colid="+lastFilter+"]").click();
  1207. // console.log(lastFilter,"li[colid="+lastFilter+"]",$("li[colid="+lastFilter+"]"));
  1208. var lastgrBut=$("button[groupid="+lastGroupClickID+"]")
  1209. lastgrBut[0].scrollIntoView();
  1210. lastgrBut.addClass("shadow-pulse");
  1211. });
  1212. }
  1213. function pulsing(element) {
  1214. element.animate({ opacity: 0 }, 250, function() {
  1215. $(this).animate({ opacity: 1 }, 250, pulsing);
  1216. });
  1217. }
  1218.  
  1219. function insertSubFolders(subfolders) { //fill view with subfolders //subfolders not stored, request when needed
  1220. var buts = $("button.dgl2_groupButton"); //button wrapper
  1221.  
  1222. subfolders.sort(function (l, u) {
  1223. return l.name.toLowerCase().localeCompare(u.name.toLowerCase());
  1224. });
  1225.  
  1226. if (buts.length > 0) {
  1227. var subf
  1228. if($("#dgl2_grContext").is(":visible")){
  1229. var cont=$("#dgl2_grContext select");
  1230. var newopt;
  1231. cont.empty();
  1232. for (subf of subfolders) {
  1233. if (subf.thumb == null) continue; //no thumb=db problem, so also no text entry
  1234. newBut = $(getSubFolderOptionTemplate(subf.name, subf.size, subf.owner.userId, subf.folderId, subf.type, subf.thumb));
  1235. cont.append(newBut);
  1236. }
  1237. if(subfolders.length==0){
  1238. $("#dgl2_grContext span.desc").html("This group does<br/>not allow submissions<br/>using the gallery<br/>system!");
  1239. }else{
  1240. $("#dgl2_grContext span.desc").text("Submit to a Folder");
  1241. }
  1242.  
  1243. }else{
  1244. var par = buts.first().parent();
  1245. var newBut;
  1246. par.empty();
  1247. for (subf of subfolders) {
  1248. if (subf.thumb == null) continue;
  1249. newBut = $(getSubFolderTemplate(subf.name, subf.size, subf.owner.userId, subf.folderId, subf.type, subf.thumb));
  1250. par.append(newBut);
  1251. }
  1252. if(subfolders.length==0){
  1253. par.append("This group does not allow submissions using the gallery system!");
  1254. }
  1255. par.not("[dgl2]").attr("dgl2", 1).click(Ev_groupClick);
  1256. }
  1257. }
  1258. }
  1259.  
  1260. function insertGroups() { //fill view with groups //groups are stored
  1261. lastFilter = 0;
  1262. groups.sort(function (l, u) {
  1263. return l.username.toLowerCase().localeCompare(u.username.toLowerCase());
  1264. });
  1265.  
  1266. $("span.dgl2_titleText").text("Add to Group");
  1267. $("span.dgl2_descr").text("Add this deviation to one of your groups");
  1268.  
  1269. var par = $("div.dgl2_groupWrapper"); //group list wrapper
  1270. var newBut;
  1271. var hasEmptyId=false;
  1272. par.empty();
  1273. for (var gr of groups) {
  1274. newBut = $(getGroupTemplate(gr.username, gr.usericon, gr.userId));
  1275. newBut.contextmenu(Ev_groupContext);
  1276. if(typeof gr.userId=="undefined")hasEmptyId=true;
  1277. //console.log(gr.userId);
  1278. if (listedGroups.includes(parseInt(gr.userId))) {
  1279. newBut.addClass("dgl2_inGroup"); //button div inside wrapper; used in template
  1280. }
  1281. if(par.find("div[groupName='"+gr.username+"']").length==0){
  1282. par.append(newBut);
  1283. }
  1284. }
  1285. par.not("[dgl2]").attr("dgl2", 1).click(Ev_groupClick);
  1286. hideHiddenGroups()
  1287.  
  1288. if(hasEmptyId && !notScanThisInstance){
  1289. requestAllGroupIDs();
  1290. }
  1291. }
  1292. function uniqBy(a, key) {
  1293. let seen = new Set();
  1294. return a.filter(item => {
  1295. let k = key(item);
  1296. return seen.has(k) ? false : seen.add(k);
  1297. });
  1298. }
  1299. var ngrpCnt=0;
  1300. function delayIteratefetchGrId(groups,index,delay){
  1301. if(index<groups.length){
  1302. grabIDfromPage(groups[index].username).then(()=>{
  1303. setTimeout(function(){delayIteratefetchGrId(groups,index+1,delay);},delay);
  1304. });
  1305. }else{
  1306. $("#dgl2_refresh rect").css("fill", "");
  1307. $("#dgl2_refresh").css("cursor", "pointer");
  1308. $("span.dgl2_descr").text("Add this deviation to a group folder");
  1309. }
  1310. }
  1311. function requestAllGroupIDs(){
  1312. var nullgr=groups.filter(gr=>typeof gr.userId=="undefined");
  1313. var remtim=nullgr.length*1.1;
  1314. var remtex=""
  1315. if(remtim<60){remtex="seconds"}
  1316. else if(remtim>=60 && remtim<60*60){remtex="minutes";remtim/=60.0;}
  1317. else if(remtim>=60*60){remtex="hours";remtim/=60.0*60.0;}
  1318. myConfirm(`${nullgr.length} 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").then((choice)=>{
  1319. if(choice){
  1320. ngrpCnt=0;
  1321. delayIteratefetchGrId(nullgr,0,250);
  1322. }
  1323. });
  1324. notScanThisInstance=true;
  1325. }
  1326. function insertHTML() {
  1327.  
  1328. // console.log("check");
  1329. if ($("div.dgl2_groupdialog").length > 0) return;
  1330.  
  1331. // console.log("run");
  1332. addCss();
  1333. $("<div class='dgl2_groupdialog'><div class='dgl2_groupCol'><div class='dgl2_titlebar'></div><div class='dgl2_groupWrapper'></div></div></div>").appendTo($("body"));
  1334. $("div.dgl2_titlebar").html(getTitleBarTemplate());
  1335. insertSearchBar();
  1336.  
  1337.  
  1338. var devInd=location.href.indexOf("?");
  1339. if(devInd==-1){
  1340. devID = location.href.match(/\d+$/)[0];
  1341. }else{
  1342. devID = location.href.substring(0,devInd).match(/\d+$/)[0];
  1343. }
  1344.  
  1345. // console.log(devID);
  1346. userName =$("a.user-link").attr("data-username"); // "dediggefedde";
  1347.  
  1348. var proms=[
  1349. GM.getValue("collections", ""),
  1350. GM.getValue("collectionOrder", ""),
  1351. GM.getValue("macros", ""),
  1352. GM.getValue("macroOrder", "")
  1353. ];
  1354.  
  1355. Promise.all(proms).then(([cols, colOrder,macs,macOrder]) => {
  1356. if (cols != "") {
  1357. collections = JSON.parse(cols);
  1358. }
  1359. collections.forEach(el=>{if(!el.hasOwnProperty("showing")){el.showing=1;};}); //backward-compatibility for collection-showing attribute before v3.0
  1360.  
  1361. if (colOrder != "") {
  1362. collectionOrder = JSON.parse(colOrder);
  1363. }
  1364.  
  1365. if (macs != "") {
  1366. macros = JSON.parse(macs);
  1367. macros.forEach(function(el){el.data=uniqBy(el.data,JSON.stringify);});//unique macros
  1368. }
  1369.  
  1370. if (macOrder != "") {
  1371. macroOrder = JSON.parse(macOrder);
  1372. }
  1373. insertCollections();
  1374. }).catch(function (e) {
  1375. console.log("Error Loading Database:",e);
  1376. return insertCollections();
  1377. });
  1378.  
  1379. fillListedGroups(devID).then(function () {
  1380. return GM.getValue("groups", "");
  1381. }).then(function (grps) {
  1382. if (grps == "") {
  1383. Ev_getGroupClick();
  1384. } else {
  1385. groups = JSON.parse(grps);
  1386. insertGroups();
  1387. }
  1388. }).catch(function (e) {
  1389. console.log("dev_group_list_2 error:", e);
  1390. });
  1391.  
  1392. ; //can run in parallel
  1393.  
  1394. }
  1395.  
  1396. //outdated, now I'm using my own template
  1397. //in case classNames (hopefully) change sometime. Also necessary since browsing=other class names than refreshing site.
  1398. // function classNormalizer() { //_2n5r3 _3Isw- _3yw1l _1F3vX _34hvg
  1399. // // if($("div.dgl2_groupdialog").length>0)return;
  1400. // $("<div class='dgl2_groupdialog'><div class='dgl2_groupCol'><div class='dgl2_titlebar'></div><div class='dgl2_groupWrapper'></div></div></div>").appendTo($("body"));
  1401.  
  1402. // // $("div.dgl2_groupdialog div._13bQf").hide();
  1403. // // $("div.dgl2_groupdialog button._2-StF").hide();
  1404.  
  1405. // // $("span.u1km9, span._3M1Kg").addClass("dgl2_descr"); //refr u1km9 browse _3M1Kg //top description //added by template
  1406. // // $("button._3PBqc, button._3UhBt").addClass("dgl2_groupButton"); //refr _3UhBt _3PBqc, browse _3PBqc //button to add to group //added by template
  1407. // // $("div._1cFkE, div._3Lbo4").addClass("dgl2_groupButDiv"); //refr _1cFkE _11Rtb, browse _3Lbo4 _3EbZ8 //div inside button //outdated
  1408. // // $("div.A9uFL, div.JcCFR").addClass("dgl2_groupWrapper"); //refr A9uFL _2IeoY, browse JcCFR _159Ra //div parent of group buttons
  1409. // // $("div._1HNK0, div._1ezbx").addClass("dgl2_titlebar"); //refr _1HNK0, browse _1ezbx //title bar
  1410. // // $("div._2n5r3, div._2qgZz").addClass("dgl2_groupdialog"); //refr _2n5r3, browse _2qgZz // dialog(opening div) for group submission
  1411. // // $("div._2UK-E, div._2kU4L").addClass("dgl2_groupCol"); //refr _2UK, browse _2kU4L//left column (containing groups)
  1412.  
  1413.  
  1414. // // if($("div.dgl2_titlebar").length==0 && $("div.dgl2_groupCol:not([nogroups])").length>0){
  1415. // // $("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>');
  1416. // // classNormalizer();
  1417. // // }
  1418.  
  1419. // }
  1420.  
  1421. function getLowestFree(collection) {
  1422. collection.sort(function (a, b) {
  1423. return a.id - b.id;
  1424. }); //changing order does not matter thanks to index/order array
  1425. var lowest = -1;
  1426. var i;
  1427. for (i = 0; i < collection.length; ++i) {
  1428. if (collection[i].id != i) {
  1429. lowest = i;
  1430. break;
  1431. }
  1432. }
  1433. if (lowest == -1 && collection.length > 0) {
  1434. lowest = collection[collection.length - 1].id + 1;
  1435. } else if (collection.length == 0) lowest = 0;
  1436. return lowest;
  1437.  
  1438. }
  1439.  
  1440. function escapeHtml (string) {
  1441. return String(string).replace(/[&<>"'`=\/]/g, function (s) {
  1442. return entityMap[s];
  1443. });
  1444. }
  1445. function download(data, filename) {
  1446. var file = new Blob([data], {
  1447. type: "application/json"
  1448. });
  1449. var a = document.createElement("a"),
  1450. url = URL.createObjectURL(file);
  1451. a.href = url;
  1452. a.download = filename;
  1453. document.body.appendChild(a);
  1454. a.click();
  1455. setTimeout(function () {
  1456. document.body.removeChild(a);
  1457. window.URL.revokeObjectURL(url);
  1458. }, 0);
  1459. }
  1460.  
  1461. function upload() {
  1462. return new Promise(function (resolve, reject) {
  1463. var inp = $('<input type="file" id="input">').appendTo("body").click()
  1464. inp.change(function () {
  1465. var reader = new FileReader();
  1466. reader.onload = function (evt) {
  1467. resolve(evt.target.result);
  1468. };
  1469. reader.readAsBinaryString($(this).prop("files")[0]);
  1470. });
  1471. return "";
  1472. });
  1473. }
  1474.  
  1475. function colIndexById(id) {
  1476. for (var i = 0; i < collections.length; ++i) {
  1477. if (collections[i].id == id) return i;
  1478. }
  1479. return -1;
  1480. }
  1481.  
  1482. function makIndexById(id) {
  1483. for (var i = 0; i < macros.length; ++i) {
  1484. if (macros[i].id == id) return i;
  1485. }
  1486. return -1;
  1487. }
  1488.  
  1489. function myAlert(tex, titl = "") {
  1490. var box = $("#dgl2_alertBox");
  1491. if (box.length == 0) box = $("<div id='dgl2_alertBox'></div>").appendTo("body");
  1492. box.html(tex);
  1493. box.attr("title", titl);
  1494. box.dialog({
  1495. width: "50%",
  1496. height: "auto",
  1497. maxHeight: (window.innerHeight * 0.8),
  1498. buttons: {
  1499. Ok: function () {
  1500. $(this).dialog("close");
  1501. }
  1502. }
  1503. });
  1504. }
  1505.  
  1506. function myConfirm(tex, titl = "") {
  1507. var dfd = new $.Deferred();
  1508. var box = $("#dgl2_alertBox");
  1509. if (box.length == 0) box = $("<div id='dgl2_alertBox'></div>").appendTo("body");
  1510. box.html(tex);
  1511. box.attr("title", titl);
  1512. box.dialog({
  1513. width: "50%",
  1514. height: "auto",
  1515. maxHeight: (window.innerHeight * 0.8),
  1516. buttons: {
  1517. "OK": function () {
  1518. $(this).dialog("close");
  1519. dfd.resolve(true);
  1520. },
  1521. Cancel: function () {
  1522. $(this).dialog("close");
  1523. dfd.resolve(false);
  1524. }
  1525. }
  1526. });
  1527. return dfd.promise();
  1528. }
  1529.  
  1530. function myPrompt(tex, titl = "", def = "") {
  1531. var dfd = new $.Deferred();
  1532. var box = $("#dgl2_alertBox");
  1533. if (box.length == 0) box = $("<div id='dgl2_alertBox'></div>").appendTo("body");
  1534. box.html(tex + "<br/><input type='text' id='dgl2_promptVal' value='" + def + "' class='text ui-widget-content ui-corner-all'>");
  1535. box.attr("title", titl);
  1536. box.dialog({
  1537. width: "50%",
  1538. height: "auto",
  1539. maxHeight: (window.innerHeight * 0.8),
  1540. buttons: {
  1541. "OK": function () {
  1542. $(this).dialog("close");
  1543. dfd.resolve($("#dgl2_promptVal").val());
  1544. },
  1545. Cancel: function () {
  1546. $(this).dialog("close");
  1547. dfd.resolve(false);
  1548. }
  1549. }
  1550. });
  1551. return dfd.promise();
  1552.  
  1553. }
  1554.  
  1555. function showPopup(event) {
  1556. event.preventDefault();
  1557. event.stopPropagation();
  1558. insertHTML(); //does nothing if already inserted
  1559. $("div.dgl2_groupdialog").dialog({
  1560. width: "80%",
  1561. height: (window.innerHeight * 0.8),
  1562. draggable: false,
  1563. resizable: false,
  1564. dialogClass: 'groupPopup',
  1565. create: function (event) {
  1566. $(event.target).parent().css({
  1567. 'position': 'fixed',
  1568. "left": '10%',
  1569. "top": '10%'
  1570. });
  1571. $(event.target).parent().find("div.ui-dialog-titlebar").prepend($("div.dgl2_titlebar"));
  1572. $(event.target).parent().find("span.ui-dialog-title").hide();
  1573. }
  1574. });
  1575. $("div.groupPopup").keydown(function(event){
  1576. if(event.target.tagName!="INPUT" && !$("#dgl2_grContext").is(":visible")){
  1577. highlightLetter(String.fromCharCode(event.which));
  1578. }
  1579. });
  1580. }
  1581.  
  1582.  
  1583. function groupById(id) {
  1584. for (var g of groups) {
  1585. if (g.userId == id) return g;
  1586. }
  1587. return null;
  1588. }
  1589. //bind script to buttons. dynamic browsing compatible
  1590. function addListener() {
  1591. var els = $('*[data-hook="group_counter"]:not([dgl2=1])');
  1592. els.click(showPopup).attr("dgl2", 1).find("svg").html(
  1593. '<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"/>' +
  1594. '<path stroke="#0A0" stroke-width="4" stroke-opacity="0.8" d="M12 18H24M18 12V24"/>'
  1595. );
  1596. }
  1597. $(document).ready(function(){
  1598. $(document).mousedown(function(event){
  1599. if($(event.target).closest("#dgl2_grContext").length==0){
  1600. $("#dgl2_grContext").hide().find("select").empty();
  1601. }
  1602. });
  1603. });
  1604.  
  1605. setInterval(addListener, 1000);
  1606. })();
  1607.  
  1608. /* resources:
  1609. group template
  1610. <button class="_3PBqc"> <!--_3UhBt-->
  1611. <div class="_3Lbo4 _3EbZ8">
  1612. <div class="_1lUlZ">
  1613. <div class="_3tZow">
  1614. <span class="_23Ekg _3q2EJ">
  1615. <img data-hook="user_avatar" alt="iterators's avatar" class="dIDzJ" src="https://a.deviantart.net/avatars-big/i/t/iterators.png?3">
  1616. </span>
  1617. </div>
  1618. <div class="_3_Nxk">
  1619. <span class="_3HH04 _3y1P2 _3kEx1 _32s2-">
  1620. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8">
  1621. <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>
  1622. </svg>
  1623. </span>
  1624. </div>
  1625. </div>
  1626. <div class="_22Jp8">iterators</div>
  1627. </div>
  1628. </button>
  1629.  
  1630. group_request_response_structure
  1631. userId 14471598
  1632. useridUuid 55b0c0d3-fed7-45ff-8951-0d8554eb01b1
  1633. username deviantARTSupporters
  1634. usericon https://a.deviantart.net/avatars-big/d/e/deviantartsupporters.gif?12
  1635. type group
  1636. isNewDeviant false
  1637.  
  1638. subfolder request response structure
  1639. commentCount: 0
  1640. description: ""
  1641. folderId: 13361368
  1642. name: "Featured"
  1643. owner: Object { userId: 11209451, useridUuid: "294e75e1-94ec-4009-883a-b13eff743164", username: "iterators", … }
  1644. parentId: null
  1645. size: 5
  1646. thumb:
  1647. author: Object { userId: 7514199, useridUuid: "56613871-de84-49fc-b1c1-f9fd0f796461", username: "Championx91", … }
  1648. blockReason: null
  1649. deviationId: 710763111
  1650. files: Array(9) [ {…}, {…}, {…}, … ]
  1651. 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, … }
  1652. isAntisocial: true
  1653. isBlocked: false
  1654. isCommentable: true
  1655. isDeleted: false
  1656. isDownloadable: false
  1657. isFavourited: false
  1658. isJournal: false
  1659. isMature: false
  1660. isPublished: true
  1661. isPurchasable: false
  1662. isShareable: false
  1663. isTextEditable: false
  1664. legacyTextEditUrl: null
  1665. printId: null
  1666. publishedTime: "2017-10-20T10:39:40-0700"
  1667. stats: Object { comments: 17, favourites: 19 }
  1668. title: "Suggestions unwatch fav"
  1669. typeId: 1
  1670. url: "https://www.deviantart.com/championx91/art/Suggestions-unwatch-fav-710763111"
  1671. type: "gallery"
  1672.  
  1673. required:
  1674. groupid = groups request
  1675. folderid = subfolders/favourites requests
  1676. deviationid = url \d+$
  1677. csrf_token = $("input[name=validate_token]").value; //deviation page
  1678. moduleID = window.document.body.innerHTML.match(/{\\\"id\\\":(\d+),\\\"type\\\":\\\"group_list_members/i) //https://www.deviantart.com/dediggefedde/about
  1679.  
  1680. groups GET
  1681. https://www.deviantart.com/_napi/da-user-profile/api/module/groups/members?username=Dediggefedde&moduleid=1725444339&offset=30&limit=24
  1682. Limit max 60
  1683. finished if hasMore==false
  1684. needs moduleID, username
  1685.  
  1686. subfolders GET
  1687. https://www.deviantart.com/_napi/shared_api/deviation/group_folders?groupid=13042771&type=gallery
  1688. needs groupID
  1689.  
  1690. favourites GET
  1691. https://www.deviantart.com/_napi/shared_api/deviation/group_folders?groupid=28209298&type=collection
  1692. needs groupID
  1693.  
  1694. add to subfolder POST
  1695. {"groupid":11209451,"type":"gallery","folderid":23881334,"deviationid":501848540,"csrf_token":"wPPvYvd4br1JpNjT.pyuz1q.lVPn5jT3ohUO8uD0QLruZb-V9d5NDb1FOyl7OcHe7w"}
  1696. https://www.deviantart.com/_napi/shared_api/deviation/group_add
  1697. needs:
  1698. csrf_token THjPWGH1SRH_-epZ.py75wu.VTYjdFg4YApQBqsZBS5ISBHG1W4aWv-oTkk5WDA4t-w
  1699. deviationid 298766207
  1700. folderid 23881334
  1701. groupid 11209451
  1702. type gallery
  1703. */