TitleList

Common functions for working on lists of titles

目前为 2019-09-23 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/390248/735269/TitleList.js

  1. //{
  2. // Common functions for working on lists of titles, loading them, highlighting
  3. // titles based on these lists.
  4. //
  5. // Copyright (c) 2019, Guido Villa
  6. // Original idea and some of the code is taken from IMDb 'My Movies' enhancer:
  7. // Copyright (c) 2008-2018, Ricardo Mendonça Ferreira (ric@mpcnet.com.br)
  8. // Released under the GPL license - http://www.gnu.org/copyleft/gpl.html
  9. //
  10. // --------------------------------------------------------------------
  11. //
  12. // ==UserScript==
  13. // @name TitleList
  14. // @description Common functions for working on lists of titles
  15. // @namespace https://greasyfork.org/en/scripts/390248-titlelist
  16. // @updateURL about:blank
  17. // @homepageURL https://greasyfork.org/en/scripts/390248-titlelist
  18. // @copyright 2019, Guido Villa
  19. // @license GPL-3.0-or-later
  20. // @oujs:author Guido
  21. // @date 21.09.2019
  22. // @version 1.0
  23. // ==/UserScript==
  24. //
  25. // To-do (priority: [H]igh, [M]edium, [L]ow):
  26. // - [M] Reorder functions
  27. // - [H] Extend library to work on all the scripts
  28. // - [M] Move string literals
  29. // - [M] correct public/private
  30. // - [M] main context as default context
  31. // - [M] move scripts to github or similar
  32. // - [M] do we need that the library is not cached? if so, how?
  33. // - [M] See if ok that old versions are public
  34. // - [M] changes to a list aren't reflected in page till reload. Change?
  35. // - [M] Automatically handle case with only one list
  36. // - [M] Add indication of URL to use to @require library itself
  37. // - [M] correct @namespace and @homepageURL
  38. // - [M] Auto-update
  39. // - [L] Make public
  40. //
  41. // History:
  42. // --------
  43. // 2019.09.21 [1.0] First version
  44. // 2019.09.18 [0.1] First test version, private use only
  45. //
  46. //}
  47.  
  48. /* jshint esversion: 6, supernew: true */
  49.  
  50. const Library_Version_TITLELIST = '1.0';
  51.  
  52. // FUNCTIONS ************************************************************************************************************
  53.  
  54. var TL = new (function() {
  55. 'use strict';
  56. var self = this;
  57.  
  58. this.mainContext = null;
  59.  
  60.  
  61. // startup function
  62. this.startup = function(ctx) {
  63. self.mainContext = ctx;
  64.  
  65. //TODO forse salvare una variabile we_are_in_a_title_page nel contesto?
  66. //TODO per altri casi lo startup deve fare anche altro
  67. if (!( !ctx.isTitlePage || ctx.isTitlePage(document) )) return;
  68.  
  69. // find current logged in user, or quit script
  70. if (!self.getLoggedUser(ctx)) return;
  71.  
  72. // Load list data for this user from local storage
  73. ctx.allLists = self.loadSavedLists(ctx);
  74.  
  75. // start the title processing function
  76. self.processTitles(ctx);
  77. if (ctx.interval >= 100) {
  78. ctx.timer = setInterval(function() {self.processTitles(ctx);}, ctx.interval);
  79. }
  80. };
  81.  
  82.  
  83. // Return name of user currently logged on <ctx> site
  84. // Return last saved value and log error if no user is found
  85. this.getLoggedUser = function(ctx) {
  86. var user = ctx.getUser();
  87.  
  88. if (!user) {
  89. console.error(ctx.name + ": user not logged in (or couldn't get user info) on URL " + document.URL);
  90. user = GM_getValue(ctx.name + '-lastUser', '');
  91. console.error("Using last user: " + user);
  92. }
  93. GM_setValue(ctx.name + '-lastUser', user);
  94. ctx.user = user;
  95. return user;
  96. };
  97.  
  98.  
  99. /* PRIVATE member */
  100. // Load a single saved lists
  101. function loadSavedList(listName) {
  102. var list;
  103. var userData = GM_getValue(listName, null);
  104. if (userData) {
  105. try {
  106. list = JSON.parse(userData);
  107. } catch(err) {
  108. alert("Error loading saved list named '" + listName + "'!\n" + err.message);
  109. }
  110. }
  111. return list;
  112. }
  113.  
  114.  
  115. // Load lists saved for the current user
  116. this.loadSavedLists = function(ctx) {
  117. var lists = {};
  118.  
  119. var listNames = loadSavedList('TitleLists-' + ctx.user);
  120. if (!listNames) return lists;
  121.  
  122. for (var listName in listNames) {
  123. lists[listName] = loadSavedList('TitleList-' + ctx.user + '-' + listName);
  124. }
  125. return lists;
  126. };
  127.  
  128.  
  129. // Save single list for the current user
  130. this.saveList = function(ctx, list, name) {
  131. var listNames = ( loadSavedList('TitleLists-' + ctx.user) || {} );
  132.  
  133. listNames[name] = 1;
  134. var userData = JSON.stringify(listNames);
  135. GM_setValue('TitleLists-' + ctx.user, userData);
  136.  
  137. userData = JSON.stringify(list);
  138. GM_setValue('TitleList-' + ctx.user + '-' + name, userData);
  139. };
  140.  
  141.  
  142. // Receives a title (and corresponding entry) and finds all lists title is in.
  143. // Argument "entry" is for "virtual" lists determined by attributes in the DOM
  144. this.inLists = function(ctx, tt, entry) {
  145. var lists = ( ctx.getListsFromEntry && ctx.getListsFromEntry(tt, entry) || {} );
  146.  
  147. for (var list in ctx.allLists) {
  148. if (ctx.allLists[list][tt.id]) lists[list] = true;
  149. }
  150.  
  151. return lists;
  152. };
  153.  
  154.  
  155. // Process all title cards in current page
  156. this.processTitles = function(ctx) {
  157. var entries = ctx.getTitleEntries(document);
  158. if (!entries) return;
  159.  
  160. var entry, tt, lists, processingType;
  161. for (var i = 0; i < entries.length; i++) {
  162. entry = entries[i];
  163.  
  164. // if entry has already been previously processed, skip it
  165. if (entry.TLProcessed) continue;
  166.  
  167. // see if entry is valid
  168. if (ctx.isValidEntry && !ctx.isValidEntry(entry)) continue;
  169.  
  170. tt = ctx.getIdFromEntry(entry);
  171. if (!tt) continue;
  172.  
  173. if (ctx.modifyEntry) ctx.modifyEntry(entry);
  174. lists = self.inLists(ctx, tt, entry);
  175.  
  176. processingType = ctx.determineType(lists, tt, entry);
  177.  
  178. if (processingType) {
  179. ctx.processItem(entry, tt, processingType);
  180. entry.TLProcessingType = processingType;
  181. }
  182.  
  183. entry.TLProcessed = true; // set to "true" after processing (so we skip it on next pass)
  184. }
  185. };
  186.  
  187.  
  188. this.toggleTitle = function(evt) {
  189. evt.stopPropagation();
  190. evt.preventDefault();
  191. var data = evt.target.dataset;
  192. var ctx = self.mainContext;
  193.  
  194. // get title entry
  195. var entry = evt.target;
  196. if (Number.isInteger(Number(data.howToFindEntry))) {
  197. for (var i = 0; i < Number(data.howToFindEntry); i++) entry = entry.parentNode;
  198. } else {
  199. entry = entry.closest(data.howToFindEntry);
  200. }
  201.  
  202. var tt = ctx.getIdFromEntry(entry);
  203. if (!tt) return;
  204.  
  205. // check if item is in list
  206. var list = ctx.allLists[data.toggleList];
  207. if (!list) list = ctx.allLists[data.toggleList] = {};
  208. if (list[tt.id]) {
  209. delete list[tt.id];
  210. ctx.unProcessItem(entry, tt, data.toggleType);
  211. entry.TLProcessingType = "-" + data.toggleType;
  212. } else {
  213. list[tt.id] = tt.title;
  214. ctx.processItem(entry, tt, data.toggleType);
  215. entry.TLProcessingType = data.toggleType;
  216. }
  217. self.saveList(ctx, list, data.toggleList);
  218. };
  219.  
  220.  
  221.  
  222. this.addToggleEventOnClick = function(button, toggleType, toggleList, howToFindEntry) {
  223. button.dataset.toggleType = toggleType;
  224. button.dataset.toggleList = toggleList;
  225. button.dataset.howToFindEntry = howToFindEntry;
  226. button.addEventListener('click', self.toggleTitle, false);
  227. };
  228.  
  229.  
  230. })();