Greasyfork 快捷编辑收藏

在GF脚本页添加快速打开收藏集编辑页面功能

Pada tanggal 02 November 2023. Lihat %(latest_version_link).

  1. /* eslint-disable no-multi-spaces */
  2.  
  3. // ==UserScript==
  4. // @name Greasyfork 快捷编辑收藏
  5. // @name:zh-CN Greasyfork 快捷编辑收藏
  6. // @name:zh-TW Greasyfork 快捷編輯收藏
  7. // @name:en Greasyfork script-set-edit button
  8. // @name:en-US Greasyfork script-set-edit button
  9. // @namespace Greasyfork-Favorite
  10. // @version 0.1.7
  11. // @description 在GF脚本页添加快速打开收藏集编辑页面功能
  12. // @description:zh-CN 在GF脚本页添加快速打开收藏集编辑页面功能
  13. // @description:zh-TW 在GF腳本頁添加快速打開收藏集編輯頁面功能
  14. // @description:en Add open script-set-edit-page button in GF script page
  15. // @description:en-US Add open script-set-edit-page button in GF script page
  16. // @author PY-DNG
  17. // @license GPL-3
  18. // @match http*://*.greatest.deepsurf.us/*
  19. // @match http*://*.sleazyfork.org/*
  20. // @require https://greatest.deepsurf.us/scripts/456034-basic-functions-for-userscripts/code/script.js?version=1226884
  21. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAbBJREFUOE+Vk7GKGlEUhr8pAiKKDlqpCDpLUCzWBxCENBa+hBsL9wHsLWxXG4tNtcGH0MIiWopY7JSGEUWsbESwUDMw4Z7siLsZDbnlPff/7n/+e67G38sA6sAXIPVWXgA/gCdgfinRPuhfCoXCw3Q65XA4eLBl6zvw1S2eAZqmvTqOc5/NZhkMBqRSKWzbvgYxgbwquoAX4MGyLHK5HIlEgtFo9C+IOFEAo1gsWsvlUmyPx2MymYxAhsMh6XT6lpM7BXjWdf1xNpuRz+fl8GQywTAMGo0G1WpVnJxOJ692vinADPgcDAaZz+cCOR6PmKZJPB4XUb/fp1wuewF+KoBCf1JVBVE5dDodms3mWdDtdqlUKl6AX+8ALmS9XgtM0/5kvNlspKX9fv8RIgBp4bISCoXo9XqsVitKpRK6rrPb7STQ7XZ7eVRaeAYerz14OBxGOfL7/eIgmUwKzHEcJZEQ1eha1wBqPxqNihufzyeQWCzmtiPPqJYM0jWIyiISibBYLAgEAtTrdVqt1nmQXN0rcH/LicqmVqvRbrdN27bfjbKru+nk7ZD3Z7q4+b++82/YPKIrXsKZ3AAAAABJRU5ErkJggg==
  22. // @grant GM_xmlhttpRequest
  23. // @grant GM_setValue
  24. // @grant GM_getValue
  25. // ==/UserScript==
  26.  
  27. /* global LogLevel DoLog Err $ $All $CrE $AEL $$CrE addStyle detectDom destroyEvent copyProp copyProps parseArgs escJsStr replaceText getUrlArgv dl_browser dl_GM AsyncManager */
  28.  
  29. (function __MAIN__() {
  30. 'use strict';
  31.  
  32. const CONST = {
  33. Text: {
  34. 'zh-CN': {
  35. FavEdit: '收藏集:',
  36. Add: '加入此集',
  37. Edit: '手动编辑',
  38. CopySID: '复制脚本ID',
  39. Working: ['正在添加...', '就快好了...'],
  40. Error: {
  41. Unknown: '未知错误'
  42. }
  43. },
  44. 'zh-TW': {
  45. FavEdit: '收藏集:',
  46. Add: '加入此集',
  47. Edit: '手動編輯',
  48. CopySID: '複製腳本ID',
  49. Working: ['正在添加...', '就快好了...'],
  50. Error: {
  51. Unknown: '未知錯誤'
  52. }
  53. },
  54. 'en': {
  55. FavEdit: 'Add to/Remove from favorite list: ',
  56. Add: 'Add',
  57. Edit: 'Edit Manually',
  58. CopySID: 'Copy-Script-ID',
  59. Working: ['Working...', 'Just a moment...'],
  60. Error: {
  61. Unknown: 'Unknown Error'
  62. }
  63. },
  64. 'default': {
  65. FavEdit: 'Add to/Remove from favorite list: ',
  66. Add: 'Add',
  67. Edit: 'Edit Manually',
  68. CopySID: 'Copy-Script-ID',
  69. Working: ['Working...', 'Just a moment...'],
  70. Error: {
  71. Unknown: 'Unknown Error'
  72. }
  73. },
  74. }
  75. }
  76.  
  77. // Get i18n code
  78. let i18n = navigator.language;
  79. if (!Object.keys(CONST.Text).includes(i18n)) {i18n = 'default';}
  80.  
  81. main()
  82. function main() {
  83. const HOST = getHost();
  84. const API = getAPI();
  85.  
  86. // Common actions
  87. commons();
  88.  
  89. // API-based actions
  90. switch(API[1]) {
  91. case "scripts":
  92. API[2] && centerScript(API);
  93. break;
  94. default:
  95. DoLog('API is {}'.replace('{}', API));
  96. }
  97. }
  98.  
  99. function centerScript(API) {
  100. switch(API[3]) {
  101. case undefined:
  102. pageScript();
  103. break;
  104. case 'code':
  105. pageCode();
  106. break;
  107. case 'feedback':
  108. pageFeedback();
  109. break;
  110. }
  111. }
  112.  
  113. function commons() {
  114. // Your common actions here...
  115. }
  116.  
  117. function pageScript() {
  118. addFavPanel();
  119. }
  120.  
  121. function pageCode() {
  122. addFavPanel();
  123. }
  124.  
  125. function pageFeedback() {
  126. addFavPanel();
  127. }
  128.  
  129. function addFavPanel() {
  130. if (!getUserpage()) {return false;}
  131. GUI();
  132.  
  133. function GUI() {
  134. // Get elements
  135. const script_after = $('#script-feedback-suggestion+*') || $('#new-script-discussion');
  136. const script_parent = script_after.parentElement;
  137.  
  138. // My elements
  139. const script_favorite = $CrE('div');
  140. script_favorite.id = 'script-favorite';
  141. script_favorite.style.margin = '0.75em 0';
  142. script_favorite.innerHTML = CONST.Text[i18n].FavEdit;
  143.  
  144. const favorite_groups = $CrE('select');
  145. favorite_groups.id = 'favorite-groups';
  146.  
  147. const stored_sets = GM_getValue('script-sets', {sets: []}).sets;
  148. for (const set of stored_sets) {
  149. // Make <option>
  150. const option = $CrE('option');
  151. option.innerText = set.name;
  152. option.value = set.linkedit;
  153. $APD(favorite_groups, option);
  154. }
  155. adjustWidth();
  156.  
  157. getScriptSets(function(sets) {
  158. clearChildnodes(favorite_groups);
  159. for (const set of sets) {
  160. // Make <option>
  161. const option = $CrE('option');
  162. option.innerText = set.name;
  163. option.value = set.linkedit;
  164. $APD(favorite_groups, option);
  165. }
  166. adjustWidth();
  167.  
  168. // Set edit-button.href
  169. favorite_edit.href = favorite_groups.value;
  170. })
  171. favorite_groups.addEventListener('change', function(e) {
  172. favorite_edit.href = favorite_groups.value;
  173. });
  174.  
  175. const favorite_add = $CrE('a');
  176. favorite_add.id = 'favorite-add';
  177. favorite_add.innerHTML = CONST.Text[i18n].Add;
  178. favorite_add.style.margin = favorite_add.style.margin = '0px 0.5em';
  179. favorite_add.href = 'javascript:void(0);'
  180. favorite_add.addEventListener('click', function(e) {
  181. addFav();
  182. });
  183.  
  184. const favorite_edit = $CrE('a');
  185. favorite_edit.id = 'favorite-edit';
  186. favorite_edit.innerHTML = CONST.Text[i18n].Edit;
  187. favorite_edit.style.margin = favorite_edit.style.margin = '0px 0.5em';
  188. favorite_edit.target = '_blank';
  189.  
  190. const favorite_copy = $CrE('a');
  191. favorite_copy.id = 'favorite-copy';
  192. favorite_copy.href = 'javascript: void(0);';
  193. favorite_copy.innerHTML = CONST.Text[i18n].CopySID;
  194. favorite_copy.addEventListener('click', function() {
  195. copyText(getStrSID());
  196. });
  197.  
  198. // Append to document
  199. $APD(script_favorite, favorite_groups);
  200. script_parent.insertBefore(script_favorite, script_after);
  201. $APD(script_favorite, favorite_add);
  202. $APD(script_favorite, favorite_edit);
  203. $APD(script_favorite, favorite_copy);
  204.  
  205. function adjustWidth() {
  206. favorite_groups.style.width = Math.max.apply(null, Array.from(favorite_groups.children).map((o) => (o.innerText.length))).toString() + 'em';
  207. favorite_groups.style.maxWidth = '40vw';
  208. }
  209.  
  210. function addFav() {
  211. const iframe = $CrE('iframe');
  212. iframe.style.width = iframe.style.height = iframe.style.border = '0';
  213. iframe.addEventListener('load', edit_onload, {once: true});
  214. iframe.src = favorite_groups.value;
  215. $APD(document.body, iframe);
  216. displayNotice(CONST.Text[i18n].Working[0]);
  217.  
  218. function edit_onload() {
  219. const oDom = iframe.contentDocument;
  220. const input = $CrE('input');
  221. input.value = getStrSID();
  222. input.name = 'scripts-included[]';
  223. input.type = 'hidden';
  224. $APD($(oDom, '#script-set-scripts'), input);
  225. $(oDom, 'button[name="save"]').click();
  226. iframe.addEventListener('load', finish_onload, {once: true});
  227. displayNotice(CONST.Text[i18n].Working[1]);
  228. }
  229.  
  230. function finish_onload() {
  231. const status = $(iframe.contentDocument, 'p.notice');
  232. const status_text = status ? status.innerText : CONST.Text[i18n].Error.Unknown;
  233. displayNotice(status_text);
  234. iframe.parentElement.removeChild(iframe);
  235. }
  236.  
  237. function displayNotice(text) {
  238. const notice = $CrE('p');
  239. notice.classList.add('notice');
  240. notice.id = 'fav-notice';
  241. notice.innerText = text;
  242. const old_notice = $('#fav-notice');
  243. old_notice && old_notice.parentElement.removeChild(old_notice);
  244. $('#script-content').insertAdjacentElement('afterbegin', notice);
  245. }
  246. }
  247. }
  248. }
  249.  
  250. function getScriptSets(callback, args=[]) {
  251. const userpage = getUserpage();
  252. getDocument(userpage, function(oDom) {
  253. /*
  254. const user_script_sets = oDom.querySelector('#user-script-sets');
  255. const script_sets = [];
  256.  
  257. for (const li of user_script_sets.querySelectorAll('li')) {
  258. // Get fav info
  259. const name = li.childNodes[0].nodeValue.trimRight();
  260. const link = li.children[0].href;
  261. const linkedit = li.children[1] ? li.children[1].href : 'https://greatest.deepsurf.us/' + $('#language-selector-locale').value + '/users/' + $('#nav-user-info>.user-profile-link>a').href.match(/[a-zA-Z\-]+\/users\/([^\/]*)/)[1] + '/sets/' + li.children[0].href.match(/[\?&]set=(\d+)/)[1] + '/edit';
  262.  
  263. // Append to script_sets
  264. script_sets.push({
  265. name: name,
  266. link: link,
  267. linkedit: linkedit
  268. });
  269. }
  270. */
  271. const script_sets = Array.from($(oDom, 'ul#user-script-sets').children).map(li => ({
  272. name: li.children[0].innerText,
  273. link: li.children[0].href,
  274. linkedit: li.children[1].href
  275. }));
  276.  
  277. // Save to GM_storage
  278. GM_setValue('script-sets', {
  279. sets: script_sets,
  280. time: (new Date()).getTime(),
  281. version: '0.2'
  282. });
  283.  
  284. // callback
  285. callback.apply(null, [script_sets].concat(args));
  286. });
  287. }
  288.  
  289. function getUserpage() {
  290. const a = $('#nav-user-info>.user-profile-link>a');
  291. return a ? a.href : null;
  292. }
  293.  
  294. function getStrSID(url=location.href) {
  295. const API = getAPI(url);
  296. const strSID = API[2].match(/\d+/);
  297. return strSID;
  298. }
  299.  
  300. function getSID(url=location.href) {
  301. return Number(getStrSID(url));
  302. }
  303. // Basic functions
  304. function $APD(a,b) {return a.appendChild(b);}
  305.  
  306. // Remove all childnodes from an element
  307. function clearChildnodes(element) {
  308. const cns = []
  309. for (const cn of element.childNodes) {
  310. cns.push(cn);
  311. }
  312. for (const cn of cns) {
  313. element.removeChild(cn);
  314. }
  315. }
  316.  
  317. // Download and parse a url page into a html document(dom).
  318. // when xhr onload: callback.apply([dom, args])
  319. function getDocument(url, callback, args=[]) {
  320. GM_xmlhttpRequest({
  321. method : 'GET',
  322. url : url,
  323. responseType : 'blob',
  324. onloadstart : function() {
  325. DoLog(LogLevel.Info, 'getting document, url=\'' + url + '\'');
  326. },
  327. onload : function(response) {
  328. const htmlblob = response.response;
  329. parseDocument(htmlblob, callback, args);
  330. }
  331. })
  332. }
  333.  
  334. function parseDocument(htmlblob, callback, args=[]) {
  335. const reader = new FileReader();
  336. reader.onload = function(e) {
  337. const htmlText = reader.result;
  338. const dom = new DOMParser().parseFromString(htmlText, 'text/html');
  339. args = [dom].concat(args);
  340. callback.apply(null, args);
  341. //callback(dom, htmlText);
  342. }
  343. reader.readAsText(htmlblob, document.characterSet);
  344. }
  345.  
  346. // Copy text to clipboard (needs to be called in an user event)
  347. function copyText(text) {
  348. // Create a new textarea for copying
  349. const newInput = document.createElement('textarea');
  350. document.body.appendChild(newInput);
  351. newInput.value = text;
  352. newInput.select();
  353. document.execCommand('copy');
  354. document.body.removeChild(newInput);
  355. }
  356.  
  357. // get '/' splited API array from a url
  358. function getAPI(url=location.href) {
  359. return url.replace(/https?:\/\/(.*?\.){1,2}.*?\//, '').replace(/\?.*/, '').match(/[^\/]+?(?=(\/|$))/g);
  360. }
  361.  
  362. // get host part from a url(includes '^https://', '/$')
  363. function getHost(url=location.href) {
  364. const match = location.href.match(/https?:\/\/[^\/]+\//);
  365. return match ? match[0] : match;
  366. }
  367.  
  368. function randint(min, max) {
  369. return Math.floor(Math.random() * (max - min + 1)) + min;
  370. }
  371. })();