您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Base class for search enhancement scripts
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.sleazyfork.org/scripts/416105/1114720/Brazen%20Base%20Search%20Enhancer.js
// ==UserScript== // @name Brazen Base Search Enhancer // @namespace brazenvoid // @version 3.2.1 // @author brazenvoid // @license GPL-3.0-only // @description Base class for search enhancement scripts // ==/UserScript== // Selectors const SELECTOR_BUTTON_DISABLE_FILTERS = 'brazen-disable-filters' const SELECTOR_BUTTON_SUBSCRIPTION_LOADER = 'brazen-subscriptions-loader' const SELECTOR_BUTTON_SYNC = '#brazen-sync' const SELECTOR_INPUT_RESTORE_CONFIG = '#brazen-restore-settings' // Preset filter configuration keys const CONFIG_PAGINATOR_LIMIT = 'Pagination Limit' const CONFIG_PAGINATOR_THRESHOLD = 'Pagination Threshold' const FILTER_DURATION_RANGE = 'Duration' const FILTER_PERCENTAGE_RATING_RANGE = 'Rating' const FILTER_SUBSCRIBED_VIDEOS = 'Hide Subscribed Videos' const FILTER_TEXT_BLACKLIST = 'Blacklist' const FILTER_TEXT_SEARCH = 'Search' const FILTER_TEXT_SANITIZATION = 'Text Sanitization Rules' const FILTER_TEXT_WHITELIST = 'Whitelist' const FILTER_UNRATED = 'Unrated' const FILTER_VIEWED_VIDEOS = 'Hide Browsed Videos' // const FILTER_WATCHED_VIDEOS = 'Hide Watched Videos' const STORE_SUBSCRIPTIONS = 'Account Subscriptions' // const STORE_VIEWED_ADDRESSES = 'Viewed Addresses Store' // const STORE_WATCHED_ADDRESSES = 'Watched Addresses Store' const UI_MARK_WATCHED_VIDEOS = 'Mark Watched Videos' // Item preset attributes const ITEM_ATTRIBUTE_DURATION = 'presetDurationRange' const ITEM_ATTRIBUTE_NAME = 'presetName' const ITEM_ATTRIBUTE_RATING = 'presetPercentageRating' const ITEM_ATTRIBUTE_USER = 'username' const ITEM_PROCESSED_ONCE = 'scriptItemProcessedOnce' // Configuration const OPTION_ALWAYS_SHOW_SETTINGS_PANE = 'Always Show Settings Pane' const OPTION_DISABLE_COMPLIANCE_VALIDATION = 'Disable All Filters' class BrazenItemAttributesResolver { /** * @callback ItemAttributesResolverCallback * @param {JQuery} item * @return {*} */ /** * @param {JQuery.Selector} itemPageLinkSelector * @param {JQuery.Selector} itemPageDeepAnalysisSelector */ constructor(itemPageLinkSelector, itemPageDeepAnalysisSelector) { /** * @type {{}} * @private */ this._attributes = {} /** * @type {{}} * @private */ this._deepAttributes = {} /** * @type {boolean} * @private */ this._hasDeepAttributes = false /** * @type {JQuery.Selector} * @protected */ this._itemPageLinkSelector = itemPageLinkSelector /** * @type {JQuery.Selector} * @protected */ this._itemPageDeepAnalysisSelector = itemPageDeepAnalysisSelector /** * @type {JQuery<HTMLElement> | jQuery | HTMLElement} * @private */ this._sandbox = $('<div id="brazen-item-attributes-resolver-sandbox" hidden/>').appendTo('body') } /** * @param {string} attributeName * @returns {string} * @private */ _generateAttributeName(attributeName) { return 'scriptItemAttribute' + attributeName[0].toUpperCase() + attributeName.slice(1) } /** * @param {string} name * @param {ItemAttributesResolverCallback} resolutionCallback * @returns {this} */ addAttribute(name, resolutionCallback) { this._attributes[name] = resolutionCallback return this } /** * @param {string} name * @param {ItemAttributesResolverCallback} resolutionCallback * @returns {this} */ addDeepAttribute(name, resolutionCallback) { this._deepAttributes[name] = resolutionCallback this._hasDeepAttributes = true return this } /** * @param {JQuery} item * @param {string} attributeName * @returns {*} */ getAttribute(item, attributeName) { return item[0][this._generateAttributeName(attributeName)] } /** * @param {JQuery} item * @param {Function|null} afterResolutionCallback */ resolveAttributes(item, afterResolutionCallback = null) { let element = item[0] for (const attributeName in this._attributes) { element[this._generateAttributeName(attributeName)] = this._attributes[attributeName](item) } if (this._hasDeepAttributes) { this._sandbox.load(item.find(this._itemPageLinkSelector).attr('href') + ' ' + this._itemPageDeepAnalysisSelector, () => { for (const attributeName in this._deepAttributes) { element[this._generateAttributeName(attributeName)] = this._deepAttributes[attributeName](this._sandbox) } this._sandbox.empty() }) } } } class BrazenBaseSearchEnhancer { /** * @typedef {{configKey: string, validate: SearchEnhancerFilterValidationCallback, comply: SearchEnhancerFilterComplianceCallback}} ComplianceFilter */ /** * @callback SearchEnhancerFilterValidationCallback * @param {*} configValues * @return boolean */ /** * @callback SearchEnhancerFilterComplianceCallback * @param {JQuery} item * @param {*} configValues * @return {*} */ /** * @callback SubscriptionsFilterExclusionsCallback * @return {boolean} */ /** * @return BrazenBaseSearchEnhancer */ static initialize() { BrazenBaseSearchEnhancer.throwOverrideError() } static throwOverrideError() { throw new Error('Method must be overridden.') } /** * @param {Object} configuration * @param {Object} configuration.about * @param {string} configuration.about.name * @param {string} configuration.about.link * @param {string} configuration.about.version * @param {string} configuration.about.released * @param {boolean} configuration.isUserLoggedIn * @param {JQuery.Selector} configuration.itemPageDeepAnalysisSelector * @param {JQuery.Selector} configuration.itemPageLinkSelector * @param {string} configuration.itemSelectors */ constructor(configuration) { /** * Array of item compliance filters ordered in intended sequence of execution * @type {ComplianceFilter[]} * @private */ this._complianceFilters = [] /** * @type {boolean} * @private */ this._isUserLoggedIn = configuration.isUserLoggedIn /** * @type {string} * @private */ this._itemClassesSelector = configuration.itemSelectors /** * Pagination manager * @type BrazenPaginator|null * @private */ this._paginator = null /** * @type {BrazenItemAttributesResolver} * @protected */ this._itemAttributesResolver = new BrazenItemAttributesResolver(configuration.itemPageLinkSelector, configuration.itemPageDeepAnalysisSelector) /** * @type {{name: string, link: string, version: string, released: string}} * @private */ this._scriptBrandingInformation = configuration.about /** * @type {BrazenSubscriptionsLoader} * @protected */ this._subscriptionsLoader = new BrazenSubscriptionsLoader( (status) => this._subscriptionsLoaderButton.text(status), (subscriptions) => { this._configurationManager.getField(STORE_SUBSCRIPTIONS).value = subscriptions.length ? '"' + subscriptions.join('""') + '"' : '' this._configurationManager.save() BrazenUIGenerator.addTransientChangeToButton( this._subscriptionsLoaderButton, 'btn-secondary', 'btn-success', 'Load Subscriptions', 'Subscriptions Loaded!') }) /** * @type {JQuery} * @protected */ this._subscriptionsLoaderButton = null /** * @type {JQuery<HTMLElement> | jQuery | HTMLElement} * @protected */ this._syncConfigButton = null /** * @type {BrazenUIGenerator} * @protected */ this._uiGen = new BrazenUIGenerator(configuration.about.name) /** * Local storage store with defaults * @type {BrazenConfigurationManager} * @protected */ this._configurationManager = BrazenConfigurationManager.create(this._uiGen) .addFlagField(OPTION_DISABLE_COMPLIANCE_VALIDATION, 'Disables all search filters.') .addFlagField(OPTION_ALWAYS_SHOW_SETTINGS_PANE, 'Always show configuration interface.') .onExternalConfigurationChange(() => this._syncConfigButton.prop('disable', false)) // 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} * @param {JQuery} item * @protected */ this._onFirstHitAfterCompliance = null /** * Operations to perform before compliance checks, the first time a item is retrieved * @type {Function} * @param {JQuery} item * @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} * @param {JQuery} item * @protected */ this._onItemHide = (item) => item.addClass('noncompliant-item').hide() /** * Logic to show compliant item * @type {Function} * @param {JQuery} item * @protected */ this._onItemShow = (item) => item.removeClass('noncompliant-item').show() /** * Must be used to compose the UI * @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 } /** * @param {JQuery} button * @private */ _onGlobalDisableClick(button) { if (button.hasClass('btn-danger')) { this._configurationManager.getField(OPTION_DISABLE_COMPLIANCE_VALIDATION).value = true button.removeClass('btn-danger').addClass('btn-success').text('Enable Filters') } else { this._configurationManager.getField(OPTION_DISABLE_COMPLIANCE_VALIDATION).value = false button.removeClass('btn-success').addClass('btn-danger').text('Disable Filters') } this._validateCompliance() } /** * @private */ _buildNativeUI() { // Shortcuts Menu // -- Add sync config button this._syncConfigButton = this._uiGen .createShortcutButton('Sync Config', 'warning', () => this._onResetConfig()) .attr('id', SELECTOR_BUTTON_SYNC.slice(1)) .prop('disabled', true) // -- Add global disable button let globalDisable = this._configurationManager.getField(OPTION_DISABLE_COMPLIANCE_VALIDATION) if (globalDisable.value) { this._uiGen.createShortcutButton('Enable Filters', 'success', (button) => this._onGlobalDisableClick(button)) } else { this._uiGen.createShortcutButton('Disable Filters', 'danger', (button) => this._onGlobalDisableClick(button)) } // Accordion Tabs // -- Add about tab this._uiGen.createAccordionTab('About', [ this._uiGen.createFormLabelGroup( 'Name', `<a href="${this._scriptBrandingInformation.link}" target="_blank">${this._scriptBrandingInformation.name}</a>`), this._uiGen.createFormLabelGroup( 'Version', this._scriptBrandingInformation.version), this._uiGen.createFormLabelGroup( 'Release Date', this._scriptBrandingInformation.released), this._uiGen.createFormLabelGroup( 'Author', '<a href="https://greasyfork.org/en/users/220654-brazenvoid" target="_blank">Brazenvoid</a>'), this._uiGen.createFormLabelGroup( 'Support', '<a href="https://www.patreon.com/brazen_scripts" target="_blank">Patreon</a>'), ]) // Actions Menu // -- Add Apply Config Button this._uiGen.createActionsButton('Apply', 'warning', () => { this._configurationManager.update() this._validateCompliance() }) // -- Add Save Config Button this._uiGen.createActionsButton('Save', 'success', () => { this._configurationManager.save() this._validateCompliance() }) // -- Add Reset Config Button this._uiGen.createActionsButton('Reset', 'danger', () => this._onResetConfig()) } /** * @param item * @private */ _complyItem(item) { let globalDisableState = this._configurationManager.getValue(OPTION_DISABLE_COMPLIANCE_VALIDATION) let globalBeforeComplianceValidation = Utilities.callEventHandler(this._onBeforeCompliance, [item], true) let itemComplies = true if (!globalDisableState && globalBeforeComplianceValidation && this._validateItemWhiteList(item)) { let configField for (let complianceFilter of this._complianceFilters) { configField = this._configurationManager.getFieldOrFail(complianceFilter.configKey) if (complianceFilter.validate(this._configurationManager.getValue(configField))) { itemComplies = complianceFilter.comply(item, this._configurationManager.getValue(configField)) if (!itemComplies) { this._configurationManager.trackState(configField) break } } } } itemComplies ? Utilities.callEventHandler(this._onItemShow, [item]) : Utilities.callEventHandler(this._onItemHide, [item]) item.css('opacity', 'unset') } /** * @param {string} configKey * @param {SearchEnhancerFilterValidationCallback|null} validationCallback * @param {SearchEnhancerFilterComplianceCallback} complianceCallback * @protected */ _addItemComplexComplianceFilter(configKey, validationCallback, complianceCallback) { this._addItemComplianceFilter(configKey, complianceCallback, validationCallback) } /** * @param {string} configKey * @param {SearchEnhancerFilterComplianceCallback|string} action * @param {SearchEnhancerFilterValidationCallback|null} validationCallback * @protected */ _addItemComplianceFilter(configKey, action, validationCallback = null) { let configType = this._configurationManager.getField(configKey).type if (typeof action === 'string') { let attributeName = action switch (configType) { case CONFIG_TYPE_FLAG: action = (item) => this.getItemAttribute(item, attributeName) break case CONFIG_TYPE_RANGE: action = (item, range) => Validator.isInRange(this.getItemAttribute(item, attributeName), range.minimum, range.maximum) break default: throw new Error('Associated config type requires explicit action callback definition.') } } if (validationCallback === null) { switch (configType) { case CONFIG_TYPE_FLAG: case CONFIG_TYPE_RADIOS_GROUP: case CONFIG_TYPE_SELECT: validationCallback = (value) => value break case CONFIG_TYPE_CHECKBOXES_GROUP: validationCallback = (valueKeys) => valueKeys.length break case CONFIG_TYPE_NUMBER: validationCallback = (value) => value > 0 break case CONFIG_TYPE_RANGE: validationCallback = (range) => range.minimum > 0 || range.maximum > 0 break case CONFIG_TYPE_RULESET: validationCallback = (rules) => rules.length break case CONFIG_TYPE_TEXT: validationCallback = (value) => value.length break default: throw new Error('Associated config type requires explicit validation callback definition.') } } this._complianceFilters.push({ configKey: configKey, validate: validationCallback, comply: action, }) } /** * @param {string} helpText * @protected */ _addItemBlacklistFilter(helpText) { this._configurationManager .addRulesetField(FILTER_TEXT_BLACKLIST, helpText) .onOptimize = (rules) => Utilities.buildWholeWordMatchingRegex(rules) ?? '' this._addItemComplexComplianceFilter( FILTER_TEXT_BLACKLIST, (value) => value.length, (item, value) => this.getItemAttribute(item, ITEM_ATTRIBUTE_NAME).match(value) === null, ) } /** * @param {string|null} helpText * @protected */ _addItemDurationRangeFilter(helpText = null) { this._configurationManager.addRangeField(FILTER_DURATION_RANGE, 'seconds', 0, 100000, helpText ?? 'Filter items by duration.') this._addItemComplianceFilter(FILTER_DURATION_RANGE, (item, range) => { let duration = this.getItemAttribute(item, ITEM_ATTRIBUTE_DURATION) return duration > 0 ? Validator.isInRange(duration, range.minimum, range.maximum) : duration === -1 }) } /** * @param {string|null} helpText * @param {string|null} unratedHelpText * @protected */ _addItemPercentageRatingRangeFilter(helpText = null, unratedHelpText = null) { this._configurationManager.addRangeField(FILTER_PERCENTAGE_RATING_RANGE, '%', 0, 100000, helpText ?? 'Filter items by percentage rating.') .addFlagField(FILTER_UNRATED, unratedHelpText ?? 'Hide items with zero or no rating.') this._addItemComplianceFilter(FILTER_PERCENTAGE_RATING_RANGE, (item, range) => { let rating = this.getItemAttribute(item, ITEM_ATTRIBUTE_RATING) return rating === 0 ? !this._configurationManager.getValue(FILTER_UNRATED) : Validator.isInRange(rating, range.minimum, range.maximum) }) } /** * @param {string} helpText * @protected */ _addItemTextSanitizationFilter(helpText) { let field = this._configurationManager.addRulesetField(FILTER_TEXT_SANITIZATION, helpText, false) field.onTranslateFromUI = (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 } field.onFormatForUI = (rules) => { let sanitizationRulesText = [] for (let substitute in rules) { sanitizationRulesText.push(substitute + '=' + rules[substitute].join(',')) } return sanitizationRulesText } field.onOptimize = (rules) => { let optimizedRules = {} for (const substitute in rules) { optimizedRules[substitute] = Utilities.buildWholeWordMatchingRegex(rules[substitute]) } return optimizedRules } } /** * @param {string|null} helpText * @protected */ _addItemTextSearchFilter(helpText = null) { this._configurationManager.addTextField(FILTER_TEXT_SEARCH, helpText ?? 'Show videos with these comma separated words in their names.') this._addItemComplianceFilter(FILTER_TEXT_SEARCH, (item, value) => this.getItemAttribute(item, ITEM_ATTRIBUTE_NAME).includes(value)) } // /** // * @protected // */ // _addItemCustomWatchedFilters () // { // this._configurationManager. // addFlagField(FILTER_VIEWED_VIDEOS, 'Tracks and hides all items present on opened pages on next page load. Should be purged regularly.'). // addFlagField(FILTER_WATCHED_VIDEOS, 'Tracks and hides 3,000 recent seen items.'). // addTextField(STORE_VIEWED_ADDRESSES, ''). // addTextField(STORE_WATCHED_ADDRESSES, '') // } /** * @param {string} helpText * @protected */ _addItemWhitelistFilter(helpText) { this._configurationManager .addRulesetField(FILTER_TEXT_WHITELIST, helpText) .onOptimize = (rules) => Utilities.buildWholeWordMatchingRegex(rules) } _addPaginationConfiguration() { this._configurationManager .addNumberField(CONFIG_PAGINATOR_LIMIT, 'pages', 1, 50, 'Limit paginator to concatenate the specified number of maximum pages.') .addNumberField(CONFIG_PAGINATOR_THRESHOLD, 'results', 1, 1000, 'Make paginator ensure the specified number of minimum results.') } /** * @param {SubscriptionsFilterExclusionsCallback} exclusionsCallback Add page exclusions here * @return {BrazenSubscriptionsLoader} * @protected */ _addSubscriptionsFilter(exclusionsCallback) { this._configurationManager .addFlagField(FILTER_SUBSCRIBED_VIDEOS, 'Hide videos from subscribed channels.') .addTextField(STORE_SUBSCRIPTIONS, 'Recorded subscription accounts.') this._addItemComplexComplianceFilter( FILTER_SUBSCRIBED_VIDEOS, (value) => value && this._isUserLoggedIn && exclusionsCallback(), (item) => { let username = this.getItemAttribute(item, ITEM_ATTRIBUTE_USER) return username === false ? true : !(new RegExp('"([^"]*' + username + '[^"]*)"')).test(this._configurationManager.getValue(STORE_SUBSCRIPTIONS)) }) return this._subscriptionsLoader } /** * 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.css('opacity', 0.5).each((index, element) => { let item = $(element) // First run processing if (typeof element[ITEM_PROCESSED_ONCE] === 'undefined') { element[ITEM_PROCESSED_ONCE] = false this._itemAttributesResolver.resolveAttributes(item) Utilities.callEventHandler(this._onFirstHitBeforeCompliance, [item]) } // Compliance filtering this._complyItem(item) // After first run processing if (!element[ITEM_PROCESSED_ONCE]) { Utilities.callEventHandler(this._onFirstHitAfterCompliance, [item]) element[ITEM_PROCESSED_ONCE] = true } }) this._configurationManager.updateTrackersInterface() } /** * @protected * @return {JQuery} */ _createPaginationTab() { return this._uiGen.createAccordionTab('Pagination', [ this._configurationManager.createElement(CONFIG_PAGINATOR_THRESHOLD), this._configurationManager.createElement(CONFIG_PAGINATOR_LIMIT), ]) } /** * @protected * @return {JQuery} */ _createSettingsBackupRestoreTab() { let inputControl = $(`<input id="${SELECTOR_INPUT_RESTORE_CONFIG}" class="form-control" type="text">`) return this._uiGen.createAccordionTab('Backup & Restore', [ this._uiGen.createFormHelpText( this._uiGen.createFormGroup( this._uiGen .createButton('Backup Configuration', 'secondary', (button) => this._onBackupSettings(button)) .addClass('btn-block'), ), null, 'Backup settings to the clipboard.', ), this._uiGen.createFormHelpText(this._uiGen.createFormGroup(inputControl), inputControl, 'Paste backed up configuration.'), this._uiGen.createFormHelpText( this._uiGen.createFormGroup( this._uiGen .createButton('Restore Configuration', 'danger', (button) => this._onRestoreSettings(button)) .addClass('btn-block'), ), null, 'Restore backup settings.', ), ]) } /** * @protected * @return {JQuery} */ _createSubscriptionLoaderTab() { this._subscriptionsLoaderButton = this._uiGen.createButton('Load Subscriptions', 'secondary', (button) => { if (this._isUserLoggedIn) { this._subscriptionsLoaderButton.removeClass('btn-secondary').addClass('btn-warning') this._subscriptionsLoader.run() } else { BrazenUIGenerator.addTransientChangeToButton( button, 'btn-secondary', 'btn-danger', 'Load Subscriptions', 'Please login before proceeding.') } }).attr('id', SELECTOR_BUTTON_SUBSCRIPTION_LOADER) return this._uiGen.createFormHelpText( this._uiGen.createFormGroup(this._subscriptionsLoaderButton), null, 'Makes a copy of your subscriptions in cache for related filters.') } /** * @param {JQuery} button * @private */ _onBackupSettings(button) { navigator.clipboard .writeText(this._configurationManager.backup()) .then(() => BrazenUIGenerator.addTransientChangeToButton( button, 'btn-secondary', 'btn-success', 'Backup Configuration', 'Config Copied To Clipboard!')) .catch(() => button.removeClass('btn-secondary').addClass('btn-danger').text('Error!')) } /** * @private */ _onResetConfig() { this._configurationManager.revertChanges() this._validateCompliance() } /** * @param {JQuery} button * @return {boolean} * @private */ _onRestoreSettings(button) { let settings = $('#' + SELECTOR_INPUT_RESTORE_CONFIG).val().trim() if (!settings) { BrazenUIGenerator.addTransientContentChangeToButton(button, 'Restore Configuration', 'No Configuration Found!') } else { try { this._configurationManager.restore(settings) BrazenUIGenerator.addTransientChangeToButton( button, 'btn-danger', 'btn-success', 'Restore Configuration', 'Configuration Restored!') this._validateCompliance() } catch (e) { button.text('Error!') return false } } return true } /** * @protected */ _setupUI() { this.constructor.throwOverrideError() } /** * @protected */ _setupAttributes() { this.constructor.throwOverrideError() } /** * @protected */ _setupCompliance() { this.constructor.throwOverrideError() } /** * @protected */ _setupConfiguration() { this.constructor.throwOverrideError() } /** * @protected */ _setupComplianceFilters() { this.constructor.throwOverrideError() } /** * @param {boolean} firstRun * @protected */ _validateCompliance(firstRun = false) { let itemLists = Utilities.callEventHandler(this._onGetItemLists) if (!firstRun) { this._configurationManager.resetTrackers() itemLists.each((index, itemsList) => { this._complyItemsList($(itemsList)) }) } else { itemLists.each((index, itemList) => { let itemListObject = $(itemList) if (this._paginator && itemListObject.is(this._paginator.getListSelector())) { ChildObserver.create().onNodesAdded((itemsAdded) => { this._complyItemsList($(itemsAdded), true) this._paginator.run(this._configurationManager.getValue(CONFIG_PAGINATOR_THRESHOLD), this._configurationManager.getValue(CONFIG_PAGINATOR_LIMIT)) }).observe(itemList) } else { ChildObserver.create().onNodesAdded((itemsAdded) => this._complyItemsList($(itemsAdded), true)).observe(itemList) } this._complyItemsList(itemListObject) }) } if (this._paginator) { this._paginator.run(this._configurationManager.getValue(CONFIG_PAGINATOR_THRESHOLD), this._configurationManager.getValue(CONFIG_PAGINATOR_LIMIT)) } } /** * @param {JQuery} item * @return {boolean} * @protected */ _validateItemWhiteList(item) { let field = this._configurationManager.getField(FILTER_TEXT_WHITELIST) if (field) { let validationResult = field.value.length ? Validator.regexMatches(this.getItemAttribute(item, ITEM_ATTRIBUTE_NAME), field.optimized) : true if (!validationResult) { this._configurationManager.trackState(field) } return validationResult } return true } /** * @param {JQuery} item * @param {string} attributeName * @returns {*} */ getItemAttribute(item, attributeName) { return this._itemAttributesResolver.getAttribute(item, attributeName) } /** * Initialize the script and do basic UI removals */ initialize() { if (Utilities.callEventHandler(this._onValidateInit)) { this._setupConfiguration() this._configurationManager.initialize(this._scriptBrandingInformation.name) this._setupAttributes() this._setupCompliance() this._setupComplianceFilters() this._setupUI() // Paginator if (this._paginator) { this._paginator.initialize() } // UI generation Utilities.callEventHandler(this._onBeforeUIBuild) Utilities.callEventHandler(this._onUIBuild) this._buildNativeUI() this._uiGen.initialize() Utilities.callEventHandler(this._onAfterUIBuild) this._configurationManager.updateInterface() // First run this._validateCompliance(true) Utilities.callEventHandler(this._onAfterInitialization) } } /** * @returns {boolean} */ isUserLoggedIn() { return this._isUserLoggedIn } }