// ==UserScript==
// @name 「Feedly」中键标记已读 + 收藏导出为*.url
// @namespace https://www.wdssmq.com/
// @version 0.3.6
// @author 沉冰浮水
// @description 新标签页打开条目时自动标记为已读,收藏计数
// @link https://github.com/wdssmq/userscript/tree/master/feedly
// @link https://greasyfork.org/zh-CN/scripts/381793
// @null ----------------------------
// @contributionURL https://github.com/wdssmq#%E4%BA%8C%E7%BB%B4%E7%A0%81
// @contributionAmount 5.93
// @null ----------------------------
// @link https://github.com/wdssmq/userscript
// @link https://afdian.net/@wdssmq
// @link https://greasyfork.org/zh-CN/users/6865-wdssmq
// @null ----------------------------
// @match https://feedly.com/*
// @grant GM_openInTab
// @grant GM_setClipboard
// @downloadURL none
// ==/UserScript==
/* jshint esversion: 6 */
(function () {
'use strict';
const gm_name = "feedly";
const curDate = new Date();
// ---------------------------------------------------
const _curUrl = () => { return window.location.href; };
// ---------------------------------------------------
const _log = (...args) => console.log(`[${gm_name}]\n`, ...args);
// ---------------------------------------------------
// const $ = window.$ || unsafeWindow.$;
function $n(e) {
return document.querySelector(e);
}
function $na(e) {
return document.querySelectorAll(e);
}
// ---------------------------------------------------
const fnElChange = (el, fn = () => { }) => {
const observer = new MutationObserver((mutationRecord, mutationObserver) => {
// _log('mutationRecord = ', mutationRecord);
// _log('mutationObserver === observer', mutationObserver === observer);
fn(mutationRecord, mutationObserver);
// mutationObserver.disconnect();
});
observer.observe(el, {
// attributes: false,
// attributeFilter: ["class"],
childList: true,
// characterData: false,
subtree: true,
});
};
function fnFindDomUp(el, selector, up = 1) {
// _log("fnFindDomUp", el, selector, up);
const elParent = el.parentNode;
if (selector.indexOf(".") == 0 && elParent.className.indexOf(selector.split(".")[1]) > -1) {
return elParent;
}
const elFind = elParent.parentNode.querySelector(selector);
if (elFind) {
return elFind;
}
if (up > 1) {
return fnFindDomUp(elParent, selector, up - 1);
} else {
return null;
}
}
// localStorage 封装
const lsObj = {
setItem: function (key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
getItem: function (key, def = "") {
const item = localStorage.getItem(key);
if (item) {
return JSON.parse(item);
}
return def;
},
};
// 数据读写封装
const gob = {
// 非 ls 字段
_loaded: false,
_curStars: 0,
_time: {
cycle: 0,
rem: 0,
},
// ls 字段
data: {
bolReset: false,
lstStars: 0,
diffStars: { decr: 0, incr: 0 },
// decrease 减少
// increase 增加
},
// 读取
load: function () {
if (this._loaded) {
return;
}
this._loaded = true;
this.data = lsObj.getItem("feedly_bld_data", this.data);
_log("load", gob);
},
// 保存
save: function () {
lsObj.setItem("feedly_bld_data", this.data);
_log("save", gob);
},
};
// 判断当前地址是否是收藏页
const fnCheckUrl = () => {
if ("https://feedly.com/i/saved" === _curUrl()) {
return true;
}
return false;
};
// 当前星标数获取
function fnLaterGetItems(obj) {
const $listWrap = $n("div.list-entries");
if ($listWrap) {
obj.$list = $listWrap.querySelectorAll("div.content a");
return obj.$list.length;
}
return 0;
}
// 收藏数 View
function fnLaterViewStars() {
gob._curStars = fnLaterGetItems(gob);
const strText = `Read later(${gob._curStars} 丨 -${gob.data.diffStars.decr} 丨 +${gob.data.diffStars.incr})(${gob._time.cycle} - ${gob._time.rem})`;
$n("h1 #header-title").innerHTML = strText;
if ($n("header.header h2")) {
$n("header.header h2").innerHTML = strText;
}
$n("#header-title").innerHTML = strText;
}
// 滚动条滚动时触发
function fnLaterOnScroll() {
if (!fnCheckUrl()) {
return;
}
fnLaterViewStars();
fnColorStars();
// 全部条目加载后执行
if ($n(".list-entries > h2")) {
!gob._loaded && _log("fnLaterOnScroll", "页面加载完成");
fnLaterControl();
} else {
_log("fnLaterOnScroll", "页面加载中");
}
}
// 星标部分入口函数
function fnLaterMain(record, observer) {
if (!fnCheckUrl()) {
return;
}
// 随机直接返回
if (Math.random() > 0.4) {
return;
}
gob._curStars = fnLaterGetItems(gob);
if (gob._curStars === 0) {
return;
}
// observer.disconnect();
// 绑定事件
if ($n("#feedlyFrame") && $n("#feedlyFrame").dataset.addEL !== "done") {
fnLaterOnScroll();
$n("#feedlyFrame").addEventListener("scroll", fnLaterOnScroll);
$n("#feedlyFrame").dataset.addEL = "done";
_log("fnLaterMain", "绑定滚动监听");
}
}
fnElChange($n("#box"), fnLaterMain);
const curTime = Math.floor(curDate.getTime() / 1000);
const curHours = Math.floor(curTime / 3600);
// const cur4Hours = Math.floor(curTime / (60 * 60 * 4));
const cur4Minutes = Math.floor(curTime / 240);
const fnCheckControl = (diff) => {
const iTime = curHours;
gob._time.cycle = iTime;
gob._time.rem = iTime % 4;
// 累计已读少于 17 或者累计新增大于累计已读
if (diff.decr < 17 || diff.incr - diff.decr >= 4) {
return "default";
}
// 每 4 小时且累计已读大于累计新增
if (iTime % 4 === 0 && diff.decr - diff.incr >= 4) {
return "reset";
}
return "lock";
};
// 星标变动控制
function fnLaterControl() {
if (gob._loaded) {
return;
}
gob.load();
// 解锁重置判断
if (gob.data.bolReset) {
gob.data.diffStars.decr = 0;
gob.data.diffStars.incr = 0;
}
// 星标变化计数
const diff = gob._curStars - gob.data.lstStars;
// 写入新的星标数
gob.data.lstStars = gob._curStars;
// 初始化时直接返回
if (diff === gob._curStars) {
gob.save();
return;
}
// 大于上一次记录
if (diff > 0) {
// 新增星标计数
gob.data.diffStars.incr += Math.abs(diff);
} else {
// 已读星标计数
gob.data.diffStars.decr += Math.abs(diff);
}
// 记录变量原始值
const strReset = gob.data.bolReset.toString();
// _log("fnLaterControl", strReset);
gob.data.bolReset = ((diff) => {
if (fnCheckControl(diff) === "reset") {
return true;
}
return false;
})(gob.data.diffStars);
// 更新 localStorage 存储
if (diff !== 0 || strReset !== gob.data.bolReset.toString()) {
gob.save();
}
}
// 按规则给星标条目着色
function fnColorStars(offset = 0) {
// _log("fnColorStars", curTime, cur4Minutes);
const $stars = gob.$list;
const isLock = fnCheckControl(gob.data.diffStars);
if (isLock === "lock") {
$n(".list-entries").style.backgroundColor = "#666";
}
let pickCount = 0;
[].forEach.call($stars, function ($e, i) {
// _log("fnColorStars", $e, i);
// _log("fnColorStars", "==============================");
const $ago = fnFindDomUp($e, ".ago", 2);
const href = $e.href + $ago.innerHTML;
const hash = parseInt(href.replace(/\D/g, ""));
// _log("fnColorStars", href, hash);
// const intNum = parseInt(hash + cur4Minutes + i);
const intNum = parseInt(hash + cur4Minutes + offset);
if (intNum % 4 === 0) {
// _log("fnColorStars", intNum, intNum % 4);
pickCount++;
$e.parentNode.parentNode.style.backgroundColor = "#ddd";
} else {
$e.parentNode.parentNode.style.backgroundColor = "transparent";
if (isLock === "lock" || pickCount > 7) {
// console.log($e.parentNode.parentNode.classList);
$e.parentNode.parentNode.style.backgroundColor = "#666";
}
}
});
if (pickCount <= 4) {
fnColorStars(offset + 1);
}
}
// 星标文章导出为 *.url 文件
function fnMKShell($list) {
const today = new Date(); // 获得当前日期
const year = today.getFullYear(); // 获得年份
const month = today.getMonth() + 1; // 此方法获得的月份是从0---11,所以要加1才是当前月份
const day = today.getDate(); // 获得当前日期
const arrDate = [year, month, day];
let strRlt =
'if [ ! -d "foldername" ]; then\n' +
"mkdir foldername\n" +
"fi\n" +
"cd foldername\n";
strRlt = strRlt.replace(/foldername/g, "later-" + arrDate.join("-"));
$list.forEach(function (e, i) {
// console.log(e);
let strTitle = `${i}丨`;
strTitle += e.textContent.replace(/\\|\/|:|\*|!|\?]|<|>/g, "");
const lenTitle = strTitle.length;
if (lenTitle >= 155) {
strTitle = `${i}丨标题过长丨${lenTitle}`;
}
let strUrl = e.href;
// strRlt += `\n#${i} - ${lenTitle}\n`;
strRlt += 'echo [InternetShortcut] > "' + strTitle + '.url"\n';
strRlt += 'echo "URL=' + strUrl + '" >> "' + strTitle + '.url"\n';
});
strRlt += "exit\n\n";
strRlt = strRlt.replace(/\/\/\//g, "//www.bilibili.com/");
//console.log(strRlt);
return strRlt;
//$n("body").innerHTML = strRlt.replace(/\n/g, "
");
}
$n("#box").addEventListener("mouseup", function (event) {
if (event.target.innerHTML.indexOf("Read later") > -1) {
const $el = event.target;
console.log($el);
GM_setClipboard(fnMKShell($na("div.content a")));
}
}, false);
// 拿回订阅源地址
// 绑定监听事件到 div#box 上
$n("#box").addEventListener("mouseup", function (event) {
// 输出触发事件的元素
// 根据内容判断是否执行相应操作
const elText = event.target.innerHTML;
if (
// elText.indexOf("Feed not found") > -1 ||
elText.indexOf("Wrong feed URL") > -1
) {
// 内部再输出一次确定判断条件正确
console.log(event.target);
// 拿到解码后的订阅源地址
const curUrl = ((url) => {
return url.replace("https://feedly.com/i/subscription/feed/", "");
})(decodeURIComponent(curUrl));
// 输出到页面中
$n("#feedlyPageFX h2").insertAdjacentHTML(
"beforeend",
`