From fd8ff8faa25da39cc12d12c987d7d0f2d045045b Mon Sep 17 00:00:00 2001 From: Jan Ulrych <ulrych@marbes.cz> Date: Tue, 2 Apr 2024 00:45:20 +0200 Subject: [PATCH 1/9] =?UTF-8?q?Re=20#11123=20-=20Neza=C4=8Di=C5=A1t=C4=9Bn?= =?UTF-8?q?=C3=A1=20logika=20pro=20ozna=C4=8Den=C3=AD=20c=C3=ADlov=C3=A9ho?= =?UTF-8?q?=20stavu=20sc=C3=A9n=C3=A1=C5=99e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app.component.html | 58 ++++++++ src/app/app.component.ts | 79 ++++++++++- src/app/model/final-state-target.interface.ts | 10 ++ src/app/model/html-element.interface.ts | 1 + src/app/model/message.interface.ts | 7 + src/app/utils/attribute-grabber.util.ts | 24 +++- src/app/utils/constants.util.ts | 4 +- src/app/utils/storage.util.ts | 47 ++++++- .../pages/overview/overview.component.html | 10 ++ .../view/pages/overview/overview.component.ts | 1 + src/core/background.ts | 46 ++++--- src/core/listeners/change.listener.ts | 2 +- src/core/listeners/click.listener.ts | 128 ++++++++++++++++-- src/core/listeners/copy.listener.ts | 4 +- src/core/listeners/cut.listener.ts | 4 +- src/core/listeners/keydown.listener.ts | 2 +- src/core/listeners/paste.listener.ts | 4 +- src/core/listeners/resize.listener.ts | 4 +- src/core/listeners/select.listener.ts | 2 +- 19 files changed, 392 insertions(+), 45 deletions(-) create mode 100644 src/app/model/final-state-target.interface.ts create mode 100644 src/app/model/message.interface.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 1f5b28f..1162083 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,8 +1,66 @@ <div id="scenario-tracker-content"> + <ng-container *ngIf="finalStateTarget"> + <h5>Text:</h5> + <p>{{finalStateTarget.text}}</p> + <h5>Attributes:</h5> + <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> + <input type="checkbox" (change)="saveAttribute($event, attribute, finalStateTarget.attributes[attribute])"> + {{attribute}} = {{finalStateTarget.attributes[attribute]}} + </div> + <h5>Explicitly set styles:</h5> + <div *ngFor="let attribute of Object.keys(finalStateTarget.styles)"> + <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> + style.{{attribute}} = {{finalStateTarget.styles[attribute]}} + </div> +<!-- <h5>Computed styles:</h5>--> <!-- TODO: Commented out just because it is too long, but we should show it to the user --> +<!-- <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)">--> +<!-- <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])">--> +<!-- style.{{attribute}} = {{finalStateTarget.computedStyles[attribute]}}--> +<!-- </div>--> + <custom-button + buttonText="Cancel" + size="S" + iconType="fontawesome" + icon="fa fa-trash" + additionalClasses="warning-button" + (click)="deleteFinalStateTarget()" + > + </custom-button> + <custom-button + buttonText="Add" + size="S" + iconType="fontawesome" + icon="fa fa-plus" + additionalClasses="success-button" + (click)="addFinalState()" + > + </custom-button> + </ng-container> + + + <h5>Final states:</h5> + <div *ngFor="let finalState of finalStates; let i = index"> + <b>{{finalState.element.tagName}}</b> + <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> + <!-- show only first 3 --> + <p *ngIf="j < 3">{{attribute}}: {{finalState.selectedAttributes[attribute]}}</p> + </div> + <custom-button + buttonText="Remove" + size="S" + iconType="fontawesome" + icon="fa fa-trash" + additionalClasses="delete-button" + (click)="deleteFinalState(i)" + > + </custom-button> + </div> + <overview [deleteActionHandler]="deleteAction" [toggleRecordingHandler]="toggleRecording" [deleteAllActionsHandler]="deleteAllActions" + [markFinalStateHandler]="markFinalState" [actions]="actions" [recording]="recording" > diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bf3c213..0422152 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,6 +1,18 @@ import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; import { Action } from './model/action.interface'; -import { getActions, getRecording, saveActions, saveRecording } from './utils/storage.util'; +import { + getActions, + getFinalStates, + getRecording, + saveActions, saveFinalStates, + saveMarkFinalState, + saveRecording +} from './utils/storage.util'; +import {Message} from "./model/message.interface"; +import {Result} from "./model/result.interface"; +import {FinalStateTarget} from "./model/final-state-target.interface"; +import {ObjectKeys} from "./model/object-keys.interface"; +import {HtmlElement} from "./model/html-element.interface"; @Component({ selector: 'app-root', @@ -9,17 +21,75 @@ import { getActions, getRecording, saveActions, saveRecording } from './utils/st }) export class AppComponent implements OnInit { actions: Action[] = []; + finalStates: Result[] = []; recording: boolean = false; + finalStateTarget?: FinalStateTarget = undefined; + savedAttributes: ObjectKeys = {}; + protected readonly Object = Object; // just for the template constructor(private ref: ChangeDetectorRef){} ngOnInit(): void { + chrome.runtime.onMessage.addListener((message: Message) => { + if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state + console.log("Final state target:", message) + this.finalStateTarget = message.finalStateClickTarget; + this.savedAttributes = {}; + this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually + } + }); chrome.storage.onChanged.addListener(() => { this.getData(); }); this.getData(); } + public deleteFinalStateTarget(){ + console.log("Asdf") + + const xPath = this.finalStateTarget!.xPath; + this.finalStateTarget = undefined; + this.savedAttributes = {}; + + this.ref.detectChanges(); + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.sendMessage(tabs[0].id!, xPath); + }) + } + + public saveAttribute(event: Event, attribute: string, value: string){ + if((event.target as HTMLInputElement).checked){ + console.log(`Saving attribute ${attribute}=${value}`) + this.savedAttributes[attribute] = value; + } else { + console.log(`Deleting attribute ${attribute}=${value}`) + delete this.savedAttributes[attribute]; + } + console.log(this.savedAttributes) + } + + public addFinalState(){ + const element: HtmlElement = { + tagName: this.finalStateTarget!.tagName, + globalAttributes: {}, + elementAttributes: {}, + xPath: this.finalStateTarget!.xPath + }; + + const finalState: Result = { + element: element, + selectedAttributes: this.savedAttributes + }; + this.finalStates.push(finalState); + saveFinalStates(this.finalStates); + this.deleteFinalStateTarget(); + } + + public deleteFinalState = (index: number) => { + this.finalStates.splice(index, 1); + saveFinalStates(this.finalStates); + } + /** * Loads all data needed by the component. */ @@ -30,6 +100,9 @@ export class AppComponent implements OnInit { await getRecording().then(recording => { this.recording = recording; }); + await getFinalStates().then(finalStates => { + this.finalStates = finalStates; + }); this.ref.detectChanges(); // we need to automatically detect changes, because chrome.storage.onChanged.addListener // is outside of the Angular scope @@ -52,4 +125,8 @@ export class AppComponent implements OnInit { this.actions.splice(index, 1); saveActions(this.actions); } + + public markFinalState = () => { + saveMarkFinalState(true); + } } diff --git a/src/app/model/final-state-target.interface.ts b/src/app/model/final-state-target.interface.ts new file mode 100644 index 0000000..694252c --- /dev/null +++ b/src/app/model/final-state-target.interface.ts @@ -0,0 +1,10 @@ +import {ObjectKeys} from "./object-keys.interface"; + +export interface FinalStateTarget{ + tagName: string, + text: string, + xPath: string, + attributes: ObjectKeys, + styles: ObjectKeys, + computedStyles: ObjectKeys +} \ No newline at end of file diff --git a/src/app/model/html-element.interface.ts b/src/app/model/html-element.interface.ts index b09c341..8c7dd1f 100644 --- a/src/app/model/html-element.interface.ts +++ b/src/app/model/html-element.interface.ts @@ -2,6 +2,7 @@ import { ObjectKeys } from "./object-keys.interface"; export interface HtmlElement{ tagName: string, + xPath: string, globalAttributes: ObjectKeys, elementAttributes: ObjectKeys } \ No newline at end of file diff --git a/src/app/model/message.interface.ts b/src/app/model/message.interface.ts new file mode 100644 index 0000000..4202d48 --- /dev/null +++ b/src/app/model/message.interface.ts @@ -0,0 +1,7 @@ +import {Action} from "./action.interface"; +import {FinalStateTarget} from "./final-state-target.interface"; + +export interface Message{ + action?: Action, + finalStateClickTarget?: FinalStateTarget +} \ No newline at end of file diff --git a/src/app/utils/attribute-grabber.util.ts b/src/app/utils/attribute-grabber.util.ts index 2b4bca8..0759684 100644 --- a/src/app/utils/attribute-grabber.util.ts +++ b/src/app/utils/attribute-grabber.util.ts @@ -1,12 +1,12 @@ -import { HtmlElement } from "../model/html-element.interface"; +import {HtmlElement} from "../model/html-element.interface"; export const extractAllAttributes = (element: HTMLElement): HtmlElement => { - const extracted: HtmlElement = { + return { tagName: element.tagName, + xPath: getElementsXpath(element), globalAttributes: extractGlobalAttributes(element), elementAttributes: extractElementAttributes(element) }; - return extracted; } /** @@ -369,4 +369,22 @@ const extractFromVideo = (element: HTMLVideoElement): {} => { controls: element.controls, controlsList: element.getAttribute('controlslist') } +} + +export const getElementsXpath = (element: HTMLElement): string => { + if (element.tagName == 'HTML') + return '/HTML[1]'; + if (element === document.body) + return '/HTML[1]/BODY[1]'; + + var ix = 0; + var siblings = element.parentNode!.childNodes; + for (var i = 0; i < siblings.length; i++) { + var sibling = siblings[i]; + if (sibling === element) + return getElementsXpath(element.parentNode! as HTMLElement) + '/' + element.tagName + '[' + (ix + 1) + ']'; + if (sibling.nodeType === 1 && (sibling as HTMLElement).tagName === element.tagName) + ix++; + } + return "" } \ No newline at end of file diff --git a/src/app/utils/constants.util.ts b/src/app/utils/constants.util.ts index 366335c..efe449b 100644 --- a/src/app/utils/constants.util.ts +++ b/src/app/utils/constants.util.ts @@ -1,2 +1,4 @@ export const KEY_ACTIONS = "actions"; -export const KEY_RECORDING = "recording"; \ No newline at end of file +export const KEY_RECORDING = "recording"; +export const KEY_MARK_FINAL_STATE = "markFinalState"; +export const KEY_FINAL_STATES = "finalStates"; \ No newline at end of file diff --git a/src/app/utils/storage.util.ts b/src/app/utils/storage.util.ts index 25e4409..905c9d6 100644 --- a/src/app/utils/storage.util.ts +++ b/src/app/utils/storage.util.ts @@ -1,5 +1,6 @@ import { Action } from "../model/action.interface"; -import { KEY_ACTIONS, KEY_RECORDING } from "./constants.util"; +import {KEY_ACTIONS, KEY_FINAL_STATES, KEY_MARK_FINAL_STATE, KEY_RECORDING} from "./constants.util"; +import {Result} from "../model/result.interface"; /** * Loads all actions from the storage. @@ -25,6 +26,30 @@ export async function getRecording(): Promise<boolean>{ return recording; } +/** + * Loads the recording status from the storage. + * @returns Whether we are recording or not. + */ +export async function getMarkFinalState(): Promise<boolean>{ + let markFinalState: boolean = false; + await chrome.storage.local.get(KEY_MARK_FINAL_STATE).then(data => { + markFinalState = data[KEY_MARK_FINAL_STATE] as boolean; + }); + return markFinalState; +} + +/** + * Loads the list of final states from the storage. + * @returns List of final states. + */ +export async function getFinalStates(): Promise<Result[]>{ + let finalStates: Result[] = []; + await chrome.storage.local.get(KEY_FINAL_STATES).then(data => { + finalStates = data[KEY_FINAL_STATES] as Result[]; + }); + return finalStates; +} + /** * Saves actions to the storage. * @param actions Actions to be saved. @@ -43,4 +68,24 @@ export async function saveRecording(recording: boolean) { await chrome.storage.local.set({ recording: recording }); +} + +/** + * Saves the flag that the next click is gonna mark the final state. + * @param markFinalState Whether the next click is gonna mark the final state. + */ +export async function saveMarkFinalState(markFinalState: boolean) { + await chrome.storage.local.set({ + markFinalState: markFinalState + }); +} + +/** + * Saves the list of final states to the storage. + * @param finalStates Final states to be saved. + */ +export async function saveFinalStates(finalStates: Result[]) { + await chrome.storage.local.set({ + finalStates: finalStates + }); } \ No newline at end of file diff --git a/src/app/view/pages/overview/overview.component.html b/src/app/view/pages/overview/overview.component.html index 3ee8721..5ee6efc 100644 --- a/src/app/view/pages/overview/overview.component.html +++ b/src/app/view/pages/overview/overview.component.html @@ -2,6 +2,16 @@ <h1>Scenario Tracker</h1> <!-- Controls --> +<custom-button + buttonText="Mark final state" + size="M" + iconType="fontawesome" + icon="fa fa-solid fa-flag-checkered" + additionalClasses="info-button" + (click)="markFinalStateHandler()"> +</custom-button> + +<div style="display: inline-block; width: 10px;"></div> <custom-button buttonText="Delete all actions" size="M" diff --git a/src/app/view/pages/overview/overview.component.ts b/src/app/view/pages/overview/overview.component.ts index 636b6d2..f174fb8 100644 --- a/src/app/view/pages/overview/overview.component.ts +++ b/src/app/view/pages/overview/overview.component.ts @@ -15,6 +15,7 @@ export class OverviewComponent implements OnInit { @Input() deleteActionHandler!: Function; @Input() deleteAllActionsHandler!: Function; @Input() toggleRecordingHandler!: Function; + @Input() markFinalStateHandler!: Function; readonly ActionEnum = ActionEnum diff --git a/src/core/background.ts b/src/core/background.ts index 796bd3f..d654c9a 100644 --- a/src/core/background.ts +++ b/src/core/background.ts @@ -1,6 +1,8 @@ /// <reference types="chrome"/> -import { getActions, getRecording, saveActions } from "src/app/utils/storage.util"; +import {getActions, getRecording, saveActions} from "src/app/utils/storage.util"; +import {Message} from "../app/model/message.interface"; +import {Action} from "../app/model/action.interface"; console.log("Background loaded") @@ -9,27 +11,35 @@ chrome.sidePanel.setPanelBehavior({openPanelOnActionClick: true}).catch((error) // clear the local storage on refresh / reload // prepare the storage for actions -if(true){ // set to false if you dont want to clear the storage on each extension refresh +if (true) { // set to false if you dont want to clear the storage on each extension refresh chrome.runtime.onInstalled.addListener(() => { chrome.storage.local.clear(); - chrome.storage.local.set({ actions: [], recording: false }); - }) + chrome.storage.local.set({ + actions: [], + recording: false, + markFinalState: false, + finalStates: [] + }); + }); } // listens for incoming messages from content script -chrome.runtime.onMessage.addListener((meesage: any) => { - console.log("Message received: ", meesage); +chrome.runtime.onMessage.addListener((message: Message) => { + console.log("Message received: ", message); - // determine whether we are recording - getRecording().then(recording => { - if(recording === true){ // if we are recording, save the action - // load actions from storage - getActions().then(actions => { - // add new action to the loaded actions - actions.push(meesage); - // save the updated action list - saveActions(actions); - }); - } - }) + if (message.action) { // in background script, we listen only for actions performed by actions + const action: Action = message.action; + // determine whether we are recording + getRecording().then(recording => { + if (recording) { // if we are recording, save the action + // load actions from storage + getActions().then(actions => { + // add new action to the loaded actions + actions.push(action); + // save the updated action list + saveActions(actions); + }); + } + }) + } }) \ No newline at end of file diff --git a/src/core/listeners/change.listener.ts b/src/core/listeners/change.listener.ts index 332c4c9..f43ff6a 100644 --- a/src/core/listeners/change.listener.ts +++ b/src/core/listeners/change.listener.ts @@ -17,5 +17,5 @@ document.addEventListener("change", (event) => { recordedOn: new Date(), documentAction: documentAction }; - chrome.runtime.sendMessage(action); + chrome.runtime.sendMessage({action: action}); }) \ No newline at end of file diff --git a/src/core/listeners/click.listener.ts b/src/core/listeners/click.listener.ts index 9df457f..3e6f838 100644 --- a/src/core/listeners/click.listener.ts +++ b/src/core/listeners/click.listener.ts @@ -1,13 +1,119 @@ -import { ActionEnum } from "src/app/model/action.enum"; -import { DocumentAction } from "../../app/model/document-action.interface"; -import { HtmlElement } from "src/app/model/html-element.interface"; -import { extractAllAttributes, extractElementAttributes, extractGlobalAttributes } from "src/app/utils/attribute-grabber.util"; -import { Action } from "src/app/model/action.interface"; +import {ActionEnum} from "src/app/model/action.enum"; +import {DocumentAction} from "../../app/model/document-action.interface"; +import {HtmlElement} from "src/app/model/html-element.interface"; +import {extractAllAttributes, getElementsXpath} from "src/app/utils/attribute-grabber.util"; +import {Action} from "src/app/model/action.interface"; +import {getMarkFinalState, saveMarkFinalState} from "../../app/utils/storage.util"; +import {Message} from "../../app/model/message.interface"; +import {FinalStateTarget} from "../../app/model/final-state-target.interface"; +import {ObjectKeys} from "../../app/model/object-keys.interface"; document.addEventListener("click", function (event) { - console.log("Click detected:", event.target); + getMarkFinalState().then(markFinalState => { + const target: HTMLElement = event.target as HTMLElement; + if (markFinalState) { + if(target instanceof HTMLLabelElement){ + const label = target as HTMLLabelElement; + const labelFor = label.getAttribute('for'); + console.log(labelFor) + if(labelFor !== null && labelFor !== ''){ + // when we click on label, two click events are fired: + // first event for the label + // second event for the input that the label is for (attribute 'for') + // therefore if the label has the 'for' attribute specified, we do not use it + return; + } + } + markFinalStateHandler(target); + } else { + clickListenerHandler(target); + } + }); +}); + +const onBeforeUnload = (e: BeforeUnloadEvent) => { + // Cancel the event + e.preventDefault(); + // unfortunately, we cannot set custom message to the dialog, as is it considered dangerous +} + +chrome.storage.onChanged.addListener(() => { + getMarkFinalState().then(markFinalState => { + if (markFinalState) { + // try to keep the user on the page, so the selected element is visible + window.addEventListener('beforeunload', onBeforeUnload); + } else { + window.removeEventListener('beforeunload', onBeforeUnload); + } + }); +}) + +chrome.runtime.onMessage.addListener((xPath: string) => { + const target = (document.evaluate(xPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue as HTMLElement); + target.style.outline = ""; +}) + +const markFinalStateHandler = (target: HTMLElement) => { + console.log("Final state detected:", target); + const xPath: string = getElementsXpath(target); + console.log("XPATH:", xPath); + + // enable normal click listener + saveMarkFinalState(false); + + const attributes: ObjectKeys = {}; + for (let attribute of Array.from(target.attributes)) { + if (attribute.name === 'style') { + continue; + } + attributes[attribute.name] = attribute.value; + } + + const styles: ObjectKeys = {}; + for (let attribute of Object.keys(target.style)) { + const value = target.style.getPropertyValue(attribute); + if (value !== '') { + styles[attribute] = target.style.getPropertyValue(attribute); + } + } + + const computedStyles: ObjectKeys = {}; + for (let entry of Object.entries(getComputedStyle(target))) { + if (entry[1] !== '' && !isNumeric(entry[0])) { // only styles that has some value and those, whose key is not numeric + computedStyles[entry[0]] = entry[1]; + } + } + + let text: string; + if (target instanceof HTMLInputElement) { + text = (target as HTMLInputElement).value; + } else { + text = target.innerText; + } + + const finalStateTarget: FinalStateTarget = { + tagName: target.tagName, + text: text, + xPath: xPath, + attributes: attributes, + styles: styles, + computedStyles: computedStyles + } + + const message: Message = { + finalStateClickTarget: finalStateTarget + }; + + // send the chosen element to the component + chrome.runtime.sendMessage(message); + + // highlight the element on the page + target.style.outline = "5px solid rgba(218,172,0,0.8)"; +} + +const clickListenerHandler = (target: HTMLElement) => { + console.log("Click detected:", target); - const target = event.target as HTMLElement; const element: HtmlElement = extractAllAttributes(target); const documentAction: DocumentAction = { target: element @@ -18,5 +124,9 @@ document.addEventListener("click", function (event) { recordedOn: new Date(), }; // send message about the event to the background script - chrome.runtime.sendMessage(action); -}); \ No newline at end of file + chrome.runtime.sendMessage({action: action}); +} + +const isNumeric = (value: string) => { + return /^-?\d+$/.test(value); +} \ No newline at end of file diff --git a/src/core/listeners/copy.listener.ts b/src/core/listeners/copy.listener.ts index bc9aa17..f482b51 100644 --- a/src/core/listeners/copy.listener.ts +++ b/src/core/listeners/copy.listener.ts @@ -2,7 +2,7 @@ import { ActionEnum } from "src/app/model/action.enum"; import { Action } from "src/app/model/action.interface"; import { DocumentAction } from "src/app/model/document-action.interface"; import { HtmlElement } from "src/app/model/html-element.interface"; -import { extractAllAttributes, extractElementAttributes, extractGlobalAttributes } from "src/app/utils/attribute-grabber.util"; +import { extractAllAttributes } from "src/app/utils/attribute-grabber.util"; document.addEventListener("copy", function (event) { const selection = document.getSelection(); @@ -21,5 +21,5 @@ document.addEventListener("copy", function (event) { recordedOn: new Date(), documentAction: documentAction }; - chrome.runtime.sendMessage(action); + chrome.runtime.sendMessage({action: action}); }); \ No newline at end of file diff --git a/src/core/listeners/cut.listener.ts b/src/core/listeners/cut.listener.ts index 6955bda..2969644 100644 --- a/src/core/listeners/cut.listener.ts +++ b/src/core/listeners/cut.listener.ts @@ -2,7 +2,7 @@ import { ActionEnum } from "src/app/model/action.enum"; import { Action } from "src/app/model/action.interface"; import { DocumentAction } from "src/app/model/document-action.interface"; import { HtmlElement } from "src/app/model/html-element.interface"; -import { extractAllAttributes, extractElementAttributes, extractGlobalAttributes } from "src/app/utils/attribute-grabber.util"; +import { extractAllAttributes } from "src/app/utils/attribute-grabber.util"; document.addEventListener("cut", function (event) { const selection = document.getSelection(); @@ -21,5 +21,5 @@ document.addEventListener("cut", function (event) { recordedOn: new Date(), documentAction: documentAction }; - chrome.runtime.sendMessage(action); + chrome.runtime.sendMessage({action: action}); }); \ No newline at end of file diff --git a/src/core/listeners/keydown.listener.ts b/src/core/listeners/keydown.listener.ts index e48f7a0..c4a1b02 100644 --- a/src/core/listeners/keydown.listener.ts +++ b/src/core/listeners/keydown.listener.ts @@ -17,5 +17,5 @@ document.addEventListener("keydown", (event) => { recordedOn: new Date(), documentAction: documentAction }; - chrome.runtime.sendMessage(action); + chrome.runtime.sendMessage({action: action}); }) \ No newline at end of file diff --git a/src/core/listeners/paste.listener.ts b/src/core/listeners/paste.listener.ts index dca689a..ab3c318 100644 --- a/src/core/listeners/paste.listener.ts +++ b/src/core/listeners/paste.listener.ts @@ -2,7 +2,7 @@ import { ActionEnum } from "src/app/model/action.enum"; import { Action } from "src/app/model/action.interface"; import { DocumentAction } from "src/app/model/document-action.interface"; import { HtmlElement } from "src/app/model/html-element.interface"; -import { extractAllAttributes, extractElementAttributes, extractGlobalAttributes } from "src/app/utils/attribute-grabber.util"; +import { extractAllAttributes } from "src/app/utils/attribute-grabber.util"; document.addEventListener("paste", function (event) { const pasted = event.clipboardData @@ -23,5 +23,5 @@ document.addEventListener("paste", function (event) { recordedOn: new Date(), documentAction: documentAction }; - chrome.runtime.sendMessage(action); + chrome.runtime.sendMessage({action: action}); }); \ No newline at end of file diff --git a/src/core/listeners/resize.listener.ts b/src/core/listeners/resize.listener.ts index 10b491b..cdd16e4 100644 --- a/src/core/listeners/resize.listener.ts +++ b/src/core/listeners/resize.listener.ts @@ -1,5 +1,3 @@ - - import { ActionEnum } from "src/app/model/action.enum"; import { Action } from "src/app/model/action.interface"; import { WindowAction } from "src/app/model/window-action.interface"; @@ -21,5 +19,5 @@ const resized = () => { recordedOn: new Date(), windowAction: documentAction }; - chrome.runtime.sendMessage(action); + chrome.runtime.sendMessage({action: action}); } \ No newline at end of file diff --git a/src/core/listeners/select.listener.ts b/src/core/listeners/select.listener.ts index 24a60ac..f4e5b79 100644 --- a/src/core/listeners/select.listener.ts +++ b/src/core/listeners/select.listener.ts @@ -21,5 +21,5 @@ document.addEventListener("select", (event) => { recordedOn: new Date(), documentAction: documentAction }; - chrome.runtime.sendMessage(action); + chrome.runtime.sendMessage({action: action}); }) \ No newline at end of file -- GitLab From fcce975c50d0da525b4378094555b7d8eecfefc4 Mon Sep 17 00:00:00 2001 From: Jan Ulrych <ulrych@marbes.cz> Date: Tue, 2 Apr 2024 12:12:18 +0200 Subject: [PATCH 2/9] =?UTF-8?q?Re=20#11123=20-=20Vy=C4=8Di=C5=A1t=C4=9Bn?= =?UTF-8?q?=C3=AD,=20odd=C4=9Blen=C3=AD=20do=20v=C3=ADce=20soubor=C5=AF,?= =?UTF-8?q?=20koment=C3=A1=C5=99e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app.component.html | 6 +- src/app/app.component.ts | 22 ++++- src/app/model/message.interface.ts | 3 +- src/app/utils/attribute-grabber.util.ts | 24 ++---- src/app/utils/element.util.ts | 44 ++++++++++ src/app/utils/storage.util.ts | 8 +- .../pages/overview/overview.component.html | 2 + .../view/pages/overview/overview.component.ts | 2 + src/core/content-script.ts | 13 ++- src/core/listeners/click.listener.ts | 86 ++++++++++--------- 10 files changed, 139 insertions(+), 71 deletions(-) create mode 100644 src/app/utils/element.util.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 1162083..2631a37 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,4 +1,5 @@ <div id="scenario-tracker-content"> + <!-- START: MARK FINAL STATE --> <ng-container *ngIf="finalStateTarget"> <h5>Text:</h5> <p>{{finalStateTarget.text}}</p> @@ -12,7 +13,8 @@ <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> style.{{attribute}} = {{finalStateTarget.styles[attribute]}} </div> -<!-- <h5>Computed styles:</h5>--> <!-- TODO: Commented out just because it is too long, but we should show it to the user --> + <!-- TODO: It is now commented out just because the output is too long, but we should show it to the user --> +<!-- <h5>Computed styles:</h5>--> <!-- <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)">--> <!-- <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])">--> <!-- style.{{attribute}} = {{finalStateTarget.computedStyles[attribute]}}--> @@ -37,7 +39,6 @@ </custom-button> </ng-container> - <h5>Final states:</h5> <div *ngFor="let finalState of finalStates; let i = index"> <b>{{finalState.element.tagName}}</b> @@ -55,6 +56,7 @@ > </custom-button> </div> + <!-- END: MARK FINAL STATE --> <overview [deleteActionHandler]="deleteAction" diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 0422152..7bf70a9 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -21,15 +21,18 @@ import {HtmlElement} from "./model/html-element.interface"; }) export class AppComponent implements OnInit { actions: Action[] = []; - finalStates: Result[] = []; recording: boolean = false; + // START: MARK FINAL STATE + finalStates: Result[] = []; finalStateTarget?: FinalStateTarget = undefined; savedAttributes: ObjectKeys = {}; protected readonly Object = Object; // just for the template + // END: MARK FINAL STATE constructor(private ref: ChangeDetectorRef){} ngOnInit(): void { + // START: MARK FINAL STATE chrome.runtime.onMessage.addListener((message: Message) => { if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state console.log("Final state target:", message) @@ -38,22 +41,28 @@ export class AppComponent implements OnInit { this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually } }); + // END: MARK FINAL STATE chrome.storage.onChanged.addListener(() => { this.getData(); }); this.getData(); } + // START: MARK FINAL STATE + /** + * Deletes the clicked final state target and sets all variables to default values. + */ public deleteFinalStateTarget(){ - console.log("Asdf") - const xPath = this.finalStateTarget!.xPath; this.finalStateTarget = undefined; this.savedAttributes = {}; this.ref.detectChanges(); + const message: Message = { + xPath: xPath + } chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { - chrome.tabs.sendMessage(tabs[0].id!, xPath); + chrome.tabs.sendMessage(tabs[0].id!, message); }) } @@ -89,6 +98,7 @@ export class AppComponent implements OnInit { this.finalStates.splice(index, 1); saveFinalStates(this.finalStates); } + // END: MARK FINAL STATE /** * Loads all data needed by the component. @@ -100,9 +110,11 @@ export class AppComponent implements OnInit { await getRecording().then(recording => { this.recording = recording; }); + // START: MARK FINAL STATE await getFinalStates().then(finalStates => { this.finalStates = finalStates; }); + // END: MARK FINAL STATE this.ref.detectChanges(); // we need to automatically detect changes, because chrome.storage.onChanged.addListener // is outside of the Angular scope @@ -126,7 +138,9 @@ export class AppComponent implements OnInit { saveActions(this.actions); } + // START: MARK FINAL STATE public markFinalState = () => { saveMarkFinalState(true); } + // END: MARK FINAL STATE } diff --git a/src/app/model/message.interface.ts b/src/app/model/message.interface.ts index 4202d48..f2fb5b2 100644 --- a/src/app/model/message.interface.ts +++ b/src/app/model/message.interface.ts @@ -3,5 +3,6 @@ import {FinalStateTarget} from "./final-state-target.interface"; export interface Message{ action?: Action, - finalStateClickTarget?: FinalStateTarget + finalStateClickTarget?: FinalStateTarget, + xPath?: string } \ No newline at end of file diff --git a/src/app/utils/attribute-grabber.util.ts b/src/app/utils/attribute-grabber.util.ts index 0759684..655651a 100644 --- a/src/app/utils/attribute-grabber.util.ts +++ b/src/app/utils/attribute-grabber.util.ts @@ -1,5 +1,11 @@ import {HtmlElement} from "../model/html-element.interface"; +import {getElementsXpath} from "./element.util"; +/** + * Extracts all attributes from the given element. + * @param element HTML element. + * @returns Interface that can be saved. + */ export const extractAllAttributes = (element: HTMLElement): HtmlElement => { return { tagName: element.tagName, @@ -369,22 +375,4 @@ const extractFromVideo = (element: HTMLVideoElement): {} => { controls: element.controls, controlsList: element.getAttribute('controlslist') } -} - -export const getElementsXpath = (element: HTMLElement): string => { - if (element.tagName == 'HTML') - return '/HTML[1]'; - if (element === document.body) - return '/HTML[1]/BODY[1]'; - - var ix = 0; - var siblings = element.parentNode!.childNodes; - for (var i = 0; i < siblings.length; i++) { - var sibling = siblings[i]; - if (sibling === element) - return getElementsXpath(element.parentNode! as HTMLElement) + '/' + element.tagName + '[' + (ix + 1) + ']'; - if (sibling.nodeType === 1 && (sibling as HTMLElement).tagName === element.tagName) - ix++; - } - return "" } \ No newline at end of file diff --git a/src/app/utils/element.util.ts b/src/app/utils/element.util.ts new file mode 100644 index 0000000..50ab0ad --- /dev/null +++ b/src/app/utils/element.util.ts @@ -0,0 +1,44 @@ +export const getElementsXpath = (element: HTMLElement): string => { + if (element.tagName == 'HTML') + return '/HTML[1]'; + if (element === document.body) + return '/HTML[1]/BODY[1]'; + + var ix = 0; + var siblings = element.parentNode!.childNodes; + for (var i = 0; i < siblings.length; i++) { + var sibling = siblings[i]; + if (sibling === element) + return getElementsXpath(element.parentNode! as HTMLElement) + '/' + element.tagName + '[' + (ix + 1) + ']'; + if (sibling.nodeType === 1 && (sibling as HTMLElement).tagName === element.tagName) + ix++; + } + return "" +} + +export const getElementByXpath = (xPath: string): HTMLElement => { + return (document.evaluate(xPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue as HTMLElement); +} + +export const cancelElementHighlightingByXpath = (xPath: string) => { + const element: HTMLElement = getElementByXpath(xPath); + element.style.outline = ""; +} + +export const highlightElementByXpath = (xPath: string) => { + const element: HTMLElement = getElementByXpath(xPath); + element.style.outline = "5px solid rgba(218,172,0,0.8)"; +} + +/** + * @param target Whether the element is a 'label' element with 'for' attribute. + */ +export const isLabelFor = (target: HTMLElement): boolean => { + if(!(target instanceof HTMLLabelElement)){ + return false; + } + + const label = target as HTMLLabelElement; + const labelFor = label.getAttribute('for'); + return labelFor !== null && labelFor !== ''; +} \ No newline at end of file diff --git a/src/app/utils/storage.util.ts b/src/app/utils/storage.util.ts index 905c9d6..5d49c4a 100644 --- a/src/app/utils/storage.util.ts +++ b/src/app/utils/storage.util.ts @@ -56,7 +56,7 @@ export async function getFinalStates(): Promise<Result[]>{ */ export async function saveActions(actions: Action[]) { await chrome.storage.local.set({ - actions: actions + [KEY_ACTIONS]: actions }); } @@ -66,7 +66,7 @@ export async function saveActions(actions: Action[]) { */ export async function saveRecording(recording: boolean) { await chrome.storage.local.set({ - recording: recording + [KEY_RECORDING]: recording }); } @@ -76,7 +76,7 @@ export async function saveRecording(recording: boolean) { */ export async function saveMarkFinalState(markFinalState: boolean) { await chrome.storage.local.set({ - markFinalState: markFinalState + [KEY_MARK_FINAL_STATE]: markFinalState }); } @@ -86,6 +86,6 @@ export async function saveMarkFinalState(markFinalState: boolean) { */ export async function saveFinalStates(finalStates: Result[]) { await chrome.storage.local.set({ - finalStates: finalStates + [KEY_FINAL_STATES]: finalStates }); } \ No newline at end of file diff --git a/src/app/view/pages/overview/overview.component.html b/src/app/view/pages/overview/overview.component.html index 5ee6efc..34e0eaf 100644 --- a/src/app/view/pages/overview/overview.component.html +++ b/src/app/view/pages/overview/overview.component.html @@ -2,6 +2,7 @@ <h1>Scenario Tracker</h1> <!-- Controls --> +<!-- START: MARK FINAL STATE --> <custom-button buttonText="Mark final state" size="M" @@ -10,6 +11,7 @@ additionalClasses="info-button" (click)="markFinalStateHandler()"> </custom-button> +<!-- END: MARK FINAL STATE --> <div style="display: inline-block; width: 10px;"></div> <custom-button diff --git a/src/app/view/pages/overview/overview.component.ts b/src/app/view/pages/overview/overview.component.ts index f174fb8..b0d0be5 100644 --- a/src/app/view/pages/overview/overview.component.ts +++ b/src/app/view/pages/overview/overview.component.ts @@ -15,7 +15,9 @@ export class OverviewComponent implements OnInit { @Input() deleteActionHandler!: Function; @Input() deleteAllActionsHandler!: Function; @Input() toggleRecordingHandler!: Function; + // START: MARK FINAL STATE @Input() markFinalStateHandler!: Function; + // END: MARK FINAL STATE readonly ActionEnum = ActionEnum diff --git a/src/core/content-script.ts b/src/core/content-script.ts index bf19e18..cd49310 100644 --- a/src/core/content-script.ts +++ b/src/core/content-script.ts @@ -1,5 +1,8 @@ /// <reference types="chrome"/> +import {cancelElementHighlightingByXpath} from "../app/utils/element.util"; +import {Message} from "../app/model/message.interface"; + console.log("Content script loaded") // import event listeners @@ -10,4 +13,12 @@ import "./listeners/cut.listener" import "./listeners/keydown.listener" import "./listeners/select.listener" import "./listeners/resize.listener" -import "./listeners/change.listener" \ No newline at end of file +import "./listeners/change.listener" + +// listener for canceling the highlighting of certain element +// needs to be in content-script, because it is the only one, that has access to the DOM of the webpage +chrome.runtime.onMessage.addListener((message: Message) => { + if(message.xPath){ + cancelElementHighlightingByXpath(message.xPath); + } +}) \ No newline at end of file diff --git a/src/core/listeners/click.listener.ts b/src/core/listeners/click.listener.ts index 3e6f838..85c89de 100644 --- a/src/core/listeners/click.listener.ts +++ b/src/core/listeners/click.listener.ts @@ -1,89 +1,70 @@ import {ActionEnum} from "src/app/model/action.enum"; import {DocumentAction} from "../../app/model/document-action.interface"; import {HtmlElement} from "src/app/model/html-element.interface"; -import {extractAllAttributes, getElementsXpath} from "src/app/utils/attribute-grabber.util"; +import {extractAllAttributes} from "src/app/utils/attribute-grabber.util"; import {Action} from "src/app/model/action.interface"; import {getMarkFinalState, saveMarkFinalState} from "../../app/utils/storage.util"; import {Message} from "../../app/model/message.interface"; import {FinalStateTarget} from "../../app/model/final-state-target.interface"; import {ObjectKeys} from "../../app/model/object-keys.interface"; +import {highlightElementByXpath, isLabelFor, getElementsXpath} from "../../app/utils/element.util"; +// add click listener to the DOM document.addEventListener("click", function (event) { getMarkFinalState().then(markFinalState => { const target: HTMLElement = event.target as HTMLElement; if (markFinalState) { - if(target instanceof HTMLLabelElement){ - const label = target as HTMLLabelElement; - const labelFor = label.getAttribute('for'); - console.log(labelFor) - if(labelFor !== null && labelFor !== ''){ - // when we click on label, two click events are fired: - // first event for the label - // second event for the input that the label is for (attribute 'for') - // therefore if the label has the 'for' attribute specified, we do not use it - return; - } - } + // the click indicates final state markFinalStateHandler(target); } else { + // the click is just a normal user interaction with the website clickListenerHandler(target); } }); }); -const onBeforeUnload = (e: BeforeUnloadEvent) => { - // Cancel the event - e.preventDefault(); - // unfortunately, we cannot set custom message to the dialog, as is it considered dangerous -} - -chrome.storage.onChanged.addListener(() => { - getMarkFinalState().then(markFinalState => { - if (markFinalState) { - // try to keep the user on the page, so the selected element is visible - window.addEventListener('beforeunload', onBeforeUnload); - } else { - window.removeEventListener('beforeunload', onBeforeUnload); - } - }); -}) - -chrome.runtime.onMessage.addListener((xPath: string) => { - const target = (document.evaluate(xPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue as HTMLElement); - target.style.outline = ""; -}) - const markFinalStateHandler = (target: HTMLElement) => { console.log("Final state detected:", target); - const xPath: string = getElementsXpath(target); - console.log("XPATH:", xPath); + + if(isLabelFor(target)){ + // When we click on label, two click events are fired: + // - first event for the label, + // - second event for the input that the label is for (label's attribute 'for'). + // Therefore if the label has the 'for' attribute specified, we do not use it. + // (if we assume that labels do not change and are generally not used for recognizing final state) + return; + } // enable normal click listener saveMarkFinalState(false); + // extract all attributes const attributes: ObjectKeys = {}; for (let attribute of Array.from(target.attributes)) { - if (attribute.name === 'style') { + if (attribute.name === 'style') { // the style attribute is extracted below continue; } attributes[attribute.name] = attribute.value; } + // extract all explicitly set styles const styles: ObjectKeys = {}; for (let attribute of Object.keys(target.style)) { const value = target.style.getPropertyValue(attribute); - if (value !== '') { + if (value !== '') { // only styles, whose value is not empty styles[attribute] = target.style.getPropertyValue(attribute); } } + // extract all computed styles const computedStyles: ObjectKeys = {}; for (let entry of Object.entries(getComputedStyle(target))) { - if (entry[1] !== '' && !isNumeric(entry[0])) { // only styles that has some value and those, whose key is not numeric + if (entry[1] !== '' && !isNumeric(entry[0])) { // only styles that have some value and those, whose key is not numeric computedStyles[entry[0]] = entry[1]; } } + // extract the text of the element let text: string; if (target instanceof HTMLInputElement) { text = (target as HTMLInputElement).value; @@ -91,6 +72,8 @@ const markFinalStateHandler = (target: HTMLElement) => { text = target.innerText; } + const xPath: string = getElementsXpath(target); + const finalStateTarget: FinalStateTarget = { tagName: target.tagName, text: text, @@ -108,7 +91,7 @@ const markFinalStateHandler = (target: HTMLElement) => { chrome.runtime.sendMessage(message); // highlight the element on the page - target.style.outline = "5px solid rgba(218,172,0,0.8)"; + highlightElementByXpath(xPath); } const clickListenerHandler = (target: HTMLElement) => { @@ -127,6 +110,27 @@ const clickListenerHandler = (target: HTMLElement) => { chrome.runtime.sendMessage({action: action}); } +// when we listen for click that marks final state, we want to prevent +// user from redirecting to another page if he marks for example <a> as final state +chrome.storage.onChanged.addListener(() => { + getMarkFinalState().then(markFinalState => { + if (markFinalState) { + // try to keep the user on the page, so the selected element is visible + window.addEventListener('beforeunload', onBeforeUnload); + } else { + // remove the dialog + window.removeEventListener('beforeunload', onBeforeUnload); + } + }); +}); + +const onBeforeUnload = (e: BeforeUnloadEvent) => { + // Cancel the event + e.preventDefault(); + // unfortunately, we cannot set custom message to the dialog, as is it considered dangerous + // -> we need to emphasize this in user docs +} + const isNumeric = (value: string) => { return /^-?\d+$/.test(value); } \ No newline at end of file -- GitLab From cb6dc0b29d5201ff4efe1d3550d59b0e8ad4bbd5 Mon Sep 17 00:00:00 2001 From: vondrp <vondrovic@centrum.cz> Date: Wed, 3 Apr 2024 18:54:36 +0200 Subject: [PATCH 3/9] =?UTF-8?q?Re=20#11122=20-=20inicializace=20v=C4=9Btve?= =?UTF-8?q?,=20p=C5=99esunut=C3=AD=20v=C4=9Bc=C3=AD=20z=20final=20state=20?= =?UTF-8?q?z=20app.module=20do=20nov=C3=A9=20final-state=20componenty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app.component.html | 59 -------- src/app/app.component.ts | 75 ---------- src/app/app.module.ts | 4 +- .../final-stage/final-stage.component.css | 0 .../final-stage/final-stage.component.html | 60 ++++++++ .../final-stage/final-stage.component.spec.ts | 23 +++ .../final-stage/final-stage.component.ts | 135 ++++++++++++++++++ 7 files changed, 221 insertions(+), 135 deletions(-) create mode 100644 src/app/view/pages/final-stage/final-stage.component.css create mode 100644 src/app/view/pages/final-stage/final-stage.component.html create mode 100644 src/app/view/pages/final-stage/final-stage.component.spec.ts create mode 100644 src/app/view/pages/final-stage/final-stage.component.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 2631a37..73c12c5 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,63 +1,4 @@ <div id="scenario-tracker-content"> - <!-- START: MARK FINAL STATE --> - <ng-container *ngIf="finalStateTarget"> - <h5>Text:</h5> - <p>{{finalStateTarget.text}}</p> - <h5>Attributes:</h5> - <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> - <input type="checkbox" (change)="saveAttribute($event, attribute, finalStateTarget.attributes[attribute])"> - {{attribute}} = {{finalStateTarget.attributes[attribute]}} - </div> - <h5>Explicitly set styles:</h5> - <div *ngFor="let attribute of Object.keys(finalStateTarget.styles)"> - <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> - style.{{attribute}} = {{finalStateTarget.styles[attribute]}} - </div> - <!-- TODO: It is now commented out just because the output is too long, but we should show it to the user --> -<!-- <h5>Computed styles:</h5>--> -<!-- <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)">--> -<!-- <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])">--> -<!-- style.{{attribute}} = {{finalStateTarget.computedStyles[attribute]}}--> -<!-- </div>--> - <custom-button - buttonText="Cancel" - size="S" - iconType="fontawesome" - icon="fa fa-trash" - additionalClasses="warning-button" - (click)="deleteFinalStateTarget()" - > - </custom-button> - <custom-button - buttonText="Add" - size="S" - iconType="fontawesome" - icon="fa fa-plus" - additionalClasses="success-button" - (click)="addFinalState()" - > - </custom-button> - </ng-container> - - <h5>Final states:</h5> - <div *ngFor="let finalState of finalStates; let i = index"> - <b>{{finalState.element.tagName}}</b> - <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> - <!-- show only first 3 --> - <p *ngIf="j < 3">{{attribute}}: {{finalState.selectedAttributes[attribute]}}</p> - </div> - <custom-button - buttonText="Remove" - size="S" - iconType="fontawesome" - icon="fa fa-trash" - additionalClasses="delete-button" - (click)="deleteFinalState(i)" - > - </custom-button> - </div> - <!-- END: MARK FINAL STATE --> - <overview [deleteActionHandler]="deleteAction" [toggleRecordingHandler]="toggleRecording" diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 7bf70a9..6c128f8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -22,84 +22,16 @@ import {HtmlElement} from "./model/html-element.interface"; export class AppComponent implements OnInit { actions: Action[] = []; recording: boolean = false; - // START: MARK FINAL STATE - finalStates: Result[] = []; - finalStateTarget?: FinalStateTarget = undefined; - savedAttributes: ObjectKeys = {}; - protected readonly Object = Object; // just for the template - // END: MARK FINAL STATE constructor(private ref: ChangeDetectorRef){} ngOnInit(): void { - // START: MARK FINAL STATE - chrome.runtime.onMessage.addListener((message: Message) => { - if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state - console.log("Final state target:", message) - this.finalStateTarget = message.finalStateClickTarget; - this.savedAttributes = {}; - this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually - } - }); - // END: MARK FINAL STATE chrome.storage.onChanged.addListener(() => { this.getData(); }); this.getData(); } - // START: MARK FINAL STATE - /** - * Deletes the clicked final state target and sets all variables to default values. - */ - public deleteFinalStateTarget(){ - const xPath = this.finalStateTarget!.xPath; - this.finalStateTarget = undefined; - this.savedAttributes = {}; - - this.ref.detectChanges(); - const message: Message = { - xPath: xPath - } - chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { - chrome.tabs.sendMessage(tabs[0].id!, message); - }) - } - - public saveAttribute(event: Event, attribute: string, value: string){ - if((event.target as HTMLInputElement).checked){ - console.log(`Saving attribute ${attribute}=${value}`) - this.savedAttributes[attribute] = value; - } else { - console.log(`Deleting attribute ${attribute}=${value}`) - delete this.savedAttributes[attribute]; - } - console.log(this.savedAttributes) - } - - public addFinalState(){ - const element: HtmlElement = { - tagName: this.finalStateTarget!.tagName, - globalAttributes: {}, - elementAttributes: {}, - xPath: this.finalStateTarget!.xPath - }; - - const finalState: Result = { - element: element, - selectedAttributes: this.savedAttributes - }; - this.finalStates.push(finalState); - saveFinalStates(this.finalStates); - this.deleteFinalStateTarget(); - } - - public deleteFinalState = (index: number) => { - this.finalStates.splice(index, 1); - saveFinalStates(this.finalStates); - } - // END: MARK FINAL STATE - /** * Loads all data needed by the component. */ @@ -110,11 +42,6 @@ export class AppComponent implements OnInit { await getRecording().then(recording => { this.recording = recording; }); - // START: MARK FINAL STATE - await getFinalStates().then(finalStates => { - this.finalStates = finalStates; - }); - // END: MARK FINAL STATE this.ref.detectChanges(); // we need to automatically detect changes, because chrome.storage.onChanged.addListener // is outside of the Angular scope @@ -138,9 +65,7 @@ export class AppComponent implements OnInit { saveActions(this.actions); } - // START: MARK FINAL STATE public markFinalState = () => { saveMarkFinalState(true); } - // END: MARK FINAL STATE } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 83d6fd9..d74c2da 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,6 +8,7 @@ import { TimeDeltaComponent } from './view/components/time-delta/time-delta.comp import { ActionTypeComponent } from './view/components/action-type/action-type.component'; import { HumanDescComponent } from './view/components/human-desc/human-desc.component'; import { TechnicalDescComponent } from './view/components/technical-desc/technical-desc.component'; +import { FinalStageComponent } from './view/pages/final-stage/final-stage.component'; @NgModule({ declarations: [ @@ -17,7 +18,8 @@ import { TechnicalDescComponent } from './view/components/technical-desc/technic TimeDeltaComponent, ActionTypeComponent, HumanDescComponent, - TechnicalDescComponent + TechnicalDescComponent, + FinalStageComponent, ], imports: [ BrowserModule diff --git a/src/app/view/pages/final-stage/final-stage.component.css b/src/app/view/pages/final-stage/final-stage.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/view/pages/final-stage/final-stage.component.html b/src/app/view/pages/final-stage/final-stage.component.html new file mode 100644 index 0000000..f9ae98b --- /dev/null +++ b/src/app/view/pages/final-stage/final-stage.component.html @@ -0,0 +1,60 @@ +<div id="scenario-tracker-content"> + <!-- START: MARK FINAL STATE --> + <ng-container *ngIf="finalStateTarget"> + <h5>Text:</h5> + <p>{{finalStateTarget.text}}</p> + <h5>Attributes:</h5> + <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> + <input type="checkbox" (change)="saveAttribute($event, attribute, finalStateTarget.attributes[attribute])"> + {{attribute}} = {{finalStateTarget.attributes[attribute]}} + </div> + <h5>Explicitly set styles:</h5> + <div *ngFor="let attribute of Object.keys(finalStateTarget.styles)"> + <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> + style.{{attribute}} = {{finalStateTarget.styles[attribute]}} + </div> + <!-- TODO: It is now commented out just because the output is too long, but we should show it to the user --> +<!-- <h5>Computed styles:</h5>--> +<!-- <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)">--> +<!-- <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])">--> +<!-- style.{{attribute}} = {{finalStateTarget.computedStyles[attribute]}}--> +<!-- </div>--> + <custom-button + buttonText="Cancel" + size="S" + iconType="fontawesome" + icon="fa fa-trash" + additionalClasses="warning-button" + (click)="deleteFinalStateTarget()" + > + </custom-button> + <custom-button + buttonText="Add" + size="S" + iconType="fontawesome" + icon="fa fa-plus" + additionalClasses="success-button" + (click)="addFinalState()" + > + </custom-button> + </ng-container> + + <h5>Final states:</h5> + <div *ngFor="let finalState of finalStates; let i = index"> + <b>{{finalState.element.tagName}}</b> + <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> + <!-- show only first 3 --> + <p *ngIf="j < 3">{{attribute}}: {{finalState.selectedAttributes[attribute]}}</p> + </div> + <custom-button + buttonText="Remove" + size="S" + iconType="fontawesome" + icon="fa fa-trash" + additionalClasses="delete-button" + (click)="deleteFinalState(i)" + > + </custom-button> + </div> + <!-- END: MARK FINAL STATE --> +</div> \ No newline at end of file diff --git a/src/app/view/pages/final-stage/final-stage.component.spec.ts b/src/app/view/pages/final-stage/final-stage.component.spec.ts new file mode 100644 index 0000000..0c969af --- /dev/null +++ b/src/app/view/pages/final-stage/final-stage.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FinalStageComponent } from './final-stage.component'; + +describe('FinalStageComponent', () => { + let component: FinalStageComponent; + let fixture: ComponentFixture<FinalStageComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FinalStageComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FinalStageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/view/pages/final-stage/final-stage.component.ts b/src/app/view/pages/final-stage/final-stage.component.ts new file mode 100644 index 0000000..4e606c3 --- /dev/null +++ b/src/app/view/pages/final-stage/final-stage.component.ts @@ -0,0 +1,135 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Action } from 'src/app/model/action.interface'; +import { ActionEnum } from 'src/app/model/action.enum'; + +import { Message } from 'src/app/model/message.interface'; + +import { Result } from 'src/app/model/result.interface'; +import { FinalStateTarget } from 'src/app/model/final-state-target.interface'; +import { ObjectKeys } from 'src/app/model/object-keys.interface'; +import { HtmlElement } from 'src/app/model/html-element.interface'; + + +import { saveFinalStates } from 'src/app/utils/storage.util'; +import { getFinalStates } from 'src/app/utils/storage.util'; + +import { saveMarkFinalState } from 'src/app/utils/storage.util'; +import { ChangeDetectorRef } from '@angular/core'; + + +@Component({ + selector: 'final-stage', + templateUrl: './final-stage.component.html', + styleUrls: ['./final-stage.component.css'] +}) + +export class FinalStageComponent implements OnInit { + + // START: MARK FINAL STATE + finalStates: Result[] = []; + finalStateTarget?: FinalStateTarget = undefined; + savedAttributes: ObjectKeys = {}; + protected readonly Object = Object; // just for the template + // END: MARK FINAL STATE + + constructor(private ref: ChangeDetectorRef){} + + ngOnInit(): void { + // START: MARK FINAL STATE + chrome.runtime.onMessage.addListener((message: Message) => { + if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state + console.log("Final state target:", message) + this.finalStateTarget = message.finalStateClickTarget; + this.savedAttributes = {}; + this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually + } + }); + // END: MARK FINAL STATE + /* + chrome.storage.onChanged.addListener(() => { + this.getData(); + }); + this.getData(); + */ + } + + // START: MARK FINAL STATE + /** + * Deletes the clicked final state target and sets all variables to default values. + */ + public deleteFinalStateTarget(){ + const xPath = this.finalStateTarget!.xPath; + this.finalStateTarget = undefined; + this.savedAttributes = {}; + + this.ref.detectChanges(); + const message: Message = { + xPath: xPath + } + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.sendMessage(tabs[0].id!, message); + }) + } + + public saveAttribute(event: Event, attribute: string, value: string){ + if((event.target as HTMLInputElement).checked){ + console.log(`Saving attribute ${attribute}=${value}`) + this.savedAttributes[attribute] = value; + } else { + console.log(`Deleting attribute ${attribute}=${value}`) + delete this.savedAttributes[attribute]; + } + console.log(this.savedAttributes) + } + + public addFinalState(){ + const element: HtmlElement = { + tagName: this.finalStateTarget!.tagName, + globalAttributes: {}, + elementAttributes: {}, + xPath: this.finalStateTarget!.xPath + }; + + const finalState: Result = { + element: element, + selectedAttributes: this.savedAttributes + }; + this.finalStates.push(finalState); + saveFinalStates(this.finalStates); + this.deleteFinalStateTarget(); + } + + public deleteFinalState = (index: number) => { + this.finalStates.splice(index, 1); + saveFinalStates(this.finalStates); + } + // END: MARK FINAL STATE + + /** + * Loads all data needed by the component. + */ + private async getData() { + /* + await getActions().then(actions => { + this.actions = actions; + }); + await getRecording().then(recording => { + this.recording = recording; + }); + */ + // START: MARK FINAL STATE + await getFinalStates().then(finalStates => { + this.finalStates = finalStates; + }); + // END: MARK FINAL STATE + this.ref.detectChanges(); + // we need to automatically detect changes, because chrome.storage.onChanged.addListener + // is outside of the Angular scope + } + + // START: MARK FINAL STATE + public markFinalState = () => { + saveMarkFinalState(true); + } + // END: MARK FINAL STATE +} -- GitLab From 7ae28533dbb29d1b6a5684f3963981245726fbec Mon Sep 17 00:00:00 2001 From: vondrp <vondrovic@centrum.cz> Date: Wed, 3 Apr 2024 23:43:50 +0200 Subject: [PATCH 4/9] =?UTF-8?q?Re=2011122=20-=20p=C5=99euspo=C5=99=C3=A1d?= =?UTF-8?q?=C3=A1n=C3=AD=20projektu,=20za=C5=99=C3=ADzen=20p=C5=99echod=20?= =?UTF-8?q?stav=C5=AF=20mezi=20overview=20a=20final=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app.component.html | 17 ++- src/app/app.component.ts | 87 ++++++++++++ .../final-stage/final-stage.component.html | 19 ++- .../final-stage/final-stage.component.ts | 127 ++---------------- .../view/pages/overview/overview.component.ts | 2 +- 5 files changed, 133 insertions(+), 119 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index 73c12c5..6c6dc53 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,5 +1,8 @@ + + <div id="scenario-tracker-content"> - <overview + + <overview *ngIf="showOverview" [deleteActionHandler]="deleteAction" [toggleRecordingHandler]="toggleRecording" [deleteAllActionsHandler]="deleteAllActions" @@ -8,4 +11,16 @@ [recording]="recording" > </overview> + + <final-stage *ngIf="!showOverview" + [actions]="actions" + + [finalStateTarget] = "finalStateTarget" + [savedAttributes]="savedAttributes" + [saveAttribute]="saveAttribute" + [deleteFinalStateTarget] = "deleteFinalStateTarget" + [addFinalState] = "addFinalState" + [deleteFinalState] = "deleteFinalState" + [unMarkFinalState]="unMarkFinalState" + ></final-stage> </div> \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 6c128f8..3a4595f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -22,16 +22,86 @@ import {HtmlElement} from "./model/html-element.interface"; export class AppComponent implements OnInit { actions: Action[] = []; recording: boolean = false; + showOverview: boolean = true; + + // START: MARK FINAL STATE + finalStates: Result[] = []; + finalStateTarget?: FinalStateTarget = undefined; + savedAttributes: ObjectKeys = {}; + //protected readonly Object = Object; // just for the template + // END: MARK FINAL STATE constructor(private ref: ChangeDetectorRef){} ngOnInit(): void { + // START: MARK FINAL STATE + chrome.runtime.onMessage.addListener((message: Message) => { + if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state + console.log("Final state target:", message) + this.finalStateTarget = message.finalStateClickTarget; + this.savedAttributes = {}; + this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually + } + }); + // END: MARK FINAL STATE chrome.storage.onChanged.addListener(() => { this.getData(); }); this.getData(); } + // START: MARK FINAL STATE + /** + * Deletes the clicked final state target and sets all variables to default values. + */ + public deleteFinalStateTarget(){ + const xPath = this.finalStateTarget!.xPath; + this.finalStateTarget = undefined; + this.savedAttributes = {}; + + this.ref.detectChanges(); + const message: Message = { + xPath: xPath + } + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.sendMessage(tabs[0].id!, message); + }) + } + + public saveAttribute(event: Event, attribute: string, value: string){ + if((event.target as HTMLInputElement).checked){ + console.log(`Saving attribute ${attribute}=${value}`) + this.savedAttributes[attribute] = value; + } else { + console.log(`Deleting attribute ${attribute}=${value}`) + delete this.savedAttributes[attribute]; + } + console.log(this.savedAttributes) + } + + public addFinalState(){ + const element: HtmlElement = { + tagName: this.finalStateTarget!.tagName, + globalAttributes: {}, + elementAttributes: {}, + xPath: this.finalStateTarget!.xPath + }; + + const finalState: Result = { + element: element, + selectedAttributes: this.savedAttributes + }; + this.finalStates.push(finalState); + saveFinalStates(this.finalStates); + this.deleteFinalStateTarget(); + } + + public deleteFinalState = (index: number) => { + this.finalStates.splice(index, 1); + saveFinalStates(this.finalStates); + } + // END: MARK FINAL STATE + /** * Loads all data needed by the component. */ @@ -42,6 +112,11 @@ export class AppComponent implements OnInit { await getRecording().then(recording => { this.recording = recording; }); + // START: MARK FINAL STATE + await getFinalStates().then(finalStates => { + this.finalStates = finalStates; + }); + // END: MARK FINAL STATE this.ref.detectChanges(); // we need to automatically detect changes, because chrome.storage.onChanged.addListener // is outside of the Angular scope @@ -65,7 +140,19 @@ export class AppComponent implements OnInit { saveActions(this.actions); } + // START: MARK FINAL STATE public markFinalState = () => { saveMarkFinalState(true); + this.toggleView(); + } + + public unMarkFinalState = () => { + saveMarkFinalState(false); + this.toggleView(); + } + // END: MARK FINAL STATE + + toggleView() { + this.showOverview = !this.showOverview; } } diff --git a/src/app/view/pages/final-stage/final-stage.component.html b/src/app/view/pages/final-stage/final-stage.component.html index f9ae98b..5151bfb 100644 --- a/src/app/view/pages/final-stage/final-stage.component.html +++ b/src/app/view/pages/final-stage/final-stage.component.html @@ -1,4 +1,4 @@ -<div id="scenario-tracker-content"> +<!-- <div id="scenario-tracker-content"> --> <!-- START: MARK FINAL STATE --> <ng-container *ngIf="finalStateTarget"> <h5>Text:</h5> @@ -56,5 +56,20 @@ > </custom-button> </div> + + <h5>Unmark </h5> + <custom-button + buttonText="Return " + size="S" + iconType="fontawesome" + icon="fa fa-arrow-left" + additionalClasses="delete-button" + (click)="unMarkFinalState()" + > + </custom-button> + <!-- END: MARK FINAL STATE --> -</div> \ No newline at end of file + <!-- +</div> + +--> \ No newline at end of file diff --git a/src/app/view/pages/final-stage/final-stage.component.ts b/src/app/view/pages/final-stage/final-stage.component.ts index 4e606c3..c4bfcd5 100644 --- a/src/app/view/pages/final-stage/final-stage.component.ts +++ b/src/app/view/pages/final-stage/final-stage.component.ts @@ -1,20 +1,8 @@ import { Component, OnInit, Input } from '@angular/core'; import { Action } from 'src/app/model/action.interface'; -import { ActionEnum } from 'src/app/model/action.enum'; - -import { Message } from 'src/app/model/message.interface'; - import { Result } from 'src/app/model/result.interface'; import { FinalStateTarget } from 'src/app/model/final-state-target.interface'; import { ObjectKeys } from 'src/app/model/object-keys.interface'; -import { HtmlElement } from 'src/app/model/html-element.interface'; - - -import { saveFinalStates } from 'src/app/utils/storage.util'; -import { getFinalStates } from 'src/app/utils/storage.util'; - -import { saveMarkFinalState } from 'src/app/utils/storage.util'; -import { ChangeDetectorRef } from '@angular/core'; @Component({ @@ -24,112 +12,21 @@ import { ChangeDetectorRef } from '@angular/core'; }) export class FinalStageComponent implements OnInit { + @Input() actions: Action[] = []; + @Input() finalStates: Result[] = []; + @Input() finalStateTarget?: FinalStateTarget = undefined; + @Input() savedAttributes: ObjectKeys = {}; - // START: MARK FINAL STATE - finalStates: Result[] = []; - finalStateTarget?: FinalStateTarget = undefined; - savedAttributes: ObjectKeys = {}; - protected readonly Object = Object; // just for the template - // END: MARK FINAL STATE - - constructor(private ref: ChangeDetectorRef){} - - ngOnInit(): void { - // START: MARK FINAL STATE - chrome.runtime.onMessage.addListener((message: Message) => { - if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state - console.log("Final state target:", message) - this.finalStateTarget = message.finalStateClickTarget; - this.savedAttributes = {}; - this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually - } - }); - // END: MARK FINAL STATE - /* - chrome.storage.onChanged.addListener(() => { - this.getData(); - }); - this.getData(); - */ - } - // START: MARK FINAL STATE - /** - * Deletes the clicked final state target and sets all variables to default values. - */ - public deleteFinalStateTarget(){ - const xPath = this.finalStateTarget!.xPath; - this.finalStateTarget = undefined; - this.savedAttributes = {}; + @Input() saveAttribute!: Function; + @Input() deleteFinalStateTarget!: Function; + @Input() addFinalState!: Function; + @Input() deleteFinalState!: Function; - this.ref.detectChanges(); - const message: Message = { - xPath: xPath - } - chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { - chrome.tabs.sendMessage(tabs[0].id!, message); - }) - } - - public saveAttribute(event: Event, attribute: string, value: string){ - if((event.target as HTMLInputElement).checked){ - console.log(`Saving attribute ${attribute}=${value}`) - this.savedAttributes[attribute] = value; - } else { - console.log(`Deleting attribute ${attribute}=${value}`) - delete this.savedAttributes[attribute]; - } - console.log(this.savedAttributes) - } - - public addFinalState(){ - const element: HtmlElement = { - tagName: this.finalStateTarget!.tagName, - globalAttributes: {}, - elementAttributes: {}, - xPath: this.finalStateTarget!.xPath - }; + @Input() unMarkFinalState!: Function; - const finalState: Result = { - element: element, - selectedAttributes: this.savedAttributes - }; - this.finalStates.push(finalState); - saveFinalStates(this.finalStates); - this.deleteFinalStateTarget(); - } - - public deleteFinalState = (index: number) => { - this.finalStates.splice(index, 1); - saveFinalStates(this.finalStates); - } - // END: MARK FINAL STATE - - /** - * Loads all data needed by the component. - */ - private async getData() { - /* - await getActions().then(actions => { - this.actions = actions; - }); - await getRecording().then(recording => { - this.recording = recording; - }); - */ - // START: MARK FINAL STATE - await getFinalStates().then(finalStates => { - this.finalStates = finalStates; - }); - // END: MARK FINAL STATE - this.ref.detectChanges(); - // we need to automatically detect changes, because chrome.storage.onChanged.addListener - // is outside of the Angular scope - } - - // START: MARK FINAL STATE - public markFinalState = () => { - saveMarkFinalState(true); + protected readonly Object = Object; // just for the template + ngOnInit(): void { + } - // END: MARK FINAL STATE } diff --git a/src/app/view/pages/overview/overview.component.ts b/src/app/view/pages/overview/overview.component.ts index b0d0be5..11c7158 100644 --- a/src/app/view/pages/overview/overview.component.ts +++ b/src/app/view/pages/overview/overview.component.ts @@ -20,7 +20,7 @@ export class OverviewComponent implements OnInit { // END: MARK FINAL STATE readonly ActionEnum = ActionEnum - + constructor() { } ngOnInit(): void { -- GitLab From 36aba3c4d7c666f01141b1fa891c6fa8f06ab747 Mon Sep 17 00:00:00 2001 From: vondrp <vondrovic@centrum.cz> Date: Thu, 4 Apr 2024 22:02:55 +0200 Subject: [PATCH 5/9] =?UTF-8?q?Re=2011122=20-=20p=C5=99id=C3=A1n=C3=AD=20o?= =?UTF-8?q?bsahu=20v=C4=9Btve=2011115=5Fkomponenta=5Fcela=5Fakce=20(=C3=BA?= =?UTF-8?q?kol=201115)=20=09=09-=20pro=20p=C5=99=C3=ADpadn=C3=A9=20sjednoc?= =?UTF-8?q?en=C3=AD=20styl=C5=AF,=20zabr=C3=A1n=C4=9Bn=C3=AD=20pot=C3=AD?= =?UTF-8?q?=C5=BE=C3=AD=20p=C5=99i=20mergnut=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - vytvoÅ™enà samostatné componenty pro final-stage - zajiÅ¡tÄ›nà pÅ™epÃnánà mezi overview a final-stage - nastylovánà final-stage podle návrhu ve figmÄ› - k dispozici zakomentovaný ukázkový FinalStageTarget - vytvoÅ™eno pole se seznamem povinných atributů - doÄasné Å™eÅ¡enà --- src/app/app.component.css | 3 + src/app/app.component.html | 25 ++-- src/app/app.component.ts | 3 + src/app/app.module.ts | 2 + .../action-type/action-type.component.css | 6 +- .../action-type/action-type.component.html | 3 +- .../captured-action.component.css | 92 +++++++++++++++ .../captured-action.component.html | 19 ++++ .../captured-action.component.spec.ts | 23 ++++ .../captured-action.component.ts | 31 +++++ .../custom-button/custom-button.component.css | 7 ++ .../final-stage/final-stage.component.css | 90 +++++++++++++++ .../final-stage/final-stage.component.html | 107 ++++++++++++------ .../final-stage/final-stage.component.ts | 32 +++++- .../pages/overview/overview.component.html | 34 +++--- .../view/pages/overview/overview.component.ts | 8 ++ 16 files changed, 420 insertions(+), 65 deletions(-) create mode 100644 src/app/view/components/captured-action/captured-action.component.css create mode 100644 src/app/view/components/captured-action/captured-action.component.html create mode 100644 src/app/view/components/captured-action/captured-action.component.spec.ts create mode 100644 src/app/view/components/captured-action/captured-action.component.ts diff --git a/src/app/app.component.css b/src/app/app.component.css index e69de29..0583ba8 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -0,0 +1,3 @@ +.component { + width: 100%; +} \ No newline at end of file diff --git a/src/app/app.component.html b/src/app/app.component.html index 6c6dc53..8171fee 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,6 +1,4 @@ - - -<div id="scenario-tracker-content"> +<div id="scenario-tracker-content" class="component"> <overview *ngIf="showOverview" [deleteActionHandler]="deleteAction" @@ -13,14 +11,15 @@ </overview> <final-stage *ngIf="!showOverview" - [actions]="actions" - - [finalStateTarget] = "finalStateTarget" - [savedAttributes]="savedAttributes" - [saveAttribute]="saveAttribute" - [deleteFinalStateTarget] = "deleteFinalStateTarget" - [addFinalState] = "addFinalState" - [deleteFinalState] = "deleteFinalState" - [unMarkFinalState]="unMarkFinalState" - ></final-stage> + [actions]="actions" + + [finalStateTarget]="finalStateTarget" + [savedAttributes]="savedAttributes" + [saveAttribute]="saveAttribute" + [deleteFinalStateTarget] = "deleteFinalStateTarget" + [addFinalState] = "addFinalState" + [deleteFinalState] = "deleteFinalState" + [unMarkFinalState]="unMarkFinalState" + > + </final-stage> </div> \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3a4595f..00770a8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -35,6 +35,8 @@ export class AppComponent implements OnInit { ngOnInit(): void { // START: MARK FINAL STATE + + console.log("App init") chrome.runtime.onMessage.addListener((message: Message) => { if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state console.log("Final state target:", message) @@ -43,6 +45,7 @@ export class AppComponent implements OnInit { this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually } }); + // END: MARK FINAL STATE chrome.storage.onChanged.addListener(() => { this.getData(); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d74c2da..6b7b19b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,6 +8,7 @@ import { TimeDeltaComponent } from './view/components/time-delta/time-delta.comp import { ActionTypeComponent } from './view/components/action-type/action-type.component'; import { HumanDescComponent } from './view/components/human-desc/human-desc.component'; import { TechnicalDescComponent } from './view/components/technical-desc/technical-desc.component'; +import { CapturedActionComponent } from './view/components/captured-action/captured-action.component'; import { FinalStageComponent } from './view/pages/final-stage/final-stage.component'; @NgModule({ @@ -19,6 +20,7 @@ import { FinalStageComponent } from './view/pages/final-stage/final-stage.compon ActionTypeComponent, HumanDescComponent, TechnicalDescComponent, + CapturedActionComponent, FinalStageComponent, ], imports: [ diff --git a/src/app/view/components/action-type/action-type.component.css b/src/app/view/components/action-type/action-type.component.css index 2c93e11..9eb106a 100644 --- a/src/app/view/components/action-type/action-type.component.css +++ b/src/app/view/components/action-type/action-type.component.css @@ -4,4 +4,8 @@ padding: 4px 8px; text-align: center; border-radius: 5px; - } \ No newline at end of file + } + +.action-type::first-letter{ + text-transform: uppercase; +} \ No newline at end of file diff --git a/src/app/view/components/action-type/action-type.component.html b/src/app/view/components/action-type/action-type.component.html index 48e0ddd..b8c20d3 100644 --- a/src/app/view/components/action-type/action-type.component.html +++ b/src/app/view/components/action-type/action-type.component.html @@ -1,4 +1,3 @@ -<div class="action-type" - [style.backgroundColor] = "color"> +<div class="action-type" [style.backgroundColor] = "color"> {{text}} </div> \ No newline at end of file diff --git a/src/app/view/components/captured-action/captured-action.component.css b/src/app/view/components/captured-action/captured-action.component.css new file mode 100644 index 0000000..4fb67f1 --- /dev/null +++ b/src/app/view/components/captured-action/captured-action.component.css @@ -0,0 +1,92 @@ +.container { + display: flex; + flex-wrap: nowrap; + flex-direction: row; +} + +.component { + overflow: hidden; + white-space: nowrap; +} + +.inner-component { + width: 100%; +} + +#first-component { + padding: 5px; + display: flex; + align-items: center; + width: 25%; +} + +#second-component { + padding: 5px; + display: flex; + flex-direction: column; + align-items: flex-start; + width: 40% +} + +#third-component { + padding: 5px; + display: flex; + align-items: center; + width: 10%; +} + +#fourth-component { + padding: 5px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + width: 25% +} + +.pl5 { + padding-left: 3px; +} + +/* TEST */ +.flex-container { + + display: flex; + + /* flex-flow: nowrap; */ + flex-wrap: nowrap; + align-content: stretch; + + background-color: #bbdefb; + height: 100%; + padding: 15px; + gap: 5px; + + } + + .flex-container > div{ + background: #ffecb3; + border: 3px solid #ffcc80; + border-radius: 5px; + padding: 8px; + } + + + .item1 { + /* flex:1 1 25%; */ + flex-grow:1; + flex-shrink:1; + flex-basis:25%; + } + + .item2 { + /* flex:0 1 50%; */ + flex-shrink:1; + flex-basis:50%; + } + + .item3 { + /* flex:0 1 25%; */ + flex-basis:25%; + align-self:auto; + } \ No newline at end of file diff --git a/src/app/view/components/captured-action/captured-action.component.html b/src/app/view/components/captured-action/captured-action.component.html new file mode 100644 index 0000000..f7d57e4 --- /dev/null +++ b/src/app/view/components/captured-action/captured-action.component.html @@ -0,0 +1,19 @@ +<div class="container"> + <div id="first-component" class="component"> + <action-type [text]="ActionEnum[action.type].toLowerCase()" [color]="getColor()" class="inner-component"></action-type> + </div> + <div id="second-component" class="component"> + <technical-desc [action]="action" class="inner-component"></technical-desc> + <human-desc [action]="action" class="inner-component"></human-desc> + </div> + <div id="third-component" class="component"> + <ng-container *ngIf="startTime"> + <time-delta [actionTime]="action.recordedOn" [startTime]="startTime"></time-delta> + </ng-container> + </div> + <div id="fourth-component" class="component"> + <custom-button (click)="deleteActionHandler(actionIndex)" backgroundColor="#A92D2D" icon="fa fa-trash" iconType="fontawesome" buttonText="" size="S" color="white"></custom-button> + <custom-button backgroundColor="#8F8F8F" icon="fa fa-eye" iconType="fontawesome" buttonText="" class="pl5" size="S" color="white"></custom-button> + </div> +</div> +<hr> diff --git a/src/app/view/components/captured-action/captured-action.component.spec.ts b/src/app/view/components/captured-action/captured-action.component.spec.ts new file mode 100644 index 0000000..5705bb3 --- /dev/null +++ b/src/app/view/components/captured-action/captured-action.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CapturedActionComponent } from './captured-action.component'; + +describe('CapturedActionComponent', () => { + let component: CapturedActionComponent; + let fixture: ComponentFixture<CapturedActionComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CapturedActionComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CapturedActionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/view/components/captured-action/captured-action.component.ts b/src/app/view/components/captured-action/captured-action.component.ts new file mode 100644 index 0000000..6881e52 --- /dev/null +++ b/src/app/view/components/captured-action/captured-action.component.ts @@ -0,0 +1,31 @@ +import { Component, Input } from '@angular/core'; +import { Action } from 'src/app/model/action.interface'; +import { ActionEnum } from 'src/app/model/action.enum'; + +@Component({ + selector: 'captured-action', + templateUrl: './captured-action.component.html', + styleUrl: './captured-action.component.css' +}) +export class CapturedActionComponent { + @Input() action!: Action; + @Input() deleteActionHandler!: Function; + @Input() startTime?: Date; + @Input() actionIndex!: number; + + readonly ActionEnum = ActionEnum + + getColor(): string { + switch(this.action.type) { + case ActionEnum.CLICK: return "#3C5C79"; //Blue + case ActionEnum.COPY: return "#823838"; //Red + case ActionEnum.PASTE: return "#795D3C"; //Brown + case ActionEnum.CUT: return "#743C79"; //Purple + case ActionEnum.KEYDOWN: return "#38825A"; //Green + case ActionEnum.RESIZE: return "#3C796F"; //Sea Blue + case ActionEnum.CHANGE: return "#BF9000"; //Golden + case ActionEnum.SELECT: return "#3F822B"; //Dark Green + default: return ""; + } + } +} diff --git a/src/app/view/components/custom-button/custom-button.component.css b/src/app/view/components/custom-button/custom-button.component.css index 556cff3..53fbb6c 100644 --- a/src/app/view/components/custom-button/custom-button.component.css +++ b/src/app/view/components/custom-button/custom-button.component.css @@ -104,6 +104,13 @@ background-color: #f8f9fa; } +.return-button { + /* Add your custom styles for the "Return" button here */ + background-color: #007bff; /* Example background color */ + color: white; /* Example text color */ + /* Add any other styles you want */ + } + /* Possible alternatives */ .alternative-button-1 { diff --git a/src/app/view/pages/final-stage/final-stage.component.css b/src/app/view/pages/final-stage/final-stage.component.css index e69de29..93c5be8 100644 --- a/src/app/view/pages/final-stage/final-stage.component.css +++ b/src/app/view/pages/final-stage/final-stage.component.css @@ -0,0 +1,90 @@ +.fs_container { + background-color: rgb(232, 232, 232); + margin: 10px auto; /* Adjust the margin to center horizontally */ + padding: 10px; /* Optionally add padding */ + /*display: flex; /* Use flexbox for layout */ + /*justify-content: center; /* Center horizontally */ + /*align-items: center; /* Center vertically */ + +} + + +.custom-checkbox { + appearance: none; + width: 1.3em; + height: 1.3em; + border: 1px solid #ccc; + border-radius: 3px; + vertical-align: middle; + cursor: pointer; + background-color: rgb(128, 128, 128); + padding: 10px; /* Remove padding */ + display: flex; /* Use flexbox for centering */ + justify-content: center; /* Center horizontally */ + align-items: center; /* Center vertically */ +} + +.custom-checkbox:checked::after { + content: '\f00c'; /* Font Awesome checkmark icon */ + font-family: 'Font Awesome 5 Free'; /* Specify Font Awesome font family */ + font-weight: 900; /* Adjust weight if necessary */ + font-size: 1em; + color: white; +} + +.checkbox-container { + display: flex; /* Use flexbox for layout */ + align-items: center; /* Align items vertically */ + margin-bottom: 5px; /* Add margin between checkboxes */ + flex-wrap: nowrap; /* Prevent wrapping of items */ +} + +.checkbox-container label { + margin-left: 5px; /* Add space between checkbox and text */ +} + + +.button-container { + width: 100%; /* Ensures the container spans the full width */ + } + +.return-fs-button { + width: 100%; + background-color: #ffffff; /* Button background color */ + color: #808080; /* Grey text color */ + border: 1px solid #808080; /* Grey border */ + padding: 10px; /* Adjust padding as needed */ + cursor: pointer; + box-sizing: border-box; /* Include padding and border in width */ + text-decoration: none; /* Remove default link underline */ + display: block; /* Ensures the button occupies the entire line */ + text-align: right; +} + +.return-fs-button:hover { + background-color: #f0f0f0; /* Lighter background color on hover */ +} + +.return-fs-button:active { + background-color: #d9d9d9; /* Darker background color when button is active */ +} + +.attribute-name { + text-transform: capitalize; +} + +.attribute-value { + color: #333333; /* Darker grey color */ + text-decoration: underline; /* Underline */ + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); /* Shadow around the value */ + margin-left: 5px; +} + +.mandatory-attribute-name { + text-transform: lowercase; + color: #333; +} + +.mandatory-attribute-value { + color: #333; +} \ No newline at end of file diff --git a/src/app/view/pages/final-stage/final-stage.component.html b/src/app/view/pages/final-stage/final-stage.component.html index 5151bfb..f0ad702 100644 --- a/src/app/view/pages/final-stage/final-stage.component.html +++ b/src/app/view/pages/final-stage/final-stage.component.html @@ -1,30 +1,81 @@ +<div class="button-container"> + +<custom-button + buttonText="Not a final state, go back" + size="S" + iconType="fontawesome" + icon="fa fa-arrow-left" + additionalClasses="return-fs-button" + (click)="unMarkFinalState()" + > + </custom-button> +</div> +<h3>How to recognize final state?</h3> <!-- <div id="scenario-tracker-content"> --> <!-- START: MARK FINAL STATE --> - <ng-container *ngIf="finalStateTarget"> - <h5>Text:</h5> - <p>{{finalStateTarget.text}}</p> - <h5>Attributes:</h5> + <ng-container *ngIf="finalStateTarget"> + <div class="fs_container"> + <h3> Selected element: {{finalStateTarget.tagName}} </h3> + + <!-- not chooseable attributes - default --> <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> - <input type="checkbox" (change)="saveAttribute($event, attribute, finalStateTarget.attributes[attribute])"> - {{attribute}} = {{finalStateTarget.attributes[attribute]}} + <ng-container *ngIf="mandatoryAttributes.includes(attribute)"> + <div class="checkbox-container"> + <input type="checkbox" class="custom-checkbox" checked style="display: none;" (change)="saveAttribute($event, attribute, finalStateTarget.attributes[attribute])"> + <label> + <span class="mandatory-attribute-name"> {{attribute}}: </span> + <span class="mandatory-attribute-value">{{finalStateTarget.attributes[attribute]}} </span> + </label> + </div> + </ng-container> + </div> + + <ng-container *ngIf="finalStateTarget.text"> + <h5>Text:</h5> + <p>{{finalStateTarget.text}}</p> + </ng-container> + <h4>Select characteristic properties</h4> + <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> + <ng-container *ngIf="!mandatoryAttributes.includes(attribute)"> + <div class="checkbox-container"> + <input type="checkbox" class="custom-checkbox" (change)="saveAttribute($event, attribute, finalStateTarget.attributes[attribute])"> + <label> + <span class="attribute-name"> {{attribute}}: </span> + <span class="attribute-value">{{finalStateTarget.attributes[attribute]}} </span> + </label> + </div> + </ng-container> </div> - <h5>Explicitly set styles:</h5> + <h5>Styles</h5> + <!-- <h5>Explicitly set styles:</h5> --> <div *ngFor="let attribute of Object.keys(finalStateTarget.styles)"> - <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> - style.{{attribute}} = {{finalStateTarget.styles[attribute]}} + <div class="checkbox-container"> + <input type="checkbox" class="custom-checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> + <label> + <span class="attribute-name"> {{attribute}}: </span> + <span class="attribute-value">{{finalStateTarget.styles[attribute]}} </span> + </label> + </div> + </div> + <!-- <h5>Computed styles:</h5> --> + <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)"> + <div class="checkbox-container"> + <input type="checkbox" class="custom-checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> + <label> + <span class="attribute-name"> {{attribute}}: </span> + <span class="attribute-value">{{finalStateTarget.computedStyles[attribute]}} </span> + </label> + </div> </div> - <!-- TODO: It is now commented out just because the output is too long, but we should show it to the user --> -<!-- <h5>Computed styles:</h5>--> -<!-- <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)">--> -<!-- <input type="checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])">--> -<!-- style.{{attribute}} = {{finalStateTarget.computedStyles[attribute]}}--> -<!-- </div>--> + + <span style="margin-top: 1em;"> <custom-button buttonText="Cancel" size="S" iconType="fontawesome" icon="fa fa-trash" - additionalClasses="warning-button" + additionalClasses="delete-button" + additionalStyles="width: 10em;" (click)="deleteFinalStateTarget()" > </custom-button> @@ -33,21 +84,24 @@ size="S" iconType="fontawesome" icon="fa fa-plus" - additionalClasses="success-button" + additionalClasses="success-button pl_fs" + additionalStyles="margin-left: 2em; width: 10em;" (click)="addFinalState()" > </custom-button> + </span> + </div> </ng-container> - <h5>Final states:</h5> - <div *ngFor="let finalState of finalStates; let i = index"> + <h3>Final states:</h3> + <div *ngFor="let finalState of finalStates; let i = index" class="fs_container"> <b>{{finalState.element.tagName}}</b> <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> <!-- show only first 3 --> <p *ngIf="j < 3">{{attribute}}: {{finalState.selectedAttributes[attribute]}}</p> </div> <custom-button - buttonText="Remove" + buttonText="" size="S" iconType="fontawesome" icon="fa fa-trash" @@ -55,18 +109,7 @@ (click)="deleteFinalState(i)" > </custom-button> - </div> - - <h5>Unmark </h5> - <custom-button - buttonText="Return " - size="S" - iconType="fontawesome" - icon="fa fa-arrow-left" - additionalClasses="delete-button" - (click)="unMarkFinalState()" - > - </custom-button> + </div> <!-- END: MARK FINAL STATE --> <!-- diff --git a/src/app/view/pages/final-stage/final-stage.component.ts b/src/app/view/pages/final-stage/final-stage.component.ts index c4bfcd5..3747622 100644 --- a/src/app/view/pages/final-stage/final-stage.component.ts +++ b/src/app/view/pages/final-stage/final-stage.component.ts @@ -4,6 +4,8 @@ import { Result } from 'src/app/model/result.interface'; import { FinalStateTarget } from 'src/app/model/final-state-target.interface'; import { ObjectKeys } from 'src/app/model/object-keys.interface'; +import { Message } from 'src/app/model/message.interface'; +import { ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'final-stage', @@ -15,6 +17,8 @@ export class FinalStageComponent implements OnInit { @Input() actions: Action[] = []; @Input() finalStates: Result[] = []; @Input() finalStateTarget?: FinalStateTarget = undefined; + //finalStateTarget?: FinalStateTarget = undefined; //used for testing view + @Input() savedAttributes: ObjectKeys = {}; @@ -25,8 +29,34 @@ export class FinalStageComponent implements OnInit { @Input() unMarkFinalState!: Function; + // temporary solution - for future make better + mandatoryAttributes: string[] = ['id', 'class', 'name'] + + constructor(private ref: ChangeDetectorRef){} + protected readonly Object = Object; // just for the template ngOnInit(): void { - + // Initialize finalStateTarget to the default value for testing purposes + /* + this.finalStateTarget = { + tagName: 'div', // Example tag name + text: 'Assault rifle-wielding standard trooper', // Example text + xPath: '/HTML[1]/BODY[1]/DIV[1]', // Example XPath + attributes: { + dir: 'ltr', // Example attribute + army: 'Republic Clone Army', // Example additional attribute + id: 'random-id', // Random id + class: 'random-class' // Random class + }, + styles: { + outline: 'rgba(218, 172, 0, 0.8) solid 5px', // Example style + color: 'blue' // Another example style + }, + computedStyles: { + fontFamily: 'Arial', // Example computed style + fontSize: '16px' // Another example computed style + } + }; + */ } } diff --git a/src/app/view/pages/overview/overview.component.html b/src/app/view/pages/overview/overview.component.html index 34e0eaf..730799c 100644 --- a/src/app/view/pages/overview/overview.component.html +++ b/src/app/view/pages/overview/overview.component.html @@ -2,17 +2,6 @@ <h1>Scenario Tracker</h1> <!-- Controls --> -<!-- START: MARK FINAL STATE --> -<custom-button - buttonText="Mark final state" - size="M" - iconType="fontawesome" - icon="fa fa-solid fa-flag-checkered" - additionalClasses="info-button" - (click)="markFinalStateHandler()"> -</custom-button> -<!-- END: MARK FINAL STATE --> - <div style="display: inline-block; width: 10px;"></div> <custom-button buttonText="Delete all actions" @@ -41,10 +30,9 @@ additionalClasses="start-button" (click)="toggleRecordingHandler()" *ngIf="!recording"></custom-button> - <!-- List of actions --> -<div *ngFor="let action of actions; index as i"> - <div *ngIf="action.documentAction" [ngSwitch]="action.type"> +<div *ngFor="let action of actions; index as i" class="action-block"> + <!-- <div *ngIf="action.documentAction" [ngSwitch]="action.type"> <h2 *ngSwitchCase="ActionEnum.CLICK">Clicked on {{action.documentAction.target.tagName}}</h2> <h2 *ngSwitchCase="ActionEnum.COPY">Copied '{{action.documentAction.text}}' from {{action.documentAction.target.tagName}}</h2> <h2 *ngSwitchCase="ActionEnum.CUT">Cut '{{action.documentAction.text}}' from {{action.documentAction.target.tagName}}</h2> @@ -53,10 +41,24 @@ <h2 *ngSwitchCase="ActionEnum.CHANGE">Input changed to {{action.documentAction.value}}</h2> <h2 *ngSwitchCase="ActionEnum.SELECT">Selected '{{action.documentAction.text}}' in {{action.documentAction.target.tagName}}</h2> <p>id = {{action.documentAction.target.globalAttributes['id']}}, class = {{action.documentAction.target.globalAttributes['class']}}</p> + <captured-action [action]="action"></captured-action> </div> <div *ngIf="action.windowAction" [ngSwitch]="action.type"> <h2 *ngSwitchCase="ActionEnum.RESIZE">Window resized to {{action.windowAction.width}} x {{action.windowAction.height}}</h2> </div> <p>recorded at {{action.recordedOn | date:"dd.MM.yyyy hh:mm:ss"}}</p> - <button (click)="deleteActionHandler(i)">Delete action</button> -</div> \ No newline at end of file + <button (click)="deleteActionHandler(i)">Delete action</button> --> + <captured-action [action]="action" [deleteActionHandler]="deleteActionHandler" [startTime]="startTime" [actionIndex]="i"></captured-action> +</div> + +<!-- START: MARK FINAL STATE --> +<custom-button +buttonText="Mark final state" +size="M" +iconType="fontawesome" +icon="fa fa-solid fa-flag-checkered" +additionalClasses="info-button" +additionalStyles="margin-top: 2em; margin-left:1em;" +(click)="markFinalStateHandler()"> +</custom-button> +<!-- END: MARK FINAL STATE --> \ No newline at end of file diff --git a/src/app/view/pages/overview/overview.component.ts b/src/app/view/pages/overview/overview.component.ts index 11c7158..e60396b 100644 --- a/src/app/view/pages/overview/overview.component.ts +++ b/src/app/view/pages/overview/overview.component.ts @@ -21,8 +21,16 @@ export class OverviewComponent implements OnInit { readonly ActionEnum = ActionEnum + startTime?: Date; + constructor() { } ngOnInit(): void { } + + setStartTime() { + if(this.startTime == null) { + this.startTime = new Date(); + } + } } -- GitLab From 5e5e84ab5139a9838dd2b8cba4670ec4c8d96d28 Mon Sep 17 00:00:00 2001 From: vondrp <vondrovic@centrum.cz> Date: Fri, 5 Apr 2024 20:44:22 +0200 Subject: [PATCH 6/9] Re #11121 + #111122 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dle návrhu ve firmÄ› vytvoÅ™ena komponenta zobrazenà cÃlového stavu + dodateÄnÄ› ÄásteÄnÄ› upravena komponenta oznaÄenà cÃlového stavu - k stylům custom-button pÅ™idáno edit button + XS velikost - v app.component.html pÅ™edávané funkci pojmenovány Handler (pro konzistenci) - pÅ™idán seznam atributů, které se vždy zaznamenajà --- src/app/app.component.html | 15 ++-- src/app/app.component.ts | 33 ++++--- src/app/app.module.ts | 2 + .../custom-button/custom-button.component.css | 22 +++-- .../custom-button/custom-button.component.ts | 2 +- .../final-state/final-state.component.css | 50 +++++++++++ .../final-state/final-state.component.html | 39 +++++++++ .../final-state/final-state.component.spec.ts | 23 +++++ .../final-state/final-state.component.ts | 22 +++++ .../final-stage/final-stage.component.css | 10 ++- .../final-stage/final-stage.component.html | 85 +++++++++++++------ .../final-stage/final-stage.component.ts | 21 ++--- 12 files changed, 256 insertions(+), 68 deletions(-) create mode 100644 src/app/view/components/final-state/final-state.component.css create mode 100644 src/app/view/components/final-state/final-state.component.html create mode 100644 src/app/view/components/final-state/final-state.component.spec.ts create mode 100644 src/app/view/components/final-state/final-state.component.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 8171fee..7348bdf 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -4,7 +4,7 @@ [deleteActionHandler]="deleteAction" [toggleRecordingHandler]="toggleRecording" [deleteAllActionsHandler]="deleteAllActions" - [markFinalStateHandler]="markFinalState" + [markFinalStateHandler]="markFinalStateHandler" [actions]="actions" [recording]="recording" > @@ -12,14 +12,15 @@ <final-stage *ngIf="!showOverview" [actions]="actions" - + [mainAttributes]="mainAttributes" [finalStateTarget]="finalStateTarget" [savedAttributes]="savedAttributes" - [saveAttribute]="saveAttribute" - [deleteFinalStateTarget] = "deleteFinalStateTarget" - [addFinalState] = "addFinalState" - [deleteFinalState] = "deleteFinalState" - [unMarkFinalState]="unMarkFinalState" + [saveAttributeHandler]="saveAttributeHandler" + [isAttributeSavedHandler]="isAttributeSavedHandler" + [deleteFinalStateTargetHandler] = "deleteFinalStateTargetHandler" + [addFinalStateHandler] = "addFinalStateHandler" + [deleteFinalStateHandler] = "deleteFinalStateHandler" + [unMarkFinalStateHandler]="unMarkFinalStateHandler" > </final-stage> </div> \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 00770a8..464870b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -28,6 +28,9 @@ export class AppComponent implements OnInit { finalStates: Result[] = []; finalStateTarget?: FinalStateTarget = undefined; savedAttributes: ObjectKeys = {}; + + // array of attributes of final state which are always saved when the showed up + mainAttributes: string[] = ['id', 'class', 'name']; //protected readonly Object = Object; // just for the template // END: MARK FINAL STATE @@ -35,8 +38,6 @@ export class AppComponent implements OnInit { ngOnInit(): void { // START: MARK FINAL STATE - - console.log("App init") chrome.runtime.onMessage.addListener((message: Message) => { if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state console.log("Final state target:", message) @@ -57,7 +58,7 @@ export class AppComponent implements OnInit { /** * Deletes the clicked final state target and sets all variables to default values. */ - public deleteFinalStateTarget(){ + public deleteFinalStateTargetHandler(){ const xPath = this.finalStateTarget!.xPath; this.finalStateTarget = undefined; this.savedAttributes = {}; @@ -71,7 +72,7 @@ export class AppComponent implements OnInit { }) } - public saveAttribute(event: Event, attribute: string, value: string){ + public saveAttributeHandler(event: Event, attribute: string, value: string){ if((event.target as HTMLInputElement).checked){ console.log(`Saving attribute ${attribute}=${value}`) this.savedAttributes[attribute] = value; @@ -82,7 +83,19 @@ export class AppComponent implements OnInit { console.log(this.savedAttributes) } - public addFinalState(){ + isAttributeSavedHandler(attribute: string): boolean { + return this.savedAttributes.hasOwnProperty(attribute); + } + + public addFinalStateHandler(){ + // Call saveAttribute for each mandatory attribute + Object.keys(this.finalStateTarget!.attributes).forEach(attribute => { + if (this.mainAttributes.includes(attribute)) { + const mockEvent = { target: { checked: true } } as any; + this.saveAttributeHandler(mockEvent, attribute, this.finalStateTarget!.attributes[attribute]); + } + }); + const element: HtmlElement = { tagName: this.finalStateTarget!.tagName, globalAttributes: {}, @@ -96,10 +109,10 @@ export class AppComponent implements OnInit { }; this.finalStates.push(finalState); saveFinalStates(this.finalStates); - this.deleteFinalStateTarget(); + this.deleteFinalStateTargetHandler(); } - public deleteFinalState = (index: number) => { + public deleteFinalStateHandler = (index: number) => { this.finalStates.splice(index, 1); saveFinalStates(this.finalStates); } @@ -144,13 +157,13 @@ export class AppComponent implements OnInit { } // START: MARK FINAL STATE - public markFinalState = () => { + public markFinalStateHandler = () => { saveMarkFinalState(true); this.toggleView(); } - public unMarkFinalState = () => { - saveMarkFinalState(false); + public unMarkFinalStateHandler = () => { + //saveMarkFinalState(false); this.toggleView(); } // END: MARK FINAL STATE diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6b7b19b..5a4ba8f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -10,6 +10,7 @@ import { HumanDescComponent } from './view/components/human-desc/human-desc.comp import { TechnicalDescComponent } from './view/components/technical-desc/technical-desc.component'; import { CapturedActionComponent } from './view/components/captured-action/captured-action.component'; import { FinalStageComponent } from './view/pages/final-stage/final-stage.component'; +import { FinalStateComponent } from './view/components/final-state/final-state.component'; @NgModule({ declarations: [ @@ -22,6 +23,7 @@ import { FinalStageComponent } from './view/pages/final-stage/final-stage.compon TechnicalDescComponent, CapturedActionComponent, FinalStageComponent, + FinalStateComponent, ], imports: [ BrowserModule diff --git a/src/app/view/components/custom-button/custom-button.component.css b/src/app/view/components/custom-button/custom-button.component.css index 53fbb6c..63732bf 100644 --- a/src/app/view/components/custom-button/custom-button.component.css +++ b/src/app/view/components/custom-button/custom-button.component.css @@ -11,6 +11,11 @@ /* Sizes styles */ /* small button S */ +.XS { + font-size: 10px; + padding: 4px 8px; +} + .S { font-size: 12px; padding: 6px 10px; @@ -104,12 +109,17 @@ background-color: #f8f9fa; } -.return-button { - /* Add your custom styles for the "Return" button here */ - background-color: #007bff; /* Example background color */ - color: white; /* Example text color */ - /* Add any other styles you want */ - } + +.edit-button { + background-color: #999; /* Example color */ + color: #fff; /* Example text color */ + +} + +.edit-button:hover { + background-color: #aaa; /* Example hover color */ +} + /* Possible alternatives */ diff --git a/src/app/view/components/custom-button/custom-button.component.ts b/src/app/view/components/custom-button/custom-button.component.ts index 7abaf6b..7ac7617 100644 --- a/src/app/view/components/custom-button/custom-button.component.ts +++ b/src/app/view/components/custom-button/custom-button.component.ts @@ -12,7 +12,7 @@ export class CustomButtonComponent { @Input() buttonText: string = 'Button'; @Input() color: string = ''; // not given default value, because it overwrite possible value in styles @Input() backgroundColor: string = '#666;'; - @Input() size: 'S' | 'M' | 'L' = 'M'; + @Input() size: 'XS' | 'S' | 'M' | 'L' = 'M'; @Input() additionalClasses: string = ''; @Input() additionalStyles: string = ''; @Input() iconType: 'fontawesome' | 'custom' | 'image' = 'custom'; diff --git a/src/app/view/components/final-state/final-state.component.css b/src/app/view/components/final-state/final-state.component.css new file mode 100644 index 0000000..83ffecf --- /dev/null +++ b/src/app/view/components/final-state/final-state.component.css @@ -0,0 +1,50 @@ +.container { + display: flex; + flex-wrap: nowrap; + flex-direction: row; + + border-bottom: 2px ridge #E8E8E8; +} + +.component { + overflow: hidden; + white-space: nowrap; +} + +.first-component { + padding: 5px; + display: flex; + align-items: flex-start; + text-align: left; + width: 25%; + flex-direction: column; +} + +.first-component p { + margin: 0; /* or padding: 0; */ +} + +.second-component { + padding: 5px; + display: flex; + flex-direction: column; + align-items: flex-start; + width: 50% +} + +.second-component p { + margin: 0; /* or padding: 0; */ +} + +.third-component { + padding: 5px; + display: flex; + align-items: center; + width: 25%; +} + + +.main-attributes { + color: #909090; + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); /* Shadow around the value */ +} \ No newline at end of file diff --git a/src/app/view/components/final-state/final-state.component.html b/src/app/view/components/final-state/final-state.component.html new file mode 100644 index 0000000..6f6df1c --- /dev/null +++ b/src/app/view/components/final-state/final-state.component.html @@ -0,0 +1,39 @@ + + +<div class="container"> + <div class="component first-component"> + <p><b>{{finalState.element.tagName}}</b></p> + + <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> + <ng-container *ngIf="mainAttributes.includes(attribute)"> + <!-- show only first 3 --> + <p class="main-attributes S" *ngIf="j < 3">{{attribute}}: {{finalState.selectedAttributes[attribute]}}</p> + </ng-container> + </div> + </div> + <div class="component second-component"> + <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> + <!-- show only first 3 --> + <ng-container *ngIf="!mainAttributes.includes(attribute)"> + <p *ngIf="j < 3"><span style="text-transform: capitalize;">{{attribute}}: </span> <b>{{finalState.selectedAttributes[attribute]}}</b></p> + </ng-container> + </div> + </div> + <div class="component third-component"> + <custom-button + buttonText="" + size="S" + iconType="fontawesome" + icon="fa fa-trash" + additionalClasses="delete-button" + (click)="deleteFinalState(index)" + > + </custom-button> + + <custom-button backgroundColor="#8F8F8F" icon="fa fa-eye" iconType="fontawesome" + buttonText="" additionalStyles="margin-left:5px;" size="S" color="white"></custom-button> + + + </div> + +</div> \ No newline at end of file diff --git a/src/app/view/components/final-state/final-state.component.spec.ts b/src/app/view/components/final-state/final-state.component.spec.ts new file mode 100644 index 0000000..f97ce0d --- /dev/null +++ b/src/app/view/components/final-state/final-state.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FinalStateComponent } from './final-state.component'; + +describe('FinalStateComponent', () => { + let component: FinalStateComponent; + let fixture: ComponentFixture<FinalStateComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FinalStateComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(FinalStateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/view/components/final-state/final-state.component.ts b/src/app/view/components/final-state/final-state.component.ts new file mode 100644 index 0000000..c676af0 --- /dev/null +++ b/src/app/view/components/final-state/final-state.component.ts @@ -0,0 +1,22 @@ +import { Component, Input } from '@angular/core'; + +import { Action } from 'src/app/model/action.interface'; +import { Result } from 'src/app/model/result.interface'; +import { FinalStateTarget } from 'src/app/model/final-state-target.interface'; +import { ObjectKeys } from 'src/app/model/object-keys.interface'; + +@Component({ + selector: 'final-state', + templateUrl: './final-state.component.html', + styleUrls: ['./final-state.component.css'] +}) +export class FinalStateComponent { + + @Input() finalState!: Result; + + @Input() deleteFinalState!: Function; + @Input() index!: number; + @Input() mainAttributes: string[] = [] + + protected readonly Object = Object; // just for the template +} diff --git a/src/app/view/pages/final-stage/final-stage.component.css b/src/app/view/pages/final-stage/final-stage.component.css index 93c5be8..d386906 100644 --- a/src/app/view/pages/final-stage/final-stage.component.css +++ b/src/app/view/pages/final-stage/final-stage.component.css @@ -80,11 +80,13 @@ margin-left: 5px; } -.mandatory-attribute-name { + +.main-attributes-name { text-transform: lowercase; - color: #333; } -.mandatory-attribute-value { - color: #333; + +.main-attributes { + color: #909090; + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); /* Shadow around the value */ } \ No newline at end of file diff --git a/src/app/view/pages/final-stage/final-stage.component.html b/src/app/view/pages/final-stage/final-stage.component.html index f0ad702..d291b6e 100644 --- a/src/app/view/pages/final-stage/final-stage.component.html +++ b/src/app/view/pages/final-stage/final-stage.component.html @@ -6,7 +6,7 @@ iconType="fontawesome" icon="fa fa-arrow-left" additionalClasses="return-fs-button" - (click)="unMarkFinalState()" + (click)="unMarkFinalStateHandler()" > </custom-button> </div> @@ -19,12 +19,13 @@ <!-- not chooseable attributes - default --> <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> - <ng-container *ngIf="mandatoryAttributes.includes(attribute)"> + <ng-container *ngIf="mainAttributes.includes(attribute)"> <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" checked style="display: none;" (change)="saveAttribute($event, attribute, finalStateTarget.attributes[attribute])"> + <input type="checkbox" class="custom-checkbox" checked style="display: none;" + (change)="saveAttributeHandler($event, attribute, finalStateTarget.attributes[attribute])"> <label> - <span class="mandatory-attribute-name"> {{attribute}}: </span> - <span class="mandatory-attribute-value">{{finalStateTarget.attributes[attribute]}} </span> + <span class="main-attributes main-attributes-name"> {{attribute}}: </span> + <span class="main-attributes">{{finalStateTarget.attributes[attribute]}} </span> </label> </div> </ng-container> @@ -36,12 +37,23 @@ </ng-container> <h4>Select characteristic properties</h4> <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> - <ng-container *ngIf="!mandatoryAttributes.includes(attribute)"> + <ng-container *ngIf="!mainAttributes.includes(attribute)"> <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" (change)="saveAttribute($event, attribute, finalStateTarget.attributes[attribute])"> + <input type="checkbox" class="custom-checkbox" + [checked]="isAttributeSavedHandler(attribute)" + (change)="saveAttributeHandler($event, attribute, finalStateTarget.attributes[attribute])"> <label> - <span class="attribute-name"> {{attribute}}: </span> + <span class="attribute-name"> {{attribute}} : </span> <span class="attribute-value">{{finalStateTarget.attributes[attribute]}} </span> + <custom-button + buttonText="" + size="XS" + iconType="fontawesome" + icon="fa fa-edit" + additionalClasses="edit-button" + additionalStyles="margin-left: 3px;" + > + </custom-button> </label> </div> </ng-container> @@ -50,20 +62,44 @@ <!-- <h5>Explicitly set styles:</h5> --> <div *ngFor="let attribute of Object.keys(finalStateTarget.styles)"> <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> + <input type="checkbox" class="custom-checkbox" + [checked]="isAttributeSavedHandler('style.'+attribute)" + (change)="saveAttributeHandler($event, 'style.'+attribute, finalStateTarget.styles[attribute])"> <label> <span class="attribute-name"> {{attribute}}: </span> - <span class="attribute-value">{{finalStateTarget.styles[attribute]}} </span> + <span class="attribute-value">{{finalStateTarget.styles[attribute]}} </span> + <custom-button + buttonText="" + size="XS" + iconType="fontawesome" + icon="fa fa-edit" + additionalClasses="edit-button" + additionalStyles="margin-left: 3px;" + > + </custom-button> + </label> </div> </div> <!-- <h5>Computed styles:</h5> --> <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)"> <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" (change)="saveAttribute($event, 'style.'+attribute, finalStateTarget.attributes[attribute])"> + <input type="checkbox" class="custom-checkbox" + [checked]="isAttributeSavedHandler('style.'+attribute)" + (change)="saveAttributeHandler($event, 'style.'+attribute, finalStateTarget.computedStyles[attribute])"> <label> <span class="attribute-name"> {{attribute}}: </span> <span class="attribute-value">{{finalStateTarget.computedStyles[attribute]}} </span> + <custom-button + buttonText="" + size="XS" + iconType="fontawesome" + icon="fa fa-edit" + additionalClasses="edit-button" + additionalStyles="margin-left: 3px;" + + > + </custom-button> </label> </div> </div> @@ -76,7 +112,7 @@ icon="fa fa-trash" additionalClasses="delete-button" additionalStyles="width: 10em;" - (click)="deleteFinalStateTarget()" + (click)="deleteFinalStateTargetHandler()" > </custom-button> <custom-button @@ -86,7 +122,7 @@ icon="fa fa-plus" additionalClasses="success-button pl_fs" additionalStyles="margin-left: 2em; width: 10em;" - (click)="addFinalState()" + (click)="addFinalStateHandler()" > </custom-button> </span> @@ -94,21 +130,14 @@ </ng-container> <h3>Final states:</h3> - <div *ngFor="let finalState of finalStates; let i = index" class="fs_container"> - <b>{{finalState.element.tagName}}</b> - <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> - <!-- show only first 3 --> - <p *ngIf="j < 3">{{attribute}}: {{finalState.selectedAttributes[attribute]}}</p> - </div> - <custom-button - buttonText="" - size="S" - iconType="fontawesome" - icon="fa fa-trash" - additionalClasses="delete-button" - (click)="deleteFinalState(i)" - > - </custom-button> + <div *ngFor="let finalState of finalStates; let i = index"> + <final-state + [index]="i" + [finalState]="finalState" + [deleteFinalState]="deleteFinalStateHandler()" + [mainAttributes]="mainAttributes" + > + </final-state> </div> <!-- END: MARK FINAL STATE --> diff --git a/src/app/view/pages/final-stage/final-stage.component.ts b/src/app/view/pages/final-stage/final-stage.component.ts index 3747622..62525b3 100644 --- a/src/app/view/pages/final-stage/final-stage.component.ts +++ b/src/app/view/pages/final-stage/final-stage.component.ts @@ -4,8 +4,6 @@ import { Result } from 'src/app/model/result.interface'; import { FinalStateTarget } from 'src/app/model/final-state-target.interface'; import { ObjectKeys } from 'src/app/model/object-keys.interface'; -import { Message } from 'src/app/model/message.interface'; -import { ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'final-stage', @@ -22,17 +20,16 @@ export class FinalStageComponent implements OnInit { @Input() savedAttributes: ObjectKeys = {}; - @Input() saveAttribute!: Function; - @Input() deleteFinalStateTarget!: Function; - @Input() addFinalState!: Function; - @Input() deleteFinalState!: Function; + @Input() saveAttributeHandler!: Function; + @Input() isAttributeSavedHandler!: Function; + @Input() deleteFinalStateTargetHandler!: Function; + @Input() addFinalStateHandler!: Function; + @Input() deleteFinalStateHandler!: Function; + @Input() unMarkFinalStateHandler!: Function; + + @Input() mainAttributes: string[] = []; - @Input() unMarkFinalState!: Function; - - // temporary solution - for future make better - mandatoryAttributes: string[] = ['id', 'class', 'name'] - - constructor(private ref: ChangeDetectorRef){} + constructor(){} protected readonly Object = Object; // just for the template ngOnInit(): void { -- GitLab From ee22c82dcc8ac797a09c27b5928b8e85ede538e4 Mon Sep 17 00:00:00 2001 From: vondrp <vondrovic@centrum.cz> Date: Sat, 6 Apr 2024 13:59:59 +0200 Subject: [PATCH 7/9] =?UTF-8?q?Re=20#11122=20-=20oprava=20u=C5=BE=C3=ADv?= =?UTF-8?q?=C3=A1n=C3=AD=20logiky?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - vyÄiÅ¡tÄ›nà komentářů - pÅ™esunutà logiky final stage s app.component do final-stage.component - pÅ™idánà stránky s textem "Click on the element you want to select Waiting..." - oprava pÅ™edávánà dat finalStates - oprava volánà funkce smazánà položek finalStates - oprava chybi vyvolané mergem (duplicitnà startTime?: Date v overview.component) + zmÄ›ny v package-lock.json a package.json - následkem npm audit-fix --force (pÅ™i Å™eÅ¡enà problémů po npm install) --- package-lock.json | 1681 +++++++++-------- package.json | 6 +- src/app/app.component.html | 8 +- src/app/app.component.ts | 101 +- .../custom-button/custom-button.component.css | 6 +- .../final-state/final-state.component.css | 6 +- .../final-state/final-state.component.html | 2 - .../final-state/final-state.component.ts | 5 +- .../final-stage/final-stage.component.css | 83 +- .../final-stage/final-stage.component.html | 280 +-- .../final-stage/final-stage.component.ts | 135 +- .../pages/overview/overview.component.html | 5 +- .../view/pages/overview/overview.component.ts | 5 +- 13 files changed, 1214 insertions(+), 1109 deletions(-) diff --git a/package-lock.json b/package-lock.json index cfa7e3c..3ae94ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "zone.js": "~0.14.4" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.3.2", + "@angular-devkit/build-angular": "^17.2.3", "@angular/cli": "^17.3.2", "@angular/compiler-cli": "^17.3.1", "@types/jasmine": "~3.10.0", @@ -106,70 +106,70 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "17.3.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.3.2.tgz", - "integrity": "sha512-muPCUyL0uHvRkLH4NLWiccER6P2vCm/Q5DDvqyN4XOzzY3tAHHLrKrpvY87sgd2oNJ6Ci8x7GPNcfzR5KELCnw==", - "dependencies": { - "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1703.2", - "@angular-devkit/build-webpack": "0.1703.2", - "@angular-devkit/core": "17.3.2", - "@babel/core": "7.24.0", + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.2.3.tgz", + "integrity": "sha512-AZsEHZj+k2Lxb7uQUwfEpSE6TvQhCoIgP6XLKgKxZHUOiTUVXDj84WhNcbup5SsSG1cafmoVN7APxxuSwHcoeg==", + "dependencies": { + "@ampproject/remapping": "2.2.1", + "@angular-devkit/architect": "0.1702.3", + "@angular-devkit/build-webpack": "0.1702.3", + "@angular-devkit/core": "17.2.3", + "@babel/core": "7.23.9", "@babel/generator": "7.23.6", "@babel/helper-annotate-as-pure": "7.22.5", "@babel/helper-split-export-declaration": "7.22.6", "@babel/plugin-transform-async-generator-functions": "7.23.9", "@babel/plugin-transform-async-to-generator": "7.23.3", - "@babel/plugin-transform-runtime": "7.24.0", - "@babel/preset-env": "7.24.0", - "@babel/runtime": "7.24.0", + "@babel/plugin-transform-runtime": "7.23.9", + "@babel/preset-env": "7.23.9", + "@babel/runtime": "7.23.9", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "17.3.2", + "@ngtools/webpack": "17.2.3", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.18", + "autoprefixer": "10.4.17", "babel-loader": "9.1.3", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", "copy-webpack-plugin": "11.0.0", - "critters": "0.0.22", + "critters": "0.0.20", "css-loader": "6.10.0", - "esbuild-wasm": "0.20.1", + "esbuild-wasm": "0.20.0", "fast-glob": "3.3.2", "http-proxy-middleware": "2.0.6", - "https-proxy-agent": "7.0.4", - "inquirer": "9.2.15", + "https-proxy-agent": "7.0.2", + "inquirer": "9.2.14", "jsonc-parser": "3.2.1", "karma-source-map-support": "1.4.0", "less": "4.2.0", "less-loader": "11.1.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.2.1", - "magic-string": "0.30.8", - "mini-css-extract-plugin": "2.8.1", + "magic-string": "0.30.7", + "mini-css-extract-plugin": "2.8.0", "mrmime": "2.0.0", "open": "8.4.2", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.1", - "piscina": "4.4.0", + "piscina": "4.3.1", "postcss": "8.4.35", - "postcss-loader": "8.1.1", + "postcss-loader": "8.1.0", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.71.1", - "sass-loader": "14.1.1", + "sass": "1.70.0", + "sass-loader": "14.1.0", "semver": "7.6.0", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.29.1", + "terser": "5.27.0", "tree-kill": "1.2.2", "tslib": "2.6.2", - "undici": "6.7.1", - "vite": "5.1.5", + "undici": "6.6.2", + "vite": "5.0.12", "watchpack": "2.4.0", - "webpack": "5.90.3", - "webpack-dev-middleware": "6.1.2", + "webpack": "5.90.1", + "webpack-dev-middleware": "6.1.1", "webpack-dev-server": "4.15.1", "webpack-merge": "5.10.0", "webpack-subresource-integrity": "5.1.0" @@ -180,7 +180,7 @@ "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.20.1" + "esbuild": "0.20.0" }, "peerDependencies": { "@angular/compiler-cli": "^17.0.0", @@ -195,7 +195,7 @@ "ng-packagr": "^17.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.2 <5.5" + "typescript": ">=5.2 <5.4" }, "peerDependenciesMeta": { "@angular/localize": { @@ -233,53 +233,52 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { + "version": "0.1702.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1702.3.tgz", + "integrity": "sha512-4jeBgtBIZxAeJyiwSdbRE4+rWu34j0UMCKia8s7473rKj0Tn4+dXlHmA/kuFYIp6K/9pE/hBoeUFxLNA/DZuRQ==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@angular-devkit/core": "17.2.3", + "rxjs": "7.8.1" }, "engines": { - "node": ">=6.0.0" + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", - "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.2.3.tgz", + "integrity": "sha512-A7WWl1/VsZw6utFFPBib1wSbAB5OeBgAgQmVpVe9wW8u9UZa6CLc7b3InWtRRyBXTo9Sa5GNZDFfwlXhy3iW3w==", "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.0", - "@babel/parser": "^7.24.0", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", + "rxjs": "7.8.1", + "source-map": "0.7.4" }, "engines": { - "node": ">=6.9.0" + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" } }, "node_modules/@angular-devkit/build-angular/node_modules/@babel/generator": { @@ -641,6 +640,21 @@ "node": ">=12" } }, + "node_modules/@angular-devkit/build-angular/node_modules/@ngtools/webpack": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.2.3.tgz", + "integrity": "sha512-+d5Q7/ctDHePYZXcg0GFwL/AbyEkPMHoCiT7pmLI0B0n87D/mYKK/qmVN1VANBrFLTuIe8RtcL0aJ9pw8HAxWA==", + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^17.0.0", + "typescript": ">=5.2 <5.4", + "webpack": "^5.54.0" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/@types/node": { "version": "20.11.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", @@ -662,15 +676,83 @@ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "node_modules/@angular-devkit/build-angular/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "node_modules/@angular-devkit/build-angular/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@angular-devkit/build-angular/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/inquirer": { + "version": "9.2.14", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.14.tgz", + "integrity": "sha512-4ByIMt677Iz5AvjyKrDpzaepIyMewNvDcvwpVVRZNmy9dLakVoVgdCHZXbK1SlVJra1db0JZ6XkJyHsanpdrdQ==", + "dependencies": { + "@ljharb/through": "^2.3.12", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", + "cli-cursor": "^3.1.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^3.2.0", + "lodash": "^4.17.21", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } }, "node_modules/@angular-devkit/build-angular/node_modules/less": { "version": "4.2.0", @@ -697,6 +779,17 @@ "source-map": "~0.6.0" } }, + "node_modules/@angular-devkit/build-angular/node_modules/magic-string": { + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", + "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -786,53 +879,46 @@ "tslib": "^2.1.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "node_modules/@angular-devkit/build-angular/node_modules/sass": { + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", + "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" }, - "engines": { - "node": ">= 10.13.0" + "bin": { + "sass": "sass.js" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@angular-devkit/build-angular/node_modules/terser": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", + "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, "node_modules/@angular-devkit/build-angular/node_modules/vite": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.5.tgz", - "integrity": "sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "dependencies": { "esbuild": "^0.19.3", - "postcss": "^8.4.35", + "postcss": "^8.4.32", "rollup": "^4.2.0" }, "bin": { @@ -929,58 +1015,25 @@ "node": ">=10.13.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack": { - "version": "5.90.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", - "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", + "node_modules/@angular-devkit/build-angular/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "node": ">=8" } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1703.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1703.2.tgz", - "integrity": "sha512-w7rVFQcZK4iTCd/MLfQWIkDkwBOfAs++txNQyS9qYID8KvLs1V+oWYd2qDBRelRv1u3YtaCIS1pQx3GFKBC3OA==", + "version": "0.1702.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1702.3.tgz", + "integrity": "sha512-G9F2Ori8WxJtMvOQGxTdg7d+5aAO1IPeEtMiZwFPrw65Ey6Gvfm0h2+3FnQdzeKrZmGaTk5E6gffHXJJQfCnmQ==", "dependencies": { - "@angular-devkit/architect": "0.1703.2", + "@angular-devkit/architect": "0.1702.3", "rxjs": "7.8.1" }, "engines": { @@ -993,6 +1046,57 @@ "webpack-dev-server": "^4.0.0" } }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { + "version": "0.1702.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1702.3.tgz", + "integrity": "sha512-4jeBgtBIZxAeJyiwSdbRE4+rWu34j0UMCKia8s7473rKj0Tn4+dXlHmA/kuFYIp6K/9pE/hBoeUFxLNA/DZuRQ==", + "dependencies": { + "@angular-devkit/core": "17.2.3", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.2.3.tgz", + "integrity": "sha512-A7WWl1/VsZw6utFFPBib1wSbAB5OeBgAgQmVpVe9wW8u9UZa6CLc7b3InWtRRyBXTo9Sa5GNZDFfwlXhy3iW3w==", + "dependencies": { + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -1001,6 +1105,14 @@ "tslib": "^2.1.0" } }, + "node_modules/@angular-devkit/build-webpack/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/@angular-devkit/core": { "version": "17.3.2", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.2.tgz", @@ -1190,48 +1302,6 @@ "typescript": ">=5.2 <5.5" } }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@angular/core": { "version": "17.3.1", "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.1.tgz", @@ -1340,24 +1410,24 @@ } }, "node_modules/@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -1368,6 +1438,11 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1436,9 +1511,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", - "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz", + "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", @@ -2069,9 +2144,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", - "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz", + "integrity": "sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g==", "dependencies": { "@babel/helper-plugin-utils": "^7.24.0" }, @@ -2098,11 +2173,11 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz", - "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", + "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-create-class-features-plugin": "^7.24.4", "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -2601,12 +2676,12 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz", - "integrity": "sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", + "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", "dependencies": { "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.22.5", "babel-plugin-polyfill-corejs2": "^0.4.8", "babel-plugin-polyfill-corejs3": "^0.9.0", "babel-plugin-polyfill-regenerator": "^0.5.5", @@ -2758,13 +2833,13 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz", - "integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", + "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", "dependencies": { "@babel/compat-data": "^7.23.5", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", @@ -2817,7 +2892,7 @@ "@babel/plugin-transform-new-target": "^7.23.3", "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.24.0", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", "@babel/plugin-transform-object-super": "^7.23.3", "@babel/plugin-transform-optional-catch-binding": "^7.23.4", "@babel/plugin-transform-optional-chaining": "^7.23.4", @@ -2877,9 +2952,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", - "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2971,9 +3046,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz", - "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.0.tgz", + "integrity": "sha512-fGFDEctNh0CcSwsiRPxiaqX0P5rq+AqE0SRhYGZ4PX46Lg1FNR6oCxJghf8YgY0WQEgQuh3lErUFE4KxLeRmmw==", "cpu": [ "ppc64" ], @@ -2986,9 +3061,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz", - "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.0.tgz", + "integrity": "sha512-3bMAfInvByLHfJwYPJRlpTeaQA75n8C/QKpEaiS4HrFWFiJlNI0vzq/zCjBrhAYcPyVPG7Eo9dMrcQXuqmNk5g==", "cpu": [ "arm" ], @@ -3001,9 +3076,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz", - "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.0.tgz", + "integrity": "sha512-aVpnM4lURNkp0D3qPoAzSG92VXStYmoVPOgXveAUoQBWRSuQzt51yvSju29J6AHPmwY1BjH49uR29oyfH1ra8Q==", "cpu": [ "arm64" ], @@ -3016,9 +3091,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz", - "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.0.tgz", + "integrity": "sha512-uK7wAnlRvjkCPzh8jJ+QejFyrP8ObKuR5cBIsQZ+qbMunwR8sbd8krmMbxTLSrDhiPZaJYKQAU5Y3iMDcZPhyQ==", "cpu": [ "x64" ], @@ -3031,9 +3106,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", - "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.0.tgz", + "integrity": "sha512-AjEcivGAlPs3UAcJedMa9qYg9eSfU6FnGHJjT8s346HSKkrcWlYezGE8VaO2xKfvvlZkgAhyvl06OJOxiMgOYQ==", "cpu": [ "arm64" ], @@ -3046,9 +3121,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz", - "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.0.tgz", + "integrity": "sha512-bsgTPoyYDnPv8ER0HqnJggXK6RyFy4PH4rtsId0V7Efa90u2+EifxytE9pZnsDgExgkARy24WUQGv9irVbTvIw==", "cpu": [ "x64" ], @@ -3061,9 +3136,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz", - "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.0.tgz", + "integrity": "sha512-kQ7jYdlKS335mpGbMW5tEe3IrQFIok9r84EM3PXB8qBFJPSc6dpWfrtsC/y1pyrz82xfUIn5ZrnSHQQsd6jebQ==", "cpu": [ "arm64" ], @@ -3076,9 +3151,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz", - "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.0.tgz", + "integrity": "sha512-uG8B0WSepMRsBNVXAQcHf9+Ko/Tr+XqmK7Ptel9HVmnykupXdS4J7ovSQUIi0tQGIndhbqWLaIL/qO/cWhXKyQ==", "cpu": [ "x64" ], @@ -3091,9 +3166,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz", - "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.0.tgz", + "integrity": "sha512-2ezuhdiZw8vuHf1HKSf4TIk80naTbP9At7sOqZmdVwvvMyuoDiZB49YZKLsLOfKIr77+I40dWpHVeY5JHpIEIg==", "cpu": [ "arm" ], @@ -3106,9 +3181,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz", - "integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.0.tgz", + "integrity": "sha512-uTtyYAP5veqi2z9b6Gr0NUoNv9F/rOzI8tOD5jKcCvRUn7T60Bb+42NDBCWNhMjkQzI0qqwXkQGo1SY41G52nw==", "cpu": [ "arm64" ], @@ -3121,9 +3196,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz", - "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.0.tgz", + "integrity": "sha512-c88wwtfs8tTffPaoJ+SQn3y+lKtgTzyjkD8NgsyCtCmtoIC8RDL7PrJU05an/e9VuAke6eJqGkoMhJK1RY6z4w==", "cpu": [ "ia32" ], @@ -3136,9 +3211,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz", - "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.0.tgz", + "integrity": "sha512-lR2rr/128/6svngnVta6JN4gxSXle/yZEZL3o4XZ6esOqhyR4wsKyfu6qXAL04S4S5CgGfG+GYZnjFd4YiG3Aw==", "cpu": [ "loong64" ], @@ -3151,9 +3226,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz", - "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.0.tgz", + "integrity": "sha512-9Sycc+1uUsDnJCelDf6ZNqgZQoK1mJvFtqf2MUz4ujTxGhvCWw+4chYfDLPepMEvVL9PDwn6HrXad5yOrNzIsQ==", "cpu": [ "mips64el" ], @@ -3166,9 +3241,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz", - "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.0.tgz", + "integrity": "sha512-CoWSaaAXOZd+CjbUTdXIJE/t7Oz+4g90A3VBCHLbfuc5yUQU/nFDLOzQsN0cdxgXd97lYW/psIIBdjzQIwTBGw==", "cpu": [ "ppc64" ], @@ -3181,9 +3256,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz", - "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.0.tgz", + "integrity": "sha512-mlb1hg/eYRJUpv8h/x+4ShgoNLL8wgZ64SUr26KwglTYnwAWjkhR2GpoKftDbPOCnodA9t4Y/b68H4J9XmmPzA==", "cpu": [ "riscv64" ], @@ -3196,9 +3271,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz", - "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.0.tgz", + "integrity": "sha512-fgf9ubb53xSnOBqyvWEY6ukBNRl1mVX1srPNu06B6mNsNK20JfH6xV6jECzrQ69/VMiTLvHMicQR/PgTOgqJUQ==", "cpu": [ "s390x" ], @@ -3211,9 +3286,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", - "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.0.tgz", + "integrity": "sha512-H9Eu6MGse++204XZcYsse1yFHmRXEWgadk2N58O/xd50P9EvFMLJTQLg+lB4E1cF2xhLZU5luSWtGTb0l9UeSg==", "cpu": [ "x64" ], @@ -3226,9 +3301,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz", - "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.0.tgz", + "integrity": "sha512-lCT675rTN1v8Fo+RGrE5KjSnfY0x9Og4RN7t7lVrN3vMSjy34/+3na0q7RIfWDAj0e0rCh0OL+P88lu3Rt21MQ==", "cpu": [ "x64" ], @@ -3241,9 +3316,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz", - "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.0.tgz", + "integrity": "sha512-HKoUGXz/TOVXKQ+67NhxyHv+aDSZf44QpWLa3I1lLvAwGq8x1k0T+e2HHSRvxWhfJrFxaaqre1+YyzQ99KixoA==", "cpu": [ "x64" ], @@ -3256,9 +3331,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz", - "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.0.tgz", + "integrity": "sha512-GDwAqgHQm1mVoPppGsoq4WJwT3vhnz/2N62CzhvApFD1eJyTroob30FPpOZabN+FgCjhG+AgcZyOPIkR8dfD7g==", "cpu": [ "x64" ], @@ -3271,9 +3346,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz", - "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.0.tgz", + "integrity": "sha512-0vYsP8aC4TvMlOQYozoksiaxjlvUcQrac+muDqj1Fxy6jh9l9CZJzj7zmh8JGfiV49cYLTorFLxg7593pGldwQ==", "cpu": [ "arm64" ], @@ -3286,9 +3361,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz", - "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.0.tgz", + "integrity": "sha512-p98u4rIgfh4gdpV00IqknBD5pC84LCub+4a3MO+zjqvU5MVXOc3hqR2UgT2jI2nh3h8s9EQxmOsVI3tyzv1iFg==", "cpu": [ "ia32" ], @@ -3301,9 +3376,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz", - "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.0.tgz", + "integrity": "sha512-NgJnesu1RtWihtTtXGFMU5YSE6JyyHPMxCwBZK7a6/8d31GuSo9l0Ss7w1Jw5QnKUawG6UEehs883kcXf5fYwg==", "cpu": [ "x64" ], @@ -3315,6 +3390,14 @@ "node": ">=12" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, "node_modules/@fortawesome/fontawesome-free": { "version": "5.15.4", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", @@ -3496,34 +3579,19 @@ } }, "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" }, "node_modules/@ljharb/through": { "version": "2.3.13", "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@ngtools/webpack": { - "version": "17.3.2", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.2.tgz", - "integrity": "sha512-E8zejFF4aJ8l2XcF+GgnE/1IqsZepnPT1xzulLB4LXtjVuXLFLoF9xkHQwxs7cJWWZsxd/SlNsCIcn/ezrYBcQ==", - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "call-bind": "^1.0.7" }, - "peerDependencies": { - "@angular/compiler-cli": "^17.0.0", - "typescript": ">=5.2 <5.5", - "webpack": "^5.54.0" + "engines": { + "node": ">= 0.4" } }, "node_modules/@nodelib/fs.scandir": { @@ -4240,9 +4308,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", + "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -4343,13 +4411,13 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dependencies": { "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" + "@types/node": "*", + "@types/send": "*" } }, "node_modules/@types/sockjs": { @@ -4737,9 +4805,9 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", "funding": [ { "type": "opencollective", @@ -4755,8 +4823,8 @@ } ], "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -5632,9 +5700,9 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "node_modules/critters": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.22.tgz", - "integrity": "sha512-NU7DEcQZM2Dy8XTKFHxtdnIM/drE312j2T4PCVaSUcS0oBeyT/NImpRw/Ap0zOr/1SE7SgPK9tGPg1WK/sVakw==", + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.20.tgz", + "integrity": "sha512-CImNRorKOl5d8TWcnAz5n5izQ6HFsvz29k327/ELy6UFcmbiZNOsinaKvzv16WZR0P6etfSWYzE47C4/56B3Uw==", "dependencies": { "chalk": "^4.1.0", "css-select": "^5.1.0", @@ -5642,7 +5710,7 @@ "domhandler": "^5.0.2", "htmlparser2": "^8.0.2", "postcss": "^8.4.23", - "postcss-media-query-parser": "^0.2.3" + "pretty-bytes": "^5.3.0" } }, "node_modules/critters/node_modules/ansi-styles": { @@ -6183,9 +6251,9 @@ "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==" }, "node_modules/esbuild": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", - "integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.0.tgz", + "integrity": "sha512-6iwE3Y2RVYCME1jLpBqq7LQWK3MW6vjV2bZy6gt/WrqkY+WE74Spyc0ThAOYpMtITvnjX09CrC6ym7A/m9mebA==", "hasInstallScript": true, "optional": true, "bin": { @@ -6195,35 +6263,35 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.1", - "@esbuild/android-arm": "0.20.1", - "@esbuild/android-arm64": "0.20.1", - "@esbuild/android-x64": "0.20.1", - "@esbuild/darwin-arm64": "0.20.1", - "@esbuild/darwin-x64": "0.20.1", - "@esbuild/freebsd-arm64": "0.20.1", - "@esbuild/freebsd-x64": "0.20.1", - "@esbuild/linux-arm": "0.20.1", - "@esbuild/linux-arm64": "0.20.1", - "@esbuild/linux-ia32": "0.20.1", - "@esbuild/linux-loong64": "0.20.1", - "@esbuild/linux-mips64el": "0.20.1", - "@esbuild/linux-ppc64": "0.20.1", - "@esbuild/linux-riscv64": "0.20.1", - "@esbuild/linux-s390x": "0.20.1", - "@esbuild/linux-x64": "0.20.1", - "@esbuild/netbsd-x64": "0.20.1", - "@esbuild/openbsd-x64": "0.20.1", - "@esbuild/sunos-x64": "0.20.1", - "@esbuild/win32-arm64": "0.20.1", - "@esbuild/win32-ia32": "0.20.1", - "@esbuild/win32-x64": "0.20.1" + "@esbuild/aix-ppc64": "0.20.0", + "@esbuild/android-arm": "0.20.0", + "@esbuild/android-arm64": "0.20.0", + "@esbuild/android-x64": "0.20.0", + "@esbuild/darwin-arm64": "0.20.0", + "@esbuild/darwin-x64": "0.20.0", + "@esbuild/freebsd-arm64": "0.20.0", + "@esbuild/freebsd-x64": "0.20.0", + "@esbuild/linux-arm": "0.20.0", + "@esbuild/linux-arm64": "0.20.0", + "@esbuild/linux-ia32": "0.20.0", + "@esbuild/linux-loong64": "0.20.0", + "@esbuild/linux-mips64el": "0.20.0", + "@esbuild/linux-ppc64": "0.20.0", + "@esbuild/linux-riscv64": "0.20.0", + "@esbuild/linux-s390x": "0.20.0", + "@esbuild/linux-x64": "0.20.0", + "@esbuild/netbsd-x64": "0.20.0", + "@esbuild/openbsd-x64": "0.20.0", + "@esbuild/sunos-x64": "0.20.0", + "@esbuild/win32-arm64": "0.20.0", + "@esbuild/win32-ia32": "0.20.0", + "@esbuild/win32-x64": "0.20.0" } }, "node_modules/esbuild-wasm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.1.tgz", - "integrity": "sha512-6v/WJubRsjxBbQdz6izgvx7LsVFvVaGmSdwrFHmEzoVgfXL89hkKPoQHsnVI2ngOkcBUQT9kmAM1hVL1k/Av4A==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.0.tgz", + "integrity": "sha512-Lc9KeQCg1Zf8kCtfDXgy29rx0x8dOuhDWbkP76Wc64q7ctOOc1Zv1C39AxiE+y4N6ONyXtJk4HKpM7jlU7/jSA==", "bin": { "esbuild": "bin/esbuild" }, @@ -7130,6 +7198,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dev": true, "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -7316,6 +7385,7 @@ "version": "9.2.15", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", + "dev": true, "dependencies": { "@ljharb/through": "^2.3.12", "ansi-escapes": "^4.3.2", @@ -7341,6 +7411,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -7355,6 +7426,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -7366,6 +7438,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -7376,12 +7449,14 @@ "node_modules/inquirer/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/inquirer/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -7390,6 +7465,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8277,6 +8353,7 @@ "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -8430,9 +8507,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", - "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.0.tgz", + "integrity": "sha512-CxmUYPFcTgET1zImteG/LZOy/4T5rTojesQXkSNBiquhydn78tfbCE9sjIjnJ/UcjNjOC1bphTCCW5rrS7cXAg==", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -9517,9 +9594,9 @@ } }, "node_modules/piscina": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.4.0.tgz", - "integrity": "sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.3.1.tgz", + "integrity": "sha512-MBj0QYm3hJQ/C/wIXTN1OCYC8uQ4BBJ4LVele2P4ZwVQAH04vkk8E1SpDbuemLAL1dZorbuOob9rYqJeWCcCRg==", "optionalDependencies": { "nice-napi": "^1.0.2" } @@ -9631,9 +9708,9 @@ } }, "node_modules/postcss-loader": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", - "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.0.tgz", + "integrity": "sha512-AbperNcX3rlob7Ay7A/HQcrofug1caABBkopoFeOQMspZBqcqj6giYn1Bwey/0uiOPAcR+NQD0I2HC7rXzk91w==", "dependencies": { "cosmiconfig": "^9.0.0", "jiti": "^1.20.0", @@ -9660,11 +9737,6 @@ } } }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==" - }, "node_modules/postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", @@ -9737,6 +9809,17 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/proc-log": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", @@ -10260,6 +10343,8 @@ "version": "1.71.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", + "optional": true, + "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -10273,9 +10358,9 @@ } }, "node_modules/sass-loader": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.1.tgz", - "integrity": "sha512-QX8AasDg75monlybel38BZ49JP5Z+uSKfKwF2rO7S74BywaRmGQMUBw9dtkS+ekyM/QnP+NOrRYq8ABMZ9G8jw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.0.tgz", + "integrity": "sha512-LS2mLeFWA+orYxHNu+O18Xe4jR0kyamNOOUsE3NyBP4DvIL+8stHpNX0arYTItdPe80kluIiJ7Wfe/9iHSRO0Q==", "dependencies": { "neo-async": "^2.6.2" }, @@ -11049,23 +11134,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/terser": { - "version": "5.29.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz", - "integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/terser-webpack-plugin": { "version": "5.3.10", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", @@ -11363,9 +11431,12 @@ } }, "node_modules/undici": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.1.tgz", - "integrity": "sha512-+Wtb9bAQw6HYWzCnxrPTMVEV3Q1QjYanI0E4q02ehReMuquQdLTEFEYbfs7hcImVYKcQkWSwT6buEmSVIiDDtQ==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.6.2.tgz", + "integrity": "sha512-vSqvUE5skSxQJ5sztTZ/CdeJb1Wq0Hf44hlYMciqHghvz+K88U0l7D6u1VsndoFgskDcnU+nG3gYmMzJVzd9Qg==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, "engines": { "node": ">=18.0" } @@ -11560,7 +11631,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -11586,19 +11656,18 @@ } }, "node_modules/webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", - "peer": true, + "version": "5.90.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz", + "integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==", "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", + "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.11.5", "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", @@ -11612,7 +11681,7 @@ "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", + "terser-webpack-plugin": "^5.3.10", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -11633,9 +11702,9 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.2.tgz", - "integrity": "sha512-Wu+EHmX326YPYUpQLKmKbTyZZJIB8/n6R09pTmB03kJmnMsVPTo9COzHZFr01txwaCAuZvfBJE4ZCHRcKs5JaQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.1.tgz", + "integrity": "sha512-y51HrHaFeeWir0YO4f0g+9GwZawuigzcAdRNon6jErXy/SqV/+O6eaVAzDqE6t3e3NpGeR5CS+cCDaTC+V3yEQ==", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.12", @@ -11804,7 +11873,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -11820,7 +11888,6 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peer": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -11828,14 +11895,12 @@ "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "peer": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -12142,111 +12207,102 @@ } }, "@angular-devkit/build-angular": { - "version": "17.3.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.3.2.tgz", - "integrity": "sha512-muPCUyL0uHvRkLH4NLWiccER6P2vCm/Q5DDvqyN4XOzzY3tAHHLrKrpvY87sgd2oNJ6Ci8x7GPNcfzR5KELCnw==", - "requires": { - "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1703.2", - "@angular-devkit/build-webpack": "0.1703.2", - "@angular-devkit/core": "17.3.2", - "@babel/core": "7.24.0", + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.2.3.tgz", + "integrity": "sha512-AZsEHZj+k2Lxb7uQUwfEpSE6TvQhCoIgP6XLKgKxZHUOiTUVXDj84WhNcbup5SsSG1cafmoVN7APxxuSwHcoeg==", + "requires": { + "@ampproject/remapping": "2.2.1", + "@angular-devkit/architect": "0.1702.3", + "@angular-devkit/build-webpack": "0.1702.3", + "@angular-devkit/core": "17.2.3", + "@babel/core": "7.23.9", "@babel/generator": "7.23.6", "@babel/helper-annotate-as-pure": "7.22.5", "@babel/helper-split-export-declaration": "7.22.6", "@babel/plugin-transform-async-generator-functions": "7.23.9", "@babel/plugin-transform-async-to-generator": "7.23.3", - "@babel/plugin-transform-runtime": "7.24.0", - "@babel/preset-env": "7.24.0", - "@babel/runtime": "7.24.0", + "@babel/plugin-transform-runtime": "7.23.9", + "@babel/preset-env": "7.23.9", + "@babel/runtime": "7.23.9", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "17.3.2", + "@ngtools/webpack": "17.2.3", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.18", + "autoprefixer": "10.4.17", "babel-loader": "9.1.3", "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", "copy-webpack-plugin": "11.0.0", - "critters": "0.0.22", + "critters": "0.0.20", "css-loader": "6.10.0", - "esbuild": "0.20.1", - "esbuild-wasm": "0.20.1", + "esbuild": "0.20.0", + "esbuild-wasm": "0.20.0", "fast-glob": "3.3.2", "http-proxy-middleware": "2.0.6", - "https-proxy-agent": "7.0.4", - "inquirer": "9.2.15", + "https-proxy-agent": "7.0.2", + "inquirer": "9.2.14", "jsonc-parser": "3.2.1", "karma-source-map-support": "1.4.0", "less": "4.2.0", "less-loader": "11.1.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.2.1", - "magic-string": "0.30.8", - "mini-css-extract-plugin": "2.8.1", + "magic-string": "0.30.7", + "mini-css-extract-plugin": "2.8.0", "mrmime": "2.0.0", "open": "8.4.2", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.1", - "piscina": "4.4.0", + "piscina": "4.3.1", "postcss": "8.4.35", - "postcss-loader": "8.1.1", + "postcss-loader": "8.1.0", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.71.1", - "sass-loader": "14.1.1", + "sass": "1.70.0", + "sass-loader": "14.1.0", "semver": "7.6.0", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.29.1", + "terser": "5.27.0", "tree-kill": "1.2.2", "tslib": "2.6.2", - "undici": "6.7.1", - "vite": "5.1.5", + "undici": "6.6.2", + "vite": "5.0.12", "watchpack": "2.4.0", - "webpack": "5.90.3", - "webpack-dev-middleware": "6.1.2", + "webpack": "5.90.1", + "webpack-dev-middleware": "6.1.1", "webpack-dev-server": "4.15.1", "webpack-merge": "5.10.0", "webpack-subresource-integrity": "5.1.0" }, "dependencies": { - "@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "@angular-devkit/architect": { + "version": "0.1702.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1702.3.tgz", + "integrity": "sha512-4jeBgtBIZxAeJyiwSdbRE4+rWu34j0UMCKia8s7473rKj0Tn4+dXlHmA/kuFYIp6K/9pE/hBoeUFxLNA/DZuRQ==", "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@angular-devkit/core": "17.2.3", + "rxjs": "7.8.1" } }, - "@babel/core": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", - "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "@angular-devkit/core": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.2.3.tgz", + "integrity": "sha512-A7WWl1/VsZw6utFFPBib1wSbAB5OeBgAgQmVpVe9wW8u9UZa6CLc7b3InWtRRyBXTo9Sa5GNZDFfwlXhy3iW3w==", "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.0", - "@babel/parser": "^7.24.0", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", + "rxjs": "7.8.1", + "source-map": "0.7.4" }, "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" } } }, @@ -12399,6 +12455,12 @@ "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "optional": true }, + "@ngtools/webpack": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.2.3.tgz", + "integrity": "sha512-+d5Q7/ctDHePYZXcg0GFwL/AbyEkPMHoCiT7pmLI0B0n87D/mYKK/qmVN1VANBrFLTuIe8RtcL0aJ9pw8HAxWA==", + "requires": {} + }, "@types/node": { "version": "20.11.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", @@ -12415,15 +12477,62 @@ "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", "requires": {} }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "inquirer": { + "version": "9.2.14", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.14.tgz", + "integrity": "sha512-4ByIMt677Iz5AvjyKrDpzaepIyMewNvDcvwpVVRZNmy9dLakVoVgdCHZXbK1SlVJra1db0JZ6XkJyHsanpdrdQ==", + "requires": { + "@ljharb/through": "^2.3.12", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", + "cli-cursor": "^3.1.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^3.2.0", + "lodash": "^4.17.21", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + } }, "less": { "version": "4.2.0", @@ -12442,6 +12551,14 @@ "tslib": "^2.3.0" } }, + "magic-string": { + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", + "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -12493,43 +12610,35 @@ "tslib": "^2.1.0" } }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "sass": { + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", + "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} - } + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "terser": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", + "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" } }, "vite": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.5.tgz", - "integrity": "sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "requires": { "esbuild": "^0.19.3", "fsevents": "~2.3.3", - "postcss": "^8.4.35", + "postcss": "^8.4.32", "rollup": "^4.2.0" }, "dependencies": { @@ -12574,48 +12683,54 @@ "graceful-fs": "^4.1.2" } }, - "webpack": { - "version": "5.90.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", - "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } } } }, "@angular-devkit/build-webpack": { - "version": "0.1703.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1703.2.tgz", - "integrity": "sha512-w7rVFQcZK4iTCd/MLfQWIkDkwBOfAs++txNQyS9qYID8KvLs1V+oWYd2qDBRelRv1u3YtaCIS1pQx3GFKBC3OA==", + "version": "0.1702.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1702.3.tgz", + "integrity": "sha512-G9F2Ori8WxJtMvOQGxTdg7d+5aAO1IPeEtMiZwFPrw65Ey6Gvfm0h2+3FnQdzeKrZmGaTk5E6gffHXJJQfCnmQ==", "requires": { - "@angular-devkit/architect": "0.1703.2", + "@angular-devkit/architect": "0.1702.3", "rxjs": "7.8.1" }, "dependencies": { + "@angular-devkit/architect": { + "version": "0.1702.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1702.3.tgz", + "integrity": "sha512-4jeBgtBIZxAeJyiwSdbRE4+rWu34j0UMCKia8s7473rKj0Tn4+dXlHmA/kuFYIp6K/9pE/hBoeUFxLNA/DZuRQ==", + "requires": { + "@angular-devkit/core": "17.2.3", + "rxjs": "7.8.1" + } + }, + "@angular-devkit/core": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.2.3.tgz", + "integrity": "sha512-A7WWl1/VsZw6utFFPBib1wSbAB5OeBgAgQmVpVe9wW8u9UZa6CLc7b3InWtRRyBXTo9Sa5GNZDFfwlXhy3iW3w==", + "requires": { + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", + "rxjs": "7.8.1", + "source-map": "0.7.4" + } + }, + "picomatch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==" + }, "rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -12623,6 +12738,11 @@ "requires": { "tslib": "^2.1.0" } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" } } }, @@ -12746,42 +12866,6 @@ "semver": "^7.0.0", "tslib": "^2.3.0", "yargs": "^17.2.1" - }, - "dependencies": { - "@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - } } }, "@angular/core": { @@ -12839,27 +12923,32 @@ "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==" }, "@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -12914,9 +13003,9 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", - "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz", + "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==", "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", @@ -13339,9 +13428,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", - "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz", + "integrity": "sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g==", "requires": { "@babel/helper-plugin-utils": "^7.24.0" } @@ -13356,11 +13445,11 @@ } }, "@babel/plugin-transform-class-static-block": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz", - "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", + "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-create-class-features-plugin": "^7.24.4", "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" } @@ -13661,12 +13750,12 @@ } }, "@babel/plugin-transform-runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz", - "integrity": "sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", + "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", "requires": { "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.22.5", "babel-plugin-polyfill-corejs2": "^0.4.8", "babel-plugin-polyfill-corejs3": "^0.9.0", "babel-plugin-polyfill-regenerator": "^0.5.5", @@ -13757,13 +13846,13 @@ } }, "@babel/preset-env": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz", - "integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", + "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", "requires": { "@babel/compat-data": "^7.23.5", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", @@ -13816,7 +13905,7 @@ "@babel/plugin-transform-new-target": "^7.23.3", "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.24.0", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", "@babel/plugin-transform-object-super": "^7.23.3", "@babel/plugin-transform-optional-catch-binding": "^7.23.4", "@babel/plugin-transform-optional-chaining": "^7.23.4", @@ -13866,9 +13955,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "@babel/runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", - "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "requires": { "regenerator-runtime": "^0.14.0" } @@ -13941,143 +14030,148 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" }, "@esbuild/aix-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz", - "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.0.tgz", + "integrity": "sha512-fGFDEctNh0CcSwsiRPxiaqX0P5rq+AqE0SRhYGZ4PX46Lg1FNR6oCxJghf8YgY0WQEgQuh3lErUFE4KxLeRmmw==", "optional": true }, "@esbuild/android-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz", - "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.0.tgz", + "integrity": "sha512-3bMAfInvByLHfJwYPJRlpTeaQA75n8C/QKpEaiS4HrFWFiJlNI0vzq/zCjBrhAYcPyVPG7Eo9dMrcQXuqmNk5g==", "optional": true }, "@esbuild/android-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz", - "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.0.tgz", + "integrity": "sha512-aVpnM4lURNkp0D3qPoAzSG92VXStYmoVPOgXveAUoQBWRSuQzt51yvSju29J6AHPmwY1BjH49uR29oyfH1ra8Q==", "optional": true }, "@esbuild/android-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz", - "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.0.tgz", + "integrity": "sha512-uK7wAnlRvjkCPzh8jJ+QejFyrP8ObKuR5cBIsQZ+qbMunwR8sbd8krmMbxTLSrDhiPZaJYKQAU5Y3iMDcZPhyQ==", "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", - "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.0.tgz", + "integrity": "sha512-AjEcivGAlPs3UAcJedMa9qYg9eSfU6FnGHJjT8s346HSKkrcWlYezGE8VaO2xKfvvlZkgAhyvl06OJOxiMgOYQ==", "optional": true }, "@esbuild/darwin-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz", - "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.0.tgz", + "integrity": "sha512-bsgTPoyYDnPv8ER0HqnJggXK6RyFy4PH4rtsId0V7Efa90u2+EifxytE9pZnsDgExgkARy24WUQGv9irVbTvIw==", "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz", - "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.0.tgz", + "integrity": "sha512-kQ7jYdlKS335mpGbMW5tEe3IrQFIok9r84EM3PXB8qBFJPSc6dpWfrtsC/y1pyrz82xfUIn5ZrnSHQQsd6jebQ==", "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz", - "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.0.tgz", + "integrity": "sha512-uG8B0WSepMRsBNVXAQcHf9+Ko/Tr+XqmK7Ptel9HVmnykupXdS4J7ovSQUIi0tQGIndhbqWLaIL/qO/cWhXKyQ==", "optional": true }, "@esbuild/linux-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz", - "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.0.tgz", + "integrity": "sha512-2ezuhdiZw8vuHf1HKSf4TIk80naTbP9At7sOqZmdVwvvMyuoDiZB49YZKLsLOfKIr77+I40dWpHVeY5JHpIEIg==", "optional": true }, "@esbuild/linux-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz", - "integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.0.tgz", + "integrity": "sha512-uTtyYAP5veqi2z9b6Gr0NUoNv9F/rOzI8tOD5jKcCvRUn7T60Bb+42NDBCWNhMjkQzI0qqwXkQGo1SY41G52nw==", "optional": true }, "@esbuild/linux-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz", - "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.0.tgz", + "integrity": "sha512-c88wwtfs8tTffPaoJ+SQn3y+lKtgTzyjkD8NgsyCtCmtoIC8RDL7PrJU05an/e9VuAke6eJqGkoMhJK1RY6z4w==", "optional": true }, "@esbuild/linux-loong64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz", - "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.0.tgz", + "integrity": "sha512-lR2rr/128/6svngnVta6JN4gxSXle/yZEZL3o4XZ6esOqhyR4wsKyfu6qXAL04S4S5CgGfG+GYZnjFd4YiG3Aw==", "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz", - "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.0.tgz", + "integrity": "sha512-9Sycc+1uUsDnJCelDf6ZNqgZQoK1mJvFtqf2MUz4ujTxGhvCWw+4chYfDLPepMEvVL9PDwn6HrXad5yOrNzIsQ==", "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz", - "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.0.tgz", + "integrity": "sha512-CoWSaaAXOZd+CjbUTdXIJE/t7Oz+4g90A3VBCHLbfuc5yUQU/nFDLOzQsN0cdxgXd97lYW/psIIBdjzQIwTBGw==", "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz", - "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.0.tgz", + "integrity": "sha512-mlb1hg/eYRJUpv8h/x+4ShgoNLL8wgZ64SUr26KwglTYnwAWjkhR2GpoKftDbPOCnodA9t4Y/b68H4J9XmmPzA==", "optional": true }, "@esbuild/linux-s390x": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz", - "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.0.tgz", + "integrity": "sha512-fgf9ubb53xSnOBqyvWEY6ukBNRl1mVX1srPNu06B6mNsNK20JfH6xV6jECzrQ69/VMiTLvHMicQR/PgTOgqJUQ==", "optional": true }, "@esbuild/linux-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", - "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.0.tgz", + "integrity": "sha512-H9Eu6MGse++204XZcYsse1yFHmRXEWgadk2N58O/xd50P9EvFMLJTQLg+lB4E1cF2xhLZU5luSWtGTb0l9UeSg==", "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz", - "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.0.tgz", + "integrity": "sha512-lCT675rTN1v8Fo+RGrE5KjSnfY0x9Og4RN7t7lVrN3vMSjy34/+3na0q7RIfWDAj0e0rCh0OL+P88lu3Rt21MQ==", "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz", - "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.0.tgz", + "integrity": "sha512-HKoUGXz/TOVXKQ+67NhxyHv+aDSZf44QpWLa3I1lLvAwGq8x1k0T+e2HHSRvxWhfJrFxaaqre1+YyzQ99KixoA==", "optional": true }, "@esbuild/sunos-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz", - "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.0.tgz", + "integrity": "sha512-GDwAqgHQm1mVoPppGsoq4WJwT3vhnz/2N62CzhvApFD1eJyTroob30FPpOZabN+FgCjhG+AgcZyOPIkR8dfD7g==", "optional": true }, "@esbuild/win32-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz", - "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.0.tgz", + "integrity": "sha512-0vYsP8aC4TvMlOQYozoksiaxjlvUcQrac+muDqj1Fxy6jh9l9CZJzj7zmh8JGfiV49cYLTorFLxg7593pGldwQ==", "optional": true }, "@esbuild/win32-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz", - "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.0.tgz", + "integrity": "sha512-p98u4rIgfh4gdpV00IqknBD5pC84LCub+4a3MO+zjqvU5MVXOc3hqR2UgT2jI2nh3h8s9EQxmOsVI3tyzv1iFg==", "optional": true }, "@esbuild/win32-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz", - "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.0.tgz", + "integrity": "sha512-NgJnesu1RtWihtTtXGFMU5YSE6JyyHPMxCwBZK7a6/8d31GuSo9l0Ss7w1Jw5QnKUawG6UEehs883kcXf5fYwg==", "optional": true }, + "@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==" + }, "@fortawesome/fontawesome-free": { "version": "5.15.4", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", @@ -14209,9 +14303,9 @@ } }, "@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" }, "@ljharb/through": { "version": "2.3.13", @@ -14221,12 +14315,6 @@ "call-bind": "^1.0.7" } }, - "@ngtools/webpack": { - "version": "17.3.2", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.2.tgz", - "integrity": "sha512-E8zejFF4aJ8l2XcF+GgnE/1IqsZepnPT1xzulLB4LXtjVuXLFLoF9xkHQwxs7cJWWZsxd/SlNsCIcn/ezrYBcQ==", - "requires": {} - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -14750,9 +14838,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", + "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -14853,13 +14941,13 @@ } }, "@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "requires": { "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" + "@types/node": "*", + "@types/send": "*" } }, "@types/sockjs": { @@ -15181,12 +15269,12 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", "requires": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -15827,9 +15915,9 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "critters": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.22.tgz", - "integrity": "sha512-NU7DEcQZM2Dy8XTKFHxtdnIM/drE312j2T4PCVaSUcS0oBeyT/NImpRw/Ap0zOr/1SE7SgPK9tGPg1WK/sVakw==", + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.20.tgz", + "integrity": "sha512-CImNRorKOl5d8TWcnAz5n5izQ6HFsvz29k327/ELy6UFcmbiZNOsinaKvzv16WZR0P6etfSWYzE47C4/56B3Uw==", "requires": { "chalk": "^4.1.0", "css-select": "^5.1.0", @@ -15837,7 +15925,7 @@ "domhandler": "^5.0.2", "htmlparser2": "^8.0.2", "postcss": "^8.4.23", - "postcss-media-query-parser": "^0.2.3" + "pretty-bytes": "^5.3.0" }, "dependencies": { "ansi-styles": { @@ -16224,40 +16312,40 @@ "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==" }, "esbuild": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", - "integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.0.tgz", + "integrity": "sha512-6iwE3Y2RVYCME1jLpBqq7LQWK3MW6vjV2bZy6gt/WrqkY+WE74Spyc0ThAOYpMtITvnjX09CrC6ym7A/m9mebA==", "optional": true, "requires": { - "@esbuild/aix-ppc64": "0.20.1", - "@esbuild/android-arm": "0.20.1", - "@esbuild/android-arm64": "0.20.1", - "@esbuild/android-x64": "0.20.1", - "@esbuild/darwin-arm64": "0.20.1", - "@esbuild/darwin-x64": "0.20.1", - "@esbuild/freebsd-arm64": "0.20.1", - "@esbuild/freebsd-x64": "0.20.1", - "@esbuild/linux-arm": "0.20.1", - "@esbuild/linux-arm64": "0.20.1", - "@esbuild/linux-ia32": "0.20.1", - "@esbuild/linux-loong64": "0.20.1", - "@esbuild/linux-mips64el": "0.20.1", - "@esbuild/linux-ppc64": "0.20.1", - "@esbuild/linux-riscv64": "0.20.1", - "@esbuild/linux-s390x": "0.20.1", - "@esbuild/linux-x64": "0.20.1", - "@esbuild/netbsd-x64": "0.20.1", - "@esbuild/openbsd-x64": "0.20.1", - "@esbuild/sunos-x64": "0.20.1", - "@esbuild/win32-arm64": "0.20.1", - "@esbuild/win32-ia32": "0.20.1", - "@esbuild/win32-x64": "0.20.1" + "@esbuild/aix-ppc64": "0.20.0", + "@esbuild/android-arm": "0.20.0", + "@esbuild/android-arm64": "0.20.0", + "@esbuild/android-x64": "0.20.0", + "@esbuild/darwin-arm64": "0.20.0", + "@esbuild/darwin-x64": "0.20.0", + "@esbuild/freebsd-arm64": "0.20.0", + "@esbuild/freebsd-x64": "0.20.0", + "@esbuild/linux-arm": "0.20.0", + "@esbuild/linux-arm64": "0.20.0", + "@esbuild/linux-ia32": "0.20.0", + "@esbuild/linux-loong64": "0.20.0", + "@esbuild/linux-mips64el": "0.20.0", + "@esbuild/linux-ppc64": "0.20.0", + "@esbuild/linux-riscv64": "0.20.0", + "@esbuild/linux-s390x": "0.20.0", + "@esbuild/linux-x64": "0.20.0", + "@esbuild/netbsd-x64": "0.20.0", + "@esbuild/openbsd-x64": "0.20.0", + "@esbuild/sunos-x64": "0.20.0", + "@esbuild/win32-arm64": "0.20.0", + "@esbuild/win32-ia32": "0.20.0", + "@esbuild/win32-x64": "0.20.0" } }, "esbuild-wasm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.1.tgz", - "integrity": "sha512-6v/WJubRsjxBbQdz6izgvx7LsVFvVaGmSdwrFHmEzoVgfXL89hkKPoQHsnVI2ngOkcBUQT9kmAM1hVL1k/Av4A==" + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.0.tgz", + "integrity": "sha512-Lc9KeQCg1Zf8kCtfDXgy29rx0x8dOuhDWbkP76Wc64q7ctOOc1Zv1C39AxiE+y4N6ONyXtJk4HKpM7jlU7/jSA==" }, "escalade": { "version": "3.1.2", @@ -16920,6 +17008,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dev": true, "requires": { "agent-base": "^7.0.2", "debug": "4" @@ -17046,6 +17135,7 @@ "version": "9.2.15", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", + "dev": true, "requires": { "@ljharb/through": "^2.3.12", "ansi-escapes": "^4.3.2", @@ -17068,6 +17158,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -17075,12 +17166,14 @@ "chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -17088,12 +17181,14 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, "requires": { "tslib": "^2.1.0" } @@ -17102,6 +17197,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -17749,6 +17845,7 @@ "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dev": true, "requires": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -17862,9 +17959,9 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "mini-css-extract-plugin": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", - "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.0.tgz", + "integrity": "sha512-CxmUYPFcTgET1zImteG/LZOy/4T5rTojesQXkSNBiquhydn78tfbCE9sjIjnJ/UcjNjOC1bphTCCW5rrS7cXAg==", "requires": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -18655,9 +18752,9 @@ "optional": true }, "piscina": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.4.0.tgz", - "integrity": "sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.3.1.tgz", + "integrity": "sha512-MBj0QYm3hJQ/C/wIXTN1OCYC8uQ4BBJ4LVele2P4ZwVQAH04vkk8E1SpDbuemLAL1dZorbuOob9rYqJeWCcCRg==", "requires": { "nice-napi": "^1.0.2" } @@ -18721,20 +18818,15 @@ } }, "postcss-loader": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", - "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.0.tgz", + "integrity": "sha512-AbperNcX3rlob7Ay7A/HQcrofug1caABBkopoFeOQMspZBqcqj6giYn1Bwey/0uiOPAcR+NQD0I2HC7rXzk91w==", "requires": { "cosmiconfig": "^9.0.0", "jiti": "^1.20.0", "semver": "^7.5.4" } }, - "postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==" - }, "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", @@ -18781,6 +18873,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" + }, "proc-log": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", @@ -19159,6 +19256,8 @@ "version": "1.71.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", + "optional": true, + "peer": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -19166,9 +19265,9 @@ } }, "sass-loader": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.1.tgz", - "integrity": "sha512-QX8AasDg75monlybel38BZ49JP5Z+uSKfKwF2rO7S74BywaRmGQMUBw9dtkS+ekyM/QnP+NOrRYq8ABMZ9G8jw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.0.tgz", + "integrity": "sha512-LS2mLeFWA+orYxHNu+O18Xe4jR0kyamNOOUsE3NyBP4DvIL+8stHpNX0arYTItdPe80kluIiJ7Wfe/9iHSRO0Q==", "requires": { "neo-async": "^2.6.2" } @@ -19757,17 +19856,6 @@ } } }, - "terser": { - "version": "5.29.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz", - "integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==", - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - } - }, "terser-webpack-plugin": { "version": "5.3.10", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", @@ -19948,9 +20036,12 @@ "devOptional": true }, "undici": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.7.1.tgz", - "integrity": "sha512-+Wtb9bAQw6HYWzCnxrPTMVEV3Q1QjYanI0E4q02ehReMuquQdLTEFEYbfs7hcImVYKcQkWSwT6buEmSVIiDDtQ==" + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.6.2.tgz", + "integrity": "sha512-vSqvUE5skSxQJ5sztTZ/CdeJb1Wq0Hf44hlYMciqHghvz+K88U0l7D6u1VsndoFgskDcnU+nG3gYmMzJVzd9Qg==", + "requires": { + "@fastify/busboy": "^2.0.0" + } }, "undici-types": { "version": "5.26.5", @@ -20083,7 +20174,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", - "peer": true, "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -20106,19 +20196,18 @@ } }, "webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", - "peer": true, + "version": "5.90.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz", + "integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==", "requires": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", + "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.11.5", "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", @@ -20132,7 +20221,7 @@ "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", + "terser-webpack-plugin": "^5.3.10", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -20141,7 +20230,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -20153,20 +20241,17 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peer": true, "requires": {} }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "peer": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "peer": true, "requires": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -20176,9 +20261,9 @@ } }, "webpack-dev-middleware": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.2.tgz", - "integrity": "sha512-Wu+EHmX326YPYUpQLKmKbTyZZJIB8/n6R09pTmB03kJmnMsVPTo9COzHZFr01txwaCAuZvfBJE4ZCHRcKs5JaQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.1.tgz", + "integrity": "sha512-y51HrHaFeeWir0YO4f0g+9GwZawuigzcAdRNon6jErXy/SqV/+O6eaVAzDqE6t3e3NpGeR5CS+cCDaTC+V3yEQ==", "requires": { "colorette": "^2.0.10", "memfs": "^3.4.12", diff --git a/package.json b/package.json index 057b7c7..8c32c93 100644 --- a/package.json +++ b/package.json @@ -19,14 +19,14 @@ "@angular/platform-browser": "^17.3.1", "@angular/platform-browser-dynamic": "^17.3.1", "@angular/router": "^17.3.1", + "@fortawesome/fontawesome-free": "^5.15.4", "@types/chrome": "^0.0.263", "rxjs": "~7.4.0", "tslib": "^2.3.0", - "zone.js": "~0.14.4", - "@fortawesome/fontawesome-free": "^5.15.4" + "zone.js": "~0.14.4" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.3.2", + "@angular-devkit/build-angular": "^17.2.3", "@angular/cli": "^17.3.2", "@angular/compiler-cli": "^17.3.1", "@types/jasmine": "~3.10.0", diff --git a/src/app/app.component.html b/src/app/app.component.html index 806a9a2..6a8f331 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -12,15 +12,11 @@ <final-stage *ngIf="!showOverview" [actions]="actions" + [finalStates] = "finalStates" [mainAttributes]="mainAttributes" [finalStateTarget]="finalStateTarget" [savedAttributes]="savedAttributes" - [saveAttributeHandler]="saveAttributeHandler" - [isAttributeSavedHandler]="isAttributeSavedHandler" - [deleteFinalStateTargetHandler] = "deleteFinalStateTargetHandler" - [addFinalStateHandler] = "addFinalStateHandler" - [deleteFinalStateHandler] = "deleteFinalStateHandler" [unMarkFinalStateHandler]="unMarkFinalStateHandler" > </final-stage> -</div> \ No newline at end of file +</div> diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 464870b..5bc8b6b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -8,7 +8,6 @@ import { saveMarkFinalState, saveRecording } from './utils/storage.util'; -import {Message} from "./model/message.interface"; import {Result} from "./model/result.interface"; import {FinalStateTarget} from "./model/final-state-target.interface"; import {ObjectKeys} from "./model/object-keys.interface"; @@ -24,100 +23,22 @@ export class AppComponent implements OnInit { recording: boolean = false; showOverview: boolean = true; - // START: MARK FINAL STATE + // data used and mofified in final state - here so it could be shared if necessary finalStates: Result[] = []; finalStateTarget?: FinalStateTarget = undefined; savedAttributes: ObjectKeys = {}; - - // array of attributes of final state which are always saved when the showed up mainAttributes: string[] = ['id', 'class', 'name']; - //protected readonly Object = Object; // just for the template - // END: MARK FINAL STATE constructor(private ref: ChangeDetectorRef){} ngOnInit(): void { - // START: MARK FINAL STATE - chrome.runtime.onMessage.addListener((message: Message) => { - if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state - console.log("Final state target:", message) - this.finalStateTarget = message.finalStateClickTarget; - this.savedAttributes = {}; - this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually - } - }); - - // END: MARK FINAL STATE chrome.storage.onChanged.addListener(() => { + console.log("Get data"); this.getData(); }); this.getData(); } - // START: MARK FINAL STATE - /** - * Deletes the clicked final state target and sets all variables to default values. - */ - public deleteFinalStateTargetHandler(){ - const xPath = this.finalStateTarget!.xPath; - this.finalStateTarget = undefined; - this.savedAttributes = {}; - - this.ref.detectChanges(); - const message: Message = { - xPath: xPath - } - chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { - chrome.tabs.sendMessage(tabs[0].id!, message); - }) - } - - public saveAttributeHandler(event: Event, attribute: string, value: string){ - if((event.target as HTMLInputElement).checked){ - console.log(`Saving attribute ${attribute}=${value}`) - this.savedAttributes[attribute] = value; - } else { - console.log(`Deleting attribute ${attribute}=${value}`) - delete this.savedAttributes[attribute]; - } - console.log(this.savedAttributes) - } - - isAttributeSavedHandler(attribute: string): boolean { - return this.savedAttributes.hasOwnProperty(attribute); - } - - public addFinalStateHandler(){ - // Call saveAttribute for each mandatory attribute - Object.keys(this.finalStateTarget!.attributes).forEach(attribute => { - if (this.mainAttributes.includes(attribute)) { - const mockEvent = { target: { checked: true } } as any; - this.saveAttributeHandler(mockEvent, attribute, this.finalStateTarget!.attributes[attribute]); - } - }); - - const element: HtmlElement = { - tagName: this.finalStateTarget!.tagName, - globalAttributes: {}, - elementAttributes: {}, - xPath: this.finalStateTarget!.xPath - }; - - const finalState: Result = { - element: element, - selectedAttributes: this.savedAttributes - }; - this.finalStates.push(finalState); - saveFinalStates(this.finalStates); - this.deleteFinalStateTargetHandler(); - } - - public deleteFinalStateHandler = (index: number) => { - this.finalStates.splice(index, 1); - saveFinalStates(this.finalStates); - } - // END: MARK FINAL STATE - /** * Loads all data needed by the component. */ @@ -128,11 +49,12 @@ export class AppComponent implements OnInit { await getRecording().then(recording => { this.recording = recording; }); - // START: MARK FINAL STATE + + // for final state await getFinalStates().then(finalStates => { this.finalStates = finalStates; }); - // END: MARK FINAL STATE + this.ref.detectChanges(); // we need to automatically detect changes, because chrome.storage.onChanged.addListener // is outside of the Angular scope @@ -156,7 +78,12 @@ export class AppComponent implements OnInit { saveActions(this.actions); } - // START: MARK FINAL STATE + toggleView() { + this.showOverview = !this.showOverview; + } + + + // mark final state code public markFinalStateHandler = () => { saveMarkFinalState(true); this.toggleView(); @@ -166,9 +93,5 @@ export class AppComponent implements OnInit { //saveMarkFinalState(false); this.toggleView(); } - // END: MARK FINAL STATE - - toggleView() { - this.showOverview = !this.showOverview; - } + } diff --git a/src/app/view/components/custom-button/custom-button.component.css b/src/app/view/components/custom-button/custom-button.component.css index 63732bf..a969591 100644 --- a/src/app/view/components/custom-button/custom-button.component.css +++ b/src/app/view/components/custom-button/custom-button.component.css @@ -111,13 +111,13 @@ .edit-button { - background-color: #999; /* Example color */ - color: #fff; /* Example text color */ + background-color: #999; + color: #fff; } .edit-button:hover { - background-color: #aaa; /* Example hover color */ + background-color: #aaa; } diff --git a/src/app/view/components/final-state/final-state.component.css b/src/app/view/components/final-state/final-state.component.css index 83ffecf..bb73a8e 100644 --- a/src/app/view/components/final-state/final-state.component.css +++ b/src/app/view/components/final-state/final-state.component.css @@ -21,7 +21,7 @@ } .first-component p { - margin: 0; /* or padding: 0; */ + margin: 0; } .second-component { @@ -33,7 +33,7 @@ } .second-component p { - margin: 0; /* or padding: 0; */ + margin: 0; } .third-component { @@ -46,5 +46,5 @@ .main-attributes { color: #909090; - text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); /* Shadow around the value */ + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); } \ No newline at end of file diff --git a/src/app/view/components/final-state/final-state.component.html b/src/app/view/components/final-state/final-state.component.html index 6f6df1c..cdd1d13 100644 --- a/src/app/view/components/final-state/final-state.component.html +++ b/src/app/view/components/final-state/final-state.component.html @@ -1,5 +1,3 @@ - - <div class="container"> <div class="component first-component"> <p><b>{{finalState.element.tagName}}</b></p> diff --git a/src/app/view/components/final-state/final-state.component.ts b/src/app/view/components/final-state/final-state.component.ts index c676af0..b3c4327 100644 --- a/src/app/view/components/final-state/final-state.component.ts +++ b/src/app/view/components/final-state/final-state.component.ts @@ -1,9 +1,6 @@ import { Component, Input } from '@angular/core'; - -import { Action } from 'src/app/model/action.interface'; import { Result } from 'src/app/model/result.interface'; -import { FinalStateTarget } from 'src/app/model/final-state-target.interface'; -import { ObjectKeys } from 'src/app/model/object-keys.interface'; + @Component({ selector: 'final-state', diff --git a/src/app/view/pages/final-stage/final-stage.component.css b/src/app/view/pages/final-stage/final-stage.component.css index d386906..13f4250 100644 --- a/src/app/view/pages/final-stage/final-stage.component.css +++ b/src/app/view/pages/final-stage/final-stage.component.css @@ -1,14 +1,10 @@ .fs_container { background-color: rgb(232, 232, 232); - margin: 10px auto; /* Adjust the margin to center horizontally */ - padding: 10px; /* Optionally add padding */ - /*display: flex; /* Use flexbox for layout */ - /*justify-content: center; /* Center horizontally */ - /*align-items: center; /* Center vertically */ + margin: 10px auto; + padding: 10px; } - .custom-checkbox { appearance: none; width: 1.3em; @@ -18,55 +14,55 @@ vertical-align: middle; cursor: pointer; background-color: rgb(128, 128, 128); - padding: 10px; /* Remove padding */ - display: flex; /* Use flexbox for centering */ - justify-content: center; /* Center horizontally */ - align-items: center; /* Center vertically */ + padding: 10px; + display: flex; + justify-content: center; + align-items: center; } .custom-checkbox:checked::after { - content: '\f00c'; /* Font Awesome checkmark icon */ - font-family: 'Font Awesome 5 Free'; /* Specify Font Awesome font family */ - font-weight: 900; /* Adjust weight if necessary */ + content: '\f00c'; + font-family: 'Font Awesome 5 Free'; + font-weight: 900; font-size: 1em; color: white; } .checkbox-container { - display: flex; /* Use flexbox for layout */ - align-items: center; /* Align items vertically */ - margin-bottom: 5px; /* Add margin between checkboxes */ - flex-wrap: nowrap; /* Prevent wrapping of items */ + display: flex; + align-items: center; + margin-bottom: 5px; + flex-wrap: nowrap; } .checkbox-container label { - margin-left: 5px; /* Add space between checkbox and text */ + margin-left: 5px; } .button-container { - width: 100%; /* Ensures the container spans the full width */ + width: 100%; } .return-fs-button { width: 100%; - background-color: #ffffff; /* Button background color */ - color: #808080; /* Grey text color */ - border: 1px solid #808080; /* Grey border */ - padding: 10px; /* Adjust padding as needed */ + background-color: #ffffff; + color: #808080; + border: 1px solid #808080; + padding: 10px; cursor: pointer; - box-sizing: border-box; /* Include padding and border in width */ - text-decoration: none; /* Remove default link underline */ - display: block; /* Ensures the button occupies the entire line */ + box-sizing: border-box; + text-decoration: none; + display: block; text-align: right; } .return-fs-button:hover { - background-color: #f0f0f0; /* Lighter background color on hover */ + background-color: #f0f0f0; } .return-fs-button:active { - background-color: #d9d9d9; /* Darker background color when button is active */ + background-color: #d9d9d9; } .attribute-name { @@ -74,9 +70,9 @@ } .attribute-value { - color: #333333; /* Darker grey color */ - text-decoration: underline; /* Underline */ - text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); /* Shadow around the value */ + color: #333333; + text-decoration: underline; + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); margin-left: 5px; } @@ -88,5 +84,28 @@ .main-attributes { color: #909090; - text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); /* Shadow around the value */ + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; +} + +.content-container { + text-align: center; +} + +.instruction { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; +} + +.waiting { + font-size: 16px; + color: #888; } \ No newline at end of file diff --git a/src/app/view/pages/final-stage/final-stage.component.html b/src/app/view/pages/final-stage/final-stage.component.html index d291b6e..e5dcc47 100644 --- a/src/app/view/pages/final-stage/final-stage.component.html +++ b/src/app/view/pages/final-stage/final-stage.component.html @@ -1,147 +1,165 @@ <div class="button-container"> - -<custom-button - buttonText="Not a final state, go back" - size="S" - iconType="fontawesome" - icon="fa fa-arrow-left" - additionalClasses="return-fs-button" - (click)="unMarkFinalStateHandler()" - > - </custom-button> + <custom-button + buttonText="Not a final state, go back" + size="S" + iconType="fontawesome" + icon="fa fa-arrow-left" + additionalClasses="return-fs-button" + (click)="unMarkFinalStateHandler()" + > + </custom-button> </div> -<h3>How to recognize final state?</h3> -<!-- <div id="scenario-tracker-content"> --> - <!-- START: MARK FINAL STATE --> - <ng-container *ngIf="finalStateTarget"> - <div class="fs_container"> - <h3> Selected element: {{finalStateTarget.tagName}} </h3> - <!-- not chooseable attributes - default --> - <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> - <ng-container *ngIf="mainAttributes.includes(attribute)"> - <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" checked style="display: none;" +<ng-container *ngIf="!(!isMarkFinalState && finalStates && finalStates.length == 0)"> + <div class="content-container"> + <h2>How to recognize final state?</h2> + </div> +</ng-container> + + +<ng-container *ngIf="(!isMarkFinalState && finalStates && finalStates.length == 0); else elseBlock"> + <div class="container content-container"> + <p class="instruction">Click on the element you want to select</p> + <p class="waiting">Waiting...</p> + </div> +</ng-container> + + +<ng-template #elseBlock> +<ng-container *ngIf="!isMarkFinalState && finalStates && finalStates.length > 0"> + <div class="content-container"> + <p style="font-weight: bold;">Click on the next element you want to select</p> + </div> +</ng-container> +</ng-template> + +<ng-container *ngIf="finalStateTarget"> + <div class="fs_container"> + <h3> Selected element: {{finalStateTarget.tagName}} </h3> + + <!-- not chooseable attributes - default --> + <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> + <ng-container *ngIf="mainAttributes.includes(attribute)"> + <div class="checkbox-container"> + <input type="checkbox" class="custom-checkbox" checked style="display: none;" + (change)="saveAttributeHandler($event, attribute, finalStateTarget.attributes[attribute])"> + <label> + <span class="main-attributes main-attributes-name"> {{attribute}}: </span> + <span class="main-attributes">{{finalStateTarget.attributes[attribute]}} </span> + </label> + </div> + </ng-container> + </div> + + <ng-container *ngIf="finalStateTarget.text"> + <h5>Text:</h5> + <p>{{finalStateTarget.text}}</p> + </ng-container> + <h4>Select characteristic properties</h4> + <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> + <ng-container *ngIf="!mainAttributes.includes(attribute)"> + <div class="checkbox-container"> + <input type="checkbox" class="custom-checkbox" + [checked]="isAttributeSavedHandler(attribute)" (change)="saveAttributeHandler($event, attribute, finalStateTarget.attributes[attribute])"> + <label> + <span class="attribute-name"> {{attribute}} : </span> + <span class="attribute-value">{{finalStateTarget.attributes[attribute]}} </span> + <custom-button + buttonText="" + size="XS" + iconType="fontawesome" + icon="fa fa-edit" + additionalClasses="edit-button" + additionalStyles="margin-left: 3px;" + > + </custom-button> + </label> + </div> + </ng-container> + </div> + <h5>Styles</h5> + <!-- <h5>Explicitly set styles:</h5> --> + <div *ngFor="let attribute of Object.keys(finalStateTarget.styles)"> + <div class="checkbox-container"> + <input type="checkbox" class="custom-checkbox" + [checked]="isAttributeSavedHandler('style.'+attribute)" + (change)="saveAttributeHandler($event, 'style.'+attribute, finalStateTarget.styles[attribute])"> <label> - <span class="main-attributes main-attributes-name"> {{attribute}}: </span> - <span class="main-attributes">{{finalStateTarget.attributes[attribute]}} </span> - </label> - </div> - </ng-container> - </div> + <span class="attribute-name"> {{attribute}}: </span> + <span class="attribute-value">{{finalStateTarget.styles[attribute]}} </span> + <custom-button + buttonText="" + size="XS" + iconType="fontawesome" + icon="fa fa-edit" + additionalClasses="edit-button" + additionalStyles="margin-left: 3px;" + > + </custom-button> - <ng-container *ngIf="finalStateTarget.text"> - <h5>Text:</h5> - <p>{{finalStateTarget.text}}</p> - </ng-container> - <h4>Select characteristic properties</h4> - <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> - <ng-container *ngIf="!mainAttributes.includes(attribute)"> + </label> + </div> + </div> + <!-- <h5>Computed styles:</h5> --> + <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)"> <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" - [checked]="isAttributeSavedHandler(attribute)" - (change)="saveAttributeHandler($event, attribute, finalStateTarget.attributes[attribute])"> + <input type="checkbox" class="custom-checkbox" + [checked]="isAttributeSavedHandler('style.'+attribute)" + (change)="saveAttributeHandler($event, 'style.'+attribute, finalStateTarget.computedStyles[attribute])"> <label> - <span class="attribute-name"> {{attribute}} : </span> - <span class="attribute-value">{{finalStateTarget.attributes[attribute]}} </span> + <span class="attribute-name"> {{attribute}}: </span> + <span class="attribute-value">{{finalStateTarget.computedStyles[attribute]}} </span> <custom-button - buttonText="" - size="XS" - iconType="fontawesome" - icon="fa fa-edit" - additionalClasses="edit-button" - additionalStyles="margin-left: 3px;" - > - </custom-button> - </label> + buttonText="" + size="XS" + iconType="fontawesome" + icon="fa fa-edit" + additionalClasses="edit-button" + additionalStyles="margin-left: 3px;" + + > + </custom-button> + </label> </div> - </ng-container> - </div> - <h5>Styles</h5> - <!-- <h5>Explicitly set styles:</h5> --> - <div *ngFor="let attribute of Object.keys(finalStateTarget.styles)"> - <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" - [checked]="isAttributeSavedHandler('style.'+attribute)" - (change)="saveAttributeHandler($event, 'style.'+attribute, finalStateTarget.styles[attribute])"> - <label> - <span class="attribute-name"> {{attribute}}: </span> - <span class="attribute-value">{{finalStateTarget.styles[attribute]}} </span> - <custom-button - buttonText="" - size="XS" - iconType="fontawesome" - icon="fa fa-edit" - additionalClasses="edit-button" - additionalStyles="margin-left: 3px;" - > - </custom-button> - - </label> </div> - </div> - <!-- <h5>Computed styles:</h5> --> - <div *ngFor="let attribute of Object.keys(finalStateTarget.computedStyles)"> - <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" - [checked]="isAttributeSavedHandler('style.'+attribute)" - (change)="saveAttributeHandler($event, 'style.'+attribute, finalStateTarget.computedStyles[attribute])"> - <label> - <span class="attribute-name"> {{attribute}}: </span> - <span class="attribute-value">{{finalStateTarget.computedStyles[attribute]}} </span> - <custom-button - buttonText="" - size="XS" - iconType="fontawesome" - icon="fa fa-edit" - additionalClasses="edit-button" - additionalStyles="margin-left: 3px;" - - > - </custom-button> - </label> - </div> - </div> - <span style="margin-top: 1em;"> - <custom-button - buttonText="Cancel" - size="S" - iconType="fontawesome" - icon="fa fa-trash" - additionalClasses="delete-button" - additionalStyles="width: 10em;" - (click)="deleteFinalStateTargetHandler()" - > - </custom-button> - <custom-button - buttonText="Add" - size="S" - iconType="fontawesome" - icon="fa fa-plus" - additionalClasses="success-button pl_fs" - additionalStyles="margin-left: 2em; width: 10em;" - (click)="addFinalStateHandler()" + <span style="margin-top: 1em;"> + <custom-button + buttonText="Cancel" + size="S" + iconType="fontawesome" + icon="fa fa-trash" + additionalClasses="delete-button" + additionalStyles="width: 10em;" + (click)="deleteFinalStateTargetHandler()" > - </custom-button> - </span> - </div> - </ng-container> - - <h3>Final states:</h3> - <div *ngFor="let finalState of finalStates; let i = index"> - <final-state - [index]="i" - [finalState]="finalState" - [deleteFinalState]="deleteFinalStateHandler()" - [mainAttributes]="mainAttributes" + </custom-button> + <custom-button + buttonText="Add" + size="S" + iconType="fontawesome" + icon="fa fa-plus" + additionalClasses="success-button pl_fs" + additionalStyles="margin-left: 2em; width: 10em;" + (click)="addFinalStateHandler()" > - </final-state> - </div> + </custom-button> + </span> + </div> + </ng-container> + - <!-- END: MARK FINAL STATE --> - <!-- -</div> +<ng-container *ngIf="finalStates && finalStates.length > 0"> + <h3>Final states:</h3> + <div *ngFor="let finalState of finalStates; let i = index"> + <final-state + [index]="i" + [finalState]="finalState" + [deleteFinalState]="deleteFinalStateHandler" + [mainAttributes]="mainAttributes" + > + </final-state> + </div> ---> \ No newline at end of file +</ng-container> \ No newline at end of file diff --git a/src/app/view/pages/final-stage/final-stage.component.ts b/src/app/view/pages/final-stage/final-stage.component.ts index 62525b3..20f2d2b 100644 --- a/src/app/view/pages/final-stage/final-stage.component.ts +++ b/src/app/view/pages/final-stage/final-stage.component.ts @@ -1,9 +1,23 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit, Input, ChangeDetectorRef } from '@angular/core'; import { Action } from 'src/app/model/action.interface'; import { Result } from 'src/app/model/result.interface'; import { FinalStateTarget } from 'src/app/model/final-state-target.interface'; import { ObjectKeys } from 'src/app/model/object-keys.interface'; +import { HtmlElement } from 'src/app/model/html-element.interface'; + +import { Message } from 'src/app/model/message.interface'; + +import { + getActions, + getFinalStates, + getRecording, + saveActions, saveFinalStates, + getMarkFinalState, + saveMarkFinalState, + saveRecording +} from 'src/app/utils/storage.util'; + @Component({ selector: 'final-stage', @@ -15,45 +29,104 @@ export class FinalStageComponent implements OnInit { @Input() actions: Action[] = []; @Input() finalStates: Result[] = []; @Input() finalStateTarget?: FinalStateTarget = undefined; - //finalStateTarget?: FinalStateTarget = undefined; //used for testing view - @Input() savedAttributes: ObjectKeys = {}; - - @Input() saveAttributeHandler!: Function; - @Input() isAttributeSavedHandler!: Function; - @Input() deleteFinalStateTargetHandler!: Function; - @Input() addFinalStateHandler!: Function; - @Input() deleteFinalStateHandler!: Function; @Input() unMarkFinalStateHandler!: Function; @Input() mainAttributes: string[] = []; - constructor(){} + constructor(private ref: ChangeDetectorRef){} + + isMarkFinalState: boolean = false; protected readonly Object = Object; // just for the template + ngOnInit(): void { - // Initialize finalStateTarget to the default value for testing purposes - /* - this.finalStateTarget = { - tagName: 'div', // Example tag name - text: 'Assault rifle-wielding standard trooper', // Example text - xPath: '/HTML[1]/BODY[1]/DIV[1]', // Example XPath - attributes: { - dir: 'ltr', // Example attribute - army: 'Republic Clone Army', // Example additional attribute - id: 'random-id', // Random id - class: 'random-class' // Random class - }, - styles: { - outline: 'rgba(218, 172, 0, 0.8) solid 5px', // Example style - color: 'blue' // Another example style - }, - computedStyles: { - fontFamily: 'Arial', // Example computed style - fontSize: '16px' // Another example computed style + console.log("Final stage ngOnInit()"); + chrome.runtime.onMessage.addListener((message: Message) => { + if (message.finalStateClickTarget) { + this.isMarkFinalState = true; + } else { + // Otherwise, check getMarkFinalState + getMarkFinalState().then(markFinalState => { + this.isMarkFinalState = markFinalState; + }); + } + + if(message.finalStateClickTarget){ // in this component, we only listen for messages marking the final state + console.log("Final state target:", message) + this.finalStateTarget = message.finalStateClickTarget; + this.savedAttributes = {}; + this.ref.detectChanges(); // chrome.runtime.onMessage is outside of Angular scope, so we need to detect changes manually } - }; - */ + }); + } + + /** + * Deletes the clicked final state target and sets all variables to default values. + */ + public deleteFinalStateTargetHandler(){ + const xPath = this.finalStateTarget!.xPath; + this.finalStateTarget = undefined; + this.savedAttributes = {}; + + this.ref.detectChanges(); + const message: Message = { + xPath: xPath + } + chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { + chrome.tabs.sendMessage(tabs[0].id!, message); + }) + } + + public saveAttributeHandler(event: Event, attribute: string, value: string){ + if((event.target as HTMLInputElement).checked){ + console.log(`Saving attribute ${attribute}=${value}`) + this.savedAttributes[attribute] = value; + } else { + console.log(`Deleting attribute ${attribute}=${value}`) + delete this.savedAttributes[attribute]; } + console.log(this.savedAttributes) + } + + isAttributeSavedHandler(attribute: string): boolean { + return this.savedAttributes.hasOwnProperty(attribute); + } + + public addFinalStateHandler(){ + // Call saveAttribute for each mandatory attribute + Object.keys(this.finalStateTarget!.attributes).forEach(attribute => { + if (this.mainAttributes.includes(attribute)) { + const mockEvent = { target: { checked: true } } as any; + this.saveAttributeHandler(mockEvent, attribute, this.finalStateTarget!.attributes[attribute]); + } + }); + + const element: HtmlElement = { + tagName: this.finalStateTarget!.tagName, + globalAttributes: {}, + elementAttributes: {}, + xPath: this.finalStateTarget!.xPath + }; + + const finalState: Result = { + element: element, + selectedAttributes: this.savedAttributes + }; + this.finalStates.push(finalState); + saveFinalStates(this.finalStates); + this.deleteFinalStateTargetHandler(); + + saveMarkFinalState(true); + + this.isMarkFinalState = false; + } + + public deleteFinalStateHandler = (index: number) => { + this.finalStates.splice(index, 1); + saveFinalStates(this.finalStates); + + this.isMarkFinalState = false; + } } diff --git a/src/app/view/pages/overview/overview.component.html b/src/app/view/pages/overview/overview.component.html index 81c20d0..285c1b8 100644 --- a/src/app/view/pages/overview/overview.component.html +++ b/src/app/view/pages/overview/overview.component.html @@ -51,7 +51,7 @@ <captured-action [action]="action" [deleteActionHandler]="deleteActionHandler" [startTime]="startTime" [actionIndex]="i"></captured-action> </div> -<!-- START: MARK FINAL STATE --> + <custom-button buttonText="Mark final state" size="M" @@ -60,5 +60,4 @@ icon="fa fa-solid fa-flag-checkered" additionalClasses="info-button" additionalStyles="margin-top: 2em; margin-left:1em;" (click)="markFinalStateHandler()"> -</custom-button> -<!-- END: MARK FINAL STATE --> \ No newline at end of file +</custom-button> \ No newline at end of file diff --git a/src/app/view/pages/overview/overview.component.ts b/src/app/view/pages/overview/overview.component.ts index b6ef2e6..8139376 100644 --- a/src/app/view/pages/overview/overview.component.ts +++ b/src/app/view/pages/overview/overview.component.ts @@ -15,16 +15,13 @@ export class OverviewComponent implements OnInit { @Input() deleteActionHandler!: Function; @Input() deleteAllActionsHandler!: Function; @Input() toggleRecordingHandler!: Function; - // START: MARK FINAL STATE + @Input() markFinalStateHandler!: Function; - // END: MARK FINAL STATE readonly ActionEnum = ActionEnum startTime?: Date; - startTime?: Date; - constructor() { } ngOnInit(): void { -- GitLab From 593a74309767f6d333ff66c36fe212a14bd60753 Mon Sep 17 00:00:00 2001 From: vondrp <vondrovic@centrum.cz> Date: Sat, 6 Apr 2024 15:01:56 +0200 Subject: [PATCH 8/9] =?UTF-8?q?Re=20#11122=20-=20oprava=20ozna=C4=8Den?= =?UTF-8?q?=C3=AD=20zobrazen=C3=A9ho=20elementu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pÅ™i return z final-stage do overview se zruÅ¡Ã oznaÅ¡enà html elementu - pÅ™i zruÅ¡enà elementu (cancel) lze zvolit jiný element --- src/app/app.component.ts | 4 +--- .../final-stage/final-stage.component.html | 2 +- .../final-stage/final-stage.component.ts | 23 +++++++++++++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5bc8b6b..427072c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -11,7 +11,6 @@ import { import {Result} from "./model/result.interface"; import {FinalStateTarget} from "./model/final-state-target.interface"; import {ObjectKeys} from "./model/object-keys.interface"; -import {HtmlElement} from "./model/html-element.interface"; @Component({ selector: 'app-root', @@ -90,8 +89,7 @@ export class AppComponent implements OnInit { } public unMarkFinalStateHandler = () => { - //saveMarkFinalState(false); + saveMarkFinalState(false); // no longer mark another element this.toggleView(); } - } diff --git a/src/app/view/pages/final-stage/final-stage.component.html b/src/app/view/pages/final-stage/final-stage.component.html index e5dcc47..9c75e0a 100644 --- a/src/app/view/pages/final-stage/final-stage.component.html +++ b/src/app/view/pages/final-stage/final-stage.component.html @@ -5,7 +5,7 @@ iconType="fontawesome" icon="fa fa-arrow-left" additionalClasses="return-fs-button" - (click)="unMarkFinalStateHandler()" + (click)="returnHandler()" > </custom-button> </div> diff --git a/src/app/view/pages/final-stage/final-stage.component.ts b/src/app/view/pages/final-stage/final-stage.component.ts index 20f2d2b..1dff8db 100644 --- a/src/app/view/pages/final-stage/final-stage.component.ts +++ b/src/app/view/pages/final-stage/final-stage.component.ts @@ -9,13 +9,9 @@ import { HtmlElement } from 'src/app/model/html-element.interface'; import { Message } from 'src/app/model/message.interface'; import { - getActions, - getFinalStates, - getRecording, - saveActions, saveFinalStates, + saveFinalStates, getMarkFinalState, saveMarkFinalState, - saveRecording } from 'src/app/utils/storage.util'; @@ -31,8 +27,8 @@ export class FinalStageComponent implements OnInit { @Input() finalStateTarget?: FinalStateTarget = undefined; @Input() savedAttributes: ObjectKeys = {}; + //@Input() unMarkFinalStateHandler!: Function; @Input() unMarkFinalStateHandler!: Function; - @Input() mainAttributes: string[] = []; constructor(private ref: ChangeDetectorRef){} @@ -77,6 +73,10 @@ export class FinalStageComponent implements OnInit { chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { chrome.tabs.sendMessage(tabs[0].id!, message); }) + + // called so we can mark another element + saveMarkFinalState(true); + this.isMarkFinalState = false; } public saveAttributeHandler(event: Event, attribute: string, value: string){ @@ -119,7 +119,6 @@ export class FinalStageComponent implements OnInit { this.deleteFinalStateTargetHandler(); saveMarkFinalState(true); - this.isMarkFinalState = false; } @@ -129,4 +128,14 @@ export class FinalStageComponent implements OnInit { this.isMarkFinalState = false; } + + + public returnHandler() + { + // unmark selected object + if (this.finalStateTarget) { + this.deleteFinalStateTargetHandler(); + } + this.unMarkFinalStateHandler(); + } } -- GitLab From 1d39c42a4df7dcf21ab5c007cade35a6ef2d955f Mon Sep 17 00:00:00 2001 From: vondrp <vondrovic@centrum.cz> Date: Sat, 6 Apr 2024 15:47:45 +0200 Subject: [PATCH 9/9] =?UTF-8?q?Re=20#11122=20:=20Odstranil=20text-shadow,?= =?UTF-8?q?=20mainAttributy=20p=C5=99idal=20do=20element=20atribut=C5=AF?= =?UTF-8?q?=20m=C3=ADsto=20selected=20atribut=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/final-state/final-state.component.css | 1 - .../components/final-state/final-state.component.html | 9 ++------- .../view/components/final-state/final-state.component.ts | 1 - src/app/view/pages/final-stage/final-stage.component.css | 2 -- .../view/pages/final-stage/final-stage.component.html | 3 --- src/app/view/pages/final-stage/final-stage.component.ts | 9 ++++++--- 6 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/app/view/components/final-state/final-state.component.css b/src/app/view/components/final-state/final-state.component.css index bb73a8e..0b79471 100644 --- a/src/app/view/components/final-state/final-state.component.css +++ b/src/app/view/components/final-state/final-state.component.css @@ -46,5 +46,4 @@ .main-attributes { color: #909090; - text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); } \ No newline at end of file diff --git a/src/app/view/components/final-state/final-state.component.html b/src/app/view/components/final-state/final-state.component.html index cdd1d13..74912f0 100644 --- a/src/app/view/components/final-state/final-state.component.html +++ b/src/app/view/components/final-state/final-state.component.html @@ -2,19 +2,14 @@ <div class="component first-component"> <p><b>{{finalState.element.tagName}}</b></p> - <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> - <ng-container *ngIf="mainAttributes.includes(attribute)"> - <!-- show only first 3 --> - <p class="main-attributes S" *ngIf="j < 3">{{attribute}}: {{finalState.selectedAttributes[attribute]}}</p> - </ng-container> + <div *ngFor="let attribute of Object.keys(finalState.element.elementAttributes); let j = index"> + <p class="main-attributes S" *ngIf="j < 3">{{attribute}}: {{finalState.element.elementAttributes[attribute]}}</p> </div> </div> <div class="component second-component"> <div *ngFor="let attribute of Object.keys(finalState.selectedAttributes); let j = index"> <!-- show only first 3 --> - <ng-container *ngIf="!mainAttributes.includes(attribute)"> <p *ngIf="j < 3"><span style="text-transform: capitalize;">{{attribute}}: </span> <b>{{finalState.selectedAttributes[attribute]}}</b></p> - </ng-container> </div> </div> <div class="component third-component"> diff --git a/src/app/view/components/final-state/final-state.component.ts b/src/app/view/components/final-state/final-state.component.ts index b3c4327..92e5d85 100644 --- a/src/app/view/components/final-state/final-state.component.ts +++ b/src/app/view/components/final-state/final-state.component.ts @@ -13,7 +13,6 @@ export class FinalStateComponent { @Input() deleteFinalState!: Function; @Input() index!: number; - @Input() mainAttributes: string[] = [] protected readonly Object = Object; // just for the template } diff --git a/src/app/view/pages/final-stage/final-stage.component.css b/src/app/view/pages/final-stage/final-stage.component.css index 13f4250..460a05b 100644 --- a/src/app/view/pages/final-stage/final-stage.component.css +++ b/src/app/view/pages/final-stage/final-stage.component.css @@ -72,7 +72,6 @@ .attribute-value { color: #333333; text-decoration: underline; - text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); margin-left: 5px; } @@ -84,7 +83,6 @@ .main-attributes { color: #909090; - text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); } .container { diff --git a/src/app/view/pages/final-stage/final-stage.component.html b/src/app/view/pages/final-stage/final-stage.component.html index 9c75e0a..c3f488e 100644 --- a/src/app/view/pages/final-stage/final-stage.component.html +++ b/src/app/view/pages/final-stage/final-stage.component.html @@ -41,8 +41,6 @@ <div *ngFor="let attribute of Object.keys(finalStateTarget.attributes)"> <ng-container *ngIf="mainAttributes.includes(attribute)"> <div class="checkbox-container"> - <input type="checkbox" class="custom-checkbox" checked style="display: none;" - (change)="saveAttributeHandler($event, attribute, finalStateTarget.attributes[attribute])"> <label> <span class="main-attributes main-attributes-name"> {{attribute}}: </span> <span class="main-attributes">{{finalStateTarget.attributes[attribute]}} </span> @@ -157,7 +155,6 @@ [index]="i" [finalState]="finalState" [deleteFinalState]="deleteFinalStateHandler" - [mainAttributes]="mainAttributes" > </final-state> </div> diff --git a/src/app/view/pages/final-stage/final-stage.component.ts b/src/app/view/pages/final-stage/final-stage.component.ts index 1dff8db..1b0e29d 100644 --- a/src/app/view/pages/final-stage/final-stage.component.ts +++ b/src/app/view/pages/final-stage/final-stage.component.ts @@ -96,17 +96,20 @@ export class FinalStageComponent implements OnInit { public addFinalStateHandler(){ // Call saveAttribute for each mandatory attribute + const elementAttributes: ObjectKeys = {} as ObjectKeys; + Object.keys(this.finalStateTarget!.attributes).forEach(attribute => { if (this.mainAttributes.includes(attribute)) { - const mockEvent = { target: { checked: true } } as any; - this.saveAttributeHandler(mockEvent, attribute, this.finalStateTarget!.attributes[attribute]); + elementAttributes[attribute] = this.finalStateTarget!.attributes[attribute]; + //const mockEvent = { target: { checked: true } } as any; + //this.saveAttributeHandler(mockEvent, attribute, this.finalStateTarget!.attributes[attribute]); } }); const element: HtmlElement = { tagName: this.finalStateTarget!.tagName, globalAttributes: {}, - elementAttributes: {}, + elementAttributes: elementAttributes, xPath: this.finalStateTarget!.xPath }; -- GitLab