├── .gitignore ├── .parcelrc ├── images ├── dark-card.png └── dark-card-editor.png ├── hacs.json ├── tsconfig.json ├── .github └── workflows │ └── validate.yaml ├── src ├── index.ts ├── models.ts ├── DepartureCard │ ├── DepartureCard.config.ts │ ├── DepartureCard.styles.ts │ └── index.ts ├── DepartureCardEditor.ts └── translations.ts ├── package.json ├── CHANGELOG.md ├── readme.md ├── dist ├── hasl4-departure-card-editor.js.map ├── hasl4-departure-card-editor.js └── hasl4-departure-card.js └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .parcel-cache 3 | 4 | dist/index.js* -------------------------------------------------------------------------------- /.parcelrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@parcel/config-default", 3 | "namers": [ "parcel-namer-rewrite" ] 4 | } 5 | -------------------------------------------------------------------------------- /images/dark-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasl-sensor/lovelace-hasl-departure-card/HEAD/images/dark-card.png -------------------------------------------------------------------------------- /images/dark-card-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasl-sensor/lovelace-hasl-departure-card/HEAD/images/dark-card-editor.png -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HASL Departure Card", 3 | "filename": "hasl4-departure-card.js", 4 | "render_readme": true, 5 | "country": "SE" , 6 | "homeassistant": "2024.1.0" 7 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2020", 4 | "isolatedModules": true, 5 | "target": "es2019", 6 | "moduleResolution": "node", 7 | "experimentalDecorators": true, 8 | "useDefineForClassFields": false, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/validate.yaml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | validate-hacs: 10 | runs-on: "ubuntu-latest" 11 | steps: 12 | - uses: "actions/checkout@v3" 13 | - name: HACS validation 14 | uses: "hacs/action@main" 15 | with: 16 | category: "plugin" 17 | 18 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { HASLDepartureCard } from "./DepartureCard" 2 | 3 | declare global { 4 | interface Window { 5 | customCards: Array 6 | } 7 | } 8 | customElements.define('hasl4-departure-card', HASLDepartureCard) 9 | 10 | window.customCards = window.customCards || [] 11 | window.customCards.push({ 12 | type: "hasl4-departure-card", 13 | name: "HASL4 Departure card", 14 | description: "Show departure times for SL Trafik", 15 | }) 16 | -------------------------------------------------------------------------------- /src/models.ts: -------------------------------------------------------------------------------- 1 | export enum TransportType { 2 | METRO = 'METRO', 3 | BUS = 'BUS', 4 | TRAM = 'TRAM', 5 | TRAIN = 'TRAIN', 6 | SHIP = 'SHIP', 7 | FERRY = 'FETTRY', 8 | TAXI = 'TAXI', 9 | } 10 | 11 | export type DepartureDeviation = { 12 | importance_level: number 13 | consequence: string 14 | message: string 15 | } 16 | 17 | export type Departure = { 18 | destination: string 19 | deviations?: DepartureDeviation[] 20 | direction: string 21 | direction_code: number 22 | state: string 23 | display: string 24 | stop_point: { 25 | name: string 26 | designation: string 27 | } 28 | line: { 29 | id: number 30 | designation: string 31 | transport_mode: TransportType 32 | group_of_lines: string 33 | } 34 | scheduled: string 35 | expected: string 36 | } 37 | 38 | export type DepartureAttributes = { 39 | friendly_name: string 40 | departures: Departure[] 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hasl-departure-card", 3 | "version": "3.4.1", 4 | "description": "HASL Departure Lovelace Card", 5 | "source": "src/index.ts", 6 | "module": "dist/hasl4-departure-card.js", 7 | "targets": { 8 | "module": { 9 | "includeNodeModules": true 10 | } 11 | }, 12 | "scripts": { 13 | "prebuild": "rm -rf dist/*", 14 | "build": "NODE_ENV=production parcel build", 15 | "watch": "parcel watch", 16 | "serve": "NODE_ENV=production parcel serve", 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "dependencies": { 20 | "@swc/helpers": "^0.5.6", 21 | "lit": "^3.1.2" 22 | }, 23 | "devDependencies": { 24 | "custom-card-helpers": "^1.9.0", 25 | "home-assistant-js-websocket": "^8.0.1", 26 | "parcel": "^2.12.0", 27 | "parcel-namer-rewrite": "^2.10.3-rc.2", 28 | "typescript": "^5.4.2" 29 | }, 30 | "volta": { 31 | "node": "18.19.1" 32 | }, 33 | "parcel-namer-rewrite": { 34 | "chain": "@parcel/namer-default", 35 | "hashing": "always", 36 | "disable": false, 37 | "rules": { 38 | "DepartureCardEditor.(.*)": "hasl4-departure-card-editor.$1" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/DepartureCard/DepartureCard.config.ts: -------------------------------------------------------------------------------- 1 | import { LovelaceCardConfig } from "custom-card-helpers" 2 | 3 | export type EntityInfoAction = { 4 | entityId: string 5 | } 6 | 7 | export type ServiceCallAction = { 8 | domain: string 9 | service: string 10 | data: object 11 | } 12 | 13 | export type ClickAction = 'info' | EntityInfoAction | ServiceCallAction 14 | 15 | export interface DepartureCardConfig extends LovelaceCardConfig { 16 | title?: string 17 | entity?: string 18 | entities: string[] 19 | 20 | show_entity_name?: boolean 21 | show_header?: boolean 22 | show_icon?: boolean 23 | hide_line_number?: boolean 24 | show_departures: boolean 25 | direction: number 26 | max_departures: number 27 | hide_departed: boolean 28 | show_departed_offeset?: number 29 | show_time_always?: boolean 30 | show_updated?: boolean 31 | 32 | language?: string 33 | 34 | click_action?: ClickAction 35 | } 36 | 37 | export const DEFAULT_CONFIG: Partial = { 38 | title: '', 39 | entities: [], 40 | 41 | show_entity_name: true, 42 | show_header: true, 43 | show_icon: true, 44 | show_departures: true, 45 | direction: 0, 46 | max_departures: 5, 47 | hide_departed: true, 48 | show_departed_offeset: 5, 49 | show_updated: true, 50 | 51 | tap_action: 'info', 52 | } 53 | -------------------------------------------------------------------------------- /src/DepartureCard/DepartureCard.styles.ts: -------------------------------------------------------------------------------- 1 | import {css} from 'lit' 2 | 3 | const lineColorsStyles = css` 4 | .line-icon { 5 | border-radius: 3px; 6 | padding: 3px 3px 0 3px; 7 | color: #fff; 8 | min-width: 22px; 9 | height: 22px; 10 | font-weight: 500; 11 | display: inline-block; 12 | text-align: center; 13 | text-shadow: 1px 1px 2px var(--outline-color); 14 | } 15 | 16 | .bus { 17 | border: 1px solid var(--outline-color); 18 | color: var(--primary-text-color); 19 | } 20 | 21 | .red { 22 | background-color: #d71d24; 23 | } 24 | .blue { 25 | background-color: #0089ca; 26 | } 27 | .green { 28 | background-color: #179d4d; 29 | } 30 | 31 | .train { 32 | background-color: #ec619f; 33 | } 34 | 35 | .tram { 36 | background-color: #985141; 37 | } 38 | 39 | .tram_7 { 40 | background-color: #878a83; 41 | } 42 | 43 | .tram_12 { 44 | background-color: #778da7; 45 | } 46 | 47 | .tram_21 { 48 | background-color: #b76020; 49 | } 50 | 51 | .tram_22 { 52 | background-color: #d77d00; 53 | } 54 | ` 55 | 56 | const departureEntityStyles = css` 57 | .card-header .name { 58 | white-space: nowrap; 59 | overflow: hidden; 60 | text-overflow: ellipsis; 61 | } 62 | 63 | .departures > :first-child { 64 | margin-top: 0; 65 | } 66 | 67 | .departure.departed { 68 | color: var(--secondary-text-color); 69 | } 70 | 71 | .departure.departed > .main { 72 | text-decoration: line-through; 73 | } 74 | 75 | .row { 76 | margin-top: 8px; 77 | 78 | display: flex; 79 | justify-content: space-between; 80 | } 81 | 82 | .col { 83 | display: flex; 84 | flex-direction: column; 85 | justify-content: center; 86 | position: relative; 87 | } 88 | 89 | .col.icon { 90 | flex-basis: 40px; 91 | } 92 | 93 | .row.name { 94 | height: 40px; 95 | padding-left: 8px; 96 | font-weight: 400; 97 | font-size: large; 98 | align-items: center; 99 | justify-content: center; 100 | } 101 | .row.header { 102 | height: 40px; 103 | font-size: medium; 104 | font-weight: 400; 105 | font-family: var(--paper-font-headline_-_font-family); 106 | letter-spacing: var(--paper-font-headline_-_letter-spacing); 107 | line-height: var(--paper-font-headline_-_line-height); 108 | text-rendering: var(--paper-font-common-expensive-kerning_-_text-rendering); 109 | opacity: var(--dark-primary-opacity); 110 | } 111 | 112 | .main { 113 | flex: 2; 114 | } 115 | 116 | .transport-icon { 117 | width: 40px; 118 | height: 40px; 119 | display: inline-flex; 120 | justify-content: center; 121 | align-items: center; 122 | } 123 | 124 | .warning { 125 | color: var(--warning-color); 126 | position: absolute; 127 | bottom: 0; 128 | right: 0; 129 | } 130 | 131 | .warning-message { 132 | color: var(--warning-color); 133 | font-size: smaller; 134 | text-decoration: unset; 135 | } 136 | 137 | .mr1 { 138 | margin-right: 8px; 139 | } 140 | 141 | .updated { 142 | padding-left: 16px; 143 | padding-top: 8px; 144 | font-size: smaller; 145 | } 146 | 147 | .center { text-align: center; } 148 | .left { text-align: left; } 149 | .right { text-align: right; } 150 | 151 | ha-icon { 152 | transition: color 0.3s ease-in-out, filter 0.3s ease-in-out; 153 | width: 24px; 154 | height: 24px; 155 | color: var(--paper-item-icon-color); 156 | } 157 | ` 158 | 159 | export default [departureEntityStyles, lineColorsStyles] 160 | -------------------------------------------------------------------------------- /src/DepartureCardEditor.ts: -------------------------------------------------------------------------------- 1 | import type { HomeAssistant, LovelaceCardEditor } from "custom-card-helpers" 2 | import { LitElement, css, html } from "lit" 3 | import { customElement, property, state } from "lit/decorators" 4 | 5 | import { DepartureCardConfig } from './DepartureCard/DepartureCard.config' 6 | import { getLanguage, translateTo, languages } from "./translations" 7 | 8 | @customElement('hasl4-departure-card-editor') 9 | export class HASLDepartureCardEditor extends LitElement implements LovelaceCardEditor { 10 | @property({ attribute: false }) 11 | public hass?: HomeAssistant; 12 | 13 | @state() 14 | private _config?: DepartureCardConfig 15 | 16 | @state() 17 | private _schema?: any 18 | 19 | public setConfig(config: DepartureCardConfig): void { 20 | this._config = config 21 | this._schema = this.getSchema(translateTo(getLanguage())) 22 | 23 | // Migrate to multiple entities 24 | if (config.entity && !config.entities?.length) { 25 | const { entity, ...rest } = config 26 | this._dispatchConfigChangedEvent({ ...rest, entities: [config.entity] }) 27 | } 28 | } 29 | 30 | private getSchema = (_ : (key :string) => string) => { 31 | const haveMultipleEntities = this._config?.entities?.length > 1 32 | 33 | return [ 34 | { name: "title", selector: { text: {} } }, 35 | // { name: "language", selector: { select: { mode: 'dropdown', options: languages } } }, 36 | { 37 | name: "", 38 | type: "expandable", 39 | icon: "mdi:database", 40 | title: _("editor_entities"), 41 | schema: [ 42 | { name: "show_entity_name", type: "boolean", disabled: haveMultipleEntities }, 43 | { name: "entities", selector: { entity: { multiple: true, filter: [ 44 | { domain: 'sensor', integration: 'hasl3' }, 45 | { domain: 'sensor', integration: 'london_tfl' } 46 | ]}} }, 47 | ], 48 | }, 49 | { 50 | name: "", 51 | type: "expandable", 52 | icon: "mdi:clock", 53 | title: _("editor_departures"), 54 | schema: [ 55 | { name: "show_departures", type: "boolean" }, 56 | { name: "show_header", type: "boolean" }, 57 | { 58 | name: "direction", 59 | selector: { 60 | select: { 61 | options: [ 62 | { value: 0, label: _(`editor_direction_all`) }, 63 | { value: 1, label: _(`editor_direction_left`) }, 64 | { value: 2, label: _(`editor_direction_right`) }, 65 | ] 66 | } 67 | } 68 | }, 69 | { name: "show_icon", type: "boolean" }, 70 | { name: "hide_line_number", type: "boolean" }, 71 | { name: "show_time_always", type: "boolean" }, 72 | { name: "max_departures", selector: { number: { mode: "box", min: 1, max: 10 } }, }, 73 | { 74 | name: "", 75 | type: "grid", 76 | schema: [ 77 | { name: "hide_departed", type: "boolean" }, 78 | { name: "show_departed_offeset", selector: { number: { mode: "box", min: 0, max: 30 } }, }, 79 | ], 80 | }, 81 | ], 82 | }, 83 | { name: "show_updated", type: "boolean", disabled: haveMultipleEntities }, 84 | ]}; 85 | 86 | private _valueChanged(ev: CustomEvent): void { 87 | ev.stopPropagation(); 88 | this._dispatchConfigChangedEvent(ev.detail.value) 89 | } 90 | 91 | private _dispatchConfigChangedEvent(newConfig) { 92 | const event = new Event("config-changed", { bubbles: true, composed: true}); 93 | event.detail = { config: newConfig }; 94 | this.dispatchEvent(event); 95 | } 96 | 97 | render() { 98 | const lang = getLanguage() 99 | const _ = translateTo(lang) 100 | 101 | return html` 102 | _(`editor_${item.name}`) || item.label || item.name } 107 | @value-changed=${this._valueChanged}> 108 | 109 | ` 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # hasl-cards 2 | 3 | Changelog for HASL departure card 4 | 5 | The format is based on [Keep a Changelog][keep-a-changelog] 6 | 7 | 8 | ## [Unreleased] 9 | ## What's Changed 10 | 11 | ## [3.4.1] (2025-02-04) 12 | ## What's Changed 13 | * Added slight border to bus lane badge to make it more visible 14 | 15 | ## [3.4.0] (2025-02-03) 16 | ## What's Changed 17 | * Card now supports setting `hide_line_number` in the card configuration. This allows you to hide the line number in the card. 18 | * Added back `show_icon` translation string 19 | 20 | ## [3.3.0] (2025-01-24) 21 | ## What's Changed 22 | * Card now supports setting multiple `entities` in the card configuration. This allows you to show multiple departure boards in one card. 23 | * Previous configurations are still supported, but will be migrated automatically as soon as you open the card editor and save the configuration. 24 | * Editor bundle name changed to `hasl-departure-card-editor.js` to help with manual installation. 25 | * The card supports selecting LondonTfl sensors (thanks @morosanmihail !) 26 | 27 | ## [3.2.0] (2025-01-17) 28 | ## What's Changed 29 | * Card is no longer compatible with HASL prior to version 3.2.0 30 | * Complete rewrite on TypeScript using Lit 31 | * Use Parcel to build the package 32 | * Added visual editor for settings 33 | 34 | ## [2.6.2] (2023-06-12) 35 | ## What's Changed 36 | * Fixed NaN and departure time calculations in https://github.com/hasl-sensor/lovelace-hasl-departure-card/issues/30 and https://github.com/hasl-sensor/lovelace-hasl-departure-card/issues/23 37 | 38 | 39 | ## [2.6.1] (2023-02-17) 40 | ## What's Changed 41 | * Reference captured `config` instead of reading from `this` by @barbatron in https://github.com/hasl-sensor/lovelace-hasl-departure-card/pull/22 42 | * make compact true by default by @morlic in https://github.com/hasl-sensor/lovelace-hasl-departure-card/pull/26 43 | * Fixed bus name replacements by @jockesyk in https://github.com/hasl-sensor/lovelace-hasl-departure-card/pull/27 44 | * Fix for old browsers without support for toLocaleString by @jockesyk in https://github.com/hasl-sensor/lovelace-hasl-departure-card/pull/28 45 | 46 | ## New Contributors 47 | * @barbatron made their first contribution in https://github.com/hasl-sensor/lovelace-hasl-departure-card/pull/22 48 | * @morlic made their first contribution in https://github.com/hasl-sensor/lovelace-hasl-departure-card/pull/26 49 | * @jockesyk made their first contribution in https://github.com/hasl-sensor/lovelace-hasl-departure-card/pull/27 50 | 51 | ## [2.6.0] (2022-09-10) 52 | 53 | ### Added 54 | - Implemented [hasl-sensor/integration #42](https://github.com/hasl-sensor/integration/issues/42) Bus line directions and name of bus direction 55 | - Implemented [#12](https://github.com/hasl-sensor/lovelace-hasl-departure-card/issues/12) Only show "Last updated" info if it was updated longer than x minutes ago 56 | 57 | ### Fixed 58 | - Fix [#15](https://github.com/hasl-sensor/lovelace-hasl-departure-card/issues/15) UpdatedDate and new HASL v3 doesn't work 59 | 60 | ### Changed 61 | - Requires at least HA version 2021.12.0, which is same as hasl3 62 | 63 | ## [2.5.0] (2020-03-04) 64 | 65 | - Card config fix #5 66 | - Added french translation [@matfouc](https://github.com/matfouc) 67 | 68 | ## [2.4.0] (2019-11-20) 69 | 70 | - Added tap_action #1 71 | - Added offset #2 72 | - Fixed show always time #3 73 | - Cleaned up a litle bit 74 | 75 | ## [2.3.0] (2019-11-09) 76 | 77 | - Migrated to HACS 78 | - Separated the cards into 2 repos and removed the traffic card 79 | 80 | ## [2.2.0] (2019-11-05) 81 | 82 | - Added show card name config variable in both cards 83 | - Bugfixes 84 | 85 | ## [2.1.0] (2019-05-15) 86 | 87 | - Migrated to separate project 88 | 89 | [keep-a-changelog]: http://keepachangelog.com/en/1.0.0/ 90 | [Unreleased]: https://github.com/hasl-platform/lovelace-hasl-departure-card/compare/master...dev 91 | [2.6.1]: https://github.com/hasl-platform/lovelace-hasl-departure-card/releases/tag/v2.6.1 92 | [2.6.0]: https://github.com/hasl-platform/lovelace-hasl-departure-card/releases/tag/v2.6.0 93 | [2.5.0]: https://github.com/hasl-platform/lovelace-hasl-departure-card/releases/tag/v2.5.0 94 | [2.4.0]: https://github.com/hasl-platform/lovelace-hasl-departure-card/releases/tag/v2.4.0 95 | [2.3.0]: https://github.com/hasl-platform/lovelace-hasl-departure-card/releases/tag/v2.3.0 96 | [2.4.0]: https://github.com/hasl-platform/lovelace-hasl-departure-card/releases/tag/v2.4.0 97 | [2.5.0]: https://github.com/hasl-platform/lovelace-hasl-departure-card/releases/tag/v2.5.0 98 | -------------------------------------------------------------------------------- /src/translations.ts: -------------------------------------------------------------------------------- 1 | const defaultLang = `en-US` 2 | const defaultTranslation = { 3 | entity_missing: 'Entity data missing', 4 | line: 'Line', 5 | destination: 'Destination', 6 | departure: 'Departure', 7 | min: 'min', 8 | last_updated: 'Last updated', 9 | now: 'Now', 10 | // configuration translations 11 | editor_show_name: 'Show card name', 12 | editor_entities: 'Entities', 13 | editor_departures: 'Departures', 14 | editor_title: 'Card name', 15 | editor_show_entity_name: 'Show entity name', 16 | editor_show_departures: 'Show departures', 17 | editor_show_header: 'Show departure header', 18 | editor_show_icon: 'Show transport icon', 19 | editor_show_transport_icon: 'Show transport icon', 20 | editor_max_departures: 'Maximum departures to show', 21 | editor_hide_departed: 'Hide already departed', 22 | editor_show_departed_offeset: '... but show departed number of minutes ago', 23 | editor_show_time_always: 'Always show departure time in HH:MM form', 24 | editor_hide_line_number: 'Hide line number', 25 | editor_show_updated: `Show 'Last Updated'`, 26 | editor_direction: `Direction filter`, 27 | editor_direction_all: `All`, 28 | editor_direction_left: `Left`, 29 | editor_direction_right: `Right`, 30 | language: 'Language', 31 | } 32 | type Translation = typeof defaultTranslation 33 | export type TranslationKey = keyof Translation 34 | 35 | export const translations: {[lang: string]: Translation} = { 36 | [defaultLang]: defaultTranslation, 37 | 'sv-SE': { 38 | entity_missing: 'Ingen data hittades', 39 | line: 'Linje', 40 | destination: 'Till', 41 | departure: 'Avresa', 42 | min: 'min', 43 | last_updated: 'Senast uppdaterad', 44 | now: 'Nu', 45 | editor_show_name: 'Visa kortnamn', 46 | editor_entities: 'Enheter', 47 | editor_departures: 'Avgångar', 48 | editor_title: 'Kortnamn', 49 | editor_show_entity_name: 'Visa enhetsnamn', 50 | editor_show_departures: 'Visa avgångar', 51 | editor_show_header: 'Visa avgångshuvud', 52 | editor_show_icon: 'Visa transportikon', 53 | editor_show_transport_icon: 'Visa transportikon', 54 | editor_max_departures: 'Max antal avgångar', 55 | editor_hide_departed: 'Dölj redan avgångna', 56 | editor_show_departed_offeset: '... men visa avgångna för antal minuter sedan', 57 | editor_show_time_always: 'Visa alltid avgångstid i HH:MM-form', 58 | editor_hide_line_number: 'Dölj linjenummer', 59 | editor_show_updated: `Visa 'Senast uppdaterad'`, 60 | editor_direction: `Riktning filter`, 61 | editor_direction_all: `Alla`, 62 | editor_direction_left: `Vänster`, 63 | editor_direction_right: `Höger`, 64 | language: 'Språk', 65 | }, 66 | 'fr-FR': { 67 | entity_missing: 'Aucune info trouvée', 68 | line: 'Ligne', 69 | destination: 'Terminus', 70 | departure: 'Départ', 71 | min: 'min', 72 | last_updated: 'Mis à jour', 73 | now: 'Maintenant', 74 | editor_show_name: 'Afficher le nom de la carte', 75 | editor_entities: 'Entités', 76 | editor_departures: 'Départs', 77 | editor_title: 'Nom de la carte', 78 | editor_show_entity_name: 'Afficher le nom de l\'entité', 79 | editor_show_departures: 'Afficher les départs', 80 | editor_show_header: 'Afficher l\'entête des départs', 81 | editor_show_icon: 'Afficher l\'icône de transport', 82 | editor_show_transport_icon: 'Afficher l\'icône de transport', 83 | editor_max_departures: 'Nombre maximum de départs', 84 | editor_hide_departed: 'Masquer les départs passés', 85 | editor_show_departed_offeset: '... mais montrer les départs depuis le nombre de minutes', 86 | editor_show_time_always: 'Toujours afficher l\'heure de départ en HH:MM', 87 | editor_hide_line_number: 'Masquer le numéro de ligne', 88 | editor_show_updated: `Afficher 'Mis à jour'`, 89 | editor_direction: `Filtre de direction`, 90 | editor_direction_all: `Tous`, 91 | editor_direction_left: `Gauche`, 92 | editor_direction_right: `Droite`, 93 | language: 'Langue', 94 | } 95 | } 96 | 97 | export const languages = Object.keys(translations) 98 | export const t = (key: TranslationKey, lang?: string): string => translations[lang]?.[key] ?? defaultTranslation[key] 99 | export const getLanguage = (configLang?: string): string => configLang ?? navigator.language ?? defaultLang 100 | export const translateTo = (lang?: string) => (key: TranslationKey) => t(key, lang) 101 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # HASL Departure Lovelace Card 2 | 3 | [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs) 4 | [![ha_version](https://img.shields.io/badge/homeassistant-2024.1.0%2B-yellow.svg)](https://www.home-assistant.io) 5 | [![version](https://img.shields.io/badge/version-3.3.0-green.svg)](#) 6 | [![maintained](https://img.shields.io/maintenance/yes/2025.svg)](#) 7 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 8 | 9 | Present departure times from HASL 3.2.0+ Departure sensors. 10 | 11 | Now with deviations! 12 | 13 | ![card](/images/dark-card.png) 14 | 15 | ## Manual Installation 16 | 17 | Copy [`hasl4-departure-card.js`](./dist/hasl4-departure-card.js) and [`hasl4-departure-card-editor.js`](./dist/hasl4-departure-card-editor.js) files to `/www/hasl4-departure-card.js` 18 | 19 | Where `` is your Home Assistant configuration directory. 20 | Then use the following in your `ui-lovelace.yaml` file: 21 | 22 | ```yaml 23 | resources: 24 | - url: /local/hasl4-departure-card.js 25 | type: js 26 | ``` 27 | 28 | ## Basic setup 29 | 30 | In your lovelace dashboard, Edit Dashboard -> Add Card -> Search for 'HASL Departure Card' 31 | 32 | ## Configuration 33 | 34 | Card fully supports configuration through the UI 35 | 36 | ![card editor](/images/dark-card-editor.png) 37 | 38 | 39 | ### Options 40 | | Name | Type | Required? | Description | 41 | | -------------------- | ---------------- | --------- | ----------------------------------------------------------------------------------------------------------- | 42 | | entities | entity[] | required | The array of entity_id's of the 'Departure' sensors. | 43 | | title | string | optional | If set, this will be rendered as the card name. | 44 | | show_entity_name | bool | optional | Render a friendly name of a selected `entity`. Disabled when `entities` are set to more than 1 entity | 45 | | show_header | bool | optional | Render headers in each section such as "Line", "Destination" and "Departure". | 46 | | show_icon | bool | optional | Render transport icon for each line. | 47 | | show_departures | bool | optional | Render departures section. | 48 | | max_departures | number | optional | Max departures to show, default to all. | 49 | | direction | number | optional | Render departures only in said direction | 50 | | hide_departed | bool | optional | If set, will hide already departured vehicles. | 51 | | show_departed_offset | bool | optional | If set, will show some departed vehicles, which departed less than the offset minutes ago. | 52 | | show_time_always | bool | optional | Always present time in HH:MM form. If not set, time will be presented as "in X minutes" or "X minutes ago". | 53 | | show_updated | bool | optional | Render the 'last updated' text. Disabled when `entities` are set to more than 1 entity | 54 | | language | string | optional | The texts will be rendered in this language. Can be one of `sv-SE`, `en-EN`, `fr-FR`. | 55 | | click_action | string or object | optional | Action when tapping the card. See section `click_action` below. | 56 | | hide_line_number | bool | optional | Render the line number. | 57 | 58 | Setting `entities` to more than one entity will render departures from all of the sensors as one list sorted by `expected` time but will disable `show_entity_name` and `show_updated` options. 59 | 60 | #### `click_action` 61 | 62 | The `click_action` option can be used to specify what happens when the card is tapped. It can be one of the following: 63 | 64 | - Display information about the entity that was clicked: 65 | ```yaml 66 | click_action: info 67 | ``` 68 | 69 | - Display information about the specific entity 70 | ```yaml 71 | click_action: 72 | entityId: sun.sun 73 | ``` 74 | 75 | - Call a service: 76 | ```yaml 77 | click_action: 78 | domain: light 79 | service: turn_on 80 | data: 81 | entity_id: light.living_room 82 | ``` 83 | 84 | ## Development 85 | 86 | You'll need Node.js and npm installed on your computer. 87 | ([Volta](https://volta.sh/) is recommended for managing Node.js versions) 88 | 89 | ```sh 90 | npm install 91 | npm run build 92 | ``` 93 | 94 | ## Credits 95 | - Huge thanks to [@dimmanramone](https://github.com/dimmanramone) for pimping the card! 96 | - [@DSorlov](https://github.com/DSorlov) for his work on the original card 97 | 98 | ### Support the developers 99 | - [@NecroKote](https://buymeacoffee.com/mkukhta) - maintainer 100 | -------------------------------------------------------------------------------- /dist/hasl4-departure-card-editor.js.map: -------------------------------------------------------------------------------- 1 | {"mappings":";;;;;;;;;;;;;;;;;;;AAQO,MAAM,kDAAgC,CAAA,GAAA,iBAAS;IAU7C,UAAU,MAA2B,EAAQ;QAClD,IAAI,CAAC,OAAO,GAAG;QACf,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAA,GAAA,kBAAU,EAAE,CAAA,GAAA,kBAAU;QAEpD,+BAA+B;QAC/B,IAAI,OAAO,MAAM,IAAI,CAAC,OAAO,QAAQ,EAAE,QAAQ;YAC7C,MAAM,UAAE,MAAM,EAAE,GAAG,MAAM,GAAG;YAC5B,IAAI,CAAC,2BAA2B,CAAC;gBAAE,GAAG,IAAI;gBAAE,UAAU;oBAAC,OAAO,MAAM;iBAAC;YAAC;QACxE;IACF;IA0DQ,cAAc,EAAe,EAAQ;QAC3C,GAAG,eAAe;QAClB,IAAI,CAAC,2BAA2B,CAAC,GAAG,MAAM,CAAC,KAAK;IAClD;IAEQ,4BAA4B,SAAS,EAAE;QAC7C,MAAM,QAAQ,IAAI,MAAM,kBAAkB;YAAE,SAAS;YAAM,UAAU;QAAI;QACzE,MAAM,MAAM,GAAG;YAAE,QAAQ;QAAU;QACnC,IAAI,CAAC,aAAa,CAAC;IACrB;IAEA,SAAS;QACP,MAAM,OAAO,CAAA,GAAA,kBAAU;QACvB,MAAM,IAAI,CAAA,GAAA,kBAAU,EAAE;QAEtB,OAAO,CAAA,GAAA,WAAG,CAAC,CAAC;;cAEF,EAAE,IAAI,CAAC,IAAI,CAAC;cACZ,EAAE,IAAI,CAAC,OAAO,CAAC;gBACb,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;sBACf,EAAE,CAAC,MAAM,OAAS,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,CAAE;uBACtE,EAAE,IAAI,CAAC,aAAa,CAAC;;IAExC,CAAC;IACH;;;aAhFQ,YAAY,CAAC;YACnB,MAAM,uBAAuB,IAAI,CAAC,OAAO,EAAE,UAAU,SAAS;YAE9D,OAAO;gBACP;oBAAE,MAAM;oBAAS,UAAU;wBAAE,MAAM,CAAC;oBAAE;gBAAE;gBACxC,wFAAwF;gBACxF;oBACE,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,OAAO,EAAE;oBACT,QAAQ;wBACN;4BAAE,MAAM;4BAAoB,MAAM;4BAAW,UAAU;wBAAqB;wBAC5E;4BAAE,MAAM;4BAAY,UAAU;gCAAE,QAAQ;oCAAE,UAAU;oCAAM,QAAQ;wCAChE;4CAAE,QAAQ;4CAAU,aAAa;wCAAQ;wCACzC;4CAAE,QAAQ;4CAAU,aAAa;wCAAa;qCAC/C;gCAAA;4BAAC;wBAAE;qBACL;gBACH;gBACA;oBACE,MAAM;oBACN,MAAM;oBACN,MAAM;oBACN,OAAO,EAAE;oBACT,QAAQ;wBACN;4BAAE,MAAM;4BAAmB,MAAM;wBAAU;wBAC3C;4BAAE,MAAM;4BAAe,MAAM;wBAAU;wBACvC;4BACE,MAAM;4BACN,UAAU;gCACR,QAAQ;oCACN,SAAS;wCACP;4CAAE,OAAO;4CAAG,OAAO,EAAE,CAAC,oBAAoB,CAAC;wCAAE;wCAC7C;4CAAE,OAAO;4CAAG,OAAO,EAAE,CAAC,qBAAqB,CAAC;wCAAE;wCAC9C;4CAAE,OAAO;4CAAG,OAAO,EAAE,CAAC,sBAAsB,CAAC;wCAAE;qCAChD;gCACH;4BACF;wBACF;wBACA;4BAAE,MAAM;4BAAa,MAAM;wBAAU;wBACrC;4BAAE,MAAM;4BAAoB,MAAM;wBAAU;wBAC5C;4BAAE,MAAM;4BAAoB,MAAM;wBAAU;wBAC5C;4BAAE,MAAM;4BAAkB,UAAU;gCAAE,QAAQ;oCAAE,MAAM;oCAAO,KAAK;oCAAG,KAAK;gCAAG;4BAAE;wBAAG;wBAClF;4BACE,MAAM;4BACN,MAAM;4BACN,QAAQ;gCACN;oCAAE,MAAM;oCAAiB,MAAM;gCAAU;gCACzC;oCAAE,MAAM;oCAAyB,UAAU;wCAAE,QAAQ;4CAAE,MAAM;4CAAO,KAAK;4CAAG,KAAK;wCAAG;oCAAE;gCAAG;6BAC1F;wBACH;qBACD;gBACH;gBACA;oBAAE,MAAM;oBAAgB,MAAM;oBAAW,UAAU;gBAAqB;aACzE;QAAA;;AA2BH;;IArGG,CAAA,GAAA,eAAO,EAAE;QAAE,WAAW;IAAM;GADlB;;IAIV,CAAA,GAAA,YAAI;GAJM;;IAOV,CAAA,GAAA,YAAI;GAPM;AAAA;IADZ,CAAA,GAAA,oBAAY,EAAE;GACF","sources":["src/DepartureCardEditor.ts"],"sourcesContent":["import type { HomeAssistant, LovelaceCardEditor } from \"custom-card-helpers\"\nimport { LitElement, css, html } from \"lit\"\nimport { customElement, property, state } from \"lit/decorators\"\n\nimport { DepartureCardConfig } from './DepartureCard/DepartureCard.config'\nimport { getLanguage, translateTo, languages } from \"./translations\"\n\n@customElement('hasl4-departure-card-editor')\nexport class HASLDepartureCardEditor extends LitElement implements LovelaceCardEditor {\n @property({ attribute: false })\n public hass?: HomeAssistant;\n\n @state()\n private _config?: DepartureCardConfig\n\n @state()\n private _schema?: any\n\n public setConfig(config: DepartureCardConfig): void {\n this._config = config\n this._schema = this.getSchema(translateTo(getLanguage()))\n\n // Migrate to multiple entities\n if (config.entity && !config.entities?.length) {\n const { entity, ...rest } = config\n this._dispatchConfigChangedEvent({ ...rest, entities: [config.entity] })\n }\n }\n\n private getSchema = (_ : (key :string) => string) => {\n const haveMultipleEntities = this._config?.entities?.length > 1\n\n return [\n { name: \"title\", selector: { text: {} } },\n // { name: \"language\", selector: { select: { mode: 'dropdown', options: languages } } },\n {\n name: \"\",\n type: \"expandable\",\n icon: \"mdi:database\",\n title: _(\"editor_entities\"),\n schema: [\n { name: \"show_entity_name\", type: \"boolean\", disabled: haveMultipleEntities },\n { name: \"entities\", selector: { entity: { multiple: true, filter: [\n { domain: 'sensor', integration: 'hasl3' },\n { domain: 'sensor', integration: 'london_tfl' }\n ]}} },\n ],\n },\n {\n name: \"\",\n type: \"expandable\",\n icon: \"mdi:clock\",\n title: _(\"editor_departures\"),\n schema: [\n { name: \"show_departures\", type: \"boolean\" },\n { name: \"show_header\", type: \"boolean\" },\n {\n name: \"direction\",\n selector: {\n select: {\n options: [\n { value: 0, label: _(`editor_direction_all`) },\n { value: 1, label: _(`editor_direction_left`) },\n { value: 2, label: _(`editor_direction_right`) },\n ]\n }\n }\n },\n { name: \"show_icon\", type: \"boolean\" },\n { name: \"hide_line_number\", type: \"boolean\" },\n { name: \"show_time_always\", type: \"boolean\" },\n { name: \"max_departures\", selector: { number: { mode: \"box\", min: 1, max: 10 } }, },\n {\n name: \"\",\n type: \"grid\",\n schema: [\n { name: \"hide_departed\", type: \"boolean\" },\n { name: \"show_departed_offeset\", selector: { number: { mode: \"box\", min: 0, max: 30 } }, },\n ],\n },\n ],\n },\n { name: \"show_updated\", type: \"boolean\", disabled: haveMultipleEntities },\n ]};\n\n private _valueChanged(ev: CustomEvent): void {\n ev.stopPropagation();\n this._dispatchConfigChangedEvent(ev.detail.value)\n }\n\n private _dispatchConfigChangedEvent(newConfig) {\n const event = new Event(\"config-changed\", { bubbles: true, composed: true});\n event.detail = { config: newConfig };\n this.dispatchEvent(event);\n }\n\n render() {\n const lang = getLanguage()\n const _ = translateTo(lang)\n\n return html`\n _(`editor_${item.name}`) || item.label || item.name }\n @value-changed=${this._valueChanged}>\n \n `\n }\n}\n"],"names":[],"version":3,"file":"hasl4-departure-card-editor.js.map"} -------------------------------------------------------------------------------- /dist/hasl4-departure-card-editor.js: -------------------------------------------------------------------------------- 1 | 2 | function $parcel$export(e, n, v, s) { 3 | Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); 4 | } 5 | 6 | var $parcel$global = globalThis; 7 | var parcelRequire = $parcel$global["parcelRequire55a1"]; 8 | var parcelRegister = parcelRequire.register; 9 | parcelRegister("jlj1D", function(module, exports) { 10 | 11 | $parcel$export(module.exports, "HASLDepartureCardEditor", () => $91eb62869907e39c$export$8d2c87e174389bfd); 12 | 13 | var $39J5i = parcelRequire("39J5i"); 14 | 15 | var $j0ZcV = parcelRequire("j0ZcV"); 16 | 17 | var $1ZxoT = parcelRequire("1ZxoT"); 18 | 19 | var $gjUL4 = parcelRequire("gjUL4"); 20 | class $91eb62869907e39c$export$8d2c87e174389bfd extends (0, $j0ZcV.LitElement) { 21 | setConfig(config) { 22 | this._config = config; 23 | this._schema = this.getSchema((0, $gjUL4.translateTo)((0, $gjUL4.getLanguage)())); 24 | // Migrate to multiple entities 25 | if (config.entity && !config.entities?.length) { 26 | const { entity: entity, ...rest } = config; 27 | this._dispatchConfigChangedEvent({ 28 | ...rest, 29 | entities: [ 30 | config.entity 31 | ] 32 | }); 33 | } 34 | } 35 | _valueChanged(ev) { 36 | ev.stopPropagation(); 37 | this._dispatchConfigChangedEvent(ev.detail.value); 38 | } 39 | _dispatchConfigChangedEvent(newConfig) { 40 | const event = new Event("config-changed", { 41 | bubbles: true, 42 | composed: true 43 | }); 44 | event.detail = { 45 | config: newConfig 46 | }; 47 | this.dispatchEvent(event); 48 | } 49 | render() { 50 | const lang = (0, $gjUL4.getLanguage)(); 51 | const _ = (0, $gjUL4.translateTo)(lang); 52 | return (0, $j0ZcV.html)` 53 | _(`editor_${item.name}`) || item.label || item.name} 58 | @value-changed=${this._valueChanged}> 59 | 60 | `; 61 | } 62 | constructor(...args){ 63 | super(...args); 64 | this.getSchema = (_)=>{ 65 | const haveMultipleEntities = this._config?.entities?.length > 1; 66 | return [ 67 | { 68 | name: "title", 69 | selector: { 70 | text: {} 71 | } 72 | }, 73 | // { name: "language", selector: { select: { mode: 'dropdown', options: languages } } }, 74 | { 75 | name: "", 76 | type: "expandable", 77 | icon: "mdi:database", 78 | title: _("editor_entities"), 79 | schema: [ 80 | { 81 | name: "show_entity_name", 82 | type: "boolean", 83 | disabled: haveMultipleEntities 84 | }, 85 | { 86 | name: "entities", 87 | selector: { 88 | entity: { 89 | multiple: true, 90 | filter: [ 91 | { 92 | domain: "sensor", 93 | integration: "hasl3" 94 | }, 95 | { 96 | domain: "sensor", 97 | integration: "london_tfl" 98 | } 99 | ] 100 | } 101 | } 102 | } 103 | ] 104 | }, 105 | { 106 | name: "", 107 | type: "expandable", 108 | icon: "mdi:clock", 109 | title: _("editor_departures"), 110 | schema: [ 111 | { 112 | name: "show_departures", 113 | type: "boolean" 114 | }, 115 | { 116 | name: "show_header", 117 | type: "boolean" 118 | }, 119 | { 120 | name: "direction", 121 | selector: { 122 | select: { 123 | options: [ 124 | { 125 | value: 0, 126 | label: _(`editor_direction_all`) 127 | }, 128 | { 129 | value: 1, 130 | label: _(`editor_direction_left`) 131 | }, 132 | { 133 | value: 2, 134 | label: _(`editor_direction_right`) 135 | } 136 | ] 137 | } 138 | } 139 | }, 140 | { 141 | name: "show_icon", 142 | type: "boolean" 143 | }, 144 | { 145 | name: "hide_line_number", 146 | type: "boolean" 147 | }, 148 | { 149 | name: "show_time_always", 150 | type: "boolean" 151 | }, 152 | { 153 | name: "max_departures", 154 | selector: { 155 | number: { 156 | mode: "box", 157 | min: 1, 158 | max: 10 159 | } 160 | } 161 | }, 162 | { 163 | name: "", 164 | type: "grid", 165 | schema: [ 166 | { 167 | name: "hide_departed", 168 | type: "boolean" 169 | }, 170 | { 171 | name: "show_departed_offeset", 172 | selector: { 173 | number: { 174 | mode: "box", 175 | min: 0, 176 | max: 30 177 | } 178 | } 179 | } 180 | ] 181 | } 182 | ] 183 | }, 184 | { 185 | name: "show_updated", 186 | type: "boolean", 187 | disabled: haveMultipleEntities 188 | } 189 | ]; 190 | }; 191 | } 192 | } 193 | (0, $39J5i.__decorate)([ 194 | (0, $1ZxoT.property)({ 195 | attribute: false 196 | }) 197 | ], $91eb62869907e39c$export$8d2c87e174389bfd.prototype, "hass", void 0); 198 | (0, $39J5i.__decorate)([ 199 | (0, $1ZxoT.state)() 200 | ], $91eb62869907e39c$export$8d2c87e174389bfd.prototype, "_config", void 0); 201 | (0, $39J5i.__decorate)([ 202 | (0, $1ZxoT.state)() 203 | ], $91eb62869907e39c$export$8d2c87e174389bfd.prototype, "_schema", void 0); 204 | $91eb62869907e39c$export$8d2c87e174389bfd = (0, $39J5i.__decorate)([ 205 | (0, $1ZxoT.customElement)("hasl4-departure-card-editor") 206 | ], $91eb62869907e39c$export$8d2c87e174389bfd); 207 | 208 | }); 209 | 210 | 211 | //# sourceMappingURL=hasl4-departure-card-editor.js.map 212 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/DepartureCard/index.ts: -------------------------------------------------------------------------------- 1 | import { LitElement, html, nothing, css } from 'lit' 2 | import { property, state } from 'lit/decorators'; 3 | 4 | import type { HomeAssistant, LovelaceCard } from "custom-card-helpers"; 5 | import type { HassEntity } from "home-assistant-js-websocket"; 6 | 7 | import type { DepartureAttributes } from "../models" 8 | import { TransportType } from '../models' 9 | import { translateTo, getLanguage } from '../translations' 10 | import { DepartureCardConfig, DEFAULT_CONFIG, ClickAction, EntityInfoAction, ServiceCallAction } from './DepartureCard.config' 11 | import styles from './DepartureCard.styles' 12 | 13 | const diffMinutes = (from: Date, to: Date) => { 14 | const diffMinutes = Math.ceil((from.getTime() - to.getTime()) / 1000 / 60) 15 | return diffMinutes 16 | } 17 | 18 | export class HASLDepartureCard extends LitElement implements LovelaceCard { 19 | static styles = styles; 20 | 21 | @state() 22 | private config?: DepartureCardConfig 23 | 24 | @property({ attribute: false }) 25 | public hass?: HomeAssistant 26 | 27 | setConfig(config: DepartureCardConfig) { 28 | this.config = {...DEFAULT_CONFIG, ...config} 29 | } 30 | 31 | getCardSize() { 32 | const singleEntitityExtras = (this.isManyEntitiesSet() 33 | ? () => 0 34 | : () => { 35 | const [_, attrs] = this.getFirstEntity() 36 | if (!attrs) return 0 37 | 38 | return (this.config.show_entity_name && attrs.friendly_name) ? 1 : 0 39 | })() 40 | 41 | const deps = this.getDepartures(); 42 | 43 | const size = [ 44 | !!this.config.title ? 1 : 0, 45 | singleEntitityExtras, 46 | !!this.config.show_header ? 1 : 0, 47 | deps?.length || 0, 48 | ].reduce((sum, entity) => sum += entity ? entity : 0, 0); 49 | 50 | return Math.max(size, 1); 51 | }; 52 | 53 | getLayoutOptions() { 54 | return { 55 | grid_min_columns: 3, 56 | grid_min_rows: 2, 57 | }; 58 | } 59 | 60 | // configuration card is loaded in async manner 61 | static async getConfigElement () { 62 | return await import('../DepartureCardEditor').then(() => document.createElement("hasl4-departure-card-editor")) 63 | } 64 | 65 | static getStubConfig = () => ({...DEFAULT_CONFIG}) 66 | 67 | render() { 68 | if (!this.config || !this.hass) return nothing 69 | const lang = getLanguage(this.config?.language) 70 | const _ = translateTo(lang) 71 | 72 | const departures = 73 | this.config?.show_departures 74 | ? () => { 75 | const data = this.renderDepartures() 76 | return (data === nothing) 77 | ? html`${_(`entity_missing`)}` 78 | : data 79 | } 80 | : () => nothing 81 | 82 | const renderLastUpdated = 83 | this.isManyEntitiesSet() 84 | ? () => nothing 85 | : () => { 86 | const [data, __] = this.getFirstEntity() 87 | if (!data) return nothing; 88 | 89 | return (this.config?.show_updated && data.last_updated) 90 | ? html` 91 |
92 | ${_("last_updated")} 93 | ${new Date(data.last_updated).toLocaleTimeString(lang)} 94 |
` 95 | : nothing 96 | } 97 | 98 | return html` 99 | 100 | ${this.config?.title 101 | ? html`

${this.config.title}

` 102 | : nothing} 103 |
104 | ${departures()} 105 | ${renderLastUpdated()} 106 |
107 |
108 | ` 109 | } 110 | 111 | private isManyEntitiesSet = () => this.config?.entities?.length > 1 112 | 113 | private getFirstEntity(): [HassEntity, DepartureAttributes] | [undefined, undefined] { 114 | const data = this.hass?.states[this.config?.entities?.[0] || this.config?.entity]; 115 | const attrs = data?.attributes 116 | if (data && attrs && isDepartureAttrs(attrs)) { 117 | return [data, attrs] 118 | } 119 | return [undefined, undefined] 120 | } 121 | 122 | private getDeparturesFor(attrs: DepartureAttributes | undefined) { 123 | if (!attrs) return [] 124 | 125 | const now = new Date() 126 | 127 | return (attrs.departures 128 | // filter direction 129 | ?.filter((d) => { 130 | if (this.config?.direction === 0) return true 131 | return d.direction_code === this.config?.direction 132 | }) 133 | // filter by departure time 134 | .filter((d) => { 135 | if (!this.config?.hide_departed) return true 136 | 137 | const diff = diffMinutes(new Date(d.expected), now) 138 | return diff + this.config?.show_departed_offeset >= 0 139 | }) || []).slice(0, this.config?.max_departures) 140 | } 141 | 142 | private getDeparturesCombined(entities: string[]) { 143 | const now = new Date() 144 | 145 | return entities 146 | // filter invalid entities 147 | .filter(entity => { 148 | if (!!entity === false) return false 149 | 150 | const data = this.hass?.states[entity] 151 | if (data === undefined) return false 152 | if (!isDepartureAttrs(data.attributes)) return false 153 | 154 | return true 155 | }) 156 | // map entity name to departures and gather all together 157 | .map(entity => { 158 | const state = this.hass?.states[entity] 159 | if (isDepartureAttrs(state.attributes)) 160 | return state.attributes 161 | }) 162 | .flatMap(attrs => attrs.departures) 163 | // filter by departure time 164 | .filter((d) => { 165 | if (!this.config?.hide_departed) return true 166 | 167 | const diff = diffMinutes(new Date(d.expected), now) 168 | return diff + this.config?.show_departed_offeset >= 0 169 | }) 170 | // filter direction 171 | .filter((d) => { 172 | if (this.config?.direction === 0) return true 173 | return d.direction_code === this.config?.direction 174 | }) 175 | // sort by expected departure time 176 | .sort((a, b) => new Date(a.expected).getTime() - new Date(b.expected).getTime()) 177 | // limit to max departures 178 | .slice(0, this.config?.max_departures) 179 | } 180 | 181 | private getDepartures() { 182 | if (this.isManyEntitiesSet()) { 183 | return this.getDeparturesCombined(this.config?.entities || []) 184 | } 185 | 186 | const [_, attrs] = this.getFirstEntity() 187 | if (!attrs) return undefined; 188 | 189 | return this.getDeparturesFor(attrs) 190 | } 191 | 192 | private renderDepartures = () => { 193 | const renderEntityName = () => { 194 | const [_, attrs] = this.getFirstEntity() 195 | if (!attrs) return nothing; 196 | 197 | return (this.config.show_entity_name && attrs.friendly_name) 198 | ? html`
${attrs.friendly_name} 212 | ${isMany ? '' : renderEntityName()} 213 | ${this.config.show_header ? html` 214 |
215 | ${this.config?.show_icon ? html`
` : nothing} 216 |
${_("line")}
217 |
${_("departure")}
218 |
`: nothing} 219 | 220 | ${departures.map(dep => { 221 | const expectedAt = new Date(dep.expected) 222 | const diff = diffMinutes(expectedAt, now) 223 | 224 | const isAtThePlatform = diff === 0 225 | const isDeparted = diff < 0 226 | const hasDeviations = (dep.deviations?.length || 0) > 0; 227 | const mostImportantDeviation = dep.deviations?.sort((a, b) => b.importance_level - a.importance_level)?.[0]; 228 | 229 | const departureTime = this.config?.show_time_always 230 | ? expectedAt.toLocaleTimeString( 231 | lang, { 232 | hour: "numeric", 233 | minute: "numeric", 234 | } 235 | ) 236 | : (() => { 237 | return isAtThePlatform 238 | ? _("now") 239 | : html`` 240 | })() 241 | 242 | const icon = { 243 | [TransportType.METRO]: 'mdi:subway', 244 | [TransportType.BUS]: 'mdi:bus', 245 | [TransportType.TRAM]: 'mdi:tram', 246 | [TransportType.TRAIN]: 'mdi:train', 247 | [TransportType.SHIP]: 'mdi:ship', 248 | [TransportType.FERRY]: 'mdi:ferry', 249 | [TransportType.TAXI]: 'mdi:taxi', 250 | }[dep.line.transport_mode] || 'mdi:train' 251 | 252 | const lineIconClass = this.lineIconClass(dep.line.transport_mode, dep.line.designation, dep.line.group_of_lines) 253 | 254 | return html` 255 |
256 | ${this.config?.show_icon ? html` 257 |
258 | 259 |
260 | ` : nothing} 261 | ${this.config?.hide_line_number ? nothing : html` 262 |
263 | ${dep.line.designation} 264 | ${hasDeviations ? html`` : nothing} 265 |
266 | `} 267 |
268 | ${dep.destination} 269 | ${hasDeviations ? html`${mostImportantDeviation.message}` : nothing} 270 |
271 |
272 | ${departureTime} 273 |
274 |
` 275 | })} 276 |
277 | ` 278 | } 279 | 280 | private lineIconClass(type: TransportType, line: string, group: string) { 281 | let cls = '' 282 | switch (type) { 283 | case TransportType.BUS: 284 | cls = `bus bus_${line}` 285 | cls = group === "blåbuss" ? `${cls} blue` : cls 286 | break 287 | case TransportType.METRO: 288 | cls = `metro metro_${line}` 289 | switch (line) { 290 | case "10": 291 | case "11": 292 | cls = `${cls} blue` 293 | break; 294 | case "13": 295 | case "14": 296 | cls = `${cls} red` 297 | break; 298 | default: 299 | cls = `${cls} green` 300 | } 301 | break 302 | case TransportType.TRAM: 303 | cls = `tram tram_${line}` 304 | break 305 | case TransportType.TRAIN: 306 | cls = `train train_${line}` 307 | break 308 | } 309 | return cls 310 | } 311 | 312 | private clickHandler = (entity?: string) => (e: Event) => { 313 | const action = this.config?.click_action 314 | if (action === undefined) return 315 | 316 | if (action == 'info' && entity) { 317 | e.preventDefault() 318 | this._showAttributes(this, "hass-more-info", { entityId: entity }) 319 | return 320 | } else if (isEntityInfoAction(action)) { 321 | e.preventDefault() 322 | this._showAttributes(this, "hass-more-info", { entityId: action.entityId }) 323 | return 324 | } else if (isServiceCallAction(action)) { 325 | e.preventDefault() 326 | this._serviceCall(action.domain, action.service, action.data) 327 | return 328 | } 329 | } 330 | 331 | private _serviceCall (domain, service, data) { 332 | this.hass.callService(domain, service, data) 333 | } 334 | 335 | private _showAttributes (el: HTMLElement, type: string, detail?: object, options?: { bubbles?: boolean, cancelable?: boolean, composed?: boolean }) { 336 | const event = new Event(type, { 337 | bubbles: Boolean(options?.bubbles), 338 | cancelable: Boolean(options?.cancelable), 339 | composed: Boolean(options?.composed) || true 340 | }); 341 | event.detail = detail || {}; 342 | 343 | el.dispatchEvent(event); 344 | return event; 345 | } 346 | } 347 | 348 | const isEntityInfoAction = (a: ClickAction): a is EntityInfoAction => (a as any).entityId !== undefined 349 | const isServiceCallAction = (a: ClickAction): a is ServiceCallAction => (a as any).service !== undefined 350 | 351 | function isDepartureAttrs(item: { [key:string]: any }): item is DepartureAttributes { 352 | return (item as DepartureAttributes).departures !== undefined 353 | } 354 | 355 | declare global { 356 | interface Event { 357 | detail?: object 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /dist/hasl4-departure-card.js: -------------------------------------------------------------------------------- 1 | 2 | function $parcel$export(e, n, v, s) { 3 | Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); 4 | } 5 | 6 | var $parcel$global = globalThis; 7 | 8 | var $parcel$modules = {}; 9 | var $parcel$inits = {}; 10 | 11 | var parcelRequire = $parcel$global["parcelRequire55a1"]; 12 | 13 | if (parcelRequire == null) { 14 | parcelRequire = function(id) { 15 | if (id in $parcel$modules) { 16 | return $parcel$modules[id].exports; 17 | } 18 | if (id in $parcel$inits) { 19 | var init = $parcel$inits[id]; 20 | delete $parcel$inits[id]; 21 | var module = {id: id, exports: {}}; 22 | $parcel$modules[id] = module; 23 | init.call(module.exports, module, module.exports); 24 | return module.exports; 25 | } 26 | var err = new Error("Cannot find module '" + id + "'"); 27 | err.code = 'MODULE_NOT_FOUND'; 28 | throw err; 29 | }; 30 | 31 | parcelRequire.register = function register(id, init) { 32 | $parcel$inits[id] = init; 33 | }; 34 | 35 | $parcel$global["parcelRequire55a1"] = parcelRequire; 36 | } 37 | 38 | var parcelRegister = parcelRequire.register; 39 | parcelRegister("39J5i", function(module, exports) { 40 | 41 | $parcel$export(module.exports, "__decorate", () => $24c52f343453d62d$export$29e00dfd3077644b); 42 | /****************************************************************************** 43 | Copyright (c) Microsoft Corporation. 44 | 45 | Permission to use, copy, modify, and/or distribute this software for any 46 | purpose with or without fee is hereby granted. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 49 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 50 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 51 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 52 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 53 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 54 | PERFORMANCE OF THIS SOFTWARE. 55 | ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol */ var $24c52f343453d62d$var$extendStatics = function(d, b) { 56 | $24c52f343453d62d$var$extendStatics = Object.setPrototypeOf || ({ 57 | __proto__: [] 58 | }) instanceof Array && function(d, b) { 59 | d.__proto__ = b; 60 | } || function(d, b) { 61 | for(var p in b)if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; 62 | }; 63 | return $24c52f343453d62d$var$extendStatics(d, b); 64 | }; 65 | function $24c52f343453d62d$export$a8ba968b8961cb8a(d, b) { 66 | if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); 67 | $24c52f343453d62d$var$extendStatics(d, b); 68 | function __() { 69 | this.constructor = d; 70 | } 71 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 72 | } 73 | var $24c52f343453d62d$export$18ce0697a983be9b = function() { 74 | $24c52f343453d62d$export$18ce0697a983be9b = Object.assign || function __assign(t) { 75 | for(var s, i = 1, n = arguments.length; i < n; i++){ 76 | s = arguments[i]; 77 | for(var p in s)if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; 78 | } 79 | return t; 80 | }; 81 | return $24c52f343453d62d$export$18ce0697a983be9b.apply(this, arguments); 82 | }; 83 | function $24c52f343453d62d$export$3c9a16f847548506(s, e) { 84 | var t = {}; 85 | for(var p in s)if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; 86 | if (s != null && typeof Object.getOwnPropertySymbols === "function") { 87 | for(var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++)if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; 88 | } 89 | return t; 90 | } 91 | function $24c52f343453d62d$export$29e00dfd3077644b(decorators, target, key, desc) { 92 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 93 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 94 | else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 95 | return c > 3 && r && Object.defineProperty(target, key, r), r; 96 | } 97 | function $24c52f343453d62d$export$d5ad3fd78186038f(paramIndex, decorator) { 98 | return function(target, key) { 99 | decorator(target, key, paramIndex); 100 | }; 101 | } 102 | function $24c52f343453d62d$export$3a84e1ae4e97e9b0(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { 103 | function accept(f) { 104 | if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); 105 | return f; 106 | } 107 | var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; 108 | var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; 109 | var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); 110 | var _, done = false; 111 | for(var i = decorators.length - 1; i >= 0; i--){ 112 | var context = {}; 113 | for(var p in contextIn)context[p] = p === "access" ? {} : contextIn[p]; 114 | for(var p in contextIn.access)context.access[p] = contextIn.access[p]; 115 | context.addInitializer = function(f) { 116 | if (done) throw new TypeError("Cannot add initializers after decoration has completed"); 117 | extraInitializers.push(accept(f || null)); 118 | }; 119 | var result = (0, decorators[i])(kind === "accessor" ? { 120 | get: descriptor.get, 121 | set: descriptor.set 122 | } : descriptor[key], context); 123 | if (kind === "accessor") { 124 | if (result === void 0) continue; 125 | if (result === null || typeof result !== "object") throw new TypeError("Object expected"); 126 | if (_ = accept(result.get)) descriptor.get = _; 127 | if (_ = accept(result.set)) descriptor.set = _; 128 | if (_ = accept(result.init)) initializers.unshift(_); 129 | } else if (_ = accept(result)) { 130 | if (kind === "field") initializers.unshift(_); 131 | else descriptor[key] = _; 132 | } 133 | } 134 | if (target) Object.defineProperty(target, contextIn.name, descriptor); 135 | done = true; 136 | } 137 | function $24c52f343453d62d$export$d831c04e792af3d(thisArg, initializers, value) { 138 | var useValue = arguments.length > 2; 139 | for(var i = 0; i < initializers.length; i++)value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); 140 | return useValue ? value : void 0; 141 | } 142 | function $24c52f343453d62d$export$6a2a36740a146cb8(x) { 143 | return typeof x === "symbol" ? x : "".concat(x); 144 | } 145 | function $24c52f343453d62d$export$d1a06452d3489bc7(f, name, prefix) { 146 | if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; 147 | return Object.defineProperty(f, "name", { 148 | configurable: true, 149 | value: prefix ? "".concat(prefix, " ", name) : name 150 | }); 151 | } 152 | function $24c52f343453d62d$export$f1db080c865becb9(metadataKey, metadataValue) { 153 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); 154 | } 155 | function $24c52f343453d62d$export$1050f835b63b671e(thisArg, _arguments, P, generator) { 156 | function adopt(value) { 157 | return value instanceof P ? value : new P(function(resolve) { 158 | resolve(value); 159 | }); 160 | } 161 | return new (P || (P = Promise))(function(resolve, reject) { 162 | function fulfilled(value) { 163 | try { 164 | step(generator.next(value)); 165 | } catch (e) { 166 | reject(e); 167 | } 168 | } 169 | function rejected(value) { 170 | try { 171 | step(generator["throw"](value)); 172 | } catch (e) { 173 | reject(e); 174 | } 175 | } 176 | function step(result) { 177 | result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); 178 | } 179 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 180 | }); 181 | } 182 | function $24c52f343453d62d$export$67ebef60e6f28a6(thisArg, body) { 183 | var _ = { 184 | label: 0, 185 | sent: function() { 186 | if (t[0] & 1) throw t[1]; 187 | return t[1]; 188 | }, 189 | trys: [], 190 | ops: [] 191 | }, f, y, t, g; 192 | return g = { 193 | next: verb(0), 194 | "throw": verb(1), 195 | "return": verb(2) 196 | }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { 197 | return this; 198 | }), g; 199 | function verb(n) { 200 | return function(v) { 201 | return step([ 202 | n, 203 | v 204 | ]); 205 | }; 206 | } 207 | function step(op) { 208 | if (f) throw new TypeError("Generator is already executing."); 209 | while(g && (g = 0, op[0] && (_ = 0)), _)try { 210 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 211 | if (y = 0, t) op = [ 212 | op[0] & 2, 213 | t.value 214 | ]; 215 | switch(op[0]){ 216 | case 0: 217 | case 1: 218 | t = op; 219 | break; 220 | case 4: 221 | _.label++; 222 | return { 223 | value: op[1], 224 | done: false 225 | }; 226 | case 5: 227 | _.label++; 228 | y = op[1]; 229 | op = [ 230 | 0 231 | ]; 232 | continue; 233 | case 7: 234 | op = _.ops.pop(); 235 | _.trys.pop(); 236 | continue; 237 | default: 238 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { 239 | _ = 0; 240 | continue; 241 | } 242 | if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { 243 | _.label = op[1]; 244 | break; 245 | } 246 | if (op[0] === 6 && _.label < t[1]) { 247 | _.label = t[1]; 248 | t = op; 249 | break; 250 | } 251 | if (t && _.label < t[2]) { 252 | _.label = t[2]; 253 | _.ops.push(op); 254 | break; 255 | } 256 | if (t[2]) _.ops.pop(); 257 | _.trys.pop(); 258 | continue; 259 | } 260 | op = body.call(thisArg, _); 261 | } catch (e) { 262 | op = [ 263 | 6, 264 | e 265 | ]; 266 | y = 0; 267 | } finally{ 268 | f = t = 0; 269 | } 270 | if (op[0] & 5) throw op[1]; 271 | return { 272 | value: op[0] ? op[1] : void 0, 273 | done: true 274 | }; 275 | } 276 | } 277 | var $24c52f343453d62d$export$45d3717a4c69092e = Object.create ? function(o, m, k, k2) { 278 | if (k2 === undefined) k2 = k; 279 | var desc = Object.getOwnPropertyDescriptor(m, k); 280 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) desc = { 281 | enumerable: true, 282 | get: function() { 283 | return m[k]; 284 | } 285 | }; 286 | Object.defineProperty(o, k2, desc); 287 | } : function(o, m, k, k2) { 288 | if (k2 === undefined) k2 = k; 289 | o[k2] = m[k]; 290 | }; 291 | function $24c52f343453d62d$export$f33643c0debef087(m, o) { 292 | for(var p in m)if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) $24c52f343453d62d$export$45d3717a4c69092e(o, m, p); 293 | } 294 | function $24c52f343453d62d$export$19a8beecd37a4c45(o) { 295 | var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; 296 | if (m) return m.call(o); 297 | if (o && typeof o.length === "number") return { 298 | next: function() { 299 | if (o && i >= o.length) o = void 0; 300 | return { 301 | value: o && o[i++], 302 | done: !o 303 | }; 304 | } 305 | }; 306 | throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); 307 | } 308 | function $24c52f343453d62d$export$8d051b38c9118094(o, n) { 309 | var m = typeof Symbol === "function" && o[Symbol.iterator]; 310 | if (!m) return o; 311 | var i = m.call(o), r, ar = [], e; 312 | try { 313 | while((n === void 0 || n-- > 0) && !(r = i.next()).done)ar.push(r.value); 314 | } catch (error) { 315 | e = { 316 | error: error 317 | }; 318 | } finally{ 319 | try { 320 | if (r && !r.done && (m = i["return"])) m.call(i); 321 | } finally{ 322 | if (e) throw e.error; 323 | } 324 | } 325 | return ar; 326 | } 327 | function $24c52f343453d62d$export$afc72e2116322959() { 328 | for(var ar = [], i = 0; i < arguments.length; i++)ar = ar.concat($24c52f343453d62d$export$8d051b38c9118094(arguments[i])); 329 | return ar; 330 | } 331 | function $24c52f343453d62d$export$6388937ca91ccae8() { 332 | for(var s = 0, i = 0, il = arguments.length; i < il; i++)s += arguments[i].length; 333 | for(var r = Array(s), k = 0, i = 0; i < il; i++)for(var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)r[k] = a[j]; 334 | return r; 335 | } 336 | function $24c52f343453d62d$export$1216008129fb82ed(to, from, pack) { 337 | if (pack || arguments.length === 2) { 338 | for(var i = 0, l = from.length, ar; i < l; i++)if (ar || !(i in from)) { 339 | if (!ar) ar = Array.prototype.slice.call(from, 0, i); 340 | ar[i] = from[i]; 341 | } 342 | } 343 | return to.concat(ar || Array.prototype.slice.call(from)); 344 | } 345 | function $24c52f343453d62d$export$10c90e4f7922046c(v) { 346 | return this instanceof $24c52f343453d62d$export$10c90e4f7922046c ? (this.v = v, this) : new $24c52f343453d62d$export$10c90e4f7922046c(v); 347 | } 348 | function $24c52f343453d62d$export$e427f37a30a4de9b(thisArg, _arguments, generator) { 349 | if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); 350 | var g = generator.apply(thisArg, _arguments || []), i, q = []; 351 | return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() { 352 | return this; 353 | }, i; 354 | function verb(n) { 355 | if (g[n]) i[n] = function(v) { 356 | return new Promise(function(a, b) { 357 | q.push([ 358 | n, 359 | v, 360 | a, 361 | b 362 | ]) > 1 || resume(n, v); 363 | }); 364 | }; 365 | } 366 | function resume(n, v) { 367 | try { 368 | step(g[n](v)); 369 | } catch (e) { 370 | settle(q[0][3], e); 371 | } 372 | } 373 | function step(r) { 374 | r.value instanceof $24c52f343453d62d$export$10c90e4f7922046c ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); 375 | } 376 | function fulfill(value) { 377 | resume("next", value); 378 | } 379 | function reject(value) { 380 | resume("throw", value); 381 | } 382 | function settle(f, v) { 383 | if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); 384 | } 385 | } 386 | function $24c52f343453d62d$export$bbd80228419bb833(o) { 387 | var i, p; 388 | return i = {}, verb("next"), verb("throw", function(e) { 389 | throw e; 390 | }), verb("return"), i[Symbol.iterator] = function() { 391 | return this; 392 | }, i; 393 | function verb(n, f) { 394 | i[n] = o[n] ? function(v) { 395 | return (p = !p) ? { 396 | value: $24c52f343453d62d$export$10c90e4f7922046c(o[n](v)), 397 | done: false 398 | } : f ? f(v) : v; 399 | } : f; 400 | } 401 | } 402 | function $24c52f343453d62d$export$e3b29a3d6162315f(o) { 403 | if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); 404 | var m = o[Symbol.asyncIterator], i; 405 | return m ? m.call(o) : (o = typeof $24c52f343453d62d$export$19a8beecd37a4c45 === "function" ? $24c52f343453d62d$export$19a8beecd37a4c45(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() { 406 | return this; 407 | }, i); 408 | function verb(n) { 409 | i[n] = o[n] && function(v) { 410 | return new Promise(function(resolve, reject) { 411 | v = o[n](v), settle(resolve, reject, v.done, v.value); 412 | }); 413 | }; 414 | } 415 | function settle(resolve, reject, d, v) { 416 | Promise.resolve(v).then(function(v) { 417 | resolve({ 418 | value: v, 419 | done: d 420 | }); 421 | }, reject); 422 | } 423 | } 424 | function $24c52f343453d62d$export$4fb47efe1390b86f(cooked, raw) { 425 | if (Object.defineProperty) Object.defineProperty(cooked, "raw", { 426 | value: raw 427 | }); 428 | else cooked.raw = raw; 429 | return cooked; 430 | } 431 | var $24c52f343453d62d$var$__setModuleDefault = Object.create ? function(o, v) { 432 | Object.defineProperty(o, "default", { 433 | enumerable: true, 434 | value: v 435 | }); 436 | } : function(o, v) { 437 | o["default"] = v; 438 | }; 439 | function $24c52f343453d62d$export$c21735bcef00d192(mod) { 440 | if (mod && mod.__esModule) return mod; 441 | var result = {}; 442 | if (mod != null) { 443 | for(var k in mod)if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) $24c52f343453d62d$export$45d3717a4c69092e(result, mod, k); 444 | } 445 | $24c52f343453d62d$var$__setModuleDefault(result, mod); 446 | return result; 447 | } 448 | function $24c52f343453d62d$export$da59b14a69baef04(mod) { 449 | return mod && mod.__esModule ? mod : { 450 | default: mod 451 | }; 452 | } 453 | function $24c52f343453d62d$export$d5dcaf168c640c35(receiver, state, kind, f) { 454 | if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); 455 | if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); 456 | return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); 457 | } 458 | function $24c52f343453d62d$export$d40a35129aaff81f(receiver, state, value, kind, f) { 459 | if (kind === "m") throw new TypeError("Private method is not writable"); 460 | if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); 461 | if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); 462 | return kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value), value; 463 | } 464 | function $24c52f343453d62d$export$81fdc39f203e4e04(state, receiver) { 465 | if (receiver === null || typeof receiver !== "object" && typeof receiver !== "function") throw new TypeError("Cannot use 'in' operator on non-object"); 466 | return typeof state === "function" ? receiver === state : state.has(receiver); 467 | } 468 | function $24c52f343453d62d$export$88ac25d8e944e405(env, value, async) { 469 | if (value !== null && value !== void 0) { 470 | if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); 471 | var dispose; 472 | if (async) { 473 | if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); 474 | dispose = value[Symbol.asyncDispose]; 475 | } 476 | if (dispose === void 0) { 477 | if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); 478 | dispose = value[Symbol.dispose]; 479 | } 480 | if (typeof dispose !== "function") throw new TypeError("Object not disposable."); 481 | env.stack.push({ 482 | value: value, 483 | dispose: dispose, 484 | async: async 485 | }); 486 | } else if (async) env.stack.push({ 487 | async: true 488 | }); 489 | return value; 490 | } 491 | var $24c52f343453d62d$var$_SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) { 492 | var e = new Error(message); 493 | return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; 494 | }; 495 | function $24c52f343453d62d$export$8f076105dc360e92(env) { 496 | function fail(e) { 497 | env.error = env.hasError ? new $24c52f343453d62d$var$_SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; 498 | env.hasError = true; 499 | } 500 | function next() { 501 | while(env.stack.length){ 502 | var rec = env.stack.pop(); 503 | try { 504 | var result = rec.dispose && rec.dispose.call(rec.value); 505 | if (rec.async) return Promise.resolve(result).then(next, function(e) { 506 | fail(e); 507 | return next(); 508 | }); 509 | } catch (e) { 510 | fail(e); 511 | } 512 | } 513 | if (env.hasError) throw env.error; 514 | } 515 | return next(); 516 | } 517 | var $24c52f343453d62d$export$2e2bcd8739ae039 = { 518 | __extends: $24c52f343453d62d$export$a8ba968b8961cb8a, 519 | __assign: $24c52f343453d62d$export$18ce0697a983be9b, 520 | __rest: $24c52f343453d62d$export$3c9a16f847548506, 521 | __decorate: $24c52f343453d62d$export$29e00dfd3077644b, 522 | __param: $24c52f343453d62d$export$d5ad3fd78186038f, 523 | __metadata: $24c52f343453d62d$export$f1db080c865becb9, 524 | __awaiter: $24c52f343453d62d$export$1050f835b63b671e, 525 | __generator: $24c52f343453d62d$export$67ebef60e6f28a6, 526 | __createBinding: $24c52f343453d62d$export$45d3717a4c69092e, 527 | __exportStar: $24c52f343453d62d$export$f33643c0debef087, 528 | __values: $24c52f343453d62d$export$19a8beecd37a4c45, 529 | __read: $24c52f343453d62d$export$8d051b38c9118094, 530 | __spread: $24c52f343453d62d$export$afc72e2116322959, 531 | __spreadArrays: $24c52f343453d62d$export$6388937ca91ccae8, 532 | __spreadArray: $24c52f343453d62d$export$1216008129fb82ed, 533 | __await: $24c52f343453d62d$export$10c90e4f7922046c, 534 | __asyncGenerator: $24c52f343453d62d$export$e427f37a30a4de9b, 535 | __asyncDelegator: $24c52f343453d62d$export$bbd80228419bb833, 536 | __asyncValues: $24c52f343453d62d$export$e3b29a3d6162315f, 537 | __makeTemplateObject: $24c52f343453d62d$export$4fb47efe1390b86f, 538 | __importStar: $24c52f343453d62d$export$c21735bcef00d192, 539 | __importDefault: $24c52f343453d62d$export$da59b14a69baef04, 540 | __classPrivateFieldGet: $24c52f343453d62d$export$d5dcaf168c640c35, 541 | __classPrivateFieldSet: $24c52f343453d62d$export$d40a35129aaff81f, 542 | __classPrivateFieldIn: $24c52f343453d62d$export$81fdc39f203e4e04, 543 | __addDisposableResource: $24c52f343453d62d$export$88ac25d8e944e405, 544 | __disposeResources: $24c52f343453d62d$export$8f076105dc360e92 545 | }; 546 | 547 | }); 548 | 549 | parcelRegister("j0ZcV", function(module, exports) { 550 | $parcel$export(module.exports, "css", () => (parcelRequire("j8KxL")).css); 551 | $parcel$export(module.exports, "html", () => (parcelRequire("l56HR")).html); 552 | $parcel$export(module.exports, "LitElement", () => (parcelRequire("eGUNk")).LitElement); 553 | $parcel$export(module.exports, "nothing", () => (parcelRequire("l56HR")).nothing); 554 | parcelRequire("2emM7"); 555 | parcelRequire("l56HR"); 556 | parcelRequire("eGUNk"); 557 | parcelRequire("dJV7N"); 558 | 559 | }); 560 | parcelRegister("2emM7", function(module, exports) { 561 | 562 | $parcel$export(module.exports, "defaultConverter", () => $19fe8e3abedf4df0$export$7312b35fbf521afb); 563 | $parcel$export(module.exports, "notEqual", () => $19fe8e3abedf4df0$export$53a6892c50694894); 564 | $parcel$export(module.exports, "ReactiveElement", () => $19fe8e3abedf4df0$export$c7c07a37856565d); 565 | $parcel$export(module.exports, "css", () => (parcelRequire("j8KxL")).css); 566 | 567 | var $j8KxL = parcelRequire("j8KxL"); 568 | /** 569 | * @license 570 | * Copyright 2017 Google LLC 571 | * SPDX-License-Identifier: BSD-3-Clause 572 | */ const { is: $19fe8e3abedf4df0$var$i, defineProperty: $19fe8e3abedf4df0$var$e, getOwnPropertyDescriptor: $19fe8e3abedf4df0$var$r, getOwnPropertyNames: $19fe8e3abedf4df0$var$h, getOwnPropertySymbols: $19fe8e3abedf4df0$var$o, getPrototypeOf: $19fe8e3abedf4df0$var$n } = Object, $19fe8e3abedf4df0$var$a = globalThis, $19fe8e3abedf4df0$var$c = $19fe8e3abedf4df0$var$a.trustedTypes, $19fe8e3abedf4df0$var$l = $19fe8e3abedf4df0$var$c ? $19fe8e3abedf4df0$var$c.emptyScript : "", $19fe8e3abedf4df0$var$p = $19fe8e3abedf4df0$var$a.reactiveElementPolyfillSupport, $19fe8e3abedf4df0$var$d = (t, s)=>t, $19fe8e3abedf4df0$export$7312b35fbf521afb = { 573 | toAttribute (t, s) { 574 | switch(s){ 575 | case Boolean: 576 | t = t ? $19fe8e3abedf4df0$var$l : null; 577 | break; 578 | case Object: 579 | case Array: 580 | t = null == t ? t : JSON.stringify(t); 581 | } 582 | return t; 583 | }, 584 | fromAttribute (t, s) { 585 | let i = t; 586 | switch(s){ 587 | case Boolean: 588 | i = null !== t; 589 | break; 590 | case Number: 591 | i = null === t ? null : Number(t); 592 | break; 593 | case Object: 594 | case Array: 595 | try { 596 | i = JSON.parse(t); 597 | } catch (t) { 598 | i = null; 599 | } 600 | } 601 | return i; 602 | } 603 | }, $19fe8e3abedf4df0$export$53a6892c50694894 = (t, s)=>!$19fe8e3abedf4df0$var$i(t, s), $19fe8e3abedf4df0$var$y = { 604 | attribute: !0, 605 | type: String, 606 | converter: $19fe8e3abedf4df0$export$7312b35fbf521afb, 607 | reflect: !1, 608 | hasChanged: $19fe8e3abedf4df0$export$53a6892c50694894 609 | }; 610 | Symbol.metadata ??= Symbol("metadata"), $19fe8e3abedf4df0$var$a.litPropertyMetadata ??= new WeakMap; 611 | class $19fe8e3abedf4df0$export$c7c07a37856565d extends HTMLElement { 612 | static addInitializer(t) { 613 | this._$Ei(), (this.l ??= []).push(t); 614 | } 615 | static get observedAttributes() { 616 | return this.finalize(), this._$Eh && [ 617 | ...this._$Eh.keys() 618 | ]; 619 | } 620 | static createProperty(t, s = $19fe8e3abedf4df0$var$y) { 621 | if (s.state && (s.attribute = !1), this._$Ei(), this.elementProperties.set(t, s), !s.noAccessor) { 622 | const i = Symbol(), r = this.getPropertyDescriptor(t, i, s); 623 | void 0 !== r && $19fe8e3abedf4df0$var$e(this.prototype, t, r); 624 | } 625 | } 626 | static getPropertyDescriptor(t, s, i) { 627 | const { get: e, set: h } = $19fe8e3abedf4df0$var$r(this.prototype, t) ?? { 628 | get () { 629 | return this[s]; 630 | }, 631 | set (t) { 632 | this[s] = t; 633 | } 634 | }; 635 | return { 636 | get () { 637 | return e?.call(this); 638 | }, 639 | set (s) { 640 | const r = e?.call(this); 641 | h.call(this, s), this.requestUpdate(t, r, i); 642 | }, 643 | configurable: !0, 644 | enumerable: !0 645 | }; 646 | } 647 | static getPropertyOptions(t) { 648 | return this.elementProperties.get(t) ?? $19fe8e3abedf4df0$var$y; 649 | } 650 | static _$Ei() { 651 | if (this.hasOwnProperty($19fe8e3abedf4df0$var$d("elementProperties"))) return; 652 | const t = $19fe8e3abedf4df0$var$n(this); 653 | t.finalize(), void 0 !== t.l && (this.l = [ 654 | ...t.l 655 | ]), this.elementProperties = new Map(t.elementProperties); 656 | } 657 | static finalize() { 658 | if (this.hasOwnProperty($19fe8e3abedf4df0$var$d("finalized"))) return; 659 | if (this.finalized = !0, this._$Ei(), this.hasOwnProperty($19fe8e3abedf4df0$var$d("properties"))) { 660 | const t = this.properties, s = [ 661 | ...$19fe8e3abedf4df0$var$h(t), 662 | ...$19fe8e3abedf4df0$var$o(t) 663 | ]; 664 | for (const i of s)this.createProperty(i, t[i]); 665 | } 666 | const t = this[Symbol.metadata]; 667 | if (null !== t) { 668 | const s = litPropertyMetadata.get(t); 669 | if (void 0 !== s) for (const [t, i] of s)this.elementProperties.set(t, i); 670 | } 671 | this._$Eh = new Map; 672 | for (const [t, s] of this.elementProperties){ 673 | const i = this._$Eu(t, s); 674 | void 0 !== i && this._$Eh.set(i, t); 675 | } 676 | this.elementStyles = this.finalizeStyles(this.styles); 677 | } 678 | static finalizeStyles(s) { 679 | const i = []; 680 | if (Array.isArray(s)) { 681 | const e = new Set(s.flat(1 / 0).reverse()); 682 | for (const s of e)i.unshift((0, $j8KxL.getCompatibleStyle)(s)); 683 | } else void 0 !== s && i.push((0, $j8KxL.getCompatibleStyle)(s)); 684 | return i; 685 | } 686 | static _$Eu(t, s) { 687 | const i = s.attribute; 688 | return !1 === i ? void 0 : "string" == typeof i ? i : "string" == typeof t ? t.toLowerCase() : void 0; 689 | } 690 | constructor(){ 691 | super(), this._$Ep = void 0, this.isUpdatePending = !1, this.hasUpdated = !1, this._$Em = null, this._$Ev(); 692 | } 693 | _$Ev() { 694 | this._$ES = new Promise((t)=>this.enableUpdating = t), this._$AL = new Map, this._$E_(), this.requestUpdate(), this.constructor.l?.forEach((t)=>t(this)); 695 | } 696 | addController(t) { 697 | (this._$EO ??= new Set).add(t), void 0 !== this.renderRoot && this.isConnected && t.hostConnected?.(); 698 | } 699 | removeController(t) { 700 | this._$EO?.delete(t); 701 | } 702 | _$E_() { 703 | const t = new Map, s = this.constructor.elementProperties; 704 | for (const i of s.keys())this.hasOwnProperty(i) && (t.set(i, this[i]), delete this[i]); 705 | t.size > 0 && (this._$Ep = t); 706 | } 707 | createRenderRoot() { 708 | const t = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions); 709 | return (0, $j8KxL.adoptStyles)(t, this.constructor.elementStyles), t; 710 | } 711 | connectedCallback() { 712 | this.renderRoot ??= this.createRenderRoot(), this.enableUpdating(!0), this._$EO?.forEach((t)=>t.hostConnected?.()); 713 | } 714 | enableUpdating(t) {} 715 | disconnectedCallback() { 716 | this._$EO?.forEach((t)=>t.hostDisconnected?.()); 717 | } 718 | attributeChangedCallback(t, s, i) { 719 | this._$AK(t, i); 720 | } 721 | _$EC(t, s) { 722 | const i = this.constructor.elementProperties.get(t), e = this.constructor._$Eu(t, i); 723 | if (void 0 !== e && !0 === i.reflect) { 724 | const r = (void 0 !== i.converter?.toAttribute ? i.converter : $19fe8e3abedf4df0$export$7312b35fbf521afb).toAttribute(s, i.type); 725 | this._$Em = t, null == r ? this.removeAttribute(e) : this.setAttribute(e, r), this._$Em = null; 726 | } 727 | } 728 | _$AK(t, s) { 729 | const i = this.constructor, e = i._$Eh.get(t); 730 | if (void 0 !== e && this._$Em !== e) { 731 | const t = i.getPropertyOptions(e), r = "function" == typeof t.converter ? { 732 | fromAttribute: t.converter 733 | } : void 0 !== t.converter?.fromAttribute ? t.converter : $19fe8e3abedf4df0$export$7312b35fbf521afb; 734 | this._$Em = e, this[e] = r.fromAttribute(s, t.type), this._$Em = null; 735 | } 736 | } 737 | requestUpdate(t, s, i) { 738 | if (void 0 !== t) { 739 | if (i ??= this.constructor.getPropertyOptions(t), !(i.hasChanged ?? $19fe8e3abedf4df0$export$53a6892c50694894)(this[t], s)) return; 740 | this.P(t, s, i); 741 | } 742 | !1 === this.isUpdatePending && (this._$ES = this._$ET()); 743 | } 744 | P(t, s, i) { 745 | this._$AL.has(t) || this._$AL.set(t, s), !0 === i.reflect && this._$Em !== t && (this._$Ej ??= new Set).add(t); 746 | } 747 | async _$ET() { 748 | this.isUpdatePending = !0; 749 | try { 750 | await this._$ES; 751 | } catch (t) { 752 | Promise.reject(t); 753 | } 754 | const t = this.scheduleUpdate(); 755 | return null != t && await t, !this.isUpdatePending; 756 | } 757 | scheduleUpdate() { 758 | return this.performUpdate(); 759 | } 760 | performUpdate() { 761 | if (!this.isUpdatePending) return; 762 | if (!this.hasUpdated) { 763 | if (this.renderRoot ??= this.createRenderRoot(), this._$Ep) { 764 | for (const [t, s] of this._$Ep)this[t] = s; 765 | this._$Ep = void 0; 766 | } 767 | const t = this.constructor.elementProperties; 768 | if (t.size > 0) for (const [s, i] of t)!0 !== i.wrapped || this._$AL.has(s) || void 0 === this[s] || this.P(s, this[s], i); 769 | } 770 | let t = !1; 771 | const s = this._$AL; 772 | try { 773 | t = this.shouldUpdate(s), t ? (this.willUpdate(s), this._$EO?.forEach((t)=>t.hostUpdate?.()), this.update(s)) : this._$EU(); 774 | } catch (s) { 775 | throw t = !1, this._$EU(), s; 776 | } 777 | t && this._$AE(s); 778 | } 779 | willUpdate(t) {} 780 | _$AE(t) { 781 | this._$EO?.forEach((t)=>t.hostUpdated?.()), this.hasUpdated || (this.hasUpdated = !0, this.firstUpdated(t)), this.updated(t); 782 | } 783 | _$EU() { 784 | this._$AL = new Map, this.isUpdatePending = !1; 785 | } 786 | get updateComplete() { 787 | return this.getUpdateComplete(); 788 | } 789 | getUpdateComplete() { 790 | return this._$ES; 791 | } 792 | shouldUpdate(t) { 793 | return !0; 794 | } 795 | update(t) { 796 | this._$Ej &&= this._$Ej.forEach((t)=>this._$EC(t, this[t])), this._$EU(); 797 | } 798 | updated(t) {} 799 | firstUpdated(t) {} 800 | } 801 | $19fe8e3abedf4df0$export$c7c07a37856565d.elementStyles = [], $19fe8e3abedf4df0$export$c7c07a37856565d.shadowRootOptions = { 802 | mode: "open" 803 | }, $19fe8e3abedf4df0$export$c7c07a37856565d[$19fe8e3abedf4df0$var$d("elementProperties")] = new Map, $19fe8e3abedf4df0$export$c7c07a37856565d[$19fe8e3abedf4df0$var$d("finalized")] = new Map, $19fe8e3abedf4df0$var$p?.({ 804 | ReactiveElement: $19fe8e3abedf4df0$export$c7c07a37856565d 805 | }), ($19fe8e3abedf4df0$var$a.reactiveElementVersions ??= []).push("2.0.4"); 806 | 807 | }); 808 | parcelRegister("j8KxL", function(module, exports) { 809 | 810 | $parcel$export(module.exports, "css", () => $def2de46b9306e8a$export$dbf350e5966cf602); 811 | $parcel$export(module.exports, "adoptStyles", () => $def2de46b9306e8a$export$2ca4a66ec4cecb90); 812 | $parcel$export(module.exports, "getCompatibleStyle", () => $def2de46b9306e8a$export$ee69dfd951e24778); 813 | /** 814 | * @license 815 | * Copyright 2019 Google LLC 816 | * SPDX-License-Identifier: BSD-3-Clause 817 | */ const $def2de46b9306e8a$var$t = globalThis, $def2de46b9306e8a$export$b4d10f6001c083c2 = $def2de46b9306e8a$var$t.ShadowRoot && (void 0 === $def2de46b9306e8a$var$t.ShadyCSS || $def2de46b9306e8a$var$t.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, $def2de46b9306e8a$var$s = Symbol(), $def2de46b9306e8a$var$o = new WeakMap; 818 | class $def2de46b9306e8a$export$505d1e8739bad805 { 819 | constructor(t, e, o){ 820 | if (this._$cssResult$ = !0, o !== $def2de46b9306e8a$var$s) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); 821 | this.cssText = t, this.t = e; 822 | } 823 | get styleSheet() { 824 | let t = this.o; 825 | const s = this.t; 826 | if ($def2de46b9306e8a$export$b4d10f6001c083c2 && void 0 === t) { 827 | const e = void 0 !== s && 1 === s.length; 828 | e && (t = $def2de46b9306e8a$var$o.get(s)), void 0 === t && ((this.o = t = new CSSStyleSheet).replaceSync(this.cssText), e && $def2de46b9306e8a$var$o.set(s, t)); 829 | } 830 | return t; 831 | } 832 | toString() { 833 | return this.cssText; 834 | } 835 | } 836 | const $def2de46b9306e8a$export$8d80f9cac07cdb3 = (t)=>new $def2de46b9306e8a$export$505d1e8739bad805("string" == typeof t ? t : t + "", void 0, $def2de46b9306e8a$var$s), $def2de46b9306e8a$export$dbf350e5966cf602 = (t, ...e)=>{ 837 | const o = 1 === t.length ? t[0] : e.reduce((e, s, o)=>e + ((t)=>{ 838 | if (!0 === t._$cssResult$) return t.cssText; 839 | if ("number" == typeof t) return t; 840 | throw Error("Value passed to 'css' function must be a 'css' function result: " + t + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security."); 841 | })(s) + t[o + 1], t[0]); 842 | return new $def2de46b9306e8a$export$505d1e8739bad805(o, t, $def2de46b9306e8a$var$s); 843 | }, $def2de46b9306e8a$export$2ca4a66ec4cecb90 = (s, o)=>{ 844 | if ($def2de46b9306e8a$export$b4d10f6001c083c2) s.adoptedStyleSheets = o.map((t)=>t instanceof CSSStyleSheet ? t : t.styleSheet); 845 | else for (const e of o){ 846 | const o = document.createElement("style"), n = $def2de46b9306e8a$var$t.litNonce; 847 | void 0 !== n && o.setAttribute("nonce", n), o.textContent = e.cssText, s.appendChild(o); 848 | } 849 | }, $def2de46b9306e8a$export$ee69dfd951e24778 = $def2de46b9306e8a$export$b4d10f6001c083c2 ? (t)=>t : (t)=>t instanceof CSSStyleSheet ? ((t)=>{ 850 | let e = ""; 851 | for (const s of t.cssRules)e += s.cssText; 852 | return $def2de46b9306e8a$export$8d80f9cac07cdb3(e); 853 | })(t) : t; 854 | 855 | }); 856 | 857 | 858 | parcelRegister("l56HR", function(module, exports) { 859 | 860 | $parcel$export(module.exports, "html", () => $f58f44579a4747ac$export$c0bb0b647f701bb5); 861 | $parcel$export(module.exports, "noChange", () => $f58f44579a4747ac$export$9c068ae9cc5db4e8); 862 | $parcel$export(module.exports, "nothing", () => $f58f44579a4747ac$export$45b790e32b2810ee); 863 | $parcel$export(module.exports, "render", () => $f58f44579a4747ac$export$b3890eb0ae9dca99); 864 | /** 865 | * @license 866 | * Copyright 2017 Google LLC 867 | * SPDX-License-Identifier: BSD-3-Clause 868 | */ const $f58f44579a4747ac$var$t = globalThis, $f58f44579a4747ac$var$i = $f58f44579a4747ac$var$t.trustedTypes, $f58f44579a4747ac$var$s = $f58f44579a4747ac$var$i ? $f58f44579a4747ac$var$i.createPolicy("lit-html", { 869 | createHTML: (t)=>t 870 | }) : void 0, $f58f44579a4747ac$var$e = "$lit$", $f58f44579a4747ac$var$h = `lit$${(Math.random() + "").slice(9)}$`, $f58f44579a4747ac$var$o = "?" + $f58f44579a4747ac$var$h, $f58f44579a4747ac$var$n = `<${$f58f44579a4747ac$var$o}>`, $f58f44579a4747ac$var$r = document, $f58f44579a4747ac$var$l = ()=>$f58f44579a4747ac$var$r.createComment(""), $f58f44579a4747ac$var$c = (t)=>null === t || "object" != typeof t && "function" != typeof t, $f58f44579a4747ac$var$a = Array.isArray, $f58f44579a4747ac$var$u = (t)=>$f58f44579a4747ac$var$a(t) || "function" == typeof t?.[Symbol.iterator], $f58f44579a4747ac$var$d = "[ \n\f\r]", $f58f44579a4747ac$var$f = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, $f58f44579a4747ac$var$v = /-->/g, $f58f44579a4747ac$var$_ = />/g, $f58f44579a4747ac$var$m = RegExp(`>|${$f58f44579a4747ac$var$d}(?:([^\\s"'>=/]+)(${$f58f44579a4747ac$var$d}*=${$f58f44579a4747ac$var$d}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`, "g"), $f58f44579a4747ac$var$p = /'/g, $f58f44579a4747ac$var$g = /"/g, $f58f44579a4747ac$var$$ = /^(?:script|style|textarea|title)$/i, $f58f44579a4747ac$var$y = (t)=>(i, ...s)=>({ 871 | _$litType$: t, 872 | strings: i, 873 | values: s 874 | }), $f58f44579a4747ac$export$c0bb0b647f701bb5 = $f58f44579a4747ac$var$y(1), $f58f44579a4747ac$export$7ed1367e7fa1ad68 = $f58f44579a4747ac$var$y(2), $f58f44579a4747ac$export$9c068ae9cc5db4e8 = Symbol.for("lit-noChange"), $f58f44579a4747ac$export$45b790e32b2810ee = Symbol.for("lit-nothing"), $f58f44579a4747ac$var$A = new WeakMap, $f58f44579a4747ac$var$E = $f58f44579a4747ac$var$r.createTreeWalker($f58f44579a4747ac$var$r, 129); 875 | function $f58f44579a4747ac$var$C(t, i) { 876 | if (!Array.isArray(t) || !t.hasOwnProperty("raw")) throw Error("invalid template strings array"); 877 | return void 0 !== $f58f44579a4747ac$var$s ? $f58f44579a4747ac$var$s.createHTML(i) : i; 878 | } 879 | const $f58f44579a4747ac$var$P = (t, i)=>{ 880 | const s = t.length - 1, o = []; 881 | let r, l = 2 === i ? "" : "", c = $f58f44579a4747ac$var$f; 882 | for(let i = 0; i < s; i++){ 883 | const s = t[i]; 884 | let a, u, d = -1, y = 0; 885 | for(; y < s.length && (c.lastIndex = y, u = c.exec(s), null !== u);)y = c.lastIndex, c === $f58f44579a4747ac$var$f ? "!--" === u[1] ? c = $f58f44579a4747ac$var$v : void 0 !== u[1] ? c = $f58f44579a4747ac$var$_ : void 0 !== u[2] ? ($f58f44579a4747ac$var$$.test(u[2]) && (r = RegExp("" === u[0] ? (c = r ?? $f58f44579a4747ac$var$f, d = -1) : void 0 === u[1] ? d = -2 : (d = c.lastIndex - u[2].length, a = u[1], c = void 0 === u[3] ? $f58f44579a4747ac$var$m : '"' === u[3] ? $f58f44579a4747ac$var$g : $f58f44579a4747ac$var$p) : c === $f58f44579a4747ac$var$g || c === $f58f44579a4747ac$var$p ? c = $f58f44579a4747ac$var$m : c === $f58f44579a4747ac$var$v || c === $f58f44579a4747ac$var$_ ? c = $f58f44579a4747ac$var$f : (c = $f58f44579a4747ac$var$m, r = void 0); 886 | const x = c === $f58f44579a4747ac$var$m && t[i + 1].startsWith("/>") ? " " : ""; 887 | l += c === $f58f44579a4747ac$var$f ? s + $f58f44579a4747ac$var$n : d >= 0 ? (o.push(a), s.slice(0, d) + $f58f44579a4747ac$var$e + s.slice(d) + $f58f44579a4747ac$var$h + x) : s + $f58f44579a4747ac$var$h + (-2 === d ? i : x); 888 | } 889 | return [ 890 | $f58f44579a4747ac$var$C(t, l + (t[s] || "") + (2 === i ? "" : "")), 891 | o 892 | ]; 893 | }; 894 | class $f58f44579a4747ac$var$V { 895 | constructor({ strings: t, _$litType$: s }, n){ 896 | let r; 897 | this.parts = []; 898 | let c = 0, a = 0; 899 | const u = t.length - 1, d = this.parts, [f, v] = $f58f44579a4747ac$var$P(t, s); 900 | if (this.el = $f58f44579a4747ac$var$V.createElement(f, n), $f58f44579a4747ac$var$E.currentNode = this.el.content, 2 === s) { 901 | const t = this.el.content.firstChild; 902 | t.replaceWith(...t.childNodes); 903 | } 904 | for(; null !== (r = $f58f44579a4747ac$var$E.nextNode()) && d.length < u;){ 905 | if (1 === r.nodeType) { 906 | if (r.hasAttributes()) for (const t of r.getAttributeNames())if (t.endsWith($f58f44579a4747ac$var$e)) { 907 | const i = v[a++], s = r.getAttribute(t).split($f58f44579a4747ac$var$h), e = /([.?@])?(.*)/.exec(i); 908 | d.push({ 909 | type: 1, 910 | index: c, 911 | name: e[2], 912 | strings: s, 913 | ctor: "." === e[1] ? $f58f44579a4747ac$var$k : "?" === e[1] ? $f58f44579a4747ac$var$H : "@" === e[1] ? $f58f44579a4747ac$var$I : $f58f44579a4747ac$var$R 914 | }), r.removeAttribute(t); 915 | } else t.startsWith($f58f44579a4747ac$var$h) && (d.push({ 916 | type: 6, 917 | index: c 918 | }), r.removeAttribute(t)); 919 | if ($f58f44579a4747ac$var$$.test(r.tagName)) { 920 | const t = r.textContent.split($f58f44579a4747ac$var$h), s = t.length - 1; 921 | if (s > 0) { 922 | r.textContent = $f58f44579a4747ac$var$i ? $f58f44579a4747ac$var$i.emptyScript : ""; 923 | for(let i = 0; i < s; i++)r.append(t[i], $f58f44579a4747ac$var$l()), $f58f44579a4747ac$var$E.nextNode(), d.push({ 924 | type: 2, 925 | index: ++c 926 | }); 927 | r.append(t[s], $f58f44579a4747ac$var$l()); 928 | } 929 | } 930 | } else if (8 === r.nodeType) { 931 | if (r.data === $f58f44579a4747ac$var$o) d.push({ 932 | type: 2, 933 | index: c 934 | }); 935 | else { 936 | let t = -1; 937 | for(; -1 !== (t = r.data.indexOf($f58f44579a4747ac$var$h, t + 1));)d.push({ 938 | type: 7, 939 | index: c 940 | }), t += $f58f44579a4747ac$var$h.length - 1; 941 | } 942 | } 943 | c++; 944 | } 945 | } 946 | static createElement(t, i) { 947 | const s = $f58f44579a4747ac$var$r.createElement("template"); 948 | return s.innerHTML = t, s; 949 | } 950 | } 951 | function $f58f44579a4747ac$var$N(t, i, s = t, e) { 952 | if (i === $f58f44579a4747ac$export$9c068ae9cc5db4e8) return i; 953 | let h = void 0 !== e ? s._$Co?.[e] : s._$Cl; 954 | const o = $f58f44579a4747ac$var$c(i) ? void 0 : i._$litDirective$; 955 | return h?.constructor !== o && (h?._$AO?.(!1), void 0 === o ? h = void 0 : (h = new o(t), h._$AT(t, s, e)), void 0 !== e ? (s._$Co ??= [])[e] = h : s._$Cl = h), void 0 !== h && (i = $f58f44579a4747ac$var$N(t, h._$AS(t, i.values), h, e)), i; 956 | } 957 | class $f58f44579a4747ac$var$S { 958 | constructor(t, i){ 959 | this._$AV = [], this._$AN = void 0, this._$AD = t, this._$AM = i; 960 | } 961 | get parentNode() { 962 | return this._$AM.parentNode; 963 | } 964 | get _$AU() { 965 | return this._$AM._$AU; 966 | } 967 | u(t) { 968 | const { el: { content: i }, parts: s } = this._$AD, e = (t?.creationScope ?? $f58f44579a4747ac$var$r).importNode(i, !0); 969 | $f58f44579a4747ac$var$E.currentNode = e; 970 | let h = $f58f44579a4747ac$var$E.nextNode(), o = 0, n = 0, l = s[0]; 971 | for(; void 0 !== l;){ 972 | if (o === l.index) { 973 | let i; 974 | 2 === l.type ? i = new $f58f44579a4747ac$var$M(h, h.nextSibling, this, t) : 1 === l.type ? i = new l.ctor(h, l.name, l.strings, this, t) : 6 === l.type && (i = new $f58f44579a4747ac$var$L(h, this, t)), this._$AV.push(i), l = s[++n]; 975 | } 976 | o !== l?.index && (h = $f58f44579a4747ac$var$E.nextNode(), o++); 977 | } 978 | return $f58f44579a4747ac$var$E.currentNode = $f58f44579a4747ac$var$r, e; 979 | } 980 | p(t) { 981 | let i = 0; 982 | for (const s of this._$AV)void 0 !== s && (void 0 !== s.strings ? (s._$AI(t, s, i), i += s.strings.length - 2) : s._$AI(t[i])), i++; 983 | } 984 | } 985 | class $f58f44579a4747ac$var$M { 986 | get _$AU() { 987 | return this._$AM?._$AU ?? this._$Cv; 988 | } 989 | constructor(t, i, s, e){ 990 | this.type = 2, this._$AH = $f58f44579a4747ac$export$45b790e32b2810ee, this._$AN = void 0, this._$AA = t, this._$AB = i, this._$AM = s, this.options = e, this._$Cv = e?.isConnected ?? !0; 991 | } 992 | get parentNode() { 993 | let t = this._$AA.parentNode; 994 | const i = this._$AM; 995 | return void 0 !== i && 11 === t?.nodeType && (t = i.parentNode), t; 996 | } 997 | get startNode() { 998 | return this._$AA; 999 | } 1000 | get endNode() { 1001 | return this._$AB; 1002 | } 1003 | _$AI(t, i = this) { 1004 | t = $f58f44579a4747ac$var$N(this, t, i), $f58f44579a4747ac$var$c(t) ? t === $f58f44579a4747ac$export$45b790e32b2810ee || null == t || "" === t ? (this._$AH !== $f58f44579a4747ac$export$45b790e32b2810ee && this._$AR(), this._$AH = $f58f44579a4747ac$export$45b790e32b2810ee) : t !== this._$AH && t !== $f58f44579a4747ac$export$9c068ae9cc5db4e8 && this._(t) : void 0 !== t._$litType$ ? this.$(t) : void 0 !== t.nodeType ? this.T(t) : $f58f44579a4747ac$var$u(t) ? this.k(t) : this._(t); 1005 | } 1006 | S(t) { 1007 | return this._$AA.parentNode.insertBefore(t, this._$AB); 1008 | } 1009 | T(t) { 1010 | this._$AH !== t && (this._$AR(), this._$AH = this.S(t)); 1011 | } 1012 | _(t) { 1013 | this._$AH !== $f58f44579a4747ac$export$45b790e32b2810ee && $f58f44579a4747ac$var$c(this._$AH) ? this._$AA.nextSibling.data = t : this.T($f58f44579a4747ac$var$r.createTextNode(t)), this._$AH = t; 1014 | } 1015 | $(t) { 1016 | const { values: i, _$litType$: s } = t, e = "number" == typeof s ? this._$AC(t) : (void 0 === s.el && (s.el = $f58f44579a4747ac$var$V.createElement($f58f44579a4747ac$var$C(s.h, s.h[0]), this.options)), s); 1017 | if (this._$AH?._$AD === e) this._$AH.p(i); 1018 | else { 1019 | const t = new $f58f44579a4747ac$var$S(e, this), s = t.u(this.options); 1020 | t.p(i), this.T(s), this._$AH = t; 1021 | } 1022 | } 1023 | _$AC(t) { 1024 | let i = $f58f44579a4747ac$var$A.get(t.strings); 1025 | return void 0 === i && $f58f44579a4747ac$var$A.set(t.strings, i = new $f58f44579a4747ac$var$V(t)), i; 1026 | } 1027 | k(t) { 1028 | $f58f44579a4747ac$var$a(this._$AH) || (this._$AH = [], this._$AR()); 1029 | const i = this._$AH; 1030 | let s, e = 0; 1031 | for (const h of t)e === i.length ? i.push(s = new $f58f44579a4747ac$var$M(this.S($f58f44579a4747ac$var$l()), this.S($f58f44579a4747ac$var$l()), this, this.options)) : s = i[e], s._$AI(h), e++; 1032 | e < i.length && (this._$AR(s && s._$AB.nextSibling, e), i.length = e); 1033 | } 1034 | _$AR(t = this._$AA.nextSibling, i) { 1035 | for(this._$AP?.(!1, !0, i); t && t !== this._$AB;){ 1036 | const i = t.nextSibling; 1037 | t.remove(), t = i; 1038 | } 1039 | } 1040 | setConnected(t) { 1041 | void 0 === this._$AM && (this._$Cv = t, this._$AP?.(t)); 1042 | } 1043 | } 1044 | class $f58f44579a4747ac$var$R { 1045 | get tagName() { 1046 | return this.element.tagName; 1047 | } 1048 | get _$AU() { 1049 | return this._$AM._$AU; 1050 | } 1051 | constructor(t, i, s, e, h){ 1052 | this.type = 1, this._$AH = $f58f44579a4747ac$export$45b790e32b2810ee, this._$AN = void 0, this.element = t, this.name = i, this._$AM = e, this.options = h, s.length > 2 || "" !== s[0] || "" !== s[1] ? (this._$AH = Array(s.length - 1).fill(new String), this.strings = s) : this._$AH = $f58f44579a4747ac$export$45b790e32b2810ee; 1053 | } 1054 | _$AI(t, i = this, s, e) { 1055 | const h = this.strings; 1056 | let o = !1; 1057 | if (void 0 === h) t = $f58f44579a4747ac$var$N(this, t, i, 0), o = !$f58f44579a4747ac$var$c(t) || t !== this._$AH && t !== $f58f44579a4747ac$export$9c068ae9cc5db4e8, o && (this._$AH = t); 1058 | else { 1059 | const e = t; 1060 | let n, r; 1061 | for(t = h[0], n = 0; n < h.length - 1; n++)r = $f58f44579a4747ac$var$N(this, e[s + n], i, n), r === $f58f44579a4747ac$export$9c068ae9cc5db4e8 && (r = this._$AH[n]), o ||= !$f58f44579a4747ac$var$c(r) || r !== this._$AH[n], r === $f58f44579a4747ac$export$45b790e32b2810ee ? t = $f58f44579a4747ac$export$45b790e32b2810ee : t !== $f58f44579a4747ac$export$45b790e32b2810ee && (t += (r ?? "") + h[n + 1]), this._$AH[n] = r; 1062 | } 1063 | o && !e && this.j(t); 1064 | } 1065 | j(t) { 1066 | t === $f58f44579a4747ac$export$45b790e32b2810ee ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t ?? ""); 1067 | } 1068 | } 1069 | class $f58f44579a4747ac$var$k extends $f58f44579a4747ac$var$R { 1070 | constructor(){ 1071 | super(...arguments), this.type = 3; 1072 | } 1073 | j(t) { 1074 | this.element[this.name] = t === $f58f44579a4747ac$export$45b790e32b2810ee ? void 0 : t; 1075 | } 1076 | } 1077 | class $f58f44579a4747ac$var$H extends $f58f44579a4747ac$var$R { 1078 | constructor(){ 1079 | super(...arguments), this.type = 4; 1080 | } 1081 | j(t) { 1082 | this.element.toggleAttribute(this.name, !!t && t !== $f58f44579a4747ac$export$45b790e32b2810ee); 1083 | } 1084 | } 1085 | class $f58f44579a4747ac$var$I extends $f58f44579a4747ac$var$R { 1086 | constructor(t, i, s, e, h){ 1087 | super(t, i, s, e, h), this.type = 5; 1088 | } 1089 | _$AI(t, i = this) { 1090 | if ((t = $f58f44579a4747ac$var$N(this, t, i, 0) ?? $f58f44579a4747ac$export$45b790e32b2810ee) === $f58f44579a4747ac$export$9c068ae9cc5db4e8) return; 1091 | const s = this._$AH, e = t === $f58f44579a4747ac$export$45b790e32b2810ee && s !== $f58f44579a4747ac$export$45b790e32b2810ee || t.capture !== s.capture || t.once !== s.once || t.passive !== s.passive, h = t !== $f58f44579a4747ac$export$45b790e32b2810ee && (s === $f58f44579a4747ac$export$45b790e32b2810ee || e); 1092 | e && this.element.removeEventListener(this.name, this, s), h && this.element.addEventListener(this.name, this, t), this._$AH = t; 1093 | } 1094 | handleEvent(t) { 1095 | "function" == typeof this._$AH ? this._$AH.call(this.options?.host ?? this.element, t) : this._$AH.handleEvent(t); 1096 | } 1097 | } 1098 | class $f58f44579a4747ac$var$L { 1099 | constructor(t, i, s){ 1100 | this.element = t, this.type = 6, this._$AN = void 0, this._$AM = i, this.options = s; 1101 | } 1102 | get _$AU() { 1103 | return this._$AM._$AU; 1104 | } 1105 | _$AI(t) { 1106 | $f58f44579a4747ac$var$N(this, t); 1107 | } 1108 | } 1109 | const $f58f44579a4747ac$export$8613d1ca9052b22e = { 1110 | P: $f58f44579a4747ac$var$e, 1111 | A: $f58f44579a4747ac$var$h, 1112 | C: $f58f44579a4747ac$var$o, 1113 | M: 1, 1114 | L: $f58f44579a4747ac$var$P, 1115 | R: $f58f44579a4747ac$var$S, 1116 | D: $f58f44579a4747ac$var$u, 1117 | V: $f58f44579a4747ac$var$N, 1118 | I: $f58f44579a4747ac$var$M, 1119 | H: $f58f44579a4747ac$var$R, 1120 | N: $f58f44579a4747ac$var$H, 1121 | U: $f58f44579a4747ac$var$I, 1122 | B: $f58f44579a4747ac$var$k, 1123 | F: $f58f44579a4747ac$var$L 1124 | }, $f58f44579a4747ac$var$Z = $f58f44579a4747ac$var$t.litHtmlPolyfillSupport; 1125 | $f58f44579a4747ac$var$Z?.($f58f44579a4747ac$var$V, $f58f44579a4747ac$var$M), ($f58f44579a4747ac$var$t.litHtmlVersions ??= []).push("3.1.2"); 1126 | const $f58f44579a4747ac$export$b3890eb0ae9dca99 = (t, i, s)=>{ 1127 | const e = s?.renderBefore ?? i; 1128 | let h = e._$litPart$; 1129 | if (void 0 === h) { 1130 | const t = s?.renderBefore ?? null; 1131 | e._$litPart$ = h = new $f58f44579a4747ac$var$M(i.insertBefore($f58f44579a4747ac$var$l(), t), t, void 0, s ?? {}); 1132 | } 1133 | return h._$AI(t), h; 1134 | }; 1135 | 1136 | }); 1137 | 1138 | parcelRegister("eGUNk", function(module, exports) { 1139 | $parcel$export(module.exports, "css", () => (parcelRequire("j8KxL")).css); 1140 | $parcel$export(module.exports, "ReactiveElement", () => (parcelRequire("2emM7")).ReactiveElement); 1141 | $parcel$export(module.exports, "html", () => (parcelRequire("l56HR")).html); 1142 | $parcel$export(module.exports, "noChange", () => (parcelRequire("l56HR")).noChange); 1143 | $parcel$export(module.exports, "nothing", () => (parcelRequire("l56HR")).nothing); 1144 | $parcel$export(module.exports, "render", () => (parcelRequire("l56HR")).render); 1145 | 1146 | $parcel$export(module.exports, "LitElement", () => $ab210b2da7b39b9d$export$3f2f9f5909897157); 1147 | 1148 | var $2emM7 = parcelRequire("2emM7"); 1149 | 1150 | var $l56HR = parcelRequire("l56HR"); 1151 | /** 1152 | * @license 1153 | * Copyright 2017 Google LLC 1154 | * SPDX-License-Identifier: BSD-3-Clause 1155 | */ class $ab210b2da7b39b9d$export$3f2f9f5909897157 extends (0, $2emM7.ReactiveElement) { 1156 | constructor(){ 1157 | super(...arguments), this.renderOptions = { 1158 | host: this 1159 | }, this._$Do = void 0; 1160 | } 1161 | createRenderRoot() { 1162 | const t = super.createRenderRoot(); 1163 | return this.renderOptions.renderBefore ??= t.firstChild, t; 1164 | } 1165 | update(t) { 1166 | const i = this.render(); 1167 | this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t), this._$Do = (0, $l56HR.render)(i, this.renderRoot, this.renderOptions); 1168 | } 1169 | connectedCallback() { 1170 | super.connectedCallback(), this._$Do?.setConnected(!0); 1171 | } 1172 | disconnectedCallback() { 1173 | super.disconnectedCallback(), this._$Do?.setConnected(!1); 1174 | } 1175 | render() { 1176 | return 0, $l56HR.noChange; 1177 | } 1178 | } 1179 | $ab210b2da7b39b9d$export$3f2f9f5909897157._$litElement$ = !0, $ab210b2da7b39b9d$export$3f2f9f5909897157["finalized"] = !0, globalThis.litElementHydrateSupport?.({ 1180 | LitElement: $ab210b2da7b39b9d$export$3f2f9f5909897157 1181 | }); 1182 | const $ab210b2da7b39b9d$var$r = globalThis.litElementPolyfillSupport; 1183 | $ab210b2da7b39b9d$var$r?.({ 1184 | LitElement: $ab210b2da7b39b9d$export$3f2f9f5909897157 1185 | }); 1186 | const $ab210b2da7b39b9d$export$f5c524615a7708d6 = { 1187 | _$AK: (t, e, i)=>{ 1188 | t._$AK(e, i); 1189 | }, 1190 | _$AL: (t)=>t._$AL 1191 | }; 1192 | (globalThis.litElementVersions ??= []).push("4.0.4"); 1193 | 1194 | }); 1195 | 1196 | parcelRegister("dJV7N", function(module, exports) { 1197 | /** 1198 | * @license 1199 | * Copyright 2022 Google LLC 1200 | * SPDX-License-Identifier: BSD-3-Clause 1201 | */ const $a00bca1a101a9088$export$6acf61af03e62db = !1; 1202 | 1203 | }); 1204 | 1205 | 1206 | parcelRegister("1ZxoT", function(module, exports) { 1207 | $parcel$export(module.exports, "customElement", () => (parcelRequire("1KSgR")).customElement); 1208 | $parcel$export(module.exports, "property", () => (parcelRequire("dsTCw")).property); 1209 | $parcel$export(module.exports, "state", () => (parcelRequire("pklEb")).state); 1210 | parcelRequire("1KSgR"); 1211 | parcelRequire("dsTCw"); 1212 | parcelRequire("pklEb"); 1213 | parcelRequire("fsW8U"); 1214 | parcelRequire("e0PuK"); 1215 | parcelRequire("kmDQA"); 1216 | parcelRequire("k7g5J"); 1217 | parcelRequire("7p6n6"); 1218 | parcelRequire("9PNfg"); 1219 | 1220 | }); 1221 | parcelRegister("1KSgR", function(module, exports) { 1222 | 1223 | $parcel$export(module.exports, "customElement", () => $14742f68afc766d6$export$da64fc29f17f9d0e); 1224 | /** 1225 | * @license 1226 | * Copyright 2017 Google LLC 1227 | * SPDX-License-Identifier: BSD-3-Clause 1228 | */ const $14742f68afc766d6$export$da64fc29f17f9d0e = (t)=>(e, o)=>{ 1229 | void 0 !== o ? o.addInitializer(()=>{ 1230 | customElements.define(t, e); 1231 | }) : customElements.define(t, e); 1232 | }; 1233 | 1234 | }); 1235 | 1236 | parcelRegister("dsTCw", function(module, exports) { 1237 | 1238 | $parcel$export(module.exports, "property", () => $9cd908ed2625c047$export$d541bacb2bda4494); 1239 | 1240 | var $2emM7 = parcelRequire("2emM7"); 1241 | /** 1242 | * @license 1243 | * Copyright 2017 Google LLC 1244 | * SPDX-License-Identifier: BSD-3-Clause 1245 | */ const $9cd908ed2625c047$var$o = { 1246 | attribute: !0, 1247 | type: String, 1248 | converter: (0, $2emM7.defaultConverter), 1249 | reflect: !1, 1250 | hasChanged: (0, $2emM7.notEqual) 1251 | }, $9cd908ed2625c047$export$8d623b1670eb40f4 = (t = $9cd908ed2625c047$var$o, e, r)=>{ 1252 | const { kind: n, metadata: i } = r; 1253 | let s = globalThis.litPropertyMetadata.get(i); 1254 | if (void 0 === s && globalThis.litPropertyMetadata.set(i, s = new Map), s.set(r.name, t), "accessor" === n) { 1255 | const { name: o } = r; 1256 | return { 1257 | set (r) { 1258 | const n = e.get.call(this); 1259 | e.set.call(this, r), this.requestUpdate(o, n, t); 1260 | }, 1261 | init (e) { 1262 | return void 0 !== e && this.P(o, void 0, t), e; 1263 | } 1264 | }; 1265 | } 1266 | if ("setter" === n) { 1267 | const { name: o } = r; 1268 | return function(r) { 1269 | const n = this[o]; 1270 | e.call(this, r), this.requestUpdate(o, n, t); 1271 | }; 1272 | } 1273 | throw Error("Unsupported decorator location: " + n); 1274 | }; 1275 | function $9cd908ed2625c047$export$d541bacb2bda4494(t) { 1276 | return (e, o)=>"object" == typeof o ? $9cd908ed2625c047$export$8d623b1670eb40f4(t, e, o) : ((t, e, o)=>{ 1277 | const r = e.hasOwnProperty(o); 1278 | return e.constructor.createProperty(o, r ? { 1279 | ...t, 1280 | wrapped: !0 1281 | } : t), r ? Object.getOwnPropertyDescriptor(e, o) : void 0; 1282 | })(t, e, o); 1283 | } 1284 | 1285 | }); 1286 | 1287 | parcelRegister("pklEb", function(module, exports) { 1288 | 1289 | $parcel$export(module.exports, "state", () => $04c21ea1ce1f6057$export$ca000e230c0caa3e); 1290 | 1291 | var $dsTCw = parcelRequire("dsTCw"); 1292 | /** 1293 | * @license 1294 | * Copyright 2017 Google LLC 1295 | * SPDX-License-Identifier: BSD-3-Clause 1296 | */ function $04c21ea1ce1f6057$export$ca000e230c0caa3e(r) { 1297 | return (0, $dsTCw.property)({ 1298 | ...r, 1299 | state: !0, 1300 | attribute: !1 1301 | }); 1302 | } 1303 | 1304 | }); 1305 | 1306 | parcelRegister("fsW8U", function(module, exports) { 1307 | /** 1308 | * @license 1309 | * Copyright 2017 Google LLC 1310 | * SPDX-License-Identifier: BSD-3-Clause 1311 | */ function $b4269277b3c48b0c$export$b2b799818fbabcf3(t) { 1312 | return (n, o)=>{ 1313 | const c = "function" == typeof n ? n : n[o]; 1314 | Object.assign(c, t); 1315 | }; 1316 | } 1317 | 1318 | }); 1319 | 1320 | parcelRegister("e0PuK", function(module, exports) { 1321 | 1322 | var $3fOhc = parcelRequire("3fOhc"); 1323 | /** 1324 | * @license 1325 | * Copyright 2017 Google LLC 1326 | * SPDX-License-Identifier: BSD-3-Clause 1327 | */ function $02a1f3a787c54a30$export$2fa187e846a241c4(e, r) { 1328 | return (n, s, i)=>{ 1329 | const o = (t)=>t.renderRoot?.querySelector(e) ?? null; 1330 | if (r) { 1331 | const { get: e, set: r } = "object" == typeof s ? n : i ?? (()=>{ 1332 | const t = Symbol(); 1333 | return { 1334 | get () { 1335 | return this[t]; 1336 | }, 1337 | set (e) { 1338 | this[t] = e; 1339 | } 1340 | }; 1341 | })(); 1342 | return (0, $3fOhc.desc)(n, s, { 1343 | get () { 1344 | let t = e.call(this); 1345 | return void 0 === t && (t = o(this), (null !== t || this.hasUpdated) && r.call(this, t)), t; 1346 | } 1347 | }); 1348 | } 1349 | return (0, $3fOhc.desc)(n, s, { 1350 | get () { 1351 | return o(this); 1352 | } 1353 | }); 1354 | }; 1355 | } 1356 | 1357 | }); 1358 | parcelRegister("3fOhc", function(module, exports) { 1359 | 1360 | $parcel$export(module.exports, "desc", () => $25e9c5a8f7ecfc69$export$51987bb50e1f6752); 1361 | /** 1362 | * @license 1363 | * Copyright 2017 Google LLC 1364 | * SPDX-License-Identifier: BSD-3-Clause 1365 | */ const $25e9c5a8f7ecfc69$export$51987bb50e1f6752 = (e, t, c)=>(c.configurable = !0, c.enumerable = !0, Reflect.decorate && "object" != typeof t && Object.defineProperty(e, t, c), c); 1366 | 1367 | }); 1368 | 1369 | 1370 | parcelRegister("kmDQA", function(module, exports) { 1371 | 1372 | var $3fOhc = parcelRequire("3fOhc"); 1373 | /** 1374 | * @license 1375 | * Copyright 2017 Google LLC 1376 | * SPDX-License-Identifier: BSD-3-Clause 1377 | */ let $ed34c589b230c255$var$e; 1378 | function $ed34c589b230c255$export$dcd0d083aa86c355(r) { 1379 | return (n, o)=>(0, $3fOhc.desc)(n, o, { 1380 | get () { 1381 | return (this.renderRoot ?? ($ed34c589b230c255$var$e ??= document.createDocumentFragment())).querySelectorAll(r); 1382 | } 1383 | }); 1384 | } 1385 | 1386 | }); 1387 | 1388 | parcelRegister("k7g5J", function(module, exports) { 1389 | 1390 | var $3fOhc = parcelRequire("3fOhc"); 1391 | /** 1392 | * @license 1393 | * Copyright 2017 Google LLC 1394 | * SPDX-License-Identifier: BSD-3-Clause 1395 | */ function $ea50f1870b80cbec$export$163dfc35cc43f240(r) { 1396 | return (n, e)=>(0, $3fOhc.desc)(n, e, { 1397 | async get () { 1398 | return await this.updateComplete, this.renderRoot?.querySelector(r) ?? null; 1399 | } 1400 | }); 1401 | } 1402 | 1403 | }); 1404 | 1405 | parcelRegister("7p6n6", function(module, exports) { 1406 | 1407 | var $3fOhc = parcelRequire("3fOhc"); 1408 | /** 1409 | * @license 1410 | * Copyright 2021 Google LLC 1411 | * SPDX-License-Identifier: BSD-3-Clause 1412 | */ function $563fcf7ce7e6c5aa$export$4682af2d9ee91415(o) { 1413 | return (e, n)=>{ 1414 | const { slot: r, selector: s } = o ?? {}, c = "slot" + (r ? `[name=${r}]` : ":not([name])"); 1415 | return (0, $3fOhc.desc)(e, n, { 1416 | get () { 1417 | const t = this.renderRoot?.querySelector(c), e = t?.assignedElements(o) ?? []; 1418 | return void 0 === s ? e : e.filter((t)=>t.matches(s)); 1419 | } 1420 | }); 1421 | }; 1422 | } 1423 | 1424 | }); 1425 | 1426 | parcelRegister("9PNfg", function(module, exports) { 1427 | 1428 | var $3fOhc = parcelRequire("3fOhc"); 1429 | /** 1430 | * @license 1431 | * Copyright 2017 Google LLC 1432 | * SPDX-License-Identifier: BSD-3-Clause 1433 | */ function $728f1385dd7bf557$export$1bdbe53f9df1b8(n) { 1434 | return (o, r)=>{ 1435 | const { slot: e } = n ?? {}, s = "slot" + (e ? `[name=${e}]` : ":not([name])"); 1436 | return (0, $3fOhc.desc)(o, r, { 1437 | get () { 1438 | const t = this.renderRoot?.querySelector(s); 1439 | return t?.assignedNodes(n) ?? []; 1440 | } 1441 | }); 1442 | }; 1443 | } 1444 | 1445 | }); 1446 | 1447 | 1448 | parcelRegister("gjUL4", function(module, exports) { 1449 | 1450 | $parcel$export(module.exports, "getLanguage", () => $8ae640dd6c4226ad$export$64783e31db14f0ba); 1451 | $parcel$export(module.exports, "translateTo", () => $8ae640dd6c4226ad$export$df5de7d5c552d075); 1452 | const $8ae640dd6c4226ad$var$defaultLang = `en-US`; 1453 | const $8ae640dd6c4226ad$var$defaultTranslation = { 1454 | entity_missing: "Entity data missing", 1455 | line: "Line", 1456 | destination: "Destination", 1457 | departure: "Departure", 1458 | min: "min", 1459 | last_updated: "Last updated", 1460 | now: "Now", 1461 | // configuration translations 1462 | editor_show_name: "Show card name", 1463 | editor_entities: "Entities", 1464 | editor_departures: "Departures", 1465 | editor_title: "Card name", 1466 | editor_show_entity_name: "Show entity name", 1467 | editor_show_departures: "Show departures", 1468 | editor_show_header: "Show departure header", 1469 | editor_show_icon: "Show transport icon", 1470 | editor_show_transport_icon: "Show transport icon", 1471 | editor_max_departures: "Maximum departures to show", 1472 | editor_hide_departed: "Hide already departed", 1473 | editor_show_departed_offeset: "... but show departed number of minutes ago", 1474 | editor_show_time_always: "Always show departure time in HH:MM form", 1475 | editor_hide_line_number: "Hide line number", 1476 | editor_show_updated: `Show 'Last Updated'`, 1477 | editor_direction: `Direction filter`, 1478 | editor_direction_all: `All`, 1479 | editor_direction_left: `Left`, 1480 | editor_direction_right: `Right`, 1481 | language: "Language" 1482 | }; 1483 | const $8ae640dd6c4226ad$export$150b732325d14d04 = { 1484 | [$8ae640dd6c4226ad$var$defaultLang]: $8ae640dd6c4226ad$var$defaultTranslation, 1485 | "sv-SE": { 1486 | entity_missing: "Ingen data hittades", 1487 | line: "Linje", 1488 | destination: "Till", 1489 | departure: "Avresa", 1490 | min: "min", 1491 | last_updated: "Senast uppdaterad", 1492 | now: "Nu", 1493 | editor_show_name: "Visa kortnamn", 1494 | editor_entities: "Enheter", 1495 | editor_departures: "Avg\xe5ngar", 1496 | editor_title: "Kortnamn", 1497 | editor_show_entity_name: "Visa enhetsnamn", 1498 | editor_show_departures: "Visa avg\xe5ngar", 1499 | editor_show_header: "Visa avg\xe5ngshuvud", 1500 | editor_show_icon: "Visa transportikon", 1501 | editor_show_transport_icon: "Visa transportikon", 1502 | editor_max_departures: "Max antal avg\xe5ngar", 1503 | editor_hide_departed: "D\xf6lj redan avg\xe5ngna", 1504 | editor_show_departed_offeset: "... men visa avg\xe5ngna f\xf6r antal minuter sedan", 1505 | editor_show_time_always: "Visa alltid avg\xe5ngstid i HH:MM-form", 1506 | editor_hide_line_number: "D\xf6lj linjenummer", 1507 | editor_show_updated: `Visa 'Senast uppdaterad'`, 1508 | editor_direction: `Riktning filter`, 1509 | editor_direction_all: `Alla`, 1510 | editor_direction_left: `V\xe4nster`, 1511 | editor_direction_right: `H\xf6ger`, 1512 | language: "Spr\xe5k" 1513 | }, 1514 | "fr-FR": { 1515 | entity_missing: "Aucune info trouvée", 1516 | line: "Ligne", 1517 | destination: "Terminus", 1518 | departure: "Départ", 1519 | min: "min", 1520 | last_updated: "Mis \xe0 jour", 1521 | now: "Maintenant", 1522 | editor_show_name: "Afficher le nom de la carte", 1523 | editor_entities: "Entit\xe9s", 1524 | editor_departures: "D\xe9parts", 1525 | editor_title: "Nom de la carte", 1526 | editor_show_entity_name: "Afficher le nom de l'entit\xe9", 1527 | editor_show_departures: "Afficher les d\xe9parts", 1528 | editor_show_header: "Afficher l'ent\xeate des d\xe9parts", 1529 | editor_show_icon: "Afficher l'ic\xf4ne de transport", 1530 | editor_show_transport_icon: "Afficher l'ic\xf4ne de transport", 1531 | editor_max_departures: "Nombre maximum de d\xe9parts", 1532 | editor_hide_departed: "Masquer les d\xe9parts pass\xe9s", 1533 | editor_show_departed_offeset: "... mais montrer les d\xe9parts depuis le nombre de minutes", 1534 | editor_show_time_always: "Toujours afficher l'heure de d\xe9part en HH:MM", 1535 | editor_hide_line_number: "Masquer le num\xe9ro de ligne", 1536 | editor_show_updated: `Afficher 'Mis \xe0 jour'`, 1537 | editor_direction: `Filtre de direction`, 1538 | editor_direction_all: `Tous`, 1539 | editor_direction_left: `Gauche`, 1540 | editor_direction_right: `Droite`, 1541 | language: "Langue" 1542 | } 1543 | }; 1544 | const $8ae640dd6c4226ad$export$d0d68bb9ed2c643d = Object.keys($8ae640dd6c4226ad$export$150b732325d14d04); 1545 | const $8ae640dd6c4226ad$export$625550452a3fa3ec = (key, lang)=>$8ae640dd6c4226ad$export$150b732325d14d04[lang]?.[key] ?? $8ae640dd6c4226ad$var$defaultTranslation[key]; 1546 | const $8ae640dd6c4226ad$export$64783e31db14f0ba = (configLang)=>configLang ?? navigator.language ?? $8ae640dd6c4226ad$var$defaultLang; 1547 | const $8ae640dd6c4226ad$export$df5de7d5c552d075 = (lang)=>(key)=>$8ae640dd6c4226ad$export$625550452a3fa3ec(key, lang); 1548 | 1549 | }); 1550 | 1551 | parcelRegister("3gXF4", function(module, exports) { 1552 | module.exports = import("./hasl4-departure-card-editor.js").then(()=>parcelRequire("jlj1D")); 1553 | 1554 | }); 1555 | 1556 | 1557 | var $39J5i = parcelRequire("39J5i"); 1558 | parcelRequire("j0ZcV"); 1559 | var $l56HR = parcelRequire("l56HR"); 1560 | var $eGUNk = parcelRequire("eGUNk"); 1561 | parcelRequire("1ZxoT"); 1562 | var $dsTCw = parcelRequire("dsTCw"); 1563 | var $pklEb = parcelRequire("pklEb"); 1564 | var $829f1babd4ccc0b8$export$6d07abd9f0bba447; 1565 | (function(TransportType) { 1566 | TransportType["METRO"] = "METRO"; 1567 | TransportType["BUS"] = "BUS"; 1568 | TransportType["TRAM"] = "TRAM"; 1569 | TransportType["TRAIN"] = "TRAIN"; 1570 | TransportType["SHIP"] = "SHIP"; 1571 | TransportType["FERRY"] = "FETTRY"; 1572 | TransportType["TAXI"] = "TAXI"; 1573 | })($829f1babd4ccc0b8$export$6d07abd9f0bba447 || ($829f1babd4ccc0b8$export$6d07abd9f0bba447 = {})); 1574 | 1575 | 1576 | 1577 | var $gjUL4 = parcelRequire("gjUL4"); 1578 | const $b0717bc2acc03fc5$export$c2f8e0cc249a8d8f = { 1579 | title: "", 1580 | entities: [], 1581 | show_entity_name: true, 1582 | show_header: true, 1583 | show_icon: true, 1584 | show_departures: true, 1585 | direction: 0, 1586 | max_departures: 5, 1587 | hide_departed: true, 1588 | show_departed_offeset: 5, 1589 | show_updated: true, 1590 | tap_action: "info" 1591 | }; 1592 | 1593 | 1594 | parcelRequire("j0ZcV"); 1595 | var $j8KxL = parcelRequire("j8KxL"); 1596 | const $57faf62096e30446$var$lineColorsStyles = (0, $j8KxL.css)` 1597 | .line-icon { 1598 | border-radius: 3px; 1599 | padding: 3px 3px 0 3px; 1600 | color: #fff; 1601 | min-width: 22px; 1602 | height: 22px; 1603 | font-weight: 500; 1604 | display: inline-block; 1605 | text-align: center; 1606 | text-shadow: 1px 1px 2px var(--outline-color); 1607 | } 1608 | 1609 | .bus { 1610 | border: 1px solid var(--outline-color); 1611 | color: var(--primary-text-color); 1612 | } 1613 | 1614 | .red { 1615 | background-color: #d71d24; 1616 | } 1617 | .blue { 1618 | background-color: #0089ca; 1619 | } 1620 | .green { 1621 | background-color: #179d4d; 1622 | } 1623 | 1624 | .train { 1625 | background-color: #ec619f; 1626 | } 1627 | 1628 | .tram { 1629 | background-color: #985141; 1630 | } 1631 | 1632 | .tram_7 { 1633 | background-color: #878a83; 1634 | } 1635 | 1636 | .tram_12 { 1637 | background-color: #778da7; 1638 | } 1639 | 1640 | .tram_21 { 1641 | background-color: #b76020; 1642 | } 1643 | 1644 | .tram_22 { 1645 | background-color: #d77d00; 1646 | } 1647 | `; 1648 | const $57faf62096e30446$var$departureEntityStyles = (0, $j8KxL.css)` 1649 | .card-header .name { 1650 | white-space: nowrap; 1651 | overflow: hidden; 1652 | text-overflow: ellipsis; 1653 | } 1654 | 1655 | .departures > :first-child { 1656 | margin-top: 0; 1657 | } 1658 | 1659 | .departure.departed { 1660 | color: var(--secondary-text-color); 1661 | } 1662 | 1663 | .departure.departed > .main { 1664 | text-decoration: line-through; 1665 | } 1666 | 1667 | .row { 1668 | margin-top: 8px; 1669 | 1670 | display: flex; 1671 | justify-content: space-between; 1672 | } 1673 | 1674 | .col { 1675 | display: flex; 1676 | flex-direction: column; 1677 | justify-content: center; 1678 | position: relative; 1679 | } 1680 | 1681 | .col.icon { 1682 | flex-basis: 40px; 1683 | } 1684 | 1685 | .row.name { 1686 | height: 40px; 1687 | padding-left: 8px; 1688 | font-weight: 400; 1689 | font-size: large; 1690 | align-items: center; 1691 | justify-content: center; 1692 | } 1693 | .row.header { 1694 | height: 40px; 1695 | font-size: medium; 1696 | font-weight: 400; 1697 | font-family: var(--paper-font-headline_-_font-family); 1698 | letter-spacing: var(--paper-font-headline_-_letter-spacing); 1699 | line-height: var(--paper-font-headline_-_line-height); 1700 | text-rendering: var(--paper-font-common-expensive-kerning_-_text-rendering); 1701 | opacity: var(--dark-primary-opacity); 1702 | } 1703 | 1704 | .main { 1705 | flex: 2; 1706 | } 1707 | 1708 | .transport-icon { 1709 | width: 40px; 1710 | height: 40px; 1711 | display: inline-flex; 1712 | justify-content: center; 1713 | align-items: center; 1714 | } 1715 | 1716 | .warning { 1717 | color: var(--warning-color); 1718 | position: absolute; 1719 | bottom: 0; 1720 | right: 0; 1721 | } 1722 | 1723 | .warning-message { 1724 | color: var(--warning-color); 1725 | font-size: smaller; 1726 | text-decoration: unset; 1727 | } 1728 | 1729 | .mr1 { 1730 | margin-right: 8px; 1731 | } 1732 | 1733 | .updated { 1734 | padding-left: 16px; 1735 | padding-top: 8px; 1736 | font-size: smaller; 1737 | } 1738 | 1739 | .center { text-align: center; } 1740 | .left { text-align: left; } 1741 | .right { text-align: right; } 1742 | 1743 | ha-icon { 1744 | transition: color 0.3s ease-in-out, filter 0.3s ease-in-out; 1745 | width: 24px; 1746 | height: 24px; 1747 | color: var(--paper-item-icon-color); 1748 | } 1749 | `; 1750 | var $57faf62096e30446$export$2e2bcd8739ae039 = [ 1751 | $57faf62096e30446$var$departureEntityStyles, 1752 | $57faf62096e30446$var$lineColorsStyles 1753 | ]; 1754 | 1755 | 1756 | const $66d5822390d71e6e$var$diffMinutes = (from, to)=>{ 1757 | const diffMinutes = Math.ceil((from.getTime() - to.getTime()) / 1000 / 60); 1758 | return diffMinutes; 1759 | }; 1760 | 1761 | class $66d5822390d71e6e$export$7ded24e6705f9c64 extends (0, $eGUNk.LitElement) { 1762 | static{ 1763 | this.styles = (0, $57faf62096e30446$export$2e2bcd8739ae039); 1764 | } 1765 | setConfig(config) { 1766 | this.config = { 1767 | ...(0, $b0717bc2acc03fc5$export$c2f8e0cc249a8d8f), 1768 | ...config 1769 | }; 1770 | } 1771 | getCardSize() { 1772 | const singleEntitityExtras = (this.isManyEntitiesSet() ? ()=>0 : ()=>{ 1773 | const [_, attrs] = this.getFirstEntity(); 1774 | if (!attrs) return 0; 1775 | return this.config.show_entity_name && attrs.friendly_name ? 1 : 0; 1776 | })(); 1777 | const deps = this.getDepartures(); 1778 | const size = [ 1779 | !!this.config.title ? 1 : 0, 1780 | singleEntitityExtras, 1781 | !!this.config.show_header ? 1 : 0, 1782 | deps?.length || 0 1783 | ].reduce((sum, entity)=>sum += entity ? entity : 0, 0); 1784 | return Math.max(size, 1); 1785 | } 1786 | getLayoutOptions() { 1787 | return { 1788 | grid_min_columns: 3, 1789 | grid_min_rows: 2 1790 | }; 1791 | } 1792 | // configuration card is loaded in async manner 1793 | static async getConfigElement() { 1794 | return await (parcelRequire("3gXF4")).then(()=>document.createElement("hasl4-departure-card-editor")); 1795 | } 1796 | static{ 1797 | this.getStubConfig = ()=>({ 1798 | ...(0, $b0717bc2acc03fc5$export$c2f8e0cc249a8d8f) 1799 | }); 1800 | } 1801 | render() { 1802 | if (!this.config || !this.hass) return 0, $l56HR.nothing; 1803 | const lang = (0, $gjUL4.getLanguage)(this.config?.language); 1804 | const _ = (0, $gjUL4.translateTo)(lang); 1805 | const departures = this.config?.show_departures ? ()=>{ 1806 | const data = this.renderDepartures(); 1807 | return data === (0, $l56HR.nothing) ? (0, $l56HR.html)`${_(`entity_missing`)}` : data; 1808 | } : ()=>(0, $l56HR.nothing); 1809 | const renderLastUpdated = this.isManyEntitiesSet() ? ()=>(0, $l56HR.nothing) : ()=>{ 1810 | const [data, __] = this.getFirstEntity(); 1811 | if (!data) return 0, $l56HR.nothing; 1812 | return this.config?.show_updated && data.last_updated ? (0, $l56HR.html)` 1813 |
1814 | ${_("last_updated")} 1815 | ${new Date(data.last_updated).toLocaleTimeString(lang)} 1816 |
` : (0, $l56HR.nothing); 1817 | }; 1818 | return (0, $l56HR.html)` 1819 | 1820 | ${this.config?.title ? (0, $l56HR.html)`

${this.config.title}

` : (0, $l56HR.nothing)} 1821 |
1822 | ${departures()} 1823 | ${renderLastUpdated()} 1824 |
1825 |
1826 | `; 1827 | } 1828 | getFirstEntity() { 1829 | const data = this.hass?.states[this.config?.entities?.[0] || this.config?.entity]; 1830 | const attrs = data?.attributes; 1831 | if (data && attrs && $66d5822390d71e6e$var$isDepartureAttrs(attrs)) return [ 1832 | data, 1833 | attrs 1834 | ]; 1835 | return [ 1836 | undefined, 1837 | undefined 1838 | ]; 1839 | } 1840 | getDeparturesFor(attrs) { 1841 | if (!attrs) return []; 1842 | const now = new Date(); 1843 | return (attrs.departures?.filter((d)=>{ 1844 | if (this.config?.direction === 0) return true; 1845 | return d.direction_code === this.config?.direction; 1846 | }).filter((d)=>{ 1847 | if (!this.config?.hide_departed) return true; 1848 | const diff = $66d5822390d71e6e$var$diffMinutes(new Date(d.expected), now); 1849 | return diff + this.config?.show_departed_offeset >= 0; 1850 | }) || []).slice(0, this.config?.max_departures); 1851 | } 1852 | getDeparturesCombined(entities) { 1853 | const now = new Date(); 1854 | return entities// filter invalid entities 1855 | .filter((entity)=>{ 1856 | if (!!entity === false) return false; 1857 | const data = this.hass?.states[entity]; 1858 | if (data === undefined) return false; 1859 | if (!$66d5822390d71e6e$var$isDepartureAttrs(data.attributes)) return false; 1860 | return true; 1861 | })// map entity name to departures and gather all together 1862 | .map((entity)=>{ 1863 | const state = this.hass?.states[entity]; 1864 | if ($66d5822390d71e6e$var$isDepartureAttrs(state.attributes)) return state.attributes; 1865 | }).flatMap((attrs)=>attrs.departures)// filter by departure time 1866 | .filter((d)=>{ 1867 | if (!this.config?.hide_departed) return true; 1868 | const diff = $66d5822390d71e6e$var$diffMinutes(new Date(d.expected), now); 1869 | return diff + this.config?.show_departed_offeset >= 0; 1870 | })// filter direction 1871 | .filter((d)=>{ 1872 | if (this.config?.direction === 0) return true; 1873 | return d.direction_code === this.config?.direction; 1874 | })// sort by expected departure time 1875 | .sort((a, b)=>new Date(a.expected).getTime() - new Date(b.expected).getTime())// limit to max departures 1876 | .slice(0, this.config?.max_departures); 1877 | } 1878 | getDepartures() { 1879 | if (this.isManyEntitiesSet()) return this.getDeparturesCombined(this.config?.entities || []); 1880 | const [_, attrs] = this.getFirstEntity(); 1881 | if (!attrs) return undefined; 1882 | return this.getDeparturesFor(attrs); 1883 | } 1884 | lineIconClass(type, line, group) { 1885 | let cls = ""; 1886 | switch(type){ 1887 | case (0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).BUS: 1888 | cls = `bus bus_${line}`; 1889 | cls = group === "bl\xe5buss" ? `${cls} blue` : cls; 1890 | break; 1891 | case (0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).METRO: 1892 | cls = `metro metro_${line}`; 1893 | switch(line){ 1894 | case "10": 1895 | case "11": 1896 | cls = `${cls} blue`; 1897 | break; 1898 | case "13": 1899 | case "14": 1900 | cls = `${cls} red`; 1901 | break; 1902 | default: 1903 | cls = `${cls} green`; 1904 | } 1905 | break; 1906 | case (0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).TRAM: 1907 | cls = `tram tram_${line}`; 1908 | break; 1909 | case (0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).TRAIN: 1910 | cls = `train train_${line}`; 1911 | break; 1912 | } 1913 | return cls; 1914 | } 1915 | _serviceCall(domain, service, data) { 1916 | this.hass.callService(domain, service, data); 1917 | } 1918 | _showAttributes(el, type, detail, options) { 1919 | const event = new Event(type, { 1920 | bubbles: Boolean(options?.bubbles), 1921 | cancelable: Boolean(options?.cancelable), 1922 | composed: Boolean(options?.composed) || true 1923 | }); 1924 | event.detail = detail || {}; 1925 | el.dispatchEvent(event); 1926 | return event; 1927 | } 1928 | constructor(...args){ 1929 | super(...args); 1930 | this.isManyEntitiesSet = ()=>this.config?.entities?.length > 1; 1931 | this.renderDepartures = ()=>{ 1932 | const renderEntityName = ()=>{ 1933 | const [_, attrs] = this.getFirstEntity(); 1934 | if (!attrs) return 0, $l56HR.nothing; 1935 | return this.config.show_entity_name && attrs.friendly_name ? (0, $l56HR.html)`
${attrs.friendly_name} 1945 | ${isMany ? "" : renderEntityName()} 1946 | ${this.config.show_header ? (0, $l56HR.html)` 1947 |
1948 | ${this.config?.show_icon ? (0, $l56HR.html)`
` : (0, $l56HR.nothing)} 1949 |
${_("line")}
1950 |
${_("departure")}
1951 |
` : (0, $l56HR.nothing)} 1952 | 1953 | ${departures.map((dep)=>{ 1954 | const expectedAt = new Date(dep.expected); 1955 | const diff = $66d5822390d71e6e$var$diffMinutes(expectedAt, now); 1956 | const isAtThePlatform = diff === 0; 1957 | const isDeparted = diff < 0; 1958 | const hasDeviations = (dep.deviations?.length || 0) > 0; 1959 | const mostImportantDeviation = dep.deviations?.sort((a, b)=>b.importance_level - a.importance_level)?.[0]; 1960 | const departureTime = this.config?.show_time_always ? expectedAt.toLocaleTimeString(lang, { 1961 | hour: "numeric", 1962 | minute: "numeric" 1963 | }) : (()=>{ 1964 | return isAtThePlatform ? _("now") : (0, $l56HR.html)``; 1965 | })(); 1966 | const icon = { 1967 | [(0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).METRO]: "mdi:subway", 1968 | [(0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).BUS]: "mdi:bus", 1969 | [(0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).TRAM]: "mdi:tram", 1970 | [(0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).TRAIN]: "mdi:train", 1971 | [(0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).SHIP]: "mdi:ship", 1972 | [(0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).FERRY]: "mdi:ferry", 1973 | [(0, $829f1babd4ccc0b8$export$6d07abd9f0bba447).TAXI]: "mdi:taxi" 1974 | }[dep.line.transport_mode] || "mdi:train"; 1975 | const lineIconClass = this.lineIconClass(dep.line.transport_mode, dep.line.designation, dep.line.group_of_lines); 1976 | return (0, $l56HR.html)` 1977 |
1978 | ${this.config?.show_icon ? (0, $l56HR.html)` 1979 |
1980 | 1981 |
1982 | ` : (0, $l56HR.nothing)} 1983 | ${this.config?.hide_line_number ? (0, $l56HR.nothing) : (0, $l56HR.html)` 1984 |
1985 | ${dep.line.designation} 1986 | ${hasDeviations ? (0, $l56HR.html)`` : (0, $l56HR.nothing)} 1987 |
1988 | `} 1989 |
1990 | ${dep.destination} 1991 | ${hasDeviations ? (0, $l56HR.html)`${mostImportantDeviation.message}` : (0, $l56HR.nothing)} 1992 |
1993 |
1994 | ${departureTime} 1995 |
1996 |
`; 1997 | })} 1998 |
1999 | `; 2000 | }; 2001 | this.clickHandler = (entity)=>(e)=>{ 2002 | const action = this.config?.click_action; 2003 | if (action === undefined) return; 2004 | if (action == "info" && entity) { 2005 | e.preventDefault(); 2006 | this._showAttributes(this, "hass-more-info", { 2007 | entityId: entity 2008 | }); 2009 | return; 2010 | } else if ($66d5822390d71e6e$var$isEntityInfoAction(action)) { 2011 | e.preventDefault(); 2012 | this._showAttributes(this, "hass-more-info", { 2013 | entityId: action.entityId 2014 | }); 2015 | return; 2016 | } else if ($66d5822390d71e6e$var$isServiceCallAction(action)) { 2017 | e.preventDefault(); 2018 | this._serviceCall(action.domain, action.service, action.data); 2019 | return; 2020 | } 2021 | }; 2022 | } 2023 | } 2024 | (0, $39J5i.__decorate)([ 2025 | (0, $pklEb.state)() 2026 | ], $66d5822390d71e6e$export$7ded24e6705f9c64.prototype, "config", void 0); 2027 | (0, $39J5i.__decorate)([ 2028 | (0, $dsTCw.property)({ 2029 | attribute: false 2030 | }) 2031 | ], $66d5822390d71e6e$export$7ded24e6705f9c64.prototype, "hass", void 0); 2032 | const $66d5822390d71e6e$var$isEntityInfoAction = (a)=>a.entityId !== undefined; 2033 | const $66d5822390d71e6e$var$isServiceCallAction = (a)=>a.service !== undefined; 2034 | function $66d5822390d71e6e$var$isDepartureAttrs(item) { 2035 | return item.departures !== undefined; 2036 | } 2037 | 2038 | 2039 | customElements.define("hasl4-departure-card", (0, $66d5822390d71e6e$export$7ded24e6705f9c64)); 2040 | window.customCards = window.customCards || []; 2041 | window.customCards.push({ 2042 | type: "hasl4-departure-card", 2043 | name: "HASL4 Departure card", 2044 | description: "Show departure times for SL Trafik" 2045 | }); 2046 | 2047 | 2048 | //# sourceMappingURL=hasl4-departure-card.js.map 2049 | --------------------------------------------------------------------------------