chuijs 4.0.2 → 4.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/CHANGELOG.md +597 -0
  2. package/README.md +43 -644
  3. package/framework/appLayout/default/chui_app_layout.js +602 -496
  4. package/framework/appLayout/default/global_style.css +288 -97
  5. package/framework/appLayout/default/main_theme_style.css +188 -101
  6. package/framework/components/chui_accordion/accordion.js +38 -35
  7. package/framework/components/chui_accordion/styles.css +13 -12
  8. package/framework/components/chui_action_sheet/action_sheet.js +93 -64
  9. package/framework/components/chui_action_sheet/styles.css +12 -5
  10. package/framework/components/chui_actions_bar/actions_bar.js +338 -0
  11. package/framework/components/chui_actions_bar/styles.css +139 -0
  12. package/framework/components/chui_affix/affix.js +16 -11
  13. package/framework/components/chui_affix/styles.css +5 -0
  14. package/framework/components/chui_alert/alert.js +34 -19
  15. package/framework/components/chui_alert/styles.css +7 -0
  16. package/framework/components/chui_avatar/avatar.js +36 -35
  17. package/framework/components/chui_avatar/styles.css +1 -1
  18. package/framework/components/chui_avatar_group/avatar_group.js +25 -23
  19. package/framework/components/chui_avatar_group/styles.css +1 -1
  20. package/framework/components/chui_backtop/backtop.js +60 -21
  21. package/framework/components/chui_backtop/styles.css +21 -0
  22. package/framework/components/chui_badge/badge.js +30 -28
  23. package/framework/components/chui_badge/styles.css +6 -6
  24. package/framework/components/chui_banner/banner.js +222 -0
  25. package/framework/components/chui_banner/styles.css +170 -0
  26. package/framework/components/chui_breadcrumb/breadcrumb.js +15 -13
  27. package/framework/components/chui_breadcrumb/styles.css +1 -1
  28. package/framework/components/chui_button/button.js +52 -36
  29. package/framework/components/chui_button/styles.css +7 -7
  30. package/framework/components/chui_calendar/calendar.js +18 -16
  31. package/framework/components/chui_card/card.js +52 -32
  32. package/framework/components/chui_card_scroll/card_scroll.js +169 -0
  33. package/framework/components/chui_card_scroll/styles.css +86 -0
  34. package/framework/components/chui_chat/chat.js +140 -79
  35. package/framework/components/chui_chat/styles.css +18 -7
  36. package/framework/components/chui_clipboard_history/clipboard_history.js +53 -39
  37. package/framework/components/chui_clipboard_history/styles.css +13 -9
  38. package/framework/components/chui_code/code.js +10 -10
  39. package/framework/components/chui_code/styles.css +4 -4
  40. package/framework/components/chui_code_editor/code_editor.js +73 -57
  41. package/framework/components/chui_code_editor/styles.css +6 -5
  42. package/framework/components/chui_command_palette/command_palette.js +87 -64
  43. package/framework/components/chui_command_palette/styles.css +5 -5
  44. package/framework/components/chui_console/console.js +13 -13
  45. package/framework/components/chui_console/styles.css +6 -3
  46. package/framework/components/chui_content_block/content_block.js +35 -21
  47. package/framework/components/chui_content_block/styles.css +2 -2
  48. package/framework/components/chui_context_menu/context_menu.js +171 -58
  49. package/framework/components/chui_context_menu/styles.css +82 -8
  50. package/framework/components/chui_custom_element/custom_element.js +25 -18
  51. package/framework/components/chui_data_grid/data_grid.js +633 -471
  52. package/framework/components/chui_data_grid/styles.css +24 -22
  53. package/framework/components/chui_descriptions/descriptions.js +34 -17
  54. package/framework/components/chui_descriptions/styles.css +1 -1
  55. package/framework/components/chui_details/details.js +42 -36
  56. package/framework/components/chui_details/styles.css +12 -11
  57. package/framework/components/chui_diff_viewer/diff_viewer.js +172 -147
  58. package/framework/components/chui_diff_viewer/styles.css +10 -10
  59. package/framework/components/chui_divider/divider.js +19 -17
  60. package/framework/components/chui_divider/styles.css +2 -2
  61. package/framework/components/chui_drawer/drawer.js +63 -38
  62. package/framework/components/chui_drawer/styles.css +18 -4
  63. package/framework/components/chui_drop_zone/drop_zone.js +37 -27
  64. package/framework/components/chui_drop_zone/styles.css +10 -6
  65. package/framework/components/chui_empty_state/empty_state.js +20 -15
  66. package/framework/components/chui_fieldset/fieldset.js +40 -23
  67. package/framework/components/chui_fieldset/styles.css +6 -6
  68. package/framework/components/chui_file_explorer/file_explorer.js +260 -189
  69. package/framework/components/chui_file_explorer/styles.css +8 -7
  70. package/framework/components/chui_form/form.js +40 -28
  71. package/framework/components/chui_form/styles.css +1 -1
  72. package/framework/components/chui_gantt/gantt.js +102 -78
  73. package/framework/components/chui_gantt/styles.css +5 -5
  74. package/framework/components/chui_graphs/graphs.js +376 -239
  75. package/framework/components/chui_graphs/styles.css +27 -6
  76. package/framework/components/chui_hero/hero.js +98 -37
  77. package/framework/components/chui_hero/styles.css +176 -7
  78. package/framework/components/chui_html_block/html_block.js +8 -8
  79. package/framework/components/chui_html_block/styles.css +3 -3
  80. package/framework/components/chui_hx/hx.js +3 -3
  81. package/framework/components/chui_hx/styles.css +13 -6
  82. package/framework/components/chui_icons/icons.js +2115 -2115
  83. package/framework/components/chui_icons/styles.css +4 -3
  84. package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
  85. package/framework/components/chui_inputs/chui_check_box/styles.css +30 -21
  86. package/framework/components/chui_inputs/chui_chips/chips.js +141 -70
  87. package/framework/components/chui_inputs/chui_chips/styles.css +32 -7
  88. package/framework/components/chui_inputs/chui_color_picker/color_picker.js +55 -38
  89. package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
  90. package/framework/components/chui_inputs/chui_combo_box/combo_box.js +127 -109
  91. package/framework/components/chui_inputs/chui_combo_box/styles.css +23 -21
  92. package/framework/components/chui_inputs/chui_date/date.js +161 -127
  93. package/framework/components/chui_inputs/chui_date/styles.css +20 -19
  94. package/framework/components/chui_inputs/chui_date_range/date_range.js +61 -50
  95. package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
  96. package/framework/components/chui_inputs/chui_datetime/datetime.js +47 -39
  97. package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
  98. package/framework/components/chui_inputs/chui_email/email.js +48 -36
  99. package/framework/components/chui_inputs/chui_email/styles.css +11 -11
  100. package/framework/components/chui_inputs/chui_file/file.js +71 -44
  101. package/framework/components/chui_inputs/chui_file/styles.css +23 -19
  102. package/framework/components/chui_inputs/chui_hotkey/hotkey.js +101 -76
  103. package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
  104. package/framework/components/chui_inputs/chui_masked/masked.js +83 -72
  105. package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
  106. package/framework/components/chui_inputs/chui_mention/mention.js +87 -40
  107. package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
  108. package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +242 -212
  109. package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +23 -15
  110. package/framework/components/chui_inputs/chui_number/number.js +84 -70
  111. package/framework/components/chui_inputs/chui_number/styles.css +13 -13
  112. package/framework/components/chui_inputs/chui_otp/otp.js +71 -62
  113. package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
  114. package/framework/components/chui_inputs/chui_password/password.js +72 -56
  115. package/framework/components/chui_inputs/chui_password/styles.css +15 -15
  116. package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
  117. package/framework/components/chui_inputs/chui_radio_button/styles.css +22 -22
  118. package/framework/components/chui_inputs/chui_rating/rating.js +54 -50
  119. package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
  120. package/framework/components/chui_inputs/chui_select_box/select_box.js +132 -108
  121. package/framework/components/chui_inputs/chui_select_box/styles.css +21 -21
  122. package/framework/components/chui_inputs/chui_slider/slider.js +41 -38
  123. package/framework/components/chui_inputs/chui_slider/styles.css +7 -5
  124. package/framework/components/chui_inputs/chui_text/styles.css +14 -14
  125. package/framework/components/chui_inputs/chui_text/text.js +69 -52
  126. package/framework/components/chui_inputs/chui_text_area/styles.css +14 -14
  127. package/framework/components/chui_inputs/chui_text_area/text_area.js +59 -36
  128. package/framework/components/chui_inputs/chui_time/styles.css +18 -20
  129. package/framework/components/chui_inputs/chui_time/time.js +71 -49
  130. package/framework/components/chui_inputs/chui_toggle/styles.css +13 -6
  131. package/framework/components/chui_inputs/chui_toggle/toggle.js +27 -27
  132. package/framework/components/chui_json_editor/json_editor.js +137 -127
  133. package/framework/components/chui_json_editor/styles.css +5 -5
  134. package/framework/components/chui_json_viewer/json_viewer.js +133 -114
  135. package/framework/components/chui_json_viewer/styles.css +9 -8
  136. package/framework/components/chui_kanban/kanban.js +78 -60
  137. package/framework/components/chui_kanban/styles.css +8 -4
  138. package/framework/components/chui_kbd/kbd.js +12 -10
  139. package/framework/components/chui_kbd/styles.css +1 -1
  140. package/framework/components/chui_label/label.js +31 -29
  141. package/framework/components/chui_label/styles.css +3 -3
  142. package/framework/components/chui_link/link.js +36 -29
  143. package/framework/components/chui_link/styles.css +11 -4
  144. package/framework/components/chui_list/list.js +42 -38
  145. package/framework/components/chui_list/styles.css +4 -3
  146. package/framework/components/chui_markdown_editor/markdown_editor.js +125 -109
  147. package/framework/components/chui_markdown_editor/styles.css +5 -5
  148. package/framework/components/chui_media/audio.js +457 -364
  149. package/framework/components/chui_media/audio_styles.css +74 -37
  150. package/framework/components/chui_media/image.js +99 -64
  151. package/framework/components/chui_media/image_compare.js +86 -55
  152. package/framework/components/chui_media/image_compare_styles.css +5 -5
  153. package/framework/components/chui_media/image_styles.css +4 -4
  154. package/framework/components/chui_media/video.js +264 -171
  155. package/framework/components/chui_media/video_styles.css +57 -24
  156. package/framework/components/chui_menu_bar/menu_bar.js +67 -65
  157. package/framework/components/chui_menu_bar/styles.css +14 -12
  158. package/framework/components/chui_message_bar/message_bar.js +551 -0
  159. package/framework/components/chui_message_bar/styles.css +248 -0
  160. package/framework/components/chui_modal/modal.js +81 -56
  161. package/framework/components/chui_modal/styles.css +3 -2
  162. package/framework/components/chui_notification/notification.js +107 -69
  163. package/framework/components/chui_notification/notification_download.js +28 -77
  164. package/framework/components/chui_notification/notification_download_card.js +115 -0
  165. package/framework/components/chui_notification/notification_download_progress.css +5 -4
  166. package/framework/components/chui_notification/notification_download_progress.js +73 -105
  167. package/framework/components/chui_notification/notification_update.js +44 -37
  168. package/framework/components/chui_notification/status_plate.css +3 -3
  169. package/framework/components/chui_notification/status_plate.js +2 -2
  170. package/framework/components/chui_notification/styles.css +6 -1
  171. package/framework/components/chui_notification/styles_download.css +2 -2
  172. package/framework/components/chui_notification/styles_update.css +3 -3
  173. package/framework/components/chui_org_chart/org_chart.js +101 -62
  174. package/framework/components/chui_org_chart/styles.css +2 -2
  175. package/framework/components/chui_pagination/pagination.js +65 -30
  176. package/framework/components/chui_pagination/styles.css +1 -1
  177. package/framework/components/chui_paragraph/paragraph.js +2 -2
  178. package/framework/components/chui_paragraph/styles.css +3 -3
  179. package/framework/components/chui_popover/popover.js +77 -50
  180. package/framework/components/chui_popover/styles.css +1 -1
  181. package/framework/components/chui_popups/popups.js +210 -181
  182. package/framework/components/chui_popups/styles.css +7 -7
  183. package/framework/components/chui_print_button/print_button.js +48 -33
  184. package/framework/components/chui_progress_bar/progress_bar.js +49 -19
  185. package/framework/components/chui_progress_bar/styles.css +6 -6
  186. package/framework/components/chui_progress_ring/progress_ring.js +32 -30
  187. package/framework/components/chui_progress_ring/styles.css +1 -1
  188. package/framework/components/chui_qrcode/qrcode.js +85 -41
  189. package/framework/components/chui_query_builder/query_builder.js +263 -170
  190. package/framework/components/chui_query_builder/styles.css +4 -4
  191. package/framework/components/chui_radio_group/radio_group.js +36 -26
  192. package/framework/components/chui_radio_group/styles.css +2 -2
  193. package/framework/components/chui_result/result.js +41 -31
  194. package/framework/components/chui_result/styles.css +3 -3
  195. package/framework/components/chui_scheduler/scheduler.js +126 -104
  196. package/framework/components/chui_scheduler/styles.css +11 -7
  197. package/framework/components/chui_screen_spinner/screen_spinner.js +167 -0
  198. package/framework/components/chui_screen_spinner/styles.css +49 -0
  199. package/framework/components/chui_segmented_control/segmented_control.js +90 -27
  200. package/framework/components/chui_segmented_control/styles.css +32 -6
  201. package/framework/components/chui_settings/settings.js +160 -87
  202. package/framework/components/chui_settings/styles.css +4 -4
  203. package/framework/components/chui_shortcut_help/shortcut_help.js +82 -61
  204. package/framework/components/chui_sidebar_nav/sidebar_nav.js +132 -53
  205. package/framework/components/chui_sidebar_nav/styles.css +9 -3
  206. package/framework/components/chui_skeleton/skeleton.js +29 -24
  207. package/framework/components/chui_slideshow/slideshow.js +122 -103
  208. package/framework/components/chui_slideshow/styles.css +22 -16
  209. package/framework/components/chui_spinner/spinner.js +17 -17
  210. package/framework/components/chui_spinner/styles.css +0 -1
  211. package/framework/components/chui_split_button/split_button.js +53 -30
  212. package/framework/components/chui_split_button/styles.css +22 -6
  213. package/framework/components/chui_splitter/splitter.js +46 -39
  214. package/framework/components/chui_splitter/styles.css +2 -2
  215. package/framework/components/chui_statistic/statistic.js +111 -84
  216. package/framework/components/chui_status_bar/status_bar.js +11 -9
  217. package/framework/components/chui_status_bar/styles.css +2 -2
  218. package/framework/components/chui_stepper/stepper.js +230 -54
  219. package/framework/components/chui_stepper/styles.css +79 -14
  220. package/framework/components/chui_sticky_bar/sticky_bar.js +53 -17
  221. package/framework/components/chui_sticky_bar/styles.css +11 -7
  222. package/framework/components/chui_tabs/chui_tabs.js +100 -46
  223. package/framework/components/chui_tabs/styles.css +43 -15
  224. package/framework/components/chui_text_editor/styles.css +24 -23
  225. package/framework/components/chui_text_editor/text_editor.js +103 -79
  226. package/framework/components/chui_text_editor/text_editor_panel.js +428 -367
  227. package/framework/components/chui_theme_toggle/theme_toggle.js +46 -25
  228. package/framework/components/chui_timeline/styles.css +3 -1
  229. package/framework/components/chui_timeline/timeline.js +14 -12
  230. package/framework/components/chui_toast_queue/styles.css +11 -11
  231. package/framework/components/chui_toast_queue/toast_queue.js +82 -73
  232. package/framework/components/chui_toolbar/styles.css +2 -2
  233. package/framework/components/chui_toolbar/toolbar.js +20 -13
  234. package/framework/components/chui_tooltip/styles.css +2 -2
  235. package/framework/components/chui_tooltip/tooltip.js +40 -27
  236. package/framework/components/chui_tour/styles.css +12 -7
  237. package/framework/components/chui_tour/tour.js +132 -98
  238. package/framework/components/chui_transfer/styles.css +8 -7
  239. package/framework/components/chui_transfer/transfer.js +143 -123
  240. package/framework/components/chui_tree_table/styles.css +4 -4
  241. package/framework/components/chui_tree_table/tree_table.js +88 -74
  242. package/framework/components/chui_tree_view/styles.css +14 -10
  243. package/framework/components/chui_tree_view/tree_view.js +298 -253
  244. package/framework/components/chui_user_bar/styles.css +145 -0
  245. package/framework/components/chui_user_bar/user_bar.js +402 -0
  246. package/framework/components/chui_user_list_bar/styles.css +173 -0
  247. package/framework/components/chui_user_list_bar/user_list_bar.js +485 -0
  248. package/framework/components/chui_watermark/watermark.js +41 -33
  249. package/framework/components/chui_webview/styles.css +2 -2
  250. package/framework/components/chui_webview/webview.js +64 -52
  251. package/framework/components/chui_write_bar/styles.css +172 -0
  252. package/framework/components/chui_write_bar/write_bar.js +320 -0
  253. package/framework/main_window.js +1234 -0
  254. package/framework/modules/chui_a11y/chui_a11y.js +153 -123
  255. package/framework/modules/chui_animations/animations.js +105 -43
  256. package/framework/modules/chui_animations/motion.js +120 -0
  257. package/framework/modules/chui_animations/styles.css +37 -4
  258. package/framework/modules/chui_bridge/preload.js +36 -25
  259. package/framework/modules/chui_functions.js +69 -63
  260. package/framework/modules/chui_highlight/chui_highlight.js +25 -22
  261. package/framework/modules/chui_highlight/styles.css +13 -10
  262. package/framework/modules/chui_i18n/chui_i18n.js +12 -10
  263. package/framework/modules/chui_i18n/locales/a11y.js +5 -5
  264. package/framework/modules/chui_i18n/locales/app_layout.js +5 -5
  265. package/framework/modules/chui_i18n/locales/calendar.js +3 -3
  266. package/framework/modules/chui_i18n/locales/common.js +3 -3
  267. package/framework/modules/chui_i18n/locales/data.js +36 -26
  268. package/framework/modules/chui_i18n/locales/editors.js +40 -13
  269. package/framework/modules/chui_i18n/locales/feedback.js +65 -3
  270. package/framework/modules/chui_i18n/locales/forms.js +8 -8
  271. package/framework/modules/chui_i18n/locales/graphics.js +8 -8
  272. package/framework/modules/chui_i18n/locales/media.js +4 -4
  273. package/framework/modules/chui_i18n/locales/navigation.js +12 -8
  274. package/framework/modules/chui_i18n/locales/validate.js +3 -3
  275. package/framework/modules/chui_json/chui_json.js +11 -11
  276. package/framework/modules/chui_layout/chui_layout.js +12 -10
  277. package/framework/modules/chui_logger/chui_logger.js +47 -28
  278. package/framework/modules/chui_page/page.js +39 -33
  279. package/framework/modules/chui_palette/palette.js +61 -0
  280. package/framework/modules/chui_qr/chui_qr.js +179 -88
  281. package/framework/modules/chui_scrollbar/chui_scrollbar.js +79 -68
  282. package/framework/modules/chui_scrollbar/styles.css +4 -2
  283. package/framework/modules/chui_services/chui_channels.js +27 -13
  284. package/framework/modules/chui_services/chui_services.js +47 -39
  285. package/framework/modules/chui_services/desktop.js +108 -101
  286. package/framework/modules/chui_sortable/chui_sortable.js +33 -28
  287. package/framework/modules/chui_validate/chui_validate.js +10 -7
  288. package/framework/tray_bar/chui_menu_items.js +136 -46
  289. package/index.d.ts +5046 -0
  290. package/index.js +199 -1254
  291. package/package.json +81 -68
  292. package/framework/components/telegram_bot/chui_telegram_bot.js +0 -184
@@ -1,12 +1,12 @@
1
1
  const { Icon, Icons } = require("../chui_icons/icons");
2
- const {Label} = require('../chui_label/label');
3
- const Store = require('electron-store');
4
- const {makeKeyboardButton} = require("../../modules/chui_a11y/chui_a11y");
5
- const {Log} = require("../../modules/chui_logger/chui_logger");
6
- const {I18n} = require("../../modules/chui_i18n/chui_i18n");
7
- const {optionOr} = require("../../modules/chui_functions");
8
- const {Select} = require("../chui_inputs/chui_select_box/select_box");
9
- const {Toggle} = require("../chui_inputs/chui_toggle/toggle");
2
+ const { Label } = require("../chui_label/label");
3
+ const Store = require("electron-store");
4
+ const { makeKeyboardButton } = require("../../modules/chui_a11y/chui_a11y");
5
+ const { Log } = require("../../modules/chui_logger/chui_logger");
6
+ const { I18n } = require("../../modules/chui_i18n/chui_i18n");
7
+ const { optionOr } = require("../../modules/chui_functions");
8
+ const { Select } = require("../chui_inputs/chui_select_box/select_box");
9
+ const { Toggle } = require("../chui_inputs/chui_toggle/toggle");
10
10
  const store = new Store();
11
11
 
12
12
  /**
@@ -18,131 +18,146 @@ const store = new Store();
18
18
  * (`setPin("top" | "bottom" | "left" | "right")`) — тогда она висит поверх содержимого.
19
19
  */
20
20
  class Audio {
21
- #chui_ap_main = document.createElement("chui_ap_main");
22
- #chui_ap_block = document.createElement("chui_ap_block");
23
- #chui_ap_row = document.createElement("chui_ap_row");
24
- #chui_at = document.createElement("audio");
21
+ #chuiApMain = document.createElement("chui_ap_main");
22
+ #chuiApBlock = document.createElement("chui_ap_block");
23
+ #chuiApRow = document.createElement("chui_ap_row");
24
+ #chuiAt = document.createElement("audio");
25
25
  // Управление
26
- #chui_ap_play_pause = document.createElement("chui_ap_play_pause");
27
- #chui_ap_info = document.createElement("chui_ap_info");
28
- #chui_ap_time1 = undefined;
29
- #chui_ap_time2 = undefined;
30
- #chui_ap_seek_block = document.createElement("chui_ap_seek_block");
31
- #chui_ap_seek = document.createElement("input");
32
- #chui_ap_seek_buf = document.createElement("chui_ap_seek_buf");
33
- #chui_ap_volume_block = document.createElement("chui_ap_volume_block");
34
- #chui_ap_volume_icon = document.createElement("chui_ap_volume_icon");
35
- #chui_ap_volume = document.createElement("input");
26
+ #chuiApPlayPause = document.createElement("chui_ap_play_pause");
27
+ #chuiApInfo = document.createElement("chui_ap_info");
28
+ #chuiApTime1 = undefined;
29
+ #chuiApTime2 = undefined;
30
+ #chuiApSeekBlock = document.createElement("chui_ap_seek_block");
31
+ #chuiApSeek = document.createElement("input");
32
+ #chuiApSeekBuf = document.createElement("chui_ap_seek_buf");
33
+ #chuiApVolumeBlock = document.createElement("chui_ap_volume_block");
34
+ #chuiApVolumeIcon = document.createElement("chui_ap_volume_icon");
35
+ #chuiApVolume = document.createElement("input");
36
36
  // Эквалайзер
37
- #chui_ap_fx_icon = document.createElement("chui_ap_fx_icon");
38
- #chui_ap_fx_dropdown = document.createElement("chui_ap_fx_dropdown");
39
- #chui_audio_fx = new AudioFX(this.#chui_at);
37
+ #chuiApFxIcon = document.createElement("chui_ap_fx_icon");
38
+ #chuiApFxDropdown = document.createElement("chui_ap_fx_dropdown");
39
+ #chuiAudioFx = new AudioFX(this.#chuiAt);
40
40
  // Состояние
41
41
  #status = document.createElement("div");
42
42
  #pin = false;
43
43
  #destroyed = false;
44
44
  #onPlay = () => this.#renderPlayState(true);
45
45
  #onPause = () => this.#renderPlayState(false);
46
- #onTimeUpdate = () => { this.#displayBufferedAmount(); this.#renderProgress(); };
47
- #onDurationChange = () => { this.#setSliderMax(); this.#renderProgress(); };
48
- constructor(options = {
49
- src: String(), autoplay: Boolean(), loop: Boolean(), muted: Boolean(), volume: Number(),
50
- pin: String(), width: String(), height: String(), label: String()
51
- }) {
52
- require('../../modules/chui_functions').setStyles(__dirname + "/audio_styles.css", 'chUiJS_Audio');
53
- this.#chui_at.preload = "metadata";
54
- this.#chui_at.controls = false;
46
+ #onTimeUpdate = () => {
47
+ this.#displayBufferedAmount();
48
+ this.#renderProgress();
49
+ };
50
+ #onDurationChange = () => {
51
+ this.#setSliderMax();
52
+ this.#renderProgress();
53
+ };
54
+ constructor(
55
+ options = {
56
+ src: String(),
57
+ autoplay: Boolean(),
58
+ loop: Boolean(),
59
+ muted: Boolean(),
60
+ volume: Number(),
61
+ pin: String(),
62
+ width: String(),
63
+ height: String(),
64
+ label: String(),
65
+ }
66
+ ) {
67
+ require("../../modules/chui_functions").setStyles(__dirname + "/audio_styles.css", "chUiJS_Audio");
68
+ this.#chuiAt.preload = "metadata";
69
+ this.#chuiAt.controls = false;
55
70
  // Плеер — именованная группа: без имени скринридер читает подряд безымянные кнопки
56
- this.#chui_ap_main.setAttribute("role", "group");
57
- this.#chui_ap_main.setAttribute("aria-label", optionOr(options.label, I18n.t("media.audioPlayer")));
71
+ this.#chuiApMain.setAttribute("role", "group");
72
+ this.#chuiApMain.setAttribute("aria-label", optionOr(options.label, I18n.t("media.audioPlayer")));
58
73
  // НАСТРОЙКИ
59
- if (options.src !== undefined) this.#chui_at.setAttribute("src", options.src);
60
- if (options.autoplay !== undefined) this.#chui_at.autoplay = options.autoplay === true;
61
- if (options.loop !== undefined) this.#chui_at.loop = options.loop === true;
62
- if (options.muted !== undefined) this.#chui_at.muted = options.muted === true;
63
- this.#chui_at.volume = options.volume === undefined ? 1 : Math.min(1, Math.max(0, Number(options.volume)));
64
- if (options.width !== undefined) this.#chui_ap_main.style.width = options.width;
65
- if (options.height !== undefined) this.#chui_ap_main.style.height = options.height;
74
+ if (options.src !== undefined) this.#chuiAt.setAttribute("src", options.src);
75
+ if (options.autoplay !== undefined) this.#chuiAt.autoplay = options.autoplay === true;
76
+ if (options.loop !== undefined) this.#chuiAt.loop = options.loop === true;
77
+ if (options.muted !== undefined) this.#chuiAt.muted = options.muted === true;
78
+ this.#chuiAt.volume = options.volume === undefined ? 1 : Math.min(1, Math.max(0, Number(options.volume)));
79
+ if (options.width !== undefined) this.#chuiApMain.style.width = options.width;
80
+ if (options.height !== undefined) this.#chuiApMain.style.height = options.height;
66
81
  // ПОЛОСА ПРОГРЕССА
67
- this.#chui_ap_time1 = new Label({text: `0:00`});
68
- this.#chui_ap_time2 = new Label({text: `0:00`});
69
- this.#chui_ap_seek.type = "range";
70
- this.#chui_ap_seek.id = "chui_ap_seek";
71
- this.#chui_ap_seek.max = "0";
72
- this.#chui_ap_seek.value = "0";
73
- this.#chui_ap_seek.step = "any";
74
- this.#chui_ap_seek.setAttribute("aria-label", I18n.t("media.position"));
82
+ this.#chuiApTime1 = new Label({ text: `0:00` });
83
+ this.#chuiApTime2 = new Label({ text: `0:00` });
84
+ this.#chuiApSeek.type = "range";
85
+ this.#chuiApSeek.id = "chui_ap_seek";
86
+ this.#chuiApSeek.max = "0";
87
+ this.#chuiApSeek.value = "0";
88
+ this.#chuiApSeek.step = "any";
89
+ this.#chuiApSeek.setAttribute("aria-label", I18n.t("media.position"));
75
90
  // До метаданных длительность неизвестна: ползунок включается в #setSliderMax()
76
91
  this.#setSliderMax();
77
- this.#chui_ap_seek_block.appendChild(this.#chui_ap_seek);
78
- this.#chui_ap_seek_block.appendChild(this.#chui_ap_seek_buf);
79
- this.#chui_ap_info.appendChild(this.#chui_ap_time1.set());
80
- this.#chui_ap_info.appendChild(this.#chui_ap_seek_block);
81
- this.#chui_ap_info.appendChild(this.#chui_ap_time2.set());
82
- // КНОПКИ
83
- this.#setButton(this.#chui_ap_play_pause, I18n.t("media.play"), Icons.AUDIO_VIDEO.PLAY_ARROW, "18px");
84
- this.#setButton(this.#chui_ap_volume_icon, I18n.t("media.mute"), Icons.AUDIO_VIDEO.VOLUME_UP, "18px");
85
- this.#setButton(this.#chui_ap_fx_icon, I18n.t("media.equalizer"), Icons.AUDIO_VIDEO.EQUALIZER, "18px");
92
+ this.#chuiApSeekBlock.appendChild(this.#chuiApSeek);
93
+ this.#chuiApSeekBlock.appendChild(this.#chuiApSeekBuf);
94
+ this.#chuiApInfo.appendChild(this.#chuiApTime1.set());
95
+ this.#chuiApInfo.appendChild(this.#chuiApSeekBlock);
96
+ this.#chuiApInfo.appendChild(this.#chuiApTime2.set());
97
+ // КНОПКИ: кегль глифа задаёт стиль кнопки (--player_icon_size), значок его наследует
98
+ this.#setButton(this.#chuiApPlayPause, I18n.t("media.play"), Icons.AUDIO_VIDEO.PLAY_ARROW);
99
+ this.#setButton(this.#chuiApVolumeIcon, I18n.t("media.mute"), Icons.AUDIO_VIDEO.VOLUME_UP);
100
+ this.#setButton(this.#chuiApFxIcon, I18n.t("media.equalizer"), Icons.AUDIO_VIDEO.EQUALIZER);
86
101
  // ГРОМКОСТЬ
87
- this.#chui_ap_volume.type = "range";
88
- this.#chui_ap_volume.id = "chui_ap_volume";
89
- this.#chui_ap_volume.max = "100";
90
- this.#chui_ap_volume.value = String(Math.round(this.#chui_at.volume * 100));
91
- this.#chui_ap_volume.step = "1";
92
- this.#chui_ap_volume.setAttribute("aria-label", I18n.t("media.volume"));
93
- this.#chui_ap_volume_block.appendChild(this.#chui_ap_volume_icon);
94
- this.#chui_ap_volume_block.appendChild(this.#chui_ap_volume);
95
- this.#chui_ap_volume_block.appendChild(this.#chui_ap_fx_icon);
102
+ this.#chuiApVolume.type = "range";
103
+ this.#chuiApVolume.id = "chui_ap_volume";
104
+ this.#chuiApVolume.max = "100";
105
+ this.#chuiApVolume.value = String(Math.round(this.#chuiAt.volume * 100));
106
+ this.#chuiApVolume.step = "1";
107
+ this.#chuiApVolume.setAttribute("aria-label", I18n.t("media.volume"));
108
+ this.#chuiApVolumeBlock.appendChild(this.#chuiApVolumeIcon);
109
+ this.#chuiApVolumeBlock.appendChild(this.#chuiApVolume);
110
+ this.#chuiApVolumeBlock.appendChild(this.#chuiApFxIcon);
96
111
  // СБОРКА: одна строка — воспроизведение, прогресс со временем, звук и эквалайзер
97
- this.#chui_ap_row.appendChild(this.#chui_ap_play_pause);
98
- this.#chui_ap_row.appendChild(this.#chui_ap_info);
99
- this.#chui_ap_row.appendChild(this.#chui_ap_volume_block);
100
- this.#chui_ap_block.appendChild(this.#chui_ap_row);
112
+ this.#chuiApRow.appendChild(this.#chuiApPlayPause);
113
+ this.#chuiApRow.appendChild(this.#chuiApInfo);
114
+ this.#chuiApRow.appendChild(this.#chuiApVolumeBlock);
115
+ this.#chuiApBlock.appendChild(this.#chuiApRow);
101
116
  // Эквалайзер — секция внутри карточки, под полосой управления: наложением он быть
102
117
  // не может, у карточки `overflow: hidden` (её скругление)
103
- this.#chui_ap_block.appendChild(this.#chui_ap_fx_dropdown);
104
- this.#chui_ap_fx_dropdown.appendChild(this.#chui_audio_fx.set());
105
- this.#chui_ap_main.appendChild(this.#chui_at);
106
- this.#chui_ap_main.appendChild(this.#chui_ap_block);
118
+ this.#chuiApBlock.appendChild(this.#chuiApFxDropdown);
119
+ this.#chuiApFxDropdown.appendChild(this.#chuiAudioFx.set());
120
+ this.#chuiApMain.appendChild(this.#chuiAt);
121
+ this.#chuiApMain.appendChild(this.#chuiApBlock);
107
122
  // О смене состояния и ошибках сообщает скрытый live-регион
108
123
  this.#status.classList.add("chui_visually_hidden");
109
124
  this.#status.setAttribute("role", "status");
110
125
  this.#status.setAttribute("aria-live", "polite");
111
- this.#chui_ap_main.appendChild(this.#status);
126
+ this.#chuiApMain.appendChild(this.#status);
112
127
  // СОБЫТИЯ
113
128
  // Состояние кнопки идёт от самого элемента: play() может отклониться политикой
114
129
  // автозапуска, и «пауза» на кнопке была бы неправдой
115
- this.#chui_at.addEventListener("play", this.#onPlay);
116
- this.#chui_at.addEventListener("pause", this.#onPause);
117
- this.#chui_at.addEventListener("timeupdate", this.#onTimeUpdate);
118
- this.#chui_at.addEventListener("loadedmetadata", this.#onDurationChange);
119
- this.#chui_at.addEventListener("durationchange", this.#onDurationChange);
120
- this.#chui_at.addEventListener("progress", () => this.#displayBufferedAmount());
121
- this.#chui_at.addEventListener("volumechange", () => this.#renderVolume());
122
- this.#chui_at.addEventListener("error", () => this.#onMediaError());
123
- this.#chui_ap_play_pause.addEventListener("click", () => this.#playPause());
124
- this.#chui_ap_volume_icon.addEventListener("click", () => this.setMuted(!this.getMuted()));
125
- this.#chui_ap_seek.addEventListener("input", () => {
126
- this.#chui_at.currentTime = Number(Number(this.#chui_ap_seek.value).toFixed(6));
130
+ this.#chuiAt.addEventListener("play", this.#onPlay);
131
+ this.#chuiAt.addEventListener("pause", this.#onPause);
132
+ this.#chuiAt.addEventListener("timeupdate", this.#onTimeUpdate);
133
+ this.#chuiAt.addEventListener("loadedmetadata", this.#onDurationChange);
134
+ this.#chuiAt.addEventListener("durationchange", this.#onDurationChange);
135
+ this.#chuiAt.addEventListener("progress", () => this.#displayBufferedAmount());
136
+ this.#chuiAt.addEventListener("volumechange", () => this.#renderVolume());
137
+ this.#chuiAt.addEventListener("error", () => this.#onMediaError());
138
+ this.#chuiApPlayPause.addEventListener("click", () => this.#playPause());
139
+ this.#chuiApVolumeIcon.addEventListener("click", () => this.setMuted(!this.getMuted()));
140
+ this.#chuiApSeek.addEventListener("input", () => {
141
+ this.#chuiAt.currentTime = Number(Number(this.#chuiApSeek.value).toFixed(6));
127
142
  this.#renderProgress();
128
143
  });
129
- this.#chui_ap_volume.addEventListener("input", () => {
130
- this.#chui_at.volume = Number(this.#chui_ap_volume.value) / 100;
131
- this.#chui_at.muted = false;
144
+ this.#chuiApVolume.addEventListener("input", () => {
145
+ this.#chuiAt.volume = Number(this.#chuiApVolume.value) / 100;
146
+ this.#chuiAt.muted = false;
132
147
  });
133
148
  // ЭКВАЛАЙЗЕР: панель открывает кнопка, закрывают повторное нажатие, клик мимо и Escape
134
- this.#chui_ap_fx_icon.setAttribute("aria-haspopup", "true");
135
- this.#chui_ap_fx_icon.setAttribute("aria-expanded", "false");
136
- this.#chui_ap_fx_icon.addEventListener("click", (event) => {
149
+ this.#chuiApFxIcon.setAttribute("aria-haspopup", "true");
150
+ this.#chuiApFxIcon.setAttribute("aria-expanded", "false");
151
+ this.#chuiApFxIcon.addEventListener("click", (event) => {
137
152
  // Клик по кнопке не должен тут же закрывать панель общим обработчиком окна
138
153
  event.stopPropagation();
139
154
  this.#toggleFx();
140
155
  });
141
- this.#chui_ap_fx_dropdown.addEventListener("keydown", (event) => {
156
+ this.#chuiApFxDropdown.addEventListener("keydown", (event) => {
142
157
  if (event.key !== "Escape") return;
143
158
  event.preventDefault();
144
159
  this.#toggleFx(false);
145
- this.#chui_ap_fx_icon.focus();
160
+ this.#chuiApFxIcon.focus();
146
161
  });
147
162
  this.#setMediaActions();
148
163
  this.#renderPlayState(false);
@@ -150,19 +165,19 @@ class Audio {
150
165
  this.setPin(options.pin === undefined ? false : options.pin);
151
166
  }
152
167
  #toggleFx(open = undefined) {
153
- const следующий = open === undefined ? !this.#chui_ap_fx_dropdown.classList.contains("fx_open") : open;
154
- if (следующий === this.#chui_ap_fx_dropdown.classList.contains("fx_open")) return;
155
- this.#chui_ap_fx_dropdown.classList.toggle("fx_open", следующий);
156
- this.#chui_ap_fx_icon.classList.toggle("fx_icon_open", следующий);
157
- this.#chui_ap_fx_icon.setAttribute("aria-expanded", String(следующий));
168
+ const следующий = open === undefined ? !this.#chuiApFxDropdown.classList.contains("fx_open") : open;
169
+ if (следующий === this.#chuiApFxDropdown.classList.contains("fx_open")) return;
170
+ this.#chuiApFxDropdown.classList.toggle("fx_open", следующий);
171
+ this.#chuiApFxIcon.classList.toggle("fx_icon_open", следующий);
172
+ this.#chuiApFxIcon.setAttribute("aria-expanded", String(следующий));
158
173
  if (следующий) document.addEventListener("mousedown", this.#onFxOutside);
159
174
  else document.removeEventListener("mousedown", this.#onFxOutside);
160
175
  }
161
176
  #onFxOutside = (event) => {
162
- if (this.#chui_ap_fx_dropdown.contains(event.target)) return;
163
- if (this.#chui_ap_fx_icon.contains(event.target)) return;
177
+ if (this.#chuiApFxDropdown.contains(event.target)) return;
178
+ if (this.#chuiApFxIcon.contains(event.target)) return;
164
179
  this.#toggleFx(false);
165
- }
180
+ };
166
181
  #playPause = async () => {
167
182
  if (this.getPaused()) {
168
183
  try {
@@ -171,78 +186,82 @@ class Audio {
171
186
  Log.error(`Audio: воспроизведение не началось: ${error}`);
172
187
  }
173
188
  } else this.pause();
174
- }
189
+ };
175
190
  #renderPlayState(playing) {
176
- this.#setIcon(this.#chui_ap_play_pause, playing ? Icons.AUDIO_VIDEO.PAUSE : Icons.AUDIO_VIDEO.PLAY_ARROW, "18px");
177
- this.#setLabel(this.#chui_ap_play_pause, playing ? I18n.t("media.pause") : I18n.t("media.play"));
191
+ this.#setIcon(this.#chuiApPlayPause, playing ? Icons.AUDIO_VIDEO.PAUSE : Icons.AUDIO_VIDEO.PLAY_ARROW);
192
+ this.#setLabel(this.#chuiApPlayPause, playing ? I18n.t("media.pause") : I18n.t("media.play"));
178
193
  }
179
194
  #renderVolume = () => {
180
- const доля = Math.min(100, Math.max(0, Math.round(this.#chui_at.volume * 100)));
181
- this.#chui_ap_volume.value = String(доля);
182
- this.#chui_ap_volume.disabled = this.getMuted();
183
- this.#chui_ap_main.style.setProperty('--volume-before-width', `${доля}%`);
184
- this.#setIcon(this.#chui_ap_volume_icon, this.getMuted() || доля === 0
185
- ? Icons.AUDIO_VIDEO.VOLUME_OFF : Icons.AUDIO_VIDEO.VOLUME_UP, "18px");
186
- this.#setLabel(this.#chui_ap_volume_icon, I18n.t(this.getMuted() ? "media.unmute" : "media.mute"));
187
- }
195
+ const доля = Math.min(100, Math.max(0, Math.round(this.#chuiAt.volume * 100)));
196
+ this.#chuiApVolume.value = String(доля);
197
+ this.#chuiApVolume.disabled = this.getMuted();
198
+ this.#chuiApMain.style.setProperty("--volume_before_width", `${доля}%`);
199
+ this.#setIcon(
200
+ this.#chuiApVolumeIcon,
201
+ this.getMuted() || доля === 0 ? Icons.AUDIO_VIDEO.VOLUME_OFF : Icons.AUDIO_VIDEO.VOLUME_UP
202
+ );
203
+ this.#setLabel(this.#chuiApVolumeIcon, I18n.t(this.getMuted() ? "media.unmute" : "media.mute"));
204
+ };
188
205
  #displayBufferedAmount = () => {
189
- const max = Number(this.#chui_ap_seek.max);
206
+ const max = Number(this.#chuiApSeek.max);
190
207
  if (!(max > 0)) return;
191
208
  try {
192
- const конец = Math.floor(this.#chui_at.buffered.end(0) / max * 100);
193
- this.#chui_ap_seek_buf.style.width = `${Math.min(100, Math.max(0, конец))}%`;
209
+ const конец = Math.floor((this.#chuiAt.buffered.end(0) / max) * 100);
210
+ this.#chuiApSeekBuf.style.width = `${Math.min(100, Math.max(0, конец))}%`;
194
211
  } catch (_error) {
195
212
  // Буфер ещё пуст (метаданные не пришли) — рисовать нечего
196
213
  }
197
- }
214
+ };
198
215
  // Длительность приходит с метаданными: без неё полоса прокрутки делила бы на ноль,
199
216
  // а у живого потока её нет вовсе (duration = Infinity)
200
217
  #setSliderMax = () => {
201
218
  const duration = this.getDuration();
202
219
  const известна = Number.isFinite(duration) && duration > 0;
203
- this.#chui_ap_seek.max = известна ? String(Math.floor(duration)) : "0";
220
+ this.#chuiApSeek.max = известна ? String(Math.floor(duration)) : "0";
204
221
  // Без известной длительности ползунок бессмысленен: перетаскивание отправляло бы плеер в начало
205
- this.#chui_ap_seek.disabled = !известна;
206
- }
222
+ this.#chuiApSeek.disabled = !известна;
223
+ };
207
224
  #renderProgress = () => {
208
- const max = Number(this.#chui_ap_seek.max);
225
+ const max = Number(this.#chuiApSeek.max);
209
226
  const position = this.getCurrentTime();
210
227
  const duration = this.getDuration();
211
228
  if (!(max > 0)) {
212
229
  // «0:07 / 0:00» читалось бы как поломка: у источника без длительности только прошедшее
213
- this.#chui_ap_time1.setText(this.#calculateTime(position));
214
- this.#chui_ap_time2.setText("");
215
- this.#chui_ap_main.style.setProperty('--seek-before-width', '0%');
230
+ this.#chuiApTime1.setText(this.#calculateTime(position));
231
+ this.#chuiApTime2.setText("");
232
+ this.#chuiApMain.style.setProperty("--seek_before_width", "0%");
216
233
  return;
217
234
  }
218
- this.#chui_ap_time1.setText(this.#calculateTime(position));
219
- this.#chui_ap_time2.setText(this.#calculateTime(duration));
220
- this.#chui_ap_seek.value = String(position);
235
+ this.#chuiApTime1.setText(this.#calculateTime(position));
236
+ this.#chuiApTime2.setText(this.#calculateTime(duration));
237
+ this.#chuiApSeek.value = String(position);
221
238
  // Доля ограничена сотней: `max` — округлённая длительность, а позиция идёт дальше,
222
239
  // и без ограничения полоса целиком заливалась акцентом
223
- const доля = Math.min(100, Math.max(0, Math.floor(position / max * 100)));
224
- this.#chui_ap_main.style.setProperty('--seek-before-width', `${доля}%`);
225
- }
240
+ const доля = Math.min(100, Math.max(0, Math.floor((position / max) * 100)));
241
+ this.#chuiApMain.style.setProperty("--seek_before_width", `${доля}%`);
242
+ };
226
243
  #calculateTime = (secs) => {
227
244
  const seconds = Number.isFinite(Number(secs)) ? Number(secs) : 0;
228
245
  const minutes = Math.floor(seconds / 60);
229
246
  const rest = Math.floor(seconds % 60);
230
247
  return `${minutes}:${rest < 10 ? `0${rest}` : `${rest}`}`;
231
- }
248
+ };
232
249
  #onMediaError = () => {
233
- const код = this.#chui_at.error === null ? undefined : this.#chui_at.error.code;
250
+ const код = this.#chuiAt.error === null ? undefined : this.#chuiAt.error.code;
234
251
  const причина = Audio.#ERRORS[код] === undefined ? "" : `: ${I18n.t(Audio.#ERRORS[код])}`;
235
252
  Log.error(`Audio: ошибка воспроизведения${причина}`);
236
- this.#setStatus(`${I18n.t("media.audioNotPlaying", {reason: I18n.t("media.error.interrupted")})}${причина}`);
253
+ this.#setStatus(`${I18n.t("media.audioNotPlaying", { reason: I18n.t("media.error.interrupted") })}${причина}`);
254
+ };
255
+ #setStatus(text) {
256
+ this.#status.innerText = text;
237
257
  }
238
- #setStatus(text) { this.#status.innerText = text; }
239
- #setButton(node, label, icon, size) {
240
- node.innerHTML = new Icon(icon, size).getHTML();
258
+ #setButton(node, label, icon) {
259
+ node.innerHTML = new Icon(icon).getHTML();
241
260
  node.setAttribute("aria-label", label);
242
261
  makeKeyboardButton(node);
243
262
  }
244
- #setIcon(node, icon, size) {
245
- const глиф = new Icon(icon, size).getHTML();
263
+ #setIcon(node, icon) {
264
+ const глиф = new Icon(icon).getHTML();
246
265
  if (node.innerHTML !== глиф) node.innerHTML = глиф;
247
266
  }
248
267
  #setLabel(node, label) {
@@ -281,96 +300,164 @@ class Audio {
281
300
  return this;
282
301
  }
283
302
  this.#pin = значение;
284
- if (значение === false) this.#chui_ap_main.removeAttribute("data_pin");
285
- else this.#chui_ap_main.setAttribute("data_pin", значение);
303
+ if (значение === false) this.#chuiApMain.removeAttribute("data_pin");
304
+ else this.#chuiApMain.setAttribute("data_pin", значение);
286
305
  return this;
287
306
  }
288
- getPin() { return this.#pin; }
307
+ getPin() {
308
+ return this.#pin;
309
+ }
289
310
  // === ЭКВАЛАЙЗЕР ===
290
311
  /** Включить или выключить эквалайзер: то же, что делает выключатель в панели */
291
- setEqualizerEnabled(boolean = Boolean()) { return this.#chui_audio_fx.setEnabled(boolean), this; }
292
- isEqualizerEnabled() { return this.#chui_audio_fx.isEnabled(); }
312
+ setEqualizerEnabled(boolean = Boolean()) {
313
+ return (this.#chuiAudioFx.setEnabled(boolean), this);
314
+ }
315
+ isEqualizerEnabled() {
316
+ return this.#chuiAudioFx.isEnabled();
317
+ }
293
318
  /** Раскрыть или свернуть панель эквалайзера */
294
- setEqualizerOpen(open = Boolean()) { this.#toggleFx(open === true); return this; }
319
+ setEqualizerOpen(open = Boolean()) {
320
+ this.#toggleFx(open === true);
321
+ return this;
322
+ }
295
323
  /** Показать панель эквалайзера, если она скрыта, и наоборот */
296
- isEqualizerOpen() { return this.#chui_ap_fx_dropdown.classList.contains("fx_open"); }
324
+ isEqualizerOpen() {
325
+ return this.#chuiApFxDropdown.classList.contains("fx_open");
326
+ }
297
327
  /** Прочитать сохранённое состояние эквалайзера из настроек */
298
- restoreFX() { this.#chui_audio_fx.restore(); return this; }
328
+ restoreFX() {
329
+ this.#chuiAudioFx.restore();
330
+ return this;
331
+ }
299
332
  // === СТАНДАРТНЫЕ ВОЗМОЖНОСТИ `<audio>` ===
300
333
  /** Сам элемент: для всего, чего нет в списке ниже */
301
- getAudio() { return this.#chui_at; }
334
+ getAudio() {
335
+ return this.#chuiAt;
336
+ }
302
337
  /** Воспроизведение: промис, как у `<audio>` */
303
- play() { return this.#chui_at.play(); }
304
- pause() { this.#chui_at.pause(); return this; }
305
- load() { this.#chui_at.load(); return this; }
306
- canPlayType(type = String()) { return this.#chui_at.canPlayType(type); }
307
- getSrc() { return this.#chui_at.getAttribute("src") ?? ""; }
338
+ play() {
339
+ return this.#chuiAt.play();
340
+ }
341
+ pause() {
342
+ this.#chuiAt.pause();
343
+ return this;
344
+ }
345
+ load() {
346
+ this.#chuiAt.load();
347
+ return this;
348
+ }
349
+ canPlayType(type = String()) {
350
+ return this.#chuiAt.canPlayType(type);
351
+ }
352
+ getSrc() {
353
+ return this.#chuiAt.getAttribute("src") ?? "";
354
+ }
308
355
  setSrc(url = String()) {
309
- this.#chui_at.setAttribute("src", url);
310
- this.#chui_at.load();
356
+ this.#chuiAt.setAttribute("src", url);
357
+ this.#chuiAt.load();
311
358
  return this;
312
359
  }
313
- getSrcObject() { return this.#chui_at.srcObject; }
360
+ getSrcObject() {
361
+ return this.#chuiAt.srcObject;
362
+ }
314
363
  /** Живой поток — штатное `srcObject`, а не путь */
315
364
  setSrcObject(object) {
316
- this.#chui_at.srcObject = object;
365
+ this.#chuiAt.srcObject = object;
317
366
  return this;
318
367
  }
319
- getCurrentTime() { return this.#chui_at.currentTime; }
368
+ getCurrentTime() {
369
+ return this.#chuiAt.currentTime;
370
+ }
320
371
  setCurrentTime(seconds = Number()) {
321
- this.#chui_at.currentTime = Number(seconds);
372
+ this.#chuiAt.currentTime = Number(seconds);
322
373
  // Подписи и заливка не ждут `timeupdate`: перемотка из кода видна сразу
323
374
  this.#renderProgress();
324
375
  return this;
325
376
  }
326
- getDuration() { return this.#chui_at.duration; }
327
- getBuffered() { return this.#chui_at.buffered; }
328
- getPaused() { return this.#chui_at.paused; }
329
- getEnded() { return this.#chui_at.ended; }
330
- getReadyState() { return this.#chui_at.readyState; }
331
- getVolume() { return this.#chui_at.volume; }
377
+ getDuration() {
378
+ return this.#chuiAt.duration;
379
+ }
380
+ getBuffered() {
381
+ return this.#chuiAt.buffered;
382
+ }
383
+ getPaused() {
384
+ return this.#chuiAt.paused;
385
+ }
386
+ getEnded() {
387
+ return this.#chuiAt.ended;
388
+ }
389
+ getReadyState() {
390
+ return this.#chuiAt.readyState;
391
+ }
392
+ getVolume() {
393
+ return this.#chuiAt.volume;
394
+ }
332
395
  setVolume(value = Number()) {
333
- this.#chui_at.volume = Math.min(1, Math.max(0, Number(value)));
396
+ this.#chuiAt.volume = Math.min(1, Math.max(0, Number(value)));
334
397
  return this;
335
398
  }
336
- getMuted() { return this.#chui_at.muted; }
399
+ getMuted() {
400
+ return this.#chuiAt.muted;
401
+ }
337
402
  setMuted(boolean = Boolean()) {
338
- this.#chui_at.muted = boolean === true;
403
+ this.#chuiAt.muted = boolean === true;
404
+ return this;
405
+ }
406
+ getPlaybackRate() {
407
+ return this.#chuiAt.playbackRate;
408
+ }
409
+ setPlaybackRate(rate = Number()) {
410
+ this.#chuiAt.playbackRate = Number(rate);
411
+ return this;
412
+ }
413
+ getDefaultPlaybackRate() {
414
+ return this.#chuiAt.defaultPlaybackRate;
415
+ }
416
+ setDefaultPlaybackRate(rate = Number()) {
417
+ this.#chuiAt.defaultPlaybackRate = Number(rate);
418
+ return this;
419
+ }
420
+ getLoop() {
421
+ return this.#chuiAt.loop;
422
+ }
423
+ setLoop(boolean = Boolean()) {
424
+ this.#chuiAt.loop = boolean === true;
425
+ return this;
426
+ }
427
+ getAutoplay() {
428
+ return this.#chuiAt.autoplay;
429
+ }
430
+ setAutoplay(boolean = Boolean()) {
431
+ this.#chuiAt.autoplay = boolean === true;
339
432
  return this;
340
433
  }
341
- getPlaybackRate() { return this.#chui_at.playbackRate; }
342
- setPlaybackRate(rate = Number()) { this.#chui_at.playbackRate = Number(rate); return this; }
343
- getDefaultPlaybackRate() { return this.#chui_at.defaultPlaybackRate; }
344
- setDefaultPlaybackRate(rate = Number()) { this.#chui_at.defaultPlaybackRate = Number(rate); return this; }
345
- getLoop() { return this.#chui_at.loop; }
346
- setLoop(boolean = Boolean()) { this.#chui_at.loop = boolean === true; return this; }
347
- getAutoplay() { return this.#chui_at.autoplay; }
348
- setAutoplay(boolean = Boolean()) { this.#chui_at.autoplay = boolean === true; return this; }
349
434
  addEventListener(type = String(), listener = () => {}) {
350
- this.#chui_at.addEventListener(type, listener);
435
+ this.#chuiAt.addEventListener(type, listener);
351
436
  return this;
352
437
  }
353
438
  removeEventListener(type = String(), listener = () => {}) {
354
- this.#chui_at.removeEventListener(type, listener);
439
+ this.#chuiAt.removeEventListener(type, listener);
355
440
  return this;
356
441
  }
357
- set() { return this.#chui_ap_main; }
442
+ set() {
443
+ return this.#chuiApMain;
444
+ }
358
445
  detach() {
359
446
  this.pause();
360
- this.#chui_ap_main.remove();
447
+ this.#chuiApMain.remove();
361
448
  return this;
362
449
  }
363
450
  /** Остановить плеер и снять всё, что живёт вне его узлов. Повторный вызов безопасен */
364
451
  destroy() {
365
452
  if (this.#destroyed) return this;
366
453
  this.#destroyed = true;
367
- this.#chui_at.pause();
368
- this.#chui_at.removeAttribute("src");
369
- this.#chui_at.load(); // отпускает декодер и прочитанный файл
454
+ this.#chuiAt.pause();
455
+ this.#chuiAt.removeAttribute("src");
456
+ this.#chuiAt.load(); // отпускает декодер и прочитанный файл
370
457
  this.#unsetMediaActions();
371
458
  document.removeEventListener("mousedown", this.#onFxOutside);
372
- this.#chui_audio_fx.destroy();
373
- if (this.#chui_ap_main.parentElement !== null) this.#chui_ap_main.remove();
459
+ this.#chuiAudioFx.destroy();
460
+ if (this.#chuiApMain.parentElement !== null) this.#chuiApMain.remove();
374
461
  return this;
375
462
  }
376
463
  static #ERRORS = {
@@ -378,34 +465,34 @@ class Audio {
378
465
  2: "media.error.notFound",
379
466
  3: "media.error.decode",
380
467
  4: "media.error.format",
381
- }
468
+ };
382
469
  }
383
470
  class AudioFX {
384
- #chui_ap_equalizer_main = document.createElement('chui_ap_equalizer_main')
385
- #chui_ap_equalizer_controls = document.createElement('chui_ap_equalizer_controls')
386
- #chui_ap_equalizer_block = document.createElement('chui_ap_equalizer_block')
387
- #chui_ap_equalizer_preamp_block = document.createElement("chui_ap_equalizer_preamp_block")
388
- #chui_ap_equalizer_band_block = document.createElement("chui_ap_equalizer_band_block")
471
+ #chuiApEqualizerMain = document.createElement("chui_ap_equalizer_main");
472
+ #chuiApEqualizerControls = document.createElement("chui_ap_equalizer_controls");
473
+ #chuiApEqualizerBlock = document.createElement("chui_ap_equalizer_block");
474
+ #chuiApEqualizerPreampBlock = document.createElement("chui_ap_equalizer_preamp_block");
475
+ #chuiApEqualizerBandBlock = document.createElement("chui_ap_equalizer_band_block");
389
476
  /* Поле пресетов и переключатель — общекитовые `Select` и `Toggle`, вложенные в панель:
390
477
  клавиатура, роли и состояния у них уже есть, а размеры для полосы панели задаёт
391
478
  `audio_styles.css`. Прежде тут были свои элементы, и список пресетов ломался:
392
479
  при ограниченной высоте слоя `Select` сжимал строки до 11 px — лечится `flex: none`
393
480
  у опций (`chui_select_box/styles.css`) */
394
- #switch = new Toggle({ label: I18n.t("media.enableEqualizer") })
395
- #presets = new Select()
396
- #eqBands = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]
481
+ #switch = new Toggle({ label: I18n.t("media.enableEqualizer") });
482
+ #presets = new Select();
483
+ #eqBands = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000];
397
484
  // Слайдеры эквалайзера по id: узлы свои, берём их из карты, а не поиском по документу
398
485
  #nodes = new Map();
399
486
  #media = undefined;
400
487
  #filters = undefined;
401
- #fx_status = "chuijs.framework.settings.fx_status"
402
- #fx_preset = "chuijs.framework.settings.fx_preset"
488
+ #fxStatus = "chuijs.framework.settings.fx_status";
489
+ #fxPreset = "chuijs.framework.settings.fx_preset";
403
490
  #audioContext = new AudioContext();
404
491
  #destroyed = false;
405
492
  constructor(audio) {
406
493
  this.#media = this.#audioContext.createMediaElementSource(audio);
407
494
  this.#filters = this.#eqBands.map((band, i) => {
408
- let filter = this.#audioContext.createBiquadFilter()
495
+ const filter = this.#audioContext.createBiquadFilter();
409
496
  if (i === 0) {
410
497
  filter.type = "lowshelf";
411
498
  } else if (i === this.#eqBands.length - 1) {
@@ -413,57 +500,60 @@ class AudioFX {
413
500
  } else {
414
501
  filter.type = "peaking";
415
502
  }
416
- filter.gain.value = 0
417
- filter.Q.value = 1
418
- filter.frequency.value = band
419
- return filter
420
- })
503
+ filter.gain.value = 0;
504
+ filter.Q.value = 1;
505
+ filter.frequency.value = band;
506
+ return filter;
507
+ });
421
508
  this.#filters.reduce((prev, curr) => {
422
- prev.connect(curr)
423
- return curr
424
- })
509
+ prev.connect(curr);
510
+ return curr;
511
+ });
425
512
  this.#media.connect(this.#filters[0]);
426
513
  this.#filters[this.#filters.length - 1].connect(this.#audioContext.destination);
427
514
  //
428
- this.#chui_ap_equalizer_preamp_block.appendChild(this.#setSliderPreamp(this.#filters))
429
- this.#filters.forEach((filter) => this.#chui_ap_equalizer_band_block.appendChild(this.#setSliderBand(filter)))
515
+ this.#chuiApEqualizerPreampBlock.appendChild(this.#setSliderPreamp(this.#filters));
516
+ this.#filters.forEach((filter) => this.#chuiApEqualizerBandBlock.appendChild(this.#setSliderBand(filter)));
430
517
  // Пресеты: подпись варианта — перевод, значение — имя пресета, поэтому выбор
431
518
  // читается из события списка, а не сверкой подписи
432
- this.#presets.addOptions(...AudioFX.PRESETS.map((preset) => ({
433
- title: I18n.t(preset.titleKey), value: preset.name
434
- })))
519
+ this.#presets.addOptions(
520
+ ...AudioFX.PRESETS.map((preset) => ({
521
+ title: I18n.t(preset.titleKey),
522
+ value: preset.name,
523
+ }))
524
+ );
435
525
  // Видимой подписи у поля нет: имя для скринридера ставим вводу (так же делал
436
526
  // прежний свой список, у которого подписи не было вовсе)
437
- this.#presets.set().querySelector("input").setAttribute("aria-label", I18n.t("media.preset"))
438
- this.#presets.addValueChangeListener((event) => this.#usePreset(event.detail.value))
527
+ this.#presets.set().querySelector("input").setAttribute("aria-label", I18n.t("media.preset"));
528
+ this.#presets.addValueChangeListener((event) => this.#usePreset(event.detail.value));
439
529
  // Переключатель — общекитовый `Toggle`; подпись он отдаёт в `aria-label`
440
530
  this.#switch.addChangeListener(() => {
441
- if (this.#switch.getValue()) this.#fx_ON(this.#audioContext)
442
- else this.#fx_OFF(this.#audioContext)
443
- })
531
+ if (this.#switch.getValue()) this.#fxON(this.#audioContext);
532
+ else this.#fxOFF(this.#audioContext);
533
+ });
444
534
  //
445
- this.#chui_ap_equalizer_controls.appendChild(this.#presets.set())
446
- this.#chui_ap_equalizer_controls.appendChild(this.#switch.set())
535
+ this.#chuiApEqualizerControls.appendChild(this.#presets.set());
536
+ this.#chuiApEqualizerControls.appendChild(this.#switch.set());
447
537
  //
448
- this.#chui_ap_equalizer_block.appendChild(this.#chui_ap_equalizer_preamp_block)
449
- this.#chui_ap_equalizer_block.appendChild(this.#chui_ap_equalizer_band_block)
538
+ this.#chuiApEqualizerBlock.appendChild(this.#chuiApEqualizerPreampBlock);
539
+ this.#chuiApEqualizerBlock.appendChild(this.#chuiApEqualizerBandBlock);
450
540
  //
451
- this.#chui_ap_equalizer_main.appendChild(this.#chui_ap_equalizer_controls)
452
- this.#chui_ap_equalizer_main.appendChild(this.#chui_ap_equalizer_block)
541
+ this.#chuiApEqualizerMain.appendChild(this.#chuiApEqualizerControls);
542
+ this.#chuiApEqualizerMain.appendChild(this.#chuiApEqualizerBlock);
453
543
  // По умолчанию эквалайзер выключен — и вид полосы должен говорить то же: пока
454
544
  // `restoreFX()` не позвал хозяин, выключатель выключен, а дорожки уже серые
455
- this.#setStatusBlock(false)
545
+ this.#setStatusBlock(false);
456
546
  }
457
547
  #usePreset(name = String()) {
458
- this.#filters.forEach((filter) => this.#setPreset(filter, name, true))
459
- this.#setPresetName(name)
548
+ this.#filters.forEach((filter) => this.#setPreset(filter, name, true));
549
+ this.#setPresetName(name);
460
550
  }
461
551
  /** Имя пресета в поле: значение ставится извне, поэтому без уведомления слушателей */
462
552
  #setPresetName(name = String()) {
463
- const known = AudioFX.PRESETS.some((preset) => preset.name === name)
464
- this.#presets.setValue(known ? name : AudioFX.PRESETS[0].name, true)
553
+ const known = AudioFX.PRESETS.some((preset) => preset.name === name);
554
+ this.#presets.setValue(known ? name : AudioFX.PRESETS[0].name, true);
465
555
  }
466
- #fx_ON(audioContext) {
556
+ #fxON(audioContext) {
467
557
  try {
468
558
  this.#media.disconnect(audioContext.destination);
469
559
  this.#media.connect(this.#filters[0]);
@@ -472,158 +562,160 @@ class AudioFX {
472
562
  this.#media.connect(this.#filters[0]);
473
563
  this.#filters[this.#filters.length - 1].connect(audioContext.destination);
474
564
  }
475
- store.delete(this.#fx_status)
476
- store.set(this.#fx_status, true)
477
- this.#setStatusBlock(true)
565
+ store.delete(this.#fxStatus);
566
+ store.set(this.#fxStatus, true);
567
+ this.#setStatusBlock(true);
478
568
  }
479
- #fx_OFF(audioContext) {
569
+ #fxOFF(audioContext) {
480
570
  this.#filters[this.#filters.length - 1].disconnect(audioContext.destination);
481
571
  this.#media.disconnect(this.#filters[0]);
482
572
  this.#media.connect(audioContext.destination);
483
- store.delete(this.#fx_status)
484
- store.set(this.#fx_status, false)
485
- this.#setStatusBlock(false)
573
+ store.delete(this.#fxStatus);
574
+ store.set(this.#fxStatus, false);
575
+ this.#setStatusBlock(false);
486
576
  }
487
577
  #setPreset(filter, name) {
488
- store.delete(this.#fx_preset)
489
- store.set(this.#fx_preset, name)
490
- let filter_test = this.#getPreset(name)
491
- this.#renderPreampSlider(undefined, filter_test, filter)
492
- let input = filter_test.inputs.filter(input => input.id === String(filter.frequency.value))[0]
493
- this.#renderSlider(input, filter, filter_test.preamp)
578
+ store.delete(this.#fxPreset);
579
+ store.set(this.#fxPreset, name);
580
+ const preset = this.#getPreset(name);
581
+ this.#renderPreampSlider(undefined, preset, filter);
582
+ const input = preset.inputs.filter((input) => input.id === String(filter.frequency.value))[0];
583
+ this.#renderSlider(input, filter, preset.preamp);
494
584
  }
495
585
  #setStatusBlock(status) {
496
586
  //
497
587
  // Поле пресетов при выключенном эквалайзере не отвечает, а выключатель трогать
498
588
  // нельзя: он и есть способ включить эквалайзер обратно (родной флажок в выключенном
499
589
  // состоянии не шлёт `change` — включить было бы нечем)
500
- this.#presets.setDisabled(!status)
590
+ this.#presets.setDisabled(!status);
501
591
  //
502
- for (let band of this.#eqBands) {
503
- let sliderBand = this.#node(String(band));
592
+ for (const band of this.#eqBands) {
593
+ const sliderBand = this.#node(String(band));
504
594
  sliderBand.disabled = !status;
505
595
  if (status) {
506
- sliderBand.style.cursor = "pointer"
507
- this.#chui_ap_equalizer_preamp_block.classList.remove("chui_ap_equalizer_disabled")
508
- this.#chui_ap_equalizer_band_block.classList.remove("chui_ap_equalizer_disabled")
596
+ sliderBand.style.cursor = "pointer";
597
+ this.#chuiApEqualizerPreampBlock.classList.remove("chui_ap_equalizer_disabled");
598
+ this.#chuiApEqualizerBandBlock.classList.remove("chui_ap_equalizer_disabled");
509
599
  } else {
510
- sliderBand.style.cursor = "not-allowed"
511
- this.#chui_ap_equalizer_preamp_block.classList.add("chui_ap_equalizer_disabled")
512
- this.#chui_ap_equalizer_band_block.classList.add("chui_ap_equalizer_disabled")
600
+ sliderBand.style.cursor = "not-allowed";
601
+ this.#chuiApEqualizerPreampBlock.classList.add("chui_ap_equalizer_disabled");
602
+ this.#chuiApEqualizerBandBlock.classList.add("chui_ap_equalizer_disabled");
513
603
  }
514
604
  }
515
605
  //
516
- let sliderPreamp = this.#node("preamp");
606
+ const sliderPreamp = this.#node("preamp");
517
607
  sliderPreamp.disabled = !status;
518
608
  if (status) {
519
- sliderPreamp.style.cursor = "pointer"
520
- this.#chui_ap_equalizer_preamp_block.classList.remove("chui_ap_equalizer_disabled")
521
- this.#chui_ap_equalizer_band_block.classList.remove("chui_ap_equalizer_disabled")
609
+ sliderPreamp.style.cursor = "pointer";
610
+ this.#chuiApEqualizerPreampBlock.classList.remove("chui_ap_equalizer_disabled");
611
+ this.#chuiApEqualizerBandBlock.classList.remove("chui_ap_equalizer_disabled");
522
612
  } else {
523
- sliderPreamp.style.cursor = "not-allowed"
524
- this.#chui_ap_equalizer_preamp_block.classList.add("chui_ap_equalizer_disabled")
525
- this.#chui_ap_equalizer_band_block.classList.add("chui_ap_equalizer_disabled")
613
+ sliderPreamp.style.cursor = "not-allowed";
614
+ this.#chuiApEqualizerPreampBlock.classList.add("chui_ap_equalizer_disabled");
615
+ this.#chuiApEqualizerBandBlock.classList.add("chui_ap_equalizer_disabled");
526
616
  }
527
617
  }
528
618
  #getPreset(name) {
529
- return AudioFX.PRESETS.filter(preset => preset.name === name)[0];
619
+ return AudioFX.PRESETS.filter((preset) => preset.name === name)[0];
530
620
  }
531
621
  /** Узел эквалайзера по id — из карты слайдеров этого эквалайзера */
532
622
  #node(id = String()) {
533
623
  return this.#nodes.get(id);
534
624
  }
535
625
  #setSliderPreamp(filters) {
536
- let sliderMain = document.createElement("chui_eq_slider_main")
537
- let val = document.createElement("slider_label")
538
- let slider = document.createElement('input')
539
- let label = document.createElement("slider_label")
540
- sliderMain.id = "preamp_main"
541
- slider.id = "preamp"
542
- slider.type = 'range'
543
- slider.className = 'eq_slider_test'
544
- slider.step = "1"
545
- slider.min = "-10"
546
- slider.max = "10"
547
- slider.value = "0"
548
- slider.setAttribute("aria-label", I18n.t("media.preamp"))
549
- slider.setAttribute("aria-valuetext", I18n.t("media.decibels", {value: 0}))
626
+ const sliderMain = document.createElement("chui_eq_slider_main");
627
+ const val = document.createElement("slider_label");
628
+ const slider = document.createElement("input");
629
+ const label = document.createElement("slider_label");
630
+ sliderMain.id = "preamp_main";
631
+ slider.id = "preamp";
632
+ slider.type = "range";
633
+ slider.className = "eq_slider_test";
634
+ slider.step = "1";
635
+ slider.min = "-10";
636
+ slider.max = "10";
637
+ slider.value = "0";
638
+ slider.setAttribute("aria-label", I18n.t("media.preamp"));
639
+ slider.setAttribute("aria-valuetext", I18n.t("media.decibels", { value: 0 }));
550
640
  // Доля заливки рельса: заливка рисуется градиентом по тому же числу, что у полос
551
641
  // прогресса и громкости
552
- slider.style.setProperty('--fx-before-width', `50%`);
553
- slider.oninput = (e) => this.#renderPreampSlider(e.target, this.#getPreset(store.get(this.#fx_preset)), filters)
554
- val.id = "val_preamp"
642
+ slider.style.setProperty("--fx_before_width", `50%`);
643
+ slider.oninput = (e) => this.#renderPreampSlider(e.target, this.#getPreset(store.get(this.#fxPreset)), filters);
644
+ val.id = "val_preamp";
555
645
  this.#nodes.set("preamp", slider);
556
646
  this.#nodes.set("val_preamp", val);
557
- val.innerText = String(slider.value)
558
- label.innerText = "AMP"
559
- sliderMain.appendChild(val)
560
- sliderMain.appendChild(slider)
561
- sliderMain.appendChild(label)
562
- return sliderMain
647
+ val.innerText = String(slider.value);
648
+ label.innerText = "AMP";
649
+ sliderMain.appendChild(val);
650
+ sliderMain.appendChild(slider);
651
+ sliderMain.appendChild(label);
652
+ return sliderMain;
563
653
  }
564
654
  #setSliderBand(filter) {
565
- let freq_value = String(filter.frequency.value);
566
- let sliderMain = document.createElement("chui_eq_slider_main")
567
- let val = document.createElement("slider_label")
568
- let slider = document.createElement('input')
569
- let label = document.createElement("slider_label")
570
- slider.id = freq_value
571
- slider.type = 'range'
572
- slider.className = 'eq_slider_test'
573
- slider.step = "0.1"
574
- slider.min = "-10"
575
- slider.max = "10"
576
- slider.value = String(filter.gain.value)
577
- slider.setAttribute("aria-label", I18n.t("media.hertzGain", {freq: freq_value}))
578
- slider.setAttribute("aria-valuetext", I18n.t("media.decibels", {value: slider.value}))
579
- slider.style.setProperty('--fx-before-width', `50%`);
580
- slider.oninput = (e) => this.#renderSlider(e.target, filter, Number(this.#node("preamp").value))
581
- val.id = "val_" + freq_value
582
- this.#nodes.set(freq_value, slider);
583
- this.#nodes.set("val_" + freq_value, val);
584
- val.innerText = String(slider.value)
585
- label.innerText = freq_value.replace("000", "K")
586
- sliderMain.appendChild(val)
587
- sliderMain.appendChild(slider)
588
- sliderMain.appendChild(label)
589
- return sliderMain
655
+ const freqValue = String(filter.frequency.value);
656
+ const sliderMain = document.createElement("chui_eq_slider_main");
657
+ const val = document.createElement("slider_label");
658
+ const slider = document.createElement("input");
659
+ const label = document.createElement("slider_label");
660
+ slider.id = freqValue;
661
+ slider.type = "range";
662
+ slider.className = "eq_slider_test";
663
+ slider.step = "0.1";
664
+ slider.min = "-10";
665
+ slider.max = "10";
666
+ slider.value = String(filter.gain.value);
667
+ slider.setAttribute("aria-label", I18n.t("media.hertzGain", { freq: freqValue }));
668
+ slider.setAttribute("aria-valuetext", I18n.t("media.decibels", { value: slider.value }));
669
+ slider.style.setProperty("--fx_before_width", `50%`);
670
+ slider.oninput = (e) => this.#renderSlider(e.target, filter, Number(this.#node("preamp").value));
671
+ val.id = "val_" + freqValue;
672
+ this.#nodes.set(freqValue, slider);
673
+ this.#nodes.set("val_" + freqValue, val);
674
+ val.innerText = String(slider.value);
675
+ label.innerText = freqValue.replace("000", "K");
676
+ sliderMain.appendChild(val);
677
+ sliderMain.appendChild(slider);
678
+ sliderMain.appendChild(label);
679
+ return sliderMain;
590
680
  }
591
681
  restore() {
592
682
  setTimeout(() => {
593
683
  if (this.#destroyed) return;
594
- let status = store.get(this.#fx_status)
595
- let preset = store.get(this.#fx_preset)
684
+ const status = store.get(this.#fxStatus);
685
+ const preset = store.get(this.#fxPreset);
596
686
  if (status) {
597
- this.#fx_ON(this.#audioContext)
598
- this.#switch.setValue(true)
599
- this.#setPresetName(store.get(this.#fx_preset))
600
- this.#setStatusBlock(status)
687
+ this.#fxON(this.#audioContext);
688
+ this.#switch.setValue(true);
689
+ this.#setPresetName(store.get(this.#fxPreset));
690
+ this.#setStatusBlock(status);
601
691
  } else {
602
- this.#fx_OFF(this.#audioContext)
603
- this.#switch.setValue(false)
692
+ this.#fxOFF(this.#audioContext);
693
+ this.#switch.setValue(false);
604
694
  if (status === undefined || preset === undefined) {
605
- this.#setPresetName("Default")
695
+ this.#setPresetName("Default");
606
696
  } else {
607
- this.#setPresetName(store.get(this.#fx_preset))
697
+ this.#setPresetName(store.get(this.#fxPreset));
608
698
  }
609
- this.#setStatusBlock(status)
699
+ this.#setStatusBlock(status);
610
700
  }
611
- }, 250)
701
+ }, 250);
612
702
  }
613
703
  set() {
614
- return this.#chui_ap_equalizer_main
704
+ return this.#chuiApEqualizerMain;
615
705
  }
616
706
  /** Включить или выключить эквалайзер: тот же путь, что у выключателя в панели */
617
707
  setEnabled(boolean = Boolean()) {
618
708
  const включён = this.#switch.getValue();
619
709
  if (boolean === включён) return this;
620
710
  this.#switch.setValue(boolean);
621
- if (boolean) this.#fx_ON(this.#audioContext);
622
- else this.#fx_OFF(this.#audioContext);
711
+ if (boolean) this.#fxON(this.#audioContext);
712
+ else this.#fxOFF(this.#audioContext);
623
713
  return this;
624
714
  }
625
715
  /** Состояние выключателя в панели */
626
- isEnabled() { return this.#switch.getValue(); }
716
+ isEnabled() {
717
+ return this.#switch.getValue();
718
+ }
627
719
  /**
628
720
  * Отпустить звуковой граф: контекст держит аудиопоток, а Chromium разрешает ограниченное
629
721
  * число контекстов на страницу. Повторный вызов безопасен.
@@ -637,33 +729,33 @@ class AudioFX {
637
729
  return this;
638
730
  }
639
731
  #renderPreampSlider(input, preset, filters = []) {
640
- let p_slider = this.#node("preamp");
641
- let p_val = this.#node("val_preamp");
732
+ const preampSlider = this.#node("preamp");
733
+ const preampValue = this.#node("val_preamp");
642
734
  if (input === undefined) {
643
- p_slider.value = preset.preamp
735
+ preampSlider.value = preset.preamp;
644
736
  } else {
645
- p_slider.value = input.value
646
- preset.inputs.forEach(inp => {
647
- let slid = this.#node(inp.id)
648
- filters.forEach(filter => {
737
+ preampSlider.value = input.value;
738
+ preset.inputs.forEach((inp) => {
739
+ const slid = this.#node(inp.id);
740
+ filters.forEach((filter) => {
649
741
  if (String(inp.id) === String(filter.frequency.value)) {
650
- filter.gain.value = Number(slid.value) + Number(input.value)
742
+ filter.gain.value = Number(slid.value) + Number(input.value);
651
743
  }
652
- })
653
- })
744
+ });
745
+ });
654
746
  }
655
- p_val.innerText = String(p_slider.value)
656
- p_slider.setAttribute("aria-valuetext", I18n.t("media.decibels", {value: p_slider.value}))
657
- p_slider.style.setProperty('--fx-before-width', `${(50 + (p_slider.value * 5)).toFixed(1)}%`);
747
+ preampValue.innerText = String(preampSlider.value);
748
+ preampSlider.setAttribute("aria-valuetext", I18n.t("media.decibels", { value: preampSlider.value }));
749
+ preampSlider.style.setProperty("--fx_before_width", `${(50 + preampSlider.value * 5).toFixed(1)}%`);
658
750
  }
659
751
  #renderSlider(input, filter, preamp) {
660
- let slider = this.#node(input.id);
661
- let val = this.#node("val_" + input.id);
662
- slider.value = input.value
663
- filter.gain.value = Number(Number(slider.value) + preamp)
664
- val.innerText = String(slider.value)
665
- slider.setAttribute("aria-valuetext", I18n.t("media.decibels", {value: slider.value}))
666
- slider.style.setProperty('--fx-before-width', `${(50 + (slider.value * 5)).toFixed(1)}%`);
752
+ const slider = this.#node(input.id);
753
+ const val = this.#node("val_" + input.id);
754
+ slider.value = input.value;
755
+ filter.gain.value = Number(Number(slider.value) + preamp);
756
+ val.innerText = String(slider.value);
757
+ slider.setAttribute("aria-valuetext", I18n.t("media.decibels", { value: slider.value }));
758
+ slider.style.setProperty("--fx_before_width", `${(50 + slider.value * 5).toFixed(1)}%`);
667
759
  }
668
760
  // ya.music.Audio.fx
669
761
  static PRESETS = [
@@ -682,7 +774,7 @@ class AudioFX {
682
774
  { id: "12000", value: 0 },
683
775
  { id: "14000", value: 0 },
684
776
  { id: "16000", value: 0 },
685
- ]
777
+ ],
686
778
  },
687
779
  {
688
780
  name: "Classical",
@@ -699,7 +791,7 @@ class AudioFX {
699
791
  { id: "12000", value: -3.5 },
700
792
  { id: "14000", value: -3.5 },
701
793
  { id: "16000", value: -4.5 },
702
- ]
794
+ ],
703
795
  },
704
796
  {
705
797
  name: "Club",
@@ -716,7 +808,7 @@ class AudioFX {
716
808
  { id: "12000", value: -0.5 },
717
809
  { id: "14000", value: -0.5 },
718
810
  { id: "16000", value: -0.5 },
719
- ]
811
+ ],
720
812
  },
721
813
  {
722
814
  name: "Dance",
@@ -733,7 +825,7 @@ class AudioFX {
733
825
  { id: "12000", value: -3.5 },
734
826
  { id: "14000", value: -0.5 },
735
827
  { id: "16000", value: -0.5 },
736
- ]
828
+ ],
737
829
  },
738
830
  {
739
831
  name: "Full Bass",
@@ -750,7 +842,7 @@ class AudioFX {
750
842
  { id: "12000", value: -5 },
751
843
  { id: "14000", value: -5.5 },
752
844
  { id: "16000", value: -5.5 },
753
- ]
845
+ ],
754
846
  },
755
847
  {
756
848
  name: "Full Treble",
@@ -767,7 +859,7 @@ class AudioFX {
767
859
  { id: "12000", value: 8 },
768
860
  { id: "14000", value: 8 },
769
861
  { id: "16000", value: 8 },
770
- ]
862
+ ],
771
863
  },
772
864
  {
773
865
  name: "Full Bass & Treble",
@@ -784,7 +876,7 @@ class AudioFX {
784
876
  { id: "12000", value: 5.5 },
785
877
  { id: "14000", value: 6 },
786
878
  { id: "16000", value: 6 },
787
- ]
879
+ ],
788
880
  },
789
881
  {
790
882
  name: "Laptop Speakers / Headphone",
@@ -801,7 +893,7 @@ class AudioFX {
801
893
  { id: "12000", value: 4.5 },
802
894
  { id: "14000", value: 6 },
803
895
  { id: "16000", value: 7 },
804
- ]
896
+ ],
805
897
  },
806
898
  {
807
899
  name: "Large Hall",
@@ -818,7 +910,7 @@ class AudioFX {
818
910
  { id: "12000", value: -2 },
819
911
  { id: "14000", value: -0.5 },
820
912
  { id: "16000", value: -0.5 },
821
- ]
913
+ ],
822
914
  },
823
915
  {
824
916
  name: "Live",
@@ -835,7 +927,7 @@ class AudioFX {
835
927
  { id: "12000", value: 1 },
836
928
  { id: "14000", value: 1 },
837
929
  { id: "16000", value: 1 },
838
- ]
930
+ ],
839
931
  },
840
932
  {
841
933
  name: "Party",
@@ -852,7 +944,7 @@ class AudioFX {
852
944
  { id: "12000", value: -0.5 },
853
945
  { id: "14000", value: 3.5 },
854
946
  { id: "16000", value: 3.5 },
855
- ]
947
+ ],
856
948
  },
857
949
  {
858
950
  name: "Pop",
@@ -869,7 +961,7 @@ class AudioFX {
869
961
  { id: "12000", value: -1 },
870
962
  { id: "14000", value: -0.5 },
871
963
  { id: "16000", value: -0.5 },
872
- ]
964
+ ],
873
965
  },
874
966
  {
875
967
  name: "Reggae",
@@ -886,7 +978,7 @@ class AudioFX {
886
978
  { id: "12000", value: -0.5 },
887
979
  { id: "14000", value: -0.5 },
888
980
  { id: "16000", value: -0.5 },
889
- ]
981
+ ],
890
982
  },
891
983
  {
892
984
  name: "Ska",
@@ -903,7 +995,7 @@ class AudioFX {
903
995
  { id: "12000", value: 4.5 },
904
996
  { id: "14000", value: 5.5 },
905
997
  { id: "16000", value: 4.5 },
906
- ]
998
+ ],
907
999
  },
908
1000
  {
909
1001
  name: "Soft",
@@ -920,7 +1012,7 @@ class AudioFX {
920
1012
  { id: "12000", value: 4.5 },
921
1013
  { id: "14000", value: 5.5 },
922
1014
  { id: "16000", value: 6 },
923
- ]
1015
+ ],
924
1016
  },
925
1017
  {
926
1018
  name: "Rock",
@@ -937,7 +1029,7 @@ class AudioFX {
937
1029
  { id: "12000", value: 5.5 },
938
1030
  { id: "14000", value: 5.5 },
939
1031
  { id: "16000", value: 5.5 },
940
- ]
1032
+ ],
941
1033
  },
942
1034
  {
943
1035
  name: "Soft Rock",
@@ -954,7 +1046,7 @@ class AudioFX {
954
1046
  { id: "12000", value: -0.5 },
955
1047
  { id: "14000", value: 1 },
956
1048
  { id: "16000", value: 4 },
957
- ]
1049
+ ],
958
1050
  },
959
1051
  {
960
1052
  name: "Techno",
@@ -971,8 +1063,9 @@ class AudioFX {
971
1063
  { id: "12000", value: 4.5 },
972
1064
  { id: "14000", value: 4.5 },
973
1065
  { id: "16000", value: 4 },
974
- ]
975
- }]
1066
+ ],
1067
+ },
1068
+ ];
976
1069
  }
977
1070
 
978
- exports.Audio = Audio
1071
+ exports.Audio = Audio;