chuijs 4.0.2 → 4.0.3
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 +61 -0
- package/README.md +43 -644
- package/framework/appLayout/default/chui_app_layout.js +308 -260
- package/framework/appLayout/default/global_style.css +85 -48
- package/framework/components/chui_accordion/accordion.js +29 -26
- package/framework/components/chui_accordion/styles.css +6 -5
- package/framework/components/chui_action_sheet/action_sheet.js +59 -33
- package/framework/components/chui_action_sheet/styles.css +2 -1
- package/framework/components/chui_affix/affix.js +11 -6
- package/framework/components/chui_alert/alert.js +23 -14
- package/framework/components/chui_avatar/avatar.js +23 -22
- package/framework/components/chui_avatar_group/avatar_group.js +21 -19
- package/framework/components/chui_backtop/backtop.js +35 -19
- package/framework/components/chui_badge/badge.js +15 -13
- package/framework/components/chui_breadcrumb/breadcrumb.js +15 -13
- package/framework/components/chui_button/button.js +42 -22
- package/framework/components/chui_button/styles.css +2 -2
- package/framework/components/chui_calendar/calendar.js +12 -10
- package/framework/components/chui_card/card.js +43 -23
- package/framework/components/chui_chat/chat.js +72 -33
- package/framework/components/chui_clipboard_history/clipboard_history.js +30 -21
- package/framework/components/chui_clipboard_history/styles.css +4 -2
- package/framework/components/chui_code/code.js +2 -2
- package/framework/components/chui_code/styles.css +2 -2
- package/framework/components/chui_code_editor/code_editor.js +47 -31
- package/framework/components/chui_code_editor/styles.css +3 -2
- package/framework/components/chui_command_palette/command_palette.js +60 -37
- package/framework/components/chui_console/console.js +5 -5
- package/framework/components/chui_console/styles.css +1 -1
- package/framework/components/chui_content_block/content_block.js +33 -19
- package/framework/components/chui_content_block/styles.css +2 -2
- package/framework/components/chui_context_menu/context_menu.js +9 -4
- package/framework/components/chui_custom_element/custom_element.js +22 -15
- package/framework/components/chui_data_grid/data_grid.js +396 -234
- package/framework/components/chui_data_grid/styles.css +10 -8
- package/framework/components/chui_descriptions/descriptions.js +34 -17
- package/framework/components/chui_details/details.js +32 -26
- package/framework/components/chui_details/styles.css +5 -4
- package/framework/components/chui_diff_viewer/diff_viewer.js +91 -66
- package/framework/components/chui_diff_viewer/styles.css +1 -1
- package/framework/components/chui_divider/divider.js +13 -11
- package/framework/components/chui_drawer/drawer.js +27 -25
- package/framework/components/chui_drop_zone/drop_zone.js +31 -21
- package/framework/components/chui_empty_state/empty_state.js +20 -15
- package/framework/components/chui_fieldset/fieldset.js +37 -20
- package/framework/components/chui_fieldset/styles.css +1 -1
- package/framework/components/chui_file_explorer/file_explorer.js +157 -86
- package/framework/components/chui_file_explorer/styles.css +2 -1
- package/framework/components/chui_form/form.js +31 -19
- package/framework/components/chui_form/styles.css +1 -1
- package/framework/components/chui_gantt/gantt.js +64 -40
- package/framework/components/chui_graphs/graphs.js +301 -200
- package/framework/components/chui_graphs/styles.css +1 -1
- package/framework/components/chui_hero/hero.js +40 -22
- package/framework/components/chui_html_block/html_block.js +6 -6
- package/framework/components/chui_html_block/styles.css +2 -2
- package/framework/components/chui_hx/hx.js +3 -3
- package/framework/components/chui_hx/styles.css +11 -4
- package/framework/components/chui_icons/icons.js +2109 -2109
- package/framework/components/chui_icons/styles.css +2 -2
- package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
- package/framework/components/chui_inputs/chui_check_box/styles.css +18 -18
- package/framework/components/chui_inputs/chui_chips/chips.js +50 -40
- package/framework/components/chui_inputs/chui_chips/styles.css +4 -2
- package/framework/components/chui_inputs/chui_color_picker/color_picker.js +36 -24
- package/framework/components/chui_inputs/chui_combo_box/combo_box.js +100 -82
- package/framework/components/chui_inputs/chui_combo_box/styles.css +14 -12
- package/framework/components/chui_inputs/chui_date/date.js +102 -66
- package/framework/components/chui_inputs/chui_date/styles.css +12 -11
- package/framework/components/chui_inputs/chui_date_range/date_range.js +40 -29
- package/framework/components/chui_inputs/chui_datetime/datetime.js +35 -27
- package/framework/components/chui_inputs/chui_email/email.js +43 -31
- package/framework/components/chui_inputs/chui_email/styles.css +5 -5
- package/framework/components/chui_inputs/chui_file/file.js +68 -41
- package/framework/components/chui_inputs/chui_file/styles.css +13 -12
- package/framework/components/chui_inputs/chui_hotkey/hotkey.js +61 -36
- package/framework/components/chui_inputs/chui_masked/masked.js +39 -28
- package/framework/components/chui_inputs/chui_mention/mention.js +77 -30
- package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +173 -153
- package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +9 -7
- package/framework/components/chui_inputs/chui_number/number.js +58 -41
- package/framework/components/chui_inputs/chui_number/styles.css +7 -7
- package/framework/components/chui_inputs/chui_otp/otp.js +38 -29
- package/framework/components/chui_inputs/chui_password/password.js +58 -36
- package/framework/components/chui_inputs/chui_password/styles.css +8 -8
- package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
- package/framework/components/chui_inputs/chui_radio_button/styles.css +20 -20
- package/framework/components/chui_inputs/chui_rating/rating.js +26 -22
- package/framework/components/chui_inputs/chui_select_box/select_box.js +102 -78
- package/framework/components/chui_inputs/chui_select_box/styles.css +11 -11
- package/framework/components/chui_inputs/chui_slider/slider.js +29 -26
- package/framework/components/chui_inputs/chui_slider/styles.css +2 -1
- package/framework/components/chui_inputs/chui_text/styles.css +8 -8
- package/framework/components/chui_inputs/chui_text/text.js +65 -44
- package/framework/components/chui_inputs/chui_text_area/styles.css +6 -6
- package/framework/components/chui_inputs/chui_text_area/text_area.js +51 -28
- package/framework/components/chui_inputs/chui_time/styles.css +5 -3
- package/framework/components/chui_inputs/chui_time/time.js +38 -27
- package/framework/components/chui_inputs/chui_toggle/styles.css +3 -3
- package/framework/components/chui_inputs/chui_toggle/toggle.js +16 -16
- package/framework/components/chui_json_editor/json_editor.js +53 -43
- package/framework/components/chui_json_viewer/json_viewer.js +52 -33
- package/framework/components/chui_json_viewer/styles.css +3 -2
- package/framework/components/chui_kanban/kanban.js +52 -34
- package/framework/components/chui_kbd/kbd.js +12 -10
- package/framework/components/chui_label/label.js +14 -12
- package/framework/components/chui_label/styles.css +2 -2
- package/framework/components/chui_link/link.js +27 -20
- package/framework/components/chui_list/list.js +31 -27
- package/framework/components/chui_list/styles.css +2 -1
- package/framework/components/chui_markdown_editor/markdown_editor.js +60 -44
- package/framework/components/chui_media/audio.js +326 -227
- package/framework/components/chui_media/audio_styles.css +40 -21
- package/framework/components/chui_media/image.js +70 -38
- package/framework/components/chui_media/image_compare.js +52 -21
- package/framework/components/chui_media/image_styles.css +3 -3
- package/framework/components/chui_media/video.js +155 -62
- package/framework/components/chui_media/video_styles.css +33 -16
- package/framework/components/chui_menu_bar/menu_bar.js +26 -24
- package/framework/components/chui_menu_bar/styles.css +4 -2
- package/framework/components/chui_modal/modal.js +44 -43
- package/framework/components/chui_modal/styles.css +3 -2
- package/framework/components/chui_notification/notification.js +50 -34
- package/framework/components/chui_notification/notification_download.js +31 -25
- package/framework/components/chui_notification/notification_download_progress.css +3 -2
- package/framework/components/chui_notification/notification_download_progress.js +47 -41
- package/framework/components/chui_notification/notification_update.js +23 -16
- package/framework/components/chui_notification/status_plate.js +2 -2
- package/framework/components/chui_notification/styles_download.css +1 -1
- package/framework/components/chui_notification/styles_update.css +1 -1
- package/framework/components/chui_org_chart/org_chart.js +74 -35
- package/framework/components/chui_pagination/pagination.js +62 -27
- package/framework/components/chui_paragraph/paragraph.js +2 -2
- package/framework/components/chui_paragraph/styles.css +1 -1
- package/framework/components/chui_popover/popover.js +49 -28
- package/framework/components/chui_popups/popups.js +137 -108
- package/framework/components/chui_popups/styles.css +2 -2
- package/framework/components/chui_print_button/print_button.js +33 -23
- package/framework/components/chui_progress_bar/progress_bar.js +23 -15
- package/framework/components/chui_progress_bar/styles.css +2 -2
- package/framework/components/chui_progress_ring/progress_ring.js +17 -15
- package/framework/components/chui_qrcode/qrcode.js +75 -31
- package/framework/components/chui_query_builder/query_builder.js +216 -123
- package/framework/components/chui_radio_group/radio_group.js +36 -26
- package/framework/components/chui_radio_group/styles.css +1 -1
- package/framework/components/chui_result/result.js +26 -16
- package/framework/components/chui_scheduler/scheduler.js +57 -35
- package/framework/components/chui_scheduler/styles.css +6 -2
- package/framework/components/chui_segmented_control/segmented_control.js +27 -21
- package/framework/components/chui_settings/settings.js +122 -48
- package/framework/components/chui_shortcut_help/shortcut_help.js +55 -32
- package/framework/components/chui_sidebar_nav/sidebar_nav.js +33 -23
- package/framework/components/chui_skeleton/skeleton.js +29 -24
- package/framework/components/chui_slideshow/slideshow.js +60 -41
- package/framework/components/chui_slideshow/styles.css +18 -12
- package/framework/components/chui_spinner/spinner.js +8 -8
- package/framework/components/chui_spinner/styles.css +0 -1
- package/framework/components/chui_split_button/split_button.js +23 -18
- package/framework/components/chui_split_button/styles.css +1 -1
- package/framework/components/chui_splitter/splitter.js +35 -28
- package/framework/components/chui_statistic/statistic.js +65 -38
- package/framework/components/chui_status_bar/status_bar.js +11 -9
- package/framework/components/chui_stepper/stepper.js +27 -22
- package/framework/components/chui_sticky_bar/sticky_bar.js +9 -4
- package/framework/components/chui_sticky_bar/styles.css +3 -1
- package/framework/components/chui_tabs/chui_tabs.js +28 -22
- package/framework/components/chui_tabs/styles.css +3 -3
- package/framework/components/chui_text_editor/styles.css +20 -19
- package/framework/components/chui_text_editor/text_editor.js +85 -61
- package/framework/components/chui_text_editor/text_editor_panel.js +346 -284
- package/framework/components/chui_theme_toggle/theme_toggle.js +27 -22
- package/framework/components/chui_timeline/timeline.js +14 -12
- package/framework/components/chui_toast_queue/toast_queue.js +52 -46
- package/framework/components/chui_toolbar/toolbar.js +20 -13
- package/framework/components/chui_tooltip/tooltip.js +37 -24
- package/framework/components/chui_tour/styles.css +8 -3
- package/framework/components/chui_tour/tour.js +61 -27
- package/framework/components/chui_transfer/styles.css +2 -1
- package/framework/components/chui_transfer/transfer.js +75 -42
- package/framework/components/chui_tree_table/tree_table.js +43 -29
- package/framework/components/chui_tree_view/styles.css +4 -2
- package/framework/components/chui_tree_view/tree_view.js +142 -99
- package/framework/components/chui_watermark/watermark.js +24 -16
- package/framework/components/chui_webview/styles.css +2 -2
- package/framework/components/chui_webview/webview.js +62 -50
- package/framework/components/telegram_bot/chui_telegram_bot.js +71 -64
- package/framework/modules/chui_a11y/chui_a11y.js +77 -46
- package/framework/modules/chui_animations/animations.js +42 -42
- package/framework/modules/chui_animations/styles.css +1 -1
- package/framework/modules/chui_bridge/preload.js +36 -25
- package/framework/modules/chui_functions.js +52 -50
- package/framework/modules/chui_highlight/chui_highlight.js +25 -22
- package/framework/modules/chui_i18n/chui_i18n.js +9 -7
- 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 +3 -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 +8 -8
- package/framework/modules/chui_i18n/locales/validate.js +3 -3
- package/framework/modules/chui_json/chui_json.js +9 -9
- package/framework/modules/chui_layout/chui_layout.js +12 -10
- package/framework/modules/chui_logger/chui_logger.js +29 -25
- package/framework/modules/chui_page/page.js +33 -27
- package/framework/modules/chui_qr/chui_qr.js +158 -67
- package/framework/modules/chui_scrollbar/chui_scrollbar.js +35 -24
- package/framework/modules/chui_scrollbar/styles.css +3 -1
- package/framework/modules/chui_services/chui_channels.js +27 -13
- package/framework/modules/chui_services/chui_services.js +39 -31
- package/framework/modules/chui_services/desktop.js +32 -31
- package/framework/modules/chui_sortable/chui_sortable.js +17 -12
- package/framework/modules/chui_validate/chui_validate.js +7 -4
- package/framework/tray_bar/chui_menu_items.js +136 -46
- package/index.d.ts +4814 -0
- package/index.js +385 -292
- package/package.json +79 -68
|
@@ -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
|
/**
|
|
@@ -43,13 +43,28 @@ class Audio {
|
|
|
43
43
|
#destroyed = false;
|
|
44
44
|
#onPlay = () => this.#renderPlayState(true);
|
|
45
45
|
#onPause = () => this.#renderPlayState(false);
|
|
46
|
-
#onTimeUpdate = () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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");
|
|
53
68
|
this.#chui_at.preload = "metadata";
|
|
54
69
|
this.#chui_at.controls = false;
|
|
55
70
|
// Плеер — именованная группа: без имени скринридер читает подряд безымянные кнопки
|
|
@@ -64,8 +79,8 @@ class Audio {
|
|
|
64
79
|
if (options.width !== undefined) this.#chui_ap_main.style.width = options.width;
|
|
65
80
|
if (options.height !== undefined) this.#chui_ap_main.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`});
|
|
82
|
+
this.#chui_ap_time1 = new Label({ text: `0:00` });
|
|
83
|
+
this.#chui_ap_time2 = new Label({ text: `0:00` });
|
|
69
84
|
this.#chui_ap_seek.type = "range";
|
|
70
85
|
this.#chui_ap_seek.id = "chui_ap_seek";
|
|
71
86
|
this.#chui_ap_seek.max = "0";
|
|
@@ -162,7 +177,7 @@ class Audio {
|
|
|
162
177
|
if (this.#chui_ap_fx_dropdown.contains(event.target)) return;
|
|
163
178
|
if (this.#chui_ap_fx_icon.contains(event.target)) return;
|
|
164
179
|
this.#toggleFx(false);
|
|
165
|
-
}
|
|
180
|
+
};
|
|
166
181
|
#playPause = async () => {
|
|
167
182
|
if (this.getPaused()) {
|
|
168
183
|
try {
|
|
@@ -171,30 +186,37 @@ class Audio {
|
|
|
171
186
|
Log.error(`Audio: воспроизведение не началось: ${error}`);
|
|
172
187
|
}
|
|
173
188
|
} else this.pause();
|
|
174
|
-
}
|
|
189
|
+
};
|
|
175
190
|
#renderPlayState(playing) {
|
|
176
|
-
this.#setIcon(
|
|
191
|
+
this.#setIcon(
|
|
192
|
+
this.#chui_ap_play_pause,
|
|
193
|
+
playing ? Icons.AUDIO_VIDEO.PAUSE : Icons.AUDIO_VIDEO.PLAY_ARROW,
|
|
194
|
+
"18px"
|
|
195
|
+
);
|
|
177
196
|
this.#setLabel(this.#chui_ap_play_pause, playing ? I18n.t("media.pause") : I18n.t("media.play"));
|
|
178
197
|
}
|
|
179
198
|
#renderVolume = () => {
|
|
180
199
|
const доля = Math.min(100, Math.max(0, Math.round(this.#chui_at.volume * 100)));
|
|
181
200
|
this.#chui_ap_volume.value = String(доля);
|
|
182
201
|
this.#chui_ap_volume.disabled = this.getMuted();
|
|
183
|
-
this.#chui_ap_main.style.setProperty(
|
|
184
|
-
this.#setIcon(
|
|
185
|
-
|
|
202
|
+
this.#chui_ap_main.style.setProperty("--volume-before-width", `${доля}%`);
|
|
203
|
+
this.#setIcon(
|
|
204
|
+
this.#chui_ap_volume_icon,
|
|
205
|
+
this.getMuted() || доля === 0 ? Icons.AUDIO_VIDEO.VOLUME_OFF : Icons.AUDIO_VIDEO.VOLUME_UP,
|
|
206
|
+
"18px"
|
|
207
|
+
);
|
|
186
208
|
this.#setLabel(this.#chui_ap_volume_icon, I18n.t(this.getMuted() ? "media.unmute" : "media.mute"));
|
|
187
|
-
}
|
|
209
|
+
};
|
|
188
210
|
#displayBufferedAmount = () => {
|
|
189
211
|
const max = Number(this.#chui_ap_seek.max);
|
|
190
212
|
if (!(max > 0)) return;
|
|
191
213
|
try {
|
|
192
|
-
const конец = Math.floor(this.#chui_at.buffered.end(0) / max * 100);
|
|
214
|
+
const конец = Math.floor((this.#chui_at.buffered.end(0) / max) * 100);
|
|
193
215
|
this.#chui_ap_seek_buf.style.width = `${Math.min(100, Math.max(0, конец))}%`;
|
|
194
216
|
} catch (_error) {
|
|
195
217
|
// Буфер ещё пуст (метаданные не пришли) — рисовать нечего
|
|
196
218
|
}
|
|
197
|
-
}
|
|
219
|
+
};
|
|
198
220
|
// Длительность приходит с метаданными: без неё полоса прокрутки делила бы на ноль,
|
|
199
221
|
// а у живого потока её нет вовсе (duration = Infinity)
|
|
200
222
|
#setSliderMax = () => {
|
|
@@ -203,7 +225,7 @@ class Audio {
|
|
|
203
225
|
this.#chui_ap_seek.max = известна ? String(Math.floor(duration)) : "0";
|
|
204
226
|
// Без известной длительности ползунок бессмысленен: перетаскивание отправляло бы плеер в начало
|
|
205
227
|
this.#chui_ap_seek.disabled = !известна;
|
|
206
|
-
}
|
|
228
|
+
};
|
|
207
229
|
#renderProgress = () => {
|
|
208
230
|
const max = Number(this.#chui_ap_seek.max);
|
|
209
231
|
const position = this.getCurrentTime();
|
|
@@ -212,7 +234,7 @@ class Audio {
|
|
|
212
234
|
// «0:07 / 0:00» читалось бы как поломка: у источника без длительности только прошедшее
|
|
213
235
|
this.#chui_ap_time1.setText(this.#calculateTime(position));
|
|
214
236
|
this.#chui_ap_time2.setText("");
|
|
215
|
-
this.#chui_ap_main.style.setProperty(
|
|
237
|
+
this.#chui_ap_main.style.setProperty("--seek-before-width", "0%");
|
|
216
238
|
return;
|
|
217
239
|
}
|
|
218
240
|
this.#chui_ap_time1.setText(this.#calculateTime(position));
|
|
@@ -220,22 +242,24 @@ class Audio {
|
|
|
220
242
|
this.#chui_ap_seek.value = String(position);
|
|
221
243
|
// Доля ограничена сотней: `max` — округлённая длительность, а позиция идёт дальше,
|
|
222
244
|
// и без ограничения полоса целиком заливалась акцентом
|
|
223
|
-
const доля = Math.min(100, Math.max(0, Math.floor(position / max * 100)));
|
|
224
|
-
this.#chui_ap_main.style.setProperty(
|
|
225
|
-
}
|
|
245
|
+
const доля = Math.min(100, Math.max(0, Math.floor((position / max) * 100)));
|
|
246
|
+
this.#chui_ap_main.style.setProperty("--seek-before-width", `${доля}%`);
|
|
247
|
+
};
|
|
226
248
|
#calculateTime = (secs) => {
|
|
227
249
|
const seconds = Number.isFinite(Number(secs)) ? Number(secs) : 0;
|
|
228
250
|
const minutes = Math.floor(seconds / 60);
|
|
229
251
|
const rest = Math.floor(seconds % 60);
|
|
230
252
|
return `${minutes}:${rest < 10 ? `0${rest}` : `${rest}`}`;
|
|
231
|
-
}
|
|
253
|
+
};
|
|
232
254
|
#onMediaError = () => {
|
|
233
255
|
const код = this.#chui_at.error === null ? undefined : this.#chui_at.error.code;
|
|
234
256
|
const причина = Audio.#ERRORS[код] === undefined ? "" : `: ${I18n.t(Audio.#ERRORS[код])}`;
|
|
235
257
|
Log.error(`Audio: ошибка воспроизведения${причина}`);
|
|
236
|
-
this.#setStatus(`${I18n.t("media.audioNotPlaying", {reason: I18n.t("media.error.interrupted")})}${причина}`);
|
|
258
|
+
this.#setStatus(`${I18n.t("media.audioNotPlaying", { reason: I18n.t("media.error.interrupted") })}${причина}`);
|
|
259
|
+
};
|
|
260
|
+
#setStatus(text) {
|
|
261
|
+
this.#status.innerText = text;
|
|
237
262
|
}
|
|
238
|
-
#setStatus(text) { this.#status.innerText = text; }
|
|
239
263
|
#setButton(node, label, icon, size) {
|
|
240
264
|
node.innerHTML = new Icon(icon, size).getHTML();
|
|
241
265
|
node.setAttribute("aria-label", label);
|
|
@@ -285,67 +309,133 @@ class Audio {
|
|
|
285
309
|
else this.#chui_ap_main.setAttribute("data_pin", значение);
|
|
286
310
|
return this;
|
|
287
311
|
}
|
|
288
|
-
getPin() {
|
|
312
|
+
getPin() {
|
|
313
|
+
return this.#pin;
|
|
314
|
+
}
|
|
289
315
|
// === ЭКВАЛАЙЗЕР ===
|
|
290
316
|
/** Включить или выключить эквалайзер: то же, что делает выключатель в панели */
|
|
291
|
-
setEqualizerEnabled(boolean = Boolean()) {
|
|
292
|
-
|
|
317
|
+
setEqualizerEnabled(boolean = Boolean()) {
|
|
318
|
+
return (this.#chui_audio_fx.setEnabled(boolean), this);
|
|
319
|
+
}
|
|
320
|
+
isEqualizerEnabled() {
|
|
321
|
+
return this.#chui_audio_fx.isEnabled();
|
|
322
|
+
}
|
|
293
323
|
/** Раскрыть или свернуть панель эквалайзера */
|
|
294
|
-
setEqualizerOpen(open = Boolean()) {
|
|
324
|
+
setEqualizerOpen(open = Boolean()) {
|
|
325
|
+
this.#toggleFx(open === true);
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
295
328
|
/** Показать панель эквалайзера, если она скрыта, и наоборот */
|
|
296
|
-
isEqualizerOpen() {
|
|
329
|
+
isEqualizerOpen() {
|
|
330
|
+
return this.#chui_ap_fx_dropdown.classList.contains("fx_open");
|
|
331
|
+
}
|
|
297
332
|
/** Прочитать сохранённое состояние эквалайзера из настроек */
|
|
298
|
-
restoreFX() {
|
|
333
|
+
restoreFX() {
|
|
334
|
+
this.#chui_audio_fx.restore();
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
299
337
|
// === СТАНДАРТНЫЕ ВОЗМОЖНОСТИ `<audio>` ===
|
|
300
338
|
/** Сам элемент: для всего, чего нет в списке ниже */
|
|
301
|
-
getAudio() {
|
|
339
|
+
getAudio() {
|
|
340
|
+
return this.#chui_at;
|
|
341
|
+
}
|
|
302
342
|
/** Воспроизведение: промис, как у `<audio>` */
|
|
303
|
-
play() {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
343
|
+
play() {
|
|
344
|
+
return this.#chui_at.play();
|
|
345
|
+
}
|
|
346
|
+
pause() {
|
|
347
|
+
this.#chui_at.pause();
|
|
348
|
+
return this;
|
|
349
|
+
}
|
|
350
|
+
load() {
|
|
351
|
+
this.#chui_at.load();
|
|
352
|
+
return this;
|
|
353
|
+
}
|
|
354
|
+
canPlayType(type = String()) {
|
|
355
|
+
return this.#chui_at.canPlayType(type);
|
|
356
|
+
}
|
|
357
|
+
getSrc() {
|
|
358
|
+
return this.#chui_at.getAttribute("src") ?? "";
|
|
359
|
+
}
|
|
308
360
|
setSrc(url = String()) {
|
|
309
361
|
this.#chui_at.setAttribute("src", url);
|
|
310
362
|
this.#chui_at.load();
|
|
311
363
|
return this;
|
|
312
364
|
}
|
|
313
|
-
getSrcObject() {
|
|
365
|
+
getSrcObject() {
|
|
366
|
+
return this.#chui_at.srcObject;
|
|
367
|
+
}
|
|
314
368
|
/** Живой поток — штатное `srcObject`, а не путь */
|
|
315
369
|
setSrcObject(object) {
|
|
316
370
|
this.#chui_at.srcObject = object;
|
|
317
371
|
return this;
|
|
318
372
|
}
|
|
319
|
-
getCurrentTime() {
|
|
373
|
+
getCurrentTime() {
|
|
374
|
+
return this.#chui_at.currentTime;
|
|
375
|
+
}
|
|
320
376
|
setCurrentTime(seconds = Number()) {
|
|
321
377
|
this.#chui_at.currentTime = Number(seconds);
|
|
322
378
|
// Подписи и заливка не ждут `timeupdate`: перемотка из кода видна сразу
|
|
323
379
|
this.#renderProgress();
|
|
324
380
|
return this;
|
|
325
381
|
}
|
|
326
|
-
getDuration() {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
382
|
+
getDuration() {
|
|
383
|
+
return this.#chui_at.duration;
|
|
384
|
+
}
|
|
385
|
+
getBuffered() {
|
|
386
|
+
return this.#chui_at.buffered;
|
|
387
|
+
}
|
|
388
|
+
getPaused() {
|
|
389
|
+
return this.#chui_at.paused;
|
|
390
|
+
}
|
|
391
|
+
getEnded() {
|
|
392
|
+
return this.#chui_at.ended;
|
|
393
|
+
}
|
|
394
|
+
getReadyState() {
|
|
395
|
+
return this.#chui_at.readyState;
|
|
396
|
+
}
|
|
397
|
+
getVolume() {
|
|
398
|
+
return this.#chui_at.volume;
|
|
399
|
+
}
|
|
332
400
|
setVolume(value = Number()) {
|
|
333
401
|
this.#chui_at.volume = Math.min(1, Math.max(0, Number(value)));
|
|
334
402
|
return this;
|
|
335
403
|
}
|
|
336
|
-
getMuted() {
|
|
404
|
+
getMuted() {
|
|
405
|
+
return this.#chui_at.muted;
|
|
406
|
+
}
|
|
337
407
|
setMuted(boolean = Boolean()) {
|
|
338
408
|
this.#chui_at.muted = boolean === true;
|
|
339
409
|
return this;
|
|
340
410
|
}
|
|
341
|
-
getPlaybackRate() {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
411
|
+
getPlaybackRate() {
|
|
412
|
+
return this.#chui_at.playbackRate;
|
|
413
|
+
}
|
|
414
|
+
setPlaybackRate(rate = Number()) {
|
|
415
|
+
this.#chui_at.playbackRate = Number(rate);
|
|
416
|
+
return this;
|
|
417
|
+
}
|
|
418
|
+
getDefaultPlaybackRate() {
|
|
419
|
+
return this.#chui_at.defaultPlaybackRate;
|
|
420
|
+
}
|
|
421
|
+
setDefaultPlaybackRate(rate = Number()) {
|
|
422
|
+
this.#chui_at.defaultPlaybackRate = Number(rate);
|
|
423
|
+
return this;
|
|
424
|
+
}
|
|
425
|
+
getLoop() {
|
|
426
|
+
return this.#chui_at.loop;
|
|
427
|
+
}
|
|
428
|
+
setLoop(boolean = Boolean()) {
|
|
429
|
+
this.#chui_at.loop = boolean === true;
|
|
430
|
+
return this;
|
|
431
|
+
}
|
|
432
|
+
getAutoplay() {
|
|
433
|
+
return this.#chui_at.autoplay;
|
|
434
|
+
}
|
|
435
|
+
setAutoplay(boolean = Boolean()) {
|
|
436
|
+
this.#chui_at.autoplay = boolean === true;
|
|
437
|
+
return this;
|
|
438
|
+
}
|
|
349
439
|
addEventListener(type = String(), listener = () => {}) {
|
|
350
440
|
this.#chui_at.addEventListener(type, listener);
|
|
351
441
|
return this;
|
|
@@ -354,7 +444,9 @@ class Audio {
|
|
|
354
444
|
this.#chui_at.removeEventListener(type, listener);
|
|
355
445
|
return this;
|
|
356
446
|
}
|
|
357
|
-
set() {
|
|
447
|
+
set() {
|
|
448
|
+
return this.#chui_ap_main;
|
|
449
|
+
}
|
|
358
450
|
detach() {
|
|
359
451
|
this.pause();
|
|
360
452
|
this.#chui_ap_main.remove();
|
|
@@ -366,7 +458,7 @@ class Audio {
|
|
|
366
458
|
this.#destroyed = true;
|
|
367
459
|
this.#chui_at.pause();
|
|
368
460
|
this.#chui_at.removeAttribute("src");
|
|
369
|
-
this.#chui_at.load();
|
|
461
|
+
this.#chui_at.load(); // отпускает декодер и прочитанный файл
|
|
370
462
|
this.#unsetMediaActions();
|
|
371
463
|
document.removeEventListener("mousedown", this.#onFxOutside);
|
|
372
464
|
this.#chui_audio_fx.destroy();
|
|
@@ -378,34 +470,34 @@ class Audio {
|
|
|
378
470
|
2: "media.error.notFound",
|
|
379
471
|
3: "media.error.decode",
|
|
380
472
|
4: "media.error.format",
|
|
381
|
-
}
|
|
473
|
+
};
|
|
382
474
|
}
|
|
383
475
|
class AudioFX {
|
|
384
|
-
#chui_ap_equalizer_main = document.createElement(
|
|
385
|
-
#chui_ap_equalizer_controls = document.createElement(
|
|
386
|
-
#chui_ap_equalizer_block = document.createElement(
|
|
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")
|
|
476
|
+
#chui_ap_equalizer_main = document.createElement("chui_ap_equalizer_main");
|
|
477
|
+
#chui_ap_equalizer_controls = document.createElement("chui_ap_equalizer_controls");
|
|
478
|
+
#chui_ap_equalizer_block = document.createElement("chui_ap_equalizer_block");
|
|
479
|
+
#chui_ap_equalizer_preamp_block = document.createElement("chui_ap_equalizer_preamp_block");
|
|
480
|
+
#chui_ap_equalizer_band_block = document.createElement("chui_ap_equalizer_band_block");
|
|
389
481
|
/* Поле пресетов и переключатель — общекитовые `Select` и `Toggle`, вложенные в панель:
|
|
390
482
|
клавиатура, роли и состояния у них уже есть, а размеры для полосы панели задаёт
|
|
391
483
|
`audio_styles.css`. Прежде тут были свои элементы, и список пресетов ломался:
|
|
392
484
|
при ограниченной высоте слоя `Select` сжимал строки до 11 px — лечится `flex: none`
|
|
393
485
|
у опций (`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]
|
|
486
|
+
#switch = new Toggle({ label: I18n.t("media.enableEqualizer") });
|
|
487
|
+
#presets = new Select();
|
|
488
|
+
#eqBands = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000];
|
|
397
489
|
// Слайдеры эквалайзера по id: узлы свои, берём их из карты, а не поиском по документу
|
|
398
490
|
#nodes = new Map();
|
|
399
491
|
#media = undefined;
|
|
400
492
|
#filters = undefined;
|
|
401
|
-
#fx_status = "chuijs.framework.settings.fx_status"
|
|
402
|
-
#fx_preset = "chuijs.framework.settings.fx_preset"
|
|
493
|
+
#fx_status = "chuijs.framework.settings.fx_status";
|
|
494
|
+
#fx_preset = "chuijs.framework.settings.fx_preset";
|
|
403
495
|
#audioContext = new AudioContext();
|
|
404
496
|
#destroyed = false;
|
|
405
497
|
constructor(audio) {
|
|
406
498
|
this.#media = this.#audioContext.createMediaElementSource(audio);
|
|
407
499
|
this.#filters = this.#eqBands.map((band, i) => {
|
|
408
|
-
let filter = this.#audioContext.createBiquadFilter()
|
|
500
|
+
let filter = this.#audioContext.createBiquadFilter();
|
|
409
501
|
if (i === 0) {
|
|
410
502
|
filter.type = "lowshelf";
|
|
411
503
|
} else if (i === this.#eqBands.length - 1) {
|
|
@@ -413,55 +505,58 @@ class AudioFX {
|
|
|
413
505
|
} else {
|
|
414
506
|
filter.type = "peaking";
|
|
415
507
|
}
|
|
416
|
-
filter.gain.value = 0
|
|
417
|
-
filter.Q.value = 1
|
|
418
|
-
filter.frequency.value = band
|
|
419
|
-
return filter
|
|
420
|
-
})
|
|
508
|
+
filter.gain.value = 0;
|
|
509
|
+
filter.Q.value = 1;
|
|
510
|
+
filter.frequency.value = band;
|
|
511
|
+
return filter;
|
|
512
|
+
});
|
|
421
513
|
this.#filters.reduce((prev, curr) => {
|
|
422
|
-
prev.connect(curr)
|
|
423
|
-
return curr
|
|
424
|
-
})
|
|
514
|
+
prev.connect(curr);
|
|
515
|
+
return curr;
|
|
516
|
+
});
|
|
425
517
|
this.#media.connect(this.#filters[0]);
|
|
426
518
|
this.#filters[this.#filters.length - 1].connect(this.#audioContext.destination);
|
|
427
519
|
//
|
|
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)))
|
|
520
|
+
this.#chui_ap_equalizer_preamp_block.appendChild(this.#setSliderPreamp(this.#filters));
|
|
521
|
+
this.#filters.forEach((filter) => this.#chui_ap_equalizer_band_block.appendChild(this.#setSliderBand(filter)));
|
|
430
522
|
// Пресеты: подпись варианта — перевод, значение — имя пресета, поэтому выбор
|
|
431
523
|
// читается из события списка, а не сверкой подписи
|
|
432
|
-
this.#presets.addOptions(
|
|
433
|
-
|
|
434
|
-
|
|
524
|
+
this.#presets.addOptions(
|
|
525
|
+
...AudioFX.PRESETS.map((preset) => ({
|
|
526
|
+
title: I18n.t(preset.titleKey),
|
|
527
|
+
value: preset.name,
|
|
528
|
+
}))
|
|
529
|
+
);
|
|
435
530
|
// Видимой подписи у поля нет: имя для скринридера ставим вводу (так же делал
|
|
436
531
|
// прежний свой список, у которого подписи не было вовсе)
|
|
437
|
-
this.#presets.set().querySelector("input").setAttribute("aria-label", I18n.t("media.preset"))
|
|
438
|
-
this.#presets.addValueChangeListener((event) => this.#usePreset(event.detail.value))
|
|
532
|
+
this.#presets.set().querySelector("input").setAttribute("aria-label", I18n.t("media.preset"));
|
|
533
|
+
this.#presets.addValueChangeListener((event) => this.#usePreset(event.detail.value));
|
|
439
534
|
// Переключатель — общекитовый `Toggle`; подпись он отдаёт в `aria-label`
|
|
440
535
|
this.#switch.addChangeListener(() => {
|
|
441
|
-
if (this.#switch.getValue()) this.#fx_ON(this.#audioContext)
|
|
442
|
-
else this.#fx_OFF(this.#audioContext)
|
|
443
|
-
})
|
|
536
|
+
if (this.#switch.getValue()) this.#fx_ON(this.#audioContext);
|
|
537
|
+
else this.#fx_OFF(this.#audioContext);
|
|
538
|
+
});
|
|
444
539
|
//
|
|
445
|
-
this.#chui_ap_equalizer_controls.appendChild(this.#presets.set())
|
|
446
|
-
this.#chui_ap_equalizer_controls.appendChild(this.#switch.set())
|
|
540
|
+
this.#chui_ap_equalizer_controls.appendChild(this.#presets.set());
|
|
541
|
+
this.#chui_ap_equalizer_controls.appendChild(this.#switch.set());
|
|
447
542
|
//
|
|
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)
|
|
543
|
+
this.#chui_ap_equalizer_block.appendChild(this.#chui_ap_equalizer_preamp_block);
|
|
544
|
+
this.#chui_ap_equalizer_block.appendChild(this.#chui_ap_equalizer_band_block);
|
|
450
545
|
//
|
|
451
|
-
this.#chui_ap_equalizer_main.appendChild(this.#chui_ap_equalizer_controls)
|
|
452
|
-
this.#chui_ap_equalizer_main.appendChild(this.#chui_ap_equalizer_block)
|
|
546
|
+
this.#chui_ap_equalizer_main.appendChild(this.#chui_ap_equalizer_controls);
|
|
547
|
+
this.#chui_ap_equalizer_main.appendChild(this.#chui_ap_equalizer_block);
|
|
453
548
|
// По умолчанию эквалайзер выключен — и вид полосы должен говорить то же: пока
|
|
454
549
|
// `restoreFX()` не позвал хозяин, выключатель выключен, а дорожки уже серые
|
|
455
|
-
this.#setStatusBlock(false)
|
|
550
|
+
this.#setStatusBlock(false);
|
|
456
551
|
}
|
|
457
552
|
#usePreset(name = String()) {
|
|
458
|
-
this.#filters.forEach((filter) => this.#setPreset(filter, name, true))
|
|
459
|
-
this.#setPresetName(name)
|
|
553
|
+
this.#filters.forEach((filter) => this.#setPreset(filter, name, true));
|
|
554
|
+
this.#setPresetName(name);
|
|
460
555
|
}
|
|
461
556
|
/** Имя пресета в поле: значение ставится извне, поэтому без уведомления слушателей */
|
|
462
557
|
#setPresetName(name = String()) {
|
|
463
|
-
const known = AudioFX.PRESETS.some((preset) => preset.name === name)
|
|
464
|
-
this.#presets.setValue(known ? name : AudioFX.PRESETS[0].name, true)
|
|
558
|
+
const known = AudioFX.PRESETS.some((preset) => preset.name === name);
|
|
559
|
+
this.#presets.setValue(known ? name : AudioFX.PRESETS[0].name, true);
|
|
465
560
|
}
|
|
466
561
|
#fx_ON(audioContext) {
|
|
467
562
|
try {
|
|
@@ -472,146 +567,147 @@ class AudioFX {
|
|
|
472
567
|
this.#media.connect(this.#filters[0]);
|
|
473
568
|
this.#filters[this.#filters.length - 1].connect(audioContext.destination);
|
|
474
569
|
}
|
|
475
|
-
store.delete(this.#fx_status)
|
|
476
|
-
store.set(this.#fx_status, true)
|
|
477
|
-
this.#setStatusBlock(true)
|
|
570
|
+
store.delete(this.#fx_status);
|
|
571
|
+
store.set(this.#fx_status, true);
|
|
572
|
+
this.#setStatusBlock(true);
|
|
478
573
|
}
|
|
479
574
|
#fx_OFF(audioContext) {
|
|
480
575
|
this.#filters[this.#filters.length - 1].disconnect(audioContext.destination);
|
|
481
576
|
this.#media.disconnect(this.#filters[0]);
|
|
482
577
|
this.#media.connect(audioContext.destination);
|
|
483
|
-
store.delete(this.#fx_status)
|
|
484
|
-
store.set(this.#fx_status, false)
|
|
485
|
-
this.#setStatusBlock(false)
|
|
578
|
+
store.delete(this.#fx_status);
|
|
579
|
+
store.set(this.#fx_status, false);
|
|
580
|
+
this.#setStatusBlock(false);
|
|
486
581
|
}
|
|
487
582
|
#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)
|
|
583
|
+
store.delete(this.#fx_preset);
|
|
584
|
+
store.set(this.#fx_preset, name);
|
|
585
|
+
let filter_test = this.#getPreset(name);
|
|
586
|
+
this.#renderPreampSlider(undefined, filter_test, filter);
|
|
587
|
+
let input = filter_test.inputs.filter((input) => input.id === String(filter.frequency.value))[0];
|
|
588
|
+
this.#renderSlider(input, filter, filter_test.preamp);
|
|
494
589
|
}
|
|
495
590
|
#setStatusBlock(status) {
|
|
496
591
|
//
|
|
497
592
|
// Поле пресетов при выключенном эквалайзере не отвечает, а выключатель трогать
|
|
498
593
|
// нельзя: он и есть способ включить эквалайзер обратно (родной флажок в выключенном
|
|
499
594
|
// состоянии не шлёт `change` — включить было бы нечем)
|
|
500
|
-
this.#presets.setDisabled(!status)
|
|
595
|
+
this.#presets.setDisabled(!status);
|
|
501
596
|
//
|
|
502
597
|
for (let band of this.#eqBands) {
|
|
503
598
|
let sliderBand = this.#node(String(band));
|
|
504
599
|
sliderBand.disabled = !status;
|
|
505
600
|
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")
|
|
601
|
+
sliderBand.style.cursor = "pointer";
|
|
602
|
+
this.#chui_ap_equalizer_preamp_block.classList.remove("chui_ap_equalizer_disabled");
|
|
603
|
+
this.#chui_ap_equalizer_band_block.classList.remove("chui_ap_equalizer_disabled");
|
|
509
604
|
} 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")
|
|
605
|
+
sliderBand.style.cursor = "not-allowed";
|
|
606
|
+
this.#chui_ap_equalizer_preamp_block.classList.add("chui_ap_equalizer_disabled");
|
|
607
|
+
this.#chui_ap_equalizer_band_block.classList.add("chui_ap_equalizer_disabled");
|
|
513
608
|
}
|
|
514
609
|
}
|
|
515
610
|
//
|
|
516
611
|
let sliderPreamp = this.#node("preamp");
|
|
517
612
|
sliderPreamp.disabled = !status;
|
|
518
613
|
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")
|
|
614
|
+
sliderPreamp.style.cursor = "pointer";
|
|
615
|
+
this.#chui_ap_equalizer_preamp_block.classList.remove("chui_ap_equalizer_disabled");
|
|
616
|
+
this.#chui_ap_equalizer_band_block.classList.remove("chui_ap_equalizer_disabled");
|
|
522
617
|
} 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")
|
|
618
|
+
sliderPreamp.style.cursor = "not-allowed";
|
|
619
|
+
this.#chui_ap_equalizer_preamp_block.classList.add("chui_ap_equalizer_disabled");
|
|
620
|
+
this.#chui_ap_equalizer_band_block.classList.add("chui_ap_equalizer_disabled");
|
|
526
621
|
}
|
|
527
622
|
}
|
|
528
623
|
#getPreset(name) {
|
|
529
|
-
return AudioFX.PRESETS.filter(preset => preset.name === name)[0];
|
|
624
|
+
return AudioFX.PRESETS.filter((preset) => preset.name === name)[0];
|
|
530
625
|
}
|
|
531
626
|
/** Узел эквалайзера по id — из карты слайдеров этого эквалайзера */
|
|
532
627
|
#node(id = String()) {
|
|
533
628
|
return this.#nodes.get(id);
|
|
534
629
|
}
|
|
535
630
|
#setSliderPreamp(filters) {
|
|
536
|
-
let sliderMain = document.createElement("chui_eq_slider_main")
|
|
537
|
-
let val = document.createElement("slider_label")
|
|
538
|
-
let slider = document.createElement(
|
|
539
|
-
let label = document.createElement("slider_label")
|
|
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}))
|
|
631
|
+
let sliderMain = document.createElement("chui_eq_slider_main");
|
|
632
|
+
let val = document.createElement("slider_label");
|
|
633
|
+
let slider = document.createElement("input");
|
|
634
|
+
let label = document.createElement("slider_label");
|
|
635
|
+
sliderMain.id = "preamp_main";
|
|
636
|
+
slider.id = "preamp";
|
|
637
|
+
slider.type = "range";
|
|
638
|
+
slider.className = "eq_slider_test";
|
|
639
|
+
slider.step = "1";
|
|
640
|
+
slider.min = "-10";
|
|
641
|
+
slider.max = "10";
|
|
642
|
+
slider.value = "0";
|
|
643
|
+
slider.setAttribute("aria-label", I18n.t("media.preamp"));
|
|
644
|
+
slider.setAttribute("aria-valuetext", I18n.t("media.decibels", { value: 0 }));
|
|
550
645
|
// Доля заливки рельса: заливка рисуется градиентом по тому же числу, что у полос
|
|
551
646
|
// прогресса и громкости
|
|
552
|
-
slider.style.setProperty(
|
|
553
|
-
slider.oninput = (e) =>
|
|
554
|
-
|
|
647
|
+
slider.style.setProperty("--fx-before-width", `50%`);
|
|
648
|
+
slider.oninput = (e) =>
|
|
649
|
+
this.#renderPreampSlider(e.target, this.#getPreset(store.get(this.#fx_preset)), filters);
|
|
650
|
+
val.id = "val_preamp";
|
|
555
651
|
this.#nodes.set("preamp", slider);
|
|
556
652
|
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
|
|
653
|
+
val.innerText = String(slider.value);
|
|
654
|
+
label.innerText = "AMP";
|
|
655
|
+
sliderMain.appendChild(val);
|
|
656
|
+
sliderMain.appendChild(slider);
|
|
657
|
+
sliderMain.appendChild(label);
|
|
658
|
+
return sliderMain;
|
|
563
659
|
}
|
|
564
660
|
#setSliderBand(filter) {
|
|
565
661
|
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(
|
|
569
|
-
let label = document.createElement("slider_label")
|
|
570
|
-
slider.id = freq_value
|
|
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: freq_value}))
|
|
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_" + freq_value
|
|
662
|
+
let sliderMain = document.createElement("chui_eq_slider_main");
|
|
663
|
+
let val = document.createElement("slider_label");
|
|
664
|
+
let slider = document.createElement("input");
|
|
665
|
+
let label = document.createElement("slider_label");
|
|
666
|
+
slider.id = freq_value;
|
|
667
|
+
slider.type = "range";
|
|
668
|
+
slider.className = "eq_slider_test";
|
|
669
|
+
slider.step = "0.1";
|
|
670
|
+
slider.min = "-10";
|
|
671
|
+
slider.max = "10";
|
|
672
|
+
slider.value = String(filter.gain.value);
|
|
673
|
+
slider.setAttribute("aria-label", I18n.t("media.hertzGain", { freq: freq_value }));
|
|
674
|
+
slider.setAttribute("aria-valuetext", I18n.t("media.decibels", { value: slider.value }));
|
|
675
|
+
slider.style.setProperty("--fx-before-width", `50%`);
|
|
676
|
+
slider.oninput = (e) => this.#renderSlider(e.target, filter, Number(this.#node("preamp").value));
|
|
677
|
+
val.id = "val_" + freq_value;
|
|
582
678
|
this.#nodes.set(freq_value, slider);
|
|
583
679
|
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
|
|
680
|
+
val.innerText = String(slider.value);
|
|
681
|
+
label.innerText = freq_value.replace("000", "K");
|
|
682
|
+
sliderMain.appendChild(val);
|
|
683
|
+
sliderMain.appendChild(slider);
|
|
684
|
+
sliderMain.appendChild(label);
|
|
685
|
+
return sliderMain;
|
|
590
686
|
}
|
|
591
687
|
restore() {
|
|
592
688
|
setTimeout(() => {
|
|
593
689
|
if (this.#destroyed) return;
|
|
594
|
-
let status = store.get(this.#fx_status)
|
|
595
|
-
let preset = store.get(this.#fx_preset)
|
|
690
|
+
let status = store.get(this.#fx_status);
|
|
691
|
+
let preset = store.get(this.#fx_preset);
|
|
596
692
|
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)
|
|
693
|
+
this.#fx_ON(this.#audioContext);
|
|
694
|
+
this.#switch.setValue(true);
|
|
695
|
+
this.#setPresetName(store.get(this.#fx_preset));
|
|
696
|
+
this.#setStatusBlock(status);
|
|
601
697
|
} else {
|
|
602
|
-
this.#fx_OFF(this.#audioContext)
|
|
603
|
-
this.#switch.setValue(false)
|
|
698
|
+
this.#fx_OFF(this.#audioContext);
|
|
699
|
+
this.#switch.setValue(false);
|
|
604
700
|
if (status === undefined || preset === undefined) {
|
|
605
|
-
this.#setPresetName("Default")
|
|
701
|
+
this.#setPresetName("Default");
|
|
606
702
|
} else {
|
|
607
|
-
this.#setPresetName(store.get(this.#fx_preset))
|
|
703
|
+
this.#setPresetName(store.get(this.#fx_preset));
|
|
608
704
|
}
|
|
609
|
-
this.#setStatusBlock(status)
|
|
705
|
+
this.#setStatusBlock(status);
|
|
610
706
|
}
|
|
611
|
-
}, 250)
|
|
707
|
+
}, 250);
|
|
612
708
|
}
|
|
613
709
|
set() {
|
|
614
|
-
return this.#chui_ap_equalizer_main
|
|
710
|
+
return this.#chui_ap_equalizer_main;
|
|
615
711
|
}
|
|
616
712
|
/** Включить или выключить эквалайзер: тот же путь, что у выключателя в панели */
|
|
617
713
|
setEnabled(boolean = Boolean()) {
|
|
@@ -623,7 +719,9 @@ class AudioFX {
|
|
|
623
719
|
return this;
|
|
624
720
|
}
|
|
625
721
|
/** Состояние выключателя в панели */
|
|
626
|
-
isEnabled() {
|
|
722
|
+
isEnabled() {
|
|
723
|
+
return this.#switch.getValue();
|
|
724
|
+
}
|
|
627
725
|
/**
|
|
628
726
|
* Отпустить звуковой граф: контекст держит аудиопоток, а Chromium разрешает ограниченное
|
|
629
727
|
* число контекстов на страницу. Повторный вызов безопасен.
|
|
@@ -640,30 +738,30 @@ class AudioFX {
|
|
|
640
738
|
let p_slider = this.#node("preamp");
|
|
641
739
|
let p_val = this.#node("val_preamp");
|
|
642
740
|
if (input === undefined) {
|
|
643
|
-
p_slider.value = preset.preamp
|
|
741
|
+
p_slider.value = preset.preamp;
|
|
644
742
|
} else {
|
|
645
|
-
p_slider.value = input.value
|
|
646
|
-
preset.inputs.forEach(inp => {
|
|
647
|
-
let slid = this.#node(inp.id)
|
|
648
|
-
filters.forEach(filter => {
|
|
743
|
+
p_slider.value = input.value;
|
|
744
|
+
preset.inputs.forEach((inp) => {
|
|
745
|
+
let slid = this.#node(inp.id);
|
|
746
|
+
filters.forEach((filter) => {
|
|
649
747
|
if (String(inp.id) === String(filter.frequency.value)) {
|
|
650
|
-
filter.gain.value = Number(slid.value) + Number(input.value)
|
|
748
|
+
filter.gain.value = Number(slid.value) + Number(input.value);
|
|
651
749
|
}
|
|
652
|
-
})
|
|
653
|
-
})
|
|
750
|
+
});
|
|
751
|
+
});
|
|
654
752
|
}
|
|
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(
|
|
753
|
+
p_val.innerText = String(p_slider.value);
|
|
754
|
+
p_slider.setAttribute("aria-valuetext", I18n.t("media.decibels", { value: p_slider.value }));
|
|
755
|
+
p_slider.style.setProperty("--fx-before-width", `${(50 + p_slider.value * 5).toFixed(1)}%`);
|
|
658
756
|
}
|
|
659
757
|
#renderSlider(input, filter, preamp) {
|
|
660
758
|
let slider = this.#node(input.id);
|
|
661
759
|
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(
|
|
760
|
+
slider.value = input.value;
|
|
761
|
+
filter.gain.value = Number(Number(slider.value) + preamp);
|
|
762
|
+
val.innerText = String(slider.value);
|
|
763
|
+
slider.setAttribute("aria-valuetext", I18n.t("media.decibels", { value: slider.value }));
|
|
764
|
+
slider.style.setProperty("--fx-before-width", `${(50 + slider.value * 5).toFixed(1)}%`);
|
|
667
765
|
}
|
|
668
766
|
// ya.music.Audio.fx
|
|
669
767
|
static PRESETS = [
|
|
@@ -682,7 +780,7 @@ class AudioFX {
|
|
|
682
780
|
{ id: "12000", value: 0 },
|
|
683
781
|
{ id: "14000", value: 0 },
|
|
684
782
|
{ id: "16000", value: 0 },
|
|
685
|
-
]
|
|
783
|
+
],
|
|
686
784
|
},
|
|
687
785
|
{
|
|
688
786
|
name: "Classical",
|
|
@@ -699,7 +797,7 @@ class AudioFX {
|
|
|
699
797
|
{ id: "12000", value: -3.5 },
|
|
700
798
|
{ id: "14000", value: -3.5 },
|
|
701
799
|
{ id: "16000", value: -4.5 },
|
|
702
|
-
]
|
|
800
|
+
],
|
|
703
801
|
},
|
|
704
802
|
{
|
|
705
803
|
name: "Club",
|
|
@@ -716,7 +814,7 @@ class AudioFX {
|
|
|
716
814
|
{ id: "12000", value: -0.5 },
|
|
717
815
|
{ id: "14000", value: -0.5 },
|
|
718
816
|
{ id: "16000", value: -0.5 },
|
|
719
|
-
]
|
|
817
|
+
],
|
|
720
818
|
},
|
|
721
819
|
{
|
|
722
820
|
name: "Dance",
|
|
@@ -733,7 +831,7 @@ class AudioFX {
|
|
|
733
831
|
{ id: "12000", value: -3.5 },
|
|
734
832
|
{ id: "14000", value: -0.5 },
|
|
735
833
|
{ id: "16000", value: -0.5 },
|
|
736
|
-
]
|
|
834
|
+
],
|
|
737
835
|
},
|
|
738
836
|
{
|
|
739
837
|
name: "Full Bass",
|
|
@@ -750,7 +848,7 @@ class AudioFX {
|
|
|
750
848
|
{ id: "12000", value: -5 },
|
|
751
849
|
{ id: "14000", value: -5.5 },
|
|
752
850
|
{ id: "16000", value: -5.5 },
|
|
753
|
-
]
|
|
851
|
+
],
|
|
754
852
|
},
|
|
755
853
|
{
|
|
756
854
|
name: "Full Treble",
|
|
@@ -767,7 +865,7 @@ class AudioFX {
|
|
|
767
865
|
{ id: "12000", value: 8 },
|
|
768
866
|
{ id: "14000", value: 8 },
|
|
769
867
|
{ id: "16000", value: 8 },
|
|
770
|
-
]
|
|
868
|
+
],
|
|
771
869
|
},
|
|
772
870
|
{
|
|
773
871
|
name: "Full Bass & Treble",
|
|
@@ -784,7 +882,7 @@ class AudioFX {
|
|
|
784
882
|
{ id: "12000", value: 5.5 },
|
|
785
883
|
{ id: "14000", value: 6 },
|
|
786
884
|
{ id: "16000", value: 6 },
|
|
787
|
-
]
|
|
885
|
+
],
|
|
788
886
|
},
|
|
789
887
|
{
|
|
790
888
|
name: "Laptop Speakers / Headphone",
|
|
@@ -801,7 +899,7 @@ class AudioFX {
|
|
|
801
899
|
{ id: "12000", value: 4.5 },
|
|
802
900
|
{ id: "14000", value: 6 },
|
|
803
901
|
{ id: "16000", value: 7 },
|
|
804
|
-
]
|
|
902
|
+
],
|
|
805
903
|
},
|
|
806
904
|
{
|
|
807
905
|
name: "Large Hall",
|
|
@@ -818,7 +916,7 @@ class AudioFX {
|
|
|
818
916
|
{ id: "12000", value: -2 },
|
|
819
917
|
{ id: "14000", value: -0.5 },
|
|
820
918
|
{ id: "16000", value: -0.5 },
|
|
821
|
-
]
|
|
919
|
+
],
|
|
822
920
|
},
|
|
823
921
|
{
|
|
824
922
|
name: "Live",
|
|
@@ -835,7 +933,7 @@ class AudioFX {
|
|
|
835
933
|
{ id: "12000", value: 1 },
|
|
836
934
|
{ id: "14000", value: 1 },
|
|
837
935
|
{ id: "16000", value: 1 },
|
|
838
|
-
]
|
|
936
|
+
],
|
|
839
937
|
},
|
|
840
938
|
{
|
|
841
939
|
name: "Party",
|
|
@@ -852,7 +950,7 @@ class AudioFX {
|
|
|
852
950
|
{ id: "12000", value: -0.5 },
|
|
853
951
|
{ id: "14000", value: 3.5 },
|
|
854
952
|
{ id: "16000", value: 3.5 },
|
|
855
|
-
]
|
|
953
|
+
],
|
|
856
954
|
},
|
|
857
955
|
{
|
|
858
956
|
name: "Pop",
|
|
@@ -869,7 +967,7 @@ class AudioFX {
|
|
|
869
967
|
{ id: "12000", value: -1 },
|
|
870
968
|
{ id: "14000", value: -0.5 },
|
|
871
969
|
{ id: "16000", value: -0.5 },
|
|
872
|
-
]
|
|
970
|
+
],
|
|
873
971
|
},
|
|
874
972
|
{
|
|
875
973
|
name: "Reggae",
|
|
@@ -886,7 +984,7 @@ class AudioFX {
|
|
|
886
984
|
{ id: "12000", value: -0.5 },
|
|
887
985
|
{ id: "14000", value: -0.5 },
|
|
888
986
|
{ id: "16000", value: -0.5 },
|
|
889
|
-
]
|
|
987
|
+
],
|
|
890
988
|
},
|
|
891
989
|
{
|
|
892
990
|
name: "Ska",
|
|
@@ -903,7 +1001,7 @@ class AudioFX {
|
|
|
903
1001
|
{ id: "12000", value: 4.5 },
|
|
904
1002
|
{ id: "14000", value: 5.5 },
|
|
905
1003
|
{ id: "16000", value: 4.5 },
|
|
906
|
-
]
|
|
1004
|
+
],
|
|
907
1005
|
},
|
|
908
1006
|
{
|
|
909
1007
|
name: "Soft",
|
|
@@ -920,7 +1018,7 @@ class AudioFX {
|
|
|
920
1018
|
{ id: "12000", value: 4.5 },
|
|
921
1019
|
{ id: "14000", value: 5.5 },
|
|
922
1020
|
{ id: "16000", value: 6 },
|
|
923
|
-
]
|
|
1021
|
+
],
|
|
924
1022
|
},
|
|
925
1023
|
{
|
|
926
1024
|
name: "Rock",
|
|
@@ -937,7 +1035,7 @@ class AudioFX {
|
|
|
937
1035
|
{ id: "12000", value: 5.5 },
|
|
938
1036
|
{ id: "14000", value: 5.5 },
|
|
939
1037
|
{ id: "16000", value: 5.5 },
|
|
940
|
-
]
|
|
1038
|
+
],
|
|
941
1039
|
},
|
|
942
1040
|
{
|
|
943
1041
|
name: "Soft Rock",
|
|
@@ -954,7 +1052,7 @@ class AudioFX {
|
|
|
954
1052
|
{ id: "12000", value: -0.5 },
|
|
955
1053
|
{ id: "14000", value: 1 },
|
|
956
1054
|
{ id: "16000", value: 4 },
|
|
957
|
-
]
|
|
1055
|
+
],
|
|
958
1056
|
},
|
|
959
1057
|
{
|
|
960
1058
|
name: "Techno",
|
|
@@ -971,8 +1069,9 @@ class AudioFX {
|
|
|
971
1069
|
{ id: "12000", value: 4.5 },
|
|
972
1070
|
{ id: "14000", value: 4.5 },
|
|
973
1071
|
{ id: "16000", value: 4 },
|
|
974
|
-
]
|
|
975
|
-
}
|
|
1072
|
+
],
|
|
1073
|
+
},
|
|
1074
|
+
];
|
|
976
1075
|
}
|
|
977
1076
|
|
|
978
|
-
exports.Audio = Audio
|
|
1077
|
+
exports.Audio = Audio;
|