ecsa

为csp的素材商店网页-我的下载页面 增加“按素材类型排序”和按素材类型筛选快捷按钮 Support sorting and filtering on clip studio assets favorites and downloads page

当前为 2023-10-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name ecsa
  3. // @namespace boni
  4. // @version 1.04
  5. // @description 为csp的素材商店网页-我的下载页面 增加“按素材类型排序”和按素材类型筛选快捷按钮 Support sorting and filtering on clip studio assets favorites and downloads page
  6. // @match https://assets.clip-studio.com/*/download-list/*
  7. // @match https://assets.clip-studio.com/*/starred*
  8. // @grant none
  9. // @license GPL-3.0-only
  10. // ==/UserScript==
  11. (function() {
  12. 'use strict';
  13. const getSortBtnLabel = () => {
  14. // Get the current URL
  15. const currentURL = window.location.href;
  16. const withoutProtocol = currentURL.replace("https://", "");
  17. // Split by "/"
  18. const urlSegments = withoutProtocol.split("/");
  19. // Get the first part
  20. const lang = urlSegments[1];
  21. const translations = {
  22. "zh-tw": {
  23. category: "按素材类别排序",
  24. time: "按下载时间排序",
  25. },
  26. "ja-jp": {
  27. category: "素材タイプ別に並べ替え",
  28. time: "ダウンロード時間別に並べ替え",
  29. },
  30. "en-us": {
  31. category: "Sort by Category",
  32. time: "Sort by Download Time"
  33. },
  34. "de-de": {
  35. category: "Nach Kategorie sortieren",
  36. time: "Nach Download-Zeit sortieren"
  37. },
  38. "es-es": {
  39. category: "Ordenar por categoría",
  40. time: "Ordenar por tiempo de descarga"
  41. },
  42. "fr-fr": {
  43. category: "Trier par catégorie",
  44. time: "Trier par heure de téléchargement"
  45. },
  46. "ko-kr": {
  47. category: "카테고리별 정렬",
  48. time: "다운로드 시간별 정렬"
  49. }
  50. };
  51. return translations[lang]
  52. }
  53. // text for "all" option
  54. const getAllText = () => {
  55. if (window.location.href.includes("starred")) {
  56. // Find the first <a> element inside the .btn-group.selectFilter
  57. const selectFilter = document.querySelector('.btn-group.selectFilter');
  58. if (selectFilter) {
  59. const firstOption = selectFilter.querySelector('a');
  60. if (firstOption) {
  61. const firstOptionText = firstOption.textContent.trim();
  62. return firstOptionText
  63. }
  64. }
  65. } else {
  66. // Find the <ul> element inside the .dropdown.selectFilter
  67. const dropdown = document.querySelector('.dropdown.selectFilter');
  68. if (dropdown) {
  69. const ul = dropdown.querySelector('ul.dropdown-menu');
  70. if (ul) {
  71. const firstOption = ul.querySelector('li:first-child a');
  72. if (firstOption) {
  73. const firstOptionText = firstOption.textContent.trim();
  74. return firstOptionText
  75. }
  76. }
  77. }
  78. }
  79. }
  80. // Define liElementsByType in the global scope
  81. const liElementsByType = {};
  82. let container = document.querySelector("ul.layput__cardPanel");
  83. let sortAsset = false;
  84. let orig = container.innerHTML;
  85. let types = []
  86. let allText = getAllText()
  87. let sortBtnText = getSortBtnLabel()
  88. let currentLocation = ''
  89. if (window.location.href.includes("starred")) {
  90. currentLocation = 's'
  91. } else {
  92. currentLocation = 'd'
  93. }
  94.  
  95.  
  96. const toggleSort = (sort) => {
  97. // Set a value in localStorage
  98. localStorage.setItem( currentLocation +'sorted', sort === true ? 1 : 0);
  99. sortAsset = sort
  100. const sortButton = document.getElementById("sortButton");
  101. sortButton.textContent = sortAsset ? sortBtnText.time : sortBtnText.category;
  102. // sortButton.disabled = type !== allText;
  103. if (sort) {
  104. // Clear the existing content on the page
  105. container.innerHTML = '';
  106. // Sort the <li> elements by type value (custom sorting logic)
  107. const sortedTypes = Object.keys(liElementsByType).sort();
  108. // Reconstruct the sorted list of <li> elements
  109. const sortedLiElements = [];
  110. sortedTypes.forEach((type) => {
  111. sortedLiElements.push(...liElementsByType[type]);
  112. });
  113. // Append the sorted <li> elements back to the container
  114. sortedLiElements.forEach((li) => {
  115. container.appendChild(li);
  116. });
  117. } else {
  118. container.innerHTML = orig;
  119. }
  120. }
  121. // Function to sort the <li> elements by type
  122. const preprocessAssets = () => {
  123. const liElements = document.querySelectorAll("li.materialCard");
  124. liElements.forEach((li) => {
  125. const materialTypeLink = li.querySelector("a[href*='/search?type=']");
  126. if (materialTypeLink) {
  127. const type = materialTypeLink.textContent.trim(); // Get the text content of the <a> tag
  128. if (!types.includes(type)) {
  129. types.push(type)
  130. }
  131. if (type) {
  132. if (!liElementsByType[type]) {
  133. liElementsByType[type] = [];
  134. }
  135. liElementsByType[type].push(li);
  136. }
  137. }
  138. });
  139. // Find the existing button element
  140. const existingButton = document.querySelector(".btn.btn-default.operationButton.favoriteButton");
  141. if (existingButton) {
  142. // Create a new button element
  143. const sortButton = document.createElement("button");
  144. sortButton.type = "button";
  145. sortButton.className = "btn btn-primary ";
  146. sortButton.id = "sortButton";
  147. sortButton.textContent = sortBtnText.category;
  148. sortButton.style.marginLeft = '10px'
  149. // Add an event listener to the new button if needed
  150. sortButton.addEventListener("click", function() {
  151. // Handle button click event
  152. sortAsset = !sortAsset
  153. sortButton.textContent = sortAsset ? sortBtnText.time : sortBtnText.category;
  154. toggleSort(sortAsset)
  155. });
  156. // Insert the new button after the existing button
  157. existingButton.parentNode.insertBefore(sortButton, existingButton.nextSibling);
  158. const options = [...types];
  159. options.unshift(allText)
  160. const dropdown = createDropdown(options);
  161. existingButton.parentNode.insertBefore(dropdown, sortButton.nextSibling);
  162. }
  163. const filterBtn = document.getElementById("filterButton");
  164. if(filterBtn.textContent === getAllText()) {
  165. // Read a value from localStorage
  166. const sorted = localStorage.getItem(currentLocation + 'sorted');
  167. // Check if the value exists
  168. if (sorted == 1) {
  169. // Use the value
  170. toggleSort(true)
  171. } else {
  172. }
  173. }
  174. };
  175. // Create a function to generate the dropdown HTML
  176. function createDropdown(types) {
  177. const dropdown = document.createElement("div");
  178. dropdown.className = "dropdown selectFilter ";
  179. dropdown.style.display = 'inline-block'
  180. dropdown.style.marginLeft = '10px'
  181. dropdown.style.marginTop = '0px'
  182. const button = document.createElement("button");
  183. button.className = "btn btn-default dropdown-toggle filterButton";
  184. button.id = "filterButton"
  185. button.type = "button";
  186. button.setAttribute("data-toggle", "dropdown");
  187. button.setAttribute("aria-haspopup", "true");
  188. button.setAttribute("aria-expanded", "true");
  189. const filterOption = localStorage.getItem(currentLocation + 'filtered');
  190.  
  191. // set sort button text but only allow change when 'all' option is selected
  192. const sorted = localStorage.getItem(currentLocation +'sorted');
  193. sortButton.textContent = sorted ? sortBtnText.time : sortBtnText.category;
  194. // toggleSort()
  195.  
  196. if( types.includes(filterOption) && filterOption !== getAllText() ) {
  197. const sortButton = document.getElementById("sortButton");
  198.  
  199. // sortButton.textContent = sorted ? sortBtnText.time : sortBtnText.category;
  200. sortButton.disabled = true
  201. button.textContent =filterOption
  202. container.innerHTML = '';
  203. liElementsByType[filterOption].forEach((li) => {
  204. container.appendChild(li);
  205. });
  206. } else {
  207.  
  208. button.textContent = types[0]; // Set the default text
  209.  
  210. }
  211. button.style.borderRadius = '0px'
  212. button.style.textAlign = 'left'
  213. const caret = document.createElement("span");
  214. caret.className = "caret";
  215. caret.style.position = "absolute";
  216. if (window.location.href.includes("starred")) {
  217. // The URL contains "starred"
  218. caret.style.top = "15px"
  219. } else {
  220. // The URL does not contain "starred"
  221. }
  222. caret.style.right = "10px";
  223. const ul = document.createElement("ul");
  224. ul.className = "dropdown-menu";
  225. // Create options from the 'types' array
  226. types.forEach((type) => {
  227. const li = document.createElement("li");
  228. const a = document.createElement("a");
  229. a.textContent = type;
  230. li.appendChild(a);
  231. ul.appendChild(li);
  232. li.addEventListener("click", function(event) {
  233. localStorage.setItem(currentLocation + 'filtered', type);
  234. // Prevent the default behavior of following the link (if it's an anchor)
  235. event.preventDefault();
  236. container.innerHTML = 'xs';
  237. // Enable or disable the new button based on the selected option
  238. const sortButton = document.getElementById("sortButton");
  239. sortButton.disabled = type !== allText;
  240. button.firstChild.textContent = type;
  241. const h4Element = document.querySelector("h4.text-right");
  242. if (type !== allText) {
  243. liElementsByType[type].forEach((li) => {
  244. container.appendChild(li);
  245. });
  246. localStorage.setItem(currentLocation +'filtered', type);
  247. } else {
  248. container.innerHTML = orig;
  249. const sorted = localStorage.getItem(currentLocation + 'sorted');
  250. // Check if the value exists
  251. if (sorted == 1) {
  252. // Use the value
  253. toggleSort(true)
  254. } else {
  255. }
  256. }
  257. });
  258. });
  259. // Append elements to the dropdown
  260. button.appendChild(caret);
  261. dropdown.appendChild(button);
  262. dropdown.appendChild(ul);
  263. return dropdown;
  264. }
  265. // Wait for the page to fully load before executing the sorting function
  266. window.onload = function() {
  267. preprocessAssets();
  268. };
  269. })();