diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 57654cfa..5f72d294 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -29,7 +29,7 @@ jobs: runs-on: macos-11 permissions: actions: read - contents: read + contents: write security-events: write strategy: @@ -100,6 +100,25 @@ jobs: - name: Add license to dmg run: | npx dmg-license resources/license.json dist/*.dmg + + - name: Import + uses: apple-actions/import-codesign-certs@v1 + with: + p12-file-base64: ${{ secrets.CSC_LINK }} + p12-password: ${{ secrets.CSC_KEY_PASSWORD }} + + - name: Create PKG manually + env: + CSC_LINK: ${{ secrets.CSC_LINK }} + CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + APPLEID: ${{ secrets.APPLEID }} + APPLEIDPASS: ${{ secrets.APPLEIDPASS }} + run: | + pkgbuild --component dist/mac-universal/Cider.app --install-location /Applications dist/Cider.pkg --sign ${{ secrets.PSC_NAME }} + xcrun altool --notarize-app --primary-bundle-id com.ciderapp.cider -f dist/Cider.pkg --username ${{ secrets.APPLEID }} --password ${{ secrets.APPLEIDPASS }} + sleep 5m + xcrun stapler staple dist/Cider.pkg || true + mv dist/*.dmg dist/Cider.dmg - name: Upload a Build Artifact uses: actions/upload-artifact@v2.2.3 @@ -107,5 +126,20 @@ jobs: # Artifact name name: macOS # A file, directory or wildcard pattern that describes what to upload - path: dist/*.dmg + path: | + dist/*.dmg + dist/*.pkg # The desired behavior if no files are found using the provided path. + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: | + dist/Cider.dmg + dist/Cider.pkg + body: signed Develop MacOS Builds + name: macOS builds + tag_name: macos-beta + prerelease : true + generate_release_notes: true + append_body : false + fail_on_unmatched_files: false \ No newline at end of file diff --git a/package.json b/package.json index a1362af9..f2eaa78d 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "get-port": "^5.1.1", "jsonc": "^2.0.0", "lastfmapi": "^0.1.1", - "mdns-js": "git+https://github.com/bitfocus/node-mdns-js.git", + "mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git", "mpris-service": "^2.1.2", "music-metadata": "^7.11.4", "node-gyp": "^8.4.1", diff --git a/src/i18n/de_DE.jsonc b/src/i18n/de_DE.jsonc index 57c29597..ec5deb48 100644 --- a/src/i18n/de_DE.jsonc +++ b/src/i18n/de_DE.jsonc @@ -4,7 +4,7 @@ "i18n.languageName": "Deutsch", // name of language in native language "i18n.languageNameEnglish": "German", // name of language in English "i18n.category": "main", // main = real language, fun = fun community languages - "i18n.authors": "@motz0815", // Authors, if you contribute to this file feel free to add your name seperated with a space + "i18n.authors": "@motz0815 @n0chteil", // Authors, if you contribute to this file feel free to add your name seperated with a space // App info "app.name": "Cider", @@ -46,7 +46,12 @@ "term.podcasts": "Podcasts", "term.playlists": "Playlists", "term.playlist": "Playlist", - "term.play": "Play", + "term.newPlaylist": "Neue Playlist", + "term.newPlaylistFolder": "Neuer Playlist-Ordner", + "term.createNewPlaylist": "Neue Playlist erstellen", + "term.createNewPlaylistFolder": "Neuen Playlist-Ordner erstellen", + "term.deletePlaylist": "Bist du sicher, dass du diese Playlist löschen willst?", + "term.play": "Wiedergabe", "term.pause": "Pause", "term.previous": "Zurück", "term.next": "Weiter", @@ -56,6 +61,7 @@ "term.mute": "Stummschalten", "term.unmute": "Stummschaltung aufheben", "term.share": "Teilen", + "term.share.success": "In die Zwischenablage kopiert", "term.settings": "Einstellungen", "term.seeAll": "Alle Sehen", "term.sortBy": "Sortieren nach", @@ -65,6 +71,7 @@ "term.sortBy.genre": "Genre", "term.sortBy.releaseDate": "Veröffentlichungsdatum", "term.sortBy.duration": "Länge", + "term.sortBy.dateAdded": "Hinzugefügt am", "term.sortOrder": "A-Z", "term.sortOrder.ascending": "Aufsteigend", "term.sortOrder.descending": "Absteigend", @@ -82,12 +89,12 @@ "term.connecting": "Verbindet", "term.disconnect": "Trennen", "term.authed": "Autorisiert", - "term.confirm": "Bestätigen ?", + "term.confirm": "Bestätigen?", "term.more": "Mehr", "term.less": "Weniger", - "term.showMore": "Zeige mehr", - "term.showLess": "Zeige weniger", - "term.topSongs" : "Top Songs", + "term.showMore": "Mehr anzeigen", + "term.showLess": "Weniger anzeigen", + "term.topSongs": "Top Songs", "term.latestReleases": "Letzte Veröffentlichungen", "term.time.added": "Hinzugefügt", "term.time.released": "Veröffentlicht", @@ -100,24 +107,48 @@ "term.time.second": "Sekunde", "term.fullscreenView": "Vollbildansicht", "term.defaultView": "Normale Ansicht", + "term.audioSettings": "Audio-Einstellungen", "term.spacializedAudioSetting": "Räumliches Audio", "term.clearAll": "Alle löschen", - "term.recentStations": "Letzte Stationen", + "term.recentStations": "Letzte Sender", "term.language": "Sprache", "term.funLanguages": "Spaß", - "term.noLyrics": "Lädt... / Liedtext nicht gefunden./ Instrumental.", + "term.noLyrics": "Lädt... / Lyrik nicht gefunden./ Instrumental.", "term.copyright": "Copyright", - "term.rightsReserved": "All Rights Reserved.", + "term.rightsReserved": "Alle Rechte vorbehalten.", "term.sponsor": "Unterstütze dieses Projekt", "term.ciderTeam": "Cider Team", "term.developer": "Entwickler", "term.socialTeam": "Social Team", + "term.socials": "Soziale Medien", "term.contributors": "Mithelfer", "term.equalizer": "Equalizer", "term.reset": "Zurücksetzen", "term.tracks": "Lieder", // Assume x amount of tracks. e.g. 50 tracks "term.videos": "Videos", "term.menu": "Menü", + "term.check": "Nach Updates suchen", + "term.aboutArtist": "Über {{artistName}}", // e.g. 'About Doja Cat' + "term.topResult": "Bestes Ergebnis", // Search Results + "term.sharedPlaylists": "Geteilte Playlists", // Search Results + "term.people": "Personen", // Search Results + "term.newpreset.name": "Neuer EQ-Preset Name", // Equalizer Preset + "term.addedpreset": "Preset erstellt", + "term.deletepreset.warn": "Bist du sicher, dass du dieses Preset löschen willst?", + "term.deletedpreset": "Preset gelöscht", + "term.defaultPresets": "Standard-Presets", + "term.userPresets": "Nutzer-Presets", + "term.requestError": "Es gab ein Problem bei der Anfrage.", + "term.song.link.generate": "song.link URL wird gesucht...", + "term.musicVideos": "Musikvideos", // Search page friendlyTypes + "term.stations": "Radiosender", + "term.curators": "Kuratoren", + "term.appleCurators": "Apple-Kuratoren", + "term.radioShows": "Radiosendungen", + "term.recordLabels": "Plattenfirma", + "term.videoExtras": "Video-Extras", + "term.top": "Beste", + "term.version": "Version", // Home "home.title": "Home", @@ -131,7 +162,7 @@ // Errors "error.appleMusicSubRequired": "Apple Music benötigt ein Abonnement.", - "error.connectionError": "Es ist ein Fehler aufgetreten, während sich mit Apple Music verbunden wurde.", + "error.connectionError": "Es gab ein Problem beim Verbinden mit Apple Music.", "error.noResults": "Keine Ergebnisse.", "error.noResults.description": "Versuche einen anderen Suchbegriff.", @@ -157,6 +188,7 @@ "action.removeFromQueue": "Aus Warteschlange entfernen", "action.removeFromQueue.success": "Aus Warteschlange entfernt", "action.removeFromQueue.error": "Fehler beim Entfernen aus der Warteschlange", + "action.createPlaylist": "Neue Playlist erstellen", "action.addToPlaylist": "Zur Playlist hinzufügen", "action.removeFromPlaylist": "Aus Playlist entfernen", "action.addToFavorites": "Zu Favoriten hinzufügen", @@ -184,7 +216,14 @@ "action.removeTracks": "Entferne ${self.selectedItems.length} Lieder aus der Warteschlange", "action.import": "Importieren", "action.export": "Exportieren", - "action.showAlbum": "Zeige ganzes Album", + "action.showAlbum": "Ganzes Album anzeigen", + "action.tray.minimize": "Zu Tray minimieren", + "action.tray.quit": "Beenden", + "action.tray.show": "{appName} anzeigen", + "action.update": "Update", + "action.copy": "Kopieren", + "action.newpreset": "Neues Preset", // Equalizer Preset + "action.deletepreset": "Preset löschen", // Settings - General "settings.header.general": "Allgemein", @@ -197,21 +236,37 @@ "settings.option.general.language.unsorted": "Unsortiert", // Update Cider + "settings.option.general.updateCider": "Cider updaten", // Button. Refer to term.check for the check button + "settings.option.general.updateCider.branch": "Update-Branch", // Dropdown + "settings.option.general.updateCider.branch.description": "Der Branch, von welchem Cider geupdatet werden soll", + "settings.option.general.updateCider.branch.main": "Stabil", + "settings.option.general.updateCider.branch.develop": "Entwicklung", // Settings - Audio "settings.header.audio": "Audio", "settings.header.audio.description": "Passe die Audio-Einstellungen für Cider an.", "settings.option.audio.quality": "Audioqualität", // Dropdown + "settings.header.audio.quality.hireslossless": "Hi-Res Lossless", + "settings.header.audio.quality.hireslossless.description": "bis zu 24-bit/192 kHz", + "settings.header.audio.quality.lossless": "Lossless", + "settings.header.audio.quality.lossless.description": "bis zu 24-bit/48 kHz", "settings.header.audio.quality.high": "Hoch", - "settings.header.audio.quality.low": "Niedrig", - "settings.header.audio.quality.auto": "Auto", + "settings.header.audio.quality.standard": "Standard", "settings.option.audio.seamlessTransition": "Nahtloser Audioübergang", // Toggle "settings.option.audio.enableAdvancedFunctionality": "Akiviere erweiterte Funktionalität", // Toggle "settings.option.audio.enableAdvancedFunctionality.description": "Das Aktivieren der erweiterten Funktionalität ermöglicht spezielle Features wie Audio-Normalisierung, Equalizer und Visualizer, jedoch könnte dies auf einigen Systemen zu Aussetzern in der Musik führen.", + "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™️", // Toggle + "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Psychoakustische Verbesserungen die die Musik lebensechter erscheinen lassen | Designed by Maikiwi.", + "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "CAP ist nicht mit räumlichem Audio kompatibel. Bitte deaktiviere räumliches Audio zuerst.", + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "CAP-Stärke", // Toggle + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "Ändert die Stärke der Nachbearbeitung. (Aggressiv könnte zu ungewünschten Änderungen führen)", + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "Standard", + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "Aggressiv", "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "Audio-Normalisierung", // Toggle "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "Normalisiert die Lautstärke aller Lieder, um die Hörerfahrung einheitlicher zu machen.", "settings.option.audio.enableAdvancedFunctionality.audioSpatialization": "Räumliches Audio", // Toggle "settings.option.audio.enableAdvancedFunctionality.audioSpatialization.description": "Macht die Musik räumlicher (3D-Effekt) (Beachte: Dies ist kein Dolby Atmos)", + "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "Räumliches Audio ist nicht mit CAP kompatibel. Bitte deaktiviere CAP zuerst.", // Settings - Visual "settings.header.visual": "Visuell", @@ -235,8 +290,15 @@ "settings.option.visual.hardwareAcceleration.description": "Erfordert Neustart der Anwendung", "settings.header.visual.hardwareAcceleration.default": "Normal", "settings.header.visual.hardwareAcceleration.webGPU": "WebGPU", + "settings.header.visual.theme": "Theme", + "settings.option.visual.theme.github.download": "Von GitHub URL installieren", + "settings.prompt.visual.theme.github.URL": "Gib die URL des Themes ein, welches du installieren möchtest", + "settings.notyf.visual.theme.install.success": "Theme erfolgreich installiert", + "settings.notyf.visual.theme.install.error": "Theme-Installation fehlgeschlagen", // Settings - Visual - Theme name + "settings.option.visual.theme.default": "Cider", + "settings.option.visual.theme.dark": "Dunkel", // Refer to term.disabled for the disabled option "settings.option.visual.showPersonalInfo": "Persönliche Daten anzeigen", // Toggle @@ -255,8 +317,8 @@ "settings.option.connectivity.discordRPC": "Discord Rich Presence", // Dropdown "settings.option.connectivity.playbackNotifications": "Wiedergabe-Benachrichtigungen", // Toggle // Refer to term.disabled for the disabled option - "settings.header.connectivity.discordRPC.cider": "Zeige als 'Cider'", - "settings.header.connectivity.discordRPC.appleMusic": "Zeige als 'Apple Music'", + "settings.header.connectivity.discordRPC.cider": "'Cider' anzeigen", + "settings.header.connectivity.discordRPC.appleMusic": "'Apple Music' anzeigen", "settings.option.connectivity.discordRPC.clearOnPause": "Leere Discord Rich Presence wenn pausiert", // Toggle "settings.option.connectivity.lastfmScrobble": "LastFM Scrobbling", // Option to Connect "settings.option.connectivity.lastfmScrobble.delay": "LastFM Scrobble Delay (%)", @@ -269,34 +331,34 @@ "settings.header.experimental": "Experimentell", "settings.header.experimental.description": "Passe die experimentellen Einstellungen für Cider an.", "settings.option.experimental.compactUI": "Kompaktes UI", // Toggle - "settings.option.experimental.closeButtonBehaviour": "Verhalten der Schließtaste", - "settings.option.experimental.closeButtonBehaviour.quit": "Cider Schließen", - "settings.option.experimental.closeButtonBehaviour.minimizeTaskbar": "In Taskleiste minimieren", - "settings.option.experimental.closeButtonBehaviour.minimizeTray": "In den Tray minimieren", + "settings.option.experimental.close_button_hide": "Schließtaste soll die App verbergen", + "settings.option.experimental.copy_log": "Kopiere Logs in die Zwischenablage", + "settings.option.experimental.inline_playlists": "Inline Playlists und Alben", // Refer to term.disabled & term.enabled // Spatialization Menu - "spatial.spatialProperties" : "Räumliche Einstellungen", - "spatial.width" : "Breite", - "spatial.height" : "Höhe", - "spatial.depth" : "Tiefe", - "spatial.gain" : "Gain", - "spatial.roomMaterials" : "Raummaterialien", - "spatial.roomDimensions" : "Raumabmessungen", - "spatial.roomPositions" : "Raumpositionen", - "spatial.setDimensions" : "Setze Abmessungen", - "spatial.setPositions" : "Setze Positionen", - "spatial.up" : "Oben", - "spatial.front" : "Vorne", - "spatial.left" : "Links", - "spatial.right" : "Rechts", - "spatial.back" : "Hinten", - "spatial.down" : "Unten", - "spatial.listener" : "Hörer*in", - "spatial.audioSource" : "Audioquelle", + "spatial.notTurnedOn": "Die Audio-Spatialisierung ist deaktiviert. Um sie zu verwenden, musst du diese zuerst aktivieren.", + "spatial.spatialProperties": "Räumliche Einstellungen", + "spatial.width": "Breite", + "spatial.height": "Höhe", + "spatial.depth": "Tiefe", + "spatial.gain": "Gain", + "spatial.roomMaterials": "Raummaterialien", + "spatial.roomDimensions": "Raumabmessungen", + "spatial.roomPositions": "Raumpositionen", + "spatial.setDimensions": "Setze Abmessungen", + "spatial.setPositions": "Setze Positionen", + "spatial.up": "Oben", + "spatial.front": "Vorne", + "spatial.left": "Links", + "spatial.right": "Rechts", + "spatial.back": "Hinten", + "spatial.down": "Unten", + "spatial.listener": "Hörer*in", + "spatial.audioSource": "Audioquelle", // Settings - Unfinished - "settings.header.unfinished": "Unfertig", + "settings.header.unfinished": "Unvollendet", // Web Remote "remote.web.title": "Cider Remote", diff --git a/src/i18n/en_PISS.jsonc b/src/i18n/en_PISS.jsonc index bb4b38b6..1cb5088f 100644 --- a/src/i18n/en_PISS.jsonc +++ b/src/i18n/en_PISS.jsonc @@ -124,7 +124,12 @@ "term.contributors": "more pisseneers", "term.equalizer": "advanced piss configuration", "term.reset": "repiss", - "term.tracks": "pisses", // Assume x amount of tracks. e.g. 50 tracks + // Example for multiple plural forms : look up the key for your language in https://github.com/prantlf/fast-plural-rules/blob/master/docs/languages.md#supported-languages + "term.track": { + "one" : "piss", + "other" : "pisses" + }, + // "term.tracks": "pisses", // Assume x amount of tracks. e.g. 50 tracks "term.videos": "piss videos", "term.menu": "piss menu", "term.check": "piss!", @@ -136,6 +141,8 @@ "term.addedpreset": "obtained prepiss", "term.deletepreset.warn": "are you sure you want to obliterate this prepiss?", "term.deletedpreset": "obliterated prepiss", + "term.defaultPresets": "default prepisses", + "term.userPresets": "your prepisses", "term.requestError": "the request got pissed on", "term.song.link.generate": "pissing on piss.link…", "term.musicVideos": "piss videos", // Search page friendlyTypes @@ -146,6 +153,7 @@ "term.recordLabels": "piss labels", "term.videoExtras": "extra piss videos", "term.top": "topiss", + "term.version": "pission", // Home "home.title": "piss central", @@ -290,6 +298,10 @@ "settings.header.visual.hardwareAcceleration.default": "default piss", "settings.header.visual.hardwareAcceleration.webGPU": "WebPISS", "settings.header.visual.theme": "colored piss", + "settings.option.visual.theme.github.download": "piss from PissHub URL", + "settings.prompt.visual.theme.github.URL": "piss on the URL for your colorful piss", + "settings.notyf.visual.theme.install.success": "colored piss obtained", + "settings.notyf.visual.theme.install.error": "colored piss got pissed on…", // Settings - Visual - Theme name "settings.option.visual.theme.default": "same old piss", diff --git a/src/i18n/en_US.jsonc b/src/i18n/en_US.jsonc index 7f65bf75..9b4e1652 100644 --- a/src/i18n/en_US.jsonc +++ b/src/i18n/en_US.jsonc @@ -299,6 +299,8 @@ "settings.header.visual.hardwareAcceleration.webGPU": "WebGPU", "settings.header.visual.theme": "Theme", "settings.option.visual.theme.github.download": "Install from GitHub URL", + "settings.option.visual.theme.github.explore": "Explore GitHub Themes", + "settings.option.visual.theme.github.install.confirm": "Are you sure you want to install {{ repo }}?", "settings.prompt.visual.theme.github.URL": "Enter the URL of the theme you want to install", "settings.notyf.visual.theme.install.success": "Theme installed successfully", "settings.notyf.visual.theme.install.error": "Theme installation failed", diff --git a/src/i18n/ja_JP.jsonc b/src/i18n/ja_JP.jsonc index c13bc722..e57fc75a 100644 --- a/src/i18n/ja_JP.jsonc +++ b/src/i18n/ja_JP.jsonc @@ -64,6 +64,7 @@ "term.sortBy.genre": "ジャンル", "term.sortBy.releaseDate": "配信開始日", "term.sortBy.duration": "時間", + "term.sortBy.dateAdded": "追加日", "term.sortOrder": "並べ替え", "term.sortOrder.ascending": "昇順", "term.sortOrder.descending": "降順", @@ -114,12 +115,17 @@ "term.contributors": "貢献者", "term.equalizer": "イコライザー", "term.reset": "リセット", - "term.tracks": "曲", // Assume x amount of tracks. e.g. 50 tracks + "term.track": { + "one" : "曲", + "other" : "曲" + }, + "term.tracks": "曲", "term.videos": "ビデオ", "term.menu": "メニュー", "term.check": "確認", "term.aboutArtist": "{{artistName}}について", // e.g. 'About Doja Cat' - + "term.version": "バージョン", + // Home "home.title": "ホーム", "home.recentlyPlayed": "最近の再生", @@ -199,6 +205,9 @@ "settings.header.general.description": "Ciderの一般設定", // Language optgroups + "settings.option.general.language.main": "メイン", + "settings.option.general.language.fun": "荒らし", + "settings.option.general.language.unsorted": "未分類", // Update Cider "settings.option.general.updateCider": "Cider 更新", // Button @@ -250,8 +259,11 @@ "settings.option.visual.hardwareAcceleration.description": "アプリを再起動する必要があります", "settings.header.visual.hardwareAcceleration.default": "既定", "settings.header.visual.hardwareAcceleration.webGPU": "WebGPU", + "settings.header.visual.theme": "テーマ", // Settings - Visual - Theme name + "settings.option.visual.theme.default": "Cider", + "settings.option.visual.theme.dark": "ダーク", // Refer to term.disabled for the disabled option "settings.option.visual.showPersonalInfo": "プロフィールを表示", // Toggle @@ -284,7 +296,9 @@ "settings.header.experimental.description": "開発中の実験的な機能は不完全で不安定である可能性があります", "settings.option.experimental.compactUI": "コンパクトインターフェース", // Toggle "settings.option.experimental.close_button_hide": "「閉じる」ボタンでアプリを隠します", // Dropdown - + "settings.option.experimental.copy_log": "ログをコピーする", + "settings.option.experimental.inline_playlists": "プレイリストをインラインで表示", // Toggle + // Refer to term.disabled & term.enabled // Spatialization Menu "spatial.spatialProperties": "空間化のプロパティ", diff --git a/src/i18n/ru_RU.jsonc b/src/i18n/ru_RU.jsonc new file mode 100644 index 00000000..57e3e171 --- /dev/null +++ b/src/i18n/ru_RU.jsonc @@ -0,0 +1,386 @@ +{ // Base File + + // i18n Info + "i18n.languageName": "Русский (RU)", // name of language in native language + "i18n.languageNameEnglish": "Russian (RU)", // name of language in English + "i18n.category": "main", // main = real language, fun = fun community languages + "i18n.authors": "@h0ckerman", // Authors, if you contribute to this file feel free to add your name seperated with a space + + // App info + "app.name": "Cider", + + "date.format": "${d} ${m}, ${y}", + + // Dialogs + "dialog.cancel": "Отмена", + "dialog.ok": "OK", + + // Notification + "notification.updatingLibrarySongs": "Обновление медиатеки песен...", + "notification.updatingLibraryAlbums": "Обновление медиатеки альбомов...", + "notification.updatingLibraryArtists": "Обновление медиатеки артистов...", + + // Terms + "term.appleInc": "Apple Inc.", + "term.appleMusic": "Apple Music", + "term.applePodcasts": "Подкасты Apple", + "term.itunes": "iTunes", + "term.github": "GitHub", + "term.discord": "Discord", + "term.learnMore": "Узнать больше", + "term.accountSettings": "Настройки аккаунта", + "term.logout": "Выйти", + "term.login": "Войти", + "term.about": "О Cider", + "term.privateSession": "Частная сессия", + "term.queue": "Очередь", + "term.history": "История", + "term.search": "Поиск", + "term.library": "Медиатека", // FROM APPLE WEB + "term.listenNow": "Слушайте сейчас", // FROM APPLE MUSIC WEB + "term.browse": "Обзор", // WEB + "term.radio": "Радио", + "term.recentlyAdded": "Недавно добавлено", // FROM APPLE MUSIC WEB + "term.songs": "Песни", + "term.albums": "Альбомы", + "term.artists": "Артисты", + "term.podcasts": "Подкасты", + "term.playlists": "Плейлисты", + "term.playlist": "Плейлист", + "term.newPlaylist": "Новый Плейлист", + "term.newPlaylistFolder": "Новая Папка Плейлиста", + "term.createNewPlaylist": "Создать новый плейлист", // FROM ITUNES + "term.createNewPlaylistFolder": "Создать новую папку", + "term.deletePlaylist": "Вы уверены, что хотите удалить плейлист?", + "term.play": "Воспроизвести", // FROM ITUNES + "term.pause": "Пауза", + "term.previous": "Предыдущий", + "term.next": "Следующий", + "term.shuffle": "Перемешать", + "term.repeat": "Повторять", + "term.volume": "Громкость", + "term.mute": "Заглушить", + "term.unmute": "Включить звук", + "term.share": "Поделиться", + "term.share.success": "Скопировано в буфер обмена", + "term.settings": "Настройки", + "term.seeAll": "Смотреть все", + "term.sortBy": "Сортировать по", + "term.sortBy.album": "Альбом", + "term.sortBy.artist": "Артист", + "term.sortBy.name": "Имя", + "term.sortBy.genre": "Жанр", + "term.sortBy.releaseDate": "Дата выхода", + "term.sortBy.duration": "Длительность", + "term.sortBy.dateAdded": "Дата добавления", + "term.sortOrder": "А-Я", + "term.sortOrder.ascending": "По возрастанию", + "term.sortOrder.descending": "По убыванию", + "term.viewAs": "Показывать", + "term.viewAs.coverArt": "Обложки", + "term.viewAs.list": "Список", + "term.size": "Размер", + "term.size.normal": "Обычный", + "term.size.compact": "Компактный", + "term.enable": "Включить", + "term.disable": "Выключить", + "term.enabled": "Включено", + "term.disabled": "Выключено", + "term.connect": "Подключить", + "term.connecting": "Подключение", + "term.disconnect": "Отключить", + "term.authed": "Авторизовано", + "term.confirm": "Подтвердить?", + "term.more": "Больше", + "term.less": "Меньше", + "term.showMore": "Показать больше", + "term.showLess": "Показать меньше", + "term.topSongs": "Лучшие песни", + "term.latestReleases": "Последние релизы", + "term.time.added": "Добавлено", + "term.time.released": "Релиз: ", // From itunes + "term.time.updated": "Обновлено", + "term.time.hour": { + "one": "час", + "few": "часа", + "other": "часов" + }, + // "term.time.hour": "час", + "term.time.minute": { + "one": "минута", + "few": "минуты", + "other": "минут" + }, + // "term.time.minute": "минута", + "term.time.second": { + "one": "секунд", + "few": "секунды", + "other": "секунд" + }, + // "term.time.second": "секунда", + "term.fullscreenView": "Полноэкранное отображение", + "term.defaultView": "Стандартное отображение", + "term.audioSettings": "Настройки звука", + "term.clearAll": "Очистить", + "term.recentStations": "Вы недавно слушали", // FROM ITUNES + "term.language": "Язык", + "term.funLanguages": "Забавные", + "term.noLyrics": "Загрузка... / Текст песни не найден./ Инструментал.", + "term.copyright": "Авторское право", + "term.rightsReserved": "Все права защищены.", + "term.sponsor": "Поддержать проект", + "term.ciderTeam": "Команда Cider", + "term.developer": "Разработчик", + "term.socialTeam": "Социальная команда", + "term.socials": "Социальные сети", + "term.contributors": "Внесли вклад", + "term.equalizer": "Эквалайзер", + "term.reset": "Сбросить", + "term.tracks": { + "one" : "песня", + "few": "песни", + "other" : "песен" + }, // Assume x amount of tracks. e.g. 50 tracks + "term.videos": "Видео", + "term.menu": "Меню", + "term.check": "Проверить", + "term.aboutArtist": "О {{artistName}}", // e.g. 'About Doja Cat' + "term.topResult": "Топ результатов", // Search Results, FROM ITUNES + "term.sharedPlaylists": "Общие плейлисты", // Search Results NOT SURE + "term.people": "Люди", // Search Results NOT SURE + "term.newpreset.name": "Название нового пресета эквалайзера", // Equalizer Preset + "term.addedpreset": "Пресет добавлен", + "term.deletepreset.warn": "Вы уверены, что хотите удалить пресет?", + "term.deletedpreset": "Пресет удален", + "term.defaultPresets": "Предустановки по умолчанию", + "term.userPresets": "Предустановки пользователя", + "term.requestError": "Возникла проблема с запросом.", + "term.song.link.generate": "Получение ссылки с song.link...", + "term.musicVideos": "Музыкальные видео", // Search page friendlyTypes FROM ITUNES + "term.stations": "Станции", // FROM ITUNES + "term.curators": "Кураторы", // FROM ITUNES + "term.appleCurators": "Кураторы Apple", + "term.radioShows": "Радиошоу", // FROM ITUNES + "term.recordLabels": "Лейблы звукозаписи", + "term.videoExtras": "Ещё", // FROM ITUNES + "term.top": "Top", // NOT SURE WHAT IT IS, NOT TRANSLATED + "term.version": "Версия", + // Home + "home.title": "Главная", + "home.recentlyPlayed": "Недавно прослушанные", + "home.recentlyAdded": "Недавно добавленные", + "home.artistsFeed": "Лента исполнителей", + "home.artistsFeed.noArtist": "Подпишитесь на некоторых артистов, и их последние релизы будут здесь", + "home.madeForYou": "Специально для вас", // FROM APPLE MUSIC WEB + "home.friendsListeningTo": "Что слушают друзья", + "home.followedArtists": "Отслеживаемые артисты", + + // Errors + "error.appleMusicSubRequired": "Для прослушивания Apple Music требуется подписка.", + "error.connectionError": "Возникла проблема с подключением к Apple Music.", + "error.noResults": "Нет результатов.", + "error.noResults.description": "Попробуйте ещё раз.", + + // Podcasts + "podcast.followOnCider": "Отслеживать в Cider", + "podcast.followedOnCider": "Отслеживаемые в Cider", + "podcast.subscribeOnItunes": "Подписаться в iTunes", + "podcast.subscribedOnItunes": "Подписан в iTunes", + "podcast.itunesStore": "iTunes Store", + "podcast.episodes": "Выпуски радиошоу", // FROM APPLE MUSIC WEB + "podcast.playEpisode": "Воспроизвести", + "podcast.website": "Веб-сайт", // FROM ITUNES + + // Actions + "action.addToLibrary": "Добавить в медиатеку", + "action.addToLibrary.success": "Добавлено в медиатеку", + "action.addToLibrary.error": "Ошибка Добавления в медиатику", + "action.removeFromLibrary": "Убрать из медиатеки", + "action.removeFromLibrary.success": "Удалено из медиатеки", + "action.addToQueue": "Воспроизвести далее", // FROM WEB + "action.addToQueue.success": "Добавлено в очередь", + "action.addToQueue.error": "Ошибка добавления в очередь", + "action.removeFromQueue": "Убрать из очереди", + "action.removeFromQueue.success": "Удалено из очереди", + "action.removeFromQueue.error": "Ошибка удаления из очереди", + "action.createPlaylist": "Создать новый плейлист", + "action.addToPlaylist": "Добавить в плейлист", // FROM ITUNES + "action.removeFromPlaylist": "Удалить из плейлиста", + "action.addToFavorites": "Добавить в избранное", + "action.follow": "Отслеживать", + "action.follow.success": "В отслеживаемых", + "action.follow.error": "Ошибка отслеживания", + "action.unfollow": "Отписаться", + "action.unfollow.success": "Отписан", + "action.unfollow.error": "Ошибка отписки", + "action.playNext": "Воспроизвести далее", + "action.playLater": "Воспроизвести позже", + "action.startRadio": "Создать станцию", + "action.goToArtist": "Перейти к артисту", + "action.goToAlbum": "Перейти к альбому", + "action.moveToTop": "Переместить наверх", + "action.share": "Поделиться", + "action.rename": "Переименовать", + "action.love": "Нравится", // FROM WEB + "action.unlove": "Больше не нравится", // FROM WEB + "action.dislike": "Меньше подобных рекомендаций", // FROM WEB + "action.undoDislike": "Вернуть подобные рекомендации", // FROM WEB + "action.showWebRemoteQR": "Веб-интерфейс", + "action.playTracksNext": "Воспроизвести ${app.selectedMediaItems.length} песен следующими", + "action.playTracksLater": "Воспроизвести ${app.selectedMediaItems.length} песен позже", + "action.removeTracks": "Удалить ${self.selectedItems.length} песен из очереди", + "action.import": "Импорт", + "action.export": "Экспорт", + "action.showAlbum": "Показать весь альбом", // FROM WEB + "action.tray.minimize": "Свернуть в трей", + "action.tray.quit": "Выйти", + "action.tray.show": "Показать", + "action.update": "Обновить", + "action.copy": "Скопировать", + "action.newpreset": "Новый пресет...", // Equalizer Preset + "action.deletepreset": "Удалить пресет", + + // Settings - General + "settings.header.general": "Общие", + "settings.header.general.description": "Настройка общих параметров Cider.", + "settings.option.general.language": "Язык", + + // Language optgroups + "settings.option.general.language.main": "Языки", + "settings.option.general.language.fun": "Забавные языки", + "settings.option.general.language.unsorted": "Неотсортированные", + + // Update Cider + "settings.option.general.updateCider": "Обновить Cider", // Button. Refer to term.check for the check button + "settings.option.general.updateCider.branch": "Ветка обновления Cider", // Dropdown + "settings.option.general.updateCider.branch.description": "Выберите ветку обновления Cider", + "settings.option.general.updateCider.branch.main": "Стабильная", + "settings.option.general.updateCider.branch.develop": "Нестабильная", + + // Settings - Audio + "settings.header.audio": "Звук", + "settings.header.audio.description": "Настройка звука Cider.", + "settings.option.audio.quality": "Качество звука", // Dropdown FROM IOS + "settings.header.audio.quality.hireslossless": "Высокое разрешение, без потерь", // FROM IOS + "settings.header.audio.quality.hireslossless.description": "ALAC до 24 бит/192 кГц", // FROM IOS + "settings.header.audio.quality.lossless": "Без потерь", // FROM IOS + "settings.header.audio.quality.lossless.description": "ALAC до 24 бит/48 кГц", // FROM IOS + "settings.header.audio.quality.high": "Высокое качество", // FROM IOS + "settings.header.audio.quality.high.description": "AAC 256 кб/с", // FROM IOS + "settings.header.audio.quality.standard": "Высокая эффективность", // FROM IOS + "settings.header.audio.quality.standard.description": "НЕ-ААС для экономии трафика(64 кб/с)", // FROM IOS + "settings.option.audio.seamlessTransition": "Плавный переход между песнями", // Toggle + "settings.option.audio.enableAdvancedFunctionality": "Включить расширенный функционал", // Toggle + "settings.option.audio.enableAdvancedFunctionality.description": "Включение функции AudioContext позволит использовать расширенные функции звука, такие как нормализация звука, эквалайзеры и визуализаторы, однако в некоторых системах это может вызвать заикание звуковых дорожек.", + "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™️", // Toggle + "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Психоакустические улучшения, благодаря которым все звучит богаче и живее | Разработано Maikiwi.", + "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "CAP не совместим с пространственным звучанием. Пожалуйста, отключите пространственное звучание, чтобы продолжить.", + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "Режим CAP", // Toggle + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "Изменяет режим обработки звука. (Режим агрессии может привести к нежелаемым результатам)", + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "Стандартный", + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "Агрессивный", + "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "Нормализация звука", // Toggle + "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "Нормализует пиковую громкость для отдельных треков, чтобы создать более однородное впечатление от прослушивания.", + "settings.option.audio.enableAdvancedFunctionality.audioSpatialization": "Пространственное звучание", // Toggle + "settings.option.audio.enableAdvancedFunctionality.audioSpatialization.description": "Пространственное аудио; сделать звук более трехмерным (примечание: это не Dolby Atmos)", + "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "Пространственная обработка несовместима с CAP. Пожалуйста, отключите CAP, чтобы продолжить.", + + // Settings - Visual + "settings.header.visual": "Внешний вид", + "settings.header.visual.description": "Настройка внешнего вида Cider.", + "settings.option.visual.windowBackgroundStyle": "Стиль фона Cider", // Toggle + "settings.header.visual.windowBackgroundStyle.none": "Отсутствует", + "settings.header.visual.windowBackgroundStyle.artwork": "Обложка", + "settings.header.visual.windowBackgroundStyle.image": "Изображение", + "settings.option.visual.animatedArtwork": "Анимированная обложка", // Dropdown + "settings.header.visual.animatedArtwork.always": "Всегда", + "settings.header.visual.animatedArtwork.limited": "Ограничено специальными страницами", + "settings.header.visual.animatedArtwork.disable": "Выключено везде", + "settings.option.visual.animatedArtworkQuality": "Качество анимированной обложки", // Dropdown + "settings.header.visual.animatedArtworkQuality.low": "Низкое", + "settings.header.visual.animatedArtworkQuality.medium": "Среднее", + "settings.header.visual.animatedArtworkQuality.high": "Высокое", + "settings.header.visual.animatedArtworkQuality.veryHigh": "Очень высокое", + "settings.header.visual.animatedArtworkQuality.extreme": "Наивысшее", + "settings.option.visual.animatedWindowBackground": "Анимированный фон окна Cider", // Toggle + "settings.option.visual.hardwareAcceleration": "Аппаратное ускорение", // Dropdown + "settings.option.visual.hardwareAcceleration.description": "Требует перезагрузки приложения", + "settings.header.visual.hardwareAcceleration.default": "По умолчанию", + "settings.header.visual.hardwareAcceleration.webGPU": "WebGPU", + "settings.header.visual.theme": "Тема", + "settings.option.visual.theme.github.download": "Установить с GitHub", + "settings.prompt.visual.theme.github.URL": "Введите URL темы, которую хотите установить", + "settings.notyf.visual.theme.install.success": "Тема успешно установлена", + "settings.notyf.visual.theme.install.error": "Не удалось установить тему", + + // Settings - Visual - Theme name + "settings.option.visual.theme.default": "Cider", + "settings.option.visual.theme.dark": "Тёмная", + + // Refer to term.disabled for the disabled option + "settings.option.visual.showPersonalInfo": "Показать личную информацию", // Toggle + + // Settings - Lyrics + "settings.header.lyrics": "Текст песни", + "settings.header.lyrics.description": "Настройка текста песен.", + "settings.option.lyrics.enableMusixmatch": "Получать текст песен из Musixmatch", // Toggle + "settings.option.lyrics.enableMusixmatchKaraoke": "Включить режим караоке (только с Musixmatch)", // Toggle + "settings.option.lyrics.musixmatchPreferredLanguage": "Предпочтительный язык перевода Musixmatch", // Dropdown + "settings.option.lyrics.enableYoutubeLyrics": "Включить Youtube Lyrics для музыкальных клипов", // Toggle + + // Settings - Connectivity + "settings.header.connectivity": "Прочее", + "settings.header.connectivity.description": "Настройка прочих параметров Cider.", + "settings.option.connectivity.discordRPC": "Discord Rich Presence", // Dropdown + "settings.option.connectivity.playbackNotifications": "Уведомления о воспроизведении", // Toggle + // Refer to term.disabled for the disabled option + "settings.header.connectivity.discordRPC.cider": "Отображать как 'Cider'", + "settings.header.connectivity.discordRPC.appleMusic": "Отображать как 'Apple Music'", + "settings.option.connectivity.discordRPC.clearOnPause": "Отключать Discord Rich Presence на паузе", // Toggle + "settings.option.connectivity.lastfmScrobble": "Last.fm cкробблинг", // Option to Connect + "settings.option.connectivity.lastfmScrobble.delay": "Частота скробблинга Last.fm (%)", + "settings.option.connectivity.lastfmScrobble.nowPlaying": "Включить Last.fm Сейчас играет", + "settings.option.connectivity.lastfmScrobble.removeFeatured": "Удалять фит-исполнителей из названия песни (Last.fm)", + "settings.option.connectivity.lastfmScrobble.filterLoop": "Фильтровать зацикленный трек (Last.fm)", + // Refer to term.connect for the connect button + + // Settings - Experimental + "settings.header.experimental": "Экспериментальные", + "settings.header.experimental.description": "Настройка экспериментальных функций Cider.", + "settings.option.experimental.compactUI": "Компактный интерфейс", // Toggle + "settings.option.experimental.close_button_hide": "Кнопка «Закрыть» скрывает приложение", + "settings.option.experimental.copy_log": "Скопировать логи в буфер обмена", + "settings.option.experimental.inline_playlists": "Встроенные плейлисты и альбомы", // NOT SURE + + // Refer to term.disabled & term.enabled + // Spatialization Menu + "spatial.notTurnedOn": "Пространственное аудио отключено. Чтобы использовать, сначала включите его.", + "spatial.spatialProperties": "Настройка пространственного аудио", + "spatial.width": "Ширина", + "spatial.height": "Высота", + "spatial.depth": "Глубина", + "spatial.gain": "Усиление", + "spatial.roomMaterials": "Материалы комнаты", + "spatial.roomDimensions": "Размеры комнаты", + "spatial.roomPositions": "Позиции комнаты", + "spatial.setDimensions": "Установить размеры", + "spatial.setPositions": "Установить позиции", + "spatial.up": "Сверху", + "spatial.front": "Спереди", + "spatial.left": "Слева", + "spatial.right": "Справа", + "spatial.back": "Сзади", + "spatial.down": "Снизу", + "spatial.listener": "Слушатель", + "spatial.audioSource": "Источник звука", + + // Settings - Unfinished + "settings.header.unfinished": "Не завершено", + + // Web Remote + "remote.web.title": "Веб-интерфейс", + "remote.web.description": "Отсканируйте QR-код, чтобы подключить телефон к Cider Remote", + + // About + "about.thanks": "Большое спасибо команде Cider Collective и всем нашим вкладчикам." +} diff --git a/src/i18n/zh_TW.jsonc b/src/i18n/zh_TW.jsonc index c605d61d..d92e0b35 100644 --- a/src/i18n/zh_TW.jsonc +++ b/src/i18n/zh_TW.jsonc @@ -123,11 +123,17 @@ "term.contributors": "貢獻者", "term.equalizer": "等化器", "term.reset": "重設", - "term.tracks": "首歌曲", // Assume x amount of tracks. e.g. 50 tracks + "term.track": { + "one" : "首歌曲", + "other" : "首歌曲" + }, + "term.tracks": "首歌曲", "term.videos": "影片", "term.menu": "選單", "term.check": "檢查", "term.aboutArtist": "關於{{artistName}}", // e.g. 'About Doja Cat' + "term.requestError": "請求發生錯誤。", + "term.song.link.generate": "正在取得 song.link 的分享網址...", "term.version": "版本", // Home @@ -269,6 +275,10 @@ "settings.header.visual.hardwareAcceleration.default": "預設", "settings.header.visual.hardwareAcceleration.webGPU": "WebGPU", "settings.header.visual.theme": "主題", + "settings.option.visual.theme.github.download" : "從 GitHub 網址安裝" , + "settings.prompt.visual.theme.github.URL" : "輸入你要安裝的主題網址" , + "settings.notyf.visual.theme.install.success" : "主題成功安裝" , + "settings.notyf.visual.theme.install.error" : "主題安裝失敗" , // Settings - Visual - Theme name "settings.option.visual.theme.default": "Cider", diff --git a/src/main/base/browserwindow.ts b/src/main/base/browserwindow.ts index f3d151d4..0cd560ee 100644 --- a/src/main/base/browserwindow.ts +++ b/src/main/base/browserwindow.ts @@ -46,6 +46,7 @@ export class BrowserWindow { "pages/about", "pages/library-videos", "pages/remote-pair", + "pages/themes-github", "components/mediaitem-artwork", "components/artwork-material", "components/menu-panel", @@ -217,11 +218,11 @@ export class BrowserWindow { res.send("Stopped") break; case "next": - BrowserWindow.win.webContents.executeJavaScript("MusicKit.getInstance().skipToNextItem()") + BrowserWindow.win.webContents.executeJavaScript("if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.nextPlayableItemIndex);}") res.send("Next") break; case "previous": - BrowserWindow.win.webContents.executeJavaScript("MusicKit.getInstance().skipToPreviousItem()") + BrowserWindow.win.webContents.executeJavaScript("if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex);}") res.send("Previous") break; default: { @@ -430,7 +431,7 @@ export class BrowserWindow { description: themeJson.description || themeDescription, path: themePath, file: theme, - test: join(themePath, "theme.json") + github_repo: themeJson.github_repo || "" }); } else { themeObjects.push({ @@ -438,7 +439,7 @@ export class BrowserWindow { description: themeDescription, path: themePath, file: theme, - test: join(themePath, "theme.json") + github_repo: "" }); } } diff --git a/src/main/base/wsapi.ts b/src/main/base/wsapi.ts index 95d2b521..619a0ac6 100644 --- a/src/main/base/wsapi.ts +++ b/src/main/base/wsapi.ts @@ -182,11 +182,12 @@ export class wsapi { response.message = "Unmuted"; break; case "next": - this._win.webContents.executeJavaScript(`MusicKit.getInstance().skipToNextItem()`); + this._win.webContents.executeJavaScript(`if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null) { + MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.nextPlayableItemIndex);}`); response.message = "Next"; break; case "previous": - this._win.webContents.executeJavaScript(`MusicKit.getInstance().skipToPreviousItem()`); + this._win.webContents.executeJavaScript(`if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex)}`); response.message = "Previous"; break; case "musickit-api": diff --git a/src/main/plugins/discordrpc.ts b/src/main/plugins/discordrpc.ts index 49bb35df..3b11557e 100644 --- a/src/main/plugins/discordrpc.ts +++ b/src/main/plugins/discordrpc.ts @@ -9,6 +9,7 @@ export default class DiscordRichPresence { */ private static _store: any; private _app : any; + private _attributes : any; private static _connection: boolean = false; /** @@ -148,7 +149,7 @@ export default class DiscordRichPresence { state: `${attributes.artistName ? `by ${attributes.artistName}` : ''}`, startTimestamp: attributes.startTime, endTimestamp: attributes.endTime, - largeImageKey: attributes.artwork.url.replace('{w}', '1024').replace('{h}', '1024'), + largeImageKey: attributes?.artwork?.url?.replace('{w}', '1024').replace('{h}', '1024'), largeImageText: attributes.albumName, instance: false, // Whether the activity is in a game session buttons: [ @@ -217,9 +218,8 @@ export default class DiscordRichPresence { }) .then(res => res.json()) .then(function(json){ - self._activity['largeImageKey'] = json.url - console.log(json.url) - self._client.setActivity(self._activity); + self._attributes["artwork"]["url"] = json.url + self.updateActivity(self._attributes) }) }) @@ -237,6 +237,7 @@ export default class DiscordRichPresence { * @param attributes Music Attributes (attributes.status = current state) */ onPlaybackStateDidChange(attributes: object): void { + this._attributes = attributes this.updateActivity(attributes) } @@ -245,6 +246,7 @@ export default class DiscordRichPresence { * @param attributes Music Attributes */ onNowPlayingItemDidChange(attributes: object): void { + this._attributes = attributes this.updateActivity(attributes) } } diff --git a/src/main/plugins/lastfm.ts b/src/main/plugins/lastfm.ts index 10440584..6110ad31 100644 --- a/src/main/plugins/lastfm.ts +++ b/src/main/plugins/lastfm.ts @@ -81,7 +81,7 @@ export default class LastFMPlugin { private scrobbleSong(attributes: any) { if(this._timer) clearTimeout(this._timer); var self = this; - this._timer = setTimeout(() => { + this._timer = setTimeout(async () => { const currentAttributes = attributes; if (!self._lastfm || self._lastfm.cachedAttributes === attributes) { @@ -92,15 +92,17 @@ export default class LastFMPlugin { if (self._lastfm.cachedAttributes.playParams.id === attributes.playParams.id) return; } + const artist = await this.getPrimaryArtist(attributes) + if (currentAttributes.status && currentAttributes === attributes) { if (fs.existsSync(this.sessionPath)) { // Scrobble playing song. if (attributes.status === true) { self._lastfm.track.scrobble({ - 'artist': this.filterArtistName(attributes.artistName), + 'artist': artist, 'track': attributes.name, 'album': attributes.albumName, - 'albumArtist': self.filterArtistName(attributes.artistName), + 'albumArtist': artist, 'timestamp': new Date().getTime() / 1000 }, function (err: any, scrobbled: any) { if (err) { @@ -115,29 +117,11 @@ export default class LastFMPlugin { self.authenticate(); } } else { - return console.log('[LastFM] Did not add ', attributes.name, '—', self.filterArtistName(attributes.artistName), 'because now playing a other song.'); + return console.log('[LastFM] Did not add ', attributes.name, '—', artist, 'because now playing a other song.'); }},Math.round(attributes.durationInMillis * (self._store.lastfm.scrobble_after / 100))); } - private filterArtistName(artist: any) { - if (!this._store.lastfm.enabledRemoveFeaturingArtists) return artist; - - artist = artist.split(' '); - if (artist.includes('&')) { - artist.length = artist.indexOf('&'); - } - if (artist.includes('and')) { - artist.length = artist.indexOf('and'); - } - artist = artist.join(' '); - if (artist.includes(',')) { - artist = artist.split(',') - artist = artist[0] - } - return artist.charAt(0).toUpperCase() + artist.slice(1); - } - - private updateNowPlayingSong(attributes: any) { + private async updateNowPlayingSong(attributes: any) { if (!this._lastfm || this._lastfm.cachedNowPlayingAttributes === attributes || !this._store.lastfm.NowPlaying) { return } @@ -147,13 +131,15 @@ export default class LastFMPlugin { } if (fs.existsSync(this.sessionPath)) { + const artist = await this.getPrimaryArtist(attributes) + // update Now Playing if (attributes.status === true) { this._lastfm.track.updateNowPlaying({ - 'artist': this.filterArtistName(attributes.artistName), + 'artist': artist, 'track': attributes.name, 'album': attributes.albumName, - 'albumArtist': this.filterArtistName(attributes.artistName) + 'albumArtist': artist }, function (err: any, nowPlaying: any) { if (err) { return console.error('[LastFM] An error occurred while updating nowPlayingSong', err); @@ -169,6 +155,40 @@ export default class LastFMPlugin { } } + private async getPrimaryArtist (attributes: any) { + const songId = attributes.playParams.catalogId || attributes.playParams.id + + if (!this._store.lastfm.enabledRemoveFeaturingArtists || !songId) return attributes.artistName; + + const res = await this._win.webContents.executeJavaScript(` + (async () => { + const subMk = await MusicKit.getInstance().api.v3.music("/v1/catalog/" + MusicKit.getInstance().storefrontId + "/songs/${songId}", { + include: { + songs: ["artists"] + } + }) + if (!subMk) console.error('[LastFM] Request failed: /v1/catalog/us/songs/${songId}') + return subMk.data + })() + `).catch(console.error) + if (!res) return attributes.artistName + + const data = res.data + if (!data.length) { + console.error(`[LastFM] Unable to locate song with id of ${songId}`) + return attributes.artistName; + } + + const artists = res.data[0].relationships.artists.data + if (!artists.length) { + console.error(`[LastFM] Unable to find artists related to the song with id of ${songId}`) + return attributes.artistName; + } + + const primaryArtist = artists[0] + return primaryArtist.attributes.name + } + /** * Base Plugin Details (Eventually implemented into a GUI in settings) */ @@ -253,4 +273,4 @@ export default class LastFMPlugin { this.scrobbleSong(attributes) } -} \ No newline at end of file +} diff --git a/src/preload/cider-preload.js b/src/preload/cider-preload.js index f6317878..bf6f2924 100644 --- a/src/preload/cider-preload.js +++ b/src/preload/cider-preload.js @@ -23,7 +23,7 @@ const MusicKitInterop = { /** wsapi */ MusicKit.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, async () => { - await MusicKitInterop.modifyNamesOnLocale(); + // await MusicKitInterop.modifyNamesOnLocale(); if (MusicKitInterop.filterTrack(MusicKitInterop.getAttributes(), false, true) || !app.cfg.lastfm.filterLoop) { global.ipcRenderer.send('nowPlayingItemDidChange', MusicKitInterop.getAttributes()); } @@ -127,11 +127,14 @@ const MusicKitInterop = { }, next: () => { - MusicKit.getInstance().skipToNextItem().then(r => console.log(`[MusicKitInterop.next] Skipping to Next ${r}`)); + if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null) + MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.nextPlayableItemIndex); + // MusicKit.getInstance().skipToNextItem().then(r => console.log(`[MusicKitInterop.next] Skipping to Next ${r}`)); }, previous: () => { - MusicKit.getInstance().skipToPreviousItem().then(r => console.log(`[MusicKitInterop.previous] Skipping to Previous ${r}`)); + if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) + MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex); } } diff --git a/src/renderer/audio/audio.js b/src/renderer/audio/audio.js index f195e0c8..8dbaccac 100644 --- a/src/renderer/audio/audio.js +++ b/src/renderer/audio/audio.js @@ -6,7 +6,6 @@ var CiderAudio = { spatialNode : null, spatialInput: null, audioBands : null, - preampNode : null, vibrantbassNode: null, llpw: null, llpwEnabled: null @@ -30,7 +29,6 @@ var CiderAudio = { CiderAudio.audioNodes.gainNode.disconnect(); } catch(e){} try{ CiderAudio.audioNodes.spatialNode.disconnect();} catch(e){} try{ - // CiderAudio.audioNodes.preampNode.disconnect(); for (var i of CiderAudio.audioNodes.llpw){ i.disconnect(); } @@ -47,8 +45,9 @@ var CiderAudio = { spatialNode : null, spatialInput: null, audioBands : null, - preampNode : null, vibrantbassNode: null, + llpw: null, + llpwEnabled: null } } catch (e) {} CiderAudio.source.connect(CiderAudio.context.destination);} catch(e){} @@ -76,10 +75,8 @@ var CiderAudio = { CiderAudio.audioNodes.gainNode.gain.setTargetAtTime(1, CiderAudio.context.currentTime+ 1, 0.5); }, spatialOn: function (){ - try{ - CiderAudio.audioNodes.gainNode.disconnect(CiderAudio.context.destination);} catch(e){} CiderAudio.audioNodes.spatialNode = new ResonanceAudio(CiderAudio.context); - CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.context.destination); + //CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.context.destination); let roomDimensions = { width: 32, height: 12, @@ -96,13 +93,10 @@ var CiderAudio = { }; CiderAudio.audioNodes.spatialNode.setRoomProperties(roomDimensions, roomMaterials); CiderAudio.audioNodes.spatialInput = CiderAudio.audioNodes.spatialNode.createSource(); - CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.spatialInput.input); + CiderAudio.hierarchical_loading(); }, spatialOff: function (){ - try{ - CiderAudio.audioNodes.spatialNode.output.disconnect(CiderAudio.context.destination); - CiderAudio.audioNodes.gainNode.disconnect(CiderAudio.audioNodes.spatialInput.input);} catch(e){} - CiderAudio.audioNodes.gainNode.connect(CiderAudio.context.destination); + CiderAudio.hierarchical_loading(); }, sendAudio: function (){ var options = { @@ -119,17 +113,119 @@ var CiderAudio = { ); } }, - equalizer: function (){ - let BANDS = app.cfg.audio.equalizer.frequencies; - let GAIN = app.cfg.audio.equalizer.gain; - let Q = app.cfg.audio.equalizer.Q; - let VIBRANTBASSBANDS = app.cfg.audio.vibrantBass.frequencies; - let VIBRANTBASSGAIN = app.cfg.audio.vibrantBass.gain; - let VIBRANTBASSQ = app.cfg.audio.vibrantBass.Q; + llpw_h2_2: function (status, hierarchy){ + if (status === true) { let LLPW_Q = [5, 1, 3.536, 1.25, 8.409, 1.25, 14.14, 7.071, 5, 0.625, 16.82, 20, 20, 20, 28.28, 28.28, 28.28, 20, 33.64, 33.64, 10, 28.28, 7.071, 3.856]; let LLPW_GAIN = [0.38, -1.81, -0.23, -0.51, 0.4, 0.84, 0.36, -0.34, 0.27, -1.2, -0.42, -0.67, 0.81, 1.31, -0.71, 0.68, -1.04, 0.79, -0.73, -1.33, 1.17, 0.57, 0.35, 6.33]; let LLPW_FREQUENCIES = [16.452, 24.636, 37.134, 74.483, 159.54, 308.18, 670.21, 915.81, 1200.7, 2766.4, 2930.6, 4050.6, 4409.1, 5395.2, 5901.6, 6455.5, 7164.1, 7724.1, 8449, 10573, 12368, 14198, 17910, 18916]; - CiderAudio.audioNodes.audioBands = []; CiderAudio.audioNodes.vibrantbassNode = []; CiderAudio.audioNodes.llpw = []; + CiderAudio.audioNodes.llpw = [] + + for (i = 0; i < LLPW_FREQUENCIES.length; i++) { + CiderAudio.audioNodes.llpw[i] = CiderAudio.context.createBiquadFilter(); + CiderAudio.audioNodes.llpw[i].type = 'peaking'; // 'peaking'; + CiderAudio.audioNodes.llpw[i].frequency.value = LLPW_FREQUENCIES[i]; + CiderAudio.audioNodes.llpw[i].Q.value = LLPW_Q[i]; + CiderAudio.audioNodes.llpw[i].gain.value = LLPW_GAIN[i] * app.cfg.audio.ciderPPE_value; + } + + for (i = 1; i < LLPW_FREQUENCIES.length; i ++) { + CiderAudio.audioNodes.llpw[i-1].connect(CiderAudio.audioNodes.llpw[i]); + } + if (hierarchy === 2) { + try{ + CiderAudio.audioNodes.llpw[LLPW_FREQUENCIES.length-1].connect(CiderAudio.audioNodes.vibrantbassNode[0]);} catch(e){}} + + else if (hierarchy === 1) { + try{ + CiderAudio.audioNodes.llpw[LLPW_FREQUENCIES.length-1].connect(CiderAudio.audioNodes.audioBands[0]);} catch(e){}} + } + + }, + vibrantbass_h2_1: function (status){ + if (status === true) { + let VIBRANTBASSBANDS = app.cfg.audio.vibrantBass.frequencies; + let VIBRANTBASSGAIN = app.cfg.audio.vibrantBass.gain; + let VIBRANTBASSQ = app.cfg.audio.vibrantBass.Q; + CiderAudio.audioNodes.vibrantbassNode = [] + + for (i = 0; i < VIBRANTBASSBANDS.length; i++) { + CiderAudio.audioNodes.vibrantbassNode[i] = CiderAudio.context.createBiquadFilter(); + CiderAudio.audioNodes.vibrantbassNode[i].type = 'peaking'; // 'peaking'; + CiderAudio.audioNodes.vibrantbassNode[i].frequency.value = VIBRANTBASSBANDS[i]; + CiderAudio.audioNodes.vibrantbassNode[i].Q.value = VIBRANTBASSQ[i]; + CiderAudio.audioNodes.vibrantbassNode[i].gain.value = VIBRANTBASSGAIN[i] * app.cfg.audio.vibrantBass.multiplier; + } + + for (i = 1; i < VIBRANTBASSBANDS.length; i ++) { + CiderAudio.audioNodes.vibrantbassNode[i-1].connect(CiderAudio.audioNodes.vibrantbassNode[i]); + } + + CiderAudio.audioNodes.vibrantbassNode[VIBRANTBASSBANDS.length-1].connect(CiderAudio.audioNodes.audioBands[0]); + } + + }, + hierarchical_unloading: function (){ + try {CiderAudio.audioNodes.spatialNode.output.disconnect();} catch(e){} + try {CiderAudio.audioNodes.gainNode.disconnect();} catch(e){} + try {for (var i of CiderAudio.audioNodes.llpw){i.disconnect();} CiderAudio.audioNodes.llpw = []} catch(e){} + try {for (var i of CiderAudio.audioNodes.vibrantbassNode){i.disconnect();} CiderAudio.audioNodes.vibrantbassNode = []} catch(e){} + + console.log("[Cider][Audio] Finished hierarchical unloading"); + + }, + hierarchical_loading: function (){ + CiderAudio.hierarchical_unloading(); + if (app.cfg.audio.vibrantBass.multiplier !== 0) { // If vibrant bass is enabled + if (app.cfg.advanced.ciderPPE) { // If CAP & vibrant bass is enabled + CiderAudio.vibrantbass_h2_1(true) + if (app.cfg.audio.spatial) { + app.cfg.advanced.ciderPPE = false; + notyf.error(app.getLz('settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility')); + CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.spatialInput.input); + CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.audioNodes.vibrantbassNode[0]); + } + else {CiderAudio.llpw_h2_2(true, 2); CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.llpw[0]);} + } + else { // If only vibrant bass is enabled + CiderAudio.vibrantbass_h2_1(true) + //CiderAudio.llpw_h2_2(false, 0) + if (app.cfg.audio.spatial) { + CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.spatialInput.input); + CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.audioNodes.vibrantbassNode[0]);} + else {CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.vibrantbassNode[0]);} + } + } + else { // If vibrant bass is disabled + if (app.cfg.advanced.ciderPPE) { // If CAP is enabled & vibrant bass is disabled + //CiderAudio.vibrantbass_h2_1(false) + if (app.cfg.audio.spatial) { + app.cfg.advanced.ciderPPE = false; + notyf.error(app.getLz('settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility')); + CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.spatialInput.input); + CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.audioNodes.audioBands[0]); + } + else {CiderAudio.llpw_h2_2(true, 1); CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.llpw[0]);} + } + else { // If CAP & vibrant bass is disabled + //CiderAudio.vibrantbass_h2_1(false) + //CiderAudio.llpw_h2_2(false, 0) + if (app.cfg.audio.spatial) { + CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.spatialInput.input); + CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.audioNodes.audioBands[0]);} + else {CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.audioBands[0]);} + } + } + + console.log("[Cider][Audio] Finished hierarchical loading"); + + }, + + equalizer: function (){ // h1_1 + let BANDS = app.cfg.audio.equalizer.frequencies; + let GAIN = app.cfg.audio.equalizer.gain; + let Q = app.cfg.audio.equalizer.Q; + + CiderAudio.audioNodes.audioBands = []; for (i = 0; i < BANDS.length; i++) { CiderAudio.audioNodes.audioBands[i] = CiderAudio.context.createBiquadFilter(); @@ -139,50 +235,8 @@ var CiderAudio = { CiderAudio.audioNodes.audioBands[i].gain.value = GAIN[i] * app.cfg.audio.equalizer.mix; } - for (i = 0; i < LLPW_FREQUENCIES.length; i++) { - CiderAudio.audioNodes.llpw[i] = CiderAudio.context.createBiquadFilter(); - CiderAudio.audioNodes.llpw[i].type = 'peaking'; // 'peaking'; - CiderAudio.audioNodes.llpw[i].frequency.value = LLPW_FREQUENCIES[i]; - CiderAudio.audioNodes.llpw[i].Q.value = LLPW_Q[i]; - CiderAudio.audioNodes.llpw[i].gain.value = LLPW_GAIN[i] * app.cfg.audio.ciderPPE_value * CiderAudio.audioNodes.llpwEnabled; - } - /** - CiderAudio.audioNodes.preampNode = CiderAudio.context.createBiquadFilter(); - CiderAudio.audioNodes.preampNode.type = 'highshelf'; - CiderAudio.audioNodes.preampNode.frequency.value = 0; // Passthrough - CiderAudio.audioNodes.preampNode.gain.value = 0; - /** if (CiderAudio.audioNodes.llpwEnabled === 1) {CiderAudio.audioNodes.preampNode.gain.value = (-Math.max(...app.cfg.audio.equalizer.gain.concat(CiderAudio.audioNodes.llpw[23].gain.value)));} - else {CiderAudio.audioNodes.preampNode.gain.value = (-Math.max(...app.cfg.audio.equalizer.gain));}*/ - - for (i = 0; i < VIBRANTBASSBANDS.length; i++) { - CiderAudio.audioNodes.vibrantbassNode[i] = CiderAudio.context.createBiquadFilter(); - CiderAudio.audioNodes.vibrantbassNode[i].type = 'peaking'; // 'peaking'; - CiderAudio.audioNodes.vibrantbassNode[i].frequency.value = VIBRANTBASSBANDS[i]; - CiderAudio.audioNodes.vibrantbassNode[i].Q.value = VIBRANTBASSQ[i]; - CiderAudio.audioNodes.vibrantbassNode[i].gain.value = VIBRANTBASSGAIN[i] * app.cfg.audio.vibrantBass.multiplier;} - - - if (app.cfg.audio.spatial) { - try{ - CiderAudio.audioNodes.spatialNode.output.disconnect(CiderAudio.context.destination); } catch(e){} - CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.audioNodes.llpw[0]); - } else { - try{ - CiderAudio.audioNodes.gainNode.disconnect(CiderAudio.context.destination);} catch(e){} - CiderAudio.audioNodes.gainNode.connect(CiderAudio.audioNodes.llpw[0]); - } - - // CiderAudio.audioNodes.preampNode.connect(CiderAudio.audioNodes.llpw[0]); - - for (i = 1; i < LLPW_FREQUENCIES.length; i ++) { - CiderAudio.audioNodes.llpw[i-1].connect(CiderAudio.audioNodes.llpw[i]); - } - CiderAudio.audioNodes.llpw[LLPW_FREQUENCIES.length-1].connect(CiderAudio.audioNodes.vibrantbassNode[0]); - for (i = 1; i < VIBRANTBASSBANDS.length; i ++) { - CiderAudio.audioNodes.vibrantbassNode[i-1].connect(CiderAudio.audioNodes.vibrantbassNode[i]); - } - CiderAudio.audioNodes.vibrantbassNode[VIBRANTBASSBANDS.length-1].connect(CiderAudio.audioNodes.audioBands[0]); - + // Dynamic-ish loading + CiderAudio.hierarchical_loading(); for (i = 1; i < BANDS.length; i ++) { CiderAudio.audioNodes.audioBands[i-1].connect(CiderAudio.audioNodes.audioBands[i]); diff --git a/src/renderer/index.js b/src/renderer/index.js index 004b1bf4..4c77fe4f 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -597,6 +597,8 @@ const app = new Vue({ } catch (err) { } + this.mk._bag.features['seamless-audio-transitions'] = this.cfg.audio.seamless_audio + // API Fallback if (!this.chrome.userinfo) { this.chrome.userinfo = { @@ -782,7 +784,6 @@ const app = new Vue({ // app.getNowPlayingArtwork(42); app.getNowPlayingArtworkBG(32); app.loadLyrics(); - app.losslessBadge(); // Playback Notifications if (this.cfg.general.playbackNotifications && !document.hasFocus() && a.artistName && a.artwork && a.name) { @@ -1348,7 +1349,7 @@ const app = new Vue({ app.mk.seekToTime(0); } else { app.prevButtonBackIndicator = false; - app.mk.skipToPreviousItem() + app.skipToPreviousItem() } }, async getNowPlayingItemDetailed(target) { @@ -2346,25 +2347,6 @@ const app = new Vue({ }) notyf.success(app.getLz('action.removeFromLibrary.success')) }, - - async losslessBadge() { - const songID = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem["_songId"] ?? (this.mk.nowPlayingItem["songId"] ?? -1) : -1; - if (app.cfg.advanced.ciderPPE && songID != -1) { - /**let extendedAssets = await app.mk.api.song(songID, {extend : 'extendedAssetUrls'}) - if (extendedAssets.attributes.audioTraits.includes('lossless')) {*/ - app.mk.nowPlayingItem['attributes']['lossless'] = true - CiderAudio.audioNodes.llpwEnabled = 1 - console.log("[Cider][Enhanced] Audio being processed by PPE") - /**} - else { - CiderAudio.audioNodes.llpwEnabled = 0 - } */ - } - - else { - CiderAudio.audioNodes.llpwEnabled = 0 - } - }, async loadYTLyrics() { const track = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.title ?? '' : ''; const artist = (this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.artistName ?? '' : ''; @@ -2801,17 +2783,17 @@ const app = new Vue({ }) }) } else { - this.mk.clearQueue().then(function (_) { - if (app.mk.shuffleMode == 1) { - shuffleArray(query) - } - app.mk.queue.append(query) - if (childIndex != -1) { - app.mk.changeToMediaAtIndex(childIndex) - } else { - app.mk.play() - } - }) + app.mk.queue.splice(0, app.mk.queue._itemIDs.length) + if (app.mk.shuffleMode == 1) { + shuffleArray(query) + } + app.mk.queue.append(query) + if (childIndex != -1) { + app.mk.changeToMediaAtIndex(childIndex) + } else { + app.mk.play() + } + } }) } else if (parent.startsWith('listitem-hr')) { @@ -2821,7 +2803,7 @@ const app = new Vue({ [item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id }).then(function () { app.mk.play().then(() => { - const data = JSON.parse(parent.split('listitem-hr')[1] ?? '[]') + let data = JSON.parse(parent.split('listitem-hr')[1] ?? '[]') let itemsToPlay = {} let u = data.map(x => x.id) try { @@ -2846,9 +2828,8 @@ const app = new Vue({ }) }) } else { - const data = JSON.parse(parent.split('listitem-hr')[1] ?? '[]') + let data = JSON.parse(parent.split('listitem-hr')[1] ?? '[]') let itemsToPlay = {} - let u = data.map(x => x.id) data.forEach(item => { if (!itemsToPlay[item.kind]) { itemsToPlay[item.kind] = [] @@ -2856,20 +2837,30 @@ const app = new Vue({ itemsToPlay[item.kind].push(item.id) }) // loop through itemsToPlay + app.mk.queue.splice(0, app.mk.queue._itemIDs.length) let ind = 0; for (let kind in itemsToPlay) { let ids = itemsToPlay[kind] - app.mk.clearQueue().then(function () { - if (ids.length > 0) { - app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] }).then(function() { + if (ids.length > 0) { + if (app.mk.queue._itemIDs.length > 0) { + app.mk.playLater({ [kind + "s"]: itemsToPlay[kind] }).then(function () { + ind += 1; + console.log(ind, Object.keys(itemsToPlay).length) + if (ind >= Object.keys(itemsToPlay).length) { + app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.attributes.playParams.id ?? item.id)) + } + } + )} else { + app.mk.setQueue({ [kind + "s"]: itemsToPlay[kind] }).then(function () { ind += 1; - console.log(ind , Object.keys(itemsToPlay).length) - if(ind >= Object.keys(itemsToPlay).length) { + console.log(ind, Object.keys(itemsToPlay).length) + if (ind >= Object.keys(itemsToPlay).length) { app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.attributes.playParams.id ?? item.id)) } - } - )} - }) + } + )} + } + } } }) @@ -3729,6 +3720,16 @@ const app = new Vue({ if (sellang.startsWith("en") && this.mk.storefrontId != "us") sellang = "en-gb" return await sellang } + }, + skipToNextItem(){ + // app.mk.skipToNextItem() is buggy somehow so use this + if (this.mk.queue.nextPlayableItemIndex != -1 && this.mk.queue.nextPlayableItemIndex != null) + this.mk.changeToMediaAtIndex(this.mk.queue.nextPlayableItemIndex); + }, + skipToPreviousItem(){ + // app.mk.skipToPreviousItem() is buggy somehow so use this + if (this.mk.queue.previousPlayableItemIndex != -1 && this.mk.queue.previousPlayableItemIndex != null) + this.mk.changeToMediaAtIndex(this.mk.queue.previousPlayableItemIndex); } } }) @@ -4002,6 +4003,12 @@ webGPU().then() let screenWidth = screen.width; let screenHeight = screen.height; +window.onerror = function(error) { + console.log(error) + bootbox.alert("Error occured: " + error) +}; + + // Key bind to unjam MusicKit in case it fails: CTRL+F10 document.addEventListener('keydown', function (event) { if (event.ctrlKey && event.keyCode == 121) { diff --git a/src/renderer/js/showdown.min.js b/src/renderer/js/showdown.min.js new file mode 100644 index 00000000..7ed96f26 --- /dev/null +++ b/src/renderer/js/showdown.min.js @@ -0,0 +1,5531 @@ +;/*! showdown v 2.0.0-alpha1 - 14-11-2021 */ +(function(){ + /** + * Created by Tivie on 13-07-2015. + */ + + function getDefaultOpts (simple) { + 'use strict'; + + var defaultOptions = { + omitExtraWLInCodeBlocks: { + defaultValue: false, + describe: 'Omit the default extra whiteline added to code blocks', + type: 'boolean' + }, + noHeaderId: { + defaultValue: false, + describe: 'Turn on/off generated header id', + type: 'boolean' + }, + prefixHeaderId: { + defaultValue: false, + describe: 'Add a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to true will add a generic \'section-\' prefix', + type: 'string' + }, + rawPrefixHeaderId: { + defaultValue: false, + describe: 'Setting this option to true will prevent showdown from modifying the prefix. This might result in malformed IDs (if, for instance, the " char is used in the prefix)', + type: 'boolean' + }, + ghCompatibleHeaderId: { + defaultValue: false, + describe: 'Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)', + type: 'boolean' + }, + rawHeaderId: { + defaultValue: false, + describe: 'Remove only spaces, \' and " from generated header ids (including prefixes), replacing them with dashes (-). WARNING: This might result in malformed ids', + type: 'boolean' + }, + headerLevelStart: { + defaultValue: false, + describe: 'The header blocks level start', + type: 'integer' + }, + parseImgDimensions: { + defaultValue: false, + describe: 'Turn on/off image dimension parsing', + type: 'boolean' + }, + simplifiedAutoLink: { + defaultValue: false, + describe: 'Turn on/off GFM autolink style', + type: 'boolean' + }, + literalMidWordUnderscores: { + defaultValue: false, + describe: 'Parse midword underscores as literal underscores', + type: 'boolean' + }, + literalMidWordAsterisks: { + defaultValue: false, + describe: 'Parse midword asterisks as literal asterisks', + type: 'boolean' + }, + strikethrough: { + defaultValue: false, + describe: 'Turn on/off strikethrough support', + type: 'boolean' + }, + tables: { + defaultValue: false, + describe: 'Turn on/off tables support', + type: 'boolean' + }, + tablesHeaderId: { + defaultValue: false, + describe: 'Add an id to table headers', + type: 'boolean' + }, + ghCodeBlocks: { + defaultValue: true, + describe: 'Turn on/off GFM fenced code blocks support', + type: 'boolean' + }, + tasklists: { + defaultValue: false, + describe: 'Turn on/off GFM tasklist support', + type: 'boolean' + }, + smoothLivePreview: { + defaultValue: false, + describe: 'Prevents weird effects in live previews due to incomplete input', + type: 'boolean' + }, + smartIndentationFix: { + defaultValue: false, + description: 'Tries to smartly fix indentation in es6 strings', + type: 'boolean' + }, + disableForced4SpacesIndentedSublists: { + defaultValue: false, + description: 'Disables the requirement of indenting nested sublists by 4 spaces', + type: 'boolean' + }, + simpleLineBreaks: { + defaultValue: false, + description: 'Parses simple line breaks as
(GFM Style)', + type: 'boolean' + }, + requireSpaceBeforeHeadingText: { + defaultValue: false, + description: 'Makes adding a space between `#` and the header text mandatory (GFM Style)', + type: 'boolean' + }, + ghMentions: { + defaultValue: false, + description: 'Enables github @mentions', + type: 'boolean' + }, + ghMentionsLink: { + defaultValue: 'https://github.com/{u}', + description: 'Changes the link generated by @mentions. Only applies if ghMentions option is enabled.', + type: 'string' + }, + encodeEmails: { + defaultValue: true, + description: 'Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities', + type: 'boolean' + }, + openLinksInNewWindow: { + defaultValue: false, + description: 'Open all links in new windows', + type: 'boolean' + }, + backslashEscapesHTMLTags: { + defaultValue: false, + description: 'Support for HTML Tag escaping. ex: \
foo\
', + type: 'boolean' + }, + emoji: { + defaultValue: false, + description: 'Enable emoji support. Ex: `this is a :smile: emoji`', + type: 'boolean' + }, + underline: { + defaultValue: false, + description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `` and ``', + type: 'boolean' + }, + ellipsis: { + defaultValue: true, + description: 'Replaces three dots with the ellipsis unicode character', + type: 'boolean' + }, + completeHTMLDocument: { + defaultValue: false, + description: 'Outputs a complete html document, including ``, `` and `` tags', + type: 'boolean' + }, + metadata: { + defaultValue: false, + description: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).', + type: 'boolean' + }, + splitAdjacentBlockquotes: { + defaultValue: false, + description: 'Split adjacent blockquote blocks', + type: 'boolean' + }, + relativePathBaseUrl: { + defaultValue: false, + describe: 'Prepends a base URL to relative paths', + type: 'string' + }, + }; + if (simple === false) { + return JSON.parse(JSON.stringify(defaultOptions)); + } + var ret = {}; + for (var opt in defaultOptions) { + if (defaultOptions.hasOwnProperty(opt)) { + ret[opt] = defaultOptions[opt].defaultValue; + } + } + return ret; + } + + function allOptionsOn () { + 'use strict'; + var options = getDefaultOpts(true), + ret = {}; + for (var opt in options) { + if (options.hasOwnProperty(opt)) { + ret[opt] = true; + } + } + return ret; + } + + /** + * Created by Tivie on 06-01-2015. + */ +// Private properties + var showdown = {}, + parsers = {}, + extensions = {}, + globalOptions = getDefaultOpts(true), + setFlavor = 'vanilla', + flavor = { + github: { + omitExtraWLInCodeBlocks: true, + simplifiedAutoLink: true, + literalMidWordUnderscores: true, + strikethrough: true, + tables: true, + tablesHeaderId: true, + ghCodeBlocks: true, + tasklists: true, + disableForced4SpacesIndentedSublists: true, + simpleLineBreaks: true, + requireSpaceBeforeHeadingText: true, + ghCompatibleHeaderId: true, + ghMentions: true, + backslashEscapesHTMLTags: true, + emoji: true, + splitAdjacentBlockquotes: true + }, + original: { + noHeaderId: true, + ghCodeBlocks: false + }, + ghost: { + omitExtraWLInCodeBlocks: true, + parseImgDimensions: true, + simplifiedAutoLink: true, + literalMidWordUnderscores: true, + strikethrough: true, + tables: true, + tablesHeaderId: true, + ghCodeBlocks: true, + tasklists: true, + smoothLivePreview: true, + simpleLineBreaks: true, + requireSpaceBeforeHeadingText: true, + ghMentions: false, + encodeEmails: true + }, + vanilla: getDefaultOpts(true), + allOn: allOptionsOn() + }; + + /** + * helper namespace + * @type {{}} + */ + showdown.helper = {}; + + /** + * TODO LEGACY SUPPORT CODE + * @type {{}} + */ + showdown.extensions = {}; + + /** + * Set a global option + * @static + * @param {string} key + * @param {*} value + * @returns {showdown} + */ + showdown.setOption = function (key, value) { + 'use strict'; + globalOptions[key] = value; + return this; + }; + + /** + * Get a global option + * @static + * @param {string} key + * @returns {*} + */ + showdown.getOption = function (key) { + 'use strict'; + return globalOptions[key]; + }; + + /** + * Get the global options + * @static + * @returns {{}} + */ + showdown.getOptions = function () { + 'use strict'; + return globalOptions; + }; + + /** + * Reset global options to the default values + * @static + */ + showdown.resetOptions = function () { + 'use strict'; + globalOptions = getDefaultOpts(true); + }; + + /** + * Set the flavor showdown should use as default + * @param {string} name + */ + showdown.setFlavor = function (name) { + 'use strict'; + if (!flavor.hasOwnProperty(name)) { + throw Error(name + ' flavor was not found'); + } + showdown.resetOptions(); + var preset = flavor[name]; + setFlavor = name; + for (var option in preset) { + if (preset.hasOwnProperty(option)) { + globalOptions[option] = preset[option]; + } + } + }; + + /** + * Get the currently set flavor + * @returns {string} + */ + showdown.getFlavor = function () { + 'use strict'; + return setFlavor; + }; + + /** + * Get the options of a specified flavor. Returns undefined if the flavor was not found + * @param {string} name Name of the flavor + * @returns {{}|undefined} + */ + showdown.getFlavorOptions = function (name) { + 'use strict'; + if (flavor.hasOwnProperty(name)) { + return flavor[name]; + } + }; + + /** + * Get the default options + * @static + * @param {boolean} [simple=true] + * @returns {{}} + */ + showdown.getDefaultOptions = function (simple) { + 'use strict'; + return getDefaultOpts(simple); + }; + + /** + * Get or set a subParser + * + * subParser(name) - Get a registered subParser + * subParser(name, func) - Register a subParser + * @static + * @param {string} name + * @param {function} [func] + * @returns {*} + */ + showdown.subParser = function (name, func) { + 'use strict'; + if (showdown.helper.isString(name)) { + if (typeof func !== 'undefined') { + parsers[name] = func; + } else { + if (parsers.hasOwnProperty(name)) { + return parsers[name]; + } else { + throw Error('SubParser named ' + name + ' not registered!'); + } + } + } else { + throw Error('showdown.subParser function first argument must be a string (the name of the subparser)'); + } + }; + + /** + * Gets or registers an extension + * @static + * @param {string} name + * @param {object|function=} ext + * @returns {*} + */ + showdown.extension = function (name, ext) { + 'use strict'; + + if (!showdown.helper.isString(name)) { + throw Error('Extension \'name\' must be a string'); + } + + name = showdown.helper.stdExtName(name); + + // Getter + if (showdown.helper.isUndefined(ext)) { + if (!extensions.hasOwnProperty(name)) { + throw Error('Extension named ' + name + ' is not registered!'); + } + return extensions[name]; + + // Setter + } else { + // Expand extension if it's wrapped in a function + if (typeof ext === 'function') { + ext = ext(); + } + + // Ensure extension is an array + if (!showdown.helper.isArray(ext)) { + ext = [ext]; + } + + var validExtension = validate(ext, name); + + if (validExtension.valid) { + extensions[name] = ext; + } else { + throw Error(validExtension.error); + } + } + }; + + /** + * Gets all extensions registered + * @returns {{}} + */ + showdown.getAllExtensions = function () { + 'use strict'; + return extensions; + }; + + /** + * Remove an extension + * @param {string} name + */ + showdown.removeExtension = function (name) { + 'use strict'; + delete extensions[name]; + }; + + /** + * Removes all extensions + */ + showdown.resetExtensions = function () { + 'use strict'; + extensions = {}; + }; + + /** + * Validate extension + * @param {array} extension + * @param {string} name + * @returns {{valid: boolean, error: string}} + */ + function validate (extension, name) { + 'use strict'; + + var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension', + ret = { + valid: true, + error: '' + }; + + if (!showdown.helper.isArray(extension)) { + extension = [extension]; + } + + for (var i = 0; i < extension.length; ++i) { + var baseMsg = errMsg + ' sub-extension ' + i + ': ', + ext = extension[i]; + if (typeof ext !== 'object') { + ret.valid = false; + ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given'; + return ret; + } + + if (!showdown.helper.isString(ext.type)) { + ret.valid = false; + ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given'; + return ret; + } + + var type = ext.type = ext.type.toLowerCase(); + + // normalize extension type + if (type === 'language') { + type = ext.type = 'lang'; + } + + if (type === 'html') { + type = ext.type = 'output'; + } + + if (type !== 'lang' && type !== 'output' && type !== 'listener') { + ret.valid = false; + ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"'; + return ret; + } + + if (type === 'listener') { + if (showdown.helper.isUndefined(ext.listeners)) { + ret.valid = false; + ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"'; + return ret; + } + } else { + if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) { + ret.valid = false; + ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method'; + return ret; + } + } + + if (ext.listeners) { + if (typeof ext.listeners !== 'object') { + ret.valid = false; + ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given'; + return ret; + } + for (var ln in ext.listeners) { + if (ext.listeners.hasOwnProperty(ln)) { + if (typeof ext.listeners[ln] !== 'function') { + ret.valid = false; + ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln + + ' must be a function but ' + typeof ext.listeners[ln] + ' given'; + return ret; + } + } + } + } + + if (ext.filter) { + if (typeof ext.filter !== 'function') { + ret.valid = false; + ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given'; + return ret; + } + } else if (ext.regex) { + if (showdown.helper.isString(ext.regex)) { + ext.regex = new RegExp(ext.regex, 'g'); + } + if (!(ext.regex instanceof RegExp)) { + ret.valid = false; + ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given'; + return ret; + } + if (showdown.helper.isUndefined(ext.replace)) { + ret.valid = false; + ret.error = baseMsg + '"regex" extensions must implement a replace string or function'; + return ret; + } + } + } + return ret; + } + + /** + * Validate extension + * @param {object} ext + * @returns {boolean} + */ + showdown.validateExtension = function (ext) { + 'use strict'; + + var validateExtension = validate(ext, null); + if (!validateExtension.valid) { + console.warn(validateExtension.error); + return false; + } + return true; + }; + + /** + * showdownjs helper functions + */ + + if (!showdown.hasOwnProperty('helper')) { + showdown.helper = {}; + } + + if (typeof this.document === 'undefined' && typeof this.window === 'undefined') { + var jsdom = require('jsdom'); + this.window = new jsdom.JSDOM('', {}).window; // jshint ignore:line + } + showdown.helper.document = this.window.document; + + /** + * Check if var is string + * @static + * @param {string} a + * @returns {boolean} + */ + showdown.helper.isString = function (a) { + 'use strict'; + return (typeof a === 'string' || a instanceof String); + }; + + /** + * Check if var is a function + * @static + * @param {*} a + * @returns {boolean} + */ + showdown.helper.isFunction = function (a) { + 'use strict'; + var getType = {}; + return a && getType.toString.call(a) === '[object Function]'; + }; + + /** + * isArray helper function + * @static + * @param {*} a + * @returns {boolean} + */ + showdown.helper.isArray = function (a) { + 'use strict'; + return Array.isArray(a); + }; + + /** + * Check if value is undefined + * @static + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + */ + showdown.helper.isUndefined = function (value) { + 'use strict'; + return typeof value === 'undefined'; + }; + + /** + * ForEach helper function + * Iterates over Arrays and Objects (own properties only) + * @static + * @param {*} obj + * @param {function} callback Accepts 3 params: 1. value, 2. key, 3. the original array/object + */ + showdown.helper.forEach = function (obj, callback) { + 'use strict'; + // check if obj is defined + if (showdown.helper.isUndefined(obj)) { + throw new Error('obj param is required'); + } + + if (showdown.helper.isUndefined(callback)) { + throw new Error('callback param is required'); + } + + if (!showdown.helper.isFunction(callback)) { + throw new Error('callback param must be a function/closure'); + } + + if (typeof obj.forEach === 'function') { + obj.forEach(callback); + } else if (showdown.helper.isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + callback(obj[i], i, obj); + } + } else if (typeof (obj) === 'object') { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + callback(obj[prop], prop, obj); + } + } + } else { + throw new Error('obj does not seem to be an array or an iterable object'); + } + }; + + /** + * Standardidize extension name + * @static + * @param {string} s extension name + * @returns {string} + */ + showdown.helper.stdExtName = function (s) { + 'use strict'; + return s.replace(/[_?*+\/\\.^-]/g, '').replace(/\s/g, '').toLowerCase(); + }; + + function escapeCharactersCallback (wholeMatch, m1) { + 'use strict'; + var charCodeToEscape = m1.charCodeAt(0); + return '¨E' + charCodeToEscape + 'E'; + } + + /** + * Callback used to escape characters when passing through String.replace + * @static + * @param {string} wholeMatch + * @param {string} m1 + * @returns {string} + */ + showdown.helper.escapeCharactersCallback = escapeCharactersCallback; + + /** + * Escape characters in a string + * @static + * @param {string} text + * @param {string} charsToEscape + * @param {boolean} afterBackslash + * @returns {XML|string|void|*} + */ + showdown.helper.escapeCharacters = function (text, charsToEscape, afterBackslash) { + 'use strict'; + // First we have to escape the escape characters so that + // we can build a character class out of them + var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])'; + + if (afterBackslash) { + regexString = '\\\\' + regexString; + } + + var regex = new RegExp(regexString, 'g'); + text = text.replace(regex, escapeCharactersCallback); + + return text; + }; + + var rgxFindMatchPos = function (str, left, right, flags) { + 'use strict'; + var f = flags || '', + g = f.indexOf('g') > -1, + x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')), + l = new RegExp(left, f.replace(/g/g, '')), + pos = [], + t, s, m, start, end; + + do { + t = 0; + while ((m = x.exec(str))) { + if (l.test(m[0])) { + if (!(t++)) { + s = x.lastIndex; + start = s - m[0].length; + } + } else if (t) { + if (!--t) { + end = m.index + m[0].length; + var obj = { + left: {start: start, end: s}, + match: {start: s, end: m.index}, + right: {start: m.index, end: end}, + wholeMatch: {start: start, end: end} + }; + pos.push(obj); + if (!g) { + return pos; + } + } + } + } + } while (t && (x.lastIndex = s)); + + return pos; + }; + + /** + * matchRecursiveRegExp + * + * (c) 2007 Steven Levithan + * MIT License + * + * Accepts a string to search, a left and right format delimiter + * as regex patterns, and optional regex flags. Returns an array + * of matches, allowing nested instances of left/right delimiters. + * Use the "g" flag to return all matches, otherwise only the + * first is returned. Be careful to ensure that the left and + * right format delimiters produce mutually exclusive matches. + * Backreferences are not supported within the right delimiter + * due to how it is internally combined with the left delimiter. + * When matching strings whose format delimiters are unbalanced + * to the left or right, the output is intentionally as a + * conventional regex library with recursion support would + * produce, e.g. "<" and ">" both produce ["x"] when using + * "<" and ">" as the delimiters (both strings contain a single, + * balanced instance of ""). + * + * examples: + * matchRecursiveRegExp("test", "\\(", "\\)") + * returns: [] + * matchRecursiveRegExp(">>t<>", "<", ">", "g") + * returns: ["t<>", ""] + * matchRecursiveRegExp("
test
", "]*>", "", "gi") + * returns: ["test"] + */ + showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) { + 'use strict'; + + var matchPos = rgxFindMatchPos (str, left, right, flags), + results = []; + + for (var i = 0; i < matchPos.length; ++i) { + results.push([ + str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), + str.slice(matchPos[i].match.start, matchPos[i].match.end), + str.slice(matchPos[i].left.start, matchPos[i].left.end), + str.slice(matchPos[i].right.start, matchPos[i].right.end) + ]); + } + return results; + }; + + /** + * + * @param {string} str + * @param {string|function} replacement + * @param {string} left + * @param {string} right + * @param {string} flags + * @returns {string} + */ + showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) { + 'use strict'; + + if (!showdown.helper.isFunction(replacement)) { + var repStr = replacement; + replacement = function () { + return repStr; + }; + } + + var matchPos = rgxFindMatchPos(str, left, right, flags), + finalStr = str, + lng = matchPos.length; + + if (lng > 0) { + var bits = []; + if (matchPos[0].wholeMatch.start !== 0) { + bits.push(str.slice(0, matchPos[0].wholeMatch.start)); + } + for (var i = 0; i < lng; ++i) { + bits.push( + replacement( + str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), + str.slice(matchPos[i].match.start, matchPos[i].match.end), + str.slice(matchPos[i].left.start, matchPos[i].left.end), + str.slice(matchPos[i].right.start, matchPos[i].right.end) + ) + ); + if (i < lng - 1) { + bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start)); + } + } + if (matchPos[lng - 1].wholeMatch.end < str.length) { + bits.push(str.slice(matchPos[lng - 1].wholeMatch.end)); + } + finalStr = bits.join(''); + } + return finalStr; + }; + + /** + * Returns the index within the passed String object of the first occurrence of the specified regex, + * starting the search at fromIndex. Returns -1 if the value is not found. + * + * @param {string} str string to search + * @param {RegExp} regex Regular expression to search + * @param {int} [fromIndex = 0] Index to start the search + * @returns {Number} + * @throws InvalidArgumentError + */ + showdown.helper.regexIndexOf = function (str, regex, fromIndex) { + 'use strict'; + if (!showdown.helper.isString(str)) { + throw 'InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string'; + } + if (regex instanceof RegExp === false) { + throw 'InvalidArgumentError: second parameter of showdown.helper.regexIndexOf function must be an instance of RegExp'; + } + var indexOf = str.substring(fromIndex || 0).search(regex); + return (indexOf >= 0) ? (indexOf + (fromIndex || 0)) : indexOf; + }; + + /** + * Splits the passed string object at the defined index, and returns an array composed of the two substrings + * @param {string} str string to split + * @param {int} index index to split string at + * @returns {[string,string]} + * @throws InvalidArgumentError + */ + showdown.helper.splitAtIndex = function (str, index) { + 'use strict'; + if (!showdown.helper.isString(str)) { + throw 'InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string'; + } + return [str.substring(0, index), str.substring(index)]; + }; + + /** + * Obfuscate an e-mail address through the use of Character Entities, + * transforming ASCII characters into their equivalent decimal or hex entities. + * + * Since it has a random component, subsequent calls to this function produce different results + * + * @param {string} mail + * @returns {string} + */ + showdown.helper.encodeEmailAddress = function (mail) { + 'use strict'; + var encode = [ + function (ch) { + return '&#' + ch.charCodeAt(0) + ';'; + }, + function (ch) { + return '&#x' + ch.charCodeAt(0).toString(16) + ';'; + }, + function (ch) { + return ch; + } + ]; + + mail = mail.replace(/./g, function (ch) { + if (ch === '@') { + // this *must* be encoded. I insist. + ch = encode[Math.floor(Math.random() * 2)](ch); + } else { + var r = Math.random(); + // roughly 10% raw, 45% hex, 45% dec + ch = ( + r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch) + ); + } + return ch; + }); + + return mail; + }; + + /** + * + * @param str + * @param targetLength + * @param padString + * @returns {string} + */ + showdown.helper.padEnd = function padEnd (str, targetLength, padString) { + 'use strict'; + /*jshint bitwise: false*/ + // eslint-disable-next-line space-infix-ops + targetLength = targetLength>>0; //floor if number or convert non-number to 0; + /*jshint bitwise: true*/ + padString = String(padString || ' '); + if (str.length > targetLength) { + return String(str); + } else { + targetLength = targetLength - str.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed + } + return String(str) + padString.slice(0,targetLength); + } + }; + + /** + * Unescape HTML entities + * @param txt + * @returns {string} + */ + showdown.helper.unescapeHTMLEntities = function (txt) { + 'use strict'; + + return txt + .replace(/"/g, '"') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/&/g, '&'); + }; + + showdown.helper._hashHTMLSpan = function (html, globals) { + return '¨C' + (globals.gHtmlSpans.push(html) - 1) + 'C'; + }; + + /** + * Prepends a base URL to relative paths. + * + * @param {string} baseUrl the base URL to prepend to a relative path + * @param {string} url the path to modify, which may be relative + * @returns {string} the full URL + */ + showdown.helper.applyBaseUrl = function (baseUrl, url) { + // Only prepend if given a base URL and the path is not absolute. + if (baseUrl && !this.isAbsolutePath(url)) { + var urlResolve = require('url').resolve; + url = urlResolve(baseUrl, url); + } + + return url; + }; + + /** + * Checks if the given path is absolute. + * + * @param {string} path the path to test for absolution + * @returns {boolean} `true` if the given path is absolute, else `false` + */ + showdown.helper.isAbsolutePath = function (path) { + // Absolute paths begin with '[protocol:]//' or '#' (anchors) + return /(^([a-z]+:)?\/\/)|(^#)/i.test(path); + }; + + /** + * Showdown's Event Object + * @param {string} name Name of the event + * @param {string} text Text + * @param {{}} params optional. params of the event + * @constructor + */ + showdown.helper.Event = function (name, text, params) { + 'use strict'; + + var regexp = params.regexp || null; + var matches = params.matches || {}; + var options = params.options || {}; + var converter = params.converter || null; + var globals = params.globals || {}; + + /** + * Get the name of the event + * @returns {string} + */ + this.getName = function () { + return name; + }; + + this.getEventName = function () { + return name; + }; + + this._stopExecution = false; + + this.parsedText = params.parsedText || null; + + this.getRegexp = function () { + return regexp; + }; + + this.getOptions = function () { + return options; + }; + + this.getConverter = function () { + return converter; + }; + + this.getGlobals = function () { + return globals; + }; + + this.getCapturedText = function () { + return text; + }; + + this.getText = function () { + return text; + }; + + this.setText = function (newText) { + text = newText; + }; + + this.getMatches = function () { + return matches; + }; + + this.setMatches = function (newMatches) { + matches = newMatches; + }; + + this.preventDefault = function (bool) { + this._stopExecution = !bool; + }; + }; + + /** + * POLYFILLS + */ +// use this instead of builtin is undefined for IE8 compatibility + if (typeof (console) === 'undefined') { + console = { + warn: function (msg) { + 'use strict'; + alert(msg); + }, + log: function (msg) { + 'use strict'; + alert(msg); + }, + error: function (msg) { + 'use strict'; + throw msg; + } + }; + } + + /** + * Common regexes. + * We declare some common regexes to improve performance + */ + showdown.helper.regexes = { + asteriskDashTildeAndColon: /([*_:~])/g, + asteriskDashAndTilde: /([*_~])/g + }; + + /** + * EMOJIS LIST + */ + showdown.helper.emojis = { + '+1':'\ud83d\udc4d', + '-1':'\ud83d\udc4e', + '100':'\ud83d\udcaf', + '1234':'\ud83d\udd22', + '1st_place_medal':'\ud83e\udd47', + '2nd_place_medal':'\ud83e\udd48', + '3rd_place_medal':'\ud83e\udd49', + '8ball':'\ud83c\udfb1', + 'a':'\ud83c\udd70\ufe0f', + 'ab':'\ud83c\udd8e', + 'abc':'\ud83d\udd24', + 'abcd':'\ud83d\udd21', + 'accept':'\ud83c\ude51', + 'aerial_tramway':'\ud83d\udea1', + 'airplane':'\u2708\ufe0f', + 'alarm_clock':'\u23f0', + 'alembic':'\u2697\ufe0f', + 'alien':'\ud83d\udc7d', + 'ambulance':'\ud83d\ude91', + 'amphora':'\ud83c\udffa', + 'anchor':'\u2693\ufe0f', + 'angel':'\ud83d\udc7c', + 'anger':'\ud83d\udca2', + 'angry':'\ud83d\ude20', + 'anguished':'\ud83d\ude27', + 'ant':'\ud83d\udc1c', + 'apple':'\ud83c\udf4e', + 'aquarius':'\u2652\ufe0f', + 'aries':'\u2648\ufe0f', + 'arrow_backward':'\u25c0\ufe0f', + 'arrow_double_down':'\u23ec', + 'arrow_double_up':'\u23eb', + 'arrow_down':'\u2b07\ufe0f', + 'arrow_down_small':'\ud83d\udd3d', + 'arrow_forward':'\u25b6\ufe0f', + 'arrow_heading_down':'\u2935\ufe0f', + 'arrow_heading_up':'\u2934\ufe0f', + 'arrow_left':'\u2b05\ufe0f', + 'arrow_lower_left':'\u2199\ufe0f', + 'arrow_lower_right':'\u2198\ufe0f', + 'arrow_right':'\u27a1\ufe0f', + 'arrow_right_hook':'\u21aa\ufe0f', + 'arrow_up':'\u2b06\ufe0f', + 'arrow_up_down':'\u2195\ufe0f', + 'arrow_up_small':'\ud83d\udd3c', + 'arrow_upper_left':'\u2196\ufe0f', + 'arrow_upper_right':'\u2197\ufe0f', + 'arrows_clockwise':'\ud83d\udd03', + 'arrows_counterclockwise':'\ud83d\udd04', + 'art':'\ud83c\udfa8', + 'articulated_lorry':'\ud83d\ude9b', + 'artificial_satellite':'\ud83d\udef0', + 'astonished':'\ud83d\ude32', + 'athletic_shoe':'\ud83d\udc5f', + 'atm':'\ud83c\udfe7', + 'atom_symbol':'\u269b\ufe0f', + 'avocado':'\ud83e\udd51', + 'b':'\ud83c\udd71\ufe0f', + 'baby':'\ud83d\udc76', + 'baby_bottle':'\ud83c\udf7c', + 'baby_chick':'\ud83d\udc24', + 'baby_symbol':'\ud83d\udebc', + 'back':'\ud83d\udd19', + 'bacon':'\ud83e\udd53', + 'badminton':'\ud83c\udff8', + 'baggage_claim':'\ud83d\udec4', + 'baguette_bread':'\ud83e\udd56', + 'balance_scale':'\u2696\ufe0f', + 'balloon':'\ud83c\udf88', + 'ballot_box':'\ud83d\uddf3', + 'ballot_box_with_check':'\u2611\ufe0f', + 'bamboo':'\ud83c\udf8d', + 'banana':'\ud83c\udf4c', + 'bangbang':'\u203c\ufe0f', + 'bank':'\ud83c\udfe6', + 'bar_chart':'\ud83d\udcca', + 'barber':'\ud83d\udc88', + 'baseball':'\u26be\ufe0f', + 'basketball':'\ud83c\udfc0', + 'basketball_man':'\u26f9\ufe0f', + 'basketball_woman':'\u26f9\ufe0f‍\u2640\ufe0f', + 'bat':'\ud83e\udd87', + 'bath':'\ud83d\udec0', + 'bathtub':'\ud83d\udec1', + 'battery':'\ud83d\udd0b', + 'beach_umbrella':'\ud83c\udfd6', + 'bear':'\ud83d\udc3b', + 'bed':'\ud83d\udecf', + 'bee':'\ud83d\udc1d', + 'beer':'\ud83c\udf7a', + 'beers':'\ud83c\udf7b', + 'beetle':'\ud83d\udc1e', + 'beginner':'\ud83d\udd30', + 'bell':'\ud83d\udd14', + 'bellhop_bell':'\ud83d\udece', + 'bento':'\ud83c\udf71', + 'biking_man':'\ud83d\udeb4', + 'bike':'\ud83d\udeb2', + 'biking_woman':'\ud83d\udeb4‍\u2640\ufe0f', + 'bikini':'\ud83d\udc59', + 'biohazard':'\u2623\ufe0f', + 'bird':'\ud83d\udc26', + 'birthday':'\ud83c\udf82', + 'black_circle':'\u26ab\ufe0f', + 'black_flag':'\ud83c\udff4', + 'black_heart':'\ud83d\udda4', + 'black_joker':'\ud83c\udccf', + 'black_large_square':'\u2b1b\ufe0f', + 'black_medium_small_square':'\u25fe\ufe0f', + 'black_medium_square':'\u25fc\ufe0f', + 'black_nib':'\u2712\ufe0f', + 'black_small_square':'\u25aa\ufe0f', + 'black_square_button':'\ud83d\udd32', + 'blonde_man':'\ud83d\udc71', + 'blonde_woman':'\ud83d\udc71‍\u2640\ufe0f', + 'blossom':'\ud83c\udf3c', + 'blowfish':'\ud83d\udc21', + 'blue_book':'\ud83d\udcd8', + 'blue_car':'\ud83d\ude99', + 'blue_heart':'\ud83d\udc99', + 'blush':'\ud83d\ude0a', + 'boar':'\ud83d\udc17', + 'boat':'\u26f5\ufe0f', + 'bomb':'\ud83d\udca3', + 'book':'\ud83d\udcd6', + 'bookmark':'\ud83d\udd16', + 'bookmark_tabs':'\ud83d\udcd1', + 'books':'\ud83d\udcda', + 'boom':'\ud83d\udca5', + 'boot':'\ud83d\udc62', + 'bouquet':'\ud83d\udc90', + 'bowing_man':'\ud83d\ude47', + 'bow_and_arrow':'\ud83c\udff9', + 'bowing_woman':'\ud83d\ude47‍\u2640\ufe0f', + 'bowling':'\ud83c\udfb3', + 'boxing_glove':'\ud83e\udd4a', + 'boy':'\ud83d\udc66', + 'bread':'\ud83c\udf5e', + 'bride_with_veil':'\ud83d\udc70', + 'bridge_at_night':'\ud83c\udf09', + 'briefcase':'\ud83d\udcbc', + 'broken_heart':'\ud83d\udc94', + 'bug':'\ud83d\udc1b', + 'building_construction':'\ud83c\udfd7', + 'bulb':'\ud83d\udca1', + 'bullettrain_front':'\ud83d\ude85', + 'bullettrain_side':'\ud83d\ude84', + 'burrito':'\ud83c\udf2f', + 'bus':'\ud83d\ude8c', + 'business_suit_levitating':'\ud83d\udd74', + 'busstop':'\ud83d\ude8f', + 'bust_in_silhouette':'\ud83d\udc64', + 'busts_in_silhouette':'\ud83d\udc65', + 'butterfly':'\ud83e\udd8b', + 'cactus':'\ud83c\udf35', + 'cake':'\ud83c\udf70', + 'calendar':'\ud83d\udcc6', + 'call_me_hand':'\ud83e\udd19', + 'calling':'\ud83d\udcf2', + 'camel':'\ud83d\udc2b', + 'camera':'\ud83d\udcf7', + 'camera_flash':'\ud83d\udcf8', + 'camping':'\ud83c\udfd5', + 'cancer':'\u264b\ufe0f', + 'candle':'\ud83d\udd6f', + 'candy':'\ud83c\udf6c', + 'canoe':'\ud83d\udef6', + 'capital_abcd':'\ud83d\udd20', + 'capricorn':'\u2651\ufe0f', + 'car':'\ud83d\ude97', + 'card_file_box':'\ud83d\uddc3', + 'card_index':'\ud83d\udcc7', + 'card_index_dividers':'\ud83d\uddc2', + 'carousel_horse':'\ud83c\udfa0', + 'carrot':'\ud83e\udd55', + 'cat':'\ud83d\udc31', + 'cat2':'\ud83d\udc08', + 'cd':'\ud83d\udcbf', + 'chains':'\u26d3', + 'champagne':'\ud83c\udf7e', + 'chart':'\ud83d\udcb9', + 'chart_with_downwards_trend':'\ud83d\udcc9', + 'chart_with_upwards_trend':'\ud83d\udcc8', + 'checkered_flag':'\ud83c\udfc1', + 'cheese':'\ud83e\uddc0', + 'cherries':'\ud83c\udf52', + 'cherry_blossom':'\ud83c\udf38', + 'chestnut':'\ud83c\udf30', + 'chicken':'\ud83d\udc14', + 'children_crossing':'\ud83d\udeb8', + 'chipmunk':'\ud83d\udc3f', + 'chocolate_bar':'\ud83c\udf6b', + 'christmas_tree':'\ud83c\udf84', + 'church':'\u26ea\ufe0f', + 'cinema':'\ud83c\udfa6', + 'circus_tent':'\ud83c\udfaa', + 'city_sunrise':'\ud83c\udf07', + 'city_sunset':'\ud83c\udf06', + 'cityscape':'\ud83c\udfd9', + 'cl':'\ud83c\udd91', + 'clamp':'\ud83d\udddc', + 'clap':'\ud83d\udc4f', + 'clapper':'\ud83c\udfac', + 'classical_building':'\ud83c\udfdb', + 'clinking_glasses':'\ud83e\udd42', + 'clipboard':'\ud83d\udccb', + 'clock1':'\ud83d\udd50', + 'clock10':'\ud83d\udd59', + 'clock1030':'\ud83d\udd65', + 'clock11':'\ud83d\udd5a', + 'clock1130':'\ud83d\udd66', + 'clock12':'\ud83d\udd5b', + 'clock1230':'\ud83d\udd67', + 'clock130':'\ud83d\udd5c', + 'clock2':'\ud83d\udd51', + 'clock230':'\ud83d\udd5d', + 'clock3':'\ud83d\udd52', + 'clock330':'\ud83d\udd5e', + 'clock4':'\ud83d\udd53', + 'clock430':'\ud83d\udd5f', + 'clock5':'\ud83d\udd54', + 'clock530':'\ud83d\udd60', + 'clock6':'\ud83d\udd55', + 'clock630':'\ud83d\udd61', + 'clock7':'\ud83d\udd56', + 'clock730':'\ud83d\udd62', + 'clock8':'\ud83d\udd57', + 'clock830':'\ud83d\udd63', + 'clock9':'\ud83d\udd58', + 'clock930':'\ud83d\udd64', + 'closed_book':'\ud83d\udcd5', + 'closed_lock_with_key':'\ud83d\udd10', + 'closed_umbrella':'\ud83c\udf02', + 'cloud':'\u2601\ufe0f', + 'cloud_with_lightning':'\ud83c\udf29', + 'cloud_with_lightning_and_rain':'\u26c8', + 'cloud_with_rain':'\ud83c\udf27', + 'cloud_with_snow':'\ud83c\udf28', + 'clown_face':'\ud83e\udd21', + 'clubs':'\u2663\ufe0f', + 'cocktail':'\ud83c\udf78', + 'coffee':'\u2615\ufe0f', + 'coffin':'\u26b0\ufe0f', + 'cold_sweat':'\ud83d\ude30', + 'comet':'\u2604\ufe0f', + 'computer':'\ud83d\udcbb', + 'computer_mouse':'\ud83d\uddb1', + 'confetti_ball':'\ud83c\udf8a', + 'confounded':'\ud83d\ude16', + 'confused':'\ud83d\ude15', + 'congratulations':'\u3297\ufe0f', + 'construction':'\ud83d\udea7', + 'construction_worker_man':'\ud83d\udc77', + 'construction_worker_woman':'\ud83d\udc77‍\u2640\ufe0f', + 'control_knobs':'\ud83c\udf9b', + 'convenience_store':'\ud83c\udfea', + 'cookie':'\ud83c\udf6a', + 'cool':'\ud83c\udd92', + 'policeman':'\ud83d\udc6e', + 'copyright':'\u00a9\ufe0f', + 'corn':'\ud83c\udf3d', + 'couch_and_lamp':'\ud83d\udecb', + 'couple':'\ud83d\udc6b', + 'couple_with_heart_woman_man':'\ud83d\udc91', + 'couple_with_heart_man_man':'\ud83d\udc68‍\u2764\ufe0f‍\ud83d\udc68', + 'couple_with_heart_woman_woman':'\ud83d\udc69‍\u2764\ufe0f‍\ud83d\udc69', + 'couplekiss_man_man':'\ud83d\udc68‍\u2764\ufe0f‍\ud83d\udc8b‍\ud83d\udc68', + 'couplekiss_man_woman':'\ud83d\udc8f', + 'couplekiss_woman_woman':'\ud83d\udc69‍\u2764\ufe0f‍\ud83d\udc8b‍\ud83d\udc69', + 'cow':'\ud83d\udc2e', + 'cow2':'\ud83d\udc04', + 'cowboy_hat_face':'\ud83e\udd20', + 'crab':'\ud83e\udd80', + 'crayon':'\ud83d\udd8d', + 'credit_card':'\ud83d\udcb3', + 'crescent_moon':'\ud83c\udf19', + 'cricket':'\ud83c\udfcf', + 'crocodile':'\ud83d\udc0a', + 'croissant':'\ud83e\udd50', + 'crossed_fingers':'\ud83e\udd1e', + 'crossed_flags':'\ud83c\udf8c', + 'crossed_swords':'\u2694\ufe0f', + 'crown':'\ud83d\udc51', + 'cry':'\ud83d\ude22', + 'crying_cat_face':'\ud83d\ude3f', + 'crystal_ball':'\ud83d\udd2e', + 'cucumber':'\ud83e\udd52', + 'cupid':'\ud83d\udc98', + 'curly_loop':'\u27b0', + 'currency_exchange':'\ud83d\udcb1', + 'curry':'\ud83c\udf5b', + 'custard':'\ud83c\udf6e', + 'customs':'\ud83d\udec3', + 'cyclone':'\ud83c\udf00', + 'dagger':'\ud83d\udde1', + 'dancer':'\ud83d\udc83', + 'dancing_women':'\ud83d\udc6f', + 'dancing_men':'\ud83d\udc6f‍\u2642\ufe0f', + 'dango':'\ud83c\udf61', + 'dark_sunglasses':'\ud83d\udd76', + 'dart':'\ud83c\udfaf', + 'dash':'\ud83d\udca8', + 'date':'\ud83d\udcc5', + 'deciduous_tree':'\ud83c\udf33', + 'deer':'\ud83e\udd8c', + 'department_store':'\ud83c\udfec', + 'derelict_house':'\ud83c\udfda', + 'desert':'\ud83c\udfdc', + 'desert_island':'\ud83c\udfdd', + 'desktop_computer':'\ud83d\udda5', + 'male_detective':'\ud83d\udd75\ufe0f', + 'diamond_shape_with_a_dot_inside':'\ud83d\udca0', + 'diamonds':'\u2666\ufe0f', + 'disappointed':'\ud83d\ude1e', + 'disappointed_relieved':'\ud83d\ude25', + 'dizzy':'\ud83d\udcab', + 'dizzy_face':'\ud83d\ude35', + 'do_not_litter':'\ud83d\udeaf', + 'dog':'\ud83d\udc36', + 'dog2':'\ud83d\udc15', + 'dollar':'\ud83d\udcb5', + 'dolls':'\ud83c\udf8e', + 'dolphin':'\ud83d\udc2c', + 'door':'\ud83d\udeaa', + 'doughnut':'\ud83c\udf69', + 'dove':'\ud83d\udd4a', + 'dragon':'\ud83d\udc09', + 'dragon_face':'\ud83d\udc32', + 'dress':'\ud83d\udc57', + 'dromedary_camel':'\ud83d\udc2a', + 'drooling_face':'\ud83e\udd24', + 'droplet':'\ud83d\udca7', + 'drum':'\ud83e\udd41', + 'duck':'\ud83e\udd86', + 'dvd':'\ud83d\udcc0', + 'e-mail':'\ud83d\udce7', + 'eagle':'\ud83e\udd85', + 'ear':'\ud83d\udc42', + 'ear_of_rice':'\ud83c\udf3e', + 'earth_africa':'\ud83c\udf0d', + 'earth_americas':'\ud83c\udf0e', + 'earth_asia':'\ud83c\udf0f', + 'egg':'\ud83e\udd5a', + 'eggplant':'\ud83c\udf46', + 'eight_pointed_black_star':'\u2734\ufe0f', + 'eight_spoked_asterisk':'\u2733\ufe0f', + 'electric_plug':'\ud83d\udd0c', + 'elephant':'\ud83d\udc18', + 'email':'\u2709\ufe0f', + 'end':'\ud83d\udd1a', + 'envelope_with_arrow':'\ud83d\udce9', + 'euro':'\ud83d\udcb6', + 'european_castle':'\ud83c\udff0', + 'european_post_office':'\ud83c\udfe4', + 'evergreen_tree':'\ud83c\udf32', + 'exclamation':'\u2757\ufe0f', + 'expressionless':'\ud83d\ude11', + 'eye':'\ud83d\udc41', + 'eye_speech_bubble':'\ud83d\udc41‍\ud83d\udde8', + 'eyeglasses':'\ud83d\udc53', + 'eyes':'\ud83d\udc40', + 'face_with_head_bandage':'\ud83e\udd15', + 'face_with_thermometer':'\ud83e\udd12', + 'fist_oncoming':'\ud83d\udc4a', + 'factory':'\ud83c\udfed', + 'fallen_leaf':'\ud83c\udf42', + 'family_man_woman_boy':'\ud83d\udc6a', + 'family_man_boy':'\ud83d\udc68‍\ud83d\udc66', + 'family_man_boy_boy':'\ud83d\udc68‍\ud83d\udc66‍\ud83d\udc66', + 'family_man_girl':'\ud83d\udc68‍\ud83d\udc67', + 'family_man_girl_boy':'\ud83d\udc68‍\ud83d\udc67‍\ud83d\udc66', + 'family_man_girl_girl':'\ud83d\udc68‍\ud83d\udc67‍\ud83d\udc67', + 'family_man_man_boy':'\ud83d\udc68‍\ud83d\udc68‍\ud83d\udc66', + 'family_man_man_boy_boy':'\ud83d\udc68‍\ud83d\udc68‍\ud83d\udc66‍\ud83d\udc66', + 'family_man_man_girl':'\ud83d\udc68‍\ud83d\udc68‍\ud83d\udc67', + 'family_man_man_girl_boy':'\ud83d\udc68‍\ud83d\udc68‍\ud83d\udc67‍\ud83d\udc66', + 'family_man_man_girl_girl':'\ud83d\udc68‍\ud83d\udc68‍\ud83d\udc67‍\ud83d\udc67', + 'family_man_woman_boy_boy':'\ud83d\udc68‍\ud83d\udc69‍\ud83d\udc66‍\ud83d\udc66', + 'family_man_woman_girl':'\ud83d\udc68‍\ud83d\udc69‍\ud83d\udc67', + 'family_man_woman_girl_boy':'\ud83d\udc68‍\ud83d\udc69‍\ud83d\udc67‍\ud83d\udc66', + 'family_man_woman_girl_girl':'\ud83d\udc68‍\ud83d\udc69‍\ud83d\udc67‍\ud83d\udc67', + 'family_woman_boy':'\ud83d\udc69‍\ud83d\udc66', + 'family_woman_boy_boy':'\ud83d\udc69‍\ud83d\udc66‍\ud83d\udc66', + 'family_woman_girl':'\ud83d\udc69‍\ud83d\udc67', + 'family_woman_girl_boy':'\ud83d\udc69‍\ud83d\udc67‍\ud83d\udc66', + 'family_woman_girl_girl':'\ud83d\udc69‍\ud83d\udc67‍\ud83d\udc67', + 'family_woman_woman_boy':'\ud83d\udc69‍\ud83d\udc69‍\ud83d\udc66', + 'family_woman_woman_boy_boy':'\ud83d\udc69‍\ud83d\udc69‍\ud83d\udc66‍\ud83d\udc66', + 'family_woman_woman_girl':'\ud83d\udc69‍\ud83d\udc69‍\ud83d\udc67', + 'family_woman_woman_girl_boy':'\ud83d\udc69‍\ud83d\udc69‍\ud83d\udc67‍\ud83d\udc66', + 'family_woman_woman_girl_girl':'\ud83d\udc69‍\ud83d\udc69‍\ud83d\udc67‍\ud83d\udc67', + 'fast_forward':'\u23e9', + 'fax':'\ud83d\udce0', + 'fearful':'\ud83d\ude28', + 'feet':'\ud83d\udc3e', + 'female_detective':'\ud83d\udd75\ufe0f‍\u2640\ufe0f', + 'ferris_wheel':'\ud83c\udfa1', + 'ferry':'\u26f4', + 'field_hockey':'\ud83c\udfd1', + 'file_cabinet':'\ud83d\uddc4', + 'file_folder':'\ud83d\udcc1', + 'film_projector':'\ud83d\udcfd', + 'film_strip':'\ud83c\udf9e', + 'fire':'\ud83d\udd25', + 'fire_engine':'\ud83d\ude92', + 'fireworks':'\ud83c\udf86', + 'first_quarter_moon':'\ud83c\udf13', + 'first_quarter_moon_with_face':'\ud83c\udf1b', + 'fish':'\ud83d\udc1f', + 'fish_cake':'\ud83c\udf65', + 'fishing_pole_and_fish':'\ud83c\udfa3', + 'fist_raised':'\u270a', + 'fist_left':'\ud83e\udd1b', + 'fist_right':'\ud83e\udd1c', + 'flags':'\ud83c\udf8f', + 'flashlight':'\ud83d\udd26', + 'fleur_de_lis':'\u269c\ufe0f', + 'flight_arrival':'\ud83d\udeec', + 'flight_departure':'\ud83d\udeeb', + 'floppy_disk':'\ud83d\udcbe', + 'flower_playing_cards':'\ud83c\udfb4', + 'flushed':'\ud83d\ude33', + 'fog':'\ud83c\udf2b', + 'foggy':'\ud83c\udf01', + 'football':'\ud83c\udfc8', + 'footprints':'\ud83d\udc63', + 'fork_and_knife':'\ud83c\udf74', + 'fountain':'\u26f2\ufe0f', + 'fountain_pen':'\ud83d\udd8b', + 'four_leaf_clover':'\ud83c\udf40', + 'fox_face':'\ud83e\udd8a', + 'framed_picture':'\ud83d\uddbc', + 'free':'\ud83c\udd93', + 'fried_egg':'\ud83c\udf73', + 'fried_shrimp':'\ud83c\udf64', + 'fries':'\ud83c\udf5f', + 'frog':'\ud83d\udc38', + 'frowning':'\ud83d\ude26', + 'frowning_face':'\u2639\ufe0f', + 'frowning_man':'\ud83d\ude4d‍\u2642\ufe0f', + 'frowning_woman':'\ud83d\ude4d', + 'middle_finger':'\ud83d\udd95', + 'fuelpump':'\u26fd\ufe0f', + 'full_moon':'\ud83c\udf15', + 'full_moon_with_face':'\ud83c\udf1d', + 'funeral_urn':'\u26b1\ufe0f', + 'game_die':'\ud83c\udfb2', + 'gear':'\u2699\ufe0f', + 'gem':'\ud83d\udc8e', + 'gemini':'\u264a\ufe0f', + 'ghost':'\ud83d\udc7b', + 'gift':'\ud83c\udf81', + 'gift_heart':'\ud83d\udc9d', + 'girl':'\ud83d\udc67', + 'globe_with_meridians':'\ud83c\udf10', + 'goal_net':'\ud83e\udd45', + 'goat':'\ud83d\udc10', + 'golf':'\u26f3\ufe0f', + 'golfing_man':'\ud83c\udfcc\ufe0f', + 'golfing_woman':'\ud83c\udfcc\ufe0f‍\u2640\ufe0f', + 'gorilla':'\ud83e\udd8d', + 'grapes':'\ud83c\udf47', + 'green_apple':'\ud83c\udf4f', + 'green_book':'\ud83d\udcd7', + 'green_heart':'\ud83d\udc9a', + 'green_salad':'\ud83e\udd57', + 'grey_exclamation':'\u2755', + 'grey_question':'\u2754', + 'grimacing':'\ud83d\ude2c', + 'grin':'\ud83d\ude01', + 'grinning':'\ud83d\ude00', + 'guardsman':'\ud83d\udc82', + 'guardswoman':'\ud83d\udc82‍\u2640\ufe0f', + 'guitar':'\ud83c\udfb8', + 'gun':'\ud83d\udd2b', + 'haircut_woman':'\ud83d\udc87', + 'haircut_man':'\ud83d\udc87‍\u2642\ufe0f', + 'hamburger':'\ud83c\udf54', + 'hammer':'\ud83d\udd28', + 'hammer_and_pick':'\u2692', + 'hammer_and_wrench':'\ud83d\udee0', + 'hamster':'\ud83d\udc39', + 'hand':'\u270b', + 'handbag':'\ud83d\udc5c', + 'handshake':'\ud83e\udd1d', + 'hankey':'\ud83d\udca9', + 'hatched_chick':'\ud83d\udc25', + 'hatching_chick':'\ud83d\udc23', + 'headphones':'\ud83c\udfa7', + 'hear_no_evil':'\ud83d\ude49', + 'heart':'\u2764\ufe0f', + 'heart_decoration':'\ud83d\udc9f', + 'heart_eyes':'\ud83d\ude0d', + 'heart_eyes_cat':'\ud83d\ude3b', + 'heartbeat':'\ud83d\udc93', + 'heartpulse':'\ud83d\udc97', + 'hearts':'\u2665\ufe0f', + 'heavy_check_mark':'\u2714\ufe0f', + 'heavy_division_sign':'\u2797', + 'heavy_dollar_sign':'\ud83d\udcb2', + 'heavy_heart_exclamation':'\u2763\ufe0f', + 'heavy_minus_sign':'\u2796', + 'heavy_multiplication_x':'\u2716\ufe0f', + 'heavy_plus_sign':'\u2795', + 'helicopter':'\ud83d\ude81', + 'herb':'\ud83c\udf3f', + 'hibiscus':'\ud83c\udf3a', + 'high_brightness':'\ud83d\udd06', + 'high_heel':'\ud83d\udc60', + 'hocho':'\ud83d\udd2a', + 'hole':'\ud83d\udd73', + 'honey_pot':'\ud83c\udf6f', + 'horse':'\ud83d\udc34', + 'horse_racing':'\ud83c\udfc7', + 'hospital':'\ud83c\udfe5', + 'hot_pepper':'\ud83c\udf36', + 'hotdog':'\ud83c\udf2d', + 'hotel':'\ud83c\udfe8', + 'hotsprings':'\u2668\ufe0f', + 'hourglass':'\u231b\ufe0f', + 'hourglass_flowing_sand':'\u23f3', + 'house':'\ud83c\udfe0', + 'house_with_garden':'\ud83c\udfe1', + 'houses':'\ud83c\udfd8', + 'hugs':'\ud83e\udd17', + 'hushed':'\ud83d\ude2f', + 'ice_cream':'\ud83c\udf68', + 'ice_hockey':'\ud83c\udfd2', + 'ice_skate':'\u26f8', + 'icecream':'\ud83c\udf66', + 'id':'\ud83c\udd94', + 'ideograph_advantage':'\ud83c\ude50', + 'imp':'\ud83d\udc7f', + 'inbox_tray':'\ud83d\udce5', + 'incoming_envelope':'\ud83d\udce8', + 'tipping_hand_woman':'\ud83d\udc81', + 'information_source':'\u2139\ufe0f', + 'innocent':'\ud83d\ude07', + 'interrobang':'\u2049\ufe0f', + 'iphone':'\ud83d\udcf1', + 'izakaya_lantern':'\ud83c\udfee', + 'jack_o_lantern':'\ud83c\udf83', + 'japan':'\ud83d\uddfe', + 'japanese_castle':'\ud83c\udfef', + 'japanese_goblin':'\ud83d\udc7a', + 'japanese_ogre':'\ud83d\udc79', + 'jeans':'\ud83d\udc56', + 'joy':'\ud83d\ude02', + 'joy_cat':'\ud83d\ude39', + 'joystick':'\ud83d\udd79', + 'kaaba':'\ud83d\udd4b', + 'key':'\ud83d\udd11', + 'keyboard':'\u2328\ufe0f', + 'keycap_ten':'\ud83d\udd1f', + 'kick_scooter':'\ud83d\udef4', + 'kimono':'\ud83d\udc58', + 'kiss':'\ud83d\udc8b', + 'kissing':'\ud83d\ude17', + 'kissing_cat':'\ud83d\ude3d', + 'kissing_closed_eyes':'\ud83d\ude1a', + 'kissing_heart':'\ud83d\ude18', + 'kissing_smiling_eyes':'\ud83d\ude19', + 'kiwi_fruit':'\ud83e\udd5d', + 'koala':'\ud83d\udc28', + 'koko':'\ud83c\ude01', + 'label':'\ud83c\udff7', + 'large_blue_circle':'\ud83d\udd35', + 'large_blue_diamond':'\ud83d\udd37', + 'large_orange_diamond':'\ud83d\udd36', + 'last_quarter_moon':'\ud83c\udf17', + 'last_quarter_moon_with_face':'\ud83c\udf1c', + 'latin_cross':'\u271d\ufe0f', + 'laughing':'\ud83d\ude06', + 'leaves':'\ud83c\udf43', + 'ledger':'\ud83d\udcd2', + 'left_luggage':'\ud83d\udec5', + 'left_right_arrow':'\u2194\ufe0f', + 'leftwards_arrow_with_hook':'\u21a9\ufe0f', + 'lemon':'\ud83c\udf4b', + 'leo':'\u264c\ufe0f', + 'leopard':'\ud83d\udc06', + 'level_slider':'\ud83c\udf9a', + 'libra':'\u264e\ufe0f', + 'light_rail':'\ud83d\ude88', + 'link':'\ud83d\udd17', + 'lion':'\ud83e\udd81', + 'lips':'\ud83d\udc44', + 'lipstick':'\ud83d\udc84', + 'lizard':'\ud83e\udd8e', + 'lock':'\ud83d\udd12', + 'lock_with_ink_pen':'\ud83d\udd0f', + 'lollipop':'\ud83c\udf6d', + 'loop':'\u27bf', + 'loud_sound':'\ud83d\udd0a', + 'loudspeaker':'\ud83d\udce2', + 'love_hotel':'\ud83c\udfe9', + 'love_letter':'\ud83d\udc8c', + 'low_brightness':'\ud83d\udd05', + 'lying_face':'\ud83e\udd25', + 'm':'\u24c2\ufe0f', + 'mag':'\ud83d\udd0d', + 'mag_right':'\ud83d\udd0e', + 'mahjong':'\ud83c\udc04\ufe0f', + 'mailbox':'\ud83d\udceb', + 'mailbox_closed':'\ud83d\udcea', + 'mailbox_with_mail':'\ud83d\udcec', + 'mailbox_with_no_mail':'\ud83d\udced', + 'man':'\ud83d\udc68', + 'man_artist':'\ud83d\udc68‍\ud83c\udfa8', + 'man_astronaut':'\ud83d\udc68‍\ud83d\ude80', + 'man_cartwheeling':'\ud83e\udd38‍\u2642\ufe0f', + 'man_cook':'\ud83d\udc68‍\ud83c\udf73', + 'man_dancing':'\ud83d\udd7a', + 'man_facepalming':'\ud83e\udd26‍\u2642\ufe0f', + 'man_factory_worker':'\ud83d\udc68‍\ud83c\udfed', + 'man_farmer':'\ud83d\udc68‍\ud83c\udf3e', + 'man_firefighter':'\ud83d\udc68‍\ud83d\ude92', + 'man_health_worker':'\ud83d\udc68‍\u2695\ufe0f', + 'man_in_tuxedo':'\ud83e\udd35', + 'man_judge':'\ud83d\udc68‍\u2696\ufe0f', + 'man_juggling':'\ud83e\udd39‍\u2642\ufe0f', + 'man_mechanic':'\ud83d\udc68‍\ud83d\udd27', + 'man_office_worker':'\ud83d\udc68‍\ud83d\udcbc', + 'man_pilot':'\ud83d\udc68‍\u2708\ufe0f', + 'man_playing_handball':'\ud83e\udd3e‍\u2642\ufe0f', + 'man_playing_water_polo':'\ud83e\udd3d‍\u2642\ufe0f', + 'man_scientist':'\ud83d\udc68‍\ud83d\udd2c', + 'man_shrugging':'\ud83e\udd37‍\u2642\ufe0f', + 'man_singer':'\ud83d\udc68‍\ud83c\udfa4', + 'man_student':'\ud83d\udc68‍\ud83c\udf93', + 'man_teacher':'\ud83d\udc68‍\ud83c\udfeb', + 'man_technologist':'\ud83d\udc68‍\ud83d\udcbb', + 'man_with_gua_pi_mao':'\ud83d\udc72', + 'man_with_turban':'\ud83d\udc73', + 'tangerine':'\ud83c\udf4a', + 'mans_shoe':'\ud83d\udc5e', + 'mantelpiece_clock':'\ud83d\udd70', + 'maple_leaf':'\ud83c\udf41', + 'martial_arts_uniform':'\ud83e\udd4b', + 'mask':'\ud83d\ude37', + 'massage_woman':'\ud83d\udc86', + 'massage_man':'\ud83d\udc86‍\u2642\ufe0f', + 'meat_on_bone':'\ud83c\udf56', + 'medal_military':'\ud83c\udf96', + 'medal_sports':'\ud83c\udfc5', + 'mega':'\ud83d\udce3', + 'melon':'\ud83c\udf48', + 'memo':'\ud83d\udcdd', + 'men_wrestling':'\ud83e\udd3c‍\u2642\ufe0f', + 'menorah':'\ud83d\udd4e', + 'mens':'\ud83d\udeb9', + 'metal':'\ud83e\udd18', + 'metro':'\ud83d\ude87', + 'microphone':'\ud83c\udfa4', + 'microscope':'\ud83d\udd2c', + 'milk_glass':'\ud83e\udd5b', + 'milky_way':'\ud83c\udf0c', + 'minibus':'\ud83d\ude90', + 'minidisc':'\ud83d\udcbd', + 'mobile_phone_off':'\ud83d\udcf4', + 'money_mouth_face':'\ud83e\udd11', + 'money_with_wings':'\ud83d\udcb8', + 'moneybag':'\ud83d\udcb0', + 'monkey':'\ud83d\udc12', + 'monkey_face':'\ud83d\udc35', + 'monorail':'\ud83d\ude9d', + 'moon':'\ud83c\udf14', + 'mortar_board':'\ud83c\udf93', + 'mosque':'\ud83d\udd4c', + 'motor_boat':'\ud83d\udee5', + 'motor_scooter':'\ud83d\udef5', + 'motorcycle':'\ud83c\udfcd', + 'motorway':'\ud83d\udee3', + 'mount_fuji':'\ud83d\uddfb', + 'mountain':'\u26f0', + 'mountain_biking_man':'\ud83d\udeb5', + 'mountain_biking_woman':'\ud83d\udeb5‍\u2640\ufe0f', + 'mountain_cableway':'\ud83d\udea0', + 'mountain_railway':'\ud83d\ude9e', + 'mountain_snow':'\ud83c\udfd4', + 'mouse':'\ud83d\udc2d', + 'mouse2':'\ud83d\udc01', + 'movie_camera':'\ud83c\udfa5', + 'moyai':'\ud83d\uddff', + 'mrs_claus':'\ud83e\udd36', + 'muscle':'\ud83d\udcaa', + 'mushroom':'\ud83c\udf44', + 'musical_keyboard':'\ud83c\udfb9', + 'musical_note':'\ud83c\udfb5', + 'musical_score':'\ud83c\udfbc', + 'mute':'\ud83d\udd07', + 'nail_care':'\ud83d\udc85', + 'name_badge':'\ud83d\udcdb', + 'national_park':'\ud83c\udfde', + 'nauseated_face':'\ud83e\udd22', + 'necktie':'\ud83d\udc54', + 'negative_squared_cross_mark':'\u274e', + 'nerd_face':'\ud83e\udd13', + 'neutral_face':'\ud83d\ude10', + 'new':'\ud83c\udd95', + 'new_moon':'\ud83c\udf11', + 'new_moon_with_face':'\ud83c\udf1a', + 'newspaper':'\ud83d\udcf0', + 'newspaper_roll':'\ud83d\uddde', + 'next_track_button':'\u23ed', + 'ng':'\ud83c\udd96', + 'no_good_man':'\ud83d\ude45‍\u2642\ufe0f', + 'no_good_woman':'\ud83d\ude45', + 'night_with_stars':'\ud83c\udf03', + 'no_bell':'\ud83d\udd15', + 'no_bicycles':'\ud83d\udeb3', + 'no_entry':'\u26d4\ufe0f', + 'no_entry_sign':'\ud83d\udeab', + 'no_mobile_phones':'\ud83d\udcf5', + 'no_mouth':'\ud83d\ude36', + 'no_pedestrians':'\ud83d\udeb7', + 'no_smoking':'\ud83d\udead', + 'non-potable_water':'\ud83d\udeb1', + 'nose':'\ud83d\udc43', + 'notebook':'\ud83d\udcd3', + 'notebook_with_decorative_cover':'\ud83d\udcd4', + 'notes':'\ud83c\udfb6', + 'nut_and_bolt':'\ud83d\udd29', + 'o':'\u2b55\ufe0f', + 'o2':'\ud83c\udd7e\ufe0f', + 'ocean':'\ud83c\udf0a', + 'octopus':'\ud83d\udc19', + 'oden':'\ud83c\udf62', + 'office':'\ud83c\udfe2', + 'oil_drum':'\ud83d\udee2', + 'ok':'\ud83c\udd97', + 'ok_hand':'\ud83d\udc4c', + 'ok_man':'\ud83d\ude46‍\u2642\ufe0f', + 'ok_woman':'\ud83d\ude46', + 'old_key':'\ud83d\udddd', + 'older_man':'\ud83d\udc74', + 'older_woman':'\ud83d\udc75', + 'om':'\ud83d\udd49', + 'on':'\ud83d\udd1b', + 'oncoming_automobile':'\ud83d\ude98', + 'oncoming_bus':'\ud83d\ude8d', + 'oncoming_police_car':'\ud83d\ude94', + 'oncoming_taxi':'\ud83d\ude96', + 'open_file_folder':'\ud83d\udcc2', + 'open_hands':'\ud83d\udc50', + 'open_mouth':'\ud83d\ude2e', + 'open_umbrella':'\u2602\ufe0f', + 'ophiuchus':'\u26ce', + 'orange_book':'\ud83d\udcd9', + 'orthodox_cross':'\u2626\ufe0f', + 'outbox_tray':'\ud83d\udce4', + 'owl':'\ud83e\udd89', + 'ox':'\ud83d\udc02', + 'package':'\ud83d\udce6', + 'page_facing_up':'\ud83d\udcc4', + 'page_with_curl':'\ud83d\udcc3', + 'pager':'\ud83d\udcdf', + 'paintbrush':'\ud83d\udd8c', + 'palm_tree':'\ud83c\udf34', + 'pancakes':'\ud83e\udd5e', + 'panda_face':'\ud83d\udc3c', + 'paperclip':'\ud83d\udcce', + 'paperclips':'\ud83d\udd87', + 'parasol_on_ground':'\u26f1', + 'parking':'\ud83c\udd7f\ufe0f', + 'part_alternation_mark':'\u303d\ufe0f', + 'partly_sunny':'\u26c5\ufe0f', + 'passenger_ship':'\ud83d\udef3', + 'passport_control':'\ud83d\udec2', + 'pause_button':'\u23f8', + 'peace_symbol':'\u262e\ufe0f', + 'peach':'\ud83c\udf51', + 'peanuts':'\ud83e\udd5c', + 'pear':'\ud83c\udf50', + 'pen':'\ud83d\udd8a', + 'pencil2':'\u270f\ufe0f', + 'penguin':'\ud83d\udc27', + 'pensive':'\ud83d\ude14', + 'performing_arts':'\ud83c\udfad', + 'persevere':'\ud83d\ude23', + 'person_fencing':'\ud83e\udd3a', + 'pouting_woman':'\ud83d\ude4e', + 'phone':'\u260e\ufe0f', + 'pick':'\u26cf', + 'pig':'\ud83d\udc37', + 'pig2':'\ud83d\udc16', + 'pig_nose':'\ud83d\udc3d', + 'pill':'\ud83d\udc8a', + 'pineapple':'\ud83c\udf4d', + 'ping_pong':'\ud83c\udfd3', + 'pisces':'\u2653\ufe0f', + 'pizza':'\ud83c\udf55', + 'place_of_worship':'\ud83d\uded0', + 'plate_with_cutlery':'\ud83c\udf7d', + 'play_or_pause_button':'\u23ef', + 'point_down':'\ud83d\udc47', + 'point_left':'\ud83d\udc48', + 'point_right':'\ud83d\udc49', + 'point_up':'\u261d\ufe0f', + 'point_up_2':'\ud83d\udc46', + 'police_car':'\ud83d\ude93', + 'policewoman':'\ud83d\udc6e‍\u2640\ufe0f', + 'poodle':'\ud83d\udc29', + 'popcorn':'\ud83c\udf7f', + 'post_office':'\ud83c\udfe3', + 'postal_horn':'\ud83d\udcef', + 'postbox':'\ud83d\udcee', + 'potable_water':'\ud83d\udeb0', + 'potato':'\ud83e\udd54', + 'pouch':'\ud83d\udc5d', + 'poultry_leg':'\ud83c\udf57', + 'pound':'\ud83d\udcb7', + 'rage':'\ud83d\ude21', + 'pouting_cat':'\ud83d\ude3e', + 'pouting_man':'\ud83d\ude4e‍\u2642\ufe0f', + 'pray':'\ud83d\ude4f', + 'prayer_beads':'\ud83d\udcff', + 'pregnant_woman':'\ud83e\udd30', + 'previous_track_button':'\u23ee', + 'prince':'\ud83e\udd34', + 'princess':'\ud83d\udc78', + 'printer':'\ud83d\udda8', + 'purple_heart':'\ud83d\udc9c', + 'purse':'\ud83d\udc5b', + 'pushpin':'\ud83d\udccc', + 'put_litter_in_its_place':'\ud83d\udeae', + 'question':'\u2753', + 'rabbit':'\ud83d\udc30', + 'rabbit2':'\ud83d\udc07', + 'racehorse':'\ud83d\udc0e', + 'racing_car':'\ud83c\udfce', + 'radio':'\ud83d\udcfb', + 'radio_button':'\ud83d\udd18', + 'radioactive':'\u2622\ufe0f', + 'railway_car':'\ud83d\ude83', + 'railway_track':'\ud83d\udee4', + 'rainbow':'\ud83c\udf08', + 'rainbow_flag':'\ud83c\udff3\ufe0f‍\ud83c\udf08', + 'raised_back_of_hand':'\ud83e\udd1a', + 'raised_hand_with_fingers_splayed':'\ud83d\udd90', + 'raised_hands':'\ud83d\ude4c', + 'raising_hand_woman':'\ud83d\ude4b', + 'raising_hand_man':'\ud83d\ude4b‍\u2642\ufe0f', + 'ram':'\ud83d\udc0f', + 'ramen':'\ud83c\udf5c', + 'rat':'\ud83d\udc00', + 'record_button':'\u23fa', + 'recycle':'\u267b\ufe0f', + 'red_circle':'\ud83d\udd34', + 'registered':'\u00ae\ufe0f', + 'relaxed':'\u263a\ufe0f', + 'relieved':'\ud83d\ude0c', + 'reminder_ribbon':'\ud83c\udf97', + 'repeat':'\ud83d\udd01', + 'repeat_one':'\ud83d\udd02', + 'rescue_worker_helmet':'\u26d1', + 'restroom':'\ud83d\udebb', + 'revolving_hearts':'\ud83d\udc9e', + 'rewind':'\u23ea', + 'rhinoceros':'\ud83e\udd8f', + 'ribbon':'\ud83c\udf80', + 'rice':'\ud83c\udf5a', + 'rice_ball':'\ud83c\udf59', + 'rice_cracker':'\ud83c\udf58', + 'rice_scene':'\ud83c\udf91', + 'right_anger_bubble':'\ud83d\uddef', + 'ring':'\ud83d\udc8d', + 'robot':'\ud83e\udd16', + 'rocket':'\ud83d\ude80', + 'rofl':'\ud83e\udd23', + 'roll_eyes':'\ud83d\ude44', + 'roller_coaster':'\ud83c\udfa2', + 'rooster':'\ud83d\udc13', + 'rose':'\ud83c\udf39', + 'rosette':'\ud83c\udff5', + 'rotating_light':'\ud83d\udea8', + 'round_pushpin':'\ud83d\udccd', + 'rowing_man':'\ud83d\udea3', + 'rowing_woman':'\ud83d\udea3‍\u2640\ufe0f', + 'rugby_football':'\ud83c\udfc9', + 'running_man':'\ud83c\udfc3', + 'running_shirt_with_sash':'\ud83c\udfbd', + 'running_woman':'\ud83c\udfc3‍\u2640\ufe0f', + 'sa':'\ud83c\ude02\ufe0f', + 'sagittarius':'\u2650\ufe0f', + 'sake':'\ud83c\udf76', + 'sandal':'\ud83d\udc61', + 'santa':'\ud83c\udf85', + 'satellite':'\ud83d\udce1', + 'saxophone':'\ud83c\udfb7', + 'school':'\ud83c\udfeb', + 'school_satchel':'\ud83c\udf92', + 'scissors':'\u2702\ufe0f', + 'scorpion':'\ud83e\udd82', + 'scorpius':'\u264f\ufe0f', + 'scream':'\ud83d\ude31', + 'scream_cat':'\ud83d\ude40', + 'scroll':'\ud83d\udcdc', + 'seat':'\ud83d\udcba', + 'secret':'\u3299\ufe0f', + 'see_no_evil':'\ud83d\ude48', + 'seedling':'\ud83c\udf31', + 'selfie':'\ud83e\udd33', + 'shallow_pan_of_food':'\ud83e\udd58', + 'shamrock':'\u2618\ufe0f', + 'shark':'\ud83e\udd88', + 'shaved_ice':'\ud83c\udf67', + 'sheep':'\ud83d\udc11', + 'shell':'\ud83d\udc1a', + 'shield':'\ud83d\udee1', + 'shinto_shrine':'\u26e9', + 'ship':'\ud83d\udea2', + 'shirt':'\ud83d\udc55', + 'shopping':'\ud83d\udecd', + 'shopping_cart':'\ud83d\uded2', + 'shower':'\ud83d\udebf', + 'shrimp':'\ud83e\udd90', + 'signal_strength':'\ud83d\udcf6', + 'six_pointed_star':'\ud83d\udd2f', + 'ski':'\ud83c\udfbf', + 'skier':'\u26f7', + 'skull':'\ud83d\udc80', + 'skull_and_crossbones':'\u2620\ufe0f', + 'sleeping':'\ud83d\ude34', + 'sleeping_bed':'\ud83d\udecc', + 'sleepy':'\ud83d\ude2a', + 'slightly_frowning_face':'\ud83d\ude41', + 'slightly_smiling_face':'\ud83d\ude42', + 'slot_machine':'\ud83c\udfb0', + 'small_airplane':'\ud83d\udee9', + 'small_blue_diamond':'\ud83d\udd39', + 'small_orange_diamond':'\ud83d\udd38', + 'small_red_triangle':'\ud83d\udd3a', + 'small_red_triangle_down':'\ud83d\udd3b', + 'smile':'\ud83d\ude04', + 'smile_cat':'\ud83d\ude38', + 'smiley':'\ud83d\ude03', + 'smiley_cat':'\ud83d\ude3a', + 'smiling_imp':'\ud83d\ude08', + 'smirk':'\ud83d\ude0f', + 'smirk_cat':'\ud83d\ude3c', + 'smoking':'\ud83d\udeac', + 'snail':'\ud83d\udc0c', + 'snake':'\ud83d\udc0d', + 'sneezing_face':'\ud83e\udd27', + 'snowboarder':'\ud83c\udfc2', + 'snowflake':'\u2744\ufe0f', + 'snowman':'\u26c4\ufe0f', + 'snowman_with_snow':'\u2603\ufe0f', + 'sob':'\ud83d\ude2d', + 'soccer':'\u26bd\ufe0f', + 'soon':'\ud83d\udd1c', + 'sos':'\ud83c\udd98', + 'sound':'\ud83d\udd09', + 'space_invader':'\ud83d\udc7e', + 'spades':'\u2660\ufe0f', + 'spaghetti':'\ud83c\udf5d', + 'sparkle':'\u2747\ufe0f', + 'sparkler':'\ud83c\udf87', + 'sparkles':'\u2728', + 'sparkling_heart':'\ud83d\udc96', + 'speak_no_evil':'\ud83d\ude4a', + 'speaker':'\ud83d\udd08', + 'speaking_head':'\ud83d\udde3', + 'speech_balloon':'\ud83d\udcac', + 'speedboat':'\ud83d\udea4', + 'spider':'\ud83d\udd77', + 'spider_web':'\ud83d\udd78', + 'spiral_calendar':'\ud83d\uddd3', + 'spiral_notepad':'\ud83d\uddd2', + 'spoon':'\ud83e\udd44', + 'squid':'\ud83e\udd91', + 'stadium':'\ud83c\udfdf', + 'star':'\u2b50\ufe0f', + 'star2':'\ud83c\udf1f', + 'star_and_crescent':'\u262a\ufe0f', + 'star_of_david':'\u2721\ufe0f', + 'stars':'\ud83c\udf20', + 'station':'\ud83d\ude89', + 'statue_of_liberty':'\ud83d\uddfd', + 'steam_locomotive':'\ud83d\ude82', + 'stew':'\ud83c\udf72', + 'stop_button':'\u23f9', + 'stop_sign':'\ud83d\uded1', + 'stopwatch':'\u23f1', + 'straight_ruler':'\ud83d\udccf', + 'strawberry':'\ud83c\udf53', + 'stuck_out_tongue':'\ud83d\ude1b', + 'stuck_out_tongue_closed_eyes':'\ud83d\ude1d', + 'stuck_out_tongue_winking_eye':'\ud83d\ude1c', + 'studio_microphone':'\ud83c\udf99', + 'stuffed_flatbread':'\ud83e\udd59', + 'sun_behind_large_cloud':'\ud83c\udf25', + 'sun_behind_rain_cloud':'\ud83c\udf26', + 'sun_behind_small_cloud':'\ud83c\udf24', + 'sun_with_face':'\ud83c\udf1e', + 'sunflower':'\ud83c\udf3b', + 'sunglasses':'\ud83d\ude0e', + 'sunny':'\u2600\ufe0f', + 'sunrise':'\ud83c\udf05', + 'sunrise_over_mountains':'\ud83c\udf04', + 'surfing_man':'\ud83c\udfc4', + 'surfing_woman':'\ud83c\udfc4‍\u2640\ufe0f', + 'sushi':'\ud83c\udf63', + 'suspension_railway':'\ud83d\ude9f', + 'sweat':'\ud83d\ude13', + 'sweat_drops':'\ud83d\udca6', + 'sweat_smile':'\ud83d\ude05', + 'sweet_potato':'\ud83c\udf60', + 'swimming_man':'\ud83c\udfca', + 'swimming_woman':'\ud83c\udfca‍\u2640\ufe0f', + 'symbols':'\ud83d\udd23', + 'synagogue':'\ud83d\udd4d', + 'syringe':'\ud83d\udc89', + 'taco':'\ud83c\udf2e', + 'tada':'\ud83c\udf89', + 'tanabata_tree':'\ud83c\udf8b', + 'taurus':'\u2649\ufe0f', + 'taxi':'\ud83d\ude95', + 'tea':'\ud83c\udf75', + 'telephone_receiver':'\ud83d\udcde', + 'telescope':'\ud83d\udd2d', + 'tennis':'\ud83c\udfbe', + 'tent':'\u26fa\ufe0f', + 'thermometer':'\ud83c\udf21', + 'thinking':'\ud83e\udd14', + 'thought_balloon':'\ud83d\udcad', + 'ticket':'\ud83c\udfab', + 'tickets':'\ud83c\udf9f', + 'tiger':'\ud83d\udc2f', + 'tiger2':'\ud83d\udc05', + 'timer_clock':'\u23f2', + 'tipping_hand_man':'\ud83d\udc81‍\u2642\ufe0f', + 'tired_face':'\ud83d\ude2b', + 'tm':'\u2122\ufe0f', + 'toilet':'\ud83d\udebd', + 'tokyo_tower':'\ud83d\uddfc', + 'tomato':'\ud83c\udf45', + 'tongue':'\ud83d\udc45', + 'top':'\ud83d\udd1d', + 'tophat':'\ud83c\udfa9', + 'tornado':'\ud83c\udf2a', + 'trackball':'\ud83d\uddb2', + 'tractor':'\ud83d\ude9c', + 'traffic_light':'\ud83d\udea5', + 'train':'\ud83d\ude8b', + 'train2':'\ud83d\ude86', + 'tram':'\ud83d\ude8a', + 'triangular_flag_on_post':'\ud83d\udea9', + 'triangular_ruler':'\ud83d\udcd0', + 'trident':'\ud83d\udd31', + 'triumph':'\ud83d\ude24', + 'trolleybus':'\ud83d\ude8e', + 'trophy':'\ud83c\udfc6', + 'tropical_drink':'\ud83c\udf79', + 'tropical_fish':'\ud83d\udc20', + 'truck':'\ud83d\ude9a', + 'trumpet':'\ud83c\udfba', + 'tulip':'\ud83c\udf37', + 'tumbler_glass':'\ud83e\udd43', + 'turkey':'\ud83e\udd83', + 'turtle':'\ud83d\udc22', + 'tv':'\ud83d\udcfa', + 'twisted_rightwards_arrows':'\ud83d\udd00', + 'two_hearts':'\ud83d\udc95', + 'two_men_holding_hands':'\ud83d\udc6c', + 'two_women_holding_hands':'\ud83d\udc6d', + 'u5272':'\ud83c\ude39', + 'u5408':'\ud83c\ude34', + 'u55b6':'\ud83c\ude3a', + 'u6307':'\ud83c\ude2f\ufe0f', + 'u6708':'\ud83c\ude37\ufe0f', + 'u6709':'\ud83c\ude36', + 'u6e80':'\ud83c\ude35', + 'u7121':'\ud83c\ude1a\ufe0f', + 'u7533':'\ud83c\ude38', + 'u7981':'\ud83c\ude32', + 'u7a7a':'\ud83c\ude33', + 'umbrella':'\u2614\ufe0f', + 'unamused':'\ud83d\ude12', + 'underage':'\ud83d\udd1e', + 'unicorn':'\ud83e\udd84', + 'unlock':'\ud83d\udd13', + 'up':'\ud83c\udd99', + 'upside_down_face':'\ud83d\ude43', + 'v':'\u270c\ufe0f', + 'vertical_traffic_light':'\ud83d\udea6', + 'vhs':'\ud83d\udcfc', + 'vibration_mode':'\ud83d\udcf3', + 'video_camera':'\ud83d\udcf9', + 'video_game':'\ud83c\udfae', + 'violin':'\ud83c\udfbb', + 'virgo':'\u264d\ufe0f', + 'volcano':'\ud83c\udf0b', + 'volleyball':'\ud83c\udfd0', + 'vs':'\ud83c\udd9a', + 'vulcan_salute':'\ud83d\udd96', + 'walking_man':'\ud83d\udeb6', + 'walking_woman':'\ud83d\udeb6‍\u2640\ufe0f', + 'waning_crescent_moon':'\ud83c\udf18', + 'waning_gibbous_moon':'\ud83c\udf16', + 'warning':'\u26a0\ufe0f', + 'wastebasket':'\ud83d\uddd1', + 'watch':'\u231a\ufe0f', + 'water_buffalo':'\ud83d\udc03', + 'watermelon':'\ud83c\udf49', + 'wave':'\ud83d\udc4b', + 'wavy_dash':'\u3030\ufe0f', + 'waxing_crescent_moon':'\ud83c\udf12', + 'wc':'\ud83d\udebe', + 'weary':'\ud83d\ude29', + 'wedding':'\ud83d\udc92', + 'weight_lifting_man':'\ud83c\udfcb\ufe0f', + 'weight_lifting_woman':'\ud83c\udfcb\ufe0f‍\u2640\ufe0f', + 'whale':'\ud83d\udc33', + 'whale2':'\ud83d\udc0b', + 'wheel_of_dharma':'\u2638\ufe0f', + 'wheelchair':'\u267f\ufe0f', + 'white_check_mark':'\u2705', + 'white_circle':'\u26aa\ufe0f', + 'white_flag':'\ud83c\udff3\ufe0f', + 'white_flower':'\ud83d\udcae', + 'white_large_square':'\u2b1c\ufe0f', + 'white_medium_small_square':'\u25fd\ufe0f', + 'white_medium_square':'\u25fb\ufe0f', + 'white_small_square':'\u25ab\ufe0f', + 'white_square_button':'\ud83d\udd33', + 'wilted_flower':'\ud83e\udd40', + 'wind_chime':'\ud83c\udf90', + 'wind_face':'\ud83c\udf2c', + 'wine_glass':'\ud83c\udf77', + 'wink':'\ud83d\ude09', + 'wolf':'\ud83d\udc3a', + 'woman':'\ud83d\udc69', + 'woman_artist':'\ud83d\udc69‍\ud83c\udfa8', + 'woman_astronaut':'\ud83d\udc69‍\ud83d\ude80', + 'woman_cartwheeling':'\ud83e\udd38‍\u2640\ufe0f', + 'woman_cook':'\ud83d\udc69‍\ud83c\udf73', + 'woman_facepalming':'\ud83e\udd26‍\u2640\ufe0f', + 'woman_factory_worker':'\ud83d\udc69‍\ud83c\udfed', + 'woman_farmer':'\ud83d\udc69‍\ud83c\udf3e', + 'woman_firefighter':'\ud83d\udc69‍\ud83d\ude92', + 'woman_health_worker':'\ud83d\udc69‍\u2695\ufe0f', + 'woman_judge':'\ud83d\udc69‍\u2696\ufe0f', + 'woman_juggling':'\ud83e\udd39‍\u2640\ufe0f', + 'woman_mechanic':'\ud83d\udc69‍\ud83d\udd27', + 'woman_office_worker':'\ud83d\udc69‍\ud83d\udcbc', + 'woman_pilot':'\ud83d\udc69‍\u2708\ufe0f', + 'woman_playing_handball':'\ud83e\udd3e‍\u2640\ufe0f', + 'woman_playing_water_polo':'\ud83e\udd3d‍\u2640\ufe0f', + 'woman_scientist':'\ud83d\udc69‍\ud83d\udd2c', + 'woman_shrugging':'\ud83e\udd37‍\u2640\ufe0f', + 'woman_singer':'\ud83d\udc69‍\ud83c\udfa4', + 'woman_student':'\ud83d\udc69‍\ud83c\udf93', + 'woman_teacher':'\ud83d\udc69‍\ud83c\udfeb', + 'woman_technologist':'\ud83d\udc69‍\ud83d\udcbb', + 'woman_with_turban':'\ud83d\udc73‍\u2640\ufe0f', + 'womans_clothes':'\ud83d\udc5a', + 'womans_hat':'\ud83d\udc52', + 'women_wrestling':'\ud83e\udd3c‍\u2640\ufe0f', + 'womens':'\ud83d\udeba', + 'world_map':'\ud83d\uddfa', + 'worried':'\ud83d\ude1f', + 'wrench':'\ud83d\udd27', + 'writing_hand':'\u270d\ufe0f', + 'x':'\u274c', + 'yellow_heart':'\ud83d\udc9b', + 'yen':'\ud83d\udcb4', + 'yin_yang':'\u262f\ufe0f', + 'yum':'\ud83d\ude0b', + 'zap':'\u26a1\ufe0f', + 'zipper_mouth_face':'\ud83e\udd10', + 'zzz':'\ud83d\udca4', + + /* special emojis :P */ + 'octocat': '', + 'showdown': '' + }; + + /** + * These are all the transformations that form block-level + * tags like paragraphs, headers, and list items. + */ + showdown.subParser('makehtml.blockGamut', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.blockGamut.before', text, options, globals).getText(); + + // we parse blockquotes first so that we can have headings and hrs + // inside blockquotes + text = showdown.subParser('makehtml.blockQuotes')(text, options, globals); + text = showdown.subParser('makehtml.headers')(text, options, globals); + + // Do Horizontal Rules: + text = showdown.subParser('makehtml.horizontalRule')(text, options, globals); + + text = showdown.subParser('makehtml.lists')(text, options, globals); + text = showdown.subParser('makehtml.codeBlocks')(text, options, globals); + text = showdown.subParser('makehtml.tables')(text, options, globals); + + // We already ran _HashHTMLBlocks() before, in Markdown(), but that + // was to escape raw HTML in the original Markdown source. This time, + // we're escaping the markup we've just created, so that we don't wrap + //

tags around block-level tags. + text = showdown.subParser('makehtml.hashHTMLBlocks')(text, options, globals); + text = showdown.subParser('makehtml.paragraphs')(text, options, globals); + + text = globals.converter._dispatch('makehtml.blockGamut.after', text, options, globals).getText(); + + return text; + }); + + showdown.subParser('makehtml.blockQuotes', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.blockQuotes.before', text, options, globals).getText(); + + // add a couple extra lines after the text and endtext mark + text = text + '\n\n'; + + var rgx = /(^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+/gm; + + if (options.splitAdjacentBlockquotes) { + rgx = /^ {0,3}>[\s\S]*?(?:\n\n)/gm; + } + + text = text.replace(rgx, function (bq) { + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + bq = bq.replace(/^[ \t]*>[ \t]?/gm, ''); // trim one level of quoting + + // attacklab: clean up hack + bq = bq.replace(/¨0/g, ''); + + bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines + bq = showdown.subParser('makehtml.githubCodeBlocks')(bq, options, globals); + bq = showdown.subParser('makehtml.blockGamut')(bq, options, globals); // recurse + + bq = bq.replace(/(^|\n)/g, '$1 '); + // These leading spaces screw with

 content, so we need to fix that:
+            bq = bq.replace(/(\s*
[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
+                var pre = m1;
+                // attacklab: hack around Konqueror 3.5.4 bug:
+                pre = pre.replace(/^  /mg, '¨0');
+                pre = pre.replace(/¨0/g, '');
+                return pre;
+            });
+
+            return showdown.subParser('makehtml.hashBlock')('
\n' + bq + '\n
', options, globals); + }); + + text = globals.converter._dispatch('makehtml.blockQuotes.after', text, options, globals).getText(); + return text; + }); + + /** + * Process Markdown `
` blocks.
+     */
+    showdown.subParser('makehtml.codeBlocks', function (text, options, globals) {
+        'use strict';
+
+        text = globals.converter._dispatch('makehtml.codeBlocks.before', text, options, globals).getText();
+
+        // sentinel workarounds for lack of \A and \Z, safari\khtml bug
+        text += '¨0';
+
+        var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g;
+        text = text.replace(pattern, function (wholeMatch, m1, m2) {
+            var codeblock = m1,
+                nextChar = m2,
+                end = '\n';
+
+            codeblock = showdown.subParser('makehtml.outdent')(codeblock, options, globals);
+            codeblock = showdown.subParser('makehtml.encodeCode')(codeblock, options, globals);
+            codeblock = showdown.subParser('makehtml.detab')(codeblock, options, globals);
+            codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
+            codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
+
+            if (options.omitExtraWLInCodeBlocks) {
+                end = '';
+            }
+
+            codeblock = '
' + codeblock + end + '
'; + + return showdown.subParser('makehtml.hashBlock')(codeblock, options, globals) + nextChar; + }); + + // strip sentinel + text = text.replace(/¨0/, ''); + + text = globals.converter._dispatch('makehtml.codeBlocks.after', text, options, globals).getText(); + return text; + }); + + /** + * + * * Backtick quotes are used for spans. + * + * * You can use multiple backticks as the delimiters if you want to + * include literal backticks in the code span. So, this input: + * + * Just type ``foo `bar` baz`` at the prompt. + * + * Will translate to: + * + *

Just type foo `bar` baz at the prompt.

+ * + * There's no arbitrary limit to the number of backticks you + * can use as delimters. If you need three consecutive backticks + * in your code, use four for delimiters, etc. + * + * * You can use spaces to get literal backticks at the edges: + * + * ... type `` `bar` `` ... + * + * Turns to: + * + * ... type `bar` ... + */ + showdown.subParser('makehtml.codeSpans', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.codeSpans.before', text, options, globals).getText(); + + if (typeof (text) === 'undefined') { + text = ''; + } + text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, + function (wholeMatch, m1, m2, m3) { + var c = m3; + c = c.replace(/^([ \t]*)/g, ''); // leading whitespace + c = c.replace(/[ \t]*$/g, ''); // trailing whitespace + c = showdown.subParser('makehtml.encodeCode')(c, options, globals); + c = m1 + '' + c + ''; + c = showdown.subParser('makehtml.hashHTMLSpans')(c, options, globals); + return c; + } + ); + + text = globals.converter._dispatch('makehtml.codeSpans.after', text, options, globals).getText(); + return text; + }); + + /** + * Create a full HTML document from the processed markdown + */ + showdown.subParser('makehtml.completeHTMLDocument', function (text, options, globals) { + 'use strict'; + + if (!options.completeHTMLDocument) { + return text; + } + + text = globals.converter._dispatch('makehtml.completeHTMLDocument.before', text, options, globals).getText(); + + var doctype = 'html', + doctypeParsed = '\n', + title = '', + charset = '\n', + lang = '', + metadata = ''; + + if (typeof globals.metadata.parsed.doctype !== 'undefined') { + doctypeParsed = '\n'; + doctype = globals.metadata.parsed.doctype.toString().toLowerCase(); + if (doctype === 'html' || doctype === 'html5') { + charset = ''; + } + } + + for (var meta in globals.metadata.parsed) { + if (globals.metadata.parsed.hasOwnProperty(meta)) { + switch (meta.toLowerCase()) { + case 'doctype': + break; + + case 'title': + title = '' + globals.metadata.parsed.title + '\n'; + break; + + case 'charset': + if (doctype === 'html' || doctype === 'html5') { + charset = '\n'; + } else { + charset = '\n'; + } + break; + + case 'language': + case 'lang': + lang = ' lang="' + globals.metadata.parsed[meta] + '"'; + metadata += '\n'; + break; + + default: + metadata += '\n'; + } + } + } + + text = doctypeParsed + '\n\n' + title + charset + metadata + '\n\n' + text.trim() + '\n\n'; + + text = globals.converter._dispatch('makehtml.completeHTMLDocument.after', text, options, globals).getText(); + return text; + }); + + /** + * Convert all tabs to spaces + */ + showdown.subParser('makehtml.detab', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.detab.before', text, options, globals).getText(); + + // expand first n-1 tabs + text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width + + // replace the nth with two sentinels + text = text.replace(/\t/g, '¨A¨B'); + + // use the sentinel to anchor our regex so it doesn't explode + text = text.replace(/¨B(.+?)¨A/g, function (wholeMatch, m1) { + var leadingText = m1, + numSpaces = 4 - leadingText.length % 4; // g_tab_width + + // there *must* be a better way to do this: + for (var i = 0; i < numSpaces; i++) { + leadingText += ' '; + } + + return leadingText; + }); + + // clean up sentinels + text = text.replace(/¨A/g, ' '); // g_tab_width + text = text.replace(/¨B/g, ''); + + text = globals.converter._dispatch('makehtml.detab.after', text, options, globals).getText(); + return text; + }); + + showdown.subParser('makehtml.ellipsis', function (text, options, globals) { + 'use strict'; + + if (!options.ellipsis) { + return text; + } + + text = globals.converter._dispatch('makehtml.ellipsis.before', text, options, globals).getText(); + + text = text.replace(/\.\.\./g, '…'); + + text = globals.converter._dispatch('makehtml.ellipsis.after', text, options, globals).getText(); + + return text; + }); + + /** + * Turn emoji codes into emojis + * + * List of supported emojis: https://github.com/showdownjs/showdown/wiki/Emojis + */ + showdown.subParser('makehtml.emoji', function (text, options, globals) { + 'use strict'; + + if (!options.emoji) { + return text; + } + + text = globals.converter._dispatch('makehtml.emoji.before', text, options, globals).getText(); + + var emojiRgx = /:([\S]+?):/g; + + text = text.replace(emojiRgx, function (wm, emojiCode) { + if (showdown.helper.emojis.hasOwnProperty(emojiCode)) { + return showdown.helper.emojis[emojiCode]; + } + return wm; + }); + + text = globals.converter._dispatch('makehtml.emoji.after', text, options, globals).getText(); + + return text; + }); + + /** + * Smart processing for ampersands and angle brackets that need to be encoded. + */ + showdown.subParser('makehtml.encodeAmpsAndAngles', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.encodeAmpsAndAngles.before', text, options, globals).getText(); + + // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + // http://bumppo.net/projects/amputator/ + text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&'); + + // Encode naked <'s + text = text.replace(/<(?![a-z\/?$!])/gi, '<'); + + // Encode < + text = text.replace(/ + text = text.replace(/>/g, '>'); + + text = globals.converter._dispatch('makehtml.encodeAmpsAndAngles.after', text, options, globals).getText(); + return text; + }); + + /** + * Returns the string, with after processing the following backslash escape sequences. + * + * attacklab: The polite way to do this is with the new escapeCharacters() function: + * + * text = escapeCharacters(text,"\\",true); + * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); + * + * ...but we're sidestepping its use of the (slow) RegExp constructor + * as an optimization for Firefox. This function gets called a LOT. + */ + showdown.subParser('makehtml.encodeBackslashEscapes', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.encodeBackslashEscapes.before', text, options, globals).getText(); + + text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback); + text = text.replace(/\\([`*_{}\[\]()>#+.!~=|:-])/g, showdown.helper.escapeCharactersCallback); + + text = globals.converter._dispatch('makehtml.encodeBackslashEscapes.after', text, options, globals).getText(); + return text; + }); + + /** + * Encode/escape certain characters inside Markdown code runs. + * The point is that in code, these characters are literals, + * and lose their special Markdown meanings. + */ + showdown.subParser('makehtml.encodeCode', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.encodeCode.before', text, options, globals).getText(); + + // Encode all ampersands; HTML entities are not + // entities within a Markdown code span. + text = text + .replace(/&/g, '&') + // Do the angle bracket song and dance: + .replace(//g, '>') + // Now, escape characters that are magic in Markdown: + .replace(/([*_{}\[\]\\=~-])/g, showdown.helper.escapeCharactersCallback); + + text = globals.converter._dispatch('makehtml.encodeCode.after', text, options, globals).getText(); + return text; + }); + + /** + * Within tags -- meaning between < and > -- encode [\ ` * _ ~ =] so they + * don't conflict with their use in Markdown for code, italics and strong. + */ + showdown.subParser('makehtml.escapeSpecialCharsWithinTagAttributes', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.escapeSpecialCharsWithinTagAttributes.before', text, options, globals).getText(); + + // Build a regex to find HTML tags. + var tags = /<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi, + comments = /-]|-[^>])(?:[^-]|-[^-])*)--)>/gi; + + text = text.replace(tags, function (wholeMatch) { + return wholeMatch + .replace(/(.)<\/?code>(?=.)/g, '$1`') + .replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback); + }); + + text = text.replace(comments, function (wholeMatch) { + return wholeMatch + .replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback); + }); + + text = globals.converter._dispatch('makehtml.escapeSpecialCharsWithinTagAttributes.after', text, options, globals).getText(); + return text; + }); + + /** + * Handle github codeblocks prior to running HashHTML so that + * HTML contained within the codeblock gets escaped properly + * Example: + * ```ruby + * def hello_world(x) + * puts "Hello, #{x}" + * end + * ``` + */ + showdown.subParser('makehtml.githubCodeBlocks', function (text, options, globals) { + 'use strict'; + + // early exit if option is not enabled + if (!options.ghCodeBlocks) { + return text; + } + + text = globals.converter._dispatch('makehtml.githubCodeBlocks.before', text, options, globals).getText(); + + text += '¨0'; + + text = text.replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g, function (wholeMatch, delim, language, codeblock) { + var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n'; + + // First parse the github code block + codeblock = showdown.subParser('makehtml.encodeCode')(codeblock, options, globals); + codeblock = showdown.subParser('makehtml.detab')(codeblock, options, globals); + codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines + codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace + + codeblock = '
' + codeblock + end + '
'; + + codeblock = showdown.subParser('makehtml.hashBlock')(codeblock, options, globals); + + // Since GHCodeblocks can be false positives, we need to + // store the primitive text and the parsed text in a global var, + // and then return a token + return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; + }); + + // attacklab: strip sentinel + text = text.replace(/¨0/, ''); + + return globals.converter._dispatch('makehtml.githubCodeBlocks.after', text, options, globals).getText(); + }); + + showdown.subParser('makehtml.hashBlock', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.hashBlock.before', text, options, globals).getText(); + text = text.replace(/(^\n+|\n+$)/g, ''); + text = '\n\n¨K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n'; + text = globals.converter._dispatch('makehtml.hashBlock.after', text, options, globals).getText(); + return text; + }); + + /** + * Hash and escape elements that should not be parsed as markdown + */ + showdown.subParser('makehtml.hashCodeTags', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.hashCodeTags.before', text, options, globals).getText(); + + var repFunc = function (wholeMatch, match, left, right) { + var codeblock = left + showdown.subParser('makehtml.encodeCode')(match, options, globals) + right; + return '¨C' + (globals.gHtmlSpans.push(codeblock) - 1) + 'C'; + }; + + // Hash naked + text = showdown.helper.replaceRecursiveRegExp(text, repFunc, ']*>', '', 'gim'); + + text = globals.converter._dispatch('makehtml.hashCodeTags.after', text, options, globals).getText(); + return text; + }); + + showdown.subParser('makehtml.hashElement', function (text, options, globals) { + 'use strict'; + + return function (wholeMatch, m1) { + var blockText = m1; + + // Undo double lines + blockText = blockText.replace(/\n\n/g, '\n'); + blockText = blockText.replace(/^\n/, ''); + + // strip trailing blank lines + blockText = blockText.replace(/\n+$/g, ''); + + // Replace the element text with a marker ("¨KxK" where x is its key) + blockText = '\n\n¨K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n'; + + return blockText; + }; + }); + + showdown.subParser('makehtml.hashHTMLBlocks', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.hashHTMLBlocks.before', text, options, globals).getText(); + + var blockTags = [ + 'pre', + 'div', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'blockquote', + 'table', + 'dl', + 'ol', + 'ul', + 'script', + 'noscript', + 'form', + 'fieldset', + 'iframe', + 'math', + 'style', + 'section', + 'header', + 'footer', + 'nav', + 'article', + 'aside', + 'address', + 'audio', + 'canvas', + 'figure', + 'hgroup', + 'output', + 'video', + 'details', + 'p' + ], + repFunc = function (wholeMatch, match, left, right) { + var txt = wholeMatch; + // check if this html element is marked as markdown + // if so, it's contents should be parsed as markdown + if (left.search(/\bmarkdown\b/) !== -1) { + txt = left + globals.converter.makeHtml(match) + right; + } + return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n'; + }; + + if (options.backslashEscapesHTMLTags) { + // encode backslash escaped HTML tags + text = text.replace(/\\<(\/?[^>]+?)>/g, function (wm, inside) { + return '<' + inside + '>'; + }); + } + + // hash HTML Blocks + for (var i = 0; i < blockTags.length; ++i) { + + var opTagPos, + rgx1 = new RegExp('^ {0,3}(<' + blockTags[i] + '\\b[^>]*>)', 'im'), + patLeft = '<' + blockTags[i] + '\\b[^>]*>', + patRight = ''; + // 1. Look for the first position of the first opening HTML tag in the text + while ((opTagPos = showdown.helper.regexIndexOf(text, rgx1)) !== -1) { + + // if the HTML tag is \ escaped, we need to escape it and break + + + //2. Split the text in that position + var subTexts = showdown.helper.splitAtIndex(text, opTagPos), + //3. Match recursively + newSubText1 = showdown.helper.replaceRecursiveRegExp(subTexts[1], repFunc, patLeft, patRight, 'im'); + + // prevent an infinite loop + if (newSubText1 === subTexts[1]) { + break; + } + text = subTexts[0].concat(newSubText1); + } + } + // HR SPECIAL CASE + text = text.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, + showdown.subParser('makehtml.hashElement')(text, options, globals)); + + // Special case for standalone HTML comments + text = showdown.helper.replaceRecursiveRegExp(text, function (txt) { + return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n'; + }, '^ {0,3}', 'gm'); + + // PHP and ASP-style processor instructions ( and <%...%>) + text = text.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, + showdown.subParser('makehtml.hashElement')(text, options, globals)); + + text = globals.converter._dispatch('makehtml.hashHTMLBlocks.after', text, options, globals).getText(); + return text; + }); + + /** + * Hash span elements that should not be parsed as markdown + */ + showdown.subParser('makehtml.hashHTMLSpans', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.hashHTMLSpans.before', text, options, globals).getText(); + + // Hash Self Closing tags + text = text.replace(/<[^>]+?\/>/gi, function (wm) { + return showdown.helper._hashHTMLSpan(wm, globals); + }); + + // Hash tags without properties + text = text.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g, function (wm) { + return showdown.helper._hashHTMLSpan(wm, globals); + }); + + // Hash tags with properties + text = text.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g, function (wm) { + return showdown.helper._hashHTMLSpan(wm, globals); + }); + + // Hash self closing tags without /> + text = text.replace(/<[^>]+?>/gi, function (wm) { + return showdown.helper._hashHTMLSpan(wm, globals); + }); + + text = globals.converter._dispatch('makehtml.hashHTMLSpans.after', text, options, globals).getText(); + return text; + }); + + /** + * Unhash HTML spans + */ + showdown.subParser('makehtml.unhashHTMLSpans', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.unhashHTMLSpans.before', text, options, globals).getText(); + + for (var i = 0; i < globals.gHtmlSpans.length; ++i) { + var repText = globals.gHtmlSpans[i], + // limiter to prevent infinite loop (assume 10 as limit for recurse) + limit = 0; + + while (/¨C(\d+)C/.test(repText)) { + var num = RegExp.$1; + repText = repText.replace('¨C' + num + 'C', globals.gHtmlSpans[num]); + if (limit === 10) { + console.error('maximum nesting of 10 spans reached!!!'); + break; + } + ++limit; + } + text = text.replace('¨C' + i + 'C', repText); + } + + text = globals.converter._dispatch('makehtml.unhashHTMLSpans.after', text, options, globals).getText(); + return text; + }); + + /** + * Hash and escape
 elements that should not be parsed as markdown
+     */
+    showdown.subParser('makehtml.hashPreCodeTags', function (text, options, globals) {
+        'use strict';
+        text = globals.converter._dispatch('makehtml.hashPreCodeTags.before', text, options, globals).getText();
+
+        var repFunc = function (wholeMatch, match, left, right) {
+            // encode html entities
+            var codeblock = left + showdown.subParser('makehtml.encodeCode')(match, options, globals) + right;
+            return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
+        };
+
+        // Hash 

+        text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}]*>\\s*]*>', '^ {0,3}\\s*
', 'gim'); + + text = globals.converter._dispatch('makehtml.hashPreCodeTags.after', text, options, globals).getText(); + return text; + }); + + showdown.subParser('makehtml.headers', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.headers.before', text, options, globals).getText(); + + var headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart), + + // Set text-style headers: + // Header 1 + // ======== + // + // Header 2 + // -------- + // + setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm, + setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm; + + text = text.replace(setextRegexH1, function (wholeMatch, m1) { + + var spanGamut = showdown.subParser('makehtml.spanGamut')(m1, options, globals), + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', + hLevel = headerLevelStart, + hashBlock = '' + spanGamut + ''; + return showdown.subParser('makehtml.hashBlock')(hashBlock, options, globals); + }); + + text = text.replace(setextRegexH2, function (matchFound, m1) { + var spanGamut = showdown.subParser('makehtml.spanGamut')(m1, options, globals), + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', + hLevel = headerLevelStart + 1, + hashBlock = '' + spanGamut + ''; + return showdown.subParser('makehtml.hashBlock')(hashBlock, options, globals); + }); + + // atx-style headers: + // # Header 1 + // ## Header 2 + // ## Header 2 with closing hashes ## + // ... + // ###### Header 6 + // + var atxStyle = (options.requireSpaceBeforeHeadingText) ? /^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm : /^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm; + + text = text.replace(atxStyle, function (wholeMatch, m1, m2) { + var hText = m2; + if (options.customizedHeaderId) { + hText = m2.replace(/\s?\{([^{]+?)}\s*$/, ''); + } + + var span = showdown.subParser('makehtml.spanGamut')(hText, options, globals), + hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"', + hLevel = headerLevelStart - 1 + m1.length, + header = '' + span + ''; + + return showdown.subParser('makehtml.hashBlock')(header, options, globals); + }); + + function headerId (m) { + var title, + prefix; + + // It is separate from other options to allow combining prefix and customized + if (options.customizedHeaderId) { + var match = m.match(/\{([^{]+?)}\s*$/); + if (match && match[1]) { + m = match[1]; + } + } + + title = m; + + // Prefix id to prevent causing inadvertent pre-existing style matches. + if (showdown.helper.isString(options.prefixHeaderId)) { + prefix = options.prefixHeaderId; + } else if (options.prefixHeaderId === true) { + prefix = 'section-'; + } else { + prefix = ''; + } + + if (!options.rawPrefixHeaderId) { + title = prefix + title; + } + + if (options.ghCompatibleHeaderId) { + title = title + .replace(/ /g, '-') + // replace previously escaped chars (&, ¨ and $) + .replace(/&/g, '') + .replace(/¨T/g, '') + .replace(/¨D/g, '') + // replace rest of the chars (&~$ are repeated as they might have been escaped) + // borrowed from github's redcarpet (some they should produce similar results) + .replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g, '') + .toLowerCase(); + } else if (options.rawHeaderId) { + title = title + .replace(/ /g, '-') + // replace previously escaped chars (&, ¨ and $) + .replace(/&/g, '&') + .replace(/¨T/g, '¨') + .replace(/¨D/g, '$') + // replace " and ' + .replace(/["']/g, '-') + .toLowerCase(); + } else { + title = title + .replace(/[^\w]/g, '') + .toLowerCase(); + } + + if (options.rawPrefixHeaderId) { + title = prefix + title; + } + + if (globals.hashLinkCounts[title]) { + title = title + '-' + (globals.hashLinkCounts[title]++); + } else { + globals.hashLinkCounts[title] = 1; + } + return title; + } + + text = globals.converter._dispatch('makehtml.headers.after', text, options, globals).getText(); + return text; + }); + + /** + * Turn Markdown horizontal rule shortcuts into
tags. + * + * Any 3 or more unindented consecutive hyphens, asterisks or underscores with or without a space beetween them + * in a single line is considered a horizontal rule + */ + showdown.subParser('makehtml.horizontalRule', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.horizontalRule.before', text, options, globals).getText(); + + var key = showdown.subParser('makehtml.hashBlock')('
', options, globals); + text = text.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm, key); + text = text.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm, key); + text = text.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm, key); + + text = globals.converter._dispatch('makehtml.horizontalRule.after', text, options, globals).getText(); + return text; + }); + + /** + * Turn Markdown image shortcuts into tags. + */ + showdown.subParser('makehtml.images', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.images.before', text, options, globals).getText(); + + var inlineRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g, + crazyRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g, + base64RegExp = /!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g, + referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g, + refShortcutRegExp = /!\[([^\[\]]+)]()()()()()/g; + + function writeImageTagBase64 (wholeMatch, altText, linkId, url, width, height, m5, title) { + url = url.replace(/\s/g, ''); + return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title); + } + + function writeImageTagBaseUrl (wholeMatch, altText, linkId, url, width, height, m5, title) { + url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url); + + return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title); + } + + function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) { + + var gUrls = globals.gUrls, + gTitles = globals.gTitles, + gDims = globals.gDimensions; + + linkId = linkId.toLowerCase(); + + if (!title) { + title = ''; + } + // Special case for explicit empty url + if (wholeMatch.search(/\(? ?(['"].*['"])?\)$/m) > -1) { + url = ''; + + } else if (url === '' || url === null) { + if (linkId === '' || linkId === null) { + // lower-case and turn embedded newlines into spaces + linkId = altText.toLowerCase().replace(/ ?\n/g, ' '); + } + url = '#' + linkId; + + if (!showdown.helper.isUndefined(gUrls[linkId])) { + url = gUrls[linkId]; + if (!showdown.helper.isUndefined(gTitles[linkId])) { + title = gTitles[linkId]; + } + if (!showdown.helper.isUndefined(gDims[linkId])) { + width = gDims[linkId].width; + height = gDims[linkId].height; + } + } else { + return wholeMatch; + } + } + + altText = altText + .replace(/"/g, '"') + //altText = showdown.helper.escapeCharacters(altText, '*_', false); + .replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback); + //url = showdown.helper.escapeCharacters(url, '*_', false); + url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback); + var result = '' + altText + 'x "optional title") + + // base64 encoded images + text = text.replace(base64RegExp, writeImageTagBase64); + + // cases with crazy urls like ./image/cat1).png + text = text.replace(crazyRegExp, writeImageTagBaseUrl); + + // normal cases + text = text.replace(inlineRegExp, writeImageTagBaseUrl); + + // handle reference-style shortcuts: ![img text] + text = text.replace(refShortcutRegExp, writeImageTag); + + text = globals.converter._dispatch('makehtml.images.after', text, options, globals).getText(); + return text; + }); + + showdown.subParser('makehtml.italicsAndBold', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.italicsAndBold.before', text, options, globals).getText(); + + // it's faster to have 3 separate regexes for each case than have just one + // because of backtracing, in some cases, it could lead to an exponential effect + // called "catastrophic backtrace". Ominous! + + function parseInside (txt, left, right) { + return left + txt + right; + } + + // Parse underscores + if (options.literalMidWordUnderscores) { + text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) { + return parseInside (txt, '', ''); + }); + text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) { + return parseInside (txt, '', ''); + }); + text = text.replace(/\b_(\S[\s\S]*?)_\b/g, function (wm, txt) { + return parseInside (txt, '', ''); + }); + } else { + text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) { + return (/\S$/.test(m)) ? parseInside (m, '', '') : wm; + }); + text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) { + return (/\S$/.test(m)) ? parseInside (m, '', '') : wm; + }); + text = text.replace(/_([^\s_][\s\S]*?)_/g, function (wm, m) { + // !/^_[^_]/.test(m) - test if it doesn't start with __ (since it seems redundant, we removed it) + return (/\S$/.test(m)) ? parseInside (m, '', '') : wm; + }); + } + + // Now parse asterisks + /* + if (options.literalMidWordAsterisks) { + text = text.replace(/([^*]|^)\B\*\*\*(\S[\s\S]+?)\*\*\*\B(?!\*)/g, function (wm, lead, txt) { + return parseInside (txt, lead + '', ''); + }); + text = text.replace(/([^*]|^)\B\*\*(\S[\s\S]+?)\*\*\B(?!\*)/g, function (wm, lead, txt) { + return parseInside (txt, lead + '', ''); + }); + text = text.replace(/([^*]|^)\B\*(\S[\s\S]+?)\*\B(?!\*)/g, function (wm, lead, txt) { + return parseInside (txt, lead + '', ''); + }); + } else { + */ + text = text.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g, function (wm, m) { + return (/\S$/.test(m)) ? parseInside (m, '', '') : wm; + }); + text = text.replace(/\*\*(\S[\s\S]*?)\*\*/g, function (wm, m) { + return (/\S$/.test(m)) ? parseInside (m, '', '') : wm; + }); + text = text.replace(/\*([^\s*][\s\S]*?)\*/g, function (wm, m) { + // !/^\*[^*]/.test(m) - test if it doesn't start with ** (since it seems redundant, we removed it) + return (/\S$/.test(m)) ? parseInside (m, '', '') : wm; + }); + //} + + text = globals.converter._dispatch('makehtml.italicsAndBold.after', text, options, globals).getText(); + return text; + }); + +//// +// makehtml/links.js +// Copyright (c) 2018 ShowdownJS +// +// Transforms MD links into `` html anchors +// +// A link contains link text (the visible text), a link destination (the URI that is the link destination), and +// optionally a link title. There are two basic kinds of links in Markdown. +// In inline links the destination and title are given immediately after the link text. +// In reference links the destination and title are defined elsewhere in the document. +// +// ***Author:*** +// - Estevão Soares dos Santos (Tivie) +//// + + (function () { + /** + * Helper function: Wrapper function to pass as second replace parameter + * + * @param {RegExp} rgx + * @param {string} evtRootName + * @param {{}} options + * @param {{}} globals + * @returns {Function} + */ + function replaceAnchorTagReference (rgx, evtRootName, options, globals, emptyCase) { + emptyCase = !!emptyCase; + return function (wholeMatch, text, id, url, m5, m6, title) { + // bail we we find 2 newlines somewhere + if (/\n\n/.test(wholeMatch)) { + return wholeMatch; + } + + var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, text, id, url, title, options, globals); + return writeAnchorTag(evt, options, globals, emptyCase); + }; + } + + function replaceAnchorTagBaseUrl (rgx, evtRootName, options, globals, emptyCase) { + return function (wholeMatch, text, id, url, m5, m6, title) { + url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url); + + var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, text, id, url, title, options, globals); + return writeAnchorTag(evt, options, globals, emptyCase); + }; + } + + /** + * TODO Normalize this + * Helper function: Create a capture event + * @param {RegExp} rgx + * @param {String} evtName Event name + * @param {String} wholeMatch + * @param {String} text + * @param {String} id + * @param {String} url + * @param {String} title + * @param {{}} options + * @param {{}} globals + * @returns {showdown.helper.Event|*} + */ + function createEvent (rgx, evtName, wholeMatch, text, id, url, title, options, globals) { + return globals.converter._dispatch(evtName, wholeMatch, options, globals, { + regexp: rgx, + matches: { + wholeMatch: wholeMatch, + text: text, + id: id, + url: url, + title: title + } + }); + } + + /** + * Helper Function: Normalize and write an anchor tag based on passed parameters + * @param evt + * @param options + * @param globals + * @param {boolean} emptyCase + * @returns {string} + */ + function writeAnchorTag (evt, options, globals, emptyCase) { + + var wholeMatch = evt.getMatches().wholeMatch; + var text = evt.getMatches().text; + var id = evt.getMatches().id; + var url = evt.getMatches().url; + var title = evt.getMatches().title; + var target = ''; + + if (!title) { + title = ''; + } + id = (id) ? id.toLowerCase() : ''; + + if (emptyCase) { + url = ''; + } else if (!url) { + if (!id) { + // lower-case and turn embedded newlines into spaces + id = text.toLowerCase().replace(/ ?\n/g, ' '); + } + url = '#' + id; + + if (!showdown.helper.isUndefined(globals.gUrls[id])) { + url = globals.gUrls[id]; + if (!showdown.helper.isUndefined(globals.gTitles[id])) { + title = globals.gTitles[id]; + } + } else { + return wholeMatch; + } + } + //url = showdown.helper.escapeCharacters(url, '*_:~', false); // replaced line to improve performance + url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback); + + if (title !== '' && title !== null) { + title = title.replace(/"/g, '"'); + //title = showdown.helper.escapeCharacters(title, '*_', false); // replaced line to improve performance + title = title.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback); + title = ' title="' + title + '"'; + } + + // optionLinksInNewWindow only applies + // to external links. Hash links (#) open in same page + if (options.openLinksInNewWindow && !/^#/.test(url)) { + // escaped _ + target = ' rel="noopener noreferrer" target="¨E95Eblank"'; + } + + // Text can be a markdown element, so we run through the appropriate parsers + text = showdown.subParser('makehtml.codeSpans')(text, options, globals); + text = showdown.subParser('makehtml.emoji')(text, options, globals); + text = showdown.subParser('makehtml.underline')(text, options, globals); + text = showdown.subParser('makehtml.italicsAndBold')(text, options, globals); + text = showdown.subParser('makehtml.strikethrough')(text, options, globals); + text = showdown.subParser('makehtml.ellipsis')(text, options, globals); + text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals); + + //evt = createEvent(rgx, evtRootName + '.captureEnd', wholeMatch, text, id, url, title, options, globals); + + var result = '' + text + ''; + + //evt = createEvent(rgx, evtRootName + '.beforeHash', wholeMatch, text, id, url, title, options, globals); + + result = showdown.subParser('makehtml.hashHTMLSpans')(result, options, globals); + + return result; + } + + var evtRootName = 'makehtml.links'; + + /** + * Turn Markdown link shortcuts into XHTML tags. + */ + showdown.subParser('makehtml.links', function (text, options, globals) { + + text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); + + // 1. Handle reference-style links: [link text] [id] + text = showdown.subParser('makehtml.links.reference')(text, options, globals); + + // 2. Handle inline-style links: [link text](url "optional title") + text = showdown.subParser('makehtml.links.inline')(text, options, globals); + + // 3. Handle reference-style shortcuts: [link text] + // These must come last in case there's a [link text][1] or [link text](/foo) + text = showdown.subParser('makehtml.links.referenceShortcut')(text, options, globals); + + // 4. Handle angle brackets links -> `` + // Must come after links, because you can use < and > delimiters in inline links like [this](). + text = showdown.subParser('makehtml.links.angleBrackets')(text, options, globals); + + // 5. Handle GithubMentions (if option is enabled) + text = showdown.subParser('makehtml.links.ghMentions')(text, options, globals); + + // 6. Handle tags and img tags + text = text.replace(/]*>[\s\S]*<\/a>/g, function (wholeMatch) { + return showdown.helper._hashHTMLSpan(wholeMatch, globals); + }); + + text = text.replace(/]*\/?>/g, function (wholeMatch) { + return showdown.helper._hashHTMLSpan(wholeMatch, globals); + }); + + // 7. Handle naked links (if option is enabled) + text = showdown.subParser('makehtml.links.naked')(text, options, globals); + + text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); + return text; + }); + + /** + * TODO WRITE THIS DOCUMENTATION + */ + showdown.subParser('makehtml.links.inline', function (text, options, globals) { + var evtRootName = evtRootName + '.inline'; + + text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); + + // 1. Look for empty cases: []() and [empty]() and []("title") + var rgxEmpty = /\[(.*?)]()()()()\(? ?(?:["'](.*)["'])?\)/g; + text = text.replace(rgxEmpty, replaceAnchorTagBaseUrl(rgxEmpty, evtRootName, options, globals, true)); + + // 2. Look for cases with crazy urls like ./image/cat1).png + var rgxCrazy = /\[((?:\[[^\]]*]|[^\[\]])*)]()\s?\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g; + text = text.replace(rgxCrazy, replaceAnchorTagBaseUrl(rgxCrazy, evtRootName, options, globals)); + + // 3. inline links with no title or titles wrapped in ' or ": + // [text](url.com) || [text]() || [text](url.com "title") || [text]( "title") + //var rgx2 = /\[[ ]*[\s]?[ ]*([^\n\[\]]*?)[ ]*[\s]?[ ]*] ?()\(?(?:[ ]*[\n]?[ ]*()(['"])(.*?)\5)?[ ]*[\s]?[ ]*\)/; // this regex is too slow!!! + var rgx2 = /\[([\S ]*?)]\s?()\( *?\s*(?:()(['"])(.*?)\5)? *\)/g; + text = text.replace(rgx2, replaceAnchorTagBaseUrl(rgx2, evtRootName, options, globals)); + + // 4. inline links with titles wrapped in (): [foo](bar.com (title)) + var rgx3 = /\[([\S ]*?)]\s?()\( *?\s+()()\((.*?)\) *\)/g; + text = text.replace(rgx3, replaceAnchorTagBaseUrl(rgx3, evtRootName, options, globals)); + + text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); + + return text; + }); + + /** + * TODO WRITE THIS DOCUMENTATION + */ + showdown.subParser('makehtml.links.reference', function (text, options, globals) { + var evtRootName = evtRootName + '.reference'; + + text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); + + var rgx = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g; + text = text.replace(rgx, replaceAnchorTagReference(rgx, evtRootName, options, globals)); + + text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); + + return text; + }); + + /** + * TODO WRITE THIS DOCUMENTATION + */ + showdown.subParser('makehtml.links.referenceShortcut', function (text, options, globals) { + var evtRootName = evtRootName + '.referenceShortcut'; + + text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); + + var rgx = /\[([^\[\]]+)]()()()()()/g; + text = text.replace(rgx, replaceAnchorTagReference(rgx, evtRootName, options, globals)); + + text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); + + return text; + }); + + /** + * TODO WRITE THIS DOCUMENTATION + */ + showdown.subParser('makehtml.links.ghMentions', function (text, options, globals) { + var evtRootName = evtRootName + 'ghMentions'; + + if (!options.ghMentions) { + return text; + } + + text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); + + var rgx = /(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d._-]+?[a-z\d]+)*))/gi; + + text = text.replace(rgx, function (wholeMatch, st, escape, mentions, username) { + // bail if the mentions was escaped + if (escape === '\\') { + return st + mentions; + } + + // check if options.ghMentionsLink is a string + // TODO Validation should be done at initialization not at runtime + if (!showdown.helper.isString(options.ghMentionsLink)) { + throw new Error('ghMentionsLink option must be a string'); + } + var url = options.ghMentionsLink.replace(/{u}/g, username); + var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, mentions, null, url, null, options, globals); + // captureEnd Event is triggered inside writeAnchorTag function + return st + writeAnchorTag(evt, options, globals); + }); + + text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); + + return text; + }); + + /** + * TODO WRITE THIS DOCUMENTATION + */ + showdown.subParser('makehtml.links.angleBrackets', function (text, options, globals) { + var evtRootName = 'makehtml.links.angleBrackets'; + + text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); + + // 1. Parse links first + var urlRgx = /<(((?:https?|ftp):\/\/|www\.)[^'">\s]+)>/gi; + text = text.replace(urlRgx, function (wholeMatch, url, urlStart) { + var text = url; + url = (urlStart === 'www.') ? 'http://' + url : url; + var evt = createEvent(urlRgx, evtRootName + '.captureStart', wholeMatch, text, null, url, null, options, globals); + return writeAnchorTag(evt, options, globals); + }); + + // 2. Then Mail Addresses + var mailRgx = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi; + text = text.replace(mailRgx, function (wholeMatch, mail) { + var url = 'mailto:'; + mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals); + if (options.encodeEmails) { + url = showdown.helper.encodeEmailAddress(url + mail); + mail = showdown.helper.encodeEmailAddress(mail); + } else { + url = url + mail; + } + var evt = createEvent(mailRgx, evtRootName + '.captureStart', wholeMatch, mail, null, url, null, options, globals); + return writeAnchorTag(evt, options, globals); + }); + + text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); + return text; + }); + + /** + * TODO MAKE THIS WORK (IT'S NOT ACTIVATED) + * TODO WRITE THIS DOCUMENTATION + */ + showdown.subParser('makehtml.links.naked', function (text, options, globals) { + if (!options.simplifiedAutoLink) { + return text; + } + + var evtRootName = 'makehtml.links.naked'; + + text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText(); + + // 2. Now we check for + // we also include leading markdown magic chars [_*~] for cases like __https://www.google.com/foobar__ + var urlRgx = /([_*~]*?)(((?:https?|ftp):\/\/|www\.)[^\s<>"'`´.-][^\s<>"'`´]*?\.[a-z\d.]+[^\s<>"']*)\1/gi; + text = text.replace(urlRgx, function (wholeMatch, leadingMDChars, url, urlPrefix) { + + // we now will start traversing the url from the front to back, looking for punctuation chars [_*~,;:.!?\)\]] + var len = url.length; + var suffix = ''; + for (var i = len - 1; i >= 0; --i) { + var char = url.charAt(i); + + if (/[_*~,;:.!?]/.test(char)) { + // it's a punctuation char + // we remove it from the url + url = url.slice(0, -1); + // and prepend it to the suffix + suffix = char + suffix; + } else if (/\)/.test(char)) { + var opPar = url.match(/\(/g) || []; + var clPar = url.match(/\)/g); + + // it's a curved parenthesis so we need to check for "balance" (kinda) + if (opPar.length < clPar.length) { + // there are more closing Parenthesis than opening so chop it!!!!! + url = url.slice(0, -1); + // and prepend it to the suffix + suffix = char + suffix; + } else { + // it's (kinda) balanced so our work is done + break; + } + } else if (/]/.test(char)) { + var opPar2 = url.match(/\[/g) || []; + var clPar2 = url.match(/\]/g); + // it's a squared parenthesis so we need to check for "balance" (kinda) + if (opPar2.length < clPar2.length) { + // there are more closing Parenthesis than opening so chop it!!!!! + url = url.slice(0, -1); + // and prepend it to the suffix + suffix = char + suffix; + } else { + // it's (kinda) balanced so our work is done + break; + } + } else { + // it's not a punctuation or a parenthesis so our work is done + break; + } + } + + // we copy the treated url to the text variable + var text = url; + // finally, if it's a www shortcut, we prepend http + url = (urlPrefix === 'www.') ? 'http://' + url : url; + + // url part is done so let's take care of text now + // we need to escape the text (because of links such as www.example.com/foo__bar__baz) + text = text.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback); + + // finally we dispatch the event + var evt = createEvent(urlRgx, evtRootName + '.captureStart', wholeMatch, text, null, url, null, options, globals); + + // and return the link tag, with the leadingMDChars and suffix. The leadingMDChars are added at the end too because + // we consumed those characters in the regexp + return leadingMDChars + writeAnchorTag(evt, options, globals) + suffix + leadingMDChars; + }); + + // 2. Then mails + var mailRgx = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi; + text = text.replace(mailRgx, function (wholeMatch, leadingChar, mail) { + var url = 'mailto:'; + mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals); + if (options.encodeEmails) { + url = showdown.helper.encodeEmailAddress(url + mail); + mail = showdown.helper.encodeEmailAddress(mail); + } else { + url = url + mail; + } + var evt = createEvent(mailRgx, evtRootName + '.captureStart', wholeMatch, mail, null, url, null, options, globals); + return leadingChar + writeAnchorTag(evt, options, globals); + }); + + + text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText(); + return text; + }); + })(); + + /** + * Form HTML ordered (numbered) and unordered (bulleted) lists. + */ + showdown.subParser('makehtml.lists', function (text, options, globals) { + 'use strict'; + + /** + * Process the contents of a single ordered or unordered list, splitting it + * into individual list items. + * @param {string} listStr + * @param {boolean} trimTrailing + * @returns {string} + */ + function processListItems (listStr, trimTrailing) { + // The $g_list_level global keeps track of when we're inside a list. + // Each time we enter a list, we increment it; when we leave a list, + // we decrement. If it's zero, we're not in a list anymore. + // + // We do this because when we're not inside a list, we want to treat + // something like this: + // + // I recommend upgrading to version + // 8. Oops, now this line is treated + // as a sub-list. + // + // As a single paragraph, despite the fact that the second line starts + // with a digit-period-space sequence. + // + // Whereas when we're inside a list (or sub-list), that line will be + // treated as the start of a sub-list. What a kludge, huh? This is + // an aspect of Markdown's syntax that's hard to parse perfectly + // without resorting to mind-reading. Perhaps the solution is to + // change the syntax rules such that sub-lists must start with a + // starting cardinal number; e.g. "1." or "a.". + globals.gListLevel++; + + // trim trailing blank lines: + listStr = listStr.replace(/\n{2,}$/, '\n'); + + // attacklab: add sentinel to emulate \z + listStr += '¨0'; + + var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm, + isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr)); + + // Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation, + // which is a syntax breaking change + // activating this option reverts to old behavior + // This will be removed in version 2.0 + if (options.disableForced4SpacesIndentedSublists) { + rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm; + } + + listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) { + checked = (checked && checked.trim() !== ''); + + var item = showdown.subParser('makehtml.outdent')(m4, options, globals), + bulletStyle = ''; + + // Support for github tasklists + if (taskbtn && options.tasklists) { + bulletStyle = ' class="task-list-item" style="list-style-type: none;"'; + item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () { + var otp = '
  • a
  • + // instead of: + //
    • - - a
    + // So, to prevent it, we will put a marker (¨A)in the beginning of the line + // Kind of hackish/monkey patching, but seems more effective than overcomplicating the list parser + item = item.replace(/^([-*+]|\d\.)[ \t]+[\S\n ]*/g, function (wm2) { + return '¨A' + wm2; + }); + + // SPECIAL CASE: an heading followed by a paragraph of text that is not separated by a double newline + // or/nor indented. ex: + // + // - # foo + // bar is great + // + // While this does now follow the spec per se, not allowing for this might cause confusion since + // header blocks don't need double newlines after + if (/^#+.+\n.+/.test(item)) { + item = item.replace(/^(#+.+)$/m, '$1\n'); + } + + // m1 - Leading line or + // Has a double return (multi paragraph) + if (m1 || (item.search(/\n{2,}/) > -1)) { + item = showdown.subParser('makehtml.githubCodeBlocks')(item, options, globals); + item = showdown.subParser('makehtml.blockGamut')(item, options, globals); + } else { + + // Recursion for sub-lists: + item = showdown.subParser('makehtml.lists')(item, options, globals); + item = item.replace(/\n$/, ''); // chomp(item) + item = showdown.subParser('makehtml.hashHTMLBlocks')(item, options, globals); + + // Colapse double linebreaks + item = item.replace(/\n\n+/g, '\n\n'); + + if (isParagraphed) { + item = showdown.subParser('makehtml.paragraphs')(item, options, globals); + } else { + item = showdown.subParser('makehtml.spanGamut')(item, options, globals); + } + } + + // now we need to remove the marker (¨A) + item = item.replace('¨A', ''); + // we can finally wrap the line in list item tags + item = '' + item + '\n'; + + return item; + }); + + // attacklab: strip sentinel + listStr = listStr.replace(/¨0/g, ''); + + globals.gListLevel--; + + if (trimTrailing) { + listStr = listStr.replace(/\s+$/, ''); + } + + return listStr; + } + + function styleStartNumber (list, listType) { + // check if ol and starts by a number different than 1 + if (listType === 'ol') { + var res = list.match(/^ *(\d+)\./); + if (res && res[1] !== '1') { + return ' start="' + res[1] + '"'; + } + } + return ''; + } + + /** + * Check and parse consecutive lists (better fix for issue #142) + * @param {string} list + * @param {string} listType + * @param {boolean} trimTrailing + * @returns {string} + */ + function parseConsecutiveLists (list, listType, trimTrailing) { + // check if we caught 2 or more consecutive lists by mistake + // we use the counterRgx, meaning if listType is UL we look for OL and vice versa + var olRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?\d+\.[ \t]/gm : /^ {0,3}\d+\.[ \t]/gm, + ulRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?[*+-][ \t]/gm : /^ {0,3}[*+-][ \t]/gm, + counterRxg = (listType === 'ul') ? olRgx : ulRgx, + result = ''; + + if (list.search(counterRxg) !== -1) { + (function parseCL (txt) { + var pos = txt.search(counterRxg), + style = styleStartNumber(list, listType); + if (pos !== -1) { + // slice + result += '\n\n<' + listType + style + '>\n' + processListItems(txt.slice(0, pos), !!trimTrailing) + '\n'; + + // invert counterType and listType + listType = (listType === 'ul') ? 'ol' : 'ul'; + counterRxg = (listType === 'ul') ? olRgx : ulRgx; + + //recurse + parseCL(txt.slice(pos)); + } else { + result += '\n\n<' + listType + style + '>\n' + processListItems(txt, !!trimTrailing) + '\n'; + } + })(list); + } else { + var style = styleStartNumber(list, listType); + result = '\n\n<' + listType + style + '>\n' + processListItems(list, !!trimTrailing) + '\n'; + } + + return result; + } + + // Start of list parsing + var subListRgx = /^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; + var mainListRgx = /(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; + + text = globals.converter._dispatch('lists.before', text, options, globals).getText(); + // add sentinel to hack around khtml/safari bug: + // http://bugs.webkit.org/show_bug.cgi?id=11231 + text += '¨0'; + + if (globals.gListLevel) { + text = text.replace(subListRgx, function (wholeMatch, list, m2) { + var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; + return parseConsecutiveLists(list, listType, true); + }); + } else { + text = text.replace(mainListRgx, function (wholeMatch, m1, list, m3) { + var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; + return parseConsecutiveLists(list, listType, false); + }); + } + + // strip sentinel + text = text.replace(/¨0/, ''); + text = globals.converter._dispatch('makehtml.lists.after', text, options, globals).getText(); + return text; + }); + + /** + * Parse metadata at the top of the document + */ + showdown.subParser('makehtml.metadata', function (text, options, globals) { + 'use strict'; + + if (!options.metadata) { + return text; + } + + text = globals.converter._dispatch('makehtml.metadata.before', text, options, globals).getText(); + + function parseMetadataContents (content) { + // raw is raw so it's not changed in any way + globals.metadata.raw = content; + + // escape chars forbidden in html attributes + // double quotes + content = content + // ampersand first + .replace(/&/g, '&') + // double quotes + .replace(/"/g, '"'); + + content = content.replace(/\n {4}/g, ' '); + content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) { + globals.metadata.parsed[key] = value; + return ''; + }); + } + + text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) { + parseMetadataContents(content); + return '¨M'; + }); + + text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) { + if (format) { + globals.metadata.format = format; + } + parseMetadataContents(content); + return '¨M'; + }); + + text = text.replace(/¨M/g, ''); + + text = globals.converter._dispatch('makehtml.metadata.after', text, options, globals).getText(); + return text; + }); + + /** + * Remove one level of line-leading tabs or spaces + */ + showdown.subParser('makehtml.outdent', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.outdent.before', text, options, globals).getText(); + + // attacklab: hack around Konqueror 3.5.4 bug: + // "----------bug".replace(/^-/g,"") == "bug" + text = text.replace(/^(\t|[ ]{1,4})/gm, '¨0'); // attacklab: g_tab_width + + // attacklab: clean up hack + text = text.replace(/¨0/g, ''); + + text = globals.converter._dispatch('makehtml.outdent.after', text, options, globals).getText(); + return text; + }); + + /** + * + */ + showdown.subParser('makehtml.paragraphs', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.paragraphs.before', text, options, globals).getText(); + // Strip leading and trailing lines: + text = text.replace(/^\n+/g, ''); + text = text.replace(/\n+$/g, ''); + + var grafs = text.split(/\n{2,}/g), + grafsOut = [], + end = grafs.length; // Wrap

    tags + + for (var i = 0; i < end; i++) { + var str = grafs[i]; + // if this is an HTML marker, copy it + if (str.search(/¨(K|G)(\d+)\1/g) >= 0) { + grafsOut.push(str); + + // test for presence of characters to prevent empty lines being parsed + // as paragraphs (resulting in undesired extra empty paragraphs) + } else if (str.search(/\S/) >= 0) { + str = showdown.subParser('makehtml.spanGamut')(str, options, globals); + str = str.replace(/^([ \t]*)/g, '

    '); + str += '

    '; + grafsOut.push(str); + } + } + + /** Unhashify HTML blocks */ + end = grafsOut.length; + for (i = 0; i < end; i++) { + var blockText = '', + grafsOutIt = grafsOut[i], + codeFlag = false; + // if this is a marker for an html block... + // use RegExp.test instead of string.search because of QML bug + while (/¨(K|G)(\d+)\1/.test(grafsOutIt)) { + var delim = RegExp.$1, + num = RegExp.$2; + + if (delim === 'K') { + blockText = globals.gHtmlBlocks[num]; + } else { + // we need to check if ghBlock is a false positive + if (codeFlag) { + // use encoded version of all text + blockText = showdown.subParser('makehtml.encodeCode')(globals.ghCodeBlocks[num].text, options, globals); + } else { + blockText = globals.ghCodeBlocks[num].codeblock; + } + } + blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs + + grafsOutIt = grafsOutIt.replace(/(\n\n)?¨(K|G)\d+\2(\n\n)?/, blockText); + // Check if grafsOutIt is a pre->code + if (/^]*>\s*]*>/.test(grafsOutIt)) { + codeFlag = true; + } + } + grafsOut[i] = grafsOutIt; + } + text = grafsOut.join('\n'); + // Strip leading and trailing lines: + text = text.replace(/^\n+/g, ''); + text = text.replace(/\n+$/g, ''); + return globals.converter._dispatch('makehtml.paragraphs.after', text, options, globals).getText(); + }); + + /** + * Run extension + */ + showdown.subParser('makehtml.runExtension', function (ext, text, options, globals) { + 'use strict'; + + if (ext.filter) { + text = ext.filter(text, globals.converter, options); + + } else if (ext.regex) { + // TODO remove this when old extension loading mechanism is deprecated + var re = ext.regex; + if (!(re instanceof RegExp)) { + re = new RegExp(re, 'g'); + } + text = text.replace(re, ext.replace); + } + + return text; + }); + + /** + * These are all the transformations that occur *within* block-level + * tags like paragraphs, headers, and list items. + */ + showdown.subParser('makehtml.spanGamut', function (text, options, globals) { + 'use strict'; + + text = globals.converter._dispatch('makehtml.span.before', text, options, globals).getText(); + + text = showdown.subParser('makehtml.codeSpans')(text, options, globals); + text = showdown.subParser('makehtml.escapeSpecialCharsWithinTagAttributes')(text, options, globals); + text = showdown.subParser('makehtml.encodeBackslashEscapes')(text, options, globals); + + // Process link and image tags. Images must come first, + // because ![foo][f] looks like a link. + text = showdown.subParser('makehtml.images')(text, options, globals); + + text = globals.converter._dispatch('smakehtml.links.before', text, options, globals).getText(); + text = showdown.subParser('makehtml.links')(text, options, globals); + text = globals.converter._dispatch('smakehtml.links.after', text, options, globals).getText(); + + //text = showdown.subParser('makehtml.autoLinks')(text, options, globals); + //text = showdown.subParser('makehtml.simplifiedAutoLinks')(text, options, globals); + text = showdown.subParser('makehtml.emoji')(text, options, globals); + text = showdown.subParser('makehtml.underline')(text, options, globals); + text = showdown.subParser('makehtml.italicsAndBold')(text, options, globals); + text = showdown.subParser('makehtml.strikethrough')(text, options, globals); + text = showdown.subParser('makehtml.ellipsis')(text, options, globals); + + // we need to hash HTML tags inside spans + text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals); + + // now we encode amps and angles + text = showdown.subParser('makehtml.encodeAmpsAndAngles')(text, options, globals); + + // Do hard breaks + if (options.simpleLineBreaks) { + // GFM style hard breaks + // only add line breaks if the text does not contain a block (special case for lists) + if (!/\n\n¨K/.test(text)) { + text = text.replace(/\n+/g, '
    \n'); + } + } else { + // Vanilla hard breaks + text = text.replace(/ +\n/g, '
    \n'); + } + + text = globals.converter._dispatch('makehtml.spanGamut.after', text, options, globals).getText(); + return text; + }); + + showdown.subParser('makehtml.strikethrough', function (text, options, globals) { + 'use strict'; + + if (options.strikethrough) { + text = globals.converter._dispatch('makehtml.strikethrough.before', text, options, globals).getText(); + text = text.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (wm, txt) { return '' + txt + ''; }); + text = globals.converter._dispatch('makehtml.strikethrough.after', text, options, globals).getText(); + } + + return text; + }); + + /** + * Strips link definitions from text, stores the URLs and titles in + * hash references. + * Link defs are in the form: ^[id]: url "optional title" + */ + showdown.subParser('makehtml.stripLinkDefinitions', function (text, options, globals) { + 'use strict'; + + var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm, + base64Regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm; + + // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug + text += '¨0'; + + var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) { + linkId = linkId.toLowerCase(); + if (url.match(/^data:.+?\/.+?;base64,/)) { + // remove newlines + globals.gUrls[linkId] = url.replace(/\s/g, ''); + } else { + url = showdown.helper.applyBaseUrl(options.relativePathBaseUrl, url); + + globals.gUrls[linkId] = showdown.subParser('makehtml.encodeAmpsAndAngles')(url, options, globals); // Link IDs are case-insensitive + } + + if (blankLines) { + // Oops, found blank lines, so it's not a title. + // Put back the parenthetical statement we stole. + return blankLines + title; + + } else { + if (title) { + globals.gTitles[linkId] = title.replace(/"|'/g, '"'); + } + if (options.parseImgDimensions && width && height) { + globals.gDimensions[linkId] = { + width: width, + height: height + }; + } + } + // Completely remove the definition from the text + return ''; + }; + + // first we try to find base64 link references + text = text.replace(base64Regex, replaceFunc); + + text = text.replace(regex, replaceFunc); + + // attacklab: strip sentinel + text = text.replace(/¨0/, ''); + + return text; + }); + + showdown.subParser('makehtml.tables', function (text, options, globals) { + 'use strict'; + + if (!options.tables) { + return text; + } + + var tableRgx = /^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm, + //singeColTblRgx = /^ {0,3}\|.+\|\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n(?: {0,3}\|.+\|\n)+(?:\n\n|¨0)/gm; + singeColTblRgx = /^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm; + + function parseStyles (sLine) { + if (/^:[ \t]*--*$/.test(sLine)) { + return ' style="text-align:left;"'; + } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) { + return ' style="text-align:right;"'; + } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) { + return ' style="text-align:center;"'; + } else { + return ''; + } + } + + function parseHeaders (header, style) { + var id = ''; + header = header.trim(); + // support both tablesHeaderId and tableHeaderId due to error in documentation so we don't break backwards compatibility + if (options.tablesHeaderId || options.tableHeaderId) { + id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; + } + header = showdown.subParser('makehtml.spanGamut')(header, options, globals); + + return '' + header + '\n'; + } + + function parseCells (cell, style) { + var subText = showdown.subParser('makehtml.spanGamut')(cell, options, globals); + return '' + subText + '\n'; + } + + function buildTable (headers, cells) { + var tb = '\n\n\n', + tblLgn = headers.length; + + for (var i = 0; i < tblLgn; ++i) { + tb += headers[i]; + } + tb += '\n\n\n'; + + for (i = 0; i < cells.length; ++i) { + tb += '\n'; + for (var ii = 0; ii < tblLgn; ++ii) { + tb += cells[i][ii]; + } + tb += '\n'; + } + tb += '\n
    \n'; + return tb; + } + + function parseTable (rawTable) { + var i, tableLines = rawTable.split('\n'); + + for (i = 0; i < tableLines.length; ++i) { + // strip wrong first and last column if wrapped tables are used + if (/^ {0,3}\|/.test(tableLines[i])) { + tableLines[i] = tableLines[i].replace(/^ {0,3}\|/, ''); + } + if (/\|[ \t]*$/.test(tableLines[i])) { + tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, ''); + } + // parse code spans first, but we only support one line code spans + + tableLines[i] = showdown.subParser('makehtml.codeSpans')(tableLines[i], options, globals); + } + + var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}), + rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}), + rawCells = [], + headers = [], + styles = [], + cells = []; + + tableLines.shift(); + tableLines.shift(); + + for (i = 0; i < tableLines.length; ++i) { + if (tableLines[i].trim() === '') { + continue; + } + rawCells.push( + tableLines[i] + .split('|') + .map(function (s) { + return s.trim(); + }) + ); + } + + if (rawHeaders.length < rawStyles.length) { + return rawTable; + } + + for (i = 0; i < rawStyles.length; ++i) { + styles.push(parseStyles(rawStyles[i])); + } + + for (i = 0; i < rawHeaders.length; ++i) { + if (showdown.helper.isUndefined(styles[i])) { + styles[i] = ''; + } + headers.push(parseHeaders(rawHeaders[i], styles[i])); + } + + for (i = 0; i < rawCells.length; ++i) { + var row = []; + for (var ii = 0; ii < headers.length; ++ii) { + if (showdown.helper.isUndefined(rawCells[i][ii])) { + + } + row.push(parseCells(rawCells[i][ii], styles[ii])); + } + cells.push(row); + } + + return buildTable(headers, cells); + } + + text = globals.converter._dispatch('makehtml.tables.before', text, options, globals).getText(); + + // find escaped pipe characters + text = text.replace(/\\(\|)/g, showdown.helper.escapeCharactersCallback); + + // parse multi column tables + text = text.replace(tableRgx, parseTable); + + // parse one column tables + text = text.replace(singeColTblRgx, parseTable); + + text = globals.converter._dispatch('makehtml.tables.after', text, options, globals).getText(); + + return text; + }); + + showdown.subParser('makehtml.underline', function (text, options, globals) { + 'use strict'; + + if (!options.underline) { + return text; + } + + text = globals.converter._dispatch('makehtml.underline.before', text, options, globals).getText(); + + if (options.literalMidWordUnderscores) { + text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) { + return '' + txt + ''; + }); + text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) { + return '' + txt + ''; + }); + } else { + text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) { + return (/\S$/.test(m)) ? '' + m + '' : wm; + }); + text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) { + return (/\S$/.test(m)) ? '' + m + '' : wm; + }); + } + + // escape remaining underscores to prevent them being parsed by italic and bold + text = text.replace(/(_)/g, showdown.helper.escapeCharactersCallback); + + text = globals.converter._dispatch('makehtml.underline.after', text, options, globals).getText(); + + return text; + }); + + /** + * Swap back in all the special characters we've hidden. + */ + showdown.subParser('makehtml.unescapeSpecialChars', function (text, options, globals) { + 'use strict'; + text = globals.converter._dispatch('makehtml.unescapeSpecialChars.before', text, options, globals).getText(); + + text = text.replace(/¨E(\d+)E/g, function (wholeMatch, m1) { + var charCodeToReplace = parseInt(m1); + return String.fromCharCode(charCodeToReplace); + }); + + text = globals.converter._dispatch('makehtml.unescapeSpecialChars.after', text, options, globals).getText(); + return text; + }); + + showdown.subParser('makeMarkdown.blockquote', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + var children = node.childNodes, + childrenLength = children.length; + + for (var i = 0; i < childrenLength; ++i) { + var innerTxt = showdown.subParser('makeMarkdown.node')(children[i], globals); + + if (innerTxt === '') { + continue; + } + txt += innerTxt; + } + } + // cleanup + txt = txt.trim(); + txt = '> ' + txt.split('\n').join('\n> '); + return txt; + }); + + showdown.subParser('makeMarkdown.break', function () { + 'use strict'; + + return ' \n'; + }); + + showdown.subParser('makeMarkdown.codeBlock', function (node, globals) { + 'use strict'; + + var lang = node.getAttribute('language'), + num = node.getAttribute('precodenum'); + return '```' + lang + '\n' + globals.preList[num] + '\n```'; + }); + + showdown.subParser('makeMarkdown.codeSpan', function (node) { + 'use strict'; + + return '`' + node.innerHTML + '`'; + }); + + showdown.subParser('makeMarkdown.emphasis', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + txt += '*'; + var children = node.childNodes, + childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + txt += '*'; + } + return txt; + }); + + showdown.subParser('makeMarkdown.header', function (node, globals, headerLevel) { + 'use strict'; + + var headerMark = new Array(headerLevel + 1).join('#'), + txt = ''; + + if (node.hasChildNodes()) { + txt = headerMark + ' '; + var children = node.childNodes, + childrenLength = children.length; + + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + } + return txt; + }); + + showdown.subParser('makeMarkdown.hr', function () { + 'use strict'; + + return '---'; + }); + + showdown.subParser('makeMarkdown.image', function (node) { + 'use strict'; + + var txt = ''; + if (node.hasAttribute('src')) { + txt += '![' + node.getAttribute('alt') + ']('; + txt += '<' + node.getAttribute('src') + '>'; + if (node.hasAttribute('width') && node.hasAttribute('height')) { + txt += ' =' + node.getAttribute('width') + 'x' + node.getAttribute('height'); + } + + if (node.hasAttribute('title')) { + txt += ' "' + node.getAttribute('title') + '"'; + } + txt += ')'; + } + return txt; + }); + + showdown.subParser('makeMarkdown.links', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes() && node.hasAttribute('href')) { + var children = node.childNodes, + childrenLength = children.length; + txt = '['; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + txt += ']('; + txt += '<' + node.getAttribute('href') + '>'; + if (node.hasAttribute('title')) { + txt += ' "' + node.getAttribute('title') + '"'; + } + txt += ')'; + } + return txt; + }); + + showdown.subParser('makeMarkdown.list', function (node, globals, type) { + 'use strict'; + + var txt = ''; + if (!node.hasChildNodes()) { + return ''; + } + var listItems = node.childNodes, + listItemsLenght = listItems.length, + listNum = node.getAttribute('start') || 1; + + for (var i = 0; i < listItemsLenght; ++i) { + if (typeof listItems[i].tagName === 'undefined' || listItems[i].tagName.toLowerCase() !== 'li') { + continue; + } + + // define the bullet to use in list + var bullet = ''; + if (type === 'ol') { + bullet = listNum.toString() + '. '; + } else { + bullet = '- '; + } + + // parse list item + txt += bullet + showdown.subParser('makeMarkdown.listItem')(listItems[i], globals); + ++listNum; + } + + return txt.trim(); + }); + + showdown.subParser('makeMarkdown.listItem', function (node, globals) { + 'use strict'; + + var listItemTxt = ''; + + var children = node.childNodes, + childrenLenght = children.length; + + for (var i = 0; i < childrenLenght; ++i) { + listItemTxt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + // if it's only one liner, we need to add a newline at the end + if (!/\n$/.test(listItemTxt)) { + listItemTxt += '\n'; + } else { + // it's multiparagraph, so we need to indent + listItemTxt = listItemTxt + .split('\n') + .join('\n ') + .replace(/^ {4}$/gm, '') + .replace(/\n\n+/g, '\n\n'); + } + + return listItemTxt; + }); + + + + showdown.subParser('makeMarkdown.node', function (node, globals, spansOnly) { + 'use strict'; + + spansOnly = spansOnly || false; + + var txt = ''; + + // edge case of text without wrapper paragraph + if (node.nodeType === 3) { + return showdown.subParser('makeMarkdown.txt')(node, globals); + } + + // HTML comment + if (node.nodeType === 8) { + return '\n\n'; + } + + // process only node elements + if (node.nodeType !== 1) { + return ''; + } + + var tagName = node.tagName.toLowerCase(); + + switch (tagName) { + + // + // BLOCKS + // + case 'h1': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 1) + '\n\n'; } + break; + case 'h2': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 2) + '\n\n'; } + break; + case 'h3': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 3) + '\n\n'; } + break; + case 'h4': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 4) + '\n\n'; } + break; + case 'h5': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 5) + '\n\n'; } + break; + case 'h6': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 6) + '\n\n'; } + break; + + case 'p': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.paragraph')(node, globals) + '\n\n'; } + break; + + case 'blockquote': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.blockquote')(node, globals) + '\n\n'; } + break; + + case 'hr': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.hr')(node, globals) + '\n\n'; } + break; + + case 'ol': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ol') + '\n\n'; } + break; + + case 'ul': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ul') + '\n\n'; } + break; + + case 'precode': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.codeBlock')(node, globals) + '\n\n'; } + break; + + case 'pre': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.pre')(node, globals) + '\n\n'; } + break; + + case 'table': + if (!spansOnly) { txt = showdown.subParser('makeMarkdown.table')(node, globals) + '\n\n'; } + break; + + // + // SPANS + // + case 'code': + txt = showdown.subParser('makeMarkdown.codeSpan')(node, globals); + break; + + case 'em': + case 'i': + txt = showdown.subParser('makeMarkdown.emphasis')(node, globals); + break; + + case 'strong': + case 'b': + txt = showdown.subParser('makeMarkdown.strong')(node, globals); + break; + + case 'del': + txt = showdown.subParser('makeMarkdown.strikethrough')(node, globals); + break; + + case 'a': + txt = showdown.subParser('makeMarkdown.links')(node, globals); + break; + + case 'img': + txt = showdown.subParser('makeMarkdown.image')(node, globals); + break; + + case 'br': + txt = showdown.subParser('makeMarkdown.break')(node, globals); + break; + + default: + txt = node.outerHTML + '\n\n'; + } + + // common normalization + // TODO eventually + + return txt; + }); + + showdown.subParser('makeMarkdown.paragraph', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + var children = node.childNodes, + childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + } + + // some text normalization + txt = txt.trim(); + + return txt; + }); + + showdown.subParser('makeMarkdown.pre', function (node, globals) { + 'use strict'; + + var num = node.getAttribute('prenum'); + return '
    ' + globals.preList[num] + '
    '; + }); + + showdown.subParser('makeMarkdown.strikethrough', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + txt += '~~'; + var children = node.childNodes, + childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + txt += '~~'; + } + return txt; + }); + + showdown.subParser('makeMarkdown.strong', function (node, globals) { + 'use strict'; + + var txt = ''; + if (node.hasChildNodes()) { + txt += '**'; + var children = node.childNodes, + childrenLength = children.length; + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals); + } + txt += '**'; + } + return txt; + }); + + showdown.subParser('makeMarkdown.table', function (node, globals) { + 'use strict'; + + var txt = '', + tableArray = [[], []], + headings = node.querySelectorAll('thead>tr>th'), + rows = node.querySelectorAll('tbody>tr'), + i, ii; + for (i = 0; i < headings.length; ++i) { + var headContent = showdown.subParser('makeMarkdown.tableCell')(headings[i], globals), + allign = '---'; + + if (headings[i].hasAttribute('style')) { + var style = headings[i].getAttribute('style').toLowerCase().replace(/\s/g, ''); + switch (style) { + case 'text-align:left;': + allign = ':---'; + break; + case 'text-align:right;': + allign = '---:'; + break; + case 'text-align:center;': + allign = ':---:'; + break; + } + } + tableArray[0][i] = headContent.trim(); + tableArray[1][i] = allign; + } + + for (i = 0; i < rows.length; ++i) { + var r = tableArray.push([]) - 1, + cols = rows[i].getElementsByTagName('td'); + + for (ii = 0; ii < headings.length; ++ii) { + var cellContent = ' '; + if (typeof cols[ii] !== 'undefined') { + cellContent = showdown.subParser('makeMarkdown.tableCell')(cols[ii], globals); + } + tableArray[r].push(cellContent); + } + } + + var cellSpacesCount = 3; + for (i = 0; i < tableArray.length; ++i) { + for (ii = 0; ii < tableArray[i].length; ++ii) { + var strLen = tableArray[i][ii].length; + if (strLen > cellSpacesCount) { + cellSpacesCount = strLen; + } + } + } + + for (i = 0; i < tableArray.length; ++i) { + for (ii = 0; ii < tableArray[i].length; ++ii) { + if (i === 1) { + if (tableArray[i][ii].slice(-1) === ':') { + tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii].slice(-1), cellSpacesCount - 1, '-') + ':'; + } else { + tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount, '-'); + } + } else { + tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount); + } + } + txt += '| ' + tableArray[i].join(' | ') + ' |\n'; + } + + return txt.trim(); + }); + + showdown.subParser('makeMarkdown.tableCell', function (node, globals) { + 'use strict'; + + var txt = ''; + if (!node.hasChildNodes()) { + return ''; + } + var children = node.childNodes, + childrenLength = children.length; + + for (var i = 0; i < childrenLength; ++i) { + txt += showdown.subParser('makeMarkdown.node')(children[i], globals, true); + } + return txt.trim(); + }); + + showdown.subParser('makeMarkdown.txt', function (node) { + 'use strict'; + + var txt = node.nodeValue; + + // multiple spaces are collapsed + txt = txt.replace(/ +/g, ' '); + + // replace the custom ¨NBSP; with a space + txt = txt.replace(/¨NBSP;/g, ' '); + + // ", <, > and & should replace escaped html entities + txt = showdown.helper.unescapeHTMLEntities(txt); + + // escape markdown magic characters + // emphasis, strong and strikethrough - can appear everywhere + // we also escape pipe (|) because of tables + // and escape ` because of code blocks and spans + txt = txt.replace(/([*_~|`])/g, '\\$1'); + + // escape > because of blockquotes + txt = txt.replace(/^(\s*)>/g, '\\$1>'); + + // hash character, only troublesome at the beginning of a line because of headers + txt = txt.replace(/^#/gm, '\\#'); + + // horizontal rules + txt = txt.replace(/^(\s*)([-=]{3,})(\s*)$/, '$1\\$2$3'); + + // dot, because of ordered lists, only troublesome at the beginning of a line when preceded by an integer + txt = txt.replace(/^( {0,3}\d+)\./gm, '$1\\.'); + + // +, * and -, at the beginning of a line becomes a list, so we need to escape them also (asterisk was already escaped) + txt = txt.replace(/^( {0,3})([+-])/gm, '$1\\$2'); + + // images and links, ] followed by ( is problematic, so we escape it + txt = txt.replace(/]([\s]*)\(/g, '\\]$1\\('); + + // reference URIs must also be escaped + txt = txt.replace(/^ {0,3}\[([\S \t]*?)]:/gm, '\\[$1]:'); + + return txt; + }); + + /** + * Created by Estevao on 31-05-2015. + */ + + /** + * Showdown Converter class + * @class + * @param {object} [converterOptions] + * @returns {Converter} + */ + showdown.Converter = function (converterOptions) { + 'use strict'; + + var + /** + * Options used by this converter + * @private + * @type {{}} + */ + options = {}, + + /** + * Language extensions used by this converter + * @private + * @type {Array} + */ + langExtensions = [], + + /** + * Output modifiers extensions used by this converter + * @private + * @type {Array} + */ + outputModifiers = [], + + /** + * Event listeners + * @private + * @type {{}} + */ + listeners = {}, + + /** + * The flavor set in this converter + */ + setConvFlavor = setFlavor, + + /** + * Metadata of the document + * @type {{parsed: {}, raw: string, format: string}} + */ + metadata = { + parsed: {}, + raw: '', + format: '' + }; + + _constructor(); + + /** + * Converter constructor + * @private + */ + function _constructor () { + converterOptions = converterOptions || {}; + + for (var gOpt in globalOptions) { + if (globalOptions.hasOwnProperty(gOpt)) { + options[gOpt] = globalOptions[gOpt]; + } + } + + // Merge options + if (typeof converterOptions === 'object') { + for (var opt in converterOptions) { + if (converterOptions.hasOwnProperty(opt)) { + options[opt] = converterOptions[opt]; + } + } + } else { + throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions + + ' was passed instead.'); + } + + if (options.extensions) { + showdown.helper.forEach(options.extensions, _parseExtension); + } + } + + /** + * Parse extension + * @param {*} ext + * @param {string} [name=''] + * @private + */ + function _parseExtension (ext, name) { + + name = name || null; + // If it's a string, the extension was previously loaded + if (showdown.helper.isString(ext)) { + ext = showdown.helper.stdExtName(ext); + name = ext; + + // LEGACY_SUPPORT CODE + if (showdown.extensions[ext]) { + console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' + + 'Please inform the developer that the extension should be updated!'); + legacyExtensionLoading(showdown.extensions[ext], ext); + return; + // END LEGACY SUPPORT CODE + + } else if (!showdown.helper.isUndefined(extensions[ext])) { + ext = extensions[ext]; + + } else { + throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.'); + } + } + + if (typeof ext === 'function') { + ext = ext(); + } + + if (!showdown.helper.isArray(ext)) { + ext = [ext]; + } + + var validExt = validate(ext, name); + if (!validExt.valid) { + throw Error(validExt.error); + } + + for (var i = 0; i < ext.length; ++i) { + switch (ext[i].type) { + + case 'lang': + langExtensions.push(ext[i]); + break; + + case 'output': + outputModifiers.push(ext[i]); + break; + } + if (ext[i].hasOwnProperty('listeners')) { + for (var ln in ext[i].listeners) { + if (ext[i].listeners.hasOwnProperty(ln)) { + listen(ln, ext[i].listeners[ln]); + } + } + } + } + + } + + /** + * LEGACY_SUPPORT + * @param {*} ext + * @param {string} name + */ + function legacyExtensionLoading (ext, name) { + if (typeof ext === 'function') { + ext = ext(new showdown.Converter()); + } + if (!showdown.helper.isArray(ext)) { + ext = [ext]; + } + var valid = validate(ext, name); + + if (!valid.valid) { + throw Error(valid.error); + } + + for (var i = 0; i < ext.length; ++i) { + switch (ext[i].type) { + case 'lang': + langExtensions.push(ext[i]); + break; + case 'output': + outputModifiers.push(ext[i]); + break; + default:// should never reach here + throw Error('Extension loader error: Type unrecognized!!!'); + } + } + } + + /** + * Listen to an event + * @param {string} name + * @param {function} callback + */ + function listen (name, callback) { + if (!showdown.helper.isString(name)) { + throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given'); + } + + if (typeof callback !== 'function') { + throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given'); + } + name = name.toLowerCase(); + if (!listeners.hasOwnProperty(name)) { + listeners[name] = []; + } + listeners[name].push(callback); + } + + function rTrimInputText (text) { + var rsp = text.match(/^\s*/)[0].length, + rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm'); + return text.replace(rgx, ''); + } + + /** + * + * @param {string} evtName Event name + * @param {string} text Text + * @param {{}} options Converter Options + * @param {{}} globals Converter globals + * @param {{}} pParams extra params for event + * @returns showdown.helper.Event + * @private + */ + this._dispatch = function dispatch (evtName, text, options, globals, pParams) { + evtName = evtName.toLowerCase(); + var params = pParams || {}; + params.converter = this; + params.text = text; + params.options = options; + params.globals = globals; + var event = new showdown.helper.Event(evtName, text, params); + + if (listeners.hasOwnProperty(evtName)) { + for (var ei = 0; ei < listeners[evtName].length; ++ei) { + var nText = listeners[evtName][ei](event); + if (nText && typeof nText !== 'undefined') { + event.setText(nText); + } + } + } + return event; + }; + + /** + * Listen to an event + * @param {string} name + * @param {function} callback + * @returns {showdown.Converter} + */ + this.listen = function (name, callback) { + listen(name, callback); + return this; + }; + + /** + * Converts a markdown string into HTML string + * @param {string} text + * @returns {*} + */ + this.makeHtml = function (text) { + //check if text is not falsy + if (!text) { + return text; + } + + var globals = { + gHtmlBlocks: [], + gHtmlMdBlocks: [], + gHtmlSpans: [], + gUrls: {}, + gTitles: {}, + gDimensions: {}, + gListLevel: 0, + hashLinkCounts: {}, + langExtensions: langExtensions, + outputModifiers: outputModifiers, + converter: this, + ghCodeBlocks: [], + metadata: { + parsed: {}, + raw: '', + format: '' + } + }; + + // This lets us use ¨ trema as an escape char to avoid md5 hashes + // The choice of character is arbitrary; anything that isn't + // magic in Markdown will work. + text = text.replace(/¨/g, '¨T'); + + // Replace $ with ¨D + // RegExp interprets $ as a special character + // when it's in a replacement string + text = text.replace(/\$/g, '¨D'); + + // Standardize line endings + text = text.replace(/\r\n/g, '\n'); // DOS to Unix + text = text.replace(/\r/g, '\n'); // Mac to Unix + + // Stardardize line spaces + text = text.replace(/\u00A0/g, ' '); + + if (options.smartIndentationFix) { + text = rTrimInputText(text); + } + + // Make sure text begins and ends with a couple of newlines: + text = '\n\n' + text + '\n\n'; + + // detab + text = showdown.subParser('makehtml.detab')(text, options, globals); + + /** + * Strip any lines consisting only of spaces and tabs. + * This makes subsequent regexs easier to write, because we can + * match consecutive blank lines with /\n+/ instead of something + * contorted like /[ \t]*\n+/ + */ + text = text.replace(/^[ \t]+$/mg, ''); + + //run languageExtensions + showdown.helper.forEach(langExtensions, function (ext) { + text = showdown.subParser('makehtml.runExtension')(ext, text, options, globals); + }); + + // run the sub parsers + text = showdown.subParser('makehtml.metadata')(text, options, globals); + text = showdown.subParser('makehtml.hashPreCodeTags')(text, options, globals); + text = showdown.subParser('makehtml.githubCodeBlocks')(text, options, globals); + text = showdown.subParser('makehtml.hashHTMLBlocks')(text, options, globals); + text = showdown.subParser('makehtml.hashCodeTags')(text, options, globals); + text = showdown.subParser('makehtml.stripLinkDefinitions')(text, options, globals); + text = showdown.subParser('makehtml.blockGamut')(text, options, globals); + text = showdown.subParser('makehtml.unhashHTMLSpans')(text, options, globals); + text = showdown.subParser('makehtml.unescapeSpecialChars')(text, options, globals); + + // attacklab: Restore dollar signs + text = text.replace(/¨D/g, '$$'); + + // attacklab: Restore tremas + text = text.replace(/¨T/g, '¨'); + + // render a complete html document instead of a partial if the option is enabled + text = showdown.subParser('makehtml.completeHTMLDocument')(text, options, globals); + + // Run output modifiers + showdown.helper.forEach(outputModifiers, function (ext) { + text = showdown.subParser('makehtml.runExtension')(ext, text, options, globals); + }); + + // update metadata + metadata = globals.metadata; + return text; + }; + + /** + * Converts an HTML string into a markdown string + * @param src + * @returns {string} + */ + this.makeMarkdown = function (src) { + + // replace \r\n with \n + src = src.replace(/\r\n/g, '\n'); + src = src.replace(/\r/g, '\n'); // old macs + + // due to an edge case, we need to find this: > < + // to prevent removing of non silent white spaces + // ex: this is sparta + src = src.replace(/>[ \t]+¨NBSP;<'); + + var doc = showdown.helper.document.createElement('div'); + doc.innerHTML = src; + + var globals = { + preList: substitutePreCodeTags(doc) + }; + + // remove all newlines and collapse spaces + clean(doc); + + // some stuff, like accidental reference links must now be escaped + // TODO + // doc.innerHTML = doc.innerHTML.replace(/\[[\S\t ]]/); + + var nodes = doc.childNodes, + mdDoc = ''; + + for (var i = 0; i < nodes.length; i++) { + mdDoc += showdown.subParser('makeMarkdown.node')(nodes[i], globals); + } + + function clean (node) { + for (var n = 0; n < node.childNodes.length; ++n) { + var child = node.childNodes[n]; + if (child.nodeType === 3) { + if (!/\S/.test(child.nodeValue) && !/^[ ]+$/.test(child.nodeValue)) { + node.removeChild(child); + --n; + } else { + child.nodeValue = child.nodeValue.split('\n').join(' '); + child.nodeValue = child.nodeValue.replace(/(\s)+/g, '$1'); + } + } else if (child.nodeType === 1) { + clean(child); + } + } + } + + // find all pre tags and replace contents with placeholder + // we need this so that we can remove all indentation from html + // to ease up parsing + function substitutePreCodeTags (doc) { + + var pres = doc.querySelectorAll('pre'), + presPH = []; + + for (var i = 0; i < pres.length; ++i) { + + if (pres[i].childElementCount === 1 && pres[i].firstChild.tagName.toLowerCase() === 'code') { + var content = pres[i].firstChild.innerHTML.trim(), + language = pres[i].firstChild.getAttribute('data-language') || ''; + + // if data-language attribute is not defined, then we look for class language-* + if (language === '') { + var classes = pres[i].firstChild.className.split(' '); + for (var c = 0; c < classes.length; ++c) { + var matches = classes[c].match(/^language-(.+)$/); + if (matches !== null) { + language = matches[1]; + break; + } + } + } + + // unescape html entities in content + content = showdown.helper.unescapeHTMLEntities(content); + + presPH.push(content); + pres[i].outerHTML = ''; + } else { + presPH.push(pres[i].innerHTML); + pres[i].innerHTML = ''; + pres[i].setAttribute('prenum', i.toString()); + } + } + return presPH; + } + + return mdDoc; + }; + + /** + * Set an option of this Converter instance + * @param {string} key + * @param {*} value + */ + this.setOption = function (key, value) { + options[key] = value; + }; + + /** + * Get the option of this Converter instance + * @param {string} key + * @returns {*} + */ + this.getOption = function (key) { + return options[key]; + }; + + /** + * Get the options of this Converter instance + * @returns {{}} + */ + this.getOptions = function () { + return options; + }; + + /** + * Add extension to THIS converter + * @param {{}} extension + * @param {string} [name=null] + */ + this.addExtension = function (extension, name) { + name = name || null; + _parseExtension(extension, name); + }; + + /** + * Use a global registered extension with THIS converter + * @param {string} extensionName Name of the previously registered extension + */ + this.useExtension = function (extensionName) { + _parseExtension(extensionName); + }; + + /** + * Set the flavor THIS converter should use + * @param {string} name + */ + this.setFlavor = function (name) { + if (!flavor.hasOwnProperty(name)) { + throw Error(name + ' flavor was not found'); + } + var preset = flavor[name]; + setConvFlavor = name; + for (var option in preset) { + if (preset.hasOwnProperty(option)) { + options[option] = preset[option]; + } + } + }; + + /** + * Get the currently set flavor of this converter + * @returns {string} + */ + this.getFlavor = function () { + return setConvFlavor; + }; + + /** + * Remove an extension from THIS converter. + * Note: This is a costly operation. It's better to initialize a new converter + * and specify the extensions you wish to use + * @param {Array} extension + */ + this.removeExtension = function (extension) { + if (!showdown.helper.isArray(extension)) { + extension = [extension]; + } + for (var a = 0; a < extension.length; ++a) { + var ext = extension[a]; + for (var i = 0; i < langExtensions.length; ++i) { + if (langExtensions[i] === ext) { + langExtensions[i].splice(i, 1); + } + } + for (var ii = 0; ii < outputModifiers.length; ++i) { + if (outputModifiers[ii] === ext) { + outputModifiers[ii].splice(i, 1); + } + } + } + }; + + /** + * Get all extension of THIS converter + * @returns {{language: Array, output: Array}} + */ + this.getAllExtensions = function () { + return { + language: langExtensions, + output: outputModifiers + }; + }; + + /** + * Get the metadata of the previously parsed document + * @param raw + * @returns {string|{}} + */ + this.getMetadata = function (raw) { + if (raw) { + return metadata.raw; + } else { + return metadata.parsed; + } + }; + + /** + * Get the metadata format of the previously parsed document + * @returns {string} + */ + this.getMetadataFormat = function () { + return metadata.format; + }; + + /** + * Private: set a single key, value metadata pair + * @param {string} key + * @param {string} value + */ + this._setMetadataPair = function (key, value) { + metadata.parsed[key] = value; + }; + + /** + * Private: set metadata format + * @param {string} format + */ + this._setMetadataFormat = function (format) { + metadata.format = format; + }; + + /** + * Private: set metadata raw text + * @param {string} raw + */ + this._setMetadataRaw = function (raw) { + metadata.raw = raw; + }; + }; + + var root = this; + +// AMD Loader + if (typeof define === 'function' && define.amd) { + define(function () { + 'use strict'; + return showdown; + }); + +// CommonJS/nodeJS Loader + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = showdown; + +// Regular Browser loader + } else { + root.showdown = showdown; + } +}).call(this); + +//# sourceMappingURL=showdown.js.map diff --git a/src/renderer/less/bootstrap.less b/src/renderer/less/bootstrap.less index 0a9be971..fccdd2c6 100644 --- a/src/renderer/less/bootstrap.less +++ b/src/renderer/less/bootstrap.less @@ -1,3 +1,498 @@ +// List Group +.list-group { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + border-radius: 0.25rem; +} + +.list-group-numbered { + list-style-type: none; + counter-reset: section; +} +.list-group-numbered > li::before { + content: counters(section, ".") ". "; + counter-increment: section; +} + +.list-group-item-action { + width: 100%; + color: #495057; + text-align: inherit; +} +.list-group-item-action:hover, .list-group-item-action:focus { + z-index: 1; + color: #495057; + text-decoration: none; + background-color: #f8f9fa; +} +.list-group-item-action:active { + color: #212529; + background-color: #e9ecef; +} + +.list-group-item { + position: relative; + display: block; + padding: 0.5rem 1rem; + color: #212529; + text-decoration: none; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.125); +} +.list-group-item:first-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} +.list-group-item:last-child { + border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; +} +.list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: #fff; +} +.list-group-item.active { + z-index: 2; + color: #fff; + background-color: #0d6efd; + border-color: #0d6efd; +} +.list-group-item + .list-group-item { + border-top-width: 0; +} +.list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px; +} + +.list-group-horizontal { + flex-direction: row; +} +.list-group-horizontal > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; +} +.list-group-horizontal > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; +} +.list-group-horizontal > .list-group-item.active { + margin-top: 0; +} +.list-group-horizontal > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; +} +.list-group-horizontal > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; +} + +@media (min-width: 576px) { + .list-group-horizontal-sm { + flex-direction: row; + } + .list-group-horizontal-sm > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-sm > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-sm > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-sm > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-sm > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +@media (min-width: 768px) { + .list-group-horizontal-md { + flex-direction: row; + } + .list-group-horizontal-md > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-md > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-md > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-md > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-md > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +@media (min-width: 992px) { + .list-group-horizontal-lg { + flex-direction: row; + } + .list-group-horizontal-lg > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-lg > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-lg > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-lg > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-lg > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +@media (min-width: 1200px) { + .list-group-horizontal-xl { + flex-direction: row; + } + .list-group-horizontal-xl > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-xl > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-xl > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-xl > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-xl > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +@media (min-width: 1400px) { + .list-group-horizontal-xxl { + flex-direction: row; + } + .list-group-horizontal-xxl > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-xxl > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-xxl > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-xxl > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} +.list-group-flush { + border-radius: 0; +} +.list-group-flush > .list-group-item { + border-width: 0 0 1px; +} +.list-group-flush > .list-group-item:last-child { + border-bottom-width: 0; +} + +.list-group-item-primary { + color: #084298; + background-color: #cfe2ff; +} +.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { + color: #084298; + background-color: #bacbe6; +} +.list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #084298; + border-color: #084298; +} + +.list-group-item-secondary { + color: #41464b; + background-color: #e2e3e5; +} +.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { + color: #41464b; + background-color: #cbccce; +} +.list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #41464b; + border-color: #41464b; +} + +.list-group-item-success { + color: #0f5132; + background-color: #d1e7dd; +} +.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { + color: #0f5132; + background-color: #bcd0c7; +} +.list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #0f5132; + border-color: #0f5132; +} + +.list-group-item-info { + color: #055160; + background-color: #cff4fc; +} +.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { + color: #055160; + background-color: #badce3; +} +.list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #055160; + border-color: #055160; +} + +.list-group-item-warning { + color: #664d03; + background-color: #fff3cd; +} +.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { + color: #664d03; + background-color: #e6dbb9; +} +.list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #664d03; + border-color: #664d03; +} + +.list-group-item-danger { + color: #842029; + background-color: #f8d7da; +} +.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { + color: #842029; + background-color: #dfc2c4; +} +.list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #842029; + border-color: #842029; +} + +.list-group-item-light { + color: #636464; + background-color: #fefefe; +} +.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { + color: #636464; + background-color: #e5e5e5; +} +.list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #636464; + border-color: #636464; +} + +.list-group-item-dark { + color: var(--textColor); + background-color: #333; +} +.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { + color: #141619; + background-color: #bebebf; +} +.list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #141619; + border-color: #141619; +} + + +// Card +.card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #333; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; +} +.card > hr { + margin-right: 0; + margin-left: 0; +} +.card > .list-group { + border-top: inherit; + border-bottom: inherit; +} +.card > .list-group:first-child { + border-top-width: 0; + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); +} +.card > .list-group:last-child { + border-bottom-width: 0; + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); +} +.card > .card-header + .list-group, +.card > .list-group + .card-footer { + border-top: 0; +} + +.card-body { + flex: 1 1 auto; + padding: 1rem 1rem; +} + +.card-title { + margin-bottom: 0.5rem; +} + +.card-subtitle { + margin-top: -0.25rem; + margin-bottom: 0; +} + +.card-text:last-child { + margin-bottom: 0; +} + +.card-link + .card-link { + margin-left: 1rem; +} + +.card-header { + padding: 0.5rem 1rem; + margin-bottom: 0; + background-color: rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); +} +.card-header:first-child { + border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; +} + +.card-footer { + padding: 0.5rem 1rem; + background-color: rgba(0, 0, 0, 0.03); + border-top: 1px solid rgba(0, 0, 0, 0.125); +} +.card-footer:last-child { + border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); +} + +.card-header-tabs { + margin-right: -0.5rem; + margin-bottom: -0.5rem; + margin-left: -0.5rem; + border-bottom: 0; +} + +.card-header-pills { + margin-right: -0.5rem; + margin-left: -0.5rem; +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1rem; + border-radius: calc(0.25rem - 1px); +} + +.card-img, +.card-img-top, +.card-img-bottom { + width: 100%; +} + +.card-img, +.card-img-top { + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); +} + +.card-img, +.card-img-bottom { + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); +} + +.card-group > .card { + margin-bottom: 0.75rem; +} +@media (min-width: 576px) { + .card-group { + display: flex; + flex-flow: row wrap; + } + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; + } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; + } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; + } + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { + border-bottom-right-radius: 0; + } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; + } + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; + } +} + + .modal { position: fixed; top: 0; diff --git a/src/renderer/less/elements.less b/src/renderer/less/elements.less new file mode 100644 index 00000000..fa8c617d --- /dev/null +++ b/src/renderer/less/elements.less @@ -0,0 +1,1756 @@ +// Form + +.md-select { + padding: 6px; + border-radius: 6px; + border: 1px solid rgba(200, 200, 200, 0.1); + border-top: 1px solid rgba(100, 100, 100, 0.5); + font-family: inherit; + font-size: 14px; + background: rgba(100, 100, 100, 0.25); + color: #eee; + + option { + font-size: 1em; + font-family: inherit; + padding: 8px 16px; + background: #404040; + } + + optgroup { + background: #2c2c2c; + } + + &:focus { + outline: solid 1px var(--selected); + } +} + +// Buttons +.md-btn { + font-family: inherit; + background: rgb(100 100 100 / 25%); + padding: 8px 14px; + border-radius: 6px; + font-size: 14px; + border: 1px solid rgba(100, 100, 100, 0.35); + border-top: 1px solid rgba(100, 100, 100, 0.5); + color: #eee; + white-space: nowrap; + transition: transform 0.2s var(--appleEase), box-shadow 0.2s var(--appleEase); + + &.md-btn-block { + display: block; + width: 100%; + } + + &.md-btn-glyph { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + } + + &.md-btn-primary { + background: #ff2b52a6; + color: white; + border: 1px solid rgb(220 53 69 / 25%); + border-top: 1px solid rgb(220 53 69 / 50%); + } + + &.md-btn-small { + padding: 6px 8px; + font-size: 13px; + } + + &:hover { + filter: brightness(125%); + } + + &:active { + filter: brightness(75%); + transform: scale(0.98); + transition: transform 0s var(--appleEase), box-shadow 0.2s var(--appleEase); + } + + &.md-btn-icon { + display: inline-flex; + vertical-align: middle; + justify-content: center; + + > img { + margin: 0px 16px 0px 0px; + pointer-events: none; + } + + > .md-btn-text { + margin: 0px; + } + } +} + +.btn-group { + display: inline-flex; + justify-content: center; + align-items: center; + + > .md-btn { + border-radius: 0px; + width: 100%; + } + + > .md-btn:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; + } + + > .md-btn:last-child { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + } + + > .md-btn:not(:first-child):not(:last-child) { + border-radius: 0px; + } +} + + +.md-close-btn { + -webkit-mask-image: url("ameres://icons/webui/close.svg"); + -webkit-mask-repeat: no-repeat; + -webkit-mask-position: center; + background-color: white; + opacity: 0.75; + -webkit-mask-size: contain; + height: 18px; + width: 18px; +} + +.md-ico-play { + content: url("./assets/play.svg"); + width: 10px; + height: 12px; + margin-right: 1px; + align-self: center; +} + +.md-ico-shuffle { + content: url("./assets/shuffle.svg"); + width: 1em; + height: 1em; + margin-right: 1px; + margin-bottom: -2px; + align-self: center; +} + +.md-ico-remove { + content: url("./assets/feather/x-circle-white.svg"); + width: 16px; + height: 16px; + margin-right: 1px; + margin-bottom: -1.5px; + align-self: center; +} + +.md-ico-add { + content: url("./assets/feather/plus-circle-white.svg"); + width: 1em; + height: 1em; + margin-right: 1px; + margin-bottom: -1.5px; + align-self: center; +} + +.reload-btn { + background: rgb(86 86 86 / 52%); + border-radius: 100%; + width: 32px; + height: 32px; + border: 0px; + appearance: none; + display: flex; + justify-content: center; + align-items: center; +} + +.reload-btn:hover { + background: rgb(86 86 86 / 80%); + cursor: pointer; +} + +.reload-btn > svg { + height: 50%; + color: #eee; +} + +.wr-btn { + font-family: inherit; + appearance: none; + border: 0px; + border-radius: 6px; + padding: 8px; + font-weight: 600; + background: rgb(80 80 80 / 70%); + color: white; +} + +.cd-btn-seeall { + background: transparent; + border: 0px; + color: var(--keyColor); + font-family: inherit; + font-weight: 500; + font-size: 16px; + border-radius: 4px; + padding: 6px; + + &:hover { + cursor: pointer; + background: rgb(200 200 200 / 10%) + } +} + + +// Media Item Elements + +.mediaitem-artwork { + border-radius: var(--mediaItemRadius); + overflow: hidden; + flex: 0 0 auto; + position: relative; + width: 100%; + height: 100%; + background-image: url("https://beta.music.apple.com/assets/product/MissingArtworkMusic.svg"); + background-size: cover; + background-position: center; + + .animatedartwork-view-box { + position: absolute; + top: 0px; + width: 100%; + height: 100%; + + .animated { + position: absolute; + top: 0px; + width: 100%; + height: 100%; + + video { + width: 100%; + height: 100%; + } + } + } + + &.rounded { + border-radius: 100%; + + &::after { + border-radius: 100%; + } + } + + &::after { + content: ""; + box-shadow: var(--mediaItemShadow); + z-index: 1; + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + border-radius: inherit; + } + + img { + width: 100%; + height: 100%; + object-fit: cover; + image-rendering: -webkit-optimize-contrast; + pointer-events: none; + } + + &.no-shadow { + box-shadow: none; + + &::after { + display: none; + } + } + + &.subtle-shadow { + box-shadow: var(--mediaItemShadow-ShadowSubtle); + } + + &.shadow { + box-shadow: var(--mediaItemShadow-Shadow); + } +} + +/* queue item */ +.cd-queue-item { + border-bottom: 1px solid rgb(200 200 200 / 10%); + padding: 8px; + + .row, + .col { + padding: 0px; + margin: 0px; + } + + .artwork { + width: 32px; + height: 32px; + flex: 0 0 auto; + } + + &.selected { + background: var(--selected); + } + + &:active { + background: var(--selected-click); + color: #eee; + } + + .queue-info { + display: flex; + flex-direction: column; + + .queue-title { + font-size: 14px; + } + + .queue-subtitle { + font-size: 13px; + opacity: 0.85; + } + } +} + +/* horizontal media scroller */ +.cd-hmedia-scroller { + &::-webkit-scrollbar-thumb { + box-shadow: none; + } + + &:hover::-webkit-scrollbar-thumb { + box-shadow: inset 0px 0px 10px 10px rgb(200 200 200 / 50%); + } + + &.hmedia-scroller-card { + .mediaitem-card { + margin: 16px; + } + } +} + +/* mediaitem-list-item */ +.cd-mediaitem-list-item { + width: 100%; + height: 60px; + display: flex; + flex: 0 0 auto; + flex-direction: row; + font-size: 14px; + justify-content: center-between; + align-items: center; + border-radius: var(--mediaItemRadius); + + .artwork { + height: 42px; + width: 42px; + border-radius: var(--mediaItemRadius); + object-fit: cover; + object-position: center; + flex: 0 0 auto; + background-repeat: no-repeat; + margin: 12px; + border: 0px; + outline: none; + position: relative; + overflow: hidden; + + .overlay-play { + background: rgba(0, 0, 0, 0.5); + opacity: 0; + appearance: none; + border: 0; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 0px; + z-index: 5; + cursor: pointer; + + &:hover { + opacity: 1; + } + + &:active { + background: var(--selected-click); + } + } + } + + .artwork.round { + border-radius: var(--mediaItemRadiusRound); + } + + .info-rect { + height: 100%; + display: flex; + flex-flow: column; + justify-content: center; + flex-grow: 1; + } + + .title { + width: 100%; + } + + .subtitle { + width: 90%; + font-size: .8em; + opacity: 0.7; + } + + .duration { + min-width: 60px; + text-align: center; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } + + .metainfo { + min-width: 145px; + text-align: center; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } + + .explicit-icon { + background-image: url("./assets/explicit.svg"); + height: 12px; + width: 36px; + filter: contrast(0); + background-repeat: no-repeat; + } + + /* CSS.gg + */ + @keyframes load-bar { + 10% { + box-shadow: inset 0 -4px 0 + } + 20% { + box-shadow: inset 0 -10px 0 + } + 30% { + box-shadow: inset 0 -12px 0 + } + 40% { + box-shadow: inset 0 -8px 0 + } + 50% { + box-shadow: inset 0 -4px 0 + } + 60% { + box-shadow: inset 0 -6px 0 + } + 80% { + box-shadow: inset 0 -12px 0 + } + 90% { + box-shadow: inset 0 -6px 0 + } + to { + box-shadow: inset 0 -2px 0 + } + } + + .loadbar-sound, + .loadbar-sound::after, + .loadbar-sound::before { + animation: load-bar 1.3s ease infinite alternate; + box-sizing: border-box; + width: 3px; + height: 28px; + box-shadow: inset 0 -12px 0; + } + + .loadbar-sound { + margin-left: 22px; + margin-top: -16px; + position: relative; + transform: scale(var(--load-bar, 1)); + color: var(--keyColor); + display: block; + } + + .loadbar-sound::after, + .loadbar-sound::before { + content: ""; + position: absolute; + bottom: 0 + } + + .loadbar-sound::before { + left: -4.5px; + animation-delay: -2.4s + } + + .loadbar-sound::after { + right: -4.2px; + animation-delay: -3.7s + } + + .isLibrary { + flex: 0 0 auto; + width: 40px; + text-align: center; + + button { + appearance: none; + border: 0px; + background: transparent; + cursor: pointer; + filter: contrast(0.8); + } + } + + &:hover { + background: rgb(200 200 200 / 10%); + box-shadow: var(--mediaItemShadow); + + .overlay-play { + opacity: 1; + } + } + + &.mediaitem-selected { + background: var(--selected); + box-shadow: var(--mediaItemShadow); + } + + + &:active { + background: var(--selected-click); + box-shadow: var(--mediaItemShadow); + color: #eee; + } + + // list item compact + &.compact { + height: 40px; + font-size: 13px; + + .artwork { + display: none; + } + + .info-rect { + padding-left: 1em; + } + } +} + +/* mediaitem-hrect */ +.cd-mediaitem-hrect { + background: rgb(255 255 255 / 18%); + width: 264px; + height: 100px; + display: inline-flex; + flex: 0 0 auto; + flex-direction: row; + font-size: 14px; + justify-content: center; + align-items: center; + border-radius: 6px; + cursor: pointer; + + .artwork { + height: 70px; + width: 70px; + background: blue; + border-radius: var(--mediaItemRadius); + background: var(--artwork); + background-size: contain; + flex: 0 0 auto; + background-repeat: no-repeat; + margin: 18px; + } + + .artwork.round { + border-radius: var(--mediaItemRadiusRound); + } + + .info-rect { + width: 100%; + } + + .title { + width: 100%; + text-align: center; + } + + .subtitle { + width: 100%; + text-align: center; + font-size: 12px; + } +} + +/* mediaitem-square-sp */ +.cd-mediaitem-square-sp { + --spcolor: var(""); + width: 190px; + height: 245px; + display: inline-flex; + flex: 0 0 auto; + flex-direction: column; + font-size: 14px; + justify-content: flex-start; + align-items: center; + border-radius: 6px; + margin-left: 10px; + cursor: pointer; + background-color: var(--spcolor); + + .artwork { + height: 190px; + width: 190px; + background: blue; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + background: var(--artwork); + background-size: cover; + flex: 0 0 auto; + margin: 6px; + margin-top: 0px; + + &.round { + border-radius: var(--mediaItemRadiusRound); + } + + &:hover { + box-shadow: rgb(0 0 0 / 50%) 0 0 0 1000000px inset; + } + } + + .title { + width: 90%; + text-align: center; + } + + .subtitle { + width: 100%; + text-align: center; + font-size: 12px; + } + + > .cd-mediaitem-square-large-overlay { + z-index: 3; + + &:hover { + opacity: 1; + } + } + + + .cd-mediaitem-square-large-overlay { + pointer-events: none; + } + + &:hover + .cd-mediaitem-square-large-overlay { + opacity: 1; + + } + + &:hover { + box-shadow: rgb(0 0 0 / 50%) 0 0 0 1000000px inset; + } +} + + +/* mediaitem-square-large */ +.cd-mediaitem-square-large { + width: 190px; + height: 250px; + display: inline-flex; + flex: 0 0 auto; + flex-direction: column; + font-size: 14px; + justify-content: flex-start; + align-items: center; + border-radius: 6px; + margin-left: 10px; + cursor: pointer; + + > * { + z-index: inherit; + } +} + +.cd-mediaitem-square-large .artwork { + height: 190px; + width: 190px; + background: blue; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + background: var(--artwork); + background-size: cover; + flex: 0 0 auto; + margin: 6px; + margin-top: 0px; +} + +.cd-mediaitem-square-large-overlay { + position: absolute; + width: 190px; + float: right; + height: 250px; + top: 0px; + margin: 10px; + margin-top: 0px; + opacity: 0; + +} + +.cd-mediaitem-square-large-overlay > * { + pointer-events: auto; + +} + +.cd-mediaitem-square-large > .cd-mediaitem-square-large-overlay { + z-index: 3; +} + +.cd-mediaitem-square-large > .cd-mediaitem-square-large-overlay:hover { + opacity: 1; +} + +.cd-mediaitem-square-large + .cd-mediaitem-square-large-overlay { + pointer-events: none; + +} + +.cd-mediaitem-square-large:hover + .cd-mediaitem-square-large-overlay { + opacity: 1; + +} + + +.cd-mediaitem-square-large .artwork.round { + border-radius: var(--mediaItemRadiusRound); +} + +.cd-mediaitem-square-large .title { + width: 90%; + text-align: center; +} + +.cd-mediaitem-square-large .subtitle { + width: 100%; + text-align: center; + font-size: 12px; +} + +/* mediaitem-mvview */ + +/* mediaitem-mvview */ +.cd-mediaitem-mvview { + width: 300px; + height: 250px; + display: inline-flex; + flex: 0 0 auto; + flex-direction: column; + font-size: 14px; + justify-content: flex-start; + align-items: center; + border-radius: 6px; + margin-left: 10px; + cursor: pointer; + + > * { + z-index: inherit; + } +} + +.cd-mediaitem-mvview .artwork { + height: 172px; + width: 300px; + background: blue; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + background: var(--artwork); + background-size: cover; + flex: 0 0 auto; + margin: 6px; + margin-top: 0px; +} + +.cd-mediaitem-mvview-overlay { + position: absolute; + width: 300px; + float: right; + height: 250px; + top: 0px; + margin: 10px; + margin-top: 0px; + opacity: 0; + +} + +.cd-mediaitem-mvview-overlay > * { + pointer-events: auto; + +} + +.cd-mediaitem-mvview > .cd-mediaitem-mvview-overlay { + z-index: 3; +} + +.cd-mediaitem-mvview > .cd-mediaitem-mvview-overlay:hover { + opacity: 1; +} + +.cd-mediaitem-mvview + .cd-mediaitem-mvview-overlay { + pointer-events: none; + +} + +.cd-mediaitem-mvview:hover + .cd-mediaitem-mvview-overlay { + opacity: 1; + +} + + +.cd-mediaitem-mvview .artwork.round { + border-radius: var(--mediaItemRadiusRound); +} + +.cd-mediaitem-mvview .title { + width: 90%; + text-align: center; +} + +.cd-mediaitem-mvview .subtitle { + width: 100%; + text-align: center; + font-size: 12px; +} + + +/* mediaitem-square */ +.cd-mediaitem-square { + width: 220px; + height: 238px; + display: inline-flex; + flex: 0 0 auto; + flex-direction: column; + font-size: 14px; + justify-content: center; + align-items: center; + border-radius: 6px; + + .artwork-container { + position: relative; + + .artwork { + height: 190px; + width: 190px; + background: blue; + border-radius: var(--mediaItemRadius); + background: var(--artwork); + background-size: cover; + flex: 0 0 auto; + margin: 6px; + cursor: pointer; + + &.round { + border-radius: var(--mediaItemRadiusRound); + } + } + + .badge-container { + transition: opacity 0.1s var(--appleEase); + opacity: 1; + + .socialBadge { + width: 32px; + height: 32px; + position: absolute; + right: 14px; + bottom: 14px; + border-radius: 100%; + overflow: hidden; + z-index: 2; + pointer-events: none; + } + } + + > .play-btn, + > .menu-btn { + opacity: 0; + appearance: none; + padding: 0px; + border: 0px; + width: 30px; + height: 30px; + border-radius: 50%; + background: rgba(50, 50, 50, 0.7); + cursor: pointer; + transition: opacity 0.1s var(--appleEase); + + :hover { + border-radius: 50%; + background: rgba(250, 0, 0, 0.7); + } + } + + > .play-btn { + position: absolute; + bottom: 14px; + left: 14px; + z-index: 2; + + } + + > .menu-btn { + position: absolute; + bottom: 14px; + right: 14px; + z-index: 2; + } + + &:hover { + > .badge-container { + opacity: 0; + } + + > .play-btn, + > .menu-btn { + opacity: 1; + } + } + } + + .info-rect { + width: 90%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + } + + + .title { + width: 100%; + text-align: center; + display: flex; + align-content: center; + justify-content: center; + } + + .subtitle { + width: 100%; + text-align: center; + font-size: 12px; + } + + &.mediaitem-video { + height: 200px; + width: 240px; + + .artwork { + height: 120px; + width: 212px; + } + } + + &.mediaitem-brick { + height: 200px; + width: 240px; + + .artwork { + height: 123px; + width: 220px; + } + } + + &.mediaitem-small { + width: 140px; + height: 180px; + + .artwork { + height: 128px; + width: 128px; + } + } + + &.mediaitem-card { + background: #ccc; + background: var(--spcolor); + height: 298px; + width: 230px; + max-width: 250px; + max-height: 500px; + overflow: hidden; + position: relative; + border-radius: calc(var(--mediaItemRadius) * 2); + box-shadow: var(--mediaItemShadow-ShadowSubtle); + + .artwork { + width: 230px; + height: 230px; + overflow: hidden; + border-radius: 0px; + margin: 0; + + .mediaitem-artwork { + border-radius: 0px; + + &::after { + box-shadow: unset; + } + } + } + + .info-rect-card { + padding: 10px 10px 14px; + position: relative; + width: 100%; + + &::before { + background: var(--bgartwork); + content: ""; + top: 0; + left: 0; + bottom: 0; + right: 0; + position: absolute; + background-size: cover; + background-position: bottom; + z-index: 0; + opacity: 1; + filter: brightness(0.5) blur(50px) saturate(180%); + } + } + + .title { + height: 100%; + display: flex; + justify-content: center; + align-items: center; + font-size: 0.9em; + font-weight: 500; + z-index: 1; + } + + .subtitle { + height: 100%; + justify-content: center; + align-items: center; + font-size: 0.75em; + width: 100%; + display: flex; + z-index: 1; + } + + &::after { + box-shadow: var(--mediaItemShadow); + content: ""; + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + pointer-events: none; + border-radius: inherit; + } + } +} + +/* mediaitem-square */ +.albums-square-containeru > * > .cd-mediaitem-square { + --frame: max(220px, 15vw); + width: var(--frame); + height: calc(var(--frame) * 13 / 11); + display: inline-flex; + flex: 0 0 auto; + flex-direction: column; + font-size: calc(var(--frame) / 220 * 14); + justify-content: center; + align-items: center; + border-radius: calc(var(--frame) / 220 * 6); + + .artwork-container { + position: relative; + + .artwork { + height: calc(var(--frame) * 19 / 22); + width: calc(var(--frame) * 19 / 22); + background: blue; + border-radius: var(--mediaItemRadius); + background: var(--artwork); + background-size: cover; + flex: 0 0 auto; + margin: calc(var(--frame) / 220 * 6); + cursor: pointer; + + &.round { + border-radius: var(--mediaItemRadiusRound); + } + } + + > .play-btn, + > .menu-btn { + opacity: 0; + appearance: none; + padding: 0px; + border: 0px; + width: calc(var(--frame) / 220 * 30); + height: calc(var(--frame) / 220 * 30); + border-radius: 50%; + background: rgba(50, 50, 50, 0.7); + cursor: pointer; + backdrop-filter: blur(32px) saturate(180%); + transition: opacity 0.1s var(--appleEase); + } + + > .play-btn { + position: absolute; + bottom: calc(var(--frame) / 220 * 14); + left: calc(var(--frame) / 220 * 14); + z-index: 2; + } + + > .menu-btn { + position: absolute; + bottom: calc(var(--frame) / 220 * 14); + right: calc(var(--frame) / 220 * 14); + z-index: 2; + } + + &:hover { + + > .play-btn, + > .menu-btn { + opacity: 1; + } + } + } + + + .title { + width: 90%; + text-align: center; + } + + .subtitle { + width: 100%; + text-align: center; + font-size: calc(var(--frame) / 220 * 12); + } + + &.mediaitem-video { + height: calc(var(--frame) / 220 * 200); + width: calc(var(--frame) / 220 * 240); + + .artwork { + height: calc(var(--frame) / 220 * 120); + width: calc(var(--frame) / 220 * 212); + } + } + + &.mediaitem-brick { + height: calc(var(--frame) / 220 * 200); + width: calc(var(--frame) / 220 * 240); + + .artwork { + height: calc(var(--frame) / 220 * 123); + width: calc(var(--frame)); + } + } +} + + +.listitem-horizontal { + .cd-mediaitem-list-item { + width: 350px; + height: 60px; + } +} + +.mediaitem-list-item__grid { + background: rgba(200, 200, 200, 0.05); + border-radius: 10px; + padding: var(--contentInnerPadding); + box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 0px 1px; + width: 100%; + + .cd-mediaitem-list-item { + width: 350px; + height: 60px; + } + + &::-webkit-scrollbar { + display: none; + } + + &:hover::-webkit-scrollbar { + display: initial; + + } +} + +// Graphics + +// sidebar icon +.svg-icon { + --color: #aaa; + --url: url("./assets/feather/share.svg"); + -webkit-mask-image: var(--url); + -webkit-mask-size: cover; + height: 18px; + width: 18px; + background: var(--color); + + &.inline { + display: inline-block; + } +} + +.sidebar-icon { + width: 18px; + height: 18px; + margin-right: 8px; + + > .svg-icon { + width: 100%; + height: 100%; + --color: #aaa; + } + + > svg { + width: 100%; + height: 100%; + color: #aaa; + } +} + + +/* Switch Checkbox */ +input[type=checkbox][switch] { + width: 38px; + appearance: none; + border-radius: 32px; + height: 24px; + zoom: 1; + top: 0; + cursor: pointer; + left: 0; + position: relative; + transform: scale(1); + background: rgb(142 142 147 / 100%); + padding: 0; + margin: 0; +} + +input[type=checkbox][switch]:focus, +input[type=checkbox][switch]:active { + outline: none; +} + +input[type=checkbox][switch]:checked { + background: var(--keyColor); + border: 0 solid var(--keyColor); + mix-blend-mode: unset; + + &:hover { + background: var(--keyColor-rollover); + } + + &:active { + background: var(--keyColor-pressed); + } +} + +input[type=checkbox][switch]::before { + background: white; + width: 26px; + height: 26px; + top: -1px; + left: -1px; + position: absolute; + content: ' '; + border-radius: 32px; + transition: .10s left var(--appleEase); + transform: scale(.75); +} + + +input[type=checkbox][switch]:checked::before { + background: white; + top: -1px; + left: 13px; + transition: .10s left var(--appleEase); + transform: scale(.75); +} + +input[type=checkbox][switch]:disabled::before { + opacity: .5; +} + +input[type=checkbox][switch]:active::before { + left: 13px; +} + +input[type=checkbox][switch]:checked:active::before { + left: -1px; +} + +/* End Switch Checkbox */ + + + +.header-text { + margin: 0px; +} + +.media-item--small { + background: rgb(0 0 0 / 25%); + height: 162px; + width: 132px; + display: inline-flex; + flex-direction: column; + justify-content: center; + align-items: center; + border-radius: 10px; +} + +.media-item--small .artwork { + background: red; + margin: 6px; + border-radius: 100%; + width: 90px; + height: 90px; + box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 30%); +} + +.playlist-artwork { + height: 190px; + width: 190px; + background: blue; + border-radius: 6px; + background: var(--artwork); + background-size: cover; + box-shadow: var(--mediaItemShadow); + flex: 0 0 auto; + margin: 6px; + margin-top: 0px; +} + +.media-item--small .text { + font-weight: 600; + font-size: 0.90em; +} + +.media-item--small .subtext { + font-size: 0.75em; +} + +.player-duration-time { + opacity: 0.5; +} + +.player-artwork-container { + display: flex; + align-items: center; + justify-content: center; +} + +.player-duration-container { + font-size: 0.85em; + font-weight: 500; +} + +.media-artwork { + --artwork: url(""); + width: 80vw; + height: 80vw; + max-height: 500px; + max-width: 500px; + background: black; + background-image: var(--artwork); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + border-radius: 8px; + box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55); + transition: transform .10s var(--appleEase); +} + +.media-artwork.paused { + transition: transform .35s var(--appleEase); + transform: scale(0.85); +} + +.playback-slider { + width: 90%; +} + +.volume-slider { + width: 100%; +} + +.volume-slider-container { + width: 90%; + margin: 0 auto; + padding: 0px; +} + +.volume-slider-container .col-auto, +.volume-slider-container .col { + display: flex; + align-items: center; + justify-self: center; + padding: 0px; + margin: 0px; +} + +.playback-button { + font-size: 2em; + width: 40px; + height: 36px; + padding: 0px; + background: transparent; + border: 0px; + border-radius: 0px; + box-shadow: unset; + background-size: 12px; + background-position: center; + background-repeat: no-repeat; + opacity: 0.70; + border-radius: 6px; +} + +.playback-button:active { + transform: scale(0.95); +} + +.playback-button--small { + border-radius: 6px; + font-size: 1em; + color: inherit; + background-size: 14px; + background-repeat: no-repeat; + background-position: center; + background-color: transparent; + width: 40px; + height: 32px; + border: 0px; + box-shadow: unset; + opacity: 0.70; +} + +.playback-button:hover, +.playback-button--small:hover { + background-color: rgb(200 200 200 / 10%); +} + +.playback-button:active, +.playback-button--small:active { + transform: scale(0.9); +} + +.playback-button--small.active { + background-color: rgb(200 200 200 / 25%); +} + +.playback-button--small.search { + background-image: url("./assets/search.svg"); +} + +.playback-button--small.cast { + background-image: url("./assets/cast_white.svg"); +} + +.playback-button--small.miniplayer { + background-image: url("./assets/pip.svg"); +} + +.playback-button--small.queue { + background-image: url("./assets/list.svg"); +} + +.playback-button--small.lyrics { + background-image: url("./assets/quote-right.svg"); +} + +.playback-button--small.shuffle { + background-image: url("./assets/shuffle.svg"); +} + +.playback-button--small.repeat { + background-image: url("./assets/repeat.svg"); +} + +.playback-button--small.repeat.repeatOne { + background-color: rgb(200 200 200 / 25%); + background-image: url("./assets/repeatOne.svg"); +} + +.playback-button.pause { + background-image: url('./assets/pause.svg'); +} + +.playback-button.play { + background-image: url('./assets/play.svg'); +} + +.playback-button.next { + background-image: url('./assets/forward.svg'); +} + +.playback-button.previous { + background-image: url('./assets/backward.svg'); +} + +.playback-buttons { + display: flex; + align-items: center; + justify-content: center; +} + +.player-volume-glyph { + width: 32px; + height: 16px; + background-repeat: no-repeat; + background-size: contain; + background-position: center; +} + +.player-volume-glyph.decrease { + background-image: url("./assets/volume.svg"); + opacity: 0.5; +} + +.player-volume-glyph.increase { + background-image: url("./assets/volume-2.svg"); + opacity: 0.5; +} + +.player-track-info { + width: 90%; + margin: 0 auto; +} + +.player-song-title { + font-size: 1.25em; + text-align: left; + margin: 0 auto; + font-weight: 500; +} + +.player-song-artist { + font-size: 1.0em; + text-align: left; + margin: 0 auto; + color: var(--keyColor); + font-weight: 400; +} + +.player-song-artist:hover { + cursor: pointer; + text-decoration: underline; +} + +.player-more-container { + display: flex; + align-items: center; + justify-content: center; +} + +.player-more-button { + appearance: none; + width: 32px; + height: 32px; + border-radius: 50%; + border: 0px; + background: var(--keyColor); + cursor: pointer; + box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%); + color: white; + font-weight: bold; + padding: 0px; + font-size: 16px; +} + +.back-button { + width: 40px; + height: 40px; + background-color: transparent; + background-size: 16px; + background-position: center; + background-repeat: no-repeat; + background-image: url("./assets/arrow-left.svg"); + border: 0px; + border-radius: 0px; +} + +.header-text { + height: 40px; + display: flex; + align-items: center; + +} + + +.list-entry-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px; + font-size: 1em; + font-family: inherit; +} + +.list-entry { + display: flex; + align-items: center; + /* justify-content: space-between; */ + padding: 12px; + font-size: 1em; + font-family: inherit; + border-bottom: 1px solid rgba(255 255 255 / 0.1); + cursor: pointer; +} + +.list-entry-image { + --artwork: url(""); + width: 64px; + height: 64px; + background: var(--artwork); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + border-radius: 8px; + box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55); +} + +.list-entry-image.artist { + border-radius: 50%; +} + +.list-entry-body { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + margin-left: 12px; +} + +.list-entry-name { + font-size: 14px; + font-weight: 500; + overflow: hidden; + width: 100%; +} + +.list-entry-artist { + font-size: 12px; + overflow: hidden; + width: 100%; +} + +.list-entry .handle { + height: 100%; + width: 28px; + background: var(--keyColor); + display: flex; + justify-content: center; + align-items: center; +} + +.search-panel { + background: rgb(0 0 0 / 50%); +} + +.search-header { + position: absolute; + width: 100%; + z-index: 1; + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border-bottom: 1px solid rgb(200 200 200 / 8%); +} + +.connection-error-panel { + background: rgb(0 0 0 / 50%); +} + +.search-type-container { + display: flex; +} + +.search-type-button { + background: rgb(20 20 20 / 0.85); + border-radius: 50px; + color: white; + border: 0px; + box-shadow: unset; + font-family: inherit; + padding: 8px 16px; + font-size: 14px; + font-weight: 500; + margin: 8px; + margin-top: 0px; + margin-bottom: 0px; +} + +.search-type-button.active { + background: var(--keyColor); +} + +.search-tab-container { + overflow: auto; + white-space: nowrap; + overflow-y: hidden; +} + +.search-body-container { + position: relative; + width: 100%; + height: 100%; +} + +.search-body { + position: absolute; + width: 100%; + height: 100%; + padding-top: 220px; +} + +.search-tab { + background: rgb(20 20 20 / 0.85); + border-radius: 50px; + color: white; + border: 0px; + box-shadow: unset; + font-family: inherit; + padding: 8px 16px; + font-size: 14px; + font-weight: 500; +} + +.search-tab.active { + background: var(--keyColor); +} \ No newline at end of file diff --git a/src/renderer/less/helpers.less b/src/renderer/less/helpers.less new file mode 100644 index 00000000..cdd1584f --- /dev/null +++ b/src/renderer/less/helpers.less @@ -0,0 +1,396 @@ + +.modal-fullscreen { + display: flex; + justify-content: center; + align-items: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.3); + z-index: 1000; + + .modal-window { + background: #333; + border-radius: 10px; + box-shadow: var(--mediaItemShadow-Shadow); + display: flex; + flex-flow: column; + max-height: 500px; + max-width: 360px; + background: #121212; + width: 100%; + position: relative; + + &:after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + box-shadow: var(--mediaItemShadow); + z-index: 1; + border-radius: inherit; + } + + .modal-header { + width: 100%; + padding: 6px; + } + + .modal-content { + width: 100%; + height: 100%; + overflow: hidden; + overflow-y: overlay; + } + + .modal-footer { + } + } +} + +.spatialproperties-panel { + .modal-window { + height: 700px; + max-height: 700px; + width: 800px; + max-width: 800px; + overflow: hidden; + + .info-header { + padding-left: 12px; + } + + .visual-container { + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + } + + .visual { + position: relative; + height: 250px; + width: 300px; + display: inline-flex; + align-items: flex-end; + justify-content: center; + filter: drop-shadow(2px 12px 6px rgb(0 0 0 / 25%)); + margin: 0 auto; + + .face { + position: absolute; + width: calc(12px * 6); + height: calc(12px * 6); + border-radius: 6px; + transform: rotateX(60deg) rotateZ(-45deg); + transition: transform 0.2s linear, width 0.2s linear, height 0.2s linear; + } + + .listener { + position: absolute; + width: 32px; + height: 32px; + border-radius: 6px; + transform: rotateX(60deg) rotateZ(-45deg); + transition: transform 0.2s linear, width 0.2s linear, height 0.2s linear; + background: white; + color: black; + z-index: 2; + } + + .audiosource { + position: absolute; + width: 32px; + height: 32px; + border-radius: 6px; + transform: rotateX(60deg) rotateZ(-45deg); + transition: transform 0.2s linear, width 0.2s linear, height 0.2s linear; + background: yellow; + z-index: 2; + } + + .face:nth-of-type(1) { + background: linear-gradient(45deg, #28223a, #1f2038); + z-index: 1; + } + + .face:nth-of-type(2) { + background: linear-gradient(45deg, #7d53ad, #5763ff); + transform: rotateX(60deg) rotateZ(-45deg) translateZ(30px); + opacity: 0.7; + z-index: 3; + } + } + + .modal-header { + padding: 16px; + position: relative; + overflow: hidden; + + .modal-title { + text-align: center; + } + + .close-btn { + width: 50px; + height: 100%; + background-image: var(--gfx-closeBtn); + background-position: center; + background-repeat: no-repeat; + -webkit-app-region: no-drag; + appearance: none; + border: 0; + background-color: transparent; + position: absolute; + top: 0; + right: 0; + + &:hover { + background-color: rgb(196, 43, 28) + } + } + } + } +} + +.addtoplaylist-panel { + .modal-window { + max-height: 600px; + max-width: 400px; + background: rgb(18 18 18 / 90%); + overflow: hidden; + backdrop-filter: blur(16px) saturate(180%); + + .modal-header { + padding: 16px; + position: relative; + + .modal-title { + text-align: center; + } + + .close-btn { + width: 50px; + height: 100%; + background-image: var(--gfx-closeBtn); + background-position: center; + background-repeat: no-repeat; + -webkit-app-region: no-drag; + appearance: none; + border: 0; + background-color: transparent; + position: absolute; + top: 0; + right: 0; + + &:hover { + background-color: rgb(196, 43, 28) + } + } + } + + .modal-search { + width: 100%; + padding: 0px 16px; + position: relative; + } + + .playlist-item { + appearance: none; + border: 0px; + text-align: left; + width: 100%; + margin: 0; + display: flex; + background: rgba(32, 32, 32, 0.46); + color: #eee; + font-family: inherit; + font-size: 0.98em; + padding: 6px 12px; + align-items: center; + flex-flow: row; + + .icon { + pointer-events: none; + width: 32px; + height: 32px; + display: flex; + justify-content: center; + align-items: center; + margin-right: 6px; + } + + .name { + } + + &:hover { + background: var(--selected); + } + + &:active { + background: var(--selected-click); + } + + &.focused { + background: var(--keyColor); + } + } + + .playlist-item:last-child { + border-bottom: 0px; + } + } +} + +.menu-panel { + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + z-index: 100001; + display: flex; + justify-content: center; + align-items: center; + -webkit-app-region: no-drag; + + .menu-header-body { + padding: 6px; + display: flex; + background: rgb(200 200 200 / 10%); + + .menu-option-header { + width: 40px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + border-radius: var(--mediaItemRadius); + appearance: none; + border: 0; + background: transparent; + + &.active { + .sidebar-icon > .svg-icon { + --color: var(--keyColor); + } + } + + &:hover { + background: var(--selected); + } + + &:active { + background: var(--selected-click); + } + } + } + + .menu-panel-body { + display: flex; + flex-flow: column; + background: rgb(38 38 38); + position: relative; + min-width: 200px; + box-shadow: var(--ciderShadow-Generic); + border-radius: var(--mediaItemRadius); + overflow: hidden; + font-size: 14px; + + .menu-option { + text-align: left; + display: flex; + width: 100%; + padding: 12px 16px; + appearance: none; + border: 0px; + font: inherit; + background: transparent; + color: inherit; + + &:hover { + background: var(--selected); + } + + &:active { + background: var(--selected-click); + } + } + } + + + .menu-header-text { + margin: 18px 6px; + + .close-btn { + width: 50px; + height: 42px; + background-image: var(--gfx-closeBtn); + background-position: center; + background-repeat: no-repeat; + -webkit-app-region: no-drag; + appearance: none; + border: 0; + background-color: transparent; + position: absolute; + top: 0; + right: 0; + + &:hover { + background-color: rgb(196, 43, 28) + } + } + } + + .menu-body { + overflow: overlay; + height: 100%; + } + + .menu-footer { + width: 100%; + padding: 12px; + } +} + +.queue-panel { + height: 100%; + width: 100%; + display: flex; + flex-flow: column; + + .queue-header-text { + margin: 18px 6px; + } + + .queue-body { + overflow: overlay; + height: 100%; + } + + .queue-footer { + width: 100%; + padding: 12px; + } + + .autoplay { + background: rgb(200 200 200 / 15%); + display: flex; + justify-content: center; + appearance: none; + border: 0; + border-radius: 6px; + height: 32px; + width: 32px; + } + + .infinity { + content: url("./assets/infinity.svg"); + margin: auto; + } +} diff --git a/src/renderer/less/pages.less b/src/renderer/less/pages.less new file mode 100644 index 00000000..f95f07e4 --- /dev/null +++ b/src/renderer/less/pages.less @@ -0,0 +1,821 @@ +// Helpers +.content-inner { + position: absolute; + top: var(--navigationBarHeight); + left: 0; + padding: 32px; + width: 100%; + transition: zoom 1s; + zoom: 1; +} + +.content-inner.centered { + height: 100%; + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; +} +// End Helpers + +// GitHub Themes +.github-themes-page { + display: flex; + flex-direction: column; + padding: 0px; + height: calc(100% - var(--navigationBarHeight)); + + .github-avatar { + height: 42px; + width: 42px; + margin: 6px; + border-radius: 32px; + } + + .repo-name { + margin:0px; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .repo-url { + color: var(--textColor); + font-size: 0.8em; + } + + .repo-preview-name { + margin:0px; + } + + .repos-list { + height: 100%; + overflow-y: overlay; + width: 320px; + font-size: 14px; + + >.list-group { + margin:0px; + } + .list-group-item { + padding: 12px 6px; + + &:hover { + filter: brightness(1.2); + } + + &:active { + filter: brightness(0.8); + } + } + } + + .github-preview { + height: 100%; + flex: 1; + background: var(--color2); + padding: 16px 32px; + overflow-y:overlay; + } + + .gh-content { + display: flex; + flex-direction: row; + flex: 1; + overflow: hidden; + } + + .gh-header { + padding: 16px; + } +} + +// Library - Songs page +.library-page { + padding: 0px; + + .library-header { + position: sticky; + top: 0; + left: 0; + border-bottom: 1px solid rgba(200, 200, 200, 0.05); + z-index: 6; + background: black; + padding: 0px 2em; + backdrop-filter: blur(32px); + background: rgba(0, 0, 0, 0.25); + top: var(--navigationBarHeight); + } + + .well { + margin: 2em; + } +} + +// Podcast Page +.content-inner.podcasts-page { + display: flex; + height: calc(100% - var(--navigationBarHeight)); + padding: 0px; + + .list-flat { + border-radius: 0px; + } + + .podcast-artwork { + width: 200px; + margin: 16px auto; + height: 200px; + } + + .podcasts-list { + height: 100%; + width: 280px; + background: rgb(200 200 200 / 10%); + overflow-y: overlay; + border-right: 1px solid var(--color2); + flex: none; + overflow-x: hidden; + + .podcast-list-header { + border-bottom: 1px solid var(--color2); + font-size: 0.7em; + padding: 6px; + background: #ffffff17; + text-transform: uppercase; + font-weight: 600; + opacity: 0.5; + } + + .podcasts-search { + padding: 10px; + position: sticky; + top: 0; + left: 0; + width: 100%; + border-bottom: 1px solid var(--color2); + z-index: 2; + background: #303030; + } + } + + .episodes-list { + height: 100%; + width: 100%; + background: rgb(200 200 200 / 6%); + overflow-y: overlay; + overflow-x: hidden; + + .episodes-inline-info { + padding: 14px 14px 0px 14px; + + .podcast-show-info { + display: flex; + justify-content: center; + flex-direction: column; + } + + .podcast-show-description { + margin: 32px 6px; + font-size: 0.8rem; + white-space: pre-wrap; + display: block; + } + + .podcast-artwork { + width: 120px; + margin: 0px auto; + height: 120px; + } + } + + .podcast-no-search-results { + text-align: center; + margin-top: 40px; + } + } + + .podcasts-details { + width: 300px; + flex: none; + background: rgb(255 255 255 / 5%); + overflow-y: overlay; + overflow-x: hidden; + top: 2%; + z-index: 2; + border-left: 1px solid var(--color2); + padding-bottom: 1em; + + .meta-btn { + font-size: 0.75em; + } + + .podcasts-details-header { + display: flex; + justify-content: end; + align-items: center; + position: sticky; + top: 0; + z-index: 2; + } + + .close-btn { + width: 50px; + height: 42px; + background-image: var(--gfx-closeBtn); + background-position: center; + background-repeat: no-repeat; + -webkit-app-region: no-drag; + appearance: none; + border: 0; + background-color: transparent; + position: absolute; + top: 0; + right: 0; + + &:hover { + background-color: rgb(196, 43, 28) + } + } + + .podcast-genre { + text-align: center; + margin: 6px; + font-size: 0.8em; + font-weight: 500; + opacity: 0.8; + } + + .podcast-metainfo { + text-align: center; + font-size: 0.7em; + opacity: 0.8; + } + + .podcast-header { + text-align: center; + } + + .podcast-play-btn { + width: 50%; + display: block; + margin: 0 auto; + } + + .podcast-description { + margin: 12px; + font-size: 0.75em; + white-space: pre-wrap; + display: block; + line-break: anywhere; + } + + + } + +} + +@media only screen and (max-width: 1230px) { + .content-inner.podcasts-page { + .podcasts-details { + height: 96%; + width: 300px; + flex: none; + background: rgb(20 20 20 / 97%); + overflow-y: overlay; + overflow-x: hidden; + position: absolute; + right: 2%; + top: 2%; + border-radius: 10px; + box-shadow: var(--ciderShadow-Generic); + z-index: 2; + } + } +} + +/* Album / Playlist Page */ +.playlist-page { + --bgColor: transparent; + padding: 0px; + //background: linear-gradient(180deg, var(--bgColor) 32px, var(--bgColor) 18px, transparent 60px, transparent 100%); + top: 0; + padding-top: var(--navigationBarHeight); + + .playlist-body { + padding: var(--contentInnerPadding) 2em; + margin-top: -75px; + } + + .floating-header { + position: sticky; + top: 0; + left: 0; + border-bottom: 1px solid rgba(200, 200, 200, 0.05); + z-index: 6; + padding: 0px 1em; + backdrop-filter: blur(32px); + background: rgba(0, 0, 0, 0.25); + top: var(--navigationBarHeight); + transition: opacity 0.1s var(--appleEase); + } + + .playlist-display { + padding: var(--contentInnerPadding); + min-height: 300px; + position: relative; + + .artworkContainer { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin: 0; + margin-top: calc(var(--navigationBarHeight) * -1); + margin-bottom: -10px; + padding: 0; + -webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%); + opacity: .7; + animation: playlistArtworkFadeIn 1s var(--appleEase); + + .artworkMaterial > img { + filter: brightness(100%) blur(80px) saturate(100%) contrast(1); + object-position: center; + object-fit: cover; + width: 100%; + height: 100%; + transform: unset; + } + } + + .playlistInfo { + z-index: 1; + position: absolute; + bottom: 0; + left: 0; + right: 0; + top: 0; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + + > .row { + width: calc(100% - 32px); + } + + .playlist-info { + flex-shrink: unset; + display: flex; + flex-flow: column; + justify-content: flex-end; + + .playlist-name { + font-weight: 700; + font-size: 1.6rem; + margin-bottom: 6px; + margin-right: 6px; + flex-shrink: unset; + } + + .nameEdit { + font-weight: 700; + font-size: 1.6rem; + margin-bottom: 6px; + margin-right: 6px; + flex-shrink: unset; + background: transparent; + border: 0px; + color: inherit; + font-family: inherit; + } + + .playlist-artist { + font-size: 20px; + margin-bottom: 6px; + margin-right: 6px; + flex-shrink: unset; + } + + .playlist-desc { + box-sizing: border-box; + font-size: 14px; + flex-shrink: unset; + margin-right: 5px; + max-height: 100px; + position: relative; + + .content { + height: 100px; + -webkit-mask-image: -webkit-gradient(linear, left 50%, left 90%, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); + } + + .more-btn { + appearance: none; + position: absolute; + right: 0; + bottom: 0; + padding: 0 5px; + font-size: 14px; + color: var(--keyColor); + background-color: transparent; + border: 0px; + cursor: pointer; + width: 100%; + height: 100%; + overflow: hidden; + display: flex; + justify-content: flex-end; + align-items: flex-end; + font-weight: 600; + font-family: inherit; + text-transform: uppercase; + } + } + + .playlist-desc-expanded { + box-sizing: border-box; + font-size: 14px; + position: relative; + + .more-btn { + appearance: none; + position: absolute; + right: 0; + bottom: 0; + padding: 0 5px; + font-size: 14px; + color: var(--keyColor); + background-color: transparent; + border: 0px; + cursor: pointer; + width: 100%; + height: 100%; + overflow: hidden; + display: flex; + justify-content: flex-end; + align-items: flex-end; + font-weight: 600; + font-family: inherit; + text-transform: uppercase; + } + } + } + } + + + } + + .friends-info { + display: flex; + flex-flow: column; + + .badge-container { + display: flex; + flex-flow: wrap; + + .socialBadge { + width: 40px; + height: 40px; + border-radius: 100%; + overflow: hidden; + box-shadow: var(--mediaItemShadow-ShadowSubtle); + transition: transform .2s var(--appleEase); + margin: 6px; + + &:hover { + transform: scale(1.2); + } + } + } + + .friends-name { + text-align: center; + font-size: 0.9em; + margin: 8px; + } + } + + .playlist-time { + font-size: 0.9em; + margin: 6px; + opacity: 0.7; + } + + &.inline-playlist { + overflow: hidden; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 6; + position: sticky; + margin-top: calc(var(--navigationBarHeight) * -1); + + .floating-header { + opacity: 1; + top: 0px; + z-index: 6; + padding: 1em; + backdrop-filter: unset; + background: black; + h3 { + display: none; + } + } + .playlist-inner { + background: black; + width: 80%; + height: 100%; + overflow: overlay; + box-shadow: var(--ciderShadow-Generic); + border-radius: var(--mediaItemRadius) var(--mediaItemRadius) 0px 0px; + + .close-btn { + position: sticky; + top: 16px; + left: 16px; + margin-left: 16px; + z-index: 7; + } + } + } +} + +@keyframes playlistArtworkFadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 0.7; + } +} + +// Collection Page +.collection-page { + padding-bottom: 128px; + + .top-fab { + height: 52px; + width: 52px; + position: fixed; + bottom: 32px; + right: 32px; + border-radius: 100%; + background: rgb(60 60 60); + border: 0px; + appearance: none; + box-shadow: var(--ciderShadow-Generic); + + > svg { + height: 50%; + color: #eee; + pointer-events: none; + } + + &:hover { + background: rgb(100 100 100); + } + + &:active { + background: var(--keyColor); + } + } + + .header-text { + margin-bottom: 32px; + } +} + +/* Artist Page */ + +.artist-page { + padding: 0px; + top: 0; + + .floating-header { + position: sticky; + top: 0; + left: 0; + border-bottom: 1px solid rgba(200, 200, 200, 0.05); + z-index: 6; + padding: 0px 1em; + backdrop-filter: blur(32px); + background: rgba(0, 0, 0, 0.25); + top: var(--navigationBarHeight); + transition: opacity 0.1s var(--appleEase); + } + + &.animated .artist-header .more-btn-round { + position: absolute; + bottom: 22px !important; + right: 28px; + } + + .artist-header { + //background: linear-gradient(45deg, var(--keyColor), #0e0e0e); + color: white; + display: flex; + align-items: center; + justify-content: space-between; + min-height: 400px; + position: relative; + pointer-events: none; + + .header-content { + z-index: 1; + margin-top: -16px; + } + + + .artworkContainer { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin: 0; + padding: 0; + -webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%); + opacity: .7; + animation: playlistArtworkFadeIn 1s var(--appleEase); + + .artworkMaterial > img { + filter: brightness(100%) blur(80px) saturate(100%) contrast(1); + object-position: center; + object-fit: cover; + width: 100%; + height: 100%; + transform: unset; + } + } + + .more-btn-round { + position: absolute; + bottom: 82px; + right: 28px; + } + + .animated { + width: 100%; + height: 100%; + align-self: center; + position: absolute; + overflow: hidden; + box-shadow: rgb(0 0 0 / 50%) 0 0 0 1000000px inset; + + video { + overflow: hidden; + height: 100%; + width: 100%; + min-height: 56.25vw; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + } + + .row .col.flex-center { + z-index: 4; + } + } + + .artist-image { + width: 200px; + height: 200px; + margin: 32px; + position: relative; + + .overlay-play { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; + background: rgb(0 0 0 / 50%); + transition: opacity 0.1s var(--appleEase); + border-radius: 100%; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + appearance: none; + border: 0px; + padding: 0px; + + &:hover { + opacity: 1; + } + + &:active { + background: var(--selected-click); + } + + > svg { + width: 70%; + } + } + } + + .artist-play { + width: 32px; + height: 32px; + background: rgba(100, 100, 100, 0.5); + box-shadow: var(--ciderShadow-Generic); + border-radius: 100%; + box-shadow: var(--mediaItemShadow); + display: none; + cursor: pointer; + appearance: none; + border: 0px; + padding: 0px; + + &:hover { + filter: brightness(125%); + } + + &:active { + filter: brightness(75%); + transform: scale(0.98); + transition: transform 0s var(--appleEase), box-shadow 0.2s var(--appleEase); + } + } + + .artist-title { + + .artist-play { + transform: translateY(3px); + margin: 14px; + } + + &.artist-animation-on { + width: 100%; + flex: unset; + margin-left: 0.5em; + color: whitesmoke; + position: absolute; + bottom: 0; + + .artist-play { + display: block; + } + } + } + + .artist-body { + padding: 0px var(--contentInnerPadding) 0px var(--contentInnerPadding); + margin: -140px 20px; + } + + &.animated > .artist-body { + padding: 0px var(--contentInnerPadding) 0px var(--contentInnerPadding); + margin-top: -57px; + } + + .showmoreless { + font-family: inherit; + font-size: 16px; + font-weight: 500; + background: transparent; + border: 0px; + border-radius: 6px; + appearance: none; + color: var(--keyColor); + padding: 8px 12px; + cursor: pointer; + margin-top: 12px; + float: right; + } + + .showmoreless:hover { + background: rgb(200 200 200 / 10%); + } +} + +/* Artist Page End */ + +// Settings page +.settings-page { + padding: 0px; + + .md-option-header { + padding: 1.25em 1.25em; + border-bottom: unset; + border-top: unset; + font-weight: 600; + font-size: 1.0em; + background: rgb(255 255 255 / 3%); + } + + .settings-option-body { + margin: 16px; + } +} diff --git a/src/renderer/style.less b/src/renderer/style.less index 161e0c2e..85949811 100644 --- a/src/renderer/style.less +++ b/src/renderer/style.less @@ -7,6 +7,9 @@ @import url("less/ameframework.less"); @import url("less/bootstrap.less"); @import url("less/notyf.less"); +@import url("less/elements.less"); +@import url("less/helpers.less"); +@import url("less/pages.less"); :root { --appleEase: cubic-bezier(0.42, 0, 0.58, 1); @@ -68,6 +71,14 @@ body[loading] { } } +body.stopanimation * { + animation: unset !important; + + .loadbar-sound { + display: none; + } +} + body.notransparency::before { content: ""; position: absolute; @@ -79,14 +90,6 @@ body.notransparency::before { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==); } -body.stopanimation * { - animation: unset !important; - - .loadbar-sound { - display: none; - } -} - *, *:before, *:after { @@ -137,7 +140,7 @@ body.stopanimation * { width: 100%; height: 100%; background: var(--color1); - color: white; + color: var(--textColor); user-select: none; margin: 0 auto; position: relative; @@ -163,21 +166,6 @@ body.stopanimation * { z-index: 1; } } - - //&::before { - // position: absolute; - // top: -50%; - // left: -50%; - // width: var(--bgWidth); - // height: var(--bgHeight); - // background-image: var(--bgColor); - // content: ""; - // z-index: -1; - // transform: rotateZ(0deg); - // transform-origin: center; - // animation: bgRotate 10s linear infinite; - // filter: brightness(100%) saturate(200%) contrast(1.5); - //} } .bgGradientMaterial-base { @@ -352,11 +340,6 @@ input[type=range].web-slider::-webkit-slider-runnable-track { padding: 0px; } -.md-btn { - font-family: inherit; - font-size: 14px; -} - #app-main { display: flex; width: 100%; @@ -400,24 +383,6 @@ input[type=range].web-slider::-webkit-slider-runnable-track { position: relative; } -.content-inner { - position: absolute; - top: var(--navigationBarHeight); - left: 0; - padding: 32px; - width: 100%; - transition: zoom 1s; - zoom: 1; -} - -.content-inner.centered { - height: 100%; - display: flex; - flex-flow: column; - justify-content: center; - align-items: center; -} - .app-drawer { width: 300px; flex: 0 0 auto; @@ -1478,450 +1443,17 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { } } -.header-text { - margin: 0px; -} - -.media-item--small { - background: rgb(0 0 0 / 25%); - height: 162px; - width: 132px; - display: inline-flex; - flex-direction: column; - justify-content: center; - align-items: center; - border-radius: 10px; -} - -.media-item--small .artwork { - background: red; - margin: 6px; - border-radius: 100%; - width: 90px; - height: 90px; - box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 30%); -} - -.playlist-artwork { - height: 190px; - width: 190px; - background: blue; - border-radius: 6px; - background: var(--artwork); - background-size: cover; - box-shadow: var(--mediaItemShadow); - flex: 0 0 auto; - margin: 6px; - margin-top: 0px; -} - -.media-item--small .text { - font-weight: 600; - font-size: 0.90em; -} - -.media-item--small .subtext { - font-size: 0.75em; -} - -.player-duration-time { - opacity: 0.5; -} - -.player-artwork-container { - display: flex; - align-items: center; - justify-content: center; -} - -.player-duration-container { - font-size: 0.85em; - font-weight: 500; -} - -.media-artwork { - --artwork: url(""); - width: 80vw; - height: 80vw; - max-height: 500px; - max-width: 500px; - background: black; - background-image: var(--artwork); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - border-radius: 8px; - box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55); - transition: transform .10s var(--appleEase); -} - -.media-artwork.paused { - transition: transform .35s var(--appleEase); - transform: scale(0.85); -} - -.playback-slider { - width: 90%; -} - -.volume-slider { - width: 100%; -} - -.volume-slider-container { - width: 90%; - margin: 0 auto; - padding: 0px; -} - -.volume-slider-container .col-auto, -.volume-slider-container .col { - display: flex; - align-items: center; - justify-self: center; - padding: 0px; - margin: 0px; -} - -.playback-button { - font-size: 2em; - width: 40px; - height: 36px; - padding: 0px; - background: transparent; - border: 0px; - border-radius: 0px; - box-shadow: unset; - background-size: 12px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.70; - border-radius: 6px; -} - -.playback-button:active { - transform: scale(0.95); -} - -.playback-button--small { - border-radius: 6px; - font-size: 1em; - color: inherit; - background-size: 14px; - background-repeat: no-repeat; - background-position: center; - background-color: transparent; - width: 40px; - height: 32px; - border: 0px; - box-shadow: unset; - opacity: 0.70; -} - -.playback-button:hover, -.playback-button--small:hover { - background-color: rgb(200 200 200 / 10%); -} - -.playback-button:active, -.playback-button--small:active { - transform: scale(0.9); -} - -.playback-button--small.active { - background-color: rgb(200 200 200 / 25%); -} - -.playback-button--small.search { - background-image: url("./assets/search.svg"); -} - -.playback-button--small.cast { - background-image: url("./assets/cast_white.svg"); -} - -.playback-button--small.miniplayer { - background-image: url("./assets/pip.svg"); -} - -.playback-button--small.queue { - background-image: url("./assets/list.svg"); -} - -.playback-button--small.lyrics { - background-image: url("./assets/quote-right.svg"); -} - -.playback-button--small.shuffle { - background-image: url("./assets/shuffle.svg"); -} - -.playback-button--small.repeat { - background-image: url("./assets/repeat.svg"); -} - -.playback-button--small.repeat.repeatOne { - background-color: rgb(200 200 200 / 25%); - background-image: url("./assets/repeatOne.svg"); -} - -.playback-button.pause { - background-image: url('./assets/pause.svg'); -} - -.playback-button.play { - background-image: url('./assets/play.svg'); -} - -.playback-button.next { - background-image: url('./assets/forward.svg'); -} - -.playback-button.previous { - background-image: url('./assets/backward.svg'); -} - -.playback-buttons { - display: flex; - align-items: center; - justify-content: center; -} - -.player-volume-glyph { - width: 32px; - height: 16px; - background-repeat: no-repeat; - background-size: contain; - background-position: center; -} - -.player-volume-glyph.decrease { - background-image: url("./assets/volume.svg"); - opacity: 0.5; -} - -.player-volume-glyph.increase { - background-image: url("./assets/volume-2.svg"); - opacity: 0.5; -} - -.player-track-info { - width: 90%; - margin: 0 auto; -} - -.player-song-title { - font-size: 1.25em; - text-align: left; - margin: 0 auto; - font-weight: 500; -} - -.player-song-artist { - font-size: 1.0em; - text-align: left; - margin: 0 auto; - color: var(--keyColor); - font-weight: 400; -} - -.player-song-artist:hover { - cursor: pointer; - text-decoration: underline; -} - -.player-more-container { - display: flex; - align-items: center; - justify-content: center; -} - -.player-more-button { - appearance: none; - width: 32px; - height: 32px; - border-radius: 50%; - border: 0px; - background: var(--keyColor); - cursor: pointer; - box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%); - color: white; - font-weight: bold; - padding: 0px; - font-size: 16px; -} - -.back-button { - width: 40px; - height: 40px; - background-color: transparent; - background-size: 16px; - background-position: center; - background-repeat: no-repeat; - background-image: url("./assets/arrow-left.svg"); - border: 0px; - border-radius: 0px; -} - -.header-text { - height: 40px; - display: flex; - align-items: center; - -} - - .flex-center { display: flex; align-items: center; flex-wrap: wrap; } -.list-entry-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px; - font-size: 1em; - font-family: inherit; -} - -.list-entry { - display: flex; - align-items: center; - /* justify-content: space-between; */ - padding: 12px; - font-size: 1em; - font-family: inherit; - border-bottom: 1px solid rgba(255 255 255 / 0.1); - cursor: pointer; -} - -.list-entry-image { - --artwork: url(""); - width: 64px; - height: 64px; - background: var(--artwork); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - border-radius: 8px; - box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55); -} - -.list-entry-image.artist { - border-radius: 50%; -} - -.list-entry-body { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - margin-left: 12px; -} - -.list-entry-name { - font-size: 14px; - font-weight: 500; - overflow: hidden; - width: 100%; -} - -.list-entry-artist { - font-size: 12px; - overflow: hidden; - width: 100%; -} - -.list-entry .handle { - height: 100%; - width: 28px; - background: var(--keyColor); - display: flex; - justify-content: center; - align-items: center; -} - .md-container { width: 100%; position: relative; } -.search-panel { - background: rgb(0 0 0 / 50%); -} - -.search-header { - position: absolute; - width: 100%; - z-index: 1; - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border-bottom: 1px solid rgb(200 200 200 / 8%); -} - -.connection-error-panel { - background: rgb(0 0 0 / 50%); -} - -.search-type-container { - display: flex; -} - -.search-type-button { - background: rgb(20 20 20 / 0.85); - border-radius: 50px; - color: white; - border: 0px; - box-shadow: unset; - font-family: inherit; - padding: 8px 16px; - font-size: 14px; - font-weight: 500; - margin: 8px; - margin-top: 0px; - margin-bottom: 0px; -} - -.search-type-button.active { - background: var(--keyColor); -} - -.search-tab-container { - overflow: auto; - white-space: nowrap; - overflow-y: hidden; -} - -.search-body-container { - position: relative; - width: 100%; - height: 100%; -} - -.search-body { - position: absolute; - width: 100%; - height: 100%; - padding-top: 220px; -} - -.search-tab { - background: rgb(20 20 20 / 0.85); - border-radius: 50px; - color: white; - border: 0px; - box-shadow: unset; - font-family: inherit; - padding: 8px 16px; - font-size: 14px; - font-weight: 500; -} - -.search-tab.active { - background: var(--keyColor); -} .lyric-body { -webkit-mask-image: -webkit-gradient(linear, left 95%, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); @@ -2235,159 +1767,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { } } -.md-close-btn { - -webkit-mask-image: url("ameres://icons/webui/close.svg"); - -webkit-mask-repeat: no-repeat; - -webkit-mask-position: center; - background-color: white; - opacity: 0.75; - -webkit-mask-size: contain; - height: 18px; - width: 18px; -} - -.md-btn { - background: rgb(100 100 100 / 25%); - padding: 8px 14px; - border-radius: 6px; - font-size: 14px; - border: 1px solid rgba(100, 100, 100, 0.35); - border-top: 1px solid rgba(100, 100, 100, 0.5); - color: #eee; - white-space: nowrap; - transition: transform 0.2s var(--appleEase), box-shadow 0.2s var(--appleEase); - - &.md-btn-block { - display: block; - width: 100%; - } - - &.md-btn-glyph { - display: flex; - align-items: center; - justify-content: center; - width: 100%; - } - - &.md-btn-primary { - background: #ff2b52a6; - color: white; - border: 1px solid rgb(220 53 69 / 25%); - border-top: 1px solid rgb(220 53 69 / 50%); - } - - &.md-btn-small { - padding: 6px 8px; - font-size: 13px; - } - - &:hover { - filter: brightness(125%); - } - - &:active { - filter: brightness(75%); - transform: scale(0.98); - transition: transform 0s var(--appleEase), box-shadow 0.2s var(--appleEase); - } - - &.md-btn-icon { - display: inline-flex; - vertical-align: middle; - justify-content: center; - - > img { - margin: 0px 16px 0px 0px; - pointer-events: none; - } - - > .md-btn-text { - margin: 0px; - } - } -} - -.btn-group { - display:inline-flex; - justify-content: center; - align-items: center; - > .md-btn { - border-radius:0px; - width:100%; - } - > .md-btn:first-child { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; - } - > .md-btn:last-child { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; - } - > .md-btn:not(:first-child):not(:last-child) { - border-radius: 0px; - } -} - -.md-ico-play { - content: url("./assets/play.svg"); - width: 10px; - height: 12px; - margin-right: 1px; - align-self: center; -} - -.md-ico-shuffle { - content: url("./assets/shuffle.svg"); - width: 1em; - height: 1em; - margin-right: 1px; - margin-bottom: -2px; - align-self: center; -} - -.md-ico-remove { - content: url("./assets/feather/x-circle-white.svg"); - width: 16px; - height: 16px; - margin-right: 1px; - margin-bottom: -1.5px; - align-self: center; -} - -.md-ico-add { - content: url("./assets/feather/plus-circle-white.svg"); - width: 1em; - height: 1em; - margin-right: 1px; - margin-bottom: -1.5px; - align-self: center; -} - -.md-select { - padding: 6px; - border-radius: 6px; - border: 1px solid rgba(200, 200, 200, 0.1); - border-top: 1px solid rgba(100, 100, 100, 0.5); - font-family: inherit; - font-size: 14px; - background: rgba(100, 100, 100, 0.25); - color: #eee; - - option { - font-size: 1em; - font-family: inherit; - padding: 8px 16px; - background: #404040; - } - - optgroup { - background: #2c2c2c; - } - - &:focus { - outline: solid 1px var(--selected); - } -} .sidebar-playlist { .folder-button-active { @@ -2408,254 +1787,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { } } -.modal-fullscreen { - display: flex; - justify-content: center; - align-items: center; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.3); - z-index: 1000; - - .modal-window { - background: #333; - border-radius: 10px; - box-shadow: var(--mediaItemShadow-Shadow); - display: flex; - flex-flow: column; - max-height: 500px; - max-width: 360px; - background: #121212; - width: 100%; - position: relative; - - &:after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - box-shadow: var(--mediaItemShadow); - z-index: 1; - border-radius: inherit; - } - - .modal-header { - width: 100%; - padding: 6px; - } - - .modal-content { - width: 100%; - height: 100%; - overflow: hidden; - overflow-y: overlay; - } - - .modal-footer { - } - } -} - -.spatialproperties-panel { - .modal-window { - height: 700px; - max-height: 700px; - width: 800px; - max-width: 800px; - overflow: hidden; - - .info-header { - padding-left: 12px; - } - - .visual-container { - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; - } - - .visual { - position: relative; - height: 250px; - width: 300px; - display: inline-flex; - align-items: flex-end; - justify-content: center; - filter: drop-shadow(2px 12px 6px rgb(0 0 0 / 25%)); - margin: 0 auto; - - .face { - position: absolute; - width: calc(12px * 6); - height: calc(12px * 6); - border-radius: 6px; - transform: rotateX(60deg) rotateZ(-45deg); - transition: transform 0.2s linear, width 0.2s linear, height 0.2s linear; - } - - .listener { - position: absolute; - width: 32px; - height: 32px; - border-radius: 6px; - transform: rotateX(60deg) rotateZ(-45deg); - transition: transform 0.2s linear, width 0.2s linear, height 0.2s linear; - background: white; - color: black; - z-index: 2; - } - - .audiosource { - position: absolute; - width: 32px; - height: 32px; - border-radius: 6px; - transform: rotateX(60deg) rotateZ(-45deg); - transition: transform 0.2s linear, width 0.2s linear, height 0.2s linear; - background: yellow; - z-index: 2; - } - - .face:nth-of-type(1) { - background: linear-gradient(45deg, #28223a, #1f2038); - z-index: 1; - } - - .face:nth-of-type(2) { - background: linear-gradient(45deg, #7d53ad, #5763ff); - transform: rotateX(60deg) rotateZ(-45deg) translateZ(30px); - opacity: 0.7; - z-index: 3; - } - } - - .modal-header { - padding: 16px; - position: relative; - overflow: hidden; - - .modal-title { - text-align: center; - } - - .close-btn { - width: 50px; - height: 100%; - background-image: var(--gfx-closeBtn); - background-position: center; - background-repeat: no-repeat; - -webkit-app-region: no-drag; - appearance: none; - border: 0; - background-color: transparent; - position: absolute; - top: 0; - right: 0; - - &:hover { - background-color: rgb(196, 43, 28) - } - } - } - } -} - -.addtoplaylist-panel { - .modal-window { - max-height: 600px; - max-width: 400px; - background: rgb(18 18 18 / 90%); - overflow: hidden; - backdrop-filter: blur(16px) saturate(180%); - - .modal-header { - padding: 16px; - position: relative; - - .modal-title { - text-align: center; - } - - .close-btn { - width: 50px; - height: 100%; - background-image: var(--gfx-closeBtn); - background-position: center; - background-repeat: no-repeat; - -webkit-app-region: no-drag; - appearance: none; - border: 0; - background-color: transparent; - position: absolute; - top: 0; - right: 0; - - &:hover { - background-color: rgb(196, 43, 28) - } - } - } - - .modal-search { - width: 100%; - padding: 0px 16px; - position: relative; - } - - .playlist-item { - appearance: none; - border: 0px; - text-align: left; - width: 100%; - margin: 0; - display: flex; - background: rgba(32, 32, 32, 0.46); - color: #eee; - font-family: inherit; - font-size: 0.98em; - padding: 6px 12px; - align-items: center; - flex-flow: row; - - .icon { - pointer-events: none; - width: 32px; - height: 32px; - display: flex; - justify-content: center; - align-items: center; - margin-right: 6px; - } - - .name { - } - - &:hover { - background: var(--selected); - } - - &:active { - background: var(--selected-click); - } - - &.focused { - background: var(--keyColor); - } - } - - .playlist-item:last-child { - border-bottom: 0px; - } - } -} - #navigation-bar { width: 100%; background: rgba(0, 0, 0, 0.25); @@ -2707,39 +1838,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { } } -.reload-btn { - background: rgb(86 86 86 / 52%); - border-radius: 100%; - width: 32px; - height: 32px; - border: 0px; - appearance: none; - display: flex; - justify-content: center; - align-items: center; -} - -.reload-btn:hover { - background: rgb(86 86 86 / 80%); - cursor: pointer; -} - -.reload-btn > svg { - height: 50%; - color: #eee; -} - -.wr-btn { - font-family: inherit; - appearance: none; - border: 0px; - border-radius: 6px; - padding: 8px; - font-weight: 600; - background: rgb(80 80 80 / 70%); - color: white; -} - .well { background: rgba(200, 200, 200, 0.05); border-radius: 10px; @@ -2768,717 +1866,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { } } -// Library - Songs page -.library-page { - padding: 0px; - - .library-header { - position: sticky; - top: 0; - left: 0; - border-bottom: 1px solid rgba(200, 200, 200, 0.05); - z-index: 6; - background: black; - padding: 0px 2em; - backdrop-filter: blur(32px); - background: rgba(0, 0, 0, 0.25); - top: var(--navigationBarHeight); - } - - .well { - margin: 2em; - } -} - -// Podcast Page -.content-inner.podcasts-page { - display: flex; - height: calc(100% - var(--navigationBarHeight)); - padding: 0px; - - .list-flat { - border-radius: 0px; - } - - .podcast-artwork { - width: 200px; - margin: 16px auto; - height: 200px; - } - - .podcasts-list { - height: 100%; - width: 280px; - background: rgb(200 200 200 / 10%); - overflow-y: overlay; - border-right: 1px solid var(--color2); - flex: none; - overflow-x: hidden; - - .podcast-list-header { - border-bottom: 1px solid var(--color2); - font-size: 0.7em; - padding: 6px; - background: #ffffff17; - text-transform: uppercase; - font-weight: 600; - opacity: 0.5; - } - - .podcasts-search { - padding: 10px; - position: sticky; - top: 0; - left: 0; - width: 100%; - border-bottom: 1px solid var(--color2); - z-index: 2; - background: #303030; - } - } - - .episodes-list { - height: 100%; - width: 100%; - background: rgb(200 200 200 / 6%); - overflow-y: overlay; - overflow-x: hidden; - - .episodes-inline-info { - padding: 14px 14px 0px 14px; - - .podcast-show-info { - display: flex; - justify-content: center; - flex-direction: column; - } - - .podcast-show-description { - margin: 32px 6px; - font-size: 0.8rem; - white-space: pre-wrap; - display: block; - } - - .podcast-artwork { - width: 120px; - margin: 0px auto; - height: 120px; - } - } - - .podcast-no-search-results { - text-align: center; - margin-top: 40px; - } - } - - .podcasts-details { - width: 300px; - flex: none; - background: rgb(255 255 255 / 5%); - overflow-y: overlay; - overflow-x: hidden; - top: 2%; - z-index: 2; - border-left: 1px solid var(--color2); - padding-bottom: 1em; - - .meta-btn { - font-size: 0.75em; - } - - .podcasts-details-header { - display: flex; - justify-content: end; - align-items: center; - position: sticky; - top: 0; - z-index: 2; - } - - .close-btn { - width: 50px; - height: 42px; - background-image: var(--gfx-closeBtn); - background-position: center; - background-repeat: no-repeat; - -webkit-app-region: no-drag; - appearance: none; - border: 0; - background-color: transparent; - position: absolute; - top: 0; - right: 0; - - &:hover { - background-color: rgb(196, 43, 28) - } - } - - .podcast-genre { - text-align: center; - margin: 6px; - font-size: 0.8em; - font-weight: 500; - opacity: 0.8; - } - - .podcast-metainfo { - text-align: center; - font-size: 0.7em; - opacity: 0.8; - } - - .podcast-header { - text-align: center; - } - - .podcast-play-btn { - width: 50%; - display: block; - margin: 0 auto; - } - - .podcast-description { - margin: 12px; - font-size: 0.75em; - white-space: pre-wrap; - display: block; - line-break: anywhere; - } - - - } - -} - -@media only screen and (max-width: 1230px) { - .content-inner.podcasts-page { - .podcasts-details { - height: 96%; - width: 300px; - flex: none; - background: rgb(20 20 20 / 97%); - overflow-y: overlay; - overflow-x: hidden; - position: absolute; - right: 2%; - top: 2%; - border-radius: 10px; - box-shadow: var(--ciderShadow-Generic); - z-index: 2; - } - } -} - -/* Album / Playlist Page */ -.playlist-page { - --bgColor: transparent; - padding: 0px; - //background: linear-gradient(180deg, var(--bgColor) 32px, var(--bgColor) 18px, transparent 60px, transparent 100%); - top: 0; - padding-top: var(--navigationBarHeight); - - .playlist-body { - padding: var(--contentInnerPadding) 2em; - margin-top: -75px; - } - - .floating-header { - position: sticky; - top: 0; - left: 0; - border-bottom: 1px solid rgba(200, 200, 200, 0.05); - z-index: 6; - padding: 0px 1em; - backdrop-filter: blur(32px); - background: rgba(0, 0, 0, 0.25); - top: var(--navigationBarHeight); - transition: opacity 0.1s var(--appleEase); - } - - .playlist-display { - padding: var(--contentInnerPadding); - min-height: 300px; - position: relative; - - .artworkContainer { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - margin: 0; - margin-top: calc(var(--navigationBarHeight) * -1); - margin-bottom: -10px; - padding: 0; - -webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%); - opacity: .7; - animation: playlistArtworkFadeIn 1s var(--appleEase); - - .artworkMaterial > img { - filter: brightness(100%) blur(80px) saturate(100%) contrast(1); - object-position: center; - object-fit: cover; - width: 100%; - height: 100%; - transform: unset; - } - } - - .playlistInfo { - z-index: 1; - position: absolute; - bottom: 0; - left: 0; - right: 0; - top: 0; - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - - > .row { - width: calc(100% - 32px); - } - - .playlist-info { - flex-shrink: unset; - display: flex; - flex-flow: column; - justify-content: flex-end; - - .playlist-name { - font-weight: 700; - font-size: 1.6rem; - margin-bottom: 6px; - margin-right: 6px; - flex-shrink: unset; - } - - .nameEdit { - font-weight: 700; - font-size: 1.6rem; - margin-bottom: 6px; - margin-right: 6px; - flex-shrink: unset; - background: transparent; - border: 0px; - color: inherit; - font-family: inherit; - } - - .playlist-artist { - font-size: 20px; - margin-bottom: 6px; - margin-right: 6px; - flex-shrink: unset; - } - - .playlist-desc { - box-sizing: border-box; - font-size: 14px; - flex-shrink: unset; - margin-right: 5px; - max-height: 100px; - position: relative; - - .content { - height: 100px; - -webkit-mask-image: -webkit-gradient(linear, left 50%, left 90%, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); - } - - .more-btn { - appearance: none; - position: absolute; - right: 0; - bottom: 0; - padding: 0 5px; - font-size: 14px; - color: var(--keyColor); - background-color: transparent; - border: 0px; - cursor: pointer; - width: 100%; - height: 100%; - overflow: hidden; - display: flex; - justify-content: flex-end; - align-items: flex-end; - font-weight: 600; - font-family: inherit; - text-transform: uppercase; - } - } - - .playlist-desc-expanded { - box-sizing: border-box; - font-size: 14px; - position: relative; - - .more-btn { - appearance: none; - position: absolute; - right: 0; - bottom: 0; - padding: 0 5px; - font-size: 14px; - color: var(--keyColor); - background-color: transparent; - border: 0px; - cursor: pointer; - width: 100%; - height: 100%; - overflow: hidden; - display: flex; - justify-content: flex-end; - align-items: flex-end; - font-weight: 600; - font-family: inherit; - text-transform: uppercase; - } - } - } - } - - - } - - .friends-info { - display: flex; - flex-flow: column; - - .badge-container { - display: flex; - flex-flow: wrap; - - .socialBadge { - width: 40px; - height: 40px; - border-radius: 100%; - overflow: hidden; - box-shadow: var(--mediaItemShadow-ShadowSubtle); - transition: transform .2s var(--appleEase); - margin: 6px; - - &:hover { - transform: scale(1.2); - } - } - } - - .friends-name { - text-align: center; - font-size: 0.9em; - margin: 8px; - } - } - - .playlist-time { - font-size: 0.9em; - margin: 6px; - opacity: 0.7; - } - - &.inline-playlist { - overflow: hidden; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - z-index: 6; - position: sticky; - margin-top: calc(var(--navigationBarHeight) * -1); - - .floating-header { - opacity: 1; - top: 0px; - z-index: 6; - padding: 1em; - backdrop-filter: unset; - background: black; - h3 { - display: none; - } - } - .playlist-inner { - background: black; - width: 80%; - height: 100%; - overflow: overlay; - box-shadow: var(--ciderShadow-Generic); - border-radius: var(--mediaItemRadius) var(--mediaItemRadius) 0px 0px; - - .close-btn { - position: sticky; - top: 16px; - left: 16px; - margin-left: 16px; - z-index: 7; - } - } - } -} - -@keyframes playlistArtworkFadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 0.7; - } -} - -// Collection Page -.collection-page { - padding-bottom: 128px; - - .top-fab { - height: 52px; - width: 52px; - position: fixed; - bottom: 32px; - right: 32px; - border-radius: 100%; - background: rgb(60 60 60); - border: 0px; - appearance: none; - box-shadow: var(--ciderShadow-Generic); - - > svg { - height: 50%; - color: #eee; - pointer-events: none; - } - - &:hover { - background: rgb(100 100 100); - } - - &:active { - background: var(--keyColor); - } - } - - .header-text { - margin-bottom: 32px; - } -} - -/* Artist Page */ - -.artist-page { - padding: 0px; - top: 0; - - .floating-header { - position: sticky; - top: 0; - left: 0; - border-bottom: 1px solid rgba(200, 200, 200, 0.05); - z-index: 6; - padding: 0px 1em; - backdrop-filter: blur(32px); - background: rgba(0, 0, 0, 0.25); - top: var(--navigationBarHeight); - transition: opacity 0.1s var(--appleEase); - } - - &.animated .artist-header .more-btn-round { - position: absolute; - bottom: 22px !important; - right: 28px; - } - - .artist-header { - //background: linear-gradient(45deg, var(--keyColor), #0e0e0e); - color: white; - display: flex; - align-items: center; - justify-content: space-between; - min-height: 400px; - position: relative; - pointer-events: none; - - .header-content { - z-index: 1; - margin-top: -16px; - } - - - .artworkContainer { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - margin: 0; - padding: 0; - -webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%); - opacity: .7; - animation: playlistArtworkFadeIn 1s var(--appleEase); - - .artworkMaterial > img { - filter: brightness(100%) blur(80px) saturate(100%) contrast(1); - object-position: center; - object-fit: cover; - width: 100%; - height: 100%; - transform: unset; - } - } - - .more-btn-round { - position: absolute; - bottom: 82px; - right: 28px; - } - - .animated { - width: 100%; - height: 100%; - align-self: center; - position: absolute; - overflow: hidden; - box-shadow: rgb(0 0 0 / 50%) 0 0 0 1000000px inset; - - video { - overflow: hidden; - height: 100%; - width: 100%; - min-height: 56.25vw; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - } - - .row .col.flex-center { - z-index: 4; - } - } - - .artist-image { - width: 200px; - height: 200px; - margin: 32px; - position: relative; - - .overlay-play { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0; - background: rgb(0 0 0 / 50%); - transition: opacity 0.1s var(--appleEase); - border-radius: 100%; - z-index: 1; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - appearance: none; - border: 0px; - padding: 0px; - - &:hover { - opacity: 1; - } - - &:active { - background: var(--selected-click); - } - - > svg { - width: 70%; - } - } - } - - .artist-play { - width: 32px; - height: 32px; - background: rgba(100, 100, 100, 0.5); - box-shadow: var(--ciderShadow-Generic); - border-radius: 100%; - box-shadow: var(--mediaItemShadow); - display: none; - cursor: pointer; - appearance: none; - border: 0px; - padding: 0px; - - &:hover { - filter: brightness(125%); - } - - &:active { - filter: brightness(75%); - transform: scale(0.98); - transition: transform 0s var(--appleEase), box-shadow 0.2s var(--appleEase); - } - } - - .artist-title { - - .artist-play { - transform: translateY(3px); - margin: 14px; - } - - &.artist-animation-on { - width: 100%; - flex: unset; - margin-left: 0.5em; - color: whitesmoke; - position: absolute; - bottom: 0; - - .artist-play { - display: block; - } - } - } - - .artist-body { - padding: 0px var(--contentInnerPadding) 0px var(--contentInnerPadding); - margin: -140px 20px; - } - - &.animated > .artist-body { - padding: 0px var(--contentInnerPadding) 0px var(--contentInnerPadding); - margin-top: -57px; - } - - .showmoreless { - font-family: inherit; - font-size: 16px; - font-weight: 500; - background: transparent; - border: 0px; - border-radius: 6px; - appearance: none; - color: var(--keyColor); - padding: 8px 12px; - cursor: pointer; - margin-top: 12px; - float: right; - } - - .showmoreless:hover { - background: rgb(200 200 200 / 10%); - } -} - -/* Artist Page End */ - .text-overflow-elipsis { display: -webkit-box; min-width: 0px; @@ -3489,1138 +1876,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { } -.mediaitem-artwork { - border-radius: var(--mediaItemRadius); - overflow: hidden; - flex: 0 0 auto; - position: relative; - width: 100%; - height: 100%; - background-image: url("https://beta.music.apple.com/assets/product/MissingArtworkMusic.svg"); - background-size: cover; - background-position: center; - - .animatedartwork-view-box { - position: absolute; - top: 0px; - width: 100%; - height: 100%; - - .animated { - position: absolute; - top: 0px; - width: 100%; - height: 100%; - - video { - width: 100%; - height: 100%; - } - } - } - - &.rounded { - border-radius: 100%; - - &::after { - border-radius: 100%; - } - } - - &::after { - content: ""; - box-shadow: var(--mediaItemShadow); - z-index: 1; - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - border-radius: inherit; - } - - img { - width: 100%; - height: 100%; - object-fit: cover; - image-rendering: -webkit-optimize-contrast; - pointer-events: none; - } - - &.no-shadow { - box-shadow: none; - - &::after { - display: none; - } - } - - &.subtle-shadow { - box-shadow: var(--mediaItemShadow-ShadowSubtle); - } - - &.shadow { - box-shadow: var(--mediaItemShadow-Shadow); - } -} - -.menu-panel { - width: 100%; - height: 100%; - position: fixed; - top: 0; - left: 0; - z-index: 100001; - display: flex; - justify-content: center; - align-items: center; - -webkit-app-region: no-drag; - - .menu-header-body { - padding: 6px; - display: flex; - background: rgb(200 200 200 / 10%); - - .menu-option-header { - width: 40px; - height: 40px; - display: flex; - justify-content: center; - align-items: center; - border-radius: var(--mediaItemRadius); - appearance: none; - border: 0; - background: transparent; - - &.active { - .sidebar-icon > .svg-icon { - --color: var(--keyColor); - } - } - - &:hover { - background: var(--selected); - } - - &:active { - background: var(--selected-click); - } - } - } - - .menu-panel-body { - display: flex; - flex-flow: column; - background: rgb(38 38 38); - position: relative; - min-width: 200px; - box-shadow: var(--ciderShadow-Generic); - border-radius: var(--mediaItemRadius); - overflow: hidden; - font-size: 14px; - - .menu-option { - text-align: left; - display: flex; - width: 100%; - padding: 12px 16px; - appearance: none; - border: 0px; - font: inherit; - background: transparent; - color: inherit; - - &:hover { - background: var(--selected); - } - - &:active { - background: var(--selected-click); - } - } - } - - - .menu-header-text { - margin: 18px 6px; - - .close-btn { - width: 50px; - height: 42px; - background-image: var(--gfx-closeBtn); - background-position: center; - background-repeat: no-repeat; - -webkit-app-region: no-drag; - appearance: none; - border: 0; - background-color: transparent; - position: absolute; - top: 0; - right: 0; - - &:hover { - background-color: rgb(196, 43, 28) - } - } - } - - .menu-body { - overflow: overlay; - height: 100%; - } - - .menu-footer { - width: 100%; - padding: 12px; - } -} - -.queue-panel { - height: 100%; - width: 100%; - display: flex; - flex-flow: column; - - .queue-header-text { - margin: 18px 6px; - } - - .queue-body { - overflow: overlay; - height: 100%; - } - - .queue-footer { - width: 100%; - padding: 12px; - } - - .autoplay { - background: rgb(200 200 200 / 15%); - display: flex; - justify-content: center; - appearance: none; - border: 0; - border-radius: 6px; - height: 32px; - width: 32px; - } - - .infinity { - content: url("./assets/infinity.svg"); - margin: auto; - } -} - -/* queue item */ -.cd-queue-item { - border-bottom: 1px solid rgb(200 200 200 / 10%); - padding: 8px; - - .row, - .col { - padding: 0px; - margin: 0px; - } - - .artwork { - width: 32px; - height: 32px; - flex: 0 0 auto; - } - - &.selected { - background: var(--selected); - } - - &:active { - background: var(--selected-click); - color: #eee; - } - - .queue-info { - display: flex; - flex-direction: column; - - .queue-title { - font-size: 14px; - } - - .queue-subtitle { - font-size: 13px; - opacity: 0.85; - } - } -} - -/* horizontal media scroller */ -.cd-hmedia-scroller { - &::-webkit-scrollbar-thumb { - box-shadow: none; - } - - &:hover::-webkit-scrollbar-thumb { - box-shadow: inset 0px 0px 10px 10px rgb(200 200 200 / 50%); - } - - &.hmedia-scroller-card { - .mediaitem-card { - margin: 16px; - } - } -} - -/* mediaitem-list-item */ -.cd-mediaitem-list-item { - width: 100%; - height: 60px; - display: flex; - flex: 0 0 auto; - flex-direction: row; - font-size: 14px; - justify-content: center-between; - align-items: center; - border-radius: var(--mediaItemRadius); - - .artwork { - height: 42px; - width: 42px; - border-radius: var(--mediaItemRadius); - object-fit: cover; - object-position: center; - flex: 0 0 auto; - background-repeat: no-repeat; - margin: 12px; - border: 0px; - outline: none; - position: relative; - overflow: hidden; - - .overlay-play { - background: rgba(0, 0, 0, 0.5); - opacity: 0; - appearance: none; - border: 0; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - padding: 0px; - z-index: 5; - cursor: pointer; - - &:hover { - opacity: 1; - } - - &:active { - background: var(--selected-click); - } - } - } - - .artwork.round { - border-radius: var(--mediaItemRadiusRound); - } - - .info-rect { - height: 100%; - display: flex; - flex-flow: column; - justify-content: center; - flex-grow: 1; - } - - .title { - width: 100%; - } - - .subtitle { - width: 90%; - font-size: .8em; - opacity: 0.7; - } - - .duration { - min-width: 60px; - text-align: center; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - } - - .metainfo { - min-width: 145px; - text-align: center; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - } - - .explicit-icon { - background-image: url("./assets/explicit.svg"); - height: 12px; - width: 36px; - filter: contrast(0); - background-repeat: no-repeat; - } - - /* CSS.gg - */ - @keyframes load-bar { - 10% { - box-shadow: inset 0 -4px 0 - } - 20% { - box-shadow: inset 0 -10px 0 - } - 30% { - box-shadow: inset 0 -12px 0 - } - 40% { - box-shadow: inset 0 -8px 0 - } - 50% { - box-shadow: inset 0 -4px 0 - } - 60% { - box-shadow: inset 0 -6px 0 - } - 80% { - box-shadow: inset 0 -12px 0 - } - 90% { - box-shadow: inset 0 -6px 0 - } - to { - box-shadow: inset 0 -2px 0 - } - } - - .loadbar-sound, - .loadbar-sound::after, - .loadbar-sound::before { - animation: load-bar 1.3s ease infinite alternate; - box-sizing: border-box; - width: 3px; - height: 28px; - box-shadow: inset 0 -12px 0; - } - - .loadbar-sound { - margin-left: 22px; - margin-top: -16px; - position: relative; - transform: scale(var(--load-bar, 1)); - color: var(--keyColor); - display: block; - } - - .loadbar-sound::after, - .loadbar-sound::before { - content: ""; - position: absolute; - bottom: 0 - } - - .loadbar-sound::before { - left: -4.5px; - animation-delay: -2.4s - } - - .loadbar-sound::after { - right: -4.2px; - animation-delay: -3.7s - } - - .isLibrary { - flex: 0 0 auto; - width: 40px; - text-align: center; - - button { - appearance: none; - border: 0px; - background: transparent; - cursor: pointer; - filter: contrast(0.8); - } - } - - &:hover { - background: rgb(200 200 200 / 10%); - box-shadow: var(--mediaItemShadow); - - .overlay-play { - opacity: 1; - } - } - - &.mediaitem-selected { - background: var(--selected); - box-shadow: var(--mediaItemShadow); - } - - - &:active { - background: var(--selected-click); - box-shadow: var(--mediaItemShadow); - color: #eee; - } - - // list item compact - &.compact { - height: 40px; - font-size: 13px; - - .artwork { - display: none; - } - - .info-rect { - padding-left: 1em; - } - } -} - -/* mediaitem-hrect */ -.cd-mediaitem-hrect { - background: rgb(255 255 255 / 18%); - width: 264px; - height: 100px; - display: inline-flex; - flex: 0 0 auto; - flex-direction: row; - font-size: 14px; - justify-content: center; - align-items: center; - border-radius: 6px; - cursor: pointer; - - .artwork { - height: 70px; - width: 70px; - background: blue; - border-radius: var(--mediaItemRadius); - background: var(--artwork); - background-size: contain; - flex: 0 0 auto; - background-repeat: no-repeat; - margin: 18px; - } - - .artwork.round { - border-radius: var(--mediaItemRadiusRound); - } - - .info-rect { - width: 100%; - } - - .title { - width: 100%; - text-align: center; - } - - .subtitle { - width: 100%; - text-align: center; - font-size: 12px; - } -} - -/* mediaitem-square-sp */ -.cd-mediaitem-square-sp { - --spcolor: var(""); - width: 190px; - height: 245px; - display: inline-flex; - flex: 0 0 auto; - flex-direction: column; - font-size: 14px; - justify-content: flex-start; - align-items: center; - border-radius: 6px; - margin-left: 10px; - cursor: pointer; - background-color: var(--spcolor); - - .artwork { - height: 190px; - width: 190px; - background: blue; - border-top-left-radius: 6px; - border-top-right-radius: 6px; - background: var(--artwork); - background-size: cover; - flex: 0 0 auto; - margin: 6px; - margin-top: 0px; - - &.round { - border-radius: var(--mediaItemRadiusRound); - } - - &:hover { - box-shadow: rgb(0 0 0 / 50%) 0 0 0 1000000px inset; - } - } - - .title { - width: 90%; - text-align: center; - } - - .subtitle { - width: 100%; - text-align: center; - font-size: 12px; - } - - > .cd-mediaitem-square-large-overlay { - z-index: 3; - - &:hover { - opacity: 1; - } - } - - + .cd-mediaitem-square-large-overlay { - pointer-events: none; - } - - &:hover + .cd-mediaitem-square-large-overlay { - opacity: 1; - - } - - &:hover { - box-shadow: rgb(0 0 0 / 50%) 0 0 0 1000000px inset; - } -} - - -/* mediaitem-square-large */ -.cd-mediaitem-square-large { - width: 190px; - height: 250px; - display: inline-flex; - flex: 0 0 auto; - flex-direction: column; - font-size: 14px; - justify-content: flex-start; - align-items: center; - border-radius: 6px; - margin-left: 10px; - cursor: pointer; - - > * { - z-index: inherit; - } -} - -.cd-mediaitem-square-large .artwork { - height: 190px; - width: 190px; - background: blue; - border-top-left-radius: 6px; - border-top-right-radius: 6px; - background: var(--artwork); - background-size: cover; - flex: 0 0 auto; - margin: 6px; - margin-top: 0px; -} - -.cd-mediaitem-square-large-overlay { - position: absolute; - width: 190px; - float: right; - height: 250px; - top: 0px; - margin: 10px; - margin-top: 0px; - opacity: 0; - -} - -.cd-mediaitem-square-large-overlay > * { - pointer-events: auto; - -} - -.cd-mediaitem-square-large > .cd-mediaitem-square-large-overlay { - z-index: 3; -} - -.cd-mediaitem-square-large > .cd-mediaitem-square-large-overlay:hover { - opacity: 1; -} - -.cd-mediaitem-square-large + .cd-mediaitem-square-large-overlay { - pointer-events: none; - -} - -.cd-mediaitem-square-large:hover + .cd-mediaitem-square-large-overlay { - opacity: 1; - -} - - -.cd-mediaitem-square-large .artwork.round { - border-radius: var(--mediaItemRadiusRound); -} - -.cd-mediaitem-square-large .title { - width: 90%; - text-align: center; -} - -.cd-mediaitem-square-large .subtitle { - width: 100%; - text-align: center; - font-size: 12px; -} - -/* mediaitem-mvview */ - -/* mediaitem-mvview */ -.cd-mediaitem-mvview { - width: 300px; - height: 250px; - display: inline-flex; - flex: 0 0 auto; - flex-direction: column; - font-size: 14px; - justify-content: flex-start; - align-items: center; - border-radius: 6px; - margin-left: 10px; - cursor: pointer; - - > * { - z-index: inherit; - } -} - -.cd-mediaitem-mvview .artwork { - height: 172px; - width: 300px; - background: blue; - border-top-left-radius: 6px; - border-top-right-radius: 6px; - background: var(--artwork); - background-size: cover; - flex: 0 0 auto; - margin: 6px; - margin-top: 0px; -} - -.cd-mediaitem-mvview-overlay { - position: absolute; - width: 300px; - float: right; - height: 250px; - top: 0px; - margin: 10px; - margin-top: 0px; - opacity: 0; - -} - -.cd-mediaitem-mvview-overlay > * { - pointer-events: auto; - -} - -.cd-mediaitem-mvview > .cd-mediaitem-mvview-overlay { - z-index: 3; -} - -.cd-mediaitem-mvview > .cd-mediaitem-mvview-overlay:hover { - opacity: 1; -} - -.cd-mediaitem-mvview + .cd-mediaitem-mvview-overlay { - pointer-events: none; - -} - -.cd-mediaitem-mvview:hover + .cd-mediaitem-mvview-overlay { - opacity: 1; - -} - - -.cd-mediaitem-mvview .artwork.round { - border-radius: var(--mediaItemRadiusRound); -} - -.cd-mediaitem-mvview .title { - width: 90%; - text-align: center; -} - -.cd-mediaitem-mvview .subtitle { - width: 100%; - text-align: center; - font-size: 12px; -} - - -/* mediaitem-square */ -.cd-mediaitem-square { - width: 220px; - height: 238px; - display: inline-flex; - flex: 0 0 auto; - flex-direction: column; - font-size: 14px; - justify-content: center; - align-items: center; - border-radius: 6px; - - .artwork-container { - position: relative; - - .artwork { - height: 190px; - width: 190px; - background: blue; - border-radius: var(--mediaItemRadius); - background: var(--artwork); - background-size: cover; - flex: 0 0 auto; - margin: 6px; - cursor: pointer; - - &.round { - border-radius: var(--mediaItemRadiusRound); - } - } - - .badge-container { - transition: opacity 0.1s var(--appleEase); - opacity: 1; - - .socialBadge { - width: 32px; - height: 32px; - position: absolute; - right: 14px; - bottom: 14px; - border-radius: 100%; - overflow: hidden; - z-index: 2; - pointer-events: none; - } - } - - > .play-btn, - > .menu-btn { - opacity: 0; - appearance: none; - padding: 0px; - border: 0px; - width: 30px; - height: 30px; - border-radius: 50%; - background: rgba(50, 50, 50, 0.7); - cursor: pointer; - transition: opacity 0.1s var(--appleEase); - - :hover { - border-radius: 50%; - background: rgba(250, 0, 0, 0.7); - } - } - - > .play-btn { - position: absolute; - bottom: 14px; - left: 14px; - z-index: 2; - - } - - > .menu-btn { - position: absolute; - bottom: 14px; - right: 14px; - z-index: 2; - } - - &:hover { - > .badge-container { - opacity: 0; - } - - > .play-btn, - > .menu-btn { - opacity: 1; - } - } - } - - .info-rect { - width: 90%; - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - } - - - .title { - width: 100%; - text-align: center; - display: flex; - align-content: center; - justify-content: center; - } - - .subtitle { - width: 100%; - text-align: center; - font-size: 12px; - } - - &.mediaitem-video { - height: 200px; - width: 240px; - - .artwork { - height: 120px; - width: 212px; - } - } - - &.mediaitem-brick { - height: 200px; - width: 240px; - - .artwork { - height: 123px; - width: 220px; - } - } - - &.mediaitem-small { - width: 140px; - height: 180px; - - .artwork { - height: 128px; - width: 128px; - } - } - - &.mediaitem-card { - background: #ccc; - background: var(--spcolor); - height: 298px; - width: 230px; - max-width: 250px; - max-height: 500px; - overflow: hidden; - position: relative; - border-radius: calc(var(--mediaItemRadius) * 2); - box-shadow: var(--mediaItemShadow-ShadowSubtle); - - .artwork { - width: 230px; - height: 230px; - overflow: hidden; - border-radius: 0px; - margin: 0; - - .mediaitem-artwork { - border-radius: 0px; - - &::after { - box-shadow: unset; - } - } - } - - .info-rect-card { - padding: 10px 10px 14px; - position: relative; - width: 100%; - - &::before { - background: var(--bgartwork); - content: ""; - top: 0; - left: 0; - bottom: 0; - right: 0; - position: absolute; - background-size: cover; - background-position: bottom; - z-index: 0; - opacity: 1; - filter: brightness(0.5) blur(50px) saturate(180%); - } - } - - .title { - height: 100%; - display: flex; - justify-content: center; - align-items: center; - font-size: 0.9em; - font-weight: 500; - z-index: 1; - } - - .subtitle { - height: 100%; - justify-content: center; - align-items: center; - font-size: 0.75em; - width: 100%; - display: flex; - z-index: 1; - } - - &::after { - box-shadow: var(--mediaItemShadow); - content: ""; - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - pointer-events: none; - border-radius: inherit; - } - } -} - -/* mediaitem-square */ -.albums-square-containeru > * > .cd-mediaitem-square { - --frame: max(220px, 15vw); - width: var(--frame); - height: calc(var(--frame) * 13 / 11); - display: inline-flex; - flex: 0 0 auto; - flex-direction: column; - font-size: calc(var(--frame) / 220 * 14); - justify-content: center; - align-items: center; - border-radius: calc(var(--frame) / 220 * 6); - - .artwork-container { - position: relative; - - .artwork { - height: calc(var(--frame) * 19 / 22); - width: calc(var(--frame) * 19 / 22); - background: blue; - border-radius: var(--mediaItemRadius); - background: var(--artwork); - background-size: cover; - flex: 0 0 auto; - margin: calc(var(--frame) / 220 * 6); - cursor: pointer; - - &.round { - border-radius: var(--mediaItemRadiusRound); - } - } - - > .play-btn, - > .menu-btn { - opacity: 0; - appearance: none; - padding: 0px; - border: 0px; - width: calc(var(--frame) / 220 * 30); - height: calc(var(--frame) / 220 * 30); - border-radius: 50%; - background: rgba(50, 50, 50, 0.7); - cursor: pointer; - backdrop-filter: blur(32px) saturate(180%); - transition: opacity 0.1s var(--appleEase); - } - - > .play-btn { - position: absolute; - bottom: calc(var(--frame) / 220 * 14); - left: calc(var(--frame) / 220 * 14); - z-index: 2; - } - - > .menu-btn { - position: absolute; - bottom: calc(var(--frame) / 220 * 14); - right: calc(var(--frame) / 220 * 14); - z-index: 2; - } - - &:hover { - - > .play-btn, - > .menu-btn { - opacity: 1; - } - } - } - - - .title { - width: 90%; - text-align: center; - } - - .subtitle { - width: 100%; - text-align: center; - font-size: calc(var(--frame) / 220 * 12); - } - - &.mediaitem-video { - height: calc(var(--frame) / 220 * 200); - width: calc(var(--frame) / 220 * 240); - - .artwork { - height: calc(var(--frame) / 220 * 120); - width: calc(var(--frame) / 220 * 212); - } - } - - &.mediaitem-brick { - height: calc(var(--frame) / 220 * 200); - width: calc(var(--frame) / 220 * 240); - - .artwork { - height: calc(var(--frame) / 220 * 123); - width: calc(var(--frame)); - } - } -} - -.cd-btn-seeall { - background: transparent; - border: 0px; - color: var(--keyColor); - font-family: inherit; - font-weight: 500; - font-size: 16px; - border-radius: 4px; - padding: 6px; - - &:hover { - cursor: pointer; - background: rgb(200 200 200 / 10%) - } -} - .fullscreen-view-container { position: absolute; top: 0; @@ -5414,81 +2669,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { // Cider App -.listitem-horizontal { - .cd-mediaitem-list-item { - width: 350px; - height: 60px; - } -} - -.mediaitem-list-item__grid { - background: rgba(200, 200, 200, 0.05); - border-radius: 10px; - padding: var(--contentInnerPadding); - box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 0px 1px; - width: 100%; - - .cd-mediaitem-list-item { - width: 350px; - height: 60px; - } - - &::-webkit-scrollbar { - display: none; - } - - &:hover::-webkit-scrollbar { - display: initial; - - } -} - -.settings-page { - padding: 0px; - - .md-option-header { - padding: 1.25em 1.25em; - border-bottom: unset; - border-top: unset; - font-weight: 600; - font-size: 1.0em; - background: rgb(255 255 255 / 3%); - } - - .settings-option-body { - margin: 16px; - } -} - -// sidebar icon -.svg-icon { - --color: #aaa; - --url: url("./assets/feather/share.svg"); - -webkit-mask-image: var(--url); - -webkit-mask-size: cover; - height: 18px; - width: 18px; - background: var(--color); -} - -.sidebar-icon { - width: 18px; - height: 18px; - margin-right: 8px; - - > .svg-icon { - width: 100%; - height: 100%; - --color: #aaa; - } - - > svg { - width: 100%; - height: 100%; - color: #aaa; - } -} - @keyframes rotate { from { transform: rotate(0deg); @@ -5639,18 +2819,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { right: -300px; } -/* Transitions End */ - -// @media (prefers-color-scheme: light) { -// :root { -// /* */ -// --gfx-closeBtn: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6MjY6MjgtMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI2OjI4LTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6MjY6MjgtMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PtPY89oAAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAEpJREFUGJWNkMENwDAIA1Gh3F7sP0Ty7xD5JgpU+OuTLVvMmQohhRTCnCEK8TpfBl9eBpcBu/HXcsAZ9FQjSrWqW2Pa95gzGofPBQZRG2fS0899AAAAAElFTkSuQmCC'); -// --gfx-maxBtn: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6MjU6NTItMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI1OjUyLTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6MjU6NTItMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PntJ23wAAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAEhJREFUGJVjYGBgmMzAwPCXgYHhPw78l4GBYTIjlKHHwMDwlAE7EGFgYLjJANUlgEMRDPxnIqAADoaKwn8MkCDABaShaogLcACWZRl4ajKVtAAAAABJRU5ErkJggg=='); -// --gfx-restoreBtn: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAIn2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgZGM6Zm9ybWF0PSJpbWFnZS9wbmciCiAgIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiCiAgIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMTAiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLjAiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLjAiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTE3VDEyOjU1OjM3WiIKICAgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjEtMTAtMDVUMTQ6MjY6NTMtMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIxLTEwLTA1VDE0OjI2OjUzLTA3OjAwIgogICB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6ZTk5OWM2NWYtNDhhOS0wNjQyLWI2MTktZmJlYTExMmUxOGZiIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY5MzMyOWNhLWNkNjctMzY0Zi04MzU1LTY5N2ZmYzI0ZDdlZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjgyZjQwYmU3LTE0YzItZjc0Ni1hZmE1LWQxYmIxNzAyMjM4OCIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjEwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMTAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSI+CiAgIDxwaG90b3Nob3A6VGV4dExheWVycz4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qSiIgogICAgICBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKIiLz4KICAgIDwvcmRmOlNlcT4KICAgPC9waG90b3Nob3A6VGV4dExheWVycz4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0iY3JlYXRlZCIKICAgICAgeG1wTU06aW5zdGFuY2VJRD0ieG1wLmlpZDo4MmY0MGJlNy0xNGMyLWY3NDYtYWZhNS1kMWJiMTcwMjIzODgiCiAgICAgIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIKICAgICAgeG1wTU06d2hlbj0iMjAyMC0wMi0xN1QxMjo1NTozN1oiLz4KICAgICA8cmRmOmxpCiAgICAgIHhtcE1NOmFjdGlvbj0ic2F2ZWQiCiAgICAgIHhtcE1NOmNoYW5nZWQ9Ii8iCiAgICAgIHhtcE1NOmluc3RhbmNlSUQ9InhtcC5paWQ6NjkzMzI5Y2EtY2Q2Ny0zNjRmLTgzNTUtNjk3ZmZjMjRkN2VkIgogICAgICB4bXBNTTpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiCiAgICAgIHhtcE1NOndoZW49IjIwMjAtMDItMTdUMTI6NTU6MzdaIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjEtMTAtMDVUMTQ6MjY6NTMtMDc6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PluKsJ0AAAGCaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWRv0tCURTHP2lhmGFRQUODhDVZlELU0qD0C6pBDbJa9OWPQO3xnhHRGrQKBVFLv4b6C2oNmoOgKIJoC5qLWkpe56mgRJ7Luedzv/eew73ngiWcVjJ6/QBksjktOOF3zUcWXLZX7DTQQSu+qKKrM6HxMDXt64E6M971mbVqn/vXmpbjugJ1jcKjiqrlhCeFp9dzqsm7wu1KKrosfC7s0eSCwvemHivxm8nJEv+YrIWDAbC0CLuSVRyrYiWlZYTl5bgz6TWlfB/zJY54di4ksVu8C50gE/hxMcUYAYYYZETmIfrw0i8rauQPFPNnWZVcRWaVDTRWSJIih0fUNakel5gQPS4jzYbZ/7991RM+b6m6ww8NL4bx0QO2HSjkDeP72DAKJ2B9hqtsJX/1CIY/Rc9XNPchOLfg4rqixfbgchs6n9SoFi1KVnFLIgHvZ9AcgbZbsC+Welbe5/QRwpvyVTewfwC9ct659At2bGftHD0UJwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAGlJREFUGJWt0LEOgkAQhOFPShspSWyvMPFxKaDh0XgOq8tZuCqBIzRMMsXmn51slr9GlJVfmHC9LII3LGfoomCGHrnSloM9olnGE+3KKVgbi0oMNX1ZaXYCG50aLBj4HJwqgXuwn47eA95vgSGy6zr7OwAAAABJRU5ErkJggg=='); -// --gfx-minBtn: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxMAAAsTAQCanBgAAAaxaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pg0KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPg0KICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPg0KICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgeG1wOkNyZWF0ZURhdGU9IjIwMjAtMDItMTdUMTM6MDA6MzJaIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTAyLTE3VDEzOjAwOjMyWiIgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDItMTdUMTM6MDA6MzJaIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjg1ZDBlZGIwLWZkMDAtYjY0Zi05ZWZiLWEyYjQ1ODcwNWE4YSIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmEzMDAxZTFhLTI5MTktZTQ0OS1iOTRjLWMyMTIyNDhhOWY4YSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjc4N2Y3OTljLTRiMTEtZTU0YS1iMjBkLTY4NjE3ZWQ5YzVlMiIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciPg0KICAgICAgPHhtcE1NOkhpc3Rvcnk+DQogICAgICAgIDxyZGY6U2VxPg0KICAgICAgICAgIDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc4N2Y3OTljLTRiMTEtZTU0YS1iMjBkLTY4NjE3ZWQ5YzVlMiIgc3RFdnQ6d2hlbj0iMjAyMC0wMi0xN1QxMzowMDozMloiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgLz4NCiAgICAgICAgICA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ODVkMGVkYjAtZmQwMC1iNjRmLTllZmItYTJiNDU4NzA1YThhIiBzdEV2dDp3aGVuPSIyMDIwLTAyLTE3VDEzOjAwOjMyWiIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIiAvPg0KICAgICAgICA8L3JkZjpTZXE+DQogICAgICA8L3htcE1NOkhpc3Rvcnk+DQogICAgICA8cGhvdG9zaG9wOlRleHRMYXllcnM+DQogICAgICAgIDxyZGY6QmFnPg0KICAgICAgICAgIDxyZGY6bGkgcGhvdG9zaG9wOkxheWVyTmFtZT0i7qShIiBwaG90b3Nob3A6TGF5ZXJUZXh0PSLupKEiIC8+DQogICAgICAgIDwvcmRmOkJhZz4NCiAgICAgIDwvcGhvdG9zaG9wOlRleHRMYXllcnM+DQogICAgPC9yZGY6RGVzY3JpcHRpb24+DQogIDwvcmRmOlJERj4NCjwveDp4bXBtZXRhPg0KPD94cGFja2V0IGVuZD0iciI/PmeGdrgAAAAVSURBVChTYxhOgBGI/0OYQx0wMAAAyp4BAZ26NiYAAAAASUVORK5CYII='); -// } -// } - @media (prefers-color-scheme: dark) { } @@ -5783,78 +2951,6 @@ div#captions { font-weight: 500; } -/* Switch Checkbox */ -input[type=checkbox][switch] { - width: 38px; - appearance: none; - border-radius: 32px; - height: 24px; - zoom: 1; - top: 0; - cursor: pointer; - left: 0; - position: relative; - transform: scale(1); - background: rgb(142 142 147 / 100%); - padding: 0; - margin: 0; -} - -input[type=checkbox][switch]:focus, -input[type=checkbox][switch]:active { - outline: none; -} - -input[type=checkbox][switch]:checked { - background: var(--keyColor); - border: 0 solid var(--keyColor); - mix-blend-mode: unset; - - &:hover { - background: var(--keyColor-rollover); - } - - &:active { - background: var(--keyColor-pressed); - } -} - -input[type=checkbox][switch]::before { - background: white; - width: 26px; - height: 26px; - top: -1px; - left: -1px; - position: absolute; - content: ' '; - border-radius: 32px; - transition: .10s left var(--appleEase); - transform: scale(.75); -} - - -input[type=checkbox][switch]:checked::before { - background: white; - top: -1px; - left: 13px; - transition: .10s left var(--appleEase); - transform: scale(.75); -} - -input[type=checkbox][switch]:disabled::before { - opacity: .5; -} - -input[type=checkbox][switch]:active::before { - left: 13px; -} - -input[type=checkbox][switch]:checked:active::before { - left: -1px; -} - -/* End Switch Checkbox */ - .madeforyou-body { margin-top: 15px; } diff --git a/src/renderer/views/app/app-content.ejs b/src/renderer/views/app/app-content.ejs index 5087544a..1ebbd22e 100644 --- a/src/renderer/views/app/app-content.ejs +++ b/src/renderer/views/app/app-content.ejs @@ -159,6 +159,12 @@ + + + +