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.
- package/CHANGELOG.md +597 -0
- package/README.md +43 -644
- package/framework/appLayout/default/chui_app_layout.js +602 -496
- package/framework/appLayout/default/global_style.css +288 -97
- package/framework/appLayout/default/main_theme_style.css +188 -101
- package/framework/components/chui_accordion/accordion.js +38 -35
- package/framework/components/chui_accordion/styles.css +13 -12
- package/framework/components/chui_action_sheet/action_sheet.js +93 -64
- package/framework/components/chui_action_sheet/styles.css +12 -5
- package/framework/components/chui_actions_bar/actions_bar.js +338 -0
- package/framework/components/chui_actions_bar/styles.css +139 -0
- package/framework/components/chui_affix/affix.js +16 -11
- package/framework/components/chui_affix/styles.css +5 -0
- package/framework/components/chui_alert/alert.js +34 -19
- package/framework/components/chui_alert/styles.css +7 -0
- package/framework/components/chui_avatar/avatar.js +36 -35
- package/framework/components/chui_avatar/styles.css +1 -1
- package/framework/components/chui_avatar_group/avatar_group.js +25 -23
- package/framework/components/chui_avatar_group/styles.css +1 -1
- package/framework/components/chui_backtop/backtop.js +60 -21
- package/framework/components/chui_backtop/styles.css +21 -0
- package/framework/components/chui_badge/badge.js +30 -28
- package/framework/components/chui_badge/styles.css +6 -6
- package/framework/components/chui_banner/banner.js +222 -0
- package/framework/components/chui_banner/styles.css +170 -0
- package/framework/components/chui_breadcrumb/breadcrumb.js +15 -13
- package/framework/components/chui_breadcrumb/styles.css +1 -1
- package/framework/components/chui_button/button.js +52 -36
- package/framework/components/chui_button/styles.css +7 -7
- package/framework/components/chui_calendar/calendar.js +18 -16
- package/framework/components/chui_card/card.js +52 -32
- package/framework/components/chui_card_scroll/card_scroll.js +169 -0
- package/framework/components/chui_card_scroll/styles.css +86 -0
- package/framework/components/chui_chat/chat.js +140 -79
- package/framework/components/chui_chat/styles.css +18 -7
- package/framework/components/chui_clipboard_history/clipboard_history.js +53 -39
- package/framework/components/chui_clipboard_history/styles.css +13 -9
- package/framework/components/chui_code/code.js +10 -10
- package/framework/components/chui_code/styles.css +4 -4
- package/framework/components/chui_code_editor/code_editor.js +73 -57
- package/framework/components/chui_code_editor/styles.css +6 -5
- package/framework/components/chui_command_palette/command_palette.js +87 -64
- package/framework/components/chui_command_palette/styles.css +5 -5
- package/framework/components/chui_console/console.js +13 -13
- package/framework/components/chui_console/styles.css +6 -3
- package/framework/components/chui_content_block/content_block.js +35 -21
- package/framework/components/chui_content_block/styles.css +2 -2
- package/framework/components/chui_context_menu/context_menu.js +171 -58
- package/framework/components/chui_context_menu/styles.css +82 -8
- package/framework/components/chui_custom_element/custom_element.js +25 -18
- package/framework/components/chui_data_grid/data_grid.js +633 -471
- package/framework/components/chui_data_grid/styles.css +24 -22
- package/framework/components/chui_descriptions/descriptions.js +34 -17
- package/framework/components/chui_descriptions/styles.css +1 -1
- package/framework/components/chui_details/details.js +42 -36
- package/framework/components/chui_details/styles.css +12 -11
- package/framework/components/chui_diff_viewer/diff_viewer.js +172 -147
- package/framework/components/chui_diff_viewer/styles.css +10 -10
- package/framework/components/chui_divider/divider.js +19 -17
- package/framework/components/chui_divider/styles.css +2 -2
- package/framework/components/chui_drawer/drawer.js +63 -38
- package/framework/components/chui_drawer/styles.css +18 -4
- package/framework/components/chui_drop_zone/drop_zone.js +37 -27
- package/framework/components/chui_drop_zone/styles.css +10 -6
- package/framework/components/chui_empty_state/empty_state.js +20 -15
- package/framework/components/chui_fieldset/fieldset.js +40 -23
- package/framework/components/chui_fieldset/styles.css +6 -6
- package/framework/components/chui_file_explorer/file_explorer.js +260 -189
- package/framework/components/chui_file_explorer/styles.css +8 -7
- package/framework/components/chui_form/form.js +40 -28
- package/framework/components/chui_form/styles.css +1 -1
- package/framework/components/chui_gantt/gantt.js +102 -78
- package/framework/components/chui_gantt/styles.css +5 -5
- package/framework/components/chui_graphs/graphs.js +376 -239
- package/framework/components/chui_graphs/styles.css +27 -6
- package/framework/components/chui_hero/hero.js +98 -37
- package/framework/components/chui_hero/styles.css +176 -7
- package/framework/components/chui_html_block/html_block.js +8 -8
- package/framework/components/chui_html_block/styles.css +3 -3
- package/framework/components/chui_hx/hx.js +3 -3
- package/framework/components/chui_hx/styles.css +13 -6
- package/framework/components/chui_icons/icons.js +2115 -2115
- package/framework/components/chui_icons/styles.css +4 -3
- package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
- package/framework/components/chui_inputs/chui_check_box/styles.css +30 -21
- package/framework/components/chui_inputs/chui_chips/chips.js +141 -70
- package/framework/components/chui_inputs/chui_chips/styles.css +32 -7
- package/framework/components/chui_inputs/chui_color_picker/color_picker.js +55 -38
- package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
- package/framework/components/chui_inputs/chui_combo_box/combo_box.js +127 -109
- package/framework/components/chui_inputs/chui_combo_box/styles.css +23 -21
- package/framework/components/chui_inputs/chui_date/date.js +161 -127
- package/framework/components/chui_inputs/chui_date/styles.css +20 -19
- package/framework/components/chui_inputs/chui_date_range/date_range.js +61 -50
- package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
- package/framework/components/chui_inputs/chui_datetime/datetime.js +47 -39
- package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
- package/framework/components/chui_inputs/chui_email/email.js +48 -36
- package/framework/components/chui_inputs/chui_email/styles.css +11 -11
- package/framework/components/chui_inputs/chui_file/file.js +71 -44
- package/framework/components/chui_inputs/chui_file/styles.css +23 -19
- package/framework/components/chui_inputs/chui_hotkey/hotkey.js +101 -76
- package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
- package/framework/components/chui_inputs/chui_masked/masked.js +83 -72
- package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
- package/framework/components/chui_inputs/chui_mention/mention.js +87 -40
- package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
- package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +242 -212
- package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +23 -15
- package/framework/components/chui_inputs/chui_number/number.js +84 -70
- package/framework/components/chui_inputs/chui_number/styles.css +13 -13
- package/framework/components/chui_inputs/chui_otp/otp.js +71 -62
- package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
- package/framework/components/chui_inputs/chui_password/password.js +72 -56
- package/framework/components/chui_inputs/chui_password/styles.css +15 -15
- package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
- package/framework/components/chui_inputs/chui_radio_button/styles.css +22 -22
- package/framework/components/chui_inputs/chui_rating/rating.js +54 -50
- package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
- package/framework/components/chui_inputs/chui_select_box/select_box.js +132 -108
- package/framework/components/chui_inputs/chui_select_box/styles.css +21 -21
- package/framework/components/chui_inputs/chui_slider/slider.js +41 -38
- package/framework/components/chui_inputs/chui_slider/styles.css +7 -5
- package/framework/components/chui_inputs/chui_text/styles.css +14 -14
- package/framework/components/chui_inputs/chui_text/text.js +69 -52
- package/framework/components/chui_inputs/chui_text_area/styles.css +14 -14
- package/framework/components/chui_inputs/chui_text_area/text_area.js +59 -36
- package/framework/components/chui_inputs/chui_time/styles.css +18 -20
- package/framework/components/chui_inputs/chui_time/time.js +71 -49
- package/framework/components/chui_inputs/chui_toggle/styles.css +13 -6
- package/framework/components/chui_inputs/chui_toggle/toggle.js +27 -27
- package/framework/components/chui_json_editor/json_editor.js +137 -127
- package/framework/components/chui_json_editor/styles.css +5 -5
- package/framework/components/chui_json_viewer/json_viewer.js +133 -114
- package/framework/components/chui_json_viewer/styles.css +9 -8
- package/framework/components/chui_kanban/kanban.js +78 -60
- package/framework/components/chui_kanban/styles.css +8 -4
- package/framework/components/chui_kbd/kbd.js +12 -10
- package/framework/components/chui_kbd/styles.css +1 -1
- package/framework/components/chui_label/label.js +31 -29
- package/framework/components/chui_label/styles.css +3 -3
- package/framework/components/chui_link/link.js +36 -29
- package/framework/components/chui_link/styles.css +11 -4
- package/framework/components/chui_list/list.js +42 -38
- package/framework/components/chui_list/styles.css +4 -3
- package/framework/components/chui_markdown_editor/markdown_editor.js +125 -109
- package/framework/components/chui_markdown_editor/styles.css +5 -5
- package/framework/components/chui_media/audio.js +457 -364
- package/framework/components/chui_media/audio_styles.css +74 -37
- package/framework/components/chui_media/image.js +99 -64
- package/framework/components/chui_media/image_compare.js +86 -55
- package/framework/components/chui_media/image_compare_styles.css +5 -5
- package/framework/components/chui_media/image_styles.css +4 -4
- package/framework/components/chui_media/video.js +264 -171
- package/framework/components/chui_media/video_styles.css +57 -24
- package/framework/components/chui_menu_bar/menu_bar.js +67 -65
- package/framework/components/chui_menu_bar/styles.css +14 -12
- package/framework/components/chui_message_bar/message_bar.js +551 -0
- package/framework/components/chui_message_bar/styles.css +248 -0
- package/framework/components/chui_modal/modal.js +81 -56
- package/framework/components/chui_modal/styles.css +3 -2
- package/framework/components/chui_notification/notification.js +107 -69
- package/framework/components/chui_notification/notification_download.js +28 -77
- package/framework/components/chui_notification/notification_download_card.js +115 -0
- package/framework/components/chui_notification/notification_download_progress.css +5 -4
- package/framework/components/chui_notification/notification_download_progress.js +73 -105
- package/framework/components/chui_notification/notification_update.js +44 -37
- package/framework/components/chui_notification/status_plate.css +3 -3
- package/framework/components/chui_notification/status_plate.js +2 -2
- package/framework/components/chui_notification/styles.css +6 -1
- package/framework/components/chui_notification/styles_download.css +2 -2
- package/framework/components/chui_notification/styles_update.css +3 -3
- package/framework/components/chui_org_chart/org_chart.js +101 -62
- package/framework/components/chui_org_chart/styles.css +2 -2
- package/framework/components/chui_pagination/pagination.js +65 -30
- package/framework/components/chui_pagination/styles.css +1 -1
- package/framework/components/chui_paragraph/paragraph.js +2 -2
- package/framework/components/chui_paragraph/styles.css +3 -3
- package/framework/components/chui_popover/popover.js +77 -50
- package/framework/components/chui_popover/styles.css +1 -1
- package/framework/components/chui_popups/popups.js +210 -181
- package/framework/components/chui_popups/styles.css +7 -7
- package/framework/components/chui_print_button/print_button.js +48 -33
- package/framework/components/chui_progress_bar/progress_bar.js +49 -19
- package/framework/components/chui_progress_bar/styles.css +6 -6
- package/framework/components/chui_progress_ring/progress_ring.js +32 -30
- package/framework/components/chui_progress_ring/styles.css +1 -1
- package/framework/components/chui_qrcode/qrcode.js +85 -41
- package/framework/components/chui_query_builder/query_builder.js +263 -170
- package/framework/components/chui_query_builder/styles.css +4 -4
- package/framework/components/chui_radio_group/radio_group.js +36 -26
- package/framework/components/chui_radio_group/styles.css +2 -2
- package/framework/components/chui_result/result.js +41 -31
- package/framework/components/chui_result/styles.css +3 -3
- package/framework/components/chui_scheduler/scheduler.js +126 -104
- package/framework/components/chui_scheduler/styles.css +11 -7
- package/framework/components/chui_screen_spinner/screen_spinner.js +167 -0
- package/framework/components/chui_screen_spinner/styles.css +49 -0
- package/framework/components/chui_segmented_control/segmented_control.js +90 -27
- package/framework/components/chui_segmented_control/styles.css +32 -6
- package/framework/components/chui_settings/settings.js +160 -87
- package/framework/components/chui_settings/styles.css +4 -4
- package/framework/components/chui_shortcut_help/shortcut_help.js +82 -61
- package/framework/components/chui_sidebar_nav/sidebar_nav.js +132 -53
- package/framework/components/chui_sidebar_nav/styles.css +9 -3
- package/framework/components/chui_skeleton/skeleton.js +29 -24
- package/framework/components/chui_slideshow/slideshow.js +122 -103
- package/framework/components/chui_slideshow/styles.css +22 -16
- package/framework/components/chui_spinner/spinner.js +17 -17
- package/framework/components/chui_spinner/styles.css +0 -1
- package/framework/components/chui_split_button/split_button.js +53 -30
- package/framework/components/chui_split_button/styles.css +22 -6
- package/framework/components/chui_splitter/splitter.js +46 -39
- package/framework/components/chui_splitter/styles.css +2 -2
- package/framework/components/chui_statistic/statistic.js +111 -84
- package/framework/components/chui_status_bar/status_bar.js +11 -9
- package/framework/components/chui_status_bar/styles.css +2 -2
- package/framework/components/chui_stepper/stepper.js +230 -54
- package/framework/components/chui_stepper/styles.css +79 -14
- package/framework/components/chui_sticky_bar/sticky_bar.js +53 -17
- package/framework/components/chui_sticky_bar/styles.css +11 -7
- package/framework/components/chui_tabs/chui_tabs.js +100 -46
- package/framework/components/chui_tabs/styles.css +43 -15
- package/framework/components/chui_text_editor/styles.css +24 -23
- package/framework/components/chui_text_editor/text_editor.js +103 -79
- package/framework/components/chui_text_editor/text_editor_panel.js +428 -367
- package/framework/components/chui_theme_toggle/theme_toggle.js +46 -25
- package/framework/components/chui_timeline/styles.css +3 -1
- package/framework/components/chui_timeline/timeline.js +14 -12
- package/framework/components/chui_toast_queue/styles.css +11 -11
- package/framework/components/chui_toast_queue/toast_queue.js +82 -73
- package/framework/components/chui_toolbar/styles.css +2 -2
- package/framework/components/chui_toolbar/toolbar.js +20 -13
- package/framework/components/chui_tooltip/styles.css +2 -2
- package/framework/components/chui_tooltip/tooltip.js +40 -27
- package/framework/components/chui_tour/styles.css +12 -7
- package/framework/components/chui_tour/tour.js +132 -98
- package/framework/components/chui_transfer/styles.css +8 -7
- package/framework/components/chui_transfer/transfer.js +143 -123
- package/framework/components/chui_tree_table/styles.css +4 -4
- package/framework/components/chui_tree_table/tree_table.js +88 -74
- package/framework/components/chui_tree_view/styles.css +14 -10
- package/framework/components/chui_tree_view/tree_view.js +298 -253
- package/framework/components/chui_user_bar/styles.css +145 -0
- package/framework/components/chui_user_bar/user_bar.js +402 -0
- package/framework/components/chui_user_list_bar/styles.css +173 -0
- package/framework/components/chui_user_list_bar/user_list_bar.js +485 -0
- package/framework/components/chui_watermark/watermark.js +41 -33
- package/framework/components/chui_webview/styles.css +2 -2
- package/framework/components/chui_webview/webview.js +64 -52
- package/framework/components/chui_write_bar/styles.css +172 -0
- package/framework/components/chui_write_bar/write_bar.js +320 -0
- package/framework/main_window.js +1234 -0
- package/framework/modules/chui_a11y/chui_a11y.js +153 -123
- package/framework/modules/chui_animations/animations.js +105 -43
- package/framework/modules/chui_animations/motion.js +120 -0
- package/framework/modules/chui_animations/styles.css +37 -4
- package/framework/modules/chui_bridge/preload.js +36 -25
- package/framework/modules/chui_functions.js +69 -63
- package/framework/modules/chui_highlight/chui_highlight.js +25 -22
- package/framework/modules/chui_highlight/styles.css +13 -10
- package/framework/modules/chui_i18n/chui_i18n.js +12 -10
- package/framework/modules/chui_i18n/locales/a11y.js +5 -5
- package/framework/modules/chui_i18n/locales/app_layout.js +5 -5
- package/framework/modules/chui_i18n/locales/calendar.js +3 -3
- package/framework/modules/chui_i18n/locales/common.js +3 -3
- package/framework/modules/chui_i18n/locales/data.js +36 -26
- package/framework/modules/chui_i18n/locales/editors.js +40 -13
- package/framework/modules/chui_i18n/locales/feedback.js +65 -3
- package/framework/modules/chui_i18n/locales/forms.js +8 -8
- package/framework/modules/chui_i18n/locales/graphics.js +8 -8
- package/framework/modules/chui_i18n/locales/media.js +4 -4
- package/framework/modules/chui_i18n/locales/navigation.js +12 -8
- package/framework/modules/chui_i18n/locales/validate.js +3 -3
- package/framework/modules/chui_json/chui_json.js +11 -11
- package/framework/modules/chui_layout/chui_layout.js +12 -10
- package/framework/modules/chui_logger/chui_logger.js +47 -28
- package/framework/modules/chui_page/page.js +39 -33
- package/framework/modules/chui_palette/palette.js +61 -0
- package/framework/modules/chui_qr/chui_qr.js +179 -88
- package/framework/modules/chui_scrollbar/chui_scrollbar.js +79 -68
- package/framework/modules/chui_scrollbar/styles.css +4 -2
- package/framework/modules/chui_services/chui_channels.js +27 -13
- package/framework/modules/chui_services/chui_services.js +47 -39
- package/framework/modules/chui_services/desktop.js +108 -101
- package/framework/modules/chui_sortable/chui_sortable.js +33 -28
- package/framework/modules/chui_validate/chui_validate.js +10 -7
- package/framework/tray_bar/chui_menu_items.js +136 -46
- package/index.d.ts +5046 -0
- package/index.js +199 -1254
- package/package.json +81 -68
- 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(
|
|
3
|
-
const Store = require(
|
|
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
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
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
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
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
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
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 = () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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.#
|
|
57
|
-
this.#
|
|
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.#
|
|
60
|
-
if (options.autoplay !== undefined) this.#
|
|
61
|
-
if (options.loop !== undefined) this.#
|
|
62
|
-
if (options.muted !== undefined) this.#
|
|
63
|
-
this.#
|
|
64
|
-
if (options.width !== undefined) this.#
|
|
65
|
-
if (options.height !== undefined) this.#
|
|
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.#
|
|
68
|
-
this.#
|
|
69
|
-
this.#
|
|
70
|
-
this.#
|
|
71
|
-
this.#
|
|
72
|
-
this.#
|
|
73
|
-
this.#
|
|
74
|
-
this.#
|
|
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.#
|
|
78
|
-
this.#
|
|
79
|
-
this.#
|
|
80
|
-
this.#
|
|
81
|
-
this.#
|
|
82
|
-
//
|
|
83
|
-
this.#setButton(this.#
|
|
84
|
-
this.#setButton(this.#
|
|
85
|
-
this.#setButton(this.#
|
|
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.#
|
|
88
|
-
this.#
|
|
89
|
-
this.#
|
|
90
|
-
this.#
|
|
91
|
-
this.#
|
|
92
|
-
this.#
|
|
93
|
-
this.#
|
|
94
|
-
this.#
|
|
95
|
-
this.#
|
|
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.#
|
|
98
|
-
this.#
|
|
99
|
-
this.#
|
|
100
|
-
this.#
|
|
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.#
|
|
104
|
-
this.#
|
|
105
|
-
this.#
|
|
106
|
-
this.#
|
|
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.#
|
|
126
|
+
this.#chuiApMain.appendChild(this.#status);
|
|
112
127
|
// СОБЫТИЯ
|
|
113
128
|
// Состояние кнопки идёт от самого элемента: play() может отклониться политикой
|
|
114
129
|
// автозапуска, и «пауза» на кнопке была бы неправдой
|
|
115
|
-
this.#
|
|
116
|
-
this.#
|
|
117
|
-
this.#
|
|
118
|
-
this.#
|
|
119
|
-
this.#
|
|
120
|
-
this.#
|
|
121
|
-
this.#
|
|
122
|
-
this.#
|
|
123
|
-
this.#
|
|
124
|
-
this.#
|
|
125
|
-
this.#
|
|
126
|
-
this.#
|
|
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.#
|
|
130
|
-
this.#
|
|
131
|
-
this.#
|
|
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.#
|
|
135
|
-
this.#
|
|
136
|
-
this.#
|
|
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.#
|
|
156
|
+
this.#chuiApFxDropdown.addEventListener("keydown", (event) => {
|
|
142
157
|
if (event.key !== "Escape") return;
|
|
143
158
|
event.preventDefault();
|
|
144
159
|
this.#toggleFx(false);
|
|
145
|
-
this.#
|
|
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.#
|
|
154
|
-
if (следующий === this.#
|
|
155
|
-
this.#
|
|
156
|
-
this.#
|
|
157
|
-
this.#
|
|
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.#
|
|
163
|
-
if (this.#
|
|
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.#
|
|
177
|
-
this.#setLabel(this.#
|
|
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.#
|
|
181
|
-
this.#
|
|
182
|
-
this.#
|
|
183
|
-
this.#
|
|
184
|
-
this.#setIcon(
|
|
185
|
-
|
|
186
|
-
|
|
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.#
|
|
206
|
+
const max = Number(this.#chuiApSeek.max);
|
|
190
207
|
if (!(max > 0)) return;
|
|
191
208
|
try {
|
|
192
|
-
const конец = Math.floor(this.#
|
|
193
|
-
this.#
|
|
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.#
|
|
220
|
+
this.#chuiApSeek.max = известна ? String(Math.floor(duration)) : "0";
|
|
204
221
|
// Без известной длительности ползунок бессмысленен: перетаскивание отправляло бы плеер в начало
|
|
205
|
-
this.#
|
|
206
|
-
}
|
|
222
|
+
this.#chuiApSeek.disabled = !известна;
|
|
223
|
+
};
|
|
207
224
|
#renderProgress = () => {
|
|
208
|
-
const max = Number(this.#
|
|
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.#
|
|
214
|
-
this.#
|
|
215
|
-
this.#
|
|
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.#
|
|
219
|
-
this.#
|
|
220
|
-
this.#
|
|
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.#
|
|
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.#
|
|
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
|
-
#
|
|
239
|
-
|
|
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
|
|
245
|
-
const глиф = new Icon(icon
|
|
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.#
|
|
285
|
-
else this.#
|
|
303
|
+
if (значение === false) this.#chuiApMain.removeAttribute("data_pin");
|
|
304
|
+
else this.#chuiApMain.setAttribute("data_pin", значение);
|
|
286
305
|
return this;
|
|
287
306
|
}
|
|
288
|
-
getPin() {
|
|
307
|
+
getPin() {
|
|
308
|
+
return this.#pin;
|
|
309
|
+
}
|
|
289
310
|
// === ЭКВАЛАЙЗЕР ===
|
|
290
311
|
/** Включить или выключить эквалайзер: то же, что делает выключатель в панели */
|
|
291
|
-
setEqualizerEnabled(boolean = Boolean()) {
|
|
292
|
-
|
|
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()) {
|
|
319
|
+
setEqualizerOpen(open = Boolean()) {
|
|
320
|
+
this.#toggleFx(open === true);
|
|
321
|
+
return this;
|
|
322
|
+
}
|
|
295
323
|
/** Показать панель эквалайзера, если она скрыта, и наоборот */
|
|
296
|
-
isEqualizerOpen() {
|
|
324
|
+
isEqualizerOpen() {
|
|
325
|
+
return this.#chuiApFxDropdown.classList.contains("fx_open");
|
|
326
|
+
}
|
|
297
327
|
/** Прочитать сохранённое состояние эквалайзера из настроек */
|
|
298
|
-
restoreFX() {
|
|
328
|
+
restoreFX() {
|
|
329
|
+
this.#chuiAudioFx.restore();
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
299
332
|
// === СТАНДАРТНЫЕ ВОЗМОЖНОСТИ `<audio>` ===
|
|
300
333
|
/** Сам элемент: для всего, чего нет в списке ниже */
|
|
301
|
-
getAudio() {
|
|
334
|
+
getAudio() {
|
|
335
|
+
return this.#chuiAt;
|
|
336
|
+
}
|
|
302
337
|
/** Воспроизведение: промис, как у `<audio>` */
|
|
303
|
-
play() {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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.#
|
|
310
|
-
this.#
|
|
356
|
+
this.#chuiAt.setAttribute("src", url);
|
|
357
|
+
this.#chuiAt.load();
|
|
311
358
|
return this;
|
|
312
359
|
}
|
|
313
|
-
getSrcObject() {
|
|
360
|
+
getSrcObject() {
|
|
361
|
+
return this.#chuiAt.srcObject;
|
|
362
|
+
}
|
|
314
363
|
/** Живой поток — штатное `srcObject`, а не путь */
|
|
315
364
|
setSrcObject(object) {
|
|
316
|
-
this.#
|
|
365
|
+
this.#chuiAt.srcObject = object;
|
|
317
366
|
return this;
|
|
318
367
|
}
|
|
319
|
-
getCurrentTime() {
|
|
368
|
+
getCurrentTime() {
|
|
369
|
+
return this.#chuiAt.currentTime;
|
|
370
|
+
}
|
|
320
371
|
setCurrentTime(seconds = Number()) {
|
|
321
|
-
this.#
|
|
372
|
+
this.#chuiAt.currentTime = Number(seconds);
|
|
322
373
|
// Подписи и заливка не ждут `timeupdate`: перемотка из кода видна сразу
|
|
323
374
|
this.#renderProgress();
|
|
324
375
|
return this;
|
|
325
376
|
}
|
|
326
|
-
getDuration() {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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.#
|
|
396
|
+
this.#chuiAt.volume = Math.min(1, Math.max(0, Number(value)));
|
|
334
397
|
return this;
|
|
335
398
|
}
|
|
336
|
-
getMuted() {
|
|
399
|
+
getMuted() {
|
|
400
|
+
return this.#chuiAt.muted;
|
|
401
|
+
}
|
|
337
402
|
setMuted(boolean = Boolean()) {
|
|
338
|
-
this.#
|
|
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.#
|
|
435
|
+
this.#chuiAt.addEventListener(type, listener);
|
|
351
436
|
return this;
|
|
352
437
|
}
|
|
353
438
|
removeEventListener(type = String(), listener = () => {}) {
|
|
354
|
-
this.#
|
|
439
|
+
this.#chuiAt.removeEventListener(type, listener);
|
|
355
440
|
return this;
|
|
356
441
|
}
|
|
357
|
-
set() {
|
|
442
|
+
set() {
|
|
443
|
+
return this.#chuiApMain;
|
|
444
|
+
}
|
|
358
445
|
detach() {
|
|
359
446
|
this.pause();
|
|
360
|
-
this.#
|
|
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.#
|
|
368
|
-
this.#
|
|
369
|
-
this.#
|
|
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.#
|
|
373
|
-
if (this.#
|
|
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
|
-
#
|
|
385
|
-
#
|
|
386
|
-
#
|
|
387
|
-
#
|
|
388
|
-
#
|
|
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
|
-
#
|
|
402
|
-
#
|
|
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
|
-
|
|
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.#
|
|
429
|
-
this.#filters.forEach((filter) => this.#
|
|
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(
|
|
433
|
-
|
|
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.#
|
|
442
|
-
else this.#
|
|
443
|
-
})
|
|
531
|
+
if (this.#switch.getValue()) this.#fxON(this.#audioContext);
|
|
532
|
+
else this.#fxOFF(this.#audioContext);
|
|
533
|
+
});
|
|
444
534
|
//
|
|
445
|
-
this.#
|
|
446
|
-
this.#
|
|
535
|
+
this.#chuiApEqualizerControls.appendChild(this.#presets.set());
|
|
536
|
+
this.#chuiApEqualizerControls.appendChild(this.#switch.set());
|
|
447
537
|
//
|
|
448
|
-
this.#
|
|
449
|
-
this.#
|
|
538
|
+
this.#chuiApEqualizerBlock.appendChild(this.#chuiApEqualizerPreampBlock);
|
|
539
|
+
this.#chuiApEqualizerBlock.appendChild(this.#chuiApEqualizerBandBlock);
|
|
450
540
|
//
|
|
451
|
-
this.#
|
|
452
|
-
this.#
|
|
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
|
-
#
|
|
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.#
|
|
476
|
-
store.set(this.#
|
|
477
|
-
this.#setStatusBlock(true)
|
|
565
|
+
store.delete(this.#fxStatus);
|
|
566
|
+
store.set(this.#fxStatus, true);
|
|
567
|
+
this.#setStatusBlock(true);
|
|
478
568
|
}
|
|
479
|
-
#
|
|
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.#
|
|
484
|
-
store.set(this.#
|
|
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.#
|
|
489
|
-
store.set(this.#
|
|
490
|
-
|
|
491
|
-
this.#renderPreampSlider(undefined,
|
|
492
|
-
|
|
493
|
-
this.#renderSlider(input, filter,
|
|
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 (
|
|
503
|
-
|
|
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.#
|
|
508
|
-
this.#
|
|
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.#
|
|
512
|
-
this.#
|
|
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
|
-
|
|
606
|
+
const sliderPreamp = this.#node("preamp");
|
|
517
607
|
sliderPreamp.disabled = !status;
|
|
518
608
|
if (status) {
|
|
519
|
-
sliderPreamp.style.cursor = "pointer"
|
|
520
|
-
this.#
|
|
521
|
-
this.#
|
|
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.#
|
|
525
|
-
this.#
|
|
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
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
sliderMain.id = "preamp_main"
|
|
541
|
-
slider.id = "preamp"
|
|
542
|
-
slider.type =
|
|
543
|
-
slider.className =
|
|
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(
|
|
553
|
-
slider.oninput = (e) => this.#renderPreampSlider(e.target, this.#getPreset(store.get(this.#
|
|
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
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
slider.id =
|
|
571
|
-
slider.type =
|
|
572
|
-
slider.className =
|
|
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:
|
|
578
|
-
slider.setAttribute("aria-valuetext", I18n.t("media.decibels", {value: slider.value}))
|
|
579
|
-
slider.style.setProperty(
|
|
580
|
-
slider.oninput = (e) => this.#renderSlider(e.target, filter, Number(this.#node("preamp").value))
|
|
581
|
-
val.id = "val_" +
|
|
582
|
-
this.#nodes.set(
|
|
583
|
-
this.#nodes.set("val_" +
|
|
584
|
-
val.innerText = String(slider.value)
|
|
585
|
-
label.innerText =
|
|
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
|
-
|
|
595
|
-
|
|
684
|
+
const status = store.get(this.#fxStatus);
|
|
685
|
+
const preset = store.get(this.#fxPreset);
|
|
596
686
|
if (status) {
|
|
597
|
-
this.#
|
|
598
|
-
this.#switch.setValue(true)
|
|
599
|
-
this.#setPresetName(store.get(this.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
622
|
-
else this.#
|
|
711
|
+
if (boolean) this.#fxON(this.#audioContext);
|
|
712
|
+
else this.#fxOFF(this.#audioContext);
|
|
623
713
|
return this;
|
|
624
714
|
}
|
|
625
715
|
/** Состояние выключателя в панели */
|
|
626
|
-
isEnabled() {
|
|
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
|
-
|
|
641
|
-
|
|
732
|
+
const preampSlider = this.#node("preamp");
|
|
733
|
+
const preampValue = this.#node("val_preamp");
|
|
642
734
|
if (input === undefined) {
|
|
643
|
-
|
|
735
|
+
preampSlider.value = preset.preamp;
|
|
644
736
|
} else {
|
|
645
|
-
|
|
646
|
-
preset.inputs.forEach(inp => {
|
|
647
|
-
|
|
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
|
-
|
|
656
|
-
|
|
657
|
-
|
|
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
|
-
|
|
661
|
-
|
|
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(
|
|
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;
|