hwm_armory_framework

Хелпер для других скриптов склада

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/457946/1239339/hwm_armory_framework.js

  1. // ==UserScript==
  2. // @name hwm_armory_framework
  3. // @namespace https://github.com/bonArt0/hwm_scripts
  4. // @version 1.5.1
  5. // @description Helper for other hwm_armory scripts
  6. // @author bonArt
  7. // @license GPL-3.0-only
  8. // @icon https://dcdn.heroeswm.ru/i/btns/job_fl_btn_warehouse.png
  9. // @match https://*.heroeswm.ru/sklad_info.php?*
  10. // @match https://178.248.235.15/sklad_info.php?*
  11. // @match https://www.lordswm.com/sklad_info.php?*
  12. // @match https://my.lordswm.com/sklad_info.php?*
  13. // @supportURL https://www.heroeswm.ru/sms-create.php?mailto_id=117282
  14. // ==/UserScript==
  15.  
  16. /**
  17. * @enum
  18. */
  19. const FrameworkClassNames = {
  20. ARMORY_BOX: 'afw_armory_box',
  21. OVERVIEW_BOX: 'afw_armory_overview_box',
  22. INFO_BOX: 'afw_armory_info_box',
  23. ACCOUNT_BOX: 'afw_armory_account_box',
  24. ONLINE_TOGGLE_BOX: 'afw_armory_online_toggle_box',
  25. MANAGEMENT_TOGGLE_BOX: 'afw_armory_management_toggle_box',
  26. SECTORS_BOX: 'afw_armory_sectors_box',
  27. MANAGEMENT_BOX: 'afw_armory_management_box',
  28. MANAGEMENT_HEADER_BOX: 'afw_armory_management_header_box',
  29. MANAGEMENT_HEADER_PUTS_BOX: 'afw_armory_management_header_puts_box',
  30. MANAGEMENT_HEADER_BALANCE_BOX: 'afw_armory_management_header_balance_box',
  31. MANAGEMENT_HEADER_BATTLES_BOX: 'afw_armory_management_header_battles_box',
  32. MANAGEMENT_HEADER_SMITHS_BOX: 'afw_armory_management_header_smiths_box',
  33. MANAGEMENT_HEADER_CAPACITY_BOX: 'afw_armory_management_header_capacity_box',
  34. MANAGEMENT_BODY_BOX: 'afw_armory_management_body_box',
  35. MANAGEMENT_BODY_PUTS_BOX: 'afw_armory_management_body_puts_box',
  36. MANAGEMENT_BODY_BALANCE_BOX: 'afw_armory_management_body_balance_box',
  37. MANAGEMENT_BODY_BATTLES_BOX: 'afw_armory_management_body_battles_box',
  38. MANAGEMENT_BODY_SMITHS_BOX: 'afw_armory_management_body_smiths_box',
  39. MANAGEMENT_BODY_CAPACITY_BOX: 'afw_armory_management_body_capacity_box',
  40. TAKES_BOX: 'afw_armory_takes_box',
  41. TAKES_REPAIRS_HEADER_BOX: 'afw_armory_takes_repairs_header_box',
  42. TAKES_REPAIRS_BODY_BOX: 'afw_armory_takes_repairs_body_box',
  43. TAKES_LEASES_HEADER_BOX: 'afw_armory_takes_leases_header_box',
  44. TAKES_LEASES_BODY_BOX: 'afw_armory_takes_leases_body_box',
  45. TABS_BOX: 'afw_armory_tabs_box',
  46. DESCRIPTION_BOX: 'afw_armory_description_box',
  47. DESCRIPTION_FORM_BOX: 'afw_armory_description_form',
  48. ARTS_BOX: 'afw_armory_arts_box',
  49. ARTS_LIST_BOX: 'afw_armory_arts_list_box',
  50. ARTS_ROW_BOX: 'afw_armory_arts_row_box',
  51. ARTS_HEADER_BOX: 'afw_armory_arts_header_box',
  52. ARTS_FOOTER_BOX: 'afw_armory_arts_footer_box',
  53. };
  54.  
  55. /**
  56. * @enum
  57. */
  58. const ArmoryTab = {
  59. TAB_DESCRIPTION: 0,
  60. TAB_WEAPON: 1,
  61. TAB_ARMOR: 2,
  62. TAB_JEWELRY: 3,
  63. TAB_BACKPACK: 4,
  64. TAB_SETS: 5,
  65. TAB_ON_LEASE: 6,
  66. TAB_UNAVAILABLE: 7,
  67. };
  68.  
  69. /**
  70. * @type {ArmoryFramework} _ArmoryFrameworkInstance
  71. * @private
  72. */
  73. let _ArmoryFrameworkInstance;
  74.  
  75. /* <editor-fold desc="errors"> */
  76.  
  77. class AlreadyInitializedError extends Error {
  78. constructor() {
  79. super();
  80.  
  81. this.message = 'Framework already initialized';
  82. }
  83. }
  84.  
  85. class UnsupportedFeatureError extends Error {
  86. constructor(feature) {
  87. super();
  88.  
  89. this.message = `Framework doesn't support ${feature} yet`;
  90. }
  91. }
  92.  
  93. class FrameworkError extends Error {
  94. /**
  95. * @type {object}
  96. * @public
  97. */
  98. context = {};
  99.  
  100. constructor(message, context) {
  101. super(message);
  102.  
  103. this.context = context;
  104. }
  105. }
  106.  
  107. class LayoutError extends FrameworkError {
  108. constructor(context) {
  109. super(`Something happen with game layout`, context);
  110. }
  111. }
  112.  
  113. class BoxMissedException extends Error {}
  114.  
  115. /* </editor-fold> */
  116.  
  117. class ArmoryFramework {
  118. /**
  119. * @type {boolean}
  120. * @private
  121. */
  122. _initialized = false;
  123.  
  124. /**
  125. * @type {boolean}
  126. * @private
  127. */
  128. _isManagementMode;
  129.  
  130. /**
  131. * @type {ArmoryTab}
  132. */
  133. activeTab = ArmoryTab.TAB_DESCRIPTION;
  134.  
  135. /**
  136. * @type {ArmoryBox}
  137. * @public
  138. * @readonly
  139. */
  140. armory;
  141.  
  142. /**
  143. * @return {ArmoryFramework|null}
  144. */
  145. static init() {
  146. if (!_ArmoryFrameworkInstance || !_ArmoryFrameworkInstance?._initialized) {
  147. console.info('Armory Framework initialization started');
  148.  
  149. try {
  150. _ArmoryFrameworkInstance = new ArmoryFramework();
  151. } catch (e) {
  152. if (e instanceof UnsupportedFeatureError) {
  153. console.warn(e.message);
  154. return null;
  155. }
  156.  
  157. if (e instanceof AlreadyInitializedError) {
  158. console.info(e.message);
  159. return _ArmoryFrameworkInstance;
  160. }
  161.  
  162. console.error('Something happen while framework initializing', e.context);
  163. throw e;
  164. }
  165.  
  166. console.info('Armory Framework initialized');
  167. }
  168.  
  169. return _ArmoryFrameworkInstance;
  170. }
  171.  
  172. /**
  173. * @private use ArmoryFramework.init()
  174. */
  175. constructor() {
  176. this._initFramework();
  177. }
  178.  
  179. /**
  180. * @throws {Error} on any init failure
  181. * @private
  182. */
  183. _initFramework() {
  184. if (!this.isManagementMode()) {
  185. throw new UnsupportedFeatureError('non-management mode');
  186. }
  187.  
  188. if (this._initialized) {
  189. throw new AlreadyInitializedError();
  190. }
  191.  
  192. this.activeTab = this._findActiveTab();
  193. this.armory = new ArmoryBox(this._findInitialAnchor(), this.activeTab);
  194. this._initialized = true;
  195. }
  196.  
  197. /**
  198. * @returns {boolean}
  199. */
  200. isManagementMode() {
  201. if (this._isManagementMode === undefined) {
  202. this._isManagementMode = document.body.innerHTML.search('sklad_rc_on=0') > -1;
  203. }
  204.  
  205. return this._isManagementMode;
  206. }
  207.  
  208. /**
  209. * @returns {HTMLImageElement}
  210. * @throws {LayoutError} on init failure
  211. * @private
  212. */
  213. _findInitialAnchor() {
  214. const initialAnchor = document.getElementsByClassName('rs').item(0);
  215. if (initialAnchor && initialAnchor.tagName === 'IMG') {
  216. return initialAnchor;
  217. }
  218.  
  219. throw new LayoutError({element: this, anchor: initialAnchor});
  220. }
  221.  
  222. /**
  223. * @returns {ArmoryTab}
  224. * @private
  225. */
  226. _findActiveTab() {
  227. const params = new URLSearchParams(window.location.search);
  228.  
  229. if (!params.has('cat') || +params.get('cat') < 0) {
  230. return ArmoryTab.TAB_DESCRIPTION;
  231. }
  232.  
  233. if (Number.isNaN(params.get('cat'))) {
  234. return ArmoryTab.TAB_WEAPON;
  235. }
  236.  
  237. switch (+params.get('cat')) {
  238. case 0:
  239. return ArmoryTab.TAB_WEAPON;
  240. case 1:
  241. return ArmoryTab.TAB_ARMOR;
  242. case 2:
  243. return ArmoryTab.TAB_JEWELRY;
  244. case 3:
  245. return ArmoryTab.TAB_SETS;
  246. case 4:
  247. return ArmoryTab.TAB_ON_LEASE;
  248. case 5:
  249. return ArmoryTab.TAB_UNAVAILABLE;
  250. case 6:
  251. default:
  252. return ArmoryTab.TAB_BACKPACK;
  253. }
  254. }
  255.  
  256. /* <editor-fold desc="common"> */
  257.  
  258. /**
  259. * @param {string} name
  260. * @param {string} innerHTML
  261. * @returns {HTMLLabelElement}
  262. */
  263. buildCheckboxLabel(name, innerHTML) {
  264. const checkbox = document.createElement('input');
  265. checkbox.type = 'checkbox';
  266. checkbox.name = name;
  267.  
  268. const label = document.createElement('label');
  269. label.append(checkbox);
  270. label.append(innerHTML);
  271.  
  272. return label;
  273. }
  274.  
  275. /* </editor-fold> */
  276. }
  277.  
  278. /**
  279. * @abstract
  280. */
  281. class Box {
  282. /**
  283. * @type {HTMLElement}
  284. * @public
  285. * @readonly
  286. */
  287. box;
  288.  
  289. /**
  290. * @param {HTMLElement} anchor
  291. * @throws {LayoutError} on init failure
  292. */
  293. constructor(anchor) {
  294. this._initBox(anchor);
  295. }
  296.  
  297. /**
  298. * @throws {LayoutError} on init failure
  299. * @protected
  300. */
  301. _initBox(anchor) {
  302. const box = this._findBox(anchor);
  303.  
  304. if (box?.tagName === this._getBoxTag()) {
  305. box.classList.add(this._getBoxClassName());
  306. this.box = box;
  307. return;
  308. }
  309.  
  310. throw new LayoutError({element: this, box: box, anchor: anchor});
  311. }
  312.  
  313. /**
  314. * @return {HTMLElement}
  315. * @abstract
  316. * @protected
  317. */
  318. _findBox(anchor) {}
  319.  
  320. /**
  321. * @todo replace name
  322. * @return {string}
  323. * @abstract
  324. * @protected
  325. */
  326. _getBoxTag() {}
  327.  
  328. /**
  329. * @return {string}
  330. * @abstract
  331. * @protected
  332. */
  333. _getBoxClassName() {}
  334. }
  335.  
  336. /**
  337. * @abstract
  338. */
  339. class TableSectionBasedBox extends Box {
  340. _getBoxTag() {
  341. return 'TBODY';
  342. }
  343. }
  344.  
  345. /**
  346. * @abstract
  347. */
  348. class TableRowBasedBox extends Box {
  349. _getBoxTag() {
  350. return 'TR';
  351. }
  352. }
  353.  
  354. /**
  355. * @abstract
  356. */
  357. class TableCellBasedBox extends Box {
  358. _getBoxTag() {
  359. return 'TD';
  360. }
  361. }
  362.  
  363. /**
  364. * @abstract
  365. */
  366. class FormBasedBox extends Box {
  367. _getBoxTag() {
  368. return 'FORM';
  369. }
  370. }
  371.  
  372. class ArmoryBox extends TableCellBasedBox {
  373. /**
  374. * @type {OverviewBox}
  375. * @public
  376. * @readonly
  377. */
  378. overview;
  379.  
  380. /**
  381. * @type {ManagementBox}
  382. * @public
  383. * @readonly
  384. */
  385. management;
  386.  
  387. /**
  388. * @type {TakesBox|null}
  389. * @public
  390. * @readonly
  391. */
  392. takes;
  393.  
  394. /**
  395. * @type {TabsBox}
  396. * @public
  397. * @readonly
  398. */
  399. tabs;
  400.  
  401. /**
  402. * @type {DescriptionContainer|null}
  403. * @public
  404. * @readonly
  405. */
  406. description;
  407.  
  408. /**
  409. * @type {ArtsBox|null}
  410. * @public
  411. * @readonly
  412. */
  413. arts;
  414.  
  415. /**
  416. * @param {HTMLElement} anchor
  417. * @param {ArmoryTab} activeTab
  418. * @throws {Error} on init failure
  419. */
  420. constructor(anchor, activeTab) {
  421. super(anchor);
  422.  
  423. const box = this.box;
  424.  
  425. this.overview = new OverviewBox(box);
  426. this.management = new ManagementBox(box);
  427.  
  428. try {
  429. this.takes = new TakesBox(box);
  430. } catch (e) {
  431. if (!(e instanceof BoxMissedException)) {
  432. throw e;
  433. }
  434. this.takes = null;
  435. }
  436.  
  437. this.tabs = new TabsBox(box);
  438.  
  439. switch (activeTab) {
  440. case ArmoryTab.TAB_DESCRIPTION:
  441. this.description = new DescriptionContainer(box);
  442. this.arts = null;
  443. break;
  444. case ArmoryTab.TAB_ON_LEASE:
  445. case ArmoryTab.TAB_WEAPON:
  446. case ArmoryTab.TAB_ARMOR:
  447. case ArmoryTab.TAB_JEWELRY:
  448. case ArmoryTab.TAB_BACKPACK:
  449. case ArmoryTab.TAB_SETS:
  450. case ArmoryTab.TAB_UNAVAILABLE:
  451. this.description = null;
  452. this.arts = new ArtsBox(box, activeTab);
  453. }
  454. }
  455.  
  456. /**
  457. * @param {HTMLTableCellElement} anchor
  458. * @return {HTMLTableElement}
  459. */
  460. _findBox(anchor) {
  461. return anchor
  462. ?.parentElement // td#0, armory account clear part
  463. ?.parentElement // tr#0
  464. ?.parentElement // tbody#0
  465. ?.parentElement // table#0
  466. ?.parentElement // td#0, armory account
  467. ?.parentElement // tr#0
  468. ?.parentElement // tbody#0
  469. ?.parentElement // table#0
  470. ?.parentElement // td#1
  471. ?.parentElement // tr#0
  472. ?.parentElement // tbody#0, armory overview
  473. ?.parentElement // table#0
  474. ?.parentElement; // td#0, armory box
  475. }
  476.  
  477. _getBoxClassName() {
  478. return FrameworkClassNames.ARMORY_BOX;
  479. }
  480. }
  481.  
  482. /* <editor-fold desc="armory overview"> */
  483.  
  484. class OverviewBox extends TableSectionBasedBox {
  485. /**
  486. * @type {OverviewInfoBox}
  487. * @public
  488. * @readonly
  489. */
  490. info;
  491.  
  492. /**
  493. * @type {OverviewAccountBox}
  494. * @public
  495. * @readonly
  496. */
  497. account;
  498.  
  499. /**
  500. * @type {OverviewOnlineToggleBox}
  501. * @public
  502. * @readonly
  503. */
  504. onlineToggle;
  505.  
  506. /**
  507. * @type {OverviewManagementToggleBox}
  508. * @public
  509. * @readonly
  510. */
  511. managementToggle;
  512.  
  513. /**
  514. * @type {OverviewSectorsBox}
  515. * @public
  516. * @readonly
  517. */
  518. sectors;
  519.  
  520. constructor(anchor) {
  521. super(anchor);
  522.  
  523. const box = this.box;
  524.  
  525. this.info = new OverviewInfoBox(box);
  526. this.account = new OverviewAccountBox(box);
  527. this.onlineToggle = new OverviewOnlineToggleBox(box);
  528. this.managementToggle = new OverviewManagementToggleBox(box);
  529. this.sectors = new OverviewSectorsBox(box);
  530. }
  531.  
  532. /**
  533. * @param {HTMLTableCellElement} anchor
  534. * @return {HTMLTableElement}
  535. */
  536. _findBox(anchor) {
  537. return anchor
  538. ?.children.item(0) // table
  539. ?.children.item(0); // tbody
  540. }
  541.  
  542. _getBoxClassName() {
  543. return FrameworkClassNames.OVERVIEW_BOX;
  544. }
  545. }
  546.  
  547. class OverviewInfoBox extends TableCellBasedBox {
  548. /**
  549. * @returns {number}
  550. */
  551. getCurrentCapacity() {
  552. return +this.box.innerHTML.match(/<b>(\d+)<\/b> из \d+/)?.at(1);
  553. }
  554.  
  555. /**
  556. * @returns {number}
  557. */
  558. getTotalCapacity() {
  559. return +this.box.innerHTML.match(/<b>\d+<\/b> из (\d+)/).at(1);
  560. }
  561.  
  562. /**
  563. * @param {HTMLTableSectionElement} anchor
  564. * @return {HTMLTableCellElement}
  565. */
  566. _findBox(anchor) {
  567. return anchor
  568. ?.children.item(0) // tr
  569. ?.children.item(0); // td
  570. }
  571.  
  572. _getBoxClassName() {
  573. return FrameworkClassNames.INFO_BOX;
  574. }
  575. }
  576.  
  577. class OverviewAccountBox extends TableCellBasedBox {
  578. /**
  579. * @param {HTMLTableSectionElement} anchor
  580. * @return {HTMLTableElement}
  581. */
  582. _findBox(anchor) {
  583. return anchor
  584. ?.children.item(0) // tr
  585. ?.children.item(1); // td
  586. }
  587.  
  588. _getBoxClassName() {
  589. return FrameworkClassNames.ACCOUNT_BOX;
  590. }
  591. }
  592.  
  593. class OverviewOnlineToggleBox extends TableCellBasedBox {
  594. /**
  595. * @param {HTMLTableSectionElement} anchor
  596. * @return {HTMLTableCellElement}
  597. */
  598. _findBox(anchor) {
  599. return anchor
  600. ?.children.item(0) // tr
  601. ?.children.item(2); // td
  602. }
  603.  
  604. _getBoxClassName() {
  605. return FrameworkClassNames.ONLINE_TOGGLE_BOX;
  606. }
  607. }
  608.  
  609. class OverviewManagementToggleBox extends TableCellBasedBox {
  610. /**
  611. * @param {HTMLTableSectionElement} anchor
  612. * @return {HTMLTableCellElement}
  613. */
  614. _findBox(anchor) {
  615. return anchor
  616. ?.children.item(0) // tr
  617. ?.children.item(3); // td
  618. }
  619.  
  620. _getBoxClassName() {
  621. return FrameworkClassNames.MANAGEMENT_TOGGLE_BOX;
  622. }
  623. }
  624.  
  625. class OverviewSectorsBox extends TableCellBasedBox {
  626. /**
  627. * @param {HTMLTableSectionElement} anchor
  628. * @return {HTMLTableCellElement}
  629. */
  630. _findBox(anchor) {
  631. return anchor
  632. ?.children.item(1) // tr
  633. ?.children.item(0); // td
  634. }
  635.  
  636. _getBoxClassName() {
  637. return FrameworkClassNames.SECTORS_BOX;
  638. }
  639. }
  640.  
  641. /* </editor-fold> */
  642.  
  643. /* <editor-fold desc="armory management"> */
  644.  
  645. class ManagementBox extends TableSectionBasedBox {
  646. /**
  647. * @type {ManagementHeaderBox}
  648. * @public
  649. * @readonly
  650. */
  651. header;
  652.  
  653. /**
  654. * @type {ManagementBodyBox}
  655. * @public
  656. * @readonly
  657. */
  658. body;
  659.  
  660. constructor(anchor) {
  661. super(anchor);
  662.  
  663. const box = this.box;
  664.  
  665. this.header = new ManagementHeaderBox(box);
  666. this.body = new ManagementBodyBox(box);
  667. }
  668.  
  669. /**
  670. * @param {HTMLTableCellElement} anchor
  671. * @return {HTMLTableElement}
  672. */
  673. _findBox(anchor) {
  674. return anchor
  675. .children.item(1) // table
  676. .children.item(0); // tbody
  677. }
  678.  
  679. _getBoxClassName() {
  680. return FrameworkClassNames.MANAGEMENT_BOX;
  681. }
  682. }
  683.  
  684. class ManagementHeaderBox extends TableRowBasedBox {
  685. /**
  686. * @type {ManagementHeaderPutsBox}
  687. * @public
  688. * @readonly
  689. */
  690. puts;
  691.  
  692. /**
  693. * @type {ManagementHeaderBalanceBox}
  694. * @public
  695. * @readonly
  696. */
  697. balance;
  698.  
  699. /**
  700. * @type {ManagementHeaderBattlesBox}
  701. * @public
  702. * @readonly
  703. */
  704. battles;
  705.  
  706. /**
  707. * @type {ManagementHeaderSmithsBox}
  708. * @public
  709. * @readonly
  710. */
  711. smiths;
  712.  
  713. /**
  714. * @type {ManagementHeaderCapacityBox}
  715. * @public
  716. * @readonly
  717. */
  718. capacity;
  719.  
  720. constructor(anchor) {
  721. super(anchor);
  722.  
  723. this.puts = new ManagementHeaderPutsBox(this.box);
  724. this.balance = new ManagementHeaderBalanceBox(this.box);
  725. this.battles = new ManagementHeaderBattlesBox(this.box);
  726. this.smiths = new ManagementHeaderSmithsBox(this.box);
  727. this.capacity = new ManagementHeaderCapacityBox(this.box)
  728. }
  729.  
  730. /**
  731. * @param {HTMLTableSectionElement} anchor
  732. * @return {HTMLTableRowElement}
  733. */
  734. _findBox(anchor) {
  735. return anchor
  736. ?.children.item(0); // tr
  737. }
  738.  
  739. _getBoxClassName() {
  740. return FrameworkClassNames.MANAGEMENT_HEADER_BOX;
  741. }
  742. }
  743.  
  744. class ManagementHeaderPutsBox extends TableCellBasedBox {
  745. /**
  746. * @param {HTMLTableRowElement} anchor
  747. * @return {HTMLTableCellElement}
  748. * @private
  749. */
  750. _findBox(anchor) {
  751. return anchor
  752. .children.item(0); // td
  753. }
  754.  
  755. _getBoxClassName() {
  756. return FrameworkClassNames.MANAGEMENT_HEADER_PUTS_BOX;
  757. }
  758. }
  759.  
  760. class ManagementHeaderBalanceBox extends TableCellBasedBox {
  761. /**
  762. * @param {HTMLTableRowElement} anchor
  763. * @return {HTMLTableCellElement}
  764. * @private
  765. */
  766. _findBox(anchor) {
  767. return anchor
  768. .children.item(1); // td
  769. }
  770.  
  771. _getBoxClassName() {
  772. return FrameworkClassNames.MANAGEMENT_HEADER_BALANCE_BOX;
  773. }
  774. }
  775.  
  776. class ManagementHeaderBattlesBox extends TableCellBasedBox {
  777. /**
  778. * @param {HTMLTableRowElement} anchor
  779. * @return {HTMLTableCellElement}
  780. * @private
  781. */
  782. _findBox(anchor) {
  783. return anchor
  784. .children.item(2); // td
  785. }
  786.  
  787. _getBoxClassName() {
  788. return FrameworkClassNames.MANAGEMENT_HEADER_BATTLES_BOX;
  789. }
  790. }
  791.  
  792. class ManagementHeaderSmithsBox extends TableCellBasedBox {
  793. /**
  794. * @param {HTMLTableRowElement} anchor
  795. * @return {HTMLTableCellElement}
  796. * @private
  797. */
  798. _findBox(anchor) {
  799. return anchor
  800. .children.item(3); // td
  801. }
  802.  
  803. _getBoxClassName() {
  804. return FrameworkClassNames.MANAGEMENT_HEADER_SMITHS_BOX;
  805. }
  806. }
  807.  
  808. class ManagementHeaderCapacityBox extends TableCellBasedBox {
  809. /**
  810. * @param {HTMLTableRowElement} anchor
  811. * @return {HTMLTableCellElement}
  812. * @private
  813. */
  814. _findBox(anchor) {
  815. return anchor
  816. .children.item(4); // td
  817. }
  818.  
  819. _getBoxClassName() {
  820. return FrameworkClassNames.MANAGEMENT_HEADER_CAPACITY_BOX;
  821. }
  822. }
  823.  
  824. class ManagementBodyBox extends TableRowBasedBox {
  825. /**
  826. * @type {ManagementBodyPutsBox}
  827. * @public
  828. * @readonly
  829. */
  830. puts;
  831.  
  832. /**
  833. * @type {ManagementBodyBalanceBox}
  834. * @public
  835. * @readonly
  836. */
  837. balance;
  838.  
  839. /**
  840. * @type {ManagementBodyBattlesBox}
  841. * @public
  842. * @readonly
  843. */
  844. battles;
  845.  
  846. /**
  847. * @type {ManagementBodySmithsBox}
  848. * @public
  849. * @readonly
  850. */
  851. smiths;
  852.  
  853. /**
  854. * @type {ManagementBodyCapacityBox}
  855. * @public
  856. * @readonly
  857. */
  858. capacity;
  859.  
  860. constructor(anchor) {
  861. super(anchor);
  862.  
  863. this.puts = new ManagementBodyPutsBox(this.box);
  864. this.balance = new ManagementBodyBalanceBox(this.box);
  865. this.battles = new ManagementBodyBattlesBox(this.box);
  866. this.smiths = new ManagementBodySmithsBox(this.box);
  867. this.capacity = new ManagementBodyCapacityBox(this.box);
  868. }
  869.  
  870. /**
  871. * @param {HTMLTableSectionElement} anchor
  872. * @return {HTMLTableRowElement}
  873. */
  874. _findBox(anchor) {
  875. return anchor
  876. .children.item(1); // tr
  877. }
  878.  
  879. _getBoxClassName() {
  880. return FrameworkClassNames.MANAGEMENT_BODY_BOX;
  881. }
  882. }
  883.  
  884. class ManagementBodyPutsBox extends FormBasedBox {
  885. /**
  886. * @param {HTMLTableRowElement} anchor
  887. * @return {HTMLTableCellElement}
  888. * @private
  889. */
  890. _findBox(anchor) {
  891. return anchor
  892. .children.item(0) // td
  893. .children.item(0); // form
  894. }
  895.  
  896. _getBoxClassName() {
  897. return FrameworkClassNames.MANAGEMENT_BODY_PUTS_BOX;
  898. }
  899.  
  900. isDisabled() {
  901. return this.box.innerHTML.search('нет доступа') > -1;
  902. }
  903.  
  904. hideForm() {
  905. this.box.style.display = 'none';
  906. }
  907.  
  908. /**
  909. * @return {(number|string)[][]}
  910. */
  911. getArtsList() {
  912. const artsPutOptions= this.box.elements[2].options;
  913. const arts = Array.from(artsPutOptions).map((option) => [+option.value, option.innerHTML]);
  914. arts.shift(); // remove first "0" element
  915. return arts;
  916. }
  917.  
  918. /**
  919. * @return {number}
  920. */
  921. getArmoryId() {
  922. const id = this.box.children.item(0)?.value;
  923.  
  924. if (id === undefined) {
  925. throw new FrameworkError('Armory id not found', this);
  926. }
  927.  
  928. return +id;
  929. }
  930.  
  931. /**
  932. * @returns {string}
  933. */
  934. getArtsPutSign() {
  935. const sign = this.box.children.item(1)?.value;
  936.  
  937. if (sign === undefined) {
  938. throw new FrameworkError('Armory puts sign not found', this);
  939. }
  940.  
  941. return sign + '';
  942. }
  943. }
  944.  
  945. class ManagementBodyBalanceBox extends FormBasedBox {
  946. /**
  947. * @param {HTMLTableRowElement} anchor
  948. * @return {HTMLTableCellElement}
  949. * @private
  950. */
  951. _findBox(anchor) {
  952. return anchor
  953. .children.item(1) // td
  954. .children.item(0); // form
  955. }
  956.  
  957. _getBoxClassName() {
  958. return FrameworkClassNames.MANAGEMENT_BODY_BALANCE_BOX;
  959. }
  960. }
  961.  
  962. class ManagementBodyBattlesBox extends FormBasedBox {
  963. /**
  964. * @param {HTMLTableRowElement} anchor
  965. * @return {HTMLTableCellElement}
  966. * @private
  967. */
  968. _findBox(anchor) {
  969. return anchor
  970. .children.item(2) // td
  971. .children.item(0); // form
  972. }
  973.  
  974. _getBoxClassName() {
  975. return FrameworkClassNames.MANAGEMENT_BODY_BATTLES_BOX;
  976. }
  977. }
  978.  
  979. class ManagementBodySmithsBox extends FormBasedBox {
  980. /**
  981. * @param {HTMLTableRowElement} anchor
  982. * @return {HTMLTableCellElement}
  983. * @private
  984. */
  985. _findBox(anchor) {
  986. return anchor
  987. .children.item(3) // td
  988. .children.item(0); // form
  989. }
  990.  
  991. _getBoxClassName() {
  992. return FrameworkClassNames.MANAGEMENT_BODY_SMITHS_BOX;
  993. }
  994. }
  995.  
  996. class ManagementBodyCapacityBox extends FormBasedBox {
  997. /**
  998. * @param {HTMLTableRowElement} anchor
  999. * @return {HTMLTableCellElement}
  1000. * @private
  1001. */
  1002. _findBox(anchor) {
  1003. return anchor
  1004. .children.item(4) // td
  1005. .children.item(0); // form
  1006. }
  1007.  
  1008. _getBoxClassName() {
  1009. return FrameworkClassNames.MANAGEMENT_BODY_CAPACITY_BOX;
  1010. }
  1011. }
  1012.  
  1013. /* </editor-fold> */
  1014.  
  1015. /* <editor-fold desc="armory takes"> */
  1016.  
  1017. /**
  1018. * @todo group repairs and leases
  1019. */
  1020. class TakesBox extends TableSectionBasedBox {
  1021. /**
  1022. * @type {TakesRepairsContainer|null}
  1023. * @public
  1024. * @readonly
  1025. */
  1026. repairs;
  1027.  
  1028. /**
  1029. * @type {TakesLeasesContainer|null}
  1030. * @public
  1031. * @readonly
  1032. */
  1033. leases;
  1034.  
  1035. /**
  1036. * @param {HTMLElement} anchor
  1037. * @throws {Error} on init failure
  1038. * @throws {BoxMissedException} on empty box
  1039. */
  1040. constructor(anchor) {
  1041. // BoxMissedException
  1042. super(anchor);
  1043.  
  1044. const box = this.box;
  1045. if (box.children.length === 4) {
  1046. // box has both repairs and leases row pairs
  1047. this.repairs = new TakesRepairsContainer(box);
  1048. this.leases = new TakesLeasesContainer(box, false);
  1049. } else if (box.innerHTML.search('action=repair') > -1) {
  1050. // "Repair" button exists, so box has only repairs row pair
  1051. this.repairs = new TakesRepairsContainer(box);
  1052. this.leases = null;
  1053. } else {
  1054. // box has only leases row pair
  1055. this.repairs = null;
  1056. this.leases = new TakesLeasesContainer(box, true);
  1057. }
  1058. }
  1059.  
  1060. /**
  1061. * @param {HTMLTableCellElement} anchor
  1062. * @return {HTMLTableElement}
  1063. * @throws {BoxMissedException}
  1064. */
  1065. _findBox(anchor) {
  1066. const box = anchor
  1067. .children.item(2); // table
  1068.  
  1069. if (box.children.length > 0) {
  1070. return box.children.item(0); // tbody
  1071. }
  1072.  
  1073. // no arts in lease or smith
  1074. throw new BoxMissedException;
  1075. }
  1076.  
  1077. _getBoxClassName() {
  1078. return FrameworkClassNames.TAKES_BOX;
  1079. }
  1080. }
  1081.  
  1082. class TakesRepairsContainer {
  1083. /**
  1084. * @type {TakesRepairsHeaderBox}
  1085. * @public
  1086. * @readonly
  1087. */
  1088. header;
  1089.  
  1090. /**
  1091. * @type {TakesRepairsBodyBox}
  1092. * @public
  1093. * @readonly
  1094. */
  1095. body;
  1096.  
  1097. /**
  1098. * @param {HTMLElement} anchor
  1099. * @throws {Error} on init failure
  1100. */
  1101. constructor(anchor) {
  1102. this.header = new TakesRepairsHeaderBox(anchor, 0);
  1103. this.body = new TakesRepairsBodyBox(anchor, 1);
  1104. }
  1105. }
  1106.  
  1107. class TakesLeasesContainer {
  1108. /**
  1109. * @type {TakesLeasesHeaderBox}
  1110. * @public
  1111. * @readonly
  1112. */
  1113. header;
  1114.  
  1115. /**
  1116. * @type {TakesLeasesBodyBox}
  1117. * @public
  1118. * @readonly
  1119. */
  1120. body;
  1121.  
  1122. /**
  1123. * @param {HTMLElement} anchor
  1124. * @param {boolean} only
  1125. * @throws {Error} on init failure
  1126. */
  1127. constructor(anchor, only) {
  1128. this.header = new TakesLeasesHeaderBox(anchor, only ? 0 : 2);
  1129. this.body = new TakesLeasesBodyBox(anchor, only ? 1 : 3);
  1130. }
  1131. }
  1132.  
  1133. /**
  1134. * @abstract
  1135. */
  1136. class TakesRowBox extends TableCellBasedBox {
  1137. /**
  1138. * BEWARE OF MAGIC!
  1139. * For variadic TR id usage
  1140. * @todo refactor this
  1141. * @type {number}
  1142. * @private
  1143. */
  1144. static _currentRowId;
  1145.  
  1146. /**
  1147. * @param {HTMLElement} anchor
  1148. * @param {number} trId
  1149. */
  1150. constructor(anchor, trId) {
  1151. TakesRowBox._currentRowId = trId;
  1152. super(anchor);
  1153. }
  1154.  
  1155. /**
  1156. * @param {HTMLTableSectionElement} anchor
  1157. * @return {HTMLTableRowElement}
  1158. */
  1159. _findBox(anchor) {
  1160. return anchor
  1161. .children.item(TakesRowBox._currentRowId) // tr
  1162. .children.item(0); // td
  1163. }
  1164. }
  1165.  
  1166. class TakesRepairsHeaderBox extends TakesRowBox {
  1167. _getBoxClassName() {
  1168. return FrameworkClassNames.TAKES_REPAIRS_HEADER_BOX;
  1169. }
  1170. }
  1171.  
  1172. class TakesRepairsBodyBox extends TakesRowBox {
  1173. _getBoxClassName() {
  1174. return FrameworkClassNames.TAKES_REPAIRS_BODY_BOX;
  1175. }
  1176. }
  1177.  
  1178. class TakesLeasesHeaderBox extends TakesRowBox {
  1179. _getBoxClassName() {
  1180. return FrameworkClassNames.TAKES_LEASES_HEADER_BOX;
  1181. }
  1182. }
  1183.  
  1184. class TakesLeasesBodyBox extends TakesRowBox {
  1185. _getBoxClassName() {
  1186. return FrameworkClassNames.TAKES_LEASES_BODY_BOX;
  1187. }
  1188. }
  1189.  
  1190. /* </editor-fold> */
  1191.  
  1192. /* <editor-fold desc="armory tabs"> */
  1193.  
  1194. class TabsBox extends TableRowBasedBox {
  1195. /**
  1196. * @param {HTMLTableCellElement} anchor
  1197. * @return {HTMLTableElement}
  1198. */
  1199. _findBox(anchor) {
  1200. return anchor
  1201. .children.item(3) // table
  1202. .children.item(0) // tbody
  1203. .children.item(0); // tr
  1204. }
  1205.  
  1206. _getBoxClassName() {
  1207. return FrameworkClassNames.TABS_BOX;
  1208. }
  1209. }
  1210.  
  1211. /* </editor-fold> */
  1212.  
  1213. /* <editor-fold desc="armory description"> */
  1214.  
  1215. class DescriptionContainer {
  1216. /**
  1217. * @type {DescriptionBox}
  1218. * @public
  1219. * @readonly
  1220. */
  1221. text;
  1222.  
  1223. /**
  1224. * @type {DescriptionFormBox}
  1225. * @public
  1226. * @readonly
  1227. */
  1228. form;
  1229.  
  1230. /**
  1231. * @param {HTMLElement} anchor
  1232. * @throws {Error} on init failure
  1233. */
  1234. constructor(anchor) {
  1235. this.text = new DescriptionBox(anchor);
  1236. this.form = new DescriptionFormBox(anchor);
  1237. }
  1238. }
  1239.  
  1240. class DescriptionBox extends TableCellBasedBox {
  1241. /**
  1242. * @param {HTMLTableCellElement} anchor
  1243. * @return {HTMLTableElement}
  1244. */
  1245. _findBox(anchor) {
  1246. return anchor
  1247. .children.item(4) // table
  1248. .children.item(0) // tbody
  1249. .children.item(0) // tr
  1250. .children.item(0); // td
  1251. }
  1252.  
  1253. _getBoxClassName() {
  1254. return FrameworkClassNames.DESCRIPTION_BOX;
  1255. }
  1256. }
  1257.  
  1258. class DescriptionFormBox extends FormBasedBox {
  1259. /**
  1260. * @param {HTMLTableCellElement} anchor
  1261. * @return {HTMLFormElement}
  1262. */
  1263. _findBox(anchor) {
  1264. return anchor
  1265. .children.item(5); // form
  1266. }
  1267.  
  1268. _getBoxClassName() {
  1269. return FrameworkClassNames.DESCRIPTION_FORM_BOX;
  1270. }
  1271. }
  1272.  
  1273. /* </editor-fold> */
  1274.  
  1275. /* <editor-fold desc="armory arts"> */
  1276.  
  1277. class ArtsBox extends TableCellBasedBox {
  1278. /**
  1279. * @type {ArtsListBox|null}
  1280. * @public
  1281. * @readonly
  1282. */
  1283. list;
  1284.  
  1285. /**
  1286. * @param {HTMLTableCellElement} anchor
  1287. * @param {ArmoryTab} activeTab
  1288. */
  1289. constructor(anchor, activeTab) {
  1290. super(anchor);
  1291.  
  1292. try {
  1293. this.list = new ArtsListBox(this.box, activeTab);
  1294. } catch (e) {
  1295. if (!(e instanceof BoxMissedException)) {
  1296. throw e;
  1297. }
  1298. this.list = null;
  1299. }
  1300. }
  1301.  
  1302. /**
  1303. * @param {HTMLTableCellElement} anchor
  1304. * @return {HTMLTableElement}
  1305. */
  1306. _findBox(anchor) {
  1307. return anchor
  1308. .children.item(4) // table
  1309. .children.item(0) // tbody
  1310. .children.item(0) // tr
  1311. .children.item(0); // td
  1312. }
  1313.  
  1314. _getBoxClassName() {
  1315. return FrameworkClassNames.ARTS_BOX;
  1316. }
  1317. }
  1318.  
  1319. class ArtsListBox extends TableSectionBasedBox {
  1320. /**
  1321. * @type {ArtsHeaderBox}
  1322. * @public
  1323. * @readonly
  1324. */
  1325. header;
  1326.  
  1327. /**
  1328. * @type {ArtsFooterBox|null}
  1329. * @public
  1330. * @readonly
  1331. */
  1332. footer;
  1333.  
  1334. /**
  1335. * @type {ArtsRowBox[]}
  1336. * @public
  1337. * @readonly
  1338. */
  1339. rows;
  1340.  
  1341. /**
  1342. * @param {HTMLTableCellElement} anchor
  1343. * @param {ArmoryTab} activeTab
  1344. * @throws {BoxMissedException}
  1345. */
  1346. constructor(anchor, activeTab) {
  1347. // BoxMissedException
  1348. super(anchor);
  1349.  
  1350. const rows = Array.from(this.box.children)
  1351. .filter(ArtsListBox._filterTagsCallback.bind({tag: 'TR'}));
  1352.  
  1353. this.header = new ArtsHeaderBox(rows.shift());
  1354. this.footer = activeTab === ArmoryTab.TAB_UNAVAILABLE ? new ArtsFooterBox(rows.pop()) : null;
  1355. this.rows = rows.map((row) => new ArtsRowBox(row));
  1356. }
  1357.  
  1358. /**
  1359. * @param {HTMLTableCellElement} anchor
  1360. * @return {HTMLTableElement}
  1361. * @throws {BoxMissedException}
  1362. */
  1363. _findBox(anchor) {
  1364. const listContents = Array.from(anchor.children)
  1365. .filter(ArtsListBox._filterTagsCallback.bind({tag: 'TABLE'}))
  1366. if (listContents.length > 0) {
  1367. return listContents
  1368. .shift() // table
  1369. .children.item(0); // tbody
  1370. }
  1371.  
  1372. // no arts in list
  1373. throw new BoxMissedException;
  1374. }
  1375.  
  1376. _getBoxClassName() {
  1377. return FrameworkClassNames.ARTS_LIST_BOX;
  1378. }
  1379.  
  1380. static _filterTagsCallback(row) {
  1381. return row.tagName === this.tag; // there is a <script> here besides correct tags
  1382. }
  1383. }
  1384.  
  1385. class ArtsRowBox extends TableRowBasedBox {
  1386. /**
  1387. * @param {HTMLTableRowElement} anchor
  1388. * @return {HTMLTableRowElement}
  1389. */
  1390. _findBox(anchor) {
  1391. return anchor;
  1392. }
  1393.  
  1394. _getBoxClassName() {
  1395. return FrameworkClassNames.ARTS_ROW_BOX;
  1396. }
  1397. }
  1398.  
  1399. class ArtsHeaderBox extends ArtsRowBox {
  1400. _getBoxClassName() {
  1401. return FrameworkClassNames.ARTS_HEADER_BOX;
  1402. }
  1403. }
  1404.  
  1405. class ArtsFooterBox extends ArtsRowBox {
  1406. _getBoxClassName() {
  1407. return FrameworkClassNames.ARTS_FOOTER_BOX;
  1408. }
  1409. }
  1410.  
  1411. /* </editor-fold> */

QingJ © 2025

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