您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Helper methods to generate a control panel UI for scripts
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.sleazyfork.org/scripts/416104/1582893/Brazen%20UI%20Generator.js
// ==UserScript== // @name Brazen UI Generator // @namespace brazenvoid // @version 2.1.1 // @author brazenvoid // @license GPL-3.0-only // @description Helper methods to generate a control panel UI for scripts // @grant GM_addStyle // ==/UserScript== /** * @function GM_addStyle * @param {string} style */ GM_addStyle( `@keyframes brazen-fade{from{opacity:0}to{opacity:1}}#restore-settings.bv-input{margin-bottom:1rem}#settings-wrapper{bottom:5vh;overflow:auto;resize:horizontal;top:5vh;z-index:1001}.show-settings.bv-section{top:5vh;box-shadow:0 0 20px white;padding:8px;border-radius:5px;border:2px solid white}.bv-actions{display:inline-flex;justify-content:center;padding:0 0.25rem;text-align:center}.bv-actions .bv-button{width:auto}.bv-bg-colour{background-color:#4f535b}.bv-border-primary{border:1px solid black}.bv-break{margin:0.5rem 0}.bv-button{background-color:revert;padding:0.5rem 1rem;width:100%}.bv-flex-column{flex-direction:column}.bv-font-primary{color:white}.bv-font-secondary{color:black}.bv-group{align-items:center;display:flex;min-height:20px}.bv-group + .bv-group{margin-top:1rem}.bv-group.bv-range-group,.bv-group.bv-text-group{align-items:center}.bv-group.bv-range-group > input{width:75px}.bv-group.bv-range-group > input + input{margin-left:5px}.bv-group.bv-textarea-group{align-items:start;flex-direction:column;overflow:hidden}.bv-group.bv-textarea-group > textarea.bv-input{margin-top:0.5rem;resize:vertical;width:100%}input.bv-input,select.bv-input,textarea.bv-input{box-sizing:border-box;margin:0;padding:0.5rem}.bv-input.bv-checkbox-radio{margin-right:5px;scale:2}.bv-input.bv-text{width:100%}.bv-label{flex-grow:1;text-align:start}.bv-label.bv-text + .bv-input.bv-text{width:40%}.bv-section{display:flex;flex-direction:column;font-family:"roboto";font-size:1rem;font-weight:normal;left:0;padding:1rem;position:fixed;z-index:1000}.bv-section > div + div{margin-top:1rem}.bv-section hr{border:1px solid white;margin:1rem 0}.bv-section button + button{margin-left:0.25rem}.bv-section .bv-title{display:block;height:20px;margin-bottom:1rem;text-align:center;width:100%}.bv-show-settings{border:0;font-size:0.7rem;height:90vh;left:0;margin:0;padding:0;position:fixed;top:5vh;width:0.2vw;writing-mode:sideways-lr;z-index:999}.bv-show-settings .bv-title{display:block;height:20px;width:100%}.bv-tab-button{background-color:inherit;border-bottom:0;border-top-left-radius:3px;border-top-right-radius:3px;cursor:pointer;outline:none;padding:0.5rem 0.75rem;transition:0.3s}.bv-tab-button.bv-active,.bv-tab-button:hover{color:black;background-color:white}.bv-tab-panel{animation:brazen-fade 1s;display:none;flex-direction:column;padding:1rem}.bv-tab-panel.bv-active{display:flex}.bv-tabs-nav{display:flex;flex-wrap:wrap;overflow:hidden}`) class BrazenUIGenerator { /** * @param {string} selectorPrefix */ constructor(selectorPrefix) { /** * @type {JQuery} * @private */ this._section = null /** * @type {SelectorGenerator} * @private */ this._selectorGenerator = new SelectorGenerator(selectorPrefix) /** * @type {string} * @private */ this._selectorPrefix = selectorPrefix /** * @type {JQuery} * @private */ this._statusLine = null /** * @type {string} * @private */ this._statusText = '' } /** * @param {JQuery} nodes */ static appendToBody(nodes) { $('body').append(nodes) } /** * @param {JQuery} node * @param {string} text * @return {JQuery} * @private */ _addHelpTextOnHover(node, text) { if (text !== '') { node.on('mouseover', () => this.updateStatus(text, true)) node.on('mouseout', () => this.resetStatus()) } return node } /** * @return {JQuery} */ createBreakSeparator() { return $('<br class="bv-break"/>') } /** * @return {JQuery} */ createContainer() { this._section = $('<section class="bv-section bv-font-primary">') return this._section } /** * @param {JQuery|JQuery[]} children * @param {string} wrapperClasses * @return {JQuery} */ createFormActions(children, wrapperClasses = '') { return $('<div class="bv-actions"/>').addClass(wrapperClasses).append(children) } /** * @param {string} caption * @param {JQuery.EventHandler} onClick * @param {string} hoverHelp * @return {JQuery} */ createFormButton(caption, hoverHelp, onClick) { let button = $('<button class="bv-button">').text(caption).on('click', onClick) return this._addHelpTextOnHover(button, hoverHelp) } createFormCheckBoxesGroupSection(label, keyValuePairs, hoverHelp) { let section = this.createFormSection(label).addClass('bv-checkboxes-group') for (const element of keyValuePairs) { section.append( this.createFormGroup().append([ this.createFormGroupLabel(element[0], 'checkbox'), this.createFormGroupInput('checkbox').attr('data-value', element[1]), ]), ) } return this._addHelpTextOnHover(section, hoverHelp) } /** * @return {JQuery} */ createFormGroup() { return $('<div class="bv-group"/>') } /** * @param {string} id * @param {Array} keyValuePairs * * @return {JQuery} */ createFormGroupDropdown(id, keyValuePairs) { let dropdown = $('<select>').attr('id', id).addClass('bv-input') for (let i = 0; i < keyValuePairs.length; i++) { dropdown.append($('<option>').attr('value', keyValuePairs[i][0]).text(keyValuePairs[i][1]).prop('selected', (i === 0))) } return dropdown } /** * @param {string} type * * @return {JQuery} */ createFormGroupInput(type) { let input = $('<input class="bv-input">').attr('type', type) switch (type) { case 'number': case 'text': input.addClass('bv-text') break case 'radio': case 'checkbox': input.addClass('bv-checkbox-radio') break } return input } /** * @param {string} label * @param {string} inputType * @return {JQuery} */ createFormGroupLabel(label, inputType = '') { let labelFormGroup = $('<label class="bv-label">').text(label) if (inputType !== '') { switch (inputType) { case 'number': case 'text': labelFormGroup.addClass('bv-text') labelFormGroup.text(labelFormGroup.text() + ': ') break case 'radio': case 'checkbox': labelFormGroup.addClass('bv-checkbox-radio') break } } return labelFormGroup } /** * @param {string} statisticType * @return {JQuery} */ createFormGroupStatLabel(statisticType) { return $('<label class="bv-stat-label">').attr('id', this._selectorGenerator.getStatLabelSelector(statisticType)).text('0') } /** * @param {string} label * @param {string} inputType * @param {string} hoverHelp * @return {JQuery} */ createFormInputGroup(label, inputType, hoverHelp = '') { return this._addHelpTextOnHover( this.createFormGroup().append([ this.createFormGroupLabel(label, inputType), this.createFormGroupInput(inputType), ]), hoverHelp, ) } createFormRadiosGroupSection(label, keyValuePairs, hoverHelp) { let section = this.createFormSection(label).addClass('bv-radios-group') for (let i = 0; i < keyValuePairs.length; i++) { section.append( this.createFormGroup().append([ this.createFormGroupLabel(keyValuePairs[i][0], 'radio'), this.createFormGroupInput('radio').prop('checked', i === 0).attr('data-value', keyValuePairs[i][1]).on('change', (event) => { $(event.currentTarget).parents('.bv-radios-group').first().find('input').each((index, element) => { if (!element.isSameNode(event.currentTarget)) { $(element).prop('checked', false) } }) }), ]), ) } return this._addHelpTextOnHover(section, hoverHelp) } /** * @param {string} label * @param {string} inputsType * @param {number} minimum * @param {number} maximum * @param {string} hoverHelp * @return {JQuery} */ createFormRangeInputGroup(label, inputsType, minimum, maximum, hoverHelp) { return this._addHelpTextOnHover( this.createFormGroup().addClass('bv-range-group').append([ this.createFormGroupLabel(label, inputsType), this.createFormGroupInput(inputsType).attr('min', minimum).attr('max', maximum), this.createFormGroupInput(inputsType).attr('min', minimum).attr('max', maximum), ]), hoverHelp, ) } /** * @param {string} title * @return {JQuery} */ createFormSection(title = '') { return $('<div>').append($('<label class="bv-title">').text(title)) } /** * @param {string} label * @param {int} rows * @param {string} hoverHelp * @return {JQuery} */ createFormTextAreaGroup(label, rows, hoverHelp = '') { return this._addHelpTextOnHover( this.createFormGroup().addClass('bv-textarea-group').append([ this.createFormGroupLabel(label), $('<textarea class="bv-input" spellcheck="false">').attr('rows', rows), ]), hoverHelp, ) } /** * @return {JQuery} */ createSeparator() { return $('<hr/>') } /** * @return {JQuery} */ createSettingsHideButton() { return this.createFormButton('<< Hide', '', () => this._section.css('display', 'none')) } /** * @return {JQuery} */ createSettingsSection() { return this.createContainer().attr('id', 'settings-wrapper').addClass('bv-bg-colour bv-border-primary').hide() } /** * @param {string} caption * @param {JQuery} settingsSection * * @return {JQuery} */ createSettingsShowButton(caption, settingsSection) { return $('<button class="show-settings bv-section bv-bg-colour">').text(caption).on('click', () => settingsSection.slideDown(300)) } /** * @param {string} statisticsType * @param {string} label * @return {JQuery} */ createStatisticsFormGroup(statisticsType, label = '') { return this.createFormGroup().addClass('bv-stat-group').append([ this.createFormGroupLabel((label === '' ? statisticsType : label) + ' Filter'), this.createFormGroupStatLabel(statisticsType), ]) } /** * @return {JQuery} */ createStatisticsTotalsGroup() { return this.createFormGroup().append([ this.createFormGroupLabel('Total'), this.createFormGroupStatLabel('Total'), ]) } /** * @return {JQuery} */ createStatusSection() { this._statusLine = this.createFormGroupLabel('Status').attr('id', this._selectorGenerator.getSelector('status')) return this._statusLine } /** * @param {string} tabName * @param {boolean} isFirst * @return {JQuery} */ createTabButton(tabName, isFirst) { let tabButton = $('<button class="bv-tab-button bv-border-primary">'). text(tabName). on('click', (event) => { let button = $(event.currentTarget) let tabSection = button.parents('.bv-tabs-section:first') tabSection.find('.bv-tab-button').removeClass('bv-active bv-font-secondary').addClass('bv-font-primary') tabSection.find('.bv-tab-panel').removeClass('bv-active') button.removeClass('bv-font-primary').addClass('bv-active bv-font-secondary') $('#' + Utilities.toKebabCase(button.text())).addClass('bv-active') }). on('mouseenter', (event) => $(event.currentTarget).addClass('bv-font-secondary')). on('mouseleave', (event) => $(event.currentTarget).removeClass('bv-font-secondary')) return isFirst ? tabButton.addClass('bv-active bv-font-secondary') : tabButton.addClass('bv-font-primary') } /** * @param {string} tabName * @param {boolean} isFirst * @return {JQuery} */ createTabPanel(tabName, isFirst = false) { let tabPanel = $('<div class="bv-tab-panel bv-border-primary">').attr('id', Utilities.toKebabCase(tabName)) if (isFirst) { tabPanel.addClass('bv-active') } return tabPanel } /** * @param {string[]} tabNames * @param {JQuery[]} tabPanels * @return {JQuery} */ createTabsSection(tabNames, tabPanels) { let tabButtons = [] for (let i = 0; i < tabNames.length; i++) { tabButtons.push(this.createTabButton(tabNames[i], i === 0)) } let nav = $('<div class="bv-tabs-nav">').append(tabButtons) return $('<div class="bv-tabs-section">').append(nav).append(...tabPanels) } /** * @param {string} title * @return {JQuery} */ createTitle(title) { return $('<label class="bv-title">' + title + '</label>') } /** * @return {JQuery} */ getSelectedSection() { return this._section } resetStatus() { this._statusLine.text(this._statusText) } /** * @param {string} status * @param {boolean} transient */ updateStatus(status, transient = false) { if (!transient) { this._statusText = status } this._statusLine?.text(status) } }