Brazen Configuration Manager

Configuration management and related UI creation module

目前为 2021-01-20 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/418665/892799/Brazen%20Configuration%20Manager.js

  1. // ==UserScript==
  2. // @name Brazen Configuration Manager
  3. // @namespace brazenvoid
  4. // @version 1.2.0
  5. // @author brazenvoid
  6. // @license GPL-3.0-only
  7. // @description Configuration management and related UI creation module
  8. // ==/UserScript==
  9.  
  10. const CONFIG_TYPE_CHECKBOXES_GROUP = 'checkboxes'
  11. const CONFIG_TYPE_FLAG = 'flag'
  12. const CONFIG_TYPE_NUMBER = 'number'
  13. const CONFIG_TYPE_RADIOS_GROUP = 'radios'
  14. const CONFIG_TYPE_RANGE = 'range'
  15. const CONFIG_TYPE_RULESET = 'ruleset'
  16. const CONFIG_TYPE_SELECT = 'select'
  17. const CONFIG_TYPE_TEXT = 'text'
  18.  
  19. class BrazenConfigurationManager
  20. {
  21. /**
  22. * @typedef {{title: string, type: string, element: null|JQuery, value: *, maximum: int, minimum: int, options: string[], helpText: string, onFormatForUI:
  23. * ConfigurationManagerRulesetCallback, onTranslateFromUI: ConfigurationManagerRulesetCallback, onOptimize: ConfigurationManagerRulesetCallback, createElement: Function,
  24. * setFromUserInterface: Function, updateUserInterface: Function}} ConfigurationField
  25. */
  26.  
  27. /**
  28. * @callback ConfigurationManagerRulesetCallback
  29. * @param {*} values
  30. */
  31.  
  32. /**
  33. * @param {BrazenUIGenerator} uiGenerator
  34. * @return {BrazenConfigurationManager}
  35. */
  36. static create (uiGenerator)
  37. {
  38. return new BrazenConfigurationManager(uiGenerator)
  39. }
  40.  
  41. constructor (uiGenerator)
  42. {
  43. /**
  44. * @type {{}}
  45. * @private
  46. */
  47. this._config = {}
  48.  
  49. /**
  50. * @type {LocalStore}
  51. * @private
  52. */
  53. this._localStore = null
  54.  
  55. /**
  56. * @type BrazenUIGenerator
  57. * @private
  58. */
  59. this._uiGen = uiGenerator
  60. }
  61.  
  62. /**
  63. * @param {string} type
  64. * @param {string} name
  65. * @param {*} value
  66. * @param {string|null} helpText
  67. * @return ConfigurationField
  68. * @private
  69. */
  70. _createField (type, name, value, helpText)
  71. {
  72. let fieldKey = this._formatFieldKey(name)
  73. let field = this._config[fieldKey]
  74. if (!field) {
  75. field = {
  76. element: null,
  77. helpText: helpText,
  78. title: name,
  79. type: type,
  80. value: value,
  81. createElement: null,
  82. setFromUserInterface: null,
  83. updateUserInterface: null,
  84. }
  85. this._config[fieldKey] = field
  86. } else {
  87. if (helpText) {
  88. field.helpText = helpText
  89. }
  90. field.value = value
  91. }
  92. return field
  93. }
  94.  
  95. _formatFieldKey (name)
  96. {
  97. return Utilities.toKebabCase(name)
  98. }
  99.  
  100. /**
  101. * @param {boolean} ignoreIfDefaultsSet
  102. * @private
  103. */
  104. _syncLocalStore (ignoreIfDefaultsSet)
  105. {
  106. let field
  107. let storeObject = this._localStore.get()
  108.  
  109. if (!ignoreIfDefaultsSet || !this._localStore.wereDefaultsSet()) {
  110. for (let key in this._config) {
  111.  
  112. field = this._config[key]
  113. if (typeof storeObject[key] !== 'undefined') {
  114.  
  115. field.value = storeObject[key]
  116. if (field.type === CONFIG_TYPE_RULESET) {
  117. field.optimized = Utilities.callEventHandler(field.onOptimize, [field.value])
  118. }
  119. }
  120. }
  121. this.updateInterface()
  122. }
  123. return this
  124. }
  125.  
  126. /**
  127. * @return {{}}
  128. * @private
  129. */
  130. _toStoreObject ()
  131. {
  132. let storeObject = {}
  133. for (let key in this._config) {
  134. storeObject[key] = this._config[key].value
  135. }
  136. return storeObject
  137. }
  138.  
  139. backup ()
  140. {
  141. return Utilities.objectToJSON(this._toStoreObject())
  142. }
  143.  
  144. createElement (name)
  145. {
  146. return this.getFieldOrFail(name).createElement()
  147. }
  148.  
  149. initialize (scriptPrefix)
  150. {
  151. this._localStore = new LocalStore(scriptPrefix + 'settings', this._toStoreObject())
  152. this._localStore.onChange(() => this.updateInterface())
  153.  
  154. return this._syncLocalStore(true)
  155. }
  156.  
  157. addCheckboxesGroup (name, keyValuePairs, helpText)
  158. {
  159. let field = this._createField(CONFIG_TYPE_CHECKBOXES_GROUP, name, [], helpText)
  160.  
  161. field.options = keyValuePairs
  162.  
  163. field.createElement = () => {
  164. return field.element = this._uiGen.createFormCheckBoxesGroupSection(field.title, field.options, field.helpText)
  165. }
  166. field.setFromUserInterface = () => {
  167. field.value = []
  168. field.element.find('input:checked').each((index, element) => {
  169. field.value.push($(element).attr('data-value'))
  170. })
  171. }
  172. field.updateUserInterface = () => {
  173. let elements = field.element.find('input')
  174. for (let key of field.value) {
  175. elements.filter('[data-value="' + key + '"]').prop('checked', true)
  176. }
  177. }
  178. return this
  179. }
  180.  
  181. addFlagField (name, helpText)
  182. {
  183. let field = this._createField(CONFIG_TYPE_FLAG, name, false, helpText)
  184.  
  185. field.createElement = () => {
  186. let inputGroup = this._uiGen.createFormInputGroup(field.title, 'checkbox', field.helpText)
  187. field.element = inputGroup.find('input')
  188. return inputGroup
  189. }
  190. field.setFromUserInterface = () => {
  191. field.value = field.element.prop('checked')
  192. }
  193. field.updateUserInterface = () => {
  194. field.element.prop('checked', field.value)
  195. }
  196. return this
  197. }
  198.  
  199. addNumberField (name, minimum, maximum, helpText)
  200. {
  201. let field = this._createField(CONFIG_TYPE_NUMBER, name, minimum, helpText)
  202.  
  203. field.minimum = minimum
  204. field.maximum = maximum
  205.  
  206. field.createElement = () => {
  207. let inputGroup = this._uiGen.createFormInputGroup(field.title, 'number', field.helpText).attr('min', field.minimum).attr('max', field.maximum)
  208. field.element = inputGroup.find('input')
  209. return inputGroup
  210. }
  211. field.setFromUserInterface = () => {
  212. field.value = parseInt(field.element.val())
  213. }
  214. field.updateUserInterface = () => {
  215. field.element.val(field.value)
  216. }
  217. return this
  218. }
  219.  
  220. addRadiosGroup (name, keyValuePairs, helpText)
  221. {
  222. let field = this._createField(CONFIG_TYPE_RADIOS_GROUP, name, keyValuePairs[0][1], helpText)
  223.  
  224. field.options = keyValuePairs
  225.  
  226. field.createElement = () => {
  227. let inputGroup = this._uiGen.createFormRadiosGroupSection(field.title, field.options, field.helpText)
  228. field.element = inputGroup
  229. return inputGroup
  230. }
  231. field.setFromUserInterface = () => {
  232. field.value = field.element.find('input:checked').attr('data-value')
  233. }
  234. field.updateUserInterface = () => {
  235. field.element.find('input[data-value="' + field.value + '"]').prop('checked', true).trigger('change')
  236. }
  237. return this
  238. }
  239.  
  240. addRangeField (name, minimum, maximum, helpText)
  241. {
  242. let field = this._createField(CONFIG_TYPE_RANGE, name, {minimum: minimum, maximum: minimum}, helpText)
  243.  
  244. field.minimum = minimum
  245. field.maximum = maximum
  246.  
  247. field.createElement = () => {
  248. let inputGroup = this._uiGen.createFormRangeInputGroup(field.title, 'number', field.minimum, field.maximum, field.helpText)
  249. field.element = inputGroup.find('input')
  250. return inputGroup
  251. }
  252. field.setFromUserInterface = () => {
  253. field.value = {
  254. minimum: field.element.first().val(),
  255. maximum: field.element.last().val(),
  256. }
  257. }
  258. field.updateUserInterface = () => {
  259. field.element.first().val(field.value.minimum)
  260. field.element.last().val(field.value.maximum)
  261. }
  262. return this
  263. }
  264.  
  265. /**
  266. * @param {string} name
  267. * @param {number} rows
  268. * @param {string|null} helpText
  269. * @param {ConfigurationManagerRulesetCallback} onTranslateFromUI
  270. * @param {ConfigurationManagerRulesetCallback} onFormatForUI
  271. * @param {ConfigurationManagerRulesetCallback} onOptimize
  272. * @return {BrazenConfigurationManager}
  273. */
  274. addRulesetField (name, rows, helpText, onTranslateFromUI = null, onFormatForUI = null, onOptimize = null)
  275. {
  276. let field = this._createField(CONFIG_TYPE_RULESET, name, [], helpText)
  277.  
  278. field.optimized = null
  279. field.onTranslateFromUI = onTranslateFromUI ?? field.onTranslateFromUI
  280. field.onFormatForUI = onFormatForUI ?? field.onFormatForUI
  281. field.onOptimize = onOptimize ?? field.onOptimize
  282.  
  283. field.createElement = () => {
  284. let inputGroup = this._uiGen.createFormTextAreaGroup(field.title, rows, field.helpText)
  285. field.element = inputGroup.find('textarea')
  286. return inputGroup
  287. }
  288. field.setFromUserInterface = () => {
  289. let value = Utilities.trimAndKeepNonEmptyStrings(field.element.val().split(REGEX_LINE_BREAK))
  290. field.value = Utilities.callEventHandler(field.onTranslateFromUI, [value], value)
  291. field.optimized = Utilities.callEventHandler(field.onOptimize, [field.value])
  292. }
  293. field.updateUserInterface = () => {
  294. field.element.val(Utilities.callEventHandler(field.onFormatForUI, [field.value], field.value).join('\n'))
  295. }
  296. return this
  297. }
  298. addSelectField (name, keyValuePairs, helpText)
  299. {
  300. let field = this._createField(CONFIG_TYPE_SELECT, name, keyValuePairs[0][1], helpText)
  301.  
  302. field.options = keyValuePairs
  303.  
  304. field.createElement = () => {
  305. let inputGroup = this._uiGen.createFormSelectGroup(field.title, field.options, field.helpText)
  306. field.element = inputGroup.find('select')
  307. return inputGroup
  308. }
  309. field.setFromUserInterface = () => {
  310. field.value = field.element.val()
  311. }
  312. field.updateUserInterface = () => {
  313. field.element.val(field.value).trigger('change')
  314. }
  315. return this
  316. }
  317.  
  318. addTextField (name, helpText)
  319. {
  320. let field = this._createField(CONFIG_TYPE_TEXT, name, '', helpText)
  321.  
  322. field.createElement = () => {
  323. let inputGroup = this._uiGen.createFormInputGroup(field.title, 'text', field.helpText)
  324. field.element = inputGroup.find('input')
  325. return inputGroup
  326. }
  327. field.setFromUserInterface = () => {
  328. field.value = field.element.val()
  329. }
  330. field.updateUserInterface = () => {
  331. field.element.val(field.value)
  332. }
  333. return this
  334. }
  335.  
  336. /**
  337. * @param {string} name
  338. * @return {ConfigurationField|null}
  339. */
  340. getField (name)
  341. {
  342. return this._config[this._formatFieldKey(name)]
  343. }
  344.  
  345. /**
  346. * @param {string} name
  347. * @return {ConfigurationField}
  348. */
  349. getFieldOrFail (name)
  350. {
  351. let field = this._config[this._formatFieldKey(name)]
  352. if (field) {
  353. return field
  354. }
  355. throw new Error('Field named "' + name + '" could not be found')
  356. }
  357.  
  358. getValue (name)
  359. {
  360. return this.getFieldOrFail(name).value
  361. }
  362.  
  363. /**
  364. * @param {string} name
  365. * @return {boolean}
  366. */
  367. hasField (name)
  368. {
  369. return typeof this.getField(name) !== 'undefined'
  370. }
  371.  
  372. /**
  373. * @param {string} backedUpConfiguration
  374. */
  375. restore (backedUpConfiguration)
  376. {
  377. this._localStore.save(Utilities.objectFromJSON(backedUpConfiguration))
  378. this._syncLocalStore(false)
  379. return this
  380. }
  381.  
  382. revertChanges ()
  383. {
  384. return this._syncLocalStore(false)
  385. }
  386.  
  387. save ()
  388. {
  389. this.update()._localStore.save(this._toStoreObject())
  390. return this
  391. }
  392.  
  393. update ()
  394. {
  395. let field
  396. for (let fieldName in this._config) {
  397. field = this._config[fieldName]
  398. if (field.element) {
  399. field.setFromUserInterface()
  400. }
  401. }
  402. return this
  403. }
  404.  
  405. updateInterface ()
  406. {
  407. let field
  408. for (let fieldName in this._config) {
  409. field = this._config[fieldName]
  410. if (field.element) {
  411. field.updateUserInterface()
  412. }
  413. }
  414. return this
  415. }
  416. }