您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Base class for search enhancement scripts
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.sleazyfork.org/scripts/416105/879317/Brazen%20Base%20Search%20Enhancer.js
// ==UserScript== // @name Brazen Base Search Enhancer // @namespace brazen // @version 2.1.3 // @author brazenvoid // @license GPL-3.0-only // @description Base class for search enhancement scripts // ==/UserScript== const CONFIG_PAGINATOR_LIMIT = 'Pagination Limit' const CONFIG_PAGINATOR_THRESHOLD = 'Pagination Threshold' const OPTION_ALWAYS_SHOW_SETTINGS_PANE = 'Always Show Settings Pane' const OPTION_DISABLE_COMPLIANCE_VALIDATION = 'Disable Item Compliance Validation' const FILTER_TEXT_BLACKLIST = 'Blacklist' const FILTER_TEXT_SEARCH = 'Search' const FILTER_TEXT_SANITIZATION = 'Text Sanitization Rules' const FILTER_TEXT_WHITELIST = 'Whitelist' class BrazenBaseSearchEnhancer { /** * @return BrazenBaseSearchEnhancer */ static initialize () { BrazenBaseSearchEnhancer.throwOverrideError() } static throwOverrideError () { throw new Error('Method must be overridden.') } /** * @param {string} scriptPrefix * @param {string|string[]} itemClasses * @param {boolean} enablePaginator */ constructor (scriptPrefix, itemClasses, enablePaginator = false) { /** * Array of item compliance filters ordered in intended sequence of execution * @type {Function[]} * @protected */ this._complianceFilters = [] /** * @type {string[]} * @protected */ this._itemClasses = Array.isArray(itemClasses) ? itemClasses : [itemClasses] /** * @type {string} * @protected */ this._itemClassesSelector = '.' + this._itemClasses.join(',.') /** * Pagination manager * @type Paginator|null * @protected */ this._paginator = null /** * @type {string} * @protected */ this._scriptPrefix = scriptPrefix /** * @type {StatisticsRecorder} * @protected */ this._statistics = new StatisticsRecorder(this._scriptPrefix) /** * @type {BrazenUIGenerator} * @protected */ this._uiGen = new BrazenUIGenerator(this._scriptPrefix) /** * @type {Validator} * @protected */ this._validator = (new Validator(this._statistics)) /** * Local storage store with defaults * @type {ConfigurationManager} * @protected */ this._configurationManager = ConfigurationManager.create(this._uiGen). addFlagField(OPTION_DISABLE_COMPLIANCE_VALIDATION, 'Disables all search filters.'). addFlagField(OPTION_ALWAYS_SHOW_SETTINGS_PANE, 'Always show configuration interface.'). addNumberField(CONFIG_PAGINATOR_LIMIT, 0, 50, 'Limit paginator to concatenate the specified number of maximum pages. Zero equates to infinite.'). addNumberField(CONFIG_PAGINATOR_THRESHOLD, 10, 1000, 'Make paginator ensure the specified number of minimum results.'). addRulesetField(FILTER_TEXT_BLACKLIST, '', null, null, (rules) => Utilities.buildWholeWordMatchingRegex(rules)). addRulesetField(FILTER_TEXT_SANITIZATION, '', (rules) => { let sanitizationRulesText = [] for (let substitute in rules) { sanitizationRulesText.push(substitute + '=' + rules[substitute].join(',')) } return sanitizationRulesText }, (rules) => { let sanitizationRules = {}, fragments, validatedTargetWords for (let sanitizationRule of rules) { if (sanitizationRule.includes('=')) { fragments = sanitizationRule.split('=') if (fragments[0] === '') { fragments[0] = ' ' } validatedTargetWords = Utilities.trimAndKeepNonEmptyStrings(fragments[1].split(',')) if (validatedTargetWords.length) { sanitizationRules[fragments[0]] = validatedTargetWords } } } return sanitizationRules }, (rules) => { let optimizedRules = {} for (const substitute in rules) { optimizedRules[substitute] = Utilities.buildWholeWordMatchingRegex(rules[substitute]) } return optimizedRules }). addRulesetField(FILTER_TEXT_WHITELIST, '', null, null, (rules) => Utilities.buildWholeWordMatchingRegex(rules)). addTextField(FILTER_TEXT_SEARCH, 'Show videos with these comma separated words in their names.') // Events /** * Operations to perform after script initialization * @type {Function} * @protected */ this._onAfterInitialization = null /** * Operations to perform after UI generation * @type {Function} * @protected */ this._onAfterUIBuild = null /** * Operations to perform before compliance validation. This callback can also be used to skip compliance validation by returning false. * @type {null} * @protected */ this._onBeforeCompliance = null /** * Operations to perform before UI generation * @type {Function} * @protected */ this._onBeforeUIBuild = null /** * Operations to perform after compliance checks, the first time a item is retrieved * @type {Function} * @protected */ this._onFirstHitAfterCompliance = null /** * Operations to perform before compliance checks, the first time a item is retrieved * @type {Function} * @protected */ this._onFirstHitBeforeCompliance = null /** * Get item lists from the page * @type {Function} * @protected */ this._onGetItemLists = null /** * Logic to hide a non-compliant item * @type {Function} * @protected */ this._onItemHide = (item) => item.hide(100) /** * Logic to show compliant item * @type {Function} * @protected */ this._onItemShow = (item) => item.show(100) /** * Must return the generated settings section node * @type {Function} * @protected */ this._onUIBuild = null /** * Validate initiating initialization. * Can be used to stop script init on specific pages or vice versa * @type {Function} * @protected */ this._onValidateInit = () => true } /** * Filters items as per settings * @param {JQuery} itemsList * @param {boolean} fromObserver * @protected */ _complyItemsList (itemsList, fromObserver = false) { let items = fromObserver ? itemsList.filter(this._itemClassesSelector) : itemsList.find(this._itemClassesSelector) items.each((index, element) => { let item = $(element) if (typeof element['scriptProcessedOnce'] === 'undefined') { element.scriptProcessedOnce = false Utilities.callEventHandler(this._onFirstHitBeforeCompliance, [item]) } this._validateItemCompliance(item) if (!element['scriptProcessedOnce']) { Utilities.callEventHandler(this._onFirstHitAfterCompliance, [item]) element.scriptProcessedOnce = true } this._statistics.updateUI() }) } /** * @protected */ _createSettingsFormActions () { return this._uiGen.createFormSection().append([ this._uiGen.createFormActions([ this._uiGen.createFormButton('Apply', () => this._onApplyNewSettings(), 'Apply settings.'), this._uiGen.createFormButton('Save', () => this._onSaveSettings(), 'Apply and update saved configuration.'), this._uiGen.createFormButton('Reset', () => this._onResetSettings(), 'Revert to saved configuration.'), ]), ]) } /** * @param {JQuery} UISection * @private */ _embedUI (UISection) { UISection.on('mouseleave', (event) => { if (!this._configurationManager.getValue(OPTION_ALWAYS_SHOW_SETTINGS_PANE)) { $(event.currentTarget).hide(300) } }) if (this._configurationManager.getValue(OPTION_ALWAYS_SHOW_SETTINGS_PANE)) { UISection.show() } this._uiGen.constructor.appendToBody(UISection) this._uiGen.constructor.appendToBody(this._uiGen.createSettingsShowButton('', UISection)) } _onApplyNewSettings () { this._configurationManager.update() this._validateCompliance() } _onResetSettings () { this._configurationManager.revertChanges() this._validateCompliance() } _onSaveSettings () { this._onApplyNewSettings() this._configurationManager.save() } /** * @param {boolean} firstRun * @protected */ _validateCompliance (firstRun = false) { let itemLists = Utilities.callEventHandler(this._onGetItemLists) if (!firstRun) { this._statistics.reset() itemLists.each((index, itemsList) => { this._complyItemsList($(itemsList)) }) } else { itemLists.each((index, itemList) => { ChildObserver.create().onNodesAdded((itemsAdded) => this._complyItemsList($(itemsAdded), true)).observe(itemList) this._complyItemsList($(itemList)) }) } if (this._paginator) { this._paginator.run(this._configurationManager.getValue(CONFIG_PAGINATOR_THRESHOLD), this._configurationManager.getValue(CONFIG_PAGINATOR_LIMIT)) } } /** * @param {JQuery} item * @protected */ _validateItemCompliance (item) { let itemComplies = true if (!this._configurationManager.getValue(OPTION_DISABLE_COMPLIANCE_VALIDATION) && Utilities.callEventHandler(this._onBeforeCompliance, [item]) !== false) { for (let complianceFilter of this._complianceFilters) { if (!complianceFilter(item)) { itemComplies = false break } } } itemComplies ? Utilities.callEventHandler(this._onItemShow, [item]) : Utilities.callEventHandler(this._onItemHide, [item]) } /** * Initialize the script and do basic UI removals */ init () { if (Utilities.callEventHandler(this._onValidateInit)) { this._configurationManager.initialize(this._scriptPrefix) if (this._paginator) { this._paginator.initialize() } Utilities.callEventHandler(this._onBeforeUIBuild) this._embedUI(Utilities.callEventHandler(this._onUIBuild)) Utilities.callEventHandler(this._onAfterUIBuild) this._configurationManager.updateInterface() this._validateCompliance(true) this._uiGen.updateStatus('Initial run completed.') Utilities.callEventHandler(this._onAfterInitialization) } } }