Greasy Fork 还支持 简体中文。

dev_group_list2

Better Submit-to-group dialog

目前為 2020-06-13 提交的版本,檢視 最新版本

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