browndust2.com news viewer

custom news viewer for sucking browndust2.com

目前为 2024-10-16 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name browndust2.com news viewer
  3. // @namespace Violentmonkey Scripts
  4. // @match https://www.browndust2.com/robots.txt
  5. // @grant none
  6. // @version 1.1.0
  7. // @author Rplus
  8. // @description custom news viewer for sucking browndust2.com
  9. // @license WTFPL
  10. // ==/UserScript==
  11.  
  12. document.head.innerHTML = `
  13. <link rel="icon" type="image/png" sizes="16x16" href="/img/seo/favicon.png">
  14. `;
  15.  
  16. document.body.innerHTML = `
  17. <form id="filterform">
  18. Filter
  19. <input type="search" name="q" tabindex="1" id="searchinput">
  20. <style id="filter_style"></style>
  21. </form>
  22.  
  23. <div class="list" id="list" data-query=""></div>
  24. <hr>
  25. <label class="showall-label">
  26. <input type="checkbox" class="showall" >
  27. show all list
  28. </label>
  29. <style>
  30. *, *::before, *::after {
  31. box-sizing: border-box;
  32. }
  33. body {
  34. max-width: 1200px;
  35. margin: 0 auto;
  36. background-color: #e5cc9c;
  37. }
  38.  
  39. img {
  40. max-width: 100%;
  41. }
  42.  
  43. .ctx {
  44. white-space: pre-wrap;
  45. background-color: #fff9;
  46. padding: 1em;
  47. }
  48.  
  49. .list {
  50. list-style: none;
  51. margin: 2em 0;
  52. padding-left: 50px;
  53. }
  54.  
  55. summary {
  56. position: relative;
  57. top: 0;
  58. background-color: #dfb991;
  59. padding: 1em 1em .75em;
  60. min-height: 50px;
  61. cursor: pointer;
  62.  
  63. &::before {
  64. content: '';
  65. position: absolute;
  66. inset: 0;
  67. background-color: #0001;
  68. pointer-events: none;
  69. opacity: 0;
  70. transition: opacity .1s;
  71. }
  72.  
  73. &:hover::before {
  74. opacity: 1;
  75. }
  76.  
  77. & > img {
  78. position: absolute;
  79. top: 0;
  80. right: 100%;
  81. width: 50px;
  82. height: 50px;
  83. }
  84. }
  85.  
  86. details {
  87. margin-block-start: 1em;
  88.  
  89. &[open] summary {
  90. position: sticky;
  91. background-color: #ceac71;
  92. box-shadow: inset 0 -.5em #0003;
  93. }
  94. }
  95.  
  96. #filterform {
  97. position: fixed;
  98. top: 0;
  99. right: 0;
  100. transition: opacity .2s;
  101. opacity: .1;
  102.  
  103. &:hover,
  104. &:focus-within {
  105. opacity: .75;
  106. }
  107. }
  108.  
  109. body:not(:has(.showall:checked))
  110. .list[data-query=""]
  111. details:nth-child(n + 20) {
  112. display: none;
  113. }
  114.  
  115. .showall-label {
  116. position: sticky;
  117. bottom: 0;
  118. display: block;
  119. width: fit-content;
  120. margin: 0 1em 0 auto;
  121. padding: .25em 1em .25em .5em;
  122. background-color: #0002;
  123. border-radius: 1em 1em 0 0;
  124. cursor: pointer;
  125. }
  126. </style>
  127. `;
  128.  
  129. let data = [];
  130. let news_map = new Map();
  131. let query_arr = [];
  132. let id_arr = [];
  133.  
  134. function render(id = 34) {
  135. list.innerHTML = data.map(i => {
  136. let info = i.attributes;
  137. let ctx = info.NewContent || info.content;
  138. let time = format_time(info.publishedAt);
  139. return `
  140. <details name="item" data-id="${i.id}">
  141. <summary>
  142. <img src="https://www.browndust2.com/img/newsDetail/tag-${info.tag}.png" width="36" height="36" alt="${info.tag}" title="#${info.tag}">
  143. #${i.id} - <time datetime="${info.publishedAt}" title="${info.publishedAt}">${time}</time>
  144. ${info.subject}
  145. </summary>
  146. <div class="ctx"></div>
  147. </details>
  148. `;
  149. }).join('');
  150.  
  151. list.querySelectorAll('details').forEach(d => {
  152. d.addEventListener('toggle', (e) => {
  153. show(e.target, e.target.dataset.id);
  154. });
  155. })
  156.  
  157. if (id) {
  158. taget_id(id);
  159. }
  160. }
  161.  
  162. function taget_id(id) {
  163. let target = list.querySelector(`details[data-id="${id}"]`);
  164. target.open = true;
  165. show(target, id);
  166. }
  167.  
  168. function show(target, id) {
  169. let ctx = target.querySelector(':scope > div.ctx');
  170. if (!(ctx?.dataset?.init === '1')) {
  171. ctx.dataset.init = '1';
  172. let info = news_map.get(+id)?.attributes;
  173. let ori_link = `<a href="https://www.browndust2.com/zh-tw/news/view?id=${id}" target="_bd2news" title="official link">#</a>`;
  174. let content = (info?.content || info?.NewContent);
  175. content = content.replace(/\<img\s/g, '<img loading="lazy" ');
  176.  
  177. ctx.innerHTML = content + ori_link;
  178.  
  179. setTimeout(() => {
  180. target.scrollIntoView({ behavior: 'smooth', });
  181. }, 350);
  182. }
  183. }
  184.  
  185. const time_format = {
  186. weekday: 'narrow',
  187. year: 'numeric',
  188. month: '2-digit',
  189. day: '2-digit',
  190. };
  191. function format_time(time) {
  192. let _time = time ? new Date(time) : new Date();
  193. return _time.toLocaleString('zh-TW', time_format);
  194. }
  195.  
  196. function query() {
  197. let value = searchinput.value;
  198. // console.log('query', value);
  199. if (!value) {
  200. filter_style.textContent = '';
  201. list.dataset.query = '';
  202. return;
  203. }
  204.  
  205. let matched_ids = query_arr.map((i, index) => {
  206. // if (!i.includes(value)) {
  207. // if (i.indexOf(value) === -1) {
  208. let regex = new RegExp(value, 'i');
  209. if (!regex.test(i)) {
  210. return;
  211. }
  212. return id_arr[index];
  213. })
  214. .filter(Boolean);
  215.  
  216. if (!matched_ids.length) {
  217. list.dataset.query = '';
  218. } else {
  219. list.dataset.query = value;
  220. }
  221.  
  222. let selectors = matched_ids.map(i => `[data-id="${i}"]`).join();
  223. filter_style.textContent = `
  224. details {display:none;}
  225. details:is(${selectors}) { display: block; }
  226. `;
  227. }
  228.  
  229. function debounce(func, wait, immediate) {
  230. var timeout;
  231. return function() {
  232. var context = this, args = arguments;
  233. clearTimeout(timeout);
  234. if (immediate && !timeout) func.apply(context, args);
  235. timeout = setTimeout(function() {
  236. timeout = null;
  237. if (!immediate) func.apply(context, args);
  238. }, wait);
  239. };
  240. }
  241.  
  242. let data_url = test_data_url || 'https://www.browndust2.com/api/newsData_tw.json';
  243. fetch(data_url)
  244. .then(r => r.json())
  245. .then(d => {
  246. data = d.data.reverse();
  247. tags = [...new Set(data.map(i => {
  248. let info = i.attributes;
  249. news_map.set(i.id, i);
  250. id_arr.push(i.id);
  251. query_arr.push([
  252. i.id,
  253. info.content,
  254. info.NewContent,
  255. `#${info.tag}`,
  256. info.subject,
  257. ].join());
  258. return i.attributes.tag;
  259. }))];
  260.  
  261. // console.log(data[900], tags);
  262. let id = new URL(location.href)?.searchParams?.get('id') || data[data.length - 1].id || 34;
  263. render(id);
  264. });
  265.  
  266. filterform.addEventListener('submit', e => e.preventDefault());
  267. searchinput.addEventListener('input', debounce(query, 300));

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址