diff --git a/packages/provider/assets/index.html.ejs b/packages/provider/assets/index.html.ejs index 0ce3cd32969d7f75e3c52ea6e9ae8b763bb6c83b..3d35a5f69accc3305eae3ac28c870d9bf881eb7f 100644 --- a/packages/provider/assets/index.html.ejs +++ b/packages/provider/assets/index.html.ejs @@ -2,6 +2,7 @@ <html> <head> <meta charset="UTF-8"> + <script type="module" src="dbp-provider.js"></script> <script type="module" src="dbp-provider-demo.js"></script> <!-- This is for debugging only: @@ -9,16 +10,18 @@ - unhandled requests are logged in the console --> <script> - window.addEventListener('inherit', e => console.log('window eventListener("inherit",..) name "' + e.detail.name + '" not found.')); window.addEventListener('subscribe', e => console.log('window eventListener("subscribe",..) name "' + e.detail.name + '" not found.')); window.addEventListener('unsubscribe', e => console.log('window eventListener("unsubscribe",..) name "' + e.detail.name + '" not found.')); </script> + <style> + body { padding: 50px;} + </style> </head> <body> - -<dbp-provider-demo lang="de"></dbp-provider-demo> - -<p>version: <span style="color: white; background-color: black;"><%= buildInfo.info %></span></p> + <dbp-provider id="root" root="1" availability="global" lang="de"> + <dbp-provider-demo id="provider-demo" subscribe="lang:lang"></dbp-provider-demo> + </dbp-provider> + <p>version: <span style="color: white; background-color: black;"><%= buildInfo.info %></span></p> </body> </html> diff --git a/packages/provider/src/dbp-provider-demo.js b/packages/provider/src/dbp-provider-demo.js index 0c63c30c11bd21ce582fcf45bf04a78620ad2def..10a482fb5cdd9e3a17597528f42c178824cf6e85 100644 --- a/packages/provider/src/dbp-provider-demo.js +++ b/packages/provider/src/dbp-provider-demo.js @@ -1,13 +1,15 @@ import {i18n} from './i18n.js'; -import {css, html, LitElement} from 'lit-element'; +import {css, html} from 'lit-element'; import {ScopedElementsMixin} from '@open-wc/scoped-elements'; import {AuthKeycloak, LoginButton} from '@dbp-toolkit/auth'; import * as commonUtils from '@dbp-toolkit/common/utils'; import * as commonStyles from '@dbp-toolkit/common/styles'; import {Provider} from '@dbp-toolkit/provider'; +import {LanguageSelect} from '@dbp-toolkit/language-select'; +import DBPLitElement from "@dbp-toolkit/common/dbp-lit-element"; -class ProviderDemo extends ScopedElementsMixin(LitElement) { +class ProviderDemo extends ScopedElementsMixin(DBPLitElement) { constructor() { super(); @@ -18,6 +20,7 @@ class ProviderDemo extends ScopedElementsMixin(LitElement) { return { 'dbp-auth-keycloak': AuthKeycloak, 'dbp-login-button': LoginButton, + 'dbp-language-select': LanguageSelect, 'dbp-provider': Provider, 'dbp-consumer': DemoConsumer, }; @@ -25,6 +28,7 @@ class ProviderDemo extends ScopedElementsMixin(LitElement) { static get properties() { return { + ...super.properties, lang: { type: String }, }; } @@ -32,6 +36,20 @@ class ProviderDemo extends ScopedElementsMixin(LitElement) { connectedCallback() { super.connectedCallback(); i18n.changeLanguage(this.lang); + + } + + attributeChangedCallback(name, oldValue, newValue) { + console.log('ProviderDemo (' + this.id + ') attributeChangesCallback( ' + name + ', ' + oldValue + ', ' + newValue + ')'); + switch(name) { + case 'lang': + this.lang = newValue; + i18n.changeLanguage(this.lang); + break; + default: + super.attributeChangedCallback(name, oldValue, newValue); + } + this.render(); } static get styles() { @@ -46,71 +64,52 @@ class ProviderDemo extends ScopedElementsMixin(LitElement) { ]; } + get id() { + return this.getAttribute('id'); + } + render() { return html` <section class="section"> - <p>Provider <em>"root"</em> is the top most in hierarchy:</p> - <dbp-provider id="root" - init="availability=global" - ></dbp-provider> -<pre> -<dbp-provider id="root" init="availability=global" ></dbp-provider> -</pre> + <p>${i18n.t('demo.provider_description', {id: "root", description: "is the top most in hierarchy"})}</p> + <pre><dbp-provider id="root" root="1" availability="global" ></dbp-provider></pre> <div class="container"> - <h1 class="title">Provider-Demo</h1> + <h1 class="title">${i18n.t('demo.provider')}-Demo</h1> </div> <div class="container"> <dbp-auth-keycloak lang="${this.lang}" url="https://auth-dev.tugraz.at/auth" realm="tugraz" client-id="auth-dev-mw-frontend-local" load-person try-login></dbp-auth-keycloak> <dbp-login-button lang="${this.lang}" show-image></dbp-login-button> + <dbp-language-select></dbp-language-select> </div> - <div class="container"> - <h2>Provider</h2> - <p>Provider <em>"demo"</em> has only <em>border-color</em> to offer:</p> - <dbp-provider id="demo" - init="bc=blue" - ></dbp-provider> -<pre> -<dbp-provider id="demo" init="bc=blue" ></dbp-provider> -</pre> - <p>Provider <em>"foo-bar"</em> has some values in its store:</p> + <dbp-provider id="demo" + bc="blue"> <dbp-provider id="foo-bar" - init="foo=9,bar=20" - ></dbp-provider> -<pre> -<dbp-provider id="foo-bar" init="foo=9,bar=20" ></dbp-provider> -</pre> - <h2>Consumer</h2> - <p>Consumer <em>"c1"</em> will only subscribe to <em>border-color</em></p> -<pre> -<dbp-consumer id="c1" subscribe="bc:border-color" ></dbp-consumer> -</pre> - <dbp-consumer id="c1" - subscribe="bc:border-color" - ></dbp-consumer> - <p>Consumer <em>"c2"</em> subscribes to <em>foo</em></p> -<pre> -<dbp-consumer id="c2" subscribe="foo:foo" ></dbp-consumer> -</pre> - <dbp-consumer id="c2" - subscribe="foo:foo" - ></dbp-consumer> - <p>Consumer <em>"c3"</em> subscribes for <em>status</em> which is provided as <em>availability</em></p> -<pre> -<dbp-consumer id="c3" subscribe="availability:status" border-color="orange" ></dbp-consumer> -</pre> - <dbp-consumer id="c3" - subscribe="availability:status" - border-color="orange" - ></dbp-consumer> - <p>Consumer <em>"c4"</em> subscribes for <em>status</em> which is provided as <em>unknown-name</em> which does not exist...</p> -<pre> -<dbp-consumer id="c4" subscribe="unknown-name:status" border-color="darkgray" ></dbp-consumer> -</pre> - <dbp-consumer id="c4" - subscribe="unknown-name:status" - border-color="darkgray" - ></dbp-consumer> - </div> + foo="9" + bar="20"> + <div class="container"> + <h2>${i18n.t('demo.provider')}</h2> + <p>${i18n.t('demo.provider_description', {id: "demo", description: "has only \"border-color\" to offer"})}</p> <pre><dbp-provider id="demo" bc="blue" ></dbp-provider></pre> + <p>${i18n.t('demo.provider_description', {id: "foo-bar", description: "has some values in its store"})}</p> + <pre><dbp-provider id="foo-bar" foo="9" bar="20" ></dbp-provider></pre> + + <h2>${i18n.t('demo.consumer')}</h2> + <p>${i18n.t('demo.consumer_description', {id: "c1", subscriptions: "border-color"})}</p> + <pre><dbp-consumer id="c1" subscribe="border-color:bc" ></dbp-consumer></pre> + <dbp-consumer id="c1" subscribe="border-color:bc,lang:lang"></dbp-consumer> + <p>${i18n.t('demo.consumer_description', {id: "c2", subscriptions: "foo"})}</p> + <pre><dbp-consumer id="c2" subscribe="foo:foo" ></dbp-consumer></pre> + <dbp-consumer id="c2" subscribe="foo:foo,lang:lang"></dbp-consumer> + <p>${i18n.t('demo.consumer_description', {id: "c3", subscriptions: "availability:status"})}</p> + <p>Local <em>status</em> is provided as <em>availability</em></p> + <pre><dbp-consumer id="c3" subscribe="status:availability" border-color="orange" ></dbp-consumer></pre> + <dbp-consumer id="c3" subscribe="status:availability,lang:lang" border-color="orange"></dbp-consumer> + <p>${i18n.t('demo.consumer_description', {id: "c4", subscriptions: "unknown-name:status"})}</p> + <p>Remote <em>unknown-name</em> does not exist, the default value is overwritten by <em>undefined</em></i></p> + <pre><dbp-consumer id="c4" subscribe="status:unknown-name" border-color="darkgray" ></dbp-consumer></pre> + <dbp-consumer id="c4" subscribe="status:unknown-name" border-color="darkgray"></dbp-consumer> + </div> + </dbp-provider> + </dbp-provider> </section> `; } @@ -120,163 +119,16 @@ commonUtils.defineCustomElement('dbp-provider-demo', ProviderDemo); // ======================================================= -class Consumer extends HTMLElement { - constructor() { - super(); - this.connected = false; - this.deferInherited = false; - this.deferSubscribe = false; - this.deferUnSubscribe = false; - - // default values - this.inherit = ''; - this.subscribe = ''; - this.unsubscribe = ''; - - this.attachShadow({mode: 'open'}); - console.log('Consumer constructor()'); - } - - connectedCallback() { - console.log('Consumer(' + this.id() + ') connectedCallback()'); - - if (this.deferInherited) { - const attrs = this.inherit.split(','); - attrs.forEach(element => this.askProviderFor(element)); - this.deferInherited = false; - } - if (this.deferUnSubscribe) { - const attrs = this.unsubscribe.split(','); - attrs.forEach(element => this.subscribeProviderFor(element)); - this.deferSubscribe = false; - this.unsubscribe = ''; - } - if (this.deferSubscribe) { - const attrs = this.subscribe.split(','); - attrs.forEach(element => this.subscribeProviderFor(element)); - this.deferSubscribe = false; - } - this.connected = true; - } - - static get observedAttributes() { - return ['inherit', 'subscribe']; - } - - attributeChangedCallback(name, oldValue, newValue) { - console.log('Consumer(' + this.id() + ') attributeChangesCallback( ' + name + ', ' + oldValue + ', ' + newValue + ')'); - switch(name) { - case 'inherit': - this.inherit = newValue; - if (this.connected && typeof newValue === 'string') { - const attrs = newValue.split(','); - attrs.forEach(element => this.askProviderFor(element)); - } else { - this.deferInherited = newValue.length > 0; - } - break; - case 'subscribe': - if (this.subscribe && this.subscribe.length > 0) { - if (this.connected) { - const attrs = this.subscribe.split(','); - attrs.forEach(element => this.unSubscribeProviderFor(element)); - } else { - this.deferUnSubscribe = this.subscribe.length > 0; - this.unsubscribe = this.subscribe; - } - } - if (newValue !== null) { - this.subscribe = newValue; - if (this.connected) { - const attrs = newValue.split(','); - attrs.forEach(element => this.subscribeProviderFor(element)); - } else { - this.deferSubscribe = newValue && newValue.length > 0; - } - } - break; - default: - console.log('unknown attribute "' + name + '".'); - } - } - - id() { - return this.getAttribute('id'); - } - - render() {} - - askProviderFor(element) { - console.log('Consumer(' + this.id() + ') askProviderFor( ' + element + ' )'); - const pair = element.trim().split(':'); - const global = pair[0]; - const local = pair[1]; - const that = this; - const event = new CustomEvent('inherit', - { - bubbles: true, - composed: true, - detail: { - name: global, - callback: (value) => { - console.log('Consumer(' + that.id() + ') ask/Callback ' + global + ' -> ' + local + ' = ' + value); - this.attributeChangedCallback(local, that[local], value); - } - } - }); - this.parentElement.dispatchEvent(event); - //console.dir(event); - } - - subscribeProviderFor(element) { - console.log('Consumer(' + this.id() + ') subscribeProviderFor( ' + element + ' )'); - const pair = element.trim().split(':'); - const global = pair[0]; - const local = pair[1]; - const that = this; - const event = new CustomEvent('subscribe', - { - bubbles: true, - composed: true, - detail: { - name: global, - callback: (value) => { - console.log('Consumer(' + that.id() + ') sub/Callback ' + global + ' -> ' + local + ' = ' + value); - this.attributeChangedCallback(local, that[local], value); - }, - sender: this, - } - }); - this.parentElement.dispatchEvent(event); - } - - unSubscribeProviderFor(element) { - console.log('Consumer(' + this.id() + ') unSubscribeProviderFor( ' + element + ' )'); - const pair = element.trim().split(':'); - const global = pair[0]; - const event = new CustomEvent('unsubscribe', - { - bubbles: true, - composed: true, - detail: { - name: global, - sender: this, - } - }); - this.parentElement.dispatchEvent(event); - } -} - -class DemoConsumer extends Consumer -{ +class DemoConsumer extends DBPLitElement { constructor() { super(); + this.lang = 'de'; // default values this.foo = 100; this.bar = 900; this.ping = 0; - this['border-color'] = 'green'; + this.borderColor = 'green'; this.status = 'local'; @@ -285,17 +137,34 @@ class DemoConsumer extends Consumer connectedCallback() { super.connectedCallback(); - console.log('DemoConsumer(' + this.id() + ') connectedCallback()'); + i18n.changeLanguage(this.lang); + console.log('DemoConsumer(' + this.id + ') connectedCallback()'); this.render(); } - static get observedAttributes() { - return [ ...Consumer.observedAttributes, 'foo', 'bar', 'gong', 'border-color', 'ping']; + static get properties() { + return { + ...super.properties, + lang: { type: String }, + foo: { type: String }, + bar: { type: String }, + gong: { type: String }, + borderColor: { type: String, attribute: 'border-color' }, + ping: { type: String } + }; } attributeChangedCallback(name, oldValue, newValue) { - console.log('DemoConsumer(' + this.id() + ') attributeChangesCallback( ' + name + ', ' + oldValue + ', ' + newValue + ')'); + if (oldValue === newValue) { + return; + } + + console.log('DemoConsumer(' + this.id + ') attributeChangesCallback( ' + name + ', ' + oldValue + ', ' + newValue + ')'); switch(name) { + case 'lang': + this.lang = newValue; + i18n.changeLanguage(this.lang); + break; case 'foo': this.foo = parseInt(newValue); break; @@ -314,23 +183,27 @@ class DemoConsumer extends Consumer this.render(); } + get id() { + return this.getAttribute('id'); + } + render() { if (! this.connected) { - return; + return `not connected!`; } - console.log('DemoConsumer(' + this.id() + ') render()'); + console.log('DemoConsumer(' + this.id + ') render()'); const sum = this.foo + this.bar; - this.shadowRoot.innerHTML = ` + return html` <div style="border: ${this['border-color']} dotted; padding: 10px;"> <table style="width:200px;"> <tr style="background-color: #aaa;"> - <th style="text-align: left;">Item</th> - <th style="text-align: right;">Price</th> + <th style="text-align: left;">${i18n.t('consumer.item')}</th> + <th style="text-align: right;">${i18n.t('consumer.price')}</th> </tr> <tr><td>foo</td><td style="text-align: right;">${this.foo}</td></tr> <tr><td>bar</td><td style="text-align: right;">${this.bar}</td></tr> - <tr><td>sum</td><td style="text-align: right;">${sum}</td></tr> + <tr><td>${i18n.t('consumer.sum')}</td><td style="text-align: right;">${sum}</td></tr> </table> <p>Status: <b>${this.status}</b></p> </div> diff --git a/packages/provider/src/i18n/de/translation.json b/packages/provider/src/i18n/de/translation.json index e41d874c7754dcb37b97245f41047f75b306965d..981c4287b852d1888e6e42792423d9799ae9fc83 100644 --- a/packages/provider/src/i18n/de/translation.json +++ b/packages/provider/src/i18n/de/translation.json @@ -1,5 +1,16 @@ { - "provider": { + "de": "Deutsch", + "en": "Englisch", + "demo": { + "provider": "Anbieter", + "consumer": "Verbraucher", + "provider_description": "Anbieter \"{{id}}\" {{description}}", + "consumer_description": "Verbraucher \"{{id}}\" abonniert nur {{subscriptions}}" + }, + "consumer": { + "item": "Bezeichnung", + "price": "Preis", + "sum": "Summe" } } \ No newline at end of file diff --git a/packages/provider/src/i18n/en/translation.json b/packages/provider/src/i18n/en/translation.json index e41d874c7754dcb37b97245f41047f75b306965d..bc342774e83864c5cd6c4e4fda3c2f41c33847fa 100644 --- a/packages/provider/src/i18n/en/translation.json +++ b/packages/provider/src/i18n/en/translation.json @@ -1,5 +1,16 @@ { - "provider": { + "de": "German", + "en": "English", + "demo": { + "provider": "Provider", + "consumer": "Consumer", + "provider_description": "Provider \"{{id}}\" {{description}}", + "consumer_description": "Consumer \"{{id}}\" will only subscribe to {{subscriptions}}" + }, + "consumer": { + "item": "Description", + "price": "Price", + "sum": "sum" } } \ No newline at end of file diff --git a/packages/provider/src/provider.js b/packages/provider/src/provider.js index 52eda0acba6e182345d72e58e04f5e528b16210f..7cd95fc0770c2f46184cb8613354d11efa73b430 100644 --- a/packages/provider/src/provider.js +++ b/packages/provider/src/provider.js @@ -2,59 +2,20 @@ export class Provider extends HTMLElement { constructor() { super(); this.callbackStore = []; + this.root = false; console.log('Provider constructor()'); } - static get observedAttributes() { - return ['init']; - } - - attributeChangedCallback(name, oldValue, newValue) { - console.log('Provider(' + this.id() + ') attribute "' + name + '" changed from "' + oldValue + '" to "' + newValue + '".'); - switch(name) { - case 'init': - this.init = newValue; - if (newValue) { - const attrs = newValue.split(','); - attrs.forEach(element => { - const pair = element.trim().split('='); - const name = pair[0].trim(); - const value = pair[1].trim().replace('"', '').replace("'", ''); - if (name.length > 0) { - this[name] = value; - this.callbackStore.forEach(item => { - if (item.name === name) { - item.callback(value); - } - }); - } - }); - } - break; - default: - console.log('unknown attribute "' + name + '".'); - } - } - - connectedCallback() { - console.log('Provider(' + this.id() + ') connectedCallback()'); + console.log('Provider(' + this.id + ') connectedCallback()'); const that = this; - this.addEventListener('inherit', function (e) { - if (that[e.detail.name]) { - console.log('Provider(' + that.id() + ') eventListener("inherit",..) name "' + e.detail.name + '" found.'); - //console.dir(e.detail); - e.detail.callback(that[e.detail.name]); - e.stopPropagation(); - } - }, false); this.addEventListener('subscribe', function (e) { const name = e.detail.name; - if (that[name]) { - console.log('Provider(' + that.id() + ') eventListener("subscribe",..) name "' + name + '" found.'); + if (Object.hasOwnProperty.call(that, name) || that.root) { + console.log('Provider(' + that.id + ') eventListener("subscribe",..) name "' + name + '" found.'); that.callbackStore.push({name: name, callback: e.detail.callback, sender: e.detail.sender}); e.detail.callback(that[name]); @@ -65,13 +26,13 @@ export class Provider extends HTMLElement { this.addEventListener('unsubscribe', function (e) { const name = e.detail.name; const sender = e.detail.sender; - if (that[name]) { - console.log('Provider(' + that.id() + ') eventListener("unsubscribe",..) name "' + name + '" found.'); + if (Object.hasOwnProperty.call(that, name) || that.root) { + console.log('Provider(' + that.id + ') eventListener("unsubscribe",..) name "' + name + '" found.'); that.callbackStore.forEach(item => { if (item.sender === sender && item.name === name) { const index = that.callbackStore.indexOf(item); that.callbackStore.splice(index, 1); - console.log('Provider(' + that.id() + ') eventListener for name "' + name + '" removed.'); + console.log('Provider(' + that.id + ') eventListener for name "' + name + '" removed.'); } }); @@ -84,8 +45,8 @@ export class Provider extends HTMLElement { const name = e.detail.name; const value = e.detail.value; - if (that[name]) { - console.log('Provider(' + that.id() + ') eventListener("set-property",..) name "' + name + '" found.'); + if (Object.hasOwnProperty.call(that, name) || that.root) { + console.log('Provider(' + that.id + ') eventListener("set-property",..) name "' + name + '" found.'); that[name] = value; that.callbackStore.forEach(item => { @@ -97,9 +58,51 @@ export class Provider extends HTMLElement { e.stopPropagation(); } }, false); + + // Options for the observer (which mutations to observe) + const config = { attributes: true, childList: false, subtree: false }; + + // Callback function to execute when mutations are observed + const callback = function(mutationsList, observer) { + // Use traditional 'for loops' for IE 11 + for(const mutation of mutationsList) { + if (mutation.type === 'attributes') { + const name = mutation.attributeName; + const value = that.getAttribute(name); + if (that[name] !== value) { + console.log('Provider (' + that.id + ') observed attribute "' + name + '" changed'); + that[name] = value; + that.callbackStore.forEach(item => { + if (item.name === name) { + item.callback(value); + } + }); + } + } + } + }; + + // Create an observer instance linked to the callback function + const observer = new MutationObserver(callback); + + // Start observing the target node for configured mutations + observer.observe(this, config); + + // get all *not observed* attributes + if (this.hasAttributes()) { + const attrs = this.attributes; + for(let i = attrs.length - 1; i >= 0; i--) { + if (['id', 'class', 'style', 'data-tag-name'].includes(attrs[i].name)) { + continue; + } + + this[attrs[i].name] = attrs[i].value; + console.log('Provider (' + that.id + ') found attribute "' + attrs[i].name + '" = "' + attrs[i].value + '"'); + } + } } - id() { + get id() { return this.getAttribute('id'); } }