您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Base library for my scripts
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/375557/870039/Base%20Brazen%20Resource.js
// ==UserScript== // @name Base Brazen Resource // @namespace brazen // @version 3.0.2 // @author brazenvoid // @license GPL-3.0-only // @description Base library for my scripts // @run-at document-end // ==/UserScript== class ChildObserver { /** * @callback observerOnMutation * @param {NodeList} nodes */ /** * @return {ChildObserver} */ static create () { return new ChildObserver } /** * ChildObserver constructor */ constructor () { this._node = null this._observer = null this._onNodesAdded = null this._onNodesRemoved = null } /** * @return {ChildObserver} * @private */ _observeNodes () { this._observer.observe(this._node, {childList: true}) return this } /** * Attach an observer to the specified node(s) * @param {Node} node * @returns {ChildObserver} */ observe (node) { this._node = node this._observer = new MutationObserver((mutations) => { for (let mutation of mutations) { if (mutation.addedNodes.length && this._onNodesAdded !== null) { this._onNodesAdded( mutation.addedNodes, mutation.previousSibling, mutation.nextSibling, mutation.target, ) } if (mutation.removedNodes.length && this._onNodesRemoved !== null) { this._onNodesRemoved( mutation.removedNodes, mutation.previousSibling, mutation.nextSibling, mutation.target, ) } } }) return this._observeNodes() } /** * @param {observerOnMutation} eventHandler * @returns {ChildObserver} */ onNodesAdded (eventHandler) { this._onNodesAdded = eventHandler return this } /** * @param {observerOnMutation} eventHandler * @returns {ChildObserver} */ onNodesRemoved (eventHandler) { this._onNodesRemoved = eventHandler return this } pauseObservation () { this._observer.disconnect() } resumeObservation () { this._observeNodes() } } class LocalStore { /** * @callback storeEventHandler * @param {Object} store */ /** * @param {string} scriptPrefix * @param {Object} defaults * @return {LocalStore} */ static createGlobalConfigStore (scriptPrefix, defaults) { return new LocalStore(scriptPrefix + 'globals', defaults) } static createPresetConfigStore (scriptPrefix, defaults) { return new LocalStore(scriptPrefix + 'presets', [ { name: 'default', config: defaults, }, ]) } /** * @param {string} key * @param {Object} defaults */ constructor (key, defaults) { /** * @type {string} * @private */ this._key = key /** * @type {Object} * @private */ this._store = {} /** * @type {string} * @private */ this._defaults = this._toJSON(defaults) /** * @type {storeEventHandler} */ this._onChange = null } /** * @param {string} json * @return {Object} * @private */ _fromJSON (json) { /** @type {{arrays: Object, objects: Object, properties: Object}} */ let parsedJSON = JSON.parse(json) let arrayObject = {} let store = {} for (let property in parsedJSON.arrays) { arrayObject = JSON.parse(parsedJSON.arrays[property]) store[property] = [] for (let key in arrayObject) { store[property].push(arrayObject[key]) } } for (let property in parsedJSON.objects) { store[property] = this._fromJSON(parsedJSON.objects[property]) } for (let property in parsedJSON.properties) { store[property] = parsedJSON.properties[property] } return store } /** * @return {string} * @private */ _getStore () { return window.localStorage.getItem(this._key) } /** * @return {Object} * @private */ _getDefaults () { return this._fromJSON(this._defaults) } /** * @param {Object} store * @return {string} * @private */ _toJSON (store) { let arrayToObject let json = {arrays: {}, objects: {}, properties: {}} for (let property in store) { if (typeof store[property] === 'object') { if (Array.isArray(store[property])) { arrayToObject = {} for (let key in store[property]) { arrayToObject[key] = store[property][key] } json.arrays[property] = JSON.stringify(arrayToObject) } else { json.objects[property] = this._toJSON(store[property]) } } else { json.properties[property] = store[property] } } return JSON.stringify(json) } _handleOnChange () { if (this._onChange !== null) { this._onChange(this._store) } } /** * @return {LocalStore} */ delete () { window.localStorage.removeItem(this._key) return this } /** * @param {string} filename */ exportToFile (filename) { let linkElement = document.createElement('a') let file = new Blob([this._toJSON(this._store)], {type: 'application/json'}) linkElement.href = URL.createObjectURL(file) linkElement.download = filename linkElement.click() URL.revokeObjectURL(linkElement.href) linkElement.remove() } /** * @return {*} */ get () { return this._store } importFromFile (file) { } /** * @return {boolean} */ isPurged () { return this._getStore() === null } /** * @param {storeEventHandler} handler * @return {LocalStore} */ onChange (handler) { this._onChange = handler return this } /** * @return {LocalStore} */ restoreDefaults () { this._store = this._getDefaults() this._handleOnChange() return this } /** * @return {LocalStore} */ retrieve () { let storedStore = this._getStore() if (storedStore === null) { this.restoreDefaults() } else { this._store = this._fromJSON(storedStore) } this._handleOnChange() return this } /** * @return {LocalStore} */ save () { window.localStorage.setItem(this._key, this._toJSON(this._store)) this._handleOnChange() return this } /** * @param {*} data * @return {LocalStore} */ update (data) { this._store = data return this.save() } } class SelectorGenerator { /** * @param {string} selectorPrefix */ constructor (selectorPrefix) { /** * @type {string} * @private */ this._prefix = selectorPrefix } /** * @param {string} selector * @return {string} */ getSelector (selector) { return this._prefix + selector } /** * @param {string} settingName * @return {string} */ getSettingsInputSelector (settingName) { return this.getSelector(Utilities.toKebabCase(settingName) + '-setting') } /** * @param {string} settingName * @param {boolean} getMinInputSelector * @return {string} */ getSettingsRangeInputSelector (settingName, getMinInputSelector) { return this.getSelector(Utilities.toKebabCase(settingName) + (getMinInputSelector ? '-min' : '-max') + '-setting') } /** * @param {string} statisticType * @return {string} */ getStatLabelSelector (statisticType) { return this.getSelector(Utilities.toKebabCase(statisticType) + '-stat') } } class StatisticsRecorder { /** * @param {string} selectorPrefix */ constructor (selectorPrefix) { /** * @type {SelectorGenerator} * @private */ this._selectorGenerator = new SelectorGenerator(selectorPrefix) /** * @type {{Total: number}} * @private */ this._statistics = {Total: 0} } /** * @param {string} statisticType * @param {boolean} validationResult * @param {number} value */ record (statisticType, validationResult, value = 1) { if (!validationResult) { if (typeof this._statistics[statisticType] !== 'undefined') { this._statistics[statisticType] += value } else { this._statistics[statisticType] = value } this._statistics.Total += value } } reset () { for (const statisticType in this._statistics) { this._statistics[statisticType] = 0 } } updateUI () { let label, labelSelector for (const statisticType in this._statistics) { labelSelector = this._selectorGenerator.getStatLabelSelector(statisticType) label = document.getElementById(labelSelector) if (label !== null) { label.textContent = this._statistics[statisticType] } } } } class Utilities { /** * @param {string[]} words * @return {RegExp} */ static buildWholeWordMatchingRegex (words) { let patternedWords = [] for (let i = 0; i < words.length; i++) { patternedWords.push('\\b' + words[i] + '\\b') } return new RegExp('(' + patternedWords.join('|') + ')', 'gi') } /** * @param milliseconds * @return {Promise<*>} */ static sleep (milliseconds) { return new Promise(resolve => setTimeout(resolve, milliseconds)) } /** * @param {string} text * @return {string} */ static toKebabCase (text) { return text.toLowerCase().replace(' ', '-') } } class Validator { static iFramesRemover () { GM_addStyle(' iframe { display: none !important; } ') } /** * @param {StatisticsRecorder} statisticsRecorder */ constructor (statisticsRecorder) { /** * @type {StatisticsRecorder} * @private */ this._statisticsRecorder = statisticsRecorder } /** * @param {string} text * @param {Object} rules * @return {string} */ sanitize (text, rules) { for (const substitute in rules) { text = text.replace(rules[substitute], substitute) } return text.trim() } /** * @param {JQuery} textNode * @param {Object} rules * @return {Validator} */ sanitizeTextNode (textNode, rules) { textNode.text(this.sanitize(textNode.text(), rules)) return this } /** * @param {string} selector * @param {Object} rules * @return {Validator} */ sanitizeNodeOfSelector (selector, rules) { let node = $(selector) if (node.length) { let sanitizedText = this.sanitize(node.text(), rules) node.text(sanitizedText) document.title = sanitizedText } return this } /** * @param {string} name * @param {JQuery} item * @param {string} selector * @return {boolean} */ validateNodeExistence (name, item, selector) { let validationCheck = item.find(selector).length > 0 this._statisticsRecorder.record(name, validationCheck) return validationCheck } /** * @param {string} name * @param {JQuery} item * @param {string} selector * @return {boolean} */ validateNodeNonExistence (name, item, selector) { let validationCheck = item.find(selector).length === 0 this._statisticsRecorder.record(name, validationCheck) return validationCheck } /** * @param {string} name * @param {number} value * @param {number[]} bounds * @return {boolean} */ validateRange (name, value, bounds) { let validationCheck = true if (bounds[0] > 0 && bounds[1] > 0) { validationCheck = value >= bounds[0] && value <= bounds[1] } else { if (bounds[0] > 0) { validationCheck = value >= bounds[0] } if (bounds[1] > 0) { validationCheck = value <= bounds[1] } } this._statisticsRecorder.record(name, validationCheck) return validationCheck } /** * @param {string} name * @param {number} lowerBound * @param {number} upperBound * @param getValueCallback * @return {boolean} */ validateRangeFilter (name, lowerBound, upperBound, getValueCallback) { if (lowerBound > 0 || upperBound > 0) { return this.validateRange(name, getValueCallback(), [lowerBound, upperBound]) } return true } /** * @param {string} text * @param {Object} rules * @param {string} key * @return {boolean} */ validateTextContains (text, rules, key) { let validationCheck = true if (rules) { this._statisticsRecorder.record(key, validationCheck = text.match(rules) !== null) } return validationCheck } /** * @param {string} text * @param {Object} rules * @param {string} key * @return {boolean} */ validateTextDoesNotContain (text, rules, key) { let validationCheck = true if (rules) { this._statisticsRecorder.record(key, validationCheck = text.match(rules) === null) } return validationCheck } }