1024社区收藏夹

为社区帖子添加一个本地收藏夹,支持导入导出功能。支持图片批量下载。该插件在手机端几乎可以用网页替代社区客户端。支持手机和电脑浏览器Tampermonkey、暴力猴。

  1. // ==UserScript==
  2. // @name 1024社区收藏夹
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.43
  5. // @description 为社区帖子添加一个本地收藏夹,支持导入导出功能。支持图片批量下载。该插件在手机端几乎可以用网页替代社区客户端。支持手机和电脑浏览器Tampermonkey、暴力猴。
  6. // @author niuhe
  7. // @include http://*/htm_data/*
  8. // @match https://*/htm_data/*
  9. // @match http://*/htm_mob/*
  10. // @match https://*/htm_mob/*
  11. // @match http://*/profile.php?action=favor
  12. // @match https://*/profile.php?action=favor
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_listValues
  16. // @grant GM_setClipboard
  17. // @grant GM_download
  18. // @grant GM_deleteValue
  19. // @grant GM_info
  20. // @license MIT
  21. // ==/UserScript==
  22.  
  23. //为了和手机格式统一,url一律以http://**/htm_mob/格式保存
  24.  
  25.  
  26.  
  27.  
  28.  
  29. const DAT_EVENT_IMPORT = "dat_import";
  30. const DAT_EVENT_RECORD = "dat_record";
  31. const DAT_EVENT_STATUS = "dat_status";
  32. const SEP_TAG = "<:separator;>"
  33.  
  34. var url_base = "http://**/htm_mob/"
  35. var url = document.URL
  36. var url_array = document.URL.split("/")
  37. //CSS选择器
  38. var biaoti_selector = ""
  39. var bankuai_selector = ""
  40. var image_selector = ""
  41. var tag_loc = ""
  42. //UA是否是手机端
  43. var mobile = IsMob()
  44. //用几个标识符来判断页面是收藏页/主题帖(手机)/主题帖(PC)
  45.  
  46. var is_collection_page = (document.URL.split("/").pop() == "profile.php?action=favor")
  47. var is_htm_mob = (url_array[3] == "htm_mob")
  48. var is_htm_data = (url_array[3] == "htm_data")
  49.  
  50.  
  51. url_array[2] = "**"
  52. url_array[3] = "htm_mob"
  53. url = url_array.join("/")
  54.  
  55. var time1 = TimeFormat();
  56. var biaoti = ""
  57. var bankuai = ""
  58.  
  59.  
  60. //获取主题帖标题和板块信息
  61. if (is_collection_page == false)
  62. {
  63. console.log("I am getting bankuai info")
  64. if (is_htm_mob)
  65. {
  66. biaoti_selector = "#main > div.f18"
  67. bankuai_selector = "#header > table > tbody > tr > td > h3"
  68. tag_loc = "h guide"
  69. }
  70. if (is_htm_data)
  71. {
  72. biaoti_selector = "#main > div:nth-child(4) > table > tbody > tr.tr1.do_not_catch > th:nth-child(2) > table > tbody > tr > td > h4"
  73. bankuai_selector = "#main > div:nth-child(1) > table > tbody > tr:nth-child(1) > td > b > a:nth-child(2)"
  74. tag_loc = 'tiptop'
  75. }
  76. biaoti = document.querySelector(biaoti_selector).textContent
  77. if (is_htm_mob)
  78. {
  79. var a = document.querySelector(biaoti_selector).innerText
  80. biaoti = a.substr(0,a.length-4)
  81.  
  82. }
  83. bankuai = document.querySelector(bankuai_selector).textContent
  84. console.log("biaoti" + biaoti)
  85. }
  86.  
  87.  
  88. var key = url_array.slice(4,7).join("/")
  89. var content = biaoti + " " + bankuai + SEP_TAG + url + SEP_TAG + time1
  90. //console.log(content)
  91.  
  92.  
  93. document.addEventListener(DAT_EVENT_IMPORT, function (e) {
  94. //Custom event after CSV import
  95. alert("导入成功,刷新页面查看")
  96.  
  97. });
  98.  
  99.  
  100. document.addEventListener(DAT_EVENT_STATUS, function (e) {
  101. // Custom event on set status
  102. alert("导入成功,已和本地收藏合并");
  103. });
  104.  
  105. //默认设置cookie的path是根目录
  106. function setCookie ( name, value, expdays, path="/" )
  107. {
  108. var expdate = new Date();
  109. //设置Cookie过期日期
  110. expdate.setDate(expdate.getDate() + expdays) ;
  111. //添加Cookie
  112. document.cookie = name + "=" + escape(value) + ";expires=" + expdate.toUTCString() + ";path=" + path;
  113. console.log("set cookie success")
  114. }
  115.  
  116.  
  117. function getCookieValue(name) {
  118. let result = document.cookie.match("(^|[^;]+)\\s*" + name + "\\s*=\\s*([^;]+)")
  119. return result ? result.pop() : ""
  120. }
  121.  
  122. //清除所有cookie函数
  123. function clearAllCookie() {
  124. var keys = document.cookie.match(/[^ =;]+(?=\=)/g);
  125. if (keys) {
  126. for (var i = keys.length; i--;)
  127. document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString()
  128. }
  129. }
  130.  
  131.  
  132. function IsMob() {
  133. var userAgentInfo = navigator.userAgent;
  134. var Agents = ["Android", "iPhone",
  135. "SymbianOS", "Windows Phone",
  136. "iPad", "iPod"];
  137. var flag = false;
  138. for (var v = 0; v < Agents.length; v++) {
  139. if (userAgentInfo.indexOf(Agents[v]) > 0) {
  140. flag = true;
  141. break;
  142. }
  143. }
  144. return flag;
  145. }
  146.  
  147.  
  148. //手机和PC自适应,并重定向页面
  149. function Set_Cookie()
  150. {
  151. if (mobile)
  152. {
  153. let is_mob= getCookieValue("ismob")
  154. if (is_mob != "1")
  155. {
  156. setCookie("ismob","1",360)
  157. console.log(" Mob cookie changed")
  158. }
  159. else
  160. {
  161. console.log(" Mob cookie not changed")
  162. }
  163.  
  164. }
  165. else
  166. {
  167. let is_mob= getCookieValue("ismob")
  168. if (is_mob == "1")
  169. {
  170. setCookie("ismob","0",360)
  171. console.log(" PC cookie changed,Redirect Page")
  172. }
  173. else
  174. {
  175. console.log(" PC cookie not changed")
  176. }
  177.  
  178. }
  179. }
  180.  
  181.  
  182. function sleep(ms) {
  183. return new Promise(resolve => setTimeout(resolve, ms));
  184. }
  185.  
  186.  
  187. function TimeFormat() {
  188. var fmt = "yyyy-MM-dd hh:mm:ss"
  189. var myDate = new Date();
  190. var o = {
  191. "M+": myDate.getMonth() + 1, //月份
  192. "d+": myDate.getDate(), //日
  193. "h+": myDate.getHours(), //小时
  194. "m+": myDate.getMinutes(), //分
  195. "s+": myDate.getSeconds(), //秒
  196. "q+": Math.floor((myDate.getMonth() + 3) / 3), //季度
  197. "S": myDate.getMilliseconds() //毫秒
  198. };
  199. if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (myDate.getFullYear() + "").substr(4 - RegExp.$1.length));
  200. for (var k in o)
  201. {
  202. if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
  203. }
  204. return fmt;
  205. }
  206.  
  207. function Collecte(){
  208. if (mobile == true)
  209. {
  210. var tmp = document.createElement("text")
  211. tmp.textContent = "|"
  212. document.getElementsByClassName(tag_loc)[0].appendChild(tmp)
  213. }
  214. var a = document.createElement('a');
  215. //a.href = 'data:application/dat;charset=utf-8,' + encodeURIComponent(data);
  216. //supported by chrome 14+ and firefox 20+
  217. //a.download = 'favorite.dat';
  218. if ("none" == GM_getValue(key,"none"))
  219. {
  220. a.text = " 收藏 "
  221. a.style = "color:#2F5FA1;"
  222. }
  223. else
  224. {
  225. a.text = " 已收藏 "
  226. a.style = "color:#FF0000;"
  227. }
  228.  
  229. document.getElementsByClassName(tag_loc)[0].appendChild(a);
  230.  
  231. a.addEventListener("click", function (value) {
  232. if (a.text == " 收藏 ")
  233. {
  234. GM_setValue(key,content);
  235. a.text = " 已收藏 "
  236. a.style = "color:#FF0000;"
  237.  
  238. }
  239. else
  240. {
  241. GM_deleteValue(key);
  242. a.text = " 收藏 "
  243. a.style = "color:#2F5FA1;"
  244. }
  245. //alert("添加收藏成功\n\n" + content)
  246. });
  247. }
  248.  
  249. function Export_Collection(data){
  250. if (mobile == true)
  251. {
  252. var tmp = document.createElement("text")
  253. tmp.textContent = "|"
  254. document.getElementsByClassName(tag_loc)[0].appendChild(tmp)
  255. }
  256. var keys = GM_listValues()
  257. var a = document.createElement('a');
  258. //supported by chrome 14+ and firefox 20+
  259. a.download = 'favorite.dat';
  260. a.text = " 導出 "
  261. //needed for firefox
  262. document.getElementsByClassName(tag_loc)[0].appendChild(a);
  263. a.addEventListener("click", function (value) {
  264. var keys = GM_listValues()
  265. var data = []
  266. for(var i = 0, len = keys.length; i < len; i++){
  267. data.push(GM_getValue(keys[i]))
  268. }
  269. data = data.join("\n")
  270. a.href = 'data:application/dat;charset=utf-8,' + encodeURIComponent(data);
  271. alert("导出成功")
  272. });
  273. }
  274.  
  275. function Gen_Input()
  276. {
  277. let input = document.createElement("input");
  278. input.type = "file";
  279. input.accept=".dat"
  280. Object.assign(input.style, {
  281. position: "relative"
  282. });
  283. input.addEventListener("input", function (value) {
  284. let reader = new FileReader();
  285. reader.readAsText(value.target.files[0]);
  286. reader.onload = function () {
  287. var dat_array = reader.result.split("\n")
  288. for (var k = 0 ,len2 = dat_array.length; k < len2;k++)
  289. {
  290. GM_setValue(Gen_Key(dat_array[k]),dat_array[k])
  291. }
  292. document.dispatchEvent(new Event(DAT_EVENT_IMPORT))
  293. };
  294. });
  295. return input
  296. }
  297.  
  298.  
  299. function Import_Collection(){
  300. if (mobile == true)
  301. {
  302. var tmp = document.createElement("text")
  303. tmp.textContent = "|"
  304. document.getElementsByClassName(tag_loc)[0].appendChild(tmp)
  305. }
  306. var a = document.createElement('a');
  307. //supported by chrome 14+ and firefox 20+
  308. a.download = 'favorite.dat';
  309. a.text = " 導入 "
  310. //needed for firefox
  311. document.getElementsByClassName(tag_loc)[0].appendChild(a);
  312. var input = Gen_Input()
  313. document.getElementsByClassName(tag_loc)[0].appendChild(input);
  314.  
  315. }
  316.  
  317.  
  318. async function downloader(image_urls) {
  319. console.log('start downloading');
  320.  
  321. // Sleep in loop
  322. for(var img = 1, length1 = image_urls.length; img <= length1; img++){
  323. var extension = image_urls[img-1].split(".").pop()
  324. GM_download(image_urls[img-1], biaoti + "[" + img + "]." + extension)
  325. console.log("Download Image" + img)
  326. await sleep(2000)
  327. }
  328. await sleep(2000);
  329. }
  330.  
  331. function Download_Pics()
  332. {
  333. if (mobile == true)
  334. {
  335. var tmp = document.createElement("text")
  336. tmp.textContent = "|"
  337. document.getElementsByClassName(tag_loc)[0].appendChild(tmp)
  338. }
  339. var a = document.createElement('a');
  340. //supported by chrome 14+ and firefox 20+
  341. a.text = " 下載 "
  342. //needed for firefox
  343. document.getElementsByClassName(tag_loc)[0].appendChild(a);
  344. a.addEventListener("click", async function (value) {
  345. var images = null
  346. var image_urls = []
  347. if (mobile == true)
  348. {
  349. images = document.querySelector(".tpc_cont").getElementsByTagName("img")
  350. }
  351. else
  352. {
  353. images = document.querySelector(".tpc_content").getElementsByTagName("img")
  354. }
  355. for(var i = 0, len = images.length; i < len; i++){
  356. image_urls.push(images[i].src)
  357. }
  358. var result = image_urls.join("\n")
  359. GM_setClipboard(result)
  360. var info = confirm("已复制" + image_urls.length + "张图片链接到粘贴板,现在下载吗?")
  361. if (info == true)
  362. {
  363. await downloader(image_urls)
  364. }
  365.  
  366. });
  367.  
  368. }
  369.  
  370.  
  371. function Gen_Key(value)
  372. {
  373. var url = value.split("<:separator;>")[1]
  374. var url_array = url.split("/")
  375. var key = url_array.slice(4,7).join("/")
  376. return key
  377. }
  378.  
  379. function Gen_Node(index,key,row_base,value) //
  380. {
  381. var tmp = value.split("<:separator;>")
  382. var time = tmp[2]
  383.  
  384. var tmp_url = tmp[1]
  385. tmp_url = tmp_url.replace(/\*\*/, document.URL.split("/")[2])
  386. tmp_url = tmp_url.replace(/htm_mob/, "htm_data")
  387. var tmp_str = tmp[0].split(" ")
  388. var tmp_bankuai = tmp_str.pop()
  389. var tmp_biaoti = tmp_str.join(" ")
  390. var tmp_node = row_base.cloneNode(true)
  391. tmp_node.className = "tr3"
  392. tmp_node.children[0].innerText = index
  393. tmp_node.children[1].innerText = tmp_biaoti
  394. tmp_node.children[1].setAttribute("onclick","window.open('" + tmp_url + "');")
  395. tmp_node.children[2].innerText = tmp_bankuai
  396. tmp_node.children[3].innerText = time
  397. tmp_node.children[4].innerText = "删除"
  398. tmp_node.children[4].setAttribute("index",index)
  399. tmp_node.children[4].setAttribute("align","center")
  400. tmp_node.children[4].onclick = function()
  401. {
  402. if (tmp_node.children[4].innerText == "删除")
  403. {
  404. tmp_node.remove()
  405. GM_deleteValue(key);
  406.  
  407. }
  408. };
  409. return tmp_node
  410. }
  411.  
  412. //Gen node for mobile
  413. function Gen_Mob_Node(index,key,row_base,value) //
  414. {
  415. var tmp = value.split("<:separator;>")
  416. var time = tmp[2]
  417.  
  418. var tmp_url = tmp[1]
  419. tmp_url = tmp_url.replace(/\*\*/, document.URL.split("/")[2])
  420. tmp_url = tmp_url.replace(/htm_data/, "htm_mob")
  421. var tmp_str = tmp[0].split(" ")
  422. var tmp_bankuai = tmp_str.pop()
  423. var tmp_biaoti = tmp_str.join(" ")
  424. var tmp_node = row_base.cloneNode(true)
  425. tmp_node.className = "tr3 tac"
  426. tmp_node.children[0].innerText = tmp_biaoti
  427. tmp_node.children[0].setAttribute("onclick","window.open('" + tmp_url + "');")
  428. tmp_node.children[1].innerText = "删除"
  429. tmp_node.children[1].setAttribute("index",index)
  430. tmp_node.children[1].setAttribute("align","center")
  431. tmp_node.children[1].onclick = function()
  432. {
  433. if (tmp_node.children[1].innerText == "删除")
  434. {
  435. GM_deleteValue(key);
  436. tmp_node.children[1].innerText = "已删除"
  437. }
  438. };
  439. return tmp_node
  440. }
  441.  
  442.  
  443. //time key
  444. function extract_key_list()
  445. {
  446. var keys = GM_listValues()
  447. var result = []
  448. var tmps = []
  449. for (var i=0 ,len1 = keys.length; i < len1; i++)
  450. {
  451. var tmp = []
  452. var bar = GM_getValue(keys[i])
  453. if (bar)
  454. {
  455. //time
  456. tmp.push(bar.split("<:separator;>").pop())
  457. }
  458.  
  459. tmp.push(keys[i])
  460. tmps.push(tmp)
  461. }
  462.  
  463. tmps.sort(function (a,b){return Date.parse(a[0]) < Date.parse(b[0]) ? 1 : -1;})
  464. console.log(tmps)
  465. for (var j=0 ,len2 = tmps.length; j < len2; j++)
  466. {
  467. result.push(tmps[j][1])
  468. }
  469.  
  470. return result
  471. }
  472.  
  473.  
  474.  
  475.  
  476.  
  477. function PC_Check_Collection()
  478. {
  479. console.log("打开收藏夹")
  480. try
  481. {
  482. var father = document.getElementsByClassName("t")[2].getElementsByTagName("tr")
  483. }
  484. catch (err)
  485. {
  486. alert("需要登录才能查看/编辑收藏夹\n\n未登录状态只能进行添加/导入/导出操作\n\n收藏内容保存在本地,卸载脚本会丢失收藏夹,请及时导出备份!")
  487. }
  488. if (father.length > 2 )
  489. {
  490. for (var i= 2 ,len = father.length; i < len;i++)
  491. {
  492. father[i].remove()
  493. }
  494. }
  495. father[1].getElementsByTagName("td")[3].innerText = "時間"
  496. father[1].getElementsByTagName("td")[4].innerText = "操作"
  497. document.querySelector("#main > div:nth-child(3) > table > tbody > tr > td:nth-child(2) > form > center").remove()
  498.  
  499. var row_base = father[1]
  500.  
  501.  
  502. var keys = extract_key_list()
  503.  
  504. for(var j = 0, len1 = keys.length; j < len1; j++){
  505. var tmp_node = Gen_Node(j+1,keys[j] ,row_base, GM_getValue(keys[j]))
  506. father[1].parentElement.append(tmp_node)
  507. }
  508. var upload_entry = document.querySelector("#main > div:nth-child(3) > table > tbody > tr > td:nth-child(1) > form > div > table > tbody > tr:nth-child(4) > th")
  509. upload_entry.innerText = " 導入 "
  510. var input = Gen_Input()
  511. upload_entry.appendChild(input)
  512. }
  513.  
  514. function Mob_Check_Collection()
  515. {
  516. console.log("打开收藏夹")
  517. try
  518. {
  519. document.querySelector("#main > div:nth-child(3) > table > tbody > tr:nth-child(2) > td > form > center").remove()
  520.  
  521. var father = document.getElementsByClassName("t")[2].getElementsByTagName("tr")
  522. }
  523. catch (err)
  524. {
  525. alert("需要登录才能查看/编辑收藏夹\n\n未登录状态只能进行添加/导入/导出操作\n\n收藏内容保存在本地,卸载脚本会丢失收藏夹,请及时导出备份!")
  526. }
  527. if (father.length > 1 )
  528. {
  529. for (var i= 1 ,len = father.length; i < len;i++)
  530. {
  531. father[i].remove()
  532. }
  533. }
  534.  
  535. father[0].children[1].width = "15%"
  536. father[0].children[1].innerText = "操作"
  537. father[0].children[1].innerText.align="right"
  538.  
  539. var row_base = father[0]
  540.  
  541.  
  542. var keys = extract_key_list()
  543.  
  544. for(var j = 0, len1 = keys.length; j < len1; j++){
  545. var tmp_node = Gen_Mob_Node(j+1,keys[j] ,row_base, GM_getValue(keys[j]))
  546. father[0].parentElement.append(tmp_node)
  547. }
  548.  
  549. document.getElementsByClassName("tr1")[0].parentNode.remove()
  550.  
  551. var upload_entry = document.getElementsByClassName("tr1")[0].children[0]
  552. upload_entry.innerText = "導入 "
  553.  
  554. var input = Gen_Input()
  555. upload_entry.appendChild(input)
  556. }
  557.  
  558. (function() {
  559. 'use strict';
  560. Set_Cookie()
  561.  
  562.  
  563. if (is_collection_page == false)
  564. {
  565. Collecte()
  566. if (bankuai == "新時代的我們" || bankuai == "達蓋爾的旗幟")
  567. {
  568. Download_Pics()
  569. }
  570. Export_Collection()
  571. Import_Collection()
  572. //Import_Collection()
  573. }
  574. else
  575. {
  576.  
  577.  
  578. if (mobile )
  579. {
  580. Mob_Check_Collection()
  581. }
  582. else
  583. {
  584. PC_Check_Collection()
  585. }
  586. }
  587.  
  588. })();