// ==UserScript==
// @name read_log
// @include *wikipedia*
// @supportURL https://github.com/sxlgkxk/browser_script/issues
// @version 0.3
// @description read log
// @namespace http://sxlgkxk.github.io/
// @author sxlgkxk
// @icon http://sxlgkxk.github.io/im/avatar.jpg
// @license MIT
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function () {
let blockWidth = 10
let blockPadding = 3
// weeksCount=53
let weeksCount = 30 // 393, 94
let canvasWidth = (blockWidth + blockPadding) * weeksCount + blockPadding
let canvasHeight = (blockWidth + blockPadding) * 7 + blockPadding
//-------------------------------- common functions --------------------------------
function addScript(src) {
let scripts_dom = document.createElement('script');
scripts_dom.src = src;
scripts_dom.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(scripts_dom);
}
addScript('https://unpkg.com/axios/dist/axios.min.js')
function getScrollPercent() {
let h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
return ((h[st] || b[st]) / ((h[sh] || b[sh])) * 100).toFixed(2);
}
function getGistToken() {
let gist_token = localStorage.getItem('gist_token')
if (gist_token != null && gist_token.length > 10) {
gist_token = prompt('gist_token?')
if (gist_token) {
localStorage.setItem('gist_token', gist_token)
}
}
return gist_token
}
async function gist_get_async(gist_id, filename) {
// gist_get_async("cfa70a44bb181edbb2be19dacbcf6808", "read_log.json").then((content)=>{console.log(content)})
let response = await axios.get("https://api.github.com/gists/" + gist_id)
let data = response.data
let content = data.files[filename].content
return content
}
async function gist_set_async(gist_id, filename, content) {
// gist_set_async("cfa70a44bb181edbb2be19dacbcf6808", "read_log.json", "[]").then((content)=>{console.log(content)})
let files = {}
files[filename] = { "content": content }
let gist_token = getGistToken()
return await axios.patch("https://api.github.com/gists/" + gist_id, { files: files }, { headers: { Authorization: "token " + gist_token } })
}
function addStyle(html) {
let style = document.createElement("div")
document.body.before(style)
style.innerHTML = `<style>` + html + `</style>`
}
//-------------------------------- code snippets --------------------------------
addStyle(`
#wpmArea{
position: absolute;
right: 10px;
left:10px;
background-color: rgba(255, 255, 255, 0.2);
}
#heatmap{
background-color: #0d1117;
margin: 0 auto;
display:block;
}
table#history_table {
border-collapse: collapse;
width: 100%;
}
#history_table th, #history_table td {
text-align: left;
padding: 8px;
}
button.pagiBtn{
background-color: #333;
color: #fff;
margin: 4px;
padding-left: 15px;
padding-right: 15px;
padding-top: 9px;
padding-bottom: 9px;
}
`)
for (let i = 1; i <= 31; i++) {
addStyle(`td.historyDay` + i + `{background-color: rgba(` + Math.floor(Math.random() * 255) + `,` + Math.floor(Math.random() * 255) + `,` + Math.floor(Math.random() * 255) + `, 0.5);}`)
}
function getColor(count) {
let colors = [
"#161b22",
"#cef1dd",
"#9ee3bb",
"#6dd499",
"#3cc677",
"#33a865",
"#2a8b53",
"#216d41"
]
let max_count = 60 * 8
return colors[Math.ceil(Math.min(count / max_count, 1) * (colors.length - 1))]
}
function setBlock(x, y, count, ctx) {
ctx.fillStyle = getColor(count);
ctx.fillRect(x * (blockWidth + blockPadding) - blockWidth, y * (blockWidth + blockPadding) - blockWidth, blockWidth, blockWidth);
}
function getRegularWeekday(date) {
let weekday = date.getDay()
return weekday ? weekday : 7
}
function sum(array) {
return array.reduce((partialSum, a) => partialSum + a, 0)
}
function refreshHeatmap() {
let log = localStorage.getItem('readlog')
log = log ? JSON.parse(log) : {}
let now = new Date();
let now1 = new Date(now - getRegularWeekday(now) * 3600 * 24 * 1000)
let canvas = document.querySelector('canvas#heatmap')
let ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < 365; i++) {
let date = new Date(now - i * 3600 * 24 * 1000)
let weekday = getRegularWeekday(date)
let y = weekday
let date1 = new Date(date - getRegularWeekday(date) * 3600 * 24 * 1000)
let x = weeksCount - (now1 - date1) / 1000 / 3600 / 24 / 7
let dateStr = getDateStr(date)
let uuid = getUuid()
let data = log[dateStr]
let count = data ? sum(Object.values(data)) : 0
setBlock(x, y, count, ctx)
}
}
function getUuid() {
let uuid = localStorage.getItem('uuid')
if (!uuid) {
uuid = String(Math.random()).substring(2, 4)
localStorage.setItem('uuid', uuid)
}
return uuid
}
function getHistoryDateStr(date = null) {
date = new Date(date)
let month = String(date.getMonth() + 1)
let day = String(date.getDate())
return month + "." + day
}
function getDateStr(date = null) {
date = date ? date : new Date()
let year = String(date.getFullYear()).substring(2, 4)
let month = String(date.getMonth() + 1).padStart(2, '0')
let day = String(date.getDate()).padStart(2, '0')
return year + month + day
}
function updateLocalLog(uuid, date) {
let log = localStorage.getItem('readlog')
log = log ? JSON.parse(log) : {}
if (!log[date]) log[date] = {}
if (!log[date][uuid]) log[date][uuid] = 0
log[date][uuid] += 1
localStorage.setItem('readlog', JSON.stringify(log))
}
function updateGist() {
let gist_id = "cfa70a44bb181edbb2be19dacbcf6808"
let filename = "read_log.json"
let log = localStorage.getItem('readlog')
log = log ? JSON.parse(log) : {}
// pull
gist_get_async(gist_id, filename).then((content) => {
let remoteLog = JSON.parse(content)
for (let [date, data] of Object.entries(remoteLog)) {
if (!log[date]) {
log[date] = data;
continue
}
for (let [uuid, count] of Object.entries(data)) {
if (uuid != getUuid()) {
log[date][uuid] = count
}
}
}
// push
gist_set_async(gist_id, filename, JSON.stringify(log)).then((response) => { alert("update success") })
localStorage.setItem('readlog', JSON.stringify(log))
refreshHeatmap()
})
}
//-------------------------------- statistics --------------------------------
// chapter_dom = document.querySelector("div.chapter-detail")
// if (!chapter_dom) chapter_dom = document.body
// heatmap_panel = document.createElement("div")
// chapter_dom.before(heatmap_panel)
// heatmap_panel.innerHTML = `<canvas id="heatmap" width="` + canvasWidth + `" height="` + canvasHeight + `"></canvas>`
// canvas = document.querySelector("canvas#heatmap")
// canvas.onclick = updateGist
// refreshHeatmap()
//-------------------------------- wpm --------------------------------
// function log(){
// addLine()
// uuid=getUuid()
// date=getDateStr()
// updateLocalLog(uuid, date)
// lastGistUpdateDate=localStorage.getItem('lastGistUpdateDate')
// if (!lastGistUpdateDate || lastGistUpdateDate!=date){
// updateGist()
// localStorage.setItem('lastGistUpdateDate', date)
// }
// refreshHeatmap()
// }
// document.log=log
// setInterval(()=>{document.log()},1000*60)
// refreshHeatmap()
addStyle(`
canvas#wpmCanvas {
background-color: #333;
position: fixed;
bottom: 0px;
right: 100px;
width: 50px;
height: 50px;
opacity: 0.8;
z-index: 3000;
}
`)
// wpm_dom = document.createElement('div')
// document.body.before(wpm_dom)
// wpm_dom.innerHTML = `<canvas id="wpmCanvas" width="50px" height="50px"></canvas>`
// let wpmQueue=[0]
function drawWpmCanvas(){
// console.log('draw')
let canvas = document.querySelector("canvas#wpmCanvas")
let ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
// lines
ctx.beginPath();
ctx.moveTo(50,50-50*Math.min(wpmQueue[wpmQueue.length-1]/500, 1));
for(let i=wpmQueue.length-1;i>=0;i--){
let y=50*Math.min(wpmQueue[i]/500,1);
let x=50-(wpmQueue.length-1-i)*5-5;
ctx.lineTo(x, 50-y);
// console.log(x, y)
}
ctx.strokeStyle = "white";
ctx.stroke();
// draw wpm number
ctx.font = "16px Arial bold";
ctx.fillStyle = "#9aa83a";
ctx.fillText(wpmQueue[wpmQueue.length-1], 5, 21);
}
// drawWpmCanvas()
// let wpmArea = document.createElement("div")
// wpmArea.id="wpmArea"
// wpmArea.classList.add("wpmArea")
// wpmArea.style.top = "0px"
// wpmArea.style.height = "0px"
// document.body.before(wpmArea)
let forceUpdateWpmArea=false;
let lastStartTime=new Date().getTime()
function updateWpmArea(){
let _lineStart=parseInt(wpmArea.style.top.replace("px",""))
let _lineEnd=_lineStart+parseInt(wpmArea.style.height.replace("px",""))
let lineEnd=document.documentElement["scrollTop"]+window.innerHeight*0.2
let lineStart=Math.min(_lineEnd, lineEnd)-1;
// queue push
if(new Date().getTime()-lastStartTime>1000*60 || forceUpdateWpmArea){
forceUpdateWpmArea=false;
lastStartTime=new Date().getTime()
wpmArea.style.top=lineStart+"px"
wpmQueue.push(wpmArea[wpmQueue.length-1])
}
wpmArea.style.height=(lineEnd-_lineStart)+"px"
let words_cnt=countWords(_lineStart, lineEnd)
// console.log(_lineStart, lineEnd, words_cnt)
wpmQueue[wpmQueue.length-1]=Math.round(words_cnt/((new Date().getTime()-lastStartTime)/1000/60))
// drawWpmCanvas()
}
// let wpmInterval=setInterval(updateWpmArea, 1000)
// document.querySelector('canvas#wpmCanvas').onclick = function(){
// forceUpdateWpmArea=true;
// updateWpmArea()
// updateWpmArea()
// }
function countWords(lineStart, lineEnd){
let items=document.querySelector('div.chapterContent')
if(!items) return 0;
items=items.querySelectorAll('p')
let words_cnt=0;
for(let item of items){
let top=item.offsetTop;
let text=item.innerText;
let height=item.offsetHeight;
let _words_cnt=text.split(" ").length;
let end=top+height;
if(lineStart > end || lineEnd < top) continue;
if(lineStart<top && lineEnd>end){
words_cnt+=_words_cnt;
// console.log("1: "+text)
}else if(lineStart<top && lineEnd>top){
words_cnt+=_words_cnt*(lineEnd-top)/height;
// console.log("2: "+text)
}else if(lineStart<end && lineEnd>end){
words_cnt+=_words_cnt*(end-lineStart)/height;
// console.log("3: "+text)
}else if(lineStart>top && lineEnd<end){
words_cnt+=_words_cnt*(lineEnd-lineStart)/height;
// console.log("4: "+text)
}
}
return Math.round(words_cnt)
}
let cnt=countWords(0, 100000)
console.log(cnt)
//-------------------------------- history --------------------------------
let chapter_dom = document.querySelector("div.chapter-detail")
if (!chapter_dom) chapter_dom = document.body
let history_panel = document.createElement("div")
chapter_dom.before(history_panel)
history_panel.innerHTML = `<div>
<table id="history_table">
</table>
<button id="history_prev" class="pagiBtn">\<</button>
<button id="history_next" class="pagiBtn">\></button>
<input id="history_input" type="text" value="1" size="3">
<button id="history_go" class="pagiBtn">go</button>
</div>`
let history_data = localStorage.getItem('history_data')
history_data = history_data ? JSON.parse(history_data) : {}
history_data[location.href] = { date: new Date().getTime(), title: document.title.replace(" - Wikipedia", ''), url: location.href }
localStorage.setItem('history_data', JSON.stringify(history_data))
let history_list = Object.values(history_data).sort((a, b) => { return b.date - a.date })
function setHistoryTable(page) {
page = page ? page : 1
let history_pageSize = 10
let history_table = document.querySelector("#history_table")
history_table.innerHTML = ""
let start = (page - 1) * history_pageSize
let end = Math.min(start + history_pageSize, history_list.length)
for (let i = start; i < end; i++) {
let day = new Date(history_list[i].date).getDate()
history_table.innerHTML += `<tr>
<td class="historyDay`+ day + `">` + getHistoryDateStr(history_list[i].date) + `</td>
<td><a href="`+ history_list[i].url + `">` + history_list[i].title + `</a></td>
</tr>`
}
}
setHistoryTable(1)
document.querySelector('#history_prev').onclick = () => {
let history_page = document.querySelector('#history_input').value;
setHistoryTable(--history_page);
document.querySelector('#history_input').value = history_page
}
document.querySelector('#history_next').onclick = () => {
let history_page = document.querySelector('#history_input').value;
setHistoryTable(++history_page);
document.querySelector('#history_input').value = history_page
}
document.querySelector('#history_go').onclick = () => {
let history_page = document.querySelector('#history_input').value;
setHistoryTable(history_page);
}
})();