diff --git a/custom_components/browser_mod/browser_mod.js b/custom_components/browser_mod/browser_mod.js index f6ae008..4ecd630 100644 --- a/custom_components/browser_mod/browser_mod.js +++ b/custom_components/browser_mod/browser_mod.js @@ -37,33 +37,52 @@ function __decorate(decorators, target, key, desc) { * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ -const t$2=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,e$5=Symbol(),n$4=new Map;class s$3{constructor(t,n){if(this._$cssResult$=!0,n!==e$5)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t;}get styleSheet(){let e=n$4.get(this.cssText);return t$2&&void 0===e&&(n$4.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const o$4=t=>new s$3("string"==typeof t?t:t+"",e$5),r$2=(t,...n)=>{const o=1===t.length?t[0]:n.reduce(((e,n,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;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.")})(n)+t[s+1]),t[0]);return new s$3(o,e$5)},i$3=(e,n)=>{t$2?e.adoptedStyleSheets=n.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):n.forEach((t=>{const n=document.createElement("style"),s=window.litNonce;void 0!==s&&n.setAttribute("nonce",s),n.textContent=t.cssText,e.appendChild(n);}));},S$1=t$2?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const n of t.cssRules)e+=n.cssText;return o$4(e)})(t):t; +const t$3=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,e$5=Symbol(),n$4=new Map;class s$3{constructor(t,n){if(this._$cssResult$=!0,n!==e$5)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t;}get styleSheet(){let e=n$4.get(this.cssText);return t$3&&void 0===e&&(n$4.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const o$5=t=>new s$3("string"==typeof t?t:t+"",e$5),r$2=(t,...n)=>{const o=1===t.length?t[0]:n.reduce(((e,n,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;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.")})(n)+t[s+1]),t[0]);return new s$3(o,e$5)},i$4=(e,n)=>{t$3?e.adoptedStyleSheets=n.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):n.forEach((t=>{const n=document.createElement("style"),s=window.litNonce;void 0!==s&&n.setAttribute("nonce",s),n.textContent=t.cssText,e.appendChild(n);}));},S$1=t$3?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const n of t.cssRules)e+=n.cssText;return o$5(e)})(t):t; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */var s$2;const e$4=window.trustedTypes,r$1=e$4?e$4.emptyScript:"",h$1=window.reactiveElementPolyfillSupport,o$3={toAttribute(t,i){switch(i){case Boolean:t=t?r$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,i){let s=t;switch(i){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t);}catch(t){s=null;}}return s}},n$3=(t,i)=>i!==t&&(i==i||t==t),l$2={attribute:!0,type:String,converter:o$3,reflect:!1,hasChanged:n$3};class a$1 extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o();}static addInitializer(t){var i;null!==(i=this.l)&&void 0!==i||(this.l=[]),this.l.push(t);}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((i,s)=>{const e=this._$Eh(s,i);void 0!==e&&(this._$Eu.set(e,s),t.push(e));})),t}static createProperty(t,i=l$2){if(i.state&&(i.attribute=!1),this.finalize(),this.elementProperties.set(t,i),!i.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,e=this.getPropertyDescriptor(t,s,i);void 0!==e&&Object.defineProperty(this.prototype,t,e);}}static getPropertyDescriptor(t,i,s){return {get(){return this[i]},set(e){const r=this[t];this[i]=e,this.requestUpdate(t,r,s);},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||l$2}static finalize(){if(this.hasOwnProperty("finalized"))return !1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),this.elementProperties=new Map(t.elementProperties),this._$Eu=new Map,this.hasOwnProperty("properties")){const t=this.properties,i=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of i)this.createProperty(s,t[s]);}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(i){const s=[];if(Array.isArray(i)){const e=new Set(i.flat(1/0).reverse());for(const i of e)s.unshift(S$1(i));}else void 0!==i&&s.push(S$1(i));return s}static _$Eh(t,i){const s=i.attribute;return !1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}o(){var t;this._$Ep=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(t=this.constructor.l)||void 0===t||t.forEach((t=>t(this)));}addController(t){var i,s;(null!==(i=this._$Eg)&&void 0!==i?i:this._$Eg=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t));}removeController(t){var i;null===(i=this._$Eg)||void 0===i||i.splice(this._$Eg.indexOf(t)>>>0,1);}_$Em(){this.constructor.elementProperties.forEach(((t,i)=>{this.hasOwnProperty(i)&&(this._$Et.set(i,this[i]),delete this[i]);}));}createRenderRoot(){var t;const s=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return i$3(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostConnected)||void 0===i?void 0:i.call(t)}));}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostDisconnected)||void 0===i?void 0:i.call(t)}));}attributeChangedCallback(t,i,s){this._$AK(t,s);}_$ES(t,i,s=l$2){var e,r;const h=this.constructor._$Eh(t,s);if(void 0!==h&&!0===s.reflect){const n=(null!==(r=null===(e=s.converter)||void 0===e?void 0:e.toAttribute)&&void 0!==r?r:o$3.toAttribute)(i,s.type);this._$Ei=t,null==n?this.removeAttribute(h):this.setAttribute(h,n),this._$Ei=null;}}_$AK(t,i){var s,e,r;const h=this.constructor,n=h._$Eu.get(t);if(void 0!==n&&this._$Ei!==n){const t=h.getPropertyOptions(n),l=t.converter,a=null!==(r=null!==(e=null===(s=l)||void 0===s?void 0:s.fromAttribute)&&void 0!==e?e:"function"==typeof l?l:null)&&void 0!==r?r:o$3.fromAttribute;this._$Ei=n,this[n]=a(i,t.type),this._$Ei=null;}}requestUpdate(t,i,s){let e=!0;void 0!==t&&(((s=s||this.constructor.getPropertyOptions(t)).hasChanged||n$3)(this[t],i)?(this._$AL.has(t)||this._$AL.set(t,i),!0===s.reflect&&this._$Ei!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):e=!1),!this.isUpdatePending&&e&&(this._$Ep=this._$E_());}async _$E_(){this.isUpdatePending=!0;try{await this._$Ep;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((t,i)=>this[i]=t)),this._$Et=void 0);let i=!1;const s=this._$AL;try{i=this.shouldUpdate(s),i?(this.willUpdate(s),null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostUpdate)||void 0===i?void 0:i.call(t)})),this.update(s)):this._$EU();}catch(t){throw i=!1,this._$EU(),t}i&&this._$AE(s);}willUpdate(t){}_$AE(t){var i;null===(i=this._$Eg)||void 0===i||i.forEach((t=>{var i;return null===(i=t.hostUpdated)||void 0===i?void 0:i.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t);}_$EU(){this._$AL=new Map,this.isUpdatePending=!1;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(t){return !0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,i)=>this._$ES(i,this[i],t))),this._$EC=void 0),this._$EU();}updated(t){}firstUpdated(t){}}a$1.finalized=!0,a$1.elementProperties=new Map,a$1.elementStyles=[],a$1.shadowRootOptions={mode:"open"},null==h$1||h$1({ReactiveElement:a$1}),(null!==(s$2=globalThis.reactiveElementVersions)&&void 0!==s$2?s$2:globalThis.reactiveElementVersions=[]).push("1.3.1"); + */var s$2;const e$4=window.trustedTypes,r$1=e$4?e$4.emptyScript:"",h$1=window.reactiveElementPolyfillSupport,o$4={toAttribute(t,i){switch(i){case Boolean:t=t?r$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,i){let s=t;switch(i){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t);}catch(t){s=null;}}return s}},n$3=(t,i)=>i!==t&&(i==i||t==t),l$2={attribute:!0,type:String,converter:o$4,reflect:!1,hasChanged:n$3};class a$1 extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o();}static addInitializer(t){var i;null!==(i=this.l)&&void 0!==i||(this.l=[]),this.l.push(t);}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((i,s)=>{const e=this._$Eh(s,i);void 0!==e&&(this._$Eu.set(e,s),t.push(e));})),t}static createProperty(t,i=l$2){if(i.state&&(i.attribute=!1),this.finalize(),this.elementProperties.set(t,i),!i.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,e=this.getPropertyDescriptor(t,s,i);void 0!==e&&Object.defineProperty(this.prototype,t,e);}}static getPropertyDescriptor(t,i,s){return {get(){return this[i]},set(e){const r=this[t];this[i]=e,this.requestUpdate(t,r,s);},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||l$2}static finalize(){if(this.hasOwnProperty("finalized"))return !1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),this.elementProperties=new Map(t.elementProperties),this._$Eu=new Map,this.hasOwnProperty("properties")){const t=this.properties,i=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of i)this.createProperty(s,t[s]);}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(i){const s=[];if(Array.isArray(i)){const e=new Set(i.flat(1/0).reverse());for(const i of e)s.unshift(S$1(i));}else void 0!==i&&s.push(S$1(i));return s}static _$Eh(t,i){const s=i.attribute;return !1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}o(){var t;this._$Ep=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(t=this.constructor.l)||void 0===t||t.forEach((t=>t(this)));}addController(t){var i,s;(null!==(i=this._$Eg)&&void 0!==i?i:this._$Eg=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t));}removeController(t){var i;null===(i=this._$Eg)||void 0===i||i.splice(this._$Eg.indexOf(t)>>>0,1);}_$Em(){this.constructor.elementProperties.forEach(((t,i)=>{this.hasOwnProperty(i)&&(this._$Et.set(i,this[i]),delete this[i]);}));}createRenderRoot(){var t;const s=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return i$4(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostConnected)||void 0===i?void 0:i.call(t)}));}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostDisconnected)||void 0===i?void 0:i.call(t)}));}attributeChangedCallback(t,i,s){this._$AK(t,s);}_$ES(t,i,s=l$2){var e,r;const h=this.constructor._$Eh(t,s);if(void 0!==h&&!0===s.reflect){const n=(null!==(r=null===(e=s.converter)||void 0===e?void 0:e.toAttribute)&&void 0!==r?r:o$4.toAttribute)(i,s.type);this._$Ei=t,null==n?this.removeAttribute(h):this.setAttribute(h,n),this._$Ei=null;}}_$AK(t,i){var s,e,r;const h=this.constructor,n=h._$Eu.get(t);if(void 0!==n&&this._$Ei!==n){const t=h.getPropertyOptions(n),l=t.converter,a=null!==(r=null!==(e=null===(s=l)||void 0===s?void 0:s.fromAttribute)&&void 0!==e?e:"function"==typeof l?l:null)&&void 0!==r?r:o$4.fromAttribute;this._$Ei=n,this[n]=a(i,t.type),this._$Ei=null;}}requestUpdate(t,i,s){let e=!0;void 0!==t&&(((s=s||this.constructor.getPropertyOptions(t)).hasChanged||n$3)(this[t],i)?(this._$AL.has(t)||this._$AL.set(t,i),!0===s.reflect&&this._$Ei!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):e=!1),!this.isUpdatePending&&e&&(this._$Ep=this._$E_());}async _$E_(){this.isUpdatePending=!0;try{await this._$Ep;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((t,i)=>this[i]=t)),this._$Et=void 0);let i=!1;const s=this._$AL;try{i=this.shouldUpdate(s),i?(this.willUpdate(s),null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostUpdate)||void 0===i?void 0:i.call(t)})),this.update(s)):this._$EU();}catch(t){throw i=!1,this._$EU(),t}i&&this._$AE(s);}willUpdate(t){}_$AE(t){var i;null===(i=this._$Eg)||void 0===i||i.forEach((t=>{var i;return null===(i=t.hostUpdated)||void 0===i?void 0:i.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t);}_$EU(){this._$AL=new Map,this.isUpdatePending=!1;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(t){return !0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,i)=>this._$ES(i,this[i],t))),this._$EC=void 0),this._$EU();}updated(t){}firstUpdated(t){}}a$1.finalized=!0,a$1.elementProperties=new Map,a$1.elementStyles=[],a$1.shadowRootOptions={mode:"open"},null==h$1||h$1({ReactiveElement:a$1}),(null!==(s$2=globalThis.reactiveElementVersions)&&void 0!==s$2?s$2:globalThis.reactiveElementVersions=[]).push("1.3.1"); /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ -var t$1;const i$2=globalThis.trustedTypes,s$1=i$2?i$2.createPolicy("lit-html",{createHTML:t=>t}):void 0,e$3=`lit$${(Math.random()+"").slice(9)}$`,o$2="?"+e$3,n$2=`<${o$2}>`,l$1=document,h=(t="")=>l$1.createComment(t),r=t=>null===t||"object"!=typeof t&&"function"!=typeof t,d=Array.isArray,u=t=>{var i;return d(t)||"function"==typeof(null===(i=t)||void 0===i?void 0:i[Symbol.iterator])},c=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,v=/-->/g,a=/>/g,f=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,_=/'/g,m=/"/g,g=/^(?:script|style|textarea|title)$/i,p=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),$=p(1),b=Symbol.for("lit-noChange"),w=Symbol.for("lit-nothing"),T=new WeakMap,x=(t,i,s)=>{var e,o;const n=null!==(e=null==s?void 0:s.renderBefore)&&void 0!==e?e:i;let l=n._$litPart$;if(void 0===l){const t=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;n._$litPart$=l=new N(i.insertBefore(h(),t),t,void 0,null!=s?s:{});}return l._$AI(t),l},A=l$1.createTreeWalker(l$1,129,null,!1),C=(t,i)=>{const o=t.length-1,l=[];let h,r=2===i?"":"",d=c;for(let i=0;i"===u[0]?(d=null!=h?h:c,p=-1):void 0===u[1]?p=-2:(p=d.lastIndex-u[2].length,o=u[1],d=void 0===u[3]?f:'"'===u[3]?m:_):d===m||d===_?d=f:d===v||d===a?d=c:(d=f,h=void 0);const y=d===f&&t[i+1].startsWith("/>")?" ":"";r+=d===c?s+n$2:p>=0?(l.push(o),s.slice(0,p)+"$lit$"+s.slice(p)+e$3+y):s+e$3+(-2===p?(l.push(void 0),i):y);}const u=r+(t[o]||"")+(2===i?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return [void 0!==s$1?s$1.createHTML(u):u,l]};class E{constructor({strings:t,_$litType$:s},n){let l;this.parts=[];let r=0,d=0;const u=t.length-1,c=this.parts,[v,a]=C(t,s);if(this.el=E.createElement(v,n),A.currentNode=this.el.content,2===s){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(l=A.nextNode())&&c.length0){l.textContent=i$2?i$2.emptyScript:"";for(let i=0;i2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=w;}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,i=this,s,e){const o=this.strings;let n=!1;if(void 0===o)t=P(this,t,i,0),n=!r(t)||t!==this._$AH&&t!==b,n&&(this._$AH=t);else {const e=t;let l,h;for(t=o[0],l=0;lt}):void 0,e$3=`lit$${(Math.random()+"").slice(9)}$`,o$3="?"+e$3,n$2=`<${o$3}>`,l$1=document,h=(t="")=>l$1.createComment(t),r=t=>null===t||"object"!=typeof t&&"function"!=typeof t,d=Array.isArray,u=t=>{var i;return d(t)||"function"==typeof(null===(i=t)||void 0===i?void 0:i[Symbol.iterator])},c=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,v=/-->/g,a=/>/g,f=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,_=/'/g,m=/"/g,g=/^(?:script|style|textarea|title)$/i,p=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),$=p(1),b=Symbol.for("lit-noChange"),w=Symbol.for("lit-nothing"),T=new WeakMap,x=(t,i,s)=>{var e,o;const n=null!==(e=null==s?void 0:s.renderBefore)&&void 0!==e?e:i;let l=n._$litPart$;if(void 0===l){const t=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;n._$litPart$=l=new N(i.insertBefore(h(),t),t,void 0,null!=s?s:{});}return l._$AI(t),l},A=l$1.createTreeWalker(l$1,129,null,!1),C=(t,i)=>{const o=t.length-1,l=[];let h,r=2===i?"":"",d=c;for(let i=0;i"===u[0]?(d=null!=h?h:c,p=-1):void 0===u[1]?p=-2:(p=d.lastIndex-u[2].length,o=u[1],d=void 0===u[3]?f:'"'===u[3]?m:_):d===m||d===_?d=f:d===v||d===a?d=c:(d=f,h=void 0);const y=d===f&&t[i+1].startsWith("/>")?" ":"";r+=d===c?s+n$2:p>=0?(l.push(o),s.slice(0,p)+"$lit$"+s.slice(p)+e$3+y):s+e$3+(-2===p?(l.push(void 0),i):y);}const u=r+(t[o]||"")+(2===i?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return [void 0!==s$1?s$1.createHTML(u):u,l]};class E{constructor({strings:t,_$litType$:s},n){let l;this.parts=[];let r=0,d=0;const u=t.length-1,c=this.parts,[v,a]=C(t,s);if(this.el=E.createElement(v,n),A.currentNode=this.el.content,2===s){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(l=A.nextNode())&&c.length0){l.textContent=i$3?i$3.emptyScript:"";for(let i=0;i2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=w;}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,i=this,s,e){const o=this.strings;let n=!1;if(void 0===o)t=P(this,t,i,0),n=!r(t)||t!==this._$AH&&t!==b,n&&(this._$AH=t);else {const e=t;let l,h;for(t=o[0],l=0;l"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(n){n.createProperty(e.key,i);}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this));},finisher(n){n.createProperty(e.key,i);}};function e$2(e){return (n,t)=>void 0!==t?((i,e,n)=>{e.constructor.createProperty(n,i);})(e,n,t):i$1(e,n)} +const i$2=(i,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(n){n.createProperty(e.key,i);}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this));},finisher(n){n.createProperty(e.key,i);}};function e$2(e){return (n,t)=>void 0!==t?((i,e,n)=>{e.constructor.createProperty(n,i);})(e,n,t):i$2(e,n)} + +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */function t$1(t){return e$2({...t,state:!0})} + +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +const o$1=({finisher:e,descriptor:t})=>(o,n)=>{var r;if(void 0===n){const n=null!==(r=o.originalKey)&&void 0!==r?r:o.key,i=null!=t?{kind:"method",placement:"prototype",key:n,descriptor:t(o.key)}:{...o,key:n};return null!=e&&(i.finisher=function(t){e(t,n);}),i}{const r=o.constructor;void 0!==t&&Object.defineProperty(o,n,t(n)),null==e||e(r,n);}}; + +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */function i$1(i,n){return o$1({descriptor:o=>{const t={get(){var o,n;return null!==(n=null===(o=this.renderRoot)||void 0===o?void 0:o.querySelector(i))&&void 0!==n?n:null},enumerable:!0,configurable:!0};if(n){const n="symbol"==typeof o?Symbol():"__"+o;t.get=function(){var o,t;return void 0===this[n]&&(this[n]=null!==(t=null===(o=this.renderRoot)||void 0===o?void 0:o.querySelector(i))&&void 0!==t?t:null),this[n]};}return t}})} /** * @license @@ -258,6 +277,18 @@ const loadLoadCardHelpers = async () => { }, ]); await ((_c = (_b = (_a = routes === null || routes === void 0 ? void 0 : routes.routes) === null || _a === void 0 ? void 0 : _a.a) === null || _b === void 0 ? void 0 : _b.load) === null || _c === void 0 ? void 0 : _c.call(_b)); +}; +const loadHaForm = async () => { + if (customElements.get("ha-form")) + return; + await loadLoadCardHelpers(); + const helpers = await window.loadCardHelpers(); + if (!helpers) + return; + const card = await helpers.createCardElement({ type: "entity" }); + if (!card) + return; + await card.getConfigElement(); }; const ID_STORAGE_KEY = "browser_mod-browser-id"; @@ -891,10 +922,7 @@ const ServicesMixin = (SuperClass) => { ]; for (const service of cmds) { this.addEventListener(`command-${service}`, (ev) => { - this._service_action({ - service, - data: ev.detail, - }); + this.service(service, ev.detail); }); } document.body.addEventListener("ll-custom", (ev) => { @@ -903,6 +931,9 @@ const ServicesMixin = (SuperClass) => { } }); } + async service(service, data) { + this._service_action({ service, data }); + } async _service_action({ service, data }) { let _service = service; if (!_service.startsWith("browser_mod.") && _service.includes(".")) ; @@ -1019,7 +1050,7 @@ class BrowserModPopup extends s { }, 10); } } - async setupDialog(title, content, { right_button = undefined, right_button_action = undefined, left_button = undefined, left_button_action = undefined, dismissable = true, dismiss_action = undefined, timeout = undefined, timeout_action = undefined, } = {}) { + async setupDialog(title, content, { right_button = undefined, right_button_action = undefined, left_button = undefined, left_button_action = undefined, dismissable = true, dismiss_action = undefined, timeout = undefined, timeout_action = undefined, size = undefined, } = {}) { this.title = title; if (content && typeof content === "object") { // Create a card from config in content @@ -1046,6 +1077,8 @@ class BrowserModPopup extends s { dismiss_action, timeout_action, }; + this.wide = size === "wide" ? "" : undefined; + this.fullscreen = size === "fullscreen" ? "" : undefined; } _primary() { var _a, _b, _c; @@ -1247,6 +1280,12 @@ __decorate([ __decorate([ e$2() ], BrowserModPopup.prototype, "dismissable", void 0); +__decorate([ + e$2({ reflect: true }) +], BrowserModPopup.prototype, "wide", void 0); +__decorate([ + e$2({ reflect: true }) +], BrowserModPopup.prototype, "fullscreen", void 0); customElements.define("browser-mod-popup", BrowserModPopup); const PopupMixin = (SuperClass) => { return class PopupMixinClass extends SuperClass { @@ -1336,6 +1375,371 @@ var pjson = { dependencies: dependencies }; +const configSchema = [ + { + name: "entity", + label: "Entity", + selector: { entity: {} }, + }, + { + name: "title", + label: "Title", + selector: { text: {} }, + }, + { + name: "size", + selector: { + select: { mode: "dropdown", options: ["normal", "wide", "fullscreen"] }, + }, + }, + { + type: "grid", + schema: [ + { + name: "right_button", + label: "Right button", + selector: { text: {} }, + }, + { + name: "left_button", + label: "Left button", + selector: { text: {} }, + }, + ], + }, + { + type: "grid", + schema: [ + { + name: "right_button_action", + label: "Right button action", + selector: { object: {} }, + }, + { + name: "left_button_action", + label: "Left button action", + selector: { object: {} }, + }, + ], + }, + { + type: "grid", + schema: [ + { + name: "dismissable", + label: "User dismissable", + selector: { boolean: {} }, + }, + { + name: "timeout", + label: "Auto close timeout (ms)", + selector: { number: { mode: "box" } }, + }, + ], + }, + { + type: "grid", + schema: [ + { + name: "dismiss_action", + label: "Dismiss action", + selector: { object: {} }, + }, + { + name: "timeout_action", + label: "Timeout action", + selector: { object: {} }, + }, + ], + }, +]; +class PopupCardEditor extends s { + constructor() { + super(...arguments); + this._selectedTab = 0; + this._cardGUIMode = true; + this._cardGUIModeAvailable = true; + } + setConfig(config) { + this._config = config; + } + connectedCallback() { + super.connectedCallback(); + loadHaForm(); + } + _handleSwitchTab(ev) { + this._selectedTab = parseInt(ev.detail.index, 10); + } + _configChanged(ev) { + ev.stopPropagation(); + if (!this._config) + return; + this._config = Object.assign({}, ev.detail.value); + this.dispatchEvent(new CustomEvent("config-changed", { detail: { config: this._config } })); + } + _cardConfigChanged(ev) { + ev.stopPropagation(); + if (!this._config) + return; + const card = Object.assign({}, ev.detail.config); + this._config = Object.assign(Object.assign({}, this._config), { card }); + this._cardGUIModeAvailable = ev.detail.guiModeAvailable; + this.dispatchEvent(new CustomEvent("config-changed", { detail: { config: this._config } })); + } + _toggleCardMode(ev) { + var _a; + (_a = this._cardEditorEl) === null || _a === void 0 ? void 0 : _a.toggleMode(); + } + _deleteCard(ev) { + if (!this._config) + return; + this._config = Object.assign({}, this._config); + delete this._config.card; + this.dispatchEvent(new CustomEvent("config-changed", { detail: { config: this._config } })); + } + _cardGUIModeChanged(ev) { + ev.stopPropagation(); + this._cardGUIMode = ev.detail.guiMode; + this._cardGUIModeAvailable = ev.detail.guiModeAvailable; + } + render() { + if (!this.hass || !this._config) { + return $ ``; + } + return $ ` +
+
+ + + + +
+
+ ${[this._renderSettingsEditor, this._renderCardEditor][this._selectedTab].bind(this)()} +
+
+ `; + } + _renderSettingsEditor() { + return $ `
+ { var _a; return (_a = s.label) !== null && _a !== void 0 ? _a : s.name; }} + @value-changed=${this._configChanged} + > +
`; + } + _renderCardEditor() { + return $ ` +
+ ${this._config.card + ? $ ` +
+ + ${!this._cardEditorEl || this._cardGUIMode + ? "Show code editor" + : "Show visual editor"} + + + Change card type + +
+ + ` + : $ ` + + `} +
+ `; + } + static get styles() { + return r$2 ` + mwc-tab-bar { + border-bottom: 1px solid var(--divider-color); + } + .box { + margin-top: 8px; + border: 1px solid var(--divider-color); + padding: 12px; + } + .box .toolbar { + display: flex; + justify-content: flex-end; + width: 100%; + gap: 8px; + } + .gui-mode-button { + margin-right: auto; + } + `; + } +} +__decorate([ + t$1() +], PopupCardEditor.prototype, "_config", void 0); +__decorate([ + e$2() +], PopupCardEditor.prototype, "lovelace", void 0); +__decorate([ + e$2() +], PopupCardEditor.prototype, "hass", void 0); +__decorate([ + t$1() +], PopupCardEditor.prototype, "_selectedTab", void 0); +__decorate([ + t$1() +], PopupCardEditor.prototype, "_cardGUIMode", void 0); +__decorate([ + t$1() +], PopupCardEditor.prototype, "_cardGUIModeAvailable", void 0); +__decorate([ + i$1("hui-card-element-editor") +], PopupCardEditor.prototype, "_cardEditorEl", void 0); +(async () => { + while (!window.browser_mod) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + await window.browser_mod.connectionPromise; + if (!customElements.get("popup-card-editor")) + customElements.define("popup-card-editor", PopupCardEditor); + window.customCards = window.customCards || []; + window.customCards.push({ + type: "popup-card", + name: "Popup card", + preview: false, + description: "Replace the more-info dialog for a given entity in the view that includes this card. (Browser Mod)", + }); +})(); + +class PopupCard extends s { + constructor() { + super(); + this.popup = this.popup.bind(this); + } + static getConfigElement() { + return document.createElement("popup-card-editor"); + } + static getStubConfig(hass, entities) { + const entity = entities[0]; + return { + entity, + title: "Custom popup", + dismissable: true, + card: { type: "markdown", content: "This replaces the more-info dialog" }, + }; + } + setConfig(config) { + this._config = config; + (async () => { + const ch = await window.loadCardHelpers(); + this._element = await ch.createCardElement(config.card); + this._element.hass = this.hass; + })(); + } + async connectedCallback() { + super.connectedCallback(); + window.addEventListener("hass-more-info", this.popup); + if (this.parentElement.localName === "hui-card-preview") { + this.editMode = true; + } + } + async disconnectedCallback() { + super.disconnectedCallback(); + window.removeEventListener("hass-more-info", this.popup); + } + popup(ev) { + var _a, _b; + if (((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.entityId) === this._config.entity) { + ev.stopPropagation(); + ev.preventDefault(); + const config = Object.assign({}, this._config); + delete config.card; + (_b = window.browser_mod) === null || _b === void 0 ? void 0 : _b.service("popup", Object.assign({ content: this._config.card }, this._config)); + setTimeout(() => this.dispatchEvent(new CustomEvent("hass-more-info", { + bubbles: true, + composed: true, + cancelable: false, + detail: { entityID: "." }, + })), 50); + } + } + updated(changedProperties) { + super.updated(changedProperties); + if (changedProperties.has("hass")) { + if (this._element) + this._element.hass = this.hass; + } + } + render() { + if (!this.editMode) + return $ ``; + return $ ` +
+

${this._config.title}

+
+ ${this._element}
`; + } + static get styles() { + return r$2 ` + :host { + display: none !important; + } + :host([edit-mode="true"]) { + display: block !important; + border: 1px solid var(--primary-color); + } + h2 { + padding-left: 16px; + padding-top: 16px; + margin: 0; + } + `; + } +} +__decorate([ + e$2() +], PopupCard.prototype, "hass", void 0); +__decorate([ + t$1() +], PopupCard.prototype, "_config", void 0); +__decorate([ + e$2({ attribute: "edit-mode", reflect: true }) +], PopupCard.prototype, "editMode", void 0); +__decorate([ + t$1() +], PopupCard.prototype, "_element", void 0); +(async () => { + while (!window.browser_mod) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + await window.browser_mod.connectionPromise; + if (!customElements.get("popup-card")) + customElements.define("popup-card", PopupCard); +})(); + /* TODO: - Fix nomenclature @@ -1346,6 +1750,7 @@ var pjson = { - Card-mod integration X Timeout X Fullscreen + x Popup-card x Motion/occupancy tracker x Information about interaction requirement x Information about fullykiosk diff --git a/custom_components/browser_mod/services.yaml b/custom_components/browser_mod/services.yaml index fce57e2..1b1cd25 100644 --- a/custom_components/browser_mod/services.yaml +++ b/custom_components/browser_mod/services.yaml @@ -61,6 +61,15 @@ popup: description: "Popup content (Test or lovelace card configuration)" selector: object: + size: + name: Size + selector: + select: + mode: dropdown + options: + - normal + - wide + - fullscreen right_button: name: Right button description: Text of the right button @@ -99,7 +108,7 @@ popup: number: mode: box timeout_action: - name: Timeout + name: Timeout action description: Action to perform when popup is closed by timeout selector: object: diff --git a/js/helpers.ts b/js/helpers.ts index c7755fe..4f0993e 100644 --- a/js/helpers.ts +++ b/js/helpers.ts @@ -75,3 +75,13 @@ export const loadLoadCardHelpers = async () => { ]); await routes?.routes?.a?.load?.(); }; + +export const loadHaForm = async () => { + if (customElements.get("ha-form")) return; + await loadLoadCardHelpers(); + const helpers = await window.loadCardHelpers(); + if (!helpers) return; + const card = await helpers.createCardElement({ type: "entity" }); + if (!card) return; + await card.getConfigElement(); +}; diff --git a/js/plugin/main.ts b/js/plugin/main.ts index 8e00f65..7215945 100644 --- a/js/plugin/main.ts +++ b/js/plugin/main.ts @@ -13,6 +13,7 @@ import { ActivityMixin } from "./activity"; import "./popups"; import { PopupMixin } from "./popups"; import pjson from "../../package.json"; +import "./popup-card"; /* TODO: @@ -24,6 +25,7 @@ import pjson from "../../package.json"; - Card-mod integration X Timeout X Fullscreen + x Popup-card x Motion/occupancy tracker x Information about interaction requirement x Information about fullykiosk diff --git a/js/plugin/popup-card-editor.ts b/js/plugin/popup-card-editor.ts new file mode 100644 index 0000000..bd03bd3 --- /dev/null +++ b/js/plugin/popup-card-editor.ts @@ -0,0 +1,264 @@ +import { LitElement, html, css } from "lit"; +import { property, query, state } from "lit/decorators.js"; +import { loadHaForm } from "../helpers"; + +const configSchema = [ + { + name: "entity", + label: "Entity", + selector: { entity: {} }, + }, + { + name: "title", + label: "Title", + selector: { text: {} }, + }, + { + name: "size", + selector: { + select: { mode: "dropdown", options: ["normal", "wide", "fullscreen"] }, + }, + }, + { + type: "grid", + schema: [ + { + name: "right_button", + label: "Right button", + selector: { text: {} }, + }, + { + name: "left_button", + label: "Left button", + selector: { text: {} }, + }, + ], + }, + { + type: "grid", + schema: [ + { + name: "right_button_action", + label: "Right button action", + selector: { object: {} }, + }, + { + name: "left_button_action", + label: "Left button action", + selector: { object: {} }, + }, + ], + }, + { + type: "grid", + schema: [ + { + name: "dismissable", + label: "User dismissable", + selector: { boolean: {} }, + }, + { + name: "timeout", + label: "Auto close timeout (ms)", + selector: { number: { mode: "box" } }, + }, + ], + }, + { + type: "grid", + schema: [ + { + name: "dismiss_action", + label: "Dismiss action", + selector: { object: {} }, + }, + { + name: "timeout_action", + label: "Timeout action", + selector: { object: {} }, + }, + ], + }, +]; + +class PopupCardEditor extends LitElement { + @state() _config; + + @property() lovelace; + @property() hass; + + @state() _selectedTab = 0; + @state() _cardGUIMode = true; + @state() _cardGUIModeAvailable = true; + + @query("hui-card-element-editor") private _cardEditorEl?; + + setConfig(config) { + this._config = config; + } + + connectedCallback() { + super.connectedCallback(); + loadHaForm(); + } + + _handleSwitchTab(ev: CustomEvent) { + this._selectedTab = parseInt(ev.detail.index, 10); + } + + _configChanged(ev: CustomEvent) { + ev.stopPropagation(); + if (!this._config) return; + this._config = { ...ev.detail.value }; + this.dispatchEvent( + new CustomEvent("config-changed", { detail: { config: this._config } }) + ); + } + + _cardConfigChanged(ev: CustomEvent) { + ev.stopPropagation(); + if (!this._config) return; + const card = { ...ev.detail.config }; + this._config = { ...this._config, card }; + this._cardGUIModeAvailable = ev.detail.guiModeAvailable; + + this.dispatchEvent( + new CustomEvent("config-changed", { detail: { config: this._config } }) + ); + } + _toggleCardMode(ev) { + this._cardEditorEl?.toggleMode(); + } + _deleteCard(ev) { + if (!this._config) return; + this._config = { ...this._config }; + delete this._config.card; + + this.dispatchEvent( + new CustomEvent("config-changed", { detail: { config: this._config } }) + ); + } + _cardGUIModeChanged(ev: CustomEvent) { + ev.stopPropagation(); + this._cardGUIMode = ev.detail.guiMode; + this._cardGUIModeAvailable = ev.detail.guiModeAvailable; + } + + render() { + if (!this.hass || !this._config) { + return html``; + } + + return html` +
+
+ + + + +
+
+ ${[this._renderSettingsEditor, this._renderCardEditor][ + this._selectedTab + ].bind(this)()} +
+
+ `; + } + + _renderSettingsEditor() { + return html`
+ s.label ?? s.name} + @value-changed=${this._configChanged} + > +
`; + } + + _renderCardEditor() { + return html` +
+ ${this._config.card + ? html` +
+ + ${!this._cardEditorEl || this._cardGUIMode + ? "Show code editor" + : "Show visual editor"} + + + Change card type + +
+ + ` + : html` + + `} +
+ `; + } + + static get styles() { + return css` + mwc-tab-bar { + border-bottom: 1px solid var(--divider-color); + } + .box { + margin-top: 8px; + border: 1px solid var(--divider-color); + padding: 12px; + } + .box .toolbar { + display: flex; + justify-content: flex-end; + width: 100%; + gap: 8px; + } + .gui-mode-button { + margin-right: auto; + } + `; + } +} + +(async () => { + while (!window.browser_mod) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + await window.browser_mod.connectionPromise; + + if (!customElements.get("popup-card-editor")) + customElements.define("popup-card-editor", PopupCardEditor); + (window as any).customCards = (window as any).customCards || []; + (window as any).customCards.push({ + type: "popup-card", + name: "Popup card", + preview: false, + description: + "Replace the more-info dialog for a given entity in the view that includes this card. (Browser Mod)", + }); +})(); diff --git a/js/plugin/popup-card.ts b/js/plugin/popup-card.ts new file mode 100644 index 0000000..a5deea0 --- /dev/null +++ b/js/plugin/popup-card.ts @@ -0,0 +1,123 @@ +import { LitElement, html, css } from "lit"; +import { property, state } from "lit/decorators.js"; + +import "./popup-card-editor"; + +class PopupCard extends LitElement { + @property() hass; + @state() _config; + @property({ attribute: "edit-mode", reflect: true }) editMode; + @state() _element; + + static getConfigElement() { + return document.createElement("popup-card-editor"); + } + + static getStubConfig(hass, entities) { + const entity = entities[0]; + return { + entity, + title: "Custom popup", + dismissable: true, + card: { type: "markdown", content: "This replaces the more-info dialog" }, + }; + } + + constructor() { + super(); + this.popup = this.popup.bind(this); + } + + setConfig(config) { + this._config = config; + (async () => { + const ch = await window.loadCardHelpers(); + this._element = await ch.createCardElement(config.card); + this._element.hass = this.hass; + })(); + } + + async connectedCallback() { + super.connectedCallback(); + window.addEventListener("hass-more-info", this.popup); + + if (this.parentElement.localName === "hui-card-preview") { + this.editMode = true; + } + } + + async disconnectedCallback() { + super.disconnectedCallback(); + window.removeEventListener("hass-more-info", this.popup); + } + + popup(ev: CustomEvent) { + if (ev.detail?.entityId === this._config.entity) { + ev.stopPropagation(); + ev.preventDefault(); + const config = { ...this._config }; + delete config.card; + + window.browser_mod?.service("popup", { + content: this._config.card, + ...this._config, + }); + setTimeout( + () => + this.dispatchEvent( + new CustomEvent("hass-more-info", { + bubbles: true, + composed: true, + cancelable: false, + detail: { entityID: "." }, + }) + ), + 50 + ); + } + } + + updated(changedProperties) { + super.updated(changedProperties); + if (changedProperties.has("hass")) { + if (this._element) this._element.hass = this.hass; + } + } + + render() { + if (!this.editMode) return html``; + return html` +
+

${this._config.title}

+
+ ${this._element}
`; + } + + static get styles() { + return css` + :host { + display: none !important; + } + :host([edit-mode="true"]) { + display: block !important; + border: 1px solid var(--primary-color); + } + h2 { + padding-left: 16px; + padding-top: 16px; + margin: 0; + } + `; + } +} + +(async () => { + while (!window.browser_mod) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + await window.browser_mod.connectionPromise; + + if (!customElements.get("popup-card")) + customElements.define("popup-card", PopupCard); +})(); diff --git a/js/plugin/popups.ts b/js/plugin/popups.ts index aa41b88..ee5d1ad 100644 --- a/js/plugin/popups.ts +++ b/js/plugin/popups.ts @@ -12,6 +12,8 @@ class BrowserModPopup extends LitElement { @property() right_button; @property() left_button; @property() dismissable; + @property({ reflect: true }) wide; + @property({ reflect: true }) fullscreen; _actions; timeout; _timeoutStart; @@ -47,6 +49,7 @@ class BrowserModPopup extends LitElement { dismiss_action = undefined, timeout = undefined, timeout_action = undefined, + size = undefined, } = {} ) { this.title = title; @@ -76,6 +79,8 @@ class BrowserModPopup extends LitElement { dismiss_action, timeout_action, }; + this.wide = size === "wide" ? "" : undefined; + this.fullscreen = size === "fullscreen" ? "" : undefined; } _primary() { diff --git a/js/plugin/services.ts b/js/plugin/services.ts index 07a9582..a163d07 100644 --- a/js/plugin/services.ts +++ b/js/plugin/services.ts @@ -68,10 +68,7 @@ export const ServicesMixin = (SuperClass) => { ]; for (const service of cmds) { this.addEventListener(`command-${service}`, (ev) => { - this._service_action({ - service, - data: ev.detail, - }); + this.service(service, ev.detail); }); } @@ -82,6 +79,10 @@ export const ServicesMixin = (SuperClass) => { }); } + async service(service, data) { + this._service_action({ service, data }); + } + async _service_action({ service, data }) { let _service: String = service; if (!_service.startsWith("browser_mod.") && _service.includes(".")) {