- // ==UserScript==
- // @name zhihusb
- // @namespace http://tampermonkey.net/
- // @version 0.1.3
- // @description try to take over the world!
- // @author You
- // @match https://www.zhihu.com/question/*
- // @grant 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', '<span class="gankuaigun" style="color: #f00;"> 危险分子 +<span class="num">' + info.num + '</span> </span>');
- }
- };
- 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 (zhegesbid) {
- 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', '<span class="gankuaigun" style="color: #f00;"> 危险分子 +<span class="num">' + sbid[zhegesbid].num + '</span> </span>');
- }
-
- 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);
- })();