在微博网页端,为每个帖子创建一个收藏和新页面打开按钮。
// ==UserScript== // @name 微博帖子一键收藏、屏蔽、新页面打开 // @namespace http://tampermonkey.net/ // @version 20241122.2 // @description 在微博网页端,为每个帖子创建一个收藏和新页面打开按钮。 // @author Fat Cabbage // @license MIT // @match https://www.weibo.com/* // @match https://weibo.com/* // @match https://s.weibo.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=weibo.com // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @require https://code.jquery.com/jquery-3.5.1.min.js // ==/UserScript== /* globals jQuery, $, waitForKeyElements */ let blockConfig = new Map(); let onScrollFlag = false; let needUpdateNodeList = []; let blogCaches = new Map(); let menuSettingFav; let menuSettingBlock; class ClassName { static button = 'button_a656'; static buttonFavorite = 'button_a656_favorite'; static buttonOpenNewTab = 'button_a656_open_new_tab'; } class Domain { static WEIBO = 'weibo.com'; static WEIBO_S = 's.weibo.com'; static WEIBO_3W = 'www.weibo.com'; static domain = location.hostname; } class Const { static ID = 'ID'; static BLOG_ID = 'blogID'; static IS_FAVORITE = 'isFavorites'; static LAST_UPDATED = 'lastUpdated'; static IS_LOADING = 'isLoading'; static RES_OK = 'ok'; static RES_CODE = 'code'; static FAV_TOTAL_FAV = 'fav_total_num'; static FAV_TAGS = 'tags'; static FAV_TAGS_TAG = 'tag'; static FAV_TAGS_COUNT = 'count'; static FAV_TOTAL_TAG = 'total_number'; static FAV_SELECT_TAGS = 'CONST_FAV_SELECT_TAGS'; static BLOCK_SELECT_LEVEL = 'CONST_BLOCK_SELECT_LEVEL'; static LIST_TO_BE_BLOCK = 'LIST_TO_BE_BLOCK'; static LIST_TO_BE_BLOCK_UPDATING = 'LIST_TO_BE_BLOCK_UPDATING'; static BLOCK_FULL_LIST_STORE = 'BLOCK_FULL_LIST_STORE'; static BLOCK_FULL_LIST_STORE_LAST_UPDATE_DATE = 'BLOCK_FULL_LIST_STORE_LAST_UPDATE_DATE'; static PROMPT_TIME_MS = 1000; } class Selector { static settingButtonSelector = 'button[title="设置"]'; static rootNodeClass; static postNodeFullClass; static buttonLocateSelector; static userASector; static userTitleSector; static timeASector; static forwardNodeStartClass; static forwardNodeSelector; static likeButtonSelector; static { if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { Selector.rootNodeClass = 'vue-recycle-scroller__item-wrapper'; Selector.rootNodeClass = 'Main_full'; Selector.postNodeFullClass = `Feed_wrap`; Selector.buttonLocateSelector = 'div[class*="head_main"]'; Selector.userASector = `a[class*="head_name"]`; Selector.userTitleSector = `a[class*="head_name"] > span`; Selector.timeASector = `a[class^="head-info_time"]`; Selector.forwardNodeStartClass = 'retweet Feed_retweet' Selector.forwardNodeSelector = 'div.retweet[class*="Feed_retweet"]' Selector.likeButtonSelector = 'button[title="赞"]'; } else if (Domain.domain === Domain.WEIBO_S) { Selector.rootNodeClass = 'main-full'; Selector.postNodeFullClass = 'card'; Selector.buttonLocateSelector = 'div.menu.s-fr'; // buttonLocateSelector = 'div.from > a:last-child'; Selector.userASector = `div.info > :nth-child(2) > a:first-child`; Selector.userTitleSector = Selector.userASector; Selector.timeASector = `div.from > a:first-child`; Selector.likeButtonSelector = 'a[title="赞"]'; } } } class BlogCache { static get(blogID, key) { if (!blogCaches.has(blogID)) { let blogCache = {}; blogCaches.set(blogID, blogCache); } return blogCaches.get(blogID)[key]; } static set(blogID, key, value) { let blogCache = blogCaches.get(blogID); if (blogCache == null) { blogCache = {}; } blogCache[key] = value; blogCache[Const.LAST_UPDATED] = new Date(); blogCaches.set(blogID, blogCache); } } class TimeConvert { static toSeconds(milliseconds) { return milliseconds / 1000; } static toMinutes(milliseconds) { return TimeConvert.toSeconds(milliseconds) / 60; } static toHours(milliseconds) { return TimeConvert.toMinutes(milliseconds) / 60; } static toDays(milliseconds) { return TimeConvert.toHours(milliseconds) / 24; } } class Do { static setFavTags() { function getFavTagInfo() { let url = 'https://weibo.com/ajax/favorites/tags?is_show_total=1'; let token = Cookie.get('XSRF-TOKEN'); return $.ajax({ url: url, type: 'GET', headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(res => { if (typeof res === `string`) { console.log(res); return false; } return res; }); } getFavTagInfo().then(async res => { if (res) { let tagList = res[Const.FAV_TAGS]; let tagText = []; for (let i = 0; i < tagList.length; i++) { let t = tagList[i]; let name = t[Const.FAV_TAGS_TAG]; let count = t[Const.FAV_TAGS_COUNT]; tagText.push(`${i} ${name} ( 共 ${count} 条 )`); } let tagTmp = tagText.join('\n'); GM_.get(Const.FAV_SELECT_TAGS).then(selected => { if (selected === '') { selected = '无'; } let info = `收藏总数: ${res[Const.FAV_TOTAL_FAV]} 标签总数:${res[Const.FAV_TOTAL_TAG]} 当前标签选择:${selected} 1. 输入标签索引,以空格分隔,最多可设置两个标签 2. 输入为空表示不设置标签 3. 如需新建标签,请通过微博手动添加 示例:要选择第1个与第3个已有标签,输入“0 2”\n ${tagTmp}`; let input = prompt(info).trim() let nameTmp; if (input === '') { alert('标签设置已清空'); nameTmp = ''; } else { let split = input.split(' '); let nameList = []; for (let i = 0; i < split.length; i++) { let j = parseInt(split[i]); if (j < 0 || j > tagList.length - 1) { alert(`第${i + 1}个输入不合法,需要重新输入`); return; } let t = tagList[j]; let name = t[Const.FAV_TAGS_TAG]; nameList.push(name); } nameTmp = nameList.join(' '); alert(`标签已设置: ${nameTmp}`); } GM_.set(Const.FAV_SELECT_TAGS, nameTmp); GM_.unregisterMenuCommand(menuSettingFav); if (nameTmp === '') { nameTmp = '无'; } menuSettingFav = GM_.registerMenuCommand(`设置收藏标签 当前:${nameTmp}`, () => { Do.setFavTags(); }); }); } else { alert('获取标签列表失败'); } }) } static setBlockLevels() { let levelList = ['不看微博', '禁止互动', '禁止关注']; let levelText = []; for (let i = 0; i < levelList.length; i++) { let item = levelList[i]; levelText.push(`${i} ${item}`); } let tagTmp = levelText.join('\n'); GM_.get(Const.BLOCK_SELECT_LEVEL).then(selected => { if (selected === '') { selected = '不看微博'; } let info = `当前屏蔽设置:${selected} 1. 输入屏蔽级别索引,以空格分隔 2. 输入为空表示默认设置(${levelList[0]}) 示例:要选择第1个与第3个屏蔽级别,输入“0 2”\n ${tagTmp}`; let input = prompt(info).trim() let nameTmp; if (input === '') { alert('屏蔽已设为默认'); nameTmp = '不看微博'; } else { let split = input.split(' '); let nameList = []; for (let i = 0; i < split.length; i++) { let j = parseInt(split[i]); if (j < 0 || j > levelList.length - 1) { alert(`第${i + 1}个输入不合法,需要重新输入`); return; } let name = levelList[j]; nameList.push(name); } nameTmp = nameList.join(' '); alert(`屏蔽已设置: ${nameTmp}`); } GM_.set(Const.BLOCK_SELECT_LEVEL, nameTmp); GM_.unregisterMenuCommand(menuSettingBlock); if (nameTmp === '') { nameTmp = '无'; } menuSettingBlock = GM_.registerMenuCommand(`设置屏蔽级别 当前:${nameTmp}`, () => { Do.setBlockLevels(); }); }); } } class Locate { static articleNode(node, type = 'default') { let postList = node.querySelectorAll(`article[class*="Feed_wrap"]`); if (postList.length > 0) { return postList[0]; } while (node != null) { if (node.className == null) { return null; } if (type === 'default') { if (node.className.indexOf(Selector.postNodeFullClass) >= 0) { return node; } } else if (type === 'forward') { if (node.className.indexOf(Selector.forwardNodeStartClass) >= 0) { return node; } } node = node.parentNode; } return null; } static likeButton(articleNode) { return articleNode.querySelector(Selector.likeButtonSelector) } } class BlogView { static getBlogID(articleNode) { let timeA = articleNode.querySelector(Selector.timeASector); let url = timeA.href; let index = url.lastIndexOf('/'); return url.substring(index + 1); } static getUserID(articleNode) { let url = articleNode.querySelector(Selector.userASector).href; let slashIndex = url.lastIndexOf('/'); let questionIndex = url.lastIndexOf('?'); if (questionIndex >= 0) { return url.substring(slashIndex + 1, questionIndex); } else { return url.substring(slashIndex + 1); } } static getUsername(articleNode) { return articleNode.querySelector(Selector.userTitleSector).text; } static getBlogIDNum(articleNode) { if (Domain.domain === Domain.WEIBO_S) { let node = articleNode; while (node != null) { let actionType = node.getAttribute(`action-type`); if (actionType === `feed_list_item`) { return node.getAttribute(`mid`); } node = node.parentNode; } return null; } return null; } static getLink(articleNode) { return articleNode.querySelector(Selector.timeASector).href; } } class Button { static buttonClassList; static addBaseClass(node) { if (Button.buttonClassList == null) { let settingButton = document.querySelector(Selector.settingButtonSelector) let classList = settingButton.classList Button.buttonClassList = Array.from(classList).filter(className => className.startsWith('IconBox_')); } for (let cl of Button.buttonClassList) { node.classList.add(cl); } } static createFavorite(type = 'default', like = false) { let buttonNode = document.createElement('button'); buttonNode.classList.add(ClassName.button); buttonNode.classList.add(ClassName.buttonFavorite); Button.addBaseClass(buttonNode); buttonNode.style.width = 'auto'; buttonNode.style.minWidth = '46.667px'; buttonNode.style.height = '28px'; buttonNode.style.marginLeft = '0px'; buttonNode.style.marginRight = '5px'; buttonNode.style.paddingLeft = '10px'; buttonNode.style.paddingRight = '10px'; buttonNode.addEventListener('click', ev => { let buttonNode = ev.target; let articleNode = Locate.articleNode(buttonNode, type); let blogID = BlogView.getBlogID(articleNode); let ID = BlogCache.get(blogID, Const.ID) let isFavorites = BlogCache.get(blogID, Const.IS_FAVORITE) let error = false; if (isFavorites) { Favorite.remove(ID).then(res => { if (res) { BlogCache.set(blogID, Const.IS_FAVORITE, false); buttonNode.innerText = `已取消收藏`; setTimeout(() => { buttonNode.innerText = `收藏`; }, Const.PROMPT_TIME_MS); } else { buttonNode.innerText = `取消收藏失败`; error = true; } }); } else { Favorite.set(ID).then(res => { if (res) { BlogCache.set(blogID, Const.IS_FAVORITE, true); buttonNode.innerText = `已收藏`; setTimeout(() => { buttonNode.innerText = `取消收藏`; }, Const.PROMPT_TIME_MS); } else { buttonNode.innerText = `收藏失败`; error = true; } }); } }); return buttonNode; } static createOpen(type = 'default') { let buttonNode = document.createElement('button'); buttonNode.textContent = '新页面打开'; buttonNode.classList.add(ClassName.button); buttonNode.classList.add(ClassName.buttonOpenNewTab); Button.addBaseClass(buttonNode); buttonNode.setAttribute('href', '#woo_svg_nav_sun'); buttonNode.style.width = 'auto'; buttonNode.style.minWidth = '46.667px'; buttonNode.style.height = '28px'; buttonNode.style.marginRight = '5px'; buttonNode.style.marginLeft = '0px'; buttonNode.style.paddingLeft = '10px'; buttonNode.style.paddingRight = '10px'; buttonNode.addEventListener('click', ev => { let buttonNode = ev.target; let articleNode = Locate.articleNode(buttonNode, type); let link = BlogView.getLink(articleNode); window.open(link); }); return buttonNode; } static createBlock(type = 'default') { let buttonNode = document.createElement('button'); buttonNode.textContent = '屏蔽用户'; buttonNode.classList.add(ClassName.button); buttonNode.classList.add(ClassName.buttonOpenNewTab); Button.addBaseClass(buttonNode); buttonNode.style.width = 'auto'; buttonNode.style.minWidth = '46.667px'; buttonNode.style.height = '28px'; buttonNode.style.marginLeft = '0px'; buttonNode.style.marginRight = '5px'; buttonNode.style.paddingLeft = '10px'; buttonNode.style.paddingRight = '10px'; buttonNode.addEventListener('click', ev => { let buttonNode = ev.target; let articleNode = Locate.articleNode(buttonNode, type); let userID = BlogView.getUserID(articleNode); let username = BlogView.getUsername(articleNode); Block.set(userID, username).then(res => { if (res) { buttonNode.innerText = `已屏蔽`; } else { buttonNode.innerText = `屏蔽失败`; } }); }); return buttonNode; } } class Favorite { static get(ID) { BlogCache.set(ID, Const.IS_LOADING, true); if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { let url; if (Domain.domain === Domain.WEIBO) { url = `https://weibo.com/ajax/statuses/show?id=${ID}`; } else { url = `https://www.weibo.com/ajax/statuses/show?id=${ID}`; } return $.ajax({ url: url, type: 'GET', }).then(res => { BlogCache.set(ID, Const.ID, res['id']); BlogCache.set(ID, Const.BLOG_ID, ID); BlogCache.set(ID, Const.IS_FAVORITE, res['favorited']); BlogCache.set(ID, Const.IS_LOADING, false); }); } else if (Domain.domain === Domain.WEIBO_S) { return new Promise(() => { BlogCache.set(ID, Const.BLOG_ID, ID); BlogCache.set(ID, Const.IS_FAVORITE, false); BlogCache.set(ID, Const.IS_LOADING, false); }) } } static set(ID) { if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { let data = JSON.stringify({'id': `${ID}`}); let token = Cookie.get('XSRF-TOKEN'); let url; if (Domain.domain === Domain.WEIBO) { url = `https://weibo.com/ajax/statuses/createFavorites`; } else { url = `https://www.weibo.com/ajax/statuses/createFavorites`; } return $.ajax({ url: url, type: 'POST', data: data, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(async res => { if (typeof res === `string`) { console.log(res); return false; } return GM_.get(Const.FAV_SELECT_TAGS).then(tags => { if (tags !== '') { tags = tags.split(' '); tags = tags.join(','); Favorite.setTags(ID, tags); } return res[Const.RES_OK] === 1; }); }); } else if (Domain.domain === Domain.WEIBO_S) { let data = { 'mid': `${ID}` }; return $.ajax({ url: `https://s.weibo.com/ajax_Mblog/favAdd`, type: 'POST', data: data }).then(res => { if (typeof res === `string`) { console.log(res); return false; } return res[Const.RES_CODE] === `100000`; }); } } static remove(ID) { if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { let data = JSON.stringify({'id': `${ID}`}); let token = Cookie.get('XSRF-TOKEN'); let url; if (Domain.domain === Domain.WEIBO) { url = `https://weibo.com/ajax/statuses/destoryFavorites`; } else { url = `https://www.weibo.com/ajax/statuses/destoryFavorites`; } return $.ajax({ url: url, type: 'POST', data: data, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(res => { if (typeof res === `string`) { console.log(res); return false; } return res[Const.RES_OK] === 1; }); } else if (Domain.domain === Domain.WEIBO_S) { let data = { 'mid': `${ID}` }; return $.ajax({ url: `https://s.weibo.com/ajax_Mblog/favDel`, type: 'POST', data: data }).then(res => { if (typeof res === `string`) { console.log(res); return false; } return res[Const.RES_CODE] === `100000`; }); } } static setTags(ID, tags) { let data = JSON.stringify({'id': `${ID}`, 'tags': `${tags}`}); let token = Cookie.get('XSRF-TOKEN'); let url = 'https://weibo.com/ajax/favorites/tags/update'; return $.ajax({ url: url, type: 'POST', data: data, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(res => { if (typeof res === `string`) { console.log(res); return false; } return res[Const.RES_OK] === 1; }); } } class Block { static get(ID) { return Block.getList().then(list => { for (let i = 0; i < list.length; i++) { if (list[i] === id) { return true; } } return false; }); } static set(ID, username) { if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { return GM_.get(Const.BLOCK_SELECT_LEVEL).then(tags => { let tmp = tags.split(' ') let _status = tmp.indexOf('不看微博') === -1 ? 0 : 1; let _interact = tmp.indexOf('禁止互动') === -1 ? 0 : 1; let _follow = tmp.indexOf('禁止关注') === -1 ? 0 : 1; if ([_status, _interact, _follow].indexOf(1) === -1) { _status = 1; } let data = JSON.stringify({ 'uid': `${ID}`, 'screen_name': `${username}`, 'status': _status, 'interact': _interact, 'follow': _follow }); let token = Cookie.get('XSRF-TOKEN'); let url; if (Domain.domain === Domain.WEIBO) { url = `https://weibo.com/ajax/statuses/filterUser`; } else { url = `https://www.weibo.com/ajax/statuses/`; } return $.ajax({ url: url, type: 'POST', data: data, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(async res => { if (typeof res === `string`) { console.log(res); return false; } return res[Const.RES_OK] === 1; }); }); } else if (Domain.domain === Domain.WEIBO_S) { return GMHelper.addBlockItem(ID, username); } } static remove(ID, username) { if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { let data = JSON.stringify({ 'uid': `${ID}`, 'screen_name': `${username}` }); let token = Cookie.get('XSRF-TOKEN'); let url; if (Domain.domain === Domain.WEIBO) { url = `https://weibo.com/ajax/setting/deleteFilteredUser`; } else { url = `https://www.weibo.com/ajax/statuses/`; } return $.ajax({ url: url, type: 'POST', data: data, headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(async res => { if (typeof res === `string`) { console.log(res); return false; } return res[Const.RES_OK] === 1; }); } else if (Domain.domain === Domain.WEIBO_S) { return GMHelper.removeBlockItem(ID); } } static getList() { return GM_.get(Const.BLOCK_FULL_LIST_STORE).then(res => { return res.split(' '); }); } static addFromServer() { function loadPage(page) { let token = Cookie.get('XSRF-TOKEN'); let url = `https://weibo.com/ajax/setting/getFilteredUsers?page=${page}`; return $.ajax({ url: url, type: 'GET', headers: { 'Content-Type': 'application/json; charset=utf-8', 'X-Xsrf-Token': `${token}` } }).then(async res => { if (typeof res === `string`) { console.log(res); return false; } return res; }); } function getBlockListRec(list, page, total = 0, rememberTotal = true) { return new Promise(resolve => { loadPage(page).then(res => { if (rememberTotal) { total = res['total']; } res['card_group'].forEach(item => { let scheme = item['scheme']; let lastIndex = scheme.lastIndexOf('='); let ID = scheme.substring(lastIndex + 1); list.push(ID); }); if (list.length < total) { let random = Math.random() * (200 - 50) + 50; let timeout = 300 + random; setTimeout(() => { getBlockListRec(list, page + 1, total, false).then(() => resolve()); }, timeout); } else { resolve(); } }); }); } if (Domain.domain === Domain.WEIBO) { let blockList = []; return getBlockListRec(blockList, 1).then(() => { Block.getList().then(list => { let split = blockList.join(' '); list = list.concat(split); list = [...new Set(list)]; GM_.set(Const.BLOCK_FULL_LIST_STORE, list); GM_.set(Const.BLOCK_FULL_LIST_STORE_LAST_UPDATE_DATE, new Date().toString()); }); }); } } static logStore() { Block.getList().then(list => { GM_.get(Const.BLOCK_FULL_LIST_STORE_LAST_UPDATE_DATE).then(res => { console.log(`存储的用户屏蔽数量:${list.length},上次更新时间:${res}`); }); }); } } class GMHelper { static waitValue(name, expectedValue, timeout = 100, nullValue = expectedValue) { GM_.get(name, nullValue).then(res => { if (res === expectedValue) { return; } let inspection = setInterval(() => { GM_.get(name, nullValue).then(res => { if (res === expectedValue) { clearInterval(inspection); } }); }, timeout); }); } static addBlockItem(ID, username) { GMHelper.waitValue(Const.LIST_TO_BE_BLOCK_UPDATING, 'false'); GM_.set(Const.LIST_TO_BE_BLOCK_UPDATING, 'true'); GM_.get(Const.LIST_TO_BE_BLOCK, '').then(res => { let split = res.split(';'); for (let i = 0; i < split.length; i++) { let iID = split[i].split(' ')[0]; if (iID === ID) { GM_.set(Const.LIST_TO_BE_BLOCK_UPDATING, 'false'); return; } } let item = `${ID} ${username}`; split.push(item); let text = split.join(' '); GM_.set(Const.LIST_TO_BE_BLOCK, text); GM_.set(Const.LIST_TO_BE_BLOCK_UPDATING, 'false'); }); return Promise.resolve(); } static removeBlockItem(ID) { GMHelper.waitValue(Const.LIST_TO_BE_BLOCK_UPDATING, 'false'); GM_.set(Const.LIST_TO_BE_BLOCK_UPDATING, 'true'); GM_.get(Const.LIST_TO_BE_BLOCK, '').then(res => { let split = res.split(';'); split = split.filter(s => { let sID = s.split(' ')[0]; return sID === ID; }) let text = split.join(' '); GM_.set(Const.LIST_TO_BE_BLOCK, text); GM_.set(Const.LIST_TO_BE_BLOCK_UPDATING, 'false'); }); } } class GM_ { static async get(name, defaultV = '') { let value = await GM.getValue(name); if (value === undefined || value.trim() === '') { value = defaultV; } return Promise.resolve(value); } static set(name, value) { GM.setValue(name, value); } static registerMenuCommand(name, fun) { return GM_registerMenuCommand(name, fun); } static unregisterMenuCommand(name) { GM_unregisterMenuCommand(name) } } class Cookie { static get(name) { let cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { let cookie = cookies[i].trim(); if (cookie.startsWith(name + '=')) { return cookie.substring(name.length + 1); } } return null; } } (function () { 'use strict'; GM_.get(Const.FAV_SELECT_TAGS, '无').then(selected => { menuSettingFav = GM_.registerMenuCommand(`设置收藏标签 当前:${selected}`, () => { Do.setFavTags(); }); }); GM_.get(Const.BLOCK_SELECT_LEVEL, '不看微博').then(selected => { menuSettingBlock = GM_.registerMenuCommand(`设置屏蔽 当前:${selected}`, () => { Do.setBlockLevels(); }); }); setTimeout(() => { document.addEventListener('DOMContentLoaded', function () { onScrollFlag = true; }); window.onscroll = () => { onScrollFlag = true; } updateFavoriteButton(); updateFavoriteButton2(); listenRootBlock(); GM_.get(Const.BLOCK_FULL_LIST_STORE_LAST_UPDATE_DATE, '').then(lastDate => { Block.logStore(); if (lastDate !== '') { let time_diff = new Date() - new Date(lastDate); time_diff = TimeConvert.toDays(time_diff); if (time_diff <= 1) { return; } } // Block.addFromServer().then(() => Block.logStore()); }); }, 2000); })(); function updateFavoriteButton() { onScrollFlag = true; setInterval(() => { if (onScrollFlag) { for (let [articleNode, config] of blockConfig) { function isInViewPortOfOne(el) { let viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight let screenTop = document.documentElement.scrollTop let screenBottom = screenTop + viewPortHeight let bounding = el.getBoundingClientRect(); let top = screenTop + bounding.top; let bottom = bounding.bottom; return screenTop <= top && top <= screenBottom } let isVisible = isInViewPortOfOne(articleNode) if (isVisible) { needUpdateNodeList.push(articleNode); } else { needUpdateNodeList = needUpdateNodeList.filter(item => item !== articleNode); } } onScrollFlag = false; } }, 100); } function updateFavoriteButton2() { setInterval(() => { let articleNode = needUpdateNodeList.pop(); if (articleNode) { let res = updateFavoriteButton3(articleNode); if (!res) { needUpdateNodeList.push(articleNode); } let forwardNode = articleNode.querySelector(Selector.forwardNodeSelector); if (forwardNode != null) { updateFavoriteButton3(forwardNode); } } }, 100); } function updateFavoriteButton3(articleNode) { let blogID = BlogView.getBlogID(articleNode); let isLoading = BlogCache.get(blogID, Const.IS_LOADING); if (isLoading) { return false; } let buttonNode = articleNode.querySelector(`button[class*="${ClassName.buttonFavorite}"]`); if (blogCaches.has(blogID)) { if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { let lastUpdate = BlogCache.get(blogID, Const.LAST_UPDATED); if (lastUpdate) { let time_diff = new Date() - new Date(lastUpdate); time_diff = TimeConvert.toHours(time_diff); // Greater than 1 hour if (time_diff > 1) { Favorite.get(blogID).then(() => { updateButtonText(blogID, buttonNode) }); } else { updateButtonText(blogID, buttonNode); } } else { Favorite.get(blogID).then(() => { updateButtonText(blogID, buttonNode) }); } } else if (Domain.domain === Domain.WEIBO_S) { // s.weibo.com do not update status, due to lack of API support updateButtonText(blogID, buttonNode); } } else { Favorite.get(blogID).then(() => { updateButtonText(blogID, buttonNode) }); } let likeButton = Locate.likeButton(articleNode) likeButton.parentElement.onclick = () => { likeButton.click(); } return true; } function listenRootBlock() { setInterval(() => { let rootNode = document.querySelector(`div[class*="${Selector.rootNodeClass}"]`); if (rootNode == null) { return; } let isLoadEvent = rootNode.getAttribute('data_a656_is_load_event'); if (isLoadEvent != null) { return; } rootNode.setAttribute('data_a656_is_load_event', true.toString()); if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { new MutationObserver((mutationsLi) => { for (let mutations of mutationsLi) { if (mutations.type === 'childList') { mutations.addedNodes.forEach(node => { if (node.nodeName === '#text' || node.nodeName === '#comment') { return; } let articleNode = Locate.articleNode(node, 'default'); if (articleNode == null) { return; } if (blockConfig.get(articleNode) == null) { blockConfig.set(articleNode, {}); } placeButtons(articleNode); }) } } }).observe(rootNode, {childList: true, subtree: true}); onScrollFlag = true; let postList = rootNode.querySelectorAll(`article[class*="Feed_wrap"]`); postList.forEach(articleNode => { if (!blockConfig.has(articleNode)) { blockConfig.set(articleNode, {}); } let blogID = BlogView.getBlogID(articleNode); Favorite.get(blogID); placeButtons(articleNode) }); } else if (Domain.domain === Domain.WEIBO_S) { let postList = rootNode.querySelectorAll(`div[class="${Selector.postNodeFullClass}"]`); onScrollFlag = true; postList = Array.from(postList).filter(function (div) { return !div.querySelector('div.wb-ad-tile'); }); postList.forEach(articleNode => { if (!blockConfig.has(articleNode)) { blockConfig.set(articleNode, {}); } let blogID = BlogView.getBlogID(articleNode); let id = BlogView.getBlogIDNum(articleNode); BlogCache.set(blogID, Const.ID, id); Favorite.get(blogID); placeButtons(articleNode) }); } }, 500); } function placeButtons(node) { if (node == null) { return; } if (node.getAttribute('data_a656_value1') === 'true') { return; } node.setAttribute('data_a656_value1', true.toString()); let favoriteButtonNode = Button.createFavorite(); let openButton = Button.createOpen(); let blockButton = Button.createBlock(); let targetNode = node.querySelector(Selector.buttonLocateSelector); if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { targetNode.parentNode.insertBefore(favoriteButtonNode, targetNode.nextSibling); targetNode.parentNode.insertBefore(openButton, targetNode.nextSibling); if (Domain.domain === Domain.WEIBO) { targetNode.parentNode.insertBefore(blockButton, targetNode.nextSibling); } } else if (Domain.domain === Domain.WEIBO_S) { let wrap = document.createElement('div'); wrap.style.position = 'absolute'; wrap.style.top = '-10px'; wrap.style.right = '18px'; wrap.style.width = '300px'; wrap.style.display = 'flex'; wrap.style.flexDirection = 'row-reverse'; wrap.appendChild(favoriteButtonNode); wrap.appendChild(openButton); // wrap.appendChild(blockButton); targetNode.appendChild(wrap); } let forwardNode = node.querySelector(Selector.forwardNodeSelector); if (forwardNode != null) { let favoriteButtonNode = Button.createFavorite('forward'); let openButton = Button.createOpen('forward'); let blockButton = Button.createBlock('forward'); let targetNode = forwardNode.querySelector('a').parentNode; if (Domain.domain === Domain.WEIBO || Domain.domain === Domain.WEIBO_3W) { openButton.style.marginLeft = 'auto'; favoriteButtonNode.style.marginRight = '26px'; targetNode.appendChild(blockButton); targetNode.appendChild(openButton); targetNode.appendChild(favoriteButtonNode); } } } function updateButtonText(blogID, buttonNode) { if (BlogCache.get(blogID, Const.IS_FAVORITE)) { buttonNode.innerText = '取消收藏'; } else { buttonNode.innerText = '收藏'; } } function toast(msg, duration) { duration = isNaN(duration) ? 3000 : duration; let m = document.createElement('div'); m.innerHTML = msg; m.style.setProperty('font-size', '20px', 'important'); m.style.setProperty('color', 'rgb(255, 255, 255)', 'important'); m.style.setProperty('background-color', 'rgba(0,0,0,0.6)', 'important'); m.style.setProperty('border-style', 'solid', 'important'); m.style.setProperty('border-color', '#ffffff', 'important'); m.style.setProperty('z-index', '256', 'important'); m.style.cssText = 'font-size: 20px; ' + 'color: rgb(255, 255, 255); ' + 'background-color: rgba(0,0,0,0.6); ' + 'border-style: solid; ' + 'border-color: #ffffff; ' + 'z-index: 256; ' + 'padding: 10px 15px; ' + 'margin: 0 0 0 -60px; ' + 'border-radius: 4px; ' + 'position: fixed; ' + 'top: 50%; ' + 'left: 50%; ' + 'width: 130px; ' + 'text-align: center;'; document.body.appendChild(m); setTimeout(function () { let d = 0.5; m.style.opacity = '0'; setTimeout(function () { document.body.removeChild(m) }, d * 1000); }, duration); }