From 9b3ec47f41af72bb36b687b8ce070408418a3fff Mon Sep 17 00:00:00 2001 From: dfcarvajal Date: Sun, 27 Dec 2020 17:20:09 +0100 Subject: [PATCH] Eliminado CCS --- .../compact-custom-header.js | 3180 ----------------- .../compact-custom-header.js.gz | Bin 19672 -> 0 bytes 2 files changed, 3180 deletions(-) delete mode 100644 www/community/compact-custom-header/compact-custom-header.js delete mode 100644 www/community/compact-custom-header/compact-custom-header.js.gz diff --git a/www/community/compact-custom-header/compact-custom-header.js b/www/community/compact-custom-header/compact-custom-header.js deleted file mode 100644 index 3734973..0000000 --- a/www/community/compact-custom-header/compact-custom-header.js +++ /dev/null @@ -1,3180 +0,0 @@ -console.info( - `%c COMPACT-CUSTOM-HEADER \n%c Version 1.4.9 `, - "color: orange; font-weight: bold; background: black", - "color: white; font-weight: bold; background: dimgray" -); - -class CompactCustomHeader { - constructor() { - this.LitElement = Object.getPrototypeOf( - customElements.get("ha-panel-lovelace") - ); - this.hass = document.querySelector("home-assistant").hass; - this.fireEvent = (node, type, detail, options = {}) => { - detail = detail === null || detail === undefined ? {} : detail; - const event = new Event(type, { - bubbles: options.bubbles === undefined ? true : options.bubbles, - cancelable: Boolean(options.cancelable), - composed: options.composed === undefined ? true : options.composed - }); - event.detail = detail; - node.dispatchEvent(event); - return event; - }; - - let ll = document.querySelector("home-assistant"); - ll = ll && ll.shadowRoot; - ll = ll && ll.querySelector("home-assistant-main"); - this.main = ll; - ll = ll && ll.shadowRoot; - ll = ll && ll.querySelector("app-drawer-layout partial-panel-resolver"); - this.panelResolver = ll; - ll = (ll && ll.shadowRoot) || ll; - ll = ll && ll.querySelector("ha-panel-lovelace"); - ll = ll && ll.shadowRoot; - ll = ll && ll.querySelector("hui-root"); - this.lovelace = ll.lovelace; - this.root = ll.shadowRoot; - - this.frontendVersion = Number(window.frontendVersion); - this.newSidebar = this.frontendVersion >= 20190710; - this.header = this.root.querySelector("app-header"); - this.editMode = this.header.className == "edit-mode"; - this.view = this.root.querySelector("ha-app-layout #view"); - - this.sidebarClosed = false; - this.firstRun = true; - this.buttons = {}; - this.prevColor = {}; - - this.defaultConfig = { - header: true, - disable: false, - yaml_editor: false, - menu: "show", - voice: "show", - notifications: "show", - options: "show", - clock_format: 12, - clock_am_pm: true, - clock_date: false, - date_locale: this.hass.language, - chevrons: false, - redirect: true, - background: "", - hide_tabs: "", - show_tabs: "", - edit_mode_show_tabs: false, - default_tab: "", - default_tab_template: "", - kiosk_mode: false, - sidebar_swipe: true, - sidebar_closed: false, - disable_sidebar: false, - hide_help: false, - hide_config: false, - hide_unused: false, - tab_color: {}, - button_color: {}, - statusbar_color: "", - swipe: false, - swipe_amount: "15", - swipe_animate: "none", - swipe_skip: "", - swipe_wrap: true, - swipe_prevent_default: false, - swipe_skip_hidden: true, - warning: true, - compact_header: true, - view_css: "", - time_css: "", - date_css: "", - header_css: "", - tab_css: {}, - button_css: {} - }; - - this.cchConfig = this.buildConfig( - this.lovelace.config.cch || {}, - this.hass.user.name - ); - } - - run() { - const tabContainer = this.root.querySelector("paper-tabs"); - const tabs = tabContainer - ? Array.from(tabContainer.querySelectorAll("paper-tab")) - : []; - let disabled = - window.location.href.includes("disable_cch") || this.cchConfig.disable; - - if (this.firstRun || this.buttons == undefined) { - this.buttons = this.getButtonElements(tabContainer); - } - if (!this.buttons.menu || !this.buttons.options || this.editMode) return; - if (!disabled) { - this.insertEditMenu(tabs); - this.hideMenuItems(); - this.styleHeader(tabContainer, tabs); - this.styleButtons(tabs, tabContainer); - if (this.firstRun) this.sidebarMod(); - this.hideTabs(tabContainer, tabs); - for (let button in this.buttons) { - if (this.cchConfig[button] == "clock") this.insertClock(button); - } - if (!this.editMode) this.tabContainerMargin(tabContainer); - if (this.cchConfig.swipe) this.swipeNavigation(tabs, tabContainer); - if (this.firstRun) this.defaultTab(tabs, tabContainer); - } - if (this.firstRun) { - this.observers(tabContainer, tabs, disabled); - this.breakingChangeNotification(); - } - this.firstRun = false; - this.fireEvent(this.header, "iron-resize"); - } - - buildConfig(config, user_name) { - let exceptionConfig = {}; - let highestMatch = 0; - // Count number of matching conditions and choose config with most matches. - if (config.exceptions) { - config.exceptions.forEach(exception => { - const matches = countMatches(exception.conditions, user_name); - if (matches > highestMatch) { - highestMatch = matches; - exceptionConfig = exception.config; - } - }); - } - // If exception config uses hide_tabs and main config uses show_tabs, - // delete show_tabs and vice versa. - if ( - exceptionConfig.hide_tabs && - config.show_tabs && - exceptionConfig.hide_tabs.length && - config.show_tabs.length - ) { - delete config.show_tabs; - } else if ( - exceptionConfig.show_tabs && - config.hide_tabs && - exceptionConfig.show_tabs.length && - config.hide_tabs.length - ) { - delete config.hide_tabs; - } - - return { ...this.defaultConfig, ...config, ...exceptionConfig }; - - function countMatches(conditions, user_name) { - const userVars = { - user: user_name, - user_agent: navigator.userAgent - }; - let count = 0; - for (const cond in conditions) { - if (cond == "user" && conditions[cond].includes(",")) { - conditions[cond].split(/[ ,]+/).forEach(user => { - if (userVars[cond] == user) count++; - }); - } else { - if ( - userVars[cond] == conditions[cond] || - (cond == "query_string" && - window.location.search.includes(conditions[cond])) || - (cond == "user_agent" && - userVars[cond].includes(conditions[cond])) || - (cond == "media_query" && - window.matchMedia(conditions[cond]).matches) - ) { - count++; - } else { - return 0; - } - } - } - return count; - } - } - - observers(tabContainer, tabs, disabled) { - // Watch for changes in Lovelace. - const callback = mutations => { - // Theme changed. - if (mutations[0].target.nodeName == "HTML") { - mutations = [mutations[0]]; - this.styleHeader(tabContainer, tabs); - this.conditionalStyling(tabs); - return; - } - mutations.forEach(({ addedNodes, target }) => { - if (addedNodes.length && target.nodeName == "PARTIAL-PANEL-RESOLVER") { - // Navigated back to lovelace from elsewhere in HA. - this.buttons = this.getButtonElements(); - this.run(); - } else if (target.className == "edit-mode" && addedNodes.length) { - // Entered edit mode. - this.editMode = true; - if (!disabled) this.removeStyles(tabContainer, tabs, this.header); - this.buttons.options = this.root.querySelector("paper-menu-button"); - this.insertEditMenu(tabs); - this.fireEvent(this.header, "iron-resize"); - } else if (target.nodeName == "APP-HEADER" && addedNodes.length) { - // Exited edit mode. - let editor = this.root - .querySelector("ha-app-layout") - .querySelector("editor"); - if (editor) { - this.root.querySelector("ha-app-layout").removeChild(editor); - } - for (let node of addedNodes) { - if (node.nodeName == "APP-TOOLBAR") { - this.editMode = false; - this.buttons = this.getButtonElements(); - this.root.querySelectorAll("[id^='cch']").forEach(style => { - style.remove(); - }); - setTimeout(() => { - this.run(); - if (!disabled) this.conditionalStyling(tabs, this.header); - }, 100); - } - } - } else if ( - // Viewing unused entities - this.frontendVersion < 20190911 && - addedNodes.length && - !addedNodes[0].nodeName == "HUI-UNUSED-ENTITIES" - ) { - let editor = this.root - .querySelector("ha-app-layout") - .querySelector("editor"); - if (editor) { - this.root.querySelector("ha-app-layout").removeChild(editor); - } - if (this.cchConfig.conditional_styles) { - this.buttons = this.getButtonElements(tabContainer); - this.conditionalStyling(tabs, this.header); - } - } else if (target.id == "view" && addedNodes.length) { - // Navigating to new tab/view. - this.run(); - if (tabContainer) this.scrollTabIconIntoView(); - } - }); - }; - let observer = new MutationObserver(callback); - observer.observe(this.panelResolver, { childList: true }); - observer.observe(document.querySelector("html"), { attributes: true }); - observer.observe(this.view, { childList: true }); - observer.observe(this.root.querySelector("app-header"), { - childList: true - }); - - if (!disabled) { - // Watch for changes in entities. - window.hassConnection.then(({ conn }) => { - conn.socket.onmessage = () => { - if (this.cchConfig.conditional_styles && !this.editMode) { - this.conditionalStyling(tabs, this.header); - } - }; - }); - } - } - - getButtonElements(disabled) { - let buttons = {}; - buttons.options = this.root.querySelector("paper-menu-button"); - if (!this.editMode) { - buttons.menu = this.root.querySelector("ha-menu-button"); - buttons.voice = - this.root.querySelector("ha-start-voice-button") || - this.root.querySelector('[icon="hass:microphone"]'); - if (!this.newSidebar) { - buttons.notifications = this.root.querySelector( - "hui-notifications-button" - ); - } - } - // Remove space taken up by "hidden" menu button anytime we get buttons. - if ( - buttons.menu && - buttons.menu.style.visibility == "hidden" && - !disabled - ) { - buttons.menu.style.display = "none"; - } else if (buttons.menu) { - buttons.menu.style.display = ""; - } - return buttons; - } - - tabContainerMargin(tabContainer) { - let marginRight = 0; - let marginLeft = 15; - for (const button in this.buttons) { - if (!this.buttons[button]) continue; - let paperIconButton = - this.buttons[button].querySelector("paper-icon-button") || - this.buttons[button].shadowRoot.querySelector("paper-icon-button"); - let visible = paperIconButton - ? this.buttons[button].style.display !== "none" && - !paperIconButton.hasAttribute("hidden") - : this.buttons[button].style.display !== "none"; - if (this.cchConfig[button] == "show" && visible) { - if (button == "menu") marginLeft += 45; - else marginRight += 45; - } else if (this.cchConfig[button] == "clock" && visible) { - const clockWidth = - (this.cchConfig.clock_format == 12 && this.cchConfig.clock_am_pm) || - this.cchConfig.clock_date - ? 110 - : 80; - if (button == "menu") marginLeft += clockWidth + 15; - else marginRight += clockWidth; - } - } - if (tabContainer) { - tabContainer.style.marginRight = `${marginRight}px`; - tabContainer.style.marginLeft = `${marginLeft}px`; - } - } - - scrollTabIconIntoView() { - let paperTabs = this.root.querySelector("paper-tabs"); - let currentTab = paperTabs.querySelector(".iron-selected"); - if (!paperTabs || !currentTab) return; - let tab = currentTab.getBoundingClientRect(); - let container = paperTabs.shadowRoot - .querySelector("#tabsContainer") - .getBoundingClientRect(); - // If tab's icon isn't in view scroll it in. - if (container.right < tab.right || container.left > tab.left) { - if ("scrollMarginInline" in document.documentElement.style) { - currentTab.scrollIntoView({ inline: "center" }); - } else if (Element.prototype.scrollIntoViewIfNeeded) { - currentTab.scrollIntoViewIfNeeded(true); - } else { - currentTab.scrollIntoView(); - } - } - } - - hideMenuItems() { - // Hide items in options menu. - if ( - this.cchConfig.hide_help || - this.cchConfig.hide_config || - this.cchConfig.hide_unused - ) { - const localized = (item, string) => { - let localString = this.hass.localize( - `ui.panel.lovelace.menu.${string}` - ); - return ( - item.innerHTML.includes(localString) || - item.getAttribute("aria-label") == localString - ); - }; - this.buttons.options - .querySelector("paper-listbox") - .querySelectorAll("paper-item") - .forEach(item => { - if ( - (this.cchConfig.hide_help && localized(item, "help")) || - (this.cchConfig.hide_unused && - localized(item, "unused_entities")) || - (this.cchConfig.hide_config && localized(item, "configure_ui")) - ) { - item.parentNode.removeChild(item); - } - }); - } - } - - insertEditMenu(tabs, disabled) { - if ( - this.buttons.options && - (this.editMode || - (this.lovelace.mode == "yaml" && this.cchConfig.yaml_editor)) - ) { - // If any tabs are hidden, add "show all tabs" option. - if (this.cchConfig.hide_tabs && !this.cchConfig.edit_mode_show_tabs) { - let show_tabs = document.createElement("paper-item"); - show_tabs.setAttribute("id", "show_tabs"); - show_tabs.addEventListener("click", () => { - for (let i = 0; i < tabs.length; i++) { - tabs[i].style.removeProperty("display"); - } - }); - show_tabs.innerHTML = "Show all tabs"; - this.insertMenuItem( - this.buttons.options.querySelector("paper-listbox"), - show_tabs - ); - } - - // Add menu item to open CCH settings. - let cchSettings = document.createElement("paper-item"); - cchSettings.setAttribute("id", "cch_settings"); - cchSettings.addEventListener("click", () => this.showEditor()); - cchSettings.innerHTML = "CCH Settings"; - this.insertMenuItem( - this.buttons.options.querySelector("paper-listbox"), - cchSettings - ); - if (!disabled) this.hideMenuItems(); - } - } - - removeStyles(tabContainer, tabs, { style }) { - this.root.querySelector("app-header").style.backgroundColor = "#455a64"; - this.root.querySelectorAll("[id^='cch']").forEach(style => { - style.remove(); - }); - if (this.cchConfig.tab_css) { - for (let [key, value] of Object.entries(this.cchConfig.tab_css)) { - key = this.getViewIndex(key); - value = value.replace(/: /g, ":").replace(/; /g, ";"); - let css = tabs[key].style.cssText - .replace(/: /g, ":") - .replace(/; /g, ";"); - tabs[key].style.cssText = css.replace(value, ""); - } - } - if (this.cchConfig.header_css) { - let value = this.cchConfig.header_css - .replace(/: /g, ":") - .replace(/; /g, ";"); - let css = style.cssText.replace(/: /g, ":").replace(/; /g, ";"); - style.cssText = css.replace(value, ""); - } - if (tabContainer) { - tabContainer.style.marginLeft = ""; - tabContainer.style.marginRight = ""; - } - this.view.style = ""; - for (let i = 0; i < tabs.length; i++) { - tabs[i].style.color = ""; - } - if (this.cchConfig.edit_mode_show_tabs) { - for (let i = 0; i < tabs.length; i++) { - tabs[i].style.removeProperty("display"); - } - } - let viewStyle = document.createElement("style"); - viewStyle.setAttribute("id", "cch_view_styling"); - viewStyle.innerHTML = ` - hui-view { - min-height: 100vh; - } - hui-panel-view { - min-height: calc(100vh - 52px); - } - `; - this.root.appendChild(viewStyle); - } - - styleHeader(tabContainer, tabs) { - // Fix for old background config option. - if (typeof this.cchConfig.background == "boolean") { - this.cchConfig.background = ""; - } - this.prevColor.background = - this.cchConfig.background || - getComputedStyle(document.body).getPropertyValue("--cch-background") || - getComputedStyle(document.body).getPropertyValue("--primary-color"); - let statusBarColor = - this.cchConfig.statusbar_color || this.prevColor.background; - // Match mobile status bar color to header color. - let themeColor = document.querySelector('[name="theme-color"]'); - let themeColorApple = - document.querySelector( - '[name="apple-mobile-web-app-status-bar-style"]' - ) || document.createElement("meta"); - colorStatusBar(statusBarColor); - // If browser is idle or in background sometimes theme-color needs reset. - let observeStatus = new MutationObserver(() => { - if (themeColor.content != statusBarColor) colorStatusBar(statusBarColor); - }); - if (this.firstRun) { - observeStatus.observe(themeColor, { - attributes: true, - attributeFilter: ["content"] - }); - } - - // Adjust view size & padding for new header size. - if (!this.cchConfig.header || this.cchConfig.kiosk_mode) { - this.header.style.display = "none"; - this.view.style.minHeight = "100vh"; - if ( - this.frontendVersion >= 20190911 && - !this.root.querySelector("#cch_view_styling") - ) { - let viewStyle = document.createElement("style"); - viewStyle.setAttribute("id", "cch_view_styling"); - viewStyle.innerHTML = ` - hui-view { - ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} - } - hui-panel-view { - ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} - } - `; - this.root.appendChild(viewStyle); - } - } else { - this.view.style.minHeight = "100vh"; - this.view.style.marginTop = "-48.5px"; - this.view.style.paddingTop = "48.5px"; - this.view.style.boxSizing = "border-box"; - this.header.style.background = this.prevColor.background; - this.conditionalStyling(tabs, this.header); - this.header.querySelector("app-toolbar").style.background = "transparent"; - if ( - this.frontendVersion >= 20190911 && - !this.root.querySelector("#cch_view_styling") - ) { - let viewStyle = document.createElement("style"); - viewStyle.setAttribute("id", "cch_view_styling"); - viewStyle.innerHTML = ` - hui-view { - margin-top: -48.5px; - padding-top: 52px; - min-height: 100vh; - ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} - } - hui-panel-view { - margin-top: -52px; - padding-top: 52px; - min-height: calc(100vh - 52px); - ${this.cchConfig.view_css ? this.cchConfig.view_css : ""} - } - `; - this.root.appendChild(viewStyle); - } - } - - // Match sidebar elements to header's size. - if (this.newSidebar && this.cchConfig.compact_header) { - let sidebar = this.main.shadowRoot.querySelector("ha-sidebar").shadowRoot; - sidebar.querySelector(".menu").style = "height:49px;"; - sidebar.querySelector("paper-listbox").style = - "height:calc(100% - 180px);"; - } - - // Current tab icon color. - let conditionalTabs = this.cchConfig.conditional_styles - ? JSON.stringify(this.cchConfig.conditional_styles).includes("tab") - : false; - if ( - !this.root.querySelector("#cch_iron_selected") && - !this.editMode && - !conditionalTabs && - tabContainer - ) { - let style = document.createElement("style"); - style.setAttribute("id", "cch_iron_selected"); - style.innerHTML = ` - .iron-selected { - ${ - this.cchConfig.active_tab_color - ? `color: ${`${ - this.cchConfig.active_tab_color - } !important`}` - : "var(--cch-active-tab-color)" - } - } - `; - tabContainer.appendChild(style); - } - - // Style current tab indicator. - let indicator = this.cchConfig.tab_indicator_color; - if ( - indicator && - !this.root.querySelector("#cch_header_colors") && - !this.editMode - ) { - let style = document.createElement("style"); - style.setAttribute("id", "cch_header_colors"); - style.innerHTML = ` - paper-tabs { - ${ - indicator - ? `--paper-tabs-selection-bar-color: ${indicator} !important` - : "var(--cch-tab-indicator-color) !important" - } - } - `; - this.root.appendChild(style); - } - - // Tab's icon color. - let all_tabs_color = - this.cchConfig.all_tabs_color || "var(--cch-all-tabs-color)"; - if ( - (this.cchConfig.tab_color && - Object.keys(this.cchConfig.tab_color).length) || - all_tabs_color - ) { - for (let i = 0; i < tabs.length; i++) { - tabs[i].style.color = this.cchConfig.tab_color[i] || all_tabs_color; - } - } - - // Add custom css. - if (this.cchConfig.tab_css) { - for (let [key, value] of Object.entries(this.cchConfig.tab_css)) { - key = this.getViewIndex(key); - if (tabs[key]) tabs[key].style.cssText += value; - } - } - if (this.cchConfig.header_css) - this.header.style.cssText += this.cchConfig.header_css; - if (this.cchConfig.view_css && this.frontendVersion < 20190911) { - this.view.style.cssText += this.cchConfig.view_css; - } - - if (tabContainer) { - // Shift the header up to hide unused portion. - this.root.querySelector("app-toolbar").style.marginTop = this.cchConfig - .compact_header - ? "-64px" - : ""; - - tabs.forEach(({ style }) => { - style.marginTop = "-1px"; - }); - - // Show/hide tab navigation chevrons. - if (!this.cchConfig.chevrons) { - let chevron = tabContainer.shadowRoot.querySelectorAll( - '[icon^="paper-tabs:chevron"]' - ); - chevron[0].style.display = "none"; - chevron[1].style.display = "none"; - } else { - // Remove space taken up by "not-visible" chevron. - let style = document.createElement("style"); - style.setAttribute("id", "cch_chevron"); - style.innerHTML = ` - .not-visible { - display:none; - } - `; - tabContainer.shadowRoot.appendChild(style); - } - } - function colorStatusBar(statusBarColor) { - themeColor = document.querySelector("meta[name=theme-color]"); - themeColor.setAttribute("content", statusBarColor); - themeColor.setAttribute("default-content", statusBarColor); - if ( - !document.querySelector( - '[name="apple-mobile-web-app-status-bar-style"]' - ) - ) { - themeColorApple.name = "apple-mobile-web-app-status-bar-style"; - themeColorApple.content = statusBarColor; - document.getElementsByTagName("head")[0].appendChild(themeColorApple); - } else { - themeColorApple.setAttribute("content", statusBarColor); - } - } - } - - styleButtons({ length }, tabContainer) { - let topMargin = - length > 0 && this.cchConfig.compact_header ? "margin-top:111px;" : ""; - let topMarginMenu = - length > 0 && this.cchConfig.compact_header ? "margin-top:115px;" : ""; - // Reverse buttons object so "menu" is first in the overflow menu. - this.buttons = this.reverseObject(this.buttons); - for (const button in this.buttons) { - if (!this.buttons[button]) continue; - if (button == "options" && this.cchConfig[button] == "overflow") { - this.cchConfig[button] = "show"; - } - let buttonStyle = ` - z-index:1; - ${ - button == "menu" - ? `padding: 8px 0; margin-bottom:5px; ${topMarginMenu}` - : "padding: 8px;" - } - ${ - button == "voice" && this.cchConfig["voice"] == "clock" - ? "width: 100px; padding:4px;" - : "" - } - ${button == "menu" ? "" : topMargin} - ${button == "options" ? "margin-right:-5px;" : ""} - `; - if ( - this.cchConfig[button] == "show" || - this.cchConfig[button] == "clock" - ) { - if (button == "menu") { - let paperIconButton = this.buttons[button].querySelector( - "paper-icon-button" - ) - ? this.buttons[button].querySelector("paper-icon-button") - : this.buttons[button].shadowRoot.querySelector( - "paper-icon-button" - ); - if (!paperIconButton) continue; - paperIconButton.style.cssText = buttonStyle; - } else { - this.buttons[button].style.cssText = buttonStyle; - } - } else if (this.cchConfig[button] == "overflow") { - const menu_items = this.buttons.options.querySelector("paper-listbox"); - let paperIconButton = this.buttons[button].querySelector( - "paper-icon-button" - ) - ? this.buttons[button].querySelector("paper-icon-button") - : this.buttons[button].shadowRoot.querySelector("paper-icon-button"); - if (paperIconButton && paperIconButton.hasAttribute("hidden")) { - continue; - } - const id = `menu_item_${button}`; - if (!menu_items.querySelector(`#${id}`)) { - const wrapper = document.createElement("paper-item"); - wrapper.setAttribute("id", id); - wrapper.innerText = this.getTranslation(button); - wrapper.appendChild(this.buttons[button]); - wrapper.addEventListener("click", () => { - paperIconButton.click(); - }); - paperIconButton.style.pointerEvents = "none"; - this.insertMenuItem(menu_items, wrapper); - if (button == "notifications" && !this.newSidebar) { - let style = document.createElement("style"); - style.innerHTML = ` - .indicator { - top: 5px; - right: 0px; - width: 10px; - height: 10px; - ${ - this.cchConfig.notify_indicator_color - ? `background-color:${ - this.cchConfig.notify_indicator_color - }` - : "" - } - } - .indicator > div{ - display:none; - } - `; - paperIconButton.parentNode.appendChild(style); - } - } - } else if (this.cchConfig[button] == "hide") { - this.buttons[button].style.display = "none"; - } - // Hide menu button if hiding the sidebar. - if ( - this.newSidebar && - (this.cchConfig.kiosk_mode || this.cchConfig.disable_sidebar) - ) { - this.buttons.menu.style.display = "none"; - } - } - - // Remove empty space taken up by hidden menu button. - if (this.buttons.menu && this.newSidebar && this.firstRun) { - new MutationObserver(() => { - if (this.buttons.menu.style.visibility == "hidden") { - this.buttons.menu.style.display = "none"; - } else { - this.buttons.menu.style.display = ""; - } - this.tabContainerMargin(tabContainer); - }).observe(this.buttons.menu, { - attributes: true, - attributeFilter: ["style"] - }); - } - - // Use color vars set in HA theme. - this.buttons.menu.style.color = "var(--cch-button-color-menu)"; - if (!this.newSidebar) { - this.buttons.notifications.style.color = - "var(--cch-button-color-notifications)"; - } - if (this.buttons.voice) this.buttons.voice.style.color = "var(--cch-button-color-voice)"; - this.buttons.options.style.color = "var(--cch-button-color-options)"; - if (this.cchConfig.all_buttons_color) { - this.root.querySelector("app-toolbar").style.color = - this.cchConfig.all_buttons_color || "var(--cch-all-buttons-color)"; - } - - // Use colors set in CCH config. - for (const button in this.buttons) { - if (this.cchConfig.button_color[button]) { - this.buttons[button].style.color = this.cchConfig.button_color[button]; - } - } - - // Notification indicator's color for HA 0.96 and above. - if ( - this.newSidebar && - this.cchConfig.menu != "hide" && - !this.buttons.menu.shadowRoot.querySelector("#cch_dot") - ) { - let style = document.createElement("style"); - style.setAttribute("id", "cch_dot"); - let indicator = - this.cchConfig.notify_indicator_color || - getComputedStyle(this.header).getPropertyValue( - "--cch-tab-indicator-color" - ) || - ""; - let border = getComputedStyle(this.header) - .getPropertyValue("background") - .includes("url") - ? "border-color: transparent !important" - : `border-color: ${getComputedStyle(this.header).getPropertyValue( - "background-color" - )} !important;`; - style.innerHTML = ` - .dot { - ${topMargin} - z-index: 2; - ${indicator ? `background: ${indicator} !important` : ""} - ${border} - } - `; - this.buttons.menu.shadowRoot.appendChild(style); - } else if ( - // Notification indicator's color for HA 0.95 and below. - this.cchConfig.notify_indicator_color && - this.cchConfig.notifications == "show" && - !this.newSidebar - ) { - let style = document.createElement("style"); - style.innerHTML = ` - .indicator { - background-color:${this.cchConfig.notify_indicator_color || - "var(--cch-notify-indicator-color)"} !important; - color: ${this.cchConfig.notify_text_color || - "var(--cch-notify-text-color), var(--primary-text-color)"}; - } - `; - this.buttons.notifications.shadowRoot.appendChild(style); - } - - // Add buttons's custom css. - let buttonCss = this.cchConfig.button_css; - if (buttonCss) { - for (const [key, value] of Object.entries(buttonCss)) { - if (!this.buttons[key]) { - continue; - } else { - this.buttons[key].style.cssText += value; - } - } - } - } - - getTranslation(button) { - switch (button) { - case "notifications": - return this.hass.localize("ui.notification_drawer.title"); - default: - return button.charAt(0).toUpperCase() + button.slice(1); - } - } - - defaultTab(tabs, tabContainer) { - let firstTab = tabs.indexOf(tabs.filter(tab => tab.style.display == "")[0]); - let default_tab = this.cchConfig.default_tab; - if (typeof default_tab == "object" && !default_tab.length) return; - let template = this.cchConfig.default_tab_template; - if ((default_tab || template) && tabContainer) { - if (template) default_tab = this.templateEval(template, this.hass.states); - default_tab = this.getViewIndex(default_tab); - let activeTab = tabs.indexOf( - tabContainer.querySelector(".iron-selected") - ); - if ( - activeTab != default_tab && - activeTab == firstTab && - (!this.cchConfig.redirect || - (this.cchConfig.redirect && - tabs[default_tab].style.display != "none")) - ) { - tabs[default_tab].click(); - } - } - } - - sidebarMod() { - let menu = this.buttons.menu.querySelector("paper-icon-button"); - let sidebar = this.main.shadowRoot.querySelector("app-drawer"); - - // HA 0.95 and below - if (!this.newSidebar) { - if (!this.cchConfig.sidebar_swipe || this.cchConfig.kiosk_mode) { - sidebar.removeAttribute("swipe-open"); - } - if ( - (this.cchConfig.sidebar_closed || this.cchConfig.kiosk_mode) && - !this.sidebarClosed - ) { - if (sidebar.hasAttribute("opened")) menu.click(); - this.sidebarClosed = true; - } - // HA 0.96 and above - } else if (this.cchConfig.disable_sidebar || this.cchConfig.kiosk_mode) { - sidebar.style.display = "none"; - sidebar.addEventListener( - "mouseenter", - event => { - event.stopPropagation(); - }, - true - ); - let style = document.createElement("style"); - style.type = "text/css"; - style.appendChild( - document.createTextNode( - ":host(:not([expanded])) {width: 0px !important;}" - ) - ); - this.main.shadowRoot - .querySelector("ha-sidebar") - .shadowRoot.appendChild(style); - - style = document.createElement("style"); - style.type = "text/css"; - style.appendChild( - document.createTextNode(":host {--app-drawer-width: 0px !important;}") - ); - this.main.shadowRoot.appendChild(style); - } - } - - hideTabs(tabContainer, tabs) { - let hidden_tabs = String(this.cchConfig.hide_tabs).length - ? String(this.cchConfig.hide_tabs) - .replace(/\s+/g, "") - .split(",") - : null; - let shown_tabs = String(this.cchConfig.show_tabs).length - ? String(this.cchConfig.show_tabs) - .replace(/\s+/g, "") - .split(",") - : null; - - // Set the tab config source. - if (!hidden_tabs && shown_tabs) { - let all_tabs = []; - shown_tabs = this.buildRanges(shown_tabs); - for (let i = 0; i < tabs.length; i++) all_tabs.push(i); - // Invert shown_tabs to hidden_tabs. - hidden_tabs = all_tabs.filter(el => !shown_tabs.includes(el)); - } else { - hidden_tabs = this.buildRanges(hidden_tabs); - } - - // Hide tabs. - for (const tab of hidden_tabs) { - if (!tabs[tab]) continue; - tabs[tab].style.display = "none"; - } - - if (this.cchConfig.redirect && tabContainer) { - const activeTab = tabContainer.querySelector("paper-tab.iron-selected"); - const activeTabIndex = tabs.indexOf(activeTab); - // Is the current tab hidden and is there at least one tab is visible. - if ( - hidden_tabs.includes(activeTabIndex) && - hidden_tabs.length != tabs.length - ) { - let i = 0; - // Find the first visible tab and navigate. - while (hidden_tabs.includes(i)) { - i++; - } - tabs[i].click(); - } - } - return hidden_tabs; - } - - insertMenuItem(menu_items, element) { - let first_item = menu_items.querySelector("paper-item"); - if (!menu_items.querySelector(`#${element.id}`)) { - first_item.parentNode.insertBefore(element, first_item); - } - } - - insertClock(button) { - if (!this.buttons[button]) return; - const clock_button = this.buttons[button].querySelector("paper-icon-button") - ? this.buttons[button] - : this.buttons[button].shadowRoot; - const clockIcon = - clock_button.querySelector("paper-icon-button") || this.buttons[button]; - const clockIronIcon = - clockIcon.querySelector("iron-icon") || - clockIcon.shadowRoot.querySelector("iron-icon"); - const clockWidth = - (this.cchConfig.clock_format == 12 && this.cchConfig.clock_am_pm) || - this.cchConfig.clock_date - ? 105 - : 80; - - if ( - !this.newSidebar && - this.cchConfig.notifications == "clock" && - this.cchConfig.clock_date && - !this.buttons.notifications.shadowRoot.querySelector("#cch_indicator") - ) { - let style = document.createElement("style"); - style.setAttribute("id", "cch_indicator"); - style.innerHTML = ` - .indicator { - top: unset; - bottom: -3px; - right: 0px; - width: 90%; - height: 3px; - border-radius: 0; - ${ - this.cchConfig.notify_indicator_color - ? `background-color:${this.cchConfig.notify_indicator_color}` - : "" - } - } - .indicator > div{ - display:none; - } - `; - this.buttons.notifications.shadowRoot.appendChild(style); - } - - let clockElement = clockIronIcon.parentNode.getElementById("cch_clock"); - if (this.cchConfig.menu == "clock") { - this.buttons.menu.style.marginTop = this.cchConfig.compact_header - ? "111px" - : ""; - this.buttons.menu.style.zIndex = "1"; - } - if (!clockElement) { - clockIcon.style.cssText = ` - margin-right:-5px; - width:${clockWidth}px; - text-align: center; - `; - clockElement = document.createElement("p"); - clockElement.setAttribute("id", "cch_clock"); - let clockAlign = "center"; - let padding = ""; - let fontSize = ""; - if (this.cchConfig.clock_date && this.cchConfig.menu == "clock") { - clockAlign = "left"; - padding = "margin-right:-20px"; - fontSize = "font-size:12pt"; - } else if (this.cchConfig.clock_date) { - clockAlign = "right"; - padding = "margin-left:-20px"; - fontSize = "font-size:12pt"; - } - clockElement.style.cssText = ` - margin-top: ${this.cchConfig.clock_date ? "-4px" : "2px"}; - text-align: ${clockAlign}; - ${padding}; - ${fontSize}; - `; - clockIronIcon.parentNode.insertBefore(clockElement, clockIronIcon); - clockIronIcon.style.display = "none"; - let style = document.createElement("style"); - style.setAttribute("id", "cch_clock"); - style.innerHTML = ` - time { - ${this.cchConfig.time_css} - } - date { - ${this.cchConfig.date_css} - } - `; - clockIronIcon.parentNode.insertBefore(style, clockIronIcon); - } - - const clockFormat = { - hour12: this.cchConfig.clock_format != 24, - hour: "2-digit", - minute: "2-digit" - }; - this.updateClock(clockElement, clockFormat); - } - - updateClock(clock, clockFormat) { - let date = new Date(); - let seconds = date.getSeconds(); - let locale = this.cchConfig.date_locale || this.hass.language; - let time = date.toLocaleTimeString([], clockFormat); - let options = { - weekday: "short", - month: "2-digit", - day: "2-digit" - }; - date = this.cchConfig.clock_date - ? `
${date.toLocaleDateString(locale, options)}` - : ""; - if (!this.cchConfig.clock_am_pm && this.cchConfig.clock_format == 12) { - clock.innerHTML = `${date}`; - } else { - clock.innerHTML = `${date}`; - } - window.setTimeout(() => { - this.updateClock(clock, clockFormat); - }, (60 - seconds) * 1000); - } - - // Abandon all hope, ye who enter here. - conditionalStyling(tabs) { - let _hass = document.querySelector("home-assistant").hass; - const conditional_styles = this.cchConfig.conditional_styles; - let tabContainer = tabs[0] ? tabs[0].parentNode : ""; - let styling = []; - - if (Array.isArray(conditional_styles)) { - for (let i = 0; i < conditional_styles.length; i++) { - styling.push(Object.assign({}, conditional_styles[i])); - } - } else { - styling.push(Object.assign({}, conditional_styles)); - } - - function exists(configItem) { - return configItem !== undefined && configItem !== null; - } - - function notificationCount() { - if (this.newSidebar) { - let badge = this.main.shadowRoot - .querySelector("ha-sidebar") - .shadowRoot.querySelector("span.notification-badge"); - if (!badge) return 0; - else return parseInt(badge.innerHTML); - } - let i = 0; - let drawer = this.root - .querySelector("hui-notification-drawer") - .shadowRoot.querySelector(".notifications"); - for (let notification of drawer.querySelectorAll(".notification")) { - if (notification.style.display !== "none") i++; - } - return i; - } - - for (let i = 0; i < styling.length; i++) { - let template = styling[i].template; - let condition = styling[i].condition; - - if (template) { - if (!template.length) template = [template]; - template.forEach(template => { - this.templates(template, tabs, _hass, this.header); - }); - } else if (condition) { - let entity = styling[i].entity; - if (_hass.states[entity] == undefined && entity !== "notifications") { - console.log(`CCH conditional styling: ${entity} does not exist.`); - continue; - } - let entState = - entity == "notifications" - ? notificationCount() - : _hass.states[entity].state; - let condState = condition.state; - let above = condition.above; - let below = condition.below; - - let toStyle = - (exists(condState) && entState == condState) || - (exists(above) && - exists(below) && - entState > above && - entState < below) || - (exists(above) && entState > above) || - (exists(below) && entState < below); - - let tabIndex = styling[i].tab ? Object.keys(styling[i].tab)[0] : null; - let tabCondition = styling[i].tab ? styling[i].tab[tabIndex] : null; - let tabElem = tabs[this.getViewIndex(tabIndex)]; - let tabkey = `tab_${this.getViewIndex(tabIndex)}`; - let button = styling[i].button - ? Object.keys(styling[i].button)[0] - : null; - let background = styling[i].background; - - // Conditionally style tabs. - if (toStyle && exists(tabIndex) && tabElem) { - if (tabCondition.hide) tabElem.style.display = "none"; - if (tabCondition.color) { - if (this.prevColor[tabkey] == undefined) { - Object.assign(this.prevColor, { - [tabkey]: window - .getComputedStyle(tabElem, null) - .getPropertyValue("color") - }); - } - tabElem.style.color = tabCondition.color; - } - if (tabCondition.on_icon) { - tabElem - .querySelector("ha-icon") - .setAttribute("icon", tabCondition.on_icon); - } - } else if (!toStyle && exists(tabIndex) && tabElem) { - if (tabCondition.hide) { - tabElem.style.display = ""; - } - if (tabCondition.color && this.prevColor[tabkey]) { - tabElem.style.color = this.prevColor[tabkey]; - } - if (tabCondition.off_icon) { - tabElem - .querySelector("ha-icon") - .setAttribute("icon", tabCondition.off_icon); - } - } - - if (toStyle && button) { - if (!this.buttons[button]) continue; - let buttonCondition = styling[i].button[button]; - let buttonElem = this.buttons[button].querySelector( - "paper-icon-button" - ) - ? this.buttons[button].querySelector("paper-icon-button") - : this.buttons[button].shadowRoot.querySelector( - "paper-icon-button" - ); - if (buttonCondition.hide) { - buttonElem.style.display = "none"; - } - if (buttonCondition.color) { - if (this.prevColor.button == undefined) this.prevColor.button = {}; - if (this.prevColor.button[button] == undefined) { - this.prevColor.button[button] = window - .getComputedStyle(buttonElem, null) - .getPropertyValue("color"); - } - buttonElem.style.color = buttonCondition.color; - } - if (buttonCondition.on_icon) { - let icon = - buttonElem.querySelector("iron-icon") || - buttonElem.shadowRoot.querySelector("iron-icon"); - icon.setAttribute("icon", buttonCondition.on_icon); - } - } else if (!toStyle && button) { - let buttonCondition = styling[i].button[button]; - let buttonElem = this.buttons[button].querySelector( - "paper-icon-button" - ) - ? this.buttons[button].querySelector("paper-icon-button") - : this.buttons[button].shadowRoot.querySelector( - "paper-icon-button" - ); - if (buttonCondition.hide) { - buttonElem.style.display = ""; - } - if ( - buttonCondition.color && - this.prevColor.button && - this.prevColor.button[button] - ) { - buttonElem.style.color = this.prevColor.button[button]; - } - if (buttonCondition.off_icon) { - let icon = - buttonElem.querySelector("iron-icon") || - buttonElem.shadowRoot.querySelector("iron-icon"); - icon.setAttribute("icon", buttonCondition.off_icon); - } - } - - // Conditionally style background. - if (toStyle && background) { - if (this.prevColor.background == undefined) { - this.prevColor.background = window - .getComputedStyle(this.header, null) - .getPropertyValue("background"); - } - this.header.style.background = styling[i].background; - } else if (!toStyle && background) { - this.header.style.background = this.prevColor.background; - } - } - } - this.tabContainerMargin(tabContainer); - } - - templates(template, tabs, _hass, { style }) { - let states = _hass.states; - for (const condition in template) { - if (condition == "tab") { - for (const tab in template[condition]) { - let tempCond = template[condition][tab]; - if (!tempCond.length) tempCond = [tempCond]; - tempCond.forEach(templateObj => { - let tabIndex = this.getViewIndex(Object.keys(template[condition])); - let styleTarget = Object.keys(templateObj); - let tabTemplate = templateObj[styleTarget]; - let tabElement = tabs[tabIndex]; - if (styleTarget == "icon") { - tabElement - .querySelector("ha-icon") - .setAttribute("icon", this.templateEval(tabTemplate, states)); - } else if (styleTarget == "color") { - tabElement.style.color = this.templateEval(tabTemplate, states); - } else if (styleTarget == "display") { - this.templateEval(tabTemplate, states) == "show" - ? (tabElement.style.display = "") - : (tabElement.style.display = "none"); - } - }); - } - } else if (condition == "button") { - for (const button in template[condition]) { - let tempCond = template[condition][button]; - if (!tempCond.length) tempCond = [tempCond]; - tempCond.forEach(templateObj => { - let buttonName = Object.keys(template[condition]); - if (this.newSidebar && buttonName == "notifications") return; - let styleTarget = Object.keys(templateObj); - let buttonElem = this.buttons[buttonName]; - let tempCond = templateObj[styleTarget]; - let iconTarget = buttonElem.querySelector("paper-icon-button") - ? buttonElem.querySelector("paper-icon-button") - : buttonElem.shadowRoot.querySelector("paper-icon-button"); - if (styleTarget == "icon") { - iconTarget.setAttribute( - "icon", - this.templateEval(tempCond, states) - ); - } else if (styleTarget == "color") { - let tar = - iconTarget.querySelector("iron-icon") || - iconTarget.shadowRoot.querySelector("iron-icon"); - tar.style.color = this.templateEval(tempCond, states); - } else if (styleTarget == "display") { - this.templateEval(tempCond, states) == "show" - ? (buttonElem.style.display = "") - : (buttonElem.style.display = "none"); - } - }); - } - } else if (condition == "background") { - style.background = this.templateEval(template[condition], states); - } - } - } - - // Get range (e.g., "5 to 9") and build (5,6,7,8,9). - buildRanges(array) { - let ranges = []; - if (!array) return []; - const sortNumber = (a, b) => a - b; - const range = (start, end) => - new Array(end - start + 1).fill(undefined).map((_, i) => i + start); - for (let i in array) { - if (typeof array[i] == "string" && array[i].includes("to")) { - let split = array[i].split("to"); - if (parseInt(split[1]) > parseInt(split[0])) { - ranges.push(range(parseInt(split[0]), parseInt(split[1]))); - } else { - ranges.push(range(parseInt(split[1]), parseInt(split[0]))); - } - } else if (isNaN(array[i])) { - let views = this.lovelace.config.views; - for (let view in views) { - if ( - views[view]["title"] == array[i] || - views[view]["path"] == array[i] - ) { - ranges.push(parseInt(view)); - } - } - } else { - ranges.push(parseInt(array[i])); - } - } - return ranges.flat().sort(sortNumber); - } - - showEditor() { - window.scrollTo(0, 0); - if (!this.root.querySelector("ha-app-layout editor")) { - const container = document.createElement("editor"); - const nest = document.createElement("div"); - nest.style.cssText = ` - padding: 20px; - max-width: 600px; - margin: 15px auto; - background: var(--paper-card-background-color); - border: 6px solid var(--paper-card-background-color); - `; - container.style.cssText = ` - width: 100%; - min-height: 100%; - box-sizing: border-box; - position: absolute; - background: var(--background-color, grey); - z-index: 2; - padding: 5px; - `; - this.root - .querySelector("ha-app-layout") - .insertBefore(container, this.view); - container.appendChild(nest); - nest.appendChild(document.createElement("compact-custom-header-editor")); - } - } - - getViewIndex(viewString) { - let views = this.lovelace.config.views; - if (isNaN(viewString)) { - for (let view in views) { - if ( - views[view]["title"] == viewString || - views[view]["path"] == viewString - ) { - return view; - } - } - } else { - return parseInt(viewString); - } - } - - reverseObject(object) { - let newObject = {}; - let keys = []; - for (let key in object) keys.push(key); - for (let i = keys.length - 1; i >= 0; i--) { - let value = object[keys[i]]; - newObject[keys[i]] = value; - } - return newObject; - } - - templateEval(template, states) { - let entity = states; - try { - if (template.includes("return")) { - return eval(`(function() {${template}}())`); - } else { - return eval(template); - } - } catch (e) { - console.log( - `%cCCH Template Failed:%c\n${template}\n%c${e}`, - "text-decoration: underline;", - "", - "color: red;" - ); - } - } - - swipeNavigation(tabs, tabContainer) { - // To make it easier to update lovelace-swipe-navigation - // keep this as close to the standalone lovelace addon as possible. - if (!tabContainer) return; - let swipe_amount = this.cchConfig.swipe_amount || 15; - let swipe_groups = this.cchConfig.swipe_groups; - let animate = this.cchConfig.swipe_animate || "none"; - let skip_tabs = this.cchConfig.swipe_skip - ? this.buildRanges(this.cchConfig.swipe_skip.split(",")) - : []; - let wrap = - this.cchConfig.swipe_wrap != undefined ? this.cchConfig.swipe_wrap : true; - let prevent_default = - this.cchConfig.swipe_prevent_default != undefined - ? this.cchConfig.swipe_prevent_default - : false; - - swipe_amount /= 10 ** 2; - const appLayout = this.root.querySelector("ha-app-layout"); - let inGroup = true; - let xDown; - let yDown; - let xDiff; - let yDiff; - let activeTab; - let firstTab; - let lastTab; - let left; - let fTabs; - - appLayout.addEventListener("touchstart", handleTouchStart.bind(this), { - passive: true - }); - appLayout.addEventListener("touchmove", handleTouchMove, { - passive: false - }); - appLayout.addEventListener("touchend", handleTouchEnd, { passive: true }); - - click = click.bind(this); - clearClassNames = clearClassNames.bind(this); - animation = animation.bind(this); - - if (!this.root.querySelector("#cch_swipe_animation")) { - let swipeAnimations = document.createElement("style"); - swipeAnimations.setAttribute("id", "cch_swipe_animation"); - swipeAnimations.innerHTML = ` - @keyframes swipeOutRight, swipeOutLeft { - 0% { transform: translateX(0px); opacity: 1; } - } - @keyframes swipeOutRight { - 100% { transform: translateX(${screen.width / 1.5}px); opacity: 0; } - } - @keyframes swipeOutLeft { - 100% { transform: translateX(-${screen.width / 1.5}px); opacity: 0; } - } - @keyframes swipeInRight, swipeInLeft { - 100% { transform: translateX(0px); opacity: 1; } - } - @keyframes swipeInRight { - 0% { transform: translateX(${screen.width / 1.5}px); opacity: 0; } - } - @keyframes swipeInLeft { - 0% { transform: translateX(-${screen.width / 1.5}px); opacity: 0; } - } - @keyframes fadeOut { - 0% { opacity: 1; } - 100% { opacity: 0; } - } - @keyframes fadeIn { - 0% { opacity: 0; } - 100% { opacity: 1; } - } - @keyframes flipOut { - 0% { transform: rotatey(0deg); opacity: 1; } - 100% { transform: rotatey(90deg); opacity: 0; } - } - @keyframes flipIn{ - 0% { transform: rotatey(90deg); opacity: 0; } - 100% { transform: rotatey(0deg); opacity: 1; } - } - .swipeOutRight { animation: swipeOutRight .20s 1; } - .swipeOutLeft { animation: swipeOutLeft .20s 1; } - .swipeInRight { animation: swipeInRight .20s 1; } - .swipeInLeft { animation: swipeInLeft .20s 1; } - .fadeIn { animation: fadeIn .20s 1; } - .fadeOut { animation: fadeOut .20s 1; } - .flipIn { animation: flipIn .20s 1; } - .flipOut { animation: flipOut .20s 1; } - .swipeOutRight, - .swipeOutLeft, - .swipeInRight, - .swipeInLeft, - .fadeIn, - .fadeOut, - .flipIn, - .flipOut { - animation-fill-mode: forwards; - } - `; - this.view.parentNode.appendChild(swipeAnimations); - } - - function handleTouchStart(event) { - filterTabs(this.cchConfig); - if (swipe_groups && !inGroup) return; - let ignored = [ - "APP-HEADER", - "HA-SLIDER", - "SWIPE-CARD", - "HUI-MAP-CARD", - "ROUND-SLIDER", - "HUI-THERMOSTAT-CARD" - ]; - let path = (event.composedPath && event.composedPath()) || event.path; - if (path) { - for (let element of path) { - if (element.nodeName == "HUI-VIEW") break; - else if (ignored.includes(element.nodeName)) return; - } - } - xDown = event.touches[0].clientX; - yDown = event.touches[0].clientY; - } - - function handleTouchMove(event) { - if (xDown && yDown) { - xDiff = xDown - event.touches[0].clientX; - yDiff = yDown - event.touches[0].clientY; - if (Math.abs(xDiff) > Math.abs(yDiff) && prevent_default) { - event.preventDefault(); - } - } - } - - function handleTouchEnd() { - if (activeTab < 0 || Math.abs(xDiff) < Math.abs(yDiff)) { - xDown = yDown = xDiff = yDiff = null; - return; - } - if (xDiff > Math.abs(screen.width * swipe_amount)) { - left = false; - if (!wrap && fTabs[activeTab] == lastTab) return; - else if (fTabs[activeTab] == lastTab && wrap) click(firstTab); - else click(fTabs[activeTab + 1]); - } else if (xDiff < -Math.abs(screen.width * swipe_amount)) { - left = true; - if (!wrap && fTabs[activeTab] == firstTab) return; - else if (fTabs[activeTab] == firstTab && wrap) click(lastTab); - else click(fTabs[activeTab - 1]); - } - xDown = yDown = xDiff = yDiff = null; - } - - function filterTabs(config) { - let currentTab = tabs.indexOf( - tabContainer.querySelector(".iron-selected") - ); - if (swipe_groups) { - let groups = swipe_groups.replace(/, /g, ",").split(","); - for (let group in groups) { - let firstLast = groups[group].replace(/ /g, "").split("to"); - if ( - wrap && - currentTab >= firstLast[0] && - currentTab <= firstLast[1] - ) { - inGroup = true; - firstTab = tabs[parseInt(firstLast[0])]; - lastTab = tabs[parseInt(firstLast[1])]; - fTabs = tabs.filter( - element => - tabs.indexOf(element) >= firstLast[0] && - tabs.indexOf(element) <= firstLast[1] - ); - break; - } else { - inGroup = false; - } - } - } - if (config.swipe_skip_hidden) { - fTabs = tabs.filter( - element => - !skip_tabs.includes(tabs.indexOf(element)) && - getComputedStyle(element, null).display != "none" - ); - } else { - fTabs = tabs.filter( - element => !skip_tabs.includes(tabs.indexOf(element)) - ); - } - if (!swipe_groups) { - firstTab = fTabs[0]; - lastTab = fTabs[fTabs.length - 1]; - } - activeTab = fTabs.indexOf(tabContainer.querySelector(".iron-selected")); - } - - function animation(secs, transform, opacity, timeout) { - setTimeout(() => { - this.view.style.transition = `transform ${secs}s, opacity ${secs}s`; - this.view.style.transform = transform ? transform : ""; - this.view.style.opacity = opacity; - }, timeout); - } - - function clearClassNames(huiView) { - [ - "swipeOutRight", - "swipeOutLeft", - "swipeInRight", - "swipeInLeft", - "fadeIn", - "fadeOut", - "flipIn", - "flipOut" - ].forEach(name => { - if (huiView.classList.contains(name)) { - huiView.classList.remove(name); - } - if (this.view.classList.contains(name)) { - this.view.classList.remove(name); - } - }); - huiView.style.overflowX = ""; - this.view.style.overflowX = ""; - } - - function navigate(tab, timeout) { - setTimeout(() => { - tab.dispatchEvent( - new MouseEvent("click", { bubbles: false, cancelable: true }) - ); - }, timeout); - } - - function click(tab) { - if ( - !tab || - this.animation_running || - (tab.style.display == "none" && this.cchConfig.swipe_skip_hidden) - ) { - return; - } - if (animate) - if ( - !wrap && - ((activeTab == firstTab && left) || (activeTab == lastTab && !left)) - ) { - return; - } else if (animate == "swipe") { - const getHuiView = () => { - return ( - this.view.querySelector("hui-view") || - this.view.querySelector("hui-panel-view") - ); - }; - this.animation_running = true; - let huiView = getHuiView(); - clearClassNames(huiView); - huiView.style.overflowX = "hidden"; - this.view.style.overflowX = "hidden"; - // Swipe view off screen and fade out. - huiView.classList.add(left ? "swipeOutRight" : "swipeOutLeft"); - this.view.classList.add("fadeOut"); - setTimeout(() => { - this.view.style.opacity = "0"; - clearClassNames(huiView); - }, 210); - // Watch for destination view to load. - const observer = new MutationObserver(mutations => { - mutations.forEach(({ addedNodes }) => { - addedNodes.forEach(({ nodeName }) => { - if (nodeName) { - // Swipe view on screen and fade in. - huiView = getHuiView(); - huiView.style.overflowX = "hidden"; - this.view.style.overflowX = "hidden"; - this.view.classList.add("fadeIn"); - huiView.classList.add(left ? "swipeInLeft" : "swipeInRight"); - setTimeout(() => { - this.view.style.opacity = "1"; - clearClassNames(huiView); - }, 210); - observer.disconnect(); - return; - } - }); - }); - }); - observer.observe(this.view, { childList: true }); - // Navigate to next view and trigger the observer. - navigate(tab, 220); - } else if (animate == "fade") { - animation(0.16, "", 0, 0); - const observer = new MutationObserver(mutations => { - mutations.forEach(({ addedNodes }) => { - addedNodes.forEach(({ nodeName }) => { - if (nodeName == "HUI-VIEW" || nodeName == "HUI-PANEL-VIEW") { - animation(0.16, "", 1, 0); - observer.disconnect(); - } - }); - }); - }); - observer.observe(this.view, { childList: true }); - navigate(tab, 170); - } else if (animate == "flip") { - animation(0.25, "rotatey(90deg)", 0.25, 0); - const observer = new MutationObserver(mutations => { - mutations.forEach(({ addedNodes }) => { - addedNodes.forEach(({ nodeName }) => { - if (nodeName == "HUI-VIEW" || nodeName == "HUI-PANEL-VIEW") { - animation(0.25, "rotatey(0deg)", 1, 50); - observer.disconnect(); - } - }); - }); - }); - observer.observe(this.view, { childList: true }); - navigate(tab, 270); - } else { - navigate(tab, 0); - } - this.animation_running = false; - } - } - - breakingChangeNotification() { - if ( - this.lovelace.config.cch == undefined && - JSON.stringify(this.lovelace.config.views).includes( - "custom:compact-custom-header" - ) - ) { - this.hass.callService("persistent_notification", "create", { - title: "CCH Breaking Change", - notification_id: "CCH_Breaking_Change", - message: - "Compact-Custom-Header's configuration method has changed. You are " + - "receiving this notification because you have updated CCH, but are " + - "using the old config method. Please, visit the [upgrade guide]" + - "(https://maykar.github.io/compact-custom-header/1_1_0_upgrade/) " + - "for more info." - }); - } - } -} - -const cch = new CompactCustomHeader(); -cch.run(); - -class CompactCustomHeaderEditor extends cch.LitElement { - static get properties() { - return { _config: {} }; - } - - firstUpdated() { - this.html = cch.LitElement.prototype.html; - if ( - !customElements.get("paper-toggle-button") && - customElements.get("ha-switch") - ) { - customElements.define( - "paper-toggle-button", - class extends customElements.get("ha-switch") {} - ); - } - - let ll = document.querySelector("home-assistant"); - ll = ll && ll.shadowRoot; - ll = ll && ll.querySelector("home-assistant-main"); - ll = ll && ll.shadowRoot; - ll = ll && ll.querySelector("app-drawer-layout partial-panel-resolver"); - ll = (ll && ll.shadowRoot) || ll; - ll = ll && ll.querySelector("ha-panel-lovelace"); - ll = ll && ll.shadowRoot; - this._lovelace = ll && ll.querySelector("hui-root").lovelace; - - this.deepcopy = this.deepcopy.bind(this); - this._config = this._lovelace.config.cch - ? this.deepcopy(this._lovelace.config.cch) - : {}; - } - - render() { - if (!this._config || !this._lovelace) return this.html``; - return this.html` -
- X -
- ${this.renderStyle()} - - -

Exceptions

-
- ${ - this._config.exceptions - ? this._config.exceptions.map( - (exception, index) => this.html` - - - ` - ) - : "" - } -
- ${ - this._mwc_button - ? this.html` - Add Exception - - ` - : this.html` - Add Exception - - ` - } - -

Current User

-

${cch.hass.user.name}

-

Current User Agent

-
- ${navigator.userAgent} -
-

- ${ - !this.exception - ? this.html` - ${this._save_button} - ` - : "" - } - ${ - !this.exception - ? this.html` - ${this._cancel_button} - ` - : "" - } -

- `; - } - - get _mwc_button() { - return customElements.get("mwc-button") ? true : false; - } - - _close() { - let editor = this.parentNode.parentNode.parentNode.querySelector("editor"); - this.parentNode.parentNode.parentNode.removeChild(editor); - } - - _save() { - for (const key in this._config) { - if (this._config[key] == cch.defaultConfig[key]) delete this._config[key]; - // Remove old config option. - if (typeof this._config.background == "boolean") { - this._config.background = ""; - } - } - let newConfig = { ...this._lovelace.config, ...{ cch: this._config } }; - if (cch.lovelace.mode == "storage") { - try { - this._lovelace.saveConfig(newConfig).then(() => { - window.location.href = window.location.href; - }); - } catch (e) { - alert(`Save failed: ${e}`); - } - } else { - window.prompt( - "Copy to clipboard: Ctrl+C, Enter\n" + - "This option is experimental, check the copied config and backup.", - this.obj2yaml({ cch: newConfig.cch }) - ); - } - } - - get _save_button() { - let text = cch.lovelace.mode == "storage" ? "Save and Reload" : "Copy YAML"; - return this._mwc_button - ? this.html` - ${text} - ` - : this.html` - ${text} - `; - } - get _cancel_button() { - return this._mwc_button - ? this.html` - Cancel - ` - : this.html` - Cancel - `; - } - - _addException() { - let newExceptions; - if (this._config.exceptions) { - newExceptions = this._config.exceptions.slice(0); - newExceptions.push({ conditions: {}, config: {} }); - } else { - newExceptions = [{ conditions: {}, config: {} }]; - } - this._config = { ...this._config, exceptions: newExceptions }; - - cch.fireEvent(this, "config-changed", { config: this._config }); - } - - _configChanged({ detail }) { - if (!this._config) return; - this._config = { ...this._config, ...detail.config }; - cch.fireEvent(this, "config-changed", { config: this._config }); - } - - _exceptionChanged(ev) { - if (!this._config) return; - const target = ev.target.index; - const newExceptions = this._config.exceptions.slice(0); - newExceptions[target] = ev.detail.exception; - this._config = { ...this._config, exceptions: newExceptions }; - - cch.fireEvent(this, "config-changed", { config: this._config }); - } - - _exceptionDelete(ev) { - if (!this._config) return; - const target = ev.target; - const newExceptions = this._config.exceptions.slice(0); - newExceptions.splice(target.index, 1); - this._config = { ...this._config, exceptions: newExceptions }; - - cch.fireEvent(this, "config-changed", { config: this._config }); - this.requestUpdate(); - } - - deepcopy(value) { - if (!(!!value && typeof value == "object")) return value; - if (Object.prototype.toString.call(value) == "[object Date]") { - return new Date(value.getTime()); - } - if (Array.isArray(value)) return value.map(this.deepcopy); - const result = {}; - Object.keys(value).forEach(key => { - result[key] = this.deepcopy(value[key]); - }); - return result; - } - - obj2yaml(obj) { - if (typeof obj == "string") obj = JSON.parse(obj); - const ret = []; - convert(obj, ret); - return ret.join("\n"); - function getType(obj) { - if (obj instanceof Array) { - return "array"; - } else if (typeof obj == "string") { - return "string"; - } else if (typeof obj == "boolean") { - return "boolean"; - } else if (typeof obj == "number") { - return "number"; - } else if (typeof obj == "undefined" || obj === null) { - return "null"; - } else { - return "hash"; - } - } - function convert(obj, ret) { - const type = getType(obj); - switch (getType(obj)) { - case "array": - convertArray(obj, ret); - break; - case "hash": - convertHash(obj, ret); - break; - case "string": - convertString(obj, ret); - break; - case "null": - ret.push("null"); - break; - case "number": - ret.push(obj.toString()); - break; - case "boolean": - ret.push(obj ? "true" : "false"); - break; - } - } - function convertArray(obj, ret) { - if (obj.length === 0) ret.push("[]"); - for (let i = 0; i < obj.length; i++) { - const ele = obj[i]; - const recurse = []; - convert(ele, recurse); - for (let j = 0; j < recurse.length; j++) { - ret.push((j == 0 ? "- " : " ") + recurse[j]); - } - } - } - function convertHash(obj, ret) { - for (const k in obj) { - const recurse = []; - if (obj.hasOwnProperty(k)) { - const ele = obj[k]; - convert(ele, recurse); - const type = getType(ele); - if ( - type == "string" || - type == "null" || - type == "number" || - type == "boolean" - ) { - ret.push(`${k}: ${recurse[0]}`); - } else { - ret.push(`${k}: `); - for (let i = 0; i < recurse.length; i++) { - ret.push(` ${recurse[i]}`); - } - } - } - } - } - function convertString(obj, ret) { - if ((obj.includes("'") && obj.includes('"')) || obj.length > 45) { - if (obj.includes(";")) { - obj = obj.includes("; ") ? obj.split("; ") : obj.split(";"); - obj[0] = `>\n ${obj[0]}`; - if (obj[obj.length - 1].trim() == "") obj.pop(); - obj = obj.join(";\n "); - obj = obj.replace(/\n$/, ""); - ret.push(obj); - } else { - ret.push(`>\n ${obj}`); - } - } else if (obj.includes('"')) { - obj = obj.replace(/\n$/, ""); - ret.push(`'${obj}'`); - } else { - obj = obj.replace(/\n$/, ""); - ret.push(`"${obj}"`); - } - } - } - - renderStyle() { - return this.html` - - `; - } -} - -customElements.define( - "compact-custom-header-editor", - CompactCustomHeaderEditor -); - -class CchConfigEditor extends cch.LitElement { - static get properties() { - return { - defaultConfig: {}, - config: {}, - exception: {}, - _closed: {} - }; - } - - constructor() { - super(); - this.buttonOptions = ["show", "hide", "clock", "overflow"]; - this.overflowOptions = ["show", "hide", "clock"]; - this.swipeAnimation = ["none", "swipe", "fade", "flip"]; - } - - get _clock() { - return ( - this.getConfig("menu") == "clock" || - this.getConfig("voice") == "clock" || - this.getConfig("notifications") == "clock" || - this.getConfig("options") == "clock" - ); - } - - getConfig(item) { - return this.config[item] !== undefined - ? this.config[item] - : cch.defaultConfig[item]; - } - - render() { - this.exception = this.exception !== undefined && this.exception !== false; - return this.html` - - - - ${ - !this.exception - ? this.html` -

- Compact Custom Header  ₁.₄.₉ -

-

- - - - Docs    - - - - Github    - - - - Forums -

- ${ - this.getConfig("warning") - ? this.html` -
-
- Modifying options marked with a - or hiding the options button will remove your ability to - edit from the UI. You can disable CCH by adding - "?disable_cch" to the end of your URL to temporarily restore - the default header. -
-
- ` - : "" - } - ` - : "" - } - ${this.renderStyle()} -
- - Disable CCH - - - Compact Header - - - Kiosk Mode - ${ - this.getConfig("warning") - ? this.html` - - ` - : "" - } - - - Display Header - ${ - this.getConfig("warning") - ? this.html` - - ` - : "" - } - - - Display Tab Chevrons - - - Hidden Tab Redirect - - - Hide & Disable Sidebar - - - Close Sidebar - - ${ - !this.exception - ? this.html` - - Display CCH Warnings - - ` - : "" - } - - Swipe Open Sidebar - -
-

Menu Items

-
- - Hide "Configure UI" - ${ - this.getConfig("warning") - ? this.html` - - ` - : "" - } - - - Hide "Help" - - - Hide "Unused Entities" - -
-

Buttons

-
-
- - - - ${this.buttonOptions.map( - option => this.html` - ${option} - ` - )} - - -
-
- - - - ${this.buttonOptions.map( - option => this.html` - ${option} - ` - )} - - -
-
-
-
- - - - ${this.overflowOptions.map( - option => this.html` - ${option} - ` - )} - - -
-
- - - - ${this.buttonOptions.map( - option => this.html` - ${option} - ` - )} - - -
-
- ${ - this._clock - ? this.html` -

Clock Options

-
- - - 12 - 24 - - - - - -
- - AM / PM - - Date -
-
- ` - : "" - } -

Tabs

- - 0 ? "1" : "0"}" - > - Hide Tabs - Show Tabs - - -
-
0 ? "initial" : "none" - }" - > - - -
-
0 ? "none" : "initial" - }" - > - - -
- - -
-

Swipe Navigation

-
- - Swipe Navigation - - ${ - this.config.swipe - ? this.html` - - Wrapping - - - Prevent Default - -
-
-
- - - ${this.swipeAnimation.map( - option => this.html` - ${option} - ` - )} - - -
- - -
- - - - ` - : "" - } - - `; - } - - _toggleCard() { - this._closed = !this._closed; - cch.fireEvent(this, "iron-resize"); - } - - _tabVisibility() { - let show = this.shadowRoot.querySelector("#show"); - let hide = this.shadowRoot.querySelector("#hide"); - if (this.shadowRoot.querySelector("#tabs").value == "Hide Tabs") { - show.style.display = "none"; - hide.style.display = "initial"; - } else { - hide.style.display = "none"; - show.style.display = "initial"; - } - } - - _valueChanged(ev) { - if (!this.config) return; - if (ev.target.configValue) { - if (ev.target.value === "") { - delete this.config[ev.target.configValue]; - } else { - this.config = { - ...this.config, - [ev.target.configValue]: - ev.target.checked !== undefined - ? ev.target.checked - : ev.target.value - }; - } - } - cch.fireEvent(this, "cch-config-changed", { config: this.config }); - } - - renderStyle() { - return this.html` - - `; - } -} - -customElements.define("cch-config-editor", CchConfigEditor); - -class CchExceptionEditor extends cch.LitElement { - static get properties() { - return { config: {}, exception: {}, _closed: {} }; - } - - constructor() { - super(); - this._closed = true; - } - - render() { - if (!this.exception) { - return this.html``; - } - return this.html` - ${this.renderStyle()} - - - - -
-
- ${Object.values(this.exception.conditions) - .join(", ") - .substring(0, 40) || "New Exception"} - - - - -
-

Conditions

- - -

Config

- - -
-
- `; - } - - renderStyle() { - return this.html` - - `; - } - - _toggleCard() { - this._closed = !this._closed; - cch.fireEvent(this, "iron-resize"); - } - - _deleteException() { - cch.fireEvent(this, "cch-exception-delete"); - } - - _conditionsChanged({ detail }) { - if (!this.exception) return; - const newException = { ...this.exception, conditions: detail.conditions }; - cch.fireEvent(this, "cch-exception-changed", { exception: newException }); - } - - _configChanged(ev) { - ev.stopPropagation(); - if (!this.exception) return; - const newException = { ...this.exception, config: ev.detail.config }; - cch.fireEvent(this, "cch-exception-changed", { exception: newException }); - } -} - -customElements.define("cch-exception-editor", CchExceptionEditor); - -class CchConditionsEditor extends cch.LitElement { - static get properties() { - return { conditions: {} }; - } - get _user() { - return this.conditions.user || ""; - } - get _user_agent() { - return this.conditions.user_agent || ""; - } - get _media_query() { - return this.conditions.media_query || ""; - } - get _query_string() { - return this.conditions.query_string || ""; - } - - render() { - if (!this.conditions) return this.html``; - return this.html` - - - - - - - - - `; - } - - _valueChanged(ev) { - if (!this.conditions) return; - const target = ev.target; - if (this[`_${target.configValue}`] === target.value) return; - if (target.configValue) { - if (target.value === "") { - delete this.conditions[target.configValue]; - } else { - this.conditions = { - ...this.conditions, - [target.configValue]: target.value - }; - } - } - cch.fireEvent(this, "cch-conditions-changed", { - conditions: this.conditions - }); - } -} - -customElements.define("cch-conditions-editor", CchConditionsEditor); diff --git a/www/community/compact-custom-header/compact-custom-header.js.gz b/www/community/compact-custom-header/compact-custom-header.js.gz deleted file mode 100644 index 5c8293c93a0257ba488107e9dcd2bd8980cb5194..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19672 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