CKUIToolkit

A simple settings modal framework.

Questo script non dovrebbe essere installato direttamente. È una libreria per altri script da includere con la chiave // @require https://update.greatest.deepsurf.us/scripts/441653/1625560/CKUIToolkit.js

  1. // ==UserScript==
  2. // @name CKUIToolkit
  3. // @namespace ckylin-script-lib-combined-ui-components
  4. // @version 1.2.4
  5. // @match http://*
  6. // @match https://*
  7. // // @require https://greatest.deepsurf.us/scripts/429720-cktools/code/CKTools.js?version=1029952
  8. // @resource popjs https://fastly.jsdelivr.net/gh/CKylinMC/PopNotify.js@master/PopNotify.js
  9. // @resource popcss https://fastly.jsdelivr.net/gh/CKylinMC/PopNotify.js@master/PopNotify.css
  10. // @resource fpjs https://fastly.jsdelivr.net/gh/CKylinMC/FloatWindow.js@main/floatwin.js
  11. // @resource fpcss https://fastly.jsdelivr.net/gh/CKylinMC/FloatWindow.js@main/floatwin.modal.css
  12. // @resource cktools https://greatest.deepsurf.us/scripts/429720-cktools/code/CKTools.js?version=1029952
  13. // @author CKylinMC
  14. // @license GPL-3.0-only
  15. // @grant GM_getResourceText
  16. // @grant unsafeWindow
  17. // ==/UserScript==
  18.  
  19. (function () {
  20. if (typeof (unsafeWindow) == 'undefined') {
  21. unsafeWindow = window;
  22. }
  23. if (typeof (GM_getResourceText) != 'function') {
  24. GM_getResourceText = () => null;
  25. }
  26. unsafeWindow.CKUIToolkit_loaded = false;
  27. //======[Apply all resources]
  28. const resourceList = [
  29. { name: 'popjs', type: 'js', source: 'https://fastly.jsdelivr.net/gh/CKylinMC/PopNotify.js@master/PopNotify.js'},
  30. { name: 'popcss', type: 'css', source:'https://fastly.jsdelivr.net/gh/CKylinMC/PopNotify.js@master/PopNotify.css' },
  31. { name: 'fpjs', type: 'js', source: 'https://fastly.jsdelivr.net/gh/CKylinMC/FloatWindow.js@main/floatwin.js' },
  32. { name: 'fpcss', type: 'css', source: 'https://fastly.jsdelivr.net/gh/CKylinMC/FloatWindow.js@main/floatwin.modal.css' },
  33. { name: 'cktools', type: 'js', source: 'https://greatest.deepsurf.us/scripts/429720-cktools/code/CKTools.js?version=1029952' },
  34. { name: 'popcsspatch', type: 'rawcss', content: "div.popNotifyUnitFrame{z-index:110000!important;}.CKTOOLS-modal-content{color: #616161!important;max-height: 80vh;overflow: auto;}" },
  35. { name: 'settingscss', type: 'rawcss', content: `
  36. body .ckfp-container{
  37. /* FloatPanel colors override */
  38. --page-background: white;
  39. --page-frontcolor: black;
  40. --highlight-background: white;
  41. --highlight-frontcolor: rgb(16, 140, 255);
  42. --btn-border: #74baff;
  43. --btn-hover-border: #03a9f4;
  44. --btn-bg: dodgerblue;
  45. --btn-hover-bg: #1976d2;
  46. --btn-fg: white;
  47. }
  48. .ckui-base{
  49. /* color-schema */
  50. --ckui-text-color: black;
  51. --ckui-label-color: rgb(16, 140, 255);
  52. --ckui-description-color: rgb(92, 92, 92);
  53. --ckui-input-text-color: rgb(51, 51, 51);
  54. --ckui-input-bg-color: rgb(255, 255, 255);
  55. --ckui-input-border-color: rgb(204, 204, 204);
  56. --ckui-btn-bg-color: rgb(16, 140, 255);
  57. --ckui-btn-border-color: rgb(16, 140, 255);
  58. --ckui-btn-text-color: rgb(255, 255, 255);
  59. --ckui-btn-hover-bg-color: rgb(0, 122, 255);
  60. --ckui-header-color: rgb(16, 140, 255);
  61. --ckui-header-border-color: rgb(16, 140, 255);
  62. --ckui-toggle-hint-color: gray;
  63. --ckui-input-shadow: rgba(0, 0, 0, 0.075);
  64. /* PopNotify colors override */
  65. --popnotify-success-bg: rgb(172, 255, 223);
  66. --popnotify-success-text: rgb(0, 114, 70);
  67. --popnotify-info-bg: rgb(195, 226, 255);
  68. --popnotify-info-text: rgb(0, 80, 155);
  69. --popnotify-error-bg: rgb(255, 196, 196);
  70. --popnotify-error-text: rgb(255, 66, 66);
  71. --popnotify-warn-bg: rgb(255, 218, 139);
  72. --popnotify-warn-text: rgb(177, 94, 0);
  73. --popnotify-default-bg: white;
  74. --popnotify-default-text: black;
  75. }
  76. body.ckui-dark .ckfp-container{
  77. /* FloatPanel colors override */
  78. --page-background: rgb(40, 40, 40) !important;
  79. --page-frontcolor: white !important;
  80. --highlight-background: rgb(89, 89, 89) !important;
  81. --highlight-frontcolor: rgb(16, 140, 255) !important;
  82. --btn-border: #74baff !important;
  83. --btn-hover-border: #03a9f4 !important;
  84. --btn-bg: dodgerblue !important;
  85. --btn-hover-bg: #1976d2 !important;
  86. --btn-fg: rgb(255, 255, 255) !important;
  87. }
  88. .ckui-base.ckui-dark{
  89. /* color-schema */
  90. --ckui-text-color: rgb(230, 230, 230);
  91. --ckui-label-color: rgb(100, 180, 255);
  92. --ckui-description-color: rgb(160, 160, 160);
  93. --ckui-input-text-color: rgb(220, 220, 220);
  94. --ckui-input-bg-color: rgb(45, 45, 45);
  95. --ckui-input-border-color: rgb(80, 80, 80);
  96. --ckui-btn-bg-color: rgb(50, 120, 200);
  97. --ckui-btn-border-color: rgb(50, 120, 200);
  98. --ckui-btn-text-color: rgb(255, 255, 255);
  99. --ckui-btn-hover-bg-color: rgb(30, 100, 180);
  100. --ckui-header-color: rgb(100, 180, 255);
  101. --ckui-header-border-color: rgb(100, 180, 255);
  102. --ckui-toggle-hint-color: rgb(140, 140, 140);
  103. --ckui-input-shadow: rgba(255, 255, 255, 0.1);
  104. /* PopNotify colors override for dark theme */
  105. --popnotify-success-bg: rgb(30, 80, 50);
  106. --popnotify-success-text: rgb(150, 255, 200);
  107. --popnotify-info-bg: rgb(30, 50, 80);
  108. --popnotify-info-text: rgb(150, 200, 255);
  109. --popnotify-error-bg: rgb(80, 30, 30);
  110. --popnotify-error-text: rgb(255, 150, 150);
  111. --popnotify-warn-bg: rgb(80, 60, 20);
  112. --popnotify-warn-text: rgb(255, 200, 100);
  113. --popnotify-default-bg: rgb(50, 50, 50);
  114. --popnotify-default-text: rgb(230, 230, 230);
  115. }
  116. .ckui-base .ckui-text{
  117. font-size: 14px;
  118. line-height: 1.428571429;
  119. color: var(--ckui-text-color);
  120. }
  121. .ckui-base label{
  122. display: block;
  123. color: var(--ckui-label-color);
  124. padding-top: 12px;
  125. }
  126. .ckui-base .ckui-texttoggle{
  127. display: block;
  128. color: var(--ckui-label-color);
  129. padding-top: 12px;
  130. }
  131. .ckui-base .ckui-texttoggle-container::before{
  132. display: inline;
  133. content: "🔹";
  134. }
  135. .ckui-base .ckui-texttoggle-container .ckui-texttoggle-value{
  136. padding: 0px 6px;
  137. font-weight: bold;
  138. -webkit-user-select: none;
  139. -moz-user-select: none;
  140. -ms-user-select: none;
  141. user-select: none;
  142. cursor: pointer;
  143. }
  144. .ckui-base .ckui-texttoggle-container .ckui-texttoggle-value::before{
  145. content: "["
  146. }
  147. .ckui-base .ckui-texttoggle-container .ckui-texttoggle-value::after{
  148. content: "]"
  149. }
  150. .ckui-base .ckui-texttoggle-container::after{
  151. display: inline;
  152. content: "(点击切换)";
  153. color: var(--ckui-toggle-hint-color);
  154. font-size: 12px;
  155. font-style: italic;
  156. padding-left: 12px;
  157. }
  158. .ckui-base .ckui-description{
  159. display: block;
  160. color: var(--ckui-description-color);
  161. font-size: small;
  162. font-style: italic;
  163. }
  164. .ckui-base .ckui-toggle{
  165. padding-top: 12px;
  166. }
  167. .ckui-base label.ckui-inline-label{
  168. display: inline;
  169. line-height: 18px;
  170. }
  171. .ckui-base .ckui-input input{
  172. display: block;
  173. width: calc(100% - 28px);
  174. height: 34px;
  175. padding: 1px 3px;
  176. margin: 3px 6px;
  177. font-size: 14px;
  178. line-height: 1.428571429;
  179. color: var(--ckui-input-text-color);
  180. background-color: var(--ckui-input-bg-color);
  181. border: 1px solid var(--ckui-input-border-color);
  182. border-radius: 4px;
  183. box-shadow: inset 0 1px 1px var(--ckui-input-shadow);
  184. }
  185. .ckui-base .ckui-inputnumber input{
  186. display: block;
  187. width: calc(100% - 36px);
  188. height: 34px;
  189. padding: 1px 12px;
  190. margin: 3px 6px;
  191. font-size: 14px;
  192. line-height: 1.428571429;
  193. color: var(--ckui-input-text-color);
  194. background-color: var(--ckui-input-bg-color);
  195. border: 1px solid var(--ckui-input-border-color);
  196. border-radius: 4px;
  197. box-shadow: inset 0 1px 1px var(--ckui-input-shadow);
  198. }
  199. .ckui-base .ckui-inputarea textarea{
  200. display: block;
  201. width: calc(100% - 28px);
  202. height: 100px;
  203. padding: 6px 6px;
  204. margin: 3px 6px;
  205. font-size: 14px;
  206. line-height: 1.428571429;
  207. color: var(--ckui-input-text-color);
  208. background-color: var(--ckui-input-bg-color);
  209. border: 1px solid var(--ckui-input-border-color);
  210. border-radius: 4px;
  211. box-shadow: inset 0 1px 1px var(--ckui-input-shadow);
  212. }
  213. .ckui-base .ckui-select select{
  214. display: block;
  215. width: calc(100% - 28px);
  216. height: 34px;
  217. padding: 6px 6px;
  218. margin: 3px 6px;
  219. font-size: 14px;
  220. line-height: 1.428571429;
  221. color: var(--ckui-input-text-color);
  222. background-color: var(--ckui-input-bg-color);
  223. border: 1px solid var(--ckui-input-border-color);
  224. border-radius: 4px;
  225. box-shadow: inset 0 1px 1px var(--ckui-input-shadow);
  226. }
  227. .ckui-base .ckui-select select option{
  228. font-size: 16px;
  229. line-height: 1.428571429;
  230. color: var(--ckui-input-text-color);
  231. }
  232. .ckui-base .ckui-header::before{
  233. content: "💠";
  234. }
  235. .ckui-base .ckui-header{
  236. width: calc(100% - 8px);
  237. display: block;
  238. color: var(--ckui-header-color);
  239. padding: 12px 3px;
  240. border-bottom: 2px solid var(--ckui-header-border-color);
  241. margin: 12px 0px 12px 0px;
  242. }
  243. .ckui-base .ckui-btns{
  244. display: flex;
  245. flex-wrap: wrap;
  246. flex-direction: row;
  247. }
  248. .ckui-base .ckui-btn{
  249. display: block;
  250. width: calc(50% - 8px);
  251. height: 40px;
  252. padding: 6px 12px;
  253. margin: 6px;
  254. font-size: 14px;
  255. line-height: 1.428571429;
  256. color: var(--ckui-btn-text-color);
  257. background-color: var(--ckui-btn-bg-color);
  258. border: 1px solid var(--ckui-btn-border-color);
  259. border-radius: 4px;
  260. box-shadow: inset 0 1px 1px var(--ckui-input-shadow);
  261. }
  262. .ckui-base .ckui-btn:hover{
  263. background-color: var(--ckui-btn-hover-bg-color);
  264. }
  265. .ckui-base .ckui-btns .ckui-btn{
  266. flex: 1;
  267. }
  268.  
  269. /* PopNotify theme override */
  270. .ckui-base div.popNotifyUnitFrame {
  271. background: var(--popnotify-default-bg) !important;
  272. }
  273. .ckui-base div.popNotifyUnitFrame .popNotifyUnitTitle,
  274. .ckui-base div.popNotifyUnitFrame .popNotifyUnitContent {
  275. color: var(--popnotify-default-text) !important;
  276. }
  277. .ckui-base div.popNotifyUnitFrame .popNotifyUnitBar {
  278. background: var(--popnotify-default-text) !important;
  279. }
  280.  
  281. .ckui-base .popStyle-success {
  282. background: var(--popnotify-success-bg) !important;
  283. }
  284. .ckui-base .popStyle-success .popNotifyUnitTitle,
  285. .ckui-base .popStyle-success .popNotifyUnitContent {
  286. color: var(--popnotify-success-text) !important;
  287. }
  288. .ckui-base .popStyle-success .popNotifyUnitBar{
  289. background: var(--popnotify-success-text) !important;
  290. }
  291.  
  292. .ckui-base .popStyle-info {
  293. background: var(--popnotify-info-bg) !important;
  294. }
  295. .ckui-base .popStyle-info .popNotifyUnitTitle,
  296. .ckui-base .popStyle-info .popNotifyUnitContent {
  297. color: var(--popnotify-info-text) !important;
  298. }
  299. .ckui-base .popStyle-info .popNotifyUnitBar{
  300. background: var(--popnotify-info-text) !important;
  301. }
  302.  
  303. .ckui-base .popStyle-error {
  304. background: var(--popnotify-error-bg) !important;
  305. }
  306. .ckui-base .popStyle-error .popNotifyUnitTitle,
  307. .ckui-base .popStyle-error .popNotifyUnitContent {
  308. color: var(--popnotify-error-text) !important;
  309. }
  310. .ckui-base .popStyle-error .popNotifyUnitBar{
  311. background: var(--popnotify-error-text) !important;
  312. }
  313.  
  314. .ckui-base .popStyle-warn {
  315. background: var(--popnotify-warn-bg) !important;
  316. }
  317. .ckui-base .popStyle-warn .popNotifyUnitTitle,
  318. .ckui-base .popStyle-warn .popNotifyUnitContent {
  319. color: var(--popnotify-warn-text) !important;
  320. }
  321. .ckui-base .popStyle-warn .popNotifyUnitBar{
  322. background: var(--popnotify-warn-text) !important;
  323. }
  324. ` }
  325. ]
  326. async function applyResource() {
  327. resloop: for (let res of resourceList) {
  328. if (true||!document.querySelector("#" + res.name)) {
  329. let el;
  330. switch (res.type) {
  331. case 'js':
  332. case 'rawjs':
  333. el = document.createElement("script");
  334. break;
  335. case 'css':
  336. case 'rawcss':
  337. el = document.createElement("style");
  338. break;
  339. default:
  340. console.error('[CKUI]','Err:unknown type', res);
  341. continue resloop;
  342. }
  343. el.id = res.name;
  344. let result = res.type.startsWith('raw') ? res.content : GM_getResourceText(res.name);
  345. if (result == null || result == 'null' || typeof result === 'undefined') {
  346. console.info('[CKUI]', 'Alternative method is using:',res.type,res.name);
  347. if (!res.source) {
  348. console.info('[CKUI]', 'Failed to apply:',res.type,res.name);
  349. continue resloop;
  350. }
  351. try {
  352. let response = await fetch(res.source);
  353. if (!response.ok) {
  354. console.info('[CKUI]', 'Failed to apply:',res.type,res.name,response.statusText);
  355. continue resloop;
  356. }
  357. result = await response.text();
  358. } catch (e) {
  359. console.info('[CKUI]', 'Failed to apply:',res.type,res.name,e);
  360. continue resloop;
  361. }
  362. }
  363. el.appendChild(document.createTextNode(result));
  364. document.head.appendChild(el);
  365. console.info('[CKUI]', 'Applied:',res.type,res.name);
  366. }
  367. }
  368. console.info('[CKUI]', 'Resources all applied');
  369. }
  370. let deepClone = (obj)=>{
  371. let newObject = {};
  372. if (Array.isArray(obj)) {
  373. newObject = [];
  374. for (let i = 0; i < obj.length; i++) {
  375. newObject.push(deepClone(obj[i]));
  376. }
  377. return newObject;
  378. }
  379. Object.keys(obj).map(key => {
  380. if (typeof obj[key] === 'object') {
  381. newObject[key] = deepClone(obj[key]);
  382. } else {
  383. newObject[key] = obj[key];
  384. }
  385. });
  386. return newObject;
  387. };
  388.  
  389. let domHelper = (...args) => {
  390. return CKTools.domHelper(...args);
  391. }
  392.  
  393. const CKUIToolkit = {};
  394. class CompUtils{
  395. static getId(name) {
  396. return 'ckui-settings-' + name;
  397. }
  398. static getClass(type) {
  399. return 'ckui-' + type;
  400. }
  401. static runChecker(checker = null,...args) {
  402. if (checker && typeof (checker) == 'function') {
  403. try {
  404. const result = checker(...args);
  405. return !!result;
  406. } catch (e) {
  407. console.log('checker errored', e);
  408. return false;
  409. }
  410. } else return true;
  411. }
  412. static cfgValidator(cfg, keystr='') {
  413. let keys = keystr.split(',').map(el => el.trim()).filter(el => el.length > 0);
  414. keys.concat(['name', 'type']);
  415. if (!cfg) return false;
  416. for (let key of keys) {
  417. if (cfg[key]===undefined) return false;
  418. }
  419. return true;
  420. }
  421. }
  422. class Components{
  423. static text(cfg) {
  424. if (!CompUtils.cfgValidator(cfg, 'label')) return;
  425. return domHelper('div', {
  426. id: CompUtils.getId(cfg.name),
  427. classlist: CompUtils.getClass(cfg.type),
  428. html: cfg.label
  429. });
  430. }
  431. static header(cfg) {
  432. if (!CompUtils.cfgValidator(cfg, 'label')) return;
  433. return domHelper('div', {
  434. id: CompUtils.getId(cfg.name),
  435. classlist: CompUtils.getClass(cfg.type),
  436. html: cfg.label
  437. });
  438. }
  439. static toggle(cfg) {
  440. if (!CompUtils.cfgValidator(cfg, 'label')) return;
  441. const customId = 'toggle'+CKTools.GUID.getShort();
  442. return domHelper('div', {
  443. id: CompUtils.getId(cfg.name),
  444. classlist: CompUtils.getClass(cfg.type),
  445. childs: [
  446. domHelper('input', {
  447. id: customId,
  448. attr: {
  449. type: 'checkbox',
  450. checked: cfg.value??false
  451. },
  452. on: {
  453. change: (e) => {
  454. const value = !!(e.target?.checked);
  455. if (CompUtils.runChecker(cfg.checker,value)) cfg.value = value;
  456. // else TODO: error tip
  457. }
  458. }
  459. }),
  460. domHelper('label', {
  461. attr: {
  462. for: customId
  463. },
  464. classlist:'ckui-inline-label',
  465. html: cfg.label
  466. }),
  467. domHelper('span', {
  468. classList: 'ckui-description',
  469. html: cfg.description??''
  470. }),
  471. ]
  472. });
  473. }
  474. static input(cfg) {
  475. if (!CompUtils.cfgValidator(cfg, 'label')) return;
  476. return domHelper('div', {
  477. id: CompUtils.getId(cfg.name),
  478. classlist: CompUtils.getClass(cfg.type),
  479. childs: [
  480. domHelper('label', {
  481. html: cfg.label
  482. }),
  483. domHelper('input', {
  484. attr: {
  485. value: cfg.value??''
  486. },
  487. on: {
  488. keyup: CKTools.debounce((e) => {
  489. const value = e.target?.value;
  490. if (CompUtils.runChecker(cfg.checker,value)) cfg.value = value;
  491. // else TODO: error tip
  492. })
  493. }
  494. }),
  495. domHelper('span', {
  496. classList: 'ckui-description',
  497. html: cfg.description??''
  498. }),
  499. ]
  500. });
  501. }
  502. static inputarea(cfg) {
  503. if (!CompUtils.cfgValidator(cfg, 'label')) return;
  504. return domHelper('div', {
  505. id: CompUtils.getId(cfg.name),
  506. classlist: CompUtils.getClass(cfg.type),
  507. childs: [
  508. domHelper('label', {
  509. html: cfg.label
  510. }),
  511. domHelper('textarea', {
  512. attr: {
  513. value: cfg.value??''//not work until it put into dom
  514. },
  515. html:cfg.value??'',
  516. on: {
  517. keyup: CKTools.debounce((e) => {
  518. const value = e.target?.value ?? e.target.innerHTMl;
  519. console.log('inputarea', value,e.target?.value,e.target.innerHTMl);
  520. if (CompUtils.runChecker(cfg.checker, value)) cfg.value = value;
  521. else console.warn('checker refused');
  522. // else TODO: error tip
  523. })
  524. }
  525. }),
  526. domHelper('span', {
  527. classList: 'ckui-description',
  528. html: cfg.description??''
  529. }),
  530. ]
  531. });
  532. }
  533. static inputnumber(cfg) {
  534. if (!CompUtils.cfgValidator(cfg, 'label,min,max,step')) return;
  535. return domHelper('div', {
  536. id: CompUtils.getId(cfg.name),
  537. classlist: CompUtils.getClass(cfg.type),
  538. childs: [
  539. domHelper('label', {
  540. html: cfg.label
  541. }),
  542. domHelper('input', {
  543. attr: {
  544. type: 'number',
  545. value: isNaN(cfg.value)?undefined:+cfg.value,
  546. min: cfg.min,
  547. max: cfg.max,
  548. step: cfg.step
  549. },
  550. on: {
  551. change: (e) => {
  552. let value = e.target?.value;
  553. if (!isNaN(value)) {
  554. value = +value;
  555. }
  556. if (CompUtils.runChecker(cfg.checker, value)) {
  557. console.log('updated:', value);
  558. cfg.value = value;
  559. } else {
  560. console.log('refused to update value', value);
  561. }
  562. // else TODO: error tip
  563. }
  564. }
  565. }),
  566. domHelper('span', {
  567. classList: 'ckui-description',
  568. html: cfg.description??''
  569. }),
  570. ]
  571. });
  572. }
  573. static select(cfg) {
  574. if (!CompUtils.cfgValidator(cfg, 'label,options')) return;
  575. return domHelper('div', {
  576. id: CompUtils.getId(cfg.name),
  577. classlist: CompUtils.getClass(cfg.type),
  578. childs: [
  579. domHelper('label', {
  580. html: cfg.label
  581. }),
  582. domHelper('select', {
  583. init: select => {
  584. for (let option of cfg.options) {
  585. console.log('select option', option.opt,option.value,cfg.value == option.value);
  586. select.appendChild(domHelper('option', {
  587. attr: {
  588. value: option.value,
  589. },
  590. html: option.opt,
  591. init: optionel => {
  592. if (cfg.value == option.value) optionel.setAttribute('selected', true);
  593. }
  594. }));
  595. }
  596. },
  597. on: {
  598. change: (e) => {
  599. const value = e.target?.value??'';
  600. if (CompUtils.runChecker(cfg.checker,value)) cfg.value = value;
  601. // else TODO: error tip
  602. }
  603. }
  604. }),
  605. domHelper('span', {
  606. classList: 'ckui-description',
  607. html: cfg.description??''
  608. }),
  609. ]
  610. });
  611. }
  612. static texttoggle(cfg) {
  613. if (!CompUtils.cfgValidator(cfg, 'before,on,off,after')) return;
  614. return domHelper('div', {
  615. id: CompUtils.getId(cfg.name),
  616. classlist: CompUtils.getClass(cfg.type),
  617. childs: [
  618. domHelper('div', {
  619. classList:'ckui-texttoggle-container',
  620. childs: [
  621. domHelper('span', { text: cfg.before }),
  622. domHelper('span', {classList:'ckui-texttoggle-value',text:'...'}),
  623. domHelper('span', {text:cfg.after}),
  624. ],
  625. init: div => {
  626. const getText = () => cfg.value ? cfg.on : cfg.off;
  627. const setValue = (value) => {
  628. cfg.value = !!value;
  629. div.querySelector('.ckui-texttoggle-value').innerText = getText();
  630. }
  631. const toggleValue = () => setValue(!cfg.value);
  632. div.addEventListener('click', toggleValue);
  633. setTimeout(()=>setValue(cfg.value),50);
  634. }
  635. }),
  636. domHelper('span', {
  637. classList: 'ckui-description',
  638. html: cfg.description??''
  639. }),
  640. ]
  641. });
  642. }
  643. static raw(cfg) {
  644. if (!CompUtils.cfgValidator(cfg, 'contents')) return;
  645. return domHelper('div', {
  646. id: CompUtils.getId(cfg.name),
  647. classlist: CompUtils.getClass(cfg.type),
  648. childs:domHelper(cfg.contents)
  649. })
  650. }
  651. static window(cfg,type) {
  652. if (!CompUtils.cfgValidator(cfg, 'label,config')) return;
  653. return domHelper('div', {
  654. id: CompUtils.getId(cfg.name),
  655. classlist: CompUtils.getClass(cfg.type),
  656. childs: [
  657. domHelper('button', {
  658. classList: 'ckui-btn',
  659. html: cfg.label,
  660. on: {
  661. click: async (e) => {
  662. let subres = {};
  663. if (type == 'confirm') subres = await SettingsBuilder.open(cfg.config);
  664. else if (type == 'modal') subres = await SettingsBuilder.modal(cfg.config);
  665. else return;
  666. console.log('subres:', subres)
  667. Object.assign(cfg.config, subres);
  668. }
  669. }
  670. })
  671. ]
  672. })
  673. }
  674. static btns(cfg) {
  675. if (!CompUtils.cfgValidator(cfg, 'btns')) return;
  676. return domHelper('div', {
  677. id: CompUtils.getId(cfg.name),
  678. classlist: CompUtils.getClass(cfg.type),
  679. init: el => {
  680. for(let btn of cfg.btns) {
  681. el.appendChild(domHelper('button', {
  682. classList: 'ckui-btn',
  683. html: btn.label,
  684. on: {
  685. click: async (e) => {
  686. await btn.onclick?.();
  687. }
  688. }
  689. }));
  690. }
  691. }
  692. })
  693. }
  694. }
  695. class SettingsBuilder{
  696. static builder(cfg) {
  697. return new SettingsBuilder(cfg);
  698. }
  699. static async open(cfg, values = null) {
  700. const s = new SettingsBuilder(cfg);
  701. if(values) s.setValues(values);
  702. const result = await s.showWindow();
  703. return result;
  704. }
  705. static async modal(cfg, values = null) {
  706. const s = new SettingsBuilder(cfg);
  707. if(values) s.setValues(values);
  708. const result = await s.showAlertWindow();
  709. return result;
  710. }
  711. constructor(config) {
  712. this.config = Object.assign({
  713. title: '设置',
  714. theme: undefined,
  715. settings:[]
  716. },config);
  717. }
  718. static getThemeVariable(){
  719. const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  720. return prefersDark ? 'dark' : 'light';
  721. }
  722. findSettingObjectByName(key = '',cfg = this.config) {
  723. const settings = cfg.settings;
  724. for (const setting of settings) {
  725. if(setting.name == key) return setting;
  726. }
  727. const subSettings = settings.filter(el => el.type == 'window');
  728. if (!subSettings.length) return null;
  729. for (const setting of subSettings) {
  730. const subresult = this.findSettingObjectByName(key, setting);
  731. if(subresult) return subresult;
  732. }
  733. return null;
  734. }
  735. setValues(settingsValues = {}) {
  736. for (let vk of Object.keys(settingsValues)) {
  737. const setting = this.findSettingObjectByName(vk);
  738. if (setting) setting.value = settingsValues[vk];
  739. else console.warn('[CKUI]', `${vk} not found in scheme`);
  740. }
  741. }
  742. flatValues(cfg) {
  743. const cfgs = {};
  744. if(cfg.settings){
  745. cfg = cfg.settings;
  746. }
  747. if(!Array.isArray(cfg)){
  748. return cfgs;
  749. }
  750. for(const s of cfg){
  751. if(!s.name || !s.type) {
  752. console.warn('[CKUI]', 'missing name or type',s);
  753. continue;
  754. }
  755. switch(s.type){
  756. case "toggle":
  757. case "texttoggle":
  758. cfgs[s.name] = !!s.value;
  759. break;
  760. case "select":
  761. case "input":
  762. case "inputarea":
  763. cfgs[s.name] = s.value?s.value+"":"";
  764. break;
  765. case "inputnumber":
  766. if(!isNaN(s.value)) cfgs[s.name] = +s.value;
  767. break;
  768. case "window":
  769. {
  770. const sub = this.flatValues(s.config);
  771. Object.assign(cfgs,sub);
  772. }
  773. break;
  774. default:
  775. console.log('[CKUI]','unrecognized type',s.type);
  776. }
  777. }
  778. return cfgs;
  779. }
  780. async showAlertWindow(config = this.config) {
  781. const copiedConfig = deepClone(config);
  782. return new Promise(r => {
  783. const theme = (copiedConfig.theme??SettingsBuilder.getThemeVariable());
  784. document.body.classList.toggle('ckui-dark', theme == 'dark');
  785. FloatWindow.alert(copiedConfig.title, domHelper('div', {
  786. classlist:'ckui-base ckui-'+theme,
  787. init: el => {
  788. for (const comp of copiedConfig.settings) {
  789. const r = this.makeComponent(comp,'alert');
  790. r&&el.appendChild(r);
  791. }
  792. }
  793. }), copiedConfig.btnName ?? "确定").then(() => {
  794. r(this.flatValues(copiedConfig));
  795. });
  796. })
  797. }
  798. async showWindow(config = this.config) {
  799. const copiedConfig = deepClone(config);
  800. return new Promise(r => {
  801. const theme = (copiedConfig.theme??SettingsBuilder.getThemeVariable());
  802. document.body.classList.toggle('ckui-dark', theme == 'dark');
  803. FloatWindow.confirm(copiedConfig.title, domHelper('div', {
  804. classlist:'ckui-base ckui-'+theme,
  805. init: el => {
  806. for (const comp of copiedConfig.settings) {
  807. const r = this.makeComponent(comp);
  808. r&&el.appendChild(r);
  809. }
  810. }
  811. }), copiedConfig.saveBtn??"保存", copiedConfig.cancelBtn??"取消").then(result => {
  812. console.log('[CKUI]', 'Save?', result);
  813. result ? r(this.flatValues(copiedConfig)) : r({});
  814. });
  815. })
  816. }
  817. makeComponent(cfg, type='confirm') {
  818. if (Components.hasOwnProperty(cfg.type)) {
  819. return Components[cfg.type](cfg,type);
  820. }
  821. }
  822. }
  823. CKUIToolkit.showSettings = SettingsBuilder.open;
  824. CKUIToolkit.showModal = SettingsBuilder.modal;
  825. CKUIToolkit.builder = SettingsBuilder.builder;
  826.  
  827. unsafeWindow.CKUIToolkit = CKUIToolkit;
  828. applyResource().then(() => {unsafeWindow.CKUIToolkit_loaded = true; unsafeWindow.CKUIToolkit_onload?.();});
  829. })();