// ==UserScript==
// @name 知乎详细等级
// @namespace http://tampermonkey.net/
// @version 0.4.6
// @license MPL-2.0
// @description 精确显示知乎等级(精确到小数点后两位)
// @author C4r
// @match https://www.zhihu.com/*
// @grant none
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/min/moment.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/Chart.min.js
// ==/UserScript==
(function () {
'use strict';
let storageName = 'C4rZhihuLevel'
function httpGetAsync(theUrl, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
callback(xmlHttp.responseText);
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
function dataToChartData(data) {
// console.log('zhihu : ', data)
let timeSort = Object.keys(data).sort()
let xyArray = []
for (let time of timeSort) {
// xyArray.push({ x: Math.round((time - minTime) / timeStep), y: (data[time]) })
xyArray.push({ x: (new Date(parseInt(time))), y: (data[time]) })
}
return {
'xyArray': xyArray
}
}
function getLevelRequest() {
return new Promise((resolve, reject) => {
httpGetAsync('https://www.zhihu.com/creator/account/growth-level', (responseText) => {
console.log('get response')
// console.log(responseText)
let levelInfoStartIndex = responseText.indexOf('"account":{"growthLevel"')
if (levelInfoStartIndex >= 0) {
let levelInfoEndIndex = responseText.indexOf('}}', levelInfoStartIndex) + 2
let levelInfoStr = '{' + responseText.slice(levelInfoStartIndex, levelInfoEndIndex) + '}'
let levelInfo = JSON.parse(levelInfoStr)
let strLevelPercent = String(parseFloat(levelInfo.account.growthLevel.ratio)/100.)
strLevelPercent = strLevelPercent.slice(strLevelPercent.indexOf('.')+1)
let cLevel = parseFloat(levelInfo.account.growthLevel.level + '.' + strLevelPercent)
resolve(cLevel)
} else {
console.error('zhihu level detail not gotten')
reject('zhihu level detail not gotten')
}
})
})
}
/**
* return level by reading page
*/
function getLevelHTML() {
if ($('.CreatorHomeLevelInfo-levelTitle').length == 0) {
return undefined
} else {
if(($('.CreatorHomeLevelBar-progress').attr('style')).includes('calc' )){
let strRaw = $('.CreatorHomeLevelBar-progress').attr('style').slice(("width:").length).trim()
let strLevelPercent = String(parseFloat(strRaw.slice(strRaw.indexOf('(')+1,strRaw.indexOf('%') ).trim())/100.)
strLevelPercent = strLevelPercent.slice(strLevelPercent.indexOf('.')+1)
// let str = $('.CreatorHomeLevelInfo-levelTitle').text()
$('.CreatorHomeLevelInfo-levelTitleHint').before('.' + strLevelPercent)
// console.log('zhihu level')
let strLevel = $('img.CreatorHomeLevelInfo-LevelImage').attr('alt').split(' ')[1] + '.' + strLevelPercent
let cLevel = parseFloat(strLevel)
return cLevel
}else{
let strLevelPercent = $('.CreatorHomeLevelBar-progress').attr('style').slice(("width:").length, -2).trim();
// let str = $('.CreatorHomeLevelInfo-levelTitle').text()
$('.CreatorHomeLevelInfo-levelTitleHint').before('.' + strLevelPercent)
// console.log('zhihu level')
let strLevel = $('img.CreatorHomeLevelInfo-LevelImage').attr('alt').split(' ')[1] + '.' + strLevelPercent
let cLevel = parseFloat(strLevel)
return cLevel
}
}
}
function updateData(cLevel) {
let dataStr = localStorage.getItem(storageName)
// console.log('zhihu level dataStr ', dataStr)
let cDate = new Date()
let data = {}
if (dataStr !== null && dataStr !== undefined && dataStr.trim() !== '') {
data = JSON.parse(dataStr)
// console.log('zhihu level data ', data)
let timeSort = Object.keys(data).sort()
let lastTime = timeSort[timeSort.length - 1]
let lastLevel = data[lastTime]
// console.log('zhihu level timeSort ', timeSort)
// console.log('zhihu level lastTime ', lastTime)
// console.log('zhihu level lastLevel ', lastLevel)
if (lastLevel == cLevel) {
// level not changed
// console.log('c4r zhihu level not change ', lastLevel, ' ', cLevel)
data[cDate.getTime()] = cLevel
} else if (lastLevel > cLevel) {
// level drop
// console.error('c4r zhihu level drop from ', lastLevel, ' to ', cLevel)
data[cDate.getTime()] = cLevel
localStorage.setItem(storageName, JSON.stringify(data))
} else {
// level rise
// console.log('c4r zhihu level rise from ', lastLevel, ' to ', cLevel)
data[cDate.getTime()] = cLevel
localStorage.setItem(storageName, JSON.stringify(data))
// $('.CreatorHomeLevelInfo-levelTitleHint').before(' (上次更新 : '+ lastLevel + ' ' + (new Date()).toLocaleDateString()+ ')')
}
} else {
// initial log file
data[cDate.getTime()] = cLevel
localStorage.setItem(storageName, JSON.stringify(data))
}
return data
}
function getData() {
let dataStr = localStorage.getItem(storageName)
let data = {}
if (dataStr !== null && dataStr !== undefined && dataStr.trim() !== '') {
data = JSON.parse(dataStr)
}
return data
}
function isLevelDetailShown() {
return $('.CreatorHomeLevelInfo-levelTitle[levelDetail]').length > 0
}
function addLevelDetailTag() {
// add tag
$('.CreatorHomeLevelInfo-levelTitle').attr('levelDetail', '')
}
/**
* 画图
* @param {*} ctx element in Page
* @param {*} data data
* @returns myChart
*/
function plotLevelDetail(ctx, data) {
let chartData = dataToChartData(data)
// console.log('c4r zhihu level chartData :', chartData)
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Points',
data: chartData['xyArray'],
backgroundColor: 'rgba(123, 83, 252, 0.8)',
borderColor: 'rgba(33, 232, 234, 1)',
borderWidth: 1,
fill: false,
showLine: true,
}],
},
options: {
title: {
display: true,
text: '等级历史',
},
legend: {
display: false
},
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'week',
displayFormats: {
week: 'YY.M.D'
}
}
}]
}
}
});
return myChart
}
function isUrlCreator() {
return window.location.href.includes('www.zhihu.com/creator')
}
function isCreatorHomePage() {
return !($('.CreatorHomeLevelInfo-levelTitle').length == 0)
}
function isUrlHome() {
let href = window.location.href
if (href == 'https://www.zhihu.com/'
|| href == 'https://www.zhihu.com/question/waiting'
|| href == 'https://www.zhihu.com/explore'
|| href == 'https://www.zhihu.com/follow'
|| href == 'https://www.zhihu.com/hot') {
return true
} else {
return false
}
}
function showLevel() {
if (isLevelDetailShown() || !isCreatorHomePage()) {
return
}
let cLevel = getLevelHTML()
if (cLevel == undefined) return
addLevelDetailTag()
let data = updateData(cLevel)
$('<canvas id="levelDetailChart"></canvas>').insertAfter('.CreatorHomeLevelInfo')
let ctx = document.getElementById("levelDetailChart");
plotLevelDetail(ctx, data)
}
function callbackLevel() {
// console.log('found Level bar')
showLevel()
}
// ===============================================
if (isUrlCreator) {
showLevel()
$(document).ready(() => {
let observerLevel = new MutationObserver(callbackLevel)
observerLevel.observe($('body').get(0),
{
subtree: true, childList: true, characterData: false, attributes: true,
attributeFilter: ['.CreatorHomeLevelInfo-levelTitle:not([levelDetail]'],
attributeOldValue: false, characterDataOldValue: false
})
// debug
// let debugData = {
// 1580511600000: 3.00,
// 1577833200000: 2.10,
// 1585692000000: 3.24
// }
// localStorage.setItem(storageName, JSON.stringify(debugData))
showLevel()
})
}
if (isUrlHome()) {
// console.log('is Home page')
getLevelRequest().then((cLevel) => {
let data = updateData(cLevel)
// console.log('cLevel : ', cLevel)
$(document).ready(() => {
if($('.CreatorEntrance').length > 0){
if ($('.CreatorEntrance-indexPageTitle span').length > 0) {
$('.CreatorEntrance-indexPageTitle span').text('Lv ' + cLevel)
$('<canvas id="levelDetailChart"></canvas>').insertAfter('.ProfileSideCreator-analytics')
let ctx = document.getElementById("levelDetailChart");
plotLevelDetail(ctx, data)
}
}else{
console.log('zhihuLevel : not found Entrance, add MutationObserver')
let observerEntrance = new MutationObserver((mutationList, observer)=>{
if($('.CreatorEntrance').length > 0){
console.log('zhihuLevel : Entrance appears, disconnect MutationObserver')
if ($('.CreatorEntrance-indexPageTitle span').length > 0) {
$('.CreatorEntrance-indexPageTitle span').text('Lv ' + cLevel)
$('<canvas id="levelDetailChart"></canvas>').insertAfter('.ProfileSideCreator-analytics')
let ctx = document.getElementById("levelDetailChart");
plotLevelDetail(ctx, data)
}
observer.disconnect()
}
})
observerEntrance.observe($('body').get(0),
{
subtree: true, childList: true, characterData: false, attributes: true,
attributeFilter: ['.CreatorEntrance'],
attributeOldValue: false, characterDataOldValue: false
})
}
})
})
}
})();