您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Checks the last online on page user and in dialog
当前为
// ==UserScript== // @name VK: Check Online // @name:ru ВК: Проверка онлайна // @description Checks the last online on page user and in dialog // @description:ru Проверяет последний онлайн пользователя на странице и в диалогe // @namespace vk-check-online.user.js // @license MIT // @author askornot // @version 1.1.1 // @match https://vk.com/* // @connect vk.com // @compatible chrome Violentmonkey 2.12.7 // @compatible firefox Violentmonkey 2.12.7 // @homepageURL https://greasyfork.org/en/scripts/403717-vk-check-online // @supportURL https://greasyfork.org/en/scripts/403717-vk-check-online/feedback // @run-at document-end // @noframes // ==/UserScript== 'use strict'; const W = unsafeWindow || window; const MINUTE = 60 * 1000; const UNIXTIME = 1000; class Cache { constructor() { this.default = { uts: 0, expires: 0 }; this.data = new Map(); } get(key) { const exist = this.data.get(key); if (exist) return exist; return this.default; } set(key, value) { const expires = Date.now() + MINUTE; this.data.set(key, { uts: value, expires }); } } const request = (url, callback) => { const xhr = new XMLHttpRequest(); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { const container = document.implementation.createHTMLDocument().documentElement; if (xhr.status === 0 || xhr.status === 200) { container.innerHTML = xhr.responseText; } callback(container); } }; xhr.open('GET', url, true); xhr.send(); }; const render = (uts) => { const online = document.querySelector('.mail_box_label_info') || document.querySelector('.profile_online_lv') || document.querySelector('._im_page_peer_online'); const { lang } = window.vk; const text = lang === 3 ? 'last seen' : 'заходил(а)'; online.removeAttribute('class'); online.textContent = `${text} ${window.getDateText(uts, null)}`; }; const extractTimestamp = (body) => { const [element] = body.getElementsByTagName('ya:lastloggedin'); if (!element) return 0; const date = element.getAttribute('dc:date'); const uts = Math.floor(Date.parse(date) / UNIXTIME); return uts; }; const extract = () => { const { options, peer } = window.cur; const id = peer || (options && options.user_id); return id || 0; }; const start = () => { const id = extract(); if (id === 0 || Math.sign(id) === -1) return; const { expires, uts } = cache.get(id); if (expires > Date.now()) { render(uts); return; } request(`/foaf.php?id=${id}`, (body) => { const uts = extractTimestamp(body); if (uts === 0) return; render(uts); cache.set(id, uts); }); }; const observable = (target) => { const handlers = []; const observe = (handler) => { handlers.push(handler); }; const proxy = new Proxy(target, { get(...args) { const output = Reflect.get(...args); if (output) { handlers.forEach((fn) => fn(output)); } return output; }, }); return [proxy, observe]; }; const debounce = (timeout, fn) => { let timer; const debounced = () => fn(); const wrapped = () => { if (timer) clearTimeout(timer); timer = setTimeout(debounced, timeout); }; return wrapped; }; const debouncedStart = debounce(1000, start); const types = { object: debouncedStart }; const [instance, observe] = observable(W.nav); W.nav = instance; observe((data) => { const type = typeof data; const fn = types[type]; if (fn) fn(); }); const cache = new Cache(); debouncedStart();