From e1b9568a1c1d0f04f2dacea133eea04e9ee28af2 Mon Sep 17 00:00:00 2001 From: marcvanraalte <86408026+marcvanraalte@users.noreply.github.com> Date: Thu, 9 Feb 2023 15:42:10 +0100 Subject: [PATCH 01/27] 970 tapchanger editor (#1166) * feat(tapchanger-editor.ts):editor_added * feat(tapchanger-editor.test.ts):setup * feat(TapChanger-editor):editor_and_unit_test --------- Co-authored-by: Pascal Wilbrink --- src/editors/substation/tapchanger-editor.ts | 86 ++++++ .../substation/transformer-winding-editor.ts | 19 ++ .../editors/substation/TapChanger.scd | 267 ++++++++++++++++++ .../tapchanger-editor.test.snap.js | 29 ++ .../substation/tapchanger-editor.test.ts | 49 ++++ 5 files changed, 450 insertions(+) create mode 100644 src/editors/substation/tapchanger-editor.ts create mode 100644 test/testfiles/editors/substation/TapChanger.scd create mode 100644 test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js create mode 100644 test/unit/editors/substation/tapchanger-editor.test.ts diff --git a/src/editors/substation/tapchanger-editor.ts b/src/editors/substation/tapchanger-editor.ts new file mode 100644 index 0000000000..62e2f80b7d --- /dev/null +++ b/src/editors/substation/tapchanger-editor.ts @@ -0,0 +1,86 @@ +import { + customElement, + html, + LitElement, + TemplateResult, + property, + state, +} from 'lit-element'; + +import '../../action-pane.js'; +import './eq-function-editor.js'; +import './l-node-editor.js'; +import './sub-equipment-editor.js'; +import { getChildElementsByTagName } from '../../foundation.js'; + +@customElement('tapchanger-editor') +export class TapChangerEditor extends LitElement { + /** The document being edited as provided to editor by [[`Zeroline`]]. */ + @property({ attribute: false }) + doc!: XMLDocument; + /** SCL element TransformerWinding */ + @property({ attribute: false }) + element!: Element; + /** Whether `EqFunction` and `SubEquipment` are rendered */ + @property({ type: Boolean }) + showfunctions = false; + + @state() + get header(): string { + const name = this.element.getAttribute('name') ?? ''; + const desc = this.element.getAttribute('desc'); + + return `TapChanger.${name} ${desc ? `—TapChanger.${desc}` : ''}`; + } + + private renderLNodes(): TemplateResult { + const lNodes = getChildElementsByTagName(this.element, 'LNode'); + + return lNodes.length + ? html`
+ ${lNodes.map( + lNode => + html`` + )} +
` + : html``; + } + + renderEqFunctions(): TemplateResult { + if (!this.showfunctions) return html``; + + const eqFunctions = getChildElementsByTagName(this.element, 'EqFunction'); + return html` ${eqFunctions.map( + eqFunction => + html`` + )}`; + } + + private renderSubEquipments(): TemplateResult { + if (!this.showfunctions) return html``; + const subEquipments = getChildElementsByTagName( + this.element, + 'SubEquipment' + ); + + return html` ${subEquipments.map( + subEquipment => + html`` + )}`; + } + + render(): TemplateResult { + return html` ${this.renderLNodes()} + ${this.renderEqFunctions()} ${this.renderSubEquipments()}`; + } +} diff --git a/src/editors/substation/transformer-winding-editor.ts b/src/editors/substation/transformer-winding-editor.ts index 1f7c4f0734..f00e4d02ae 100644 --- a/src/editors/substation/transformer-winding-editor.ts +++ b/src/editors/substation/transformer-winding-editor.ts @@ -20,6 +20,9 @@ import { Menu } from '@material/mwc-menu'; import '../../action-icon.js'; import '../../action-pane.js'; +import './eq-function-editor.js'; +import './l-node-editor.js'; +import './tapchanger-editor.js'; import { styles } from './foundation.js'; import { @@ -131,6 +134,21 @@ export class TransformerWindingEditor extends LitElement { : html``; } + private renderTapChanger(): TemplateResult { + if (!this.showfunctions) return html``; + const tapChangers = getChildElementsByTagName(this.element, 'TapChanger'); + return tapChangers.length + ? html` ${tapChangers.map( + tapChanger => + html`` + )}` + : html``; + } + private renderAddButtons(): TemplateResult[] { return childTags(this.element).map( child => @@ -174,6 +192,7 @@ export class TransformerWindingEditor extends LitElement { > ${this.renderLNodes()} ${this.renderEqFunctions()} + ${this.renderTapChanger()} `; } diff --git a/test/testfiles/editors/substation/TapChanger.scd b/test/testfiles/editors/substation/TapChanger.scd new file mode 100644 index 0000000000..0c519ee311 --- /dev/null +++ b/test/testfiles/editors/substation/TapChanger.scd @@ -0,0 +1,267 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 110 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + 30000 + + + 600 + + + + + + + + + + + + + + 1000 + + + direct-with-enhanced-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + 30000 + + + 600 + + + + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + status-only + + + pulse + persistent + persistent-feedback + + + Ok + Warning + Alarm + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + on + blocked + test + test/blocked + off + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + + diff --git a/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js new file mode 100644 index 0000000000..7050dfa649 --- /dev/null +++ b/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js @@ -0,0 +1,29 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["web component rendering TapChanger element rendering LNode and EqFunction children looks like the latest snapshot"] = +` +
+ + +
+ + +
+`; +/* end snapshot web component rendering TapChanger element rendering LNode and EqFunction children looks like the latest snapshot */ + +snapshots["web component rendering TapChanger element rendering SubEquipment looks like the latest snapshot"] = +` + + + +`; +/* end snapshot web component rendering TapChanger element rendering SubEquipment looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/tapchanger-editor.test.ts b/test/unit/editors/substation/tapchanger-editor.test.ts new file mode 100644 index 0000000000..4b0f00179d --- /dev/null +++ b/test/unit/editors/substation/tapchanger-editor.test.ts @@ -0,0 +1,49 @@ +import { fixture, html, expect } from '@open-wc/testing'; + +import '../../../../src/editors/substation/tapchanger-editor.js'; +import { TapChangerEditor } from '../../../../src/editors/substation/tapchanger-editor.js'; + +describe('web component rendering TapChanger element', () => { + let element: TapChangerEditor; + let doc: XMLDocument; + + describe('rendering LNode and EqFunction children', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/TapChanger.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + element = ( + await fixture( + html`` + ) + ); + element.showfunctions = true; + await element.updateComplete; + }); + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); + + describe('rendering SubEquipment', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/TapChanger.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + element = ( + await fixture( + html`` + ) + ); + element.showfunctions = true; + await element.updateComplete; + }); + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); +}); From cc0051f54a89984af9676ed2209a0481f49fa7a2 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Wed, 22 Feb 2023 11:06:02 +0100 Subject: [PATCH 02/27] fix(editors/substation/guess-wizard): make sure guessed content is added to the substation (#1148) Co-authored-by: Pascal Wilbrink --- src/editors/substation/guess-wizard.ts | 29 ++++++++++--------- src/wizards/substation.ts | 27 +++++++---------- .../guess-wizarding-editing.test.ts | 17 ++++++++--- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/editors/substation/guess-wizard.ts b/src/editors/substation/guess-wizard.ts index eee56c72d3..4a1575b9ac 100644 --- a/src/editors/substation/guess-wizard.ts +++ b/src/editors/substation/guess-wizard.ts @@ -167,7 +167,7 @@ function createBayElement( return null; } -function guessBasedOnCSWI(doc: XMLDocument): WizardActor { +function guessBasedOnCSWI(doc: XMLDocument, substation: Element): WizardActor { return ( inputs: WizardInputElement[], wizard: Element, @@ -179,10 +179,6 @@ function guessBasedOnCSWI(doc: XMLDocument): WizardActor { item => item.value ); - const root = doc.documentElement; - - const substation = root.querySelector(':root > Substation')!; - const voltageLevel = createElement(doc, 'VoltageLevel', { name: 'E1', desc: 'guessed by OpenSCD', @@ -196,12 +192,9 @@ function guessBasedOnCSWI(doc: XMLDocument): WizardActor { voltage.textContent = '110.00'; voltageLevel.appendChild(voltage); - Array.from(doc.querySelectorAll(':root > IED')) - .sort(compareNames) - .map(ied => createBayElement(ied, ctlModelList)) - .forEach(bay => { - if (bay) voltageLevel.appendChild(bay); - }); + actions.push({ + new: { parent: doc.querySelector('SCL')!, element: substation }, + }); actions.push({ new: { @@ -210,20 +203,30 @@ function guessBasedOnCSWI(doc: XMLDocument): WizardActor { }, }); + Array.from(doc.querySelectorAll(':root > IED')) + .sort(compareNames) + .map(ied => createBayElement(ied, ctlModelList)) + .forEach(bay => { + if (bay) actions.push({ new: { parent: voltageLevel, element: bay } }); + }); + return actions; }; } /** @returns a Wizard for guessing `VoltageLevel` stucture assuming each * `LN[lnClass="CSWI"]` represents a bay controller */ -export function guessVoltageLevel(doc: XMLDocument): Wizard { +export function guessVoltageLevel( + doc: XMLDocument, + substation: Element +): Wizard { return [ { title: get('guess.wizard.title'), primary: { icon: 'play_arrow', label: get('guess.wizard.primary'), - action: guessBasedOnCSWI(doc), + action: guessBasedOnCSWI(doc, substation), }, content: [ html`

${translate('guess.wizard.description')}

`, diff --git a/src/wizards/substation.ts b/src/wizards/substation.ts index 22faec59fb..d17d65c2e8 100644 --- a/src/wizards/substation.ts +++ b/src/wizards/substation.ts @@ -7,15 +7,14 @@ import '@material/mwc-formfield'; import '../wizard-textfield.js'; import { createElement, - EditorAction, getValue, - newWizardEvent, Wizard, + WizardAction, WizardActor, WizardInputElement, } from '../foundation.js'; import { guessVoltageLevel } from '../editors/substation/guess-wizard.js'; -import { updateNamingAttributeWithReferencesAction } from "./foundation/actions.js"; +import { updateNamingAttributeWithReferencesAction } from './foundation/actions.js'; function render( name: string, @@ -46,29 +45,20 @@ function render( } export function createAction(parent: Element): WizardActor { - return (inputs: WizardInputElement[], wizard: Element): EditorAction[] => { + return (inputs: WizardInputElement[], wizard: Element): WizardAction[] => { const name = getValue(inputs.find(i => i.label === 'name')!); const desc = getValue(inputs.find(i => i.label === 'desc')!); const guess = wizard.shadowRoot?.querySelector('mwc-checkbox')?.checked; parent.ownerDocument.createElement('Substation'); - const element = createElement(parent.ownerDocument, 'Substation', { + const substation = createElement(parent.ownerDocument, 'Substation', { name, desc, }); - const action = { - new: { - parent, - element, - }, - }; - if (guess) - wizard.dispatchEvent( - newWizardEvent(guessVoltageLevel(parent.ownerDocument)) - ); + return [() => guessVoltageLevel(parent.ownerDocument, substation)]; - return [action]; + return [{ new: { parent, element: substation } }]; }; } @@ -97,7 +87,10 @@ export function substationEditWizard(element: Element): Wizard { primary: { icon: 'edit', label: get('save'), - action: updateNamingAttributeWithReferencesAction(element, 'substation.action.updatesubstation'), + action: updateNamingAttributeWithReferencesAction( + element, + 'substation.action.updatesubstation' + ), }, content: render( element.getAttribute('name') ?? '', diff --git a/test/integration/editors/substation/guess-wizarding-editing.test.ts b/test/integration/editors/substation/guess-wizarding-editing.test.ts index 40c0d8eeda..aefa5d989d 100644 --- a/test/integration/editors/substation/guess-wizarding-editing.test.ts +++ b/test/integration/editors/substation/guess-wizarding-editing.test.ts @@ -14,10 +14,12 @@ describe('guess-wizard-integration', () => { validSCL = await fetch('/test/testfiles/valid2007B4.scd') .then(response => response.text()) .then(str => new DOMParser().parseFromString(str, 'application/xml')); - validSCL.querySelector('Substation')!.innerHTML = ''; + + const substation = validSCL.querySelector('Substation')!; + substation.innerHTML = ''; element = await fixture(html``); - const wizard = guessVoltageLevel(validSCL); + const wizard = guessVoltageLevel(validSCL, substation); element.workflow.push(() => wizard); await element.requestUpdate(); }); @@ -31,6 +33,7 @@ describe('guess-wizard-integration', () => { ).length ).to.equal(5); }); + it('the first one being status-only', async () => { expect( element.wizardUI.dialog!.querySelector( @@ -38,6 +41,7 @@ describe('guess-wizard-integration', () => { )?.innerHTML ).to.equal('status-only'); }); + it('the second one being direct-with-normal-security', async () => { expect( element.wizardUI.dialog!.querySelector( @@ -45,6 +49,7 @@ describe('guess-wizard-integration', () => { )?.innerHTML ).to.equal('direct-with-normal-security'); }); + it('the second one being direct-with-enhanced-security', async () => { expect( element.wizardUI.dialog!.querySelector( @@ -52,6 +57,7 @@ describe('guess-wizard-integration', () => { )?.innerHTML ).to.equal('direct-with-enhanced-security'); }); + it('the second one being sbo-with-normal-security', async () => { expect( element.wizardUI.dialog!.querySelector( @@ -59,6 +65,7 @@ describe('guess-wizard-integration', () => { )?.innerHTML ).to.equal('sbo-with-normal-security'); }); + it('the second one being sbo-with-enhanced-security', async () => { expect( element.wizardUI.dialog!.querySelector( @@ -76,12 +83,14 @@ describe('guess-wizarding-editing-integration', () => { validSCL = await fetch('/test/testfiles/valid2007B4.scd') .then(response => response.text()) .then(str => new DOMParser().parseFromString(str, 'application/xml')); - validSCL.querySelector('Substation')!.innerHTML = ''; + + const substation = validSCL.querySelector('Substation')!; + substation.innerHTML = ''; element = ( await fixture(html``) ); - const wizard = guessVoltageLevel(validSCL); + const wizard = guessVoltageLevel(validSCL, substation); element.workflow.push(() => wizard); await element.requestUpdate(); From d9680fa1195650aeef6a5ec0290ec6cc321303ef Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Wed, 22 Feb 2023 16:03:43 +0100 Subject: [PATCH 03/27] feat(editors/ied): show all instantiated setting group values (#1155) * feat(editors/ied): show all instantieted setting group values * fix review comments --------- Co-authored-by: Pascal Wilbrink --- src/editors/ied/da-container.ts | 88 +- src/editors/ied/da-wizard.ts | 17 +- src/editors/ied/foundation.ts | 10 +- src/wizards/dai.ts | 41 +- src/wizards/foundation/dai-field-type.ts | 8 +- test/testfiles/editors/iedEditorWithIEDs.scd | 766 +++++++++--------- test/testfiles/wizards/ied.scd | 15 +- .../__snapshots__/da-container.test.snap.js | 142 +++- test/unit/editors/ied/da-container.test.ts | 32 + test/unit/editors/ied/foundation.test.ts | 35 +- .../wizards/__snapshots__/dai.test.snap.js | 105 +-- test/unit/wizards/dai.test.ts | 132 +-- 12 files changed, 770 insertions(+), 621 deletions(-) diff --git a/src/editors/ied/da-container.ts b/src/editors/ied/da-container.ts index 2b0943b389..b56a854d28 100644 --- a/src/editors/ied/da-container.ts +++ b/src/editors/ied/da-container.ts @@ -23,7 +23,7 @@ import { createDaInfoWizard } from './da-wizard.js'; import { Container, getInstanceDAElement, - getValueElement, + getValueElements, } from './foundation.js'; import { createDAIWizard } from '../../wizards/dai.js'; import { @@ -55,21 +55,6 @@ export class DAContainer extends Container { } } - /** - * Rendering an optional value of this (B)DA container. - * If there is a DAI, it gets priority on top of (B)DA values. - * @returns TemplateResult containing the value of the instance, element or nothing. - */ - private getValue(): TemplateResult { - if (this.instanceElement) { - return html`${getValueElement(this.instanceElement)?.textContent?.trim() ?? ''}`; - } - - return html`${getValueElement(this.element)?.textContent?.trim() ?? ''}`; - } - /** * Get the nested (B)DA element(s) if available. * @returns The nested (B)DA element(s) of this (B)DA container. @@ -123,11 +108,57 @@ export class DAContainer extends Container { } } - private openEditWizard(): void { - const wizard = wizards['DAI'].edit(this.element, this.instanceElement); + private openEditWizard(val: Element): void { + const wizard = wizards['DAI'].edit(this.element, val); if (wizard) this.dispatchEvent(newWizardEvent(wizard)); } + private getValueDisplayString(val: Element): string { + const sGroup = val.getAttribute('sGroup'); + const prefix = sGroup ? `SG${sGroup}: ` : ''; + const value = val.textContent?.trim(); + + return `${prefix}${value}`; + } + + private renderVal(): TemplateResult[] { + const bType = this.element!.getAttribute('bType'); + const element = this.instanceElement ?? this.element; + const hasInstantiatedVal = !!this.instanceElement?.querySelector('Val'); + + return hasInstantiatedVal + ? getValueElements(element).map( + val => html`
+
+

${this.getValueDisplayString(val)}

+
+
+ this.openEditWizard(val)} + > + +
+
` + ) + : [ + html`
+
+

+
+
+ this.openCreateWizard()} + > + +
+
`, + ]; + } + render(): TemplateResult { const bType = this.element!.getAttribute('bType'); @@ -167,26 +198,7 @@ export class DAContainer extends Container { > ` - : html`
-
-

${this.getValue()}

-
-
- ${this.instanceElement - ? html` this.openEditWizard()} - > - ` - : html` this.openCreateWizard()} - > - `} -
-
`} + : html`${this.renderVal()}`} ${this.toggleButton?.on && bType === 'Struct' ? this.getBDAElements().map( bdaElement => diff --git a/src/editors/ied/da-wizard.ts b/src/editors/ied/da-wizard.ts index c868cdbe0b..fb630be59e 100644 --- a/src/editors/ied/da-wizard.ts +++ b/src/editors/ied/da-wizard.ts @@ -9,7 +9,6 @@ import { getDescriptionAttribute, getInstanceAttribute, getNameAttribute, - newWizardEvent, Wizard, } from '../../foundation.js'; import { Nsdoc } from '../../foundation/nsdoc.js'; @@ -17,9 +16,19 @@ import { findDOTypeElement, findElement, findLogicaNodeElement, - getValueElement, + getValueElements, } from './foundation.js'; +function getValues(element: Element): string { + const hasValue = getValueElements(element).length !== 0; + + return hasValue + ? `${getValueElements(element) + .map(val => val.textContent ?? '') + .join(', ')}` + : '-'; +} + function renderFields( element: Element, instanceElement: Element | undefined, @@ -92,8 +101,8 @@ function renderFields( { const bType = element.getAttribute('bType')!; const newValue = getCustomField()[bType].value(inputs); - const name = instanceElement.getAttribute('name'); + const daiName = val.parentElement?.getAttribute('name') ?? ''; const complexAction: ComplexAction = { actions: [], - title: get('dai.action.updatedai', { daiName: name! }), + title: get('dai.action.updatedai', { daiName }), }; - const oldVal = instanceElement.querySelector('Val'); - if (oldVal) { - const newVal = oldVal.cloneNode(false); - newVal.textContent = newValue; - complexAction.actions.push({ - old: { element: oldVal }, - new: { element: newVal }, - }); - } else { - const newVal = instanceElement.ownerDocument.createElementNS( - SCL_NAMESPACE, - 'Val' - ); - newVal.textContent = newValue; - complexAction.actions.push({ - new: { parent: instanceElement, element: newVal }, - }); - } + const newVal = val.cloneNode(false); + newVal.textContent = newValue; + complexAction.actions.push({ + old: { element: val }, + new: { element: newVal }, + }); + return [complexAction]; }; } @@ -133,10 +119,15 @@ export function editDAIWizard( element: Element, instanceElement?: Element ): Wizard { + const daiName = + instanceElement?.tagName === 'DAI' + ? instanceElement?.getAttribute('name') ?? '' + : instanceElement?.parentElement?.getAttribute('name') ?? ''; + return [ { title: get('dai.wizard.title.edit', { - daiName: instanceElement?.getAttribute('name') ?? '', + daiName, }), element: instanceElement, primary: { diff --git a/src/wizards/foundation/dai-field-type.ts b/src/wizards/foundation/dai-field-type.ts index 787eb72958..c16a8465c1 100644 --- a/src/wizards/foundation/dai-field-type.ts +++ b/src/wizards/foundation/dai-field-type.ts @@ -207,8 +207,12 @@ export function getCustomField(): Record { }; } - function getInstanceValue(instanceElement?: Element): string { - return instanceElement!.querySelector('Val')?.textContent?.trim() ?? ''; + function getInstanceValue(daiOrVal?: Element): string { + const val = daiOrVal?.querySelector('Val') + ? daiOrVal?.querySelector('Val') + : daiOrVal; + + return val?.textContent?.trim() ?? ''; } function getEnumValues(element: Element): string[] { diff --git a/test/testfiles/editors/iedEditorWithIEDs.scd b/test/testfiles/editors/iedEditorWithIEDs.scd index 3c6a8b524d..80945ab95e 100644 --- a/test/testfiles/editors/iedEditorWithIEDs.scd +++ b/test/testfiles/editors/iedEditorWithIEDs.scd @@ -1,97 +1,97 @@ - - -
- TrainingIEC61850 - - - -
- - - 110.0 - +
+ TrainingIEC61850 + + + +
+ + + 110.0 + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - 20 - - - - - - - 100.0 - -
-

192.168.210.111

-

255.255.255.0

-

192.168.210.1

-

1,3,9999,23

-

23

-

00000001

-

0001

-

0001

-
- -
-

01-0C-CD-01-00-10

-

005

-

4

-

0010

-
-
- -

RJ45

-
-
-
- - -
-

192.168.0.112

-

255.255.255.0

-

192.168.210.1

-

1,3,9999,23

-

23

-

00000001

-

0001

-

0001

-
-
- -
-

192.168.0.113

-

255.255.255.0

-

192.168.210.1

-

1,3,9999,23

-

23

-

00000001

-

0001

-

0001

-
- +
+ + +
+ + 20 + + + +
+ + + 100.0 + +
+

192.168.210.111

+

255.255.255.0

+

192.168.210.1

+

1,3,9999,23

+

23

+

00000001

+

0001

+

0001

+
+ +
+

01-0C-CD-01-00-10

+

005

+

4

+

0010

+
+
+ +

RJ45

+
+
+
+ + +
+

192.168.0.112

+

255.255.255.0

+

192.168.210.1

+

1,3,9999,23

+

23

+

00000001

+

0001

+

0001

+
+
+ +
+

192.168.0.113

+

255.255.255.0

+

192.168.210.1

+

1,3,9999,23

+

23

+

00000001

+

0001

+

0001

+
+

01-0C-CD-04-00-20

007

@@ -99,306 +99,316 @@

4002

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - IED2 - - - - - - - status-only - - - - - - - - - sbo-with-enhanced-security - - - - - - - status-only - - - - - - - + + IED2 + + + + + + + status-only + + + + + + + + + sbo-with-enhanced-security + + + + + + + status-only + + + + + + + 1 - + - - sbo-with-enhanced-security - - - - - - - - - - status-only - - - - - - - - - - - - - - - status-only - - - - - - - - - direct-with-normal-security - - - - - - - sbo-with-normal-security - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - status-only - - - - - + + sbo-with-enhanced-security + + + + + + + + + + status-only + + + + + + + + + + + + + + + status-only + + + + + + + + + direct-with-normal-security + + + + + + + sbo-with-normal-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + - - - - - - - - - - status-only - - - - - - - - - - - - - - - - - - - - - - status-only - - - - - - - status-only - - - - - - - - - - - status-only - - - - - - - direct-with-enhanced-security - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + status-only + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + + status-only + + + + + + + + + + + status-only + + + + + + + direct-with-enhanced-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.001 + 0.005 + + + + + + + + + + + + + + + + + + diff --git a/test/testfiles/wizards/ied.scd b/test/testfiles/wizards/ied.scd index f685beecdb..74c2b87215 100644 --- a/test/testfiles/wizards/ied.scd +++ b/test/testfiles/wizards/ied.scd @@ -393,7 +393,16 @@ - + + + + + 0.001 + 0.005 + + + + @@ -527,8 +536,8 @@ - 21 - + 21 + diff --git a/test/unit/editors/ied/__snapshots__/da-container.test.snap.js b/test/unit/editors/ied/__snapshots__/da-container.test.snap.js index b8748b452f..d43a441a91 100644 --- a/test/unit/editors/ied/__snapshots__/da-container.test.snap.js +++ b/test/unit/editors/ied/__snapshots__/da-container.test.snap.js @@ -1,6 +1,59 @@ /* @web/test-runner snapshot v1 */ export const snapshots = {}; +snapshots["looks like the latest snapshot with a DA element containing and a DAI"] = +` + + + + +
+
+

+ status-only +

+
+
+ + +
+
+
+`; +/* end snapshot looks like the latest snapshot with a DA element containing and a DAI */ + +snapshots["with a DA element looks like the latest snapshot"] = +` + + + + +
+
+

+

+
+
+ + +
+
+
+`; +/* end snapshot with a DA element looks like the latest snapshot */ + snapshots["with a BDA element looks like the latest snapshot"] = ` -
-
+
+
+

+

+
+
+ + +
+
`; /* end snapshot with a BDA element looks like the latest snapshot */ +snapshots["with a BDA element having multiple values looks like the latest snapshot"] = +` + + + + +
+
+

+ SG1: 0.001 +

+
+
+ + +
+
+
+
+

+ SG2: 0.005 +

+
+
+ + +
+
+
+`; +/* end snapshot with a BDA element having multiple values looks like the latest snapshot */ + snapshots["with a DA element and child elements are toggled looks like the latest snapshot"] = ` -
-
@@ -61,40 +158,3 @@ snapshots["with a DA element and child elements are toggled looks like the lates `; /* end snapshot with a DA element and child elements are toggled looks like the latest snapshot */ -snapshots["looks like the latest snapshot with a DA element containing and a DAI"] = -` - - - - -
- status-only -
-
-`; -/* end snapshot looks like the latest snapshot with a DA element containing and a DAI */ - -snapshots["with a DA element looks like the latest snapshot"] = -` - - - - -
-
-
-`; -/* end snapshot with a DA element looks like the latest snapshot */ - diff --git a/test/unit/editors/ied/da-container.test.ts b/test/unit/editors/ied/da-container.test.ts index e3f1a0289b..89857a2c43 100644 --- a/test/unit/editors/ied/da-container.test.ts +++ b/test/unit/editors/ied/da-container.test.ts @@ -73,6 +73,38 @@ describe('da-container', async () => { }); }); + describe('with a BDA element having multiple values', () => { + beforeEach(async () => { + element = await fixture(html` BDA[name="scaleFactor"]' + )} + .instanceElement=${validSCL.querySelector( + 'IED[name="IED3"] DAI[name="scaleFactor"]' + )} + .daParent=${validSCL.querySelector( + 'DOType[id="DummySAV"] > DA[name="sVC"]' + )} + .ancestors=${[ + validSCL.querySelector('LNodeType[id="DummyTCTR"] > DO[name="Amp"]'), + validSCL.querySelector('DOType[id="DummySAV"] > DA[name="sVC"]'), + ]} + .nsdoc=${nsdoc} + >`); + }); + + it('check the values returned for the header', () => { + const header = element['header'](); + expect(header.values.length).to.be.equals(3); + expect(header.values[0]).to.be.equals('scaleFactor'); + expect(header.values[1]).to.be.equals('FLOAT32'); + }); + + it('looks like the latest snapshot', async () => { + expect(element).shadowDom.to.equalSnapshot(); + }); + }); + describe('with a DA element and child elements are toggled', () => { beforeEach(async () => { element = await fixture(html` { }); }); - describe('findLogicaNodeElement', async () => { + describe('findLogicalNodeElement', async () => { it('will find LN Element in list', async () => { const daElement = validSCL.querySelector( 'DataTypeTemplates > DOType[id="Dummy.XCBR1.Pos"] > DA[name="ctlModel"]' @@ -93,27 +93,40 @@ describe('ied-foundation', async () => { }); }); - describe('getValueElement', async () => { - let daiElement: Element; + describe('getValueElements', async () => { + let daiElement1: Element; + let daiElement2: Element; beforeEach(async () => { - daiElement = validSCL.querySelector( + daiElement1 = validSCL.querySelector( ':root > IED[name="IED2"] > AccessPoint[name="P1"] > Server > ' + 'LDevice[inst="CircuitBreaker_CB1"] > LN[lnType="Dummy.XCBR1"] > DOI[name="Pos"]> DAI[name="ctlModel"]' )!; + + daiElement2 = validSCL.querySelector( + ':root > IED[name="IED3"] > AccessPoint[name="P1"] > Server > ' + + 'LDevice[inst="MU01"] > LN[lnType="DummyTCTR"] > DOI[name="Amp"] > SDI[name="sVC"] > DAI[name="scaleFactor"]' + )!; }); - it('will find the val child of the element', async () => { - const value = getValueElement(daiElement); + it('returns all instantiated Val elements', async () => { + const value = getValueElements(daiElement2); + expect(value.length).to.equal(2); + expect(value[0].textContent).to.be.equal('0.001'); + expect(value[1].textContent).to.be.equal('0.005'); + }); + + it('returns one instantiated Val elements', async () => { + const value = getValueElements(daiElement1)[0]; expect(value).to.be.not.null; expect(value?.textContent).to.be.equal('status-only'); }); - it('will not find the val element of the element and return null', async () => { - daiElement.querySelector('Val')!.remove(); + it('returns empty array in case no Val i instantiated', async () => { + daiElement1.querySelector('Val')!.remove(); - const iedElement = getValueElement(daiElement); - expect(iedElement).to.be.null; + const iedElement = getValueElements(daiElement1); + expect(iedElement.length).to.equal(0); }); }); diff --git a/test/unit/wizards/__snapshots__/dai.test.snap.js b/test/unit/wizards/__snapshots__/dai.test.snap.js index 36ae2e534a..ca74babf19 100644 --- a/test/unit/wizards/__snapshots__/dai.test.snap.js +++ b/test/unit/wizards/__snapshots__/dai.test.snap.js @@ -127,7 +127,7 @@ snapshots["Wizards for SCL element DAI edit existing DAI with Val Element looks `; /* end snapshot Wizards for SCL element DAI edit existing DAI with Val Element looks like the latest snapshot */ -snapshots["Wizards for SCL element DAI edit existing DAI without Val Element looks like the latest snapshot"] = +snapshots["Wizards for SCL element DAI edit existing DAI with Val Element having on value looks like the latest snapshot"] = `
- + + - - on - - - blocked - - - test - - - test/blocked - - - off - - + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element DAI edit existing DAI with Val Element having on value looks like the latest snapshot */ + +snapshots["Wizards for SCL element DAI edit existing DAI with Val Element having multiple setting group values looks like the latest snapshot"] = +` +
+ + + +
`; -/* end snapshot Wizards for SCL element DAI edit existing DAI without Val Element looks like the latest snapshot */ +/* end snapshot Wizards for SCL element DAI edit existing DAI with Val Element having multiple setting group values looks like the latest snapshot */ diff --git a/test/unit/wizards/dai.test.ts b/test/unit/wizards/dai.test.ts index f46df00f50..168212d360 100644 --- a/test/unit/wizards/dai.test.ts +++ b/test/unit/wizards/dai.test.ts @@ -4,7 +4,6 @@ import '../../mock-wizard.js'; import { MockWizard } from '../../mock-wizard.js'; import { WizardTextField } from '../../../src/wizard-textfield.js'; -import { WizardSelect } from '../../../src/wizard-select.js'; import { ComplexAction, Create, @@ -14,11 +13,7 @@ import { WizardInputElement, } from '../../../src/foundation.js'; -import { - fetchDoc, - setWizardSelectValue, - setWizardTextFieldValue, -} from './test-support.js'; +import { fetchDoc, setWizardTextFieldValue } from './test-support.js'; import { createDAIWizard, createValue, @@ -106,68 +101,73 @@ describe('Wizards for SCL element DAI', () => { }); describe('edit existing DAI with Val Element', () => { - beforeEach(async () => { - doc = await fetchDoc('/test/testfiles/wizards/ied.scd'); - dai = doc.querySelector( - 'IED[name="IED1"] > AccessPoint[name="P1"] > Server > LDevice[inst="CircuitBreaker_CB1"] > LN[prefix="CB"][lnClass="CSWI"] > DOI[name="Beh"] > DAI[name="integer"]' - )!; - da = doc.querySelector( - 'DOType[cdc="ENS"][id="Dummy.LLN0.Beh"] > DA[name="integer"]' - )!; - - element = await fixture(html``); - const wizard = editDAIWizard(da, dai); - element.workflow.push(() => wizard); - await element.requestUpdate(); - inputs = Array.from(element.wizardUI.inputs); + describe('having on value', () => { + let val: Element; + + beforeEach(async () => { + doc = await fetchDoc('/test/testfiles/wizards/ied.scd'); + dai = doc.querySelector( + 'IED[name="IED1"] > AccessPoint[name="P1"] > Server > LDevice[inst="CircuitBreaker_CB1"] > LN[prefix="CB"][lnClass="CSWI"] > DOI[name="Beh"] > DAI[name="integer"]' + )!; + val = dai.querySelector('Val')!; + da = doc.querySelector( + 'DOType[cdc="ENS"][id="Dummy.LLN0.Beh"] > DA[name="integer"]' + )!; + + element = await fixture(html``); + const wizard = editDAIWizard(da, dai); + element.workflow.push(() => wizard); + await element.requestUpdate(); + inputs = Array.from(element.wizardUI.inputs); + }); + + it('update value should be updated in document', async function () { + await setWizardTextFieldValue(inputs[0], '8'); + + const complexActions = updateValue(da, val)(inputs, element.wizardUI); + expectUpdateComplexAction(complexActions); + + const replace = (complexActions[0]).actions[0]; + expect(replace.old.element.textContent).to.equal('5'); + expect(replace.new.element.textContent).to.equal('8'); + }); + + it('looks like the latest snapshot', async () => { + await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); + }); }); - it('update value should be updated in document', async function () { - await setWizardTextFieldValue(inputs[0], '8'); - - const complexActions = updateValue(da, dai)(inputs, element.wizardUI); - expectUpdateComplexAction(complexActions); - - const replace = (complexActions[0]).actions[0]; - expect(replace.old.element.textContent).to.equal('5'); - expect(replace.new.element.textContent).to.equal('8'); - }); - - it('looks like the latest snapshot', async () => { - await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); - }); - }); - - describe('edit existing DAI without Val Element', () => { - beforeEach(async () => { - doc = await fetchDoc('/test/testfiles/wizards/ied.scd'); - dai = doc.querySelector( - 'IED[name="IED1"] > AccessPoint[name="P1"] > Server > LDevice[inst="CircuitBreaker_CB1"] > LN[inst="1"][lnClass="XCBR"] > DOI[name="Beh"] > DAI[name="stVal"]' - )!; - da = doc.querySelector( - 'DOType[cdc="ENS"][id="Dummy.LLN0.Beh"] > DA[name="stVal"]' - )!; - - element = await fixture(html``); - const wizard = editDAIWizard(da, dai); - element.workflow.push(() => wizard); - await element.requestUpdate(); - inputs = Array.from(element.wizardUI.inputs); - }); - - it('update value should be updated in document', async function () { - await setWizardSelectValue(inputs[0], 'blocked'); - - const complexActions = updateValue(da, dai)(inputs, element.wizardUI); - expectUpdateComplexAction(complexActions); - - const create = (complexActions[0]).actions[0]; - expect(create.new.parent).to.equal(dai); - expect(create.new.element.textContent).to.equal('blocked'); - }); - - it('looks like the latest snapshot', async () => { - await expect(element.wizardUI.dialog).dom.to.equalSnapshot(); + describe('having multiple setting group values', () => { + let val: Element; + + beforeEach(async () => { + doc = await fetchDoc('/test/testfiles/wizards/ied.scd'); + dai = doc.querySelector( + ':root > IED[name="IED3"] > AccessPoint[name="P1"] > Server > ' + + 'LDevice[inst="MU01"] > LN[lnType="DummyTCTR"] > DOI[name="Amp"] > SDI[name="sVC"] > DAI[name="scaleFactor"]' + )!; + val = dai.querySelectorAll('Val')[1]; + da = doc.querySelector( + 'DAType[id="ScaledValueConfig"] > BDA[name="scaleFactor"]' + )!; + + element = await fixture(html``); + const wizard = editDAIWizard(da, dai); + element.workflow.push(() => wizard); + await element.requestUpdate(); + inputs = Array.from(element.wizardUI.inputs); + }); + + it('update value should be updated in document', async function () { + await setWizardTextFieldValue(inputs[0], '0.10'); + + const complexActions = updateValue(da, val)(inputs, element.wizardUI); + expectUpdateComplexAction(complexActions); + + const replace = (complexActions[0]).actions[0]; + expect(replace.old.element.textContent).to.equal('0.005'); + expect(replace.new.element.textContent).to.equal('0.10'); + }); }); }); From 81088f06bbae8ca022525fe29d59589ba87647a9 Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Thu, 23 Feb 2023 09:59:52 +0100 Subject: [PATCH 04/27] feat(wizards/services): add read-only wizard on access point and ied (#1109) * Services wizard (WIP) Signed-off-by: Pascal Wilbrink * Refactoring of services to prevent merge (big) conflicts Signed-off-by: Pascal Wilbrink * Fixed snapshot test Signed-off-by: Pascal Wilbrink Signed-off-by: Pascal Wilbrink * Only add the Services icon if there actually is a Services element within the IED. The Signed-off-by: Pascal Wilbrink * Wizardpages are optional in the Services Wizard. Don't show the wizard pages if no content is available Signed-off-by: Pascal Wilbrink * Map the nullable WizardPage to an actual WizardPage (nulls are already filtered out) Signed-off-by: Pascal Wilbrink Signed-off-by: Pascal Wilbrink * Added report and GSE configuration pages * Different querySelector * Fixed snapshot test * Added wizard page for Networking related configurations * Added wizard page for SMV related configurations * Added wizard page for other Client/Server related configurations * Read out values for log settingsGroup * Fixed possible null on wizard select Signed-off-by: Pascal Wilbrink Signed-off-by: Pascal Wilbrink * Added Tests Signed-off-by: Pascal Wilbrink * Fixed typo (uppercase file fetching in test) Signed-off-by: Pascal Wilbrink * Fixed typo (uppercase file fetching in test) Signed-off-by: Pascal Wilbrink * Fixed review comments Signed-off-by: Pascal Wilbrink * Added Tests for AccessPoint. Changed the check if a wizard page should be shown to a more `complex` function. Added tests for that function Signed-off-by: Pascal Wilbrink * Added array to isEmptyObject function to provide additional nullable properties Signed-off-by: Pascal Wilbrink * Removed Sitipe stuff Signed-off-by: Pascal Wilbrink --------- Signed-off-by: Pascal Wilbrink Co-authored-by: Stef3st Co-authored-by: Jakob Vogelsang --- src/WizardDivider.ts | 32 +- src/editors/ied/access-point-container.ts | 46 +- src/editors/ied/ied-container.ts | 22 + src/foundation.ts | 6 +- src/wizards/service-GSEControl.ts | 287 ++ .../service-clientServer-configurations.ts | 276 ++ src/wizards/service-log-settingsgroup.ts | 191 + src/wizards/service-networking.ts | 294 ++ src/wizards/service-report-configurations.ts | 305 ++ src/wizards/service-sampled-values.ts | 377 ++ src/wizards/services.ts | 103 + test/integration/editors/IED.test.ts | 87 +- .../services-wizard.test.snap.js | 3067 +++++++++++++++++ .../wizards/services-wizard.test.ts | 73 + test/testfiles/Services.scd | 550 +++ .../access-point-container.test.snap.js | 7 +- .../__snapshots__/ied-container.test.snap.js | 7 + test/unit/editors/ied/ied-container.test.ts | 14 + test/unit/wizards/services.test.ts | 35 + 19 files changed, 5733 insertions(+), 46 deletions(-) create mode 100644 src/wizards/service-GSEControl.ts create mode 100644 src/wizards/service-clientServer-configurations.ts create mode 100644 src/wizards/service-log-settingsgroup.ts create mode 100644 src/wizards/service-networking.ts create mode 100644 src/wizards/service-report-configurations.ts create mode 100644 src/wizards/service-sampled-values.ts create mode 100644 src/wizards/services.ts create mode 100644 test/integration/wizards/__snapshots__/services-wizard.test.snap.js create mode 100644 test/integration/wizards/services-wizard.test.ts create mode 100644 test/testfiles/Services.scd create mode 100644 test/unit/wizards/services.test.ts diff --git a/src/WizardDivider.ts b/src/WizardDivider.ts index 5a4fb60a8f..bef16841a5 100644 --- a/src/WizardDivider.ts +++ b/src/WizardDivider.ts @@ -1,11 +1,33 @@ -import {css, customElement, html, LitElement, TemplateResult} from "lit-element"; +import { + css, + customElement, + html, + LitElement, + property, + TemplateResult, +} from 'lit-element'; @customElement('wizard-divider') export class WizardDividerElement extends LitElement { + @property({ + type: String, + }) + header?: string; + render(): TemplateResult { - return html ` -
- ` + return html` ${this.renderHeader()} ${this.renderSeparator()}`; + } + + private renderHeader(): TemplateResult { + if (!this.header) { + return html``; + } + + return html`

${this.header}

`; + } + + private renderSeparator(): TemplateResult { + return html`
`; } static styles = css` @@ -18,5 +40,5 @@ export class WizardDividerElement extends LitElement { border-image: initial; border-bottom: 1px solid rgba(0, 0, 0, 0.12); } - ` + `; } diff --git a/src/editors/ied/access-point-container.ts b/src/editors/ied/access-point-container.ts index 990fc47dc8..d70189a287 100644 --- a/src/editors/ied/access-point-container.ts +++ b/src/editors/ied/access-point-container.ts @@ -8,9 +8,16 @@ import { TemplateResult, } from 'lit-element'; import { nothing } from 'lit-html'; +import { translate } from 'lit-translate'; +import { wizards } from '../../wizards/wizard-library.js'; -import { getDescriptionAttribute, getNameAttribute } from '../../foundation.js'; +import { + getDescriptionAttribute, + getNameAttribute, + newWizardEvent, +} from '../../foundation.js'; import { accessPointIcon } from '../../icons/ied-icons.js'; +import { editServicesWizard } from '../../wizards/services.js'; import '../../action-pane.js'; import './server-container.js'; @@ -23,13 +30,6 @@ export class AccessPointContainer extends Container { @property() selectedLNClasses: string[] = []; - private header(): TemplateResult { - const name = getNameAttribute(this.element); - const desc = getDescriptionAttribute(this.element); - - return html`${name}${desc ? html` — ${desc}` : nothing}`; - } - protected updated(_changedProperties: PropertyValues): void { super.updated(_changedProperties); @@ -39,6 +39,26 @@ export class AccessPointContainer extends Container { } } + private renderServicesIcon(): TemplateResult { + const services: Element | null = this.element.querySelector('Services'); + + if (!services) { + return html``; + } + + return html` + this.openSettingsWizard(services)} + > + `; + } + + private openSettingsWizard(services: Element): void { + const wizard = editServicesWizard(services); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + @state() private get lnElements(): Element[] { return Array.from(this.element.querySelectorAll(':scope > LN')).filter( @@ -49,11 +69,19 @@ export class AccessPointContainer extends Container { ); } + private header(): TemplateResult { + const name = getNameAttribute(this.element); + const desc = getDescriptionAttribute(this.element); + + return html`${name}${desc ? html` — ${desc}` : nothing}`; + } + render(): TemplateResult { const lnElements = this.lnElements; return html` ${accessPointIcon} + ${this.renderServicesIcon()} ${Array.from(this.element.querySelectorAll(':scope > Server')).map( server => html` ` + >` )} `; diff --git a/src/editors/ied/ied-container.ts b/src/editors/ied/ied-container.ts index ccb9330b03..ef3fd53ef6 100644 --- a/src/editors/ied/ied-container.ts +++ b/src/editors/ied/ied-container.ts @@ -20,6 +20,7 @@ import { newWizardEvent, } from '../../foundation.js'; import { removeIEDWizard } from '../../wizards/ied.js'; +import { editServicesWizard } from '../../wizards/services.js'; /** [[`IED`]] plugin subeditor for editing `IED` element. */ @customElement('ied-container') @@ -32,6 +33,26 @@ export class IedContainer extends Container { if (wizard) this.dispatchEvent(newWizardEvent(wizard)); } + private renderServicesIcon(): TemplateResult { + const services: Element | null = this.element.querySelector('Services'); + + if (!services) { + return html``; + } + + return html` + this.openSettingsWizard(services)} + > + `; + } + + private openSettingsWizard(services: Element): void { + const wizard = editServicesWizard(services); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + private removeIED(): void { const wizard = removeIEDWizard(this.element); if (wizard) { @@ -68,6 +89,7 @@ export class IedContainer extends Container { @click=${() => this.openEditWizard()} > + ${this.renderServicesIcon()} ${Array.from(this.element.querySelectorAll(':scope > AccessPoint')).map( ap => html` boolean; label: string }) + | (AceEditor & { + checkValidity: () => boolean; + label: string; + requestUpdate(name?: PropertyKey, oldValue?: unknown): Promise; + }) // TODO(c-dinkel): extend component | Select | WizardSelect; diff --git a/src/wizards/service-GSEControl.ts b/src/wizards/service-GSEControl.ts new file mode 100644 index 0000000000..b34c8b5606 --- /dev/null +++ b/src/wizards/service-GSEControl.ts @@ -0,0 +1,287 @@ +import { TemplateResult } from 'lit-html'; +import { get } from 'lit-translate'; +import { WizardPage } from '../foundation.js'; + +import { + createFormDivider, + createFormElementsFromInputs, + isEmptyObject, +} from './services.js'; + +interface GSESettings { + cbName: string | null; + datSet: string | null; + appID: string | null; + dataLabel: string | null; + kdaParticipant: string | null; + signature: string | null; + encryption: string | null; +} + +interface GOOSE { + max: string | null; + fixedOffs: string | null; + goose: string | null; + rGOOSE: string | null; +} + +interface ClientServices { + maxGOOSE: string | null; + goose: string | null; + rGOOSE: string | null; + gsse: string | null; +} + +interface SupSubscription { + maxGo: string | null; + maxSv: string | null; +} + +interface GSSE { + max: string | null; +} + +interface ContentOptions { + gseSettings: GSESettings; + goose: GOOSE; + clientServices: ClientServices; + supSubscription: SupSubscription; + gsse: GSSE; +} + +export function createGSEControlWizardPage( + services: Element +): WizardPage | null { + const content: TemplateResult[] | null = createGSEControlWizard(services); + + return content + ? { + title: get('wizard.title.edit', { tagName: 'GSE Control' }), + content: [...content], + } + : null; +} + +function createGSEControlWizard(parent: Element): TemplateResult[] | null { + const content: ContentOptions = { + gseSettings: { + cbName: + parent.querySelector('GSESettings')?.getAttribute('cbName') ?? null, + datSet: + parent.querySelector('GSESettings')?.getAttribute('datSet') ?? null, + appID: parent.querySelector('GSESettings')?.getAttribute('appID') ?? null, + dataLabel: + parent.querySelector('GSESettings')?.getAttribute('dataLabel') ?? null, + kdaParticipant: + parent.querySelector('GSESettings')?.getAttribute('kdaParticipant') ?? + null, + signature: + parent + .querySelector('GSESettings > McSecurity') + ?.getAttribute('signature') ?? null, + encryption: + parent + .querySelector('GSESettings > McSecurity') + ?.getAttribute('encryption') ?? null, + }, + goose: { + max: parent.querySelector('GOOSE')?.getAttribute('max') ?? null, + fixedOffs: + parent.querySelector('GOOSE')?.getAttribute('fixedOffs') ?? null, + goose: parent.querySelector('GOOSE')?.getAttribute('goose') ?? null, + rGOOSE: parent.querySelector('GOOSE')?.getAttribute('rGOOSE') ?? null, + }, + clientServices: { + maxGOOSE: + parent.querySelector('ClientServices')?.getAttribute('maxGOOSE') ?? + null, + goose: + parent.querySelector('ClientServices')?.getAttribute('goose') ?? null, + rGOOSE: + parent.querySelector('ClientServices')?.getAttribute('rGOOSE') ?? null, + gsse: + parent.querySelector('ClientServices')?.getAttribute('gsse') ?? null, + }, + supSubscription: { + maxGo: + parent.querySelector('SupSubscription')?.getAttribute('maxGo') ?? null, + maxSv: + parent.querySelector('SupSubscription')?.getAttribute('maxSv') ?? null, + }, + gsse: { + max: parent.querySelector('GSSE')?.getAttribute('max') ?? null, + }, + }; + + return isEmptyObject(content) + ? null + : [ + createFormDivider('Control Block Configuration'), + ...createFormElementsFromInputs([ + { + kind: 'Select', + label: 'cbName', + maybeValue: content.gseSettings.cbName, + helper: + 'Whether GSE control block (GOOSE) name is configurable offline (Conf) or fixed (Fix)', + values: ['Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'datSet', + maybeValue: content.gseSettings.datSet, + helper: + 'Whether GSE control blocks (GOOSE) data set and its structure is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'appID', + maybeValue: content.gseSettings.appID, + helper: + 'Whether GSE control blocks (GOOSE) ID is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'dataLabel', + maybeValue: content.gseSettings.dataLabel, + helper: + 'Deprecated!: Whether GSSE object reference is configurable offline (Conf), online(Dyn) or are fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Checkbox', + label: 'kdaParticipant', + maybeValue: content.gseSettings.kdaParticipant, + helper: + 'Whether key delivery assurance (KDA) is supported by the server', + nullable: true, + }, + { + kind: 'Checkbox', + label: 'signature', + helper: + 'Whether calculation of a signature is supported for each GOOSE', + maybeValue: content.gseSettings.signature, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'encryption', + helper: 'Whether message encryption is supported for each GOOSE', + maybeValue: content.gseSettings.encryption, + nullable: true, + default: false, + }, + ]), + createFormDivider('Publisher Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'max', + required: true, + helper: + 'The maximum number of configurable GOOSE control blocks. 0 means no GOOSE publishing supported', + maybeValue: content.goose.max?.toString() ?? null, + nullable: true, + }, + { + kind: 'Checkbox', + label: 'fixedOffs', + maybeValue: content.goose.fixedOffs, + helper: + 'Whether encoding with fixed offsets is configurable for each GSE control block (GOOSE). See also IEC 61850-8-1', + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'goose', + helper: 'Whether GOOSE publishing is supported', + maybeValue: content.goose.goose, + nullable: true, + default: true, + }, + { + kind: 'Checkbox', + label: 'rGOOSE', + helper: 'Whether GOOSE with network layer 3 (IP) is supported', + maybeValue: content.goose.rGOOSE, + nullable: true, + default: false, + }, + ]), + createFormDivider('Subscription Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'goose', + helper: + 'Whether the IED supports client side GOOSE related services', + maybeValue: content.clientServices.goose?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'TextField', + label: 'maxGOOSE', + required: true, + helper: 'The maximal number of GOOSEs the client can subscribe to', + maybeValue: content.clientServices.maxGOOSE?.toString() ?? null, + nullable: true, + }, + { + kind: 'Checkbox', + label: 'rGOOSE', + helper: + 'The maximal number of GOOSEs with network layer 3 the client can subscribe to', + maybeValue: content.clientServices.rGOOSE?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'gsse', + helper: + 'Whether the IED supports client side GSSE related services', + maybeValue: content.clientServices.gsse?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Supervision Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'maxGo', + required: false, + helper: + 'The maximum number of GOOSE supervision supported by this IED (LGOS)', + maybeValue: content.supSubscription.maxGo?.toString() ?? null, + nullable: true, + }, + ]), + createFormDivider('GSSE Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'max', + required: true, + helper: + 'The maximum number of GSSE supported as publisher. 0 means IED can only subscribe on GSSE messages', + maybeValue: content.gsse.max?.toString() ?? null, + nullable: true, + }, + ]), + ]; +} diff --git a/src/wizards/service-clientServer-configurations.ts b/src/wizards/service-clientServer-configurations.ts new file mode 100644 index 0000000000..076929c8d6 --- /dev/null +++ b/src/wizards/service-clientServer-configurations.ts @@ -0,0 +1,276 @@ +import { TemplateResult } from 'lit-html'; +import { get } from 'lit-translate'; +import { WizardPage } from '../foundation.js'; + +import { + createFormDivider, + createFormElementsFromInputs, + isEmptyObject, +} from './services.js'; + +interface DynamicAssociations { + max: string | null; +} + +interface DiscoverCapabilities { + getDirectory: string | null; + getDataObjectDefinition: string | null; + dataObjectDirectory: string | null; + getDataSetValue: string | null; + setDataSetValue: string | null; + setDataSetDirectory: string | null; + readWrite: string | null; +} + +interface FunctionalNaming { + confLdName: string | null; + supportsLdName: string | null; +} + +interface ClientCapabilities { + maxAttributes: string | null; + timerActivatedControl: string | null; + getCBValues: string | null; + GSEDir: string | null; +} + +interface ValKindManipulationConfig { + setToRO: string | null; +} + +interface SignalReferenceConfig { + max: string | null; +} + +interface ContentOptions { + dynamicAssociations: DynamicAssociations; + discoverCapabilities: DiscoverCapabilities; + functionalNaming: FunctionalNaming; + clientCapabilities: ClientCapabilities; + valKindManipulationConfig: ValKindManipulationConfig; + signalReferenceConfig: SignalReferenceConfig; +} + +export function createClientServerConfigurationsWizardPage( + services: Element +): WizardPage | null { + const content: TemplateResult[] | null = + createClientServerConfigurationsWizard(services); + + return content + ? { + title: get('wizard.title.edit', { tagName: 'Client Server Services' }), + content: [...content], + } + : null; +} + +function createClientServerConfigurationsWizard( + parent: Element +): TemplateResult[] | null { + const content: ContentOptions = { + dynamicAssociations: { + max: parent.querySelector('DynAssociation')?.getAttribute('max') ?? null, + }, + discoverCapabilities: { + getDirectory: parent.querySelector('GetDirectory') ? 'true' : null, + getDataObjectDefinition: parent.querySelector('GetDataObjectDefinition') + ? 'true' + : null, + dataObjectDirectory: parent.querySelector('DataObjectDirectory') + ? 'true' + : null, + getDataSetValue: parent.querySelector('GetDataSetValue') ? 'true' : null, + setDataSetValue: parent.querySelector('SetDataSetValue') ? 'true' : null, + setDataSetDirectory: parent.querySelector('DataSetDirectory') + ? 'true' + : null, + readWrite: parent.querySelector('ReadWrite') ? 'true' : null, + }, + functionalNaming: { + confLdName: parent.querySelector('ConfLdName') ? 'true' : null, + supportsLdName: + parent + .querySelector('ClientServices') + ?.getAttribute('supportsLdName') ?? null, + }, + clientCapabilities: { + maxAttributes: + parent.querySelector('ClientServices')?.getAttribute('maxAttributes') ?? + null, + timerActivatedControl: parent.querySelector('TimerActivatedControl') + ? 'true' + : null, + getCBValues: parent.querySelector('GetCBValues') ? 'true' : null, + GSEDir: parent.querySelector('GSEDir') ? 'true' : null, + }, + valKindManipulationConfig: { + setToRO: + parent.querySelector('ValueHandling')?.getAttribute('setToRO') ?? null, + }, + signalReferenceConfig: { + max: parent.querySelector('ConfSigRef')?.getAttribute('max') ?? null, + }, + }; + + return isEmptyObject(content) + ? null + : [ + createFormDivider('Dynamic Associations'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'max', + required: false, + helper: + 'The maximum number of guaranteed parallel association with the IED. If missing, no association is possible', + maybeValue: content.dynamicAssociations.max?.toString() ?? null, + nullable: true, + }, + ]), + createFormDivider('Discover Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'GetDirectory', + helper: + 'Whether IED supports GetServerDirectory, GetLogicalDeviceDirectory, GetLogicalNodeDirectory', + maybeValue: content.discoverCapabilities.getDirectory, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'GetDataObjectDefinition', + helper: 'Whether IED supports the service GetDataDefinition', + maybeValue: content.discoverCapabilities.getDataObjectDefinition, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'DataObjectDirectory', + helper: 'Whether IED supports the service GetDataDirectory', + maybeValue: content.discoverCapabilities.dataObjectDirectory, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'GetDataSetValue', + helper: 'Whether IED supports the service GetDataSetValues', + maybeValue: content.discoverCapabilities.getDataSetValue, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'SetDataSetValue', + helper: 'Whether IED supports the service SetDataSetValue', + maybeValue: content.discoverCapabilities.setDataSetValue, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'SetDataSetDirectory', + helper: 'Whether IED supports the service SetDataSetDirectory', + maybeValue: content.discoverCapabilities.setDataSetDirectory, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'ReadWrite', + helper: + 'Whether IED supports the service GetData, SetData, and the Operate services', + maybeValue: content.discoverCapabilities.readWrite, + nullable: true, + default: false, + }, + ]), + createFormDivider('Functional Naming'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'ConfLdName', + helper: + 'Whether the IED allows defining the attribute ldName in logical devices (LDevice)', + maybeValue: content.functionalNaming.confLdName, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'supportsLdName', + helper: + 'Whether the IED understands the logical device (LDevice) name (ldName) setting as a client', + maybeValue: content.functionalNaming.supportsLdName, + nullable: true, + default: false, + }, + ]), + createFormDivider('Client Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'maxAttributes', + required: false, + helper: + 'The maximum receivable data attributes (across all data sets)', + maybeValue: + content.clientCapabilities.maxAttributes?.toString() ?? null, + nullable: true, + }, + { + kind: 'Checkbox', + label: 'TimerActivatedControl', + helper: 'Whether IED supports time activated control', + maybeValue: content.clientCapabilities.timerActivatedControl, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'GetCBValues', + helper: 'Whether IED can read control blocks online', + maybeValue: content.clientCapabilities.getCBValues, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'GSEDir', + helper: + 'Whether IED supports GSE directory services acc. to IEC 61850-7-2', + maybeValue: content.clientCapabilities.GSEDir, + nullable: true, + default: false, + }, + ]), + createFormDivider('ValKind Manipulation Configuration'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'setToRO', + helper: + 'Whether valKind attribute in DA/BDA element that are Set can be modified to RO (only for function constrains for CF, DC, SP)', + maybeValue: content.valKindManipulationConfig.setToRO, + nullable: true, + default: false, + }, + ]), + createFormDivider('Signal Reference Configuration'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'max', + required: false, + helper: + 'The maximum object references that the IED can create (instantiation only by IED Configuration Tool)', + maybeValue: content.signalReferenceConfig.max?.toString() ?? null, + nullable: true, + }, + ]), + ]; +} diff --git a/src/wizards/service-log-settingsgroup.ts b/src/wizards/service-log-settingsgroup.ts new file mode 100644 index 0000000000..398aae4bca --- /dev/null +++ b/src/wizards/service-log-settingsgroup.ts @@ -0,0 +1,191 @@ +import { TemplateResult } from 'lit-html'; +import { get } from 'lit-translate'; +import { WizardPage } from '../foundation.js'; + +import { + createFormDivider, + createFormElementsFromInputs, + isEmptyObject, +} from './services.js'; + +interface LogSettings { + cbName: string | null; + datSet: string | null; + logEna: string | null; + trgOps: string | null; + intgPd: string | null; +} + +interface ConfLogControl { + max: string | null; +} + +interface ClientServices { + readLog: string | null; +} + +interface SGEdit { + resvTms: string | null; +} + +interface ConfSG { + resvTms: string | null; +} +interface ContentOptions { + logSettings: LogSettings; + confLogControl: ConfLogControl; + clientServices: ClientServices; + sGEdit: SGEdit; + confSG: ConfSG; +} + +export function createLogSettingsGroupServicesWizardPage( + services: Element +): WizardPage | null { + const content: TemplateResult[] | null = + createLogSettingsGroupServicesWizard(services); + + return content + ? { + title: get('wizard.title.edit', { tagName: 'Services' }), + content: [...content], + } + : null; +} + +function createLogSettingsGroupServicesWizard( + parent: Element +): TemplateResult[] | null { + const content: ContentOptions = { + logSettings: { + cbName: + parent.querySelector('LogSettings')?.getAttribute('cbName') ?? null, + datSet: + parent.querySelector('LogSettings')?.getAttribute('datSet') ?? null, + logEna: + parent.querySelector('LogSettings')?.getAttribute('logEna') ?? null, + intgPd: + parent.querySelector('LogSettings')?.getAttribute('trgOps') ?? null, + trgOps: + parent.querySelector('LogSettings')?.getAttribute('intgPd') ?? null, + }, + confLogControl: { + max: parent.querySelector('ConfLogControl')?.getAttribute('max') ?? null, + }, + clientServices: { + readLog: + parent.querySelector('CientServices')?.getAttribute('readLog') ?? null, + }, + sGEdit: { + resvTms: + parent + .querySelector('SettingGroups > SGEdit') + ?.getAttribute('resvTms') || null, + }, + confSG: { + resvTms: + parent + .querySelector('SettingGroups > ConfSG') + ?.getAttribute('resvTms') || null, + }, + }; + + return isEmptyObject(content) + ? null + : [ + createFormDivider('Log Control Configuration'), + ...createFormElementsFromInputs([ + { + kind: 'Select', + label: 'cbName', + maybeValue: content.logSettings.cbName, + helper: + 'Whether log control block name is configurable offline (Conf) or fixed (Fix)', + values: ['Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'datSet', + maybeValue: content.logSettings.datSet, + helper: + 'Whether log control blocks data set and its structure is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'logEna', + maybeValue: content.logSettings.logEna, + helper: + 'Whether log control blocks attribute logEna is configurable offline (Conf), online (Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'trgOps', + maybeValue: content.logSettings.trgOps, + helper: + 'Whether log control blocks trigger options are configurable offline (Conf), online(Dyn) or are fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'intgPd', + maybeValue: content.logSettings.intgPd, + helper: + 'Whether log control blocks integrity period is configurable offlien (Conf), online (Dyn), or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + ]), + createFormDivider('Log Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'Max', + required: true, + helper: + 'The maximum number of log control blocks instantiable by system configuration tool', + maybeValue: content.confLogControl.max, + }, + ]), + createFormDivider('Client Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'read Log', + nullable: true, + helper: + 'Whether IED supports services to handle logs as a client (see IEC 61850-7-2 for further information)', + maybeValue: content.clientServices.readLog, + }, + ]), + createFormDivider('Setting Group'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'SGEdit', + nullable: true, + helper: + 'Whether IED allows manipulating editable setting groups online', + maybeValue: content.sGEdit.resvTms, + }, + { + kind: 'Checkbox', + label: 'ConfSG', + nullable: true, + helper: + 'Whether IED accepts the system configuration tool to configure the number of setting groups', + maybeValue: content.confSG.resvTms, + }, + ]), + ]; +} diff --git a/src/wizards/service-networking.ts b/src/wizards/service-networking.ts new file mode 100644 index 0000000000..6043245b57 --- /dev/null +++ b/src/wizards/service-networking.ts @@ -0,0 +1,294 @@ +import { TemplateResult } from 'lit-html'; +import { get } from 'lit-translate'; +import { WizardPage } from '../foundation.js'; + +import { + createFormDivider, + createFormElementsFromInputs, + isEmptyObject, +} from './services.js'; + +interface FileHandling { + mms: string | null; + ftp: string | null; + ftps: string | null; +} + +interface TimeSyncProt { + sntp: string | null; + iec61850_9_3: string | null; + c37_238: string | null; + other: string | null; +} + +interface ClientServices_TimeSyncProt { + sntp: string | null; + iec61850_9_3: string | null; + c37_238: string | null; + other: string | null; +} + +interface ClientServices_McSecurity { + signature: string | null; + encryption: string | null; +} + +interface RedProt { + hsr: string | null; + prp: string | null; + rstp: string | null; +} + +interface CommProt { + ipv6: string | null; +} + +interface ContentOptions { + fileHandling: FileHandling; + timeSyncProt: TimeSyncProt; + cs_TimeSyncProt: ClientServices_TimeSyncProt; + cs_McSecurity: ClientServices_McSecurity; + redProt: RedProt; + commProt: CommProt; +} + +export function createNetworkingWizardPage( + services: Element +): WizardPage | null { + const content: TemplateResult[] | null = createNetworkingWizard(services); + + return content + ? { + title: get('wizard.title.edit', { tagName: 'Networking' }), + content: [...content], + } + : null; +} + +function createNetworkingWizard(parent: Element): TemplateResult[] | null { + const content: ContentOptions = { + fileHandling: { + mms: parent.querySelector('FileHandling')?.getAttribute('mms') ?? null, + ftp: parent.querySelector('FileHandling')?.getAttribute('ftp') ?? null, + ftps: parent.querySelector('FileHandling')?.getAttribute('ftps') ?? null, + }, + timeSyncProt: { + sntp: parent.querySelector('TimeSyncProt')?.getAttribute('sntp') ?? null, + iec61850_9_3: + parent.querySelector('TimeSyncProt')?.getAttribute('iec61850_9_3') ?? + null, + c37_238: + parent.querySelector('TimeSyncProt')?.getAttribute('c37_238') ?? null, + other: + parent.querySelector('TimeSyncProt')?.getAttribute('other') ?? null, + }, + cs_TimeSyncProt: { + sntp: + parent + .querySelector('ClientServices > TimeSyncProt') + ?.getAttribute('sntp') ?? null, + iec61850_9_3: + parent + .querySelector('ClientServices > TimeSyncProt') + ?.getAttribute('iec61850_9_3') ?? null, + c37_238: + parent + .querySelector('ClientServices > TimeSyncProt') + ?.getAttribute('c37_238') ?? null, + other: + parent + .querySelector('ClientServices > TimeSyncProt') + ?.getAttribute('other') ?? null, + }, + cs_McSecurity: { + signature: + parent + .querySelector('ClientServices > McSecurity') + ?.getAttribute('signature') ?? null, + encryption: + parent + .querySelector('ClientServices > McSecurity') + ?.getAttribute('encryption') ?? null, + }, + redProt: { + hsr: parent.querySelector('RedProt')?.getAttribute('hsr') ?? null, + prp: parent.querySelector('RedProt')?.getAttribute('prp') ?? null, + rstp: parent.querySelector('RedProt')?.getAttribute('rstp') ?? null, + }, + commProt: { + ipv6: parent.querySelector('CommProt')?.getAttribute('ipv6') ?? null, + }, + }; + + return isEmptyObject(content) + ? null + : [ + createFormDivider('File Handling'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'mms', + helper: + 'Whether the IED supports file transfer as defined by the manufacturer messaging service (MMS)', + maybeValue: content.fileHandling.mms?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'ftp', + helper: 'Whether the IED supports file transfer service (FTP)', + maybeValue: content.fileHandling.ftp?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'ftps', + helper: + 'Whether the IED supports encrypted file transfer service (FTPS)', + maybeValue: content.fileHandling.ftps?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Time Server Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'sntp', + helper: + 'Whether the IED supports simple network time protocol as time-server', + maybeValue: content.timeSyncProt.sntp?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'iec61850_9_3', + helper: + 'Whether the IED supports precision time protocol (PTP) acc. to IEC 61850-9-3 as time-server', + maybeValue: content.timeSyncProt.iec61850_9_3?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'c37_238', + helper: + 'Whether the IED supports precision time protocol (PTP) acc. to C37.238 as time-server', + maybeValue: content.timeSyncProt.c37_238?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'other', + helper: + 'Whether IED support other type of synchronization as time-server (e.g. PPS)', + maybeValue: content.timeSyncProt.other?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Time Client Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'sntp', + helper: + 'Whether the IED supports simple network time protocol as time-client', + maybeValue: content.cs_TimeSyncProt.sntp?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'iec61850_9_3', + helper: + 'Whether the IED supports precision time protocol (PTP) acc. to IEC 61850-9-3 as time-client', + maybeValue: + content.cs_TimeSyncProt.iec61850_9_3?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'c37_238', + helper: + 'Whether the IED supports precision time protocol (PTP) acc. to C37.238 as time-client', + maybeValue: content.cs_TimeSyncProt.c37_238?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'other', + helper: + 'Whether IED support other type of synchronization as time-client (e.g. PPS)', + maybeValue: content.cs_TimeSyncProt.other?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Multicast Security on Server'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'signature', + helper: + 'Whether calculation of a signature is supported for SMV/GOOSE on this IED/access point', + maybeValue: content.cs_McSecurity.signature?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'encryption', + helper: + 'Whether message encryption is supported for SMV/GOOSE on this IED/access point', + maybeValue: content.cs_McSecurity.encryption?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Redundancy Protocols'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'hsr', + helper: 'Whether the IED supports redundancy protocol HSR', + maybeValue: content.redProt.hsr?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'prp', + helper: 'Whether the IED supports redundancy protocol PRP', + maybeValue: content.redProt.prp?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'rstp', + helper: 'Whether the IED supports redundancy protocol RSTP', + maybeValue: content.redProt.rstp?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Others'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'ipv6', + helper: 'Whether the IED supports IP version 6', + maybeValue: content.commProt.ipv6?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + ]; +} diff --git a/src/wizards/service-report-configurations.ts b/src/wizards/service-report-configurations.ts new file mode 100644 index 0000000000..7781249f01 --- /dev/null +++ b/src/wizards/service-report-configurations.ts @@ -0,0 +1,305 @@ +import { TemplateResult } from 'lit-html'; +import { get } from 'lit-translate'; +import { WizardPage } from '../foundation.js'; + +import { + createFormDivider, + createFormElementsFromInputs, + isEmptyObject, +} from './services.js'; + +interface ReportSettings { + cbName: string | null; + datSet: string | null; + rptID: string | null; + optFields: string | null; + bufTime: string | null; + trgOps: string | null; + intgPd: string | null; + resvTms: string | null; + owner: string | null; +} + +interface ConfReportControl { + max: string | null; + bufMode: string | null; + maxBuf: string | null; + bufConf: string | null; +} + +interface ClientServices { + maxReports: string | null; + bufReport: string | null; + unbufReport: string | null; +} + +interface DynDataSet { + max: string | null; + maxAttributes: string | null; +} + +interface ContentOptions { + reportSettings: ReportSettings; + confReportControl: ConfReportControl; + clientServices: ClientServices; + dynDataSet: DynDataSet; +} + +export function createReportConfigurationsWizardPage( + services: Element +): WizardPage | null { + const content: TemplateResult[] | null = + createReportConfigurationsWizard(services); + + return content + ? { + title: get('wizard.title.edit', { tagName: 'Report Settings' }), + content: [...content], + } + : null; +} + +function createReportConfigurationsWizard( + parent: Element +): TemplateResult[] | null { + const content: ContentOptions = { + reportSettings: { + cbName: + parent.querySelector('ReportSettings')?.getAttribute('cbName') ?? null, + datSet: + parent.querySelector('ReportSettings')?.getAttribute('datSet') ?? null, + rptID: + parent.querySelector('ReportSettings')?.getAttribute('rptID') ?? null, + optFields: + parent.querySelector('ReportSettings')?.getAttribute('optFields') ?? + null, + bufTime: + parent.querySelector('ReportSettings')?.getAttribute('bufTime') ?? null, + trgOps: + parent.querySelector('ReportSettings')?.getAttribute('trgOps') ?? null, + intgPd: + parent.querySelector('ReportSettings')?.getAttribute('intgPd') ?? null, + resvTms: + parent.querySelector('ReportSettings')?.getAttribute('resvTms') ?? null, + owner: + parent.querySelector('ReportSettings')?.getAttribute('owner') ?? null, + }, + confReportControl: { + max: + parent.querySelector('ConfReportControl')?.getAttribute('max') ?? null, + bufMode: + parent.querySelector('ConfReportControl')?.getAttribute('bufMode') ?? + null, + maxBuf: + parent.querySelector('ConfReportControl')?.getAttribute('maxBuf') ?? + null, + bufConf: + parent.querySelector('ConfReportControl')?.getAttribute('bufConf') ?? + null, + }, + clientServices: { + maxReports: + parent.querySelector('ClientServices')?.getAttribute('maxReports') ?? + null, + bufReport: + parent.querySelector('ClientServices')?.getAttribute('bufReport') ?? + null, + unbufReport: + parent.querySelector('ClientServices')?.getAttribute('unbufReport') ?? + null, + }, + dynDataSet: { + max: parent.querySelector('DynDataSet')?.getAttribute('max') ?? null, + maxAttributes: + parent.querySelector('DynDataSet')?.getAttribute('maxAttributes') ?? + null, + }, + }; + + return isEmptyObject(content) + ? null + : [ + createFormDivider('Control Block Configuration'), + ...createFormElementsFromInputs([ + { + kind: 'Select', + label: 'cbName', + maybeValue: content.reportSettings.cbName, + helper: + 'Whether report control block name is configurable offline (Conf) or fixed (Fix)', + values: ['Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'datSet', + maybeValue: content.reportSettings.datSet, + helper: + 'Whether report control blocks data set and its structure is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'rptID', + maybeValue: content.reportSettings.rptID, + helper: + 'Whether report control blocks ID is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'optFields', + maybeValue: content.reportSettings.optFields, + helper: + 'Whether report control blocks optional fields are configurable offline (Conf), online(Dyn) or are fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'bufTime', + maybeValue: content.reportSettings.bufTime, + helper: + 'Whether report control blocks bufTime attribute is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'trgOps', + maybeValue: content.reportSettings.trgOps, + helper: + 'Whether report control blocks trigger options are configurable offline (Conf), online(Dyn) or are fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'intgPd', + maybeValue: content.reportSettings.intgPd, + helper: + 'Whether report control blocks integrity period is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Checkbox', + label: 'resvTms', + helper: + 'Whether reserve time exists in all buffered report control blocks', + maybeValue: content.reportSettings.resvTms?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'owner', + helper: + 'Whether owner attribute exists on all buffered report control blocks', + maybeValue: content.reportSettings.owner?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Publisher Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'max', + required: true, + helper: + 'The maximum number of report control blocks instantiable by system configuration tool', + maybeValue: content.confReportControl.max?.toString() ?? null, + nullable: true, + }, + { + kind: 'Select', + label: 'bufMode', + maybeValue: content.confReportControl.bufMode, + helper: + 'Whether buffered, unbuffered or both type of report control block can be created by system configuration tool', + values: ['unbuffered', 'buffered', 'both'], + default: 'both', + nullable: true, + }, + { + kind: 'TextField', + label: 'maxBuf', + required: false, + helper: + 'The maximum number of BUFFERED report control blocks instantiable by system configuration tool', + maybeValue: content.confReportControl.maxBuf?.toString() ?? null, + nullable: true, + }, + { + kind: 'Checkbox', + label: 'bufConf', + helper: + 'Whether buffered attribute can be configured by system configuration tool', + maybeValue: content.confReportControl.bufConf?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Client Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'maxReports', + required: true, + helper: + 'The maximal number of report control blocks the client can work with', + maybeValue: content.clientServices.maxReports?.toString() ?? null, + nullable: true, + }, + { + kind: 'Checkbox', + label: 'bufReport', + helper: + 'Whether the IED can use buffered report control blocks as a client', + maybeValue: content.clientServices.bufReport?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'unbufReport', + helper: + 'Whether the IED can use un-buffered report control blocks as a client', + maybeValue: content.clientServices.unbufReport?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Dynamic Reporting/DataSets'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'max', + required: true, + helper: + 'The maximum number data sets (including preconfigured once)', + maybeValue: content.dynDataSet.max?.toString() ?? null, + nullable: true, + }, + { + kind: 'TextField', + label: 'maxAttributes', + required: false, + helper: + 'The maximum number of data entries (FCDA) allowed within a dynamic data set', + maybeValue: content.dynDataSet.maxAttributes?.toString() ?? null, + nullable: true, + }, + ]), + ]; +} diff --git a/src/wizards/service-sampled-values.ts b/src/wizards/service-sampled-values.ts new file mode 100644 index 0000000000..d8523c73b5 --- /dev/null +++ b/src/wizards/service-sampled-values.ts @@ -0,0 +1,377 @@ +import { TemplateResult } from 'lit-html'; +import { get } from 'lit-translate'; +import { WizardPage } from '../foundation.js'; + +import { + createFormDivider, + createFormElementsFromInputs, + isEmptyObject, +} from './services.js'; + +interface ControlBlockConfiguration { + cbName: string | null; + datSet: string | null; + svID: string | null; + optFields: string | null; + smpRate: string | null; + nofASDU: string | null; + samplesPerSec: string | null; + synchSrcId: string | null; + pdcTimeStamp: string | null; + kdaParticipant: string | null; + signature: string | null; + encryption: string | null; + smpRateVal: string | null; + samplesPerSecVal: string | null; + secPerSamplesVal: string | null; +} + +interface PublisherCapabilities { + max: string | null; + delivery: string | null; + deliveryConf: string | null; + sv: string | null; + rSV: string | null; +} + +interface SubscriptionCapabilities { + sv: string | null; + maxSMV: string | null; + rSV: string | null; +} + +interface SuperVisionCapabilities { + maxSv: string | null; +} + +interface ContentOptions { + controlBlockConfiguration: ControlBlockConfiguration; + publisherCapabilities: PublisherCapabilities; + subscriptionCapabilities: SubscriptionCapabilities; + superVisionCapabilities: SuperVisionCapabilities; +} + +export function createSampledValuesWizardPage( + services: Element +): WizardPage | null { + const content: TemplateResult[] | null = createSampledValuesWizard(services); + + return content + ? { + title: get('wizard.title.edit', { tagName: 'Sampled Values' }), + content: [...content], + } + : null; +} + +function createSampledValuesWizard(parent: Element): TemplateResult[] | null { + const content: ContentOptions = { + controlBlockConfiguration: { + cbName: + parent.querySelector('SMVSettings')?.getAttribute('cbName') ?? null, + datSet: + parent.querySelector('SMVSettings')?.getAttribute('datSet') ?? null, + svID: parent.querySelector('SMVSettings')?.getAttribute('svID') ?? null, + optFields: + parent.querySelector('SMVSettings')?.getAttribute('optFields') ?? null, + smpRate: + parent.querySelector('SMVSettings')?.getAttribute('smpRate') ?? null, + nofASDU: + parent.querySelector('SMVSettings')?.getAttribute('nofASDU') ?? null, + samplesPerSec: + parent.querySelector('SMVSettings')?.getAttribute('samplesPerSec') ?? + null, + synchSrcId: + parent.querySelector('SMVSettings')?.getAttribute('synchSrcId') ?? null, + pdcTimeStamp: + parent.querySelector('SMVSettings')?.getAttribute('pdcTimeStamp') ?? + null, + kdaParticipant: + parent.querySelector('SMVSettings')?.getAttribute('kdaParticipant') ?? + null, + signature: + parent + .querySelector('SMVSettings > McSecurity') + ?.getAttribute('signature') ?? null, + encryption: + parent + .querySelector('SMVSettings > McSecurity') + ?.getAttribute('encryption') ?? null, + smpRateVal: + parent.querySelector('SMVSettings>SmpRate')?.childNodes[0]?.nodeValue ?? + null, + samplesPerSecVal: + parent.querySelector('SMVSettings > SamplesPerSec')?.childNodes[0] + ?.nodeValue ?? null, + secPerSamplesVal: + parent.querySelector('SMVSettings > SecPerSamples')?.childNodes[0] + ?.nodeValue ?? null, + }, + publisherCapabilities: { + max: parent.querySelector('SMVsc')?.getAttribute('max') ?? null, + delivery: parent.querySelector('SMVsc')?.getAttribute('delivery') ?? null, + deliveryConf: + parent.querySelector('SMVsc')?.getAttribute('deliveryConf') ?? null, + sv: parent.querySelector('SMVsc')?.getAttribute('sv') ?? null, + rSV: parent.querySelector('SMVsc')?.getAttribute('rSV') ?? null, + }, + subscriptionCapabilities: { + sv: parent.querySelector('ClientServices')?.getAttribute('sv') ?? null, + maxSMV: + parent.querySelector('ClientServices')?.getAttribute('maxSMV') ?? null, + rSV: parent.querySelector('ClientServices')?.getAttribute('rSV') ?? null, + }, + superVisionCapabilities: { + maxSv: + parent.querySelector('SupSubscription')?.getAttribute('maxSv') ?? null, + }, + }; + + return isEmptyObject(content) + ? null + : [ + createFormDivider('Control Block Configuration'), + ...createFormElementsFromInputs([ + { + kind: 'Select', + label: 'cbName', + maybeValue: content.controlBlockConfiguration.cbName, + helper: + 'Whether SMV control block name is configurable offline (Conf) or fixed (Fix)', + values: ['Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'datSet', + maybeValue: content.controlBlockConfiguration.datSet, + helper: + 'Whether SMV control blocks data set and its structure is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'svID', + maybeValue: content.controlBlockConfiguration.svID, + helper: + 'Whether SMV control blocks ID is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'optFields', + maybeValue: content.controlBlockConfiguration.optFields, + helper: + 'Whether SMV control blocks optional fields are configurable offline (Conf), online(Dyn) or are fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'smpRate', + maybeValue: content.controlBlockConfiguration.smpRate, + helper: + 'Whether SMV control blocks attribute smpRate is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Dyn', 'Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Select', + label: 'nofASDU', + maybeValue: content.controlBlockConfiguration.nofASDU, + helper: + 'Whether SMV control blocks attribute noASDU (number of timesstapms per packet) is configurable offline (Conf), online(Dyn) or is fixed (Fix)', + values: ['Conf', 'Fix'], + default: 'Fix', + nullable: true, + }, + { + kind: 'Checkbox', + label: 'samplesPerSec', + helper: + 'Whether SMV supported sample rate definition as SamplesPerSec or SecPerSamples', + maybeValue: + content.controlBlockConfiguration.samplesPerSec?.toString() ?? + null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'synchSrcId', + helper: 'Whether grandmaster clock ID can be included in the SMV', + maybeValue: + content.controlBlockConfiguration.synchSrcId?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'pdcTimeStamp', + helper: 'Whether the PDC timestamp can be included into SMV', + maybeValue: + content.controlBlockConfiguration.pdcTimeStamp?.toString() ?? + null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'kdaParticipant', + helper: 'Whether server supports key delivery assurance (KDA)', + maybeValue: + content.controlBlockConfiguration.kdaParticipant?.toString() ?? + null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'signature', + helper: + 'Whether calculation of a signature is supported for each GOOSE', + maybeValue: + content.controlBlockConfiguration.signature?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'encryption', + helper: 'Whether message encryption is supported for each GOOSE', + maybeValue: + content.controlBlockConfiguration.encryption?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'TextField', + label: 'SmpRate', + required: true, + helper: 'Defines the implemented SmpRate in the IED', + maybeValue: + content.controlBlockConfiguration.smpRateVal?.toString() ?? null, + nullable: true, + }, + { + kind: 'TextField', + label: 'SamplesPerSec', + required: true, + helper: 'Defines the implemented SamplesPerSec in the IED', + maybeValue: + content.controlBlockConfiguration.samplesPerSecVal?.toString() ?? + null, + nullable: true, + }, + { + kind: 'TextField', + label: 'SecPerSamples', + required: true, + helper: 'Defines the implemented SecPerSamples in the IED', + maybeValue: + content.controlBlockConfiguration.secPerSamplesVal?.toString() ?? + null, + nullable: true, + }, + ]), + createFormDivider('Publisher Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'max', + required: true, + helper: + 'The maximum number of SMV control blocks the IED can publish', + maybeValue: content.publisherCapabilities.max?.toString() ?? null, + nullable: true, + }, + { + kind: 'Select', + label: 'delivery', + maybeValue: content.publisherCapabilities.delivery, + helper: + 'Whether the IED supports publishing of muslticast, unicast or both types of SMV streams', + values: ['unicast', 'multicast', 'both'], + default: 'multicast', + nullable: true, + }, + { + kind: 'Checkbox', + label: 'deliveryConf', + helper: + 'Whether the system configurator is allowed to configure SMV control blocks', + maybeValue: + content.publisherCapabilities.deliveryConf?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'sv', + helper: 'Whether IED supports layer 2 sampled value streams', + maybeValue: content.publisherCapabilities.sv?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'Checkbox', + label: 'rSV', + helper: 'Whether the IED supports layer 3 sampled value streams', + maybeValue: content.publisherCapabilities.rSV?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Client Capabilities'), + ...createFormElementsFromInputs([ + { + kind: 'Checkbox', + label: 'sv', + helper: 'Whether the IED supports client side SMV related services', + maybeValue: content.subscriptionCapabilities.sv?.toString() ?? null, + nullable: true, + default: false, + }, + { + kind: 'TextField', + label: 'maxSMV', + required: false, + helper: + 'The maximal number of layer 2 sampled value streams the client can subscribe to', + maybeValue: + content.subscriptionCapabilities.maxSMV?.toString() ?? null, + nullable: true, + }, + { + kind: 'Checkbox', + label: 'rSV', + helper: + 'The maximal number of layer 3 sampled value streams the client can subscribe to', + maybeValue: + content.subscriptionCapabilities.rSV?.toString() ?? null, + nullable: true, + default: false, + }, + ]), + createFormDivider('Dynamic Reporting/DataSets'), + ...createFormElementsFromInputs([ + { + kind: 'TextField', + label: 'maxSv', + required: false, + helper: + 'The maximum number of SMV supervision supported by this IED (LSVS)', + maybeValue: + content.superVisionCapabilities.maxSv?.toString() ?? null, + nullable: true, + }, + ]), + ]; +} diff --git a/src/wizards/services.ts b/src/wizards/services.ts new file mode 100644 index 0000000000..b0e113d95c --- /dev/null +++ b/src/wizards/services.ts @@ -0,0 +1,103 @@ +import { html, TemplateResult } from 'lit-html'; + +import '../wizard-textfield.js'; +import '../wizard-select.js'; +import { Wizard, WizardInput } from '../foundation.js'; +import { createLogSettingsGroupServicesWizardPage } from './service-log-settingsgroup.js'; +import { createReportConfigurationsWizardPage } from './service-report-configurations.js'; +import { createGSEControlWizardPage } from './service-GSEControl.js'; +import { createNetworkingWizardPage } from './service-networking.js'; +import { createSampledValuesWizardPage } from './service-sampled-values.js'; +import { createClientServerConfigurationsWizardPage } from './service-clientServer-configurations.js'; + +export function isEmptyObject( + target: T, + dealedAsEmpty: any[] = [null, undefined, ''] +): boolean { + return ( + target === null + ? [false] + : Object.keys(target).flatMap(key => { + const value: any = (target as any)[key]; + if (typeof value === 'object') { + return isEmptyObject(value); + } else { + return [dealedAsEmpty.includes(value)]; + } + }) + ).includes(true); +} + +export function createFormElementFromInput(input: WizardInput): TemplateResult { + let templateResult: TemplateResult = html``; + switch (input.kind) { + case 'TextField': + default: + templateResult = html``; + break; + case 'Checkbox': + templateResult = html``; + break; + case 'Select': + templateResult = html` + ${input.values.map(value => { + return html` + ${value} + `; + })} + `; + break; + } + + return templateResult; +} + +export function createFormElementsFromInputs( + inputs: WizardInput[] +): TemplateResult[] { + return inputs.map(input => createFormElementFromInput(input)); +} + +export function createFormDivider(header?: string): TemplateResult { + return html``; +} + +export function editServicesWizard(services: Element): Wizard { + return [ + createLogSettingsGroupServicesWizardPage(services), + createReportConfigurationsWizardPage(services), + createGSEControlWizardPage(services), + createNetworkingWizardPage(services), + createSampledValuesWizardPage(services), + createClientServerConfigurationsWizardPage(services), + ] + .filter(page => page !== null) + .map(page => page!); +} diff --git a/test/integration/editors/IED.test.ts b/test/integration/editors/IED.test.ts index b92a906d48..c0f8d1fa4d 100644 --- a/test/integration/editors/IED.test.ts +++ b/test/integration/editors/IED.test.ts @@ -7,7 +7,7 @@ import { LitElement } from 'lit-element'; import '../../../src/editors/IED.js'; import { Editing } from '../../../src/Editing.js'; -import { Wizarding } from '../../../src/Wizarding.js'; +import { Wizarding, WizardingElement } from '../../../src/Wizarding.js'; import { initializeNsdoc, Nsdoc } from '../../../src/foundation/nsdoc.js'; import { FilterButton } from '../../../src/oscd-filter-button.js'; @@ -52,6 +52,39 @@ describe('IED Plugin', () => { }); }); + describe('Open Services Wizard', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/Services.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + nsdoc = await initializeNsdoc(); + + element = await fixture( + html`` + ); + + await element.requestUpdate(); + await element.updateComplete; + + await selectIed('WithServices'); + await new Promise(resolve => setTimeout(resolve, 100)); // await animation + }); + + it('Should open Services wizard', async () => { + element + .shadowRoot!.querySelector('ied-container')! + .shadowRoot!.querySelector( + 'mwc-icon-button[icon="settings"]' + )! + .click(); + + await element.requestUpdate(); + + expect((element as any as WizardingElement).wizardUI).to.exist; + }); + }); + describe('containing IEDs', () => { beforeEach(async () => { doc = await fetch('/test/testfiles/editors/iedEditorWithIEDs.scd') @@ -162,33 +195,6 @@ describe('IED Plugin', () => { ); } - async function selectIed(name: string): Promise { - const oscdFilterButton = element.shadowRoot!.querySelector( - 'oscd-filter-button[id="iedFilter"]' - ); - const filterButton = ( - oscdFilterButton!.shadowRoot!.querySelector('mwc-icon-button') - ); - filterButton.click(); - await element.updateComplete; - - const selectItem = ( - oscdFilterButton!.querySelector( - `mwc-radio-list-item[value="${name}"]` - ) - ); - selectItem.click(); - - const primaryButton = ( - oscdFilterButton!.shadowRoot!.querySelector( - 'mwc-button[slot="primaryAction"]' - ) - ); - primaryButton.click(); - - await element.updateComplete; - } - async function deselectLNClasses(lnClass: string): Promise { const oscdFilterButton = ( element.shadowRoot!.querySelector( @@ -217,4 +223,29 @@ describe('IED Plugin', () => { } }); }); + + async function selectIed(name: string): Promise { + const oscdFilterButton = element.shadowRoot!.querySelector( + 'oscd-filter-button[id="iedFilter"]' + ); + const filterButton = ( + oscdFilterButton!.shadowRoot!.querySelector('mwc-icon-button') + ); + filterButton.click(); + await element.updateComplete; + + const selectItem = ( + oscdFilterButton!.querySelector(`mwc-radio-list-item[value="${name}"]`) + ); + selectItem.click(); + + const primaryButton = ( + oscdFilterButton!.shadowRoot!.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + primaryButton.click(); + + await element.updateComplete; + } }); diff --git a/test/integration/wizards/__snapshots__/services-wizard.test.snap.js b/test/integration/wizards/__snapshots__/services-wizard.test.snap.js new file mode 100644 index 0000000000..0382a08410 --- /dev/null +++ b/test/integration/wizards/__snapshots__/services-wizard.test.snap.js @@ -0,0 +1,3067 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 1 looks like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + + + + + + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 1 looks like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 2 looks like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + + + + + + + + + unbuffered + + + buffered + + + both + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 2 looks like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 3 looks like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 3 looks like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 4 looks like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Conf + + + Fix + + + + + + + + + + + + + + + + + + + + + + + + + + + unicast + + + multicast + + + both + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 4 looks like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 1 should look like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + + + + + + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 1 should look like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 2 should look like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + + + + + + + + + unbuffered + + + buffered + + + both + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 2 should look like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 3 should look like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 3 should look like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 4 should look like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Conf + + + Fix + + + + + + + + + + + + + + + + + + + + + + + + + + + unicast + + + multicast + + + both + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 4 should look like snapshot */ + +snapshots["Wizards for SCL element Services AccessPoint wizards for Scl element Services should look like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+`; +/* end snapshot Wizards for SCL element Services AccessPoint wizards for Scl element Services should look like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 5 should look like snapshot"] = +` +
+ + + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Dyn + + + Conf + + + Fix + + + + + Conf + + + Fix + + + + + + + + + + + + + + + + + + + + + + + + + + + unicast + + + multicast + + + both + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 5 should look like snapshot */ + +snapshots["Wizards for SCL element Services define a Services wizards Wizard 6 should look like snapshot"] = +` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL element Services define a Services wizards Wizard 6 should look like snapshot */ + diff --git a/test/integration/wizards/services-wizard.test.ts b/test/integration/wizards/services-wizard.test.ts new file mode 100644 index 0000000000..f2ab7906e7 --- /dev/null +++ b/test/integration/wizards/services-wizard.test.ts @@ -0,0 +1,73 @@ +import { expect, fixture, html } from '@open-wc/testing'; +import { Wizard } from '../../../src/foundation.js'; +import { editServicesWizard } from '../../../src/wizards/services.js'; + +import '../../mock-wizard-editor.js'; +import { MockWizardEditor } from '../../mock-wizard-editor.js'; + +describe('Wizards for SCL element Services', () => { + let doc: XMLDocument; + let element: MockWizardEditor; + let wizard: Wizard; + + beforeEach(async () => { + element = await fixture(html``); + doc = await fetch('/test/testfiles/Services.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + wizard = editServicesWizard( + doc.querySelector('IED[name="WithServices"] Services')! + ); + + element.workflow.push(() => wizard); + await element.requestUpdate(); + }); + + describe('define a Services wizards ', () => { + it('Services wizard to have 6 pages', () => { + expect(element.wizardUI.wizard.length).to.equal(6); + }); + it('Services wizard to have 130 inputs', () => { + expect(element.wizardUI.wizard.flatMap(p => p.content).length).to.equal( + 130 + ); + }); + + [13, 22, 22, 23, 28, 22].forEach((inputs, idx) => { + it(`Services wizard ${idx + 1} to have ${inputs} inputs`, () => { + expect(element.wizardUI.wizard[idx].content!.length).to.equal(inputs); + }); + }); + + [0, 1, 2, 3, 4, 5].forEach(idx => { + it(`Wizard ${idx + 1} should look like snapshot`, () => { + expect(element.wizardUI.dialogs[idx]).to.equalSnapshot(); + }); + }); + }); + + ['AP2', 'AP3', 'AP4', 'AP5', 'AP6'].forEach(accessPointName => { + describe('AccessPoint wizards for Scl element Services', () => { + beforeEach(async () => { + element = await fixture( + html`` + ); + doc = await fetch('/test/testfiles/Services.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + wizard = editServicesWizard( + doc.querySelector(`AccessPoint[name="${accessPointName}"] Services`)! + ); + + element.workflow.push(() => wizard); + await element.requestUpdate(); + }); + + it('should look like snapshot', () => { + expect(element.wizardUI.dialog).to.equalSnapshot(); + }); + }); + }); +}); diff --git a/test/testfiles/Services.scd b/test/testfiles/Services.scd new file mode 100644 index 0000000000..825d19eb49 --- /dev/null +++ b/test/testfiles/Services.scd @@ -0,0 +1,550 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 80 + 4000 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 80 + 4000 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IEC 61850-7-4:2007B4 + + + + + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + 30000 + + + 600 + + + + + + + + + + + A + + + + + 0.01 + + + 0 + + + + + Hz + + + + + + + + + A + + + + + 0.001 + + + 0 + + + + + + + + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + unknown + forward + backward + both + + + y + z + a + f + p + n + µ + m + c + d + + da + h + k + M + G + T + P + E + Z + Y + + + + m + kg + s + A + K + mol + cd + deg + rad + sr + Gy + Bq + °C + Sv + F + C + S + H + V + ohm + J + N + Hz + lx + Lm + Wb + T + W + Pa + + + m/s + m/s² + m³/s + m/m³ + M + kg/m³ + m²/s + W/m K + J/K + ppm + 1/s + rad/s + W/m² + J/m² + S/m + K/s + Pa/s + J/kg K + VA + Watts + VAr + phi + cos(phi) + Vs + + As + + A²t + VAh + Wh + VArh + V/Hz + Hz/s + char + char/s + kgm² + dB + J/Wh + W/s + l/s + dBm + h + min + Ohm/m + percent/s + + + Ok + Warning + Alarm + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + on + blocked + test + test/blocked + off + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + + \ No newline at end of file diff --git a/test/unit/editors/ied/__snapshots__/access-point-container.test.snap.js b/test/unit/editors/ied/__snapshots__/access-point-container.test.snap.js index d2a0c043c6..5e54f5ccb8 100644 --- a/test/unit/editors/ied/__snapshots__/access-point-container.test.snap.js +++ b/test/unit/editors/ied/__snapshots__/access-point-container.test.snap.js @@ -1,9 +1,8 @@ /* @web/test-runner snapshot v1 */ export const snapshots = {}; -snapshots[ - 'access-point-container with Server Elements looks like the latest snapshot' -] = ` +snapshots["access-point-container with Server Elements looks like the latest snapshot"] = +` @@ -12,6 +11,8 @@ snapshots[ `; +/* end snapshot access-point-container with Server Elements looks like the latest snapshot */ + snapshots["access-point-container with LN Elements and all LN Classes displayed looks like the latest snapshot"] = ` diff --git a/test/unit/editors/ied/__snapshots__/ied-container.test.snap.js b/test/unit/editors/ied/__snapshots__/ied-container.test.snap.js index f8b2ceb345..062917203c 100644 --- a/test/unit/editors/ied/__snapshots__/ied-container.test.snap.js +++ b/test/unit/editors/ied/__snapshots__/ied-container.test.snap.js @@ -20,6 +20,13 @@ snapshots["ied-container looks like the latest snapshot"] = + + + + diff --git a/test/unit/editors/ied/ied-container.test.ts b/test/unit/editors/ied/ied-container.test.ts index 4b893ddcc3..4f62d77149 100644 --- a/test/unit/editors/ied/ied-container.test.ts +++ b/test/unit/editors/ied/ied-container.test.ts @@ -20,4 +20,18 @@ describe('ied-container', () => { it('looks like the latest snapshot', async () => { await expect(element).shadowDom.to.equalSnapshot(); }); + + it('Should show services icon when Services are available', async () => { + validSCL = await fetch('/test/testfiles/Services.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element = await fixture(html``); + + expect( + element.shadowRoot!.querySelector("mwc-icon-button[icon='settings']") + ).to.exist; + }); }); diff --git a/test/unit/wizards/services.test.ts b/test/unit/wizards/services.test.ts new file mode 100644 index 0000000000..c7f0b8c8e0 --- /dev/null +++ b/test/unit/wizards/services.test.ts @@ -0,0 +1,35 @@ +import { expect } from '@open-wc/testing'; + +import { isEmptyObject } from '../../../src/wizards/services.js'; + +describe('Wizards for SCL element Services', () => { + it('Simple empty input object is empty', () => { + const sut = { + foo: '', + }; + + expect(isEmptyObject(sut)).to.be.true; + }); + it('Complex empty input object is empty', () => { + const sut = { + foo: { + bar: '', + }, + }; + expect(isEmptyObject(sut)).to.be.true; + }); + it('Simple filled input object is not empty', () => { + const sut = { + foo: 'bar', + }; + expect(isEmptyObject(sut)).to.be.false; + }); + it('Compled filled input object is not empty', () => { + const sut = { + foo: { + bar: 'qux', + }, + }; + expect(isEmptyObject(sut)).to.be.false; + }); +}); From 479c49991a2f4e3e0b70ddd39e90deda3ec935ec Mon Sep 17 00:00:00 2001 From: Steffen van den Driest <35229971+Stef3st@users.noreply.github.com> Date: Mon, 27 Feb 2023 09:11:29 +0100 Subject: [PATCH 05/27] fix: added translation key for phase (#1186) --- src/translations/de.ts | 1 + src/translations/en.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/translations/de.ts b/src/translations/de.ts index 8c9f0d0d7a..0aae589f2d 100644 --- a/src/translations/de.ts +++ b/src/translations/de.ts @@ -66,6 +66,7 @@ export const de: Translations = { prefix: 'Präfix des logischen Knotens', lnInst: 'Instanz des logischen Knotens', virtual: 'Virtuell', + phase: 'Phase', }, settings: { title: 'Einstellungen', diff --git a/src/translations/en.ts b/src/translations/en.ts index e103af6d0f..e5e64fb97f 100644 --- a/src/translations/en.ts +++ b/src/translations/en.ts @@ -64,6 +64,7 @@ export const en = { prefix: 'Prefix of the Logical Node', lnInst: 'Instance of the Logical Node', virtual: 'Virtual', + phase: 'Phase', }, settings: { title: 'Settings', From 7118454ebcc21fae82d2184d00c465c78edc62f7 Mon Sep 17 00:00:00 2001 From: danyill Date: Mon, 27 Feb 2023 22:14:54 +1300 Subject: [PATCH 06/27] fix(foundation) improve ExtRef identity function (#1182) Improve ExtRef identity function, closes #1179 --- src/foundation.ts | 2 +- .../GooseSubscriberLaterBinding.test.ts | 2 +- .../editors/SMVSubscriberLaterBinding.test.ts | 2 +- .../GooseSubscriberLaterBinding.test.snap.js | 6 +-- .../SMVSubscriberLaterBinding.test.snap.js | 6 +-- .../ext-ref-later-binding-list.test.snap.js | 40 +++++++++---------- test/unit/foundation.test.ts | 3 +- .../UpdateDescritionABB.test.snap.js | 8 ++-- 8 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/foundation.ts b/src/foundation.ts index 807abf9b4a..80bee523ec 100644 --- a/src/foundation.ts +++ b/src/foundation.ts @@ -833,7 +833,7 @@ function extRefIdentity(e: Element): string | number { const intAddrIndex = Array.from( e.parentElement.querySelectorAll(`ExtRef[intAddr="${intAddr}"]`) ).indexOf(e); - if (!iedName) return `${parentIdentity}>${intAddr}[${intAddrIndex}]`; + if (intAddr) return `${parentIdentity}>${intAddr}[${intAddrIndex}]`; const [ ldInst, prefix, diff --git a/test/integration/editors/GooseSubscriberLaterBinding.test.ts b/test/integration/editors/GooseSubscriberLaterBinding.test.ts index 463acd9176..b5f1aac867 100644 --- a/test/integration/editors/GooseSubscriberLaterBinding.test.ts +++ b/test/integration/editors/GooseSubscriberLaterBinding.test.ts @@ -117,7 +117,7 @@ describe('GOOSE Subscribe Later Binding Plugin', () => { (( extRefListElement.shadowRoot!.querySelector( - 'mwc-list-item[value="GOOSE_Subscriber>>Earth_Switch> CSWI 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos q@Pos;CSWI1/Pos/q"]' + 'mwc-list-item[value="GOOSE_Subscriber>>Earth_Switch> CSWI 1>Pos;CSWI1/Pos/q[0]"]' ) )).click(); await element.requestUpdate(); diff --git a/test/integration/editors/SMVSubscriberLaterBinding.test.ts b/test/integration/editors/SMVSubscriberLaterBinding.test.ts index c00626dc7c..71963d3bac 100644 --- a/test/integration/editors/SMVSubscriberLaterBinding.test.ts +++ b/test/integration/editors/SMVSubscriberLaterBinding.test.ts @@ -118,7 +118,7 @@ describe('SMV Subscribe Later Binding plugin', () => { (( extRefListElement.shadowRoot!.querySelector( - 'mwc-list-item[value="SMV_Subscriber>>Overvoltage> PTRC 1>SMV:currentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv q@AmpSv;TCTR1/AmpSv/q"]' + 'mwc-list-item[value="SMV_Subscriber>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/q[0]"]' ) )).click(); await element.requestUpdate(); diff --git a/test/integration/editors/__snapshots__/GooseSubscriberLaterBinding.test.snap.js b/test/integration/editors/__snapshots__/GooseSubscriberLaterBinding.test.snap.js index 4fd8a84158..e9da94df03 100644 --- a/test/integration/editors/__snapshots__/GooseSubscriberLaterBinding.test.snap.js +++ b/test/integration/editors/__snapshots__/GooseSubscriberLaterBinding.test.snap.js @@ -167,7 +167,7 @@ snapshots["GOOSE Subscribe Later Binding Plugin when selecting an FCDA element w aria-disabled="false" noninteractive="" tabindex="-1" - value="Interlocking.Input2 GOOSE_Subscriber>>Earth_Switch> CSWI 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos stVal@Pos;CSWI1/Pos/stVal Interlocking.Input2 GOOSE_Subscriber1>>Earth_Switch> CSWI 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos stVal@Pos;CSWI1/Pos/stVal" + value="Interlocking.Input2 GOOSE_Subscriber>>Earth_Switch> CSWI 1>Pos;CSWI1/Pos/stVal[0] Interlocking.Input2 GOOSE_Subscriber1>>Earth_Switch> CSWI 1>Pos;CSWI1/Pos/stVal[0]" > [subscription.subscriber.subscribed] @@ -184,7 +184,7 @@ snapshots["GOOSE Subscribe Later Binding Plugin when selecting an FCDA element w mwc-list-item="" tabindex="0" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CSWI 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos stVal@Pos;CSWI1/Pos/stVal" + value="GOOSE_Subscriber>>Earth_Switch> CSWI 1>Pos;CSWI1/Pos/stVal[0]" > Pos;CSWI1/Pos/stVal @@ -204,7 +204,7 @@ snapshots["GOOSE Subscribe Later Binding Plugin when selecting an FCDA element w mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber1>>Earth_Switch> CSWI 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos stVal@Pos;CSWI1/Pos/stVal" + value="GOOSE_Subscriber1>>Earth_Switch> CSWI 1>Pos;CSWI1/Pos/stVal[0]" > Pos;CSWI1/Pos/stVal diff --git a/test/integration/editors/__snapshots__/SMVSubscriberLaterBinding.test.snap.js b/test/integration/editors/__snapshots__/SMVSubscriberLaterBinding.test.snap.js index 62cb12d57f..755295aae8 100644 --- a/test/integration/editors/__snapshots__/SMVSubscriberLaterBinding.test.snap.js +++ b/test/integration/editors/__snapshots__/SMVSubscriberLaterBinding.test.snap.js @@ -1054,7 +1054,7 @@ snapshots["SMV Subscribe Later Binding plugin when selecting an FCDA element wit aria-disabled="false" noninteractive="" tabindex="-1" - value="MeasPoint.CT1 SMV_Subscriber2>>Overvoltage> PTRC 1>SMV:currrentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv instMag.i@AmpSv;TCTR1/AmpSv/instMag.i MeasPoint.CT1 SMV_Subscriber4>>Overvoltage> PTRC 1>SMV:currrentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv instMag.i@AmpSv;TCTR1/AmpSv/instMag.i" + value="MeasPoint.CT1 SMV_Subscriber2>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0] MeasPoint.CT1 SMV_Subscriber4>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0]" > [subscription.subscriber.subscribed] @@ -1072,7 +1072,7 @@ snapshots["SMV Subscribe Later Binding plugin when selecting an FCDA element wit mwc-list-item="" tabindex="0" twoline="" - value="SMV_Subscriber2>>Overvoltage> PTRC 1>SMV:currrentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv instMag.i@AmpSv;TCTR1/AmpSv/instMag.i" + value="SMV_Subscriber2>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0]" > AmpSv;TCTR1/AmpSv/instMag.i @@ -1098,7 +1098,7 @@ snapshots["SMV Subscribe Later Binding plugin when selecting an FCDA element wit mwc-list-item="" tabindex="-1" twoline="" - value="SMV_Subscriber4>>Overvoltage> PTRC 1>SMV:currrentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv instMag.i@AmpSv;TCTR1/AmpSv/instMag.i" + value="SMV_Subscriber4>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0]" > AmpSv;TCTR1/AmpSv/instMag.i diff --git a/test/unit/editors/subscription/later-binding/__snapshots__/ext-ref-later-binding-list.test.snap.js b/test/unit/editors/subscription/later-binding/__snapshots__/ext-ref-later-binding-list.test.snap.js index 0e19bf24c1..cc691597ec 100644 --- a/test/unit/editors/subscription/later-binding/__snapshots__/ext-ref-later-binding-list.test.snap.js +++ b/test/unit/editors/subscription/later-binding/__snapshots__/ext-ref-later-binding-list.test.snap.js @@ -250,7 +250,7 @@ snapshots["extref-later-binding-list for Sampled Value Control when SVC has a si aria-disabled="false" noninteractive="" tabindex="-1" - value="MeasPoint.CT1 SMV_Subscriber>>Overvoltage> PTRC 1>SMV:currentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv instMag.i@AmpSv;TCTR1/AmpSv/instMag.i" + value="MeasPoint.CT1 SMV_Subscriber>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0]" > [subscription.subscriber.subscribed] @@ -267,7 +267,7 @@ snapshots["extref-later-binding-list for Sampled Value Control when SVC has a si mwc-list-item="" tabindex="-1" twoline="" - value="SMV_Subscriber>>Overvoltage> PTRC 1>SMV:currentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv instMag.i@AmpSv;TCTR1/AmpSv/instMag.i" + value="SMV_Subscriber>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0]" > AmpSv;TCTR1/AmpSv/instMag.i @@ -482,7 +482,7 @@ snapshots["extref-later-binding-list when SVC has a multiple subscriptions looks aria-disabled="false" noninteractive="" tabindex="-1" - value="MeasPoint.CT1 SMV_Subscriber>>Overvoltage> PTRC 1>SMV:currentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv q@AmpSv;TCTR1/AmpSv/q MeasPoint.CT1 SMV_Subscriber>>Overvoltage> PTRC 1>SMV:currentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv q@AmpSv;TCTR2/AmpSv/q MeasPoint.CT1 SMV_Subscriber>>Overvoltage> PTRC 1>SMV:currentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv q@AmpSv;TCTR3/AmpSv/q" + value="MeasPoint.CT1 SMV_Subscriber>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/q[0] MeasPoint.CT1 SMV_Subscriber>>Overvoltage> PTRC 1>AmpSv;TCTR2/AmpSv/q[0] MeasPoint.CT1 SMV_Subscriber>>Overvoltage> PTRC 1>AmpSv;TCTR3/AmpSv/q[0]" > [subscription.subscriber.subscribed] @@ -499,7 +499,7 @@ snapshots["extref-later-binding-list when SVC has a multiple subscriptions looks mwc-list-item="" tabindex="-1" twoline="" - value="SMV_Subscriber>>Overvoltage> PTRC 1>SMV:currentOnly CurrentTransformer/ LLN0 SMV_Publisher CurrentTransformer/L1 TCTR 1 AmpSv q@AmpSv;TCTR1/AmpSv/q" + value="SMV_Subscriber>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/q[0]" > AmpSv;TCTR1/AmpSv/q @@ -515,7 +515,7 @@ snapshots["extref-later-binding-list when SVC has a multiple subscriptions looks AmpSv;TCTR2/AmpSv/q @@ -531,7 +531,7 @@ snapshots["extref-later-binding-list when SVC has a multiple subscriptions looks AmpSv;TCTR3/AmpSv/q @@ -778,7 +778,7 @@ snapshots["extref-later-binding-list for GOOSE Control when GSEControl has no su aria-disabled="false" noninteractive="" tabindex="-1" - value="Interlocking.Input GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/stVal[0] Interlocking.Input3 GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB1_Disconnector/ CILO 1 EnaCls stVal@Pos;CILO/EnaCls/stVal Interlocking.Input4 GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CILO 1 EnaOpn stVal@Pos;CILO/EnaOpn2/stVal Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[1]" + value="Interlocking.Input GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/stVal[0] Interlocking.Input3 GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaCls/stVal[0] Interlocking.Input4 GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaOpn2/stVal[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[1]" > [subscription.subscriber.availableToSubscribe] @@ -814,7 +814,7 @@ snapshots["extref-later-binding-list for GOOSE Control when GSEControl has no su mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB1_Disconnector/ CILO 1 EnaCls stVal@Pos;CILO/EnaCls/stVal" + value="GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaCls/stVal[0]" > Pos;CILO/EnaCls/stVal @@ -833,7 +833,7 @@ snapshots["extref-later-binding-list for GOOSE Control when GSEControl has no su mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CILO 1 EnaOpn stVal@Pos;CILO/EnaOpn2/stVal" + value="GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaOpn2/stVal[0]" > Pos;CILO/EnaOpn2/stVal @@ -900,7 +900,7 @@ snapshots["extref-later-binding-list for GOOSE Control when GSEControl has a sin aria-disabled="false" noninteractive="" tabindex="-1" - value="Interlocking.Input2 GOOSE_Subscriber>>Earth_Switch> CSWI 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos stVal@Pos;CSWI1/Pos/stVal" + value="Interlocking.Input2 GOOSE_Subscriber>>Earth_Switch> CSWI 1>Pos;CSWI1/Pos/stVal[0]" > [subscription.subscriber.subscribed] @@ -917,7 +917,7 @@ snapshots["extref-later-binding-list for GOOSE Control when GSEControl has a sin mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CSWI 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos stVal@Pos;CSWI1/Pos/stVal" + value="GOOSE_Subscriber>>Earth_Switch> CSWI 1>Pos;CSWI1/Pos/stVal[0]" > Pos;CSWI1/Pos/stVal @@ -934,7 +934,7 @@ snapshots["extref-later-binding-list for GOOSE Control when GSEControl has a sin aria-disabled="false" noninteractive="" tabindex="-1" - value="Interlocking.Input GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/stVal[0] Interlocking.Input3 GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB1_Disconnector/ CILO 1 EnaCls stVal@Pos;CILO/EnaCls/stVal Interlocking.Input4 GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CILO 1 EnaOpn stVal@Pos;CILO/EnaOpn2/stVal Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[1]" + value="Interlocking.Input GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/stVal[0] Interlocking.Input3 GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaCls/stVal[0] Interlocking.Input4 GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaOpn2/stVal[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[1]" > [subscription.subscriber.availableToSubscribe] @@ -970,7 +970,7 @@ snapshots["extref-later-binding-list for GOOSE Control when GSEControl has a sin mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB1_Disconnector/ CILO 1 EnaCls stVal@Pos;CILO/EnaCls/stVal" + value="GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaCls/stVal[0]" > Pos;CILO/EnaCls/stVal @@ -989,7 +989,7 @@ snapshots["extref-later-binding-list for GOOSE Control when GSEControl has a sin mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CILO 1 EnaOpn stVal@Pos;CILO/EnaOpn2/stVal" + value="GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaOpn2/stVal[0]" > Pos;CILO/EnaOpn2/stVal @@ -1056,7 +1056,7 @@ snapshots["extref-later-binding-list when GSEControl has a multiple subscription aria-disabled="false" noninteractive="" tabindex="-1" - value="Interlocking.Input GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos q@Pos;CSWI1/Pos/q Interlocking.Input2 GOOSE_Subscriber>>Earth_Switch> CSWI 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos q@Pos;CSWI1/Pos/q" + value="Interlocking.Input GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/q[0] Interlocking.Input2 GOOSE_Subscriber>>Earth_Switch> CSWI 1>Pos;CSWI1/Pos/q[0]" > [subscription.subscriber.subscribed] @@ -1073,7 +1073,7 @@ snapshots["extref-later-binding-list when GSEControl has a multiple subscription mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CSWI 1 Pos q@Pos;CSWI1/Pos/q" + value="GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/q[0]" > Pos;CSWI1/Pos/q @@ -1089,7 +1089,7 @@ snapshots["extref-later-binding-list when GSEControl has a multiple subscription Pos;CSWI1/Pos/q @@ -1106,7 +1106,7 @@ snapshots["extref-later-binding-list when GSEControl has a multiple subscription aria-disabled="false" noninteractive="" tabindex="-1" - value="Interlocking.Input GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/stVal[0] Interlocking.Input3 GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB1_Disconnector/ CILO 1 EnaCls stVal@Pos;CILO/EnaCls/stVal Interlocking.Input4 GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CILO 1 EnaOpn stVal@Pos;CILO/EnaOpn2/stVal Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[1]" + value="Interlocking.Input GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/stVal[0] Interlocking.Input3 GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaCls/stVal[0] Interlocking.Input4 GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaOpn2/stVal[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[0] Restricted To Pos GOOSE_Subscriber>>Earth_Switch> CSWI 1>someRestrictedExtRef[1]" > [subscription.subscriber.availableToSubscribe] @@ -1142,7 +1142,7 @@ snapshots["extref-later-binding-list when GSEControl has a multiple subscription mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB1_Disconnector/ CILO 1 EnaCls stVal@Pos;CILO/EnaCls/stVal" + value="GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaCls/stVal[0]" > Pos;CILO/EnaCls/stVal @@ -1161,7 +1161,7 @@ snapshots["extref-later-binding-list when GSEControl has a multiple subscription mwc-list-item="" tabindex="-1" twoline="" - value="GOOSE_Subscriber>>Earth_Switch> CILO 1>GOOSE:GOOSE2 QB2_Disconnector/ LLN0 GOOSE_Publisher QB2_Disconnector/ CILO 1 EnaOpn stVal@Pos;CILO/EnaOpn2/stVal" + value="GOOSE_Subscriber>>Earth_Switch> CILO 1>Pos;CILO/EnaOpn2/stVal[0]" > Pos;CILO/EnaOpn2/stVal diff --git a/test/unit/foundation.test.ts b/test/unit/foundation.test.ts index 7f8212db16..60b8b5da6f 100644 --- a/test/unit/foundation.test.ts +++ b/test/unit/foundation.test.ts @@ -282,8 +282,7 @@ describe('foundation', () => { IEDName: 'IED1>>CircuitBreaker_CB1>GCB>IED2 P1 CircuitBreaker_CB1/ CSWI 1', FCDA: 'IED1>>CircuitBreaker_CB1>GooseDataSet1>CircuitBreaker_CB1/ XCBR 1.Pos stVal (ST)', - ExtRef: - 'IED1>>Disconnectors>DC CSWI 1>GOOSE:GCB CBSW/ LLN0 IED2 CBSW/ XSWI 2 Pos stVal@intAddr', + ExtRef: 'IED1>>Disconnectors>DC CSWI 1>intAddr[0]', 'ExtRef:not([iedName])': 'IED1>>Disconnectors>DC CSWI 1>stVal-t[0]', LN: 'IED1>>CircuitBreaker_CB1> XCBR 1', ClientLN: diff --git a/test/unit/menu/__snapshots__/UpdateDescritionABB.test.snap.js b/test/unit/menu/__snapshots__/UpdateDescritionABB.test.snap.js index 7a3bff3bc4..7bff6c2dd2 100644 --- a/test/unit/menu/__snapshots__/UpdateDescritionABB.test.snap.js +++ b/test/unit/menu/__snapshots__/UpdateDescritionABB.test.snap.js @@ -46,13 +46,13 @@ snapshots["Update method for desc attributes in ABB IEDs working on SCL files co selected="" tabindex="0" twoline="" - value="ExtRef | IED1>>Disconnectors>DC CSWI 1>IED2 CBSW/ XSWI 2 Pos stVal@01-0C-CD-01-00-01,0001,5,GOOSERCV_BIN.3.I1,400,0,GOOSERCV_BIN,Dynamic" + value="ExtRef | IED1>>Disconnectors>DC CSWI 1>01-0C-CD-01-00-01,0001,5,GOOSERCV_BIN.3.I1,400,0,GOOSERCV_BIN,Dynamic[0]" > GOOSERCV_BIN.3.I1 - ExtRef | IED1>>Disconnectors>DC CSWI 1>IED2 CBSW/ XSWI 2 Pos stVal@01-0C-CD-01-00-01,0001,5,GOOSERCV_BIN.3.I1,400,0,GOOSERCV_BIN,Dynamic + ExtRef | IED1>>Disconnectors>DC CSWI 1>01-0C-CD-01-00-01,0001,5,GOOSERCV_BIN.3.I1,400,0,GOOSERCV_BIN,Dynamic[0] some desc-GOOSERCV_BIN.3.I2 - ExtRef | IED1>>Disconnectors>DC CSWI 1>IED2 CBSW/ XSWI 2 Pos q@01-0C-CD-01-00-01,0001,5,GOOSERCV_BIN.3.I2,400,0,GOOSERCV_BIN,Dynamic + ExtRef | IED1>>Disconnectors>DC CSWI 1>01-0C-CD-01-00-01,0001,5,GOOSERCV_BIN.3.I2,400,0,GOOSERCV_BIN,Dynamic[0] From b71c3f3989c728e4ab50da340e2e58152a2d17bd Mon Sep 17 00:00:00 2001 From: marcvanraalte <86408026+marcvanraalte@users.noreply.github.com> Date: Mon, 27 Feb 2023 15:44:09 +0100 Subject: [PATCH 07/27] 971 edit wizard tapchanger (#1170) * feat(tapchanger-editor.ts):editor_added * feat(tapchanger-editor.test.ts):setup * feat(TapChanger-editor):editor_and_unit_test * feat(tapchanger-editor):start_edit_button * feat(tapchanger-editor):add_edit_wizard --------- Co-authored-by: Juan Munoz --- src/editors/substation/tapchanger-editor.ts | 22 +++- src/wizards/tapChanger.ts | 112 ++++++++++++++++ src/wizards/wizard-library.ts | 3 +- .../tapchanger-editor-wizard-editing.test.ts | 112 ++++++++++++++++ ...rmer-winding-editor-wizard-editing.test.ts | 27 ---- .../editors/substation/TapChanger.scd | 3 +- .../tapchanger-editor.test.snap.js | 14 ++ .../__snapshots__/tapchanger.test.snap.js | 56 ++++++++ test/unit/wizards/tapchanger.test.ts | 120 ++++++++++++++++++ 9 files changed, 438 insertions(+), 31 deletions(-) create mode 100644 src/wizards/tapChanger.ts create mode 100644 test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts create mode 100644 test/unit/wizards/__snapshots__/tapchanger.test.snap.js create mode 100644 test/unit/wizards/tapchanger.test.ts diff --git a/src/editors/substation/tapchanger-editor.ts b/src/editors/substation/tapchanger-editor.ts index 62e2f80b7d..025e40178a 100644 --- a/src/editors/substation/tapchanger-editor.ts +++ b/src/editors/substation/tapchanger-editor.ts @@ -7,11 +7,17 @@ import { state, } from 'lit-element'; +import { translate } from 'lit-translate'; + +import '@material/mwc-icon'; +import '@material/mwc-icon-button'; + import '../../action-pane.js'; import './eq-function-editor.js'; import './l-node-editor.js'; import './sub-equipment-editor.js'; -import { getChildElementsByTagName } from '../../foundation.js'; +import { getChildElementsByTagName, newWizardEvent } from '../../foundation.js'; +import { wizards } from '../../wizards/wizard-library.js'; @customElement('tapchanger-editor') export class TapChangerEditor extends LitElement { @@ -49,6 +55,11 @@ export class TapChangerEditor extends LitElement { : html``; } + openEditWizard(): void { + const wizard = wizards['TapChanger'].edit(this.element); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + renderEqFunctions(): TemplateResult { if (!this.showfunctions) return html``; @@ -80,7 +91,14 @@ export class TapChangerEditor extends LitElement { } render(): TemplateResult { - return html` ${this.renderLNodes()} + return html` + + this.openEditWizard()} + > + + ${this.renderLNodes()} ${this.renderEqFunctions()} ${this.renderSubEquipments()}`; } } diff --git a/src/wizards/tapChanger.ts b/src/wizards/tapChanger.ts new file mode 100644 index 0000000000..b7fcde3e48 --- /dev/null +++ b/src/wizards/tapChanger.ts @@ -0,0 +1,112 @@ +import { html, TemplateResult } from 'lit-element'; +import { get, translate } from 'lit-translate'; + +import { + cloneElement, + createElement, + getChildElementsByTagName, + getValue, + SimpleAction, + Wizard, + WizardActor, + WizardInputElement, +} from '../foundation.js'; + +function updateTapChangerAction(element: Element): WizardActor { + return (inputs: WizardInputElement[]): SimpleAction[] => { + const tapChangerAttrs: Record = {}; + const tapChangerKeys = ['name', 'desc', 'type', 'virtual']; + tapChangerKeys.forEach(key => { + tapChangerAttrs[key] = getValue(inputs.find(i => i.label === key)!); + }); + + if ( + tapChangerKeys.some( + key => tapChangerAttrs[key] !== element.getAttribute(key) + ) + ) { + const newElement = cloneElement(element, tapChangerAttrs); + return [ + { + old: { element }, + new: { element: newElement }, + }, + ]; + } + return []; + }; +} + +interface ContentOptions { + name: string | null; + desc: string | null; + type: string | null; + virtual: string | null; + reservedNames: string[]; +} + +export function contentTapChangerWizard( + content: ContentOptions +): TemplateResult[] { + return [ + html``, + html``, + html``, + html``, + ]; +} + +export function editTapChangerWizard(element: Element): Wizard { + const name = element.getAttribute('name'); + const desc = element.getAttribute('desc'); + const type = element.getAttribute('type'); + const virtual = element.getAttribute('virtual'); + const reservedNames: string[] = getChildElementsByTagName( + element.parentElement!, + 'TapChanger' + ) + .filter(sibling => sibling !== element) + .map(sibling => sibling.getAttribute('name')!); + return [ + { + title: get('wizard.title.edit', { tagName: 'TapChanger' }), + primary: { + icon: 'save', + label: get('save'), + action: updateTapChangerAction(element), + }, + content: [ + ...contentTapChangerWizard({ + name, + desc, + type, + virtual, + reservedNames, + }), + ], + }, + ]; +} diff --git a/src/wizards/wizard-library.ts b/src/wizards/wizard-library.ts index 43f5cf880f..9cebe30d35 100644 --- a/src/wizards/wizard-library.ts +++ b/src/wizards/wizard-library.ts @@ -48,6 +48,7 @@ import { createTransformerWindingWizard, editTransformerWindingWizard, } from './transformerWinding.js'; +import { editTapChangerWizard } from './tapChanger.js'; type SclElementWizard = ( element: Element, @@ -518,7 +519,7 @@ export const wizards: Record< create: emptyWizard, }, TapChanger: { - edit: emptyWizard, + edit: editTapChangerWizard, create: emptyWizard, }, Terminal: { diff --git a/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts b/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts new file mode 100644 index 0000000000..550a4df73a --- /dev/null +++ b/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts @@ -0,0 +1,112 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import '../../../mock-wizard-editor.js'; +import { MockWizardEditor } from '../../../mock-wizard-editor.js'; +import { ListItemBase } from '@material/mwc-list/mwc-list-item-base'; + +import '../../../../src/editors/substation/tapchanger-editor'; +import { TapChangerEditor } from '../../../../src/editors/substation/tapchanger-editor.js'; +import { WizardTextField } from '../../../../src/wizard-textfield.js'; +import { WizardCheckbox } from '../../../../src/wizard-checkbox.js'; +import { MenuBase } from '@material/mwc-menu/mwc-menu-base.js'; + +describe('tapchanger-editor wizarding editing integration', () => { + let doc: XMLDocument; + let parent: MockWizardEditor; + let element: TapChangerEditor | null; + + describe('edit wizard', () => { + let nameField: WizardTextField; + let descField: WizardTextField; + let primaryAction: HTMLElement; + let secondaryAction: HTMLElement; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/TapChanger.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + parent = ( + await fixture( + html` TapChanger[name="tapChComplet"]' + )} + >` + ) + ); + element = parent.querySelector('tapchanger-editor'); + await (( + element?.shadowRoot?.querySelector('mwc-icon-button[icon="edit"]') + )).click(); + await parent.updateComplete; + + nameField = ( + parent.wizardUI.dialog?.querySelector('wizard-textfield[label="name"]') + ); + descField = ( + parent.wizardUI.dialog?.querySelector('wizard-textfield[label="desc"]') + ); + + secondaryAction = ( + parent.wizardUI.dialog?.querySelector( + 'mwc-button[slot="secondaryAction"]' + ) + ); + primaryAction = ( + parent.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + }); + it('closes on secondary action', async () => { + secondaryAction.click(); + await new Promise(resolve => setTimeout(resolve, 100)); // await animation + expect(parent.wizardUI.dialog).to.not.exist; + }); + it('does not change name attribute if not unique within parent element', async () => { + const oldName = nameField.value; + nameField.value = 'empty'; + primaryAction.click(); + await parent.updateComplete; + expect( + doc + .querySelector( + 'TransformerWinding[name="withTapChanger1"] > TapChanger[name="tapChComplet"]' + ) + ?.getAttribute('name') + ).to.equal(oldName); + }); + it('changes desc attribute on primary action', async () => { + descField.value = 'newDesc'; + primaryAction.click(); + await parent.updateComplete; + expect( + doc + .querySelector( + 'TransformerWinding[name="withTapChanger1"] > TapChanger[name="tapChComplet"]' + ) + ?.getAttribute('desc') + ).to.equal('newDesc'); + }); + it('changes virtual attribute on primary action', async () => { + const virtualCheckbox = ( + parent.wizardUI.dialog?.querySelector( + 'wizard-checkbox[label="virtual"]' + ) + ); + virtualCheckbox.nullSwitch!.click(); + virtualCheckbox.maybeValue = 'false'; + primaryAction.click(); + await parent.updateComplete; + expect( + doc + .querySelector( + 'TransformerWinding[name="withTapChanger1"] > TapChanger[name="tapChComplet"]' + ) + ?.getAttribute('virtual') + ).to.equal('false'); + }); + }); +}); diff --git a/test/integration/editors/substation/transformer-winding-editor-wizard-editing.test.ts b/test/integration/editors/substation/transformer-winding-editor-wizard-editing.test.ts index da85b15256..58852ec24c 100644 --- a/test/integration/editors/substation/transformer-winding-editor-wizard-editing.test.ts +++ b/test/integration/editors/substation/transformer-winding-editor-wizard-editing.test.ts @@ -176,33 +176,6 @@ describe('transformer-winding-editor wizarding editing integration', () => { ).to.not.exist; }); }); - - describe('has a delete icon button that', () => { - let deleteButton: HTMLElement; - - beforeEach(async () => { - deleteButton = ( - element?.shadowRoot?.querySelector('mwc-icon-button[icon="delete"]') - ); - await parent.updateComplete; - }); - - it('removes the attached TransformerWinding element from the document', async () => { - expect( - doc.querySelector( - 'PowerTransformer[name="pTransVolt"] > TransformerWinding[name="some"]' - ) - ).to.exist; - - await deleteButton.click(); - - expect( - doc.querySelector( - 'PowerTransformer[name="pTransVolt"] > TransformerWinding[name="some"]' - ) - ).to.not.exist; - }); - }); }); describe('Open add wizard', () => { diff --git a/test/testfiles/editors/substation/TapChanger.scd b/test/testfiles/editors/substation/TapChanger.scd index 0c519ee311..18717c4a8f 100644 --- a/test/testfiles/editors/substation/TapChanger.scd +++ b/test/testfiles/editors/substation/TapChanger.scd @@ -20,7 +20,8 @@ - + + diff --git a/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js index 7050dfa649..ac3e201397 100644 --- a/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js @@ -6,6 +6,13 @@ snapshots["web component rendering TapChanger element rendering LNode and EqFunc label="TapChanger.tapChComplet —TapChanger.My TapChanger" tabindex="0" > + + + +
@@ -21,6 +28,13 @@ snapshots["web component rendering TapChanger element rendering SubEquipment loo label="TapChanger.tapChangerHalf " tabindex="0" > + + + + diff --git a/test/unit/wizards/__snapshots__/tapchanger.test.snap.js b/test/unit/wizards/__snapshots__/tapchanger.test.snap.js new file mode 100644 index 0000000000..888b7dee93 --- /dev/null +++ b/test/unit/wizards/__snapshots__/tapchanger.test.snap.js @@ -0,0 +1,56 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Wizards for SCL TapChanger element define an edit wizard that looks like the the latest snapshot"] = +` +
+ + + + + + + + +
+ + + + +
+`; +/* end snapshot Wizards for SCL TapChanger element define an edit wizard that looks like the the latest snapshot */ + diff --git a/test/unit/wizards/tapchanger.test.ts b/test/unit/wizards/tapchanger.test.ts new file mode 100644 index 0000000000..35658b1ec6 --- /dev/null +++ b/test/unit/wizards/tapchanger.test.ts @@ -0,0 +1,120 @@ +import { expect, fixture, html } from '@open-wc/testing'; +import { SinonSpy, spy } from 'sinon'; + +import '../../mock-wizard.js'; +import { MockWizard } from '../../mock-wizard.js'; + +import { WizardTextField } from '../../../src/wizard-textfield.js'; +import { + Create, + isCreate, + isReplace, + Replace, + WizardInputElement, +} from '../../../src/foundation.js'; +import { editTapChangerWizard } from '../../../src/wizards/tapChanger.js'; +import { WizardCheckbox } from '../../../src/wizard-checkbox.js'; + +describe('Wizards for SCL TapChanger element', () => { + let doc: XMLDocument; + let element: MockWizard; + let inputs: WizardInputElement[]; + + let primaryAction: HTMLElement; + + let actionEvent: SinonSpy; + + beforeEach(async () => { + element = await fixture(html``); + doc = await fetch('/test/testfiles/editors/substation/TapChanger.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + actionEvent = spy(); + window.addEventListener('editor-action', actionEvent); + }); + + describe('define an edit wizard that', () => { + beforeEach(async () => { + const wizard = editTapChangerWizard( + doc.querySelector('TapChanger[name="tapChComplet"]')! + ); + element.workflow.push(() => wizard); + await element.requestUpdate(); + + inputs = Array.from(element.wizardUI.inputs); + + primaryAction = ( + element.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + + await element.wizardUI.requestUpdate(); // make sure wizard is rendered + }); + + it('looks like the the latest snapshot', async () => + await expect(element.wizardUI.dialog).dom.to.equalSnapshot()); + + it('does not accept empty name attribute', async () => { + inputs[0].value = ''; + await element.requestUpdate(); + await primaryAction.click(); + expect(actionEvent).to.not.have.been.called; + }); + + it('triggers simple edit action on primary action click', async () => { + inputs[0].value = 'someNonEmptyName'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isReplace); + const editAction = action; + + expect(editAction.new.element).to.have.attribute( + 'name', + 'someNonEmptyName' + ); + }); + + it('allows to unset non required attribute virtual', async () => { + const virtualCheckbox = ( + element.wizardUI.dialog?.querySelector( + 'wizard-checkbox[label="virtual"]' + ) + ); + + virtualCheckbox.nullSwitch!.click(); + virtualCheckbox.maybeValue = 'false'; + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isReplace); + const editAction = action; + + expect(editAction.new.element).to.have.attribute('virtual', 'false'); + }); + + it('allows to create non required attribute desc', async () => { + inputs[1].value = 'someNonEmptyDesc'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isReplace); + const editAction = action; + + expect(editAction.new.element).to.have.attribute( + 'desc', + 'someNonEmptyDesc' + ); + }); + }); +}); From 6cae0da557ef69029312a854e94bc5ecc9558909 Mon Sep 17 00:00:00 2001 From: danyill Date: Tue, 28 Feb 2023 07:58:18 +1300 Subject: [PATCH 08/27] fix(menu/save-project): Add missing XML prolog on document save (#1173) Ensure XML declaration is present on save, part of #1163 --------- Co-authored-by: cad --- src/menu/SaveProject.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/menu/SaveProject.ts b/src/menu/SaveProject.ts index 28cb7cccdb..505e2e7388 100644 --- a/src/menu/SaveProject.ts +++ b/src/menu/SaveProject.ts @@ -19,13 +19,20 @@ export default class SaveProjectPlugin extends LitElement { async run(): Promise { if (this.doc) { - const blob = new Blob( - [formatXml(new XMLSerializer().serializeToString(this.doc))], - { - type: 'application/xml', - } + let documentAsString = formatXml( + new XMLSerializer().serializeToString(this.doc) ); + // Add XML declaration/prolog if it's been stripped + // TODO: This can be removed once the improved OpenSCD core edit API is present + documentAsString = documentAsString.startsWith('' + '\n' + documentAsString; + + const blob = new Blob([documentAsString], { + type: 'application/xml', + }); + const a = document.createElement('a'); a.download = this.docName; a.href = URL.createObjectURL(blob); From fc3714c6079d91b5074672e9b84a0147ea18b0b9 Mon Sep 17 00:00:00 2001 From: marcvanraalte <86408026+marcvanraalte@users.noreply.github.com> Date: Thu, 2 Mar 2023 14:22:48 +0100 Subject: [PATCH 09/27] 972 create wizard tapchanger (#1172) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(tapchanger-editor.ts):editor_added * feat(tapchanger-editor.test.ts):setup * feat(TapChanger-editor):editor_and_unit_test * feat(tapchanger-editor):start_edit_button * feat(tapchanger-editor):add_edit_wizard * fix(tapChanger.ts)changed_to_tapchanger.ts * feat(tapchanger.ts):create_wizard_added * feat(tapchanger.test.ts):added_create_wizard_test * 973 remove tapchanger (#1174) * feat(tapchanger-editor.ts):remove_added * feat(tapchanger-editor):add_button (#1175) Co-authored-by: Steffen van den Driest <35229971+Stef3st@users.noreply.github.com> * fix: Empty disabled typefield in Add TapChanger --------- Co-authored-by: Steffen van den Driest <35229971+Stef3st@users.noreply.github.com> Co-authored-by: Stef3st * fix: tapchanger file rename * fix:remove 'TapChanger.' text in TapChanger header * fix: removes duplicate snapshot * test: update snapshot to match correct labels --------- Co-authored-by: Steffen van den Driest <35229971+Stef3st@users.noreply.github.com> Co-authored-by: Stef3st Co-authored-by: Juan Muñoz --- src/editors/substation/tapchanger-editor.ts | 125 +++++++++++++++--- src/wizards/{tapChanger.ts => tapchanger.ts} | 48 +++++++ src/wizards/wizard-library.ts | 4 +- .../tapchanger-editor-wizard-editing.test.ts | 94 +++++++++++++ .../tapchanger-editor.test.snap.js | 110 ++++++++++++++- .../transformer-winding-editor.test.snap.js | 33 +++++ .../__snapshots__/tapchanger.test.snap.js | 54 ++++++++ .../transformerwinding.test.snap.js | 4 +- test/unit/wizards/tapchanger.test.ts | 102 +++++++++++++- test/unit/wizards/transformerwinding.test.ts | 2 +- 10 files changed, 553 insertions(+), 23 deletions(-) rename src/wizards/{tapChanger.ts => tapchanger.ts} (70%) diff --git a/src/editors/substation/tapchanger-editor.ts b/src/editors/substation/tapchanger-editor.ts index 025e40178a..bca1a0043c 100644 --- a/src/editors/substation/tapchanger-editor.ts +++ b/src/editors/substation/tapchanger-editor.ts @@ -1,23 +1,45 @@ import { + css, customElement, html, LitElement, TemplateResult, property, state, + query, } from 'lit-element'; import { translate } from 'lit-translate'; import '@material/mwc-icon'; import '@material/mwc-icon-button'; +import '@material/mwc-menu'; +import { IconButton } from '@material/mwc-icon-button'; +import { ListItem } from '@material/mwc-list/mwc-list-item'; +import { Menu } from '@material/mwc-menu'; import '../../action-pane.js'; import './eq-function-editor.js'; import './l-node-editor.js'; import './sub-equipment-editor.js'; -import { getChildElementsByTagName, newWizardEvent } from '../../foundation.js'; -import { wizards } from '../../wizards/wizard-library.js'; + +import { styles } from './foundation.js'; +import { + getChildElementsByTagName, + newActionEvent, + newWizardEvent, + SCLTag, + tags, +} from '../../foundation.js'; +import { emptyWizard, wizards } from '../../wizards/wizard-library.js'; + +function childTags(element: Element | null | undefined): SCLTag[] { + if (!element) return []; + + return tags[element.tagName].children.filter( + child => wizards[child].create !== emptyWizard + ); +} @customElement('tapchanger-editor') export class TapChangerEditor extends LitElement { @@ -36,9 +58,39 @@ export class TapChangerEditor extends LitElement { const name = this.element.getAttribute('name') ?? ''; const desc = this.element.getAttribute('desc'); - return `TapChanger.${name} ${desc ? `—TapChanger.${desc}` : ''}`; + return `${name} ${desc ? `—${desc}` : ''}`; + } + + @query('mwc-menu') addMenu!: Menu; + @query('mwc-icon-button[icon="playlist_add"]') addButton!: IconButton; + + openEditWizard(): void { + const wizard = wizards['TapChanger'].edit(this.element); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + + private openCreateWizard(tagName: string): void { + const wizard = wizards[tagName].create(this.element!); + + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); } + updated(): void { + if (this.addMenu && this.addButton) + this.addMenu.anchor = this.addButton; + } + + remove(): void { + if (this.element.parentElement) + this.dispatchEvent( + newActionEvent({ + old: { + parent: this.element.parentElement, + element: this.element, + }, + }) + ); + } private renderLNodes(): TemplateResult { const lNodes = getChildElementsByTagName(this.element, 'LNode'); @@ -55,12 +107,7 @@ export class TapChangerEditor extends LitElement { : html``; } - openEditWizard(): void { - const wizard = wizards['TapChanger'].edit(this.element); - if (wizard) this.dispatchEvent(newWizardEvent(wizard)); - } - - renderEqFunctions(): TemplateResult { + private renderEqFunctions(): TemplateResult { if (!this.showfunctions) return html``; const eqFunctions = getChildElementsByTagName(this.element, 'EqFunction'); @@ -90,15 +137,63 @@ export class TapChangerEditor extends LitElement { )}`; } + private renderAddButtons(): TemplateResult[] { + return childTags(this.element).map( + child => + html`${child}` + ); + } + render(): TemplateResult { return html` - this.openEditWizard()} - > - + this.openEditWizard()} + > + + + this.remove()} + > + + + (this.addMenu.open = true)} + > { + const tagName = ((e.target).selected).value; + this.openCreateWizard(tagName); + }} + >${this.renderAddButtons()} ${this.renderLNodes()} - ${this.renderEqFunctions()} ${this.renderSubEquipments()}`; + ${this.renderEqFunctions()} ${this.renderSubEquipments()} + `; } + + static styles = css` + ${styles} + + :host(.moving) { + opacity: 0.3; + } + + abbr { + text-decoration: none; + border-bottom: none; + } + `; } diff --git a/src/wizards/tapChanger.ts b/src/wizards/tapchanger.ts similarity index 70% rename from src/wizards/tapChanger.ts rename to src/wizards/tapchanger.ts index b7fcde3e48..91e4ddbdb2 100644 --- a/src/wizards/tapChanger.ts +++ b/src/wizards/tapchanger.ts @@ -12,6 +12,24 @@ import { WizardInputElement, } from '../foundation.js'; +function createTapChangerAction(parent: Element): WizardActor { + return (inputs: WizardInputElement[]) => { + const tapChangerAttrs: Record = {}; + const tapChangerKeys = ['name', 'desc', 'type', 'virtual']; + tapChangerKeys.forEach(key => { + tapChangerAttrs[key] = getValue(inputs.find(i => i.label === key)!); + }); + + const tapChanger = createElement( + parent.ownerDocument, + 'TapChanger', + tapChangerAttrs + ); + + return [{ new: { parent, element: tapChanger } }]; + }; +} + function updateTapChangerAction(element: Element): WizardActor { return (inputs: WizardInputElement[]): SimpleAction[] => { const tapChangerAttrs: Record = {}; @@ -79,6 +97,36 @@ export function contentTapChangerWizard( ]; } +export function createTapChangerWizard(parent: Element): Wizard { + const name = ''; + const desc = null; + const type = 'LTC'; + const virtual = null; + const reservedNames = Array.from(parent.querySelectorAll('TapChanger')).map( + TapChanger => TapChanger.getAttribute('name')! + ); + + return [ + { + title: get('wizard.title.add', { tagName: 'TapChanger' }), + primary: { + icon: 'save', + label: get('save'), + action: createTapChangerAction(parent), + }, + content: [ + ...contentTapChangerWizard({ + name, + desc, + type, + virtual, + reservedNames, + }), + ], + }, + ]; +} + export function editTapChangerWizard(element: Element): Wizard { const name = element.getAttribute('name'); const desc = element.getAttribute('desc'); diff --git a/src/wizards/wizard-library.ts b/src/wizards/wizard-library.ts index 9cebe30d35..e0203ec7e3 100644 --- a/src/wizards/wizard-library.ts +++ b/src/wizards/wizard-library.ts @@ -48,7 +48,7 @@ import { createTransformerWindingWizard, editTransformerWindingWizard, } from './transformerWinding.js'; -import { editTapChangerWizard } from './tapChanger.js'; +import { createTapChangerWizard, editTapChangerWizard } from './tapchanger.js'; type SclElementWizard = ( element: Element, @@ -520,7 +520,7 @@ export const wizards: Record< }, TapChanger: { edit: editTapChangerWizard, - create: emptyWizard, + create: createTapChangerWizard, }, Terminal: { edit: editTerminalWizard, diff --git a/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts b/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts index 550a4df73a..fba8dbf9d2 100644 --- a/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts +++ b/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts @@ -10,6 +10,42 @@ import { WizardTextField } from '../../../../src/wizard-textfield.js'; import { WizardCheckbox } from '../../../../src/wizard-checkbox.js'; import { MenuBase } from '@material/mwc-menu/mwc-menu-base.js'; +const openAndCancelMenu: ( + parent: MockWizardEditor, + element: TapChangerEditor +) => Promise = ( + parent: MockWizardEditor, + element: TapChangerEditor +): Promise => + new Promise(async resolve => { + expect(parent.wizardUI.dialog).to.be.undefined; + + element?.shadowRoot + ?.querySelector("mwc-icon-button[icon='playlist_add']")! + .click(); + const lnodMenuItem: ListItemBase = + element?.shadowRoot?.querySelector( + `mwc-list-item[value='LNode']` + )!; + lnodMenuItem.click(); + await new Promise(resolve => setTimeout(resolve, 100)); // await animation + + expect(parent.wizardUI.dialog).to.exist; + + const secondaryAction: HTMLElement = ( + parent.wizardUI.dialog?.querySelector( + 'mwc-button[slot="secondaryAction"][dialogaction="close"]' + ) + ); + + secondaryAction.click(); + await new Promise(resolve => setTimeout(resolve, 100)); // await animation + + expect(parent.wizardUI.dialog).to.be.undefined; + + return resolve(); + }); + describe('tapchanger-editor wizarding editing integration', () => { let doc: XMLDocument; let parent: MockWizardEditor; @@ -108,5 +144,63 @@ describe('tapchanger-editor wizarding editing integration', () => { ?.getAttribute('virtual') ).to.equal('false'); }); + + describe('has a delete icon button that', () => { + let deleteButton: HTMLElement; + + beforeEach(async () => { + deleteButton = ( + element?.shadowRoot?.querySelector('mwc-icon-button[icon="delete"]') + ); + await parent.updateComplete; + }); + + it('removes the attached TapChanger element from the document', async () => { + expect( + doc.querySelector( + 'TransformerWinding[name="withTapChanger1"] > TapChanger[name="tapChComplet"]' + ) + ).to.exist; + + await deleteButton.click(); + + expect( + doc.querySelector( + 'TransformerWinding[name="withTapChanger1"] > TapChanger[name="tapChComplet"]' + ) + ).to.not.exist; + }); + }); + }); + describe('Open add wizard', () => { + let doc: XMLDocument; + let parent: MockWizardEditor; + let element: TapChangerEditor | null; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/TapChanger.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + parent = ( + await fixture( + html` TapChanger[name="tapChComplet"]' + )} + >` + ) + ); + + element = parent.querySelector('tapchanger-editor'); + + await parent.updateComplete; + }); + + it('Should open the same wizard for the second time', async () => { + await openAndCancelMenu(parent, element!); + await openAndCancelMenu(parent, element!); + }); }); }); diff --git a/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js index ac3e201397..8a8bf4a45d 100644 --- a/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/tapchanger-editor.test.snap.js @@ -3,7 +3,7 @@ export const snapshots = {}; snapshots["web component rendering TapChanger element rendering LNode and EqFunction children looks like the latest snapshot"] = ` + + + + + + + + + + + LNode + + + + + SubEquipment + + + + + EqFunction + + + +
@@ -25,7 +78,7 @@ snapshots["web component rendering TapChanger element rendering LNode and EqFunc snapshots["web component rendering TapChanger element rendering SubEquipment looks like the latest snapshot"] = ` + + + + + + + + + + + LNode + + + + + SubEquipment + + + + + EqFunction + + + + diff --git a/test/unit/editors/substation/__snapshots__/transformer-winding-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/transformer-winding-editor.test.snap.js index e547c027ff..806ee26980 100644 --- a/test/unit/editors/substation/__snapshots__/transformer-winding-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/transformer-winding-editor.test.snap.js @@ -42,6 +42,17 @@ snapshots["transformer-winding-editor with children when EqFunction elements are LNode + + + TapChanger + + + + + TapChanger + + + + + TapChanger + + +
+ + + + + + + + +
+ + + + + +`; +/* end snapshot Wizards for SCL TapChanger element define a create wizard that looks like the the latest snapshot */ + diff --git a/test/unit/wizards/__snapshots__/transformerwinding.test.snap.js b/test/unit/wizards/__snapshots__/transformerwinding.test.snap.js index 9ef977f451..195097a35c 100644 --- a/test/unit/wizards/__snapshots__/transformerwinding.test.snap.js +++ b/test/unit/wizards/__snapshots__/transformerwinding.test.snap.js @@ -55,7 +55,7 @@ snapshots["Wizards for SCL TransformerWinding element define an edit wizard that `; /* end snapshot Wizards for SCL TransformerWinding element define an edit wizard that looks like the the latest snapshot */ -snapshots["Wizards for SCL TransformerWinding element define an create wizard that looks like the the latest snapshot"] = +snapshots["Wizards for SCL TransformerWinding element define a create wizard that looks like the the latest snapshot"] = ` `; -/* end snapshot Wizards for SCL TransformerWinding element define an create wizard that looks like the the latest snapshot */ +/* end snapshot Wizards for SCL TransformerWinding element define a create wizard that looks like the the latest snapshot */ diff --git a/test/unit/wizards/tapchanger.test.ts b/test/unit/wizards/tapchanger.test.ts index 35658b1ec6..2a037b3154 100644 --- a/test/unit/wizards/tapchanger.test.ts +++ b/test/unit/wizards/tapchanger.test.ts @@ -12,7 +12,10 @@ import { Replace, WizardInputElement, } from '../../../src/foundation.js'; -import { editTapChangerWizard } from '../../../src/wizards/tapChanger.js'; +import { + createTapChangerWizard, + editTapChangerWizard, +} from '../../../src/wizards/tapchanger.js'; import { WizardCheckbox } from '../../../src/wizard-checkbox.js'; describe('Wizards for SCL TapChanger element', () => { @@ -117,4 +120,101 @@ describe('Wizards for SCL TapChanger element', () => { ); }); }); + + describe('define a create wizard that', () => { + beforeEach(async () => { + const wizard = createTapChangerWizard( + doc.querySelector('TransformerWinding')! + ); + element.workflow.push(() => wizard); + await element.requestUpdate(); + + inputs = Array.from(element.wizardUI.inputs); + + primaryAction = ( + element.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + + await element.wizardUI.requestUpdate(); // make sure wizard is rendered + }); + + it('looks like the the latest snapshot', async () => + await expect(element.wizardUI.dialog).dom.to.equalSnapshot()); + + it('does not accept empty name attribute', async () => { + await primaryAction.click(); + + expect(actionEvent).to.not.have.been.called; + }); + + it('allows to create required attributes name', async () => { + inputs[0].value = 'someNonEmptyName'; + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isCreate); + const createAction = action; + + expect(createAction.new.element).to.have.attribute( + 'name', + 'someNonEmptyName' + ); + + expect(createAction.new.element).to.not.have.attribute('desc'); + }); + + it('allows to create name and non required attribute virtual', async () => { + inputs[0].value = 'someNonEmptyName'; + + const virtualCheckbox = ( + element.wizardUI.dialog?.querySelector( + 'wizard-checkbox[label="virtual"]' + ) + ); + + virtualCheckbox.nullSwitch!.click(); + virtualCheckbox.maybeValue = 'true'; + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isCreate); + const createAction = action; + expect(createAction.new.element).to.have.attribute( + 'name', + 'someNonEmptyName' + ); + expect(createAction.new.element).to.have.attribute('virtual', 'true'); + }); + + it('allows to create name and non required attribute desc', async () => { + inputs[0].value = 'someNonEmptyName'; + const descField = ( + element.wizardUI.dialog?.querySelector('wizard-textfield[label="desc"]') + ); + + await new Promise(resolve => setTimeout(resolve, 100)); // await animation + descField.nullSwitch!.click(); + await element.updateComplete; + inputs[1].value = 'someNonEmptyDesc'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isCreate); + const createAction = action; + + expect(createAction.new.element).to.have.attribute( + 'desc', + 'someNonEmptyDesc' + ); + }); + }); }); diff --git a/test/unit/wizards/transformerwinding.test.ts b/test/unit/wizards/transformerwinding.test.ts index 3aa4c301e2..bbc4b29855 100644 --- a/test/unit/wizards/transformerwinding.test.ts +++ b/test/unit/wizards/transformerwinding.test.ts @@ -130,7 +130,7 @@ describe('Wizards for SCL TransformerWinding element', () => { }); }); - describe('define an create wizard that', () => { + describe('define a create wizard that', () => { beforeEach(async () => { const wizard = createTransformerWindingWizard( doc.querySelector('PowerTransformer')! From aaab451ca01bcd29b1bb2dc73299a0b171b30389 Mon Sep 17 00:00:00 2001 From: danyill Date: Tue, 7 Mar 2023 21:15:25 +1300 Subject: [PATCH 10/27] feat(editors/subscription): Support valKind and valImport on first instances, improve instance use counting and allow Val updates (#1169) * Scope query for finding supervision instances more tightly, closes #1161 * Add base code, refactor LGOS/LSVS creation and update tests, closes #1162 * Improve schema conformance for adding the LGOS/LSVS element, thanks Jakob --------- Co-authored-by: Christian Dinkel --- src/editors/subscription/foundation.ts | 239 ++++-- .../subscription/goose/subscriber-list.ts | 42 +- .../ext-ref-later-binding-list.ts | 23 +- .../later-binding/ext-ref-ln-binding-list.ts | 19 +- .../sampledvalues/subscriber-list.ts | 43 +- .../GooseSubscriberLaterBinding.test.ts | 41 + .../editors/SMVSubscriberLaterBinding.test.ts | 41 + .../SMVSubscriberLaterBinding.test.snap.js | 759 +++++++++++++++++- .../editors/LaterBindingGOOSE-LGOS.scd | 95 +++ .../editors/LaterBindingSMV-LSVS.scd | 177 +++- .../editors/subscription/foundation.test.ts | 36 + .../editors/subscription/supervision.test.ts | 210 ++++- 12 files changed, 1554 insertions(+), 171 deletions(-) diff --git a/src/editors/subscription/foundation.ts b/src/editors/subscription/foundation.ts index 472c661388..8df872cf23 100644 --- a/src/editors/subscription/foundation.ts +++ b/src/editors/subscription/foundation.ts @@ -1,5 +1,4 @@ -import { css, html, LitElement, query } from 'lit-element'; -import { nothing } from 'lit-html'; +import { css, LitElement, query } from 'lit-element'; import { cloneElement, @@ -11,11 +10,6 @@ import { isPublic, minAvailableLogicalNodeInstance, } from '../../foundation.js'; -import { - createTemplateStructure, - determineUninitializedStructure, - initializeElements, -} from '../../foundation/dai.js'; import { getFcdaReferences } from '../../foundation/ied.js'; import { SCL_NAMESPACE } from '../../schemas.js'; @@ -202,14 +196,43 @@ export function canRemoveSubscriptionSupervision( } /** - * Searches DataTypeTemplates for DOType>DA[valKind=Conf/RO][valImport=true] from an LN reference. - * @param lnElement - The LN Element to use for searching the starting DO Element. - * @returns - true if both conditions are found in the DA child element. + * Searches for first instantiated LGOS/LSVS LN for presence of DOI>DAI[valKind=Conf/RO][valImport=true] + * given a supervision type and if necessary then searches DataTypeTemplates for + * DOType>DA[valKind=Conf/RO][valImport=true] to determine if modifications to supervision are allowed. + * @param ied - SCL IED element. + * @param supervisionType - either 'LGOS' or 'LSVS' supervision LN classes. + * @returns boolean indicating if subscriptions are allowed. */ -function checksDataTypeTemplateConditions(lnElement: Element): boolean { - const rootNode = lnElement?.ownerDocument; - const lNodeType = lnElement.getAttribute('lnType'); - const lnClass = lnElement.getAttribute('lnClass'); +function isSupervisionModificationAllowed( + ied: Element, + supervisionType: string +): boolean { + const firstSupervisionLN = ied.querySelector( + `LN[lnClass="${supervisionType}"]` + ); + + // no supervision logical nodes => no new supervision possible + if (firstSupervisionLN === null) return false; + + // check if allowed to modify based on first instance properties + const supervisionName = supervisionType === 'LGOS' ? 'GoCBRef' : 'SvCBRef'; + const instValKind = firstSupervisionLN! + .querySelector(`DOI[name="${supervisionName}"]>DAI[name="setSrcRef"]`) + ?.getAttribute('valKind'); + const instValImport = firstSupervisionLN! + .querySelector(`DOI[name="${supervisionName}"]>DAI[name="setSrcRef"]`) + ?.getAttribute('valImport'); + + if ( + (instValKind === 'RO' || instValKind === 'Conf') && + instValImport === 'true' + ) + return true; + + // check if allowed to modify based on DataTypeTemplates for first instance + const rootNode = firstSupervisionLN?.ownerDocument; + const lNodeType = firstSupervisionLN.getAttribute('lnType'); + const lnClass = firstSupervisionLN.getAttribute('lnClass'); const dObj = rootNode.querySelector( `DataTypeTemplates > LNodeType[id="${lNodeType}"][lnClass="${lnClass}"] > DO[name="${ lnClass === 'LGOS' ? 'GoCBRef' : 'SvCBRef' @@ -257,42 +280,124 @@ export function instantiateSubscriptionSupervision( subscriberIED, supervisionType ); - if (!availableLN || !checksDataTypeTemplateConditions(availableLN)) return []; - - // Then, create the templateStructure array - const templateStructure = createTemplateStructure(availableLN, [ - controlBlock?.tagName === 'GSEControl' ? 'GoCBRef' : 'SvCBRef', - 'setSrcRef', - ]); - if (!templateStructure) return []; - // Determine where to start creating new elements (DOI/SDI/DAI) - const [parentElement, uninitializedTemplateStructure] = - determineUninitializedStructure(availableLN, templateStructure); - // // Next create all missing elements (DOI/SDI/DAI) - const newElement = initializeElements(uninitializedTemplateStructure); - newElement.querySelector('Val')!.textContent = - controlBlockReference(controlBlock); - const createActions: Create[] = []; + if ( + !availableLN || + !isSupervisionModificationAllowed(subscriberIED, supervisionType) + ) + return []; + + const actions: Create[] = []; + // If creating new LN element if (!availableLN.parentElement) { const parent = subscriberIED.querySelector( `LN[lnClass="${supervisionType}"]` )?.parentElement; - if (parent) - createActions.push({ + if (parent) { + // use Create Action for supervision LN + actions.push({ new: { - parent, + parent: parent, element: availableLN, + reference: parent!.querySelector( + `LN[lnClass="${supervisionType}"]:last-child` + )?.nextElementSibling, }, }); + } } - return createActions.concat([ - { + + // Create child elements + const supervisionName = supervisionType === 'LGOS' ? 'GoCBRef' : 'SvCBRef'; + + let doiElement = availableLN.querySelector(`DOI[name="${supervisionName}"]`); + if (!doiElement) { + doiElement = subscriberIED.ownerDocument.createElementNS( + SCL_NAMESPACE, + 'DOI' + ); + doiElement.setAttribute('name', supervisionName); + actions.push({ new: { - parent: parentElement, - element: newElement, + parent: availableLN!, + element: doiElement, }, + }); + } + + let daiElement = availableLN.querySelector( + `DOI[name="${supervisionName}"]>DAI[name="setSrcRef"]` + ); + if (!daiElement) { + daiElement = subscriberIED.ownerDocument.createElementNS( + SCL_NAMESPACE, + 'DAI' + ); + const srcValRef = subscriberIED.querySelector( + `LN[lnClass="${supervisionType}"]>DOI[name="${supervisionName}"]>DAI[name="setSrcRef"]` + ); + daiElement.setAttribute('name', 'setSrcRef'); + + // transfer valKind and valImport from first supervision instance if present + if (srcValRef?.hasAttribute('valKind')) + daiElement.setAttribute('valKind', srcValRef.getAttribute('valKind')!); + if (srcValRef?.hasAttribute('valImport')) + daiElement.setAttribute( + 'valImport', + srcValRef.getAttribute('valImport')! + ); + actions.push({ + new: { + parent: doiElement!, + element: daiElement, + }, + }); + } + + let valElement = availableLN.querySelector(`Val`); + if (!valElement) { + valElement = subscriberIED.ownerDocument.createElementNS( + SCL_NAMESPACE, + 'Val' + ); + } + valElement.textContent = controlBlockReference(controlBlock); + actions.push({ + new: { + parent: daiElement!, + element: valElement, }, - ]); + }); + + return actions; +} + +/** + * Return Val elements within an LGOS/LSVS instance for a particular IED and control block type. + * @param ied - IED SCL element. + * @param cbTagName - Either GSEControl or (defaults to) SampledValueControl. + * @param firstOnly - If true, return the first element found + * @returns an Element array of Val SCL elements within an LGOS/LSVS node. + */ +export function getSupervisionCbRefs( + ied: Element, + cbTagName: string +): Element[]; +export function getSupervisionCbRefs( + ied: Element, + cbTagName: string, + firstOnly: boolean +): Element | null; +export function getSupervisionCbRefs( + ied: Element, + cbTagName: string, + firstOnly?: boolean +): Element[] | Element | null { + const supervisionType = cbTagName === 'GSEControl' ? 'LGOS' : 'LSVS'; + const supervisionName = supervisionType === 'LGOS' ? 'GoCBRef' : 'SvCBRef'; + const selectorString = `LN[lnClass="${supervisionType}"]>DOI[name="${supervisionName}"]>DAI[name="setSrcRef"]>Val,LN0[lnClass="${supervisionType}"]>DOI[name="${supervisionName}"]>DAI[name="setSrcRef"]>Val`; + return firstOnly + ? ied.querySelector(selectorString) + : Array.from(ied.querySelectorAll(selectorString)); } /** @@ -310,12 +415,9 @@ export function removeSubscriptionSupervision( subscriberIED: Element | undefined ): Delete[] { if (!controlBlock || !subscriberIED) return []; - const supervisionType = - controlBlock?.tagName === 'GSEControl' ? 'LGOS' : 'LSVS'; - const valElement = Array.from( - subscriberIED.querySelectorAll( - `LN[lnClass="${supervisionType}"]>DOI>DAI>Val,LN0[lnClass="${supervisionType}"]>DOI>DAI>Val` - ) + const valElement = getSupervisionCbRefs( + subscriberIED, + controlBlock.tagName ).find(val => val.textContent == controlBlockReference(controlBlock)); if (!valElement) return []; const lnElement = valElement.closest('LN0, LN'); @@ -361,16 +463,14 @@ function isSupervisionAllowed( if (subscriberIED.querySelector(`LN[lnClass="${supervisionType}"]`) === null) return false; if ( - Array.from( - subscriberIED.querySelectorAll( - `LN[lnClass="${supervisionType}"]>DOI>DAI>Val` - ) - ).find(val => val.textContent == controlBlockReference(controlBlock)) + getSupervisionCbRefs(subscriberIED, controlBlock.tagName).find( + val => val.textContent == controlBlockReference(controlBlock) + ) ) return false; if ( maxSupervisions(subscriberIED, controlBlock) <= - instantiatedSupervisionsCount(subscriberIED, controlBlock, supervisionType) + instantiatedSupervisionsCount(subscriberIED, controlBlock) ) return false; @@ -390,11 +490,17 @@ export function findOrCreateAvailableLNInst( ): Element | null { let availableLN = Array.from( subscriberIED.querySelectorAll(`LN[lnClass="${supervisionType}"]`) - ).find( - ln => - ln.querySelector('DOI>DAI>Val') === null || - ln.querySelector('DOI>DAI>Val')?.textContent === '' - ); + ).find(ln => { + const supervisionName = supervisionType === 'LGOS' ? 'GoCBRef' : 'SvCBRef'; + return ( + ln.querySelector( + `DOI[name="${supervisionName}"]>DAI[name="setSrcRef"]>Val` + ) === null || + ln.querySelector( + `DOI[name="${supervisionName}"]>DAI[name="setSrcRef"]>Val` + )?.textContent === '' + ); + }); if (!availableLN) { availableLN = subscriberIED.ownerDocument.createElementNS( SCL_NAMESPACE, @@ -407,13 +513,16 @@ export function findOrCreateAvailableLNInst( openScdTag.setAttribute('type', 'OpenSCD.create'); availableLN.appendChild(openScdTag); availableLN.setAttribute('lnClass', supervisionType); - const instantiatedSibling = subscriberIED - .querySelector(`LN[lnClass="${supervisionType}"]>DOI>DAI>Val`) - ?.closest('LN'); - if (!instantiatedSibling) return null; + const instantiatedSiblings = getSupervisionCbRefs( + subscriberIED, + controlBlock.tagName, + true + )?.closest('LN'); + + if (!instantiatedSiblings) return null; availableLN.setAttribute( 'lnType', - instantiatedSibling.getAttribute('lnType') ?? '' + instantiatedSiblings?.getAttribute('lnType') ?? '' ); } @@ -508,13 +617,11 @@ export function getExistingSupervision(extRef: Element | null): Element | null { */ export function instantiatedSupervisionsCount( subscriberIED: Element, - controlBlock: Element, - supervisionType: string + controlBlock: Element ): number { - const instantiatedValues = Array.from( - subscriberIED.querySelectorAll( - `LN[lnClass="${supervisionType}"]>DOI>DAI>Val` - ) + const instantiatedValues = getSupervisionCbRefs( + subscriberIED, + controlBlock.tagName ).filter(val => val.textContent !== ''); return instantiatedValues.length; } diff --git a/src/editors/subscription/goose/subscriber-list.ts b/src/editors/subscription/goose/subscriber-list.ts index 4f2dca49dd..877f1cd8cf 100644 --- a/src/editors/subscription/goose/subscriber-list.ts +++ b/src/editors/subscription/goose/subscriber-list.ts @@ -14,10 +14,12 @@ import '@material/mwc-list/mwc-list-item'; import '../../../filtered-list.js'; import { + ComplexAction, Create, createElement, Delete, identity, + Move, newActionEvent, } from '../../../foundation.js'; import { @@ -241,7 +243,10 @@ export class SubscriberList extends SubscriberListContainer { if (!inputsElement) inputsElement = createElement(ied.ownerDocument, 'Inputs', {}); - const actions: Create[] = []; + const complexAction: ComplexAction = { + actions: [], + title: get(`subscription.connect`), + }; this.currentUsedDataset!.querySelectorAll('FCDA').forEach(fcda => { if ( @@ -254,37 +259,30 @@ export class SubscriberList extends SubscriberListContainer { ); if (inputsElement?.parentElement) - actions.push({ new: { parent: inputsElement!, element: extRef } }); + complexAction.actions.push({ + new: { parent: inputsElement!, element: extRef }, + }); else inputsElement?.appendChild(extRef); } }); - // we need to extend the actions array with the actions for the instation of the LGOS - const supervisionActions: Create[] = instantiateSubscriptionSupervision( + // we need to extend the actions array with the actions for the instantiation of the LGOS + const supervisionActions = instantiateSubscriptionSupervision( this.currentSelectedGseControl, ied ); - - /** If the IED doesn't have a Inputs element, just append it to the first LN0 element. */ - const title = get('subscription.connect'); if (inputsElement.parentElement) { - this.dispatchEvent( - newActionEvent({ - title, - actions: actions.concat(supervisionActions), - }) - ); + complexAction.actions.concat(supervisionActions); } else { - const inputAction: Create = { - new: { parent: ied.querySelector('LN0')!, element: inputsElement }, - }; - this.dispatchEvent( - newActionEvent({ - title, - actions: [inputAction].concat(supervisionActions), - }) - ); + /** If the IED doesn't have a Inputs element, just append it to the first LN0 element. */ + const inputAction: (Create | Move)[] = [ + { + new: { parent: ied.querySelector('LN0')!, element: inputsElement }, + }, + ]; + complexAction.actions = inputAction.concat(supervisionActions); } + this.dispatchEvent(newActionEvent(complexAction)); } private async unsubscribe(ied: Element): Promise { diff --git a/src/editors/subscription/later-binding/ext-ref-later-binding-list.ts b/src/editors/subscription/later-binding/ext-ref-later-binding-list.ts index 278a7f05ed..14d936bd62 100644 --- a/src/editors/subscription/later-binding/ext-ref-later-binding-list.ts +++ b/src/editors/subscription/later-binding/ext-ref-later-binding-list.ts @@ -12,6 +12,7 @@ import { get, translate } from 'lit-translate'; import { cloneElement, + ComplexAction, Delete, getDescriptionAttribute, identity, @@ -191,6 +192,11 @@ export class ExtRefLaterBindingList extends LitElement { return; } + const complexAction: ComplexAction = { + actions: [], + title: get(`subscription.connect`), + }; + const replaceAction = { old: { element: extRefElement }, new: { @@ -201,19 +207,18 @@ export class ExtRefLaterBindingList extends LitElement { ), }, }; + complexAction.actions.push(replaceAction); const subscriberIed = extRefElement.closest('IED') || undefined; - const supervisionActions = instantiateSubscriptionSupervision( - this.currentSelectedControlElement, - subscriberIed - ); - this.dispatchEvent( - newActionEvent({ - title: get(`subscription.connect`), - actions: [replaceAction, ...supervisionActions], - }) + complexAction.actions.push( + ...instantiateSubscriptionSupervision( + this.currentSelectedControlElement, + subscriberIed + ) ); + + this.dispatchEvent(newActionEvent(complexAction)); this.dispatchEvent( newSubscriptionChangedEvent( this.currentSelectedControlElement, diff --git a/src/editors/subscription/later-binding/ext-ref-ln-binding-list.ts b/src/editors/subscription/later-binding/ext-ref-ln-binding-list.ts index eaf3e364cf..a156b4dc8f 100644 --- a/src/editors/subscription/later-binding/ext-ref-ln-binding-list.ts +++ b/src/editors/subscription/later-binding/ext-ref-ln-binding-list.ts @@ -121,11 +121,17 @@ export class ExtRefLnBindingList extends LitElement { return null; } - const actions: Create[] = []; + const complexAction: ComplexAction = { + actions: [], + title: get(`subscription.connect`), + }; + let inputsElement = lnElement.querySelector(':scope > Inputs'); if (!inputsElement) { inputsElement = createElement(lnElement.ownerDocument, 'Inputs', {}); - actions.push({ new: { parent: lnElement, element: inputsElement } }); + complexAction.actions.push({ + new: { parent: lnElement, element: inputsElement }, + }); } if ( @@ -143,20 +149,21 @@ export class ExtRefLnBindingList extends LitElement { this.currentSelectedControlElement, this.currentSelectedFcdaElement ); - actions.push({ new: { parent: inputsElement, element: extRef } }); + complexAction.actions.push({ + new: { parent: inputsElement, element: extRef }, + }); } // we need to extend the actions array with the actions for the instation of the LGOS const subscriberIed = lnElement.closest('IED') || undefined; - actions.push( + complexAction.actions.push( ...instantiateSubscriptionSupervision( this.currentSelectedControlElement, subscriberIed ) ); - const title = get('subscription.connect'); - return { title, actions }; + return complexAction; } private unsubscribe(lnElement: Element): ComplexAction | null { diff --git a/src/editors/subscription/sampledvalues/subscriber-list.ts b/src/editors/subscription/sampledvalues/subscriber-list.ts index e89a1e5de5..28ec6da6e6 100644 --- a/src/editors/subscription/sampledvalues/subscriber-list.ts +++ b/src/editors/subscription/sampledvalues/subscriber-list.ts @@ -14,10 +14,12 @@ import '@material/mwc-list/mwc-list-item'; import '../../../filtered-list.js'; import { + ComplexAction, Create, createElement, Delete, identity, + Move, newActionEvent, } from '../../../foundation.js'; import { @@ -243,7 +245,10 @@ export class SubscriberList extends SubscriberListContainer { if (!inputsElement) inputsElement = createElement(ied.ownerDocument, 'Inputs', {}); - const actions: Create[] = []; + const complexAction: ComplexAction = { + actions: [], + title: get(`subscription.connect`), + }; this.currentUsedDataset!.querySelectorAll('FCDA').forEach(fcda => { if ( @@ -256,37 +261,31 @@ export class SubscriberList extends SubscriberListContainer { ); if (inputsElement?.parentElement) - actions.push({ new: { parent: inputsElement!, element: extRef } }); + complexAction.actions.push({ + new: { parent: inputsElement!, element: extRef }, + }); else inputsElement?.appendChild(extRef); } }); // we need to extend the actions array with the actions for the instantiation of the LSVS - const supervisionActions: Create[] = instantiateSubscriptionSupervision( + const supervisionActions = instantiateSubscriptionSupervision( this.currentSelectedSmvControl, ied ); - /** If the IED doesn't have a Inputs element, just append it to the first LN0 element. */ - const title = get('subscription.connect'); - if (inputsElement.parentElement) - this.dispatchEvent( - newActionEvent({ - title, - actions: actions.concat(supervisionActions), - }) - ); - else { - const inputAction: Create = { - new: { parent: ied.querySelector('LN0')!, element: inputsElement }, - }; - this.dispatchEvent( - newActionEvent({ - title, - actions: [inputAction].concat(supervisionActions), - }) - ); + if (inputsElement.parentElement) { + complexAction.actions.concat(supervisionActions); + } else { + /** If the IED doesn't have a Inputs element, just append it to the first LN0 element. */ + const inputAction: (Create | Move)[] = [ + { + new: { parent: ied.querySelector('LN0')!, element: inputsElement }, + }, + ]; + complexAction.actions = inputAction.concat(supervisionActions); } + this.dispatchEvent(newActionEvent(complexAction)); } private async unsubscribe(ied: Element): Promise { diff --git a/test/integration/editors/GooseSubscriberLaterBinding.test.ts b/test/integration/editors/GooseSubscriberLaterBinding.test.ts index b5f1aac867..c95134aa14 100644 --- a/test/integration/editors/GooseSubscriberLaterBinding.test.ts +++ b/test/integration/editors/GooseSubscriberLaterBinding.test.ts @@ -95,6 +95,47 @@ describe('GOOSE Subscribe Later Binding Plugin', () => { ).to.be.equal(4); }); + it('when subscribing an available ExtRef then a supervision instance is created', async () => { + doc = await fetch('/test/testfiles/editors/LaterBindingGOOSE-LGOS.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element = await fixture( + html`` + ); + + const extRefListElement = getExtrefLaterBindingList(element); + + const fcdaListElement = getFCDABindingList(element); + selectFCDAItem( + fcdaListElement, + 'GOOSE_Publisher2>>QB2_Disconnector>GOOSE1', + 'GOOSE_Publisher2>>QB2_Disconnector>GOOSE1sDataSet>QB1_Disconnector/ CSWI 1.Pos stVal (ST)' + ); + await element.requestUpdate(); + await extRefListElement.requestUpdate(); + + (( + extRefListElement.shadowRoot!.querySelector( + 'mwc-list-item[value="GOOSE_Subscriber1>>Earth_Switch> CILO 1>Pos;CSWI1/Pos/stVal[0]"]' + ) + )).click(); + await element.requestUpdate(); + + const supervisionInstance = element.doc.querySelector( + 'IED[name="GOOSE_Subscriber1"] LN[lnClass="LGOS"][inst="4"]' + ); + expect(supervisionInstance).to.exist; + expect( + supervisionInstance?.previousElementSibling?.getAttribute('lnClass') + ).to.equal('LGOS'); + expect( + supervisionInstance?.previousElementSibling?.getAttribute('inst') + ).to.equal('3'); + }); + it('when unsubscribing a subscribed ExtRef then the lists are changed', async () => { const fcdaListElement = getFCDABindingList(element); const extRefListElement = getExtrefLaterBindingList(element); diff --git a/test/integration/editors/SMVSubscriberLaterBinding.test.ts b/test/integration/editors/SMVSubscriberLaterBinding.test.ts index 71963d3bac..9bfc6b9e3d 100644 --- a/test/integration/editors/SMVSubscriberLaterBinding.test.ts +++ b/test/integration/editors/SMVSubscriberLaterBinding.test.ts @@ -96,6 +96,47 @@ describe('SMV Subscribe Later Binding plugin', () => { ).to.be.equal(8); }); + it('when subscribing an available ExtRef then a supervision instance is created', async () => { + doc = await fetch('/test/testfiles/editors/LaterBindingSMV-LSVS.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + element = await fixture( + html`` + ); + + const fcdaListElement = getFCDABindingList(element); + const extRefListElement = getExtrefLaterBindingList(element); + + selectFCDAItem( + fcdaListElement, + 'SMV_Publisher>>CurrentTransformer>fullSmv', + 'SMV_Publisher>>CurrentTransformer>fullSmvsDataSet>CurrentTransformer/L2 TCTR 2.AmpSv instMag.i (MX)' + ); + await element.requestUpdate(); + await extRefListElement.requestUpdate(); + + (( + extRefListElement.shadowRoot!.querySelector( + 'mwc-list-item[value="SMV_Subscriber>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0]"]' + ) + )).click(); + await element.requestUpdate(); + + const supervisionInstance = element.doc.querySelector( + 'IED[name="SMV_Subscriber"] LN[lnClass="LSVS"][inst="3"]' + ); + expect(supervisionInstance).to.exist; + expect( + supervisionInstance?.previousElementSibling?.getAttribute('lnClass') + ).to.equal('LSVS'); + expect( + supervisionInstance?.previousElementSibling?.getAttribute('inst') + ).to.equal('2'); + }); + it('when unsubscribing a subscribed ExtRef then the lists are changed', async () => { const fcdaListElement = getFCDABindingList(element); const extRefListElement = getExtrefLaterBindingList(element); diff --git a/test/integration/editors/__snapshots__/SMVSubscriberLaterBinding.test.snap.js b/test/integration/editors/__snapshots__/SMVSubscriberLaterBinding.test.snap.js index 755295aae8..08489396a6 100644 --- a/test/integration/editors/__snapshots__/SMVSubscriberLaterBinding.test.snap.js +++ b/test/integration/editors/__snapshots__/SMVSubscriberLaterBinding.test.snap.js @@ -1054,7 +1054,7 @@ snapshots["SMV Subscribe Later Binding plugin when selecting an FCDA element wit aria-disabled="false" noninteractive="" tabindex="-1" - value="MeasPoint.CT1 SMV_Subscriber2>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0] MeasPoint.CT1 SMV_Subscriber4>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0]" + value="MeasPoint.CT1 SMV_Subscriber2>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0] MeasPoint.CT1 SMV_Subscriber4>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0] MeasPoint.CT1 SMV_Subscriber5>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0] MeasPoint.CT1 SMV_Subscriber6>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0] MeasPoint.CT1 SMV_Subscriber7>>Overvoltage> PTRC 1>AmpSv;TCTR1/AmpSv/instMag.i[0]" > [subscription.subscriber.subscribed] @@ -1117,11 +1117,82 @@ snapshots["SMV Subscribe Later Binding plugin when selecting an FCDA element wit monitor_heart
+ + + AmpSv;TCTR1/AmpSv/instMag.i + (MeasPoint.CT1) + + + SMV_Subscriber5>>Overvoltage> PTRC 1 (SMV_Subscriber5>>SV_supervision> LSVS 1) + + + swap_horiz + + + monitor_heart + + + + + AmpSv;TCTR1/AmpSv/instMag.i + (MeasPoint.CT1) + + + SMV_Subscriber6>>Overvoltage> PTRC 1 (SMV_Subscriber6>>SV_supervision> LSVS 1) + + + swap_horiz + + + monitor_heart + + + + + AmpSv;TCTR1/AmpSv/instMag.i + (MeasPoint.CT1) + + + SMV_Subscriber7>>Overvoltage> PTRC 1 + + + swap_horiz + + [subscription.subscriber.availableToSubscribe] @@ -2082,6 +2153,690 @@ snapshots["SMV Subscribe Later Binding plugin when selecting an FCDA element wit arrow_back + + + AmpSv;TCTR1/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber5>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR2/AmpSv/instMag.i + (MeasPoint.CT2) + + + SMV_Subscriber5>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR2/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber5>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR3/AmpSv/instMag.i + (MeasPoint.CT3) + + + SMV_Subscriber5>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR3/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber5>>Overvoltage> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR1/VolSv/instMag.i + (MeasPoint.VT1) + + + SMV_Subscriber5>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR1/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber5>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR2/VolSv/instMag.i + (MeasPoint.VT2) + + + SMV_Subscriber5>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR2/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber5>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR3/VolSv/instMag.i + (MeasPoint.VT3) + + + SMV_Subscriber5>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR3/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber5>>Overcurrent> PTRC 1 + + + arrow_back + + + + + someRestrictedExtRef + (Restricted To AmpSV) + + + SMV_Subscriber5>>Overcurrent> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR1/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber6>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR2/AmpSv/instMag.i + (MeasPoint.CT2) + + + SMV_Subscriber6>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR2/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber6>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR3/AmpSv/instMag.i + (MeasPoint.CT3) + + + SMV_Subscriber6>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR3/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber6>>Overvoltage> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR1/VolSv/instMag.i + (MeasPoint.VT1) + + + SMV_Subscriber6>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR1/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber6>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR2/VolSv/instMag.i + (MeasPoint.VT2) + + + SMV_Subscriber6>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR2/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber6>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR3/VolSv/instMag.i + (MeasPoint.VT3) + + + SMV_Subscriber6>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR3/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber6>>Overcurrent> PTRC 1 + + + arrow_back + + + + + someRestrictedExtRef + (Restricted To AmpSV) + + + SMV_Subscriber6>>Overcurrent> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR1/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber7>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR2/AmpSv/instMag.i + (MeasPoint.CT2) + + + SMV_Subscriber7>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR2/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber7>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR3/AmpSv/instMag.i + (MeasPoint.CT3) + + + SMV_Subscriber7>>Overvoltage> PTRC 1 + + + arrow_back + + + + + AmpSv;TCTR3/AmpSv/q + (MeasPoint.CT1) + + + SMV_Subscriber7>>Overvoltage> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR1/VolSv/instMag.i + (MeasPoint.VT1) + + + SMV_Subscriber7>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR1/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber7>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR2/VolSv/instMag.i + (MeasPoint.VT2) + + + SMV_Subscriber7>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR2/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber7>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR3/VolSv/instMag.i + (MeasPoint.VT3) + + + SMV_Subscriber7>>Overcurrent> PTRC 1 + + + arrow_back + + + + + VolSv;TVTR3/VolSv/q + (MeasPoint.VT1) + + + SMV_Subscriber7>>Overcurrent> PTRC 1 + + + arrow_back + + + + + someRestrictedExtRef + (Restricted To AmpSV) + + + SMV_Subscriber7>>Overcurrent> PTRC 1 + + + arrow_back + + `; diff --git a/test/testfiles/editors/LaterBindingGOOSE-LGOS.scd b/test/testfiles/editors/LaterBindingGOOSE-LGOS.scd index 9a8eb75048..3c3f4fbd8a 100644 --- a/test/testfiles/editors/LaterBindingGOOSE-LGOS.scd +++ b/test/testfiles/editors/LaterBindingGOOSE-LGOS.scd @@ -206,6 +206,13 @@ + + + + The supervision instance to rule them all! + + + @@ -233,6 +240,93 @@ + + + + + + + + + + + + + + + + + + + The One True Original + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The One True Original + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -304,6 +398,7 @@ + diff --git a/test/testfiles/editors/LaterBindingSMV-LSVS.scd b/test/testfiles/editors/LaterBindingSMV-LSVS.scd index 399726b5cd..9d856d4a68 100644 --- a/test/testfiles/editors/LaterBindingSMV-LSVS.scd +++ b/test/testfiles/editors/LaterBindingSMV-LSVS.scd @@ -52,20 +52,26 @@
-

01-0C-CD-04-00-02

-

0003

+

01-0C-CD-04-00-02

+

0003

-

01-0C-CD-04-00-01

-

0002

+

01-0C-CD-04-00-01

+

0002

-

01-0C-CD-04-00-00

-

0001

+

01-0C-CD-04-00-00

+

0001

@@ -170,7 +176,13 @@ - + + + + SMV_PublisherCurrentTransformer/LLN0.currrentOnly + + + @@ -261,6 +273,156 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SMV_PublisherCurrentTransformer/LLN0.currrentOnly + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SMV_PublisherCurrentTransformer/LLN0.currrentOnly + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -577,6 +739,7 @@ + diff --git a/test/unit/editors/subscription/foundation.test.ts b/test/unit/editors/subscription/foundation.test.ts index dbc5edb863..5db4ba8f51 100644 --- a/test/unit/editors/subscription/foundation.test.ts +++ b/test/unit/editors/subscription/foundation.test.ts @@ -4,6 +4,7 @@ import { createExtRefElement, getExistingSupervision, getFirstSubscribedExtRef, + instantiatedSupervisionsCount, updateExtRefElement, } from '../../../../src/editors/subscription/foundation.js'; @@ -211,6 +212,23 @@ describe('foundation', () => { }); }); + describe('when using SCL Edition 2007B4 with later binding, SV and LSVS', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/LaterBindingSMV-LSVS.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + }); + + it('correctly identifies the existing number of supervision instances', () => { + const controlBlock = doc.querySelector( + 'IED[name="SMV_Publisher4"] SampledValueControl[name="voltageOnly"]' + )!; + const subscriberIED = doc.querySelector('IED[name="SMV_Subscriber2"]')!; + const count = instantiatedSupervisionsCount(subscriberIED, controlBlock); + expect(count).to.equal(1); + }); + }); + describe('when using SCL Edition 2007B4 with message binding and GOOSE', () => { beforeEach(async () => { doc = await fetch('/test/testfiles/editors/MessageBindingGOOSE2007B4.scd') @@ -249,4 +267,22 @@ describe('foundation', () => { expect(identity(supLN)).to.be.equal('IED1>>CircuitBreaker_CB1> LGOS 1'); }); }); + + describe('when using SCL Edition 2007B4 with later binding, GOOSE and LGOS', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/LaterBindingGOOSE-LGOS.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + }); + + it('correctly identifies the existing number of supervision instances', () => { + const controlBlock = doc.querySelector( + 'IED[name="GOOSE_Publisher2"] GSEControl[name="GOOSE1"]' + )!; + + const subscriberIED = doc.querySelector('IED[name="GOOSE_Subscriber3"]')!; + const count = instantiatedSupervisionsCount(subscriberIED, controlBlock); + expect(count).to.equal(0); + }); + }); }); diff --git a/test/unit/editors/subscription/supervision.test.ts b/test/unit/editors/subscription/supervision.test.ts index 81fa64a420..aa4bfc8909 100644 --- a/test/unit/editors/subscription/supervision.test.ts +++ b/test/unit/editors/subscription/supervision.test.ts @@ -14,9 +14,7 @@ describe('supervision', () => { describe('when subscribing to a SampledValueControl', () => { beforeEach(async () => { - doc = await fetch( - '/test/testfiles/editors/LaterBindingSMV-LSVS.scd' - ) + doc = await fetch('/test/testfiles/editors/LaterBindingSMV-LSVS.scd') .then(response => response.text()) .then(str => new DOMParser().parseFromString(str, 'application/xml')); }); @@ -35,27 +33,75 @@ describe('supervision', () => { expect(noSupervisionAllowedActions).to.have.length(0); }); - it('does not allow supervision because the DataTypeTemplate>DOType>DA is missing necessary attributes ', () => { + it('does not allow supervision because the DataTypeTemplate>DOType>DA is missing necessary attributes', () => { subscriberIED = doc.querySelector('IED[name="SMV_Subscriber4"]')!; const noSupervisionAllowedActions: Create[] = instantiateSubscriptionSupervision(controlElement!, subscriberIED); expect(noSupervisionAllowedActions).to.have.length(0); }); + it('does allow supervision because the first supervision node DOI/DAI[valImport=true/valKind=RO] allows it', () => { + subscriberIED = doc.querySelector('IED[name="SMV_Subscriber5"]')!; + const supervisionAllowedActions: Create[] = + instantiateSubscriptionSupervision(controlElement!, subscriberIED); + expect(supervisionAllowedActions).to.have.length(3); + }); + + it('does allow supervision because the first supervision node DOI/DAI[valImport=true/valKind=Conf] allows it', () => { + subscriberIED = doc.querySelector('IED[name="SMV_Subscriber6"]')!; + const supervisionAllowedActions: Create[] = + instantiateSubscriptionSupervision(controlElement!, subscriberIED); + expect(supervisionAllowedActions).to.have.length(3); + }); + + it('does allow supervision node use when first existing supervision node has empty Val element', () => { + subscriberIED = doc.querySelector('IED[name="SMV_Subscriber7"]')!; + const createActionswithEmptyVal: Create[] = + instantiateSubscriptionSupervision(controlElement!, subscriberIED); + expect(createActionswithEmptyVal).to.have.length(1); + + const newValAction: Create = createActionswithEmptyVal[0]; + const valParent: Node = newValAction.new.parent; + const newValElement: Node = (newValAction).new.element; + expect(valParent).to.exist; + expect(newValElement).to.exist; + expect(valParent.nodeName).to.be.equal('DAI'); + expect(newValElement.nodeName).to.be.equal('Val'); + expect(newValElement.textContent).to.not.be.empty; + }); + it('allows supervision and an existing LN[class=LSVS] should be used', () => { subscriberIED = doc.querySelector('IED[name="SMV_Subscriber2"]')!; const createActions: Create[] = instantiateSubscriptionSupervision( controlElement!, subscriberIED ); - expect(createActions).to.have.length(1); - const createAction: Create = createActions[0]; - const parentElement: Node = createAction.new.parent; - const newElement: Node = createAction.new.element; - expect(parentElement).to.exist; - expect(newElement).to.exist; - expect(parentElement.nodeName).to.be.equal('LN'); - expect(newElement.nodeName).to.be.equal('DOI'); + expect(createActions).to.have.length(3); + + const newDoiAction: Create = createActions[0]; + const lnParent: Node = newDoiAction.new.parent; + const newDoiElement: Node = (newDoiAction).new.element; + expect(lnParent).to.exist; + expect(newDoiElement).to.exist; + expect(lnParent.nodeName).to.be.equal('LN'); + expect(newDoiElement.nodeName).to.be.equal('DOI'); + + const newDaiAction: Create = createActions[1]; + const daiParent: Node = newDaiAction.new.parent; + const newDaiElement: Node = (newDaiAction).new.element; + expect(daiParent).to.exist; + expect(newDaiElement).to.exist; + expect(daiParent.nodeName).to.be.equal('DOI'); + expect(newDaiElement.nodeName).to.be.equal('DAI'); + + const newValAction: Create = createActions[2]; + const valParent: Node = newValAction.new.parent; + const newValElement: Node = (newValAction).new.element; + expect(valParent).to.exist; + expect(newValElement).to.exist; + expect(valParent.nodeName).to.be.equal('DAI'); + expect(newValElement.nodeName).to.be.equal('Val'); + expect(newValElement.textContent).to.not.be.empty; }); it('allows supervision and an a new LN[class=LSVS] should be created as well', () => { @@ -63,24 +109,44 @@ describe('supervision', () => { 'IED[name="SMV_Publisher3"] SampledValueControl[name="fullSmv"]' )!; subscriberIED = doc.querySelector('IED[name="SMV_Subscriber"]')!; - const createActionsWithNewLn: Create[] = - instantiateSubscriptionSupervision(controlElement, subscriberIED); - expect(createActionsWithNewLn).to.have.length(2); - const newLnAction: Create = createActionsWithNewLn[0]; + const actionsWithNewLn: Create[] = instantiateSubscriptionSupervision( + controlElement, + subscriberIED + ); + expect(actionsWithNewLn).to.have.length(4); + + const newLnAction: Create = actionsWithNewLn[0]; const lDeviceParent: Node = newLnAction.new.parent; - const newLnElement: Node = newLnAction.new.element; + const newLnElement: Node = (newLnAction).new.element; expect(lDeviceParent).to.exist; expect(newLnElement).to.exist; expect(lDeviceParent.nodeName).to.be.equal('LDevice'); expect(newLnElement.nodeName).to.be.equal('LN'); - const newDoiAction: Create = createActionsWithNewLn[1]; + const newDoiAction: Create = actionsWithNewLn[1]; const lnParent: Node = newDoiAction.new.parent; - const newDoiElement: Node = newDoiAction.new.element; + const newDoiElement: Node = (newDoiAction).new.element; expect(lnParent).to.exist; expect(newDoiElement).to.exist; expect(lnParent.nodeName).to.be.equal('LN'); expect(newDoiElement.nodeName).to.be.equal('DOI'); + + const newDaiAction: Create = actionsWithNewLn[2]; + const daiParent: Node = newDaiAction.new.parent; + const newDaiElement: Node = (newDaiAction).new.element; + expect(daiParent).to.exist; + expect(newDaiElement).to.exist; + expect(daiParent.nodeName).to.be.equal('DOI'); + expect(newDaiElement.nodeName).to.be.equal('DAI'); + + const newValAction: Create = actionsWithNewLn[3]; + const valParent: Node = newValAction.new.parent; + const newValElement: Node = (newValAction).new.element; + expect(valParent).to.exist; + expect(newValElement).to.exist; + expect(valParent.nodeName).to.be.equal('DAI'); + expect(newValElement.nodeName).to.be.equal('Val'); + expect(newValElement.textContent).to.not.be.empty; }); }); @@ -125,9 +191,7 @@ describe('supervision', () => { describe('when subscribing to a GSEControl', () => { beforeEach(async () => { - doc = await fetch( - '/test/testfiles/editors/LaterBindingGOOSE-LGOS.scd' - ) + doc = await fetch('/test/testfiles/editors/LaterBindingGOOSE-LGOS.scd') .then(response => response.text()) .then(str => new DOMParser().parseFromString(str, 'application/xml')); }); @@ -138,57 +202,129 @@ describe('supervision', () => { 'IED[name="GOOSE_Publisher2"] GSEControl[name="GOOSE1"]' )!; }); + it('does not allow supervision because there is no services defined in the IED', () => { subscriberIED = doc.querySelector('IED[name="GOOSE_Subscriber"]')!; const noSupervisionAllowedActions: Create[] = instantiateSubscriptionSupervision(controlElement!, subscriberIED); expect(noSupervisionAllowedActions).to.have.length(0); }); + it('does not allow supervision because the DataTypeTemplate>DOType>DA is missing necessary attributes ', () => { subscriberIED = doc.querySelector('IED[name="GOOSE_Subscriber4"]')!; const noSupervisionAllowedActions: Create[] = instantiateSubscriptionSupervision(controlElement!, subscriberIED); expect(noSupervisionAllowedActions).to.have.length(0); }); + + it('does allow supervision because the first supervision node DOI/DAI[valImport=true/valKind=RO] allows it', () => { + subscriberIED = doc.querySelector('IED[name="GOOSE_Subscriber5"]')!; + const supervisionAllowedActions: Create[] = + instantiateSubscriptionSupervision(controlElement!, subscriberIED); + expect(supervisionAllowedActions).to.have.length(3); + }); + + it('does allow supervision because the first supervision node DOI/DAI[valImport=true/valKind=Conf] allows it', () => { + subscriberIED = doc.querySelector('IED[name="GOOSE_Subscriber6"]')!; + const supervisionAllowedActions: Create[] = + instantiateSubscriptionSupervision(controlElement!, subscriberIED); + expect(supervisionAllowedActions).to.have.length(3); + }); + + it('does allow supervision node use when first existing supervision node has empty Val element', () => { + subscriberIED = doc.querySelector('IED[name="GOOSE_Subscriber7"]')!; + const createActionswithEmptyVal: Create[] = + instantiateSubscriptionSupervision(controlElement!, subscriberIED); + expect(createActionswithEmptyVal).to.have.length(1); + + const newValAction: Create = createActionswithEmptyVal[0]; + const valParent: Node = (newValAction).new.parent; + const newValElement: Node = (newValAction).new.element; + expect(valParent).to.exist; + expect(newValElement).to.exist; + expect(valParent.nodeName).to.be.equal('DAI'); + expect(newValElement.nodeName).to.be.equal('Val'); + expect(newValElement.textContent).to.not.be.empty; + }); + it('allows supervision and an existing LN[class=LGOS] should be used', () => { subscriberIED = doc.querySelector('IED[name="GOOSE_Subscriber2"]')!; const createActions: Create[] = instantiateSubscriptionSupervision( controlElement!, subscriberIED ); - expect(createActions).to.have.length(1); - const createAction: Create = createActions[0]; - const parentElement: Node = createAction.new.parent; - const newElement: Node = createAction.new.element; - expect(parentElement).to.exist; - expect(newElement).to.exist; - expect(parentElement.nodeName).to.be.equal('LN'); - expect(newElement.nodeName).to.be.equal('DOI'); + expect(createActions).to.have.length(3); + + const newDoiAction: Create = createActions[0]; + const lnParent: Node = newDoiAction.new.parent; + const newDoiElement: Node = (newDoiAction).new.element; + expect(lnParent).to.exist; + expect(newDoiElement).to.exist; + expect(lnParent.nodeName).to.be.equal('LN'); + expect(newDoiElement.nodeName).to.be.equal('DOI'); + + const newDaiAction: Create = createActions[1]; + const daiParent: Node = newDaiAction.new.parent; + const newDaiElement: Node = (newDaiAction).new.element; + expect(daiParent).to.exist; + expect(newDaiElement).to.exist; + expect(daiParent.nodeName).to.be.equal('DOI'); + expect(newDaiElement.nodeName).to.be.equal('DAI'); + + const newValAction: Create = createActions[2]; + const valParent: Node = newValAction.new.parent; + const newValElement: Node = (newValAction).new.element; + expect(valParent).to.exist; + expect(newValElement).to.exist; + expect(valParent.nodeName).to.be.equal('DAI'); + expect(newValElement.nodeName).to.be.equal('Val'); + expect(newValElement.textContent).to.not.be.empty; }); + it('allows supervision and an a new LN[class=LGOS] should be created as well', () => { controlElement = doc.querySelector( 'IED[name="GOOSE_Publisher2"] GSEControl[name="GOOSE1"]' )!; subscriberIED = doc.querySelector('IED[name="GOOSE_Subscriber1"]')!; - const createActionsWithNewLn: Create[] = - instantiateSubscriptionSupervision(controlElement, subscriberIED); - expect(createActionsWithNewLn).to.have.length(2); - const newLnAction: Create = createActionsWithNewLn[0]; + const actionsWithNewLN: Create[] = instantiateSubscriptionSupervision( + controlElement, + subscriberIED + ); + expect(actionsWithNewLN).to.have.length(4); + + const newLnAction: Create = actionsWithNewLN[0]; const lDeviceParent: Node = newLnAction.new.parent; - const newLnElement: Node = newLnAction.new.element; + const newLnElement: Node = (newLnAction).new.element; expect(lDeviceParent).to.exist; expect(newLnElement).to.exist; expect(lDeviceParent.nodeName).to.be.equal('LDevice'); expect(newLnElement.nodeName).to.be.equal('LN'); - const newDoiAction: Create = createActionsWithNewLn[1]; + const newDoiAction: Create = actionsWithNewLN[1]; const lnParent: Node = newDoiAction.new.parent; - const newDoiElement: Node = newDoiAction.new.element; + const newDoiElement: Node = (newDoiAction).new.element; expect(lnParent).to.exist; expect(newDoiElement).to.exist; expect(lnParent.nodeName).to.be.equal('LN'); expect(newDoiElement.nodeName).to.be.equal('DOI'); + + const newDaiAction: Create = actionsWithNewLN[2]; + const daiParent: Node = newDaiAction.new.parent; + const newDaiElement: Node = (newDaiAction).new.element; + expect(daiParent).to.exist; + expect(newDaiElement).to.exist; + expect(daiParent.nodeName).to.be.equal('DOI'); + expect(newDaiElement.nodeName).to.be.equal('DAI'); + + const newValAction: Create = actionsWithNewLN[3]; + const valParent: Node = newValAction.new.parent; + const newValElement: Node = (newValAction).new.element; + expect(valParent).to.exist; + expect(newValElement).to.exist; + expect(valParent.nodeName).to.be.equal('DAI'); + expect(newValElement.nodeName).to.be.equal('Val'); + expect(newValElement.textContent).to.not.be.empty; }); }); From dc8a56da66fb553f9bf6fd6ae687952efe9a5728 Mon Sep 17 00:00:00 2001 From: Jakob Vogelsang Date: Wed, 8 Mar 2023 15:28:11 +0100 Subject: [PATCH 11/27] chore(release): 0.30.0 --- CHANGELOG.md | 16 ++++++++++++++++ manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d60c0ccc..1587fe971f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [0.30.0](https://github.com/openscd/open-scd/compare/v0.29.0...v0.30.0) (2023-03-08) + + +### Features + +* **editors/ied:** show all instantiated setting group values ([#1155](https://github.com/openscd/open-scd/issues/1155)) ([d9680fa](https://github.com/openscd/open-scd/commits/d9680fa1195650aeef6a5ec0290ec6cc321303ef)) +* **editors/subscription:** Support valKind and valImport on first instances, improve instance use counting and allow Val updates ([#1169](https://github.com/openscd/open-scd/issues/1169)) ([aaab451](https://github.com/openscd/open-scd/commits/aaab451ca01bcd29b1bb2dc73299a0b171b30389)), closes [#1161](https://github.com/openscd/open-scd/issues/1161) [#1162](https://github.com/openscd/open-scd/issues/1162) +* **wizards/services:** add read-only wizard on access point and ied ([#1109](https://github.com/openscd/open-scd/issues/1109)) ([81088f0](https://github.com/openscd/open-scd/commits/81088f06bbae8ca022525fe29d59589ba87647a9)) + + +### Bug Fixes + +* added translation key for phase ([#1186](https://github.com/openscd/open-scd/issues/1186)) ([479c499](https://github.com/openscd/open-scd/commits/479c49991a2f4e3e0b70ddd39e90deda3ec935ec)) +* **editors/substation/guess-wizard:** make sure guessed content is added to the substation ([#1148](https://github.com/openscd/open-scd/issues/1148)) ([cc0051f](https://github.com/openscd/open-scd/commits/cc0051f54a89984af9676ed2209a0481f49fa7a2)) +* **menu/save-project:** Add missing XML prolog on document save ([#1173](https://github.com/openscd/open-scd/issues/1173)) ([6cae0da](https://github.com/openscd/open-scd/commits/6cae0da557ef69029312a854e94bc5ecc9558909)), closes [#1163](https://github.com/openscd/open-scd/issues/1163) + ## [0.29.0](https://github.com/openscd/open-scd/compare/v0.28.0...v0.29.0) (2023-02-08) diff --git a/manifest.json b/manifest.json index 9b2a194a7b..6da6d59798 100644 --- a/manifest.json +++ b/manifest.json @@ -40,5 +40,5 @@ "purpose": "maskable" } ], - "version": "0.29.0" + "version": "0.30.0" } diff --git a/package-lock.json b/package-lock.json index 67b073702d..8f16b957ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-scd", - "version": "0.29.0", + "version": "0.30.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "open-scd", - "version": "0.29.0", + "version": "0.30.0", "license": "Apache-2.0", "dependencies": { "@material/mwc-circular-progress-four-color": "0.22.1", diff --git a/package.json b/package.json index 8479f6c781..e40ebd12ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-scd", - "version": "0.29.0", + "version": "0.30.0", "repository": "https://github.com/openscd/open-scd.git", "description": "A bottom-up substation configuration designer for projects described using SCL `IEC 61850-6` Edition 2 or greater.", "keywords": [ From 5bded3287bc4d448e11f12c880229f292ee47995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Pupier?= Date: Mon, 13 Mar 2023 09:44:08 +0100 Subject: [PATCH 12/27] chore(update-actions): Update Node from 14.x to 18.x (#1192) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: Update Node from 14.x to 18.x - updated GitHub Actions - provide information in readme on the version to use fixes #918 Signed-off-by: Aurélien Pupier * fix: explicitly depend on source-map Adds a "source-map" dev dependency to package.json to fix the version to one where https://github.com/mozilla/source-map/issues/432 is not an issue. --------- Signed-off-by: Aurélien Pupier Co-authored-by: Christian Dinkel Co-authored-by: Sander Jansen --- .github/workflows/build-and-deploy.yml | 4 +- .github/workflows/test-and-build.yml | 4 +- .github/workflows/test.yml | 4 +- README.md | 2 +- package-lock.json | 1668 +++++++++++++++--------- package.json | 1 + 6 files changed, 1070 insertions(+), 613 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 547e59b874..f9cc00dfb3 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -11,10 +11,10 @@ jobs: - name: Checkout uses: actions/checkout@v2.3.1 - - name: Use Node.js 14.x + - name: Use Node.js 18.x uses: actions/setup-node@v1 with: - node-version: '14.x' + node-version: '18.x' - name: Install and Build run: | diff --git a/.github/workflows/test-and-build.yml b/.github/workflows/test-and-build.yml index d1c3793244..ea167dea88 100644 --- a/.github/workflows/test-and-build.yml +++ b/.github/workflows/test-and-build.yml @@ -8,10 +8,10 @@ jobs: - name: Checkout uses: actions/checkout@v2.3.1 - - name: Use Node.js 14.x + - name: Use Node.js 18.x uses: actions/setup-node@v1 with: - node-version: '14.x' + node-version: '18.x' - name: Install and Build run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index df2e4a1cb0..60bbe40e4f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,10 +11,10 @@ jobs: - name: Checkout uses: actions/checkout@v2.3.1 - - name: Use Node.js 14.x + - name: Use Node.js 18.x uses: actions/setup-node@v1 with: - node-version: '14.x' + node-version: '18.x' - name: Install and Test run: | diff --git a/README.md b/README.md index 0b947e29be..4f1d75bef8 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ If you say "hi" there we will be more than happy to help you find your way aroun ### Building -If you want to build OpenSCD yourself in order to make some changes to your local installation or to contribute to the project, you may first want to install [Node.js](https://nodejs.org/) in order to be able to use our local development setup. +If you want to build OpenSCD yourself in order to make some changes to your local installation or to contribute to the project, you may first want to install [Node.js](https://nodejs.org/) in order to be able to use our local development setup. It is recommended to use Node.js 18.x. it is is the version used to build and test on GitHub Actions. Once Node.js is installed on your system, you may get started by entering the following lines in your command prompt: diff --git a/package-lock.json b/package-lock.json index 8f16b957ae..e1e2599d83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,6 +61,7 @@ "prettier": "^2.3.2", "sinon": "^11.1.2", "snowpack": "3.8.6", + "source-map": "^0.7.4", "standard-version": "^9.3.1", "tslib": "^2.3.1", "typedoc": "^0.21.10", @@ -71,9 +72,9 @@ } }, "node_modules/@apideck/better-ajv-errors": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.1.tgz", - "integrity": "sha512-6RMV31esAxqlDIvVCG/CJxY/s8dFNVOI5w8RWIfDMhjg/iwqnawko9tJXau/leqC4+T1Bu8et99QVWCwU5wk+g==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", "dev": true, "dependencies": { "json-schema": "^0.4.0", @@ -139,13 +140,10 @@ } }, "node_modules/@babel/core/node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "json5": "lib/cli.js" }, @@ -2241,6 +2239,64 @@ "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "dev": true }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, "node_modules/@koa/cors": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-3.1.0.tgz", @@ -3606,9 +3662,9 @@ } }, "node_modules/@rollup/plugin-babel": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", - "integrity": "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.10.4", @@ -3810,13 +3866,10 @@ } }, "node_modules/@surma/rollup-plugin-off-main-thread/node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, "bin": { "json5": "lib/cli.js" }, @@ -4336,9 +4389,9 @@ } }, "node_modules/@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", "dev": true }, "node_modules/@types/whatwg-url": { @@ -4805,15 +4858,6 @@ "node": ">=12.0.0" } }, - "node_modules/@web/test-runner-core/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/@web/test-runner-coverage-v8": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/@web/test-runner-coverage-v8/-/test-runner-coverage-v8-0.4.8.tgz", @@ -4869,15 +4913,6 @@ "node": ">=0.3.1" } }, - "node_modules/@web/test-runner/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/@webcomponents/shadycss": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.11.0.tgz", @@ -5350,6 +5385,18 @@ "node": ">= 4.0.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -6144,6 +6191,15 @@ "node": ">= 4.0" } }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -7433,15 +7489,19 @@ } }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dev": true, "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/delayed-stream": { @@ -7904,31 +7964,44 @@ "dev": true }, "node_modules/es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", "dev": true, "dependencies": { + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" }, "engines": { "node": ">= 0.4" @@ -8149,6 +8222,20 @@ "integrity": "sha512-0LTiSQoPWwdcaTVIQXhGlaDwTneD0g9/tnH1PNs3zHFFH+xoCeJclDM3rQeqF9nurXPfMKm3l9+kfPRa5VpbKg==", "dev": true }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -9085,9 +9172,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -9193,6 +9280,15 @@ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", "dev": true }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -9295,12 +9391,39 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -9401,14 +9524,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9676,6 +9799,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", @@ -9705,10 +9843,22 @@ "node": ">= 4" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", - "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "dependencies": { "@sindresorhus/is": "^4.0.0", @@ -9766,6 +9916,15 @@ "uglify-js": "^3.1.4" } }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -9833,9 +9992,9 @@ } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9850,10 +10009,34 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" @@ -9973,9 +10156,9 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "node_modules/http-errors": { @@ -10143,9 +10326,9 @@ } }, "node_modules/idb": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz", - "integrity": "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", "dev": true }, "node_modules/ieee754": { @@ -10300,12 +10483,12 @@ } }, "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" }, @@ -10334,6 +10517,20 @@ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -10404,9 +10601,9 @@ } }, "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" @@ -10573,9 +10770,9 @@ "dev": true }, "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, "engines": { "node": ">= 0.4" @@ -10603,9 +10800,9 @@ } }, "node_modules/is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" @@ -10694,10 +10891,13 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10756,6 +10956,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -10793,12 +11012,12 @@ } }, "node_modules/is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10907,9 +11126,9 @@ } }, "node_modules/jake/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "node_modules/jest-worker": { @@ -11027,9 +11246,9 @@ "dev": true }, "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -11066,9 +11285,9 @@ ] }, "node_modules/jsonpointer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.0.tgz", - "integrity": "sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -11514,9 +11733,9 @@ } }, "node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -12276,6 +12495,18 @@ "node": "*" } }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/mocha/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -12895,9 +13126,9 @@ } }, "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12913,14 +13144,14 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -14152,9 +14383,9 @@ } }, "node_modules/qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -14433,13 +14664,14 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -14570,9 +14802,9 @@ } }, "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true, "engines": { "node": ">=0.6" @@ -14796,6 +15028,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", @@ -14808,12 +15041,10 @@ } }, "node_modules/rollup-plugin-terser/node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, - "optional": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -14827,23 +15058,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/rollup-plugin-terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/rollup-plugin-terser/node_modules/terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz", + "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==", "dev": true, "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "bin": { @@ -14851,14 +15074,6 @@ }, "engines": { "node": ">=10" - }, - "peerDependencies": { - "acorn": "^8.5.0" - }, - "peerDependenciesMeta": { - "acorn": { - "optional": true - } } }, "node_modules/run-async": { @@ -14931,6 +15146,20 @@ } ] }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -15386,15 +15615,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/snowpack/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/socks": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", @@ -15424,12 +15644,12 @@ } }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, "node_modules/source-map-js": { @@ -15451,12 +15671,14 @@ "source-map": "^0.6.0" } }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/sourcemap-codec": { "version": "1.4.8", @@ -15785,18 +16007,18 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.3", "side-channel": "^1.0.4" }, "funding": { @@ -15804,26 +16026,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16094,9 +16318,9 @@ } }, "node_modules/terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", "dev": true, "dependencies": { "commander": "^2.20.0", @@ -16116,6 +16340,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", @@ -16410,6 +16643,20 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -16494,9 +16741,9 @@ } }, "node_modules/ua-parser-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.2.tgz", - "integrity": "sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg==", + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.34.tgz", + "integrity": "sha512-K9mwJm/DaB6mRLZfw6q8IMXipcrmuT6yfhYmwhAkuh+81sChuYstYA+znlgaflUPaYUa3odxKPKGw6Vw/lANew==", "dev": true, "funding": [ { @@ -16526,14 +16773,14 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -16811,15 +17058,6 @@ "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/valid-url": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", @@ -16875,9 +17113,9 @@ "dev": true }, "node_modules/vm2": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.9.tgz", - "integrity": "sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==", + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", + "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", "dev": true, "dependencies": { "acorn": "^8.7.0", @@ -17094,6 +17332,26 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wicg-inert": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.1.tgz", @@ -17201,28 +17459,28 @@ } }, "node_modules/workbox-background-sync": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.4.2.tgz", - "integrity": "sha512-P7c8uG5X2k+DMICH9xeSA9eUlCOjHHYoB42Rq+RtUpuwBxUOflAXR1zdsMWj81LopE4gjKXlTw7BFd1BDAHo7g==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", + "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", "dev": true, "dependencies": { - "idb": "^6.1.4", - "workbox-core": "6.4.2" + "idb": "^7.0.1", + "workbox-core": "6.5.4" } }, "node_modules/workbox-broadcast-update": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.4.2.tgz", - "integrity": "sha512-qnBwQyE0+PWFFc/n4ISXINE49m44gbEreJUYt2ldGH3+CNrLmJ1egJOOyUqqu9R4Eb7QrXcmB34ClXG7S37LbA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", + "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", "dev": true, "dependencies": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "node_modules/workbox-build": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.4.2.tgz", - "integrity": "sha512-WMdYLhDIsuzViOTXDH+tJ1GijkFp5khSYolnxR/11zmfhNDtuo7jof72xPGFy+KRpsz6tug39RhivCj77qqO0w==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", + "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", "dev": true, "dependencies": { "@apideck/better-ajv-errors": "^0.3.1", @@ -17243,26 +17501,25 @@ "rollup": "^2.43.1", "rollup-plugin-terser": "^7.0.0", "source-map": "^0.8.0-beta.0", - "source-map-url": "^0.4.0", "stringify-object": "^3.3.0", "strip-comments": "^2.0.1", "tempy": "^0.6.0", "upath": "^1.2.0", - "workbox-background-sync": "6.4.2", - "workbox-broadcast-update": "6.4.2", - "workbox-cacheable-response": "6.4.2", - "workbox-core": "6.4.2", - "workbox-expiration": "6.4.2", - "workbox-google-analytics": "6.4.2", - "workbox-navigation-preload": "6.4.2", - "workbox-precaching": "6.4.2", - "workbox-range-requests": "6.4.2", - "workbox-recipes": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2", - "workbox-streams": "6.4.2", - "workbox-sw": "6.4.2", - "workbox-window": "6.4.2" + "workbox-background-sync": "6.5.4", + "workbox-broadcast-update": "6.5.4", + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-google-analytics": "6.5.4", + "workbox-navigation-preload": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-range-requests": "6.5.4", + "workbox-recipes": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4", + "workbox-streams": "6.5.4", + "workbox-sw": "6.5.4", + "workbox-window": "6.5.4" }, "engines": { "node": ">=10.0.0" @@ -17298,7 +17555,7 @@ "node_modules/workbox-build/node_modules/tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, "dependencies": { "punycode": "^2.1.0" @@ -17322,18 +17579,18 @@ } }, "node_modules/workbox-cacheable-response": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.4.2.tgz", - "integrity": "sha512-9FE1W/cKffk1AJzImxgEN0ceWpyz1tqNjZVtA3/LAvYL3AC5SbIkhc7ZCO82WmO9IjTfu8Vut2X/C7ViMSF7TA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", + "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", "dev": true, "dependencies": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "node_modules/workbox-cli": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-cli/-/workbox-cli-6.4.2.tgz", - "integrity": "sha512-RvKGG4WHt+9pFjYYujvGd0XVJCNALRoOYeVMotvK79m/Fwn+Ex3uNb9KY7vV8JSgUb1JlZzH6/jSiGbtCd3MOQ==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cli/-/workbox-cli-6.5.4.tgz", + "integrity": "sha512-+Cc0jYh25MofhCROZqfQkpYSAGvykyrUVekuuPaLFbJ8qxX/zzX8hRRpglfwxDwokAjz8S20oEph4s+MyQc+Yw==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -17348,7 +17605,7 @@ "stringify-object": "^3.3.0", "upath": "^1.2.0", "update-notifier": "^4.1.0", - "workbox-build": "6.4.2" + "workbox-build": "6.5.4" }, "bin": { "workbox": "build/bin.js" @@ -17450,118 +17707,118 @@ } }, "node_modules/workbox-core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.4.2.tgz", - "integrity": "sha512-1U6cdEYPcajRXiboSlpJx6U7TvhIKbxRRerfepAJu2hniKwJ3DHILjpU/zx3yvzSBCWcNJDoFalf7Vgd7ey/rw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", + "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==", "dev": true }, "node_modules/workbox-expiration": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.4.2.tgz", - "integrity": "sha512-0hbpBj0tDnW+DZOUmwZqntB/8xrXOgO34i7s00Si/VlFJvvpRKg1leXdHHU8ykoSBd6+F2KDcMP3swoCi5guLw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", + "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", "dev": true, "dependencies": { - "idb": "^6.1.4", - "workbox-core": "6.4.2" + "idb": "^7.0.1", + "workbox-core": "6.5.4" } }, "node_modules/workbox-google-analytics": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.4.2.tgz", - "integrity": "sha512-u+gxs3jXovPb1oul4CTBOb+T9fS1oZG+ZE6AzS7l40vnyfJV79DaLBvlpEZfXGv3CjMdV1sT/ltdOrKzo7HcGw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", + "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", "dev": true, "dependencies": { - "workbox-background-sync": "6.4.2", - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" + "workbox-background-sync": "6.5.4", + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" } }, "node_modules/workbox-navigation-preload": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.4.2.tgz", - "integrity": "sha512-viyejlCtlKsbJCBHwhSBbWc57MwPXvUrc8P7d+87AxBGPU+JuWkT6nvBANgVgFz6FUhCvRC8aYt+B1helo166g==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", + "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", "dev": true, "dependencies": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "node_modules/workbox-precaching": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.4.2.tgz", - "integrity": "sha512-CZ6uwFN/2wb4noHVlALL7UqPFbLfez/9S2GAzGAb0Sk876ul9ukRKPJJ6gtsxfE2HSTwqwuyNVa6xWyeyJ1XSA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", + "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", "dev": true, "dependencies": { - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" } }, "node_modules/workbox-range-requests": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.4.2.tgz", - "integrity": "sha512-SowF3z69hr3Po/w7+xarWfzxJX/3Fo0uSG72Zg4g5FWWnHpq2zPvgbWerBZIa81zpJVUdYpMa3akJJsv+LaO1Q==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", + "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", "dev": true, "dependencies": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "node_modules/workbox-recipes": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.4.2.tgz", - "integrity": "sha512-/oVxlZFpAjFVbY+3PoGEXe8qyvtmqMrTdWhbOfbwokNFtUZ/JCtanDKgwDv9x3AebqGAoJRvQNSru0F4nG+gWA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", + "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", "dev": true, "dependencies": { - "workbox-cacheable-response": "6.4.2", - "workbox-core": "6.4.2", - "workbox-expiration": "6.4.2", - "workbox-precaching": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" } }, "node_modules/workbox-routing": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.4.2.tgz", - "integrity": "sha512-0ss/n9PAcHjTy4Ad7l2puuod4WtsnRYu9BrmHcu6Dk4PgWeJo1t5VnGufPxNtcuyPGQ3OdnMdlmhMJ57sSrrSw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", + "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", "dev": true, "dependencies": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "node_modules/workbox-strategies": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.4.2.tgz", - "integrity": "sha512-YXh9E9dZGEO1EiPC3jPe2CbztO5WT8Ruj8wiYZM56XqEJp5YlGTtqRjghV+JovWOqkWdR+amJpV31KPWQUvn1Q==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", + "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", "dev": true, "dependencies": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "node_modules/workbox-streams": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.4.2.tgz", - "integrity": "sha512-ROEGlZHGVEgpa5bOZefiJEVsi5PsFjJG9Xd+wnDbApsCO9xq9rYFopF+IRq9tChyYzhBnyk2hJxbQVWphz3sog==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", + "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", "dev": true, "dependencies": { - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2" + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4" } }, "node_modules/workbox-sw": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.4.2.tgz", - "integrity": "sha512-A2qdu9TLktfIM5NE/8+yYwfWu+JgDaCkbo5ikrky2c7r9v2X6DcJ+zSLphNHHLwM/0eVk5XVf1mC5HGhYpMhhg==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", + "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==", "dev": true }, "node_modules/workbox-window": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.4.2.tgz", - "integrity": "sha512-KVyRKmrJg7iB+uym/B/CnEUEFG9CvnTU1Bq5xpXHbtgD9l+ShDekSl1wYpqw/O0JfeeQVOFb8CiNfvnwWwqnWQ==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", + "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", "dev": true, "dependencies": { "@types/trusted-types": "^2.0.2", - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "node_modules/wrap-ansi": { @@ -17937,9 +18194,9 @@ }, "dependencies": { "@apideck/better-ajv-errors": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.1.tgz", - "integrity": "sha512-6RMV31esAxqlDIvVCG/CJxY/s8dFNVOI5w8RWIfDMhjg/iwqnawko9tJXau/leqC4+T1Bu8et99QVWCwU5wk+g==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", "dev": true, "requires": { "json-schema": "^0.4.0", @@ -17986,13 +18243,10 @@ }, "dependencies": { "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true }, "semver": { "version": "6.3.0", @@ -19487,6 +19741,55 @@ "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "dev": true }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, "@koa/cors": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-3.1.0.tgz", @@ -20809,9 +21112,9 @@ } }, "@rollup/plugin-babel": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", - "integrity": "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.10.4", @@ -20968,13 +21271,10 @@ }, "dependencies": { "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true } } }, @@ -21483,9 +21783,9 @@ } }, "@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", "dev": true }, "@types/whatwg-url": { @@ -21780,12 +22080,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true } } }, @@ -21843,14 +22137,6 @@ "open": "^8.0.2", "picomatch": "^2.2.2", "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } } }, "@web/test-runner-coverage-v8": { @@ -22265,6 +22551,12 @@ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -22867,6 +23159,14 @@ "dev": true, "requires": { "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "clean-stack": { @@ -23866,12 +24166,13 @@ "dev": true }, "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "delayed-stream": { @@ -24247,31 +24548,44 @@ "dev": true }, "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", "dev": true, "requires": { + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" } }, "es-dev-server": { @@ -24464,6 +24778,17 @@ "integrity": "sha512-0LTiSQoPWwdcaTVIQXhGlaDwTneD0g9/tnH1PNs3zHFFH+xoCeJclDM3rQeqF9nurXPfMKm3l9+kfPRa5VpbKg==", "dev": true }, + "es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -25197,9 +25522,9 @@ } }, "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -25282,6 +25607,15 @@ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", "dev": true }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -25359,12 +25693,30 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -25446,14 +25798,14 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "get-own-enumerable-property-symbols": { @@ -25661,6 +26013,15 @@ } } }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" + } + }, "globby": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", @@ -25683,10 +26044,19 @@ } } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "got": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", - "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "requires": { "@sindresorhus/is": "^4.0.0", @@ -25725,6 +26095,14 @@ "source-map": "^0.6.1", "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "har-schema": { @@ -25779,9 +26157,9 @@ } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true }, "has-flag": { @@ -25790,10 +26168,25 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true + }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, "has-tostringtag": { @@ -25876,9 +26269,9 @@ } }, "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "http-errors": { @@ -26002,9 +26395,9 @@ "requires": {} }, "idb": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz", - "integrity": "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", "dev": true }, "ieee754": { @@ -26120,12 +26513,12 @@ } }, "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "requires": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" } @@ -26148,6 +26541,17 @@ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, + "is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -26189,9 +26593,9 @@ "dev": true }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true }, "is-ci": { @@ -26303,9 +26707,9 @@ "dev": true }, "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, "is-npm": { @@ -26321,9 +26725,9 @@ "dev": true }, "is-number-object": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", - "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "requires": { "has-tostringtag": "^1.0.0" @@ -26385,10 +26789,13 @@ "dev": true }, "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } }, "is-stream": { "version": "2.0.1", @@ -26423,6 +26830,19 @@ "text-extensions": "^1.0.0" } }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -26454,12 +26874,12 @@ } }, "is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "requires": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" } }, "is-wsl": { @@ -26541,9 +26961,9 @@ }, "dependencies": { "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true } } @@ -26648,9 +27068,9 @@ "dev": true }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -26679,9 +27099,9 @@ "dev": true }, "jsonpointer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.0.tgz", - "integrity": "sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", "dev": true }, "jsonschema": { @@ -27054,9 +27474,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -27662,6 +28082,17 @@ "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "has-flag": { @@ -28158,9 +28589,9 @@ "dev": true }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, "object-keys": { @@ -28170,14 +28601,14 @@ "dev": true }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, @@ -29120,9 +29551,9 @@ "dev": true }, "qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -29339,13 +29770,14 @@ } }, "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, "regexpp": { @@ -29444,9 +29876,9 @@ }, "dependencies": { "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true } } @@ -29631,12 +30063,10 @@ }, "dependencies": { "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true, - "optional": true, - "peer": true + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true }, "commander": { "version": "2.20.3", @@ -29644,20 +30074,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, "terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz", + "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==", "dev": true, "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" } } @@ -29701,6 +30126,17 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -30045,12 +30481,6 @@ "optional": true } } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true } } }, @@ -30076,9 +30506,9 @@ } }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true }, "source-map-js": { @@ -30095,14 +30525,16 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "dev": true - }, "sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", @@ -30363,39 +30795,41 @@ } }, "string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.3", "side-channel": "^1.0.4" } }, "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "stringify-object": { @@ -30599,9 +31033,9 @@ "dev": true }, "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", "dev": true, "requires": { "commander": "^2.20.0", @@ -30614,6 +31048,12 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -30841,6 +31281,17 @@ "mime-types": "~2.1.24" } }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -30900,9 +31351,9 @@ "dev": true }, "ua-parser-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.2.tgz", - "integrity": "sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg==", + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.34.tgz", + "integrity": "sha512-K9mwJm/DaB6mRLZfw6q8IMXipcrmuT6yfhYmwhAkuh+81sChuYstYA+znlgaflUPaYUa3odxKPKGw6Vw/lANew==", "dev": true }, "uglify-js": { @@ -30913,14 +31364,14 @@ "optional": true }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, @@ -31148,14 +31599,6 @@ "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^1.6.0", "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } } }, "valid-url": { @@ -31209,9 +31652,9 @@ } }, "vm2": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.9.tgz", - "integrity": "sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==", + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", + "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", "dev": true, "requires": { "acorn": "^8.7.0", @@ -31385,6 +31828,20 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, "wicg-inert": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.1.tgz", @@ -31472,28 +31929,28 @@ } }, "workbox-background-sync": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.4.2.tgz", - "integrity": "sha512-P7c8uG5X2k+DMICH9xeSA9eUlCOjHHYoB42Rq+RtUpuwBxUOflAXR1zdsMWj81LopE4gjKXlTw7BFd1BDAHo7g==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", + "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", "dev": true, "requires": { - "idb": "^6.1.4", - "workbox-core": "6.4.2" + "idb": "^7.0.1", + "workbox-core": "6.5.4" } }, "workbox-broadcast-update": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.4.2.tgz", - "integrity": "sha512-qnBwQyE0+PWFFc/n4ISXINE49m44gbEreJUYt2ldGH3+CNrLmJ1egJOOyUqqu9R4Eb7QrXcmB34ClXG7S37LbA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", + "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", "dev": true, "requires": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "workbox-build": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.4.2.tgz", - "integrity": "sha512-WMdYLhDIsuzViOTXDH+tJ1GijkFp5khSYolnxR/11zmfhNDtuo7jof72xPGFy+KRpsz6tug39RhivCj77qqO0w==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", + "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", "dev": true, "requires": { "@apideck/better-ajv-errors": "^0.3.1", @@ -31514,26 +31971,25 @@ "rollup": "^2.43.1", "rollup-plugin-terser": "^7.0.0", "source-map": "^0.8.0-beta.0", - "source-map-url": "^0.4.0", "stringify-object": "^3.3.0", "strip-comments": "^2.0.1", "tempy": "^0.6.0", "upath": "^1.2.0", - "workbox-background-sync": "6.4.2", - "workbox-broadcast-update": "6.4.2", - "workbox-cacheable-response": "6.4.2", - "workbox-core": "6.4.2", - "workbox-expiration": "6.4.2", - "workbox-google-analytics": "6.4.2", - "workbox-navigation-preload": "6.4.2", - "workbox-precaching": "6.4.2", - "workbox-range-requests": "6.4.2", - "workbox-recipes": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2", - "workbox-streams": "6.4.2", - "workbox-sw": "6.4.2", - "workbox-window": "6.4.2" + "workbox-background-sync": "6.5.4", + "workbox-broadcast-update": "6.5.4", + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-google-analytics": "6.5.4", + "workbox-navigation-preload": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-range-requests": "6.5.4", + "workbox-recipes": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4", + "workbox-streams": "6.5.4", + "workbox-sw": "6.5.4", + "workbox-window": "6.5.4" }, "dependencies": { "fs-extra": { @@ -31560,7 +32016,7 @@ "tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -31586,18 +32042,18 @@ } }, "workbox-cacheable-response": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.4.2.tgz", - "integrity": "sha512-9FE1W/cKffk1AJzImxgEN0ceWpyz1tqNjZVtA3/LAvYL3AC5SbIkhc7ZCO82WmO9IjTfu8Vut2X/C7ViMSF7TA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", + "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", "dev": true, "requires": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "workbox-cli": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-cli/-/workbox-cli-6.4.2.tgz", - "integrity": "sha512-RvKGG4WHt+9pFjYYujvGd0XVJCNALRoOYeVMotvK79m/Fwn+Ex3uNb9KY7vV8JSgUb1JlZzH6/jSiGbtCd3MOQ==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cli/-/workbox-cli-6.5.4.tgz", + "integrity": "sha512-+Cc0jYh25MofhCROZqfQkpYSAGvykyrUVekuuPaLFbJ8qxX/zzX8hRRpglfwxDwokAjz8S20oEph4s+MyQc+Yw==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -31612,7 +32068,7 @@ "stringify-object": "^3.3.0", "upath": "^1.2.0", "update-notifier": "^4.1.0", - "workbox-build": "6.4.2" + "workbox-build": "6.5.4" }, "dependencies": { "fs-extra": { @@ -31689,118 +32145,118 @@ } }, "workbox-core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.4.2.tgz", - "integrity": "sha512-1U6cdEYPcajRXiboSlpJx6U7TvhIKbxRRerfepAJu2hniKwJ3DHILjpU/zx3yvzSBCWcNJDoFalf7Vgd7ey/rw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", + "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==", "dev": true }, "workbox-expiration": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.4.2.tgz", - "integrity": "sha512-0hbpBj0tDnW+DZOUmwZqntB/8xrXOgO34i7s00Si/VlFJvvpRKg1leXdHHU8ykoSBd6+F2KDcMP3swoCi5guLw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", + "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", "dev": true, "requires": { - "idb": "^6.1.4", - "workbox-core": "6.4.2" + "idb": "^7.0.1", + "workbox-core": "6.5.4" } }, "workbox-google-analytics": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.4.2.tgz", - "integrity": "sha512-u+gxs3jXovPb1oul4CTBOb+T9fS1oZG+ZE6AzS7l40vnyfJV79DaLBvlpEZfXGv3CjMdV1sT/ltdOrKzo7HcGw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", + "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", "dev": true, "requires": { - "workbox-background-sync": "6.4.2", - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" + "workbox-background-sync": "6.5.4", + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" } }, "workbox-navigation-preload": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.4.2.tgz", - "integrity": "sha512-viyejlCtlKsbJCBHwhSBbWc57MwPXvUrc8P7d+87AxBGPU+JuWkT6nvBANgVgFz6FUhCvRC8aYt+B1helo166g==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", + "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", "dev": true, "requires": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "workbox-precaching": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.4.2.tgz", - "integrity": "sha512-CZ6uwFN/2wb4noHVlALL7UqPFbLfez/9S2GAzGAb0Sk876ul9ukRKPJJ6gtsxfE2HSTwqwuyNVa6xWyeyJ1XSA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", + "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", "dev": true, "requires": { - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" } }, "workbox-range-requests": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.4.2.tgz", - "integrity": "sha512-SowF3z69hr3Po/w7+xarWfzxJX/3Fo0uSG72Zg4g5FWWnHpq2zPvgbWerBZIa81zpJVUdYpMa3akJJsv+LaO1Q==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", + "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", "dev": true, "requires": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "workbox-recipes": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.4.2.tgz", - "integrity": "sha512-/oVxlZFpAjFVbY+3PoGEXe8qyvtmqMrTdWhbOfbwokNFtUZ/JCtanDKgwDv9x3AebqGAoJRvQNSru0F4nG+gWA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", + "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", "dev": true, "requires": { - "workbox-cacheable-response": "6.4.2", - "workbox-core": "6.4.2", - "workbox-expiration": "6.4.2", - "workbox-precaching": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" } }, "workbox-routing": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.4.2.tgz", - "integrity": "sha512-0ss/n9PAcHjTy4Ad7l2puuod4WtsnRYu9BrmHcu6Dk4PgWeJo1t5VnGufPxNtcuyPGQ3OdnMdlmhMJ57sSrrSw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", + "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", "dev": true, "requires": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "workbox-strategies": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.4.2.tgz", - "integrity": "sha512-YXh9E9dZGEO1EiPC3jPe2CbztO5WT8Ruj8wiYZM56XqEJp5YlGTtqRjghV+JovWOqkWdR+amJpV31KPWQUvn1Q==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", + "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", "dev": true, "requires": { - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "workbox-streams": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.4.2.tgz", - "integrity": "sha512-ROEGlZHGVEgpa5bOZefiJEVsi5PsFjJG9Xd+wnDbApsCO9xq9rYFopF+IRq9tChyYzhBnyk2hJxbQVWphz3sog==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", + "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", "dev": true, "requires": { - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2" + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4" } }, "workbox-sw": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.4.2.tgz", - "integrity": "sha512-A2qdu9TLktfIM5NE/8+yYwfWu+JgDaCkbo5ikrky2c7r9v2X6DcJ+zSLphNHHLwM/0eVk5XVf1mC5HGhYpMhhg==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", + "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==", "dev": true }, "workbox-window": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.4.2.tgz", - "integrity": "sha512-KVyRKmrJg7iB+uym/B/CnEUEFG9CvnTU1Bq5xpXHbtgD9l+ShDekSl1wYpqw/O0JfeeQVOFb8CiNfvnwWwqnWQ==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", + "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", "dev": true, "requires": { "@types/trusted-types": "^2.0.2", - "workbox-core": "6.4.2" + "workbox-core": "6.5.4" } }, "wrap-ansi": { diff --git a/package.json b/package.json index e40ebd12ad..25ba2d7c66 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "prettier": "^2.3.2", "sinon": "^11.1.2", "snowpack": "3.8.6", + "source-map": "^0.7.4", "standard-version": "^9.3.1", "tslib": "^2.3.1", "typedoc": "^0.21.10", From b7f1b02e91113523120ed8ffbc011755658cff7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Pupier?= Date: Wed, 22 Mar 2023 10:18:35 +0100 Subject: [PATCH 13/27] Fix typos in Markdown files (#1197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix typos in Markdown files Signed-off-by: Aurélien Pupier * Improve wording in Markdown doc files applying suggestions from code review from @danyill Co-authored-by: danyill * Improve wording in Markdown doc files applying suggestions from code review from @danyill Co-authored-by: danyill Signed-off-by: Aurélien Pupier --------- Signed-off-by: Aurélien Pupier Co-authored-by: danyill --- ROADMAP.md | 4 ++-- public/md/All-template-editor-wizards.md | 1 - public/md/Create-GOOSE-Control-Blocks.md | 2 +- public/md/Create-Sampled-Values-Control-Blocks.md | 2 +- public/md/DataTypeTemplates.md | 4 ++-- public/md/Edit-GOOSE-Control-Blocks.md | 2 +- public/md/Edit-Sampled-Value-Control-Blocks.md | 2 +- public/md/Enumeration-EnumType.md | 4 ++-- public/md/Generic-Substation-Event-Basics.md | 2 +- public/md/Validators copy.md | 2 +- public/md/Validators.md | 2 +- 11 files changed, 13 insertions(+), 14 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index ed7e0e3a22..72d11ad933 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,13 +1,13 @@ # Feature Wishes -Feeature ideas for the OpenSCD project. They are currently not prioritized. +Feature ideas for the OpenSCD project. They are currently not prioritized. - Add the remaining [process elements to the substation editor](https://github.com/openscd/open-scd/projects/1) - [Edit wizard for `Services` element](https://github.com/openscd/open-scd/projects/17) - General purpose [SCL diffing tool](https://github.com/openscd/open-scd/projects/16) - Finish the [Publisher Plugin](https://github.com/openscd/open-scd/projects/14) - Improve [IED Editor](https://github.com/openscd/open-scd/projects/11) user experience -- Implement a more featureful [Single Line Diagram](https://github.com/openscd/open-scd/projects/7) editor that can make changes +- Implement a more fully-featured [Single Line Diagram](https://github.com/openscd/open-scd/projects/7) editor that can make changes - Implement [Log Control Block manipulation](https://github.com/openscd/open-scd/issues/148) - Implement [Setting Group manipulation](https://github.com/openscd/open-scd/issues/149) - Implement [Role Based Access](https://github.com/openscd/open-scd/issues/167) Control diff --git a/public/md/All-template-editor-wizards.md b/public/md/All-template-editor-wizards.md index dd8c9266bd..0b6dbe2848 100644 --- a/public/md/All-template-editor-wizards.md +++ b/public/md/All-template-editor-wizards.md @@ -2,4 +2,3 @@ The **template editor** allows you to create the elements that combined is the d - [EnumType](https://github.com/openscd/open-scd/wiki/Enumeration-EnumType) - [EnumVal](https://github.com/openscd/open-scd/wiki/Enumeration-EnumVal) -- [EnumVal]() \ No newline at end of file diff --git a/public/md/Create-GOOSE-Control-Blocks.md b/public/md/Create-GOOSE-Control-Blocks.md index 12690d5990..9c2accca63 100644 --- a/public/md/Create-GOOSE-Control-Blocks.md +++ b/public/md/Create-GOOSE-Control-Blocks.md @@ -48,7 +48,7 @@ This page allows do define all communication related setting for the GOOSE. Thes 1. `MAC-Address`: The destination MAC address of the GOOSE 2. `APPID`: The APPID of the GOOSE. This is recommended to be unique though out the complete project. 3. `VLAN-ID`: The virtual LAN ID the GOOSE shall be limited to. -4. `VLAN-PRIORITY`: The priority of the GOOSE message. Can be used to priories time critical messages like trip signals over non time critical messages. +4. `VLAN-PRIORITY`: The priority of the GOOSE message. It can be used to prioritise time-critical messages like trip signals over non-time-critical messages. 5. `MinTime`: The minimum time between two occurring GOOSEs when a e.g. a trip occurs 6. `MaxTime`: The maximum time between two occurring GOOSEs in case there is not trip. diff --git a/public/md/Create-Sampled-Values-Control-Blocks.md b/public/md/Create-Sampled-Values-Control-Blocks.md index c15ee6a309..a56ca3d57f 100644 --- a/public/md/Create-Sampled-Values-Control-Blocks.md +++ b/public/md/Create-Sampled-Values-Control-Blocks.md @@ -63,7 +63,7 @@ This page allows do define all communication related setting for the Sampled Val 1. `MAC-Address`: The destination MAC address of the Sampled Value: `01-0C-CD-04-00-00` to `01-0C-CD-04-01-FF` 2. `APPID`: The APPID of the Sampled Value. This is recommended to be unique though out the complete project. 3. `VLAN-ID`: The virtual LAN ID the Sampled Value shall be limited to. -4. `VLAN-PRIORITY`: The priority of the Sampled Value message. Can be used to priories time critical messages like trip signals over non time critical messages. +4. `VLAN-PRIORITY`: The priority of the Sampled Value message. It can be used to prioritise time-critical messages like trip signals over non-time-critical messages.   diff --git a/public/md/DataTypeTemplates.md b/public/md/DataTypeTemplates.md index bac9cf1acf..dcb761a4ab 100644 --- a/public/md/DataTypeTemplates.md +++ b/public/md/DataTypeTemplates.md @@ -7,7 +7,7 @@ The difference between a signal list and the data model defined in the IEC  3. Data attribute 4. (Enumeration) -This hierarchical structure can be defined in the `DataTypeTemplates` section of a SCL file by defining and referencing four elements `LNodeType`, `DOType`, `DAType` and `EnumType`. One builds upon the other. In other terms, if you want ot define a `LNodeType` or lets say a LLN0 logical node class you need already some defined `DOType`s and for those you need already some `DAType`s and for some of those - ENx - you also need some `EnumType`s. +This hierarchical structure can be defined in the `DataTypeTemplates` section of a SCL file by defining and referencing four elements `LNodeType`, `DOType`, `DAType` and `EnumType`. One builds upon the other. In other words, if you want to define a `LNodeType` or let's say a LLN0 logical node class you need some already defined `DOType`s and for those you need some already existing `DAType`s and for some of those - ENx - you also need some already existing `EnumType`s. This is why this process is so hard for beginners to wrap their mind around. To help users that are not so experienced with this part of the standard as well as others that you need to know, e.g. 7-4, 7-3, 8-1, OpenSCD offers so-called templates. @@ -15,7 +15,7 @@ The template database is nothing else than a pre-define data model. Here a lot a You can see, if OpenSCD does have a template for a logical node class you need in the **Add LNodeType** wizard (_Pre-defined lnClasses from templates_). For more detail please refer to [start from templates](https://github.com/openscd/open-scd/wiki/Start-from-template) -If this option is disabled you have to start the precess [from scratch](https://github.com/openscd/open-scd/wiki/Start-from-scratch) +If this option is disabled you have to start the process [from scratch](https://github.com/openscd/open-scd/wiki/Start-from-scratch) In addition to that you can read/manipulate any element and their attributes using the wizards for diff --git a/public/md/Edit-GOOSE-Control-Blocks.md b/public/md/Edit-GOOSE-Control-Blocks.md index 0d9f0ae931..5d637ce9cb 100644 --- a/public/md/Edit-GOOSE-Control-Blocks.md +++ b/public/md/Edit-GOOSE-Control-Blocks.md @@ -34,7 +34,7 @@ This page allows do define all communication related setting for the GOOSE. Thes 1. `MAC-Address`: The destination MAC address of the GOOSE 2. `APPID`: The APPID of the GOOSE. This is recommended to be unique though out the complete project. 3. `VLAN-ID`: The virtual LAN ID the GOOSE shall be limited to. -4. `VLAN-PRIORITY`: The priority of the GOOSE message. Can be used to priories time critical messages like trip signals over non time critical messages. +4. `VLAN-PRIORITY`: The priority of the GOOSE message. It can be used to prioritise time-critical messages like trip signals over non-time-critical messages. 5. `MinTime`: The minimum time between two occurring GOOSEs when a e.g. a trip occurs 6. `MaxTime`: The maximum time between two occurring GOOSEs in case there is not trip. diff --git a/public/md/Edit-Sampled-Value-Control-Blocks.md b/public/md/Edit-Sampled-Value-Control-Blocks.md index ff5efd8261..83db21c903 100644 --- a/public/md/Edit-Sampled-Value-Control-Blocks.md +++ b/public/md/Edit-Sampled-Value-Control-Blocks.md @@ -51,7 +51,7 @@ This wizard allows do define all communication related setting for the Sampled V 1. `MAC-Address`: The destination MAC address of the Sampled Value: `01-0C-CD-04-00-00` to `01-0C-CD-04-01-FF` 2. `APPID`: The APPID of the Sampled Value. This is recommended to be unique though out the complete project. 3. `VLAN-ID`: The virtual LAN ID the Sampled Value shall be limited to. -4. `VLAN-PRIORITY`: The priority of the Sampled Value message. Can be used to priories time critical messages like trip signals over non time critical messages. +4. `VLAN-PRIORITY`: The priority of the Sampled Value message. It can be used to priorities time-critical messages like trip signals over non-time-critical messages.   diff --git a/public/md/Enumeration-EnumType.md b/public/md/Enumeration-EnumType.md index b4fbb83945..85e536fb13 100644 --- a/public/md/Enumeration-EnumType.md +++ b/public/md/Enumeration-EnumType.md @@ -21,7 +21,7 @@ OpenSCD templates contains all enumerations from IEC 61850‑7‑ *User-defined* -Do defined a user-defined from scratch open **Add EnumType** wizard and make sure to NOT select the value. Be careful with this option as this add a blanc `EnumType`. +Do defined a user-defined from scratch open **Add EnumType** wizard and make sure to NOT select the value. Be careful with this option as this adds a blank `EnumType`.   @@ -29,7 +29,7 @@ Do defined a user-defined from scratch open **Add EnumType** wizard and make sur **Edit EnumType** -1. Vavigate to the `EnumType` list in the **Template editor** and click on the `EnumType` you want to edit +1. Navigate to the `EnumType` list in the **Template editor** and click on the `EnumType` you want to edit *Settings*: diff --git a/public/md/Generic-Substation-Event-Basics.md b/public/md/Generic-Substation-Event-Basics.md index 2e8fad5d70..cd77290faf 100644 --- a/public/md/Generic-Substation-Event-Basics.md +++ b/public/md/Generic-Substation-Event-Basics.md @@ -2,7 +2,7 @@ The Generic Substation Event is a service class that has been designed to transm The GOOSE is defined in the part IEC 61850-8-1. It is often applied to communicate between servers such as bay controller and switch gear control unit. -GOOSE does not use TCP/IP but uses multicast mechanist instead to be send to all communication participants in the same LAN/VLAN. Its sending or publishing and its use or subscription are therefore completely separated. Tha has its effect also on the configuration of GOOSE related communication in the SCL. +GOOSE does not use TCP/IP but uses a multicast mechanism instead to send to all communication participants in the same LAN/VLAN. Its sending (or publishing) and its use (or subscribing) are, therefore, completely separated. This has an impact on the way GOOSE-related communications are configured in the SCL. **Publishing: GOOSE Control Block (GSEControl)** diff --git a/public/md/Validators copy.md b/public/md/Validators copy.md index a6518ea08a..4d01585903 100644 --- a/public/md/Validators copy.md +++ b/public/md/Validators copy.md @@ -1,4 +1,4 @@ -Validators are the backbone of this editor. It is very tricky to handle the complexity of an SCL file without it. This is ecpecially true for people just starting to work with SCL file and or with OpenSCD. In most cases we try to avoid producing invalid files but especially in **expert mode** with is not always possible. +Validators are the backbone of this editor. It is very tricky to handle the complexity of an SCL file without it. This is especially true for people just starting to work with SCL files and OpenSCD. In most cases, we try to avoid producing invalid files, but especially in **expert mode**, it is not always possible. There are two validation functions implemented at the moment. Both are triggered on opening an project or creating a new project. So every time the project basis change. And all validators are triggered on every editor action. That means every time there are changes in the project basis. All results are displayed in the **diagnostics** pane (Ctrl D) and are group by the validation function diff --git a/public/md/Validators.md b/public/md/Validators.md index a6518ea08a..c23c43a7fb 100644 --- a/public/md/Validators.md +++ b/public/md/Validators.md @@ -1,4 +1,4 @@ -Validators are the backbone of this editor. It is very tricky to handle the complexity of an SCL file without it. This is ecpecially true for people just starting to work with SCL file and or with OpenSCD. In most cases we try to avoid producing invalid files but especially in **expert mode** with is not always possible. +Validators are the backbone of this editor. It is very tricky to handle the complexity of an SCL file without it. This is especially true for people just working with SCL files and OpenSCD. In most cases, we try to avoid producing invalid files, but especially in **expert mode**, it is not always possible. There are two validation functions implemented at the moment. Both are triggered on opening an project or creating a new project. So every time the project basis change. And all validators are triggered on every editor action. That means every time there are changes in the project basis. All results are displayed in the **diagnostics** pane (Ctrl D) and are group by the validation function From 4daadaa2f23ce75205667c09b2b756968ede94b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Pupier?= Date: Wed, 22 Mar 2023 12:18:56 +0100 Subject: [PATCH 14/27] docs: remove duplicated Markdown files (#1198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aurélien Pupier --- public/md/Guess-substation-structure copy.md | 26 -------------------- public/md/Save-project copy.md | 3 --- public/md/Validators copy.md | 7 ------ 3 files changed, 36 deletions(-) delete mode 100644 public/md/Guess-substation-structure copy.md delete mode 100644 public/md/Save-project copy.md delete mode 100644 public/md/Validators copy.md diff --git a/public/md/Guess-substation-structure copy.md b/public/md/Guess-substation-structure copy.md deleted file mode 100644 index 0686b72380..0000000000 --- a/public/md/Guess-substation-structure copy.md +++ /dev/null @@ -1,26 +0,0 @@ - -The `Substation` is an optional element and therefore is often missing in so-called bottom-up projects. With `OpenSCD` you can add the `Substation` element in the aftermath. - -For exactly this purpose the `guess` function has been introduced to `OpenSCD` to either speed up this task or to give pure beginner an impression what to do. - -> NOTE: A guess will never be perfect as it is based on assumptions (see below) that not necessary fit to the IEDs you have in the `SCL` file. However, you can give it a try as you can always `undo`. - -This is how you can use the function: -- Open and `SCL` file with missing substation or delete all `Substation`elements -- Click on the fab button **+Add Substation** -- In the dialog **Add Substation** check the box **Guess content** -- Choose the `(ctlModel)` (**Control model**) that identify your switch gear in the IED -- Start **GUESS CONTENT** - - -> NOTE: The biggest challenge with the guess function is to identify the amount of voltage levels (`VoltageLevel`), bays (`Bay`) and so-called conducting equipment or primary equipment(`ConductingEquipment`). - -We therefore have to make some basic assumptions that is: -- Assumes only one voltage level -- Bays are identified with the logical node `CSWI`. That means, if there is at least a logical node `CSWI` and the mandatory data object `Pos` matches the chosen control model (ctlModel) a bay is created for this particular IED. -- Each of the logical nodes `CSWI` with matching `ctlModel ` represents a conducting equipment. If a `XCBR` is connected to it (same `prefix` and `inst` in the same `LDevice`), the conducting equipment `type` is `CBR` in every other case `DIS`. -- Logical node connections `LNode` are automatically assigned to the conducting equipment. Ideally this is the `CSWI`, a `XCRB` or `XSWI` and `CILO` - -> TIP: Especially the assumption that there is only one voltage level does not match big files with lots of IEDs. Use the **Move** function to allocate bays to other voltage levels. - - diff --git a/public/md/Save-project copy.md b/public/md/Save-project copy.md deleted file mode 100644 index 4135ba6067..0000000000 --- a/public/md/Save-project copy.md +++ /dev/null @@ -1,3 +0,0 @@ -One of the things that user might be different in OpenSCD compared to a desktop application is **Save project**. As OpenSCD runs in the browser we use the browser standard behavior to save files and in most browsers this means that the file is downloaded in the download folder on your machine directly. - -HOWEVER, you can change this standard behavior. In Chrome and Chromium, this setting is called **Ask where to save each file before downloading**. \ No newline at end of file diff --git a/public/md/Validators copy.md b/public/md/Validators copy.md deleted file mode 100644 index 4d01585903..0000000000 --- a/public/md/Validators copy.md +++ /dev/null @@ -1,7 +0,0 @@ -Validators are the backbone of this editor. It is very tricky to handle the complexity of an SCL file without it. This is especially true for people just starting to work with SCL files and OpenSCD. In most cases, we try to avoid producing invalid files, but especially in **expert mode**, it is not always possible. - -There are two validation functions implemented at the moment. Both are triggered on opening an project or creating a new project. So every time the project basis change. And all validators are triggered on every editor action. That means every time there are changes in the project basis. All results are displayed in the **diagnostics** pane (Ctrl D) and are group by the validation function - - -1. [Schema validator](https://github.com/openscd/open-scd/wiki/Validate-schema) -2. [Templates validator](https://github.com/openscd/open-scd/wiki/Validate-template) \ No newline at end of file From bbfde76c40606e843b3542f146e6931c1c4afcce Mon Sep 17 00:00:00 2001 From: marcvanraalte <86408026+marcvanraalte@users.noreply.github.com> Date: Thu, 23 Mar 2023 09:52:19 +0100 Subject: [PATCH 15/27] feat(line-editor.ts):unit_test_added (#1193) * feat(line-editor.ts):unit_test_added * feat(line-editor.ts):unit_test_added * fix(zeroline-pane):removed_parameters --- src/editors/substation/line-editor.ts | 103 ++++++++ src/editors/substation/zeroline-pane.ts | 25 +- test/testfiles/editors/substation/Line.scd | 235 ++++++++++++++++++ .../__snapshots__/line-editor.test.snap.js | 56 +++++ .../editors/substation/line-editor.test.ts | 68 +++++ 5 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 src/editors/substation/line-editor.ts create mode 100644 test/testfiles/editors/substation/Line.scd create mode 100644 test/unit/editors/substation/__snapshots__/line-editor.test.snap.js create mode 100644 test/unit/editors/substation/line-editor.test.ts diff --git a/src/editors/substation/line-editor.ts b/src/editors/substation/line-editor.ts new file mode 100644 index 0000000000..459c200e6e --- /dev/null +++ b/src/editors/substation/line-editor.ts @@ -0,0 +1,103 @@ +import { + customElement, + html, + LitElement, + TemplateResult, + property, + state, +} from 'lit-element'; + +import './conducting-equipment-editor.js'; +import './function-editor.js'; +import './general-equipment-editor.js'; +import './l-node-editor.js'; +import { getChildElementsByTagName } from '../../foundation.js'; + +@customElement('line-editor') +export class LineEditor extends LitElement { + /** The document being edited as provided to editor by [[`Zeroline`]]. */ + @property({ attribute: false }) + doc!: XMLDocument; + /** SCL element Line */ + @property({ attribute: false }) + element!: Element; + /** Whether `Function` and `LNode` are rendered */ + @property({ type: Boolean }) + showfunctions = false; + + @state() + get header(): string { + const name = this.element.getAttribute('name') ?? ''; + const desc = this.element.getAttribute('desc'); + + return `${name} ${desc ? `—${desc}` : ''}`; + } + + private renderConductingEquipments(): TemplateResult { + const ConductingEquipments = getChildElementsByTagName( + this.element, + 'ConductingEquipment' + ); + return html` ${ConductingEquipments.map( + ConductingEquipment => + html`` + )}`; + } + + private renderGeneralEquipments(): TemplateResult { + const GeneralEquipments = getChildElementsByTagName( + this.element, + 'GeneralEquipment' + ); + return html` ${GeneralEquipments.map( + GeneralEquipment => + html`` + )}`; + } + + private renderFunctions(): TemplateResult { + if (!this.showfunctions) return html``; + + const Functions = getChildElementsByTagName(this.element, 'Function'); + return html` ${Functions.map( + Function => + html`` + )}`; + } + + private renderLNodes(): TemplateResult { + if (!this.showfunctions) return html``; + + const lNodes = getChildElementsByTagName(this.element, 'LNode'); + return lNodes.length + ? html`
+ ${lNodes.map( + lNode => + html`` + )} +
` + : html``; + } + + render(): TemplateResult { + return html` ${this.renderConductingEquipments()}${this.renderGeneralEquipments()}${this.renderFunctions()}${this.renderLNodes()} + `; + } +} diff --git a/src/editors/substation/zeroline-pane.ts b/src/editors/substation/zeroline-pane.ts index 918f0077b9..146a860ea6 100644 --- a/src/editors/substation/zeroline-pane.ts +++ b/src/editors/substation/zeroline-pane.ts @@ -14,6 +14,7 @@ import '@material/mwc-icon-button-toggle'; import { IconButton } from '@material/mwc-icon-button'; import { IconButtonToggle } from '@material/mwc-icon-button-toggle'; +import './line-editor.js'; import './substation-editor.js'; import './ied-editor.js'; import { communicationMappingWizard } from '../../wizards/commmap-wizards.js'; @@ -113,11 +114,31 @@ export class ZerolinePane extends LitElement { return ieds.length ? html`
- ${ieds.map(ied => html``)} + ${ieds.map( + ied => + html`` + )}
` : html``; } + renderLines(): TemplateResult { + return this.doc?.querySelector(':root > Line') + ? html`
+ ${Array.from(this.doc.querySelectorAll('Line') ?? []) + .filter(isPublic) + .map( + line => + html`` + )} +
` + : html``; + } + render(): TemplateResult { return html`

`}`; + `}${this.renderLines()}`; } static styles = css` diff --git a/test/testfiles/editors/substation/Line.scd b/test/testfiles/editors/substation/Line.scd new file mode 100644 index 0000000000..3cbdb667bf --- /dev/null +++ b/test/testfiles/editors/substation/Line.scd @@ -0,0 +1,235 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + 30000 + + + 600 + + + + + + + + + + + + + + 1000 + + + direct-with-enhanced-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + 30000 + + + 600 + + + + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + status-only + + + pulse + persistent + persistent-feedback + + + Ok + Warning + Alarm + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + on + blocked + test + test/blocked + off + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + + + + + + + + 110.0 + + + + + + + + + + + + + + 380.0 + + + + + diff --git a/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js new file mode 100644 index 0000000000..ae6dbfe89d --- /dev/null +++ b/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js @@ -0,0 +1,56 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["web component rendering Line element rendering LNode and Function children looks like the latest snapshot"] = +` + + + + +
+ + +
+
+`; +/* end snapshot web component rendering Line element rendering LNode and Function children looks like the latest snapshot */ + +snapshots["web component rendering Line element rendering ConductingEquipment looks like the latest snapshot"] = +` + + + + +
+ + +
+
+`; +/* end snapshot web component rendering Line element rendering ConductingEquipment looks like the latest snapshot */ + +snapshots["web component rendering Line element rendering GeneralEquipment looks like the latest snapshot"] = +` + + + + + + +
+ + +
+
+`; +/* end snapshot web component rendering Line element rendering GeneralEquipment looks like the latest snapshot */ + diff --git a/test/unit/editors/substation/line-editor.test.ts b/test/unit/editors/substation/line-editor.test.ts new file mode 100644 index 0000000000..31e6ef9e88 --- /dev/null +++ b/test/unit/editors/substation/line-editor.test.ts @@ -0,0 +1,68 @@ +import { fixture, html, expect } from '@open-wc/testing'; + +import '../../../../src/editors/substation/line-editor.js'; +import { LineEditor } from '../../../../src/editors/substation/line-editor.js'; + +describe('web component rendering Line element', () => { + let element: LineEditor; + let doc: XMLDocument; + + describe('rendering LNode and Function children', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/Line.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + element = ( + await fixture( + html`` + ) + ); + element.showfunctions = true; + await element.updateComplete; + }); + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); + + describe('rendering ConductingEquipment', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/Line.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + element = ( + await fixture( + html`` + ) + ); + element.showfunctions = true; + await element.updateComplete; + }); + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); + describe('rendering GeneralEquipment', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/Line.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + element = ( + await fixture( + html`` + ) + ); + element.showfunctions = true; + await element.updateComplete; + }); + it('looks like the latest snapshot', async () => { + await expect(element).shadowDom.to.equalSnapshot(); + }); + }); +}); From 874be4518c7574846abbaed4cf597c779c747d5e Mon Sep 17 00:00:00 2001 From: danyill Date: Sun, 26 Mar 2023 21:07:58 +1300 Subject: [PATCH 16/27] feat(editors/binding): Allow filtering of subscribed/unsubscribed data in binding editors (#1149) Allow filtering of subscribed/unsubscribed, closes #1062 --- src/editors/subscription/fcda-binding-list.ts | 297 ++++++++++++++---- src/translations/de.ts | 1 + src/translations/en.ts | 1 + test/integration/editors/test-support.ts | 2 +- .../fcda-binding-list.test.snap.js | 245 ++++++++++++--- .../subscription/fcda-binding-list.test.ts | 160 +++++++++- .../ext-ref-ln-binding-list.test.snap.js | 54 ++-- 7 files changed, 630 insertions(+), 130 deletions(-) diff --git a/src/editors/subscription/fcda-binding-list.ts b/src/editors/subscription/fcda-binding-list.ts index e71f992b69..6187044aee 100644 --- a/src/editors/subscription/fcda-binding-list.ts +++ b/src/editors/subscription/fcda-binding-list.ts @@ -5,15 +5,23 @@ import { LitElement, property, PropertyValues, + query, state, TemplateResult, } from 'lit-element'; import { nothing, SVGTemplateResult } from 'lit-html'; +import { classMap } from 'lit-html/directives/class-map.js'; import { translate } from 'lit-translate'; import '@material/mwc-icon'; import '@material/mwc-list'; import '@material/mwc-list/mwc-list-item'; +import '@material/mwc-list/mwc-check-list-item'; +import '@material/mwc-menu'; + +import { Icon } from '@material/mwc-icon'; +import { List } from '@material/mwc-list'; +import { Menu } from '@material/mwc-menu'; import { getDescriptionAttribute, @@ -59,6 +67,64 @@ export class FcdaBindingList extends LitElement { @state() private extRefCounters = new Map(); + @property({ + type: Boolean, + hasChanged() { + return false; + }, + }) + get hideSubscribed(): boolean { + return ( + localStorage.getItem( + `fcda-binding-list-${ + this.includeLaterBinding ? 'later-binding' : 'data-binding' + }-${this.controlTag}$hideSubscribed` + ) === 'true' ?? false + ); + } + + set hideSubscribed(value: boolean) { + const oldValue = this.hideSubscribed; + localStorage.setItem( + `fcda-binding-list-${ + this.includeLaterBinding ? 'later-binding' : 'data-binding' + }-${this.controlTag}$hideSubscribed`, + `${value}` + ); + this.requestUpdate('hideSubscribed', oldValue); + } + + @property({ + type: Boolean, + hasChanged() { + return false; + }, + }) + get hideNotSubscribed(): boolean { + return ( + localStorage.getItem( + `fcda-binding-list-${ + this.includeLaterBinding ? 'later-binding' : 'data-binding' + }-${this.controlTag}$hideNotSubscribed` + ) === 'true' ?? false + ); + } + + set hideNotSubscribed(value: boolean) { + const oldValue = this.hideNotSubscribed; + localStorage.setItem( + `fcda-binding-list-${ + this.includeLaterBinding ? 'later-binding' : 'data-binding' + }-${this.controlTag}$hideNotSubscribed`, + `${value}` + ); + this.requestUpdate('hideNotSubscribed', oldValue); + } + + @query('.actions-menu') actionsMenu!: Menu; + @query('.actions-menu-icon') actionsMenuIcon!: Icon; + @query('.control-block-list') controlBlockList!: List; + private iconControlLookup: iconLookup = { SampledValueControl: smvIcon, GSEControl: gooseIcon, @@ -170,11 +236,18 @@ export class FcdaBindingList extends LitElement { renderFCDA(controlElement: Element, fcdaElement: Element): TemplateResult { const fcdaCount = this.getExtRefCount(fcdaElement, controlElement); + + const filterClasses = { + subitem: true, + 'show-subscribed': fcdaCount !== 0, + 'show-not-subscribed': fcdaCount === 0, + }; + return html` this.onFcdaSelect(controlElement, fcdaElement)} value="${identity(controlElement)} ${identity(fcdaElement)}" @@ -186,63 +259,133 @@ export class FcdaBindingList extends LitElement { `; } - render(): TemplateResult { - const controlElements = this.getControlElements(); - return html`
- ${controlElements.length > 0 - ? html`

- ${translate( - `subscription.${this.controlTag}.controlBlockList.title` - )} -

- - ${controlElements.map(controlElement => { - const fcdaElements = this.getFcdaElements(controlElement); - return html` - { + if (!this.actionsMenu.open) this.actionsMenu.show(); + else this.actionsMenu.close(); + }} + > + + + ${translate('subscription.subscriber.subscribed')} + + + ${translate('subscription.subscriber.notSubscribed')} + + + `; + } + + renderControls(controlElements: Element[]): TemplateResult { + return html` + ${controlElements + .filter(controlElement => this.getFcdaElements(controlElement).length) + .map(controlElement => { + const fcdaElements = this.getFcdaElements(controlElement); + const showSubscribed = fcdaElements.some( + fcda => this.getExtRefCount(fcda, controlElement) !== 0 + ); + const showNotSubscribed = fcdaElements.some( + fcda => this.getExtRefCount(fcda, controlElement) === 0 + ); + + const filterClasses = { + control: true, + 'show-subscribed': showSubscribed, + 'show-not-subscribed': showNotSubscribed, + }; + + return html` + - this.openEditWizard(controlElement)} - > - ${getNameAttribute(controlElement)} - ${getDescriptionAttribute(controlElement) - ? html`${getDescriptionAttribute(controlElement)}` - : nothing} - ${identity(controlElement)} - ${this.iconControlLookup[this.controlTag]} - -
  • - ${fcdaElements.map(fcdaElement => - this.renderFCDA(controlElement, fcdaElement) - )} - `; - })} -
    ` - : html`

    - ${translate( - `subscription.${this.controlTag}.controlBlockList.noControlBlockFound` + ) + .join('')}" + > + this.openEditWizard(controlElement)} + > + ${getNameAttribute(controlElement)} + ${getDescriptionAttribute(controlElement) + ? html`${getDescriptionAttribute(controlElement)}` + : nothing} + ${identity(controlElement)} + ${this.iconControlLookup[this.controlTag]} + +
  • + ${fcdaElements.map(fcdaElement => + this.renderFCDA(controlElement, fcdaElement) )} -

    `} + `; + })} +
    `; + } + + render(): TemplateResult { + const controlElements = this.getControlElements(); + return html`
    + ${this.renderTitle()} + ${controlElements + ? this.renderControls(controlElements) + : html`

    ${translate('subscription.subscriber.notSubscribed')}

    `}
    `; } @@ -257,6 +400,52 @@ export class FcdaBindingList extends LitElement { --mdc-list-item-meta-size: 48px; } + section { + position: relative; + } + + .actions-menu-icon { + float: right; + } + + .actions-menu-icon.filter-off { + color: var(--secondary); + background-color: var(--mdc-theme-background); + } + + /* Filtering rules for control blocks end up implementing logic to allow + very fast CSS response. The following rules appear to be minimal but can be + hard to understand intuitively for the multiple conditions. If modifying, + it is suggested to create a truth-table to check for side-effects */ + + /* remove all control blocks if no filters */ + filtered-list.control-block-list:not(.show-subscribed, .show-not-subscribed) + mwc-list-item { + display: none; + } + + /* remove control blocks taking care to respect multiple conditions */ + filtered-list.control-block-list.show-not-subscribed:not(.show-subscribed) + mwc-list-item.control.show-subscribed:not(.show-not-subscribed) { + display: none; + } + + filtered-list.control-block-list.show-subscribed:not(.show-not-subscribed) + mwc-list-item.control.show-not-subscribed:not(.show-subscribed) { + display: none; + } + + /* remove fcdas if not part of filter */ + filtered-list.control-block-list:not(.show-not-subscribed) + mwc-list-item.subitem.show-not-subscribed { + display: none; + } + + filtered-list.control-block-list:not(.show-subscribed) + mwc-list-item.subitem.show-subscribed { + display: none; + } + .interactive { pointer-events: all; } diff --git a/src/translations/de.ts b/src/translations/de.ts index 0aae589f2d..c284cdc02b 100644 --- a/src/translations/de.ts +++ b/src/translations/de.ts @@ -349,6 +349,7 @@ export const de: Translations = { disconnect: 'Daten-Attribute Verbindung gelöst', subscriber: { subscribed: 'Verbunden', + notSubscribed: 'Nicht Verbunden', availableToSubscribe: 'Kann verbunden werden', partiallySubscribed: 'Teilweise verbunden', noControlBlockSelected: 'Keine Kontrollblock ausgewählt', diff --git a/src/translations/en.ts b/src/translations/en.ts index e5e64fb97f..20e9e4986e 100644 --- a/src/translations/en.ts +++ b/src/translations/en.ts @@ -346,6 +346,7 @@ export const en = { disconnect: 'Disconnect data attribute', subscriber: { subscribed: 'Subscribed', + notSubscribed: 'Not Subscribed', availableToSubscribe: 'Available to subscribe', partiallySubscribed: 'Partially subscribed', noControlBlockSelected: 'No control block selected', diff --git a/test/integration/editors/test-support.ts b/test/integration/editors/test-support.ts index abc57953e5..50b527898a 100644 --- a/test/integration/editors/test-support.ts +++ b/test/integration/editors/test-support.ts @@ -25,7 +25,7 @@ export function selectFCDAItem( fcdaIdentity: string ): void { (Array.from( - listElement.shadowRoot!.querySelectorAll('mwc-list-item[class="subitem"]') + listElement.shadowRoot!.querySelectorAll('mwc-list-item.subitem') ).find(listItem => { const value = listItem.getAttribute('value') ?? ''; return value.includes(controlIdentity) && value.includes(fcdaIdentity); diff --git a/test/unit/editors/subscription/__snapshots__/fcda-binding-list.test.snap.js b/test/unit/editors/subscription/__snapshots__/fcda-binding-list.test.snap.js index bbf02d1d58..4312a96a62 100644 --- a/test/unit/editors/subscription/__snapshots__/fcda-binding-list.test.snap.js +++ b/test/unit/editors/subscription/__snapshots__/fcda-binding-list.test.snap.js @@ -4,8 +4,53 @@ export const snapshots = {}; snapshots["fcda-binding-list without a doc loaded looks like the latest snapshot"] = `

    - [subscription.undefined.controlBlockList.noControlBlockFound] + [subscription.undefined.controlBlockList.title] + + + + + + [subscription.subscriber.subscribed] + + + + + [subscription.subscriber.notSubscribed] + + +

    + +
    `; /* end snapshot fcda-binding-list without a doc loaded looks like the latest snapshot */ @@ -14,17 +59,60 @@ snapshots["fcda-binding-list with a SampledValueControl doc loaded looks like th `

    [subscription.SampledValueControl.controlBlockList.title] + + + + + + [subscription.subscriber.subscribed] + + + + + [subscription.subscriber.notSubscribed] + + +

    - + >CurrentTransformer>fullSmv AmpSv.instMag.i CurrentTransformer / L3 TCTR 1 SMV_Publisher>>CurrentTransformer>fullSmvsDataSet>CurrentTransformer/L3 TCTR 1.AmpSv instMag.i (MX) @@ -84,7 +172,7 @@ snapshots["fcda-binding-list with a SampledValueControl doc loaded looks like th >CurrentTransformer>voltageOnly VolSv.instMag.i VoltageTransformer / L1 TVTR 1 SMV_Publisher>>CurrentTransformer>voltageOnlysDataSet>VoltageTransformer/L1 TVTR 1.VolSv instMag.i (MX) @@ -372,8 +460,9 @@ snapshots["fcda-binding-list with a SampledValueControl doc loaded looks like th subdirectory_arrow_right + + 1 + >CurrentTransformer>currentOnly AmpSv.instMag.i CurrentTransformer / L1 TCTR 1 SMV_Publisher>>CurrentTransformer>currentOnlysDataSet>CurrentTransformer/L1 TCTR 1.AmpSv instMag.i (MX) @@ -540,8 +632,9 @@ snapshots["fcda-binding-list with a SampledValueControl doc loaded looks like th subdirectory_arrow_right + + 1 + subdirectory_arrow_right + + 3 +

    [subscription.GSEControl.controlBlockList.title] + + + + + + [subscription.subscriber.subscribed] + + + + + [subscription.subscriber.notSubscribed] + + +

    - + >QB2_Disconnector>GOOSE2 Pos.stVal QB2_Disconnector / CSWI 1 GOOSE_Publisher>>QB2_Disconnector>GOOSE2sDataSet>QB2_Disconnector/ CSWI 1.Pos stVal (ST) @@ -716,7 +859,7 @@ snapshots["fcda-binding-list with a GSEControl doc loaded looks like the latest - 1 + 2 subdirectory_arrow_right + + 1 + subdirectory_arrow_right + + 1 + subdirectory_arrow_right + + 1 + >QB2_Disconnector>GOOSE1 Pos.stVal QB1_Disconnector / CSWI 1 GOOSE_Publisher>>QB2_Disconnector>GOOSE1sDataSet>QB1_Disconnector/ CSWI 1.Pos stVal (ST) @@ -860,7 +1015,7 @@ snapshots["fcda-binding-list with a GSEControl doc loaded looks like the latest { let parent: MockWizardEditor; @@ -17,6 +18,7 @@ describe('fcda-binding-list', () => { let selectEvent: SinonSpy; beforeEach(async () => { + localStorage.clear(); selectEvent = spy(); window.addEventListener('fcda-select', selectEvent); }); @@ -46,6 +48,7 @@ describe('fcda-binding-list', () => { describe('with a SampledValueControl doc loaded', () => { beforeEach(async () => { + localStorage.clear(); doc = await fetch('/test/testfiles/editors/LaterBindingSMV2007B4.scd') .then(response => response.text()) .then(str => new DOMParser().parseFromString(str, 'application/xml')); @@ -54,6 +57,7 @@ describe('fcda-binding-list', () => { `); @@ -88,9 +92,7 @@ describe('fcda-binding-list', () => { selectEvent.resetHistory(); const listItem = Array.from( - element.shadowRoot?.querySelectorAll( - 'mwc-list-item[class="subitem"]' - ) ?? [] + element.shadowRoot?.querySelectorAll('mwc-list-item.subitem') ?? [] ).filter(listItem => { const value = listItem.getAttribute('value') ?? ''; return ( @@ -117,10 +119,86 @@ describe('fcda-binding-list', () => { it('looks like the latest snapshot', async () => { await expect(element).shadowDom.to.equalSnapshot(); }); + + it('is initially unfiltered', async () => { + const displayedElements = Array.from( + element.shadowRoot!.querySelectorAll('mwc-list-item') + ).filter(item => { + const displayStyle = getComputedStyle(item).display; + return displayStyle !== 'none' || displayStyle === undefined; + }); + expect(displayedElements.length).to.equal(27); + }); + + it('allows filtering of only not subscribed control blocks', async () => { + element.actionsMenuIcon.click(); + await element.updateComplete; + (( + element.actionsMenu!.querySelector('.filter-subscribed') + ))!.click(); + await new Promise(resolve => setTimeout(resolve, 200)); // await animation + await element.updateComplete; + + const fcdaList = element.shadowRoot?.querySelector('filtered-list'); + const displayedElements = Array.from( + fcdaList!.querySelectorAll('mwc-list-item')! + ).filter(item => { + const displayStyle = getComputedStyle(item).display; + return displayStyle !== 'none' || displayStyle === undefined; + }); + expect(displayedElements.length).to.equal(24); + }); + + it('allows filtering of only subscribed control blocks', async () => { + element.actionsMenuIcon.click(); + await element.updateComplete; + (( + element.actionsMenu!.querySelector('.filter-not-subscribed') + ))!.click(); + await new Promise(resolve => setTimeout(resolve, 200)); // await animation + await element.updateComplete; + + const fcdaList = element.shadowRoot?.querySelector('filtered-list'); + const displayedElements = Array.from( + fcdaList!.querySelectorAll('mwc-list-item')! + ).filter(item => { + const displayStyle = getComputedStyle(item).display; + return displayStyle !== 'none' || displayStyle === undefined; + }); + expect(displayedElements.length).to.equal(5); + }); + + it('allows filtering out of all subscribed control blocks', async () => { + element.actionsMenuIcon.click(); + await element.updateComplete; + (( + element.actionsMenu!.querySelector('.filter-subscribed') + ))!.click(); + await new Promise(resolve => setTimeout(resolve, 200)); // await animation + await element.updateComplete; + + element.actionsMenuIcon.click(); + await element.updateComplete; + (( + element.actionsMenu!.querySelector('.filter-not-subscribed') + ))!.click(); + await new Promise(resolve => setTimeout(resolve, 200)); // await animation + await element.updateComplete; + + const fcdaList = element.shadowRoot?.querySelector('filtered-list'); + const displayedElements = Array.from( + fcdaList!.querySelectorAll('mwc-list-item')! + ).filter(item => { + const displayStyle = getComputedStyle(item).display; + return displayStyle !== 'none' || displayStyle === undefined; + }); + expect(displayedElements.length).to.equal(0); + }); }); describe('with a GSEControl doc loaded', () => { beforeEach(async () => { + localStorage.clear(); doc = await fetch('/test/testfiles/editors/LaterBindingGOOSE2007B4.scd') .then(response => response.text()) .then(str => new DOMParser().parseFromString(str, 'application/xml')); @@ -129,6 +207,7 @@ describe('fcda-binding-list', () => { `); @@ -156,5 +235,80 @@ describe('fcda-binding-list', () => { it('looks like the latest snapshot', async () => { await expect(element).shadowDom.to.equalSnapshot(); }); + + it('is initially unfiltered', async () => { + const displayedElements = Array.from( + element.shadowRoot!.querySelectorAll('mwc-list-item') + ).filter(item => { + const displayStyle = getComputedStyle(item).display; + return displayStyle !== 'none' || displayStyle === undefined; + }); + expect(displayedElements.length).to.equal(9); + }); + + it('allows filtering of only not subscribed control blocks', async () => { + element.actionsMenuIcon.click(); + await element.updateComplete; + (( + element.actionsMenu!.querySelector('.filter-subscribed') + ))!.click(); + await new Promise(resolve => setTimeout(resolve, 300)); // await animation + await element.updateComplete; + + const fcdaList = element.shadowRoot?.querySelector('filtered-list'); + const displayedElements = Array.from( + fcdaList!.querySelectorAll('mwc-list-item')! + ).filter(item => { + const displayStyle = getComputedStyle(item).display; + return displayStyle !== 'none' || displayStyle === undefined; + }); + expect(displayedElements.length).to.equal(3); + }); + + it('allows filtering of only subscribed control blocks', async () => { + element.actionsMenuIcon.click(); + await element.updateComplete; + (( + element.actionsMenu!.querySelector('.filter-not-subscribed') + ))!.click(); + await new Promise(resolve => setTimeout(resolve, 300)); // await animation + await element.updateComplete; + + const fcdaList = element.shadowRoot?.querySelector('filtered-list'); + const displayedElements = Array.from( + fcdaList!.querySelectorAll('mwc-list-item')! + ).filter(item => { + const displayStyle = getComputedStyle(item).display; + return displayStyle !== 'none' || displayStyle === undefined; + }); + expect(displayedElements.length).to.equal(6); + }); + + it('allows filtering out of all subscribed control blocks', async () => { + element.actionsMenuIcon.click(); + await element.updateComplete; + (( + element.actionsMenu!.querySelector('.filter-subscribed') + ))!.click(); + await new Promise(resolve => setTimeout(resolve, 300)); // await animation + await element.updateComplete; + + element.actionsMenuIcon.click(); + await element.updateComplete; + (( + element.actionsMenu!.querySelector('.filter-not-subscribed') + ))!.click(); + await new Promise(resolve => setTimeout(resolve, 300)); // await animation + await element.updateComplete; + + const fcdaList = element.shadowRoot?.querySelector('filtered-list'); + const displayedElements = Array.from( + fcdaList!.querySelectorAll('mwc-list-item')! + ).filter(item => { + const displayStyle = getComputedStyle(item).display; + return displayStyle !== 'none' || displayStyle === undefined; + }); + expect(displayedElements.length).to.equal(0); + }); }); }); diff --git a/test/unit/editors/subscription/later-binding/__snapshots__/ext-ref-ln-binding-list.test.snap.js b/test/unit/editors/subscription/later-binding/__snapshots__/ext-ref-ln-binding-list.test.snap.js index 5afa44ad2f..28bddb134d 100644 --- a/test/unit/editors/subscription/later-binding/__snapshots__/ext-ref-ln-binding-list.test.snap.js +++ b/test/unit/editors/subscription/later-binding/__snapshots__/ext-ref-ln-binding-list.test.snap.js @@ -52,7 +52,7 @@ snapshots["for Sampled Value Control when SVC has no subscriptions looks like th aria-disabled="false" noninteractive="" tabindex="-1" - value="Some LN title (LLN0) SMV_Subscriber1>>Overvoltage PTRC - 1 SMV_Subscriber1>>Overvoltage Some LN title (LLN0) SMV_Subscriber1>>Overcurrent PTRC - 1 SMV_Subscriber1>>Overcurrent Some LN title (LLN0) SMV_Subscriber2>>Overvoltage PTRC - 1 SMV_Subscriber2>>Overvoltage Some LN title (LLN0) SMV_Subscriber2>>Overcurrent PTRC - 1 SMV_Subscriber2>>Overcurrent" + value="LLN0 SMV_Subscriber1>>Overvoltage PTRC - 1 SMV_Subscriber1>>Overvoltage LLN0 SMV_Subscriber1>>Overcurrent PTRC - 1 SMV_Subscriber1>>Overcurrent LLN0 SMV_Subscriber2>>Overvoltage PTRC - 1 SMV_Subscriber2>>Overvoltage LLN0 SMV_Subscriber2>>Overcurrent PTRC - 1 SMV_Subscriber2>>Overcurrent" > [subscription.subscriber.availableToSubscribe] @@ -72,7 +72,7 @@ snapshots["for Sampled Value Control when SVC has no subscriptions looks like th value="SMV_Subscriber1>>Overvoltage" > - Some LN title (LLN0) + LLN0 SMV_Subscriber1>>Overvoltage @@ -108,7 +108,7 @@ snapshots["for Sampled Value Control when SVC has no subscriptions looks like th value="SMV_Subscriber1>>Overcurrent" > - Some LN title (LLN0) + LLN0 SMV_Subscriber1>>Overcurrent @@ -145,7 +145,7 @@ snapshots["for Sampled Value Control when SVC has no subscriptions looks like th value="SMV_Subscriber2>>Overvoltage" > - Some LN title (LLN0) + LLN0 SMV_Subscriber2>>Overvoltage @@ -183,7 +183,7 @@ snapshots["for Sampled Value Control when SVC has no subscriptions looks like th value="SMV_Subscriber2>>Overcurrent" > - Some LN title (LLN0) + LLN0 SMV_Subscriber2>>Overcurrent @@ -226,7 +226,7 @@ snapshots["for Sampled Value Control when SVC has a single subscriptions looks l aria-disabled="false" noninteractive="" tabindex="-1" - value="Some LN title (LLN0) SMV_Subscriber1>>Overvoltage" + value="LLN0 SMV_Subscriber1>>Overvoltage" > [subscription.subscriber.subscribed] @@ -246,7 +246,7 @@ snapshots["for Sampled Value Control when SVC has a single subscriptions looks l value="SMV_Subscriber1>>Overvoltage" > - Some LN title (LLN0) + LLN0 SMV_Subscriber1>>Overvoltage @@ -259,7 +259,7 @@ snapshots["for Sampled Value Control when SVC has a single subscriptions looks l aria-disabled="false" noninteractive="" tabindex="-1" - value="PTRC - 1 SMV_Subscriber1>>Overvoltage Some LN title (LLN0) SMV_Subscriber1>>Overcurrent PTRC - 1 SMV_Subscriber1>>Overcurrent Some LN title (LLN0) SMV_Subscriber2>>Overvoltage PTRC - 1 SMV_Subscriber2>>Overvoltage Some LN title (LLN0) SMV_Subscriber2>>Overcurrent PTRC - 1 SMV_Subscriber2>>Overcurrent" + value="PTRC - 1 SMV_Subscriber1>>Overvoltage LLN0 SMV_Subscriber1>>Overcurrent PTRC - 1 SMV_Subscriber1>>Overcurrent LLN0 SMV_Subscriber2>>Overvoltage PTRC - 1 SMV_Subscriber2>>Overvoltage LLN0 SMV_Subscriber2>>Overcurrent PTRC - 1 SMV_Subscriber2>>Overcurrent" > [subscription.subscriber.availableToSubscribe] @@ -297,7 +297,7 @@ snapshots["for Sampled Value Control when SVC has a single subscriptions looks l value="SMV_Subscriber1>>Overcurrent" > - Some LN title (LLN0) + LLN0 SMV_Subscriber1>>Overcurrent @@ -334,7 +334,7 @@ snapshots["for Sampled Value Control when SVC has a single subscriptions looks l value="SMV_Subscriber2>>Overvoltage" > - Some LN title (LLN0) + LLN0 SMV_Subscriber2>>Overvoltage @@ -372,7 +372,7 @@ snapshots["for Sampled Value Control when SVC has a single subscriptions looks l value="SMV_Subscriber2>>Overcurrent" > - Some LN title (LLN0) + LLN0 SMV_Subscriber2>>Overcurrent @@ -415,7 +415,7 @@ snapshots["when SVC has a multiple subscriptions looks like the latest snapshot, aria-disabled="false" noninteractive="" tabindex="-1" - value="Some LN title (LLN0) SMV_Subscriber1>>Overvoltage Some LN title (LLN0) SMV_Subscriber2>>Overvoltage" + value="LLN0 SMV_Subscriber1>>Overvoltage LLN0 SMV_Subscriber2>>Overvoltage" > [subscription.subscriber.subscribed] @@ -435,7 +435,7 @@ snapshots["when SVC has a multiple subscriptions looks like the latest snapshot, value="SMV_Subscriber1>>Overvoltage" > - Some LN title (LLN0) + LLN0 SMV_Subscriber1>>Overvoltage @@ -451,7 +451,7 @@ snapshots["when SVC has a multiple subscriptions looks like the latest snapshot, value="SMV_Subscriber2>>Overvoltage" > - Some LN title (LLN0) + LLN0 SMV_Subscriber2>>Overvoltage @@ -464,7 +464,7 @@ snapshots["when SVC has a multiple subscriptions looks like the latest snapshot, aria-disabled="false" noninteractive="" tabindex="-1" - value="PTRC - 1 SMV_Subscriber1>>Overvoltage Some LN title (LLN0) SMV_Subscriber1>>Overcurrent PTRC - 1 SMV_Subscriber1>>Overcurrent PTRC - 1 SMV_Subscriber2>>Overvoltage Some LN title (LLN0) SMV_Subscriber2>>Overcurrent PTRC - 1 SMV_Subscriber2>>Overcurrent" + value="PTRC - 1 SMV_Subscriber1>>Overvoltage LLN0 SMV_Subscriber1>>Overcurrent PTRC - 1 SMV_Subscriber1>>Overcurrent PTRC - 1 SMV_Subscriber2>>Overvoltage LLN0 SMV_Subscriber2>>Overcurrent PTRC - 1 SMV_Subscriber2>>Overcurrent" > [subscription.subscriber.availableToSubscribe] @@ -502,7 +502,7 @@ snapshots["when SVC has a multiple subscriptions looks like the latest snapshot, value="SMV_Subscriber1>>Overcurrent" > - Some LN title (LLN0) + LLN0 SMV_Subscriber1>>Overcurrent @@ -558,7 +558,7 @@ snapshots["when SVC has a multiple subscriptions looks like the latest snapshot, value="SMV_Subscriber2>>Overcurrent" > - Some LN title (LLN0) + LLN0 SMV_Subscriber2>>Overcurrent @@ -633,7 +633,7 @@ snapshots["for GOOSE Control when GSEControl has no subscriptions looks like the aria-disabled="false" noninteractive="" tabindex="-1" - value="Some LN title (LLN0) GOOSE_Subscriber1>>Earth_Switch CILO - 1 GOOSE_Subscriber1>>Earth_Switch CSWI - 1 GOOSE_Subscriber1>>Earth_Switch XSWI - 1 GOOSE_Subscriber1>>Earth_Switch Some LN title (LLN0) GOOSE_Subscriber2>>Earth_Switch CILO - 1 GOOSE_Subscriber2>>Earth_Switch CSWI - 1 GOOSE_Subscriber2>>Earth_Switch XSWI - 1 GOOSE_Subscriber2>>Earth_Switch" + value="LLN0 GOOSE_Subscriber1>>Earth_Switch CILO - 1 GOOSE_Subscriber1>>Earth_Switch CSWI - 1 GOOSE_Subscriber1>>Earth_Switch XSWI - 1 GOOSE_Subscriber1>>Earth_Switch LLN0 GOOSE_Subscriber2>>Earth_Switch CILO - 1 GOOSE_Subscriber2>>Earth_Switch CSWI - 1 GOOSE_Subscriber2>>Earth_Switch XSWI - 1 GOOSE_Subscriber2>>Earth_Switch" > [subscription.subscriber.availableToSubscribe] @@ -654,7 +654,7 @@ snapshots["for GOOSE Control when GSEControl has no subscriptions looks like the value="GOOSE_Subscriber1>>Earth_Switch" > - Some LN title (LLN0) + LLN0 GOOSE_Subscriber1>>Earth_Switch @@ -729,7 +729,7 @@ snapshots["for GOOSE Control when GSEControl has no subscriptions looks like the value="GOOSE_Subscriber2>>Earth_Switch" > - Some LN title (LLN0) + LLN0 GOOSE_Subscriber2>>Earth_Switch @@ -807,7 +807,7 @@ snapshots["for GOOSE Control when GSEControl has a single subscription looks lik aria-disabled="false" noninteractive="" tabindex="-1" - value="Some LN title (LLN0) GOOSE_Subscriber2>>Earth_Switch" + value="LLN0 GOOSE_Subscriber2>>Earth_Switch" > [subscription.subscriber.subscribed] @@ -827,7 +827,7 @@ snapshots["for GOOSE Control when GSEControl has a single subscription looks lik value="GOOSE_Subscriber2>>Earth_Switch" > - Some LN title (LLN0) + LLN0 GOOSE_Subscriber2>>Earth_Switch @@ -840,7 +840,7 @@ snapshots["for GOOSE Control when GSEControl has a single subscription looks lik aria-disabled="false" noninteractive="" tabindex="-1" - value="Some LN title (LLN0) GOOSE_Subscriber1>>Earth_Switch CILO - 1 GOOSE_Subscriber1>>Earth_Switch CSWI - 1 GOOSE_Subscriber1>>Earth_Switch XSWI - 1 GOOSE_Subscriber1>>Earth_Switch CILO - 1 GOOSE_Subscriber2>>Earth_Switch CSWI - 1 GOOSE_Subscriber2>>Earth_Switch XSWI - 1 GOOSE_Subscriber2>>Earth_Switch" + value="LLN0 GOOSE_Subscriber1>>Earth_Switch CILO - 1 GOOSE_Subscriber1>>Earth_Switch CSWI - 1 GOOSE_Subscriber1>>Earth_Switch XSWI - 1 GOOSE_Subscriber1>>Earth_Switch CILO - 1 GOOSE_Subscriber2>>Earth_Switch CSWI - 1 GOOSE_Subscriber2>>Earth_Switch XSWI - 1 GOOSE_Subscriber2>>Earth_Switch" > [subscription.subscriber.availableToSubscribe] @@ -861,7 +861,7 @@ snapshots["for GOOSE Control when GSEControl has a single subscription looks lik value="GOOSE_Subscriber1>>Earth_Switch" > - Some LN title (LLN0) + LLN0 GOOSE_Subscriber1>>Earth_Switch @@ -996,7 +996,7 @@ snapshots["when GSEControl has a multiple subscriptions looks like the latest sn aria-disabled="false" noninteractive="" tabindex="-1" - value="Some LN title (LLN0) GOOSE_Subscriber1>>Earth_Switch Some LN title (LLN0) GOOSE_Subscriber2>>Earth_Switch CILO - 1 GOOSE_Subscriber2>>Earth_Switch" + value="LLN0 GOOSE_Subscriber1>>Earth_Switch LLN0 GOOSE_Subscriber2>>Earth_Switch CILO - 1 GOOSE_Subscriber2>>Earth_Switch" > [subscription.subscriber.subscribed] @@ -1017,7 +1017,7 @@ snapshots["when GSEControl has a multiple subscriptions looks like the latest sn value="GOOSE_Subscriber1>>Earth_Switch" > - Some LN title (LLN0) + LLN0 GOOSE_Subscriber1>>Earth_Switch @@ -1032,7 +1032,7 @@ snapshots["when GSEControl has a multiple subscriptions looks like the latest sn value="GOOSE_Subscriber2>>Earth_Switch" > - Some LN title (LLN0) + LLN0 GOOSE_Subscriber2>>Earth_Switch From 18bb1f5eb729704950fa73ad7920d7b25fb2addc Mon Sep 17 00:00:00 2001 From: danyill Date: Mon, 27 Mar 2023 21:13:02 +1300 Subject: [PATCH 17/27] Pretty print XML inside code editor, closes #895 (#1199) --- src/wizard-dialog.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wizard-dialog.ts b/src/wizard-dialog.ts index 1a2faf4c21..c1d58bf44b 100644 --- a/src/wizard-dialog.ts +++ b/src/wizard-dialog.ts @@ -42,6 +42,7 @@ import { identity, WizardInput, WizardMenuActor, + formatXml, } from './foundation.js'; function renderWizardInput( @@ -325,7 +326,9 @@ export class WizardDialog extends LitElement { style="width: 80vw; height: calc(100vh - 240px);" theme="ace/theme/solarized_${localStorage.getItem('theme')}" mode="ace/mode/xml" - value="${new XMLSerializer().serializeToString(page.element)}" + value="${formatXml( + new XMLSerializer().serializeToString(page.element) + )}" >` : page.content?.map(renderWizardInput)}
    From 3ef37084c5fbee0f106fe38280fc9646a3092d50 Mon Sep 17 00:00:00 2001 From: marcvanraalte <86408026+marcvanraalte@users.noreply.github.com> Date: Thu, 30 Mar 2023 10:36:23 +0200 Subject: [PATCH 18/27] 981 edit wizard line (#1204) * feat(line-editor.ts):unit_test_added * feat(line-editor):add_edit_wizard * feat(line-editor-wizard-editing.test):part-of-test * feat(line-editor_wizard_editing)_continue * feat(line.test,etc):editing_and_testing * fix(line.ts):removed_parameters --------- Co-authored-by: Juan Munoz --- src/editors/substation/line-editor.ts | 25 +++- src/editors/substation/tapchanger-editor.ts | 48 +++---- src/editors/substation/zeroline-pane.ts | 48 ++++--- src/translations/de.ts | 15 ++ src/translations/en.ts | 15 ++ src/wizards/line.ts | 111 ++++++++++++++ src/wizards/wizard-library.ts | 3 +- .../line-editor-wizard-editing.test.ts | 110 ++++++++++++++ .../tapchanger-editor-wizard-editing.test.ts | 2 +- test/testfiles/editors/substation/Line.scd | 2 +- .../__snapshots__/line-editor.test.snap.js | 21 +++ .../wizards/__snapshots__/line.test.snap.js | 70 +++++++++ test/unit/wizards/line.test.ts | 136 ++++++++++++++++++ 13 files changed, 552 insertions(+), 54 deletions(-) create mode 100644 src/wizards/line.ts create mode 100644 test/integration/editors/substation/line-editor-wizard-editing.test.ts create mode 100644 test/unit/wizards/__snapshots__/line.test.snap.js create mode 100644 test/unit/wizards/line.test.ts diff --git a/src/editors/substation/line-editor.ts b/src/editors/substation/line-editor.ts index 459c200e6e..18f7e0f619 100644 --- a/src/editors/substation/line-editor.ts +++ b/src/editors/substation/line-editor.ts @@ -7,11 +7,17 @@ import { state, } from 'lit-element'; +import { translate } from 'lit-translate'; + +import '@material/mwc-icon'; +import '@material/mwc-icon-button'; + import './conducting-equipment-editor.js'; import './function-editor.js'; import './general-equipment-editor.js'; import './l-node-editor.js'; -import { getChildElementsByTagName } from '../../foundation.js'; +import { getChildElementsByTagName, newWizardEvent } from '../../foundation.js'; +import { wizards } from '../../wizards/wizard-library.js'; @customElement('line-editor') export class LineEditor extends LitElement { @@ -33,6 +39,11 @@ export class LineEditor extends LitElement { return `${name} ${desc ? `—${desc}` : ''}`; } + private openEditWizard(): void { + const wizard = wizards['Line'].edit(this.element); + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + private renderConductingEquipments(): TemplateResult { const ConductingEquipments = getChildElementsByTagName( this.element, @@ -95,9 +106,13 @@ export class LineEditor extends LitElement { } render(): TemplateResult { - return html` ${this.renderConductingEquipments()}${this.renderGeneralEquipments()}${this.renderFunctions()}${this.renderLNodes()} - `; + return html` + + this.openEditWizard()} + > ${this.renderConductingEquipments()}${this.renderGeneralEquipments()}${this.renderFunctions()}${this.renderLNodes()} + `; } } diff --git a/src/editors/substation/tapchanger-editor.ts b/src/editors/substation/tapchanger-editor.ts index bca1a0043c..aadd903bae 100644 --- a/src/editors/substation/tapchanger-editor.ts +++ b/src/editors/substation/tapchanger-editor.ts @@ -64,7 +64,7 @@ export class TapChangerEditor extends LitElement { @query('mwc-menu') addMenu!: Menu; @query('mwc-icon-button[icon="playlist_add"]') addButton!: IconButton; - openEditWizard(): void { + private openEditWizard(): void { const wizard = wizards['TapChanger'].edit(this.element); if (wizard) this.dispatchEvent(newWizardEvent(wizard)); } @@ -147,8 +147,8 @@ export class TapChangerEditor extends LitElement { } render(): TemplateResult { - return html` - + return html` + this.openEditWizard()} @@ -161,27 +161,27 @@ export class TapChangerEditor extends LitElement { > - (this.addMenu.open = true)} - > { - const tagName = ((e.target).selected).value; - this.openCreateWizard(tagName); - }} - >${this.renderAddButtons()} - ${this.renderLNodes()} - ${this.renderEqFunctions()} ${this.renderSubEquipments()} - `; + slot="action" + style="position:relative;" + title="${translate('add')}" + > + (this.addMenu.open = true)} + > { + const tagName = ((e.target).selected).value; + this.openCreateWizard(tagName); + }} + >${this.renderAddButtons()} + ${this.renderLNodes()} ${this.renderEqFunctions()} + ${this.renderSubEquipments()} + `; } static styles = css` diff --git a/src/editors/substation/zeroline-pane.ts b/src/editors/substation/zeroline-pane.ts index 146a860ea6..bb85502588 100644 --- a/src/editors/substation/zeroline-pane.ts +++ b/src/editors/substation/zeroline-pane.ts @@ -122,6 +122,29 @@ export class ZerolinePane extends LitElement { : html``; } + renderSubstation(): TemplateResult { + return this.doc?.querySelector(':root > Substation') + ? html`
    + ${Array.from(this.doc.querySelectorAll('Substation') ?? []) + .filter(isPublic) + .map( + substation => + html`` + )} +
    ` + : html`

    + ${translate('substation.missing')} +

    `; + } + renderLines(): TemplateResult { return this.doc?.querySelector(':root > Line') ? html`
    @@ -131,8 +154,8 @@ export class ZerolinePane extends LitElement { line => html`` )}
    ` @@ -200,26 +223,7 @@ export class ZerolinePane extends LitElement { ${this.renderIedContainer()} - ${this.doc?.querySelector(':root > Substation') - ? html`
    - ${Array.from(this.doc.querySelectorAll('Substation') ?? []) - .filter(isPublic) - .map( - substation => - html`` - )} -
    ` - : html`

    - ${translate('substation.missing')} -

    `}${this.renderLines()}`; + ${this.renderSubstation()}${this.renderLines()}`; } static styles = css` diff --git a/src/translations/de.ts b/src/translations/de.ts index c284cdc02b..278432b6bc 100644 --- a/src/translations/de.ts +++ b/src/translations/de.ts @@ -288,6 +288,21 @@ export const de: Translations = { updateVoltagelevel: 'Spannungsebene "{{name}}" bearbeitet', }, }, + line: { + name: 'Linie', + wizard: { + nameHelper: 'Liniename', + descHelper: 'Beschreibung des Linies', + typeHelper: 'Type des Linies', + title: { + add: 'Linie hinzufügen', + edit: 'Linie bearbeiten', + }, + }, + action: { + updateLine: 'Edited line "{{name}}"', + }, + }, bay: { name: 'Feld', wizard: { diff --git a/src/translations/en.ts b/src/translations/en.ts index 20e9e4986e..3e96b658aa 100644 --- a/src/translations/en.ts +++ b/src/translations/en.ts @@ -285,6 +285,21 @@ export const en = { updateVoltagelevel: 'Edited voltagelevel "{{name}}"', }, }, + line: { + name: 'Line', + wizard: { + nameHelper: 'Line name', + descHelper: 'Line description', + typeHelper: 'Line type', + title: { + add: 'Add line', + edit: 'Edit line', + }, + }, + action: { + updateLine: 'Edited line "{{name}}"', + }, + }, bay: { name: 'Bay', wizard: { diff --git a/src/wizards/line.ts b/src/wizards/line.ts new file mode 100644 index 0000000000..45c4687b3d --- /dev/null +++ b/src/wizards/line.ts @@ -0,0 +1,111 @@ +import { html, TemplateResult } from 'lit-html'; +import { get, translate } from 'lit-translate'; + +import '../wizard-textfield.js'; +import { + cloneElement, + getValue, + patterns, + SimpleAction, + Wizard, + WizardActor, + WizardInputElement, +} from '../foundation.js'; + +const initial = { + nomFreq: '50', + numPhases: '3', +}; + +function render( + name: string, + desc: string | null, + type: string | null, + nomFreq: string | null, + numPhases: string | null +): TemplateResult[] { + return [ + html``, + html``, + html``, + html``, + html``, + ]; +} + +function updateAction(element: Element): WizardActor { + return (inputs: WizardInputElement[]): SimpleAction[] => { + const lineAttrs: Record = {}; + const lineKeys = ['name', 'desc', 'type', 'nomFreq', 'numPhases']; + lineKeys.forEach(key => { + lineAttrs[key] = getValue(inputs.find(i => i.label === key)!); + }); + + if (lineKeys.some(key => lineAttrs[key] !== element.getAttribute(key))) { + const newElement = cloneElement(element, lineAttrs); + return [ + { + old: { element }, + new: { element: newElement }, + }, + ]; + } + return []; + }; +} + +export function editLineWizard(element: Element): Wizard { + return [ + { + title: get('line.wizard.title.edit'), + element, + primary: { + icon: 'edit', + label: get('save'), + action: updateAction(element), + }, + content: render( + element.getAttribute('name') ?? '', + element.getAttribute('desc'), + element.getAttribute('type'), + element.getAttribute('nomFreq'), + element.getAttribute('numPhases') + ), + }, + ]; +} diff --git a/src/wizards/wizard-library.ts b/src/wizards/wizard-library.ts index e0203ec7e3..94a3b3d280 100644 --- a/src/wizards/wizard-library.ts +++ b/src/wizards/wizard-library.ts @@ -49,6 +49,7 @@ import { editTransformerWindingWizard, } from './transformerWinding.js'; import { createTapChangerWizard, editTapChangerWizard } from './tapchanger.js'; +import { editLineWizard } from './line.js'; type SclElementWizard = ( element: Element, @@ -335,7 +336,7 @@ export const wizards: Record< create: emptyWizard, }, Line: { - edit: emptyWizard, + edit: editLineWizard, create: emptyWizard, }, Log: { diff --git a/test/integration/editors/substation/line-editor-wizard-editing.test.ts b/test/integration/editors/substation/line-editor-wizard-editing.test.ts new file mode 100644 index 0000000000..127bbf1320 --- /dev/null +++ b/test/integration/editors/substation/line-editor-wizard-editing.test.ts @@ -0,0 +1,110 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import '../../../mock-wizard-editor.js'; +import { MockWizardEditor } from '../../../mock-wizard-editor.js'; +import { ListItemBase } from '@material/mwc-list/mwc-list-item-base'; + +import '../../../../src/editors/substation/line-editor.js'; +import { LineEditor } from '../../../../src/editors/substation/line-editor.js'; +import { WizardTextField } from '../../../../src/wizard-textfield.js'; +import { WizardCheckbox } from '../../../../src/wizard-checkbox.js'; +import { MenuBase } from '@material/mwc-menu/mwc-menu-base.js'; + +describe('line-editor wizarding editing integration', () => { + let doc: XMLDocument; + let parent: MockWizardEditor; + let element: LineEditor | null; + + describe('edit wizard', () => { + let nameField: WizardTextField; + + let primaryAction: HTMLElement; + let secondaryAction: HTMLElement; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/Line.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + parent = ( + await fixture( + html`` + ) + ); + element = parent.querySelector('line-editor'); + await (( + element?.shadowRoot?.querySelector('mwc-icon-button[icon="edit"]') + )).click(); + await parent.updateComplete; + + nameField = ( + parent.wizardUI.dialog?.querySelector('wizard-textfield[label="name"]') + ); + + secondaryAction = ( + parent.wizardUI.dialog?.querySelector( + 'mwc-button[slot="secondaryAction"]' + ) + ); + primaryAction = ( + parent.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + }); + it('closes on secondary action', async () => { + secondaryAction.click(); + await new Promise(resolve => setTimeout(resolve, 100)); // await animation + expect(parent.wizardUI.dialog).to.not.exist; + }); + + it('does not change name attribute if not unique within parent element', async () => { + const oldName = nameField.value; + nameField.value = 'Munich'; + primaryAction.click(); + await parent.updateComplete; + expect( + doc.querySelector('Line[name="Berlin"]')?.getAttribute('name') + ).to.equal(oldName); + }); + + it('changes desc attribute on primary action', async () => { + parent.wizardUI.inputs[1].value = 'newDesc'; + primaryAction.click(); + await parent.updateComplete; + expect( + doc.querySelector('Line[name="Berlin"]')?.getAttribute('desc') + ).to.equal('newDesc'); + }); + + it('changes type attribute on primary action', async () => { + parent.wizardUI.inputs[2].value = 'newType'; + primaryAction.click(); + await parent.updateComplete; + expect( + doc.querySelector('Line[name="Berlin"]')?.getAttribute('type') + ).to.equal('newType'); + }); + + it('changes nomFreq attribute on primary action', async () => { + parent.wizardUI.inputs[3].value = '50'; + primaryAction.click(); + await parent.updateComplete; + expect( + doc.querySelector('Line[name="Berlin"]')?.getAttribute('nomFreq') + ).to.equal('50'); + }); + + it('changes numPhases attribute on primary action', async () => { + parent.wizardUI.inputs[4].value = '3'; + primaryAction.click(); + await parent.updateComplete; + expect( + doc.querySelector('Line[name="Berlin"]')?.getAttribute('numPhases') + ).to.equal('3'); + }); + }); +}); diff --git a/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts b/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts index fba8dbf9d2..63c3a9b2fc 100644 --- a/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts +++ b/test/integration/editors/substation/tapchanger-editor-wizard-editing.test.ts @@ -4,7 +4,7 @@ import '../../../mock-wizard-editor.js'; import { MockWizardEditor } from '../../../mock-wizard-editor.js'; import { ListItemBase } from '@material/mwc-list/mwc-list-item-base'; -import '../../../../src/editors/substation/tapchanger-editor'; +import '../../../../src/editors/substation/tapchanger-editor.js'; import { TapChangerEditor } from '../../../../src/editors/substation/tapchanger-editor.js'; import { WizardTextField } from '../../../../src/wizard-textfield.js'; import { WizardCheckbox } from '../../../../src/wizard-checkbox.js'; diff --git a/test/testfiles/editors/substation/Line.scd b/test/testfiles/editors/substation/Line.scd index 3cbdb667bf..34b7904fcf 100644 --- a/test/testfiles/editors/substation/Line.scd +++ b/test/testfiles/editors/substation/Line.scd @@ -207,7 +207,7 @@ process - + diff --git a/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js index ae6dbfe89d..1dab9893c2 100644 --- a/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/line-editor.test.snap.js @@ -6,6 +6,13 @@ snapshots["web component rendering Line element rendering LNode and Function chi label="Berlin " tabindex="0" > + + + + @@ -23,6 +30,13 @@ snapshots["web component rendering Line element rendering ConductingEquipment lo label="Berlin " tabindex="0" > + + + + @@ -40,6 +54,13 @@ snapshots["web component rendering Line element rendering GeneralEquipment looks label="Munich " tabindex="0" > + + + + diff --git a/test/unit/wizards/__snapshots__/line.test.snap.js b/test/unit/wizards/__snapshots__/line.test.snap.js new file mode 100644 index 0000000000..52ee88afc3 --- /dev/null +++ b/test/unit/wizards/__snapshots__/line.test.snap.js @@ -0,0 +1,70 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["Wizards for SCL Line element define an edit wizard that looks like the the latest snapshot"] = +` +
    + + + + + + + + + + +
    + + + + +
    +`; +/* end snapshot Wizards for SCL Line element define an edit wizard that looks like the the latest snapshot */ + diff --git a/test/unit/wizards/line.test.ts b/test/unit/wizards/line.test.ts new file mode 100644 index 0000000000..c810a76894 --- /dev/null +++ b/test/unit/wizards/line.test.ts @@ -0,0 +1,136 @@ +import { expect, fixture, html } from '@open-wc/testing'; +import '../../mock-wizard.js'; +import { MockWizard } from '../../mock-wizard.js'; + +import { WizardTextField } from '../../../src/wizard-textfield.js'; +import { SinonSpy, spy } from 'sinon'; + +import { + Create, + isCreate, + isReplace, + Replace, + WizardInputElement, +} from '../../../src/foundation.js'; +import { editLineWizard } from '../../../src/wizards/line.js'; +import { WizardCheckbox } from '../../../src/wizard-checkbox.js'; + +describe('Wizards for SCL Line element', () => { + let doc: XMLDocument; + let element: MockWizard; + let inputs: WizardInputElement[]; + + let primaryAction: HTMLElement; + let actionEvent: SinonSpy; + + beforeEach(async () => { + element = await fixture(html``); + doc = await fetch('/test/testfiles/editors/substation/Line.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + + actionEvent = spy(); + window.addEventListener('editor-action', actionEvent); + }); + describe('define an edit wizard that', () => { + beforeEach(async () => { + const wizard = editLineWizard(doc.querySelector('Line[name="Berlin"]')!); + element.workflow.push(() => wizard); + await element.requestUpdate(); + + inputs = Array.from(element.wizardUI.inputs); + + primaryAction = ( + element.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + + await element.wizardUI.requestUpdate(); // make sure wizard is rendered + }); + + it('looks like the the latest snapshot', async () => + await expect(element.wizardUI.dialog).dom.to.equalSnapshot()); + + it('does not accept empty name attribute', async () => { + inputs[0].value = ''; + await element.requestUpdate(); + await primaryAction.click(); + expect(actionEvent).to.not.have.been.called; + }); + it('triggers simple edit action on primary action click', async () => { + inputs[0].value = 'someNonEmptyName'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isReplace); + const editAction = action; + + expect(editAction.new.element).to.have.attribute( + 'name', + 'someNonEmptyName' + ); + }); + it('allows to create non required attribute desc', async () => { + inputs[1].value = 'someNonEmptyDesc'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isReplace); + const editAction = action; + + expect(editAction.new.element).to.have.attribute( + 'desc', + 'someNonEmptyDesc' + ); + }); + it('allows to create non required attribute type', async () => { + inputs[2].value = 'someNonEmptyType'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isReplace); + const editAction = action; + + expect(editAction.new.element).to.have.attribute( + 'type', + 'someNonEmptyType' + ); + }); + it('allows to create non required attribute nomFreq', async () => { + inputs[3].value = '50'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isReplace); + const editAction = action; + + expect(editAction.new.element).to.have.attribute('nomFreq', '50'); + }); + it('allows to create non required attribute numPhases', async () => { + inputs[4].value = '3'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isReplace); + const editAction = action; + + expect(editAction.new.element).to.have.attribute('numPhases', '3'); + }); + }); +}); From 03632e21957a51b3ca6db6e7169ca88bb1e59a12 Mon Sep 17 00:00:00 2001 From: marcvanraalte <86408026+marcvanraalte@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:18:25 +0200 Subject: [PATCH 19/27] 982 create wizard line (#1207) * feat(line-editor.ts):unit_test_added * feat(line-editor):add_edit_wizard * feat(line-editor-wizard-editing.test):part-of-test * feat(line-editor_wizard_editing)_continue * feat(line.test,etc):editing_and_testing * feat(line.ts):createLineWizard_added * feat(zeroline-pane):create_Line_added * feat(line.test.ts):create_tests_added * fix(zeroline-pane.test.ts):test_add_button * fix(zeroline-pane):render_menu_correctly * fix(zeroline-pane):querySelectorAll * fix(zeroline-pane):add_snapshot --- src/editors/substation/zeroline-pane.ts | 70 ++++++++--- src/wizards/line.ts | 32 +++++ src/wizards/wizard-library.ts | 4 +- .../editors/substation/zeroline-pane.test.ts | 7 +- .../__snapshots__/zeroline-pane.test.snap.js | 68 +++++++++-- .../wizards/__snapshots__/line.test.snap.js | 67 +++++++++++ test/unit/wizards/line.test.ts | 109 +++++++++++++++++- 7 files changed, 327 insertions(+), 30 deletions(-) diff --git a/src/editors/substation/zeroline-pane.ts b/src/editors/substation/zeroline-pane.ts index bb85502588..6b781bb157 100644 --- a/src/editors/substation/zeroline-pane.ts +++ b/src/editors/substation/zeroline-pane.ts @@ -12,7 +12,9 @@ import { translate } from 'lit-translate'; import '@material/mwc-icon-button'; import '@material/mwc-icon-button-toggle'; import { IconButton } from '@material/mwc-icon-button'; +import { ListItem } from '@material/mwc-list/mwc-list-item'; import { IconButtonToggle } from '@material/mwc-icon-button-toggle'; +import { Menu } from '@material/mwc-menu'; import './line-editor.js'; import './substation-editor.js'; @@ -21,12 +23,14 @@ import { communicationMappingWizard } from '../../wizards/commmap-wizards.js'; import { gooseIcon, smvIcon, reportIcon } from '../../icons/icons.js'; import { isPublic, newWizardEvent } from '../../foundation.js'; import { selectGseControlWizard } from '../../wizards/gsecontrol.js'; -import { wizards } from '../../wizards/wizard-library.js'; +import { emptyWizard, wizards } from '../../wizards/wizard-library.js'; import { getAttachedIeds } from './foundation.js'; import { selectSampledValueControlWizard } from '../../wizards/sampledvaluecontrol.js'; import { Settings } from '../../Setting.js'; import { selectReportControlWizard } from '../../wizards/reportcontrol.js'; +import { SCLTag, tags } from '../../foundation.js'; + function shouldShowIEDs(): boolean { return localStorage.getItem('showieds') === 'on'; } @@ -43,6 +47,14 @@ function setShowFunctions(value: 'on' | 'off') { localStorage.setItem('showfunctions', value); } +function childTags(element: Element | null | undefined): SCLTag[] { + if (!element) return []; + + return tags[element.tagName].children.filter( + child => wizards[child].create !== emptyWizard + ); +} + /** [[`Zeroline`]] pane for displaying `Substation` and/or `IED` sections. */ @customElement('zeroline-pane') export class ZerolinePane extends LitElement { @@ -63,17 +75,14 @@ export class ZerolinePane extends LitElement { @query('#reportcontrol') reportcontrol!: IconButton; @query('#createsubstation') createsubstation!: IconButton; + @query('mwc-menu') addMenu!: Menu; + @query('mwc-icon-button[icon="playlist_add"]') addButton!: IconButton; + openCommunicationMapping(): void { const wizard = communicationMappingWizard(this.doc); if (wizard) this.dispatchEvent(newWizardEvent(wizard)); } - /** Opens a [[`WizardDialog`]] for creating a new `Substation` element. */ - openCreateSubstationWizard(): void { - const wizard = wizards['Substation'].create(this.doc.documentElement); - if (wizard) this.dispatchEvent(newWizardEvent(wizard)); - } - openReportControlSelection(): void { this.dispatchEvent( newWizardEvent(() => selectReportControlWizard(this.doc.documentElement)) @@ -125,7 +134,7 @@ export class ZerolinePane extends LitElement { renderSubstation(): TemplateResult { return this.doc?.querySelector(':root > Substation') ? html`
    - ${Array.from(this.doc.querySelectorAll('Substation') ?? []) + ${Array.from(this.doc.querySelectorAll(':root >Substation') ?? []) .filter(isPublic) .map( substation => @@ -148,30 +157,57 @@ export class ZerolinePane extends LitElement { renderLines(): TemplateResult { return this.doc?.querySelector(':root > Line') ? html`
    - ${Array.from(this.doc.querySelectorAll('Line') ?? []) + ${Array.from(this.doc.querySelectorAll(':root >Line') ?? []) .filter(isPublic) .map( line => html`` )}
    ` : html``; } + private openCreateWizard(tagName: string): void { + const wizard = wizards[tagName].create(this.doc.documentElement); + + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + + private renderAddButtons(): TemplateResult[] { + return childTags(this.doc.documentElement).map( + child => + html`${child}` + ); + } + + updated(): void { + if (this.addMenu && this.addButton) + this.addMenu.anchor = this.addButton; + } + render(): TemplateResult { return html`

    diff --git a/test/unit/wizards/__snapshots__/process.test.snap.js b/test/unit/wizards/__snapshots__/process.test.snap.js index b54631785d..7141393744 100644 --- a/test/unit/wizards/__snapshots__/process.test.snap.js +++ b/test/unit/wizards/__snapshots__/process.test.snap.js @@ -50,3 +50,50 @@ snapshots["Wizards for SCL Process element define an edit wizard that looks like `; /* end snapshot Wizards for SCL Process element define an edit wizard that looks like the the latest snapshot */ +snapshots["Wizards for SCL Process element define a create wizard that looks like the the latest snapshot"] = +` +
    + + + + + + +
    + + + + +
    +`; +/* end snapshot Wizards for SCL Process element define a create wizard that looks like the the latest snapshot */ + diff --git a/test/unit/wizards/process.test.ts b/test/unit/wizards/process.test.ts index 93507a036c..0547a57145 100644 --- a/test/unit/wizards/process.test.ts +++ b/test/unit/wizards/process.test.ts @@ -6,11 +6,16 @@ import { WizardTextField } from '../../../src/wizard-textfield.js'; import { SinonSpy, spy } from 'sinon'; import { + Create, + isCreate, isReplace, Replace, WizardInputElement, } from '../../../src/foundation.js'; -import { editProcessWizard } from '../../../src/wizards/process.js'; +import { + createProcessWizard, + editProcessWizard, +} from '../../../src/wizards/process.js'; describe('Wizards for SCL Process element', () => { let doc: XMLDocument; @@ -100,4 +105,82 @@ describe('Wizards for SCL Process element', () => { ); }); }); + describe('define a create wizard that', () => { + beforeEach(async () => { + const wizard = createProcessWizard( + doc.querySelector('Process[name="ProcessGenConduct"]')! + ); + element.workflow.push(() => wizard); + await element.requestUpdate(); + + inputs = Array.from(element.wizardUI.inputs); + + primaryAction = ( + element.wizardUI.dialog?.querySelector( + 'mwc-button[slot="primaryAction"]' + ) + ); + + await element.wizardUI.requestUpdate(); // make sure wizard is rendered + }); + + it('looks like the the latest snapshot', async () => + await expect(element.wizardUI.dialog).dom.to.equalSnapshot()); + + it('does not accept empty name attribute', async () => { + await primaryAction.click(); + + expect(actionEvent).to.not.have.been.called; + }); + + it('allows to create required attributes name', async () => { + inputs[0].value = 'someNonEmptyName'; + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isCreate); + const createAction = action; + + expect(createAction.new.element).to.have.attribute( + 'name', + 'someNonEmptyName' + ); + }); + + it('allows to create name and non required attribute desc', async () => { + inputs[0].value = 'someNonEmptyName'; + inputs[1].value = 'someNonEmptyDesc'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isCreate); + const createAction = action; + expect(createAction.new.element).to.have.attribute( + 'desc', + 'someNonEmptyDesc' + ); + }); + + it('allows to create name and non required attribute type', async () => { + inputs[0].value = 'someNonEmptyName'; + inputs[2].value = 'someNonEmptyType'; + + await element.requestUpdate(); + await primaryAction.click(); + + expect(actionEvent).to.be.calledOnce; + const action = actionEvent.args[0][0].detail.action; + expect(action).to.satisfy(isCreate); + const createAction = action; + expect(createAction.new.element).to.have.attribute( + 'type', + 'someNonEmptyType' + ); + }); + }); }); From efc3aa10c3854c5391c05b69911a6d4f5bb4fc2b Mon Sep 17 00:00:00 2001 From: marcvanraalte <86408026+marcvanraalte@users.noreply.github.com> Date: Mon, 24 Apr 2023 17:39:02 +0200 Subject: [PATCH 25/27] 988 remove process (#1218) * feat(line-editor.ts):unit_test_added * feat(line-editor):add_edit_wizard * feat(line-editor-wizard-editing.test):part-of-test * feat(line-editor_wizard_editing)_continue * feat(line.test,etc):editing_and_testing * feat(line.ts):createLineWizard_added * feat(zeroline-pane):create_Line_added * feat(line.test.ts):create_tests_added * feat(line-editor.ts):remove_line_and_test * feat(line-editor):add_add_button_and_test * feat(process-editor.ts):editor_added * fix(Process.scd):GeneralEquipment_added * fix(Process.scd):GeneralEquipment_added * feat(process-editor):edit_wizard_added * feat(process-editor-wizard-editing.test):added * feat(process-editor-snap):snapshot_updated * feat(process-editor-wizard-editing):added * feat(process.test.ts):test_added * feat(process.ts):create_wizard_added * feat(process.test):test_added * feat(process-editor):delete_added * fix(process.ts):fixed_tagname --------- Co-authored-by: Steffen van den Driest <35229971+Stef3st@users.noreply.github.com> --- src/editors/substation/process-editor.ts | 24 +++++++++++++++- .../process-editor-wizard-editing.test.ts | 17 +++++++++++ .../__snapshots__/process-editor.test.snap.js | 28 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/editors/substation/process-editor.ts b/src/editors/substation/process-editor.ts index b80fe4a967..99656dfe2f 100644 --- a/src/editors/substation/process-editor.ts +++ b/src/editors/substation/process-editor.ts @@ -24,7 +24,11 @@ import './substation-editor.js'; import './process-editor.js'; import { styles } from './foundation.js'; -import { newWizardEvent, getChildElementsByTagName } from '../../foundation.js'; +import { + getChildElementsByTagName, + newActionEvent, + newWizardEvent, +} from '../../foundation.js'; import { wizards } from '../../wizards/wizard-library.js'; @@ -150,6 +154,18 @@ export class ProcessEditor extends LitElement { : html``; } + remove(): void { + if (this.element.parentElement) + this.dispatchEvent( + newActionEvent({ + old: { + parent: this.element.parentElement, + element: this.element, + }, + }) + ); + } + render(): TemplateResult { return html` @@ -158,6 +174,12 @@ export class ProcessEditor extends LitElement { @click=${() => this.openEditWizard()} > + + this.remove()} + > + ${this.renderConductingEquipments()}${this.renderGeneralEquipments()}${this.renderFunctions()}${this.renderLNodes()} ${this.renderLines()} ${this.renderSubstations()}${this.renderProcesses()} `; diff --git a/test/integration/editors/substation/process-editor-wizard-editing.test.ts b/test/integration/editors/substation/process-editor-wizard-editing.test.ts index 22d0f06076..dc54f374a4 100644 --- a/test/integration/editors/substation/process-editor-wizard-editing.test.ts +++ b/test/integration/editors/substation/process-editor-wizard-editing.test.ts @@ -129,5 +129,22 @@ describe('process-editor wizarding editing integration', () => { ?.getAttribute('type') ).to.equal('newType'); }); + describe('has a delete icon button that', () => { + let deleteButton: HTMLElement; + + beforeEach(async () => { + deleteButton = ( + element?.shadowRoot?.querySelector('mwc-icon-button[icon="delete"]') + ); + await parent.updateComplete; + }); + + it('removes the Process element from the document', async () => { + expect(doc.querySelector('Process[name="ProcessGenConduct"]')).to.exist; + await deleteButton.click(); + expect(doc.querySelector('Process[name="ProcessGenConduct"]')).to.not + .exist; + }); + }); }); }); diff --git a/test/unit/editors/substation/__snapshots__/process-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/process-editor.test.snap.js index cf67d15110..7b9ad56e56 100644 --- a/test/unit/editors/substation/__snapshots__/process-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/process-editor.test.snap.js @@ -13,6 +13,13 @@ snapshots["web component rendering Process element rendering LNode, GeneralEquip + + + + @@ -39,6 +46,13 @@ snapshots["web component rendering Process element hides LNode and Function chil + + + + @@ -59,6 +73,13 @@ snapshots["web component rendering Process element rendering Substation and Proc + + + + @@ -79,6 +100,13 @@ snapshots["web component rendering Process element rendering a Line child looks + + + + From 6ce21056ab2acf46cff4e26c978d7e1d6feca774 Mon Sep 17 00:00:00 2001 From: marcvanraalte <86408026+marcvanraalte@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:14:10 +0200 Subject: [PATCH 26/27] 989 add process (#1219) * feat(line-editor.ts):unit_test_added * feat(line-editor):add_edit_wizard * feat(line-editor-wizard-editing.test):part-of-test * feat(line-editor_wizard_editing)_continue * feat(line.test,etc):editing_and_testing * feat(line.ts):createLineWizard_added * feat(zeroline-pane):create_Line_added * feat(line.test.ts):create_tests_added * feat(line-editor.ts):remove_line_and_test * feat(line-editor):add_add_button_and_test * feat(process-editor.ts):editor_added * fix(Process.scd):GeneralEquipment_added * fix(Process.scd):GeneralEquipment_added * feat(process-editor):edit_wizard_added * feat(process-editor-wizard-editing.test):added * feat(process-editor-snap):snapshot_updated * feat(process-editor-wizard-editing):added * feat(process.test.ts):test_added * feat(process.ts):create_wizard_added * feat(process.test):test_added * feat(process-editor):delete_added * feat(process-editor):add_added * fix(process.ts):fixed_tagname * fix: fixed snapshot test * fix: substation warning, styling, guess correction --------- Co-authored-by: Steffen van den Driest <35229971+Stef3st@users.noreply.github.com> Co-authored-by: Stef3st --- src/editors/Substation.ts | 4 +- src/editors/substation/process-editor.ts | 58 ++- src/editors/substation/zeroline-pane.ts | 8 +- src/wizards/substation.ts | 4 +- .../process-editor-wizard-editing.test.ts | 69 ++++ .../__snapshots__/process-editor.test.snap.js | 360 ++++++++++++++++++ 6 files changed, 498 insertions(+), 5 deletions(-) diff --git a/src/editors/Substation.ts b/src/editors/Substation.ts index 931563d1d8..c23ca9f1c1 100644 --- a/src/editors/Substation.ts +++ b/src/editors/Substation.ts @@ -21,7 +21,9 @@ export default class SubstationPlugin extends LitElement { render(): TemplateResult { return html` - ${!this.doc?.querySelector(':root > Substation') + ${!this.doc?.querySelector( + ':root > Substation, :root > Line, :root > Process' + ) ? html`

    element.tagName].children.filter( + child => wizards[child].create !== emptyWizard + ); +} @customElement('process-editor') export class ProcessEditor extends LitElement { @@ -52,11 +66,20 @@ export class ProcessEditor extends LitElement { return `${name} ${desc ? `—${desc}` : ''}`; } + @query('mwc-menu') addMenu!: Menu; + @query('mwc-icon-button[icon="playlist_add"]') addButton!: IconButton; + private openEditWizard(): void { const wizard = wizards['Process'].edit(this.element); if (wizard) this.dispatchEvent(newWizardEvent(wizard)); } + private openCreateWizard(tagName: string): void { + const wizard = wizards[tagName].create(this.element!); + + if (wizard) this.dispatchEvent(newWizardEvent(wizard)); + } + private renderConductingEquipments(): TemplateResult { const ConductingEquipments = getChildElementsByTagName( this.element, @@ -154,6 +177,20 @@ export class ProcessEditor extends LitElement { : html``; } + private renderAddButtons(): TemplateResult[] { + return childTags(this.element).map( + child => + html`${child}` + ); + } + + updated(): void { + if (this.addMenu && this.addButton) + this.addMenu.anchor = this.addButton; + } + remove(): void { if (this.element.parentElement) this.dispatchEvent( @@ -180,6 +217,25 @@ export class ProcessEditor extends LitElement { @click=${() => this.remove()} > + + (this.addMenu.open = true)} + > { + const tagName = ((e.target).selected).value; + this.openCreateWizard(tagName); + }} + >${this.renderAddButtons()} ${this.renderConductingEquipments()}${this.renderGeneralEquipments()}${this.renderFunctions()}${this.renderLNodes()} ${this.renderLines()} ${this.renderSubstations()}${this.renderProcesses()} `; diff --git a/src/editors/substation/zeroline-pane.ts b/src/editors/substation/zeroline-pane.ts index e10a981894..b493cf725d 100644 --- a/src/editors/substation/zeroline-pane.ts +++ b/src/editors/substation/zeroline-pane.ts @@ -148,11 +148,13 @@ export class ZerolinePane extends LitElement { >` )}

    ` - : html`

    + : !this.doc?.querySelector(':root > Line, :root > Process') + ? html`

    ${translate('substation.missing')} -

    `; + ` + : html``; } renderLines(): TemplateResult { @@ -305,6 +307,8 @@ export class ZerolinePane extends LitElement { section { padding: 8px 12px 16px; + display: grid; + gap: 12px; } abbr { diff --git a/src/wizards/substation.ts b/src/wizards/substation.ts index d17d65c2e8..5df44718e5 100644 --- a/src/wizards/substation.ts +++ b/src/wizards/substation.ts @@ -63,7 +63,9 @@ export function createAction(parent: Element): WizardActor { } export function createSubstationWizard(parent: Element): Wizard { - const guessable = parent.querySelector('Substation') === null; + const guessable = + parent.ownerDocument.querySelector('Substation') === null && + parent.tagName === 'SCL'; return [ { diff --git a/test/integration/editors/substation/process-editor-wizard-editing.test.ts b/test/integration/editors/substation/process-editor-wizard-editing.test.ts index dc54f374a4..c5590c86d4 100644 --- a/test/integration/editors/substation/process-editor-wizard-editing.test.ts +++ b/test/integration/editors/substation/process-editor-wizard-editing.test.ts @@ -2,10 +2,48 @@ import { expect, fixture, html } from '@open-wc/testing'; import '../../../mock-wizard-editor.js'; import { MockWizardEditor } from '../../../mock-wizard-editor.js'; +import { ListItemBase } from '@material/mwc-list/mwc-list-item-base'; import '../../../../src/editors/substation/process-editor.js'; import { ProcessEditor } from '../../../../src/editors/substation/process-editor.js'; import { WizardTextField } from '../../../../src/wizard-textfield.js'; +import { MenuBase } from '@material/mwc-menu/mwc-menu-base.js'; + +const openAndCancelMenu: ( + parent: MockWizardEditor, + element: ProcessEditor +) => Promise = ( + parent: MockWizardEditor, + element: ProcessEditor +): Promise => + new Promise(async resolve => { + expect(parent.wizardUI.dialog).to.be.undefined; + + element?.shadowRoot + ?.querySelector("mwc-icon-button[icon='playlist_add']")! + .click(); + const lnodMenuItem: ListItemBase = + element?.shadowRoot?.querySelector( + `mwc-list-item[value='LNode']` + )!; + lnodMenuItem.click(); + await new Promise(resolve => setTimeout(resolve, 100)); // await animation + + expect(parent.wizardUI.dialog).to.exist; + + const secondaryAction: HTMLElement = ( + parent.wizardUI.dialog?.querySelector( + 'mwc-button[slot="secondaryAction"][dialogaction="close"]' + ) + ); + + secondaryAction.click(); + await new Promise(resolve => setTimeout(resolve, 100)); // await animation + + expect(parent.wizardUI.dialog).to.be.undefined; + + return resolve(); + }); describe('process-editor wizarding editing integration', () => { let doc: XMLDocument; @@ -146,5 +184,36 @@ describe('process-editor wizarding editing integration', () => { .exist; }); }); + describe('Open add wizard', () => { + let doc: XMLDocument; + let parent: MockWizardEditor; + let element: ProcessEditor | null; + + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/substation/Process.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + parent = ( + await fixture( + html`` + ) + ); + + element = parent.querySelector('process-editor'); + + await parent.updateComplete; + }); + + it('Should open the same wizard for the second time', async () => { + await openAndCancelMenu(parent, element!); + await openAndCancelMenu(parent, element!); + }); + }); }); }); diff --git a/test/unit/editors/substation/__snapshots__/process-editor.test.snap.js b/test/unit/editors/substation/__snapshots__/process-editor.test.snap.js index 7b9ad56e56..d53d8c5cc2 100644 --- a/test/unit/editors/substation/__snapshots__/process-editor.test.snap.js +++ b/test/unit/editors/substation/__snapshots__/process-editor.test.snap.js @@ -20,6 +20,96 @@ snapshots["web component rendering Process element rendering LNode, GeneralEquip + + + + + + + LNode + + + + + GeneralEquipment + + + + + Function + + + + + ConductingEquipment + + + + + Substation + + + + + Line + + + + + Process + + + + @@ -53,6 +143,96 @@ snapshots["web component rendering Process element hides LNode and Function chil + + + + + + + LNode + + + + + GeneralEquipment + + + + + Function + + + + + ConductingEquipment + + + + + Substation + + + + + Line + + + + + Process + + + + @@ -80,6 +260,96 @@ snapshots["web component rendering Process element rendering Substation and Proc + + + + + + + LNode + + + + + GeneralEquipment + + + + + Function + + + + + ConductingEquipment + + + + + Substation + + + + + Line + + + + + Process + + + + @@ -107,6 +377,96 @@ snapshots["web component rendering Process element rendering a Line child looks + + + + + + + LNode + + + + + GeneralEquipment + + + + + Function + + + + + ConductingEquipment + + + + + Substation + + + + + Line + + + + + Process + + + + From 90171287ce2efd0c9bd9904d416276ceb621fcd2 Mon Sep 17 00:00:00 2001 From: Pascal Wilbrink Date: Tue, 2 May 2023 10:10:49 +0200 Subject: [PATCH 27/27] chore(release): 0.31.0 --- CHANGELOG.md | 7 +++++++ manifest.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1587fe971f..c3634cef8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [0.31.0](https://github.com/openscd/open-scd/compare/v0.30.0...v0.31.0) (2023-05-02) + + +### Features + +* **editors/binding:** Allow filtering of subscribed/unsubscribed data in binding editors ([#1149](https://github.com/openscd/open-scd/issues/1149)) ([874be45](https://github.com/openscd/open-scd/commits/874be4518c7574846abbaed4cf597c779c747d5e)), closes [#1062](https://github.com/openscd/open-scd/issues/1062) + ## [0.30.0](https://github.com/openscd/open-scd/compare/v0.29.0...v0.30.0) (2023-03-08) diff --git a/manifest.json b/manifest.json index 6da6d59798..da5fff1d45 100644 --- a/manifest.json +++ b/manifest.json @@ -40,5 +40,5 @@ "purpose": "maskable" } ], - "version": "0.30.0" + "version": "0.31.0" } diff --git a/package-lock.json b/package-lock.json index e1e2599d83..c594fbdb22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-scd", - "version": "0.30.0", + "version": "0.31.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "open-scd", - "version": "0.30.0", + "version": "0.31.0", "license": "Apache-2.0", "dependencies": { "@material/mwc-circular-progress-four-color": "0.22.1", diff --git a/package.json b/package.json index 25ba2d7c66..345800ae62 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-scd", - "version": "0.30.0", + "version": "0.31.0", "repository": "https://github.com/openscd/open-scd.git", "description": "A bottom-up substation configuration designer for projects described using SCL `IEC 61850-6` Edition 2 or greater.", "keywords": [