// ==UserScript== // @name zhihusb // @namespace http://tampermonkey.net/ // @version 0.1.1 // @description try to take over the world! // @author You // @match https://www.zhihu.com/question/* // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; (function (root, factory) { window.lscache = factory(); }(this, function () { // Prefix for all lscache keys var CACHE_PREFIX = 'lscache-'; // Suffix for the key name on the expiration items in localStorage var CACHE_SUFFIX = '-cacheexpiration'; // expiration date radix (set to Base-36 for most space savings) var EXPIRY_RADIX = 10; // time resolution in minutes var EXPIRY_UNITS = 60 * 1000; // ECMAScript max Date (epoch + 1e8 days) var MAX_DATE = Math.floor(8.64e15/EXPIRY_UNITS); var cachedStorage; var cachedJSON; var cacheBucket = ''; var warnings = false; // Determines if localStorage is supported in the browser; // result is cached for better performance instead of being run each time. // Feature detection is based on how Modernizr does it; // it's not straightforward due to FF4 issues. // It's not run at parse-time as it takes 200ms in Android. function supportsStorage() { var key = '__lscachetest__'; var value = key; if (cachedStorage !== undefined) { return cachedStorage; } // some browsers will throw an error if you try to access local storage (e.g. brave browser) // hence check is inside a try/catch try { if (!localStorage) { return false; } } catch (ex) { return false; } try { setItem(key, value); removeItem(key); cachedStorage = true; } catch (e) { // If we hit the limit, and we don't have an empty localStorage then it means we have support if (isOutOfSpace(e) && localStorage.length) { cachedStorage = true; // just maxed it out and even the set test failed. } else { cachedStorage = false; } } return cachedStorage; } // Check to set if the error is us dealing with being out of space function isOutOfSpace(e) { if (e && e.name === 'QUOTA_EXCEEDED_ERR' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED' || e.name === 'QuotaExceededError') { return true; } return false; } // Determines if native JSON (de-)serialization is supported in the browser. function supportsJSON() { /*jshint eqnull:true */ if (cachedJSON === undefined) { cachedJSON = (window.JSON != null); } return cachedJSON; } /** * Returns a string where all RegExp special characters are escaped with a \. * @param {String} text * @return {string} */ function escapeRegExpSpecialCharacters(text) { return text.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&'); } /** * Returns the full string for the localStorage expiration item. * @param {String} key * @return {string} */ function expirationKey(key) { return key + CACHE_SUFFIX; } /** * Returns the number of minutes since the epoch. * @return {number} */ function currentTime() { return Math.floor((new Date().getTime())/EXPIRY_UNITS); } /** * Wrapper functions for localStorage methods */ function getItem(key) { return localStorage.getItem(CACHE_PREFIX + cacheBucket + key); } function setItem(key, value) { // Fix for iPad issue - sometimes throws QUOTA_EXCEEDED_ERR on setItem. localStorage.removeItem(CACHE_PREFIX + cacheBucket + key); localStorage.setItem(CACHE_PREFIX + cacheBucket + key, value); } function removeItem(key) { localStorage.removeItem(CACHE_PREFIX + cacheBucket + key); } function eachKey(fn) { var prefixRegExp = new RegExp('^' + CACHE_PREFIX + escapeRegExpSpecialCharacters(cacheBucket) + '(.*)'); // Loop in reverse as removing items will change indices of tail for (var i = localStorage.length-1; i >= 0 ; --i) { var key = localStorage.key(i); key = key && key.match(prefixRegExp); key = key && key[1]; if (key && key.indexOf(CACHE_SUFFIX) < 0) { fn(key, expirationKey(key)); } } } function flushItem(key) { var exprKey = expirationKey(key); removeItem(key); removeItem(exprKey); } function flushExpiredItem(key) { var exprKey = expirationKey(key); var expr = getItem(exprKey); if (expr) { var expirationTime = parseInt(expr, EXPIRY_RADIX); // Check if we should actually kick item out of storage if (currentTime() >= expirationTime) { removeItem(key); removeItem(exprKey); return true; } } } function warn(message, err) { if (!warnings) return; if (!('console' in window) || typeof window.console.warn !== 'function') return; window.console.warn("lscache - " + message); if (err) window.console.warn("lscache - The error was: " + err.message); } var lscache = { /** * Stores the value in localStorage. Expires after specified number of minutes. * @param {string} key * @param {Object|string} value * @param {number} time */ set: function(key, value, time) { if (!supportsStorage()) return; // If we don't get a string value, try to stringify // In future, localStorage may properly support storing non-strings // and this can be removed. if (!supportsJSON()) return; try { value = JSON.stringify(value); } catch (e) { // Sometimes we can't stringify due to circular refs // in complex objects, so we won't bother storing then. return; } try { setItem(key, value); } catch (e) { if (isOutOfSpace(e)) { // If we exceeded the quota, then we will sort // by the expire time, and then remove the N oldest var storedKeys = []; var storedKey; eachKey(function(key, exprKey) { var expiration = getItem(exprKey); if (expiration) { expiration = parseInt(expiration, EXPIRY_RADIX); } else { // TODO: Store date added for non-expiring items for smarter removal expiration = MAX_DATE; } storedKeys.push({ key: key, size: (getItem(key) || '').length, expiration: expiration }); }); // Sorts the keys with oldest expiration time last storedKeys.sort(function(a, b) { return (b.expiration-a.expiration); }); var targetSize = (value||'').length; while (storedKeys.length && targetSize > 0) { storedKey = storedKeys.pop(); warn("Cache is full, removing item with key '" + key + "'"); flushItem(storedKey.key); targetSize -= storedKey.size; } try { setItem(key, value); } catch (e) { // value may be larger than total quota warn("Could not add item with key '" + key + "', perhaps it's too big?", e); return; } } else { // If it was some other error, just give up. warn("Could not add item with key '" + key + "'", e); return; } } // If a time is specified, store expiration info in localStorage if (time) { setItem(expirationKey(key), (currentTime() + time).toString(EXPIRY_RADIX)); } else { // In case they previously set a time, remove that info from localStorage. removeItem(expirationKey(key)); } }, /** * Retrieves specified value from localStorage, if not expired. * @param {string} key * @return {string|Object} */ get: function(key) { if (!supportsStorage()) return null; // Return the de-serialized item if not expired if (flushExpiredItem(key)) { return null; } // Tries to de-serialize stored value if its an object, and returns the normal value otherwise. var value = getItem(key); if (!value || !supportsJSON()) { return value; } try { // We can't tell if its JSON or a string, so we try to parse return JSON.parse(value); } catch (e) { // If we can't parse, it's probably because it isn't an object return value; } }, /** * Removes a value from localStorage. * Equivalent to 'delete' in memcache, but that's a keyword in JS. * @param {string} key */ remove: function(key) { if (!supportsStorage()) return; flushItem(key); }, /** * Returns whether local storage is supported. * Currently exposed for testing purposes. * @return {boolean} */ supported: function() { return supportsStorage(); }, /** * Flushes all lscache items and expiry markers without affecting rest of localStorage */ flush: function() { if (!supportsStorage()) return; eachKey(function(key) { flushItem(key); }); }, /** * Flushes expired lscache items and expiry markers without affecting rest of localStorage */ flushExpired: function() { if (!supportsStorage()) return; eachKey(function(key) { flushExpiredItem(key); }); }, /** * Appends CACHE_PREFIX so lscache will partition data in to different buckets. * @param {string} bucket */ setBucket: function(bucket) { cacheBucket = bucket; }, /** * Resets the string being appended to CACHE_PREFIX so lscache will use the default storage behavior. */ resetBucket: function() { cacheBucket = ''; }, /** * Sets whether to display warnings when an item is removed from the cache or not. */ enableWarnings: function(enabled) { warnings = enabled; } }; // Return the module return lscache; })); var c = document.querySelector("#QuestionAnswers-answers [data-zop-feedlist]"); if (c === null) { c = document.querySelector(".Question-mainColumn .Card+.Card"); } console.dir(c); var userinfos = {}; var userids = {}; var userextends = {}; var components = {}; var createStopComponent = function(v) { var b = document.createElement("button"); b.id = "item" + Date.now() + "-" + Math.random(); b.innerHTML = "给我滚"; b.style = 'position: absolute; right: 0; top: 0;'; b.onclick = function() { var id = userids[b.id]; var sbid = lscache.get("sbid"); var info = {id: id, info: userinfos[b.id], extend: userextends[b.id], lastupdatetime: Date.now()}; if (!sbid) { sbid = {}; } if (!sbid[id]) { info.num = 1; } else { info.num = sbid[id].num + 1; } sbid[id] = info; lscache.set("sbid", sbid); if (v.querySelector(".AuthorInfo-name .gankuaigun .num")) { v.querySelector(".AuthorInfo-name .gankuaigun .num").innerHTML = "" + info.num; } else { v.querySelector(".AuthorInfo-name").insertAdjacentHTML('beforeend', ' 危险分子 +' + info.num + ' '); } }; return b; }; setTimeout(function() { var items = Array.prototype.slice.call(c.children); var sbid = lscache.get("sbid"); var daodishinagesb = {}; items.forEach(function(v, k) { v.style = "position: relative"; var component = createStopComponent(v); var info = JSON.parse(v.querySelector(".ContentItem").dataset.zaModuleInfo); var zhegesbid = info.card.content.author_member_hash_id; if (zhengesbid) { if (sbid && sbid[zhegesbid]) { console.log("sb gei wo gun " + sbid[zhegesbid].extend.zop.authorName); //v.style.display = "none"; v.querySelector(".AuthorInfo-name").insertAdjacentHTML('beforeend', ' 危险分子 +' + sbid[zhegesbid].num + ' '); } components[component.id] = component; v.appendChild(component); userinfos[component.id] = info; userids[component.id] = zhegesbid; userextends[component.id] = { zop: JSON.parse(v.querySelector(".ContentItem").dataset.zop) }; } }); console.dir(userids); }, 3000); })();