// ==UserScript==
// @name 记住阅读进度
// @namespace http://tampermonkey.net/
// @version 4.3.2
// @description 记住页面阅读进度,即使对于单页面,也能很好的工作!
// @match *://*/*
// @exclude http://127.0.0.1*
// @exclude http://localhost*
// @exclude http://192.168.*
// @author zhuangjie
// @icon data:image/png;base64,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
// @license MIT
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_registerMenuCommand
// ==/UserScript==
(function() {
'use strict';
// Your code here...
// 编写脚本学习教程:http://www.ttlsa.com/docs/greasemonkey/#pattern.addcss
// 解决广工商学校选课网无法显示左边栏:原因是脚本中加了 window.onload 有关
// 上一个重要版本:https://cdn.jsdelivr.net/gh/18476305640/typora@master/images/2022/10/21/recoverHistorySchedule.js
// https://cdn.jsdelivr.net/gh/18476305640/typora@master/images/2022/10/26/f.txt
// 初始化事件容器-页面活跃与否事件(节省性能而使用)
(function () {
window.onblur = function () {
window.events.onblur.trigger();
}
window.onfocus = function () {
window.events.onfocus.trigger();
}
window.events = {
onblur: {
monitors: [],
add(fun) {
this.monitors.push(fun)
},
trigger() {
for(let i = 0; i < this.monitors.length; i++) {
this.monitors[i]();
}
}
},
onfocus: {
monitors: [],
add(fun) {
this.monitors.push(fun)
},
trigger() {
for(let i = 0; i < this.monitors.length; i++) {
this.monitors[i]();
}
}
}
}
})();
// 【url改变监听器】
function onUrlChange(fun) {
let initUrl = window.location.href.split("#")[0];
function urlChange() {
let currentUrl = window.location.href.split("#")[0];
if(initUrl != currentUrl) {
// 新的=>旧的
initUrl = currentUrl;
fun();
initUrl = currentUrl;
}
}
let si = setInterval(urlChange,460)
window.onblur = function() {
clearInterval(si);
}
window.onfocus = function() {
si = setInterval(urlChange,460)
}
}
// 全局url事件
window.urlChangeListener = {
events:[],
add(event) {
this.events.push(event)
},
trigger (){
for(let event of this.events) {
event()
}
}
}
onUrlChange(function(){window.urlChangeListener.trigger()})
// 防抖函数
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout != null ) clearTimeout(timeout);
timeout= setTimeout (fn, wait);
}
}
// 节流函数
const throttle = (fn, Intervals, ...args) => {
let timeNo;
return (...params) => {
if(timeNo) return;
timeNo = setTimeout(() => {
fn(...args,...params)
clearTimeout(timeNo);
timeNo = null;
}, Intervals);
}
}
// 多iframe 屏障
function iframeParclose(name) {
if(name == null) {
name = Date.now();
}
return {
set(timeout = 2000) {
const value = cache.get(name);
if(value && parseInt(value) > Date.now()) return false;
cache.set(name,Date.now() + timeout);
return true;
},
remove() {
cache.remove(name);
}
}
}
async function iframeParcloseWrapper(name,fun) {
const parclose = iframeParclose(name);
const timeout = 1200;
if(!parclose.set(timeout)) return;
try {
await fun();
}finally {
setTimeout(()=>parclose.remove(),timeout)
}
}
// 数据缓存器
let cache = {
get(key) {
return GM_getValue(key);
},
set(key,value) {
GM_setValue(key,value);
},
remove(key) {
GM_deleteValue(key)
}
}
let rpcCache; rpcCache = getRPC();
function setRPC(config) {
if(config == null) return;
cache.set("ReadingProgressConfig",rpcCache = config)
}
function getRPC() {
let result = cache.get("ReadingProgressConfig")
if(result == null) {
// 默认数据
result = {
// URL规则(满足应用)
ruleList: [
"**",
"https://www.cnblogs.com/**/p/**",
"https://blog.csdn.net/**"
],
heightThreshold: 2000, // 高度阈值
isShowSchedule: true // 是否显示进度控制
}
// 保存初始配置
setRPC(result);
}
return result;
}
GM_registerMenuCommand("开/关进度显示",function() {
iframeParcloseWrapper("ProgressDisplayStatusChange",function() {
return new Promise((resolve,reject)=>{
const rpc = getRPC();
rpc.isShowSchedule = !rpc.isShowSchedule;
setRPC(rpc)
alert(`┌(`▽′)╭ 进度显示已${rpc.isShowSchedule?"开启":"关闭"}`)
resolve();
})
})
});
GM_registerMenuCommand("配置规则",function() {
showConfigView();
});
// 显示配置规则视图
function showConfigView() {
// 后面可能会修改配置,刷新配置缓存,防止其它页面的修改导致当前页面的数据不一致
rpcCache = getRPC();
// 显示视图
var configViewContainer = document.createElement("div");
configViewContainer.style=`
width:300px; background:pink;
position: fixed;right: 0px; top: 0px;
z-index:10000;
padding: 20px;
border-radius: 14px;
`
configViewContainer.innerHTML = `
<p id="rpc_close">X</p>
<p class="rpc_config_title">URL规则:</p>
<textarea id="rpc_urlTextarea" ></textarea>
<p class="rpc_config_title">高度阈值:</p>
<input id="rpc_heightInput" />
<div id="rpc_controller">
<button id="rpc_save" >保存</button>
<span id="rpc_tis" title="规则与高度阈值都满足脚本才会生效!">一┗|`O′|┛ 说明 ~ </span>
</div>
`;
// 设置样式
document.body.appendChild(configViewContainer)
document.getElementById("rpc_close").style="color: red;font-weight: bold;font-size: 14px;cursor: pointer; position: absolute;right: 10px; top: 10px;margin: 0;";
Array.from(document.getElementsByClassName("rpc_config_title")).forEach(item => {
item.style = "font-size:14px;margin:7px 0 5px;color: black;";
});
document.getElementById("rpc_urlTextarea").style="width:100%;height:150px;border: 4px solid rgb(245, 245, 245);box-sizing: border-box;";
document.getElementById("rpc_heightInput").style="width:100%;border: 2px solid rgb(245, 245, 245);box-sizing: border-box;";
document.getElementById("rpc_controller").style="width:100%; margin-top:20px;";
document.getElementById("rpc_save").style="width:30%; border:none;border-radius:3px;padding:3px;";
document.getElementById("rpc_tis").style="color:#f5f5f5;display:block;text-align: center;float: right;cursor: pointer;";
// 回显
document.getElementById("rpc_urlTextarea").value = rpcCache.ruleList.join("\n")
document.getElementById("rpc_heightInput").value = rpcCache.heightThreshold
// 保存
document.getElementById("rpc_save").onclick=function() {
// 保存到对象
rpcCache.ruleList = document.getElementById("rpc_urlTextarea").value.split("\n")
rpcCache.heightThreshold = document.getElementById("rpc_heightInput").value
// 持久化
setRPC(rpcCache)
// 清除视图
configViewContainer.remove();
alert("保存配置成功!")
}
// 关闭
document.getElementById("rpc_close").onclick = ()=> configViewContainer.remove();
}
// 检查量下满足开启脚本条件
function checkIsSatisfyEnableCondition() {
let heightThreshold = rpcCache.heightThreshold;
let ruleList = rpcCache.ruleList;
// 判断高度是否满足
let isSatisfyHeight = getDocumentHeight() >= heightThreshold;
// 判断是否满足规则
let isSatisfyURL = seeSatisfyURL();
return isSatisfyHeight && isSatisfyURL;
}
// 看下是否满足开启脚本条件-根据URL规则
function seeSatisfyURL () {
let currentUrl = window.location.href;
let ruleList = rpcCache.ruleList;
// 当规则为空时,直接返回false
if(ruleList == null || ruleList.length == 0) return false;
for(let rule of ruleList) {
rule = rule.trim();
if(rule.indexOf("**") < 0 ) {
if(currentUrl== rule) return true;
continue;
}
// 满足泛匹配
let isOk = (function(){
let ruleChilds = rule.split("**")
if(ruleChilds == null || ruleChilds.length == 0) return false;
for(let block of ruleChilds) {
if(currentUrl.indexOf(block) < 0) {
// 表示当前测试的这个规则不满足
return false;
}
}
return true;
})();
if(isOk) return true;
}
// 当规则不为空时,且不通过上面的匹配时,返回false
return false;
}
// 【何时开始脚本】
// 获取滚动历史高度
let item_content = localStorage.getItem(getCurrentUrl())
let history_high = item_content == null?0:parseFloat(item_content);
// 是否已经初始化
let isInit = false;
// 初始化程序
do {
if(history_high <= getDocumentHeight() || document.readyState == "complete") {
setTimeout(()=>{init()},50)
break;
}
}while(history_high <= getDocumentHeight() || document.readyState == "complete");
// 【主程序】
function init() {
// 判断当前页面是否满足开启阅读进度
if(!checkIsSatisfyEnableCondition() && !isInit) {
// 当页面不满足初始化时,添加再次初始化器,当页面url改变时,会再次尝试
window.urlChangeListener.add(function() {
setTimeout(()=>{init() },1500)
})
return;
};
// 标记为已初始化
isInit = true;
// 初始化还原器
recoverMonitor();
// 初始化记录
initRecorder();
// 初始化视图(显示高度)
initView();
}
//【函数库】
//有动画地滚动, 这里不用,因为要直接恢复,而不浪费滚动的时间
let st = null; //保证多次执行 ScrollTo 函数不会相互影响
function ScrollTo(scroll, top) {
if(st != null ) {
//关闭上一次未执行完成的滚动
clearInterval(st);
}
//每次移动的跨度
let span = 5;
// 最长滚动时间
let timeout = false;
let timeout_time = 5000;
let timer = setTimeout(()=>{timeout=true},timeout_time);
st = setInterval(function () {
let currentTop = getCurrentTop();
//当在跨度内时,直接到达, 如果不在指定时间内滚动到,那将直接到达
if ((currentTop >= top - span && currentTop <= top + span) || timeout ) {
clearTimeout(timer);
timeout = false;
setTop(top);
// $(scroll).scrollTop(top);
//让st为null,让关闭定时器
let tmp_st = st;
st = null;
//关闭定时器(下一次不会再执行,但本次还会执行下去),再return;
clearInterval(tmp_st);
// console.log("滚动完成",top+"<is>"+ getCurrentTop() )
return;
}
//如果不在跨度内时,根据当前的位置与目的位置进行上下移动指定跨度
if (currentTop < top) {
setTop(currentTop + span)
} else {
setTop(currentTop - span)
}
span++
}, 20)
}
// 获取url,url经过了处理
function getCurrentUrl() {
return window.location.href.split("#")[0]
}
// 获取存储标记,用于存储滚动“责任人”
function getCurrentPageWhoRoll() {
return getCurrentUrl()+"<and>WhoRoll";
}
// 获取当前滚动的高度
function getCurrentTop() {
return document.documentElement.scrollTop || document.body.scrollTop;
}
// 获取文档高度
function getDocumentHeight() {
// 获取最大高度
return (document.documentElement.scrollHeight > document.body.scrollHeight?document.documentElement.scrollHeight:document.body.scrollHeight)
}
// 到达指定高度
function setTop(h,isCheck = true) {
let whoRoll = localStorage.getItem(getCurrentPageWhoRoll())
if(!isCheck) {
document.documentElement.scrollTop = h;
document.body.scrollTop = h;
return;
}
if(whoRoll == "document") {
if(getDocumentHeight() >= h ) {
document.documentElement.scrollTop = h;
}
}else {
if(getDocumentHeight() >= h ) {
document.body.scrollTop = h;
}
}
}
// 判断是否在滚动
function onNotScrolling(callback,ScrollingCallback) {
// 如果不在滚动调用回调
let h1 = parseInt(getCurrentTop());
setTimeout(function() {
let h2 = parseInt(getCurrentTop());
if(h1 == h2) {
callback();
}else {
ScrollingCallback();
}
},50)
}
// 检查是否到达指定位置
function checkIsArriveHeight(time,expectHeight = 0,callback,flag = true) {
setTimeout(function() {
let top_down_scope = 200/2;
let currentHiehgt = getCurrentTop();
if(!(expectHeight >= currentHiehgt-top_down_scope && expectHeight <= currentHiehgt+top_down_scope)) {
// 不管当前是否在滚动,都进行失败回调
callback();
}else {
// 只有到达了且不在滚动了才算成功,否则调用失败回调
onNotScrolling(function() {
},callback)
}
},time)
return null;
}
//【初始化视图显示】
function initView () {
let scheduleBox=document.createElement("div");
scheduleBox.innerText = "helloworld";
scheduleBox.id = "schedule_box";
scheduleBox.style.cssText=`
display:none;
height: 35px;
line-height: 35px;
font-size: 15px;
position: fixed;
right: 20px;
top: 20px;
z-index: 10000;
padding: 0px 10px;
background: #333333;
color: #fff;
overflow: hidden;
`;
// 进度显示防抖关闭函数
function showScheduleDebounce(fn, wait) {
let timer = null;
return function() {
window.showSchedule();
// 关闭定时器的
if(timer != null ) clearTimeout(timer);
timer= setTimeout (fn,wait);
}
}
// 隐藏容器-处理函数
function hideSchedule() {
// 关闭容器
scheduleBox.style.display="none";
}
window.addEventListener('scroll',showScheduleDebounce(hideSchedule,460));
document.body.appendChild(scheduleBox);
// 显示容器
window.showSchedule = function(content) {
// 是否要显示/隐藏
scheduleBox.style.display= rpcCache.isShowSchedule?"block":"none";
if(!rpcCache.isShowSchedule) return;
// 将 当前进度/ 总进度 放在显示容器中
scheduleBox.innerHTML = content || parseInt(getCurrentTop())+" / "+parseInt(getDocumentHeight() - window.innerHeight);
let scheduleChild=document.createElement("div");
scheduleChild.style.cssText=`
height: 100%;
background: rgba(26, 173, 25,0.5);
position: absolute;
top: 0px;
left: 0px;
`;
scheduleBox.appendChild(scheduleChild);
scheduleChild.style.width = scheduleBox.clientWidth*((getCurrentTop())/parseInt(getDocumentHeight() - window.innerHeight))+"px";
// 防抖关闭显示视图容器 -- 在上面的闭包中的监听了滚动
}
}
// 【初始化记录器】
function initRecorder() {
// 位置保存
// 处理函数
function handle() {
// console.log(document.documentElement.scrollTop , document.body.scrollTop )
let current_top = getCurrentTop();
let current_url = getCurrentUrl()
if(document.documentElement.scrollTop > document.body.scrollTop ) {
localStorage.setItem(getCurrentPageWhoRoll(),"document")
}else {
localStorage.setItem(getCurrentPageWhoRoll(),"body")
}
if(current_top <= 10) return;
// console.log("[记住历史进度]拿着小本本记着:",current_url,current_top+"px");
// console.log(">>> 滚动责任:",localStorage.getItem(getCurrentPageWhoRoll()));
localStorage.setItem(current_url,""+current_top)
}
// 滚动事件
window.addEventListener('scroll',debounce(handle, 460));
}
// 【还原监听器】
function recoverMonitor() {
// 位置还原
function recover() {
let item_content = localStorage.getItem(getCurrentUrl())
// 有记录
if (item_content == null) return;
// 获取历史高度
let history_high = parseFloat(item_content);
// 现在文档的高度
let current_height = getDocumentHeight();
// 如果没有历史高度,且高度不大于10就不还原
if(history_high != null && history_high >= 10 ) {
// 直接还原到历史位置
setTop(history_high);
// 检查是否恢复成功
//let fun = null;
//checkIsArriveHeight(500,history_high, function() {
// 如果失败,重试
//setTop(history_high,false);
//});
}
}
recover(); // 进入页面时还原
window.urlChangeListener.add(recover)
}
})();