Greasy Fork is available in English.

hipda-ID笔记

来自地板带着爱,记录上网冲浪的美好瞬间

目前为 2021-12-08 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name hipda-ID笔记
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description 来自地板带着爱,记录上网冲浪的美好瞬间
  6. // @author 屋大维
  7. // @license MIT
  8. // @match https://www.hi-pda.com/forum/viewthread.php?tid*
  9. // @resource IMPORTED_CSS https://code.jquery.com/ui/1.13.0/themes/base/jquery-ui.css
  10. // @require https://code.jquery.com/jquery-3.4.1.min.js
  11. // @require https://code.jquery.com/ui/1.13.0/jquery-ui.js
  12. // @icon https://icons.iconarchive.com/icons/iconshock/real-vista-project-managment/64/task-notes-icon.png
  13. // @grant GM.setValue
  14. // @grant GM.getValue
  15. // @grant GM.deleteValue
  16. // @grant GM_getResourceText
  17. // @grant GM_addStyle
  18. // ==/UserScript==
  19.  
  20. (async function() {
  21. 'use strict';
  22. // CSS
  23. const my_css = GM_getResourceText("IMPORTED_CSS");
  24. GM_addStyle(my_css);
  25. GM_addStyle(".no-close .ui-dialog-titlebar-close{display:none} textarea{height:100%;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}");
  26.  
  27. // Your code here...
  28. // helpers
  29. function htmlToElement(html) {
  30. var template = document.createElement('template');
  31. html = html.trim(); // Never return a text node of whitespace as the result
  32. template.innerHTML = html;
  33. return template.content.firstChild;
  34. }
  35.  
  36. // classes
  37. class HpPost {
  38. constructor() {
  39. }
  40.  
  41. getPostTid() {
  42. return location.href.match(/tid=(\d+)/) ? parseInt(location.href.match(/tid=(\d+)/)[1]) : -999;
  43. }
  44.  
  45. getUserUid() {
  46. return parseInt($("cite > a").attr("href").split("uid=")[1]);
  47. }
  48.  
  49. getHpThreads() {
  50. let postTid = this.getPostTid();
  51. let divs = $('#postlist > div').get();
  52. return divs.map(d => new Hpthread(postTid, d));
  53. }
  54.  
  55. addNoteManagementUI(_notebook) {
  56. var that = this;
  57. var button = htmlToElement(`
  58. <button id="noteButton_management">
  59. <span><img src="https://icons.iconarchive.com/icons/iconshock/real-vista-project-managment/32/task-notes-icon.png"></img></span>
  60. </button>
  61. `);
  62.  
  63. // create dialog
  64. let dialog = htmlToElement(`
  65. <div id="noteDialog_management" style="display: none;">
  66. <h3>hipda-ID笔记 v${GM_info.script.version}</h3>
  67. <p style="margin: 10px auto 10px auto;">来自地板带着爱</p>
  68. <div>
  69. <button id="noteButton_import">导入</button>
  70. <button id="noteButton_export">导出</button>
  71. <button id="noteButton_reset">重置</button>
  72. </div>
  73. </div>
  74. `);
  75. $("body").append(dialog);
  76.  
  77. $(document).ready( function () {
  78. $(document).on ("click", "#noteButton_import", function() {
  79. let r = confirm("确定要导入ID笔记吗?现有笔记将会被覆盖!");
  80. if (!r) {
  81. return;
  82. }
  83.  
  84. // prompt cannot handle large file, extend it in the future
  85. let data = prompt("请将 id笔记.json 中的文本复制粘贴入文本框:");
  86. if (data !== null) {
  87. // try to load
  88. try {
  89. let j = JSON.parse(data);
  90. _notebook.importNotebook(j);
  91. } catch(err) {
  92. alert("格式错误!" + err);
  93. return;
  94. }
  95. alert("导入成功!");
  96. }
  97. });
  98. $(document).on ("click", "#noteButton_export", function() {
  99. let r = confirm("确定要导出ID笔记吗?");
  100. if (!r) {
  101. return;
  102. }
  103. let a = document.createElement("a");
  104. a.href = "data:text," + _notebook.exportNotebook();
  105. a.download = "id笔记.json";
  106. a.click();
  107. });
  108. $(document).on ("click", "#noteButton_reset", function() {
  109. let r = confirm("确定要清空ID笔记吗?");
  110. if (!r) {
  111. return;
  112. }
  113. _notebook.resetNotebook();
  114. alert("ID笔记已经清空!");
  115. });
  116. $(document).on ("click", `#noteButton_management`, function () {
  117. console.log("open notebook management dialog");
  118. $(`#noteDialog_management`).dialog({
  119. title: "ID笔记:管理面板",
  120. height: 150,
  121. width: 300,
  122. closeOnEscape: true,
  123. });
  124. });
  125. });
  126.  
  127. // add UI
  128. let d = $("td.modaction").last();
  129. d.append(button);
  130.  
  131. }
  132.  
  133. }
  134.  
  135. class Hpthread {
  136. constructor(postTid, threadDiv) {
  137. this.postTid = postTid;
  138. this._thread_div = threadDiv;
  139. }
  140.  
  141. getThreadAuthorName() {
  142. return $(this._thread_div).find("div.postinfo > a").first().text();
  143. }
  144.  
  145. getThreadAuthorUid() {
  146. return parseInt($(this._thread_div).find("div.postinfo > a").first().attr("href").split("uid=")[1]);
  147. }
  148.  
  149. getThreadPid() {
  150. return parseInt($(this._thread_div).attr("id").split("_")[1]);
  151. }
  152.  
  153. getGotoUrl() {
  154. return `https://www.hi-pda.com/forum/redirect.php?goto=findpost&ptid=${this.postTid}&pid=${this.getThreadPid()}`;
  155. }
  156.  
  157. getThreadContent() {
  158. // get text without quotes
  159. return $(this._thread_div).find("td.t_msgfont").first().clone().children().remove().end().text().trim();
  160. }
  161.  
  162. getThreadBrief(n) {
  163. let content = this.getThreadContent();
  164. if (content.length <= n) {
  165. return content;
  166. }
  167. return content.slice(0, n) + "\n\n【以上为截取片段】" ;
  168. }
  169.  
  170. addNoteUI(_notebook) {
  171. let uid = this.getThreadAuthorUid();
  172. let index = $(this._thread_div).index();
  173. let userName = this.getThreadAuthorName();
  174.  
  175. var that = this;
  176. // create an UI element which contains data and hooks
  177. // button
  178. let button = htmlToElement(`
  179. <button id="noteButton_${index}" style="color:grey; margin-left:20px;">
  180. ID笔记
  181. </button>
  182. `);
  183. // note dialog
  184. let dialog = htmlToElement(`
  185. <div id="noteDialog_${index}" style="display: none;">
  186. <textarea rows="10" wrap="hard" placeholder="暂时没有笔记">
  187. </div>
  188. `);
  189. $("body").append(dialog);
  190.  
  191. // add event to button
  192. $(document).ready( function () {
  193. $(document).on ("click", `#noteButton_${index}`, function () {
  194. console.log("open note for", userName);
  195. // freshly fetched from DB
  196. $(`#noteDialog_${index}`).find('textarea').first().val(_notebook.get(uid));
  197. $(`#noteDialog_${index}`).dialog({
  198. title: `ID笔记:${userName}`,
  199. dialogClass: "no-close",
  200. closeText: "hide",
  201. closeOnEscape: true,
  202. height: 350,
  203. width: 600,
  204. buttons: [
  205. {
  206. text: "插入当前楼层",
  207. click: function() {
  208. let txt = $(`#noteDialog_${index}`).find('textarea').first();
  209. var caretPos = txt[0].selectionStart;
  210. var textAreaTxt = txt.val();
  211. var txtToAdd = `====\n引用: ${that.getGotoUrl()}\n${that.getThreadAuthorName()} 说:${that.getThreadBrief(200)}\n====`;
  212. txt.val(textAreaTxt.substring(0, caretPos) + txtToAdd + textAreaTxt.substring(caretPos) );
  213. }
  214. },
  215. {
  216. text: "确认",
  217. click: function() {
  218. // save the new note before close
  219. let newNote = $(`#noteDialog_${index}`).find('textarea').first().val();
  220. _notebook.put(uid, userName, newNote);
  221. $(this).dialog( "close" );
  222. }
  223. },
  224. {
  225. text: "取消",
  226. click: function() {
  227. // close without saving
  228. $(this).dialog( "close" );
  229. }
  230. }
  231. ]
  232. });
  233. });
  234. });
  235.  
  236. // add UI
  237. let d = $(this._thread_div).find("td[rowspan='2'].postauthor").first();
  238. d.append(button);
  239. }
  240.  
  241. }
  242.  
  243. class Notebook {
  244. constructor(user_uid) {
  245. // initialization
  246. this._name = "hipda-notebook";
  247. this._user_uid = user_uid;
  248. this._notebook = {};
  249. return (async () => {
  250. this.loadFromLocalStorage();
  251. return this;
  252. })();
  253. }
  254.  
  255. async loadFromLocalStorage() {
  256. console.log("load ID Notebook from Local Storage");
  257. let data = await GM.getValue(this._name, null);
  258. if (data !== null) {
  259. this._notebook = JSON.parse(data);
  260. }
  261. }
  262.  
  263. async saveToLocalStorage() {
  264. console.log("save ID Notebook to Local Storage");
  265. await GM.setValue(this._name, JSON.stringify(this._notebook));
  266. }
  267.  
  268. put(uid, userName, note) {
  269. // we need userName here, so user can analyze notes even after export
  270. this._notebook[uid] = {uid, userName, note};
  271. this.saveToLocalStorage();
  272. }
  273.  
  274. get(uid) {
  275. if (uid in this._notebook) {
  276. return this._notebook[uid].note;
  277. }
  278. return "";
  279. }
  280.  
  281. delete(uid) {
  282. if (uid in this._notebook) {
  283. delete this._notebook[uid];
  284. this.saveToLocalStorage();
  285. }
  286. }
  287.  
  288. exportNotebook() {
  289. // can add meta data here
  290. let output = {
  291. notebook: this._notebook,
  292. version: GM_info.script.version,
  293. timestamp: + new Date()
  294. };
  295. return JSON.stringify(output);
  296. }
  297.  
  298. importNotebook(input) {
  299. let attrs = ['notebook', 'version', 'timestamp'];
  300. for (let i=0; i<attrs.length; i++) {
  301. if (!input.hasOwnProperty(attrs[i])) {
  302. throw(`bad format: ${attrs[i]} does not exist`);
  303. }
  304. }
  305. this._notebook = {...input.notebook};
  306. this.saveToLocalStorage();
  307. }
  308.  
  309. resetNotebook() {
  310. this._notebook = {};
  311. this.saveToLocalStorage();
  312. }
  313. }
  314.  
  315.  
  316.  
  317.  
  318. // get a post object
  319.  
  320. var THIS_POST = new HpPost();
  321.  
  322. // get tid and uid; uid for future extension
  323. var tid = THIS_POST.getPostTid();
  324. var uid = THIS_POST.getUserUid();
  325.  
  326.  
  327. var notebook = await new Notebook(uid);
  328.  
  329. // render UI below
  330. // ID notes
  331. var hp_threads = THIS_POST.getHpThreads();
  332. for (let i=0; i<hp_threads.length; i++) {
  333. let hp_thread = hp_threads[i];
  334. hp_thread.addNoteUI(notebook);
  335. }
  336. // management panel
  337. THIS_POST.addNoteManagementUI(notebook);
  338.  
  339.  
  340. })();