├── icons └── music_by_pancaza.png ├── SECURITY.md ├── LICENSE ├── manifest_Firefox.json ├── manifest.json ├── js ├── options.js ├── content.js ├── service_worker.js ├── popup.js └── parser.js ├── readme.md └── html ├── options.html ├── popup.html └── bs5 ├── browser-id3-writer.6.0.0.mjs ├── bootstrap-grid.min.css └── bootstrap.bundle.min.js /icons/music_by_pancaza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vectorserver/yandex_music_fisher_mod/HEAD/icons/music_by_pancaza.png -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | NEXT Yandex Music Fisher vectorserver 2 | 3 | Copyright (c) [2024] [vectorserver] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /manifest_Firefox.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "NEXT Yandex Music Fisher vectorserver", 4 | "version": "2.1.18", 5 | "description": "Скачивание музыки с сервисов Яндекс.Музыка и Яндекс.Радио", 6 | "icons": { 7 | "128": "icons/music_by_pancaza.png" 8 | }, 9 | "action": { 10 | "default_icon": "icons/music_by_pancaza.png", 11 | "default_popup": "html/popup.html", 12 | "default_title": "NEXT Yandex Music Fisher mod by vectorserver" 13 | }, 14 | "background": { 15 | "scripts": ["js/service_worker.js"], 16 | "persistent": false 17 | }, 18 | "content_scripts": [ 19 | { 20 | "matches": [ 21 | "https://api.music.yandex.ru/*", 22 | "https://next.music.yandex.com/*", 23 | "https://next.music.yandex.kz/*", 24 | "https://next.music.yandex.by/*", 25 | "https://next.music.yandex.uz/*", 26 | "https://*.strm.yandex.net/*", 27 | "https://music.yandex.ru/*", 28 | "https://music.yandex.com/*", 29 | "https://music.yandex.kz/*", 30 | "https://music.yandex.by/*", 31 | "https://music.yandex.uz/*", 32 | "https://yastatic.net/*" 33 | ], 34 | "js": ["js/content.js"] 35 | } 36 | ], 37 | "permissions": [ 38 | "tabs", 39 | "downloads", 40 | "webRequest", 41 | "scripting", 42 | "activeTab", 43 | "storage" 44 | ], 45 | "host_permissions": [ 46 | "https://*.music.yandex.ru/*" 47 | ], 48 | "incognito": "split", 49 | "options_page": "html/options.html", 50 | "homepage_url": "https://vk.com/vectorserver" 51 | } -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "NEXT Yandex Music Fisher vectorserver", 4 | "version": "2.1.18", 5 | "description": "Скачивание музыки с сервисов Яндекс.Музыка и Яндекс.Радио", 6 | "icons": { 7 | "128": "icons/music_by_pancaza.png" 8 | }, 9 | "action": { 10 | "default_icon": "icons/music_by_pancaza.png", 11 | "default_popup": "html/popup.html", 12 | "default_title": "NEXT Yandex Music Fisher mod by vectorserver" 13 | }, 14 | "background": { 15 | "service_worker": "js/service_worker.js" 16 | }, 17 | "content_scripts": [ 18 | { 19 | "matches": [ 20 | "https://api.music.yandex.ru/*", 21 | "https://next.music.yandex.com/*", 22 | "https://next.music.yandex.ru/*", 23 | "https://next.music.yandex.kz/*", 24 | "https://next.music.yandex.by/*", 25 | "https://next.music.yandex.uz/*", 26 | "https://*.strm.yandex.net/*", 27 | "https://music.yandex.ru/*", 28 | "https://music.yandex.com/*", 29 | "https://music.yandex.kz/*", 30 | "https://music.yandex.by/*", 31 | "https://music.yandex.uz/*", 32 | "https://yastatic.net/*" 33 | ], 34 | "js": ["js/content.js"] 35 | } 36 | ], 37 | "permissions": [ 38 | "tabs", 39 | "downloads", 40 | "webRequest", 41 | "scripting", 42 | "activeTab", 43 | "storage" 44 | ], 45 | "host_permissions": [ 46 | "https://*.music.yandex.ru/*" 47 | ], 48 | "incognito": "split", 49 | "options_page": "html/options.html", 50 | "homepage_url": "https://vk.com/vectorserver", 51 | "update_url": "https://extension-updates.opera.com/api/omaha/update/" 52 | } 53 | -------------------------------------------------------------------------------- /js/options.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', async function () { 2 | const form = document.getElementById('settingsForm'); 3 | const folderInput = document.getElementById('downloadFolder'); 4 | const coverSelect = document.getElementById('coverResolution'); 5 | const audioSelect = document.getElementById('audioQuality'); 6 | const downlodadCount = document.getElementById('downlodadCount'); 7 | const savehistory = document.getElementById('savehistory'); 8 | //const checkexists = document.getElementById('checkexists'); 9 | 10 | // Восстановление сохраненных настроек 11 | const data = await chrome.storage.local.get('app_setting'); 12 | if (data.app_setting) { 13 | folderInput.value = data.app_setting.downloadFolder || 'music/%genre%'; 14 | coverSelect.value = data.app_setting.coverQuality || 600; 15 | audioSelect.value = data.app_setting.audioQuality || 'lossless-flac___aac,he-aac,mp3,flac-mp4,aac-mp4,he-aac-mp4___encraw'; 16 | downlodadCount.value = data.app_setting.downlodadCount || 4; 17 | savehistory.value = data.app_setting.savehistory || 0; 18 | //checkexists.value = data.app_setting.checkexists || 0; 19 | } 20 | 21 | const settings = { 22 | downloadFolder: folderInput.value??'music/%genre%', 23 | coverQuality: parseInt(coverSelect.value??'600'), 24 | audioQuality: audioSelect.value??'nq', 25 | downlodadCount: parseInt(downlodadCount.value??4), 26 | savehistory: savehistory.value || 0, 27 | //checkexists: checkexists.value || 0, 28 | }; 29 | await chrome.storage.local.set({ app_setting: settings }); 30 | console.log('Настройки получены!',settings); 31 | 32 | form.addEventListener('submit', async function (e) { 33 | e.preventDefault(); 34 | const settings_submit = { 35 | downloadFolder: folderInput.value??'music/%genre%', 36 | coverQuality: parseInt(coverSelect.value??'600'), 37 | audioQuality: audioSelect.value??'nq', 38 | downlodadCount: parseInt(downlodadCount.value??4), 39 | savehistory: savehistory.value || '0', 40 | //checkexists: checkexists.value || '0', 41 | }; 42 | await chrome.storage.local.set({ app_setting: settings_submit }); 43 | alert('Настройки сохранены!'); 44 | console.log('Настройки сохранены!',settings_submit); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [💰 Поддержать разработку](https://yoomoney.ru/to/410011249842897) 2 | 3 | # 🎵 Yandex Music Fisher Mod 4 | 5 | Плагин для скачивания музыки с сервиса [Yandex Music](https://music.yandex.ru/) 6 | 7 | 🧪 **NEW TEST:** https://github.com/vectorserver/yandex_music_fisher_mod/ 8 | 9 | --- 10 | 11 | ## 🧩 Установка распакованного расширения в Chrome / Opera / Edge 12 | 13 | ### 📥 Шаг 1: Скачивание репозитория 14 | 15 | 1. Перейдите по ссылке: [Скачать ZIP](https://github.com/vectorserver/yandex_music_fisher_mod/archive/refs/heads/master.zip) 16 | 2. Распакуйте архив в удобное место, например: `C:\yandex_music_fisher_mod-master\` 17 | 18 | --- 19 | 20 | ### 📁 Шаг 2: Папка с расширением 21 | 22 | Перейдите в папку: `C:\yandex_music_fisher_mod-master\` — именно она будет устанавливаться в браузер. 23 | 24 | Убедитесь, что внутри есть: 25 | - `manifest.json` 26 | - другие .js и .html файлы 27 | 28 | --- 29 | 30 | ### 🌐 Шаг 3: Открой страницу расширений 31 | 32 | - **Chrome** → `chrome://extensions/` 33 | - **Opera** → `opera://extensions/` 34 | - **Edge** → `edge://extensions/` 35 | 36 | --- 37 | 38 | ### ⚙️ Шаг 4: Включи режим разработчика 39 | 40 | - Включи переключатель **Режим разработчика** (вверху или внизу страницы). 41 | 42 | --- 43 | 44 | ### 📂 Шаг 5: Установка 45 | 46 | 1. Нажми кнопку **Загрузить распакованное расширение** 47 | 2. Выбери папку `C:\yandex_music_fisher_mod-master\` 48 | 3. Подтверди выбор папки 49 | 50 | --- 51 | 52 | ### ✅ Шаг 6: Проверка 53 | 54 | - Убедись, что расширение включено 55 | - Если есть ошибки: 56 | - Проверь путь: все файлы должны быть внутри `C:\yandex_music_fisher_mod-master\` 57 | - Убедись, что браузер поддерживает `manifest_version: 3` 58 | 59 | --- 60 | 61 | ### 🎉 Шаг 7: Готово! 62 | 63 | Расширение установлено. Иконка появится в панели браузера. 64 | 65 | --- 66 | 67 | ## 🦊 Установка в Firefox 68 | 69 | 1. Перейди: `about:debugging#/runtime/this-firefox` 70 | 2. Нажми 👉 **Загрузить временное дополнение...** 71 | 3. Укажи файл `manifest_Firefox.json` внутри папки `C:\yandex_music_fisher_mod-master\` 72 | 4. Расширение активируется до перезапуска Firefox 73 | 74 | > ⚠️ Firefox поддерживает только временную установку без подписи. После перезапуска расширение будет отключено. 75 | 76 | --- 77 | 78 | ## 📝 Важные заметки: 79 | 80 | - Edge и Opera устанавливаются аналогично, т.к. работают на Chromium 81 | - Для постоянной работы расширения в Firefox — потребуется подпись или Developer Edition 82 | - Хотите автозагрузку и обновления? Используйте подписанную версию через Chrome Web Store 83 | 84 | --- 85 | 86 | 💡 **Наслаждайтесь прослушиванием и скачиванием треков с Yandex Music с помощью Fisher Mod!** 🎧 87 | [💰 Поддержать разработку](https://yoomoney.ru/to/410011249842897) -------------------------------------------------------------------------------- /js/content.js: -------------------------------------------------------------------------------- 1 | // Выводим в консоль информацию о запуске content.js 2 | console.log('content.js loaded'); 3 | 4 | // Отправляем сообщение в background.js с командой "inject_parser" 5 | chrome.runtime.sendMessage({ action: "inject_parser" }, (data) => { 6 | if (chrome.runtime.lastError) { 7 | //console.error("Ошибка при отправке inject_parser:", chrome.runtime.lastError.message); 8 | } else { 9 | //console.log('inject_parser выполнен успешно:', data); 10 | } 11 | }); 12 | 13 | 14 | // Загружаем настройки из chrome.storage.local и сохраняем в localStorage 15 | chrome.storage.local.get('app_setting', (result) => { 16 | if (result.app_setting) { 17 | // Копируем каждую настройку в localStorage 18 | for (const [key, value] of Object.entries(result.app_setting)) { 19 | console.log('appYa_setting_'+key, value) 20 | localStorage.setItem('appYa_setting_'+key, value); 21 | } 22 | 23 | } 24 | }); 25 | 26 | 27 | 28 | 29 | // Сохраняем начальное состояние localStorage для отслеживания изменений 30 | let previousState = JSON.stringify(localStorage); 31 | // Функция для проверки изменений в localStorage 32 | const checkLocalStorageUpdates = () => { 33 | try { 34 | // Получаем текущее состояние localStorage 35 | let currentState = JSON.stringify(localStorage); 36 | 37 | // Сравниваем текущее и предыдущее состояние 38 | if (currentState !== previousState) { 39 | 40 | // Обновляем предыдущее состояние 41 | previousState = currentState; 42 | 43 | 44 | // Отправляем обновленные данные в background.js 45 | chrome.runtime.sendMessage({ 46 | action: "send_localStorage", 47 | data: { ...window.localStorage } // Передаем копию localStorage 48 | }, (response) => { 49 | if (chrome.runtime.lastError) { 50 | //console.error("Ошибка отправки данных в background.js:", chrome.runtime.lastError.message); 51 | } else { 52 | //console.log("Ответ от background.js на send_localStorage:", response); 53 | } 54 | }); 55 | } 56 | } catch (error) { 57 | //console.error("Ошибка при проверке localStorage:", error); 58 | } 59 | }; 60 | 61 | // Устанавливаем интервал проверки localStorage каждые 1000 мс (1 секунда) 62 | setInterval(checkLocalStorageUpdates, 1000); 63 | 64 | 65 | // Для SHIFT + D и SHIFT + В 66 | document.addEventListener('keydown', function(event) { 67 | if (event.shiftKey && (event.key === 'D' || event.key === 'В')) { 68 | event.preventDefault(); 69 | 70 | // Открываем popup.html 71 | chrome.runtime.sendMessage({ 72 | action: "download_SFIFTD", 73 | data: { ...window.localStorage } 74 | }); 75 | } 76 | }); 77 | 78 | // Для двойного клика мыши 79 | document.addEventListener('dblclick', function(event) { 80 | // Открываем popup.html 81 | chrome.runtime.sendMessage({ 82 | action: "download_SFIFTD", 83 | data: { ...window.localStorage } 84 | }); 85 | }); -------------------------------------------------------------------------------- /html/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NEXT Yandex Music Fisher 6 | 7 | 8 | 9 | 10 |
11 |

Настройки приложения

12 |
13 |

14 | Благодарности сюда: 15 | 💰yoomoney 16 |

17 |
18 | 19 | 20 | 27 |
28 | 29 | 30 | 31 | 36 |
37 | 38 | 39 | 40 | 46 | 47 | Единовременная закачака за один проход 48 | 49 |
50 | 51 |
52 | 54 | 55 | Переменные:
56 | Например: music/%dir%/%genre%/%year%/%artist%/%album% 57 | %dir% - Директория откуда качать 58 | %genre% - Жанр 59 | %year% - Год 60 | %artist% - Артист 61 | %album% - Альбом 62 |
Если указать music без переменных, закачка идет стандартно согласно текущему плейлисту, артисту 63 |
64 |
65 | 66 | 67 |
68 | 72 |
73 | 80 | 81 | 82 |
83 |
84 |
85 |
 86 | 
 87 | #2.1.18
 88 |     - checkexists deleted
 89 |     - artist meta exists
 90 | 
 91 | #2.1.17
 92 |     - Сохранять историю в менеджере загрузок
 93 |     - Единовременная закачака за один проход
 94 | 
 95 | #2.1.11
 96 |     - Обработчик сообщения для загрузки через SHIFT+D или двойной клик
 97 | 
 98 | #2.1
 99 |     - добавлены переменные для загрузки %genre% - Жанр %year% - Год %artist% - Артист %album% - Альбом и %dir%
100 | 
101 | #2.0
102 |     - обновленный алгоритм закачки
103 |     - добавлены метаданные (в планах текст песни)
104 |     - добавлен manifest_Firefox.json
105 | 
106 | #1.0.4.7
107 |     - Исправлена ошибка закачки файлов по 30 сек
108 |     - новый алгоритм закачки
109 | #1.0.3
110 |     - Мелкие баги с именами файлов
111 |     - добавлена одновременная загрузка 4 файлов за раз
112 |     - включена настройки (обложка, качество, папка)
113 | 
114 | 
115 | #1.0.2
116 |     - Исправлено имя файла {artist} - {title}
117 |     - добавлена одновременная загрузка 4 файлов за раз
118 |     - включена история файлов
119 | 
120 | #1.0.1.18022025
121 |     - добавлено псевдо lossless (320 кб/сек).
122 |     - добавлен чарт https://music.yandex.ru/chart.
123 | 
124 | 125 |
126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /html/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | NEXT Yandex Music Fisher mod by vectorserver 8 | 9 | 10 | 21 | 22 | 23 |
24 | 35 | 96 |
97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /js/service_worker.js: -------------------------------------------------------------------------------- 1 | // Выводим в консоль информацию о запуске service_worker.js 2 | console.log('service_worker.js loaded'); 3 | 4 | 5 | const appService = { 6 | saveToStorage(key, value) { 7 | return chrome.storage.local.set({[key]: value}); 8 | }, 9 | getFromStorage(key) { 10 | return chrome.storage.local.get(key); 11 | } 12 | }; 13 | 14 | const badgeManager = { 15 | colors: [ 16 | '#FF5733', // Красный 17 | '#33FF57', // Зеленый 18 | '#3357FF', // Синий 19 | '#FF33A1', // Розовый 20 | '#FFBD33', // Оранжевый 21 | '#33FFF4', // Голубой 22 | '#B333FF', // Фиолетовый 23 | '#33FFA1', // Бирюзовый 24 | '#FFA133', // Желтый 25 | '#A133FF', // Лиловый 26 | '#33A1FF', // Лазурный 27 | '#FFA1A1' // Розовый 28 | ], 29 | usedColors: [], // Добавляем массив использованных цветов 30 | resetColors() { 31 | this.usedColors = []; 32 | }, 33 | getRandomColor() { 34 | if (this.usedColors.length === this.colors.length) { 35 | this.resetColors(); 36 | } 37 | const availableColors = this.colors.filter(color => !this.usedColors.includes(color)); 38 | const randomColor = availableColors[Math.floor(Math.random() * availableColors.length)]; 39 | this.usedColors.push(randomColor); 40 | return randomColor; 41 | }, 42 | updateBadge(count, bg) { 43 | chrome.action.setBadgeText({text: count > 0 ? count.toString() : ""}); 44 | chrome.action.setBadgeBackgroundColor({color: bg}); 45 | } 46 | }; 47 | 48 | const downloadManager = { 49 | async downloadTracks(message, globalCount) { 50 | let {tabId, playlistName, trackIds} = message; 51 | 52 | // Ждём результат get 53 | const settings = await chrome.storage.local.get('app_setting'); 54 | 55 | // Получаем путь из результата 56 | const downloadFolder = settings?.app_setting?.downloadFolder || 'music'; 57 | playlistName = downloadFolder + '/' + playlistName; 58 | 59 | // Проверяем, содержит ли downloadFolder переменные 60 | const hasVariables = /%[^%]+%/.test(downloadFolder); 61 | 62 | 63 | let batchSize = settings?.app_setting?.downlodadCount ?? 4; 64 | globalCount += trackIds.length; 65 | let bg = badgeManager.getRandomColor(); 66 | badgeManager.updateBadge(globalCount, bg); 67 | 68 | for (let i = 0; i < trackIds.length; i += batchSize) { 69 | const batch = trackIds.slice(i, i + batchSize); 70 | 71 | await Promise.all(batch.map(trackId => 72 | new Promise((resolve, reject) => { 73 | const dataToInject = `appYa.fetchFileInfoOne(${trackId})`; 74 | 75 | chrome.scripting.executeScript({ 76 | target: {tabId: tabId}, 77 | func: (injectedData) => eval(injectedData), 78 | args: [dataToInject], 79 | world: "MAIN", 80 | }, (results) => { 81 | if (chrome.runtime.lastError) { 82 | console.error("Ошибка при выполнении скрипта:", chrome.runtime.lastError); 83 | reject(chrome.runtime.lastError.message); 84 | return; 85 | } 86 | 87 | setTimeout(async () => { 88 | if (results && results.length > 0) { 89 | let inputData = await JSON.parse(results[0].result); 90 | globalCount -= 1; 91 | badgeManager.updateBadge(globalCount, bg); 92 | 93 | if (inputData !== null && inputData.download) { 94 | 95 | // Если есть переменные в downloadFolder, обрабатываем их 96 | if (hasVariables) { 97 | let artists = inputData.trackinfo.artists.map((item) => item.name).join(', '); 98 | 99 | // Если есть переменные в downloadFolder, обрабатываем их 100 | const defaultTrackInfo = { 101 | dir: playlistName, 102 | genre: inputData.trackinfo.albums[0].genre || 'Unknown', 103 | year: inputData.trackinfo.albums[0].year || new Date().getFullYear(), 104 | artist: artists || 'Unknown Artist', 105 | album: inputData.trackinfo.albums[0].title || 'Unknown Album' 106 | }; 107 | 108 | // Словарь переменных 109 | const variables = { 110 | '%dir%': playlistName, 111 | '%genre%': defaultTrackInfo.genre, 112 | '%year%': defaultTrackInfo.year.toString(), 113 | '%artist%': defaultTrackInfo.artist, 114 | '%album%': defaultTrackInfo.album 115 | }; 116 | 117 | // Заменяем переменные в downloadFolder 118 | let processedFolder = downloadFolder; 119 | 120 | for (const [variable, value] of Object.entries(variables)) { 121 | if (downloadFolder.includes(variable)) { 122 | // Очищаем значение от недопустимых символов 123 | const cleanValue = value.toString().replace(/[<>:"/\\|?*]/g, '_'); 124 | processedFolder = processedFolder.replace(new RegExp(variable.replace(/%/g, '\\%'), 'gi'), cleanValue); 125 | } 126 | } 127 | 128 | playlistName = processedFolder; 129 | } 130 | 131 | 132 | downloadManager.downloadFile(inputData, playlistName, settings); 133 | } 134 | resolve(trackId); 135 | } else { 136 | reject("No result returned"); 137 | } 138 | }, 250); 139 | }); 140 | }) 141 | )); 142 | } 143 | }, 144 | 145 | downloadFile(inputData, playlistName, settings) { 146 | 147 | console.log('downloadFile settings', settings?.app_setting) 148 | const escapeFileName = (fileName) => fileName.replace(/[\\/:*?"<>|]/g, '_'); 149 | let artists = inputData.trackinfo.artists.map((item) => item.name).join(', '); 150 | let title = inputData.trackinfo.title; 151 | let fileName = `${playlistName}/${escapeFileName(artists)} - ${escapeFileName(title)}.mp3`; 152 | 153 | 154 | //Тут нужно еще проверка о том что файл существует settings?.app_setting.checkexists если есть то не качаем 155 | chrome.downloads.download({ 156 | url: inputData.download, 157 | filename: fileName, 158 | saveAs: false, 159 | conflictAction: 'overwrite' 160 | }, (downloadId) => { 161 | if (chrome.runtime.lastError) { 162 | console.error("Ошибка загрузки:", chrome.runtime.lastError.message); 163 | return; 164 | } 165 | 166 | 167 | // Отслеживаем завершение загрузки 168 | if (settings?.app_setting.savehistory === "0" || settings?.app_setting.savehistory === 0) { 169 | chrome.downloads.onChanged.addListener(function listener(delta) { 170 | if (delta.id === downloadId && delta.state && delta.state.current === 'complete') { 171 | // Удаляем запись из истории загрузок 172 | chrome.downloads.erase({id: downloadId}, () => { 173 | if (chrome.runtime.lastError) { 174 | console.warn("Не удалось удалить запись о загрузке:", chrome.runtime.lastError.message); 175 | } else { 176 | console.log(`Загрузка ${downloadId} удалена из истории`); 177 | } 178 | }); 179 | // Отписываемся от слушателя, чтобы не ловить другие загрузки 180 | chrome.downloads.onChanged.removeListener(listener); 181 | } 182 | }); 183 | } 184 | }); 185 | } 186 | }; 187 | 188 | 189 | const worker = { 190 | globalCount: 0, 191 | onMessage(message, sender, sendResponse) { 192 | if (message.action === "inject_parser") { 193 | sendResponse({success: true, message: 'parser.js OK', tabId: sender.tab.id}); 194 | chrome.scripting.executeScript({ 195 | target: {tabId: sender.tab.id}, 196 | files: ["html/bs5/browser-id3-writer.6.0.0.mjs", "js/parser.js"], 197 | world: "MAIN" 198 | }).then(() => { 199 | chrome.scripting.executeScript({ 200 | target: {tabId: sender.tab.id}, 201 | func: () => { 202 | if (typeof appYa !== 'undefined' && typeof appYa.init === 'function') { 203 | appYa.init(); 204 | } 205 | }, 206 | world: "MAIN" 207 | }).then(() => { 208 | appService.saveToStorage('appYa_tabID', sender.tab.id); 209 | }).catch(console.error); 210 | }).catch(console.error); 211 | 212 | return true; 213 | } 214 | 215 | if (message.action === "send_localStorage") { 216 | sendResponse({success: true, data: message.data}); 217 | if (message.data && !message.data.appYa_token) { 218 | appService.saveToStorage('appYa_db', {appYa_token: false}); 219 | } 220 | appService.saveToStorage('appYa_db', message.data).then(() => { 221 | 222 | //console.log("Данные localStorage успешно сохранены:", { appYa_db: message.data }); 223 | }).catch(console.error); 224 | return true; 225 | } 226 | 227 | if (message.action === "download_Tracks") { 228 | console.log('download_Tracks', message); 229 | sendResponse({download_Tracks: message}); 230 | downloadManager.downloadTracks(message, this.globalCount); 231 | return true; 232 | } 233 | 234 | // Обработчик сообщения для загрузки через SHIFT+D или двойной клик 235 | if (message.action === "download_SFIFTD") { 236 | // Парсим информацию о файле из localStorage данных сообщения 237 | const trDinfo = JSON.parse(message.data["appYa_get-file-info"]); 238 | 239 | // Проверяем, есть ли ID трека для загрузки 240 | if (trDinfo.downloadInfo.trackId) { 241 | // Формируем данные для загрузки: 242 | // - tabId: ID вкладки отправителя (для контекста) 243 | // - trackIds: массив ID треков для загрузки (в данном случае один трек) 244 | // - playlistName: имя плейлиста (по умолчанию 'music') 245 | let downData = { 246 | tabId: sender.tab.id, // ID вкладки, откуда пришел запрос 247 | trackIds: [trDinfo.downloadInfo.trackId], // Массив ID треков для загрузки 248 | playlistName: 'music' // Имя плейлиста для организации загрузок 249 | } 250 | 251 | // Логируем данные для отладки 252 | console.log('download_SFIFT+D', downData) 253 | 254 | // Вызываем менеджер загрузок для скачивания трека(ов) 255 | // this.globalCount - глобальный счетчик загрузок (для нумерации/отслеживания) 256 | downloadManager.downloadTracks(downData, this.globalCount); 257 | } 258 | return true; // Подтверждаем получение сообщения 259 | } 260 | 261 | 262 | } 263 | }; 264 | 265 | // Слушатель сообщений 266 | chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { 267 | return worker.onMessage(message, sender, sendResponse); 268 | }); 269 | -------------------------------------------------------------------------------- /html/bs5/browser-id3-writer.6.0.0.mjs: -------------------------------------------------------------------------------- 1 | function e(e) { 2 | return String(e).split("").map((e => e.charCodeAt(0))) 3 | } 4 | 5 | function t(t) { 6 | return new Uint8Array(e(t)) 7 | } 8 | 9 | function a(t) { 10 | const a = new ArrayBuffer(2 * t.length), r = new Uint8Array(a); 11 | return new Uint16Array(a).set(e(t)), r 12 | } 13 | 14 | function r(e) { 15 | const t = 255; 16 | return [e >>> 24 & t, e >>> 16 & t, e >>> 8 & t, e & t] 17 | } 18 | 19 | function n(e) { 20 | return 11 + e 21 | } 22 | 23 | function s(e, t, a, r) { 24 | return 11 + t + 1 + 1 + (r ? 2 + 2 * (a + 1) : a + 1) + e 25 | } 26 | 27 | function i(e) { 28 | let t = 0; 29 | return e.forEach((e => { 30 | t += 2 + 2 * e[0].length + 2 + 2 + 2 * e[1].length + 2 31 | })), 11 + t 32 | } 33 | 34 | function c(e, t) { 35 | const a = 2 * t; 36 | let r = 0; 37 | return e.forEach((e => { 38 | r += 2 + 2 * e[0].length + 2 + 4 39 | })), 18 + a + 2 + r 40 | } 41 | 42 | class ID3Writer { 43 | _setIntegerFrame(e, t) { 44 | const a = parseInt(t, 10); 45 | this.frames.push({name: e, value: a, size: n(a.toString().length)}) 46 | } 47 | 48 | _setStringFrame(e, t) { 49 | const a = t.toString(); 50 | let r = 13 + 2 * a.length; 51 | "TDAT" === e && (r = n(a.length)), this.frames.push({name: e, value: a, size: r}) 52 | } 53 | 54 | _setPictureFrame(e, t, a, r) { 55 | const n = function (e) { 56 | if (!e || !e.length) return null; 57 | if (255 === e[0] && 216 === e[1] && 255 === e[2]) return "image/jpeg"; 58 | if (137 === e[0] && 80 === e[1] && 78 === e[2] && 71 === e[3]) return "image/png"; 59 | if (71 === e[0] && 73 === e[1] && 70 === e[2]) return "image/gif"; 60 | if (87 === e[8] && 69 === e[9] && 66 === e[10] && 80 === e[11]) return "image/webp"; 61 | const t = 73 === e[0] && 73 === e[1] && 42 === e[2] && 0 === e[3], 62 | a = 77 === e[0] && 77 === e[1] && 0 === e[2] && 42 === e[3]; 63 | return t || a ? "image/tiff" : 66 === e[0] && 77 === e[1] ? "image/bmp" : 0 === e[0] && 0 === e[1] && 1 === e[2] && 0 === e[3] ? "image/x-icon" : null 64 | }(new Uint8Array(t)), i = a.toString(); 65 | if (!n) throw new Error("Unknown picture MIME type"); 66 | a || (r = !1), this.frames.push({ 67 | name: "APIC", 68 | value: t, 69 | pictureType: e, 70 | mimeType: n, 71 | useUnicodeEncoding: r, 72 | description: i, 73 | size: s(t.byteLength, n.length, i.length, r) 74 | }) 75 | } 76 | 77 | _setLyricsFrame(e, t, a) { 78 | const r = e.split("").map((e => e.charCodeAt(0))), n = t.toString(), s = a.toString(); 79 | var i, c; 80 | this.frames.push({ 81 | name: "USLT", 82 | value: s, 83 | language: r, 84 | description: n, 85 | size: (i = n.length, c = s.length, 16 + 2 * i + 2 + 2 + 2 * c) 86 | }) 87 | } 88 | 89 | _setCommentFrame(e, t, a) { 90 | const r = e.split("").map((e => e.charCodeAt(0))), n = t.toString(), s = a.toString(); 91 | var i, c; 92 | this.frames.push({ 93 | name: "COMM", 94 | value: s, 95 | language: r, 96 | description: n, 97 | size: (i = n.length, c = s.length, 16 + 2 * i + 2 + 2 + 2 * c) 98 | }) 99 | } 100 | 101 | _setPrivateFrame(e, t) { 102 | const a = e.toString(); 103 | var r, n; 104 | this.frames.push({name: "PRIV", value: t, id: a, size: (r = a.length, n = t.byteLength, 10 + r + 1 + n)}) 105 | } 106 | 107 | _setUserStringFrame(e, t) { 108 | const a = e.toString(), r = t.toString(); 109 | var n, s; 110 | this.frames.push({ 111 | name: "TXXX", 112 | description: a, 113 | value: r, 114 | size: (n = a.length, s = r.length, 13 + 2 * n + 2 + 2 + 2 * s) 115 | }) 116 | } 117 | 118 | _setUrlLinkFrame(e, t) { 119 | const a = t.toString(); 120 | var r; 121 | this.frames.push({name: e, value: a, size: (r = a.length, 10 + r)}) 122 | } 123 | 124 | _setPairedTextFrame(e, t) { 125 | this.frames.push({name: e, value: t, size: i(t)}) 126 | } 127 | 128 | _setSynchronisedLyricsFrame(e, t, a, r, n) { 129 | const s = n.toString(), i = r.split("").map((e => e.charCodeAt(0))); 130 | this.frames.push({ 131 | name: "SYLT", 132 | value: t, 133 | language: i, 134 | description: s, 135 | type: e, 136 | timestampFormat: a, 137 | size: c(t, s.length) 138 | }) 139 | } 140 | 141 | constructor(e) { 142 | if (!e || "object" != typeof e || !("byteLength" in e)) throw new Error("First argument should be an instance of ArrayBuffer or Buffer"); 143 | this.arrayBuffer = e, this.padding = 4096, this.frames = [], this.url = "" 144 | } 145 | 146 | setFrame(e, t) { 147 | switch (e) { 148 | case"TPE1": 149 | case"TCOM": 150 | case"TCON": { 151 | if (!Array.isArray(t)) throw new Error(`${e} frame value should be an array of strings`); 152 | const a = "TCON" === e ? ";" : "/", r = t.join(a); 153 | this._setStringFrame(e, r); 154 | break 155 | } 156 | case"TLAN": 157 | case"TIT1": 158 | case"TIT2": 159 | case"TIT3": 160 | case"TALB": 161 | case"TPE2": 162 | case"TPE3": 163 | case"TPE4": 164 | case"TRCK": 165 | case"TPOS": 166 | case"TMED": 167 | case"TPUB": 168 | case"TCOP": 169 | case"TKEY": 170 | case"TEXT": 171 | case"TDAT": 172 | case"TSRC": 173 | this._setStringFrame(e, t); 174 | break; 175 | case"TBPM": 176 | case"TLEN": 177 | case"TYER": 178 | this._setIntegerFrame(e, t); 179 | break; 180 | case"USLT": 181 | if (t.language = t.language || "eng", "object" != typeof t || !("description" in t) || !("lyrics" in t)) throw new Error("USLT frame value should be an object with keys description and lyrics"); 182 | if (t.language && !t.language.match(/[a-z]{3}/i)) throw new Error("Language must be coded following the ISO 639-2 standards"); 183 | this._setLyricsFrame(t.language, t.description, t.lyrics); 184 | break; 185 | case"APIC": 186 | if ("object" != typeof t || !("type" in t) || !("data" in t) || !("description" in t)) throw new Error("APIC frame value should be an object with keys type, data and description"); 187 | if (t.type < 0 || t.type > 20) throw new Error("Incorrect APIC frame picture type"); 188 | this._setPictureFrame(t.type, t.data, t.description, !!t.useUnicodeEncoding); 189 | break; 190 | case"TXXX": 191 | if ("object" != typeof t || !("description" in t) || !("value" in t)) throw new Error("TXXX frame value should be an object with keys description and value"); 192 | this._setUserStringFrame(t.description, t.value); 193 | break; 194 | case"WCOM": 195 | case"WCOP": 196 | case"WOAF": 197 | case"WOAR": 198 | case"WOAS": 199 | case"WORS": 200 | case"WPAY": 201 | case"WPUB": 202 | this._setUrlLinkFrame(e, t); 203 | break; 204 | case"COMM": 205 | if (t.language = t.language || "eng", "object" != typeof t || !("description" in t) || !("text" in t)) throw new Error("COMM frame value should be an object with keys description and text"); 206 | if (t.language && !t.language.match(/[a-z]{3}/i)) throw new Error("Language must be coded following the ISO 639-2 standards"); 207 | this._setCommentFrame(t.language, t.description, t.text); 208 | break; 209 | case"PRIV": 210 | if ("object" != typeof t || !("id" in t) || !("data" in t)) throw new Error("PRIV frame value should be an object with keys id and data"); 211 | this._setPrivateFrame(t.id, t.data); 212 | break; 213 | case"IPLS": 214 | if (!Array.isArray(t) || !Array.isArray(t[0])) throw new Error("IPLS frame value should be an array of pairs"); 215 | this._setPairedTextFrame(e, t); 216 | break; 217 | case"SYLT": 218 | if ("object" != typeof t || !("type" in t) || !("text" in t) || !("timestampFormat" in t)) throw new Error("SYLT frame value should be an object with keys type, text and timestampFormat"); 219 | if (!Array.isArray(t.text) || !Array.isArray(t.text[0])) throw new Error("SYLT frame text value should be an array of pairs"); 220 | if (t.type < 0 || t.type > 6) throw new Error("Incorrect SYLT frame content type"); 221 | if (t.timestampFormat < 1 || t.timestampFormat > 2) throw new Error("Incorrect SYLT frame time stamp format"); 222 | t.language = t.language || "eng", t.description = t.description || "", this._setSynchronisedLyricsFrame(t.type, t.text, t.timestampFormat, t.language, t.description); 223 | break; 224 | default: 225 | throw new Error(`Unsupported frame ${e}`) 226 | } 227 | return this 228 | } 229 | 230 | removeTag() { 231 | if (this.arrayBuffer.byteLength < 10) return; 232 | const e = new Uint8Array(this.arrayBuffer), t = e[3], 233 | a = ((r = [e[6], e[7], e[8], e[9]])[0] << 21) + (r[1] << 14) + (r[2] << 7) + r[3] + 10; 234 | var r, n; 235 | 73 !== (n = e)[0] || 68 !== n[1] || 51 !== n[2] || t < 2 || t > 4 || (this.arrayBuffer = new Uint8Array(e.subarray(a)).buffer) 236 | } 237 | 238 | addTag() { 239 | this.removeTag(); 240 | const e = [255, 254], n = 10 + this.frames.reduce(((e, t) => e + t.size), 0) + this.padding, 241 | s = new ArrayBuffer(this.arrayBuffer.byteLength + n), i = new Uint8Array(s); 242 | let c = 0, o = []; 243 | return o = [73, 68, 51, 3], i.set(o, c), c += o.length, c++, c++, o = function (e) { 244 | const t = 127; 245 | return [e >>> 21 & t, e >>> 14 & t, e >>> 7 & t, e & t] 246 | }(n - 10), i.set(o, c), c += o.length, this.frames.forEach((n => { 247 | switch (o = t(n.name), i.set(o, c), c += o.length, o = r(n.size - 10), i.set(o, c), c += o.length, c += 2, n.name) { 248 | case"WCOM": 249 | case"WCOP": 250 | case"WOAF": 251 | case"WOAR": 252 | case"WOAS": 253 | case"WORS": 254 | case"WPAY": 255 | case"WPUB": 256 | o = t(n.value), i.set(o, c), c += o.length; 257 | break; 258 | case"TPE1": 259 | case"TCOM": 260 | case"TCON": 261 | case"TLAN": 262 | case"TIT1": 263 | case"TIT2": 264 | case"TIT3": 265 | case"TALB": 266 | case"TPE2": 267 | case"TPE3": 268 | case"TPE4": 269 | case"TRCK": 270 | case"TPOS": 271 | case"TKEY": 272 | case"TMED": 273 | case"TPUB": 274 | case"TCOP": 275 | case"TEXT": 276 | case"TSRC": 277 | o = [1].concat(e), i.set(o, c), c += o.length, o = a(n.value), i.set(o, c), c += o.length; 278 | break; 279 | case"TXXX": 280 | case"USLT": 281 | case"COMM": 282 | o = [1], "USLT" !== n.name && "COMM" !== n.name || (o = o.concat(n.language)), o = o.concat(e), i.set(o, c), c += o.length, o = a(n.description), i.set(o, c), c += o.length, o = [0, 0].concat(e), i.set(o, c), c += o.length, o = a(n.value), i.set(o, c), c += o.length; 283 | break; 284 | case"TBPM": 285 | case"TLEN": 286 | case"TDAT": 287 | case"TYER": 288 | c++, o = t(n.value), i.set(o, c), c += o.length; 289 | break; 290 | case"PRIV": 291 | o = t(n.id), i.set(o, c), c += o.length, c++, i.set(new Uint8Array(n.value), c), c += n.value.byteLength; 292 | break; 293 | case"APIC": 294 | o = [n.useUnicodeEncoding ? 1 : 0], i.set(o, c), c += o.length, o = t(n.mimeType), i.set(o, c), c += o.length, o = [0, n.pictureType], i.set(o, c), c += o.length, n.useUnicodeEncoding ? (o = [].concat(e), i.set(o, c), c += o.length, o = a(n.description), i.set(o, c), c += o.length, c += 2) : (o = t(n.description), i.set(o, c), c += o.length, c++), i.set(new Uint8Array(n.value), c), c += n.value.byteLength; 295 | break; 296 | case"IPLS": 297 | o = [1], i.set(o, c), c += o.length, n.value.forEach((t => { 298 | o = [].concat(e), i.set(o, c), c += o.length, o = a(t[0].toString()), i.set(o, c), c += o.length, o = [0, 0].concat(e), i.set(o, c), c += o.length, o = a(t[1].toString()), i.set(o, c), c += o.length, o = [0, 0], i.set(o, c), c += o.length 299 | })); 300 | break; 301 | case"SYLT": 302 | o = [1].concat(n.language).concat(n.timestampFormat).concat(n.type), i.set(o, c), c += o.length, o = [].concat(e), i.set(o, c), c += o.length, o = a(n.description), i.set(o, c), c += o.length, c += 2, n.value.forEach((t => { 303 | o = [].concat(e), i.set(o, c), c += o.length, o = a(t[0].toString()), i.set(o, c), c += o.length, o = [0, 0], i.set(o, c), c += o.length, o = r(t[1]), i.set(o, c), c += o.length 304 | })) 305 | } 306 | })), c += this.padding, i.set(new Uint8Array(this.arrayBuffer), c), this.arrayBuffer = s, s 307 | } 308 | 309 | getBlob() { 310 | return new Blob([this.arrayBuffer], {type: "audio/mpeg"}) 311 | } 312 | 313 | getURL() { 314 | return this.url || (this.url = URL.createObjectURL(this.getBlob())), this.url 315 | } 316 | 317 | revokeURL() { 318 | URL.revokeObjectURL(this.url) 319 | } 320 | } -------------------------------------------------------------------------------- /js/popup.js: -------------------------------------------------------------------------------- 1 | console.log('js/popup.js'); 2 | 3 | 4 | const section = document.querySelector('section'); 5 | const col_one = document.getElementById('col1'); 6 | const col_two = document.getElementById('col2'); 7 | const workPanel = document.getElementById('work_panel'); 8 | const playlistPanel = document.getElementById('playlistPanel'); 9 | const playlistPanelTitle = document.getElementById('playlistPanelTitle'); 10 | const playlistPanelImage = document.getElementById('playlistPanelImage'); 11 | const playlistPanelMeta = document.getElementById('playlistPanelMeta'); 12 | const playlistPanelMetaotherData = document.getElementById('playlistPanelMetaotherData'); 13 | const playlistPanelMetaDownloadBtn = document.getElementById('playlistPanelMetaDownloadBtn'); 14 | const tokenEnd = document.getElementById('tokenEnd'); 15 | const tokenEndUrl = document.getElementById('tokenEndUrl'); 16 | const escapeFileName = (fileName) => fileName.replace(/[\\/:*?"<>|]/g, '_'); 17 | 18 | 19 | document.getElementById('open-settings').addEventListener('click', function () { 20 | if (chrome.runtime.openOptionsPage) { 21 | chrome.runtime.openOptionsPage(); 22 | } else { 23 | window.open(chrome.runtime.getURL('html/options.html')); 24 | } 25 | }); 26 | 27 | 28 | // Сервис для работы с chrome.storage.local 29 | const storageService = { 30 | getStorageData(callback) { 31 | chrome.storage.local.get((result) => callback(result)); 32 | }, 33 | saveStorageData(key, value) { 34 | chrome.storage.local.set({[key]: value}); 35 | }, 36 | monitorStorageChanges(callback) { 37 | chrome.storage.onChanged.addListener((changes, areaName) => { 38 | 39 | callback(changes); 40 | 41 | }); 42 | } 43 | }; 44 | 45 | // Обновление пользовательского интерфейса 46 | const uiUpdater = { 47 | updateUI(data, appYa_tabID, app) { 48 | const parsedData = parser.parseStorage(data); 49 | const cQ = app?.app_setting?.coverQuality ?? 300; 50 | var cQR = `${cQ}x${cQ}`; 51 | console.log('coverQuality', cQR) 52 | 53 | const authorizationPanel = document.getElementById('authorization'); 54 | const authorizationBtn = authorizationPanel.querySelector('#authorize'); 55 | 56 | 57 | if (parsedData.appYa_token) { 58 | this.updateTrackInfo(parsedData, appYa_tabID, cQR); 59 | this.updatePlaylistInfo(parsedData, appYa_tabID, cQR); 60 | let checktokenData = uiUpdater.getTokenExpirationDate(parsedData.appYa_token); 61 | tokenEnd.innerText = checktokenData; 62 | tokenEndUrl.addEventListener('click', function () { 63 | //localStorage.clear() 64 | const dataToInject = `localStorage.clear();window.location.reload();`; 65 | chrome.scripting.executeScript({ 66 | target: {tabId: appYa_tabID}, 67 | func: (injectedData) => eval(injectedData), 68 | args: [dataToInject], 69 | world: "MAIN", 70 | }, (results) => { 71 | window.close(); 72 | }); 73 | }); 74 | //console.log('parsedData',parsedData) 75 | //console.log('appYa_page',parsedData.appYa_page) 76 | } else { 77 | authorizationPanel.style.display = 'flex'; 78 | authorizationBtn.setAttribute('href', parsedData.appYa_authorizationUrl); 79 | 80 | setTimeout(() => { 81 | authorizationBtn.addEventListener('click', () => { 82 | chrome.tabs.remove(appYa_tabID, (error) => { 83 | if (chrome.runtime.lastError) { 84 | console.error(chrome.runtime.lastError); 85 | } else { 86 | console.log('Вкладка закрыта.'); 87 | } 88 | }); 89 | }); 90 | }, 1500); 91 | } 92 | }, 93 | 94 | updateTrackInfo(parsedData, appYa_tabID, cQR) { 95 | if (!parsedData.appYa_cureitTrack) { 96 | document.querySelector('body .container-fluid').innerHTML = 97 | 'Включите трек, Яндекс Музыки, потом вернитесь сюда)'; 98 | return; 99 | } 100 | 101 | workPanel.style.display = 'flex'; 102 | const track = parsedData.appYa_cureitTrack.trackinfo; 103 | const imageURL = `https://${track.coverUri.replace(/%%/g, cQR)}`; 104 | const artists = track.artists.map((item) => item.name).join(', '); 105 | const albums = track.albums.map((item) => item.year).join(', '); 106 | 107 | document.getElementById('trackName').innerText = track.title; 108 | document.getElementById('artistsList').innerHTML = `${artists}
${albums}`; 109 | document.getElementById('trackImage').src = imageURL; 110 | 111 | //section.style.backgroundColor = track.derivedColors.average; 112 | //section.style.color = track.derivedColors.waveText; 113 | //playlistPanelMetaDownloadBtn.style.backgroundColor = track.derivedColors.miniPlayer; 114 | document.querySelector('#trackPanel').style.backgroundColor = track.derivedColors.accent; 115 | 116 | document.getElementById('downloadButton').addEventListener('click', () => { 117 | eventHandlers.downloadTracks(appYa_tabID, [track.id], 'music'); 118 | }); 119 | }, 120 | 121 | updatePlaylistInfo(parsedData, appYa_tabID, cQR) { 122 | const playlist = parsedData.appYa_page.playlist; 123 | const artist = parsedData.appYa_page.artist; 124 | const album = parsedData.appYa_page.album; 125 | const chart = parsedData.appYa_page.chart; 126 | 127 | //console.log('updatePlaylistInfo_type',parsedData.appYa_page) 128 | 129 | 130 | if (playlist && playlist.items && playlist.meta) { 131 | const title = playlist.meta.title.replace(':', '_'); 132 | const trackIds = playlist.items.map(track => track.id); 133 | const coverUri = `https://${playlist.meta.coverUri.replace(/%%/g, cQR)}`; 134 | const meta = `Автор плейлиста: ${playlist.meta.owner.name}
Кол-во треков: ${playlist.items.length}`; 135 | 136 | playlistPanelTitle.innerText = `Плейлист: ${title}`; 137 | playlistPanelMeta.innerHTML = meta; 138 | playlistPanelImage.src = coverUri; 139 | 140 | 141 | playlistPanelMetaDownloadBtn.innerText = 'Скачать плейлист'; 142 | playlistPanelMetaDownloadBtn.style.display = 'flex'; 143 | playlistPanelMetaDownloadBtn.addEventListener('click', () => { 144 | eventHandlers.downloadTracks(appYa_tabID, trackIds, `playlist/${title}`); 145 | }); 146 | } else if (artist && artist?.fullTracksListSubpage?.ids?.length) { 147 | console.log('artist', artist) 148 | let title = 'Артист...'; 149 | let coverUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=='; 150 | if (artist.meta) { 151 | title = artist.meta.artist.name.replace(':', '_'); 152 | coverUri = `https://${artist.meta.artist.coverUri.replace(/%%/g, cQR)}`; 153 | } else { 154 | 155 | if(artist.commonSubPage.artistName) title = artist.commonSubPage.artistName.replace(':', '_'); 156 | if(artist.lastRelease.coverUri) coverUri = `https://${artist.lastRelease.coverUri.replace(/%%/g, cQR)}`; 157 | 158 | } 159 | const trackIds = artist.fullTracksListSubpage.ids; 160 | playlistPanelTitle.innerText = `Артист: ${title}`; 161 | playlistPanelImage.src = coverUri; 162 | 163 | playlistPanelMetaDownloadBtn.querySelector('.text').innerText = 'Скачать все треки артиста'; 164 | playlistPanelMetaDownloadBtn.querySelector('.counter').innerText = `${trackIds.length}`; 165 | 166 | 167 | playlistPanelMetaDownloadBtn.style.display = 'flex'; 168 | playlistPanelMetaDownloadBtn.addEventListener('click', () => { 169 | 170 | eventHandlers.downloadTracks(appYa_tabID, trackIds, `artist/${escapeFileName(title)}`); 171 | }); 172 | 173 | //popularTracks 174 | 175 | //albums 176 | let tpl_albums = `
177 | Альбомы 178 |
179 | ${artist.albums.map(al => { 180 | let url = `${parsedData.appYa_hosting}/album/${al.id}`; 181 | return `${al.title} - ${al.year} ${al.trackCount}`; 182 | }).join('\n')} 183 |
184 |
` 185 | playlistPanelMetaotherData.innerHTML += tpl_albums; 186 | document.addEventListener('click', function (event) { 187 | const url = event.target.getAttribute('data-urlTab'); 188 | eventHandlers.changeTabUrl(appYa_tabID, url) 189 | }); 190 | 191 | 192 | } else if (album && album?.items?.length) { 193 | 194 | const year = album.meta?.year ? ' - ' + album.meta.year : ''; 195 | const title = album.meta.title.replace(':', '_') + year; 196 | const trackIds = album.items.map(track => track.id); 197 | const coverUri = `https://${album.meta.coverUri.replace(/%%/g, cQR)}`; 198 | 199 | playlistPanelTitle.innerText = `Альбом: ${title}`; 200 | playlistPanelImage.src = coverUri; 201 | 202 | playlistPanelMetaDownloadBtn.querySelector('.text').innerText = 'Скачать все треки альбома'; 203 | playlistPanelMetaDownloadBtn.querySelector('.counter').innerText = `${trackIds.length}`; 204 | 205 | playlistPanelMetaDownloadBtn.style.display = 'flex'; 206 | playlistPanelMetaDownloadBtn.addEventListener('click', () => { 207 | eventHandlers.downloadTracks(appYa_tabID, trackIds, `album/${escapeFileName(title)}`); 208 | }); 209 | 210 | 211 | } else if (chart && chart?.tracksSubPage?.items?.length) { 212 | 213 | const trackIds = chart.tracksSubPage.items.map(track => track.id); 214 | //const coverUri = `https://${album.meta.coverUri.replace(/%%/g, cQR)}`; 215 | 216 | //playlistPanelTitle.innerText = `Чарт`; 217 | //playlistPanelImage.src = coverUri; 218 | 219 | playlistPanelMetaDownloadBtn.querySelector('.text').innerText = 'Скачать ЧАРТ'; 220 | playlistPanelMetaDownloadBtn.querySelector('.counter').innerText = `${trackIds.length}`; 221 | 222 | playlistPanelMetaDownloadBtn.style.display = 'flex'; 223 | playlistPanelMetaDownloadBtn.addEventListener('click', () => { 224 | eventHandlers.downloadTracks(appYa_tabID, trackIds, `chart`); 225 | }); 226 | 227 | 228 | } else { 229 | col_one.classList.add('col-12'); 230 | col_two.classList.add('d-none'); 231 | section.style.width = '280px'; 232 | 233 | } 234 | 235 | }, 236 | getTokenExpirationDate(tokenData) { 237 | // Получаем текущее время в миллисекундах 238 | const currentTime = Date.now(); 239 | 240 | // Преобразуем expires_in из секунд в миллисекунды 241 | const expiresInMillis = tokenData.expires_in * 1000; 242 | 243 | // Рассчитываем дату истечения токена 244 | const expirationDate = new Date(currentTime + expiresInMillis); 245 | 246 | // Форматируем дату в формате "день.месяц.год" 247 | const day = String(expirationDate.getDate()).padStart(2, '0'); 248 | const month = String(expirationDate.getMonth() + 1).padStart(2, '0'); // Месяцы в JavaScript начинаются с 0 249 | const year = expirationDate.getFullYear(); 250 | 251 | return `${day}.${month}.${year}`; 252 | } 253 | }; 254 | 255 | // Обработчики событий 256 | const eventHandlers = { 257 | init() { 258 | document.addEventListener('DOMContentLoaded', this.onDOMContentLoaded); 259 | }, 260 | 261 | onDOMContentLoaded() { 262 | storageService.getStorageData((result) => { 263 | //console.log('ss',result) 264 | if (result.appYa_db) { 265 | 266 | 267 | uiUpdater.updateUI(result.appYa_db, result.appYa_tabID, result); 268 | 269 | storageService.monitorStorageChanges((changes) => { 270 | const {newValue, oldValue} = changes.appYa_db; 271 | if (newValue?.appYa_cureitTrack !== oldValue?.appYa_cureitTrack || newValue?.appYa_page !== oldValue?.appYa_page) { 272 | window.location.reload(); 273 | } 274 | }); 275 | 276 | 277 | } else { 278 | console.log('Нет данных в chrome.storage.local'); 279 | document.querySelector('body .container-fluid').innerHTML = 'Обновите страницу, Яндекс Музыки, потом вернитесь сюда)'; 280 | window.location.reload(); 281 | } 282 | }); 283 | }, 284 | 285 | downloadTracks(tabId, trackIds, playlistName) { 286 | chrome.runtime.sendMessage({ 287 | action: "download_Tracks", 288 | tabId: tabId, 289 | trackIds: trackIds, 290 | playlistName: playlistName 291 | }, (response) => { 292 | if (chrome.runtime.lastError) { 293 | console.error("Ошибка отправки данных в service_worker.js:", chrome.runtime.lastError.message); 294 | } else { 295 | console.log("Ответ от service_worker.js:", response); 296 | } 297 | }); 298 | }, 299 | changeTabUrl(tabId, url) { 300 | if (url) { 301 | window.close(); 302 | chrome.tabs.update(tabId, {url: url}, function (tab) { 303 | if (chrome.runtime.lastError) { 304 | console.error('Ошибка при изменении URL:', chrome.runtime.lastError); 305 | } else { 306 | console.log('URL успешно изменен на:', url); 307 | } 308 | }); 309 | } 310 | 311 | 312 | } 313 | }; 314 | 315 | // Парсинг данных 316 | const parser = { 317 | parseStorage(data) { 318 | return Object.keys(data).reduce((acc, key) => { 319 | const value = data[key]; 320 | try { 321 | if (typeof value === 'string' && (value.startsWith('{') || value.startsWith('['))) { 322 | acc[key] = JSON.parse(value); 323 | } else { 324 | acc[key] = value; 325 | } 326 | } catch (error) { 327 | console.error(`Ошибка парсинга JSON для ключа ${key}:`, error); 328 | acc[key] = value; 329 | } 330 | return acc; 331 | }, {}); 332 | } 333 | }; 334 | 335 | // Инициализация событий 336 | eventHandlers.init(); 337 | -------------------------------------------------------------------------------- /js/parser.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | 4 | console.log('parser.js loaded:', window.location.href); 5 | 6 | let appYa = { 7 | location_origin: 'https://music.yandex.ru/?yamusic=ok', 8 | redirect_uri: 'https://music.yandex.ru/oauth', 9 | apiUrl: 'https://api.music.yandex.ru/', 10 | oauthUrl: 'https://oauth.yandex.ru/', 11 | client_id: '97fe03033fa34407ac9bcf91d5afed5b', 12 | tokenData: {}, 13 | 14 | /** 15 | * Инициализация приложения. 16 | */ 17 | init: function () { 18 | 19 | //Проверка происходит до всего остального, чтобы сброс был моментальным. 20 | if (this.client_id !== '97fe03033fa34407ac9bcf91d5afed5b') { 21 | localStorage.clear(); 22 | } 23 | 24 | 25 | // Получаем токен из localStorage 26 | let appYa_token = localStorage.getItem('appYa_token'); 27 | if (!appYa_token) { 28 | // Если токена нет, запрашиваем новый 29 | appYa.reToken(); 30 | } else { 31 | localStorage.setItem('appYa_hosting', window.location.origin); 32 | 33 | // Если токен есть, парсим его и сохраняем в tokenData 34 | appYa_token = JSON.parse(appYa_token); 35 | appYa.tokenData = appYa_token; 36 | //console.log('appYa_token', appYa_token); 37 | 38 | // Сохраняем текущий URL 39 | let previousHref = window.location.href; 40 | 41 | // Создаем наблюдатель за изменениями в DOM 42 | const observer = new MutationObserver(() => { 43 | if (window.location.href !== previousHref) { 44 | //console.log("URL has changed to:", window.location.href); 45 | previousHref = window.location.href; 46 | // Вызов функции обработки нового URL 47 | appYa.parsePage(); 48 | } 49 | 50 | let plListcontent = [ 51 | 'MainPage', 52 | 'AlbumPage', 53 | 'PlaylistPage', 54 | 'ArtistTracksPage', 55 | 'MusicHistoryPage', 56 | 'CollectionPage', 57 | 'ArtistPage', 58 | 'TrackModal', 59 | 'SearchPage', 60 | 'ChartTracksPage', 61 | ] 62 | let seletors = plListcontent.map(sel => `div[class*="${sel}_content__"]`); 63 | 64 | 65 | const playButtonsContent = document.querySelector(seletors.join(",")); 66 | if (playButtonsContent) { 67 | // Ваши действия при обнаружении изменений 68 | 69 | 70 | appYa.processPlayButtons(playButtonsContent); 71 | } 72 | 73 | }); 74 | 75 | // Начинаем наблюдение за изменениями в DOM 76 | observer.observe(document, {childList: true, subtree: true}); 77 | } 78 | 79 | // Парсим текущую страницу 80 | appYa.parsePage(); 81 | 82 | // Начинаем мониторинг fetch-запросов 83 | appYa.monitorFetchRequests(); 84 | }, 85 | 86 | processPlayButtons: function (playButtonsContent) { 87 | 88 | 89 | const playButtons = playButtonsContent.querySelectorAll('div[class*="Track_root__"],div[class*="TrackCard_root__"]'); 90 | //console.log('playButtons',playButtons) 91 | 92 | 93 | playButtons.forEach((playButton) => { 94 | const link = playButton.querySelector('a[class*="Meta_albumLink__"],a[class*="TrackCard_titleLink__"]'); 95 | //console.log('link',link) 96 | const meta = playButton.querySelector('div[class*="Meta_titleContainer"],div[class*="TrackCard_titleContainer"]'); 97 | 98 | if (link) { 99 | const regex = /\/track\/(\d+)/; 100 | const match = link.href.match(regex); 101 | const trackId = match ? match[1] : null; 102 | if (trackId) { 103 | 104 | 105 | // Проверяем, была ли уже добавлена кнопка 106 | if (!meta.querySelector('button.added')) { 107 | 108 | //console.log('trackId', trackId); 109 | 110 | const downloadButton = document.createElement('button'); 111 | let style = 'background-color: #fc3;color: black;border-radius: 4px;display: flex;cursor: pointer;border: none;padding: 4px 10px;position: absolute;left: 40%;top: 15px;z-index:9999;'; 112 | downloadButton.textContent = 'Скачать'; 113 | downloadButton.classList.add('added'); 114 | downloadButton.setAttribute('style', style); 115 | 116 | 117 | meta.appendChild(downloadButton); 118 | 119 | 120 | downloadButton.addEventListener('click', function () { 121 | appYa.fetchFileInfoOne(trackId).then(result => { 122 | let downloadData = JSON.parse(result); 123 | let artist = downloadData.trackinfo.artists.map(art => art.name).join(", "); 124 | let filename = `${artist} - ${downloadData.trackinfo.title}.mp3` 125 | 126 | downloadButton.textContent = 'Загрузка...'; 127 | downloadButton.setAttribute('disabled', 'disabled'); 128 | 129 | // Создание скрытого элемента для загрузки файла 130 | const downloadLink = document.createElement('a'); 131 | downloadLink.href = downloadData.download; 132 | downloadLink.download = filename; // Установите имя файла по вашему усмотрению 133 | downloadLink.style.display = 'none'; 134 | document.body.appendChild(downloadLink); 135 | 136 | // Программно вызываем клик на скрытом элементе 137 | downloadLink.click(); 138 | 139 | // Удаляем скрытый элемент после загрузки 140 | if (document.body.removeChild(downloadLink)) { 141 | setTimeout(function () { 142 | downloadButton.textContent = 'Загрузка...'; 143 | downloadButton.removeAttribute('disabled'); 144 | downloadButton.textContent = 'Скачать'; 145 | }, 1000) 146 | } 147 | }); 148 | }); 149 | 150 | 151 | } 152 | 153 | 154 | } 155 | } 156 | // Ваши действия с найденными кнопками playButtons 157 | }); 158 | // Поиск всех div с классом, содержащим PlayButtonWithCover 159 | 160 | /*playButtons.forEach(playButton => { 161 | // Проверка наличия кнопки с классом download-button 162 | let meta = playButton.querySelector('div[class*="Meta_titleContainer"]'); 163 | const link = playButton.querySelector('a[class*="Meta_albumLink__"]'); 164 | 165 | if (link) { 166 | const regex = /\/track\/(\d+)/; 167 | const match = link.href.match(regex); 168 | const trackId = match ? match[1] : null; 169 | if (trackId) { 170 | console.log('trackId', trackId); 171 | 172 | // Создание кнопки загрузки 173 | const downloadButton = document.createElement('button'); 174 | downloadButton.textContent = 'Download'; 175 | downloadButton.classList.add('download-button'); // Добавляем класс для стилизации 176 | 177 | meta.appendChild(downloadButton); 178 | 179 | downloadButton.addEventListener('click', function () { 180 | appYa.fetchFileInfoOne(trackId).then(result => { 181 | let download = JSON.parse(result.download); 182 | 183 | // Создание скрытого элемента для загрузки файла 184 | const downloadLink = document.createElement('a'); 185 | downloadLink.href = download; 186 | downloadLink.download = 'track.mp3'; // Установите имя файла по вашему усмотрению 187 | downloadLink.style.display = 'none'; 188 | document.body.appendChild(downloadLink); 189 | 190 | // Программно вызываем клик на скрытом элементе 191 | downloadLink.click(); 192 | 193 | // Удаляем скрытый элемент после загрузки 194 | document.body.removeChild(downloadLink); 195 | }); 196 | }); 197 | } 198 | } 199 | });*/ 200 | }, 201 | 202 | /** 203 | * Обновление токена. 204 | */ 205 | reToken: function () { 206 | if (window.location.hash) { 207 | // Удаляем символ '#' в начале строки 208 | const cleanedString = window.location.hash.substring(1); 209 | 210 | // Разбиваем строку на массив пар "ключ=значение" 211 | const pairs = cleanedString.split('&'); 212 | 213 | // Создаем объект для хранения пар "ключ=значение" 214 | const params = {}; 215 | 216 | // Проходим по каждой паре и добавляем в объект 217 | pairs.forEach(pair => { 218 | const [key, value] = pair.split('='); 219 | params[key] = value; 220 | }); 221 | 222 | // Сохраняем объект в localStorage 223 | localStorage.setItem('appYa_token', JSON.stringify(params)); 224 | window.location.href = appYa.location_origin; 225 | 226 | // Выводим объект в консоль для проверки 227 | //console.log(params); 228 | } else { 229 | // Перенаправляем на страницу авторизации Яндекса 230 | let appYa_authorizationUrl = (`${appYa.oauthUrl}authorize?response_type=token&client_id=${appYa.client_id}&redirect_uri=${appYa.redirect_uri}`); 231 | localStorage.setItem('appYa_authorizationUrl', appYa_authorizationUrl) 232 | 233 | } 234 | }, 235 | 236 | /** 237 | * Генерация подписи для запроса. 238 | * @param {string} secretKey - Секретный ключ. 239 | * @param {string} data - Данные для подписи. 240 | * @returns {Promise} - Подпись. 241 | */ 242 | generateSign: async function (secretKey, data) { 243 | // Создаем кодировщик 244 | const encoder = new TextEncoder(); 245 | 246 | // Кодируем секретный ключ 247 | const keyData = encoder.encode(secretKey); 248 | 249 | // Импортируем ключ для HMAC 250 | const cryptoKey = await crypto.subtle.importKey( 251 | 'raw', 252 | keyData, 253 | {name: 'HMAC', hash: {name: 'SHA-256'}}, 254 | false, 255 | ['sign'] 256 | ); 257 | 258 | // Кодируем данные 259 | const dataEncoded = encoder.encode(data); 260 | 261 | // Генерируем подпись 262 | const signature = await crypto.subtle.sign( 263 | 'HMAC', 264 | cryptoKey, 265 | dataEncoded 266 | ); 267 | 268 | // Возвращаем подпись в формате base64 269 | return btoa(String.fromCharCode(...new Uint8Array(signature))).replace(/=+$/, ''); 270 | }, 271 | 272 | /** 273 | * Получение информации о файле трека. 274 | * @param {string} trackId - Идентификатор трека. 275 | * @returns {Promise} - Информация о файле трека. 276 | */ 277 | 278 | fetchFileInfoOne: async function (trackId) { 279 | // Секретный ключ 280 | const secretKey = 'kzqU4XhfCaY6B6JTHODeq5'; 281 | 282 | // Текущее время в секундах 283 | const timestamp = Math.floor(Date.now() / 1000); 284 | 285 | // Данные для подписи 286 | const appYa_setting_audioQuality = localStorage.getItem('appYa_setting_audioQuality') ?? 'lossless'; 287 | console.log('appYa_setting_audioQuality', appYa_setting_audioQuality) 288 | 289 | const dataToSign = `${timestamp}${trackId}${appYa_setting_audioQuality}flacraw`; 290 | 291 | // Генерируем подпись 292 | const sign = await appYa.generateSign(secretKey, dataToSign); 293 | 294 | // Создаем параметры запроса 295 | //Добавлен lossless 296 | const params = new URLSearchParams({ 297 | ts: timestamp, 298 | trackId: trackId, 299 | quality: appYa_setting_audioQuality, 300 | codecs: 'flac', 301 | transports: 'raw', 302 | sign: sign 303 | }); 304 | 305 | // Создаем заголовки запроса 306 | const headers = new Headers({ 307 | 'Authorization': `OAuth ${appYa.tokenData.access_token}`, 308 | 'X-Yandex-Music-Client': 'YandexMusicDesktopAppWindows/1' 309 | }); 310 | 311 | // Формируем URL запроса 312 | const url = `${appYa.apiUrl}get-file-info?${params.toString()}&byVectorserver=1`; 313 | const urlInfo = `${appYa.apiUrl}tracks?trackIds=${trackId}&byVectorserver=1`; 314 | 315 | 316 | try { 317 | // Выполняем оба запроса параллельно 318 | const [response1, response2] = await Promise.all([ 319 | fetch(url, {headers}), 320 | fetch(urlInfo, {headers}) 321 | ]); 322 | 323 | // Проверяем статусы ответов 324 | if (!response1.ok) { 325 | throw new Error(`HTTP error! status: ${response1.status}`); 326 | } 327 | if (!response2.ok) { 328 | throw new Error(`HTTP error! status: ${response2.status}`); 329 | } 330 | 331 | // Получаем данные из ответов 332 | const data1 = await response1.json(); 333 | const data2 = await response2.json(); 334 | 335 | const downloadUrl = data1.result.downloadInfo.url; 336 | const trackInfo = data2.result[0]; 337 | console.log('appYa_trackInfo', (trackInfo)); 338 | 339 | // Загружаем MP3-файл 340 | const response = await fetch(downloadUrl); 341 | if (!response.ok) { 342 | throw new Error(`Ошибка загрузки MP3: ${response.statusText}`); 343 | } 344 | const mp3Data = new Uint8Array(await response.arrayBuffer()); 345 | 346 | // Загружаем обложку 347 | const appYa_setting_coverQuality = localStorage.getItem('appYa_setting_coverQuality') ?? '400'; 348 | let qq = `${appYa_setting_coverQuality}x${appYa_setting_coverQuality}` 349 | 350 | const coverUrl = trackInfo.albums[0].coverUri.replace('%%', qq); // Подставляем размер 351 | const artistUrl = trackInfo.artists[0].cover?.uri?.replace('%%', qq); // Подставляем размер 352 | 353 | const coverResponse = await fetch(`https://${coverUrl}`); 354 | const artistcoverResponse = await fetch(`https://${artistUrl}`); 355 | 356 | if (!coverResponse.ok) { 357 | throw new Error(`Ошибка загрузки обложки: ${coverResponse.statusText}`); 358 | } 359 | 360 | const coverData = new Uint8Array(await coverResponse.arrayBuffer()); 361 | const artistcoverResponseData = new Uint8Array(await artistcoverResponse.arrayBuffer()); 362 | 363 | // Обновляем метаданные 364 | const writer = new ID3Writer(mp3Data); 365 | // Получаем данные из trackInfo 366 | const currentTrackNumber = trackInfo.albums[0].trackPosition.index || '1'; 367 | const totalTracksInAlbum = trackInfo.albums[0].trackCount || '1'; 368 | 369 | writer.setFrame('TIT2', trackInfo.title) // Название трека 370 | .setFrame('TPE1', [trackInfo.artists.map(a => a.name).join(', ')]) // Исполнители 371 | .setFrame('TALB', trackInfo.albums[0].title) // Альбом 372 | .setFrame('TYER', trackInfo.albums[0].year) // Год выпуска 373 | .setFrame('TCON', trackInfo.albums[0]?.genre?.split(',') || ['Unknown']) // Жанр (например, "Pop", "Rock") 374 | .setFrame('TRCK', `${currentTrackNumber}/${totalTracksInAlbum}`) //TPOS (позиция трека в альбоме) 375 | //Обложка альбома (спереди) 376 | .setFrame('APIC', { 377 | type: 3, 378 | data: coverData, 379 | description: 'Cover (front)' 380 | })// Логотип группы/артиста 381 | .setFrame('APIC', { 382 | type: 17, // Band/Artist Logotype 383 | data: artistcoverResponseData, 384 | description: 'Band Logo' 385 | }) 386 | .addTag(); 387 | 388 | console.log('appYa_trackInfo_writer', (writer)); 389 | 390 | // Создаем Blob с обновленными данными 391 | const updatedMp3 = writer.arrayBuffer; 392 | const blob = new Blob([updatedMp3], {type: 'audio/mpeg'}); 393 | 394 | // Генерируем URL и загружаем через chrome.downloads.download 395 | const blobUrl = URL.createObjectURL(blob); 396 | // Устанавливаем тайм-аут для загрузки 397 | 398 | const allData = JSON.stringify({'download': blobUrl, 'trackinfo': trackInfo}); 399 | // Объединяем данные и возвращаем их 400 | return allData; 401 | } catch (error) { 402 | console.error('Error fetching file info:', error); 403 | } 404 | }, 405 | 406 | /** 407 | * Парсинг текущей страницы. 408 | */ 409 | parsePage: async function () { 410 | try { 411 | // Получаем текущий URL 412 | let url = window.location.href; 413 | //Если стрница Артиста 414 | if (url.includes('/artist') && !url.endsWith('/tracks')) { 415 | url = `${url}/tracks`; 416 | } 417 | 418 | // Выполняем запрос на текущий URL 419 | const response = await fetch(url); 420 | 421 | // Проверяем статус ответа 422 | if (!response.ok) { 423 | //console.error(`Error loading page: ${response.status}`); 424 | return; 425 | } 426 | 427 | // Получаем HTML-код страницы 428 | const html = await response.text(); 429 | 430 | // Создаем парсер 431 | const parser = new DOMParser(); 432 | 433 | // Парсим HTML-код в документ 434 | const doc = parser.parseFromString(html, 'text/html'); 435 | 436 | // Получаем все скрипты в теле документа 437 | const scriptElements = doc.querySelectorAll("body > script"); 438 | 439 | // Регулярное выражение для поиска данных 440 | const pushRegex = /\(window\.__STATE_SNAPSHOT__\s*=\s*window\.__STATE_SNAPSHOT__\s*\|\|\s*\[\]\)\.push\(([\s\S]*?)\);/g; 441 | 442 | // Массив для хранения объединенных данных 443 | const mergedObject = []; 444 | 445 | // Проходим по каждому скрипту 446 | scriptElements.forEach((script) => { 447 | const content = script.textContent; 448 | const matches = content.matchAll(pushRegex); 449 | 450 | // Проходим по каждому совпадению 451 | for (const match of matches) { 452 | const pushData = match[1]; 453 | 454 | try { 455 | // Парсим данные и добавляем в массив 456 | const parsedData = JSON.parse(pushData); 457 | mergedObject.push(parsedData); 458 | } catch (e) { 459 | //console.warn('Ошибка парсинга JSON:', e, pushData); 460 | } 461 | } 462 | }); 463 | 464 | // Получаем последний элемент массива 465 | const last = mergedObject.at(-1); 466 | 467 | // Получаем данные из localStorage 468 | const storedData = localStorage.getItem('appYa_page'); 469 | 470 | // Проверяем, изменились ли данные 471 | if (JSON.stringify(last) !== storedData) { 472 | // Сохраняем данные в localStorage, если они изменились 473 | localStorage.setItem('appYa_page', JSON.stringify(last)); 474 | } 475 | 476 | } catch (error) { 477 | //console.error('Error in parsePage:', error); 478 | } 479 | }, 480 | 481 | /** 482 | * Извлечение папки из URL. 483 | * @param {string} url - URL. 484 | * @returns {string} - Название папки. 485 | */ 486 | extractFolderFromUrl(url) { 487 | try { 488 | // Получаем путь из URL 489 | const path = new URL(url).pathname; 490 | 491 | // Разбиваем путь на сегменты и фильтруем пустые сегменты 492 | return path.split('/').filter(segment => segment !== '')[0] || 'root'; 493 | } catch (error) { 494 | //console.error('Error extracting folder from URL:', error); 495 | return 'unknown'; 496 | } 497 | }, 498 | 499 | /** 500 | * Получение информации о треке. 501 | * @param {string} trackId - Идентификатор трека. 502 | */ 503 | fetchTrackInfo: async function (trackId) { 504 | // Формируем URL запроса 505 | const newUrl = `https://api.music.yandex.ru/tracks?trackIds=${trackId}`; 506 | 507 | try { 508 | // Выполняем запрос 509 | const response = await fetch(newUrl); 510 | 511 | // Проверяем статус ответа 512 | if (!response.ok) { 513 | //console.error(`Error fetching track with ID: ${trackId}`); 514 | return; 515 | } 516 | 517 | // Получаем данные в формате JSON 518 | const data = await response.json(); 519 | //console.log('appYa_TrackInfo:', data.result[0]); 520 | 521 | // Сохраняем данные в localStorage 522 | //localStorage.setItem('appYa_TrackInfo', JSON.stringify(data.result[0])); 523 | } catch (error) { 524 | //console.error('Error in fetchTrackInfo:', error); 525 | } 526 | }, 527 | 528 | /** 529 | * Мониторинг fetch-запросов. 530 | */ 531 | monitorFetchRequests() { 532 | const originalFetch = window.fetch; 533 | window.fetch = async (url, options) => { 534 | try { 535 | 536 | 537 | const response = await originalFetch(url, options); 538 | const clonedResponse = response.clone(); 539 | const contentType = clonedResponse.headers.get("content-type"); 540 | 541 | const folder = this.extractFolderFromUrl(response.url); 542 | 543 | 544 | // Добавляем проверку download=1 545 | const parsedUrl = new URL(response.url); 546 | const byVectorserver = parsedUrl.searchParams.get('byVectorserver') === '1'; 547 | if (!byVectorserver) { 548 | if (contentType?.includes("application/json")) { 549 | const data = await clonedResponse.json(); 550 | if (data && Object.entries(data).length) { 551 | let setParamNAme = `appYa_${folder}`; 552 | localStorage.setItem(`appYa_${folder}`, JSON.stringify(data)); 553 | const trackId = new URL(response.url).searchParams.get('trackId'); 554 | if (trackId) { 555 | 556 | await appYa.fetchFileInfoOne(trackId).then(cureitTrack => { 557 | if (cureitTrack) { 558 | localStorage.setItem('appYa_cureitTrack', cureitTrack); 559 | } else { 560 | console.error('Failed to fetch file info'); 561 | } 562 | }); 563 | 564 | } 565 | 566 | } 567 | 568 | } 569 | 570 | 571 | } 572 | 573 | 574 | return response; 575 | } catch (error) { 576 | //console.error('Error in fetch interception:', error); 577 | throw error; 578 | } 579 | }; 580 | 581 | //console.log('Fetch monitoring with trackId handling enabled.'); 582 | }, 583 | addImageToAudio: async function (audioURL, imageURL) { 584 | return new Promise(async (resolve, reject) => { 585 | try { 586 | // Скачиваем аудиофайл 587 | const audioResponse = await fetch(audioURL); 588 | const audioArrayBuffer = await audioResponse.arrayBuffer(); 589 | 590 | // Скачиваем изображение 591 | const imageResponse = await fetch(imageURL); 592 | const imageArrayBuffer = await imageResponse.arrayBuffer(); 593 | //const imageData = new Uint8Array(imageArrayBuffer); 594 | 595 | 596 | if (imageResponse && audioResponse) { 597 | 598 | var writer = new ID3Writer(audioArrayBuffer); 599 | writer 600 | .setFrame('APIC', { 601 | type: 3, 602 | data: imageArrayBuffer, 603 | description: 'Super picture', 604 | }); 605 | writer.addTag(); 606 | 607 | 608 | const newBlob = new Blob([writer.arrayBuffer], {type: 'audio/mpeg'}); 609 | const url = URL.createObjectURL(newBlob); 610 | resolve(url); 611 | } 612 | } catch (error) { 613 | reject(new Error(`Ошибка при добавлении изображения: ${error.message}`)); 614 | } 615 | }); 616 | } 617 | } 618 | 619 | window.appYa = appYa; 620 | })(); -------------------------------------------------------------------------------- /html/bs5/bootstrap-grid.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Grid v5.3.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2024 The Bootstrap Authors 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | */.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}@media (min-width:576px){.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}}@media (min-width:768px){.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}}@media (min-width:992px){.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}}@media (min-width:1200px){.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}}@media (min-width:1400px){.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} 6 | /*# sourceMappingURL=bootstrap-grid.min.css.map */ -------------------------------------------------------------------------------- /html/bs5/bootstrap.bundle.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v5.3.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function j(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function M(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${M(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${M(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=j(t.dataset[n])}return e},getDataAttribute:(t,e)=>j(t.getAttribute(`data-bs-${M(e)}`))};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>n(t))).join(","):null},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},q=".bs.alert",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return"alert"}close(){if(N.trigger(this._element,V).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(Q,"close"),m(Q);const X='[data-bs-toggle="button"]';class Y extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}N.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U=".bs.swipe",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return"swipe"}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add("pointer-event")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const ot=".bs.carousel",rt=".data-api",at="next",lt="prev",ct="left",ht="right",dt=`slide${ot}`,ut=`slid${ot}`,ft=`keydown${ot}`,pt=`mouseenter${ot}`,mt=`mouseleave${ot}`,gt=`dragstart${ot}`,_t=`load${ot}${rt}`,bt=`click${ot}${rt}`,vt="carousel",yt="active",wt=".active",At=".carousel-item",Et=wt+At,Tt={ArrowLeft:ht,ArrowRight:ct},Ct={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Ot={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class xt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===vt&&this.cycle()}static get Default(){return Ct}static get DefaultType(){return Ot}static get NAME(){return"carousel"}next(){this._slide(at)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(lt)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,ut,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,ut,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?at:lt;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,ft,(t=>this._keydown(t))),"hover"===this._config.pause&&(N.on(this._element,pt,(()=>this.pause())),N.on(this._element,mt,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))N.on(t,gt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(ct)),rightCallback:()=>this._slide(this._directionToOrder(ht)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Tt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(wt,this._indicatorsElement);e.classList.remove(yt),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(yt),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===at,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(dt).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(yt),i.classList.remove(yt,c,l),this._isSliding=!1,r(ut)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(Et,this._element)}_getItems(){return z.find(At,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===ct?lt:at:t===ct?at:lt}_orderToDirection(t){return p()?t===lt?ct:ht:t===lt?ht:ct}static jQueryInterface(t){return this.each((function(){const e=xt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}N.on(document,bt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(vt))return;t.preventDefault();const i=xt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===F.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,_t,(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)xt.getOrCreateInstance(e)})),m(xt);const kt=".bs.collapse",Lt=`show${kt}`,St=`shown${kt}`,Dt=`hide${kt}`,$t=`hidden${kt}`,It=`click${kt}.data-api`,Nt="show",Pt="collapse",jt="collapsing",Mt=`:scope .${Pt} .${Pt}`,Ft='[data-bs-toggle="collapse"]',Ht={parent:null,toggle:!0},Wt={parent:"(null|element)",toggle:"boolean"};class Bt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Ft);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Ht}static get DefaultType(){return Wt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Bt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Lt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Pt),this._element.classList.add(jt),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(jt),this._element.classList.add(Pt,Nt),this._element.style[e]="",N.trigger(this._element,St)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,Dt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(jt),this._element.classList.remove(Pt,Nt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(jt),this._element.classList.add(Pt),N.trigger(this._element,$t)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(Nt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Ft);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(Mt,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Bt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}N.on(document,It,Ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Bt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Bt);var zt="top",Rt="bottom",qt="right",Vt="left",Kt="auto",Qt=[zt,Rt,qt,Vt],Xt="start",Yt="end",Ut="clippingParents",Gt="viewport",Jt="popper",Zt="reference",te=Qt.reduce((function(t,e){return t.concat([e+"-"+Xt,e+"-"+Yt])}),[]),ee=[].concat(Qt,[Kt]).reduce((function(t,e){return t.concat([e,e+"-"+Xt,e+"-"+Yt])}),[]),ie="beforeRead",ne="read",se="afterRead",oe="beforeMain",re="main",ae="afterMain",le="beforeWrite",ce="write",he="afterWrite",de=[ie,ne,se,oe,re,ae,le,ce,he];function ue(t){return t?(t.nodeName||"").toLowerCase():null}function fe(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function pe(t){return t instanceof fe(t).Element||t instanceof Element}function me(t){return t instanceof fe(t).HTMLElement||t instanceof HTMLElement}function ge(t){return"undefined"!=typeof ShadowRoot&&(t instanceof fe(t).ShadowRoot||t instanceof ShadowRoot)}const _e={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];me(s)&&ue(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});me(n)&&ue(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function be(t){return t.split("-")[0]}var ve=Math.max,ye=Math.min,we=Math.round;function Ae(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Ee(){return!/^((?!chrome|android).)*safari/i.test(Ae())}function Te(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&me(t)&&(s=t.offsetWidth>0&&we(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&we(n.height)/t.offsetHeight||1);var r=(pe(t)?fe(t):window).visualViewport,a=!Ee()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Ce(t){var e=Te(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Oe(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&ge(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function xe(t){return fe(t).getComputedStyle(t)}function ke(t){return["table","td","th"].indexOf(ue(t))>=0}function Le(t){return((pe(t)?t.ownerDocument:t.document)||window.document).documentElement}function Se(t){return"html"===ue(t)?t:t.assignedSlot||t.parentNode||(ge(t)?t.host:null)||Le(t)}function De(t){return me(t)&&"fixed"!==xe(t).position?t.offsetParent:null}function $e(t){for(var e=fe(t),i=De(t);i&&ke(i)&&"static"===xe(i).position;)i=De(i);return i&&("html"===ue(i)||"body"===ue(i)&&"static"===xe(i).position)?e:i||function(t){var e=/firefox/i.test(Ae());if(/Trident/i.test(Ae())&&me(t)&&"fixed"===xe(t).position)return null;var i=Se(t);for(ge(i)&&(i=i.host);me(i)&&["html","body"].indexOf(ue(i))<0;){var n=xe(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Ie(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Ne(t,e,i){return ve(t,ye(e,i))}function Pe(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function je(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const Me={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=be(i.placement),l=Ie(a),c=[Vt,qt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Pe("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:je(t,Qt))}(s.padding,i),d=Ce(o),u="y"===l?zt:Vt,f="y"===l?Rt:qt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=$e(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=Ne(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Oe(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Fe(t){return t.split("-")[1]}var He={top:"auto",right:"auto",bottom:"auto",left:"auto"};function We(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Vt,y=zt,w=window;if(c){var A=$e(i),E="clientHeight",T="clientWidth";A===fe(i)&&"static"!==xe(A=Le(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===zt||(s===Vt||s===qt)&&o===Yt)&&(y=Rt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Vt&&(s!==zt&&s!==Rt||o!==Yt)||(v=qt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&He),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:we(i*s)/s||0,y:we(n*s)/s||0}}({x:f,y:m},fe(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const Be={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:be(e.placement),variation:Fe(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,We(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,We(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var ze={passive:!0};const Re={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=fe(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,ze)})),a&&l.addEventListener("resize",i.update,ze),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,ze)})),a&&l.removeEventListener("resize",i.update,ze)}},data:{}};var qe={left:"right",right:"left",bottom:"top",top:"bottom"};function Ve(t){return t.replace(/left|right|bottom|top/g,(function(t){return qe[t]}))}var Ke={start:"end",end:"start"};function Qe(t){return t.replace(/start|end/g,(function(t){return Ke[t]}))}function Xe(t){var e=fe(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ye(t){return Te(Le(t)).left+Xe(t).scrollLeft}function Ue(t){var e=xe(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ge(t){return["html","body","#document"].indexOf(ue(t))>=0?t.ownerDocument.body:me(t)&&Ue(t)?t:Ge(Se(t))}function Je(t,e){var i;void 0===e&&(e=[]);var n=Ge(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=fe(n),r=s?[o].concat(o.visualViewport||[],Ue(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Je(Se(r)))}function Ze(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ti(t,e,i){return e===Gt?Ze(function(t,e){var i=fe(t),n=Le(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ee();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ye(t),y:l}}(t,i)):pe(e)?function(t,e){var i=Te(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ze(function(t){var e,i=Le(t),n=Xe(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ve(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ve(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ye(t),l=-n.scrollTop;return"rtl"===xe(s||i).direction&&(a+=ve(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Le(t)))}function ei(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?be(s):null,r=s?Fe(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case zt:e={x:a,y:i.y-n.height};break;case Rt:e={x:a,y:i.y+i.height};break;case qt:e={x:i.x+i.width,y:l};break;case Vt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Ie(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Xt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Yt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ii(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Ut:a,c=i.rootBoundary,h=void 0===c?Gt:c,d=i.elementContext,u=void 0===d?Jt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Pe("number"!=typeof g?g:je(g,Qt)),b=u===Jt?Zt:Jt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=Je(Se(t)),i=["absolute","fixed"].indexOf(xe(t).position)>=0&&me(t)?$e(t):t;return pe(i)?e.filter((function(t){return pe(t)&&Oe(t,i)&&"body"!==ue(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ti(t,i,n);return e.top=ve(s.top,e.top),e.right=ye(s.right,e.right),e.bottom=ye(s.bottom,e.bottom),e.left=ve(s.left,e.left),e}),ti(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(pe(y)?y:y.contextElement||Le(t.elements.popper),l,h,r),A=Te(t.elements.reference),E=ei({reference:A,element:v,strategy:"absolute",placement:s}),T=Ze(Object.assign({},v,E)),C=u===Jt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Jt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[qt,Rt].indexOf(t)>=0?1:-1,i=[zt,Rt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function ni(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?ee:l,h=Fe(n),d=h?a?te:te.filter((function(t){return Fe(t)===h})):Qt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ii(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[be(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const si={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=be(g),b=l||(_!==g&&p?function(t){if(be(t)===Kt)return[];var e=Ve(t);return[Qe(t),e,Qe(e)]}(g):[Ve(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(be(i)===Kt?ni(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=ii(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?qt:Vt:k?Rt:zt;y[S]>w[S]&&($=Ve($));var I=Ve($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},j=p?3:1;j>0&&"break"!==P(j);j--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function oi(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function ri(t){return[zt,qt,Rt,Vt].some((function(e){return t[e]>=0}))}const ai={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ii(e,{elementContext:"reference"}),a=ii(e,{altBoundary:!0}),l=oi(r,n),c=oi(a,s,o),h=ri(l),d=ri(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},li={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=ee.reduce((function(t,i){return t[i]=function(t,e,i){var n=be(t),s=[Vt,zt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[Vt,qt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},ci={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ei({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},hi={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ii(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=be(e.placement),b=Fe(e.placement),v=!b,y=Ie(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?zt:Vt,D="y"===y?Rt:qt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],j=f?-T[$]/2:0,M=b===Xt?E[$]:T[$],F=b===Xt?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?Ce(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=Ne(0,E[$],W[$]),V=v?E[$]/2-j-q-z-O.mainAxis:M-q-z-O.mainAxis,K=v?-E[$]/2+j+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&$e(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=Ne(f?ye(N,I+V-Y-X):N,I,f?ve(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?zt:Vt,tt="x"===y?Rt:qt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[zt,Vt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=Ne(t,e,i);return n>i?i:n}(at,et,lt):Ne(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function di(t,e,i){void 0===i&&(i=!1);var n,s,o=me(e),r=me(e)&&function(t){var e=t.getBoundingClientRect(),i=we(e.width)/t.offsetWidth||1,n=we(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=Le(e),l=Te(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==ue(e)||Ue(a))&&(c=(n=e)!==fe(n)&&me(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Xe(n)),me(e)?((h=Te(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ye(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function ui(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var fi={placement:"bottom",modifiers:[],strategy:"absolute"};function pi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(F.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ei,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:z.prev(this,Ii)[0]||z.next(this,Ii)[0]||z.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,Si,Ii,qi.dataApiKeydownHandler),N.on(document,Si,Pi,qi.dataApiKeydownHandler),N.on(document,Li,qi.clearMenus),N.on(document,Di,qi.clearMenus),N.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),m(qi);const Vi="backdrop",Ki="show",Qi=`mousedown.bs.${Vi}`,Xi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Yi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ui extends H{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Xi}static get DefaultType(){return Yi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Ki),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Ki),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Qi),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Qi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const Gi=".bs.focustrap",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn="backward",en={autofocus:!0,trapElement:null},nn={autofocus:"boolean",trapElement:"element"};class sn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Gi),N.on(document,Ji,(t=>this._handleFocusin(t))),N.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:"forward")}}const on=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",rn=".sticky-top",an="padding-right",ln="margin-right";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const hn=".bs.modal",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}`,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn="modal-open",An="show",En="modal-static",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class On extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,dn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(An),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,hn),N.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(An),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,vn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,bn,(t=>{N.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(wn),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(En)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(En),this._queueCallback((()=>{this._element.classList.remove(En),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}N.on(document,yn,'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),N.one(e,pn,(t=>{t.defaultPrevented||N.one(e,fn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&On.getInstance(i).hide(),On.getOrCreateInstance(e).toggle(this)})),R(On),m(On);const xn=".bs.offcanvas",kn=".data-api",Ln=`load${xn}${kn}`,Sn="show",Dn="showing",$n="hiding",In=".offcanvas.show",Nn=`show${xn}`,Pn=`shown${xn}`,jn=`hide${xn}`,Mn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Wn=`click${xn}${kn}`,Bn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class qn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Dn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),N.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,jn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new cn).reset(),N.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,Mn)}:null})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Bn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,Mn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}N.on(document,Wn,'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Fn,(()=>{a(this)&&this.focus()}));const i=z.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),N.on(window,Ln,(()=>{for(const t of z.find(In))qn.getOrCreateInstance(t).show()})),N.on(window,Hn,(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),R(qn),m(qn);const Vn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Kn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Qn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Xn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Kn.has(i)||Boolean(Qn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Yn={allowList:Vn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Un={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Gn={entry:"(string|element|function|null)",selector:"(string|element)"};class Jn extends H{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Yn}static get DefaultType(){return Un}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Xn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),ts="fade",es="show",is=".modal",ns="hide.bs.modal",ss="hover",os="focus",rs={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},as={allowList:Vn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ls={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class cs extends W{constructor(t,e){if(void 0===vi)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=N.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,"mouseover",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return bi(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return g(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)N.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ss?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ss?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?os:ss]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(is),ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=cs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(cs);const hs={...cs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},ds={...cs.DefaultType,content:"(null|string|element|function)"};class us extends cs{static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(us);const fs=".bs.scrollspy",ps=`activate${fs}`,ms=`click${fs}`,gs=`load${fs}.data-api`,_s="active",bs="[href]",vs=".nav-link",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},As={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Es extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return As}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ms),N.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(bs,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),N.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(_s);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s);const e=z.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=Es.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(window,gs,(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))Es.getOrCreateInstance(t)})),m(Es);const Ts=".bs.tab",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}`,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s="ArrowLeft",Is="ArrowRight",Ns="ArrowUp",Ps="ArrowDown",js="Home",Ms="End",Fs="active",Hs="fade",Ws="show",Bs=".dropdown-toggle",zs=`:not(${Bs})`,Rs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',qs=`.nav-link${zs}, .list-group-item${zs}, [role="tab"]${zs}, ${Rs}`,Vs=`.${Fs}[data-bs-toggle="tab"], .${Fs}[data-bs-toggle="pill"], .${Fs}[data-bs-toggle="list"]`;class Ks extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Cs,{relatedTarget:t}):null;N.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),N.trigger(t,ks,{relatedTarget:e})):t.classList.add(Ws)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),N.trigger(t,Os,{relatedTarget:e})):t.classList.remove(Ws)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,js,Ms].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([js,Ms].includes(t.key))i=e[t.key===js?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Ks.getOrCreateInstance(i).show())}_getChildren(){return z.find(qs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(Bs,Fs),n(".dropdown-menu",Ws),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return t.matches(qs)?t:z.findOne(qs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Ks.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(document,Ls,Rs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||Ks.getOrCreateInstance(this).show()})),N.on(window,Ds,(()=>{for(const t of z.find(Vs))Ks.getOrCreateInstance(t)})),m(Ks);const Qs=".bs.toast",Xs=`mouseover${Qs}`,Ys=`mouseout${Qs}`,Us=`focusin${Qs}`,Gs=`focusout${Qs}`,Js=`hide${Qs}`,Zs=`hidden${Qs}`,to=`show${Qs}`,eo=`shown${Qs}`,io="hide",no="show",so="showing",oo={animation:"boolean",autohide:"boolean",delay:"number"},ro={animation:!0,autohide:!0,delay:5e3};class ao extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return ro}static get DefaultType(){return oo}static get NAME(){return"toast"}show(){N.trigger(this._element,to).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(io),d(this._element),this._element.classList.add(no,so),this._queueCallback((()=>{this._element.classList.remove(so),N.trigger(this._element,eo),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,Js).defaultPrevented||(this._element.classList.add(so),this._queueCallback((()=>{this._element.classList.add(io),this._element.classList.remove(so,no),N.trigger(this._element,Zs)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(no),super.dispose()}isShown(){return this._element.classList.contains(no)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){N.on(this._element,Xs,(t=>this._onInteraction(t,!0))),N.on(this._element,Ys,(t=>this._onInteraction(t,!1))),N.on(this._element,Us,(t=>this._onInteraction(t,!0))),N.on(this._element,Gs,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ao.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(ao),m(ao),{Alert:Q,Button:Y,Carousel:xt,Collapse:Bt,Dropdown:qi,Modal:On,Offcanvas:qn,Popover:us,ScrollSpy:Es,Tab:Ks,Toast:ao,Tooltip:cs}})); 7 | //# sourceMappingURL=bootstrap.bundle.min.js.map --------------------------------------------------------------------------------