read_log

read log

  1. // ==UserScript==
  2. // @name read_log
  3. // @include *wikipedia*
  4. // @supportURL https://github.com/sxlgkxk/browser_script/issues
  5. // @version 0.3
  6. // @description read log
  7. // @namespace http://sxlgkxk.github.io/
  8. // @author sxlgkxk
  9. // @icon http://sxlgkxk.github.io/im/avatar.jpg
  10. // @license MIT
  11. // @grant GM_getValue
  12. // @grant GM_setValue
  13. // @grant GM_xmlhttpRequest
  14. // ==/UserScript==
  15.  
  16. (function () {
  17.  
  18. let blockWidth = 10
  19. let blockPadding = 3
  20. // weeksCount=53
  21. let weeksCount = 30 // 393, 94
  22. let canvasWidth = (blockWidth + blockPadding) * weeksCount + blockPadding
  23. let canvasHeight = (blockWidth + blockPadding) * 7 + blockPadding
  24.  
  25. //-------------------------------- common functions --------------------------------
  26.  
  27. function addScript(src) {
  28. let scripts_dom = document.createElement('script');
  29. scripts_dom.src = src;
  30. scripts_dom.type = 'text/javascript';
  31. document.getElementsByTagName('head')[0].appendChild(scripts_dom);
  32. }
  33. addScript('https://unpkg.com/axios/dist/axios.min.js')
  34.  
  35. function getScrollPercent() {
  36. let h = document.documentElement,
  37. b = document.body,
  38. st = 'scrollTop',
  39. sh = 'scrollHeight';
  40. return ((h[st] || b[st]) / ((h[sh] || b[sh])) * 100).toFixed(2);
  41. }
  42.  
  43. function getGistToken() {
  44. let gist_token = localStorage.getItem('gist_token')
  45. if (gist_token != null && gist_token.length > 10) {
  46. gist_token = prompt('gist_token?')
  47. if (gist_token) {
  48. localStorage.setItem('gist_token', gist_token)
  49. }
  50. }
  51. return gist_token
  52. }
  53.  
  54. async function gist_get_async(gist_id, filename) {
  55. // gist_get_async("cfa70a44bb181edbb2be19dacbcf6808", "read_log.json").then((content)=>{console.log(content)})
  56. let response = await axios.get("https://api.github.com/gists/" + gist_id)
  57. let data = response.data
  58. let content = data.files[filename].content
  59. return content
  60. }
  61.  
  62. async function gist_set_async(gist_id, filename, content) {
  63. // gist_set_async("cfa70a44bb181edbb2be19dacbcf6808", "read_log.json", "[]").then((content)=>{console.log(content)})
  64. let files = {}
  65. files[filename] = { "content": content }
  66. let gist_token = getGistToken()
  67. return await axios.patch("https://api.github.com/gists/" + gist_id, { files: files }, { headers: { Authorization: "token " + gist_token } })
  68. }
  69.  
  70. function addStyle(html) {
  71. let style = document.createElement("div")
  72. document.body.before(style)
  73. style.innerHTML = `<style>` + html + `</style>`
  74. }
  75.  
  76. //-------------------------------- code snippets --------------------------------
  77.  
  78. addStyle(`
  79. #wpmArea{
  80. position: absolute;
  81. right: 10px;
  82. left:10px;
  83. background-color: rgba(255, 255, 255, 0.2);
  84. }
  85. #heatmap{
  86. background-color: #0d1117;
  87. margin: 0 auto;
  88. display:block;
  89. }
  90. table#history_table {
  91. border-collapse: collapse;
  92. width: 100%;
  93. }
  94. #history_table th, #history_table td {
  95. text-align: left;
  96. padding: 8px;
  97. }
  98. button.pagiBtn{
  99. background-color: #333;
  100. color: #fff;
  101. margin: 4px;
  102. padding-left: 15px;
  103. padding-right: 15px;
  104. padding-top: 9px;
  105. padding-bottom: 9px;
  106. }
  107. `)
  108.  
  109. for (let i = 1; i <= 31; i++) {
  110. addStyle(`td.historyDay` + i + `{background-color: rgba(` + Math.floor(Math.random() * 255) + `,` + Math.floor(Math.random() * 255) + `,` + Math.floor(Math.random() * 255) + `, 0.5);}`)
  111. }
  112.  
  113. function getColor(count) {
  114. let colors = [
  115. "#161b22",
  116. "#cef1dd",
  117. "#9ee3bb",
  118. "#6dd499",
  119. "#3cc677",
  120. "#33a865",
  121. "#2a8b53",
  122. "#216d41"
  123. ]
  124. let max_count = 60 * 8
  125. return colors[Math.ceil(Math.min(count / max_count, 1) * (colors.length - 1))]
  126. }
  127.  
  128. function setBlock(x, y, count, ctx) {
  129. ctx.fillStyle = getColor(count);
  130. ctx.fillRect(x * (blockWidth + blockPadding) - blockWidth, y * (blockWidth + blockPadding) - blockWidth, blockWidth, blockWidth);
  131. }
  132.  
  133. function getRegularWeekday(date) {
  134. let weekday = date.getDay()
  135. return weekday ? weekday : 7
  136. }
  137.  
  138. function sum(array) {
  139. return array.reduce((partialSum, a) => partialSum + a, 0)
  140. }
  141.  
  142. function refreshHeatmap() {
  143. let log = localStorage.getItem('readlog')
  144. log = log ? JSON.parse(log) : {}
  145.  
  146. let now = new Date();
  147. let now1 = new Date(now - getRegularWeekday(now) * 3600 * 24 * 1000)
  148.  
  149. let canvas = document.querySelector('canvas#heatmap')
  150. let ctx = canvas.getContext('2d');
  151. ctx.clearRect(0, 0, canvas.width, canvas.height);
  152.  
  153. for (let i = 0; i < 365; i++) {
  154. let date = new Date(now - i * 3600 * 24 * 1000)
  155. let weekday = getRegularWeekday(date)
  156. let y = weekday
  157.  
  158. let date1 = new Date(date - getRegularWeekday(date) * 3600 * 24 * 1000)
  159. let x = weeksCount - (now1 - date1) / 1000 / 3600 / 24 / 7
  160.  
  161. let dateStr = getDateStr(date)
  162. let uuid = getUuid()
  163. let data = log[dateStr]
  164. let count = data ? sum(Object.values(data)) : 0
  165.  
  166. setBlock(x, y, count, ctx)
  167. }
  168. }
  169.  
  170. function getUuid() {
  171. let uuid = localStorage.getItem('uuid')
  172. if (!uuid) {
  173. uuid = String(Math.random()).substring(2, 4)
  174. localStorage.setItem('uuid', uuid)
  175. }
  176. return uuid
  177. }
  178.  
  179. function getHistoryDateStr(date = null) {
  180. date = new Date(date)
  181. let month = String(date.getMonth() + 1)
  182. let day = String(date.getDate())
  183. return month + "." + day
  184. }
  185.  
  186. function getDateStr(date = null) {
  187. date = date ? date : new Date()
  188. let year = String(date.getFullYear()).substring(2, 4)
  189. let month = String(date.getMonth() + 1).padStart(2, '0')
  190. let day = String(date.getDate()).padStart(2, '0')
  191. return year + month + day
  192. }
  193.  
  194. function updateLocalLog(uuid, date) {
  195. let log = localStorage.getItem('readlog')
  196. log = log ? JSON.parse(log) : {}
  197. if (!log[date]) log[date] = {}
  198. if (!log[date][uuid]) log[date][uuid] = 0
  199. log[date][uuid] += 1
  200. localStorage.setItem('readlog', JSON.stringify(log))
  201. }
  202.  
  203. function updateGist() {
  204. let gist_id = "cfa70a44bb181edbb2be19dacbcf6808"
  205. let filename = "read_log.json"
  206.  
  207. let log = localStorage.getItem('readlog')
  208. log = log ? JSON.parse(log) : {}
  209.  
  210. // pull
  211. gist_get_async(gist_id, filename).then((content) => {
  212. let remoteLog = JSON.parse(content)
  213. for (let [date, data] of Object.entries(remoteLog)) {
  214. if (!log[date]) {
  215. log[date] = data;
  216. continue
  217. }
  218. for (let [uuid, count] of Object.entries(data)) {
  219. if (uuid != getUuid()) {
  220. log[date][uuid] = count
  221. }
  222. }
  223. }
  224.  
  225. // push
  226. gist_set_async(gist_id, filename, JSON.stringify(log)).then((response) => { alert("update success") })
  227. localStorage.setItem('readlog', JSON.stringify(log))
  228.  
  229. refreshHeatmap()
  230. })
  231. }
  232.  
  233. //-------------------------------- statistics --------------------------------
  234.  
  235. // chapter_dom = document.querySelector("div.chapter-detail")
  236. // if (!chapter_dom) chapter_dom = document.body
  237. // heatmap_panel = document.createElement("div")
  238. // chapter_dom.before(heatmap_panel)
  239. // heatmap_panel.innerHTML = `<canvas id="heatmap" width="` + canvasWidth + `" height="` + canvasHeight + `"></canvas>`
  240.  
  241. // canvas = document.querySelector("canvas#heatmap")
  242. // canvas.onclick = updateGist
  243.  
  244. // refreshHeatmap()
  245.  
  246. //-------------------------------- wpm --------------------------------
  247.  
  248. // function log(){
  249. // addLine()
  250.  
  251. // uuid=getUuid()
  252. // date=getDateStr()
  253. // updateLocalLog(uuid, date)
  254.  
  255. // lastGistUpdateDate=localStorage.getItem('lastGistUpdateDate')
  256. // if (!lastGistUpdateDate || lastGistUpdateDate!=date){
  257. // updateGist()
  258. // localStorage.setItem('lastGistUpdateDate', date)
  259. // }
  260.  
  261. // refreshHeatmap()
  262. // }
  263. // document.log=log
  264.  
  265. // setInterval(()=>{document.log()},1000*60)
  266. // refreshHeatmap()
  267.  
  268. addStyle(`
  269. canvas#wpmCanvas {
  270. background-color: #333;
  271. position: fixed;
  272. bottom: 0px;
  273. right: 100px;
  274. width: 50px;
  275. height: 50px;
  276. opacity: 0.8;
  277. z-index: 3000;
  278. }
  279. `)
  280. // wpm_dom = document.createElement('div')
  281. // document.body.before(wpm_dom)
  282. // wpm_dom.innerHTML = `<canvas id="wpmCanvas" width="50px" height="50px"></canvas>`
  283. // let wpmQueue=[0]
  284.  
  285. function drawWpmCanvas(){
  286. // console.log('draw')
  287. let canvas = document.querySelector("canvas#wpmCanvas")
  288. let ctx = canvas.getContext('2d');
  289. ctx.clearRect(0, 0, canvas.width, canvas.height);
  290.  
  291. // lines
  292. ctx.beginPath();
  293. ctx.moveTo(50,50-50*Math.min(wpmQueue[wpmQueue.length-1]/500, 1));
  294. for(let i=wpmQueue.length-1;i>=0;i--){
  295. let y=50*Math.min(wpmQueue[i]/500,1);
  296. let x=50-(wpmQueue.length-1-i)*5-5;
  297. ctx.lineTo(x, 50-y);
  298. // console.log(x, y)
  299. }
  300. ctx.strokeStyle = "white";
  301. ctx.stroke();
  302.  
  303. // draw wpm number
  304. ctx.font = "16px Arial bold";
  305. ctx.fillStyle = "#9aa83a";
  306. ctx.fillText(wpmQueue[wpmQueue.length-1], 5, 21);
  307.  
  308. }
  309. // drawWpmCanvas()
  310.  
  311. // let wpmArea = document.createElement("div")
  312. // wpmArea.id="wpmArea"
  313. // wpmArea.classList.add("wpmArea")
  314. // wpmArea.style.top = "0px"
  315. // wpmArea.style.height = "0px"
  316. // document.body.before(wpmArea)
  317. let forceUpdateWpmArea=false;
  318. let lastStartTime=new Date().getTime()
  319. function updateWpmArea(){
  320. let _lineStart=parseInt(wpmArea.style.top.replace("px",""))
  321. let _lineEnd=_lineStart+parseInt(wpmArea.style.height.replace("px",""))
  322.  
  323. let lineEnd=document.documentElement["scrollTop"]+window.innerHeight*0.2
  324. let lineStart=Math.min(_lineEnd, lineEnd)-1;
  325.  
  326. // queue push
  327. if(new Date().getTime()-lastStartTime>1000*60 || forceUpdateWpmArea){
  328. forceUpdateWpmArea=false;
  329. lastStartTime=new Date().getTime()
  330. wpmArea.style.top=lineStart+"px"
  331. wpmQueue.push(wpmArea[wpmQueue.length-1])
  332. }
  333. wpmArea.style.height=(lineEnd-_lineStart)+"px"
  334.  
  335. let words_cnt=countWords(_lineStart, lineEnd)
  336. // console.log(_lineStart, lineEnd, words_cnt)
  337. wpmQueue[wpmQueue.length-1]=Math.round(words_cnt/((new Date().getTime()-lastStartTime)/1000/60))
  338. // drawWpmCanvas()
  339. }
  340. // let wpmInterval=setInterval(updateWpmArea, 1000)
  341.  
  342. // document.querySelector('canvas#wpmCanvas').onclick = function(){
  343. // forceUpdateWpmArea=true;
  344. // updateWpmArea()
  345. // updateWpmArea()
  346. // }
  347.  
  348. function countWords(lineStart, lineEnd){
  349. let items=document.querySelector('div.chapterContent')
  350. if(!items) return 0;
  351. items=items.querySelectorAll('p')
  352. let words_cnt=0;
  353. for(let item of items){
  354. let top=item.offsetTop;
  355. let text=item.innerText;
  356. let height=item.offsetHeight;
  357. let _words_cnt=text.split(" ").length;
  358. let end=top+height;
  359.  
  360. if(lineStart > end || lineEnd < top) continue;
  361.  
  362. if(lineStart<top && lineEnd>end){
  363. words_cnt+=_words_cnt;
  364. // console.log("1: "+text)
  365. }else if(lineStart<top && lineEnd>top){
  366. words_cnt+=_words_cnt*(lineEnd-top)/height;
  367. // console.log("2: "+text)
  368. }else if(lineStart<end && lineEnd>end){
  369. words_cnt+=_words_cnt*(end-lineStart)/height;
  370. // console.log("3: "+text)
  371. }else if(lineStart>top && lineEnd<end){
  372. words_cnt+=_words_cnt*(lineEnd-lineStart)/height;
  373. // console.log("4: "+text)
  374. }
  375. }
  376. return Math.round(words_cnt)
  377. }
  378. let cnt=countWords(0, 100000)
  379. console.log(cnt)
  380.  
  381. //-------------------------------- history --------------------------------
  382.  
  383. let chapter_dom = document.querySelector("div.chapter-detail")
  384. if (!chapter_dom) chapter_dom = document.body
  385. let history_panel = document.createElement("div")
  386. chapter_dom.before(history_panel)
  387. history_panel.innerHTML = `<div>
  388. <table id="history_table">
  389. </table>
  390. <button id="history_prev" class="pagiBtn">\<</button>
  391. <button id="history_next" class="pagiBtn">\></button>
  392. <input id="history_input" type="text" value="1" size="3">
  393. <button id="history_go" class="pagiBtn">go</button>
  394. </div>`
  395.  
  396. let history_data = localStorage.getItem('history_data')
  397. history_data = history_data ? JSON.parse(history_data) : {}
  398. history_data[location.href] = { date: new Date().getTime(), title: document.title.replace(" - Wikipedia", ''), url: location.href }
  399. localStorage.setItem('history_data', JSON.stringify(history_data))
  400. let history_list = Object.values(history_data).sort((a, b) => { return b.date - a.date })
  401.  
  402. function setHistoryTable(page) {
  403. page = page ? page : 1
  404. let history_pageSize = 10
  405. let history_table = document.querySelector("#history_table")
  406. history_table.innerHTML = ""
  407.  
  408. let start = (page - 1) * history_pageSize
  409. let end = Math.min(start + history_pageSize, history_list.length)
  410.  
  411. for (let i = start; i < end; i++) {
  412. let day = new Date(history_list[i].date).getDate()
  413. history_table.innerHTML += `<tr>
  414. <td class="historyDay`+ day + `">` + getHistoryDateStr(history_list[i].date) + `</td>
  415. <td><a href="`+ history_list[i].url + `">` + history_list[i].title + `</a></td>
  416. </tr>`
  417. }
  418. }
  419. setHistoryTable(1)
  420.  
  421. document.querySelector('#history_prev').onclick = () => {
  422. let history_page = document.querySelector('#history_input').value;
  423. setHistoryTable(--history_page);
  424. document.querySelector('#history_input').value = history_page
  425. }
  426. document.querySelector('#history_next').onclick = () => {
  427. let history_page = document.querySelector('#history_input').value;
  428. setHistoryTable(++history_page);
  429. document.querySelector('#history_input').value = history_page
  430. }
  431.  
  432. document.querySelector('#history_go').onclick = () => {
  433. let history_page = document.querySelector('#history_input').value;
  434. setHistoryTable(history_page);
  435. }
  436.  
  437. })();

QingJ © 2025

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