您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Base library for my scripts
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/375557/658367/Brazenvoid%27s%20Base%20Resource.js
// ==UserScript== // @name Brazenvoid's Base Resource // @namespace brazenvoid // @version 1.3.0 // @author brazenvoid // @license GPL-3.0-only // @description Base library for my scripts // @run-at document-end // ==/UserScript== const sleep = (milliseconds) => { return new Promise(resolve => setTimeout(resolve, milliseconds)) } class CaseConverters { toCamel (text) { return text.replace(/(?:^\w|[A-Z]|\b\w)/g, function (letter, index) { return index === 0 ? letter.toLowerCase() : letter.toUpperCase() }).replace(/\s+/g, '') } toKebab (text) { return text.toLowerCase().replace(' ', '-') } toKebabFromSnake (text) { return text.replace('_', '-') } toNormalFromKebab (text) { return text.replace('-', ' ') } toNormalFromSnake (text) { return text.replace('_', ' ') } toSnake (text) { return text.toLowerCase().replace(' ', '_') } } class ChildObserver { constructor (handler) { this.config = { attributes: false, childList: true, subtree: false } this.handler = handler this.observer = new MutationObserver(function (mutations) { for (let mutation of mutations) { handler(mutation.target) } }) } observe (node, doInitialRun = false) { if (doInitialRun) { this.handler(node) } this.observer.observe(node, this.config) return this } } class Filters { constructor (statisticsRecorder) { this.blacklist = [] this.sanitizationRules = [] this._optimizedBlacklist = [] this._optimizedSanitizationRules = {} this._statisticsRecorder = statisticsRecorder } init () { for (let i = 0; i < this.blacklist.length; i++) { this._optimizedBlacklist[i] = new RegExp(this.blacklist[i], 'ig') } for (const substitute in this.sanitizationRules) { this._optimizedSanitizationRules[substitute] = [] for (let i = 0; i < this.sanitizationRules[substitute].length; i++) { this._optimizedSanitizationRules[substitute][i] = new RegExp(this.sanitizationRules[substitute][i], 'ig') } } } iFramesRemover () { GM_addStyle(` iframe { display: none !important; } `) } sanitize (text) { for (const substitute in this._optimizedSanitizationRules) { for (const subject of this._optimizedSanitizationRules[substitute]) { text = text.replace(subject, substitute) } } return text } validateBlackList (text) { let validationCheck = true if (this._optimizedBlacklist.length > 0) { for (const blacklistedWord of this._optimizedBlacklist) { validationCheck = text.match(blacklistedWord) === null if (!validationCheck) { break } } this._statisticsRecorder.record('Blacklist', validationCheck) } return validationCheck } validateRange (type, value, bounds) { let validationCheck = true if (bounds[0] > 0) { validationCheck = value >= bounds[0] } if (bounds[1] > 0) { validationCheck = value <= bounds[1] } this._statisticsRecorder.record(type, validationCheck) return validationCheck } } class LocalStore { constructor (key, defaultStore) { this.onDefaultsLoaded = null this.onRetrieval = null this.onUpdated = null this._key = key this._store = {} this._defaultStore = this._toJSON(defaultStore) } _fromJSON (json) { let arrayObject = {} let parsedJSON = JSON.parse(json) 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 } _toJSON (store) { let arrayToObject = {} let json = {arrays: {}, objects: {}, properties: {}} for (let property in store) { if (typeof store[property] === 'object') { if (Array.isArray(store[property])) { 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) } delete () { window.localStorage.removeItem(this._key) return this } get () { return this._store } restoreDefaults () { this._store = this._fromJSON(this._defaultStore) if (this.onDefaultsLoaded !== null) { this.onDefaultsLoaded(this._store) } return this } retrieve () { let storedStore = window.localStorage.getItem(this._key) if (storedStore === null) { this.restoreDefaults() } else { this._store = this._fromJSON(storedStore) } if (this.onRetrieval !== null) { this.onRetrieval(this._store) } return this } save () { window.localStorage.setItem(this._key, this._toJSON(this._store)) if (this.onUpdated !== null) { this.onUpdated(this._store) } return this } } class Logger { constructor (enableDebugging) { this.enableDebugging = enableDebugging } _log (message) { if (this.enableDebugging) { console.log(message) } } logTaskCompletion (task) { this._log('Completed: ' + task) this.logSeparator() } logSeparator () { this._log('------------------------------------------------------------------------------------') } logValidation (filterName, validationResult = null) { this._log('Satisfies ' + filterName + ' Filter: ' + (validationResult ? 'true' : 'false')) } logVideoCheck (videoName) { this._log('Checking Video: ' + videoName) } } class SelectorGenerator { constructor (selectorPrefix) { this.prefix = selectorPrefix } getSelector (selector) { return this.prefix + selector }; getSelectorFromName (name) { return CaseConverters.prototype.toKebab(name) } getSettingsInputSelector (settingName) { return this.getSelector(this.getSelectorFromName(settingName) + '-setting') } getStatLabelSelector (statisticType) { return this.getSelector(this.getSelectorFromName(statisticType) + '-stat') }; } class StatisticsRecorder { constructor (logger, selectorGenerator) { this.logger = logger this.selectorGenerator = selectorGenerator this.statistics = {Total: 0} } record (statisticType, validationResult, value = 1, log = true) { if (!validationResult) { if (typeof this.statistics[statisticType] !== 'undefined') { this.statistics[statisticType] += value } else { this.statistics[statisticType] = value } this.statistics.Total += value } if (log) { this.logger.logValidation(statisticType, validationResult) } } 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 UIGenerator { constructor (showUI, selectorGenerator) { this.selectorGenerator = selectorGenerator this.showUI = showUI } appendToBody (node) { let bodyTag = document.getElementsByTagName('body')[0] bodyTag.appendChild(node) } createFormButton (caption, onClick) { let button = document.createElement('button') button.textContent = caption button.style.height = '30px' button.style.width = '100%' button.addEventListener('click', onClick) return button } createFormGroup () { let divFormGroup = document.createElement('div') divFormGroup.style.display = 'block' divFormGroup.style.height = '18px' divFormGroup.style.marginBottom = '2px' divFormGroup.style.padding = '5px 0' return divFormGroup } createFormGroupLabel (label, inputID = null) { let labelFormGroup = document.createElement('label') labelFormGroup.style.float = 'left' labelFormGroup.style.padding = '2px 0' labelFormGroup.textContent = label + ': ' if (inputID !== null) { labelFormGroup.setAttribute('for', inputID) } return labelFormGroup } createFormGroupStatLabel (statisticType) { let labelFormGroup = document.createElement('label') labelFormGroup.id = this.selectorGenerator.getStatLabelSelector(statisticType) labelFormGroup.style.float = 'right' labelFormGroup.style.padding = '2px 0' labelFormGroup.textContent = '0' return labelFormGroup } createFormGroupInput (id, defaultValue) { let inputFormGroup = document.createElement('input') inputFormGroup.id = id inputFormGroup.style.float = 'right' inputFormGroup.style.width = '100px' inputFormGroup.style.textAlign = 'center' if (defaultValue !== null) { inputFormGroup.value = defaultValue } return inputFormGroup } createSection (id, backgroundColor, width, children) { let section = document.createElement('section') section.id = this.selectorGenerator.getSelector(id) section.style.color = 'black' section.style.display = this.showUI ? 'block' : 'none' section.style.fontSize = '12px' section.style.fontWeight = 'bold' section.style.position = 'fixed' section.style.top = '250px' section.style.left = '0' section.style.width = width section.style.padding = '5px 10px' section.style.backgroundColor = backgroundColor section.style.zIndex = '1000' for (let child of children) { section.appendChild(child) } return section } createSeparator () { let separator = document.createElement('hr') separator.style.margin = '3px' return separator } createSettingsFormGroup (label, defaultValue = null) { let divFormGroup = this.createFormGroup() let inputID = this.selectorGenerator.getSettingsInputSelector(label) let labelFormGroup = this.createFormGroupLabel(label, inputID) let inputFormGroup = this.createFormGroupInput(inputID, defaultValue) divFormGroup.appendChild(labelFormGroup) divFormGroup.appendChild(inputFormGroup) return divFormGroup } createSettingShowButton (caption, settingSection, fixed = true) { let controlButton = document.createElement('button') controlButton.textContent = caption if (fixed) { controlButton.style.backgroundColor = '#ffa31a' controlButton.style.border = '0' controlButton.style.color = 'black' controlButton.style.fontSize = '14px' controlButton.style.left = '0' controlButton.style.padding = '15px 0px' controlButton.style.position = 'fixed' controlButton.style.writingMode = 'sideways-lr' controlButton.style.top = '250px' controlButton.style.width = '30px' controlButton.style.zIndex = '999' } else { controlButton.style.width = '100%' controlButton.style.margin = '2px 5px' controlButton.style.padding = '2px 5px' controlButton.style.backgroundColor = '#ffa31a' controlButton.style.border = '0' } controlButton.addEventListener('click', function () { let settingsUI = document.getElementById(settingSection.id) settingsUI.style.display = settingsUI.style.display === 'none' ? 'block' : 'none' }) return controlButton } createStatisticsFormGroup (statisticsType, label = null) { if (label === null) { label = statisticsType } let divFormGroup = this.createFormGroup() let labelFormGroup = this.createFormGroupLabel('Filtered ' + label + ' Videos') let statLabelFormGroup = this.createFormGroupStatLabel(statisticsType) divFormGroup.appendChild(labelFormGroup) divFormGroup.appendChild(statLabelFormGroup) return divFormGroup } createStoreUpdateButton (localStore) { return this.createFormButton('Update Store', function () { localStore.save() }) } createStoreReloadButton (localStore) { return this.createFormButton('Reload Store', function () { localStore.retrieve() }) } createStoreResetButton (localStore) { return this.createFormButton('Load Defaults', function () { localStore.restoreDefaults() }) } createStoreDeleteButton (localStore) { return this.createFormButton('Delete Store', function () { localStore.delete() }) } }