UIModules

Modules for UI constructing

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/384232/704113/UIModules.js

  1. // ==UserScript==
  2. // @name UIModules
  3. // @name:en UIModules
  4. // @description Modules for UI constructing
  5. // @description:en Modules for UI constructing
  6. // @namespace https://gf.qytechs.cn/users/174399
  7. // @version 0.1.0
  8. // @include *://*.mozilla.org/*
  9. // @run-at document-start
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function(window) {
  14. 'use strict';
  15. const listSymbol = Symbol('list');
  16. const modeSymbol = Symbol('mode');
  17. const valueSymbol = Symbol('value');
  18. const fn = {};
  19. fn.toJSON = function() {
  20. return {
  21. __function_body__: this.toString().replace(/\s+/g, ' '),
  22. __function_name__: this.name,
  23. __function_length__: this.length,
  24. };
  25. };
  26. function reviver(key, value) {
  27. if (key === '__function_body__') {
  28. return new Function('return ' + value)();
  29. }
  30. if (typeof value === 'object' && typeof value.__function_body__ === 'function') {
  31. return value.__function_body__;
  32. }
  33. return value;
  34. }
  35. /*
  36. const interfaceTemplate = {
  37. title: 'template interface',
  38. version: '0.1.0',
  39. mode: 'normal',
  40. tabs: {
  41. data: {
  42. 'general': {},
  43. },
  44. order: ['general'],
  45. },
  46. };
  47. const tabDataTemplate = {
  48. name: 'general',
  49. label: 'General',
  50. options: {
  51. data: {
  52. 'option-name': {},
  53. },
  54. order: ['option-name'],
  55. },
  56. };
  57. const optionDataTemplate = {
  58. name: 'option-name',
  59. label: 'Option name',
  60. value: 'option-value',
  61. description: 'description text (optional)',
  62. type: 'string',
  63. mode: 'normal',
  64. tab: 'general',
  65. };
  66. */
  67. /*
  68. <div class="interface-ui">
  69. <div class="tabs-ui tabs-ui-active">
  70. <div class="tab-ui tab-ui-active" data-name="{tabUI.name}">
  71. <div class="tab-ui-label">
  72. <span>{tabUI.label}</span>
  73. </div>
  74. <div class="tab-ui-data">
  75. <input type="radio" name="file-size" data-key="10G" value="10G" />
  76. <input type="radio" name="file-size" data-key="1G" value="1G" />
  77. <input type="radio" name="file-size" data-key="1M" value="1M" />
  78. <input type="radio" name="file-size" data-key="1Kb" value="1024" />
  79. <input type="text" name="file-name" value="foo.txt" />
  80. </div>
  81. </div>
  82. <div class="tab-ui"/>
  83. </div>
  84. </div>
  85. */
  86. function dummy() {}
  87. function Mode(name = 'normal', label = '', onChange = dummy) {
  88. let nam;
  89. Object.defineProperty(this, 'name', {
  90. get: function() { return nam; },
  91. set: function(n) {
  92. try {
  93. Mode.validateName(n);
  94. nam = n;
  95. onChange(null, n);
  96. } catch (err) {
  97. onChange(err);
  98. }
  99. },
  100. enumerable: true,
  101. });
  102. this.name = name;
  103. this.label = label || name;
  104. }
  105. Object.defineProperties(Mode, {
  106. 'NORMAL': {
  107. value: 'normal',
  108. enumerable: true,
  109. },
  110. 'ADVANCED': {
  111. value: 'advanced',
  112. enumerable: true,
  113. },
  114. 'NAMES': {
  115. get: function(){
  116. return [Mode.NORMAL, Mode.ADVANCED];
  117. },
  118. enumerable: true,
  119. },
  120. });
  121. Mode.validateName = function(name) {
  122. switch (name) {
  123. case Mode.NORMAL:
  124. case Mode.ADVANCED:
  125. return true;
  126. default:
  127. throw new Error('invalid mode name (' + name + '), valid names = [' + Mode.NAMES.join(', ') + ']');
  128. }
  129. };
  130. Mode.descriptor = {
  131. get: function() { return this[modeSymbol]; },
  132. set: function(mode) {
  133. try {
  134. Mode.validateName(mode);
  135. this[modeSymbol] = mode;
  136. } catch (error) {
  137. if (typeof this.onChange === 'function') {
  138. this.onChange(error);
  139. } else {
  140. console.error('mode validation: ', error);
  141. }
  142. }
  143. },
  144. enumerable: true,
  145. };
  146. function Datum({
  147. mode = Mode.NORMAL,
  148. label,
  149. key,
  150. name,
  151. description,
  152. onChange = dummy,
  153. validate = dummy,
  154. setter = function(v) { return v; },
  155. } = {}) {
  156. console.log('new Datum() -> args: ', JSON.stringify(arguments[0], null, 2));
  157. this.onChange = onChange;
  158. this.validate = validate;
  159. this.setter = setter;
  160. this.label = label;
  161. this.description = description;
  162. this.mode = mode;
  163. Object.defineProperty(this, 'name', { value: name });
  164. Object.defineProperty(this, 'key', { value: (key || name) });
  165. }
  166. Datum.descriptor = {
  167. get: function(){ return this[valueSymbol]; },
  168. set: function(val) {
  169. try {
  170. this.validate(val);
  171. this[valueSymbol] = this.setter(val);
  172. this.onChange(null, this[valueSymbol], val);
  173. } catch (error) {
  174. this.onChange(error);
  175. }
  176. },
  177. enumerable: true,
  178. };
  179. Object.defineProperty(Datum.prototype, 'mode', extend({}, Mode.descriptor));
  180. Datum.prototype.toJSON = function() {
  181. const { mode, name, label, description, key } = this;
  182. return { mode, name, label, description, key };
  183. };
  184. Datum.prototype.assign = function(item, checkName = false) {
  185. if (!item || item === this) {
  186. return this;
  187. }
  188. const {
  189. key,
  190. name,
  191. validate = this.validate,
  192. onChange = this.onChange,
  193. label = this.label,
  194. description = this.description,
  195. } = item;
  196. if (name !== this.name && checkName) {
  197. throw new Error('In assign (' + this.TYPE + ', name does not match, expected "' + this.name + '", but got "' + name + '"');
  198. }
  199. this.validate = validate;
  200. this.onChange = onChange;
  201. this.label = label;
  202. this.description = description;
  203. return this;
  204. };
  205. function Input({
  206. mode = Mode.NORMAL,
  207. label,
  208. name,
  209. key,
  210. value,
  211. description,
  212. type,
  213. onChange,
  214. validate,
  215. setter,
  216. } = {}) {
  217. this.index = Input.index++;
  218. console.log('new Input() -> args: ', JSON.stringify(arguments[0], null, 2));
  219. Datum.call(this, { mode, label, name, key, description, onChange, validate, setter });
  220. Object.defineProperty(this, 'value', extend({}, Datum.descriptor));
  221. Object.defineProperty(this, 'tag', { value: 'input' });
  222. let _type;
  223. Object.defineProperty(this, 'type', {
  224. get: function(){ return _type; },
  225. set: function(t) {
  226. if (Input.TYPES.indexOf(t) === -1) {
  227. throw new TypeError('invalid input type, expected one of ' + JSON.stringify(Input.TYPES) + ', but got ' + t);
  228. }
  229. _type = t;
  230. },
  231. enumerable: true,
  232. });
  233. this.type = type;
  234. this.value = value;
  235. }
  236. Input.index = 0;
  237. Input.TYPES = ['number', 'text', 'checkbox', 'date', 'email', 'radio', 'tel', 'time'];
  238. Object.defineProperty(Input.prototype, 'mode', extend({}, Mode.descriptor));
  239. Input.parse = function(item){
  240. if (item instanceof Input) {
  241. return item;
  242. }
  243. if (typeof item === 'object') {
  244. return new Input(item);
  245. }
  246. return new Input();
  247. };
  248. Input.prototype.toJSON = function() {
  249. const { value, type } = this;
  250. return extend({ value, type }, Datum.prototype.toJSON.call(this));
  251. };
  252. Input.prototype.toHTML = function() {
  253. const div = document.createElement('div');
  254. const elem = document.createElement('input');
  255. elem.setAttribute('type', this.type);
  256. elem.setAttribute('value', this.value);
  257. elem.setAttribute('name', this.name);
  258. elem.setAttribute('data-key', this.key);
  259. const id = this.name + '-' + (this.key === this.name ? this.globalIndex : this.key);
  260. elem.setAttribute('id', id);
  261. elem.setAttribute('title', this.description);
  262. div.appendChild(elem);
  263. return div.firstElementChild;
  264. };
  265. Input.prototype.assign = function(item, checkName = false) {
  266. if (!item || item === this) {
  267. return this;
  268. }
  269. Datum.prototype.assign.call(this, item, checkName);
  270. const { value = this.value } = item;
  271. this.value = value;
  272. return this;
  273. };
  274. function Option({
  275. label,
  276. name,
  277. key,
  278. value,
  279. description,
  280. onChange,
  281. validate,
  282. setter,
  283. } = {}) {
  284. this.globalIndex = Option.index++;
  285. console.log('new Option() -> args: ', JSON.stringify(arguments[0], null, 2));
  286. Datum.call(this, { label, name, key, description, onChange, validate, setter });
  287. Object.defineProperty(this, 'value', extend({}, Datum.descriptor));
  288. Object.defineProperty(this, 'tag', { value: 'option' });
  289. this.value = value;
  290. }
  291. Object.defineProperty(Option.prototype, 'mode', extend({}, Mode.descriptor));
  292. Option.parse = function(item){
  293. if (item instanceof Option) {
  294. return item;
  295. }
  296. if (typeof item === 'object') {
  297. return new Option(item);
  298. }
  299. return new Option();
  300. };
  301. Option.prototype.toJSON = function() {
  302. const { value, type } = this;
  303. return extend({ value, type }, Datum.prototype.toJSON.call(this));
  304. };
  305. Option.index = 0;
  306. Option.prototype.toHTML = function() {
  307. const div = document.createElement('div');
  308. const elem = document.createElement('option');
  309. elem.setAttribute('value', this.value);
  310. elem.setAttribute('name', this.name);
  311. elem.setAttribute('data-key', this.key);
  312. const id = this.name + '-' + (this.key === this.name ? this.globalIndex : this.key);
  313. elem.setAttribute('id', id);
  314. elem.setAttribute('title', this.description);
  315. div.appendChild(elem);
  316. return div.firstElementChild;
  317. };
  318. Option.prototype.assign = function(item, checkName = false) {
  319. if (!item || item === this) {
  320. return this;
  321. }
  322. Datum.prototype.assign.call(this, item, checkName);
  323. const { value = this.value } = item;
  324. this.value = value;
  325. return this;
  326. };
  327. function List(Item = Input, itemKey = 'name', itemDefaults = {}) {
  328. Object.defineProperty(this, 'Item', { value: Item });
  329. this.data = {};
  330. this.order = [];
  331. this.itemDefaults = itemDefaults;
  332. List.validateItemKey(itemKey);
  333. Object.defineProperty(this, 'itemKey', { value: itemKey });
  334. }
  335. List.ITEM_KEYS = ['key', 'name'];
  336. List.validateItemKey = function(key) {
  337. if (List.ITEM_KEYS.indexOf(key) === -1) {
  338. throw new Error('Invalid key property name, expected ' + JSON.stringify(List.ITEM_KEYS) + ', but got ' + key + ' (typeof = ' + typeof key + ')');
  339. }
  340. };
  341. List.prototype.add = function(...data) {
  342. const { Item, itemKey, itemDefaults } = this;
  343. for (const datum of data) {
  344. const item = datum instanceof Item ? datum : new Item(extend({}, itemDefaults, datum));
  345. const { [itemKey]: name } = item;
  346. if (this.data[name] || this.order.indexOf(name) !== -1) {
  347. throw new Error('data already exists, type = "' + Item.name + '", name = "' + name + '"');
  348. }
  349. this.data[name] = item;
  350. this.order.push(name);
  351. }
  352. };
  353. List.prototype.update = function(datum) {
  354. if (!datum || typeof datum !== 'object') {
  355. return;
  356. }
  357. const { Item, itemKey } = this;
  358. const { [itemKey]: name } = datum;
  359. const item = this.data[name];
  360. if (item instanceof Item) {
  361. item.assign(datum);
  362. }
  363. };
  364. List.prototype.remove = function(name = '') {
  365. if (!name) {
  366. return;
  367. }
  368. const { Item } = this;
  369. const item = this.data[name];
  370. const index = this.order.indexOf(name);
  371. if (item) {
  372. delete this.data[name];
  373. }
  374. if (index !== -1) {
  375. this.order.splice(index, 1);
  376. }
  377. };
  378. List.prototype.setValue = function(name = '', value) {
  379. const { Item } = this;
  380. const item = this.data[name];
  381. if (item instanceof Item) {
  382. item.value = value;
  383. }
  384. };
  385. List.prototype.getValue = function(name = '') {
  386. const { Item } = this;
  387. const item = this.data[name];
  388. if (item instanceof Item) {
  389. return item.value;
  390. }
  391. return null;
  392. };
  393. List.prototype.assign = function(_list) {
  394. if (!_list || _list === this) {
  395. return this;
  396. }
  397. if (!(_list instanceof List) && typeof _list !== 'object') {
  398. return this;
  399. }
  400. const {
  401. data = this.data,
  402. order = this.order,
  403. itemKey = this.itemKey,
  404. itemDefaults = this.itemDefaults,
  405. } = _list;
  406. if (order !== this.order && itemKey !== 'key') {
  407. this.order = [...order];
  408. }
  409. if (itemKey !== this.itemKey) {
  410. this.itemKey = itemKey;
  411. }
  412. if (itemDefaults !== this.itemDefaults) {
  413. this.itemDefaults = itemDefaults;
  414. }
  415. if (data !== this.data) {
  416. this.data = extend({}, data);
  417. }
  418. return this;
  419. };
  420. List.parse = function(elem) {
  421. if (elem instanceof List) {
  422. return elem;
  423. }
  424. if (typeof elem === 'object') {
  425. let Item;
  426. switch (elem.itemType) {
  427. case 'Datum':
  428. Item = Datum;
  429. break;
  430. case 'Input':
  431. Item = Input;
  432. break;
  433. case 'Radio':
  434. Item = Radio;
  435. break;
  436. case 'Option':
  437. Item = Option;
  438. break;
  439. case 'Tab':
  440. Item = Tab;
  441. break;
  442. case 'TabItem':
  443. Item = TabItem;
  444. break;
  445. case 'List':
  446. Item = List;
  447. break;
  448. case 'Mode':
  449. Item = Mode;
  450. break;
  451. case 'Select':
  452. Item = Select;
  453. break;
  454. default:
  455. throw new TypeError('Invalid itemType (' + itemType + ', ' + typeof itemType + ')');
  456. }
  457. const { itemKey, itemDefaults, data = {}, order = [] } = elem;
  458. const list = new List(Item, itemKey, itemDefaults);
  459. for (const key of order) {
  460. list.add(data[key]);
  461. }
  462. return list;
  463. }
  464. return new List();
  465. };
  466. List.prototype.toJSON = function() {
  467. const { data, order, itemKey, itemDefaults, Item } = this;
  468. return { data, order, itemKey, itemDefaults, itemType: Item.name };
  469. };
  470. function Radio({ mode, label, description, name, value, onChange, validate, setter } = {}) {
  471. Datum.call(this, { mode, label, description, name, onChange, validate, setter });
  472. Object.defineProperty(this, 'value', Datum.descriptor);
  473. List.call(this, Input, 'key', {
  474. type: 'radio',
  475. name: this.name,
  476. mode: this.mode,
  477. validate: this.validate,
  478. setter: this.setter,
  479. });
  480. }
  481. Object.defineProperty(Radio.prototype, 'mode', Mode.descriptor);
  482. for (const fn of ['add', 'update', 'remove', 'setValue', 'getValue']) {
  483. Radio.prototype[fn] = function(...args) {
  484. return List.prototype[fn].apply(this, args);
  485. };
  486. }
  487. Radio.prototype.changeItem = function(itemValue) {
  488. const { Item, itemDefaults } = this;
  489. console.log('------ itemDefaults: ', JSON.stringify(itemDefaults, null, 2));
  490. console.log('changeItem: ', itemValue);
  491. let error;
  492. const elem = new Item(extend({}, itemDefaults, {
  493. value: itemValue,
  494. onChange: function(err){
  495. error = err;
  496. },
  497. }));
  498. console.log('changeItem -> elem: ', JSON.stringify(elem, null, 2));
  499. if (error) {
  500. console.log('In changeItem, ', error);
  501. return;
  502. }
  503. console.log('changeItem -> item: ', JSON.stringify(elem, null, 2));
  504. for (const key of this.order) {
  505. const item = this.data[key];
  506. console.log('[' + key + ']: ', JSON.stringify(item, null, 2));
  507. if (item.value === elem.value) {
  508. this.value = itemValue;
  509. console.log('-------------------- found');
  510. break;
  511. }
  512. }
  513. };
  514. Radio.prototype.assign = function(radio) {
  515. if (!radio || radio === this) {
  516. return this;
  517. }
  518. if (radio instanceof Radio || typeof radio === 'object') {
  519. const { value = this.value } = radio;
  520. this.value = value;
  521. }
  522. Datum.prototype.assign.call(this, radio);
  523. List.prototype.assign.call(this, radio);
  524. return this;
  525. };
  526. Radio.parse = function(elem) {
  527. if (elem instanceof Radio) {
  528. return elem;
  529. }
  530. if (typeof elem === 'object') {
  531. const radio = new Radio(elem);
  532. const { data = {}, order = [] } = elem;
  533. for (const key of order) {
  534. radio.add(data[key]);
  535. }
  536. return radio;
  537. }
  538. return new Radio();
  539. };
  540. Radio.prototype.toJSON = function() {
  541. const { value } = this;
  542. return extend(
  543. { value, type: 'radio' },
  544. Datum.prototype.toJSON.call(this),
  545. List.prototype.toJSON.call(this),
  546. );
  547. };
  548. function Select({ mode, label, description, name, value, onChange, validate, setter } = {}) {
  549. Datum.call(this, { mode, label, description, name, onChange, validate, setter });
  550. Object.defineProperty(this, 'value', Datum.descriptor);
  551. List.call(this, Option, 'key', {
  552. name: this.name,
  553. mode: this.mode,
  554. validate: this.validate,
  555. setter: this.setter,
  556. });
  557. }
  558. Object.defineProperty(Select.prototype, 'mode', Mode.descriptor);
  559. for (const fn of ['add', 'update', 'remove', 'setValue', 'getValue']) {
  560. Radio.prototype[fn] = function(...args) {
  561. return List.prototype[fn].apply(this, args);
  562. };
  563. }
  564. Select.prototype.changeItem = function(itemValue) {
  565. const { Item, itemDefaults } = this;
  566. console.log('------ itemDefaults: ', JSON.stringify(itemDefaults, null, 2));
  567. console.log('changeItem: ', itemValue);
  568. let error;
  569. const elem = new Item(extend({}, itemDefaults, {
  570. value: itemValue,
  571. onChange: function(err){
  572. error = err;
  573. },
  574. }));
  575. console.log('changeItem -> elem: ', JSON.stringify(elem, null, 2));
  576. if (error) {
  577. console.log('changeItem -> elem: ', JSON.stringify(elem, null, 2));
  578. return;
  579. }
  580. console.log('changeItem -> item: ', JSON.stringify(elem, null, 2));
  581. for (const key of this.order) {
  582. const item = this.data[key];
  583. console.log('[' + key + ']: ', JSON.stringify(item, null, 2));
  584. if (item.value === elem.value) {
  585. this.value = itemValue;
  586. console.log('-------------------- found');
  587. break;
  588. }
  589. }
  590. };
  591. Select.prototype.assign = function(elem) {
  592. if (!elem || elem === this) {
  593. return this;
  594. }
  595. if (elem instanceof Select || typeof elem === 'object') {
  596. const { value = this.value } = elem;
  597. this.value = value;
  598. }
  599. Datum.prototype.assign.call(this, elem);
  600. List.prototype.assign.call(this, elem);
  601. return this;
  602. };
  603. Select.parse = function(elem) {
  604. if (elem instanceof Select) {
  605. return elem;
  606. }
  607. if (typeof elem === 'object') {
  608. const select = new Select(elem);
  609. const { data = {}, order = [] } = elem;
  610. for (const key of order) {
  611. select.add(data[key]);
  612. }
  613. return select;
  614. }
  615. return new Select();
  616. };
  617. Select.prototype.toJSON = function() {
  618. const { value } = this;
  619. return extend(
  620. { value, type: 'select' },
  621. Datum.prototype.toJSON.call(this),
  622. List.prototype.toJSON.call(this),
  623. );
  624. };
  625. const list = new List();
  626. const onChange = function(error, value) {
  627. if (error) {
  628. console.log('error occured while setting value, ', error);
  629. return;
  630. }
  631. console.log('next value: ', value);
  632. };
  633. onChange.toJSON = fn.toJSON;
  634. const validate = function(value) {
  635. let match;
  636. switch (typeof value) {
  637. case 'undefined':
  638. case 'number':
  639. break;
  640. case 'string':
  641. match = value.trim().match(/^(\d+(?:\.\d+)?)\s?(k|m|g|t)?b?/i);
  642. if (match && match[1]) break;
  643. default:
  644. throw new TypeError('invalid value');
  645. }
  646. };
  647. validate.toJSON = fn.toJSON;
  648. const setter = function(value) {
  649. let match;
  650. switch (typeof value) {
  651. case 'string':
  652. match = value.trim().match(/^(\d+(?:\.\d+)?)\s?(k|m|g|t)?b?/i);
  653. return +match[1] * Math.pow(1024, match[2] ? ['', 'k', 'm', 'g', 't'].indexOf(match[2].toLowerCase()) : undefined);
  654. case 'number':
  655. return value;
  656. default:
  657. throw new TypeError('invalid value');
  658. }
  659. };
  660. setter.toJSON = fn.toJSON;
  661. const input = new Input({
  662. name: 'file-size',
  663. label: 'File size (bytes)',
  664. description: 'Here is size of uploaded file in bytes',
  665. value: 1023,
  666. key: '1G',
  667. type: 'radio',
  668. onChange,
  669. validate,
  670. setter,
  671. });
  672. list.add(input);
  673. console.log('item: ', JSON.stringify(input, null, 2));
  674. console.log('list: ', JSON.stringify(list, null, 2));
  675. list.setValue('file-size', '1 GB');
  676. console.log('item.value: ', input.value);
  677. const radio = new Radio({ name: 'file-size', value: '1G', onChange, validate, setter });
  678. radio.add(input);
  679. radio.add(new Input({
  680. name: 'file-size',
  681. key: '10MB',
  682. label: 'File size (bytes)',
  683. value: '10 MB',
  684. type: 'radio',
  685. onChange,
  686. validate,
  687. setter,
  688. }));
  689. radio.changeItem('10MB');
  690. radio.value;
  691. console.log('radio: ', JSON.stringify(radio, null, 2));
  692. function TabItem({
  693. mode, type, name, label, description, value, onChange, validate, setter,
  694. } = {}) {
  695. let Item;
  696. switch (type) {
  697. case 'radio':
  698. Item = Radio;
  699. break;
  700. case 'select':
  701. Item = Select;
  702. break;
  703. default:
  704. Item = Input;
  705. }
  706. Object.defineProperty(this, 'Class', { value: Item });
  707. this.Class.call(this, arguments[0]);
  708. }
  709. Object.defineProperty(TabItem.prototype, 'mode', Mode.descriptor);
  710. TabItem.prototype.assign = function() {
  711. return this.Class.prototype.assign.apply(this, arguments);
  712. };
  713. TabItem.parse = function(elem) {
  714. if (elem instanceof Option) {
  715. return elem;
  716. }
  717. if (typeof elem !== 'object') {
  718. return new Option();
  719. }
  720. const { type } = elem;
  721. if (type === 'radio') {
  722. const opt = new TabItem(elem);
  723. const { data = {}, order = [] } = elem;
  724. for (const key of order) {
  725. opt.add(data[key]);
  726. }
  727. return opt;
  728. }
  729. return new TabItem(elem);
  730. };
  731. TabItem.prototype.toJSON = function() {
  732. return this.Class.prototype.toJSON.apply(this, arguments);
  733. };
  734. for (const fn of ['add', 'update', 'remove', 'setValue', 'getValue', 'changeItem']) {
  735. TabItem.prototype[fn] = function() {
  736. if (this.Class === Radio) {
  737. return Radio.prototype[fn].apply(this, arguments);
  738. }
  739. };
  740. }
  741. const opt = new TabItem({ mode: Mode.ADVANCED, type: 'radio', name: 'file-size', value: '10.5 MB', onChange, validate, setter });
  742. opt.add({
  743. key: '1MB', value: '1MB',
  744. }, {
  745. key: '2MB', value: '2MB',
  746. }, {
  747. key: '1Gb', value: '1GB',
  748. }, {
  749. key: '2KB', value: '2K',
  750. });
  751. opt.changeItem('2kb');
  752. console.log('================== value: ', opt.value);
  753. const opt2 = new TabItem({ mode: Mode.ADVANCED, type: 'text', name: 'file-name', value: 'my_file.txt', onChange });
  754. console.log('================== opt2: ', JSON.stringify(opt2, null, 2));
  755. console.log('================== opt: ', JSON.stringify(opt, null, 2));
  756. opt2.value = 'my_file_xxx.out';
  757. console.log('================== opt2: ', JSON.stringify(opt2, null, 2));
  758. const json = JSON.stringify({ setter }, null, 2);
  759. console.log('{ setter }: ', json);
  760. const parsed = JSON.parse(json, reviver);
  761. console.log('parsed.setter: ', parsed.setter);
  762. try {
  763. if (1024 === parsed.setter('1Kb')) {
  764. console.log('PASSED');
  765. } else {
  766. console.log('FAILED');
  767. }
  768. } catch (err) {
  769. console.log('setter error: ', err);
  770. }
  771. const optJson = JSON.stringify(opt2, null, 2);
  772. const parsedOpt = JSON.parse(optJson, reviver);
  773. const opt3 = TabItem.parse(parsedOpt);
  774. console.log('opt3: ', opt3);
  775. function Tab({ name, key, label, description } = {}) {
  776. this.name = name;
  777. this.key = key || name;
  778. this.label = label;
  779. this.description = description;
  780. List.call(this, TabItem);
  781. }
  782. for (const fn of ['add', 'update', 'remove', 'setValue', 'getValue']) {
  783. Tab.prototype[fn] = function() {
  784. return List.prototype[fn].apply(this, arguments);
  785. };
  786. }
  787. Tab.prototype.assign = function(elem) {
  788. throw new Error('TODO');
  789. }; // TODO
  790. Tab.prototype.toJSON = function() {
  791. const { name, key, label, description } = this;
  792. return extend({
  793. name, key, label, description,
  794. }, List.prototype.toJSON.call(this));
  795. };
  796. Tab.parse = function(elem) {
  797. if (elem instanceof Tab) {
  798. return elem;
  799. }
  800. if (typeof elem !== 'object') {
  801. return new Tab();
  802. }
  803. const tb = new Tab(elem);
  804. const { data = {}, order = [] } = elem;
  805. for (const key of order) {
  806. tb.add(data[key]);
  807. }
  808. return tb;
  809. };
  810. const tab = new Tab({ name: 'general', label: 'General', description: 'Here are general options' });
  811. tab.add({ name: 'file-owner', type: 'text', value: 'Enakin Skywalker' });
  812. tab.add({ name: 'file-size', type: 'radio', value: '1Mb', validate, setter });
  813. tab.data['file-size'].add({
  814. key: '1Mb', value: '1M',
  815. }, {
  816. key: '10Mb', value: '10M',
  817. }, {
  818. key: '1Gb', value: '1GB',
  819. });
  820. const tabJson = JSON.stringify(tab, null, 2);
  821. console.log('tab: ', tabJson);
  822. const tab2 = Tab.parse(JSON.parse(tabJson, reviver));
  823. console.log('tab2: ', tab2);
  824. function Interface({
  825. mode: modeName = Mode.NORMAL, title = 'Interface', version = '0.1.0', onChangeMode = function(){},
  826. } = {}) {
  827. const mode = new Mode(modeName, null, onChangeMode);
  828. Object.defineProperty(this, 'mode', {
  829. get: function() { return mode.name; },
  830. set: function(n) { mode.name = n; },
  831. enumerable: true,
  832. });
  833. this.title = title;
  834. this.version = version;
  835. this.onChangeMode = onChangeMode;
  836. List.call(this, Tab);
  837. }
  838. for (const fn of ['add', 'update', 'remove']) {
  839. Interface.prototype[fn] = function() {
  840. return List.prototype[fn].apply(this, arguments);
  841. };
  842. }
  843. Interface.prototype.toJSON = function() {
  844. const { mode, title, version } = this;
  845. return extend({
  846. mode, title, version,
  847. }, List.prototype.toJSON.call(this));
  848. };
  849. Interface.parse = function(elem) {
  850. if (elem instanceof Interface) {
  851. return elem;
  852. }
  853. if (typeof elem !== 'object') {
  854. return new Interface();
  855. }
  856. const intf = new Interface(elem);
  857. const { data = {}, order = [] } = elem;
  858. for (const key of order) {
  859. intf.add(data[key]);
  860. }
  861. return intf;
  862. };
  863. const iface = new Interface({
  864. onChangeMode: function(error, mode) {
  865. if (error) {
  866. console.log('onChangeMode: ', error);
  867. return;
  868. }
  869. console.log('new mode: ', mode);
  870. },
  871. });
  872. iface.add(tab);
  873. tab2.name = 'advanced';
  874. iface.add(tab2);
  875. const ifaceJson = JSON.stringify(iface, null, 2);
  876. console.log('interface: ', ifaceJson);
  877. const iface2 = Interface.parse(JSON.parse(ifaceJson));
  878. console.log('interface2: ', iface2);
  879.  
  880. function extend(target) {
  881. target = target || {};
  882. const args = Array.prototype.slice.call(arguments, 1);
  883. for (const arg of args) {
  884. for (const key of Object.keys(arg)) {
  885. target[key] = arg[key];
  886. }
  887. }
  888. return target;
  889. }
  890. const { ESModules = {} } = window;
  891. ESModules.UIModules = {
  892. Mode,
  893. Datum,
  894. Input,
  895. Option,
  896. List,
  897. Radio,
  898. Select,
  899. Tab,
  900. TabItem,
  901. Interface,
  902. functionToJSON: fn.toJSON,
  903. functionReviver: reviver,
  904. memoryValidator: validate,
  905. memorySetter: setter,
  906. };
  907. window.ESModules = ESModules;
  908. })(window)

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址