PSWriteHTML.psm1

function Compare-MultipleObjects { 
    <#
    .SYNOPSIS
    Compares multiple objects based on specified properties and displays differences.
 
    .DESCRIPTION
    The Compare-MultipleObjects function compares multiple objects based on specified properties and displays differences in a structured format. It provides options to customize the comparison output and handle various scenarios.
 
    .PARAMETER Objects
    Specifies the list of objects to compare.
 
    .PARAMETER ObjectsName
    Specifies an array of names for the objects being compared.
 
    .PARAMETER CompareSorted
    Indicates whether to compare objects in a sorted manner.
 
    .PARAMETER FormatOutput
    Indicates whether to format the output for better readability.
 
    .PARAMETER FormatDifferences
    Indicates whether to format and highlight the differences in the output.
 
    .PARAMETER Summary
    Indicates whether to display a summary of the comparison results.
 
    .PARAMETER Splitter
    Specifies the delimiter to use when joining property values.
 
    .PARAMETER Property
    Specifies the properties to compare across objects.
 
    .PARAMETER ExcludeProperty
    Specifies properties to exclude from the comparison.
 
    .PARAMETER AllProperties
    Indicates whether to compare all properties of the objects.
 
    .PARAMETER SkipProperties
    Indicates whether to skip comparing properties.
 
    .PARAMETER First
    Specifies the number of first objects to consider for comparison.
 
    .PARAMETER Last
    Specifies the number of last objects to consider for comparison.
 
    .PARAMETER Replace
    Specifies replacement values for specific properties.
 
    .PARAMETER FlattenObject
    Indicates whether to flatten the object structure for comparison.
 
    .EXAMPLE
    Compare-MultipleObjects -Objects $objects -Property 'Name', 'Age' -FormatOutput
 
    Description:
    Compares the objects in the $objects array based on the 'Name' and 'Age' properties and formats the output for better readability.
 
    .EXAMPLE
    Compare-MultipleObjects -Objects $objects -Property 'Status' -FormatDifferences
 
    Description:
    Compares the objects in the $objects array based on the 'Status' property and highlights the differences in the output.
 
    #>

    [CmdLetBinding()]
    param(
        [System.Collections.IList] $Objects,
        [Array] $ObjectsName = @(),
        [switch] $CompareSorted,
        [switch] $FormatOutput,
        [switch] $FormatDifferences,
        [switch] $Summary,
        [string] $Splitter = ', ',
        [string[]] $Property,
        [string[]] $ExcludeProperty,
        [switch] $AllProperties,
        [switch] $SkipProperties,
        [int] $First,
        [int] $Last,
        [Array] $Replace,
        [switch] $FlattenObject
    )
    if ($null -eq $Objects -or $Objects.Count -eq 1) {
        Write-Warning "Compare-MultipleObjects - Unable to compare objects. Not enough objects to compare ($($Objects.Count))."
        return
    }
    if (-not $ObjectsName) {
        $ObjectsName = @()
    }
    if ($ObjectsName.Count -gt 0 -and $Objects.Count -gt $ObjectsName.Count) {

        Write-Warning -Message "Compare-MultipleObjects - Unable to rename objects. ObjectsName small then amount of Objects ($($Objects.Count))."
    }

    function Compare-TwoArrays {
        [CmdLetBinding()]
        param(
            [string] $FieldName,
            [Array] $Object1,
            [Array] $Object2,
            [Array] $Replace
        )
        $Result = [ordered] @{
            Status = $false
            Same   = [System.Collections.Generic.List[string]]::new()
            Add    = [System.Collections.Generic.List[string]]::new()
            Remove = [System.Collections.Generic.List[string]]::new()
        }

        if ($Replace) {
            foreach ($R in $Replace) {

                if (($($R.Keys[0]) -eq '') -or ($($R.Keys[0]) -eq $FieldName)) {
                    if ($null -ne $Object1) {
                        $Object1 = $Object1 -replace $($R.Values)[0], $($R.Values)[1]
                    }
                    if ($null -ne $Object2) {
                        $Object2 = $Object2 -replace $($R.Values)[0], $($R.Values)[1]
                    }
                }
            }
        }

        if ($null -eq $Object1 -and $null -eq $Object2) {
            $Result['Status'] = $true
        } elseif (($null -eq $Object1) -or ($null -eq $Object2)) {
            $Result['Status'] = $false
            foreach ($O in $Object1) {
                $Result['Add'].Add($O)
            }
            foreach ($O in $Object2) {
                $Result['Remove'].Add($O)
            }
        } else {
            $ComparedObject = Compare-Object -ReferenceObject $Object1 -DifferenceObject $Object2 -IncludeEqual
            foreach ($_ in $ComparedObject) {
                if ($_.SideIndicator -eq '==') {
                    $Result['Same'].Add($_.InputObject)
                } elseif (($_.SideIndicator -eq '<=')) {
                    $Result['Add'].Add($_.InputObject)
                } elseif (($_.SideIndicator -eq '=>')) {
                    $Result['Remove'].Add($_.InputObject)
                }
            }
            IF ($Result['Add'].Count -eq 0 -and $Result['Remove'].Count -eq 0) {
                $Result['Status'] = $true
            } else {
                $Result['Status'] = $false
            }
        }
        $Result
    }

    if ($ObjectsName[0]) {
        $ValueSourceName = $ObjectsName[0]
    } else {
        $ValueSourceName = "Source"
    }

    [Array] $Objects = foreach ($Object in $Objects) {
        if ($null -eq $Object) {
            [PSCustomObject] @{}
        } else {
            $Object
        }
    }

    if ($FlattenObject) {
        try {
            [Array] $Objects = ConvertTo-FlatObject -Objects $Objects -ExcludeProperty $ExcludeProperty
        } catch {
            Write-Warning "Compare-MultipleObjects - Unable to flatten objects. ($($_.Exception.Message))"
        }
    }

    if ($First -or $Last) {
        [int] $TotalCount = $First + $Last
        if ($TotalCount -gt 1) {
            $Objects = $Objects | Select-Object -First $First -Last $Last
        } else {
            Write-Warning "Compare-MultipleObjects - Unable to compare objects. Not enough objects to compare ($TotalCount)."
            return
        }
    }
    $ReturnValues = @(
        $FirstElement = [ordered] @{ }
        $FirstElement['Name'] = 'Properties'
        if ($Summary) {
            $FirstElement['Same'] = $null
            $FirstElement['Different'] = $null
        }
        $FirstElement['Status'] = $false

        $FirstObjectProperties = Select-Properties -Objects $Objects -Property $Property -ExcludeProperty $ExcludeProperty -AllProperties:$AllProperties
        if (-not $SkipProperties) {
            if ($FormatOutput) {
                $FirstElement[$ValueSourceName] = $FirstObjectProperties -join $Splitter
            } else {
                $FirstElement[$ValueSourceName] = $FirstObjectProperties
            }
            [Array] $IsSame = for ($i = 1; $i -lt $Objects.Count; $i++) {
                if ($ObjectsName[$i]) {
                    $ValueToUse = $ObjectsName[$i]
                } else {
                    $ValueToUse = $i
                }
                if ($Objects[0] -is [System.Collections.IDictionary]) {
                    [string[]] $CompareObjectProperties = $Objects[$i].Keys
                } else {
                    [string[]] $CompareObjectProperties = $Objects[$i].PSObject.Properties.Name
                    [string[]] $CompareObjectProperties = Select-Properties -Objects $Objects[$i] -Property $Property -ExcludeProperty $ExcludeProperty -AllProperties:$AllProperties
                }

                if ($FormatOutput) {
                    $FirstElement["$ValueToUse"] = $CompareObjectProperties -join $Splitter
                } else {
                    $FirstElement["$ValueToUse"] = $CompareObjectProperties
                }
                if ($CompareSorted) {
                    $Value1 = $FirstObjectProperties | Sort-Object
                    $Value2 = $CompareObjectProperties | Sort-Object
                } else {
                    $Value1 = $FirstObjectProperties
                    $Value2 = $CompareObjectProperties
                }

                $Status = Compare-TwoArrays -FieldName 'Properties' -Object1 $Value1 -Object2 $Value2 -Replace $Replace
                if ($FormatDifferences) {
                    $FirstElement["$ValueToUse-Add"] = $Status['Add'] -join $Splitter
                    $FirstElement["$ValueToUse-Remove"] = $Status['Remove'] -join $Splitter
                    $FirstElement["$ValueToUse-Same"] = $Status['Same'] -join $Splitter
                } else {
                    $FirstElement["$ValueToUse-Add"] = $Status['Add']
                    $FirstElement["$ValueToUse-Remove"] = $Status['Remove']
                    $FirstElement["$ValueToUse-Same"] = $Status['Same']
                }
                $Status
            }
            if ($IsSame.Status -notcontains $false) {
                $FirstElement['Status'] = $true
            } else {
                $FirstElement['Status'] = $false
            }
            if ($Summary) {
                [Array] $Collection = (0..($IsSame.Count - 1)).Where( { $IsSame[$_].Status -eq $true }, 'Split')
                if ($FormatDifferences) {
                    $FirstElement['Same'] = ($Collection[0] | ForEach-Object {
                            $Count = $_ + 1
                            if ($ObjectsName[$Count]) {
                                $ObjectsName[$Count]
                            } else {
                                $Count
                            }
                        }
                    ) -join $Splitter
                    $FirstElement['Different'] = ($Collection[1] | ForEach-Object {
                            $Count = $_ + 1
                            if ($ObjectsName[$Count]) {
                                $ObjectsName[$Count]
                            } else {
                                $Count
                            }
                        }
                    ) -join $Splitter
                } else {
                    $FirstElement['Same'] = $Collection[0] | ForEach-Object {
                        $Count = $_ + 1
                        if ($ObjectsName[$Count]) {
                            $ObjectsName[$Count]
                        } else {
                            $Count
                        }
                    }
                    $FirstElement['Different'] = $Collection[1] | ForEach-Object {
                        $Count = $_ + 1
                        if ($ObjectsName[$Count]) {
                            $ObjectsName[$Count]
                        } else {
                            $Count
                        }
                    }
                }
            }
            [PSCustomObject] $FirstElement
        }

        foreach ($NameProperty in $FirstObjectProperties) {
            $EveryOtherElement = [ordered] @{ }
            $EveryOtherElement['Name'] = $NameProperty
            if ($Summary) {
                $EveryOtherElement['Same'] = $null
                $EveryOtherElement['Different'] = $null
            }
            $EveryOtherElement.Status = $false

            if ($FormatOutput) {
                $EveryOtherElement[$ValueSourceName] = $Objects[0].$NameProperty -join $Splitter
            } else {
                $EveryOtherElement[$ValueSourceName] = $Objects[0].$NameProperty
            }

            [Array] $IsSame = for ($i = 1; $i -lt $Objects.Count; $i++) {
                $Skip = $false

                if ($ObjectsName[$i]) {
                    $ValueToUse = $ObjectsName[$i]
                } else {
                    $ValueToUse = $i
                }

                if ($Objects[$i] -is [System.Collections.IDictionary]) {
                    if ($Objects[$i].Keys -notcontains $NameProperty) {
                        $Status = [ordered] @{
                            Status = $false
                            Same   = @()
                            Add    = @()
                            Remove = @()
                        }
                        $Skip = $true
                    }
                } elseif ($Objects[$i].PSObject.Properties.Name -notcontains $NameProperty) {
                    $Status = [ordered] @{
                        Status = $false;
                        Same   = @()
                        Add    = @()
                        Remove = @()
                    }
                    $Skip = $true
                }

                if ($FormatOutput) {
                    $EveryOtherElement["$ValueToUse"] = $Objects[$i].$NameProperty -join $Splitter
                } else {
                    $EveryOtherElement["$ValueToUse"] = $Objects[$i].$NameProperty
                }

                if ($CompareSorted) {
                    $Value1 = $Objects[0].$NameProperty | Sort-Object
                    $Value2 = $Objects[$i].$NameProperty | Sort-Object
                } else {
                    $Value1 = $Objects[0].$NameProperty
                    $Value2 = $Objects[$i].$NameProperty
                }

                if ($Value1 -is [PSCustomObject]) {
                    [ordered] @{ Status = $null; Same = @(); Add = @(); Remove = @() }
                    continue
                } elseif ($Value1 -is [System.Collections.IDictionary]) {
                    [ordered] @{ Status = $null; Same = @(); Add = @(); Remove = @() }
                    continue
                } elseif ($Value1 -is [Array] -and $Value1.Count -ne 0 -and $Value1[0] -isnot [string]) {
                    [ordered] @{ Status = $null; Same = @(); Add = @(); Remove = @() }
                    continue
                }
                if (-not $Skip) {
                    $Status = Compare-TwoArrays -FieldName $NameProperty -Object1 $Value1 -Object2 $Value2 -Replace $Replace
                } else {
                    $Status['Add'] = $Value1
                }
                if ($FormatDifferences) {
                    $EveryOtherElement["$ValueToUse-Add"] = $Status['Add'] -join $Splitter
                    $EveryOtherElement["$ValueToUse-Remove"] = $Status['Remove'] -join $Splitter
                    $EveryOtherElement["$ValueToUse-Same"] = $Status['Same'] -join $Splitter
                } else {
                    $EveryOtherElement["$ValueToUse-Add"] = $Status['Add']
                    $EveryOtherElement["$ValueToUse-Remove"] = $Status['Remove']
                    $EveryOtherElement["$ValueToUse-Same"] = $Status['Same']
                }
                $Status
            }
            if ($null -eq $IsSame.Status) {
                $EveryOtherElement['Status'] = $null
            } elseif ($IsSame.Status -notcontains $false) {
                $EveryOtherElement['Status'] = $true
            } else {
                $EveryOtherElement['Status'] = $false
            }

            if ($Summary) {
                [Array] $Collection = (0..($IsSame.Count - 1)).Where( { $IsSame[$_].Status -eq $true }, 'Split')
                if ($FormatDifferences) {
                    $EveryOtherElement['Same'] = ($Collection[0] | ForEach-Object {
                            $Count = $_ + 1
                            if ($ObjectsName[$Count]) {
                                $ObjectsName[$Count]
                            } else {
                                $Count
                            }
                        }
                    ) -join $Splitter
                    $EveryOtherElement['Different'] = ($Collection[1] | ForEach-Object {
                            $Count = $_ + 1
                            if ($ObjectsName[$Count]) {
                                $ObjectsName[$Count]
                            } else {
                                $Count
                            }
                        }
                    ) -join $Splitter
                } else {
                    $EveryOtherElement['Same'] = $Collection[0] | ForEach-Object {
                        $Count = $_ + 1
                        if ($ObjectsName[$Count]) {
                            $ObjectsName[$Count]
                        } else {
                            $Count
                        }
                    }
                    $EveryOtherElement['Different'] = $Collection[1] | ForEach-Object {
                        $Count = $_ + 1
                        if ($ObjectsName[$Count]) {
                            $ObjectsName[$Count]
                        } else {
                            $Count
                        }
                    }
                }
            }
            [PSCuStomObject] $EveryOtherElement
        }
    )
    if ($ReturnValues.Count -eq 1) {
        return , $ReturnValues
    } else {
        return $ReturnValues
    }
}
function ConvertFrom-Color { 
    <#
    .SYNOPSIS
    Converts color names or hex codes to different formats.
 
    .DESCRIPTION
    ConvertFrom-Color function converts color names or hex codes to different formats such as decimal values or System.Drawing.Color objects.
 
    .PARAMETER Color
    Specifies the color names or hex codes to convert.
 
    .PARAMETER AsDecimal
    Indicates whether to convert the color to a decimal value.
 
    .PARAMETER AsDrawingColor
    Indicates whether to convert the color to a System.Drawing.Color object.
 
    .EXAMPLE
    ConvertFrom-Color -Color Red, Blue -AsDecimal
    Converts the colors Red and Blue to decimal values.
 
    .EXAMPLE
    ConvertFrom-Color -Color "#FFA500" -AsDrawingColor
    Converts the color with hex code #FFA500 to a System.Drawing.Color object.
 
    #>

    [alias('Convert-FromColor')]
    [CmdletBinding()]
    param (
        [ValidateScript( {
                if ($($_ -in $Script:RGBColors.Keys -or $_ -match "^#([A-Fa-f0-9]{6})$" -or $_ -eq "") -eq $false) {
                    throw "The Input value is not a valid colorname nor an valid color hex code."
                } else {
                    $true 
                }
            })]
        [alias('Colors')][string[]] $Color,
        [switch] $AsDecimal,
        [switch] $AsDrawingColor
    )
    $Colors = foreach ($C in $Color) {
        $Value = $Script:RGBColors."$C"
        if ($C -match "^#([A-Fa-f0-9]{6})$") {
            $C
            continue
        }
        if ($null -eq $Value) {
            continue
        }
        $HexValue = Convert-Color -RGB $Value
        Write-Verbose "Convert-FromColor - Color Name: $C Value: $Value HexValue: $HexValue"
        if ($AsDecimal) {
            [Convert]::ToInt64($HexValue, 16)
        } elseif ($AsDrawingColor) {
            [System.Drawing.Color]::FromArgb("#$($HexValue)")
        } else {
            "#$($HexValue)"
        }
    }
    $Colors
}
function ConvertTo-FlatObject { 
    <#
    .SYNOPSIS
    Flattends a nested object into a single level object.
 
    .DESCRIPTION
    Flattends a nested object into a single level object.
 
    .PARAMETER Objects
    The object (or objects) to be flatten.
 
    .PARAMETER Separator
    The separator used between the recursive property names
 
    .PARAMETER Base
    The first index name of an embedded array:
    - 1, arrays will be 1 based: <Parent>.1, <Parent>.2, <Parent>.3, …
    - 0, arrays will be 0 based: <Parent>.0, <Parent>.1, <Parent>.2, …
    - "", the first item in an array will be unnamed and than followed with 1: <Parent>, <Parent>.1, <Parent>.2, …
 
    .PARAMETER Depth
    The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop.
 
    .PARAMETER Uncut
    The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop.
 
    .PARAMETER ExcludeProperty
    The propertys to be excluded from the output.
 
    .EXAMPLE
    $Object3 = [PSCustomObject] @{
        "Name" = "Przemyslaw Klys"
        "Age" = "30"
        "Address" = @{
            "Street" = "Kwiatowa"
            "City" = "Warszawa"
 
            "Country" = [ordered] @{
                "Name" = "Poland"
            }
            List = @(
                [PSCustomObject] @{
                    "Name" = "Adam Klys"
                    "Age" = "32"
                }
                [PSCustomObject] @{
                    "Name" = "Justyna Klys"
                    "Age" = "33"
                }
                [PSCustomObject] @{
                    "Name" = "Justyna Klys"
                    "Age" = 30
                }
                [PSCustomObject] @{
                    "Name" = "Justyna Klys"
                    "Age" = $null
                }
            )
        }
        ListTest = @(
            [PSCustomObject] @{
                "Name" = "Sława Klys"
                "Age" = "33"
            }
        )
    }
 
    $Object3 | ConvertTo-FlatObject
 
    .NOTES
    Based on https://powersnippets.com/convertto-flatobject/
    #>

    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeLine)][Object[]]$Objects,
        [String]$Separator = ".",
        [ValidateSet("", 0, 1)]$Base = 1,
        [int]$Depth = 5,
        [string[]] $ExcludeProperty,
        [Parameter(DontShow)][String[]]$Path,
        [Parameter(DontShow)][System.Collections.IDictionary] $OutputObject
    )
    Begin {
        $InputObjects = [System.Collections.Generic.List[Object]]::new()
    }
    Process {
        foreach ($O in $Objects) {
            if ($null -ne $O) {
                $InputObjects.Add($O)
            }
        }
    }
    End {
        If ($PSBoundParameters.ContainsKey("OutputObject")) {
            $Object = $InputObjects[0]
            $Iterate = [ordered] @{}
            if ($null -eq $Object) {
            } elseif ($Object.GetType().Name -in 'String', 'DateTime', 'TimeSpan', 'Version', 'Enum') {
                $Object = $Object.ToString()
            } elseif ($Depth) {
                $Depth--
                If ($Object -is [System.Collections.IDictionary]) {
                    $Iterate = $Object
                } elseif ($Object -is [Array] -or $Object -is [System.Collections.IEnumerable]) {
                    $i = $Base
                    foreach ($Item in $Object.GetEnumerator()) {
                        $NewObject = [ordered] @{}
                        If ($Item -is [System.Collections.IDictionary]) {
                            foreach ($Key in $Item.Keys) {
                                if ($Key -notin $ExcludeProperty) {
                                    $NewObject[$Key] = $Item[$Key]
                                }
                            }
                        } elseif ($Item -isnot [Array] -and $Item -isnot [System.Collections.IEnumerable]) {
                            foreach ($Prop in $Item.PSObject.Properties) {
                                if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) {
                                    $NewObject["$($Prop.Name)"] = $Item.$($Prop.Name)
                                }
                            }
                        } else {
                            $NewObject = $Item
                        }
                        $Iterate["$i"] = $NewObject
                        $i += 1
                    }
                } else {
                    foreach ($Prop in $Object.PSObject.Properties) {
                        if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) {
                            $Iterate["$($Prop.Name)"] = $Object.$($Prop.Name)
                        }
                    }
                }
            }
            If ($Iterate.Keys.Count) {
                foreach ($Key in $Iterate.Keys) {
                    if ($Key -notin $ExcludeProperty) {
                        ConvertTo-FlatObject -Objects @(, $Iterate["$Key"]) -Separator $Separator -Base $Base -Depth $Depth -Path ($Path + $Key) -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty
                    }
                }
            } else {
                $Property = $Path -Join $Separator
                if ($Property) {

                    if ($Object -is [System.Collections.IDictionary] -and $Object.Keys.Count -eq 0) {
                        $OutputObject[$Property] = $null
                    } else {
                        $OutputObject[$Property] = $Object
                    }
                }
            }
        } elseif ($InputObjects.Count -gt 0) {
            foreach ($ItemObject in $InputObjects) {
                $OutputObject = [ordered]@{}
                ConvertTo-FlatObject -Objects @(, $ItemObject) -Separator $Separator -Base $Base -Depth $Depth -Path $Path -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty
                [PSCustomObject] $OutputObject
            }
        }
    }
}
function ConvertTo-JsonLiteral { 
    <#
    .SYNOPSIS
    Converts an object to a JSON-formatted string.
 
    .DESCRIPTION
    The ConvertTo-Json cmdlet converts any object to a string in JavaScript Object Notation (JSON) format. The properties are converted to field names, the field values are converted to property values, and the methods are removed.
 
    .PARAMETER Object
    Specifies the objects to convert to JSON format. Enter a variable that contains the objects, or type a command or expression that gets the objects. You can also pipe an object to ConvertTo-JsonLiteral
 
    .PARAMETER Depth
    Specifies how many levels of contained objects are included in the JSON representation. The default value is 0.
 
    .PARAMETER AsArray
    Outputs the object in array brackets, even if the input is a single object.
 
    .PARAMETER DateTimeFormat
    Changes DateTime string format. Default "yyyy-MM-dd HH:mm:ss"
 
    .PARAMETER NumberAsString
    Provides an alternative serialization option that converts all numbers to their string representation.
 
    .PARAMETER BoolAsString
    Provides an alternative serialization option that converts all bool to their string representation.
 
    .PARAMETER PropertyName
    Uses PropertyNames provided by user (only works with Force)
 
    .PARAMETER NewLineFormat
    Provides a way to configure how new lines are converted for property names
 
    .PARAMETER NewLineFormatProperty
    Provides a way to configure how new lines are converted for values
 
    .PARAMETER PropertyName
    Allows passing property names to be used for custom objects (hashtables and alike are unaffected)
 
    .PARAMETER ArrayJoin
    Forces any array to be a string regardless of depth level
 
    .PARAMETER ArrayJoinString
    Uses defined string or char for array join. By default it uses comma with a space when used.
 
    .PARAMETER Force
    Forces using property names from first object or given thru PropertyName parameter
 
    .EXAMPLE
    Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral
 
    .EXAMPLE
    Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral -Depth 3
 
    .EXAMPLE
    Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral -NewLineFormat $NewLineFormat = @{
        NewLineCarriage = '\r\n'
        NewLine = "\n"
        Carriage = "\r"
    } -NumberAsString -BoolAsString
 
    .EXAMPLE
    Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral -NumberAsString -BoolAsString -DateTimeFormat "yyyy-MM-dd HH:mm:ss"
 
    .EXAMPLE
    # Keep in mind this advanced replace will break ConvertFrom-Json, but it's sometimes useful for projects like PSWriteHTML
    Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral -NewLineFormat $NewLineFormat = @{
        NewLineCarriage = '\r\n'
        NewLine = "\n"
        Carriage = "\r"
    } -NumberAsString -BoolAsString -AdvancedReplace @{ '.' = '\.'; '$' = '\$' }
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [alias('InputObject')][Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0, Mandatory)][Array] $Object,
        [int] $Depth,
        [switch] $AsArray,
        [string] $DateTimeFormat = "yyyy-MM-dd HH:mm:ss",
        [switch] $NumberAsString,
        [switch] $BoolAsString,
        [System.Collections.IDictionary] $NewLineFormat = @{
            NewLineCarriage = '\r\n'
            NewLine         = "\n"
            Carriage        = "\r"
        },
        [System.Collections.IDictionary] $NewLineFormatProperty = @{
            NewLineCarriage = '\r\n'
            NewLine         = "\n"
            Carriage        = "\r"
        },
        [System.Collections.IDictionary] $AdvancedReplace,
        [string] $ArrayJoinString,
        [switch] $ArrayJoin,
        [string[]]$PropertyName,
        [switch] $Force
    )
    Begin {
        $TextBuilder = [System.Text.StringBuilder]::new()
        $CountObjects = 0
        filter IsNumeric() {
            return $_ -is [byte] -or $_ -is [int16] -or $_ -is [int32] -or $_ -is [int64]  `
                -or $_ -is [sbyte] -or $_ -is [uint16] -or $_ -is [uint32] -or $_ -is [uint64] `
                -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal]
        }
        filter IsOfType() {
            return $_ -is [bool] -or $_ -is [char] -or $_ -is [datetime] -or $_ -is [string] `
                -or $_ -is [timespan] -or $_ -is [URI] `
                -or $_ -is [byte] -or $_ -is [int16] -or $_ -is [int32] -or $_ -is [int64] `
                -or $_ -is [sbyte] -or $_ -is [uint16] -or $_ -is [uint32] -or $_ -is [uint64] `
                -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal]
        }
        [int] $MaxDepth = $Depth
        [int] $InitialDepth = 0
    }
    Process {
        for ($a = 0; $a -lt $Object.Count; $a++) {
            $CountObjects++
            if ($CountObjects -gt 1) {
                $null = $TextBuilder.Append(',')
            }
            if ($Object[$a] -is [System.Collections.IDictionary]) {

                $null = $TextBuilder.AppendLine("{")
                for ($i = 0; $i -lt ($Object[$a].Keys).Count; $i++) {
                    $Property = ([string[]]$Object[$a].Keys)[$i] 
                    $DisplayProperty = $Property.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                    $null = $TextBuilder.Append("`"$DisplayProperty`":")
                    $Value = ConvertTo-StringByType -Value $Object[$a][$Property] -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $InitialDepth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -NewLineFormat $NewLineFormat -NewLineFormatProperty $NewLineFormatProperty -Force:$Force -ArrayJoin:$ArrayJoin -ArrayJoinString $ArrayJoinString -AdvancedReplace $AdvancedReplace
                    $null = $TextBuilder.Append("$Value")
                    if ($i -ne ($Object[$a].Keys).Count - 1) {
                        $null = $TextBuilder.AppendLine(',')
                    }
                }
                $null = $TextBuilder.Append("}")
            } elseif ($Object[$a] | IsOfType) {
                $Value = ConvertTo-StringByType -Value $Object[$a] -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $InitialDepth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -NewLineFormat $NewLineFormat -NewLineFormatProperty $NewLineFormatProperty -Force:$Force -ArrayJoin:$ArrayJoin -ArrayJoinString $ArrayJoinString -AdvancedReplace $AdvancedReplace
                $null = $TextBuilder.Append($Value)
            } else {
                $null = $TextBuilder.AppendLine("{")
                if ($Force -and -not $PropertyName) {
                    $PropertyName = $Object[0].PSObject.Properties.Name
                } elseif ($Force -and $PropertyName) {
                } else {
                    $PropertyName = $Object[$a].PSObject.Properties.Name
                }
                $PropertyCount = 0
                foreach ($Property in $PropertyName) {
                    $PropertyCount++
                    $DisplayProperty = $Property.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                    $null = $TextBuilder.Append("`"$DisplayProperty`":")
                    $Value = ConvertTo-StringByType -Value $Object[$a].$Property -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $InitialDepth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -NewLineFormat $NewLineFormat -NewLineFormatProperty $NewLineFormatProperty -Force:$Force -ArrayJoin:$ArrayJoin -ArrayJoinString $ArrayJoinString -AdvancedReplace $AdvancedReplace

                    $null = $TextBuilder.Append("$Value")
                    if ($PropertyCount -ne $PropertyName.Count) {
                        $null = $TextBuilder.AppendLine(',')
                    }
                }
                $null = $TextBuilder.Append("}")
            }
            $InitialDepth = 0
        }
    }
    End {
        if ($CountObjects -gt 1 -or $AsArray) {
            "[$($TextBuilder.ToString())]"
        } else {
            $TextBuilder.ToString()
        }
    }
}
function ConvertTo-PrettyObject { 
    <#
    .SYNOPSIS
    Command to help with converting standard objects that could be nested objects into single level PSCustomObject
 
    .DESCRIPTION
    Command to help with converting standard objects that could be nested objects into single level PSCustomObject
    This is a help command for PSWriteHTML module and probably PSWriteOffice module to create tables from objects
    and make sure those tables are not nested and can be easily converted to HTML or Office tables without having to manually flatten them
 
    .PARAMETER Object
     Specifies the objects to convert to pretty format. Enter a variable that contains the objects, or type a command or expression that gets the objects. You can also pipe an object to ConvertTo-JsonLiteral
 
    .PARAMETER DateTimeFormat
    Changes DateTime string format. Default "yyyy-MM-dd HH:mm:ss"
 
    .PARAMETER NumberAsString
    Provides an alternative serialization option that converts all numbers to their string representation.
 
    .PARAMETER BoolAsString
    Provides an alternative serialization option that converts all bool to their string representation.
 
    .PARAMETER PropertyName
    Uses PropertyNames provided by user (only works with Force)
 
    .PARAMETER NewLineFormat
    Provides a way to configure how new lines are converted for property names
 
    .PARAMETER NewLineFormatProperty
    Provides a way to configure how new lines are converted for values
 
    .PARAMETER PropertyName
    Allows passing property names to be used for custom objects (hashtables and alike are unaffected)
 
    .PARAMETER ArrayJoin
    Forces any array to be a string regardless of depth level
 
    .PARAMETER ArrayJoinString
    Uses defined string or char for array join. By default it uses comma with a space when used.
 
    .PARAMETER Force
    Forces using property names from first object or given thru PropertyName parameter
 
    .EXAMPLE
    $Test1 = [PSCustomObject] @{
        Number = 1
        Number2 = 2.2
        Bool = $false
        Array = @(
            'C:\Users\1Password.exe'
            "C:\Users\Ooops.exe"
            "\\EvoWin\c$\Users\przemyslaw klys\AppData\Local\1password\This is other\7\1Password.exe"
            "\\EvoWin\c$\Users\przemyslaw.klys\AppData\Local\1password\This is other\7\1Password.exe"
        )
        EmptyArray = @()
        EmptyList = [System.Collections.Generic.List[string]]::new()
        HashTable = @{
            NumberAgain = 2
            OrderedDictionary = [ordered] @{
                String = 'test'
                HashTable = @{
                    StringAgain = "oops"
                }
            }
            Array = @(
                'C:\Users\1Password.exe'
                "C:\Users\Ooops.exe"
                "\\EvoWin\c$\Users\przemyslaw klys\AppData\Local\1password\This is other\7\1Password.exe"
                "\\EvoWin\c$\Users\przemyslaw.klys\AppData\Local\1password\This is other\7\1Password.exe"
            )
        }
        DateTime = Get-Date
    }
 
    $Test1 | ConvertTo-PrettyObject -ArrayJoinString "," -ArrayJoin | ConvertTo-Json | ConvertFrom-Json
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [alias('InputObject')][Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0, Mandatory)][Array] $Object,
        [int] $Depth,
        [switch] $AsArray,
        [string] $DateTimeFormat = "yyyy-MM-dd HH:mm:ss",
        [switch] $NumberAsString,
        [switch] $BoolAsString,
        [System.Collections.IDictionary] $NewLineFormat = @{
            NewLineCarriage = '\r\n'
            NewLine         = "\n"
            Carriage        = "\r"
        },
        [System.Collections.IDictionary] $NewLineFormatProperty = @{
            NewLineCarriage = '\r\n'
            NewLine         = "\n"
            Carriage        = "\r"
        },
        [System.Collections.IDictionary] $AdvancedReplace,
        [string] $ArrayJoinString,
        [switch] $ArrayJoin,
        [string[]]$PropertyName,
        [switch] $Force
    )
    Begin {
        filter IsNumeric() {
            return $_ -is [byte] -or $_ -is [int16] -or $_ -is [int32] -or $_ -is [int64]  `
                -or $_ -is [sbyte] -or $_ -is [uint16] -or $_ -is [uint32] -or $_ -is [uint64] `
                -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal]
        }
        filter IsOfType() {
            return $_ -is [bool] -or $_ -is [char] -or $_ -is [datetime] -or $_ -is [string] `
                -or $_ -is [timespan] -or $_ -is [URI] `
                -or $_ -is [byte] -or $_ -is [int16] -or $_ -is [int32] -or $_ -is [int64] `
                -or $_ -is [sbyte] -or $_ -is [uint16] -or $_ -is [uint32] -or $_ -is [uint64] `
                -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal]
        }
    }
    Process {
        for ($a = 0; $a -lt $Object.Count; $a++) {
            $NewObject = [ordered] @{}
            if ($Object[$a] -is [System.Collections.IDictionary]) {

                for ($i = 0; $i -lt ($Object[$a].Keys).Count; $i++) {
                    $Property = ([string[]]$Object[$a].Keys)[$i]
                    $DisplayProperty = $Property.Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                    $Value = $Object[$a].$Property

                    if ($null -eq $Value) {
                        $NewObject[$DisplayProperty] = ""
                    } elseif ($Value -is [string]) {
                        foreach ($Key in $AdvancedReplace.Keys) {
                            $Value = $Value.Replace($Key, $AdvancedReplace[$Key])
                        }
                        $NewObject[$DisplayProperty] = $Value.Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)
                    } elseif ($Value -is [DateTime]) {
                        $NewObject[$DisplayProperty] = $Object[$a].$Property.ToString($DateTimeFormat)
                    } elseif ($Value -is [bool]) {
                        if ($BoolAsString) {
                            $NewObject[$DisplayProperty] = "$Value"
                        } else {
                            $NewObject[$DisplayProperty] = $Value
                        }
                    } elseif ($Value -is [System.Collections.IDictionary]) {

                        $NewObject[$DisplayProperty] = "$Value"
                    } elseif ($Value -is [System.Collections.IList] -or $Value -is [System.Collections.ReadOnlyCollectionBase]) {
                        if ($ArrayJoin) {
                            $Value = $Value -join $ArrayJoinString
                            $Value = "$Value".Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)
                            $NewObject[$DisplayProperty] = "$Value"
                        } else {

                            $Value = "$Value".Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)
                            $NewObject[$DisplayProperty] = "$Value"
                        }
                    } elseif ($Value -is [System.Enum]) {
                        $NewObject[$DisplayProperty] = ($Value).ToString()
                    } elseif (($Value | IsNumeric) -eq $true) {
                        $Value = $($Value).ToString().Replace(',', '.')
                        if ($NumberAsString) {
                            $NewObject[$DisplayProperty] = "$Value"
                        } else {
                            $NewObject[$DisplayProperty] = $Value
                        }
                    } elseif ($Value -is [PSObject]) {

                        $NewObject[$DisplayProperty] = "$Value"
                    } else {
                        $Value = $Value.ToString().Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)
                        $NewObject[$DisplayProperty] = "$Value"
                    }
                }
                [PSCustomObject] $NewObject
            } elseif ($Object[$a] | IsOfType) {

                $Object[$a]
            } else {
                if ($Force -and -not $PropertyName) {
                    $PropertyName = $Object[0].PSObject.Properties.Name
                } elseif ($Force -and $PropertyName) {
                } else {
                    $PropertyName = $Object[$a].PSObject.Properties.Name
                }
                foreach ($Property in $PropertyName) {
                    $DisplayProperty = $Property.Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                    $Value = $Object[$a].$Property
                    if ($null -eq $Value) {
                        $NewObject[$DisplayProperty] = ""
                    } elseif ($Value -is [string]) {
                        foreach ($Key in $AdvancedReplace.Keys) {
                            $Value = $Value.Replace($Key, $AdvancedReplace[$Key])
                        }
                        $NewObject[$DisplayProperty] = $Value.Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)
                    } elseif ($Value -is [DateTime]) {
                        $NewObject[$DisplayProperty] = $Object[$a].$Property.ToString($DateTimeFormat)
                    } elseif ($Value -is [bool]) {
                        if ($BoolAsString) {
                            $NewObject[$DisplayProperty] = "$Value"
                        } else {
                            $NewObject[$DisplayProperty] = $Value
                        }
                    } elseif ($Value -is [System.Collections.IDictionary]) {

                        $NewObject[$DisplayProperty] = "$Value"
                    } elseif ($Value -is [System.Collections.IList] -or $Value -is [System.Collections.ReadOnlyCollectionBase]) {
                        if ($ArrayJoin) {
                            $Value = $Value -join $ArrayJoinString
                            $Value = "$Value".Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)
                            $NewObject[$DisplayProperty] = "$Value"
                        } else {

                            $Value = "$Value".Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)
                            $NewObject[$DisplayProperty] = "$Value"
                        }
                    } elseif ($Value -is [System.Enum]) {
                        $NewObject[$DisplayProperty] = ($Value).ToString()
                    } elseif (($Value | IsNumeric) -eq $true) {

                        if ($NumberAsString) {
                            $NewObject[$DisplayProperty] = "$Value"
                        } else {
                            $NewObject[$DisplayProperty] = $Value
                        }
                    } elseif ($Value -is [PSObject]) {

                        $NewObject[$DisplayProperty] = "$Value"
                    } else {
                        $Value = $Value.ToString().Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)
                        $NewObject[$DisplayProperty] = "$Value"
                    }
                }
                [PSCustomObject] $NewObject
            }
        }
    }
}
function Copy-Dictionary { 
    <#
    .SYNOPSIS
    Copies dictionary/hashtable
 
    .DESCRIPTION
    Copies dictionary uusing PS Serializer. Replaces usage of BinnaryFormatter due to no support in PS 7.4
 
    .PARAMETER Dictionary
    Dictionary to copy
 
    .EXAMPLE
    $Test = [ordered] @{
        Test = 'Test'
        Test1 = @{
            Test2 = 'Test2'
            Test3 = @{
                Test4 = 'Test4'
            }
        }
        Test2 = @(
            "1", "2", "3"
        )
        Test3 = [PSCustomObject] @{
            Test4 = 'Test4'
            Test5 = 'Test5'
        }
    }
 
    $New1 = Copy-Dictionary -Dictionary $Test
    $New1
 
    .NOTES
 
    #>

    [alias('Copy-Hashtable', 'Copy-OrderedHashtable')]
    [cmdletbinding()]
    param(
        [System.Collections.IDictionary] $Dictionary
    )
    $clone = [System.Management.Automation.PSSerializer]::Serialize($Dictionary, [int32]::MaxValue)
    return [System.Management.Automation.PSSerializer]::Deserialize($clone)
}
function Format-TransposeTable { 
    <#
    .SYNOPSIS
    Transposes (pivot) a table of objects
 
    .DESCRIPTION
    Transposes (pivot) a table of objects
 
    .PARAMETER AllObjects
    List of objects to transpose
 
    .PARAMETER Sort
    Legacy parameter to sort the output
 
    .PARAMETER Legacy
    Allows to transpose the table in a legacy way
 
    .PARAMETER Property
    Provides a property to name the column based on the property value
 
    .EXAMPLE
    $T = [PSCustomObject] @{
        Name = "Server 1"
        Test = 1
        Test2 = 7
        Ole = 'bole'
        Trolle = 'A'
        Alle = 'sd'
    }
    $T1 = [PSCustomObject] @{
        Name = "Server 2"
        Test = 2
        Test2 = 3
        Ole = '1bole'
        Trolle = 'A'
        Alle = 'sd'
    }
 
    Format-TransposeTable -Object @($T, $T1) -Property "Name" | Format-Table
 
    .EXAMPLE
    $T2 = [ordered] @{
        Name = "Server 1"
        Test = 1
        Test2 = 7
        Ole = 'bole'
        Trolle = 'A'
        Alle = 'sd'
    }
    $T3 = [ordered] @{
        Name = "Server 2"
        Test = 2
        Test2 = 3
        Ole = '1bole'
        Trolle = 'A'
        Alle = 'sd'
    }
 
    $Test = Format-TransposeTable -Object @($T2, $T3)
    $Test | Format-Table
 
    .NOTES
    General notes
    #>

    [CmdletBinding(DefaultParameterSetName = 'Pivot')]
    param (
        [Parameter(ParameterSetName = 'Legacy')]
        [Parameter(ParameterSetName = 'Pivot')]
        [Alias("Object")]
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)][Array] $AllObjects,

        [Parameter(ParameterSetName = 'Legacy')]
        [ValidateSet("ASC", "DESC", "NONE")][String] $Sort = 'NONE',

        [Parameter(ParameterSetName = 'Legacy')]
        [switch] $Legacy,

        [Parameter(ParameterSetName = 'Pivot')]
        [string] $Property,

        [Parameter(ParameterSetName = 'Pivot')]
        [string] $Name = "Object "
    )
    begin {
        $Object = [System.Collections.Generic.List[object]]::new()
    }
    process {
        foreach ($O in $AllObjects) {
            $Object.Add($O)
        }
    }
    end {
        if (-not $Legacy) {
            if ($Object[0] -is [System.Collections.IDictionary]) {

                $ListHeader = [System.Collections.Generic.List[string]]::new()
                $ListHeader.Add('Name')
                if ($Property) {
                    foreach ($myObject in $Object) {
                        $ListHeader.Add($myObject.$Property)
                    }
                } else {
                    for ($i = 0; $i -lt $Object.Count; $i++) {
                        $ListHeader.Add("$($Name)$i")
                    }
                }
                $CountOfProperties = $Object[0].GetEnumerator().Name.Count
                [Array] $ObjectsList = for ($i = 0; $i -lt $CountOfProperties; $i++) {
                    $TranslatedObject = [ordered] @{
                        'Name' = $Object[0].GetEnumerator().Name[$i]
                    }
                    foreach ($Header in $ListHeader) {
                        if ($Header -ne 'Name') {
                            $TranslatedObject[$Header] = ''
                        }
                    }
                    $TranslatedObject
                }
                for ($i = 0; $i -lt $ObjectsList.Count; $i++) {
                    for ($j = 0; $j -lt $Object.Count; $j++) {
                        $NameOfProperty = $ObjectsList[$i].Name
                        $ObjectsList[$i][$j + 1] = $Object[$j].$NameOfProperty
                    }
                    [PSCustomObject] $ObjectsList[$i]
                }
            } else {

                $ListHeader = [System.Collections.Generic.List[string]]::new()
                $ListHeader.Add('Name')
                if ($Property) {
                    foreach ($myObject in $Object) {
                        $ListHeader.Add($myObject.$Property)
                    }
                } else {
                    if ($AllObjects.Count -eq 1) {
                        $ListHeader.Add("$($Name)")
                    } else {
                        for ($i = 0; $i -lt $Object.Count; $i++) {
                            $ListHeader.Add("$($Name)$i")
                        }
                    }
                }
                $CountOfProperties = $Object[0].PSObject.Properties.Name.Count
                [Array] $ObjectsList = for ($i = 0; $i -lt $CountOfProperties; $i++) {
                    $TranslatedObject = [ordered] @{
                        'Name' = $Object[0].PSObject.Properties.Name[$i]
                    }
                    foreach ($Header in $ListHeader) {
                        if ($Header -ne 'Name') {
                            $TranslatedObject[$Header] = ''
                        }
                    }
                    $TranslatedObject
                }
                for ($i = 0; $i -lt $ObjectsList.Count; $i++) {
                    for ($j = 0; $j -lt $Object.Count; $j++) {
                        $NameOfProperty = $ObjectsList[$i].Name
                        $ObjectsList[$i][$j + 1] = $Object[$j].$NameOfProperty
                    }
                    [PSCustomObject] $ObjectsList[$i]
                }
            }
        } else {
            foreach ($myObject in $Object) {
                if ($myObject -is [System.Collections.IDictionary]) {
                    if ($Sort -eq 'ASC') {
                        [PSCustomObject] $myObject.GetEnumerator() | Sort-Object -Property Name -Descending:$false
                    } elseif ($Sort -eq 'DESC') {
                        [PSCustomObject] $myObject.GetEnumerator() | Sort-Object -Property Name -Descending:$true
                    } else {
                        [PSCustomObject] $myObject
                    }
                } else {
                    $Output = [ordered] @{ }
                    if ($Sort -eq 'ASC') {
                        $myObject.PSObject.Properties | Sort-Object -Property Name -Descending:$false | ForEach-Object {
                            $Output["$($_.Name)"] = $_.Value
                        }
                    } elseif ($Sort -eq 'DESC') {
                        $myObject.PSObject.Properties | Sort-Object -Property Name -Descending:$true | ForEach-Object {
                            $Output["$($_.Name)"] = $_.Value
                        }
                    } else {
                        $myObject.PSObject.Properties | ForEach-Object {
                            $Output["$($_.Name)"] = $_.Value
                        }
                    }
                    $Output
                }
            }
        }
    }
}
function Get-RandomStringName { 
    <#
    .SYNOPSIS
    Generates a random string of specified length with various options.
 
    .DESCRIPTION
    This function generates a random string of specified length with options to convert the case and include only letters.
 
    .PARAMETER Size
    The length of the random string to generate. Default is 31.
 
    .PARAMETER ToLower
    Convert the generated string to lowercase.
 
    .PARAMETER ToUpper
    Convert the generated string to uppercase.
 
    .PARAMETER LettersOnly
    Generate a random string with only letters.
 
    .EXAMPLE
    Get-RandomStringName -Size 10
    Generates a random string of length 10.
 
    .EXAMPLE
    Get-RandomStringName -Size 8 -ToLower
    Generates a random string of length 8 and converts it to lowercase.
 
    .EXAMPLE
    Get-RandomStringName -Size 12 -ToUpper
    Generates a random string of length 12 and converts it to uppercase.
 
    .EXAMPLE
    Get-RandomStringName -Size 15 -LettersOnly
    Generates a random string of length 15 with only letters.
 
    #>

    [cmdletbinding()]
    param(
        [int] $Size = 31,
        [switch] $ToLower,
        [switch] $ToUpper,
        [switch] $LettersOnly
    )
    [string] $MyValue = @(
        if ($LettersOnly) {
            ( -join ((1..$Size) | ForEach-Object { (65..90) + (97..122) | Get-Random } | ForEach-Object { [char]$_ }))
        } else {
            ( -join ((48..57) + (97..122) | Get-Random -Count $Size | ForEach-Object { [char]$_ }))
        }
    )
    if ($ToLower) {
        return $MyValue.ToLower()
    }
    if ($ToUpper) {
        return $MyValue.ToUpper()
    }
    return $MyValue
}
function Remove-EmptyValue { 
    <#
    .SYNOPSIS
    Removes empty values from a hashtable recursively.
 
    .DESCRIPTION
    This function removes empty values from a given hashtable. It can be used to clean up a hashtable by removing keys with null, empty string, empty array, or empty dictionary values. The function supports recursive removal of empty values.
 
    .PARAMETER Hashtable
    The hashtable from which empty values will be removed.
 
    .PARAMETER ExcludeParameter
    An array of keys to exclude from the removal process.
 
    .PARAMETER Recursive
    Indicates whether to recursively remove empty values from nested hashtables.
 
    .PARAMETER Rerun
    Specifies the number of times to rerun the removal process recursively.
 
    .PARAMETER DoNotRemoveNull
    If specified, null values will not be removed.
 
    .PARAMETER DoNotRemoveEmpty
    If specified, empty string values will not be removed.
 
    .PARAMETER DoNotRemoveEmptyArray
    If specified, empty array values will not be removed.
 
    .PARAMETER DoNotRemoveEmptyDictionary
    If specified, empty dictionary values will not be removed.
 
    .EXAMPLE
    $hashtable = @{
        'Key1' = '';
        'Key2' = $null;
        'Key3' = @();
        'Key4' = @{}
    }
    Remove-EmptyValue -Hashtable $hashtable -Recursive
 
    Description
    -----------
    This example removes empty values from the $hashtable recursively.
 
    #>

    [alias('Remove-EmptyValues')]
    [CmdletBinding()]
    param(
        [alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable,
        [string[]] $ExcludeParameter,
        [switch] $Recursive,
        [int] $Rerun,
        [switch] $DoNotRemoveNull,
        [switch] $DoNotRemoveEmpty,
        [switch] $DoNotRemoveEmptyArray,
        [switch] $DoNotRemoveEmptyDictionary
    )
    foreach ($Key in [string[]] $Hashtable.Keys) {
        if ($Key -notin $ExcludeParameter) {
            if ($Recursive) {
                if ($Hashtable[$Key] -is [System.Collections.IDictionary]) {
                    if ($Hashtable[$Key].Count -eq 0) {
                        if (-not $DoNotRemoveEmptyDictionary) {
                            $Hashtable.Remove($Key)
                        }
                    } else {
                        Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive
                    }
                } else {
                    if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) {
                        $Hashtable.Remove($Key)
                    } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') {
                        $Hashtable.Remove($Key)
                    } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) {
                        $Hashtable.Remove($Key)
                    }
                }
            } else {
                if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) {
                    $Hashtable.Remove($Key)
                } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') {
                    $Hashtable.Remove($Key)
                } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) {
                    $Hashtable.Remove($Key)
                }
            }
        }
    }
    if ($Rerun) {
        for ($i = 0; $i -lt $Rerun; $i++) {
            Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive
        }
    }
}
function Select-Properties { 
    <#
    .SYNOPSIS
    Allows for easy selecting property names from one or multiple objects
 
    .DESCRIPTION
    Allows for easy selecting property names from one or multiple objects. This is especially useful with using AllProperties parameter where we want to make sure to get all properties from all objects.
 
    .PARAMETER Objects
    One or more objects
 
    .PARAMETER Property
    Properties to include
 
    .PARAMETER ExcludeProperty
    Properties to exclude
 
    .PARAMETER AllProperties
    All unique properties from all objects
 
    .PARAMETER PropertyNameReplacement
    Default property name when object has no properties
 
    .EXAMPLE
    $Object1 = [PSCustomobject] @{
        Name1 = '1'
        Name2 = '3'
        Name3 = '5'
    }
    $Object2 = [PSCustomobject] @{
        Name4 = '2'
        Name5 = '6'
        Name6 = '7'
    }
 
    Select-Properties -Objects $Object1, $Object2 -AllProperties
 
    #OR:
 
    $Object1, $Object2 | Select-Properties -AllProperties -ExcludeProperty Name6 -Property Name3
 
    .EXAMPLE
    $Object3 = [Ordered] @{
        Name1 = '1'
        Name2 = '3'
        Name3 = '5'
    }
    $Object4 = [Ordered] @{
        Name4 = '2'
        Name5 = '6'
        Name6 = '7'
    }
 
    Select-Properties -Objects $Object3, $Object4 -AllProperties
 
    $Object3, $Object4 | Select-Properties -AllProperties
 
    .NOTES
    General notes
    #>

    [CmdLetBinding()]
    param(
        [Array][Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] $Objects,
        [string[]] $Property,
        [string[]] $ExcludeProperty,
        [switch] $AllProperties,
        [string] $PropertyNameReplacement = '*'
    )
    Begin {
        function Select-Unique {
            [CmdLetBinding()]
            param(
                [System.Collections.IList] $Object
            )
            [Array] $CleanedList = foreach ($O in $Object) {
                if ($null -ne $O) {
                    $O
                }
            }
            $New = $CleanedList.ToLower() | Select-Object -Unique
            $Selected = foreach ($_ in $New) {
                $Index = $Object.ToLower().IndexOf($_)
                if ($Index -ne -1) {
                    $Object[$Index]
                }
            }
            $Selected
        }
        $ObjectsList = [System.Collections.Generic.List[Object]]::new()
    }
    Process {
        foreach ($Object in $Objects) {
            $ObjectsList.Add($Object)
        }
    }
    End {
        if ($ObjectsList.Count -eq 0) {
            Write-Warning 'Select-Properties - Unable to process. Objects count equals 0.'
            return
        }
        if ($ObjectsList[0] -is [System.Collections.IDictionary]) {
            if ($AllProperties) {
                [Array] $All = foreach ($_ in $ObjectsList) {
                    $_.Keys
                }

                $FirstObjectProperties = Select-Unique -Object $All
            } else {
                $FirstObjectProperties = $ObjectsList[0].Keys
            }
            if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) {

                $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) {
                    if ($Property -contains $_ -and $ExcludeProperty -notcontains $_) {
                        $_
                        continue
                    }
                }
            } elseif ($Property.Count -gt 0) {

                $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) {
                    if ($Property -contains $_) {
                        $_
                        continue
                    }
                }
            } elseif ($ExcludeProperty.Count -gt 0) {

                $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) {
                    if ($ExcludeProperty -notcontains $_) {
                        $_
                        continue
                    }
                }
            }
        } elseif ($ObjectsList[0].GetType().Name -match 'bool|byte|char|datetime|decimal|double|ExcelHyperLink|float|int|long|sbyte|short|string|timespan|uint|ulong|URI|ushort') {
            $FirstObjectProperties = $PropertyNameReplacement
        } else {
            if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) {
                $ObjectsList = $ObjectsList | Select-Object -Property $Property -ExcludeProperty $ExcludeProperty
            } elseif ($Property.Count -gt 0) {
                $ObjectsList = $ObjectsList | Select-Object -Property $Property 
            } elseif ($ExcludeProperty.Count -gt 0) {
                $ObjectsList = $ObjectsList | Select-Object -Property '*' -ExcludeProperty $ExcludeProperty
            }
            if ($AllProperties) {
                [Array] $All = foreach ($_ in $ObjectsList) {
                    $ListProperties = $_.PSObject.Properties.Name
                    if ($null -ne $ListProperties) {
                        $ListProperties
                    }
                }

                $FirstObjectProperties = Select-Unique -Object $All
            } else {
                $FirstObjectProperties = $ObjectsList[0].PSObject.Properties.Name
            }
        }
        $FirstObjectProperties
    }
}
function Send-Email { 
    <#
    .SYNOPSIS
    Sends an email with specified parameters.
 
    .DESCRIPTION
    This function sends an email using the provided parameters. It supports sending emails with attachments and inline attachments.
 
    .PARAMETER Email
    Specifies the email parameters including sender, recipients, server settings, and encoding.
 
    .PARAMETER Body
    Specifies the body of the email.
 
    .PARAMETER Attachment
    Specifies an array of file paths to be attached to the email.
 
    .PARAMETER InlineAttachments
    Specifies a dictionary of inline attachments to be included in the email.
 
    .PARAMETER Subject
    Specifies the subject of the email.
 
    .PARAMETER To
    Specifies an array of email addresses to send the email to.
 
    .PARAMETER Logger
    Specifies a custom object for logging purposes.
 
    .EXAMPLE
    Send-Email -Email $EmailParams -Body "Hello, this is a test email" -Attachment "C:\Files\attachment.txt" -Subject "Test Email" -To "recipient@example.com" -Logger $Logger
 
    .EXAMPLE
    $EmailParams = @{
        From = "sender@example.com"
        To = "recipient@example.com"
        Subject = "Test Email"
        Body = "Hello, this is a test email"
        Server = "smtp.example.com"
        Port = 587
        Password = "password"
        Encoding = "UTF8"
    }
    Send-Email -Email $EmailParams -Attachment "C:\Files\attachment.txt" -To "recipient@example.com" -Logger $Logger
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [alias('EmailParameters')][System.Collections.IDictionary] $Email,
        [string] $Body,
        [string[]] $Attachment,
        [System.Collections.IDictionary] $InlineAttachments,
        [string] $Subject,
        [string[]] $To,
        [PSCustomObject] $Logger
    )
    try {

        if ($Email.EmailTo) {
            $EmailParameters = $Email.Clone()
            $EmailParameters.EmailEncoding = $EmailParameters.EmailEncoding -replace "-", ''
            $EmailParameters.EmailEncodingSubject = $EmailParameters.EmailEncodingSubject -replace "-", ''
            $EmailParameters.EmailEncodingBody = $EmailParameters.EmailEncodingSubject -replace "-", ''
            $EmailParameters.EmailEncodingAlternateView = $EmailParameters.EmailEncodingAlternateView -replace "-", ''
        } else {
            $EmailParameters = @{
                EmailFrom                   = $Email.From
                EmailTo                     = $Email.To
                EmailCC                     = $Email.CC
                EmailBCC                    = $Email.BCC
                EmailReplyTo                = $Email.ReplyTo
                EmailServer                 = $Email.Server
                EmailServerPassword         = $Email.Password
                EmailServerPasswordAsSecure = $Email.PasswordAsSecure
                EmailServerPasswordFromFile = $Email.PasswordFromFile
                EmailServerPort             = $Email.Port
                EmailServerLogin            = $Email.Login
                EmailServerEnableSSL        = $Email.EnableSsl
                EmailEncoding               = $Email.Encoding -replace "-", ''
                EmailEncodingSubject        = $Email.EncodingSubject -replace "-", ''
                EmailEncodingBody           = $Email.EncodingBody -replace "-", ''
                EmailEncodingAlternateView  = $Email.EncodingAlternateView -replace "-", ''
                EmailSubject                = $Email.Subject
                EmailPriority               = $Email.Priority
                EmailDeliveryNotifications  = $Email.DeliveryNotifications
                EmailUseDefaultCredentials  = $Email.UseDefaultCredentials
            }
        }
    } catch {
        return @{
            Status = $False
            Error  = $($_.Exception.Message)
            SentTo = ''
        }
    }
    $SmtpClient = [System.Net.Mail.SmtpClient]::new()
    if ($EmailParameters.EmailServer) {
        $SmtpClient.Host = $EmailParameters.EmailServer
    } else {
        return @{
            Status = $False
            Error  = "Email Server Host is not set."
            SentTo = ''
        }
    }

    if ($EmailParameters.EmailServerPort) {
        $SmtpClient.Port = $EmailParameters.EmailServerPort
    } else {
        return @{
            Status = $False
            Error  = "Email Server Port is not set."
            SentTo = ''
        }
    }

    if ($EmailParameters.EmailServerLogin) {

        $Credentials = Request-Credentials -UserName $EmailParameters.EmailServerLogin `
            -Password $EmailParameters.EmailServerPassword `
            -AsSecure:$EmailParameters.EmailServerPasswordAsSecure `
            -FromFile:$EmailParameters.EmailServerPasswordFromFile `
            -NetworkCredentials 
        $SmtpClient.Credentials = $Credentials
    }
    if ($EmailParameters.EmailServerEnableSSL) {
        $SmtpClient.EnableSsl = $EmailParameters.EmailServerEnableSSL
    }
    $MailMessage = [System.Net.Mail.MailMessage]::new()
    $MailMessage.From = $EmailParameters.EmailFrom
    if ($To) {
        foreach ($T in $To) {
            $MailMessage.To.add($($T)) 
        }
    } else {
        if ($EmailParameters.Emailto) {
            foreach ($To in $EmailParameters.Emailto) {
                $MailMessage.To.add($($To)) 
            }
        }
    }
    if ($EmailParameters.EmailCC) {
        foreach ($CC in $EmailParameters.EmailCC) {
            $MailMessage.CC.add($($CC)) 
        }
    }
    if ($EmailParameters.EmailBCC) {
        foreach ($BCC in $EmailParameters.EmailBCC) {
            $MailMessage.BCC.add($($BCC)) 
        }
    }
    if ($EmailParameters.EmailReplyTo) {
        $MailMessage.ReplyTo = $EmailParameters.EmailReplyTo
    }
    $MailMessage.IsBodyHtml = $true
    if ($Subject -eq '') {
        $MailMessage.Subject = $EmailParameters.EmailSubject
    } else {
        $MailMessage.Subject = $Subject
    }

    $MailMessage.Priority = [System.Net.Mail.MailPriority]::$($EmailParameters.EmailPriority)

    if ($EmailParameters.EmailEncodingSubject) {
        $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncodingSubject)
    } elseif ($EmailParameters.EmailEncoding) {
        $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding)
    }
    if ($EmailParameters.EmailEncodingBody) {
        $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncodingBody)
    } elseif ($EmailParameters.EmailEncoding) {
        $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding)
    }

    if ($EmailParameters.EmailUseDefaultCredentials) {
        $SmtpClient.UseDefaultCredentials = $EmailParameters.EmailUseDefaultCredentials
    }
    if ($EmailParameters.EmailDeliveryNotifications) {
        $MailMessage.DeliveryNotificationOptions = $EmailParameters.EmailDeliveryNotifications
    }

    if ($PSBoundParameters.ContainsKey('InlineAttachments')) {

        if ($EmailParameters.EmailEncodingAlternateView) {
            $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, [System.Text.Encoding]::$($EmailParameters.EmailEncodingAlternateView) , 'text/html' )
        } else {
            $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, [System.Text.Encoding]::UTF8, 'text/html' )
        }

        $MailMessage.AlternateViews.Add($BodyPart)
        foreach ($Entry in $InlineAttachments.GetEnumerator()) {
            try {
                $FilePath = $Entry.Value
                Write-Verbose $FilePath
                if ($Entry.Value.StartsWith('http', [System.StringComparison]::CurrentCultureIgnoreCase)) {
                    $FileName = $Entry.Value.Substring($Entry.Value.LastIndexOf("/") + 1)
                    $FilePath = Join-Path $env:temp $FileName
                    Invoke-WebRequest -Uri $Entry.Value -OutFile $FilePath
                }
                $ContentType = Get-MimeType -FileName $FilePath
                $InAttachment = [Net.Mail.LinkedResource]::new($FilePath, $ContentType )
                $InAttachment.ContentId = $Entry.Key
                $BodyPart.LinkedResources.Add( $InAttachment )
            } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Error "Error inlining attachments: $ErrorMessage"
            }
        }
    } else {
        $MailMessage.Body = $Body
    }

    if ($PSBoundParameters.ContainsKey('Attachment')) {
        foreach ($Attach in $Attachment) {
            if (Test-Path -LiteralPath $Attach) {
                try {
                    $File = [Net.Mail.Attachment]::new($Attach)

                    $MailMessage.Attachments.Add($File)
                } catch {

                    $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                    if ($Logger) {
                        $Logger.AddErrorRecord("Error attaching file $Attach`: $ErrorMessage")
                    } else {
                        Write-Error "Error attaching file $Attach`: $ErrorMessage"
                    }
                }
            }
        }
    }

    try {
        $MailSentTo = "$($MailMessage.To) $($MailMessage.CC) $($MailMessage.BCC)".Trim()
        if ($pscmdlet.ShouldProcess("$MailSentTo", "Send-Email")) {
            $SmtpClient.Send($MailMessage)

            $MailMessage.Dispose();
            return [PSCustomObject] @{
                Status = $True
                Error  = ""
                SentTo = $MailSentTo
            }
        } else {
            return [PSCustomObject] @{
                Status = $False
                Error  = 'Email not sent (WhatIf)'
                SentTo = $MailSentTo
            }
        }
    } catch {
        $MailMessage.Dispose();
        return [PSCustomObject] @{
            Status = $False
            Error  = $($_.Exception.Message)
            SentTo = ""
        }
    }
}
function Stop-TimeLog { 
    <#
    .SYNOPSIS
    Stops the stopwatch and returns the elapsed time in a specified format.
 
    .DESCRIPTION
    The Stop-TimeLog function stops the provided stopwatch and returns the elapsed time in a specified format. The function can output the elapsed time as a single string or an array of days, hours, minutes, seconds, and milliseconds.
 
    .PARAMETER Time
    Specifies the stopwatch object to stop and retrieve the elapsed time from.
 
    .PARAMETER Option
    Specifies the format in which the elapsed time should be returned. Valid values are 'OneLiner' (default) or 'Array'.
 
    .PARAMETER Continue
    Indicates whether the stopwatch should continue running after retrieving the elapsed time.
 
    .EXAMPLE
    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    # Perform some operations
    Stop-TimeLog -Time $stopwatch
    # Output: "0 days, 0 hours, 0 minutes, 5 seconds, 123 milliseconds"
 
    .EXAMPLE
    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    # Perform some operations
    Stop-TimeLog -Time $stopwatch -Option Array
    # Output: ["0 days", "0 hours", "0 minutes", "5 seconds", "123 milliseconds"]
    #>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)][System.Diagnostics.Stopwatch] $Time,
        [ValidateSet('OneLiner', 'Array')][string] $Option = 'OneLiner',
        [switch] $Continue
    )
    Begin {
    }
    Process {
        if ($Option -eq 'Array') {
            $TimeToExecute = "$($Time.Elapsed.Days) days", "$($Time.Elapsed.Hours) hours", "$($Time.Elapsed.Minutes) minutes", "$($Time.Elapsed.Seconds) seconds", "$($Time.Elapsed.Milliseconds) milliseconds"
        } else {
            $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds"
        }
    }
    End {
        if (-not $Continue) {
            $Time.Stop()
        }
        return $TimeToExecute
    }
}
function Convert-Color { 
    <#
    .Synopsis
    This color converter gives you the hexadecimal values of your RGB colors and vice versa (RGB to HEX)
    .Description
    This color converter gives you the hexadecimal values of your RGB colors and vice versa (RGB to HEX). Use it to convert your colors and prepare your graphics and HTML web pages.
    .Parameter RBG
    Enter the Red Green Blue value comma separated. Red: 51 Green: 51 Blue: 204 for example needs to be entered as 51,51,204
    .Parameter HEX
    Enter the Hex value to be converted. Do not use the '#' symbol. (Ex: 3333CC converts to Red: 51 Green: 51 Blue: 204)
    .Example
    .\convert-color -hex FFFFFF
    Converts hex value FFFFFF to RGB
 
    .Example
    .\convert-color -RGB 123,200,255
    Converts Red = 123 Green = 200 Blue = 255 to Hex value
 
    #>

    param(
        [Parameter(ParameterSetName = "RGB", Position = 0)]
        [ValidateScript( { $_ -match '^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$' })]
        $RGB,
        [Parameter(ParameterSetName = "HEX", Position = 0)]
        [ValidateScript( { $_ -match '[A-Fa-f0-9]{6}' })]
        [string]
        $HEX
    )
    switch ($PsCmdlet.ParameterSetName) {
        "RGB" {
            if ($null -eq $RGB[2]) {
                Write-Error "Value missing. Please enter all three values seperated by comma."
            }
            $red = [convert]::Tostring($RGB[0], 16)
            $green = [convert]::Tostring($RGB[1], 16)
            $blue = [convert]::Tostring($RGB[2], 16)
            if ($red.Length -eq 1) {
                $red = '0' + $red
            }
            if ($green.Length -eq 1) {
                $green = '0' + $green
            }
            if ($blue.Length -eq 1) {
                $blue = '0' + $blue
            }
            Write-Output $red$green$blue
        }
        "HEX" {
            $red = $HEX.Remove(2, 4)
            $Green = $HEX.Remove(4, 2)
            $Green = $Green.remove(0, 2)
            $Blue = $hex.Remove(0, 4)
            $Red = [convert]::ToInt32($red, 16)
            $Green = [convert]::ToInt32($green, 16)
            $Blue = [convert]::ToInt32($blue, 16)
            Write-Output $red, $Green, $blue
        }
    }
}
function ConvertTo-StringByType { 
    <#
    .SYNOPSIS
    Private function to use within ConvertTo-JsonLiteral
 
    .DESCRIPTION
    Private function to use within ConvertTo-JsonLiteral
 
    .PARAMETER Value
    Value to convert to JsonValue
 
     .PARAMETER Depth
    Specifies how many levels of contained objects are included in the JSON representation. The default value is 0.
 
    .PARAMETER AsArray
    Outputs the object in array brackets, even if the input is a single object.
 
    .PARAMETER DateTimeFormat
    Changes DateTime string format. Default "yyyy-MM-dd HH:mm:ss"
 
    .PARAMETER NumberAsString
    Provides an alternative serialization option that converts all numbers to their string representation.
 
    .PARAMETER BoolAsString
    Provides an alternative serialization option that converts all bool to their string representation.
 
    .PARAMETER PropertyName
    Uses PropertyNames provided by user (only works with Force)
 
    .PARAMETER ArrayJoin
    Forces any array to be a string regardless of depth level
 
    .PARAMETER ArrayJoinString
    Uses defined string or char for array join. By default it uses comma with a space when used.
 
    .PARAMETER Force
    Forces using property names from first object or given thru PropertyName parameter
 
    .EXAMPLE
    $Value = ConvertTo-StringByType -Value $($Object[$a][$i]) -DateTimeFormat $DateTimeFormat
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [Object] $Value,
        [int] $Depth,
        [int] $MaxDepth,
        [string] $DateTimeFormat,
        [switch] $NumberAsString,
        [switch] $BoolAsString,
        [System.Collections.IDictionary] $NewLineFormat = @{
            NewLineCarriage = '\r\n'
            NewLine         = "\n"
            Carriage        = "\r"
        },
        [System.Collections.IDictionary] $NewLineFormatProperty = @{
            NewLineCarriage = '\r\n'
            NewLine         = "\n"
            Carriage        = "\r"
        },
        [System.Collections.IDictionary] $AdvancedReplace,
        [System.Text.StringBuilder] $TextBuilder,
        [string[]] $PropertyName,
        [switch] $ArrayJoin,
        [string] $ArrayJoinString,
        [switch] $Force
    )
    Process {
        if ($null -eq $Value) {
            "`"`""
        } elseif ($Value -is [string]) {
            $Value = $Value.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage)

            foreach ($Key in $AdvancedReplace.Keys) {
                $Value = $Value.Replace($Key, $AdvancedReplace[$Key])
            }
            "`"$Value`""
        } elseif ($Value -is [DateTime]) {
            "`"$($($Value).ToString($DateTimeFormat))`""
        } elseif ($Value -is [bool]) {
            if ($BoolAsString) {
                "`"$($Value)`""
            } else {
                $Value.ToString().ToLower()
            }
        } elseif ($Value -is [System.Collections.IDictionary]) {
            if ($MaxDepth -eq 0 -or $Depth -eq $MaxDepth) {
                "`"$($Value)`""
            } else {
                $Depth++
                $null = $TextBuilder.AppendLine("{")
                for ($i = 0; $i -lt ($Value.Keys).Count; $i++) {
                    $Property = ([string[]]$Value.Keys)[$i]
                    $DisplayProperty = $Property.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                    $null = $TextBuilder.Append("`"$DisplayProperty`":")
                    $OutputValue = ConvertTo-StringByType -Value $Value[$Property] -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $Depth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -Force:$Force -ArrayJoinString $ArrayJoinString -ArrayJoin:$ArrayJoin.IsPresent
                    $null = $TextBuilder.Append("$OutputValue")
                    if ($i -ne ($Value.Keys).Count - 1) {
                        $null = $TextBuilder.AppendLine(',')
                    }
                }
                $null = $TextBuilder.Append("}")
            }
        } elseif ($Value -is [System.Collections.IList] -or $Value -is [System.Collections.ReadOnlyCollectionBase]) {
            if ($ArrayJoin) {
                $Value = $Value -join $ArrayJoinString
                $Value = "$Value".Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                "`"$Value`""
            } else {
                if ($MaxDepth -eq 0 -or $Depth -eq $MaxDepth) {
                    $Value = "$Value".Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                    "`"$Value`""
                } else {
                    $CountInternalObjects = 0
                    $null = $TextBuilder.Append("[")
                    foreach ($V in $Value) {
                        $CountInternalObjects++
                        if ($CountInternalObjects -gt 1) {
                            $null = $TextBuilder.Append(',')
                        }
                        if ($Force -and -not $PropertyName) {
                            $PropertyName = $V.PSObject.Properties.Name
                        } elseif ($Force -and $PropertyName) {
                        } else {
                            $PropertyName = $V.PSObject.Properties.Name
                        }
                        $OutputValue = ConvertTo-StringByType -Value $V -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $Depth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -Force:$Force -PropertyName $PropertyName -ArrayJoinString $ArrayJoinString -ArrayJoin:$ArrayJoin.IsPresent
                        $null = $TextBuilder.Append($OutputValue)
                    }
                    $null = $TextBuilder.Append("]")
                }
            }
        } elseif ($Value -is [System.Enum]) {
            "`"$($($Value).ToString())`""
        } elseif (($Value | IsNumeric) -eq $true) {
            $Value = $($Value).ToString().Replace(',', '.')
            if ($NumberAsString) {
                "`"$Value`""
            } else {
                $Value
            }
        } elseif ($Value -is [PSObject]) {
            if ($MaxDepth -eq 0 -or $Depth -eq $MaxDepth) {
                "`"$($Value)`""
            } else {
                $Depth++
                $CountInternalObjects = 0
                $null = $TextBuilder.AppendLine("{")
                if ($Force -and -not $PropertyName) {
                    $PropertyName = $Value.PSObject.Properties.Name
                } elseif ($Force -and $PropertyName) {
                } else {
                    $PropertyName = $Value.PSObject.Properties.Name
                }
                foreach ($Property in $PropertyName) {
                    $CountInternalObjects++
                    if ($CountInternalObjects -gt 1) {
                        $null = $TextBuilder.AppendLine(',')
                    }
                    $DisplayProperty = $Property.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                    $null = $TextBuilder.Append("`"$DisplayProperty`":")
                    $OutputValue = ConvertTo-StringByType -Value $Value.$Property -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $Depth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -Force:$Force -ArrayJoinString $ArrayJoinString -ArrayJoin:$ArrayJoin.IsPresent
                    $null = $TextBuilder.Append("$OutputValue")
                }
                $null = $TextBuilder.Append("}")
            }
        } else {
            $Value = $Value.ToString().Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
            "`"$Value`""
        }
    }
}
function Get-MimeType { 
    <#
    .SYNOPSIS
    Get-MimeType function returns the MIME type of a file based on its extension.
 
    .DESCRIPTION
    This function takes a file name as input and returns the corresponding MIME type based on the file extension.
 
    .PARAMETER FileName
    Specifies the name of the file for which the MIME type needs to be determined.
 
    .EXAMPLE
    Get-MimeType -FileName "example.jpg"
    Returns "image/jpeg" as the MIME type for the file "example.jpg".
 
    .EXAMPLE
    Get-MimeType -FileName "example.png"
    Returns "image/png" as the MIME type for the file "example.png".
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string] $FileName
    )

    $MimeMappings = @{
        '.jpeg' = 'image/jpeg'
        '.jpg'  = 'image/jpeg'
        '.png'  = 'image/png'
    }

    $Extension = [System.IO.Path]::GetExtension( $FileName )
    $ContentType = $MimeMappings[ $Extension ]

    if ([string]::IsNullOrEmpty($ContentType)) {
        return New-Object System.Net.Mime.ContentType
    } else {
        return New-Object System.Net.Mime.ContentType($ContentType)
    }
}
function Request-Credentials { 
    <#
    .SYNOPSIS
    Requests credentials for authentication purposes.
 
    .DESCRIPTION
    The Request-Credentials function is used to prompt the user for credentials. It provides options to input the username and password directly, read the password from a file, convert the password to a secure string, and handle various error scenarios.
 
    .PARAMETER UserName
    Specifies the username for authentication.
 
    .PARAMETER Password
    Specifies the password for authentication.
 
    .PARAMETER AsSecure
    Indicates whether the password should be converted to a secure string.
 
    .PARAMETER FromFile
    Specifies whether the password should be read from a file.
 
    .PARAMETER Output
    Indicates whether the function should return output in case of errors.
 
    .PARAMETER NetworkCredentials
    Specifies if network credentials are being requested.
 
    .PARAMETER Service
    Specifies the service for which credentials are being requested.
 
    .EXAMPLE
    Request-Credentials -UserName 'JohnDoe' -Password 'P@ssw0rd' -AsSecure
    Requests credentials for the user 'JohnDoe' with the password 'P@ssw0rd' in a secure format.
 
    .EXAMPLE
    Request-Credentials -FromFile -Password 'C:\Credentials.txt' -Output -Service 'FTP'
    Reads the password from the file 'C:\Credentials.txt' and returns an error message if the file is unreadable for the FTP service.
 
    #>

    [CmdletBinding()]
    param(
        [string] $UserName,
        [string] $Password,
        [switch] $AsSecure,
        [switch] $FromFile,
        [switch] $Output,
        [switch] $NetworkCredentials,
        [string] $Service
    )
    if ($FromFile) {
        if (($Password -ne '') -and (Test-Path $Password)) {

            Write-Verbose "Request-Credentials - Reading password from file $Password"
            $Password = Get-Content -Path $Password
        } else {

            if ($Output) {
                return @{ Status = $false; Output = $Service; Extended = 'File with password unreadable.' }
            } else {
                Write-Warning "Request-Credentials - Secure password from file couldn't be read. File not readable. Terminating."
                return
            }
        }
    }
    if ($AsSecure) {
        try {
            $NewPassword = $Password | ConvertTo-SecureString -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($ErrorMessage -like '*Key not valid for use in specified state*') {
                if ($Output) {
                    return @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." }
                } else {
                    Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer."
                    return
                }
            } else {
                if ($Output) {
                    return @{ Status = $false; Output = $Service; Extended = $ErrorMessage }
                } else {
                    Write-Warning -Message "Request-Credentials - $ErrorMessage"
                    return
                }
            }
        }
    } else {
        $NewPassword = $Password
    }
    if ($UserName -and $NewPassword) {
        if ($AsSecure) {
            $Credentials = New-Object System.Management.Automation.PSCredential($Username, $NewPassword)
        } else {
            Try {
                $SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force -ErrorAction Stop
            } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($ErrorMessage -like '*Key not valid for use in specified state*') {
                    if ($Output) {
                        return  @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." }
                    } else {
                        Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer."
                        return
                    }
                } else {
                    if ($Output) {
                        return @{ Status = $false; Output = $Service; Extended = $ErrorMessage }
                    } else {
                        Write-Warning -Message "Request-Credentials - $ErrorMessage"
                        return
                    }
                }
            }
            $Credentials = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword)
        }
    } else {
        if ($Output) {
            return @{ Status = $false; Output = $Service; Extended = 'Username or/and Password is empty' }
        } else {
            Write-Warning -Message 'Request-Credentials - UserName or Password are empty.'
            return
        }
    }
    if ($NetworkCredentials) {
        return $Credentials.GetNetworkCredential()
    } else {
        return $Credentials
    }
}
function New-ApexChart {
    <#
    .SYNOPSIS
    Creates a new ApexChart with the specified options and events.
 
    .DESCRIPTION
    This function creates a new ApexChart with the provided options and events. It allows customization of the chart appearance and behavior.
 
    .PARAMETER Options
    Specifies the options for configuring the ApexChart. This should be a dictionary containing the necessary settings for the chart.
 
    .PARAMETER Events
    Specifies the events to be associated with the ApexChart. These events can trigger actions on specific interactions with the chart.
 
    .EXAMPLE
    $chartOptions = @{
        chart = @{
            type = 'line'
            height = 350
        }
        series = @(
            @{
                name = 'Series 1'
                data = [100, 200, 300, 400, 500]
            }
        )
    }
    $chartEvents = @{
        dataPointSelection = {
            Write-Host "Data point selected: $_"
        }
    }
    New-ApexChart -Options $chartOptions -Events $chartEvents
 
    Creates a new ApexChart of type 'line' with a height of 350 pixels and a single series named 'Series 1' with data points [100, 200, 300, 400, 500]. It also specifies an event to display a message when a data point is selected.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [Object] $Events
    )

    if ($Events) {
        $Options.chart.events = 'EventsReplaceMe'
    }

    $Script:HTMLSchema.Features.ChartsApex = $true

    $Div = New-HTMLTag -Tag 'div' -Attributes @{ class = 'flexElement'; id = $Options.chart.id; }
    $Script = New-HTMLTag -Tag 'script' -Value {

        Remove-EmptyValue -Hashtable $Options -Recursive -Rerun 2
        $JSON = $Options | ConvertTo-JsonLiteral -Depth 5 -AdvancedReplace @{ '.' = '\.'; '$' = '\$' }

        $JSON = $JSON.Replace('"new Date(', 'new Date(').Replace(').getTime()"', ').getTime()')

        if ($Options.chart.events) {
            $JSON = $JSON -replace ('"events":"EventsReplaceMe"', $Events)
        }

        $JSON = $JSON | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) }

        "var options = $JSON"
        "var chart = new ApexCharts(document.querySelector('#$ID'),
            options
        );"

        "chart.render();"
    } -NewLine
    $Div

    $Script:HTMLSchema.Charts.Add($Script)
}
function New-ChartInternalArea {
    <#
    .SYNOPSIS
    Creates a new internal area chart with the specified data and options.
 
    .DESCRIPTION
    This function creates a new internal area chart with the provided data and options. It allows customization of the chart appearance and behavior.
 
    .PARAMETER Options
    Specifies the options for configuring the internal area chart. This should be a dictionary containing the necessary settings for the chart.
 
    .PARAMETER Data
    Specifies the data points to be displayed on the internal area chart.
 
    .PARAMETER DataNames
    Specifies the names of the data series to be displayed on the internal area chart.
 
    .PARAMETER DataLegend
    Specifies the legend for the data series displayed on the internal area chart.
 
    .PARAMETER DataCategoriesType
    Specifies the type of categories for the X-axis of the internal area chart. Valid values are 'datetime', 'category', or 'numeric'.
 
    .EXAMPLE
    $chartOptions = @{
        chart = @{
            id = 'internal-area-chart'
        }
    }
    $chartData = @(100, 200, 300, 400, 500)
    $chartDataNames = @('Series 1')
    $chartDataLegend = @('Legend 1')
    New-ChartInternalArea -Options $chartOptions -Data $chartData -DataNames $chartDataNames -DataLegend $chartDataLegend -DataCategoriesType 'numeric'
 
    Creates a new internal area chart with a single series named 'Series 1' displaying data points [100, 200, 300, 400, 500]. The legend 'Legend 1' is associated with the series, and the X-axis categories are of numeric type.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,

        [Array] $Data,
        [Array] $DataNames,
        [Array] $DataLegend,

        #[bool] $DataLabelsEnabled = $true,
        #[int] $DataLabelsOffsetX = -6,
        #[string] $DataLabelsFontSize = '12px',
        #[string] $DataLabelsColor,
        [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category'

        #$Type
    )

    $Options.chart.type = 'area'
    $Options.series = @( New-ChartInternalDataSet -Data $Data -DataNames $DataNames )

    $Options.xaxis = [ordered] @{}
    if ($DataCategoriesType -ne '') {
        $Options.xaxis.type = $DataCategoriesType
    }
    if ($DataCategories.Count -gt 0) {
        $Options.xaxis.categories = $DataCategories
    }

}
function New-ChartInternalAxisX {
    <#
    .SYNOPSIS
    Creates a new internal X-axis configuration for a chart with the specified options.
 
    .DESCRIPTION
    This function creates a new internal X-axis configuration for a chart with the provided options. It allows customization of the X-axis appearance and behavior.
 
    .PARAMETER Options
    Specifies the options for configuring the X-axis. This should be a dictionary containing the necessary settings for the X-axis.
 
    .PARAMETER TitleText
    Specifies the title text to be displayed for the X-axis.
 
    .PARAMETER Min
    Specifies the minimum value for the X-axis.
 
    .PARAMETER Max
    Specifies the maximum value for the X-axis.
 
    .PARAMETER Type
    Specifies the type of categories for the X-axis. Valid values are 'datetime', 'category', or 'numeric'.
 
    .PARAMETER Names
    Specifies the names of the categories to be displayed on the X-axis.
 
    .EXAMPLE
    $axisOptions = @{
        xaxis = @{}
    }
    $axisTitle = 'Time'
    $axisMin = 0
    $axisMax = 100
    $axisType = 'numeric'
    $axisNames = @('Category A', 'Category B', 'Category C')
    New-ChartInternalAxisX -Options $axisOptions -TitleText $axisTitle -Min $axisMin -Max $axisMax -Type $axisType -Names $axisNames
 
    Creates a new internal X-axis configuration with a title 'Time', a minimum value of 0, a maximum value of 100, categories of numeric type, and displays categories 'Category A', 'Category B', 'Category C' on the X-axis.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [string] $TitleText,
        [Int64] $Min,
        [Int64] $Max,
        [ValidateSet('datetime', 'category', 'numeric')][string] $Type = 'category',
        [Array] $Names
    )

    if (-not $Options.Contains('xaxis')) {
        $Options.xaxis = @{ }
    }
    if ($TitleText -ne '') {
        $Options.xaxis.title = @{ }
        $Options.xaxis.title.text = $TitleText
    }
    if ($Min -gt 0) {
        $Options.xaxis.min = $Min
    }
    if ($Max -gt 0) {
        $Options.xaxis.max = $Max
    }
    if ($Type -ne '') {
        $Options.xaxis.type = $Type
    }
    if ($Names.Count -gt 0) {
        $Options.xaxis.categories = $Names
    }
}
Function New-ChartInternalBar {
    <#
    .SYNOPSIS
    Creates a new internal bar chart with the specified data and options.
 
    .DESCRIPTION
    This function creates a new internal bar chart with the provided data and options. It allows customization of the chart appearance and behavior.
 
    .PARAMETER Options
    Specifies the options for configuring the internal bar chart. This should be a dictionary containing the necessary settings for the chart.
 
    .PARAMETER Horizontal
    Specifies whether the bars should be displayed horizontally. Default is true.
 
    .PARAMETER DataLabelsEnabled
    Specifies whether data labels should be enabled on the bars. Default is true.
 
    .PARAMETER DataLabelsOffsetX
    Specifies the horizontal offset for the data labels. Default is -6.
 
    .PARAMETER DataLabelsFontSize
    Specifies the font size for the data labels. Default is '12px'.
 
    .PARAMETER DataLabelsColor
    Specifies the color for the data labels.
 
    .PARAMETER Formatter
    Specifies the formatter for the data labels.
 
    .PARAMETER Type
    Specifies the type of bar chart. Valid values are 'bar', 'barStacked', or 'barStacked100Percent'. Default is 'bar'.
 
    .PARAMETER Distributed
    Indicates whether the bars should be distributed evenly. This is a switch parameter.
 
    .PARAMETER Data
    Specifies the data points to be displayed on the internal bar chart.
 
    .PARAMETER DataNames
    Specifies the names of the data series to be displayed on the internal bar chart.
 
    .PARAMETER DataLegend
    Specifies the legend for the data series displayed on the internal bar chart.
 
    .EXAMPLE
    $chartOptions = @{
        chart = @{
            id = 'internal-bar-chart'
        }
    }
    $chartData = @(100, 200, 300, 400, 500)
    $chartDataNames = @('Series 1')
    $chartDataLegend = @('Legend 1')
    New-ChartInternalBar -Options $chartOptions -Data $chartData -DataNames $chartDataNames -DataLegend $chartDataLegend
 
    Creates a new internal bar chart with a single series named 'Series 1' displaying data points [100, 200, 300, 400, 500]. The legend 'Legend 1' is associated with the series.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $Horizontal = $true,
        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [string[]] $DataLabelsColor,
        #[string] $Title,
        #[ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default',
        [string] $Formatter,
        [ValidateSet('bar', 'barStacked', 'barStacked100Percent')] $Type = 'bar',
        #[string[]] $Colors,

        [switch] $Distributed,

        [Array] $Data,
        [Array] $DataNames,
        [Array] $DataLegend
    )

    if ($Type -eq 'bar') {
        $Options.chart.type = 'bar'
    } elseif ($Type -eq 'barStacked') {
        $Options.chart.type = 'bar'
        $Options.chart.stacked = $true
    } else {
        $Options.chart.type = 'bar'
        $Options.chart.stacked = $true
        $Options.chart.stackType = '100%'
    }

    $Options.plotOptions = @{
        bar = @{
            horizontal = $Horizontal
        }
    }
    if ($Distributed) {
        $Options.plotOptions.bar.distributed = $Distributed.IsPresent
    }
    $Options.dataLabels = [ordered] @{
        enabled = $DataLabelsEnabled
        offsetX = $DataLabelsOffsetX
        style   = @{
            fontSize = $DataLabelsFontSize
        }
    }
    if ($null -ne $DataLabelsColor) {
        $RGBColorLabel = ConvertFrom-Color -Color $DataLabelsColor
        $Options.dataLabels.style.colors = @($RGBColorLabel)
    }
    $Options.series = @(New-ChartInternalDataSet -Data $Data -DataNames $DataLegend)

    if (-not $Options.Contains('xaxis')) {
        $Options.xaxis = [ordered] @{ }
    }

    if ($DataNames.Count -gt 0) {
        $Options.xaxis.categories = $DataNames

    }
}
Register-ArgumentCompleter -CommandName New-ChartInternalBar -ParameterName DataLabelsColor -ScriptBlock $Script:ScriptBlockColors
function New-ChartInternalColors {
    <#
    .SYNOPSIS
    Creates a new internal color configuration for a chart with the specified color palette.
 
    .DESCRIPTION
    This function creates a new internal color configuration for a chart with the provided color palette. It allows customization of the colors used in the chart.
 
    .PARAMETER Options
    Specifies the options for configuring the color palette. This should be a dictionary containing the necessary settings for the colors.
 
    .PARAMETER Colors
    Specifies an array of colors to be used in the chart.
 
    .EXAMPLE
    $colorOptions = @{
        colors = @('#FF5733', '#33FF57', '#3357FF')
    }
    $colorPalette = @('#FF5733', '#33FF57', '#3357FF')
    New-ChartInternalColors -Options $colorOptions -Colors $colorPalette
 
    Creates a new internal color configuration with a color palette containing colors '#FF5733', '#33FF57', and '#3357FF' to be used in the chart.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [string[]]$Colors
    )

    if ($Colors.Count -gt 0) {

        $Options.colors = @($Colors)
    }
}
Register-ArgumentCompleter -CommandName New-ChartInternalColors -ParameterName Colors -ScriptBlock $Script:ScriptBlockColors
function New-ChartInternalDataLabels {
    <#
    .SYNOPSIS
    Creates a new internal data labels configuration for a chart with the specified settings.
 
    .DESCRIPTION
    This function creates a new internal data labels configuration for a chart with the provided options. It allows customization of the data labels appearance and behavior.
 
    .PARAMETER Options
    Specifies the options for configuring the internal data labels. This should be a dictionary containing the necessary settings for the data labels.
 
    .PARAMETER DataLabelsEnabled
    Specifies whether data labels should be enabled on the chart.
 
    .PARAMETER DataLabelsOffsetX
    Specifies the horizontal offset for the data labels.
 
    .PARAMETER DataLabelsFontSize
    Specifies the font size for the data labels.
 
    .PARAMETER DataLabelsColor
    Specifies the color for the data labels.
 
    .EXAMPLE
    $dataLabelsOptions = @{
        dataLabelsEnabled = $true
        dataLabelsOffsetX = -6
        dataLabelsFontSize = '12px'
        dataLabelsColor = @('#FF5733', '#33FF57', '#3357FF')
    }
    New-ChartInternalDataLabels -Options $dataLabelsOptions
 
    Creates a new internal data labels configuration with data labels enabled, a horizontal offset of -6, a font size of '12px', and color palette containing colors '#FF5733', '#33FF57', and '#3357FF'.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $DataLabelsEnabled,
        [int] $DataLabelsOffsetX,
        [string] $DataLabelsFontSize,
        [string[]] $DataLabelsColor
    )

    $Options.dataLabels = [ordered] @{
        enabled = $DataLabelsEnabled
        offsetX = $DataLabelsOffsetX
        style   = @{
            fontSize = $DataLabelsFontSize
        }
    }
    if ($DataLabelsColor.Count -gt 0) {
        $Options.dataLabels.style.colors = @(ConvertFrom-Color -Color $DataLabelsColor)
    }
}
Register-ArgumentCompleter -CommandName New-ChartInternalDataLabels -ParameterName DataLabelsColors -ScriptBlock $Script:ScriptBlockColors
function New-ChartInternalDataSet {
    <#
    .SYNOPSIS
    Creates a new internal data set for a chart with the specified data and data series names.
 
    .DESCRIPTION
    This function creates a new internal data set for a chart with the provided data points and corresponding data series names. It allows for defining multiple data series with their respective names.
 
    .PARAMETER Data
    Specifies the data points to be displayed in the data set.
 
    .PARAMETER DataNames
    Specifies the names of the data series in the data set.
 
    .EXAMPLE
    $dataPoints = @(100, 200, 300, 400, 500)
    $dataSeriesNames = @('Series A', 'Series B', 'Series C')
    New-ChartInternalDataSet -Data $dataPoints -DataNames $dataSeriesNames
 
    Creates a new internal data set with data points [100, 200, 300, 400, 500] and data series named 'Series A', 'Series B', 'Series C'.
    #>

    [CmdletBinding()]
    param(
        [Array] $Data,
        [Array] $DataNames
    )

    if ($null -ne $Data -and $null -ne $DataNames) {
        if ($Data[0] -is [System.Collections.ICollection]) {

            if ($Data[0].Count -eq $DataNames.Count) {
                for ($a = 0; $a -lt $Data.Count; $a++) {
                    [ordered] @{
                        name = $DataNames[$a]
                        data = $Data[$a]
                    }
                }
            } elseif ($Data.Count -eq $DataNames.Count) {
                for ($a = 0; $a -lt $Data.Count; $a++) {
                    [ordered] @{
                        name = $DataNames[$a]
                        data = $Data[$a]
                    }
                }
            } else {

                New-ChartInternalDataSet -Data $Data
            }

        } else {
            if ($null -ne $DataNames) {

                [ordered] @{
                    name = $DataNames
                    data = $Data
                }
            } else {
                [ordered]  @{
                    data = $Data
                }
            }
        }

    } elseif ($null -ne $Data) {

        if ($Data[0] -is [System.Collections.ICollection]) {

            foreach ($D in $Data) {
                [ordered] @{
                    data = $D
                }
            }
        } else {

            [ordered] @{
                data = $Data
            }
        }
    } else {
        Write-Warning -Message "New-ChartInternalDataSet - No Data provided. Unabled to create dataset."
        return [ordered] @{ }
    }
}
function New-ChartInternalGradient {
    <#
    .SYNOPSIS
    Creates a new internal gradient configuration for a chart with the specified settings.
 
    .DESCRIPTION
    This function creates a new internal gradient configuration for a chart with the provided options. It allows customization of the gradient appearance and behavior.
 
    .PARAMETER Options
    Specifies the options for configuring the internal gradient. This should be a dictionary containing the necessary settings for the gradient.
 
    .EXAMPLE
    $gradientOptions = @{
        fill = @{
            type = 'gradient'
            gradient = @{
                shade = 'dark'
                type = 'horizontal'
                shadeIntensity = 0.5
                #gradientToColors = @('#ABE5A1')
                inverseColors = $false
                opacityFrom = 1
                opacityTo = 1
                stops = @(0, 100)
            }
        }
    }
    New-ChartInternalGradient -Options $gradientOptions
 
    Creates a new internal gradient configuration with a dark horizontal gradient, shade intensity of 0.5, and opacity from 1 to 1.
    #>

    [CmdletBinding()]
    param(

    )
    $Options.fill = [ordered] @{
        type     = 'gradient'
        gradient = [ordered] @{
            shade          = 'dark'
            type           = 'horizontal'
            shadeIntensity = 0.5

            inverseColors  = $false
            opacityFrom    = 1
            opacityTo      = 1
            stops          = @(0, 100)
        }
    }
}

function New-ChartInternalGrid {
    <#
    .SYNOPSIS
    Creates a new internal grid configuration for a chart with customizable options for grid lines and colors.
 
    .DESCRIPTION
    This function creates a new internal grid configuration for a chart with the provided options. It allows customization of grid lines, colors, and padding.
 
    .PARAMETER Options
    Specifies the options for configuring the internal grid. This should be a dictionary containing the necessary settings for the grid.
 
    .PARAMETER Show
    Specifies whether the grid should be displayed on the chart.
 
    .PARAMETER BorderColor
    Specifies the color of the grid border.
 
    .PARAMETER StrokeDash
    Specifies the stroke dash array for the grid lines.
 
    .PARAMETER Position
    Specifies the position of the grid. Valid values are 'front', 'back', or 'default'.
 
    .PARAMETER xAxisLinesShow
    Specifies whether the x-axis grid lines should be displayed.
 
    .PARAMETER yAxisLinesShow
    Specifies whether the y-axis grid lines should be displayed.
 
    .PARAMETER RowColors
    Specifies an array of colors for the row grid lines.
 
    .PARAMETER RowOpacity
    Specifies the opacity of the row grid lines. Valid range is 0 to 1.
 
    .PARAMETER ColumnColors
    Specifies an array of colors for the column grid lines.
 
    .PARAMETER ColumnOpacity
    Specifies the opacity of the column grid lines. Valid range is 0 to 1.
 
    .PARAMETER PaddingTop
    Specifies the top padding for the grid.
 
    .PARAMETER PaddingRight
    Specifies the right padding for the grid.
 
    .PARAMETER PaddingBottom
    Specifies the bottom padding for the grid.
 
    .PARAMETER PaddingLeft
    Specifies the left padding for the grid.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $Show,
        [string] $BorderColor,
        [int] $StrokeDash, #: 0,
        [ValidateSet('front', 'back', 'default')][string] $Position = 'default',
        [nullable[bool]] $xAxisLinesShow = $null,
        [nullable[bool]] $yAxisLinesShow = $null,
        [alias('GridColors')][string[]] $RowColors,
        [alias('GridOpacity')][double] $RowOpacity = 0.5, # valid range 0 - 1
        [string[]] $ColumnColors ,
        [double] $ColumnOpacity = 0.5, # valid range 0 - 1
        [int] $PaddingTop,
        [int] $PaddingRight,
        [int] $PaddingBottom,
        [int] $PaddingLeft
    )

    $Options.grid = [ordered] @{ }
    $Options.grid.Show = $Show
    if ($BorderColor) {
        $options.grid.borderColor = @(ConvertFrom-Color -Color $BorderColor)
    }
    if ($StrokeDash -gt 0) {
        $Options.grid.strokeDashArray = $StrokeDash
    }
    if ($Position -ne 'Default') {
        $Options.grid.position = $Position
    }

    if ($null -ne $xAxisLinesShow) {
        $Options.grid.xaxis = @{ }
        $Options.grid.xaxis.lines = @{ }

        $Options.grid.xaxis.lines.show = $xAxisLinesShow
    }
    if ($null -ne $yAxisLinesShow) {
        $Options.grid.yaxis = @{ }
        $Options.grid.yaxis.lines = @{ }
        $Options.grid.yaxis.lines.show = $yAxisLinesShow
    }

    if ($RowColors.Count -gt 0 -or $RowOpacity -ne 0) {
        $Options.grid.row = @{ }
        if ($RowColors.Count -gt 0) {
            $Options.grid.row.colors = @(ConvertFrom-Color -Color $RowColors)
        }
        if ($RowOpacity -ne 0) {
            $Options.grid.row.opacity = $RowOpacity
        }
    }
    if ($ColumnColors.Count -gt 0 -or $ColumnOpacity -ne 0) {
        $Options.grid.column = @{ }
        if ($ColumnColors.Count -gt 0) {
            $Options.grid.column.colors = @(ConvertFrom-Color -Color $ColumnColors)
        }
        if ($ColumnOpacity -ne 0) {
            $Options.grid.column.opacity = $ColumnOpacitys
        }
    }
    if ($PaddingTop -gt 0 -or $PaddingRight -gt 0 -or $PaddingBottom -gt 0 -or $PaddingLeft -gt 0) {

        $Options.grid.padding = @{ }
        if ($PaddingTop -gt 0) {
            $Options.grid.padding.PaddingTop = $PaddingTop
        }
        if ($PaddingRight -gt 0) {
            $Options.grid.padding.PaddingRight = $PaddingRight
        }
        if ($PaddingBottom -gt 0) {
            $Options.grid.padding.PaddingBottom = $PaddingBottom
        }
        if ($PaddingLeft -gt 0) {
            $Options.grid.padding.PaddingLeft = $PaddingLeft
        }
    }
}
Register-ArgumentCompleter -CommandName New-ChartInternalGrid -ParameterName BorderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-ChartInternalGrid -ParameterName RowColors -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-ChartInternalGrid -ParameterName ColumnColors -ScriptBlock $Script:ScriptBlockColors
function New-ChartInternalLegend {
    <#
    .SYNOPSIS
    Creates a new internal legend configuration for a chart with the specified position settings.
 
    .DESCRIPTION
    This function creates a new internal legend configuration for a chart with the provided position options. It allows customization of the legend position and alignment.
 
    .PARAMETER Options
    Specifies the options for configuring the internal legend. This should be a dictionary containing the necessary settings for the legend.
 
    .PARAMETER LegendPosition
    Specifies the position of the legend on the chart. Valid values are 'top', 'topRight', 'left', 'right', 'bottom', or 'default'.
 
    .EXAMPLE
    $legendOptions = @{
        LegendPosition = 'right'
    }
    New-ChartInternalLegend -Options $legendOptions
 
    Creates a new internal legend configuration with the legend positioned on the right side of the chart with specific offset and height settings.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', 'default')][string] $LegendPosition = 'default'
    )

    if ($LegendPosition -eq 'default' -or $LegendPosition -eq 'bottom') {

    } elseif ($LegendPosition -eq 'right') {
        $Options.legend = [ordered]@{
            position = 'right'
            offsetY  = 100
            height   = 230
        }
    } elseif ($LegendPosition -eq 'top') {
        $Options.legend = [ordered]@{
            position        = 'top'
            horizontalAlign = 'left'
            offsetX         = 40
        }
    } elseif ($LegendPosition -eq 'topRight') {
        $Options.legend = [ordered]@{
            position        = 'top'
            horizontalAlign = 'right'
            floating        = $true
            offsetY         = -25
            offsetX         = -5
        }
    }
}
function New-ChartInternalLine {
    <#
    .SYNOPSIS
    Creates a new internal line chart configuration with specified data and options.
 
    .DESCRIPTION
    This function creates a new internal line chart configuration with the provided data points and data series names. It allows customization of the line chart type and data categories type.
 
    .PARAMETER Options
    Specifies the options for configuring the internal line chart. This should be a dictionary containing the necessary settings for the line chart.
 
    .PARAMETER Data
    Specifies the data points to be displayed in the line chart.
 
    .PARAMETER DataNames
    Specifies the names of the data series in the line chart.
 
    .PARAMETER DataCategoriesType
    Specifies the type of data categories for the line chart. Valid values are 'datetime', 'category', or 'numeric'.
 
    .EXAMPLE
    $lineChartOptions = @{
        chart = @{
            type = 'line'
        }
    }
    $dataPoints = @(100, 200, 300, 400, 500)
    $dataSeriesNames = @('Series A', 'Series B', 'Series C')
    New-ChartInternalLine -Options $lineChartOptions -Data $dataPoints -DataNames $dataSeriesNames -DataCategoriesType 'category'
 
    Creates a new internal line chart configuration with a line chart type, data points [100, 200, 300, 400, 500], data series named 'Series A', 'Series B', 'Series C', and data categories type as 'category'.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,

        [Array] $Data,
        [Array] $DataNames,
        #[Array] $DataLegend,

        # [bool] $DataLabelsEnabled = $true,
        #[int] $DataLabelsOffsetX = -6,
        #[string] $DataLabelsFontSize = '12px',
        # [string] $DataLabelsColor,
        [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category'

        # $Type
    )

    $Options.chart = @{
        type = 'line'
    }

    if (-not $Options.xaxis) {
        $Options.xaxis = [ordered] @{}
    }
    if ($DataCategoriesType -ne '') {
        $Options.xaxis.type = $DataCategoriesType
    }

}
function New-ChartInternalMarker {
    <#
    .SYNOPSIS
    Creates a new internal marker for a chart.
 
    .DESCRIPTION
    This function creates a new internal marker for a chart based on the specified options.
 
    .PARAMETER Options
    Specifies the options for the internal marker.
 
    .PARAMETER MarkerSize
    Specifies the size of the marker.
 
    .EXAMPLE
    $markerOptions = @{
        color = 'red'
    }
    New-ChartInternalMarker -Options $markerOptions -MarkerSize 10
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [int] $MarkerSize
    )
    if ($MarkerSize -gt 0) {
        $Options.markers = @{
            size = $MarkerSize
        }
    }
}
function New-ChartInternalPattern {
    <#
    .SYNOPSIS
    Creates a new chart internal pattern with specified options.
 
    .DESCRIPTION
    The New-ChartInternalPattern function creates a new chart internal pattern with the specified options. It allows customization of the pattern style, opacity, and type.
 
    .EXAMPLE
    New-ChartInternalPattern -Type 'pattern' -Opacity 1 -PatternStyle 'circles'
 
    Description
    -----------
    Creates a new chart internal pattern with circles pattern style and opacity of 1.
 
    .EXAMPLE
    New-ChartInternalPattern -Type 'pattern' -Opacity 0.5 -PatternStyle 'verticalLines'
 
    Description
    -----------
    Creates a new chart internal pattern with vertical lines pattern style and opacity of 0.5.
 
    #>

    [CmdletBinding()]
    param(

    )
    $Options.fill = [ordered]@{
        type    = 'pattern'
        opacity = 1
        pattern = [ordered]@{
            style = @('circles', 'slantedLines', 'verticalLines', 'horizontalLines')
        }
    }
}
function New-ChartInternalPie {
    <#
    .SYNOPSIS
    Creates a new internal pie chart.
 
    .DESCRIPTION
    This function creates a new internal pie chart based on the provided options, values, names, and type.
 
    .PARAMETER Options
    The options for the pie chart.
 
    .PARAMETER Values
    An array of values for the pie chart.
 
    .PARAMETER Names
    An array of names for the pie chart.
 
    .PARAMETER Type
    The type of the pie chart.
 
    .EXAMPLE
    New-ChartInternalPie -Options @{ chart = @{ type = "pie" } } -Values @(10, 20, 30) -Names @("A", "B", "C") -Type "pie"
    Creates a new internal pie chart with values 10, 20, 30 and names A, B, C.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [Array] $Values,
        [Array] $Names,
        [string] $Type
    )

    $Options.chart.type = $Type.ToLower()
    $Options.series = @($Values)
    $Options.labels = @($Names)
}
function New-ChartInternalRadial {
    <#
    .SYNOPSIS
    Creates a new radial chart with specified options, values, names, and type.
 
    .DESCRIPTION
    This function creates a new radial chart with the provided options, values, names, and type. The radial chart type is set to 'radialBar'.
 
    .PARAMETER Options
    An IDictionary containing the options for the radial chart.
 
    .PARAMETER Values
    An array of values to be displayed in the radial chart.
 
    .PARAMETER Names
    An array of names corresponding to the values in the radial chart.
 
    .PARAMETER Type
    Specifies the type of radial chart to create.
 
    .EXAMPLE
    $options = @{}
    $values = 1, 2, 3
    $names = 'A', 'B', 'C'
    New-ChartInternalRadial -Options $options -Values $values -Names $names -Type 1
    # Creates a radial chart of Type 1 with values 1, 2, 3 and names 'A', 'B', 'C'.
 
    .EXAMPLE
    $options = @{}
    $values = 4, 5, 6
    $names = 'X', 'Y', 'Z'
    New-ChartInternalRadial -Options $options -Values $values -Names $names -Type 2
    # Creates a radial chart of Type 2 with values 4, 5, 6 and names 'X', 'Y', 'Z'.
    #>


    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [Array] $Values,
        [Array] $Names,
        $Type
    )

    $Options.chart.type = 'radialBar'

    if ($Type -eq '1') {
        New-ChartInternalRadialType1 -Options $Options
    } elseif ($Type -eq '2') {
        New-ChartInternalRadialType2 -Options $Options
    }

    $Options.series = @($Values)
    $Options.labels = @($Names)

}
function New-ChartInternalRadialDataLabels {
    <#
    .SYNOPSIS
    Creates data labels for a radial chart with specified options.
 
    .DESCRIPTION
    The New-ChartInternalRadialDataLabels function creates data labels for a radial chart with the specified options. It allows customization of the data label appearance including the average label.
 
    .PARAMETER Options
    An IDictionary containing the options for the data labels.
 
    .PARAMETER LabelAverage
    Specifies the label to display for the average value.
 
    .EXAMPLE
    $options = @{}
    New-ChartInternalRadialDataLabels -Options $options -LabelAverage 'Average'
    # Creates data labels for a radial chart with the average label set to 'Average'.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [string] $LabelAverage = 'Average'
    )
    if ($LabelAverage -ne '') {
        $Options.plotOptions.radialBar.dataLabels = @{
            showOn = 'always'

            name   = @{

            }
            value  = @{

            }

            total  = @{
                show  = $true
                label = $LabelAverage
            }

        }
    }
}

function New-ChartInternalRadialType1 {
    <#
    .SYNOPSIS
    Creates a new radial chart of Type 1 with specified options, values, and names.
 
    .DESCRIPTION
    This function creates a new radial chart of Type 1 with the provided options, values, and names. It customizes the appearance of the radial chart with specific settings.
 
    .PARAMETER Options
    An IDictionary containing the options for the radial chart.
 
    .PARAMETER Values
    An array of values to be displayed in the radial chart.
 
    .PARAMETER Names
    An array of names corresponding to the values in the radial chart.
 
    .EXAMPLE
    $options = @{
        chart = @{
            type = 'radialBar'
        }
    }
    $values = 1, 2, 3
    $names = 'A', 'B', 'C'
    New-ChartInternalRadialType1 -Options $options -Values $values -Names $names
    # Creates a radial chart of Type 1 with values 1, 2, 3 and names 'A', 'B', 'C'.
 
    .EXAMPLE
    $options = @{
        chart = @{
            type = 'radialBar'
        }
    }
    $values = 4, 5, 6
    $names = 'X', 'Y', 'Z'
    New-ChartInternalRadialType1 -Options $options -Values $values -Names $names
    # Creates a radial chart of Type 1 with values 4, 5, 6 and names 'X', 'Y', 'Z'.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [Array] $Values,
        [Array] $Names
    )

    $Options.plotOptions = @{
        radialBar = [ordered] @{

            hollow = [ordered] @{
                margin       = 0
                size         = '70%'
                background   = '#fff'
                image        = 'undefined'
                imageOffsetX = 0
                imageOffsetY = 0
                position     = 'front'
                dropShadow   = @{
                    enabled = $true
                    top     = 3
                    left    = 0
                    blur    = 4
                    opacity = 0.24
                }
            }
            track  = [ordered] @{
                background  = '#fff'
                strokeWidth = '70%'
                margin      = 0  
                dropShadow  = [ordered] @{
                    enabled = $true
                    top     = -3
                    left    = 0
                    blur    = 4
                    opacity = 0.35
                }
            }

        }
    }

    $Options.fill = [ordered] @{
        type     = 'gradient'
        gradient = [ordered] @{
            shade          = 'dark'
            type           = 'horizontal'
            shadeIntensity = 0.5

            inverseColors  = $true
            opacityFrom    = 1
            opacityTo      = 1
            stops          = @(0, 100)
        }
    }

    $Options.stroke = [ordered] @{
        dashArray = 4
    }
}

function New-ChartInternalRadialType2 {
    <#
    .SYNOPSIS
    Creates a radial type 2 chart with customizable options.
 
    .DESCRIPTION
    The New-ChartInternalRadialType2 function creates a radial type 2 chart based on the provided values and names. It allows customization of various options such as plot angles, hollow settings, and more.
 
    .PARAMETER Options
    A dictionary containing options for the chart.
 
    .PARAMETER Values
    An array of values to be displayed on the chart.
 
    .PARAMETER Names
    An array of names corresponding to the values.
 
    .EXAMPLE
    New-ChartInternalRadialType2 -Options @{ plotOptions = @{ radialBar = @{ startAngle = -135; endAngle = 135; hollow = @{ margin = 0; size = '70%'; background = '#fff'; image = 'undefined'; imageOffsetX = 0; imageOffsetY = 0; position = 'front'; dropShadow = @{ enabled = $true; top = 3; left = 0; blur = 4; opacity = 0.24; } } } } } -Values @(10, 20, 30) -Names @('A', 'B', 'C')
    Creates a radial type 2 chart with values 10, 20, and 30 displayed as A, B, and C respectively.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [Array] $Values,
        [Array] $Names
    )
    $Options.plotOptions = @{
        radialBar = [ordered] @{

            hollow = [ordered] @{
                margin       = 0
                size         = '70%'
                background   = '#fff'
                image        = 'undefined'
                imageOffsetX = 0
                imageOffsetY = 0
                position     = 'front'
                dropShadow   = @{
                    enabled = $true
                    top     = 3
                    left    = 0
                    blur    = 4
                    opacity = 0.24
                }
            }

        }
    }
}

function New-ChartInternalSize {
    <#
    .SYNOPSIS
    Creates a new chart with specified height and width.
 
    .DESCRIPTION
    This function creates a new chart with the specified height and width values.
 
    .PARAMETER Options
    The options for the chart.
 
    .PARAMETER Height
    The height of the chart. Default is 350.
 
    .PARAMETER Width
    The width of the chart.
 
    .EXAMPLE
    $chartOptions = @{
        chart = @{
            type = 'bar'
        }
    }
    New-ChartInternalSize -Options $chartOptions -Height 400 -Width 600
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [nullable[int]] $Height = 350,
        [nullable[int]] $Width
    )
    if ($null -ne $Height) {
        $Options.chart.height = $Height
    }
    if ($null -ne $Width) {
        $Options.chart.width = $Width
    }
}
function New-ChartInternalSpark {
    <#
    .SYNOPSIS
    Creates a sparkline chart with customizable options.
 
    .DESCRIPTION
    The New-ChartInternalSpark function creates a sparkline chart with customizable options such as colors, values, and chart type. It generates a sparkline chart that can be used to visualize data trends in a compact format.
 
    .PARAMETER Options
    A dictionary containing various options for customizing the sparkline chart.
 
    .PARAMETER Color
    An array of colors to be used in the sparkline chart.
 
    .PARAMETER Values
    An array of values to be plotted on the sparkline chart.
 
    .EXAMPLE
    $options = @{
        chart = @{
            type = 'area'
            sparkline = @{
                enabled = $true
            }
        }
        stroke = @{
            curve = 'straight'
        }
        fill = @{
            opacity = 0.3
        }
    }
    $color = @('red', 'blue', 'green')
    $values = @(
        @{
            Name = 'Series 1'
            Values = @(10, 20, 30, 40, 50)
        }
        @{
            Name = 'Series 2'
            Values = @(5, 15, 25, 35, 45)
        }
    )
    New-ChartInternalSpark -Options $options -Color $color -Values $values
 
    #>


    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [string[]] $Color,
        [Array] $Values
    )
    if ($Values.Count -eq 0) {
        Write-Warning 'Get-ChartSpark - Values Empty'
    }

    if ($null -ne $Color) {
        $ColorRGB = ConvertFrom-Color -Color $Color
        $Options.colors = @($ColorRGB)
    }
    $Options.chart.type = 'area'
    $Options.chart.sparkline = @{
        enabled = $true
    }
    $Options.stroke = @{
        curve = 'straight'
    }
    $Options.fill = @{
        opacity = 0.3
    }
    $Options.series = @(

        foreach ($Data in $Values) {
            [ordered] @{
                name = $Data.Name
                data = @($Data.Values)
            }
        }
    )
}

Register-ArgumentCompleter -CommandName New-ChartInternalSpark -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartInternalStroke {
    <#
    .SYNOPSIS
    Creates a new internal stroke for a chart.
 
    .DESCRIPTION
    This function creates a new internal stroke for a chart based on the provided parameters.
 
    .PARAMETER Stroke
    Specifies the stroke properties to be set for the chart.
 
    .EXAMPLE
    $strokeParams = @{
        curve = "smooth"
        lineCap = "round"
        colors = "blue", "green", "red"
        width = 2
        dashArray = 5, 10
    }
    $newStroke = New-ChartInternalStroke -Stroke $strokeParams
    #>

    [CmdletBinding()]
    param(
        [Array] $Stroke
    )

    if ($Stroke.Count -eq 0) {
        $LineStroke = $null
    } else {
        $LineStroke = [ordered] @{
            curve     = $null
            lineCap   = $null
            colors    = $null
            width     = $null
            dashArray = $null
        }
        if ($Stroke.curve) {
            $LineStroke.curve = $Stroke.curve
        }
        if ($Stroke.lineCap) {
            $LineStroke.lineCap = $Stroke.lineCap
        }
        if ($Stroke.colors) {
            $LineStroke.colors = @($Stroke.colors)
        }
        if ($Stroke.width) {
            $LineStroke.width = $Stroke.width
        }
        if ($Stroke.dashArray) {
            $LineStroke.dashArray = $Stroke.dashArray
        }
    }
    $LineStroke
}
function New-ChartInternalStrokeDefinition {
    <#
    .SYNOPSIS
    Creates a new stroke definition for a chart with customizable options.
 
    .DESCRIPTION
    The New-ChartInternalStrokeDefinition function creates a new stroke definition for a chart based on the provided parameters. It allows customization of various stroke properties such as curve, width, color, line cap, and dash array.
 
    .PARAMETER Options
    The options for the stroke definition.
 
    .PARAMETER LineShow
    Specifies whether the stroke should be shown. Default is $true.
 
    .PARAMETER LineCurve
    An array of strings specifying the curve type of the stroke. Valid values are 'straight', 'smooth', 'stepline'.
 
    .PARAMETER LineWidth
    An array of integers specifying the width of the stroke.
 
    .PARAMETER LineCap
    An array of strings specifying the line cap style. Valid values are 'butt', 'square', 'round'.
 
    .PARAMETER LineColor
    An array of strings specifying the color of the stroke.
 
    .PARAMETER LineDash
    An array of integers specifying the dash array for the stroke.
 
    .EXAMPLE
    $options = @{
        stroke = @{
            show = $true
            curve = 'smooth'
            width = 2
            colors = 'blue', 'green', 'red'
            lineCap = 'round'
            dashArray = 5, 10
        }
    }
    New-ChartInternalStrokeDefinition -Options $options -LineShow $true -LineCurve 'smooth' -LineWidth 2 -LineCap 'round' -LineColor 'blue', 'green', 'red' -LineDash 5, 10
    # Creates a new stroke definition with specified properties.
 
    .EXAMPLE
    $options = @{
        stroke = @{
            show = $true
            curve = 'straight'
            width = 1
            colors = 'black'
            lineCap = 'butt'
            dashArray = 2, 4
        }
    }
    New-ChartInternalStrokeDefinition -Options $options -LineShow $true -LineCurve 'straight' -LineWidth 1 -LineCap 'butt' -LineColor 'black' -LineDash 2, 4
    # Creates another stroke definition with different properties.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $LineShow = $true,
        [ValidateSet('straight', 'smooth', 'stepline')][string[]] $LineCurve,
        [int[]] $LineWidth,
        [ValidateSet('butt', 'square', 'round')][string[]] $LineCap,
        [string[]] $LineColor,
        [int[]] $LineDash
    )

    $Options.stroke = [ordered] @{
        show = $LineShow
    }
    if ($LineCurve.Count -gt 0) {
        $Options.stroke.curve = $LineCurve
    }
    if ($LineWidth.Count -gt 0) {
        $Options.stroke.width = $LineWidth
    }
    if ($LineColor.Count -gt 0) {
        $Options.stroke.colors = @(ConvertFrom-Color -Color $LineColor)
    }
    if ($LineCap.Count -gt 0) {
        $Options.stroke.lineCap = $LineCap
    }
    if ($LineDash.Count -gt 0) {
        $Options.stroke.dashArray = $LineDash
    }
}
Register-ArgumentCompleter -CommandName New-ChartInternalStrokeDefinition -ParameterName LineColor -ScriptBlock $Script:ScriptBlockColors

function New-ChartInternalTheme {
    <#
    .SYNOPSIS
    Creates a new internal theme for the chart with specified options.
 
    .DESCRIPTION
    This function creates a new internal theme for the chart based on the provided options. It allows customization of the mode, palette, monochrome settings, color, shade intensity, and more.
 
    .PARAMETER Options
    Specifies the options for the new internal theme.
 
    .PARAMETER Mode
    Specifies the mode of the theme. Valid values are 'light' or 'dark'.
 
    .PARAMETER Palette
    Specifies the palette to be used. Valid values are 'palette1' through 'palette10'.
 
    .PARAMETER Monochrome
    Indicates whether monochrome mode is enabled.
 
    .PARAMETER Color
    Specifies the color to be used in the theme. Default is "DodgerBlue".
 
    .PARAMETER ShadeTo
    Specifies the shade to which the theme should transition. Valid values are 'light' or 'dark'.
 
    .PARAMETER ShadeIntensity
    Specifies the intensity of the shade. Default is 0.65.
 
    .EXAMPLE
    New-ChartInternalTheme -Options $Options -Mode 'light' -Palette 'palette1' -Monochrome -Color 'DodgerBlue' -ShadeTo 'light' -ShadeIntensity 0.65
    Creates a new internal theme with light mode, palette1, monochrome enabled, DodgerBlue color, light shade, and shade intensity of 0.65.
 
    .EXAMPLE
    New-ChartInternalTheme -Options $Options -Mode 'dark' -Palette 'palette5' -Color 'Crimson' -ShadeTo 'dark' -ShadeIntensity 0.8
    Creates a new internal theme with dark mode, palette5, monochrome disabled, Crimson color, dark shade, and shade intensity of 0.8.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [ValidateSet('light', 'dark')][string] $Mode,
        [ValidateSet(
            'palette1',
            'palette2',
            'palette3',
            'palette4',
            'palette5',
            'palette6',
            'palette7',
            'palette8',
            'palette9',
            'palette10'
        )
        ][string] $Palette = 'palette1',
        [switch] $Monochrome,
        [string] $Color = "DodgerBlue",
        [ValidateSet('light', 'dark')][string] $ShadeTo = 'light',
        [double] $ShadeIntensity = 0.65
    )

    $RGBColor = ConvertFrom-Color -Color $Color

    $Options.theme = [ordered] @{
        mode       = $Mode
        palette    = $Palette
        monochrome = [ordered] @{
            enabled        = $Monochrome.IsPresent
            color          = $RGBColor
            shadeTo        = $ShadeTo
            shadeIntensity = $ShadeIntensity
        }
    }
}

Register-ArgumentCompleter -CommandName New-ChartInternalTheme -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartInternalTimeLine {
    <#
    .SYNOPSIS
    Creates a time-based line chart with customizable options.
 
    .DESCRIPTION
    The New-ChartInternalTimeLine function creates a time-based line chart with customizable options such as colors, data points, and axis limits. It generates a line chart that visualizes data trends over time.
 
    .PARAMETER Options
    A dictionary containing various options for customizing the line chart.
 
    .PARAMETER Color
    The color to be used in the line chart.
 
    .PARAMETER Data
    An array of data points to be plotted on the line chart.
 
    .PARAMETER Min
    The minimum value for the y-axis.
 
    .PARAMETER Max
    The maximum value for the y-axis.
 
    .EXAMPLE
    $options = @{
        chart = @{
            type = 'line'
        }
    }
    $color = 'blue'
    $data = @(10, 20, 30, 40, 50)
    $min = 0
    $max = 60
    New-ChartInternalTimeLine -Options $options -Color $color -Data $data -Min $min -Max $max
    # Creates a time-based line chart with blue color, data points, and specified axis limits.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [string] $Color,
        [Array] $Data,
        [Int64] $Min,
        [Int64] $Max
    )

    if ($Data.Count -eq 0) {
        Write-Warning 'New-ChartInternalTimeLine - Data Empty'
    }

    if ($null -ne $Color) {
        $ColorRGB = ConvertFrom-Color -Color $Color
        $Options.colors = @($ColorRGB)
    }
    $Options.plotOptions = @{
        bar = @{
            horizontal  = $true
            distributed = $true
            dataLabels  = @{
                hideOverflowingLabels = $false
            }
        }
    }
    $Options.series = @(
        @{
            data = @(
                foreach ($Value in $Data) {
                    $Value
                }
            )

        }
    )
}

Register-ArgumentCompleter -CommandName New-ChartInternalSpark -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartInternalTitle {
    <#
    .SYNOPSIS
    Creates a new chart internal title with specified options.
 
    .DESCRIPTION
    This function creates a new chart internal title with the provided title text and alignment options.
 
    .PARAMETER Options
    An IDictionary object containing options for the chart title.
 
    .PARAMETER Title
    The text to be displayed as the chart title.
 
    .PARAMETER TitleAlignment
    Specifies the alignment of the chart title. Valid values are 'center', 'left', 'right', or 'default'.
 
    .EXAMPLE
    $chartOptions = [ordered]@{}
    New-ChartInternalTitle -Options $chartOptions -Title "Sales Data" -TitleAlignment "center"
    # Creates a chart title with the text "Sales Data" aligned to the center.
 
    .EXAMPLE
    $chartOptions = [ordered]@{}
    New-ChartInternalTitle -Options $chartOptions -Title "Revenue" -TitleAlignment "left"
    # Creates a chart title with the text "Revenue" aligned to the left.
 
    .EXAMPLE
    $chartOptions = [ordered]@{}
    New-ChartInternalTitle -Options $chartOptions -Title "Expenses" -TitleAlignment "right"
    # Creates a chart title with the text "Expenses" aligned to the right.
    #>


    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [string] $Title,
        [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default'
    )

    $Options.title = [ordered] @{ }
    if ($Title -ne '') {
        $Options.title.text = $Title
    }
    if ($TitleAlignment -ne 'default') {
        $Options.title.align = $TitleAlignment
    }
}
function New-ChartInternalToolbar {
    <#
    .SYNOPSIS
    Creates a new internal toolbar for a chart with specified options.
 
    .DESCRIPTION
    This function creates a new internal toolbar for a chart with the specified options. The toolbar can include tools for showing, downloading, selecting, zooming, panning, and resetting the chart.
 
    .PARAMETER Options
    The options for configuring the internal toolbar.
 
    .PARAMETER Show
    Indicates whether the toolbar should be shown. Default is $false.
 
    .PARAMETER Download
    Indicates whether the download tool should be included in the toolbar. Default is $false.
 
    .PARAMETER Selection
    Indicates whether the selection tool should be included in the toolbar. Default is $false.
 
    .PARAMETER Zoom
    Indicates whether the zoom tool should be included in the toolbar. Default is $false.
 
    .PARAMETER ZoomIn
    Indicates whether the zoom in tool should be included in the toolbar. Default is $false.
 
    .PARAMETER ZoomOut
    Indicates whether the zoom out tool should be included in the toolbar. Default is $false.
 
    .PARAMETER Pan
    Indicates whether the pan tool should be included in the toolbar. Default is $false.
 
    .PARAMETER Reset
    Indicates whether the reset tool should be included in the toolbar. Default is $false.
 
    .PARAMETER AutoSelected
    Specifies the default tool to be auto-selected. Valid values are 'zoom', 'selection', or 'pan'. Default is 'zoom'.
 
    .EXAMPLE
    New-ChartInternalToolbar -Options $Options -Show $true -Download $true -Selection $true -Zoom $true -ZoomIn $true -ZoomOut $true -Pan $true -Reset $true -AutoSelected 'zoom'
    Creates a new internal toolbar with all tools enabled and the zoom tool auto-selected.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $Show = $false,
        [bool] $Download = $false,
        [bool] $Selection = $false,
        [bool] $Zoom = $false,
        [bool] $ZoomIn = $false,
        [bool] $ZoomOut = $false,
        [bool] $Pan = $false,
        [bool] $Reset = $false,
        [ValidateSet('zoom', 'selection', 'pan')][string] $AutoSelected = 'zoom'
    )
    $Options.chart.toolbar = [ordered] @{
        show         = $show
        tools        = [ordered] @{
            download  = $Download
            selection = $Selection
            zoom      = $Zoom
            zoomin    = $ZoomIn
            zoomout   = $ZoomOut
            pan       = $Pan
            reset     = $Reset
        }
        autoSelected = $AutoSelected
    }
}
function New-ChartInternalToolTip {
    <#
    .SYNOPSIS
    Creates a new internal tooltip for a chart.
 
    .DESCRIPTION
    This function creates a new internal tooltip for a chart with the specified options.
 
    .PARAMETER Options
    Specifies the options for the tooltip.
 
    .PARAMETER Enabled
    Indicates whether the tooltip is enabled.
 
    .PARAMETER y
    Specifies the y-coordinate for the tooltip.
 
    .PARAMETER x
    Specifies the x-coordinate for the tooltip.
 
    .EXAMPLE
    $tooltipOptions = @{
        tooltip = @{
            enabled = $true
            x = 10
            y = 20
        }
    }
    New-ChartInternalToolTip -Options $tooltipOptions -Enabled $true -x 10 -y 20
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $Enabled,
        [System.Collections.IDictionary] $y,
        [System.Collections.IDictionary] $x

    )

    if (-not $Options.tooltip) {
        $Options.tooltip = @{}
    }
    $Options.tooltip.enabled = $Enabled
    $Options.tooltip.x = $x
    $Options.tooltip.y = $y

}
function New-ChartInternalZoom {
    <#
    .SYNOPSIS
    Creates a new internal zoom feature for a chart.
 
    .DESCRIPTION
    This function creates a new internal zoom feature for a chart with the specified options. The zoom feature allows users to zoom in on specific areas of the chart for a closer look.
 
    .PARAMETER Options
    Specifies the options for the zoom feature.
 
    .PARAMETER Enabled
    Indicates whether the zoom feature is enabled.
 
    .EXAMPLE
    $zoomOptions = @{}
    New-ChartInternalZoom -Options $zoomOptions -Enabled $true
    # Enables the zoom feature for the chart.
 
    .EXAMPLE
    $zoomOptions = @{}
    New-ChartInternalZoom -Options $zoomOptions -Enabled $false
    # Disables the zoom feature for the chart.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [switch] $Enabled
    )
    if ($Enabled) {
        $Options.chart.zoom = @{
            type    = 'x'
            enabled = $Enabled.IsPresent
        }
    }
}
function New-HTMLChartArea {
    <#
    .SYNOPSIS
    Creates a new HTML chart area with customizable options.
 
    .DESCRIPTION
    The New-HTMLChartArea function creates a new HTML chart area with various customizable options for creating interactive charts.
 
    .PARAMETER Chart
    Specifies the chart configuration settings.
 
    .PARAMETER Height
    Specifies the height of the chart area. Default is 350.
 
    .PARAMETER DataLabelsEnabled
    Indicates whether data labels are enabled. Default is $true.
 
    .PARAMETER DataLabelsOffsetX
    Specifies the horizontal offset for data labels. Default is -6.
 
    .PARAMETER DataLabelsFontSize
    Specifies the font size for data labels. Default is '12px'.
 
    .PARAMETER DataLabelsColor
    Specifies the color of data labels.
 
    .PARAMETER DataCategoriesType
    Specifies the type of data categories. Valid values are 'datetime', 'category', or 'numeric'. Default is 'category'.
 
    .PARAMETER LineCurve
    Specifies the curve type for lines. Valid values are 'straight', 'smooth', or 'stepline'. Default is 'straight'.
 
    .PARAMETER LineWidth
    Specifies the width of lines.
 
    .PARAMETER LineColor
    Specifies the color of lines.
 
    .PARAMETER GridColors
    Specifies the colors of the grid lines.
 
    .PARAMETER GridOpacity
    Specifies the opacity of the grid lines.
 
    .PARAMETER LegendPosition
    Specifies the position of the legend. Valid values are 'top', 'topRight', 'left', 'right', 'bottom', or 'default'. Default is 'default'.
 
    .PARAMETER TitleX
    Specifies the title for the X-axis.
 
    .PARAMETER TitleY
    Specifies the title for the Y-axis.
 
    .PARAMETER MarkerSize
    Specifies the size of markers.
 
    .PARAMETER Data
    Specifies the data points for the chart.
 
    .PARAMETER DataNames
    Specifies the names of the data series.
 
    .PARAMETER DataLegend
    Specifies the legends for the data series.
 
    .PARAMETER Zoom
    Indicates whether zoom functionality is enabled.
 
    .PARAMETER ChartAxisY
    Specifies the configuration settings for the Y-axis.
 
    .PARAMETER Legend
    Specifies the legend settings.
 
    .PARAMETER Title
    Specifies the title of the chart.
 
    .PARAMETER TitleAlignment
    Specifies the alignment of the chart title. Valid values are 'center', 'left', 'right', or 'default'. Default is 'default'.
 
    .PARAMETER PatternedColors
    Indicates whether to use patterned colors.
 
    .PARAMETER GradientColors
    Indicates whether to use gradient colors.
 
    .PARAMETER GridOptions
    Specifies additional grid options.
 
    .PARAMETER Toolbar
    Specifies the toolbar settings.
 
    .PARAMETER Theme
    Specifies the theme settings.
 
    .PARAMETER Design
    Specifies the design settings.
 
    .EXAMPLE
    New-HTMLChartArea -Chart @{ type = 'line'; data = @((1, 10), (2, 20), (3, 30)) } -Title 'Sample Line Chart' -DataLabelsEnabled $true
 
    Creates a new HTML line chart with sample data points and enables data labels.
 
    .EXAMPLE
    New-HTMLChartArea -Chart @{ type = 'bar'; data = @((1, 10), (2, 20), (3, 30)) } -Title 'Sample Bar Chart' -DataLabelsEnabled $false
 
    Creates a new HTML bar chart with sample data points and disables data labels.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Chart,
        #[nullable[int]] $Height = 350,

        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [string[]] $DataLabelsColor,
        [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category',

        [ValidateSet('straight', 'smooth', 'stepline')] $LineCurve = 'straight',
        [int] $LineWidth,
        [string[]] $LineColor,

        [string[]] $GridColors,
        [double] $GridOpacity,

        [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', 'default')][string] $LegendPosition = 'default',

        [string] $TitleX,
        [string] $TitleY,

        [int] $MarkerSize,

        [Array] $Data,
        [Array] $DataNames,
        [Array] $DataLegend,

        [switch] $Zoom,

        [System.Collections.IDictionary] $ChartAxisY,
        [System.Collections.IDictionary] $Legend,

        [string] $Title,
        [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default',
        [switch] $PatternedColors,
        [switch] $GradientColors,
        [System.Collections.IDictionary] $GridOptions,
        [System.Collections.IDictionary] $Toolbar,
        [System.Collections.IDictionary] $Theme,
        [System.Collections.IDictionary] $Design
    )

    $Options = [ordered] @{ }
    $Options.chart = $Chart
    if ($ChartAxisY) {
        $Options.yaxis = $ChartAxisY
    }
    if ($Legend) {
        $Options.legend = $Legend
    }
    New-ChartInternalArea -Options $Options -Data $Data -DataNames $DataNames

    New-ChartInternalStrokeDefinition -Options $Options `
        -LineShow $true `
        -LineCurve $LineCurve `
        -LineWidth $LineWidth `
        -LineColor $LineColor

    New-ChartInternalDataLabels -Options $Options `
        -DataLabelsEnabled $DataLabelsEnabled `
        -DataLabelsOffsetX $DataLabelsOffsetX `
        -DataLabelsFontSize $DataLabelsFontSize `
        -DataLabelsColor $DataLabelsColor

    New-ChartInternalAxisX -Options $Options `
        -Title $TitleX `
        -DataCategoriesType $DataCategoriesType `
        -DataCategories $DataLegend

    New-ChartInternalMarker -Options $Options -MarkerSize $MarkerSize
    New-ChartInternalZoom -Options $Options -Enabled:$Zoom

    if ($Design.fill.pattern) {
        $Options.fill = [ordered] @{
            type    = 'pattern'
            pattern = $Design.fill.pattern
        }
    } elseif ($Design.fill.gradient) {
        $Options.fill = [ordered] @{
            type     = 'gradient'
            gradient = $Design.fill.gradient
        }
    } elseif ($PatternedColors) {
        New-ChartInternalPattern
    } elseif ($GradientColors) {
        New-ChartInternalGradient
    }
    New-ChartInternalTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment
    New-ChartInternalSize -Options $Options -Height $Height -Width $Width
    if ($GridOptions) {
        New-ChartInternalGrid -Options $Options @GridOptions }
    if ($Theme) {
        New-ChartInternalTheme -Options $Options @Theme }
    if ($Toolbar) {
        New-ChartInternalToolbar -Options $Options @Toolbar -Show $true }
    New-ApexChart -Options $Options
}
Register-ArgumentCompleter -CommandName New-HTMLChartArea -ParameterName DataLabelsColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLChartArea -ParameterName LineColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLChartArea -ParameterName GridColors -ScriptBlock $Script:ScriptBlockColors
function New-HTMLChartBar {
    <#
    .SYNOPSIS
    Creates a new HTML bar chart with customizable options.
 
    .DESCRIPTION
    This function generates an HTML bar chart with various customization options such as colors, data labels, axis settings, and more.
 
    .PARAMETER Chart
    Specifies the main configuration settings for the chart.
 
    .PARAMETER Type
    Specifies the type of bar chart. Valid values are 'bar', 'barStacked', 'barStacked100Percent'. Default is 'bar'.
 
    .PARAMETER Colors
    Specifies an array of colors to be used in the chart.
 
    .PARAMETER Horizontal
    Indicates whether the chart should be displayed horizontally. Default is $true.
 
    .PARAMETER DataLabelsEnabled
    Indicates whether data labels should be displayed on the chart. Default is $true.
 
    .PARAMETER DataLabelsOffsetX
    Specifies the horizontal offset for data labels. Default is -6.
 
    .PARAMETER DataLabelsFontSize
    Specifies the font size for data labels. Default is '12px'.
 
    .PARAMETER DataLabelsColor
    Specifies the color for data labels.
 
    .PARAMETER Distributed
    Indicates whether the data is distributed evenly across the chart.
 
    .PARAMETER Data
    Specifies an array of data points for the chart.
 
    .PARAMETER DataNames
    Specifies an array of names for the data points.
 
    .PARAMETER DataLegend
    Specifies an array of legends for the data points.
 
    .PARAMETER ChartAxisX
    Specifies the configuration settings for the X-axis of the chart.
 
    .PARAMETER ChartAxisY
    Specifies the configuration settings for the Y-axis of the chart.
 
    .PARAMETER Title
    Specifies the title of the chart.
 
    .PARAMETER SubTitle
    Specifies the subtitle of the chart.
 
    .PARAMETER Legend
    Specifies the legend settings for the chart.
 
    .PARAMETER PatternedColors
    Indicates whether patterned colors should be used in the chart.
 
    .PARAMETER GradientColors
    Indicates whether gradient colors should be used in the chart.
 
    .PARAMETER GridOptions
    Specifies the grid options for the chart.
 
    .PARAMETER Toolbar
    Specifies the toolbar settings for the chart.
 
    .PARAMETER Theme
    Specifies the theme settings for the chart.
 
    .PARAMETER Events
    Specifies any events to be associated with the chart.
 
    .PARAMETER Design
    Specifies the design settings for the chart.
 
    .EXAMPLE
    New-HTMLChartBar -Chart @{ type = 'bar'; data = @([1, 2, 3]); categories = @('A', 'B', 'C') } -Title @{ text = 'Sample Chart' } -Colors @('red', 'blue', 'green')
 
    Creates a new bar chart with specified data, categories, title, and colors.
 
    .EXAMPLE
    New-HTMLChartBar -Chart @{ type = 'barStacked'; data = @([10, 20, 30]); categories = @('X', 'Y', 'Z') } -Title @{ text = 'Stacked Chart' } -Colors @('orange', 'purple', 'yellow')
 
    Creates a new stacked bar chart with specified data, categories, title, and colors.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Chart,
        [ValidateSet('bar', 'barStacked', 'barStacked100Percent')] $Type = 'bar',
        [string[]] $Colors,

        [bool] $Horizontal = $true,
        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [string] $DataLabelsColor,

        [switch] $Distributed,
        [Array] $Data,
        [Array] $DataNames,
        [Array] $DataLegend,

        [System.Collections.IDictionary] $ChartAxisX,
        [Array] $ChartAxisY,
        [System.Collections.IDictionary] $Title,
        [System.Collections.IDictionary] $SubTitle,
        [System.Collections.IDictionary] $Legend,

        [switch] $PatternedColors,
        [switch] $GradientColors,
        [System.Collections.IDictionary] $GridOptions,
        [System.Collections.IDictionary] $Toolbar,
        [System.Collections.IDictionary] $Theme,
        [Object] $Events,
        [System.Collections.IDictionary] $Design
    )

    $Options = [ordered] @{ }
    $Options.chart = $Chart
    if ($Title) {
        $Options.title = $Title
    }
    if ($SubTitle) {
        $Options.subtitle = $SubTitle
    }
    if ($Legend) {
        $Options.legend = $Legend
    }

    if ($ChartAxisX) {
        New-ChartInternalAxisX -Options $Options @ChartAxisX
    }
    if ($ChartAxisY) {

        $Options.yaxis = $ChartAxisY[0]
    }

    New-ChartInternalBar -Options $Options -Horizontal $Horizontal -DataLabelsEnabled $DataLabelsEnabled `
        -DataLabelsOffsetX $DataLabelsOffsetX -DataLabelsFontSize $DataLabelsFontSize -DataLabelsColor $DataLabelsColor `
        -Data $Data -DataNames $DataNames -DataLegend $DataLegend `
        -Type $Type -Distributed:$Distributed

    New-ChartInternalColors -Options $Options -Colors $Colors

    if ($Design.fill.pattern) {
        $Options.fill = [ordered] @{
            type    = 'pattern'
            pattern = $Design.fill.pattern
        }
    } elseif ($Design.fill.gradient) {
        $Options.fill = [ordered] @{
            type     = 'gradient'
            gradient = $Design.fill.gradient
        }
    } elseif ($PatternedColors) {
        New-ChartInternalPattern
    } elseif ($GradientColors) {
        New-ChartInternalGradient
    }
    if ($GridOptions) {
        New-ChartInternalGrid -Options $Options @GridOptions }
    if ($Theme) {
        New-ChartInternalTheme -Options $Options @Theme }
    if ($Toolbar) {
        New-ChartInternalToolbar -Options $Options @Toolbar -Show $true }
    New-ApexChart -Options $Options -Events $Events
}

Register-ArgumentCompleter -CommandName New-HTMLChartBar -ParameterName Colors -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLChartBar -ParameterName DataLabelsColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLChartLine {
    <#
    .SYNOPSIS
    Creates a new HTML line chart with customizable options.
 
    .DESCRIPTION
    The New-HTMLChartLine function generates an HTML line chart with various customization options such as title, subtitle, legend, axis labels, colors, and more.
 
    .PARAMETER Chart
    Specifies the main chart configuration settings.
 
    .PARAMETER Stroke
    Specifies the stroke settings for the chart lines.
 
    .PARAMETER Series
    Specifies the data series for the chart.
 
    .PARAMETER DataLabel
    Specifies the data label settings.
 
    .PARAMETER Markers
    Specifies the marker settings for data points.
 
    .PARAMETER ChartAxisX
    Specifies the X-axis configuration settings.
 
    .PARAMETER ChartAxisY
    Specifies the Y-axis configuration settings.
 
    .PARAMETER Title
    Specifies the title of the chart.
 
    .PARAMETER SubTitle
    Specifies the subtitle of the chart.
 
    .PARAMETER Legend
    Specifies the legend settings.
 
    .PARAMETER PatternedColors
    Indicates whether to use patterned colors for the chart.
 
    .PARAMETER GradientColors
    Indicates whether to use gradient colors for the chart.
 
    .PARAMETER Colors
    Specifies custom colors for the chart elements.
 
    .PARAMETER GridOptions
    Specifies the grid options for the chart.
 
    .PARAMETER Toolbar
    Specifies the toolbar settings for the chart.
 
    .PARAMETER Theme
    Specifies the theme settings for the chart.
 
    .PARAMETER Events
    Specifies any events associated with the chart.
 
    .PARAMETER Design
    Specifies the design settings for the chart.
 
    .EXAMPLE
    New-HTMLChartLine -Chart @{width=800;height=400} -Stroke @("solid") -Series @(@{name="Series 1";data=@(10, 20, 30)}) -Title @{text="Line Chart"} -SubTitle @{text="Example"} -Legend @{show=$true} -ChartAxisX @{categories=@("Jan", "Feb", "Mar")} -ChartAxisY @("Y-Axis Label 1", "Y-Axis Label 2") -PatternedColors -GridOptions @{show=$true} -Toolbar @{show=$true} -Theme @{mode="light"} -Design @{colors=@("#FF5733", "#33FF57")}
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Chart,
        [Array] $Stroke,
        [Array] $Series,

        [System.Collections.IDictionary] $DataLabel,
        [System.Collections.IDictionary] $Markers,
        [System.Collections.IDictionary] $ChartAxisX,
        [Array] $ChartAxisY,
        [System.Collections.IDictionary] $Title,
        [System.Collections.IDictionary] $SubTitle,
        [System.Collections.IDictionary] $Legend,

        [switch] $PatternedColors,
        [switch] $GradientColors,
        [string[]] $Colors,
        [System.Collections.IDictionary] $GridOptions,
        [System.Collections.IDictionary] $Toolbar,
        [System.Collections.IDictionary] $Theme,
        [Object] $Events,
        [System.Collections.IDictionary] $Design
    )

    $Options = [ordered] @{ }
    $Options.chart = $Chart
    $Options['chart']['type'] = 'line'

    if ($Title) {
        $Options.title = $Title
    }
    if ($SubTitle) {
        $Options.subtitle = $SubTitle
    }
    if ($Legend) {
        $Options.legend = $Legend
    }
    if ($ChartAxisX) {
        New-ChartInternalAxisX -Options $Options @ChartAxisX
    }
    if ($ChartAxisY) {
        $Options.yaxis = $ChartAxisY
    }

    $Options.series = $DataSeries
    $Options.stroke = New-ChartInternalStroke -Stroke $Stroke

    $Options['colors'] = @($Colors)

    $Options.dataLabels = $DataLabel
    $Options.markers = $Markers

    if ($Design.fill.pattern) {
        $Options.fill = [ordered] @{
            type    = 'pattern'
            pattern = $Design.fill.pattern
        }
    } elseif ($Design.fill.gradient) {
        $Options.fill = [ordered] @{
            type     = 'gradient'
            gradient = $Design.fill.gradient
        }
    } elseif ($PatternedColors) {
        New-ChartInternalPattern
    } elseif ($GradientColors) {
        New-ChartInternalGradient
    }
    if ($GridOptions) {
        New-ChartInternalGrid -Options $Options @GridOptions }
    if ($Theme) {
        New-ChartInternalTheme -Options $Options @Theme }
    if ($Toolbar) {
        New-ChartInternalToolbar -Options $Options @Toolbar -Show $true }
    New-ApexChart -Options $Options -Events $Events
}

Register-ArgumentCompleter -CommandName New-HTMLChartLine -ParameterName DataLabelsColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLChartLine -ParameterName LineColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLChartLine -ParameterName GridColors -ScriptBlock $Script:ScriptBlockColors
function New-HTMLChartPie {
    <#
    .SYNOPSIS
    Creates a new HTML pie chart with customizable options.
 
    .DESCRIPTION
    The New-HTMLChartPie function generates an HTML pie chart with various customization options such as data labels, colors, titles, legends, and more.
 
    .PARAMETER Chart
    Specifies the chart configuration settings.
 
    .PARAMETER Type
    Specifies the type of chart to create.
 
    .PARAMETER DataLabelsEnabled
    Indicates whether data labels are enabled. Default is $true.
 
    .PARAMETER DataLabelsOffsetX
    Specifies the horizontal offset for data labels.
 
    .PARAMETER DataLabelsFontSize
    Specifies the font size for data labels.
 
    .PARAMETER DataLabelsColor
    Specifies the color for data labels.
 
    .PARAMETER Data
    Specifies the data values for the chart.
 
    .PARAMETER DataNames
    Specifies the names corresponding to the data values.
 
    .PARAMETER Title
    Specifies the title settings for the chart.
 
    .PARAMETER SubTitle
    Specifies the subtitle settings for the chart.
 
    .PARAMETER Legend
    Specifies the legend settings for the chart.
 
    .PARAMETER Colors
    Specifies the colors to be used in the chart.
 
    .PARAMETER PatternedColors
    Indicates whether patterned colors are used.
 
    .PARAMETER GradientColors
    Indicates whether gradient colors are used.
 
    .PARAMETER GridOptions
    Specifies the grid options for the chart.
 
    .PARAMETER Toolbar
    Specifies the toolbar settings for the chart.
 
    .PARAMETER Theme
    Specifies the theme settings for the chart.
 
    .PARAMETER Events
    Specifies the events to be associated with the chart.
 
    .PARAMETER Design
    Specifies the design settings for the chart.
 
    .EXAMPLE
    New-HTMLChartPie -Chart @{ type = 'pie' } -Type 'pie' -Data @(10, 20, 30) -DataNames @('A', 'B', 'C') -Colors @('red', 'green', 'blue')
 
    Creates a new pie chart with specified data values and names using custom colors.
 
    .EXAMPLE
    New-HTMLChartPie -Chart @{ type = 'donut' } -Type 'donut' -Data @(25, 35, 40) -DataNames @('X', 'Y', 'Z') -Colors @('orange', 'purple', 'yellow')
 
    Creates a new donut chart with specified data values and names using custom colors.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Chart,
        [string] $Type,
        #[nullable[int]] $Height = 350,
        #[nullable[int]] $Width,

        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [string[]] $DataLabelsColor,
        [Array] $Data,
        [Array] $DataNames,

        [System.Collections.IDictionary] $Title,
        [System.Collections.IDictionary] $SubTitle,
        [System.Collections.IDictionary] $Legend,

        [string[]] $Colors,

        [switch] $PatternedColors,
        [switch] $GradientColors,
        [System.Collections.IDictionary] $GridOptions,
        [System.Collections.IDictionary] $Toolbar,
        [System.Collections.IDictionary] $Theme,
        [Object] $Events,
        [System.Collections.IDictionary] $Design

    )
    $Options = [ordered] @{ }
    $Options.chart = $Chart
    if ($Title) {
        $Options.title = $Title
    }
    if ($SubTitle) {
        $Options.subtitle = $SubTitle
    }
    if ($Legend) {
        $Options.legend = $Legend
    }
    New-ChartInternalPie -Options $Options -Names $DataNames -Values $Data -Type $Type
    New-ChartInternalColors -Options $Options -Colors $Colors

    if ($Design.fill.pattern) {
        $Options.fill = [ordered] @{
            type    = 'pattern'
            pattern = $Design.fill.pattern
        }
    } elseif ($Design.fill.gradient) {
        $Options.fill = [ordered] @{
            type     = 'gradient'
            gradient = $Design.fill.gradient
        }
    } elseif ($PatternedColors) {
        New-ChartInternalPattern
    } elseif ($GradientColors) {
        New-ChartInternalGradient
    }
    New-ChartInternalSize -Options $Options -Height $Height -Width $Width
    if ($GridOptions) {
        New-ChartInternalGrid -Options $Options @GridOptions }
    if ($Theme) {
        New-ChartInternalTheme -Options $Options @Theme }
    if ($Toolbar) {
        New-ChartInternalToolbar -Options $Options @Toolbar -Show $true }
    New-ApexChart -Options $Options -Events $Events
}

Register-ArgumentCompleter -CommandName New-HTMLChartPie -ParameterName DataLabelsColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLChartPie -ParameterName Colors -ScriptBlock $Script:ScriptBlockColors
function New-HTMLChartRadial {
    <#
    .SYNOPSIS
    Creates a radial chart in HTML format.
 
    .DESCRIPTION
    This function generates a radial chart in HTML format based on the provided data and options.
 
    .PARAMETER Chart
    Specifies the chart configuration settings.
 
    .PARAMETER DataNames
    Specifies an array of names for the data points.
 
    .PARAMETER Data
    Specifies an array of values for the data points.
 
    .PARAMETER Type
    Specifies the type of radial chart to create.
 
    .PARAMETER LabelAverage
    Specifies the label for the average value on the chart.
 
    .PARAMETER PlotOptions
    Specifies additional plot options for the chart.
 
    .PARAMETER Title
    Specifies the title of the chart.
 
    .PARAMETER SubTitle
    Specifies the subtitle of the chart.
 
    .PARAMETER Legend
    Specifies the legend configuration settings.
 
    .PARAMETER Colors
    Specifies an array of colors for the chart.
 
    .PARAMETER PatternedColors
    Indicates whether to use patterned colors for the chart.
 
    .PARAMETER GradientColors
    Indicates whether to use gradient colors for the chart.
 
    .PARAMETER GridOptions
    Specifies the grid options for the chart.
 
    .PARAMETER Toolbar
    Specifies the toolbar configuration settings.
 
    .PARAMETER Theme
    Specifies the theme for the chart.
 
    .PARAMETER Events
    Specifies the events to be associated with the chart.
 
    .PARAMETER Design
    Specifies the design settings for the chart.
 
    .EXAMPLE
    New-HTMLChartRadial -Chart @{ type = 'radial' } -DataNames @('A', 'B', 'C') -Data @(10, 20, 30) -Type 'line' -LabelAverage 'Average' -Title @{ text = 'Radial Chart' } -Colors @('#FF5733', '#33FF57', '#3357FF')
 
    Creates a radial chart with three data points labeled A, B, and C, with corresponding values 10, 20, and 30, using a line type, and a custom title with specified colors.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Chart,
        #[nullable[int]] $Height = 350,
        #[nullable[int]] $Width,

        [Array] $DataNames,
        [Array] $Data,
        [string] $Type,

        [string] $LabelAverage,

        [System.Collections.IDictionary] $PlotOptions,

        [System.Collections.IDictionary] $Title,
        [System.Collections.IDictionary] $SubTitle,
        [System.Collections.IDictionary] $Legend,

        [string[]] $Colors,
        [switch] $PatternedColors,
        [switch] $GradientColors,
        [System.Collections.IDictionary] $GridOptions,
        [System.Collections.IDictionary] $Toolbar,
        [System.Collections.IDictionary] $Theme,
        [Object] $Events,
        [System.Collections.IDictionary] $Design
    )

    $Options = [ordered] @{ }
    $Options.chart = $Chart
    if ($Title) {
        $Options.title = $Title
    }
    if ($SubTitle) {
        $Options.subtitle = $SubTitle
    }
    if ($Legend) {
        $Options.legend = $Legend
    }
    New-ChartInternalRadial -Options $Options -Names $DataNames -Values $Data -Type $Type
    if ($PlotOptions) {
        $Options.plotOptions = $PlotOptions
    }

    New-ChartInternalRadialDataLabels -Options $Options -Label $LabelAverage

    New-ChartInternalColors -Options $Options -Colors $Colors

    if ($Design.fill.pattern) {
        $Options.fill = [ordered] @{
            type    = 'pattern'
            pattern = $Design.fill.pattern
        }
    } elseif ($Design.fill.gradient) {
        $Options.fill = [ordered] @{
            type     = 'gradient'
            gradient = $Design.fill.gradient
        }
    } elseif ($PatternedColors) {
        New-ChartInternalPattern
    } elseif ($GradientColors) {
        New-ChartInternalGradient
    }

    if ($GridOptions) {
        New-ChartInternalGrid -Options $Options @GridOptions }
    if ($Theme) {
        New-ChartInternalTheme -Options $Options @Theme }
    if ($Toolbar) {
        New-ChartInternalToolbar -Options $Options @Toolbar -Show $true }
    New-ApexChart -Options $Options -Events $Events
}

function New-HTMLChartSpark {
    <#
    .SYNOPSIS
    Creates a new HTML chart spark with customizable options.
 
    .DESCRIPTION
    This function generates an HTML chart spark with various customizable options such as chart type, title, subtitle, legend, colors, grid options, and more.
 
    .PARAMETER Chart
    Specifies the main chart configuration settings.
 
    .PARAMETER ChartAxisX
    Specifies the X-axis configuration settings for the chart.
 
    .PARAMETER Title
    Specifies the title of the chart.
 
    .PARAMETER SubTitle
    Specifies the subtitle of the chart.
 
    .PARAMETER Legend
    Specifies the legend configuration settings.
 
    .PARAMETER Data
    Specifies the data to be displayed in the spark chart.
 
    .PARAMETER Colors
    Specifies the colors to be used in the chart.
 
    .PARAMETER PatternedColors
    Indicates whether patterned colors should be used.
 
    .PARAMETER GradientColors
    Indicates whether gradient colors should be used.
 
    .PARAMETER GridOptions
    Specifies the grid options for the chart.
 
    .PARAMETER Toolbar
    Specifies the toolbar configuration settings.
 
    .PARAMETER Theme
    Specifies the theme of the chart.
 
    .PARAMETER Events
    Specifies any events to be associated with the chart.
 
    .PARAMETER Design
    Specifies the design settings for the chart.
 
    .EXAMPLE
    New-HTMLChartSpark -Chart @{ type = 'line' } -ChartAxisX @{ categories = @('Jan', 'Feb', 'Mar') } -Title @{ text = 'Monthly Sales' } -Data @(100, 200, 150) -Colors @('red', 'blue', 'green')
 
    Creates a new HTML chart spark with a line chart type, X-axis categories for Jan, Feb, Mar, a title of 'Monthly Sales', data points of 100, 200, 150, and colors red, blue, green.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Chart,
        [System.Collections.IDictionary] $ChartAxisX,
        #[nullable[int]] $Height = 350,
        #[nullable[int]] $Width,

        [System.Collections.IDictionary] $Title,
        [System.Collections.IDictionary] $SubTitle,
        [System.Collections.IDictionary] $Legend,

        # Data to display in Spark
        [Array] $Data,

        [string[]] $Colors,

        [switch] $PatternedColors,
        [switch] $GradientColors,
        [System.Collections.IDictionary] $GridOptions,
        [System.Collections.IDictionary] $Toolbar,
        [System.Collections.IDictionary] $Theme,
        [Object] $Events,
        [System.Collections.IDictionary] $Design
    )

    $Options = [ordered] @{ }
    $Options.chart = $Chart
    if ($Title) {
        $Options.title = $Title
    }
    if ($SubTitle) {
        $Options.subtitle = $SubTitle
    }
    if ($Legend) {
        $Options.legend = $Legend
    }
    if ($ChartAxisX) {
        New-ChartInternalAxisX -Options $Options @ChartAxisX
    }
    New-ChartInternalSpark -Options $Options -Color $Colors -Values $Data

    if ($Design.fill.pattern) {
        $Options.fill = [ordered] @{
            type    = 'pattern'
            pattern = $Design.fill.pattern
        }
    } elseif ($Design.fill.gradient) {
        $Options.fill = [ordered] @{
            type     = 'gradient'
            gradient = $Design.fill.gradient
        }
    } elseif ($PatternedColors) {
        New-ChartInternalPattern
    } elseif ($GradientColors) {
        New-ChartInternalGradient
    }

    if ($GridOptions) {
        New-ChartInternalGrid -Options $Options @GridOptions }
    if ($Theme) {
        New-ChartInternalTheme -Options $Options @Theme }
    if ($Toolbar) {
        New-ChartInternalToolbar -Options $Options @Toolbar -Show $true }
    New-ApexChart -Options $Options -Events $Events
}

Register-ArgumentCompleter -CommandName New-HTMLChartSpark -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-HTMLChartTimeLine {
    <#
    .SYNOPSIS
    Creates a new HTML timeline chart with customizable options.
 
    .DESCRIPTION
    This function generates an HTML timeline chart with various customization options such as title, subtitle, legend, data, colors, grid options, toolbar, theme, axis settings, tooltips, data labels, events, and design.
 
    .PARAMETER Chart
    Specifies the main chart configuration settings.
 
    .PARAMETER Title
    Specifies the title of the chart.
 
    .PARAMETER SubTitle
    Specifies the subtitle of the chart.
 
    .PARAMETER Legend
    Specifies the legend settings for the chart.
 
    .PARAMETER Data
    Specifies the data points to be displayed on the chart.
 
    .PARAMETER PatternedColors
    Indicates whether to use patterned colors for the chart.
 
    .PARAMETER GradientColors
    Indicates whether to use gradient colors for the chart.
 
    .PARAMETER GridOptions
    Specifies the grid options for the chart.
 
    .PARAMETER Toolbar
    Specifies the toolbar settings for the chart.
 
    .PARAMETER Theme
    Specifies the theme to be applied to the chart.
 
    .PARAMETER ChartAxisX
    Specifies the X-axis settings for the chart.
 
    .PARAMETER ChartAxisY
    Specifies the Y-axis settings for the chart.
 
    .PARAMETER ChartToolTip
    Specifies the tooltip settings for the chart.
 
    .PARAMETER DataLabel
    Specifies the data label settings for the chart.
 
    .PARAMETER Events
    Specifies any events to be associated with the chart.
 
    .PARAMETER Design
    Specifies the design options for the chart.
 
    .EXAMPLE
    $chartData = @{
        labels = @("Jan", "Feb", "Mar", "Apr")
        datasets = @(
            @{
                label = "Dataset 1"
                data = @([10, 20, 15, 25])
            },
            @{
                label = "Dataset 2"
                data = @([5, 15, 10, 20])
            }
        )
    }
 
    $chartOptions = @{
        Chart = @{
            height = 300
        }
        Title = @{
            text = "Monthly Sales"
        }
        SubTitle = @{
            text = "2021"
        }
        Legend = @{
            show = $true
        }
        Data = $chartData
        PatternedColors = $true
        GridOptions = @{
            show = $true
        }
        Theme = @{
            mode = "dark"
        }
        ChartAxisX = @{
            categories = @("Jan", "Feb", "Mar", "Apr")
        }
        ChartAxisY = @(
            @{
                seriesName = "Sales"
            }
        )
        ChartToolTip = @{
            enabled = $true
        }
        DataLabel = @{
            enabled = $true
        }
        Design = @{
            colors = @("#FF5733", "#33FF57")
        }
    }
 
    New-HTMLChartTimeLine @chartOptions
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Chart,

        [System.Collections.IDictionary] $Title,
        [System.Collections.IDictionary] $SubTitle,
        [System.Collections.IDictionary] $Legend,

        [Array] $Data,
        [switch] $PatternedColors,
        [switch] $GradientColors,

        [System.Collections.IDictionary] $GridOptions,
        [System.Collections.IDictionary] $Toolbar,
        [System.Collections.IDictionary] $Theme,

        [System.Collections.IDictionary] $ChartAxisX,
        [Array] $ChartAxisY,

        [System.Collections.IDictionary] $ChartToolTip,
        [System.Collections.IDictionary] $DataLabel,
        [Object] $Events,
        [System.Collections.IDictionary] $Design
    )

    $Options = [ordered] @{}
    $Options.chart = $Chart
    $Options['chart']['type'] = 'rangeBar'

    if ($Title) {
        $Options.title = $Title
    }
    if ($SubTitle) {
        $Options.subtitle = $SubTitle
    }
    if ($Legend) {
        $Options.legend = $Legend
    }

    if (-not $ChartAxisX) {
        $ChartAxisX = [ordered] @{}
    }
    $ChartAxisX.type = "datetime"
    New-ChartInternalAxisX -Options $Options @ChartAxisX

    if ($ChartAxisY) {

        $Options.yaxis = $ChartAxisY[0]
    }

    if ($ChartToolTip) {
        New-ChartInternalToolTip -Options $Options @ChartToolTip
    }
    if ($DataLabel) {
        $Options.dataLabels = $DataLabel
    }

    New-ChartInternalTimeLine -Options $Options -Color $Color -Data $Data

    if ($Design.fill.pattern) {
        $Options.fill = [ordered] @{
            type    = 'pattern'
            pattern = $Design.fill.pattern
        }
    } elseif ($Design.fill.gradient) {
        $Options.fill = [ordered] @{
            type     = 'gradient'
            gradient = $Design.fill.gradient
        }
    } elseif ($PatternedColors) {
        New-ChartInternalPattern
    } elseif ($GradientColors) {
        New-ChartInternalGradient
    }
    if ($GridOptions) {
        New-ChartInternalGrid -Options $Options @GridOptions }
    if ($Theme) {
        New-ChartInternalTheme -Options $Options @Theme }
    if ($Toolbar) {
        New-ChartInternalToolbar -Options $Options @Toolbar -Show $true }
    New-ApexChart -Options $Options -Events $Events
}

Register-ArgumentCompleter -CommandName New-HTMLChartSpark -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
$Script:RGBColors = [ordered] @{
    None                   = $null
    AirForceBlue           = 93, 138, 168
    Akaroa                 = 195, 176, 145
    AlbescentWhite         = 227, 218, 201
    AliceBlue              = 240, 248, 255
    Alizarin               = 227, 38, 54
    Allports               = 18, 97, 128
    Almond                 = 239, 222, 205
    AlmondFrost            = 159, 129, 112
    Amaranth               = 229, 43, 80
    Amazon                 = 59, 122, 87
    Amber                  = 255, 191, 0
    Amethyst               = 153, 102, 204
    AmethystSmoke          = 156, 138, 164
    AntiqueWhite           = 250, 235, 215
    Apple                  = 102, 180, 71
    AppleBlossom           = 176, 92, 82
    Apricot                = 251, 206, 177
    Aqua                   = 0, 255, 255
    Aquamarine             = 127, 255, 212
    Armygreen              = 75, 83, 32
    Arsenic                = 59, 68, 75
    Astral                 = 54, 117, 136
    Atlantis               = 164, 198, 57
    Atomic                 = 65, 74, 76
    AtomicTangerine        = 255, 153, 102
    Axolotl                = 99, 119, 91
    Azure                  = 240, 255, 255
    Bahia                  = 176, 191, 26
    BakersChocolate        = 93, 58, 26
    BaliHai                = 124, 152, 171
    BananaMania            = 250, 231, 181
    BattleshipGrey         = 85, 93, 80
    BayOfMany              = 35, 48, 103
    Beige                  = 245, 245, 220
    Bermuda                = 136, 216, 192
    Bilbao                 = 42, 128, 0
    BilobaFlower           = 181, 126, 220
    Bismark                = 83, 104, 114
    Bisque                 = 255, 228, 196
    Bistre                 = 61, 43, 31
    Bittersweet            = 254, 111, 94
    Black                  = 0, 0, 0
    BlackPearl             = 31, 38, 42
    BlackRose              = 85, 31, 47
    BlackRussian           = 23, 24, 43
    BlanchedAlmond         = 255, 235, 205
    BlizzardBlue           = 172, 229, 238
    Blue                   = 0, 0, 255
    BlueDiamond            = 77, 26, 127
    BlueMarguerite         = 115, 102, 189
    BlueSmoke              = 115, 130, 118
    BlueViolet             = 138, 43, 226
    Blush                  = 169, 92, 104
    BokaraGrey             = 22, 17, 13
    Bole                   = 121, 68, 59
    BondiBlue              = 0, 147, 175
    Bordeaux               = 88, 17, 26
    Bossanova              = 86, 60, 92
    Boulder                = 114, 116, 114
    Bouquet                = 183, 132, 167
    Bourbon                = 170, 108, 57
    Brass                  = 181, 166, 66
    BrickRed               = 199, 44, 72
    BrightGreen            = 102, 255, 0
    BrightRed              = 146, 43, 62
    BrightTurquoise        = 8, 232, 222
    BrilliantRose          = 243, 100, 162
    BrinkPink              = 250, 110, 121
    BritishRacingGreen     = 0, 66, 37
    Bronze                 = 205, 127, 50
    Brown                  = 165, 42, 42
    BrownPod               = 57, 24, 2
    BuddhaGold             = 202, 169, 6
    Buff                   = 240, 220, 130
    Burgundy               = 128, 0, 32
    BurlyWood              = 222, 184, 135
    BurntOrange            = 255, 117, 56
    BurntSienna            = 233, 116, 81
    BurntUmber             = 138, 51, 36
    ButteredRum            = 156, 124, 56
    CadetBlue              = 95, 158, 160
    California             = 224, 141, 60
    CamouflageGreen        = 120, 134, 107
    Canary                 = 255, 255, 153
    CanCan                 = 217, 134, 149
    CannonPink             = 145, 78, 117
    CaputMortuum           = 89, 39, 32
    Caramel                = 255, 213, 154
    Cararra                = 237, 230, 214
    Cardinal               = 179, 33, 52
    CardinGreen            = 18, 53, 36
    CareysPink             = 217, 152, 160
    CaribbeanGreen         = 0, 222, 164
    Carmine                = 175, 0, 42
    CarnationPink          = 255, 166, 201
    CarrotOrange           = 242, 142, 28
    Cascade                = 141, 163, 153
    CatskillWhite          = 226, 229, 222
    Cedar                  = 67, 48, 46
    Celadon                = 172, 225, 175
    Celeste                = 207, 207, 196
    Cello                  = 55, 79, 107
    Cement                 = 138, 121, 93
    Cerise                 = 222, 49, 99
    Cerulean               = 0, 123, 167
    CeruleanBlue           = 42, 82, 190
    Chantilly              = 239, 187, 204
    Chardonnay             = 255, 200, 124
    Charlotte              = 167, 216, 222
    Charm                  = 208, 116, 139
    Chartreuse             = 127, 255, 0
    ChartreuseYellow       = 223, 255, 0
    ChelseaCucumber        = 135, 169, 107
    Cherub                 = 246, 214, 222
    Chestnut               = 185, 78, 72
    ChileanFire            = 226, 88, 34
    Chinook                = 150, 200, 162
    Chocolate              = 210, 105, 30
    Christi                = 125, 183, 0
    Christine              = 181, 101, 30
    Cinnabar               = 235, 76, 66
    Citron                 = 159, 169, 31
    Citrus                 = 141, 182, 0
    Claret                 = 95, 25, 51
    ClassicRose            = 251, 204, 231
    ClayCreek              = 145, 129, 81
    Clinker                = 75, 54, 33
    Clover                 = 74, 93, 35
    Cobalt                 = 0, 71, 171
    CocoaBrown             = 44, 22, 8
    Cola                   = 60, 48, 36
    ColumbiaBlue           = 166, 231, 255
    CongoBrown             = 103, 76, 71
    Conifer                = 178, 236, 93
    Copper                 = 218, 138, 103
    CopperRose             = 153, 102, 102
    Coral                  = 255, 127, 80
    CoralRed               = 255, 64, 64
    CoralTree              = 173, 111, 105
    Coriander              = 188, 184, 138
    Corn                   = 251, 236, 93
    CornField              = 250, 240, 190
    Cornflower             = 147, 204, 234
    CornflowerBlue         = 100, 149, 237
    Cornsilk               = 255, 248, 220
    Cosmic                 = 132, 63, 91
    Cosmos                 = 255, 204, 203
    CostaDelSol            = 102, 93, 30
    CottonCandy            = 255, 188, 217
    Crail                  = 164, 90, 82
    Cranberry              = 205, 96, 126
    Cream                  = 255, 255, 204
    CreamCan               = 242, 198, 73
    Crimson                = 220, 20, 60
    Crusta                 = 232, 142, 90
    Cumulus                = 255, 255, 191
    Cupid                  = 246, 173, 198
    CuriousBlue            = 40, 135, 200
    Cyan                   = 0, 255, 255
    Cyprus                 = 6, 78, 64
    DaisyBush              = 85, 53, 146
    Dandelion              = 250, 218, 94
    Danube                 = 96, 130, 182
    DarkBlue               = 0, 0, 139
    DarkBrown              = 101, 67, 33
    DarkCerulean           = 8, 69, 126
    DarkChestnut           = 152, 105, 96
    DarkCoral              = 201, 90, 73
    DarkCyan               = 0, 139, 139
    DarkGoldenrod          = 184, 134, 11
    DarkGray               = 169, 169, 169
    DarkGreen              = 0, 100, 0
    DarkGreenCopper        = 73, 121, 107
    DarkGrey               = 169, 169, 169
    DarkKhaki              = 189, 183, 107
    DarkMagenta            = 139, 0, 139
    DarkOliveGreen         = 85, 107, 47
    DarkOrange             = 255, 140, 0
    DarkOrchid             = 153, 50, 204
    DarkPastelGreen        = 3, 192, 60
    DarkPink               = 222, 93, 131
    DarkPurple             = 150, 61, 127
    DarkRed                = 139, 0, 0
    DarkSalmon             = 233, 150, 122
    DarkSeaGreen           = 143, 188, 143
    DarkSlateBlue          = 72, 61, 139
    DarkSlateGray          = 47, 79, 79
    DarkSlateGrey          = 47, 79, 79
    DarkSpringGreen        = 23, 114, 69
    DarkTangerine          = 255, 170, 29
    DarkTurquoise          = 0, 206, 209
    DarkViolet             = 148, 0, 211
    DarkWood               = 130, 102, 68
    DeepBlush              = 245, 105, 145
    DeepCerise             = 224, 33, 138
    DeepKoamaru            = 51, 51, 102
    DeepLilac              = 153, 85, 187
    DeepMagenta            = 204, 0, 204
    DeepPink               = 255, 20, 147
    DeepSea                = 14, 124, 97
    DeepSkyBlue            = 0, 191, 255
    DeepTeal               = 24, 69, 59
    Denim                  = 36, 107, 206
    DesertSand             = 237, 201, 175
    DimGray                = 105, 105, 105
    DimGrey                = 105, 105, 105
    DodgerBlue             = 30, 144, 255
    Dolly                  = 242, 242, 122
    Downy                  = 95, 201, 191
    DutchWhite             = 239, 223, 187
    EastBay                = 76, 81, 109
    EastSide               = 178, 132, 190
    EchoBlue               = 169, 178, 195
    Ecru                   = 194, 178, 128
    Eggplant               = 162, 0, 109
    EgyptianBlue           = 16, 52, 166
    ElectricBlue           = 125, 249, 255
    ElectricIndigo         = 111, 0, 255
    ElectricLime           = 208, 255, 20
    ElectricPurple         = 191, 0, 255
    Elm                    = 47, 132, 124
    Emerald                = 80, 200, 120
    Eminence               = 108, 48, 130
    Endeavour              = 46, 88, 148
    EnergyYellow           = 245, 224, 80
    Espresso               = 74, 44, 42
    Eucalyptus             = 26, 162, 96
    Falcon                 = 126, 94, 96
    Fallow                 = 204, 153, 102
    FaluRed                = 128, 24, 24
    Feldgrau               = 77, 93, 83
    Feldspar               = 205, 149, 117
    Fern                   = 113, 188, 120
    FernGreen              = 79, 121, 66
    Festival               = 236, 213, 64
    Finn                   = 97, 64, 81
    FireBrick              = 178, 34, 34
    FireBush               = 222, 143, 78
    FireEngineRed          = 211, 33, 45
    Flamingo               = 233, 92, 75
    Flax                   = 238, 220, 130
    FloralWhite            = 255, 250, 240
    ForestGreen            = 34, 139, 34
    Frangipani             = 250, 214, 165
    FreeSpeechAquamarine   = 0, 168, 119
    FreeSpeechRed          = 204, 0, 0
    FrenchLilac            = 230, 168, 215
    FrenchRose             = 232, 83, 149
    FriarGrey              = 135, 134, 129
    Froly                  = 228, 113, 122
    Fuchsia                = 255, 0, 255
    FuchsiaPink            = 255, 119, 255
    Gainsboro              = 220, 220, 220
    Gallery                = 219, 215, 210
    Galliano               = 204, 160, 29
    Gamboge                = 204, 153, 0
    Ghost                  = 196, 195, 208
    GhostWhite             = 248, 248, 255
    Gin                    = 216, 228, 188
    GinFizz                = 247, 231, 206
    Givry                  = 230, 208, 171
    Glacier                = 115, 169, 194
    Gold                   = 255, 215, 0
    GoldDrop               = 213, 108, 43
    GoldenBrown            = 150, 113, 23
    GoldenFizz             = 240, 225, 48
    GoldenGlow             = 248, 222, 126
    GoldenPoppy            = 252, 194, 0
    Goldenrod              = 218, 165, 32
    GoldenSand             = 233, 214, 107
    GoldenYellow           = 253, 238, 0
    GoldTips               = 225, 189, 39
    GordonsGreen           = 37, 53, 41
    Gorse                  = 255, 225, 53
    Gossamer               = 49, 145, 119
    GrannySmithApple       = 168, 228, 160
    Gray                   = 128, 128, 128
    GrayAsparagus          = 70, 89, 69
    Green                  = 0, 128, 0
    GreenLeaf              = 76, 114, 29
    GreenVogue             = 38, 67, 72
    GreenYellow            = 173, 255, 47
    Grey                   = 128, 128, 128
    GreyAsparagus          = 70, 89, 69
    GuardsmanRed           = 157, 41, 51
    GumLeaf                = 178, 190, 181
    Gunmetal               = 42, 52, 57
    Hacienda               = 155, 135, 12
    HalfAndHalf            = 232, 228, 201
    HalfBaked              = 95, 138, 139
    HalfColonialWhite      = 246, 234, 190
    HalfPearlLusta         = 240, 234, 214
    HanPurple              = 63, 0, 255
    Harlequin              = 74, 255, 0
    HarleyDavidsonOrange   = 194, 59, 34
    Heather                = 174, 198, 207
    Heliotrope             = 223, 115, 255
    Hemp                   = 161, 122, 116
    Highball               = 134, 126, 54
    HippiePink             = 171, 75, 82
    Hoki                   = 110, 127, 128
    HollywoodCerise        = 244, 0, 161
    Honeydew               = 240, 255, 240
    Hopbush                = 207, 113, 175
    HorsesNeck             = 108, 84, 30
    HotPink                = 255, 105, 180
    HummingBird            = 201, 255, 229
    HunterGreen            = 53, 94, 59
    Illusion               = 244, 152, 173
    InchWorm               = 202, 224, 13
    IndianRed              = 205, 92, 92
    Indigo                 = 75, 0, 130
    InternationalKleinBlue = 0, 24, 168
    InternationalOrange    = 255, 79, 0
    IrisBlue               = 28, 169, 201
    IrishCoffee            = 102, 66, 40
    IronsideGrey           = 113, 112, 110
    IslamicGreen           = 0, 144, 0
    Ivory                  = 255, 255, 240
    Jacarta                = 61, 50, 93
    JackoBean              = 65, 54, 40
    JacksonsPurple         = 46, 45, 136
    Jade                   = 0, 171, 102
    JapaneseLaurel         = 47, 117, 50
    Jazz                   = 93, 43, 44
    JazzberryJam           = 165, 11, 94
    JellyBean              = 68, 121, 142
    JetStream              = 187, 208, 201
    Jewel                  = 0, 107, 60
    Jon                    = 79, 58, 60
    JordyBlue              = 124, 185, 232
    Jumbo                  = 132, 132, 130
    JungleGreen            = 41, 171, 135
    KaitokeGreen           = 30, 77, 43
    Karry                  = 255, 221, 202
    KellyGreen             = 70, 203, 24
    Keppel                 = 93, 164, 147
    Khaki                  = 240, 230, 140
    Killarney              = 77, 140, 87
    KingfisherDaisy        = 85, 27, 140
    Kobi                   = 230, 143, 172
    LaPalma                = 60, 141, 13
    LaserLemon             = 252, 247, 94
    Laurel                 = 103, 146, 103
    Lavender               = 230, 230, 250
    LavenderBlue           = 204, 204, 255
    LavenderBlush          = 255, 240, 245
    LavenderPink           = 251, 174, 210
    LavenderRose           = 251, 160, 227
    LawnGreen              = 124, 252, 0
    LemonChiffon           = 255, 250, 205
    LightBlue              = 173, 216, 230
    LightCoral             = 240, 128, 128
    LightCyan              = 224, 255, 255
    LightGoldenrodYellow   = 250, 250, 210
    LightGray              = 211, 211, 211
    LightGreen             = 144, 238, 144
    LightGrey              = 211, 211, 211
    LightPink              = 255, 182, 193
    LightSalmon            = 255, 160, 122
    LightSeaGreen          = 32, 178, 170
    LightSkyBlue           = 135, 206, 250
    LightSlateGray         = 119, 136, 153
    LightSlateGrey         = 119, 136, 153
    LightSteelBlue         = 176, 196, 222
    LightYellow            = 255, 255, 224
    Lilac                  = 204, 153, 204
    Lime                   = 0, 255, 0
    LimeGreen              = 50, 205, 50
    Limerick               = 139, 190, 27
    Linen                  = 250, 240, 230
    Lipstick               = 159, 43, 104
    Liver                  = 83, 75, 79
    Lochinvar              = 86, 136, 125
    Lochmara               = 38, 97, 156
    Lola                   = 179, 158, 181
    LondonHue              = 170, 152, 169
    Lotus                  = 124, 72, 72
    LuckyPoint             = 29, 41, 81
    MacaroniAndCheese      = 255, 189, 136
    Madang                 = 193, 249, 162
    Madras                 = 81, 65, 0
    Magenta                = 255, 0, 255
    MagicMint              = 170, 240, 209
    Magnolia               = 248, 244, 255
    Mahogany               = 215, 59, 62
    Maire                  = 27, 24, 17
    Maize                  = 230, 190, 138
    Malachite              = 11, 218, 81
    Malibu                 = 93, 173, 236
    Malta                  = 169, 154, 134
    Manatee                = 140, 146, 172
    Mandalay               = 176, 121, 57
    MandarianOrange        = 146, 39, 36
    Mandy                  = 191, 79, 81
    Manhattan              = 229, 170, 112
    Mantis                 = 125, 194, 66
    Manz                   = 217, 230, 80
    MardiGras              = 48, 25, 52
    Mariner                = 57, 86, 156
    Maroon                 = 128, 0, 0
    Matterhorn             = 85, 85, 85
    Mauve                  = 244, 187, 255
    Mauvelous              = 255, 145, 175
    MauveTaupe             = 143, 89, 115
    MayaBlue               = 119, 181, 254
    McKenzie               = 129, 97, 60
    MediumAquamarine       = 102, 205, 170
    MediumBlue             = 0, 0, 205
    MediumCarmine          = 175, 64, 53
    MediumOrchid           = 186, 85, 211
    MediumPurple           = 147, 112, 219
    MediumRedViolet        = 189, 51, 164
    MediumSeaGreen         = 60, 179, 113
    MediumSlateBlue        = 123, 104, 238
    MediumSpringGreen      = 0, 250, 154
    MediumTurquoise        = 72, 209, 204
    MediumVioletRed        = 199, 21, 133
    MediumWood             = 166, 123, 91
    Melon                  = 253, 188, 180
    Merlot                 = 112, 54, 66
    MetallicGold           = 211, 175, 55
    Meteor                 = 184, 115, 51
    MidnightBlue           = 25, 25, 112
    MidnightExpress        = 0, 20, 64
    Mikado                 = 60, 52, 31
    MilanoRed              = 168, 55, 49
    Ming                   = 54, 116, 125
    MintCream              = 245, 255, 250
    MintGreen              = 152, 255, 152
    Mischka                = 168, 169, 173
    MistyRose              = 255, 228, 225
    Moccasin               = 255, 228, 181
    Mojo                   = 149, 69, 53
    MonaLisa               = 255, 153, 153
    Mongoose               = 179, 139, 109
    Montana                = 53, 56, 57
    MoodyBlue              = 116, 108, 192
    MoonYellow             = 245, 199, 26
    MossGreen              = 173, 223, 173
    MountainMeadow         = 28, 172, 120
    MountainMist           = 161, 157, 148
    MountbattenPink        = 153, 122, 141
    Mulberry               = 211, 65, 157
    Mustard                = 255, 219, 88
    Myrtle                 = 25, 89, 5
    MySin                  = 255, 179, 71
    NavajoWhite            = 255, 222, 173
    Navy                   = 0, 0, 128
    NavyBlue               = 2, 71, 254
    NeonCarrot             = 255, 153, 51
    NeonPink               = 255, 92, 205
    Nepal                  = 145, 163, 176
    Nero                   = 20, 20, 20
    NewMidnightBlue        = 0, 0, 156
    Niagara                = 58, 176, 158
    NightRider             = 59, 47, 47
    Nobel                  = 152, 152, 152
    Norway                 = 169, 186, 157
    Nugget                 = 183, 135, 39
    OceanGreen             = 95, 167, 120
    Ochre                  = 202, 115, 9
    OldCopper              = 111, 78, 55
    OldGold                = 207, 181, 59
    OldLace                = 253, 245, 230
    OldLavender            = 121, 104, 120
    OldRose                = 195, 33, 72
    Olive                  = 128, 128, 0
    OliveDrab              = 107, 142, 35
    OliveGreen             = 181, 179, 92
    Olivetone              = 110, 110, 48
    Olivine                = 154, 185, 115
    Onahau                 = 196, 216, 226
    Opal                   = 168, 195, 188
    Orange                 = 255, 165, 0
    OrangePeel             = 251, 153, 2
    OrangeRed              = 255, 69, 0
    Orchid                 = 218, 112, 214
    OuterSpace             = 45, 56, 58
    OutrageousOrange       = 254, 90, 29
    Oxley                  = 95, 167, 119
    PacificBlue            = 0, 136, 220
    Padua                  = 128, 193, 151
    PalatinatePurple       = 112, 41, 99
    PaleBrown              = 160, 120, 90
    PaleChestnut           = 221, 173, 175
    PaleCornflowerBlue     = 188, 212, 230
    PaleGoldenrod          = 238, 232, 170
    PaleGreen              = 152, 251, 152
    PaleMagenta            = 249, 132, 239
    PalePink               = 250, 218, 221
    PaleSlate              = 201, 192, 187
    PaleTaupe              = 188, 152, 126
    PaleTurquoise          = 175, 238, 238
    PaleVioletRed          = 219, 112, 147
    PalmLeaf               = 53, 66, 48
    Panache                = 233, 255, 219
    PapayaWhip             = 255, 239, 213
    ParisDaisy             = 255, 244, 79
    Parsley                = 48, 96, 48
    PastelGreen            = 119, 221, 119
    PattensBlue            = 219, 233, 244
    Peach                  = 255, 203, 164
    PeachOrange            = 255, 204, 153
    PeachPuff              = 255, 218, 185
    PeachYellow            = 250, 223, 173
    Pear                   = 209, 226, 49
    PearlLusta             = 234, 224, 200
    Pelorous               = 42, 143, 189
    Perano                 = 172, 172, 230
    Periwinkle             = 197, 203, 225
    PersianBlue            = 34, 67, 182
    PersianGreen           = 0, 166, 147
    PersianIndigo          = 51, 0, 102
    PersianPink            = 247, 127, 190
    PersianRed             = 192, 54, 44
    PersianRose            = 233, 54, 167
    Persimmon              = 236, 88, 0
    Peru                   = 205, 133, 63
    Pesto                  = 128, 117, 50
    PictonBlue             = 102, 153, 204
    PigmentGreen           = 0, 173, 67
    PigPink                = 255, 218, 233
    PineGreen              = 1, 121, 111
    PineTree               = 42, 47, 35
    Pink                   = 255, 192, 203
    PinkFlare              = 191, 175, 178
    PinkLace               = 240, 211, 220
    PinkSwan               = 179, 179, 179
    Plum                   = 221, 160, 221
    Pohutukawa             = 102, 12, 33
    PoloBlue               = 119, 158, 203
    Pompadour              = 129, 20, 83
    Portage                = 146, 161, 207
    PotPourri              = 241, 221, 207
    PottersClay            = 132, 86, 60
    PowderBlue             = 176, 224, 230
    Prim                   = 228, 196, 207
    PrussianBlue           = 0, 58, 108
    PsychedelicPurple      = 223, 0, 255
    Puce                   = 204, 136, 153
    Pueblo                 = 108, 46, 31
    PuertoRico             = 67, 179, 174
    Pumpkin                = 255, 99, 28
    Purple                 = 128, 0, 128
    PurpleMountainsMajesty = 150, 123, 182
    PurpleTaupe            = 93, 57, 84
    QuarterSpanishWhite    = 230, 224, 212
    Quartz                 = 220, 208, 255
    Quincy                 = 106, 84, 69
    RacingGreen            = 26, 36, 33
    RadicalRed             = 255, 32, 82
    Rajah                  = 251, 171, 96
    RawUmber               = 123, 63, 0
    RazzleDazzleRose       = 254, 78, 218
    Razzmatazz             = 215, 10, 83
    Red                    = 255, 0, 0
    RedBerry               = 132, 22, 23
    RedDamask              = 203, 109, 81
    RedOxide               = 99, 15, 15
    RedRobin               = 128, 64, 64
    RichBlue               = 84, 90, 167
    Riptide                = 141, 217, 204
    RobinsEggBlue          = 0, 204, 204
    RobRoy                 = 225, 169, 95
    RockSpray              = 171, 56, 31
    RomanCoffee            = 131, 105, 83
    RoseBud                = 246, 164, 148
    RoseBudCherry          = 135, 50, 96
    RoseTaupe              = 144, 93, 93
    RosyBrown              = 188, 143, 143
    Rouge                  = 176, 48, 96
    RoyalBlue              = 65, 105, 225
    RoyalHeath             = 168, 81, 110
    RoyalPurple            = 102, 51, 152
    Ruby                   = 215, 24, 104
    Russet                 = 128, 70, 27
    Rust                   = 192, 64, 0
    RusticRed              = 72, 6, 7
    Saddle                 = 99, 81, 71
    SaddleBrown            = 139, 69, 19
    SafetyOrange           = 255, 102, 0
    Saffron                = 244, 196, 48
    Sage                   = 143, 151, 121
    Sail                   = 161, 202, 241
    Salem                  = 0, 133, 67
    Salmon                 = 250, 128, 114
    SandyBeach             = 253, 213, 177
    SandyBrown             = 244, 164, 96
    Sangria                = 134, 1, 17
    SanguineBrown          = 115, 54, 53
    SanMarino              = 80, 114, 167
    SanteFe                = 175, 110, 77
    Sapphire               = 6, 42, 120
    Saratoga               = 84, 90, 44
    Scampi                 = 102, 102, 153
    Scarlet                = 255, 36, 0
    ScarletGum             = 67, 28, 83
    SchoolBusYellow        = 255, 216, 0
    Schooner               = 139, 134, 128
    ScreaminGreen          = 102, 255, 102
    Scrub                  = 59, 60, 54
    SeaBuckthorn           = 249, 146, 69
    SeaGreen               = 46, 139, 87
    Seagull                = 140, 190, 214
    SealBrown              = 61, 12, 2
    Seance                 = 96, 47, 107
    SeaPink                = 215, 131, 127
    SeaShell               = 255, 245, 238
    Selago                 = 250, 230, 250
    SelectiveYellow        = 242, 180, 0
    SemiSweetChocolate     = 107, 68, 35
    Sepia                  = 150, 90, 62
    Serenade               = 255, 233, 209
    Shadow                 = 133, 109, 77
    Shakespeare            = 114, 160, 193
    Shalimar               = 252, 255, 164
    Shamrock               = 68, 215, 168
    ShamrockGreen          = 0, 153, 102
    SherpaBlue             = 0, 75, 73
    SherwoodGreen          = 27, 77, 62
    Shilo                  = 222, 165, 164
    ShipCove               = 119, 139, 165
    Shocking               = 241, 156, 187
    ShockingPink           = 255, 29, 206
    ShuttleGrey            = 84, 98, 111
    Sidecar                = 238, 224, 177
    Sienna                 = 160, 82, 45
    Silk                   = 190, 164, 147
    Silver                 = 192, 192, 192
    SilverChalice          = 175, 177, 174
    SilverTree             = 102, 201, 146
    SkyBlue                = 135, 206, 235
    SlateBlue              = 106, 90, 205
    SlateGray              = 112, 128, 144
    SlateGrey              = 112, 128, 144
    Smalt                  = 0, 48, 143
    SmaltBlue              = 74, 100, 108
    Snow                   = 255, 250, 250
    SoftAmber              = 209, 190, 168
    Solitude               = 235, 236, 240
    Sorbus                 = 233, 105, 44
    Spectra                = 53, 101, 77
    SpicyMix               = 136, 101, 78
    Spray                  = 126, 212, 230
    SpringBud              = 150, 255, 0
    SpringGreen            = 0, 255, 127
    SpringSun              = 236, 235, 189
    SpunPearl              = 170, 169, 173
    Stack                  = 130, 142, 132
    SteelBlue              = 70, 130, 180
    Stiletto               = 137, 63, 69
    Strikemaster           = 145, 92, 131
    StTropaz               = 50, 82, 123
    Studio                 = 115, 79, 150
    Sulu                   = 201, 220, 135
    SummerSky              = 33, 171, 205
    Sun                    = 237, 135, 45
    Sundance               = 197, 179, 88
    Sunflower              = 228, 208, 10
    Sunglow                = 255, 204, 51
    SunsetOrange           = 253, 82, 64
    SurfieGreen            = 0, 116, 116
    Sushi                  = 111, 153, 64
    SuvaGrey               = 140, 140, 140
    Swamp                  = 35, 43, 43
    SweetCorn              = 253, 219, 109
    SweetPink              = 243, 153, 152
    Tacao                  = 236, 177, 118
    TahitiGold             = 235, 97, 35
    Tan                    = 210, 180, 140
    Tangaroa               = 0, 28, 61
    Tangerine              = 228, 132, 0
    TangerineYellow        = 253, 204, 13
    Tapestry               = 183, 110, 121
    Taupe                  = 72, 60, 50
    TaupeGrey              = 139, 133, 137
    TawnyPort              = 102, 66, 77
    TaxBreak               = 79, 102, 106
    TeaGreen               = 208, 240, 192
    Teak                   = 176, 141, 87
    Teal                   = 0, 128, 128
    TeaRose                = 255, 133, 207
    Temptress              = 60, 20, 33
    Tenne                  = 200, 101, 0
    TerraCotta             = 226, 114, 91
    Thistle                = 216, 191, 216
    TickleMePink           = 245, 111, 161
    Tidal                  = 232, 244, 140
    TitanWhite             = 214, 202, 221
    Toast                  = 165, 113, 100
    Tomato                 = 255, 99, 71
    TorchRed               = 255, 3, 62
    ToryBlue               = 54, 81, 148
    Tradewind              = 110, 174, 161
    TrendyPink             = 133, 96, 136
    TropicalRainForest     = 0, 127, 102
    TrueV                  = 139, 114, 190
    TulipTree              = 229, 183, 59
    Tumbleweed             = 222, 170, 136
    Turbo                  = 255, 195, 36
    TurkishRose            = 152, 119, 123
    Turquoise              = 64, 224, 208
    TurquoiseBlue          = 118, 215, 234
    Tuscany                = 175, 89, 62
    TwilightBlue           = 253, 255, 245
    Twine                  = 186, 135, 89
    TyrianPurple           = 102, 2, 60
    Ultramarine            = 10, 17, 149
    UltraPink              = 255, 111, 255
    Valencia               = 222, 82, 70
    VanCleef               = 84, 61, 55
    VanillaIce             = 229, 204, 201
    VenetianRed            = 209, 0, 28
    Venus                  = 138, 127, 128
    Vermilion              = 251, 79, 20
    VeryLightGrey          = 207, 207, 207
    VidaLoca               = 94, 140, 49
    Viking                 = 71, 171, 204
    Viola                  = 180, 131, 149
    ViolentViolet          = 50, 23, 77
    Violet                 = 238, 130, 238
    VioletRed              = 255, 57, 136
    Viridian               = 64, 130, 109
    VistaBlue              = 159, 226, 191
    VividViolet            = 127, 62, 152
    WaikawaGrey            = 83, 104, 149
    Wasabi                 = 150, 165, 60
    Watercourse            = 0, 106, 78
    Wedgewood              = 67, 107, 149
    WellRead               = 147, 61, 65
    Wewak                  = 255, 152, 153
    Wheat                  = 245, 222, 179
    Whiskey                = 217, 154, 108
    WhiskeySour            = 217, 144, 88
    White                  = 255, 255, 255
    WhiteSmoke             = 245, 245, 245
    WildRice               = 228, 217, 111
    WildSand               = 229, 228, 226
    WildStrawberry         = 252, 65, 154
    WildWatermelon         = 255, 84, 112
    WildWillow             = 172, 191, 96
    Windsor                = 76, 40, 130
    Wisteria               = 191, 148, 228
    Wistful                = 162, 162, 208
    Yellow                 = 255, 255, 0
    YellowGreen            = 154, 205, 50
    YellowOrange           = 255, 174, 66
    YourPink               = 244, 194, 194
}
$Script:ScriptBlockColors = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $Script:RGBColors.Keys | Where-Object { $_ -like "*$wordToComplete*" }
}
function Add-CustomFormatForDatetimeSorting {
    <#
    .SYNOPSIS
    Adds custom datetime formats for sortable datatable columns.
 
    .DESCRIPTION
        This function adds code to make the datatable columns sortable with different datetime formats.
        Formatting:
        Day (of Month)
        D - 1 2 ... 30 31
        Do - 1st 2nd ... 30th 31st
        DD - 01 02 ... 30 31
 
        Month
        M - 1 2 ... 11 12
        Mo - 1st 2nd ... 11th 12th
        MM - 01 02 ... 11 12
        MMM - Jan Feb ... Nov Dec
        MMMM - January February ... November December
 
        Year
        YY - 70 71 ... 29 30
        YYYY - 1970 1971 ... 2029 2030
 
        Hour
        H - 0 1 ... 22 23
        HH - 00 01 ... 22 23
        h - 1 2 ... 11 12
        hh - 01 02 ... 11 12
 
        Minute
        m - 0 1 ... 58 59
        mm - 00 01 ... 58 59
 
        Second
        s - 0 1 ... 58 59
        ss - 00 01 ... 58 59
 
        More formats
        http://momentjs.com/docs/#/displaying/
 
    .PARAMETER CustomDateTimeFormat
        Array with strings of custom datetime format.
        The string is build from two parts. Format and locale. Locale is optional.
        format explanation: http://momentjs.com/docs/#/displaying/
        locale explanation: http://momentjs.com/docs/#/i18n/
 
    .LINK
        format explanation: http://momentjs.com/docs/#/displaying/
        locale explanation: http://momentjs.com/docs/#/i18n/
    .Example
        Add-CustomFormatForDatetimeSorting -CustomDateFormat 'dddd, MMMM Do, YYYY','HH:mm MMM D, YY'
    .Example
        Add-CustomFormatForDatetimeSorting -CustomDateFormat 'DD.MM.YYYY HH:mm:ss'
    #>

    [CmdletBinding()]
    param(
        [array]$DateTimeSortingFormat
    )
    if ($DateTimeSortingFormat) {
        [array]$OutputDateTimeSortingFormat = foreach ($format in $DateTimeSortingFormat) {
            "$.fn.dataTable.moment( '$format' );"
        }
    } else {

        $OutputDateTimeSortingFormat = "$.fn.dataTable.moment( 'L' );"
    }
    return $OutputDateTimeSortingFormat
}

function Add-TableContent {
    <#
    .SYNOPSIS
    Adds content to a table with customizable styling and conditional formatting.
 
    .DESCRIPTION
    The Add-TableContent function adds content to a table with the ability to customize styling and apply conditional formatting. It takes in content rows, content styles, content top, content formatting inline, header names, and the table itself as parameters.
 
    .PARAMETER ContentRows
    Specifies the content rows to be added to the table.
 
    .PARAMETER ContentStyle
    Specifies the styles to be applied to the content.
 
    .PARAMETER ContentTop
    Specifies the top content to be added to the table.
 
    .PARAMETER ContentFormattingInline
    Specifies the inline conditional formatting to be applied to the content.
 
    .PARAMETER HeaderNames
    Specifies the names of the headers in the table.
 
    .PARAMETER Table
    Specifies the table to which content will be added.
 
    .EXAMPLE
    Add-TableContent -ContentRows $rows -ContentStyle $styles -ContentTop $topContent -ContentFormattingInline $formatting -HeaderNames $headers -Table $table
    Adds content to a table with the specified content rows, styles, top content, formatting, headers, and table.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.Generic.List[PSCustomObject]] $ContentRows,
        [System.Collections.Generic.List[PSCUstomObject]] $ContentStyle,
        [System.Collections.Generic.List[PSCUstomObject]] $ContentTop,
        [System.Collections.Generic.List[PSCUstomObject]] $ContentFormattingInline,
        [string[]] $HeaderNames,
        [Array] $Table
    )

    if ($ContentFormattingInline.Count -gt 0) {
        [Array] $AddStyles = for ($RowCount = 1; $RowCount -lt $Table.Count; $RowCount++) {
            [string[]] $RowData = $Table[$RowCount] -replace '</td></tr>' -replace '<tr><td>' -split '</td><td>'
            foreach ($ConditionalFormatting in $ContentFormattingInline) {
                $Pass = $false
                if ($ConditionalFormatting.ConditionType -eq 'Condition') {
                    $ColumnIndexHeader = [array]::indexof($HeaderNames.ToUpper(), $($ConditionalFormatting.Name).ToUpper())
                    for ($ColumnCount = 0; $ColumnCount -lt $RowData.Count; $ColumnCount++) {

                        if ($ColumnIndexHeader -eq $ColumnCount) {
                            $Pass = New-TableConditionalFormattingInline -HeaderNames $HeaderNames -ColumnIndexHeader $ColumnIndexHeader -RowCount $RowCount -ColumnCount $ColumnCount -RowData $RowData -ConditionalFormatting $ConditionalFormatting
                            break
                        }
                    }
                } else {
                    [Array] $IsConditionTrue = foreach ($SubCondition in $ConditionalFormatting.Conditions.Output) {
                        $ColumnIndexHeader = [array]::indexof($HeaderNames.ToUpper(), $($SubCondition.Name).ToUpper())
                        for ($ColumnCount = 0; $ColumnCount -lt $RowData.Count; $ColumnCount++) {

                            if ($ColumnIndexHeader -eq $ColumnCount) {
                                New-TableConditionalFormattingInline -HeaderNames $HeaderNames -ColumnIndexHeader $ColumnIndexHeader -RowCount $RowCount -ColumnCount $ColumnCount -RowData $RowData -ConditionalFormatting $SubCondition
                            }
                        }
                    }
                    if ($ConditionalFormatting.Logic -eq 'AND') {
                        if ($IsConditionTrue -contains $true -and $IsConditionTrue -notcontains $false) {
                            $Pass = $true
                        }
                    } elseif ($ConditionalFormatting.Logic -eq 'OR') {
                        if ($IsConditionTrue -contains $true) {
                            $Pass = $true
                        }
                    } elseif ($ConditionalFormatting.Logic -eq 'NONE') {
                        if ($IsConditionTrue -contains $false -and $IsConditionTrue -notcontains $true) {
                            $Pass = $true
                        }
                    }
                }
                if ($Pass) {

                    if ($ConditionalFormatting.Row) {
                        for ($i = 0; $i -lt $RowData.Count; $i++) {
                            [PSCustomObject]@{
                                RowIndex    = $RowCount
                                ColumnIndex = ($i + 1)  
                                Style       = $ConditionalFormatting.Style
                            }
                        }
                    } elseif ($ConditionalFormatting.HighlightHeaders) {
                        foreach ($Name in $ConditionalFormatting.HighlightHeaders) {
                            $ColumnIndexHighlight = [array]::indexof($HeaderNames.ToUpper(), $($Name).ToUpper())
                            [PSCustomObject]@{
                                RowIndex    = $RowCount
                                ColumnIndex = ($ColumnIndexHighlight + 1) 
                                Style       = $ConditionalFormatting.Style
                            }
                        }
                    } else {
                        [PSCustomObject]@{
                            RowIndex    = $RowCount
                            ColumnIndex = ($ColumnIndexHeader + 1)  
                            Style       = $ConditionalFormatting.Style
                        }
                    }
                } else {
                    if ($ConditionalFormatting.FailStyle.Keys.Count -gt 0) {
                        if ($ConditionalFormatting.Row) {
                            for ($i = 0; $i -lt $RowData.Count; $i++) {
                                [PSCustomObject]@{
                                    RowIndex    = $RowCount
                                    ColumnIndex = ($i + 1)  
                                    Style       = $ConditionalFormatting.FailStyle
                                }
                            }
                        } elseif ($ConditionalFormatting.HighlightHeaders) {
                            foreach ($Name in $ConditionalFormatting.HighlightHeaders) {
                                $ColumnIndexHighlight = [array]::indexof($HeaderNames.ToUpper(), $($Name).ToUpper())
                                [PSCustomObject]@{
                                    RowIndex    = $RowCount
                                    ColumnIndex = ($ColumnIndexHighlight + 1) 
                                    Style       = $ConditionalFormatting.FailStyle
                                }
                            }
                        } else {
                            [PSCustomObject]@{
                                RowIndex    = $RowCount
                                ColumnIndex = ($ColumnIndexHeader + 1)  
                                Style       = $ConditionalFormatting.FailStyle
                            }
                        }
                    }
                }
            }
        }

        foreach ($Style in $AddStyles) {
            $ContentStyle.Add($Style)
        }
    }

    $TableRows = @{ }
    if ($ContentStyle) {
        for ($RowIndex = 0; $RowIndex -lt $Table.Count; $RowIndex++) {
            $TableRows[$RowIndex] = @{ }
        }
    }

    foreach ($Content in $ContentStyle) {
        if ($Content.RowIndex -and $Content.ColumnIndex) {

            foreach ($ColumnIndex in $Content.ColumnIndex) {

                foreach ($RowIndex in $Content.RowIndex) {
                    $TableRows[$RowIndex][$ColumnIndex - 1] = @{
                        Style = $Content.Style
                    }
                    if ($Content.Text) {
                        if ($Content.Used) {
                            $TableRows[$RowIndex][$ColumnIndex - 1]['Text'] = ''
                            $TableRows[$RowIndex][$ColumnIndex - 1]['Remove'] = $true
                        } else {
                            $TableRows[$RowIndex][$ColumnIndex - 1]['Text'] = $Content.Text
                            $TableRows[$RowIndex][$ColumnIndex - 1]['Remove'] = $false
                            $TableRows[$RowIndex][$ColumnIndex - 1]['ColSpan'] = $($Content.ColumnIndex).Count
                            $TableRows[$RowIndex][$ColumnIndex - 1]['RowSpan'] = $($Content.RowIndex).Count
                            $Content.Used = $true
                        }
                    }
                }
            }
        } elseif ($Content.RowIndex -and $Content.Name) {

            foreach ($ColumnName in $Content.Name) {
                $ColumnIndex = ([array]::indexof($HeaderNames.ToUpper(), $ColumnName.ToUpper()))
                foreach ($RowIndex in $Content.RowIndex) {
                    $TableRows[$RowIndex][$ColumnIndex] = @{
                        Style = $Content.Style
                    }
                    if ($Content.Text) {
                        if ($Content.Used) {
                            $TableRows[$RowIndex][$ColumnIndex]['Text'] = ''
                            $TableRows[$RowIndex][$ColumnIndex]['Remove'] = $true
                        } else {
                            $TableRows[$RowIndex][$ColumnIndex]['Text'] = $Content.Text
                            $TableRows[$RowIndex][$ColumnIndex]['Remove'] = $false
                            $TableRows[$RowIndex][$ColumnIndex]['ColSpan'] = $($Content.ColumnIndex).Count
                            $TableRows[$RowIndex][$ColumnIndex]['RowSpan'] = $($Content.RowIndex).Count
                            $Content.Used = $true
                        }
                    }
                }
            }
        } elseif ($Content.RowIndex -and (-not $Content.ColumnIndex -and -not $Content.Name)) {

            for ($ColumnIndex = 0; $ColumnIndex -lt $HeaderNames.Count; $ColumnIndex++) {
                foreach ($RowIndex in $Content.RowIndex) {
                    $TableRows[$RowIndex][$ColumnIndex] = @{
                        Style = $Content.Style
                    }
                }
            }
        } elseif (-not $Content.RowIndex -and ($Content.ColumnIndex -or $Content.Name)) {

            for ($RowIndex = 1; $RowIndex -lt $($Table.Count); $RowIndex++) {
                if ($Content.ColumnIndex) {

                    foreach ($ColumnIndex in $Content.ColumnIndex) {
                        $TableRows[$RowIndex][$ColumnIndex - 1] = @{
                            Style = $Content.Style
                        }
                    }
                } else {

                    foreach ($ColumnName in $Content.Name) {
                        $ColumnIndex = [array]::indexof($HeaderNames.ToUpper(), $ColumnName.ToUpper())
                        $TableRows[$RowIndex][$ColumnIndex] = @{
                            Style = $Content.Style
                        }
                    }
                }
            }
        }
    }

    [Array] $NewTable = for ($RowCount = 0; $RowCount -lt $Table.Count; $RowCount++) {

        if ($TableRows[$RowCount]) {
            [string[]] $RowData = $Table[$RowCount] -replace '</td></tr>' -replace '<tr><td>' -split '</td><td>'
            New-HTMLTag -Tag 'tr' {
                for ($ColumnCount = 0; $ColumnCount -lt $RowData.Count; $ColumnCount++) {
                    if ($TableRows[$RowCount][$ColumnCount]) {
                        if (-not $TableRows[$RowCount][$ColumnCount]['Remove']) {
                            if ($TableRows[$RowCount][$ColumnCount]['Text']) {
                                New-HTMLTag -Tag 'td' -Value { $TableRows[$RowCount][$ColumnCount]['Text'] } -Attributes @{
                                    style   = $TableRows[$RowCount][$ColumnCount]['Style']
                                    colspan = if ($TableRows[$RowCount][$ColumnCount]['ColSpan'] -gt 1) {
                                        $TableRows[$RowCount][$ColumnCount]['ColSpan'] } else {
                                    }
                                    rowspan = if ($TableRows[$RowCount][$ColumnCount]['RowSpan'] -gt 1) {
                                        $TableRows[$RowCount][$ColumnCount]['RowSpan'] } else {
                                    }
                                }

                            } else {
                                New-HTMLTag -Tag 'td' -Value { $RowData[$ColumnCount] } -Attributes @{
                                    style = $TableRows[$RowCount][$ColumnCount]['Style']
                                }
                            }
                        } else {

                        }
                    } else {
                        New-HTMLTag -Tag 'td' -Value { $RowData[$ColumnCount] }
                    }
                }
            }
        } else {
            $Table[$RowCount]
        }
    }
    $NewTable
}
function Add-TableEvent {
    <#
    .SYNOPSIS
    Adds event listeners to a table for handling row selection and deselection.
 
    .DESCRIPTION
    This function adds event listeners to a table to handle row selection and deselection. It allows for dynamic filtering of data based on user interactions.
 
    .PARAMETER Events
    An array of events to listen for.
 
    .PARAMETER HeaderNames
    An array of header names for the table columns.
 
    .PARAMETER DataStore
    The type of data store being used.
 
    .EXAMPLE
    Add-TableEvent -Events $events -HeaderNames $headerNames -DataStore 'html'
    Adds event listeners to the table using the specified events, header names, and data store type.
 
    .EXAMPLE
    Add-TableEvent -Events $events -HeaderNames $headerNames -DataStore 'json'
    Adds event listeners to the table using the specified events, header names, and data store type.
    #>

    [cmdletBinding()]
    param(
        [Array] $Events,
        [string[]] $HeaderNames,
        [string] $DataStore
    )
    foreach ($Event in $Events) {
        $ID = -join ('#', $Event.TableID)
        $ColumnID = $Event.SourceColumnID
        if ($null -ne $ColumnID) {
            $ColumnName = $HeaderNames[$ColumnID]
        } else {
            $ColumnName = $Event.SourceColumnName
            $ColumnID = $HeaderNames.IndexOf($Event.SourceColumnName)
        }
        $TargetColumnID = $Event.TargetColumnID

        $Value = @"
    var dataStore = '$DataStore'
    table.on('deselect', function (e, dt, type, indexes) {
        var table1 = `$('$ID').DataTable();
        table1.columns($TargetColumnID).search('').draw();
    });
 
    table.on('select', function (e, dt, type, indexes) {
        if (type === 'row') {
            // var data = table.rows(indexes).data().pluck('id');
            var data = table.rows(indexes).data();
            //console.log(data)
            //alert(data[0][$ColumnID])
 
            if (dataStore.toLowerCase() === 'html') {
                var findValue = escapeRegExp(data[0][$ColumnID]);
            } else {
                var findValue = escapeRegExp(data[0].$ColumnName);
            }
            var table1 = `$('$ID').DataTable();
            if (findValue != '') {
                table1.columns($TargetColumnID).search("^" + findValue + "`$", true, false, true).draw();
            } else {
                table1.columns($TargetColumnID).search('').draw();
            }
            /* Disabled due to rare cases where the search wouldn't show that there are no matches - TO DO: Remove this later
            if (table1.page.info().recordsDisplay == 0) {
                table1.columns($TargetColumnID).search('').draw();
            }
            */
        }
    });
"@

        $Value
    }
}
function Add-TableFiltering {
    <#
    .SYNOPSIS
    Adds filtering functionality to a DataTable.
 
    .DESCRIPTION
    This function adds filtering functionality to a DataTable by allowing users to search for specific data within the table.
 
    .PARAMETER Filtering
    Specifies whether filtering functionality should be enabled.
 
    .PARAMETER FilteringLocation
    Specifies the location of the filtering input box. Valid values are 'Top', 'Bottom', or 'Both'.
 
    .PARAMETER DataTableName
    Specifies the name of the DataTable to which filtering will be applied.
 
    .PARAMETER SearchRegularExpression
    Indicates whether the search should be treated as a regular expression.
 
    .EXAMPLE
    Add-TableFiltering -Filtering $true -FilteringLocation 'Bottom' -DataTableName 'myTable'
 
    Adds filtering functionality to the DataTable named 'myTable' at the bottom of the table.
 
    .EXAMPLE
    Add-TableFiltering -Filtering $true -FilteringLocation 'Both' -DataTableName 'myTable' -SearchRegularExpression
 
    Adds filtering functionality to the DataTable named 'myTable' at both the top and bottom of the table, treating the search as a regular expression.
 
    #>

    [CmdletBinding()]
    param(
        [bool] $Filtering,
        [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom',
        [string] $DataTableName,
        [alias('RegularExpression')][switch]$SearchRegularExpression
    )

    if ($SearchRegularExpression.IsPresent) {
        [string]$JSDataTableRegEx = 'true'
        [string]$JSDataTableSmart = 'false'
    } else {
        [string]$JSDataTableRegEx = 'false'
        [string]$JSDataTableSmart = 'true'
    }

    $Output = @{}
    if ($Filtering) {

        if ($FilteringLocation -eq 'Bottom') {

            $Output.FilteringTopCode = @"
                // Setup - add a text input to each footer cell
                `$('#$DataTableName tfoot th').each(function () {
                    var title = `$(this).text();
                    `$(this).html('<input type="text" placeholder="' + title + '" />');
                });
"@

            $Output.FilteringBottomCode = @"
                // Apply the search for footer cells
                table.columns().every(function () {
                    var that = this;
 
                    `$('input', this.footer()).on('keyup change', function () {
                        if (that.search() !== this.value) {
                            that.search(this.value, $JSDataTableRegEx, $JSDataTableSmart).draw();
                        }
                    });
                });
"@


        } elseif ($FilteringLocation -eq 'Both') {

            $Output.FilteringTopCode = @"
                // Setup - add a text input to each header cell
                `$('#$DataTableName thead th').each(function () {
                    var title = `$(this).text();
                    `$(this).html('<input type="text" placeholder="' + title + '" />');
                });
                // Setup - add a text input to each footer cell
                `$('#$DataTableName tfoot th').each(function () {
                    var title = `$(this).text();
                    `$(this).html('<input type="text" placeholder="' + title + '" />');
                });
"@

            $Output.FilteringBottomCode = @"
                // Apply the search for header cells
                table.columns().eq(0).each(function (colIdx) {
                    `$('input', table.column(colIdx).header()).on('keyup change', function () {
                        table
                            .column(colIdx)
                            .search(this.value)
                            .draw();
                    });
 
                    `$('input', table.column(colIdx).header()).on('click', function (e) {
                        e.stopPropagation();
                    });
                });
                // Apply the search for footer cells
                table.columns().every(function () {
                    var that = this;
 
                    `$('input', this.footer()).on('keyup change', function () {
                        if (that.search() !== this.value) {
                            that.search(this.value, $JSDataTableRegEx, $JSDataTableSmart).draw();
                        }
                    });
                });
"@


        } else {

            $Output.FilteringTopCode = @"
                // Setup - add a text input to each header cell
                `$('#$DataTableName thead th').each(function () {
                    var title = `$(this).text();
                    `$(this).html('<input type="text" placeholder="' + title + '" />');
                });
"@


            $Output.FilteringBottomCode = @"
                // Apply the search for header cells
                table.columns().eq(0).each(function (colIdx) {
                    `$('input', table.column(colIdx).header()).on('keyup change', function () {
                        table
                            .column(colIdx)
                            .search(this.value)
                            .draw();
                    });
 
                    `$('input', table.column(colIdx).header()).on('click', function (e) {
                        e.stopPropagation();
                    });
                });
"@

        }
    } else {
        $Output.FilteringTopCode = $Output.FilteringBottomCode = '' 
    }
    return $Output
}

function Add-TableHeader {
    <#
    .SYNOPSIS
    Adds table headers to a table with specified styles and responsive operations.
 
    .DESCRIPTION
    The Add-TableHeader function adds table headers to a table based on the provided HeaderRows, HeaderStyle, HeaderTop, HeaderResponsiveOperations, and HeaderNames parameters. It prepares styles for merged headers, standard header rows, and responsive operations.
 
    .PARAMETER HeaderRows
    Specifies the list of custom objects representing header rows.
 
    .PARAMETER HeaderStyle
    Specifies the list of custom objects representing header styles.
 
    .PARAMETER HeaderTop
    Specifies the list of custom objects representing top headers.
 
    .PARAMETER HeaderResponsiveOperations
    Specifies the list of custom objects representing responsive operations for headers.
 
    .PARAMETER HeaderNames
    Specifies an array of header names.
 
    .EXAMPLE
    Add-TableHeader -HeaderRows $headerRows -HeaderStyle $headerStyle -HeaderTop $headerTop -HeaderResponsiveOperations $responsiveOps -HeaderNames @("Name", "Age", "Location")
    Adds table headers with specified styles and responsive operations for the given header rows.
 
    .EXAMPLE
    $headerRows = @(
        [PSCustomObject]@{ Names = @("Name", "Age"); Title = "Personal Info"; Style = "Bold" },
        [PSCustomObject]@{ Names = @("Location"); Title = "Address"; Style = "Italic" }
    )
    $headerStyle = @(
        [PSCustomObject]@{ Names = @("Name", "Age"); Style = "Bold" },
        [PSCustomObject]@{ Names = @("Location"); Style = "Italic" }
    )
    $headerTop = @(
        [PSCustomObject]@{ Names = @("Name", "Age"); Top = "True" },
        [PSCustomObject]@{ Names = @("Location"); Top = "False" }
    )
    $responsiveOps = @(
        [PSCustomObject]@{ Names = @("Name", "Age"); ResponsiveOperations = "Collapse" },
        [PSCustomObject]@{ Names = @("Location"); ResponsiveOperations = "Hide" }
    )
    Add-TableHeader -HeaderRows $headerRows -HeaderStyle $headerStyle -HeaderTop $headerTop -HeaderResponsiveOperations $responsiveOps -HeaderNames @("Name", "Age", "Location")
    Adds table headers with specified styles and responsive operations using sample data.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.Generic.List[PSCustomObject]] $HeaderRows,
        [System.Collections.Generic.List[PSCUstomObject]] $HeaderStyle,
        [System.Collections.Generic.List[PSCUstomObject]] $HeaderTop,
        [System.Collections.Generic.List[PSCUstomObject]] $HeaderResponsiveOperations,
        [string[]] $HeaderNames
    )
    if ($HeaderRows.Count -eq 0 -and $HeaderStyle.Count -eq 0 -and $HeaderTop.Count -eq 0 -and $HeaderResponsiveOperations.Count -eq 0) {
        return
    }

    [Array] $MergeColumns = foreach ($Row in $HeaderRows) {
        $Index = foreach ($R in $Row.Names) {
            [array]::indexof($HeaderNames.ToUpper(), $R.ToUpper())
        }
        if ($Index -contains -1) {
            Write-Warning -Message "Table Header can't be processed properly. Names on the list to merge were not found in Table Header."
        } else {
            @{
                Index = $Index
                Title = $Row.Title
                Count = $Index.Count
                Style = $Row.Style
                Used  = $false
            }
        }
    }

    $ResponsiveOperations = @{ }
    foreach ($Row in $HeaderResponsiveOperations) {
        foreach ($_ in $Row.Names) {
            $Index = [array]::indexof($HeaderNames.ToUpper(), $_.ToUpper())
            $ResponsiveOperations[$Index] = @{
                Index                = $Index
                ResponsiveOperations = $Row.ResponsiveOperations
                Used                 = $false
            }
        }
    }

    $Styles = @{ }
    foreach ($Row in $HeaderStyle) {
        foreach ($_ in $Row.Names) {
            $Index = [array]::indexof($HeaderNames.ToUpper(), $_.ToUpper())
            $Styles[$Index] = @{
                Index = $Index
                Title = $Row.Title
                Count = $Index.Count
                Style = $Row.Style
                Used  = $false
            }
        }
    }

    if ($HeaderTop.Count -gt 0) {
        $UsedColumns = 0
        $ColumnsTotal = $HeaderNames.Count
        $TopHeader = New-HTMLTag -Tag 'tr' {
            foreach ($_ in $HeaderTop) {
                if ($_.ColumnCount -eq 0) {
                    $UsedColumns = $ColumnsTotal - $UsedColumns
                    New-HTMLTag -Tag 'th' -Attributes @{ colspan = $UsedColumns; style = ($_.Style) } -Value { $_.Title }
                } else {
                    if ($_.ColumnCount -le $ColumnsTotal) {
                        $UsedColumns = $UsedColumns + $_.ColumnCount
                    } else {
                        $UsedColumns = - ($ColumnsTotal - $_.ColumnCount)
                    }
                    New-HTMLTag -Tag 'th' -Attributes @{ colspan = $_.ColumnCount; style = ($_.Style) } -Value { $_.Title }
                }

            }
        }
    }

    $AddedHeader = @(
        $NewHeader = [System.Collections.Generic.List[string]]::new()
        $TopHeader
        New-HTMLTag -Tag 'tr' {
            for ($i = 0; $i -lt $HeaderNames.Count; $i++) {
                $Found = $false
                foreach ($_ in $MergeColumns) {
                    if ($_.Index -contains $i) {
                        if ($_.Used -eq $false) {
                            New-HTMLTag -Tag 'th' -Attributes @{ colspan = $_.Count; style = ($_.Style); class = $ResponsiveOperations[$i] } -Value { $_.Title }
                            $_.Used = $true
                            $Found = $true
                        } else {
                            $Found = $true

                        }
                    }
                }
                if (-not $Found) {
                    if ($MergeColumns.Count -eq 0) {

                        New-HTMLTag -Tag 'th' { $HeaderNames[$i] } -Attributes @{ style = $Styles[$i].Style; class = $ResponsiveOperations[$i].ResponsiveOperations }
                    } else {

                        New-HTMLTag -Tag 'th' { $HeaderNames[$i] } -Attributes @{ rowspan = 2; style = $Styles[$i].Style; class = $ResponsiveOperations[$i].ResponsiveOperations }
                    }
                } else {
                    $Head = New-HTMLTag -Tag 'th' { $HeaderNames[$i] } -Attributes @{ style = $Styles[$i].Style; class = $ResponsiveOperations[$i].ResponsiveOperations }
                    $NewHeader.Add($Head)
                }
            }
        }
        if ($NewHeader.Count) {
            New-HTMLTag -Tag 'tr' {
                $NewHeader
            }
        }
    )
    return $AddedHeader
}
function Add-TableRowGrouping {
    <#
    .SYNOPSIS
    Adds row grouping functionality to a DataTable.
 
    .DESCRIPTION
    This function adds row grouping functionality to a DataTable based on the provided settings. It allows collapsing and expanding groups of rows.
 
    .PARAMETER DataTableName
    The name of the DataTable to which row grouping will be applied.
 
    .PARAMETER Settings
    A dictionary containing settings for row grouping.
 
    .PARAMETER Top
    Indicates whether to add row grouping at the top of the table.
 
    .PARAMETER Bottom
    Indicates whether to add row grouping at the bottom of the table.
 
    .EXAMPLE
    Add-TableRowGrouping -DataTableName "myTable" -Settings @{ "group1" = "Group 1"; "group2" = "Group 2" } -Top
 
    Adds row grouping at the top of the table "myTable" with groups "Group 1" and "Group 2".
 
    .EXAMPLE
    Add-TableRowGrouping -DataTableName "anotherTable" -Settings @{ "groupA" = "Group A"; "groupB" = "Group B" } -Bottom
 
    Adds row grouping at the bottom of the table "anotherTable" with groups "Group A" and "Group B".
    #>

    [CmdletBinding()]
    param(
        [string] $DataTableName,
        [System.Collections.IDictionary] $Settings,
        [switch] $Top,
        [switch] $Bottom
    )
    if ($Settings.Count -gt 0) {

        if ($Top) {
            $Output = "var collapsedGroups = {};"
        }
        if ($Bottom) {
            $Output = @"
        `$('#$DataTableName tbody').on('click', 'tr.dtrg-start', function () {
            var name = `$(this).data('name');
            collapsedGroups[name] = !collapsedGroups[name];
            table.draw(false);
        });
"@

        }
        $Output
    }
}
function Add-TableState {
    <#
    .SYNOPSIS
    Adds table state functionality to a DataTable.
 
    .DESCRIPTION
    This function adds table state functionality to a DataTable, allowing for the loading of text input from a saved state.
 
    .PARAMETER Filtering
    Specifies whether filtering is enabled.
 
    .PARAMETER SavedState
    Specifies whether the saved state is being used.
 
    .PARAMETER DataTableName
    The name of the DataTable.
 
    .PARAMETER FilteringLocation
    Specifies the location of the filtering (Top, Bottom, Both).
 
    .EXAMPLE
    Add-TableState -Filtering $true -SavedState $true -DataTableName "myTable" -FilteringLocation "Top"
    Adds table state functionality to the DataTable named "myTable" with filtering at the top.
 
    .EXAMPLE
    Add-TableState -Filtering $true -SavedState $true -DataTableName "anotherTable" -FilteringLocation "Both"
    Adds table state functionality to the DataTable named "anotherTable" with filtering at both the top and bottom.
    #>

    [CmdletBinding()]
    param(
        [bool] $Filtering,
        [bool] $SavedState,
        [string] $DataTableName,
        [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom'
    )
    if ($Filtering -and $SavedState) {
        if ($FilteringLocation -eq 'Top') {
            $Output = @"
                // Setup - Looading text input from SavedState
                `$('#$DataTableName').on('stateLoaded.dt', function(e, settings, data) {
                    settings.aoPreSearchCols.forEach(function(col, index) {
                        if (col.sSearch) setTimeout(function() {
                            `$('#$DataTableName thead th:eq('+index+') input').val(col.sSearch)
                        }, 50)
                    })
                });
"@

        } elseif ($FilteringLocation -eq 'Both') {
            $Output = @"
                // Setup - Looading text input from SavedState
                `$('#$DataTableName').on('stateLoaded.dt', function(e, settings, data) {
                    settings.aoPreSearchCols.forEach(function(col, index) {
                        if (col.sSearch) setTimeout(function() {
                            `$('#$DataTableName thead th:eq('+index+') input').val(col.sSearch)
                        }, 50)
                    })
                });
                // Setup - Looading text input from SavedState
                `$('#$DataTableName').on('stateLoaded.dt', function(e, settings, data) {
                    settings.aoPreSearchCols.forEach(function(col, index) {
                        if (col.sSearch) setTimeout(function() {
                            `$('#$DataTableName tfoot th:eq('+index+') input').val(col.sSearch)
                        }, 50)
                    })
                });
"@


        } else {
            $Output = @"
                // Setup - Looading text input from SavedState
                `$('#$DataTableName').on('stateLoaded.dt', function(e, settings, data) {
                    settings.aoPreSearchCols.forEach(function(col, index) {
                        if (col.sSearch) setTimeout(function() {
                            `$('#$DataTableName tfoot th:eq('+index+') input').val(col.sSearch)
                        }, 50)
                    })
                })
"@


        }
    } else {
        $Output = ''
    }
    return $Output
}
function Convert-TableRowGrouping {
    <#
    .SYNOPSIS
    Converts a specified row grouping column in a table to a collapsible row group.
 
    .DESCRIPTION
    This function converts a specified row grouping column in a table to a collapsible row group. It adds a rowGroup property to the options object with the necessary configuration for the 'row group' plugin.
 
    .PARAMETER Options
    The options object containing the table configuration.
 
    .PARAMETER RowGroupingColumnID
    The ID of the column to be used for row grouping.
 
    .EXAMPLE
    Convert-TableRowGrouping -Options $options -RowGroupingColumnID 2
    Converts the table to have a collapsible row group using column 2 for grouping.
 
    .EXAMPLE
    Convert-TableRowGrouping -Options $options -RowGroupingColumnID 0
    Converts the table to have a collapsible row group using column 0 for grouping.
    #>

    [CmdletBinding()]
    param(
        [string] $Options,
        [int] $RowGroupingColumnID
    )
    if ($RowGroupingColumnID -gt -1) {

        $TextToReplace = @"
        rowGroup: {
            // Uses the 'row group' plugin
            dataSrc: $RowGroupingColumnID,
            startRender: function (rows, group) {
                var collapsed = !!collapsedGroups[group];
 
                rows.nodes().each(function (r) {
                    r.style.display = collapsed ? 'none' : '';
                });
 
                var toggleClass = collapsed ? 'fa-plus-square' : 'fa-minus-square';
 
                // Add group name to <tr>
                return `$('<tr/>')
                    .append('<td colspan="' + rows.columns()[0].length + '">' + '<span class="fa fa-fw ' + toggleClass + ' toggler"/> ' + group + ' (' + rows.count() + ')</td>')
                    .attr('data-name', group)
                    .toggleClass('collapsed', collapsed);
            },
        },
"@

    } else {
        $TextToReplace = ''
    }
    if ($PSEdition -eq 'Desktop') {
        $TextToFind = '"rowGroup": "",'
    } else {
        $TextToFind = '"rowGroup": "",'
    }

    $Options = $Options -Replace ($TextToFind, $TextToReplace)
    $Options
}
function New-TableConditionalFormatting {
    <#
    .SYNOPSIS
    Creates conditional formatting for a table based on specified conditions.
 
    .DESCRIPTION
    This function applies conditional formatting to a table based on the provided conditions. It generates JavaScript code to be used in DataTables for highlighting rows based on the conditions specified.
 
    .PARAMETER Options
    Specifies additional options for the conditional formatting.
 
    .PARAMETER ConditionalFormatting
    An array of objects representing the conditions for conditional formatting.
 
    .PARAMETER Header
    An array of strings representing the headers of the table.
 
    .PARAMETER DataStore
    Specifies the data store to be used for conditional formatting.
 
    .EXAMPLE
    New-TableConditionalFormatting -Options "option1" -ConditionalFormatting @($condition1, $condition2) -Header @("Header1", "Header2") -DataStore "Store1"
    Creates conditional formatting for a table with specified conditions, headers, and data store.
 
    .EXAMPLE
    $conditions = @($condition1, $condition2)
    New-TableConditionalFormatting -Options "option1" -ConditionalFormatting $conditions -Header @("Header1", "Header2") -DataStore "Store1"
    Creates conditional formatting for a table using a variable to store the conditions.
 
    #>

    [CmdletBinding()]
    param(
        [string] $Options,
        [Array] $ConditionalFormatting,
        [string[]] $Header,
        [string] $DataStore
    )
    if ($ConditionalFormatting.Count -gt 0) {
        $ConditionsReplacement = @(
            '"rowCallback": function (row, data) {'
            foreach ($Condition in $ConditionalFormatting) {
                if ($Condition.ConditionType -eq 'Condition') {

                    if ($Condition.Row) {
                        $HighlightHeaders = 'null'
                    } else {
                        [Array] $HighlightHeaders = New-TableConditionHeaderHighligher -Condition $Condition -Header $Header 
                        if ($HighlightHeaders.Count -eq 0) {
                            continue
                        }
                    }
                    [Array] $ConditionsContainer = @(
                        [ordered]@{
                            logic      = 'AND'
                            conditions = @( New-TableConditionInternal -Condition $Condition -Header $Header -DataStore $DataStore )
                        }
                    )
                    " var css = $($Condition.Style | ConvertTo-Json);"
                    if ($Condition.FailStyle.Keys.Count -gt 0) {
                        " var failCss = $($Condition.FailStyle | ConvertTo-Json);"
                    } else {
                        " var failCss = undefined;"
                    }
                    " var conditionsContainer = $($ConditionsContainer | ConvertTo-JsonLiteral -Depth 5 -AsArray -AdvancedReplace @{ '.' = '\.'; '$' = '\$' });"
                    " dataTablesConditionalFormatting(row, data, conditionsContainer, $HighlightHeaders, css, failCss);"

                } else {
                    if ($Condition.Row) {
                        $HighlightHeaders = 'null'
                    } else {
                        [Array] $HighlightHeaders = New-TableConditionHeaderHighligher -Condition $Condition -Header $Header
                        if ($HighlightHeaders.Count -eq 0) {
                            continue
                        }
                    }
                    [Array] $ConditionsContainer = @(
                        [ordered]@{
                            logic      = $Condition.Logic
                            conditions = @(
                                foreach ($NestedCondition in $Condition.Conditions) {
                                    if ($NestedCondition.Type -eq 'TableCondition') {
                                        New-TableConditionInternal -Condition $NestedCondition.Output -Header $Header -DataStore $DataStore
                                    }
                                }
                            )
                        }
                    )
                    " var css = $($Condition.Style | ConvertTo-Json);"
                    if ($Condition.FailStyle.Keys.Count -gt 0) {
                        " var failCss = $($Condition.FailStyle | ConvertTo-Json);"
                    } else {
                        " var failCss = undefined;"
                    }
                    " var conditionsContainer = $($ConditionsContainer | ConvertTo-JsonLiteral -Depth 5 -AsArray -AdvancedReplace @{ '.' = '\.'; '$' = '\$' });"
                    " dataTablesConditionalFormatting(row, data, conditionsContainer, $HighlightHeaders, css, failCss);"
                }

            }

            "}"
        )
        if ($PSEdition -eq 'Desktop') {
            $TextToFind = '"createdRow": ""'
        } else {
            $TextToFind = '"createdRow": ""'
        }
        $Options = $Options -Replace ($TextToFind, $ConditionsReplacement)
    }
    $Options
}

function New-TableConditionalFormattingInline {
    <#
    .SYNOPSIS
    Creates conditional formatting for a table based on specified criteria.
 
    .DESCRIPTION
    This function applies conditional formatting to a table based on the provided criteria. It supports formatting based on numeric values and dates.
 
    .PARAMETER HeaderNames
    Specifies the names of the headers in the table.
 
    .PARAMETER ConditionalFormatting
    Specifies the criteria for conditional formatting. It includes the type of formatting (number or date), the operator (e.g., 'between', 'betweenInclusive'), and the value(s) to compare against.
 
    .PARAMETER RowData
    Specifies the data of a row in the table.
 
    .PARAMETER ColumnCount
    Specifies the number of columns in the table.
 
    .PARAMETER RowCount
    Specifies the number of rows in the table.
 
    .PARAMETER ColumnIndexHeader
    Specifies the index of the column header.
 
    .EXAMPLE
    $headerNames = @("Name", "Age", "Date")
    $conditionalFormatting = [PSCustomObject]@{
        Type = 'number'
        Operator = 'between'
        Value = @(20, 30)
    }
    $rowData = @("John", 25, "01/01/1990")
    $columnCount = 3
    $rowCount = 1
    $columnIndexHeader = 2
 
    New-TableConditionalFormattingInline -HeaderNames $headerNames -ConditionalFormatting $conditionalFormatting -RowData $rowData -ColumnCount $columnCount -RowCount $rowCount -ColumnIndexHeader $columnIndexHeader
 
    #>

    [CmdletBinding()]
    param(
        [string[]] $HeaderNames,
        [PSCustomObject] $ConditionalFormatting,
        [Array] $RowData,
        [int] $ColumnCount,
        [int] $RowCount,
        [int] $ColumnIndexHeader
    )
    [bool] $Pass = $false
    if ($ConditionalFormatting.Type -eq 'number') {
        if ($ConditionalFormatting.operator -in 'between', 'betweenInclusive') {
            [decimal] $returnedValueLeft = 0
            [bool] $resultLeft = [decimal]::TryParse($RowData[$ColumnCount], [ref]$returnedValueLeft)
            [bool] $resultRight = $false
            [Array] $returnedValueRight = foreach ($Value in $ConditionalFormatting.Value) {
                [decimal]$returnedValue = 0
                $resultRight = [decimal]::TryParse($Value, [ref]$returnedValue)
                if ($resultRight) {
                    $returnedValue
                } else {
                    break
                }
            }
        } else {
            [decimal] $returnedValueLeft = 0
            [bool]$resultLeft = [decimal]::TryParse($RowData[$ColumnCount], [ref]$returnedValueLeft)

            [decimal]$returnedValueRight = 0
            [bool]$resultRight = [decimal]::TryParse($ConditionalFormatting.Value, [ref]$returnedValueRight)
        }
        if ($resultLeft -and $resultRight) {
            $SideLeft = $returnedValueLeft
            $SideRight = $returnedValueRight
        } else {
            $SideLeft = $RowData[$ColumnCount]
            $SideRight = $ConditionalFormatting.Value
        }
    } elseif ($ConditionalFormatting.Type -eq 'date') {
        try {
            if ($ConditionalFormatting.DateTimeFormat) {
                $SideLeft = [DateTime]::ParseExact($RowData[$ColumnCount], $ConditionalFormatting.DateTimeFormat, $null)
            } else {
                $SideLeft = [DateTime]::Parse($RowData[$ColumnCount])
            }
        } catch {
            $SideLeft = $RowData[$ColumnCount]

        }
        $SideRight = $ConditionalFormatting.Value
    } else {
        $SideLeft = $RowData[$ColumnCount]
        $SideRight = $ConditionalFormatting.Value
    }
    if ($ConditionalFormatting.ReverseCondition) {
        $TempSide = $SideLeft
        $SideLeft = $SideRight
        $SideRight = $TempSide
    }
    if ($ConditionalFormatting.Operator -eq 'gt') {
        $Pass = $SideLeft -gt $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'lt') {
        $Pass = $SideLeft -lt $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'eq') {
        $Pass = $SideLeft -eq $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'le') {
        $Pass = $SideLeft -le $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'ge') {
        $Pass = $SideLeft -ge $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'ne') {
        $Pass = $SideLeft -ne $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'in') {
        $Pass = $SideLeft -in $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'notin') {
        $Pass = $SideLeft -notin $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'like') {
        $Pass = $SideLeft -like $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'contains') {
        $Pass = $SideLeft -contains $SideRight
    } elseif ($ConditionalFormatting.Operator -eq 'betweenInclusive') {
        $Pass = $SideLeft -ge $SideRight[0] -and $SideLeft -le $SideRight[1]
    } elseif ($ConditionalFormatting.Operator -eq 'between') {
        $Pass = $SideLeft -gt $SideRight[0] -and $SideLeft -lt $SideRight[1]
    }
    $Pass
}
function New-TableConditionHeaderHighligher {
    <#
    .SYNOPSIS
    Highlights specific headers in a table based on given conditions.
 
    .DESCRIPTION
    This function highlights specific headers in a table based on the conditions provided. It searches for the specified headers and returns their column IDs.
 
    .PARAMETER Condition
    Specifies the condition object containing information about the headers to highlight.
 
    .PARAMETER Header
    Specifies the headers of the table to search for highlighting.
 
    .EXAMPLE
    $condition = [PSCustomObject]@{
        HighlightHeaders = @('Name', 'Age')
    }
    $header = @('ID', 'Name', 'Age', 'Location')
    New-TableConditionHeaderHighligher -Condition $condition -Header $header
 
    This example highlights the 'Name' and 'Age' headers in the table with headers 'ID', 'Name', 'Age', and 'Location'.
 
    #>

    [CmdletBinding()]
    param(
        [PSCustomObject] $Condition,
        [string[]]$Header
    )
    [Array] $ConditionHeaderNr = @(
        if ($Condition.HighlightHeaders) {

            foreach ($HeaderName in $Condition.HighlightHeaders) {
                $ColumnID = $Header.ToLower().IndexOf($($HeaderName.ToLower()))
                if ($ColumnID -ne -1) {
                    $ColumnID
                }
            }
        } else {

            foreach ($HeaderName in $Condition.Name) {
                $ColumnID = $Header.ToLower().IndexOf($($HeaderName.ToLower()))
                if ($ColumnID -ne -1) {
                    $ColumnID
                }
            }
        }
    )
    if ($ConditionHeaderNr.Count -gt 0) {
        $ConditionHeaderNr | ConvertTo-JsonLiteral -AsArray -AdvancedReplace @{ '.' = '\.'; '$' = '\$' }
    } else {
        $ColumnNames = @(
            if ($Condition.HighlightHeaders) {
                $Condition.HighlightHeaders
            }
            if ($Condition.Name) {
                $Condition.Name
            }
        )
        if ($ColumnNames.Count -gt 0) {
            Write-Warning "New-TableCondition - None of the column names exists $ColumnNames in condition. Skipping."
        } else {
            Write-Warning "New-TableCondition - None of the column names found to process. Please use HighlightHeaders or Row switch when using New-TableConditionGroup."
        }
    }
}
function New-TableConditionInternal {
    <#
    .SYNOPSIS
    Creates a new table condition object based on the provided parameters.
 
    .DESCRIPTION
    This function creates a new table condition object with specified properties such as column name, column ID, operator, type, value, value date, data store, case sensitivity, date time format, and reverse condition.
 
    .PARAMETER Condition
    Specifies the condition object containing properties like Name, Operator, Type, Value, case sensitivity, DateTimeFormat, and ReverseCondition.
 
    .PARAMETER Header
    Specifies an array of header strings.
 
    .PARAMETER DataStore
    Specifies the data store for the condition.
 
    .EXAMPLE
    $condition = [PSCustomObject]@{
        Name = "Column1"
        Operator = "Equals"
        Type = "String"
        Value = "Value1"
        caseSensitive = $true
        DateTimeFormat = "yyyy-MM-dd"
        ReverseCondition = $false
    }
    $header = @("Column1", "Column2", "Column3")
    $dataStore = "DataStore1"
    $result = New-TableConditionInternal -Condition $condition -Header $header -DataStore $dataStore
    $result
 
    #>

    [CmdletBinding()]
    param(
        [PSCustomObject] $Condition,
        [string[]]$Header,
        [string] $DataStore
    )
    $Cond = [ordered] @{
        columnName       = $Condition.Name
        columnId         = $Header.ToLower().IndexOf($($Condition.Name.ToLower()))
        operator         = $Condition.Operator
        type             = $Condition.Type.ToLower()
        value            = $Condition.Value
        valueDate        = $null
        dataStore        = $DataStore
        caseSensitive    = $Condition.caseSensitive
        dateTimeFormat   = $Condition.DateTimeFormat
        reverseCondition = $Condition.ReverseCondition
    }
    if ($Cond['value'] -is [datetime]) {
        $Cond['valueDate'] = @{
            year        = $Cond['value'].Year
            month       = $Cond['value'].Month
            day         = $Cond['value'].Day
            hours       = $Cond['value'].Hour
            minutes     = $Cond['value'].Minute
            seconds     = $Cond['value'].Second
            miliseconds = $Cond['value'].Millisecond
        }
    } elseif ($Cond['value'] -is [Array] -and $Cond['value'][0] -is [datetime]) {
        [Array] $Cond['valueDate'] = foreach ($Date in $Cond['value']) {
            @{
                year        = $Date.Year
                month       = $Date.Month
                day         = $Date.Day
                hours       = $Date.Hour
                minutes     = $Date.Minute
                seconds     = $Date.Second
                miliseconds = $Date.Millisecond
            }
        }
    }
    $Cond
}
function Add-ConfigurationCSS {
    <#
    .SYNOPSIS
    Adds or updates CSS configuration settings in a specified CSS dictionary.
 
    .DESCRIPTION
    The Add-ConfigurationCSS function allows you to add or update CSS configuration settings in a specified CSS dictionary. You can inject new CSS settings or overwrite existing ones based on the provided parameters.
 
    .PARAMETER CSS
    Specifies the CSS dictionary where the configuration settings will be added or updated.
 
    .PARAMETER Name
    Specifies the name of the CSS configuration settings to be added or updated.
 
    .PARAMETER Inject
    Specifies the CSS settings to be injected or overwritten in the CSS dictionary.
 
    .PARAMETER Overwrite
    Indicates whether existing CSS settings should be overwritten if they already exist.
 
    .EXAMPLE
    $CSS = @{}
    Add-ConfigurationCSS -CSS $CSS -Name "ButtonStyles" -Inject @{ 'color' = 'blue'; 'font-size' = '16px' }
 
    Description:
    Adds new CSS settings for the "ButtonStyles" configuration in the CSS dictionary.
 
    .EXAMPLE
    $CSS = @{}
    Add-ConfigurationCSS -CSS $CSS -Name "HeaderStyles" -Inject @{ 'color' = 'green'; 'font-size' = '20px' } -Overwrite
 
    Description:
    Updates existing CSS settings for the "HeaderStyles" configuration in the CSS dictionary by overwriting them.
 
    #>

    [cmdletBinding()]
    param(
        [System.Collections.IDictionary] $CSS,
        [string] $Name,
        [System.Collections.IDictionary] $Inject,
        [switch] $Overwrite
    )

    if ($Inject) {
        Remove-EmptyValue -Hashtable $Inject
        if ($Css) {
            if ($CSS[$Name] -and (-not $Overwrite)) {

                foreach ($Key in $Inject.Keys) {
                    $CSS[$Name][$Key] = $Inject[$Key]
                }
            } else {
                $CSS[$Name] = $Inject
            }
        }
    }
}
function Add-ParametersToScriptBlock {
    <#
    .SYNOPSIS
    Adds parameters to scriptblock providing ability to define scriptblock in different place and simply provide parameters later on
 
    .DESCRIPTION
    Adds parameters to scriptblock providing ability to define scriptblock in different place and simply provide parameters later on
 
    .PARAMETER ScriptBlock
    ScriptBlock to modify
 
    .PARAMETER Parameter
    Hashtable of parameters/values to add into scriptblock
 
    .EXAMPLE
    $TemplatePreExpiry = {
        EmailImage -Source "https://evotec.xyz/wp-content/uploads/2015/05/Logo-evotec-012.png" -Height 50 -Width 200
 
        EmailText -Text "Hello ", "$DisplayName" -LineBreak
        EmailText -Text "Your password is due to expire in $DaysToExpire days. You last changed password $PasswordLastSet"
 
        EmailText -Text "To change your password:"
        EmailList {
            EmailListItem "press CTRL+ALT+DEL -> Change a password.."
        }
 
        EmailText -Text "If you have forgotten you password and need to reset it, you can do this by [visiting password change website](https://evotec.xyz)"
        EmailText -Text "In case of problems please contact HelpDesk by [visting website](https://evotec.xyz) or by sending an email to servicedesk@evotec.pl."
 
        EmailText -Text @(
            "Alternatively you can always call Service Desk at ", "+48 22 600 20 20"
 
            "Kind regards,"
            "Evotec IT"
        )
    }
 
    Add-ParametersToScriptBlock -ScriptBlock $TemplatePreExpiry -Parameter $Parameter
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [scriptblock] $ScriptBlock,
        [System.Collections.IDictionary] $Parameter
    )

    if ($ScriptBlock) {
        if ($Parameter) {
            $Count = 0
            [string] $ScriptBlockParams = @(
                "param("
                foreach ($Key in $Parameter.Keys) {
                    $Count++
                    if ($Count -eq $Parameter.Keys.Count) {
                        "`$$($Key)"
                    } else {
                        "`$$($Key),"
                    }
                }
                ")"
                $ScriptBlock.ToString()
            )
            $ScriptBlockScript = [scriptblock]::Create($ScriptBlockParams)
            $ScriptBlockScript
        } else {
            $ScriptBlock
        }
    }
}
function Convert-FontToBinary {
    <#
    .SYNOPSIS
    Converts a specified font file to a base64-encoded string and replaces a given search string in content with the encoded font data.
 
    .DESCRIPTION
    The Convert-FontToBinary function takes a font file, converts it to a base64-encoded string, and then replaces a specified search string in the content with the encoded font data. This can be useful for embedding font data directly into content.
 
    .PARAMETER Content
    The content where the search string will be replaced with the encoded font data.
 
    .PARAMETER Search
    The string to search for in the content that will be replaced with the encoded font data.
 
    .PARAMETER ReplacePath
    The path to the font file that will be converted to base64.
 
    .PARAMETER FileType
    The type of file being replaced, used to construct the data URI.
 
    .EXAMPLE
    $content = "body { font-family: 'MyFont', sans-serif; }"
    $search = "'MyFont'"
    $replacePath = "C:\Fonts\MyFont.ttf"
    $fileType = "ttf"
 
    Convert-FontToBinary -Content $content -Search $search -ReplacePath $replacePath -FileType $fileType
 
    Description:
    Converts the font file "MyFont.ttf" to base64 and replaces the font reference in the content with the base64-encoded font data.
 
    #>

    [CmdLetBinding()]
    param(
        [string[]] $Content,
        [string] $Search,
        [string] $ReplacePath,
        [string] $FileType
    )
    if ($Content -like "*$Search*") {
        if ($PSEdition -eq 'Core') {
            $ImageContent = Get-Content -AsByteStream -LiteralPath $ReplacePath
        } else {
            $ImageContent = Get-Content -LiteralPath $ReplacePath -Encoding Byte
        }
        $Replace = "data:application/$FileType;charset=utf-8;base64," + [Convert]::ToBase64String($ImageContent)
        $Content = $Content.Replace($Search, $Replace)
    }
    $Content
}
function ConvertFrom-Rotate {
    <#
    .SYNOPSIS
    Converts the input value to a CSS rotation string.
 
    .DESCRIPTION
    This function converts the input value to a CSS rotation string based on the input type and content.
 
    .PARAMETER Rotate
    Specifies the value to be converted to a CSS rotation string.
 
    .EXAMPLE
    ConvertFrom-Rotate -Rotate 90
    Output: "rotate(90deg)"
 
    .EXAMPLE
    ConvertFrom-Rotate -Rotate "rotate(180deg)"
    Output: "rotate(180deg)"
 
    .EXAMPLE
    ConvertFrom-Rotate -Rotate "180deg"
    Output: "rotate(180deg)"
 
    .EXAMPLE
    ConvertFrom-Rotate -Rotate "180"
    Output: "rotate(180deg)"
    #>

    [cmdletBinding()]
    param(
        [object] $Rotate
    )
    if ($Rotate -is [int]) {
        if ($Rotate -ne 0) {
            "rotate($($Rotate)deg)"
        }
    } elseif ($Rotate -is [string]) {
        if ($Rotate) {
            if (($Rotate -like '*deg*') -and ($Rotate -notlike '*rotate*')) {
                "rotate($Rotate)"
            } elseif (($Rotate -like '*deg*') -and ($Rotate -like '*rotate*')) {
                "$Rotate"
            } else {
                $Rotate
            }
        }
    }
}
function Convert-Image {
    <#
    .SYNOPSIS
    Converts an image file to a binary format.
 
    .DESCRIPTION
    The Convert-Image function takes an image file path as input and converts it to a binary format.
 
    .PARAMETER Image
    Specifies the path to the image file to be converted.
 
    .PARAMETER Cache
    Indicates whether to cache the image file for future use.
 
    .EXAMPLE
    Convert-Image -Image "C:\Images\example.jpg" -Cache
    Description:
    Converts the image file "example.jpg" to a binary format and caches it for future use.
 
    #>

    [CmdletBinding()]
    param(
        [string] $Image,
        [switch] $Cache
    )

    $ImageFile = Get-ImageFile -Image $Image -Cache:$Cache
    if ($ImageFile) {
        Convert-ImageToBinary -ImageFile $ImageFile
    }
}
function Convert-ImagesToBinary {
    <#
    .SYNOPSIS
    Converts images in the input content to base64 format.
 
    .DESCRIPTION
    This function searches for a specified string in the input content and replaces it with the base64 representation of an image file located at the specified path.
 
    .PARAMETER Content
    The input content where the search and replacement will be performed.
 
    .PARAMETER Search
    The string to search for in the input content.
 
    .PARAMETER ReplacePath
    The path to the image file that will replace the search string in the input content.
 
    .EXAMPLE
    Convert-ImagesToBinary -Content $content -Search "logo.png" -ReplacePath "C:\Images\logo.png"
    This example searches for the string "logo.png" in the input content and replaces it with the base64 representation of the image file located at "C:\Images\logo.png".
 
    #>

    [CmdLetBinding()]
    param(
        [string[]] $Content,
        [string] $Search,
        [string] $ReplacePath
    )
    if ($Content -like "*$Search*") {
        if ($PSEdition -eq 'Core') {
            $ImageContent = Get-Content -AsByteStream -LiteralPath $ReplacePath
        } else {
            $ImageContent = Get-Content -LiteralPath $ReplacePath -Encoding Byte
        }
        $Replace = "data:image/$FileType;base64," + [Convert]::ToBase64String($ImageContent)
        $Content = $Content.Replace($Search, $Replace)
    }
    $Content
}
function Convert-ImageToBinary {
    <#
    .SYNOPSIS
    Converts an image file to base64 encoding.
 
    .DESCRIPTION
    This function converts the specified image file to base64 encoding and generates a data URI for embedding images in HTML or CSS.
 
    .PARAMETER ImageFile
    Specifies the image file to be converted. Provide the full path to the image file.
 
    .EXAMPLE
    Convert-ImageToBinary -ImageFile "C:\Images\example.jpg"
    Converts the "example.jpg" image file to base64 encoding.
 
    .EXAMPLE
    Convert-ImageToBinary -ImageFile "C:\Images\logo.svg"
    Converts the "logo.svg" image file to base64 encoding.
 
    .NOTES
    File types supported for conversion: jpg, svg.
 
    #>

    [CmdletBinding()]
    param(
        [System.IO.FileInfo] $ImageFile
    )

    if ($ImageFile.Extension -eq '.jpg') {
        $FileType = 'jpeg'
    } elseif ($ImageFile.Extension -eq '.svg') {
        $FileType = 'svg+xml'
    } else {
        $FileType = $ImageFile.Extension.Replace('.', '')
    }
    Write-Verbose "Converting $($ImageFile.FullName) to base64 ($FileType)"

    if ($PSEdition -eq 'Core') {
        $ImageContent = Get-Content -AsByteStream -LiteralPath $ImageFile.FullName
    } else {
        $ImageContent = Get-Content -LiteralPath $ImageFile.FullName -Encoding Byte
    }
    $Output = "data:image/$FileType;base64," + [Convert]::ToBase64String(($ImageContent))
    $Output
}
function ConvertFrom-Size {
    <#
    .SYNOPSIS
    Converts the input size to a pixel value.
 
    .DESCRIPTION
    This function converts the input size to a pixel value. It accepts either an integer or a string representing a number.
 
    .PARAMETER Size
    Specifies the size to convert. This can be provided as an integer or a string.
 
    .EXAMPLE
    ConvertFrom-Size -Size 12
    Output: "12px"
 
    .EXAMPLE
    ConvertFrom-Size -Size "24"
    Output: "24px"
 
    .NOTES
    Author: Your Name
    Date: Current Date
    Version: 1.0
    #>

    [cmdletBinding()]
    param(
        [alias('TextSize', 'FontSize')][object] $Size
    )
    if ($Size -is [int]) {
        "$($Size)px"
    } elseif ($Size -is [string]) {
        $IntSize = 0
        $Conversion = [int]::TryParse($Size, [ref] $IntSize)
        if ($Conversion) {
            "$($Size)px"
        } else {
            $Size
        }
    } else {
        $Size
    }
}
function ConvertTo-HTMLStyle {
    <#
    .SYNOPSIS
    Converts input parameters to HTML style attributes.
 
    .DESCRIPTION
    This function converts input parameters to HTML style attributes for styling elements in HTML documents.
 
    .PARAMETER Color
    Specifies the color of the text.
 
    .PARAMETER BackGroundColor
    Specifies the background color of the text.
 
    .PARAMETER FontSize
    Specifies the font size.
 
    .PARAMETER FontWeight
    Specifies the font weight. Valid values are 'normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900'.
 
    .PARAMETER FontStyle
    Specifies the font style. Valid values are 'normal', 'italic', 'oblique'.
 
    .PARAMETER FontVariant
    Specifies the font variant. Valid values are 'normal', 'small-caps'.
 
    .PARAMETER FontFamily
    Specifies the font family.
 
    .PARAMETER Alignment
    Specifies the text alignment. Valid values are 'left', 'center', 'right', 'justify'.
 
    .PARAMETER TextDecoration
    Specifies the text decoration. Valid values are 'none', 'line-through', 'overline', 'underline'.
 
    .PARAMETER TextTransform
    Specifies the text transformation. Valid values are 'uppercase', 'lowercase', 'capitalize'.
 
    .PARAMETER Direction
    Specifies the text direction. Valid values are 'rtl'.
 
    .PARAMETER LineBreak
    Indicates whether to add a line break.
 
    .PARAMETER WordBreak
    Specifies the word break behavior. Valid values are 'normal', 'break-all', 'keep-all', 'break-word'.
 
    .EXAMPLE
    ConvertTo-HTMLStyle -Color 'red' -FontSize '12px' -FontWeight 'bold' -Alignment 'center'
    Converts the input parameters to HTML style attributes for a text element with red color, 12px font size, bold font weight, and centered alignment.
 
    #>

    [CmdletBinding()]
    param(
        [string]$Color,
        [string]$BackGroundColor,
        [object] $FontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [string] $FontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string]  $Alignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string]  $TextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string]  $TextTransform,
        [ValidateSet('rtl')][string] $Direction,
        [switch] $LineBreak,
        [ValidateSet('normal', 'break-all', 'keep-all', 'break-word')][string] $WordBreak
    )
    $Style = @{
        'color'            = ConvertFrom-Color -Color $Color
        'background-color' = ConvertFrom-Color -Color $BackGroundColor
        'font-size'        = ConvertFrom-Size -FontSize $FontSize
        'font-weight'      = $FontWeight
        'font-variant'     = $FontVariant
        'font-family'      = $FontFamily
        'font-style'       = $FontStyle
        'text-align'       = $Alignment
        'text-decoration'  = $TextDecoration
        'text-transform'   = $TextTransform
        'word-break'       = $WordBreak
    }

    Remove-EmptyValue -Hashtable $Style
    $Style
}
function ConvertTo-LimitedCSS {
    <#
    .SYNOPSIS
    Converts a dictionary of CSS attributes into a limited CSS string.
 
    .DESCRIPTION
    This function takes a dictionary of CSS attributes and converts them into a limited CSS string. It supports adding an ID, class name, and grouping the CSS attributes.
 
    .PARAMETER ID
    The ID to be used in the CSS selector.
 
    .PARAMETER ClassName
    The class name to be used in the CSS selector.
 
    .PARAMETER Attributes
    A dictionary containing the CSS attributes to be converted.
 
    .PARAMETER Group
    Switch parameter to indicate if the CSS should be grouped within <style> tags.
 
    .EXAMPLE
    ConvertTo-LimitedCSS -ID "myID" -ClassName "myClass" -Attributes @{ "color" = "red"; "font-size" = "12px" } -Group
    Generates the following CSS:
    #myID .myClass {
        color: red;
        font-size: 12px;
    }
 
    #>

    [CmdletBinding()]
    param(
        [string] $ID,
        [string] $ClassName,
        [System.Collections.IDictionary] $Attributes,
        [switch] $Group
    )
    if ($Attributes) {
        Remove-EmptyValue -Hashtable $Attributes
    }
    if ($Attributes.Count -eq 0) {

        return
    }
    [string] $Css = @(
        if ($Group) {
            '<style>'
        }
        if ($ID) {
            "#$ID $ClassName {"
        } else {
            if ($ClassName.StartsWith('.')) {
                "$ClassName {"
            } elseif ($ClassName.StartsWith('[')) {
                "$ClassName {"
            } else {
                ".$ClassName {"
            }
        }
        foreach ($_ in $Attributes.Keys) {
            if ($null -ne $Attributes[$_]) {

                $Property = $_.Replace(' ', '')
                " $Property`: $($Attributes[$_]);"
            }
        }
        '}'
        if ($Group) {
            '</style>'
        }
    ) -join "`n"
    $CSS
}
function ConvertTo-Size {
    <#
    .SYNOPSIS
    Converts a given size value to an integer value, supporting both pixels (px) and points (pt).
 
    .DESCRIPTION
    This function takes a size value as input and converts it to an integer value. It supports both pixel (px) and point (pt) units. If the input size is in points, it will be converted to pixels using a conversion factor of 1.3333333333333333.
 
    .PARAMETER Size
    Specifies the size value to be converted. This parameter supports aliases 'TextSize' and 'FontSize'.
 
    .EXAMPLE
    ConvertTo-Size -Size 12
    Converts the size value 12 to an integer value 12.
 
    .EXAMPLE
    ConvertTo-Size -Size '14px'
    Converts the size value '14px' to an integer value 14.
 
    .EXAMPLE
    ConvertTo-Size -Size '10pt'
    Converts the size value '10pt' to an integer value 13 (converted to pixels using the conversion factor).
 
    #>

    [cmdletBinding()]
    param(
        [alias('TextSize', 'FontSize')][object] $Size
    )
    $Point = $false
    if ($Size -is [int]) {
        $Size
    } elseif ($Size -is [string]) {
        $IntSize = 0
        if ($Size -like '*px') {
            $Size = $Size -replace 'px'
        } elseif ($Size -like '*pt') {
            $Size = $Size -replace 'pt'
            $Point = $true 
        }
        $Conversion = [int]::TryParse($Size, [ref] $IntSize)
        if ($Conversion) {
            if ($Point) {
                $IntSize * 1.3333333333333333
            } else {
                $IntSize
            }
        }
    }
}
function ConvertTo-SVG {
    <#
    .SYNOPSIS
    Converts the input content to a data URI for embedding SVG images in HTML or CSS.
 
    .DESCRIPTION
    This function takes the input content and file type and converts it to a data URI suitable for embedding SVG images in HTML or CSS. It encodes the content in UTF-8 format and constructs the data URI accordingly.
 
    .PARAMETER Content
    Specifies the content to be converted to a data URI.
 
    .PARAMETER FileType
    Specifies the type of file being converted. This should be 'svg' for SVG images.
 
    .EXAMPLE
    ConvertTo-SVG -Content "SVG content here" -FileType 'svg'
    Converts the input SVG content to a data URI for embedding in HTML or CSS.
 
    #>

    [CmdLetBinding()]
    param(
        [string] $Content,
        [string] $FileType
    )
    if ($Content) {
        $Replace = "data:image/$FileType;charset=utf-8," + [uri]::EscapeDataString($Content)
        $Replace
    }
}
function Get-ConfigurationCSS {
    <#
    .SYNOPSIS
    Retrieves the CSS configuration for a specified feature and type.
 
    .DESCRIPTION
    This function retrieves the CSS configuration for a specified feature and type from the current configuration.
 
    .PARAMETER Feature
    Specifies the feature for which the CSS configuration is requested.
 
    .PARAMETER Type
    Specifies the type of CSS configuration to retrieve.
 
    .EXAMPLE
    Get-ConfigurationCSS -Feature "Header" -Type "Desktop"
    Retrieves the CSS configuration for the Header feature on the Desktop type.
 
    .EXAMPLE
    Get-ConfigurationCSS -Feature "Footer" -Type "Mobile"
    Retrieves the CSS configuration for the Footer feature on the Mobile type.
    #>

    [cmdletBinding()]
    param(
        [string] $Feature,
        [string] $Type
    )
    return $Script:CurrentConfiguration.Features.$Feature.$Type.CssInline
}
function Get-FeaturesInUse {
    <#
    .SYNOPSIS
    Defines which features will be used within HTML and in which order
 
    .DESCRIPTION
    Defines which features will be used within HTML and in which order
 
    .PARAMETER PriorityFeatures
    Define priority features - important for ordering when CSS or JS has to be processed in certain order
 
    .EXAMPLE
    Get-FeaturesInUse -PriorityFeatures 'Jquery', 'DataTables', 'Tabs', 'Test'
 
    .EXAMPLE
    Get-FeaturesInUse -PriorityFeatures 'Jquery', 'DataTables', 'Tabs', 'Test' -Email
 
    .NOTES
    General notes
    #>


    [CmdletBinding()]
    param(
        [string[]] $PriorityFeatures,
        [switch] $Email
    )
    [Array] $Features = @(
        $Script:HTMLSchema.Features.Keys
        $Script:GlobalSchema.Features.Keys
    )

    [Array] $Features = foreach ($Key in $Features | Sort-Object -Unique) {
        if ($Script:CurrentConfiguration['Features'][$Key]) {
            if ($Email) {
                if ($Script:CurrentConfiguration['Features'][$Key]['Email'] -ne $true) {
                    continue
                }
            } else {
                if ($Script:CurrentConfiguration['Features'][$Key]['Default'] -ne $true) {
                    continue
                }
            }
            $Key
        }
    }
    [Array] $TopFeatures = foreach ($Feature in $PriorityFeatures) {
        if ($Features -contains $Feature) {
            $Feature
        }
    }
    [Array] $RemainingFeatures = foreach ($Feature in $Features) {
        if ($TopFeatures -notcontains $Feature) {
            $Feature
        }
    }
    [Array] $AllFeatures = $TopFeatures + $RemainingFeatures
    $AllFeatures
}
Function Get-HTMLLogos {
    <#
    .SYNOPSIS
    Retrieves HTML logos from specified paths and converts them to binary format.
 
    .DESCRIPTION
    This function retrieves HTML logos from specified paths and converts them to binary format. It allows for customization of left and right logos with default names "Sample" and "Alternate" respectively.
 
    .PARAMETER LogoPath
    The path to the logos. Default is not set.
 
    .PARAMETER LeftLogoName
    The name of the left logo. Default is "Sample".
 
    .PARAMETER RightLogoName
    The name of the right logo. Default is "Alternate".
 
    .PARAMETER LeftLogoString
    The path to the left logo file.
 
    .PARAMETER RightLogoString
    The path to the right logo file.
 
    .EXAMPLE
    Get-HTMLLogos -LeftLogoName "CompanyLogo" -LeftLogoString "C:\Logos\CompanyLogo.png"
    Retrieves the left logo named "CompanyLogo" from the specified path.
 
    .EXAMPLE
    Get-HTMLLogos -RightLogoName "BrandLogo" -RightLogoString "C:\Logos\BrandLogo.jpg"
    Retrieves the right logo named "BrandLogo" from the specified path.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]$LogoPath,
        [string]$LeftLogoName = "Sample",
        [string]$RightLogoName = "Alternate",
        [string]$LeftLogoString,
        [string]$RightLogoString

    )
    $LogoSources = [ordered] @{ }
    if ([String]::IsNullOrEmpty($LogoPath)) {
        $LogoPath = @(
            if ([String]::IsNullOrEmpty($RightLogoString) -eq $false -or [String]::IsNullOrEmpty($LeftLogoString) -eq $false) {
                if ([String]::IsNullOrEmpty($RightLogoString) -eq $false) {
                    $LogoSources.Add($RightLogoName, $RightLogoString)
                }
                if ([String]::IsNullOrEmpty($LeftLogoString) -eq $false) {
                    $LogoSources.Add($LeftLogoName, $LeftLogoString)
                }
            } else {
                "$PSScriptRoot\Resources\Images\Other"
            }
        )
    }
    $ImageFiles = Get-ChildItem -Path (Join-Path $LogoPath '\*') -Include *.jpg, *.png, *.bmp -Recurse
    foreach ($ImageFile in $ImageFiles) {
        $ImageBinary = Convert-ImageToBinary -ImageFile $ImageFile
        $LogoSources.Add($ImageFile.BaseName, $ImageBinary)
    }
    $LogoSources
}
function Get-HTMLPartContent {
    <#
    .SYNOPSIS
    Retrieves a part of HTML content between specified start and end markers.
 
    .DESCRIPTION
    This function extracts a portion of HTML content from an array based on the provided start and end markers. The extracted content can be either before, between, or after the specified markers.
 
    .PARAMETER Content
    Specifies the array containing the HTML content.
 
    .PARAMETER Start
    Specifies the starting marker to identify the beginning of the desired content.
 
    .PARAMETER End
    Specifies the ending marker to identify the end of the desired content.
 
    .PARAMETER Type
    Specifies the type of extraction to perform. Options are 'Before', 'Between', or 'After'. Default is 'Between'.
 
    .EXAMPLE
    Get-HTMLPartContent -Content $HTMLArray -Start '<div>' -End '</div>' -Type 'Between'
    Retrieves the HTML content between the '<div>' and '</div>' markers from the provided array.
 
    .EXAMPLE
    Get-HTMLPartContent -Content $HTMLArray -Start '<head>' -End '</head>' -Type 'After'
    Retrieves the HTML content after the '</head>' marker from the provided array.
 
    .EXAMPLE
    Get-HTMLPartContent -Content $HTMLArray -Start '<body>' -End '</body>' -Type 'Before'
    Retrieves the HTML content before the '<body>' marker from the provided array.
    #>

    param(
        [Array] $Content,
        [string] $Start,
        [string] $End,
        [ValidateSet('Before', 'Between', 'After')] $Type = 'Between'
    )
    $NrStart = $Content.IndexOf($Start)
    $NrEnd = $Content.IndexOf($End)   

    if ($Type -eq 'Between') {
        if ($NrStart -eq -1) {

            return
        }
        $Content[$NrStart..$NrEnd]
    } 
    if ($Type -eq 'After') {
        if ($NrStart -eq -1) {

            return $Content
        }
        $Content[($NrEnd + 1)..($Content.Count - 1)]

    }
    if ($Type -eq 'Before') {
        if ($NrStart -eq -1) {

            return
        }
        $Content[0..$NrStart]
    }
}
function Get-ImageFile {
    <#
    .SYNOPSIS
    Downloads an image file from a specified URI.
 
    .DESCRIPTION
    This function downloads an image file from a specified URI. It supports caching of downloaded images.
 
    .PARAMETER Image
    Specifies the URI of the image to download.
 
    .PARAMETER Cache
    Indicates whether to cache the downloaded image.
 
    .EXAMPLE
    Get-ImageFile -Image "https://example.com/image.jpg" -Cache
    Downloads the image file from the specified URI and caches it.
 
    .EXAMPLE
    Get-ImageFile -Image "https://example.com/image.png"
    Downloads the image file from the specified URI without caching.
 
    #>

    [CmdletBinding()]
    param(
        [uri] $Image,
        [switch] $Cache
    )
    if (-not $Image.IsFile) {
        if ($Cache -and -not $Script:CacheImagesHTML) {
            $Script:CacheImagesHTML = @{}
        }
        $Extension = ($Image.OriginalString).Substring(($Image.OriginalString).Length - 4)
        if ($Extension -notin @('.png', '.jpg', 'jpeg', '.svg')) {
            return
        }
        $Extension = $Extension.Replace('.', '')
        $ImageFile = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).$Extension")
        if ($Cache -and $Script:CacheImagesHTML[$Image]) {
            $Script:CacheImagesHTML[$Image]
        } else {
            try {
                Invoke-WebRequest -Uri $Image -OutFile $ImageFile -ErrorAction Stop
                if ($Cache) {
                    $Script:CacheImagesHTML[$Image] = $ImageFile
                }
            } catch {
                Write-Warning "Get-Image - Couldn't download image. Error: $($_.Exception.Message)"
            }
            $ImageFile
        }
    } else {
        $Image.LocalPath
    }
}
function Get-Resources {
    <#
    .SYNOPSIS
    Retrieves and adds resources (stylesheets and scripts) to the HTML page based on the specified features and location.
 
    .DESCRIPTION
    The Get-Resources function retrieves resources such as CSS and JavaScript files and adds them to the HTML page. It allows customization based on the specified features and location. Online and offline modes are supported.
 
    .PARAMETER Online
    Indicates whether the resources should be added for online use. If specified, resources will be linked directly. Default is offline mode.
 
    .PARAMETER NoScript
    Indicates whether resources for scripts should be excluded. If specified, script-related resources will not be added.
 
    .PARAMETER Location
    Specifies the location where the resources should be added. Valid values are 'Header', 'Footer', 'HeaderAlways', 'FooterAlways', 'Body', 'BodyAlways'.
 
    .PARAMETER Features
    Specifies an array of features for which resources should be retrieved and added.
 
    .PARAMETER AddComment
    Indicates whether comments should be added to the resources. Default is false.
 
    .EXAMPLE
    Get-Resources -Online -Location 'Header' -Features 'Feature1', 'Feature2' -AddComment
    Retrieves resources for 'Feature1' and 'Feature2' in online mode, adds them to the header section of the HTML page, and includes comments.
 
    .EXAMPLE
    Get-Resources -Location 'Footer' -Features 'Feature3' -NoScript
    Retrieves resources for 'Feature3' in offline mode, adds them to the footer section of the HTML page, and excludes script-related resources.
 
    #>

    [CmdLetBinding()]
    param(
        [switch] $Online,
        [switch] $NoScript,
        [ValidateSet('Header', 'Footer', 'HeaderAlways', 'FooterAlways', 'Body', 'BodyAlways')][string] $Location,
        [string[]] $Features,
        [switch] $AddComment
    )
    Process {
        foreach ($Feature in $Features) {
            Write-Verbose "Get-Resources - Location: $Location - Feature: $Feature Online: $Online AddComment: $($AddComment.IsPresent)"
            if ($Online) {
                Add-HTMLStyle -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -Link $Script:CurrentConfiguration.Features.$Feature.$Location.'CssLink' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -AddComment:$AddComment
            } else {
                $CSSOutput = Add-HTMLStyle -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -FilePath $Script:CurrentConfiguration.Features.$Feature.$Location.'Css' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -Replace $Script:CurrentConfiguration.Features.$Feature.CustomActionsReplace -AddComment:$AddComment
                $CSSOutput

                $CSSOutput = Add-HTMLStyle -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -CssInline $Script:CurrentConfiguration.Features.$Feature.$Location.'CssInline' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -Replace $Script:CurrentConfiguration.Features.$Feature.CustomActionsReplace -AddComment:$AddComment
                $CSSOutput
            }
            if ($Online) {
                $Data = Add-HTMLScript -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -Link $Script:CurrentConfiguration.Features.$Feature.$Location.'JsLink' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -AddComment:$AddComment
                if ($Data) {
                    $Data
                }
            } else {
                $Data = Add-HTMLScript -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -FilePath $Script:CurrentConfiguration.Features.$Feature.$Location.'Js' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -ReplaceData $Script:CurrentConfiguration.Features.$Feature.CustomActionsReplace -AddComment:$AddComment
                if ($Data) {
                    $Data
                }
                $Data = Add-HTMLScript -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -Content $Script:CurrentConfiguration.Features.$Feature.$Location.'JsInLine' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -AddComment:$AddComment
                if ($Data) {
                    $Data
                }
            }

            if ($NoScript) {
                [Array] $Output = @(
                    if ($Online) {
                        Add-HTMLStyle -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -Link $Script:CurrentConfiguration.Features.$Feature.$Location.'CssLinkNoScript' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -AddComment:$AddComment
                    } else {
                        $CSSOutput = Add-HTMLStyle -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -FilePath $Script:CurrentConfiguration.Features.$Feature.$Location.'CssNoScript' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -ReplaceData $Script:CurrentConfiguration.Features.$Feature.CustomActionsReplace -AddComment:$AddComment
                        if ($CSSOutput) {
                            $CSSOutput
                        }

                        $CSSOutput = Add-HTMLStyle -Placement Inline -Resource $Script:CurrentConfiguration.Features.$Feature -CssInline $Script:CurrentConfiguration.Features.$Feature.$Location.'CssInlineNoScript' -ResourceComment $Script:CurrentConfiguration.Features.$Feature.Comment -Replace $Script:CurrentConfiguration.Features.$Feature.CustomActionsReplace -AddComment:$AddComment
                        if ($CSSOutput) {
                            $CSSOutput
                        }
                    }
                )
                if (($Output.Count -gt 0) -and ($null -ne $Output[0])) {
                    New-HTMLTag -Tag 'noscript' {
                        $Output
                    }
                }
            }
        }
    }
}

$Script:ScriptBlockConfiguration = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $Script:CurrentConfiguration.Features.Keys | Where-Object { $_ -like "*$wordToComplete*" }
}
Register-ArgumentCompleter -CommandName Get-Resources -ParameterName Color -ScriptBlock $Script:ScriptBlockConfiguration
function New-DefaultSettings {
    <#
    .SYNOPSIS
    Creates a new default settings object with predefined values for various properties.
 
    .DESCRIPTION
    This function creates a new default settings object with predefined values for properties related to email, features, charts, carousel, diagrams, logos, tabs, table options, tab panels, custom CSS and JS headers and footers, and wizard list.
 
    .EXAMPLE
    $defaultSettings = New-DefaultSettings
    Creates a new default settings object with all properties initialized to their default values.
 
    #>

    [cmdletBinding()]
    param()
    [ordered]@{
        Email             = $false
        Features          = [ordered] @{ } 
        Charts            = [System.Collections.Generic.List[string]]::new()
        Carousel          = [System.Collections.Generic.List[string]]::new()
        Diagrams          = [System.Collections.Generic.List[string]]::new()
        Logos             = ""

        TabsHeaders       = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() 
        TabsHeadersNested = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() 
        TableOptions      = [ordered] @{
            DataStore        = ''

            DataStoreOptions = [ordered] @{
                BoolAsString          = $false
                NumberAsString        = $false
                DateTimeFormat        = '' 
                NewLineFormat         = @{
                    NewLineCarriage = '<br>'
                    NewLine         = "\n"
                    Carriage        = "\r"
                }
                NewLineFormatProperty = @{
                    NewLineCarriage = '<br>'
                    NewLine         = "\n"
                    Carriage        = "\r"
                }
            }
            Type             = 'Structured'
            Folder           = if ($FilePath) {
                Split-Path -Path $FilePath } else {
                '' }
        }

        TabPanelsList     = [System.Collections.Generic.List[string]]::new()
        Table             = [ordered] @{}
        TableSimplify     = $false 
        CustomHeaderCSS   = [ordered] @{}
        CustomFooterCSS   = [ordered] @{}
        CustomHeaderJS    = [ordered] @{}
        CustomFooterJS    = [ordered] @{}

        WizardList        = [System.Collections.Generic.List[string]]::new()
    }
}
function New-DiagramInternalEvent {
    <#
    .SYNOPSIS
    Creates a new internal event for a diagram.
 
    .DESCRIPTION
    This function creates a new internal event for a diagram based on the provided parameters.
 
    .PARAMETER OnClick
    Specifies if the event is triggered on click.
 
    .PARAMETER ID
    Specifies the ID of the diagram element.
 
    .PARAMETER FadeSearch
    Specifies whether to fade the search results.
 
    .PARAMETER ColumnID
    Specifies the ID of the column to search within.
 
    .EXAMPLE
    New-DiagramInternalEvent -OnClick -ID "diagramElement1" -FadeSearch -ColumnID 2
    Creates a new internal event triggered on click for the diagram element with ID "diagramElement1", fading the search results within column 2.
 
    .EXAMPLE
    New-DiagramInternalEvent -ID "diagramElement2" -ColumnID 1
    Creates a new internal event for the diagram element with ID "diagramElement2", searching within column 1.
 
    #>

    [CmdletBinding()]
    param(
        #[switch] $OnClick,
        [string] $ID,
        #[switch] $FadeSearch,
        [nullable[int]] $ColumnID
    )

    $FadeSearch = $false
    if ($FadeSearch) {
        $EventVar = @"
        var table = `$('#$ID').DataTable();
        //table.search(params.nodes).draw();
        table.rows(':visible').every(function (rowIdx, tableLoop, rowLoop) {
            var present = true;
            if (params.nodes) {
                present = table.row(rowIdx).data().some(function (v) {
                        return v.match(new RegExp(params.nodes, 'i')) != null;
                    });
            }
            `$(table.row(rowIdx).node()).toggleClass('notMatched', !present);
        });
 
"@


    } else {
        if ($null -ne $ColumnID) {
            $EventVar = @"
        var table = `$('#$ID').DataTable();
        if (findValue != '') {
            table.columns($ColumnID).search("^" + findValue + "$", true, false, true).draw();
        } else {
            table.columns($ColumnID).search('').draw();
        }
        if (table.page.info().recordsDisplay == 0) {
            table.columns($ColumnID).search('').draw();
        }
"@

        } else {
            $EventVar = @"
        var table = `$('#$ID').DataTable();
        if (findValue != '') {
            table.search("^" + findValue + "$", true, false, true).draw();
        } else {
            table.search('').draw();
        }
        if (table.page.info().recordsDisplay == 0) {
            table.search('').draw();
        }
"@

        }
    }
    $EventVar
}
function New-HTMLCustomCSS {
    <#
    .SYNOPSIS
    Creates custom CSS styles based on the provided CSS key-value pairs.
 
    .DESCRIPTION
    This function generates custom CSS styles based on the CSS key-value pairs provided in the input dictionary. It allows for the inclusion of comments before and after each CSS block.
 
    .PARAMETER CSS
    Specifies a dictionary containing CSS key-value pairs where the key represents the CSS selector and the value represents the CSS properties.
 
    .PARAMETER AddComment
    Indicates whether comments should be added before and after each CSS block. Default is false.
 
    .EXAMPLE
    New-HTMLCustomCSS -CSS @{ '.header' = 'background-color: #333; color: white;' '.content' = 'font-size: 16px; line-height: 1.5;' } -AddComment
    Generates custom CSS styles for the '.header' and '.content' selectors with specified properties and includes comments before and after each CSS block.
 
    .EXAMPLE
    $customCSS = @{ '.button' = 'background-color: blue; color: white;' '.link' = 'text-decoration: none;' }
    New-HTMLCustomCSS -CSS $customCSS
    Creates custom CSS styles for the '.button' and '.link' selectors with specified properties without adding comments.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $CSS,
        [switch] $AddComment
    )

    $Output = foreach ($Key in $CSS.Keys) {
        if ($AddComment) {
            "<!-- CSS $Key AUTOGENERATED on DEMAND START -->" }
        if ($CSS[$Key]) {
            $CSS[$Key]
        }
        if ($AddComment) {
            "<!-- CSS $Key AUTOGENERATED on DEMAND END -->" }
    }
    if ($Output) {

        $Output

    }
}
function New-HTMLCustomJS {
    <#
    .SYNOPSIS
    Creates custom JavaScript blocks based on the provided JavaScript key-value pairs.
 
    .DESCRIPTION
    This function generates custom JavaScript blocks based on the JavaScript key-value pairs provided in the input dictionary. It allows for the inclusion of comments before and after each JavaScript block.
 
    .PARAMETER JS
    Specifies a dictionary containing JavaScript key-value pairs where the key represents the JavaScript block name and the value represents the JavaScript code.
 
    .EXAMPLE
    $customJS = @{
        'script1' = 'console.log("Hello, World!");'
        'script2' = 'alert("Welcome!");'
    }
    New-HTMLCustomJS -JS $customJS
    Creates custom JavaScript blocks named 'script1' and 'script2' with specified JavaScript code without adding comments.
 
    .EXAMPLE
    New-HTMLCustomJS -JS @{ 'analytics' = 'trackUserActivity();' }
    Generates a custom JavaScript block named 'analytics' with the specified JavaScript code and includes comments before and after the block.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $JS
    )
    foreach ($Key in $JS.Keys) {
        "<!-- JS $Key AUTOGENERATED on DEMAND START -->"
        if ($JS[$Key]) {
            if ($JS[$Key] -notlike "*<script*") {
                New-HTMLTag -Tag 'script' {
                    $JS[$Key]
                } -NewLine
            } else {
                $JS[$Key]
            }
        }
        "<!-- JS $Key AUTOGENERATED on DEMAND END -->"
    }
}
function New-HTMLTabHead {
    <#
    .SYNOPSIS
    Creates the HTML structure for a set of tabs.
 
    .DESCRIPTION
    This function generates the HTML structure for a set of tabs based on the provided TabsCollection and PageName. It creates a div element with tab headers inside.
 
    .PARAMETER TabsCollection
    An array containing the tabs information.
 
    .PARAMETER PageName
    The name of the page to which the tabs belong.
 
    .EXAMPLE
    New-HTMLTabHead -TabsCollection @($Tab1, $Tab2) -PageName 'HomePage'
    Creates the HTML structure for tabs Tab1 and Tab2 on the HomePage.
 
    #>

    [CmdletBinding()]
    Param (
        [Array] $TabsCollection,
        [string] $PageName
    )
    if ($TabsCollection.Count -gt 0) {
        $Tabs = $TabsCollection
    } else {
        $Tabs = $Script:GlobalSchema['Pages'][$PageName].TabsHeaders
    }
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'tabsWrapper' } {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'tabsSlimmer' } {

            New-HTMLTag -Tag 'div' -Attributes @{ 'data-tabs' = 'true' } {
                foreach ($Tab in $Tabs) {
                    if ($Tab.Active) {
                        $TabActive = 'active'
                    } else {
                        $TabActive = ''
                    }
                    New-HTMLTag -Tag 'div' -Attributes @{ id = $Tab.ID; class = $TabActive; } {
                        if ($Tab.Icon) {
                            New-HTMLTag -Tag 'span' -Attributes @{ class = $($Tab.Icon); style = $($Tab.StyleIcon) }
                            '&nbsp;' 
                        }
                        New-HTMLTag -Tag 'span' -Attributes @{ style = $($Tab.StyleText ) } -Value { $Tab.Name }
                    }
                }
            }
        }
    }

}

function New-InternalDiagram {
    <#
    .SYNOPSIS
    Creates a new internal diagram with specified nodes, edges, events, options, dimensions, and styling.
 
    .DESCRIPTION
    This function creates a new internal diagram with the provided nodes, edges, events, and options. It allows customization of dimensions, background image, filtering options, and more.
 
    .PARAMETER Nodes
    Specifies the collection of nodes for the diagram.
 
    .PARAMETER Edges
    Specifies the collection of edges connecting the nodes.
 
    .PARAMETER Events
    Specifies the events associated with the diagram.
 
    .PARAMETER Options
    Specifies the options for configuring the diagram.
 
    .PARAMETER Height
    Specifies the height of the diagram.
 
    .PARAMETER Width
    Specifies the width of the diagram.
 
    .PARAMETER BackgroundImage
    Specifies the background image for the diagram.
 
    .PARAMETER BackgroundSize
    Specifies the size of the background image. Default is '100% 100%'.
 
    .PARAMETER IconsAvailable
    Indicates if icons are available for use in the diagram.
 
    .PARAMETER DisableLoader
    Indicates whether the loader should be disabled.
 
    .PARAMETER EnableFiltering
    Indicates whether filtering functionality is enabled.
 
    .PARAMETER MinimumFilteringChars
    Specifies the minimum number of characters required for filtering. Default is 3.
 
    .PARAMETER EnableFilteringButton
    Indicates whether a filtering button should be displayed.
 
    .EXAMPLE
    New-InternalDiagram -Nodes @($Node1, $Node2) -Edges @($Edge1) -Events @($Event1) -Options $DiagramOptions -Height 500 -Width 800 -BackgroundImage 'bg.jpg' -EnableFiltering -EnableFilteringButton
    Creates a new internal diagram with specified nodes, edges, events, options, dimensions, and background image with filtering enabled.
 
    .EXAMPLE
    New-InternalDiagram -Nodes @($Node1, $Node2) -Edges @($Edge1) -Options $DiagramOptions -Height 400 -Width 600
    Creates a new internal diagram with specified nodes, edges, and options with custom dimensions.
 
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IList] $Nodes,
        [System.Collections.IList] $Edges,
        [System.Collections.IList] $Events,
        [System.Collections.IDictionary] $Options,
        [object] $Height,
        [object] $Width,
        [string] $BackgroundImage,
        [string] $BackgroundSize = '100% 100%',
        [switch] $IconsAvailable,
        [switch] $DisableLoader,
        [switch] $EnableFiltering,
        [int] $MinimumFilteringChars = 3,
        [switch] $EnableFilteringButton
    )
    $Script:HTMLSchema.Features.VisNetwork = $true
    $Script:HTMLSchema.Features.VisData = $true
    $Script:HTMLSchema.Features.Moment = $true
    $Script:HTMLSchema.Features.VisNetworkLoad = $true
    $Script:HTMLSchema.Features.EscapeRegex = $true

    $UseFilteringButton = $EnableFilteringButton.IsPresent

    if ($Options.physics -and $Options.physics.enabled -eq $false) {
        $DisableLoader = $true
    }
    if (-not $DisableLoader) {
        $Script:HTMLSchema.Features.VisNetworkLoadingBar = $true
    }

    if ($EnableFiltering -or $UseFilteringButton) {
        $Script:HTMLSchema.Features.VisNetworkFind = $true
    }

    [string] $ID = "Diagram-" + (Get-RandomStringName -Size 8)

    $Style = [ordered] @{
        position = 'relative'
        width    = ConvertFrom-Size -Size $Width
        height   = ConvertFrom-Size -Size $Height
    }
    if ($BackgroundImage) {
        $Style['background'] = "url('$BackgroundImage')"
        $Style['background-size'] = $BackgroundSize
    }

    $AttributesOutside = [ordered] @{
        class = 'diagram'
        style = $Style
    }

    $AttributesInside = [ordered] @{
        class = 'diagram diagramObject'
        style = @{
            position = 'absolute'
        }
        id    = "$ID"
    }

    if (-not $DisableLoader) {
        $Div = New-HTMLTag -Tag 'div' -Attributes @{ class = 'diagramWrapper' } -Value {
            New-HTMLTag -Tag 'div' -Attributes $AttributesOutside -Value {
                if ($EnableFiltering -or $UseFilteringButton) {
                    New-HTMLTag -Tag 'div' -Attributes @{ class = 'searchDiagram' } -Value {
                        New-HTMLTag -Tag 'input' -Attributes @{ type = 'search'; class = 'searchInput'; id = "searchInput$ID"; placeholder = 'Filter name...' }
                        if ($UseFilteringButton) {
                            New-HTMLTag -Tag 'button' -Attributes @{ id = "searchButton$ID"; class = 'searchButton'; type = 'button' } -Value { 'Filter' }
                        }
                    }
                }
                New-HTMLTag -Tag 'div' -Attributes $AttributesInside
            }
            New-HTMLTag -Tag 'div' -Attributes @{ id = "$ID-diagramLoadingBar"; class = 'diagramLoadingBar' } {
                New-HTMLTag -Tag 'div' -Attributes @{ class = "diagramOuterBorder" } {
                    New-HTMLTag -Tag 'div' -Attributes @{ id = "$ID-diagramText"; class = 'diagramText' } -Value { '0%' }
                    New-HTMLTag -Tag 'div' -Attributes @{ class = 'diagramBorder' } {
                        New-HTMLTag -Tag 'div' -Attributes @{ id = "$ID-diagramBar"; class = 'diagramBar' }
                    }
                }
            }
        }

    } else {
        $Div = New-HTMLTag -Tag 'div' -Attributes $AttributesOutside {
            if ($EnableFiltering -or $UseFilteringButton) {
                New-HTMLTag -Tag 'div' -Attributes @{ class = 'searchDiagram' } -Value {
                    New-HTMLTag -Tag 'input' -Attributes @{ type = 'search'; class = 'searchInput'; id = "searchInput$ID"; placeholder = 'Filter name...' }
                    if ($UseFilteringButton) {
                        New-HTMLTag -Tag 'button' -Attributes @{ id = "searchButton$ID"; class = 'searchButton'; type = 'button' } -Value { 'Filter' }
                    }
                }
            }
            New-HTMLTag -Tag 'div' -Attributes $AttributesInside
        }
    }
    $ConvertedNodes = $Nodes -join ', '
    $ConvertedEdges = $Edges -join ', '

    if ($Events.Count -gt 0) {
        [Array] $PreparedEvents = @(

            'network.on("click", function (params) {'
            'params.event = "[original event]";'
            'var findValue = escapeRegExp(params.nodes);'
            foreach ($E in $Events) {
                New-DiagramInternalEvent -ID $E.ID -ColumnID $E.ColumnID
            }
            '});'
        )
    }

    $Script = New-HTMLTag -Tag 'script' -Value {

        '// create an array with nodes'
        "var nodes = new vis.DataSet([$ConvertedNodes]); "

        '// create an array with edges'
        "var edges = new vis.DataSet([$ConvertedEdges]); "

        '// create a network'
        "var container = document.getElementById('$ID'); "
        "var data = { "
        " nodes: nodes, "
        " edges: edges"
        " }; "

        if ($Options) {
            $ConvertedOptions = $Options | ConvertTo-Json -Depth 5 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) }
            "var options = $ConvertedOptions; "
        } else {
            "var options = { }; "
        }
        $DisableLoaderString = (-not $DisableLoader).ToString().ToLower()
        $IconsAvailableString = $IconsAvailable.IsPresent.ToString().ToLower()
        "var network = loadDiagramWithFonts(container, data, options, '$ID', $DisableLoaderString , $IconsAvailableString);"
        "diagramTracker['$ID'] = network;"
        "$PreparedEvents"

        if ($EnableFiltering -or $UseFilteringButton) {
            $SearchByTyping = if ($UseFilteringButton) {
                "false" } else {
                "true" }
            "setupSearch(nodes, edges, 'searchInput$ID', 'searchButton$ID', $SearchByTyping, $MinimumFilteringChars); // Enables typing search only"
        }

    } -NewLine

    $Div
    $Script:HTMLSchema.Diagrams.Add($Script)
}
function New-InternalNavIcon {
    <#
    .SYNOPSIS
    Creates a new internal navigation icon with customizable properties.
 
    .DESCRIPTION
    This function creates a new internal navigation icon with the specified properties. It allows customization of the icon color, style, rotation, flipping, spinning, and positioning.
 
    .PARAMETER IconColor
    Specifies the color of the icon.
 
    .PARAMETER IconBrands
    Specifies the brand icon style.
 
    .PARAMETER IconRegular
    Specifies the regular icon style.
 
    .PARAMETER IconSolid
    Specifies the solid icon style.
 
    .PARAMETER IconMaterial
    Specifies the material icon style.
 
    .PARAMETER Spinning
    Indicates whether the icon should spin.
 
    .PARAMETER SpinningReverse
    Indicates whether the spinning direction should be reversed.
 
    .PARAMETER Bordered
    Indicates whether the icon should have a border.
 
    .PARAMETER BorderedCircle
    Indicates whether the icon should have a circular border.
 
    .PARAMETER PullLeft
    Indicates whether the icon should be pulled to the left.
 
    .PARAMETER PullRight
    Indicates whether the icon should be pulled to the right.
 
    .PARAMETER Rotate
    Specifies the rotation angle of the icon.
 
    .PARAMETER FlipVertical
    Indicates whether the icon should be flipped vertically.
 
    .PARAMETER FlipHorizontal
    Indicates whether the icon should be flipped horizontally.
 
    .PARAMETER ClassIcon
    Specifies the CSS class for the icon. Default value is 'icon'.
 
    .EXAMPLE
    New-InternalNavIcon -IconColor 'blue' -IconSolid 'check-circle' -Rotate '90'
 
    Creates a new internal navigation icon with a blue color, solid style 'check-circle', and rotated 90 degrees.
 
    .EXAMPLE
    New-InternalNavIcon -IconBrands 'facebook' -Spinning -FlipHorizontal
 
    Creates a new internal navigation icon with the Facebook brand icon, spinning animation, and horizontal flipping.
 
    #>

    [cmdletBinding()]
    param(
        [string] $IconColor,
        [string] $IconBrands,
        [string] $IconRegular,
        [string] $IconSolid,
        [string] $IconMaterial,
        [switch] $Spinning,
        [switch] $SpinningReverse,
        [switch] $Bordered,
        [switch] $BorderedCircle,
        [switch] $PullLeft,
        [switch] $PullRight,
        [string] $Rotate,
        [switch] $FlipVertical,
        [switch] $FlipHorizontal,
        [string] $ClassIcon = 'icon'
    )
    if ($IconRegular -or $IconBrands -or $IconSolid -or $IconMaterial) {
        New-HTMLTag -Tag 'span' -Attributes @{ class = $ClassIcon } {
            $newHTMLFontIconSplat = @{
                IconColor    = $IconColor
                IconBrands   = $IconBrands
                IconRegular  = $IconRegular
                IconSolid    = $IconSolid
                IconMaterial = $IconMaterial
                FixedWidth   = $true
            }
            if ($Spinning) {
                $newHTMLFontIconSplat['Spinning'] = $Spinning
            }
            if ($SpinningReverse) {
                $newHTMLFontIconSplat['SpinningReverse'] = $SpinningReverse
            }
            if ($Bordered) {
                $newHTMLFontIconSplat['Bordered'] = $Bordered
            }
            if ($BorderedCircle) {
                $newHTMLFontIconSplat['BorderedCircle'] = $BorderedCircle
            }
            if ($PullLeft) {
                $newHTMLFontIconSplat['PullLeft'] = $PullLeft
            }
            if ($PullRight) {
                $newHTMLFontIconSplat['PullRight'] = $PullRight
            }
            if ($Rotate) {
                $newHTMLFontIconSplat['Rotate'] = $Rotate
            }
            if ($FlipVertical) {
                $newHTMLFontIconSplat['FlipVertical'] = $FlipVertical
            }
            if ($FlipHorizontal) {
                $newHTMLFontIconSplat['FlipHorizontal'] = $FlipHorizontal
            }
            Remove-EmptyValue -Hashtable $newHTMLFontIconSplat -Recursive
            New-HTMLFontIcon @newHTMLFontIconSplat
        }
    }
}
function New-InternalNavLink {
    <#
    .SYNOPSIS
    Creates a new internal navigation link with customizable properties.
 
    .DESCRIPTION
    This function creates a new internal navigation link with the specified properties. It allows customization of the link name, color, href, icon color, icon style, spinning, border, positioning, rotation, flipping, and more.
 
    .PARAMETER Name
    Specifies the name of the navigation link.
 
    .PARAMETER NameColor
    Specifies the color of the navigation link name.
 
    .PARAMETER Href
    Specifies the URL to navigate to when the link is clicked.
 
    .PARAMETER IconColor
    Specifies the color of the icon.
 
    .PARAMETER IconBrands
    Specifies the brand icon style.
 
    .PARAMETER IconRegular
    Specifies the regular icon style.
 
    .PARAMETER IconSolid
    Specifies the solid icon style.
 
    .PARAMETER IconMaterial
    Specifies the material icon style.
 
    .PARAMETER Spinning
    Indicates whether the icon should spin.
 
    .PARAMETER SpinningReverse
    Indicates whether the spinning direction should be reversed.
 
    .PARAMETER Bordered
    Indicates whether the icon should have a border.
 
    .PARAMETER BorderedCircle
    Indicates whether the icon should have a circular border.
 
    .PARAMETER PullLeft
    Indicates whether the icon should be pulled to the left.
 
    .PARAMETER PullRight
    Indicates whether the icon should be pulled to the right.
 
    .PARAMETER Rotate
    Specifies the rotation angle of the icon.
 
    .PARAMETER FlipVertical
    Indicates whether the icon should be flipped vertically.
 
    .PARAMETER FlipHorizontal
    Indicates whether the icon should be flipped horizontally.
 
    .PARAMETER Nested
    Indicates whether the link is nested within another element.
 
    .PARAMETER MenuItems
    Indicates whether the link is part of a menu.
 
    .PARAMETER FloatItem
    Indicates whether the link should float.
 
    .PARAMETER ListItem1
    Indicates whether the link is a list item with specific styling.
 
    .PARAMETER ListItem
    Indicates whether the link is a list item.
 
    .EXAMPLE
    New-InternalNavLink -Name 'Home' -NameColor 'blue' -Href '/home' -IconBrands 'fab fa-home' -Spinning -IconColor 'red' -Bordered -PullLeft -Rotate '90' -FlipVertical
    Creates a new internal navigation link with the name 'Home', blue name color, link to '/home', spinning home icon in red color with border, pulled to the left, rotated 90 degrees, and flipped vertically.
 
    .EXAMPLE
    New-InternalNavLink -Name 'About' -Href '/about' -IconRegular 'far fa-address-card' -IconColor 'green' -BorderedCircle -PullRight -FlipHorizontal
    Creates a new internal navigation link with the name 'About', link to '/about', regular address card icon in green color with circular border, pulled to the right, and flipped horizontally.
    #>

    [cmdletBinding()]
    param(
        [string] $Name,
        [string] $NameColor,
        [string] $Href,
        [string] $IconColor,
        [string] $IconBrands,
        [string] $IconRegular,
        [string] $IconSolid,
        [string] $IconMaterial,
        [switch] $Spinning,
        [switch] $SpinningReverse,
        [switch] $Bordered,
        [switch] $BorderedCircle,
        [switch] $PullLeft,
        [switch] $PullRight,
        [string] $Rotate,
        [switch] $FlipVertical,
        [switch] $FlipHorizontal,
        [switch] $Nested,
        [switch] $MenuItems,
        [switch] $FloatItem,
        [switch] $ListItem
    )
    if ($MenuItems) {
        if ($Nested) {
            $NavLink = New-HTMLTag -Tag 'span' -Attributes @{ class = 'parent' } {
                New-HTMLTag -Tag 'span' -Attributes @{ style = @{ "padding-right" = "5px" } } {
                    New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                }
                if ($NameColor) {
                    New-HTMLSpanStyle -Color $NameColor {
                        $Name
                    }
                } else {
                    $Name
                }
            }
        } else {
            $NavLink = New-HTMLTag -Tag 'li' {
                New-HTMLTag -Tag 'a' -Attributes @{ href = $Href } {
                    New-HTMLTag -Tag 'span' -Attributes @{ style = @{ "padding-right" = "5px" } } {
                        New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                    }
                    if ($NameColor) {
                        New-HTMLSpanStyle -Color $NameColor {
                            $Name
                        }
                    } else {
                        $Name
                    }
                }
            }
        }
        $NavLink
    } elseif ($FloatItem) {
        $NavLink = New-HTMLTag -Tag 'a' -Attributes @{ href = $Href } {
            New-HTMLTag -Tag 'span' -Attributes @{ style = @{ "padding-right" = "5px" } } {
                New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
            }
            if ($NameColor) {
                New-HTMLSpanStyle -Color $NameColor {
                    $Name
                }
            } else {
                $Name
            }
        }
        $NavLink
    } elseif ($ListItem1) {
        $NavLink = New-HTMLTag -Tag 'a' -Attributes @{ href = $Href } {
            New-HTMLTag -Tag 'span' -Attributes @{ style = @{ "padding-right" = "5px"; 'margin-left' = '-10px' } } {
                New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
            }
            if ($NameColor) {
                New-HTMLSpanStyle -Color $NameColor {
                    $Name
                }
            } else {
                $Name
            }
        }
        $NavLink
    } elseif ($ListItem) {
        $NavLink = New-HTMLTag -Tag 'li' {
            New-HTMLTag -Tag 'a' -Attributes @{ href = $Href } {

                New-InternalNavIcon -ClassIcon 'side-penal-list-icon' -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent

                if ($NameColor) {
                    New-HTMLSpanStyle -Color $NameColor {
                        $Name
                    }
                } else {
                    $Name
                }
            }
        }
        $NavLink
    } else {
        if ($Nested) {
            $NavLink = New-HTMLTag -Tag 'span' -Attributes @{ class = 'its-parent' } {
                New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                if ($NameColor) {
                    New-HTMLSpanStyle -Color $NameColor {
                        $Name
                    }
                } else {
                    $Name
                }
            }
        } else {
            $NavLink = New-HTMLTag -Tag 'li' {
                New-HTMLTag -Tag 'a' -Attributes @{ href = $Href } {
                    New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                    if ($NameColor) {
                        New-HTMLSpanStyle -Color $NameColor {
                            $Name
                        }
                    } else {
                        $Name
                    }
                }
            }
        }
        $NavLink
    }
}
function New-RequestCssConfiguration {
    <#
    .SYNOPSIS
    Creates a new CSS configuration based on existing CSS code.
 
    .DESCRIPTION
    This function generates new named class names for existing CSS code based on the provided Pair dictionary. It then copies the original CSS configuration, removes unwanted sections, and renames existing CSS classes to their new names.
 
    .PARAMETER Pair
    Specifies a dictionary containing key-value pairs for mapping existing CSS classes to new names.
 
    .PARAMETER CssConfiguration
    Specifies the original CSS configuration to be modified.
 
    .PARAMETER Feature
    Specifies the feature associated with the CSS configuration.
 
    .PARAMETER Type
    Specifies the type of CSS configuration.
 
    .EXAMPLE
    $Pair = @{
        "Header" = "header-class";
        "Footer" = "footer-class"
    }
    $CssConfiguration = @{
        "header-class" = ".header";
        "footer-class" = ".footer";
    }
    $Feature = "Website"
    $Type = "Main"
    New-RequestCssConfiguration -Pair $Pair -CssConfiguration $CssConfiguration -Feature $Feature -Type $Type
    # Generates new CSS configuration based on the provided Pair dictionary.
 
    #>

    [cmdletBinding()]
    param(
        [System.Collections.IDictionary] $Pair,
        [System.Collections.IDictionary] $CssConfiguration,
        [string] $Feature,
        [string] $Type
    )

    $Name = $(Get-RandomStringName -Size 7)
    $ExpectedStyleSheetsConfiguration = [ordered] @{}
    foreach ($Key in $Pair.Keys) {
        $ExpectedStyleSheetsConfiguration[$Key] = ".$Key-$Name"
    }
    $RenamePair = [ordered] @{}
    foreach ($Key in $Pair.Keys) {
        $ClassName = $Pair[$Key]
        $RenamePair[$ClassName] = $ExpectedStyleSheetsConfiguration[$Key]
    }

    $CssConfiguration = Copy-Dictionary -Dictionary $CssConfiguration

    Remove-ConfigurationCSS -CSS $CssConfiguration -Not -Section $Pair.Values

    Rename-Dictionary -HashTable $CssConfiguration -Pair $RenamePair

    $CssOtherConfiguration = Get-ConfigurationCss -Feature $Feature -Type $Type

    Set-ConfigurationCSS -CSS ($CssOtherConfiguration + $CssConfiguration) -Feature $Feature -Type $Type

    $Script:HTMLSchema.Features.$Feature = $true

    @{
        StyleSheetConfiguration = $ExpectedStyleSheetsConfiguration
        CssConfiguration        = $CssConfiguration
    }
}
function New-TableJavaScript {
    <#
    .SYNOPSIS
    Creates a new JavaScript table with customizable options.
 
    .DESCRIPTION
    This function generates a new JavaScript table with the specified header names and options. It allows customization of the data marker, column data formatting, and deferred rendering.
 
    .PARAMETER HeaderNames
    Specifies an array of header names for the table.
 
    .PARAMETER Options
    Specifies a dictionary of options for configuring the table.
 
    .PARAMETER NewLineFormat
    Specifies a dictionary containing newline formatting characters.
 
    .EXAMPLE
    New-TableJavaScript -HeaderNames @('Name', 'Age', 'Location') -Options @{ 'paging' = $true; 'searching' = $true } -NewLineFormat @{ 'NewLineCarriage' = '\r\n'; 'NewLine' = '\n'; 'Carriage' = '\r' }
    Creates a new JavaScript table with headers 'Name', 'Age', 'Location', paging enabled, searching enabled, and custom newline formatting.
 
    #>

    [cmdletBinding()]
    param(
        [string[]] $HeaderNames,
        [System.Collections.IDictionary] $Options,
        [System.Collections.IDictionary] $NewLineFormat
    )
    $Options['data'] = "markerForDataReplacement"
    [Array] $Options['columns'] = foreach ($Property in $HeaderNames) {
        @{ data = $Property.Replace('.', '\.').Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage) }
    }
    $Options['deferRender'] = $true
}
function New-TablePercentageBarInternal {
    <#
    .SYNOPSIS
    Creates a new internal percentage bar for data visualization in a table column.
 
    .DESCRIPTION
    This function creates a new internal percentage bar for data visualization in a table column. It allows customization of the bar type, text color, border color, border style, bar color, background color, rounding value, and conditional formatting options.
 
    .PARAMETER ColumnID
    Specifies the ID of the column where the percentage bar will be displayed.
 
    .PARAMETER ColumnName
    Specifies the name of the column for reference.
 
    .PARAMETER Type
    Specifies the type of the percentage bar shape. Valid values are 'square' or 'round'.
 
    .PARAMETER TextColor
    Specifies the color of the text displayed on the percentage bar.
 
    .PARAMETER BorderColor
    Specifies the color of the border of the percentage bar.
 
    .PARAMETER BorderStyle
    Specifies the style of the border of the percentage bar. Valid values are 'solid', 'outset', 'groove', or 'ridge'.
 
    .PARAMETER BarColor
    Specifies the color of the percentage bar.
 
    .PARAMETER BackgroundColor
    Specifies the background color behind the percentage bar.
 
    .PARAMETER RoundValue
    Specifies the rounding value for the percentage bar.
 
    .PARAMETER ConditionalFormatting
    Specifies an array of conditional formatting rules for the percentage bar.
 
    .EXAMPLE
    New-TablePercentageBarInternal -ColumnID 1 -ColumnName 'Sales' -Type 'round' -TextColor 'white' -BorderColor 'black' -BorderStyle 'solid' -BarColor 'green' -BackgroundColor 'lightgray' -RoundValue 2 -ConditionalFormatting @($Condition1, $Condition2)
    Creates a new internal percentage bar in the 'Sales' column with a round shape, white text color, black border color, solid border style, green bar color, light gray background color, rounding to 2 decimal places, and conditional formatting rules.
 
    .EXAMPLE
    New-TablePercentageBarInternal -ColumnID 2 -ColumnName 'Profit' -Type 'square' -TextColor 'black' -BorderColor 'gray' -BorderStyle 'groove' -BarColor 'blue' -BackgroundColor 'white' -RoundValue 0 -ConditionalFormatting @($Condition3)
    Creates a new internal percentage bar in the 'Profit' column with a square shape, black text color, gray border color, groove border style, blue bar color, white background color, no rounding, and a single conditional formatting rule.
    #>

    [cmdletbinding()]
    param(
        [int] $ColumnID,
        [string] $ColumnName,
        [ValidateSet('square', 'round')][string] $Type,
        [string] $TextColor,
        [string] $BorderColor,
        [ValidateSet('solid', 'outset', 'groove', 'ridge')][string] $BorderStyle,
        [string] $BarColor,
        [string] $BackgroundColor,
        [int] $RoundValue,
        [Array] $ConditionalFormatting
    )

    if ($ConditionalFormatting) {
        $JsonConditions = $ConditionalFormatting | ConvertTo-Json
        [ordered]@{
            targets = $ColumnID
            render  = "`$.fn.dataTable.render.percentBar('$Type','$TextColor', '$BorderColor', '$BarColor', '$BackgroundColor', $RoundValue, '$BorderStyle', $JsonConditions)"
        }
    } else {
        [ordered]@{
            targets = $ColumnID
            render  = "`$.fn.dataTable.render.percentBar('$Type','$TextColor', '$BorderColor', '$BarColor', '$BackgroundColor', $RoundValue, '$BorderStyle')"
        }
    }
}
function New-TableServerSide {
    <#
    .SYNOPSIS
    Creates a new server-side table with customizable options.
 
    .DESCRIPTION
    This function generates a new server-side table with the specified data, table ID, header names, and options. It allows customization of the data source, data storage, and column definitions.
 
    .PARAMETER DataTable
    Specifies the data to be displayed in the table.
 
    .PARAMETER DataTableID
    Specifies the unique identifier for the table data.
 
    .PARAMETER HeaderNames
    Specifies an array of header names for the table columns.
 
    .PARAMETER Options
    Specifies a dictionary of options for configuring the server-side table.
 
    .EXAMPLE
    New-TableServerSide -DataTable @($Row1, $Row2, $Row3) -DataTableID "Table1" -HeaderNames @('Name', 'Age', 'Location') -Options @{ 'paging' = $true; 'searching' = $true }
    Creates a new server-side table with data rows, unique ID 'Table1', headers 'Name', 'Age', 'Location', paging enabled, and searching enabled.
 
    #>

    [cmdletBinding()]
    param(
        [Array] $DataTable,
        [string] $DataTableID,
        [string[]] $HeaderNames,
        [System.Collections.IDictionary] $Options
    )
    if ($Script:HTMLSchema['TableOptions']['Type'] -eq 'structured') {
        $DataPath = [io.path]::Combine($Script:HTMLSchema['TableOptions']['Folder'], 'data')
        $FilePath = [io.path]::Combine($DataPath, "$DataTableID.json")
        $null = New-Item -Path $DataPath -ItemType Directory -Force
        $Data = @{
            data = $DataTable
        }

        $Data | ConvertTo-JsonLiteral -Depth 1 `
            -NumberAsString:$Script:HTMLSchema['TableOptions']['DataStoreOptions'].NumberAsString `
            -BoolAsString:$Script:HTMLSchema['TableOptions']['DataStoreOptions'].BoolAsString `
            -DateTimeFormat $Script:HTMLSchema['TableOptions']['DataStoreOptions'].DateTimeFormat | Out-File -FilePath $FilePath
        $Options['ajax'] = -join ('data', '\', "$DataTableID.json")
    } else {

    }
    [Array] $Options['columns'] = foreach ($Property in $HeaderNames) {

        @{ data = $Property.Replace('.', '\.') }
    }
    $Options['deferRender'] = $true
}
function Get-DefaultParameters {
    <#
    .SYNOPSIS
    Retrieves the default parameters for the script.
 
    .DESCRIPTION
    This function retrieves the default parameters for the script. It initializes the configuration URL and sets up default features related to CSS, fonts, and icons.
 
    .EXAMPLE
    Get-DefaultParameters
    Retrieves the default parameters for the script.
 
    #>

    [CmdletBinding()]
    param(

    )
    $ConfigurationURL = 'https://cdn.jsdelivr.net/gh/evotecit/cdn@0.0.27'
    $Configuration = [ordered] @{
        Features = [ordered] @{
            Inject                      = @{
                HeaderAlways = @{
                    CssInline = [ordered] @{}
                }
                Default      = $true
                Email        = $false
            }
            Fonts                       = @{
                Comment      = 'Default fonts'
                HeaderAlways = @{

                    CssLink = 'https://fonts.googleapis.com/css2?family=Roboto+Condensed&display=swap'
                }
                Default      = $true
                Email        = $false
            }
            FontsAwesome                = @{
                Comment         = 'Fonts Awesome icons'
                InternalComment = 'font-awesome'
                Header          = @{
                    CssLink = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\fontsAwesome.min.css"
                }
                Other           = @{
                    Link = 'https://use.fontawesome.com/releases/v5.15.3/svgs/'
                }
                Default         = $true
                Email           = $false
                License         = 'SIL OFL 1.1 License'
            }
            FontsMaterialIcon           = @{
                Comment         = 'Material Design Iconic Font and CSS toolkit'
                InternalComment = 'font-materialicon'
                Demos           = 'http://zavoloklom.github.io/material-design-iconic-font/examples.html'
                Header          = @{
                    CssLink = 'https://cdnjs.cloudflare.com/ajax/libs/material-design-iconic-font/2.2.0/css/material-design-iconic-font.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\fontsMaterialDesignIconic.min.css"
                }
                Default         = $true
                Email           = $false
                License         = 'SIL OFL 1.1'
            }
            FontsSimpleIcons            = @{
                Comment         = 'Font Simple Icons'
                InternalComment = 'font-simple-icons'
                Header          = @{
                    CssLink = 'https://cdn.jsdelivr.net/npm/simple-icons-font@4.24.0/font/simple-icons.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\fontsSimpleIcons.min.css"
                }
                Default         = $true
                Email           = $false
                License         = 'CC0-1.0 License'
            }
            AnimateToolkit              = @{
                Comment         = 'Animation Toolkit'
                InternalComment = 'animation-toolkit'
                Header          = @{
                    CssLink = "https://cdn.jsdelivr.net/npm/animate.css@4.1.1/animate.min.css"
                    Css     = "$PSScriptRoot\Resources\CSS\animate.min.css"
                }
                Default         = $true
                Email           = $false
                License         = 'MIT'
            }
            Main                        = [ordered]@{
                HeaderAlways = [ordered]@{
                    CssInline = [ordered]@{
                        'body'          = [ordered]@{

                            'font-family' = "'Roboto Condensed', sans-serif"
                            'font-size'   = '8pt'
                            'margin'      = '0px'
                        }
                        'input'         = @{
                            'font-size' = '8pt'
                        }
                        '.main-section' = @{
                            'margin-top' = '0px'

                        }

                    }
                }
                Default      = $true
                Email        = $false
            }
            MainFlex                    = [ordered] @{
                HeaderAlways = [ordered] @{
                    CssInline = [ordered]@{
                        '.overflowHidden'      = [ordered] @{
                            'overflow'   = 'hidden'
                            'overflow-x' = 'hidden'
                            'overflow-y' = 'hidden'
                        }
                        '.flexParent'          = [ordered]@{
                            'display'         = 'flex'
                            'justify-content' = 'space-between'

                        }
                        '.flexParentInvisible' = [ordered]@{
                            'display'         = 'flex'
                            'justify-content' = 'space-between'
                        }
                        '.flexElement'         = [ordered]@{
                            'flex-basis' = '100%'
                            'max-width'  = '100%'
                        }
                        '.flexPanel'           = [ordered]@{
                            'flex-basis' = '100%'
                            'max-width'  = '100%'
                        }
                        '.flex-grid'           = [ordered]@{
                            'display' = 'flex'
                        }
                    }
                }
                Default      = $true
                Email        = $false
            }

            MainImage                   = [ordered]@{
                HeaderAlways = [ordered] @{
                    CssInline = [ordered]@{
                        '.legacyLogo'      = [ordered]@{
                            'display' = 'flex'
                        }
                        '.legacyLeftLogo'  = [ordered]@{
                            'flex-basis'     = '100%'
                            'border'         = '0px'
                            'padding-left'   = '0px'
                            'vertical-align' = 'middle'
                        }
                        '.legacyRightLogo' = [ordered]@{
                            'flex-basis'     = '100%'
                            'border'         = '0px'
                            'padding-right'  = '5em'
                            'text-align'     = 'right'
                            'vertical-align' = 'middle'
                        }
                        '.legacyImg'       = [ordered]@{
                            'border-radius' = '5px 5px 0 0'
                        }
                    }
                }
                Default      = $true
                Email        = $false
            }

            DefaultImage                = @{
                Comment      = 'Image Style'
                HeaderAlways = @{
                    CssInline = [ordered] @{
                        '.logo' = [ordered] @{
                            'margin' = '5px'
                        }
                    }

                }
                Default      = $true
                Email        = $true
            }
            DefaultPanel                = @{
                Comment      = 'Panel Style'
                HeaderAlways = @{
                    CssInline = [ordered] @{
                        '.defaultPanel' = [ordered] @{
                            'box-shadow'    = '0 4px 8px 0 rgba(0, 0, 0, 0.2)'
                            'transition'    = '0.3s'
                            'border-radius' = '5px'
                            'margin'        = '5px'
                        }
                    }
                }
                Default      = $true
                Email        = $false
            }
            DefaultSection              = @{
                Comment      = 'Section Style'
                HeaderAlways = @{
                    CssInline = [ordered] @{

                        '.defaultSection'     = [ordered] @{

                            'flex-direction' = 'column' 

                            'border'         = '1px solid #bbbbbb'
                            'padding-bottom' = '0px'
                            'margin'         = '5px'

                            'box-shadow'     = '0 4px 8px 0 rgba(0, 0, 0, 0.2)'
                            'transition'     = '0.3s'
                            'border-radius'  = '5px'
                        }
                        '.defaultSectionHead' = [ordered] @{
                            'display'          = 'flex'
                            'justify-content'  = 'center'
                            'padding'          = '5px'
                            'margin'           = '0px 0px 0px 0px'
                            'font-weight'      = 'bold'
                            "background-color" = ConvertFrom-Color -Color "DeepSkyBlue"
                            'color'            = ConvertFrom-Color -Color "White"
                        }

                    }
                }
                Default      = $true
                Email        = $false
            }
            DefaultHeadings             = @{
                Comment      = 'Heading Style'
                HeaderAlways = @{
                    CssInline = [ordered] @{
                        'h1' = [ordered] @{
                            'margin' = '5px'
                        }
                        'h2' = [ordered] @{
                            'margin' = '5px'
                        }
                        'h3' = [ordered] @{
                            'margin' = '5px'
                        }
                        'h4' = [ordered] @{
                            'margin' = '5px'
                        }
                        'h5' = [ordered] @{
                            'margin' = '5px'
                        }
                        'h6' = [ordered] @{
                            'margin' = '5px'
                        }
                    }

                }
                Default      = $true
                Email        = $true
            }
            DefaultText                 = @{
                Comment      = 'Text Style'
                HeaderAlways = @{
                    CssInline = [ordered] @{
                        '.defaultText' = [ordered] @{
                            'margin' = '5px'
                        }
                    }

                }
                Default      = $true
                Email        = $true
            }
            EmailLayout                 = @{
                Comment      = 'EmailLayout'
                HeaderAlways = @{
                    CssInLine = @{
                        '.layoutTable'                              = @{
                            'border'          = 'none'
                            'border-collapse' = 'collapse'
                            'vertical-align'  = 'top'
                            'padding'         = 0
                            'margin'          = 0
                            'border-spacing'  = '0px'
                        }
                        '.layoutTableRow'                           = @{
                            'border'          = 'none'
                            'border-collapse' = 'collapse'
                            'vertical-align'  = 'top'
                            'padding'         = 0
                            'margin'          = 0
                            'border-spacing'  = '0px'
                        }
                        '.layoutTableColumn'                        = @{
                            'border'          = 'none'
                            'border-collapse' = 'collapse'
                            'vertical-align'  = 'top'
                            'padding'         = 0
                            'margin'          = 0
                            'border-spacing'  = '0px'
                        }
                        'img'                                       = @{
                            'width'   = '100%';
                            'display' = 'block';
                        }
                        '.wrapper'                                  = @{
                            'padding-left'  = '10px';
                            'padding-right' = '10px';
                        }
                        '@media only screen and (max-width: 620px)' = @{
                            '.wrapper .section' = @{
                                'width' = '100%'
                            }
                            '.wrapper .column'  = @{
                                'width'   = '100%'
                                'display' = 'block'
                            }
                        }
                    }
                }
                Default      = $false
                Email        = $true
            }
            AccordionFAQ                = @{
                Comment      = 'Accordion FAQ'
                Header       = @{
                    CssLink = 'https://unpkg.com/accordion-js@3.3.4/dist/accordion.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\accordion.min.css"
                    JsLink  = 'https://unpkg.com/accordion-js@3.3.4/dist/accordion.min.js'
                    JS      = "$PSScriptRoot\Resources\JS\accordion.min.js"
                }
                HeaderAlways = @{
                    CssInline = @{
                        '.accordion-container' = @{
                            margin  = '5px'
                            padding = '0px'
                            color   = '#4d5974'

                        }
                        '.ac'                  = @{

                        }
                        '.ac-header'           = @{

                        }
                        '.ac-panel'            = @{

                        }
                    }
                }
                Default      = $true
                Email        = $false
            }
            AccordionSummary            = @{
                Comment     = 'Accordion Summary'
                Internal    = $true
                Header      = @{
                    CssLink = "$($ConfigurationURL)/CSS/accordionSummary.min.css"
                    Css     = "$PSScriptRoot\Resources\CSS\accordionSummary.css"
                    JsLink  = "$($ConfigurationURL)/JS/accordionSummary.min.js"
                    JS      = "$PSScriptRoot\Resources\JS\accordionSummary.js"
                }

                LicenseLink = ''
                License     = ''
                SourceCodes = 'https://codepen.io/banik/pen/exjLzB'
                Default     = $true
                Email       = $false
            }
            CarouselKineto              = @{
                Comment      = 'Kineto JS Library'
                Header       = @{
                    CssLinkOriginal = 'https://cdn.jsdelivr.net/gh/findawayer/kineto@main/dist/kineto.css'
                    CssLink         = "$($ConfigurationURL)/CSS/kineto.min.css" 
                    Css             = "$PSScriptRoot\Resources\CSS\kineto.min.css" 
                }
                HeaderAlways = @{
                    Css       = "$PSScriptRoot\Resources\CSS\kinetoStyle.css"
                    CssInLine = [ordered]@{
                        '.slide' = [ordered] @{

                        }
                    }
                }
                Body         = @{
                    JSLinkOriginal = "https://cdn.jsdelivr.net/gh/findawayer/kineto@main/dist/kineto.js"
                    JSLink         = "$($ConfigurationURL)/JS/kineto.min.js"
                    JS             = "$PSScriptRoot\Resources\JS\kineto.min.js"
                }
                LicenseLink  = 'https://github.com/findawayer/kineto/blob/main/LICENSE'
                License      = 'MIT'
                SourceCodes  = 'https://github.com/findawayer/kineto'
                Default      = $true
                Email        = $false
            }
            CarouselSlick               = @{
                Comment     = 'Slick JS'
                Header      = @{
                    CssLink = 'https://cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css', 'https://cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick-theme.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\slick.min.css", "$PSScriptRoot\Resources\CSS\slick-theme.min.css"
                    JSLink  = 'https://cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js'
                    JS      = "$PSScriptRoot\Resources\JS\slick.min.js"
                }
                LicenseLink = 'https://github.com/kenwheeler/slick/blob/main/LICENSE'
                License     = 'MIT'
                SourceCodes = 'https://github.com/kenwheeler/slick/'
                Default     = $true
                Email       = $false
            }
            CodeBlocks                  = @{
                Comment      = 'EnlighterJS CodeBlocks'
                Header       = @{
                    CssLink = 'https://cdn.jsdelivr.net/npm/enlighterjs@3.4.0/dist/enlighterjs.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\enlighterjs.min.css"
                    JsLink  = 'https://cdn.jsdelivr.net/npm/enlighterjs@3.4.0/dist/enlighterjs.min.js'
                    JS      = "$PSScriptRoot\Resources\JS\enlighterjs.min.js"
                }
                Footer       = @{

                }
                HeaderAlways = @{

                    CssInline = @{
                        'div.enlighter-default' = @{
                            'flex-basis'    = '100%'
                            'margin'        = '5px'
                            'border-radius' = '0px'
                        }
                    }
                }
                FooterAlways = @{
                    JS = "$PSScriptRoot\Resources\JS\enlighterjs-footer.js"
                }
                LicenseLink  = 'https://github.com/EnlighterJS/EnlighterJS/blob/master/LICENSE.txt'
                License      = 'Mozilla Public License 2.0'
                SourceCodes  = 'https://github.com/EnlighterJS/EnlighterJS'
                Default      = $true
                Email        = $false
            }
            CodeBlocksHighlight         = @{

                Comment     = 'HighlightJS CodeBlocks'
                Header      = @{
                    CssLink = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.2/styles/default.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\highlight.min.css"
                    JsLink  = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.2/highlight.min.js'
                    JS      = "$PSScriptRoot\Resources\JS\highlight.min.js"
                }
                License     = 'BSD 3-Clause "New" or "Revised" License'
                LicenseLink = 'https://github.com/highlightjs/highlight.js/blob/master/LICENSE'
                SourceCodes = 'https://github.com/highlightjs/highlight.js'
                Default     = $true
                Email       = $false
            }
            ChartsApex                  = @{
                Comment = 'Apex Charts'
                Header  = @{
                    JsLink = @(

                        'https://cdn.jsdelivr.net/npm/apexcharts@3.41.0/dist/apexcharts.min.js'
                    )
                    JS     = @(

                        "$PSScriptRoot\Resources\JS\apexcharts.min.js"
                    )
                }
                Default = $true
                Email   = $false
            }
            ChartsEvents                = [ordered] @{
                HeaderAlways = @{
                    Js        = "$PSScriptRoot\Resources\JS\apexchartsEvents.js"
                    JsInLine  = "var dataTablesChartsEvents = {}; var count = 0;"
                    CssInline = @{
                        'td.highlight' = @{
                            'background-color' = 'yellow';
                        }
                    }
                }
                Default      = $true
                Email        = $false
            }
            ChartsOrg                   = [ordered] @{
                Comment      = 'OrgChart'
                Header       = @{
                    CssLink = 'https://cdn.jsdelivr.net/npm/orgchart@3.8.0/dist/css/jquery.orgchart.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\jquery.orgchart.min.css"
                    JsLink  = 'https://cdn.jsdelivr.net/npm/orgchart@3.8.0/dist/js/jquery.orgchart.min.js'
                    Js      = "$PSScriptRoot\Resources\JS\jquery.orgchart.min.js"
                }
                HeaderAlways = [ordered] @{
                    CssInline = [ordered] @{

                        '.orgchartWrapper' = @{
                            'min-height'    = '420px'
                            'border'        = '1px dashed #aaa'
                            'border-radius' = '0px'
                            'text-align'    = 'center'
                            'margin'        = '5px'

                            'display'       = 'flex'
                            'flex-basis'    = '100%'
                            'overflow'      = 'hidden'
                        }

                        '.orgchart'        = @{
                            'background-image' = 'none'
                            'min-height'       = '420px'
                            'border'           = '1px dashed #aaa'

                            'flex-basis'       = '100%'
                        }

                    }
                }
                Default      = $true
                Email        = $false
                License      = 'MIT'
                LicenseLink  = 'https://github.com/dabeng/OrgChart/blob/master/LICENSE'
                SourceCodes  = 'https://github.com/dabeng/OrgChart'
                Demo         = 'https://codepen.io/collection/AWxGVb/', 'https://dabeng.github.io/OrgChart/'
            }
            ChartsOrgExportPDF          = @{
                Comment = 'OrgChartExport'
                Header  = @{
                    JsLink = 'https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/jspdf.umd.min.js'
                    Js     = "$PSScriptRoot\Resources\JS\jspdf.min.js"
                }
                Default = $true
                Email   = $false
            }
            ChartsOrgExportPNG          = @{
                Comment = 'OrgChartExport'
                Header  = @{
                    JsLink = 'https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js'
                    Js     = "$PSScriptRoot\Resources\JS\html2canvas.min.js"
                }
                Default = $true
                Email   = $false
            }
            ChartsXkcd                  = @{
                Header      = @{
                    JsLink = @(
                        'https://cdn.jsdelivr.net/npm/chart.xkcd@1.1.12/dist/chart.xkcd.min.js'
                    )
                    Js     = @(
                        "$PSScriptRoot\Resources\JS\chart.xkcd.min.js"
                    )
                }
                LicenseLink = 'https://github.com/timqian/chart.xkcd/blob/master/LICENSE'
                License     = 'MIT'
                SourceCodes = 'https://github.com/timqian/chart.xkcd'
                Default     = $true
                Email       = $false
            }
            ES6Promise                  = @{
                Comment = 'ES6Promise'
                Header  = @{
                    JSLink = "https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"
                    Js     = "$PSScriptRoot\Resources\JS\es6-promise.auto.min.js"

                }
                Default = $true
                Email   = $false
            }
            Jquery                      = @{
                Comment         = 'Jquery'
                InternalComment = 'jquery'
                Header          = @{
                    JsLink = 'https://cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js'
                    Js     = "$PSScriptRoot\Resources\JS\jquery.min.js"
                }
                LicenseLink     = 'https://github.com/jquery/jquery/blob/main/LICENSE.txt'
                License         = 'MIT'
                SourceCodes     = 'https://github.com/jquery/jquery'
                Default         = $true
                Email           = $false
            }
            DataTables                  = @{
                Comment      = 'DataTables'
                HeaderAlways = @{
                    CssInline   = @{

                        'td::before, td.sorting_1::before' = @{

                            color = '#007bff !important'
                        }

                        'div.dataTables_wrapper'           = @{

                            'margin' = '5px';
                        }

                        'tfoot input'                      = @{
                            'width'      = '100%'
                            'padding'    = '-3px'
                            'box-sizing' = 'border-box'
                        }

                        'thead input'                      = @{
                            'width'      = '100%'
                            'padding'    = '-3px'
                            'box-sizing' = 'border-box'
                        }
                        'td:first-child'                   = @{
                            'white-space' = 'nowrap'
                        }

                    }         
                    CssNoscript = "$PSScriptRoot\Resources\CSS\datatables.noscript.css"

                }
                Header       = @{
                    CssLink = @(
                        "https://cdn.datatables.net/1.13.5/css/jquery.dataTables.min.css"
                        "https://cdn.datatables.net/select/1.7.0/css/select.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.jquery.min.css"
                        "$PSScriptRoot\Resources\CSS\dataTables.select.min.css"
                    )
                    JsLink  = @(
                        "https://cdn.datatables.net/1.13.5/js/jquery.dataTables.min.js"
                        "https://cdn.datatables.net/select/1.7.0/js/dataTables.select.min.js"
                        "https://cdn.datatables.net/plug-ins/1.13.5/sorting/datetime-moment.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.jquery.min.js"
                        "$PSScriptRoot\Resources\JS\dataTables.select.min.js"
                        "$PSScriptRoot\Resources\JS\dataTables.datetimeMoment.js"
                    )
                }
                Default      = $true
                Email        = $false
            }
            DataTablesEmail             = @{
                Comment      = 'DataTables for use in Email'
                HeaderAlways = @{

                    CssInline = @{
                        'table'          = @{
                            'border-collapse' = 'collapse'
                            'box-sizing'      = 'border-box'
                            'width'           = '100%'
                        }
                        'table td'       = @{
                            'border-width' = '1px'
                            'padding'      = '4px'
                            'text-align'   = 'left'

                            'border'       = '1px solid black'
                        }
                        'table thead th' = @{

                            'text-align'       = 'center';
                            'font-weight'      = 'bold';
                            'padding'          = '4px 17px';

                            'background-color' = 'white'
                            'color'            = 'black'
                            'border'           = '1px solid black'
                        }
                        'table tfoot th' = @{

                            'text-align'       = 'center'
                            'font-weight'      = 'bold'
                            'padding'          = '4px 17px'

                            'background-color' = 'white'
                            'color'            = 'black'
                            'border'           = '1px solid black'
                        }

                    }
                }
                Default      = $false
                Email        = $true
            }
            DataTablesAutoFill          = @{
                Comment = 'DataTables AutoFill Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/autofill/2.6.0/js/dataTables.autoFill.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.autoFill.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/autofill/2.6.0/css/autoFill.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.autoFill.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesButtons           = @{
                Comment = 'DataTables Buttons Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/buttons/2.4.0/js/dataTables.buttons.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.buttons.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/buttons/2.4.0/css/buttons.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\datatables.buttons.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesButtonsHTML5      = @{
                Comment = 'DataTables ButtonsHTML5 Features'
                Header  = @{
                    JsLink = @(
                        "https://cdn.datatables.net/buttons/2.4.0/js/buttons.html5.min.js"
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\datatables.buttons.html5.min.js"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesButtonsPrint      = @{
                Comment = 'DataTables ButtonsPrint Features'
                Header  = @{
                    JsLink = @(
                        "https://cdn.datatables.net/buttons/2.4.0/js/buttons.print.min.js"
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\datatables.buttons.print.min.js"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesButtonsPDF        = @{
                Comment = 'DataTables PDF Features'
                Header  = @{
                    JsLink = @(
                        'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js'
                        'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js'
                    )
                    Js     = @(
                        "$PSScriptRoot\Resources\JS\pdfmake.min.js"
                        "$PSScriptRoot\Resources\JS\vfs_fonts.min.js"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesButtonsExcel      = @{
                Comment = 'DataTables Excel Features'
                Header  = @{
                    JsLink = @(
                        'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js'
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\jszip.min.js"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesButtonsColVis     = @{
                Comment = 'DataTables ColVis Features'
                Header  = @{
                    JsLink = @(
                        "https://cdn.datatables.net/buttons/2.4.0/js/buttons.colVis.min.js"
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\dataTables.buttons.colVis.min.js"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesConditions        = @{
                Comment      = 'DataTables Conditions'
                FooterAlways = @{
                    JS = @(
                        "$PSScriptRoot\Resources\JS\dataTables.conditions.js"
                    )
                }
                Default      = $true
                Email        = $false
            }
            DataTablesColReorder        = @{
                Comment = 'DataTables ColReorder Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/colreorder/1.7.0/js/dataTables.colReorder.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.colReorder.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/colreorder/1.7.0/css/colReorder.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.colReorder.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesFuzzySearch       = @{
                Comment = 'DataTables Fuzzy Search'
                Header  = @{
                    JsLinkOriginal = "https://cdn.datatables.net/plug-ins/1.13.4/features/fuzzySearch/dataTables.fuzzySearch.js"
                    JsLink         = "$($ConfigurationURL)/JS/dataTables.fuzzySearch.min.js"
                    JS             = "$PSScriptRoot\Resources\JS\dataTables.fuzzySearch.js"
                }
                Default = $true
                Email   = $false
            }
            DataTablesFixedColumn       = @{
                Comment = 'DataTables Fixed Column Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/fixedcolumns/4.3.0/js/dataTables.fixedColumns.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.fixedColumns.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/fixedcolumns/4.3.0/css/fixedColumns.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.fixedColumns.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesFixedHeader       = @{
                Comment      = 'DataTables Fixed Header Features'
                HeaderAlways = @{
                    JsInLine = "var dataTablesFixedTracker = {};"
                }
                Header       = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/fixedheader/3.4.0/js/dataTables.fixedHeader.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.fixedHeader.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/fixedheader/3.4.0/css/fixedHeader.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.fixedHeader.min.css"
                    )
                }
                Default      = $true
                Email        = $false
            }
            DataTablesKeyTable          = @{
                Comment = 'DataTables KeyTable Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/keytable/2.10.0/js/dataTables.keyTable.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.keyTable.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/keytable/2.10.0/css/keyTable.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.keyTable.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesPercentageBars    = @{
                Comment = 'DataTables PercentageBars'
                Header  = @{
                    JsLink = @(

                        "$($ConfigurationURL)/JS/dataTables.percentageBars.js"

                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\dataTables.percentageBars.js"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesResponsive        = @{
                Comment = 'DataTables Responsive Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/responsive/2.5.0/js/dataTables.responsive.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.responsive.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/responsive/2.5.0/css/responsive.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.responsive.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesRowGrouping       = @{
                Comment = 'DataTables RowGrouping Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/rowgroup/1.4.0/js/dataTables.rowGroup.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.rowGroup.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/rowgroup/1.4.0/css/rowGroup.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.rowGroup.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesRowReorder        = @{
                Comment = 'DataTables RowReorder Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/rowreorder/1.4.0/js/dataTables.rowReorder.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.rowReorder.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/rowreorder/1.4.0/css/rowReorder.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.rowReorder.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesScroller          = @{
                Comment = 'DataTables Scroller Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/scroller/2.2.0/js/dataTables.scroller.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.scroller.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/scroller/2.2.0/css/scroller.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.scroller.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesStateRestore      = @{
                Comment = 'DataTables State Restore Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/staterestore/1.3.0/js/dataTables.stateRestore.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.stateRestore.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/staterestore/1.3.0/css/stateRestore.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.stateRestore.min.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesDateTime          = @{
                Comment = 'DataTables DateTime Features'
                Header  = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/datetime/1.5.0/js/dataTables.dateTime.min.js"
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.dateTime.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/datetime/1.5.0/css/dataTables.dateTime.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.dateTime.css"
                    )
                }
                Default = $true
                Email   = $false
            }
            DataTablesSearchBuilder     = @{

                Comment = 'DataTables SearchBuilder'
                Update  = $true
                Header  = @{

                    JSLink  = "https://cdn.datatables.net/searchbuilder/1.5.0/js/dataTables.searchBuilder.min.js"

                    JS      = "$PSScriptRoot\Resources\JS\dataTables.searchBuilder.js"

                    CssLink = 'https://cdn.datatables.net/searchbuilder/1.5.0/css/searchBuilder.dataTables.min.css' 

                    Css     = "$PSScriptRoot\Resources\CSS\dataTables.searchBuilder.css"
                }
                Default = $true
                Email   = $false
            }
            DataTablesSearchFade        = @{
                Comment      = 'DataTables SearchFade'
                HeaderAlways = @{
                    CssInline = @{
                        '.notMatched td' = @{
                            'opacity' = 0.2
                        }
                    }
                }
                Header       = @{
                    JsLink  = "https://cdn.datatables.net/plug-ins/1.13.1/features/searchFade/dataTables.searchFade.min.js"
                    JS      = "$PSScriptRoot\Resources\JS\datatables.SearchFade.min.js"
                    CssLink = "https://cdn.datatables.net/plug-ins/1.13.1/features/searchFade/dataTables.searchFade.min.css"
                    Css     = "$PSScriptRoot\Resources\CSS\datatables.SearchFade.min.css"
                }
                Default      = $true
                Email        = $false
            }
            DataTablesSearchHighlight   = @{
                Comment = 'DataTables SearchHighlight'
                Header  = @{
                    JsLinkOriginal = "https://cdn.datatables.net/plug-ins/1.13.1/features/searchHighlight/dataTables.searchHighlight.min.js", 'https://cdn.jsdelivr.net/gh/bartaz/sandbox.js@master/jquery.highlight.js'
                    JsLink         = "https://cdn.datatables.net/plug-ins/1.13.1/features/searchHighlight/dataTables.searchHighlight.min.js", "$($ConfigurationURL)/JS/dataTables.searchHighlightRequire.min.js"
                    JS             = "$PSScriptRoot\Resources\JS\dataTables.searchHighlight.min.js", "$PSScriptRoot\Resources\JS\dataTables.searchHighlightRequire.js"
                    CSSLink        = 'https://cdn.datatables.net/plug-ins/1.13.1/features/searchHighlight/dataTables.searchHighlight.css'
                    CSS            = "$PSScriptRoot\Resources\CSS\dataTables.searchHighlight.css"
                }
                Default = $true
                Email   = $false
            }
            DataTablesSearchAlphabet    = @{
                Comment = 'DataTables AlphabetSearch'
                Header  = @{
                    JsLink   = "https://cdn.datatables.net/plug-ins/1.13.1/features/alphabetSearch/dataTables.alphabetSearch.js"

                    JsLink1  = "$($ConfigurationURL)/JS/dataTables.alphabetSearch.min.js"
                    JS       = "$PSScriptRoot\Resources\JS\dataTables.alphabetSearch.min.js"
                    CSSLink  = 'https://cdn.datatables.net/plug-ins/1.13.1/features/alphabetSearch/dataTables.alphabetSearch.css'

                    CSSLink1 = "$($ConfigurationURL)/CSS/dataTables.alphabetSearch.min.css"
                    CSS      = "$PSScriptRoot\Resources\CSS\dataTables.alphabetSearch.css"
                }
                Default = $true
                Email   = $false
            }
            DataTablesSearchPanes       = @{
                Comment      = 'DataTables Search Panes Features'
                Header       = @{
                    JsLink  = @(
                        "https://cdn.datatables.net/searchpanes/2.2.0/js/dataTables.searchPanes.min.js"
                    )
                    Js      = @(
                        "$PSScriptRoot\Resources\JS\dataTables.searchPanes.min.js"
                    )
                    CssLink = @(
                        "https://cdn.datatables.net/searchpanes/2.2.0/css/searchPanes.dataTables.min.css"
                    )
                    Css     = @(
                        "$PSScriptRoot\Resources\CSS\dataTables.searchPanes.min.css"
                    )
                }
                HeaderAlways = @{
                    CssInline = [ordered] @{
                        ".dtsp-panesContainer" = [ordered]@{
                            'width' = 'unset !important'
                        }
                    }
                }
                Default      = $true
                Email        = $false
            }
            DataTablesSearchPanesButton = @{
                Comment      = 'DataTables Search Panes when using button feature'
                HeaderAlways = @{
                    CssInline = @{
                        'div.dt-button-collection' = @{
                            'position'      = 'relative'

                            'width'         = 'auto !important'
                            'margin-top'    = '10px !important'
                            'margin-bottom' = '10px !important'
                        }
                    }
                }
                Default      = $true
                Email        = $false
            }
            DataTablesPageResize        = @{
                Comment = 'DataTables PageResize'
                Header  = @{
                    JsLink = "https://cdn.datatables.net/plug-ins/1.13.4/features/pageResize/dataTables.pageResize.min.js"
                    JS     = "$PSScriptRoot\Resources\JS\dataTables.pageResize.min.js"
                }
                Default = $true
                Email   = $false
            }
            DataTablesSelect            = @{
                Comment = 'DataTables Select'
                Header  = @{
                    JsLink  = "https://cdn.datatables.net/select/1.7.0/js/dataTables.select.js"
                    JS      = "$PSScriptRoot\Resources\JS\dataTables.select.min.js"
                    CSSLink = 'https://cdn.datatables.net/select/1.7.0/css/select.dataTables.min.css'
                    CSS     = "$PSScriptRoot\Resources\CSS\select.dataTables.min.css"
                }
                Default = $true
                Email   = $false
            }
            DataTablesSimplify          = @{
                Comment      = 'DataTables (not really) - Simplified'
                HeaderAlways = @{
                    Css = "$PSScriptRoot\Resources\CSS\datatables.simplify.css"
                }
                Default      = $true
                Email        = $true
            }
            D3Mitch                     = @{
                Comment      = 'D3Mitch Feature'
                Header       = @{
                    JsLink  = @(

                        'https://cdn.jsdelivr.net/gh/deltoss/d3-mitch-tree@1.0.2/dist/js/d3-mitch-tree.min.js'
                    )
                    CssLink = @(
                        'https://cdn.jsdelivr.net/gh/deltoss/d3-mitch-tree@1.0.2/dist/css/d3-mitch-tree.min.css'
                        'https://cdn.jsdelivr.net/gh/deltoss/d3-mitch-tree@1.0.2/dist/css/d3-mitch-tree-theme-default.min.css'
                    )
                }
                HeaderAlways = @{
                    CssInLine = @{
                        ' .hierarchicalTree' = @{
                            width  = '100%'
                            height = '100%'
                        }
                    }
                }
                LicenseLink  = 'https://github.com/deltoss/d3-mitch-tree/blob/master/LICENSE'
                License      = 'MIT'
                SourceCodes  = 'https://github.com/deltoss/d3-mitch-tree'
                Default      = $true
                Email        = $false
            }
            FullCalendar                = @{
                Comment      = 'FullCalendar Basic'
                HeaderAlways = @{
                    CssInline = @{
                        '.calendarFullCalendar' = @{
                            'flex-basis' = '100%'
                            'margin'     = '5px'
                        }
                    }
                    JsInLine  = "var calendarTracker = {};"
                }
                Header       = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/fullcalendar@6.1.8/index.global.min.js'
                    JS     = "$PSScriptRoot\Resources\JS\fullCalendar.js"
                }
                Default      = $true
                Email        = $false
                LicenseLink  = 'https://github.com/fullcalendar/fullcalendar/blob/master/LICENSE.txt'
                License      = 'MIT'
                SourceCodes  = 'https://github.com/fullcalendar/fullcalendar'
            }
            HideSection                 = [ordered] @{
                Comment      = 'Hide Section Code'
                Internal     = $true
                Header       = @{
                    JSLink = "$($ConfigurationURL)/JS/hideSection.min.js"
                    JS     = "$PSScriptRoot\Resources\JS\hideSection.js"
                }
                HeaderAlways = [ordered] @{

                    CssInline = [ordered] @{
                        '.sectionHide' = @{ 
                            'width'     = 'auto'
                            'min-width' = '1.4rem'
                        }
                        '.sectionShow' = @{
                            'width' = '100%'
                        }
                    }
                }
                Default      = $true
                Email        = $false
            }
            EscapeRegex                 = @{
                Comment      = 'Allows EscapeRegex for diagrams and table events'
                FooterAlways = @{
                    JS = "$PSScriptRoot\Resources\JS\escapeRegex.js"
                }
                Default      = $true
                Email        = $false
            }
            FancyTree                   = @{
                HeaderAlways = @{
                    CssInline = @{
                        '.fancyTree' = @{
                            'margin' = '5px'
                        }
                    }
                }
                Header       = @{
                    JSLink  = @(
                        'https://cdn.jsdelivr.net/npm/jquery.fancytree@2.38.2/dist/jquery.fancytree-all-deps.min.js'
                    )
                    CSSLink = @(
                        'https://cdn.jsdelivr.net/npm/jquery.fancytree@2.38.2/dist/skin-win8/ui.fancytree.min.css'
                    )
                }
                Default      = $true
                Email        = $false
                LicenseLink  = 'https://github.com/mar10/fancytree/blob/master/LICENSE.txt'
                License      = 'MIT'
                SourceCodes  = 'https://github.com/mar10/fancytree'
            }
            Raphael                     = @{
                Comment     = 'Raphaël: Cross-browser vector graphics the easy way'
                Demos       = 'https://dmitrybaranovskiy.github.io/raphael/'
                Header      = @{
                    JSLink = @(
                        'https://cdnjs.cloudflare.com/ajax/libs/raphael/2.3.0/raphael.min.js'
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\raphael.min.js"
                    )
                }
                License     = 'MIT'
                LicenseLink = 'https://dmitrybaranovskiy.github.io/raphael/license.html'
                SourceCodes = 'https://github.com/DmitryBaranovskiy/raphael/'
                Default     = $true
                Email       = $false
            }
            iFrame                      = @{
                Comment      = 'iFrame'
                Default      = $true
                Email        = $false
                HeaderAlways = [ordered] @{
                    CssInline = @{
                        "iframe" = @{
                            'width'     = '1px'
                            'min-width' = '100%'
                        }
                    }
                }
            }
            iFrameResizer               = @{
                Comment     = 'iFrame Resizer (iframeSizer.min.js)'
                Demos       = 'http://davidjbradshaw.github.io/iframe-resizer/'

                Header      = @{
                    JSLink = @(
                        'https://cdn.jsdelivr.net/gh/davidjbradshaw/iframe-resizer@4.3.2/js/iframeResizer.min.js'
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\iframeResizer.min.js"
                    )
                }
                Default     = $true
                Email       = $false
                License     = 'MIT'
                LicenseLink = 'https://github.com/davidjbradshaw/iframe-resizer/blob/master/LICENSE'
                SourceCodes = 'https://github.com/davidjbradshaw/iframe-resizer'
            }
            JustGage                    = @{
                Comment     = 'Just Gage Library'
                Demos       = 'https://toorshia.github.io/justgage'
                Header      = @{
                    JSLink = @(
                        'https://cdn.jsdelivr.net/npm/justgage@1.6.1/justgage.min.js'
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\justgage.min.js"
                    )
                }
                Default     = $true
                Email       = $false
                License     = 'MIT'
                LicenseLink = 'https://github.com/toorshia/justgage/blob/master/LICENSE'
                SourceCodes = 'https://github.com/toorshia/justgage'
            }

            JsTree                      = @{
                Comment     = 'JsTree Library'
                Header      = @{
                    JSLink  = @(
                        'https://cdn.jsdelivr.net/npm/jstree@3.3.15/dist/jstree.min.js'
                    )
                    CSSLink = @(
                        'https://cdn.jsdelivr.net/npm/jstree@3.3.15/dist/themes/default/style.min.css'
                    )
                    JS      = @(
                        "$PSScriptRoot\Resources\JS\jstree.min.js"
                    )
                    CSS     = @(
                        "$PSScriptRoot\Resources\CSS\jstree.min.css"
                    )
                }
                Default     = $true
                Email       = $false
                SourceCodes = 'https://github.com/vakata/jstree'
                License     = 'MIT'
                LicenseLink = 'https://github.com/vakata/jstree/blob/master/LICENSE-MIT'
            }
            MarkdownShowdown            = @{
                Comment      = 'Showdown JS Library'
                Header       = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/showdown@2.1.0/dist/showdown.min.js'
                    JS     = "$PSScriptRoot\Resources\JS\showdown.min.js"
                }
                HeaderAlways = @{
                    Css = "$PSScriptRoot\Resources\CSS\showdown.css"

                }
                Library      = 'https://showdownjs.com/'
                SourceCodes  = 'https://github.com/showdownjs/showdown'
                License      = 'MIT'
                LicenseLink  = 'https://github.com/showdownjs/showdown/blob/master/LICENSE'
                Default      = $true
                Email        = $false
            }
            MarkdownShowdownTOC         = @{
                Comment     = 'Showdown-TOC JS Library'
                Header      = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/showdown-toc@1.0.1/dist/index.umd.min.js'
                    JS     = "$PSScriptRoot\Resources\JS\showdown-toc.min.js"
                }
                Library     = 'https://github.com/ahungrynoob/showdown-toc'
                SourceCodes = 'https://github.com/ahungrynoob/showdown-toc'
                License     = 'MIT'
                LicenseLink = 'https://github.com/ahungrynoob/showdown-toc/blob/master/LICENSE'
                Default     = $true
                Email       = $false
            }
            JSXSS                       = @{
                Comment     = 'JS XSS Library'
                Header      = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/xss@1.0.14/dist/xss.min.js'
                    JS     = "$PSScriptRoot\Resources\JS\xss.min.js"
                }
                Library     = 'https://jsxss.com/en/index.html'
                SourceCodes = 'https://github.com/leizongmin/js-xss'
                License     = 'MIT'
                LicenseLink = 'https://github.com/leizongmin/js-xss/blob/master/LICENSE'
                Default     = $true
                Email       = $false
            }
            Moment                      = @{
                Comment     = 'Momment JS Library'
                Header      = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js'
                    JS     = "$PSScriptRoot\Resources\JS\moment.min.js"
                }
                Library     = 'https://momentjs.com/'
                SourceCodes = 'https://github.com/moment/moment/'
                License     = 'MIT'
                LicenseLink = 'https://github.com/moment/moment/blob/develop/LICENSE'
                Default     = $true
                Email       = $false
            }
            Mermaid                     = @{
                Comment     = 'Mermaid JS Library'
                Footer      = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/mermaid@10.4.0/dist/mermaid.min.js'
                    JS     = "$PSScriptRoot\Resources\JS\mermaid.min.js"
                }
                Library     = 'https://mermaid.js.org/'
                SourceCodes = 'https://github.com/mermaid-js/mermaid'
                License     = 'MIT'
                LicenseLink = 'https://github.com/mermaid-js/mermaid/blob/develop/LICENSE'
                Default     = $true
                Email       = $false
            }
            NavigationMenuHS            = @{
                Comment         = 'Navigation HS'
                InternalComment = 'navigation-menu'
                HeaderAlways    = @{
                    Css       = "$PSScriptRoot\Resources\CSS\jquery.hsmenu.css"
                    CssInLine = @{
                        '.hs-menubar'    = @{

                        }
                        '.hs-navigation' = @{

                        }
                    }
                }
                FooterAlways    = @{
                    Js = "$PSScriptRoot\Resources\JS\jquery.hsmenu.js"
                }
                Default         = $true
                Email           = $false
            }
            NavigationMenuDropdown      = @{
                Comment         = 'Navigation Dropdown'
                InternalComment = 'navigation-menu'
                HeaderAlways    = @{
                    Css       = "$PSScriptRoot\Resources\CSS\jquery.hsmenu-dropdown.css"
                    CssInline = @{

                        "@media only screen and (min-width: 480px)" = @{
                            ".menu-items"   = @{
                                width = '200px'
                            }
                            ".has-child ul" = @{
                                width = '200px'
                            }
                        }

                        "@media only screen and (max-width: 480px)" = @{

                        }
                    }
                }
                FooterAlways    = @{
                    Js = "$PSScriptRoot\Resources\JS\jquery.hsmenu-dropdown.js"
                }
                Default         = $true
                Email           = $false
            }
            NavigationFloat             = @{
                Comment         = 'Navigation Hamburger'
                InternalComment = 'navigation-menu'
                HeaderAlways    = @{
                    Css       = "$PSScriptRoot\Resources\CSS\jquery.hsmenu-sidepenal.css"
                    CssInLine = @{
                        '.penal-trigger'              = @{

                        }
                        ".penal-widget.top-header h2" = @{

                        }
                        '.top-header .tagline'        = @{

                        }
                        '.penal-trigger:hover'        = @{

                        }
                    }
                }
                FooterAlways    = @{
                    Js = "$PSScriptRoot\Resources\JS\jquery.hsmenu-sidepenal.js"
                }
                Default         = $true
                Email           = $false
            }
            NavigationMultilevel        = @{
                Comment      = 'Navigation Multilevel'
                HeaderAlways = @{
                    Css = "$PSScriptRoot\Resources\CSS\jquery.multilevelpushmenu_grey.css"
                }
                Header       = @{
                    CssLink = 'https://cdn.jsdelivr.net/gh/adgsm/multi-level-push-menu/jquery.multilevelpushmenu.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\jquery.multilevelpushmenu.min.css"
                }
                Footer       = @{
                    Js     = "$PSScriptRoot\Resources\JS\jquery.multilevelpushmenu.min.js"
                    JSLink = 'https://cdn.jsdelivr.net/gh/adgsm/multi-level-push-menu/jquery.multilevelpushmenu.min.js'
                }
                SourceCodes  = 'https://github.com/adgsm/multi-level-push-menu'
                LicenseLink  = 'https://opensource.org/licenses/mit-license.php'
                License      = 'MIT'
                Default      = $true
                Email        = $false
            }
            SectionScrolling            = @{
                Comment     = 'Section Scrolling'
                Internal    = $true
                Header      = @{
                    CssLink = "$($ConfigurationURL)/CSS/sectionScrolling.min.css"
                    Css     = "$PSScriptRoot\Resources\CSS\sectionScrolling.css"
                    JsLink  = "$($ConfigurationURL)/JS/sectionScrolling.min.js"
                    JS      = "$PSScriptRoot\Resources\JS\sectionScrolling.js"
                }

                LicenseLink = ''
                License     = 'https://www.bram.us/2020/01/10/smooth-scrolling-sticky-scrollspy-navigation/'
                SourceCodes = 'https://codepen.io/bramus/pen/ExaEqMJ'
                Default     = $true
                Email       = $false
            }
            Popper                      = @{
                Comment      = 'Popper and Tooltip for FullCalendar'
                HeaderAlways = @{
                    Css = "$PSScriptRoot\Resources\CSS\popper.css"
                }
                Header       = @{
                    JSLink = @(
                        'https://unpkg.com/popper.js/dist/umd/popper.min.js'
                        'https://unpkg.com/tooltip.js/dist/umd/tooltip.min.js'
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\popper.min.js"
                        "$PSScriptRoot\Resources\JS\tooltip.min.js"
                    )
                }
                SourceCodes  = 'https://github.com/popperjs/popper-core'
                LicenseLink  = 'https://github.com/popperjs/popper-core/blob/master/LICENSE.md'
                License      = 'MIT'
                Default      = $true
                Email        = $false
            }
            RedrawObjects               = @{
                Comment  = 'Allows redrawObjects for collapsed sections and changing tabs'
                Internal = $true
                Footer   = @{
                    JSLink = "$($ConfigurationURL)/JS/redrawObjects.min.js"
                    JS     = "$PSScriptRoot\Resources\JS\redrawObjects.js"
                }

                Default  = $true
                Email    = $false
            }
            Tabbis                      = @{
                Comment      = 'Elastic Tabbis'
                Internal     = $true
                HeaderAlways = @{

                    CssInline = [ordered] @{
                        ".tabsWrapper"         = [ordered]@{
                            'text-align'     = 'center'

                            'font-family'    = "'Roboto', sans-serif !important"
                            'text-transform' = 'uppercase'

                        }
                        '[data-tabs]'          = [ordered]@{
                            'display'         = 'flex'

                            'margin'          = '5px 5px 5px 5px'
                            'padding'         = '0px'
                            'box-shadow'      = '0px 5px 20px rgba(0, 0, 0, 0.1)'
                            'border-radius'   = '5px'
                            'justify-content' = 'center'
                            'flex-wrap'       = 'wrap'
                        }

                        '[data-tabs]>*'        = [ordered]@{
                            'cursor'      = 'pointer'
                            'padding'     = '10px 20px'
                            'flex-grow'   = 1
                            'flex-shrink' = 1
                            'flex-basis'  = 'auto'
                        }
                        '[data-tabs] .active'  = [ordered]@{
                            'background'    = '#1e90ff' 
                            'color'         = '#fff'
                            'border-radius' = '5px'
                        }
                        '[data-panes]>*'       = [ordered]@{
                            'display' = 'none'
                        }
                        '[data-panes]>.active' = [ordered]@{
                            'display' = 'block'
                        }
                    }
                }

                FooterAlways = @{
                    JS = @(
                        "$PSScriptRoot\Resources\JS\tabbis.js"
                        "$PSScriptRoot\Resources\JS\tabbisAdditional.js"
                    )
                }

                Default      = $true
                Email        = $false
            }
            TabsInline                  = @{

                Comment     = 'Tabs Inline'
                Header      = @{
                    JsLink  = 'https://cdn.jsdelivr.net/npm/jquery-smarttab@4.0.2/dist/js/jquery.smartTab.min.js'
                    Js      = "$PSScriptRoot\Resources\JS\jquery.smartTab.min.js"
                    CssLink = "https://cdn.jsdelivr.net/npm/jquery-smarttab@4.0.2/dist/css/smart_tab_all.min.css"
                    Css     = "$PSScriptRoot\Resources\CSS\jquery.smartTab.min.css"
                }
                SourceCodes = 'https://github.com/techlab/jquery-smarttab'
                LicenseLink = 'https://github.com/techlab/jquery-smarttab/blob/master/LICENSE'
                License     = 'MIT'
                Default     = $true
                Email       = $false
            }
            TabsInlineColor             = @{
                Comment      = 'Tabs Inline Color'
                HeaderAlways = [ordered]@{
                    CssInline = [ordered]@{
                        ':root' = [ordered]@{

                        }
                    }
                }
                Default      = $true
                Email        = $false
            }
            TimeLine                    = @{
                Comment      = 'Timeline Simple'
                HeaderAlways = @{
                    Css = "$PSScriptRoot\Resources\CSS\timeline-simple.css"
                }
                Default      = $true
                Email        = $false
            }
            Toasts                      = @{
                Comment      = 'Toasts Looking Messages'
                HeaderAlways = @{
                    Css = "$PSScriptRoot\Resources\CSS\Toasts.css"
                }
                Default      = $true
                Email        = $false
            }
            StatusButtonical            = @{
                Comment      = 'Status Buttonical'
                HeaderAlways = @{
                    Css = "$PSScriptRoot\Resources\CSS\status.css"
                }
                Default      = $true
                Email        = $false
            }
            VisData                     = [ordered]@{
                Header  = @{

                    JsLink = 'https://cdn.jsdelivr.net/npm/vis-data@7.1.9/peer/umd/vis-data.min.js'
                    Js     = "$PSScriptRoot\Resources\JS\vis-data.min.js"
                }
                Default = $true
                Email   = $false
            }
            VisNetwork                  = [ordered]@{
                Comment      = 'VIS Network Dynamic, browser based visualization libraries'
                Demos        = @(
                    'https://visjs.github.io/vis-network/examples/'
                )
                HeaderAlways = @{
                    CssInline = [ordered]@{
                        '.diagram'           = [ordered]@{
                            'min-height' = '400px'
                            'width'      = '100%'
                            'height'     = '100%'
                            'border'     = '0px solid unset'
                        }
                        '.vis-network:focus' = [ordered]@{
                            'outline' = 'none'
                        }
                    }
                    JsInLine  = "var diagramTracker = {};"
                }
                Header       = @{

                    JsLink  = 'https://cdn.jsdelivr.net/npm/vis-network@9.1.9/peer/umd/vis-network.min.js'
                    Js      = "$PSScriptRoot\Resources\JS\vis-network.min.js"
                    CssLink = 'https://cdn.jsdelivr.net/npm/vis-network@9.1.9/styles/vis-network.min.css'
                    Css     = "$PSScriptRoot\Resources\CSS\vis-network.min.css"
                }
                Default      = $true
                Email        = $false
                License      = 'Apache 2.0'
                LicenseLink  = 'https://github.com/visjs/vis-network/blob/master/LICENSE-APACHE-2.0'
                SourceCodes  = 'https://github.com/visjs'
            }
            VisNetworkClustering        = [ordered] @{
                Comment  = 'VIS Network Clustering'
                Internal = $true
                Footer   = @{
                    JSLink = "$($ConfigurationURL)/JS/vis-network.functions.min.js"
                    JS     = "$PSScriptRoot\Resources\JS\vis-network.functions.js"
                }
                Default  = $true
                Email    = $false
            }
            VisNetworkLoadingBar        = [ordered]@{
                Comment = 'VIS Network Loading Bar'
                Header  = @{
                    CssLink = "$($ConfigurationURL)/CSS/vis-network.loadingbar.min.css"
                    Css     = "$PSScriptRoot\Resources\CSS\vis-network.loadingbar.css"
                }
                Default = $true
                Email   = $false
            }
            VisNetworkLoad              = [ordered] @{
                Comment = 'VIS Network Load'
                Header  = @{
                    JSLink = "$($ConfigurationURL)/JS/vis-network.loadingbar.min.js"
                    JS     = "$PSScriptRoot\Resources\JS\vis-network.loadingbar.js"
                }
                Default = $true
                Email   = $false
            }
            VisNetworkFind              = [ordered] @{
                Comment = 'VIS Network Find'
                Header  = @{
                    JSLink  = "$($ConfigurationURL)/JS/vis-network.find.min.js"
                    JS      = "$PSScriptRoot\Resources\JS\vis-network.find.js"
                    Css     = "$PSScriptRoot\Resources\CSS\vis-network.find.css"
                    CssLink = "$($ConfigurationURL)/CSS/vis-network.find.min.css"
                }
                Default = $true
                Email   = $false
            }
            VisTimeline                 = [ordered]@{
                Comment      = 'VIS TimeLine'
                HeaderAlways = [ordered]@{
                    CssInline = [ordered] @{
                        '.vis-timeline' = @{
                            'outline' = 'none'
                            'border'  = 'none !important'
                        }
                    }
                }
                Header       = @{

                    JsLink  = 'https://cdn.jsdelivr.net/npm/vis-timeline@7.7.3/peer/umd/vis-timeline-graph2d.min.js'
                    Js      = "$PSScriptRoot\Resources\JS\vis-timeline-graph2d.min.js"
                    Css     = "$PSScriptRoot\Resources\CSS\vis-timeline-graph2d.min.css"
                    CssLink = 'https://cdn.jsdelivr.net/npm/vis-timeline@7.7.3/dist/vis-timeline-graph2d.min.css'
                }
                LicenseLink  = 'https://github.com/visjs/vis-timeline/blob/master/LICENSE.md'
                License      = 'MIT and Apache 2.0'
                SourceCodes  = 'https://github.com/visjs/vis-timeline'
                Default      = $true
                Email        = $false
            }
            QR                          = [ordered] @{
                Comment      = 'QR Code'
                Demos        = 'https://www.easyproject.cn/easyqrcodejs/tryit.html'
                HeaderAlways = @{
                    CssInline = @{
                        '.qrcode' = [ordered] @{
                            'margin' = '5px'
                        }
                    }
                }
                Header       = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/easyqrcodejs@4.4.13/dist/easy.qrcode.min.js'
                    Js     = "$PSScriptRoot\Resources\JS\easy.qrcode.min.js"
                }
                Default      = $true
                Email        = $false
                LicenseLink  = 'https://github.com/ushelp/EasyQRCodeJS/blob/master/LICENSE'
                License      = 'MIT'
                SourceCodes  = 'https://github.com/ushelp/EasyQRCodeJS'
            }
            Wizard                      = [ordered] @{
                Comment      = 'Wizard'
                Demos        = 'http://techlaboratory.net/jquery-smartwizard'
                Header       = @{
                    JsLink  = 'https://cdn.jsdelivr.net/npm/smartwizard@6.0.6/dist/js/jquery.smartWizard.min.js'
                    Js      = "$PSScriptRoot\Resources\JS\jquery.smartWizard.min.js"
                    CssLink = "https://cdn.jsdelivr.net/npm/smartwizard@6.0.6/dist/css/smart_wizard_all.min.css"
                    Css     = "$PSScriptRoot\Resources\CSS\jquery.smartWizard.min.css"
                }
                HeaderAlways = @{
                    CssInline = @{
                        '.defaultWizard' = [ordered] @{
                            'margin' = '5px'
                        }
                    }
                }
                Default      = $true
                Email        = $false
                LicenseLink  = 'https://github.com/techlab/jquery-smartwizard/blob/master/LICENSE'
                License      = 'MIT'
                SourceCodes  = 'https://github.com/techlab/jquery-smartwizard'
            }
            WizardColor                 = @{
                Comment      = 'Wizard Color'
                HeaderAlways = [ordered]@{
                    CssInline = [ordered]@{
                        ':root' = [ordered] @{

                        }
                    }
                }
                Default      = $true
                Email        = $false
            }
            WinBox                      = [ordered] @{
                Comment      = 'WinBox'
                Demos        = 'http://techlaboratory.net/jquery-smartwizard'
                Header       = @{
                    JsLink  = 'https://cdn.jsdelivr.net/npm/winbox@0.2.73/dist/js/winbox.min.js'
                    Js      = "$PSScriptRoot\Resources\JS\winbox.min.js"
                    CssLink = @(
                        "https://cdn.jsdelivr.net/npm/winbox@0.2.73/dist/css/winbox.min.css"
                        'https://cdn.jsdelivr.net/npm/winbox@0.2.73/dist/css/themes/modern.min.css'
                        'https://cdn.jsdelivr.net/npm/winbox@0.2.73/dist/css/themes/white.min.css'
                    )
                    Css     = "$PSScriptRoot\Resources\CSS\winbox.min.css", "$PSScriptRoot\Resources\CSS\winbox.modern.min.css", "$PSScriptRoot\Resources\CSS\winbox.white.min.css"
                }
                HeaderAlways = @{
                    JS = "$PSScriptRoot\Resources\JS\winbox.support.js"

                }
                Default      = $true
                Email        = $false
                LicenseLink  = 'https://github.com/nextapps-de/winbox/blob/master/LICENSE'
                License      = 'Apache-2.0 License'
                SourceCodes  = 'https://github.com/nextapps-de/winbox'
            }
            JQueryMouseWheel            = @{
                Header      = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/jquery-mousewheel@3.1.13/jquery.mousewheel.min.js'
                    JS     = "$PSScriptRoot\Resources\JS\jquery.mousewheel.min.js"
                }
                SourceCodes = 'https://github.com/jquery/jquery-mousewheel'
                License     = 'MIT'
                LicenseLink = 'https://github.com/jquery/jquery-mousewheel/blob/master/LICENSE.txt'
                Default     = $true
                Email       = $false
            }
            Mapael                      = @{
                Comment      = 'Mapael JQuery'
                Header       = @{
                    JSLink = 'https://cdn.jsdelivr.net/npm/jquery-mapael@2.2.0/js/jquery.mapael.min.js'
                    JS     = "$PSScriptRoot\Resources\JS\jquery.mapael.min.js"
                }
                HeaderAlways = @{
                    CssInline = [ordered] @{
                        ".mapael .mapTooltip" = [ordered]@{
                            position           = 'absolute'
                            'background-color' = '#fff'
                            'moz-opacity'      = 0.70
                            opacity            = 0.70;
                            filter             = 'alpha(opacity=70)';
                            'border-radius'    = '10px';
                            padding            = '10px';
                            'z-index'          = 1000;
                            'max-width'        = '200px';
                            display            = 'none';
                            color              = '#343434';
                        }
                    }
                }
                Library      = 'https://github.com/neveldo/jQuery-Mapael'
                SourceCodes  = 'https://github.com/neveldo/jQuery-Mapael'
                License      = 'MIT'
                LicenseLink  = 'https://github.com/neveldo/jQuery-Mapael/blob/master/LICENSE'
                Default      = $true
                Email        = $false
            }
            MapaelMaps_European_Union   = @{
                Internal = $true
                Header   = @{
                    JSLink = "$($ConfigurationURL)/Maps/european_union/european_union.js"
                    JS     = "$PSScriptRoot\Resources\Maps\european_union\european_union.js"
                }
                Default  = $true
                Email    = $false
            }
            MapaelMaps_Poland           = @{
                Internal = $true
                Header   = @{
                    JSLink = "$($ConfigurationURL)/Maps/poland/poland.min.js"
                    JS     = "$PSScriptRoot\Resources\Maps\poland\poland.js"
                }
                Default  = $true
                Email    = $false
            }
            MapaelMaps_usa_states       = @{
                Internal = $true
                Header   = @{
                    JSLink = "$($ConfigurationURL)/Maps/usa/usa_states.min.js"
                    JS     = "$PSScriptRoot\Resources\Maps\usa\usa_states.js"
                }
                Default  = $true
                Email    = $false
            }
            MapaelMaps_world_countries  = @{
                Internal = $true
                Header   = @{
                    JSLink = "$($ConfigurationURL)/Maps/world/world_countries.min.js"
                    JS     = "$PSScriptRoot\Resources\Maps\world\world_countries.js"
                }
                Default  = $true
                Email    = $false
            }
        }
    }
    $Configuration
}

$Global:HTMLIcons = [ordered] @{
    FontAwesomeBrands  = [ordered] @{
        '500px'                          = 'f26e'
        'accessible-icon'                = 'f368'
        'accusoft'                       = 'f369'
        'acquisitions-incorporated'      = 'f6af'
        'adn'                            = 'f170'
        'adversal'                       = 'f36a'
        'affiliatetheme'                 = 'f36b'
        'airbnb'                         = 'f834'
        'algolia'                        = 'f36c'
        'alipay'                         = 'f642'
        'amazon'                         = 'f270'
        'amazon-pay'                     = 'f42c'
        'amilia'                         = 'f36d'
        'android'                        = 'f17b'
        'angellist'                      = 'f209'
        'angrycreative'                  = 'f36e'
        'angular'                        = 'f420'
        'app-store'                      = 'f36f'
        'app-store-ios'                  = 'f370'
        'apper'                          = 'f371'
        'apple'                          = 'f179'
        'apple-pay'                      = 'f415'
        'artstation'                     = 'f77a'
        'asymmetrik'                     = 'f372'
        'atlassian'                      = 'f77b'
        'audible'                        = 'f373'
        'autoprefixer'                   = 'f41c'
        'avianex'                        = 'f374'
        'aviato'                         = 'f421'
        'aws'                            = 'f375'
        'bandcamp'                       = 'f2d5'
        'battle-net'                     = 'f835'
        'behance'                        = 'f1b4'
        'behance-square'                 = 'f1b5'
        'bimobject'                      = 'f378'
        'bitbucket'                      = 'f171'
        'bitcoin'                        = 'f379'
        'bity'                           = 'f37a'
        'black-tie'                      = 'f27e'
        'blackberry'                     = 'f37b'
        'blogger'                        = 'f37c'
        'blogger-b'                      = 'f37d'
        'bluetooth'                      = 'f293'
        'bluetooth-b'                    = 'f294'
        'bootstrap'                      = 'f836'
        'btc'                            = 'f15a'
        'buffer'                         = 'f837'
        'buromobelexperte'               = 'f37f'
        'buy-n-large'                    = 'f8a6'
        'canadian-maple-leaf'            = 'f785'
        'cc-amazon-pay'                  = 'f42d'
        'cc-amex'                        = 'f1f3'
        'cc-apple-pay'                   = 'f416'
        'cc-diners-club'                 = 'f24c'
        'cc-discover'                    = 'f1f2'
        'cc-jcb'                         = 'f24b'
        'cc-mastercard'                  = 'f1f1'
        'cc-paypal'                      = 'f1f4'
        'cc-stripe'                      = 'f1f5'
        'cc-visa'                        = 'f1f0'
        'centercode'                     = 'f380'
        'centos'                         = 'f789'
        'chrome'                         = 'f268'
        'chromecast'                     = 'f838'
        'cloudflare'                     = 'e07d'
        'cloudscale'                     = 'f383'
        'cloudsmith'                     = 'f384'
        'cloudversify'                   = 'f385'
        'codepen'                        = 'f1cb'
        'codiepie'                       = 'f284'
        'confluence'                     = 'f78d'
        'connectdevelop'                 = 'f20e'
        'contao'                         = 'f26d'
        'cotton-bureau'                  = 'f89e'
        'cpanel'                         = 'f388'
        'creative-commons'               = 'f25e'
        'creative-commons-by'            = 'f4e7'
        'creative-commons-nc'            = 'f4e8'
        'creative-commons-nc-eu'         = 'f4e9'
        'creative-commons-nc-jp'         = 'f4ea'
        'creative-commons-nd'            = 'f4eb'
        'creative-commons-pd'            = 'f4ec'
        'creative-commons-pd-alt'        = 'f4ed'
        'creative-commons-remix'         = 'f4ee'
        'creative-commons-sa'            = 'f4ef'
        'creative-commons-sampling'      = 'f4f0'
        'creative-commons-sampling-plus' = 'f4f1'
        'creative-commons-share'         = 'f4f2'
        'creative-commons-zero'          = 'f4f3'
        'critical-role'                  = 'f6c9'
        'css3'                           = 'f13c'
        'css3-alt'                       = 'f38b'
        'cuttlefish'                     = 'f38c'
        'd-and-d'                        = 'f38d'
        'd-and-d-beyond'                 = 'f6ca'
        'dailymotion'                    = 'e052'
        'dashcube'                       = 'f210'
        'deezer'                         = 'e077'
        'delicious'                      = 'f1a5'
        'deploydog'                      = 'f38e'
        'deskpro'                        = 'f38f'
        'dev'                            = 'f6cc'
        'deviantart'                     = 'f1bd'
        'dhl'                            = 'f790'
        'diaspora'                       = 'f791'
        'digg'                           = 'f1a6'
        'digital-ocean'                  = 'f391'
        'discord'                        = 'f392'
        'discourse'                      = 'f393'
        'dochub'                         = 'f394'
        'docker'                         = 'f395'
        'draft2digital'                  = 'f396'
        'dribbble'                       = 'f17d'
        'dribbble-square'                = 'f397'
        'dropbox'                        = 'f16b'
        'drupal'                         = 'f1a9'
        'dyalog'                         = 'f399'
        'earlybirds'                     = 'f39a'
        'ebay'                           = 'f4f4'
        'edge'                           = 'f282'
        'edge-legacy'                    = 'e078'
        'elementor'                      = 'f430'
        'ello'                           = 'f5f1'
        'ember'                          = 'f423'
        'empire'                         = 'f1d1'
        'envira'                         = 'f299'
        'erlang'                         = 'f39d'
        'ethereum'                       = 'f42e'
        'etsy'                           = 'f2d7'
        'evernote'                       = 'f839'
        'expeditedssl'                   = 'f23e'
        'facebook'                       = 'f09a'
        'facebook-f'                     = 'f39e'
        'facebook-messenger'             = 'f39f'
        'facebook-square'                = 'f082'
        'fantasy-flight-games'           = 'f6dc'
        'fedex'                          = 'f797'
        'fedora'                         = 'f798'
        'figma'                          = 'f799'
        'firefox'                        = 'f269'
        'firefox-browser'                = 'e007'
        'first-order'                    = 'f2b0'
        'first-order-alt'                = 'f50a'
        'firstdraft'                     = 'f3a1'
        'flickr'                         = 'f16e'
        'flipboard'                      = 'f44d'
        'fly'                            = 'f417'
        'font-awesome'                   = 'f2b4'
        'font-awesome-alt'               = 'f35c'
        'font-awesome-flag'              = 'f425'
        'fonticons'                      = 'f280'
        'fonticons-fi'                   = 'f3a2'
        'fort-awesome'                   = 'f286'
        'fort-awesome-alt'               = 'f3a3'
        'forumbee'                       = 'f211'
        'foursquare'                     = 'f180'
        'free-code-camp'                 = 'f2c5'
        'freebsd'                        = 'f3a4'
        'fulcrum'                        = 'f50b'
        'galactic-republic'              = 'f50c'
        'galactic-senate'                = 'f50d'
        'get-pocket'                     = 'f265'
        'gg'                             = 'f260'
        'gg-circle'                      = 'f261'
        'git'                            = 'f1d3'
        'git-alt'                        = 'f841'
        'git-square'                     = 'f1d2'
        'github'                         = 'f09b'
        'github-alt'                     = 'f113'
        'github-square'                  = 'f092'
        'gitkraken'                      = 'f3a6'
        'gitlab'                         = 'f296'
        'gitter'                         = 'f426'
        'glide'                          = 'f2a5'
        'glide-g'                        = 'f2a6'
        'gofore'                         = 'f3a7'
        'goodreads'                      = 'f3a8'
        'goodreads-g'                    = 'f3a9'
        'google'                         = 'f1a0'
        'google-drive'                   = 'f3aa'
        'google-pay'                     = 'e079'
        'google-play'                    = 'f3ab'
        'google-plus'                    = 'f2b3'
        'google-plus-g'                  = 'f0d5'
        'google-plus-square'             = 'f0d4'
        'google-wallet'                  = 'f1ee'
        'gratipay'                       = 'f184'
        'grav'                           = 'f2d6'
        'gripfire'                       = 'f3ac'
        'grunt'                          = 'f3ad'
        'guilded'                        = 'e07e'
        'gulp'                           = 'f3ae'
        'hacker-news'                    = 'f1d4'
        'hacker-news-square'             = 'f3af'
        'hackerrank'                     = 'f5f7'
        'hips'                           = 'f452'
        'hire-a-helper'                  = 'f3b0'
        'hive'                           = 'e07f'
        'hooli'                          = 'f427'
        'hornbill'                       = 'f592'
        'hotjar'                         = 'f3b1'
        'houzz'                          = 'f27c'
        'html5'                          = 'f13b'
        'hubspot'                        = 'f3b2'
        'ideal'                          = 'e013'
        'imdb'                           = 'f2d8'
        'innosoft'                       = 'e080'
        'instagram'                      = 'f16d'
        'instagram-square'               = 'e055'
        'instalod'                       = 'e081'
        'intercom'                       = 'f7af'
        'internet-explorer'              = 'f26b'
        'invision'                       = 'f7b0'
        'ioxhost'                        = 'f208'
        'itch-io'                        = 'f83a'
        'itunes'                         = 'f3b4'
        'itunes-note'                    = 'f3b5'
        'java'                           = 'f4e4'
        'jedi-order'                     = 'f50e'
        'jenkins'                        = 'f3b6'
        'jira'                           = 'f7b1'
        'joget'                          = 'f3b7'
        'joomla'                         = 'f1aa'
        'js'                             = 'f3b8'
        'js-square'                      = 'f3b9'
        'jsfiddle'                       = 'f1cc'
        'kaggle'                         = 'f5fa'
        'keybase'                        = 'f4f5'
        'keycdn'                         = 'f3ba'
        'kickstarter'                    = 'f3bb'
        'kickstarter-k'                  = 'f3bc'
        'korvue'                         = 'f42f'
        'laravel'                        = 'f3bd'
        'lastfm'                         = 'f202'
        'lastfm-square'                  = 'f203'
        'leanpub'                        = 'f212'
        'less'                           = 'f41d'
        'line'                           = 'f3c0'
        'linkedin'                       = 'f08c'
        'linkedin-in'                    = 'f0e1'
        'linode'                         = 'f2b8'
        'linux'                          = 'f17c'
        'lyft'                           = 'f3c3'
        'magento'                        = 'f3c4'
        'mailchimp'                      = 'f59e'
        'mandalorian'                    = 'f50f'
        'markdown'                       = 'f60f'
        'mastodon'                       = 'f4f6'
        'maxcdn'                         = 'f136'
        'mdb'                            = 'f8ca'
        'medapps'                        = 'f3c6'
        'medium'                         = 'f23a'
        'medium-m'                       = 'f3c7'
        'medrt'                          = 'f3c8'
        'meetup'                         = 'f2e0'
        'megaport'                       = 'f5a3'
        'mendeley'                       = 'f7b3'
        'microblog'                      = 'e01a'
        'microsoft'                      = 'f3ca'
        'mix'                            = 'f3cb'
        'mixcloud'                       = 'f289'
        'mixer'                          = 'e056'
        'mizuni'                         = 'f3cc'
        'modx'                           = 'f285'
        'monero'                         = 'f3d0'
        'napster'                        = 'f3d2'
        'neos'                           = 'f612'
        'nimblr'                         = 'f5a8'
        'node'                           = 'f419'
        'node-js'                        = 'f3d3'
        'npm'                            = 'f3d4'
        'ns8'                            = 'f3d5'
        'nutritionix'                    = 'f3d6'
        'octopus-deploy'                 = 'e082'
        'odnoklassniki'                  = 'f263'
        'odnoklassniki-square'           = 'f264'
        'old-republic'                   = 'f510'
        'opencart'                       = 'f23d'
        'openid'                         = 'f19b'
        'opera'                          = 'f26a'
        'optin-monster'                  = 'f23c'
        'orcid'                          = 'f8d2'
        'osi'                            = 'f41a'
        'page4'                          = 'f3d7'
        'pagelines'                      = 'f18c'
        'palfed'                         = 'f3d8'
        'patreon'                        = 'f3d9'
        'paypal'                         = 'f1ed'
        'penny-arcade'                   = 'f704'
        'perbyte'                        = 'e083'
        'periscope'                      = 'f3da'
        'phabricator'                    = 'f3db'
        'phoenix-framework'              = 'f3dc'
        'phoenix-squadron'               = 'f511'
        'php'                            = 'f457'
        'pied-piper'                     = 'f2ae'
        'pied-piper-alt'                 = 'f1a8'
        'pied-piper-hat'                 = 'f4e5'
        'pied-piper-pp'                  = 'f1a7'
        'pied-piper-square'              = 'e01e'
        'pinterest'                      = 'f0d2'
        'pinterest-p'                    = 'f231'
        'pinterest-square'               = 'f0d3'
        'playstation'                    = 'f3df'
        'product-hunt'                   = 'f288'
        'pushed'                         = 'f3e1'
        'python'                         = 'f3e2'
        'qq'                             = 'f1d6'
        'quinscape'                      = 'f459'
        'quora'                          = 'f2c4'
        'r-project'                      = 'f4f7'
        'raspberry-pi'                   = 'f7bb'
        'ravelry'                        = 'f2d9'
        'react'                          = 'f41b'
        'reacteurope'                    = 'f75d'
        'readme'                         = 'f4d5'
        'rebel'                          = 'f1d0'
        'red-river'                      = 'f3e3'
        'reddit'                         = 'f1a1'
        'reddit-alien'                   = 'f281'
        'reddit-square'                  = 'f1a2'
        'redhat'                         = 'f7bc'
        'renren'                         = 'f18b'
        'replyd'                         = 'f3e6'
        'researchgate'                   = 'f4f8'
        'resolving'                      = 'f3e7'
        'rev'                            = 'f5b2'
        'rocketchat'                     = 'f3e8'
        'rockrms'                        = 'f3e9'
        'rust'                           = 'e07a'
        'safari'                         = 'f267'
        'salesforce'                     = 'f83b'
        'sass'                           = 'f41e'
        'schlix'                         = 'f3ea'
        'scribd'                         = 'f28a'
        'searchengin'                    = 'f3eb'
        'sellcast'                       = 'f2da'
        'sellsy'                         = 'f213'
        'servicestack'                   = 'f3ec'
        'shirtsinbulk'                   = 'f214'
        'shopify'                        = 'e057'
        'shopware'                       = 'f5b5'
        'simplybuilt'                    = 'f215'
        'sistrix'                        = 'f3ee'
        'sith'                           = 'f512'
        'sketch'                         = 'f7c6'
        'skyatlas'                       = 'f216'
        'skype'                          = 'f17e'
        'slack'                          = 'f198'
        'slack-hash'                     = 'f3ef'
        'slideshare'                     = 'f1e7'
        'snapchat'                       = 'f2ab'
        'snapchat-ghost'                 = 'f2ac'
        'snapchat-square'                = 'f2ad'
        'soundcloud'                     = 'f1be'
        'sourcetree'                     = 'f7d3'
        'speakap'                        = 'f3f3'
        'speaker-deck'                   = 'f83c'
        'spotify'                        = 'f1bc'
        'squarespace'                    = 'f5be'
        'stack-exchange'                 = 'f18d'
        'stack-overflow'                 = 'f16c'
        'stackpath'                      = 'f842'
        'staylinked'                     = 'f3f5'
        'steam'                          = 'f1b6'
        'steam-square'                   = 'f1b7'
        'steam-symbol'                   = 'f3f6'
        'sticker-mule'                   = 'f3f7'
        'strava'                         = 'f428'
        'stripe'                         = 'f429'
        'stripe-s'                       = 'f42a'
        'studiovinari'                   = 'f3f8'
        'stumbleupon'                    = 'f1a4'
        'stumbleupon-circle'             = 'f1a3'
        'superpowers'                    = 'f2dd'
        'supple'                         = 'f3f9'
        'suse'                           = 'f7d6'
        'swift'                          = 'f8e1'
        'symfony'                        = 'f83d'
        'teamspeak'                      = 'f4f9'
        'telegram'                       = 'f2c6'
        'telegram-plane'                 = 'f3fe'
        'tencent-weibo'                  = 'f1d5'
        'the-red-yeti'                   = 'f69d'
        'themeco'                        = 'f5c6'
        'themeisle'                      = 'f2b2'
        'think-peaks'                    = 'f731'
        'tiktok'                         = 'e07b'
        'trade-federation'               = 'f513'
        'trello'                         = 'f181'
        'tripadvisor'                    = 'f262'
        'tumblr'                         = 'f173'
        'tumblr-square'                  = 'f174'
        'twitch'                         = 'f1e8'
        'twitter'                        = 'f099'
        'twitter-square'                 = 'f081'
        'typo3'                          = 'f42b'
        'uber'                           = 'f402'
        'ubuntu'                         = 'f7df'
        'uikit'                          = 'f403'
        'umbraco'                        = 'f8e8'
        'uncharted'                      = 'e084'
        'uniregistry'                    = 'f404'
        'unity'                          = 'e049'
        'unsplash'                       = 'e07c'
        'untappd'                        = 'f405'
        'ups'                            = 'f7e0'
        'usb'                            = 'f287'
        'usps'                           = 'f7e1'
        'ussunnah'                       = 'f407'
        'vaadin'                         = 'f408'
        'viacoin'                        = 'f237'
        'viadeo'                         = 'f2a9'
        'viadeo-square'                  = 'f2aa'
        'viber'                          = 'f409'
        'vimeo'                          = 'f40a'
        'vimeo-square'                   = 'f194'
        'vimeo-v'                        = 'f27d'
        'vine'                           = 'f1ca'
        'vk'                             = 'f189'
        'vnv'                            = 'f40b'
        'vuejs'                          = 'f41f'
        'watchman-monitoring'            = 'e087'
        'waze'                           = 'f83f'
        'weebly'                         = 'f5cc'
        'weibo'                          = 'f18a'
        'weixin'                         = 'f1d7'
        'whatsapp'                       = 'f232'
        'whatsapp-square'                = 'f40c'
        'whmcs'                          = 'f40d'
        'wikipedia-w'                    = 'f266'
        'windows'                        = 'f17a'
        'wix'                            = 'f5cf'
        'wizards-of-the-coast'           = 'f730'
        'wodu'                           = 'e088'
        'wolf-pack-battalion'            = 'f514'
        'wordpress'                      = 'f19a'
        'wordpress-simple'               = 'f411'
        'wpbeginner'                     = 'f297'
        'wpexplorer'                     = 'f2de'
        'wpforms'                        = 'f298'
        'wpressr'                        = 'f3e4'
        'xbox'                           = 'f412'
        'xing'                           = 'f168'
        'xing-square'                    = 'f169'
        'y-combinator'                   = 'f23b'
        'yahoo'                          = 'f19e'
        'yammer'                         = 'f840'
        'yandex'                         = 'f413'
        'yandex-international'           = 'f414'
        'yarn'                           = 'f7e3'
        'yelp'                           = 'f1e9'
        'yoast'                          = 'f2b1'
        'youtube'                        = 'f167'
        'youtube-square'                 = 'f431'
        'zhihu'                          = 'f63f'
    }
    FontAwesomeRegular = [ordered] @{
        'address-book'           = 'f2b9'
        'address-card'           = 'f2bb'
        'angry'                  = 'f556'
        'arrow-alt-circle-down'  = 'f358'
        'arrow-alt-circle-left'  = 'f359'
        'arrow-alt-circle-right' = 'f35a'
        'arrow-alt-circle-up'    = 'f35b'
        'bell'                   = 'f0f3'
        'bell-slash'             = 'f1f6'
        'bookmark'               = 'f02e'
        'building'               = 'f1ad'
        'calendar'               = 'f133'
        'calendar-alt'           = 'f073'
        'calendar-check'         = 'f274'
        'calendar-minus'         = 'f272'
        'calendar-plus'          = 'f271'
        'calendar-times'         = 'f273'
        'caret-square-down'      = 'f150'
        'caret-square-left'      = 'f191'
        'caret-square-right'     = 'f152'
        'caret-square-up'        = 'f151'
        'chart-bar'              = 'f080'
        'check-circle'           = 'f058'
        'check-square'           = 'f14a'
        'circle'                 = 'f111'
        'clipboard'              = 'f328'
        'clock'                  = 'f017'
        'clone'                  = 'f24d'
        'closed-captioning'      = 'f20a'
        'comment'                = 'f075'
        'comment-alt'            = 'f27a'
        'comment-dots'           = 'f4ad'
        'comments'               = 'f086'
        'compass'                = 'f14e'
        'copy'                   = 'f0c5'
        'copyright'              = 'f1f9'
        'credit-card'            = 'f09d'
        'dizzy'                  = 'f567'
        'dot-circle'             = 'f192'
        'edit'                   = 'f044'
        'envelope'               = 'f0e0'
        'envelope-open'          = 'f2b6'
        'eye'                    = 'f06e'
        'eye-slash'              = 'f070'
        'file'                   = 'f15b'
        'file-alt'               = 'f15c'
        'file-archive'           = 'f1c6'
        'file-audio'             = 'f1c7'
        'file-code'              = 'f1c9'
        'file-excel'             = 'f1c3'
        'file-image'             = 'f1c5'
        'file-pdf'               = 'f1c1'
        'file-powerpoint'        = 'f1c4'
        'file-video'             = 'f1c8'
        'file-word'              = 'f1c2'
        'flag'                   = 'f024'
        'flushed'                = 'f579'
        'folder'                 = 'f07b'
        'folder-open'            = 'f07c'
        'frown'                  = 'f119'
        'frown-open'             = 'f57a'
        'futbol'                 = 'f1e3'
        'gem'                    = 'f3a5'
        'grimace'                = 'f57f'
        'grin'                   = 'f580'
        'grin-alt'               = 'f581'
        'grin-beam'              = 'f582'
        'grin-beam-sweat'        = 'f583'
        'grin-hearts'            = 'f584'
        'grin-squint'            = 'f585'
        'grin-squint-tears'      = 'f586'
        'grin-stars'             = 'f587'
        'grin-tears'             = 'f588'
        'grin-tongue'            = 'f589'
        'grin-tongue-squint'     = 'f58a'
        'grin-tongue-wink'       = 'f58b'
        'grin-wink'              = 'f58c'
        'hand-lizard'            = 'f258'
        'hand-paper'             = 'f256'
        'hand-peace'             = 'f25b'
        'hand-point-down'        = 'f0a7'
        'hand-point-left'        = 'f0a5'
        'hand-point-right'       = 'f0a4'
        'hand-point-up'          = 'f0a6'
        'hand-pointer'           = 'f25a'
        'hand-rock'              = 'f255'
        'hand-scissors'          = 'f257'
        'hand-spock'             = 'f259'
        'handshake'              = 'f2b5'
        'hdd'                    = 'f0a0'
        'heart'                  = 'f004'
        'hospital'               = 'f0f8'
        'hourglass'              = 'f254'
        'id-badge'               = 'f2c1'
        'id-card'                = 'f2c2'
        'image'                  = 'f03e'
        'images'                 = 'f302'
        'keyboard'               = 'f11c'
        'kiss'                   = 'f596'
        'kiss-beam'              = 'f597'
        'kiss-wink-heart'        = 'f598'
        'laugh'                  = 'f599'
        'laugh-beam'             = 'f59a'
        'laugh-squint'           = 'f59b'
        'laugh-wink'             = 'f59c'
        'lemon'                  = 'f094'
        'life-ring'              = 'f1cd'
        'lightbulb'              = 'f0eb'
        'list-alt'               = 'f022'
        'map'                    = 'f279'
        'meh'                    = 'f11a'
        'meh-blank'              = 'f5a4'
        'meh-rolling-eyes'       = 'f5a5'
        'minus-square'           = 'f146'
        'money-bill-alt'         = 'f3d1'
        'moon'                   = 'f186'
        'newspaper'              = 'f1ea'
        'object-group'           = 'f247'
        'object-ungroup'         = 'f248'
        'paper-plane'            = 'f1d8'
        'pause-circle'           = 'f28b'
        'play-circle'            = 'f144'
        'plus-square'            = 'f0fe'
        'question-circle'        = 'f059'
        'registered'             = 'f25d'
        'sad-cry'                = 'f5b3'
        'sad-tear'               = 'f5b4'
        'save'                   = 'f0c7'
        'share-square'           = 'f14d'
        'smile'                  = 'f118'
        'smile-beam'             = 'f5b8'
        'smile-wink'             = 'f4da'
        'snowflake'              = 'f2dc'
        'square'                 = 'f0c8'
        'star'                   = 'f005'
        'star-half'              = 'f089'
        'sticky-note'            = 'f249'
        'stop-circle'            = 'f28d'
        'sun'                    = 'f185'
        'surprise'               = 'f5c2'
        'thumbs-down'            = 'f165'
        'thumbs-up'              = 'f164'
        'times-circle'           = 'f057'
        'tired'                  = 'f5c8'
        'trash-alt'              = 'f2ed'
        'user'                   = 'f007'
        'user-circle'            = 'f2bd'
        'window-close'           = 'f410'
        'window-maximize'        = 'f2d0'
        'window-minimize'        = 'f2d1'
        'window-restore'         = 'f2d2'
    }
    FontAwesomeSolid   = [ordered] @{
        'ad'                                  = 'f641'
        'address-book'                        = 'f2b9'
        'address-card'                        = 'f2bb'
        'adjust'                              = 'f042'
        'air-freshener'                       = 'f5d0'
        'align-center'                        = 'f037'
        'align-justify'                       = 'f039'
        'align-left'                          = 'f036'
        'align-right'                         = 'f038'
        'allergies'                           = 'f461'
        'ambulance'                           = 'f0f9'
        'american-sign-language-interpreting' = 'f2a3'
        'anchor'                              = 'f13d'
        'angle-double-down'                   = 'f103'
        'angle-double-left'                   = 'f100'
        'angle-double-right'                  = 'f101'
        'angle-double-up'                     = 'f102'
        'angle-down'                          = 'f107'
        'angle-left'                          = 'f104'
        'angle-right'                         = 'f105'
        'angle-up'                            = 'f106'
        'angry'                               = 'f556'
        'ankh'                                = 'f644'
        'apple-alt'                           = 'f5d1'
        'archive'                             = 'f187'
        'archway'                             = 'f557'
        'arrow-alt-circle-down'               = 'f358'
        'arrow-alt-circle-left'               = 'f359'
        'arrow-alt-circle-right'              = 'f35a'
        'arrow-alt-circle-up'                 = 'f35b'
        'arrow-circle-down'                   = 'f0ab'
        'arrow-circle-left'                   = 'f0a8'
        'arrow-circle-right'                  = 'f0a9'
        'arrow-circle-up'                     = 'f0aa'
        'arrow-down'                          = 'f063'
        'arrow-left'                          = 'f060'
        'arrow-right'                         = 'f061'
        'arrow-up'                            = 'f062'
        'arrows-alt'                          = 'f0b2'
        'arrows-alt-h'                        = 'f337'
        'arrows-alt-v'                        = 'f338'
        'assistive-listening-systems'         = 'f2a2'
        'asterisk'                            = 'f069'
        'at'                                  = 'f1fa'
        'atlas'                               = 'f558'
        'atom'                                = 'f5d2'
        'audio-description'                   = 'f29e'
        'award'                               = 'f559'
        'baby'                                = 'f77c'
        'baby-carriage'                       = 'f77d'
        'backspace'                           = 'f55a'
        'backward'                            = 'f04a'
        'bacon'                               = 'f7e5'
        'bacteria'                            = 'e059'
        'bacterium'                           = 'e05a'
        'bahai'                               = 'f666'
        'balance-scale'                       = 'f24e'
        'balance-scale-left'                  = 'f515'
        'balance-scale-right'                 = 'f516'
        'ban'                                 = 'f05e'
        'band-aid'                            = 'f462'
        'barcode'                             = 'f02a'
        'bars'                                = 'f0c9'
        'baseball-ball'                       = 'f433'
        'basketball-ball'                     = 'f434'
        'bath'                                = 'f2cd'
        'battery-empty'                       = 'f244'
        'battery-full'                        = 'f240'
        'battery-half'                        = 'f242'
        'battery-quarter'                     = 'f243'
        'battery-three-quarters'              = 'f241'
        'bed'                                 = 'f236'
        'beer'                                = 'f0fc'
        'bell'                                = 'f0f3'
        'bell-slash'                          = 'f1f6'
        'bezier-curve'                        = 'f55b'
        'bible'                               = 'f647'
        'bicycle'                             = 'f206'
        'biking'                              = 'f84a'
        'binoculars'                          = 'f1e5'
        'biohazard'                           = 'f780'
        'birthday-cake'                       = 'f1fd'
        'blender'                             = 'f517'
        'blender-phone'                       = 'f6b6'
        'blind'                               = 'f29d'
        'blog'                                = 'f781'
        'bold'                                = 'f032'
        'bolt'                                = 'f0e7'
        'bomb'                                = 'f1e2'
        'bone'                                = 'f5d7'
        'bong'                                = 'f55c'
        'book'                                = 'f02d'
        'book-dead'                           = 'f6b7'
        'book-medical'                        = 'f7e6'
        'book-open'                           = 'f518'
        'book-reader'                         = 'f5da'
        'bookmark'                            = 'f02e'
        'border-all'                          = 'f84c'
        'border-none'                         = 'f850'
        'border-style'                        = 'f853'
        'bowling-ball'                        = 'f436'
        'box'                                 = 'f466'
        'box-open'                            = 'f49e'
        'box-tissue'                          = 'e05b'
        'boxes'                               = 'f468'
        'braille'                             = 'f2a1'
        'brain'                               = 'f5dc'
        'bread-slice'                         = 'f7ec'
        'briefcase'                           = 'f0b1'
        'briefcase-medical'                   = 'f469'
        'broadcast-tower'                     = 'f519'
        'broom'                               = 'f51a'
        'brush'                               = 'f55d'
        'bug'                                 = 'f188'
        'building'                            = 'f1ad'
        'bullhorn'                            = 'f0a1'
        'bullseye'                            = 'f140'
        'burn'                                = 'f46a'
        'bus'                                 = 'f207'
        'bus-alt'                             = 'f55e'
        'business-time'                       = 'f64a'
        'calculator'                          = 'f1ec'
        'calendar'                            = 'f133'
        'calendar-alt'                        = 'f073'
        'calendar-check'                      = 'f274'
        'calendar-day'                        = 'f783'
        'calendar-minus'                      = 'f272'
        'calendar-plus'                       = 'f271'
        'calendar-times'                      = 'f273'
        'calendar-week'                       = 'f784'
        'camera'                              = 'f030'
        'camera-retro'                        = 'f083'
        'campground'                          = 'f6bb'
        'candy-cane'                          = 'f786'
        'cannabis'                            = 'f55f'
        'capsules'                            = 'f46b'
        'car'                                 = 'f1b9'
        'car-alt'                             = 'f5de'
        'car-battery'                         = 'f5df'
        'car-crash'                           = 'f5e1'
        'car-side'                            = 'f5e4'
        'caravan'                             = 'f8ff'
        'caret-down'                          = 'f0d7'
        'caret-left'                          = 'f0d9'
        'caret-right'                         = 'f0da'
        'caret-square-down'                   = 'f150'
        'caret-square-left'                   = 'f191'
        'caret-square-right'                  = 'f152'
        'caret-square-up'                     = 'f151'
        'caret-up'                            = 'f0d8'
        'carrot'                              = 'f787'
        'cart-arrow-down'                     = 'f218'
        'cart-plus'                           = 'f217'
        'cash-register'                       = 'f788'
        'cat'                                 = 'f6be'
        'certificate'                         = 'f0a3'
        'chair'                               = 'f6c0'
        'chalkboard'                          = 'f51b'
        'chalkboard-teacher'                  = 'f51c'
        'charging-station'                    = 'f5e7'
        'chart-area'                          = 'f1fe'
        'chart-bar'                           = 'f080'
        'chart-line'                          = 'f201'
        'chart-pie'                           = 'f200'
        'check'                               = 'f00c'
        'check-circle'                        = 'f058'
        'check-double'                        = 'f560'
        'check-square'                        = 'f14a'
        'cheese'                              = 'f7ef'
        'chess'                               = 'f439'
        'chess-bishop'                        = 'f43a'
        'chess-board'                         = 'f43c'
        'chess-king'                          = 'f43f'
        'chess-knight'                        = 'f441'
        'chess-pawn'                          = 'f443'
        'chess-queen'                         = 'f445'
        'chess-rook'                          = 'f447'
        'chevron-circle-down'                 = 'f13a'
        'chevron-circle-left'                 = 'f137'
        'chevron-circle-right'                = 'f138'
        'chevron-circle-up'                   = 'f139'
        'chevron-down'                        = 'f078'
        'chevron-left'                        = 'f053'
        'chevron-right'                       = 'f054'
        'chevron-up'                          = 'f077'
        'child'                               = 'f1ae'
        'church'                              = 'f51d'
        'circle'                              = 'f111'
        'circle-notch'                        = 'f1ce'
        'city'                                = 'f64f'
        'clinic-medical'                      = 'f7f2'
        'clipboard'                           = 'f328'
        'clipboard-check'                     = 'f46c'
        'clipboard-list'                      = 'f46d'
        'clock'                               = 'f017'
        'clone'                               = 'f24d'
        'closed-captioning'                   = 'f20a'
        'cloud'                               = 'f0c2'
        'cloud-download-alt'                  = 'f381'
        'cloud-meatball'                      = 'f73b'
        'cloud-moon'                          = 'f6c3'
        'cloud-moon-rain'                     = 'f73c'
        'cloud-rain'                          = 'f73d'
        'cloud-showers-heavy'                 = 'f740'
        'cloud-sun'                           = 'f6c4'
        'cloud-sun-rain'                      = 'f743'
        'cloud-upload-alt'                    = 'f382'
        'cocktail'                            = 'f561'
        'code'                                = 'f121'
        'code-branch'                         = 'f126'
        'coffee'                              = 'f0f4'
        'cog'                                 = 'f013'
        'cogs'                                = 'f085'
        'coins'                               = 'f51e'
        'columns'                             = 'f0db'
        'comment'                             = 'f075'
        'comment-alt'                         = 'f27a'
        'comment-dollar'                      = 'f651'
        'comment-dots'                        = 'f4ad'
        'comment-medical'                     = 'f7f5'
        'comment-slash'                       = 'f4b3'
        'comments'                            = 'f086'
        'comments-dollar'                     = 'f653'
        'compact-disc'                        = 'f51f'
        'compass'                             = 'f14e'
        'compress'                            = 'f066'
        'compress-alt'                        = 'f422'
        'compress-arrows-alt'                 = 'f78c'
        'concierge-bell'                      = 'f562'
        'cookie'                              = 'f563'
        'cookie-bite'                         = 'f564'
        'copy'                                = 'f0c5'
        'copyright'                           = 'f1f9'
        'couch'                               = 'f4b8'
        'credit-card'                         = 'f09d'
        'crop'                                = 'f125'
        'crop-alt'                            = 'f565'
        'cross'                               = 'f654'
        'crosshairs'                          = 'f05b'
        'crow'                                = 'f520'
        'crown'                               = 'f521'
        'crutch'                              = 'f7f7'
        'cube'                                = 'f1b2'
        'cubes'                               = 'f1b3'
        'cut'                                 = 'f0c4'
        'database'                            = 'f1c0'
        'deaf'                                = 'f2a4'
        'democrat'                            = 'f747'
        'desktop'                             = 'f108'
        'dharmachakra'                        = 'f655'
        'diagnoses'                           = 'f470'
        'dice'                                = 'f522'
        'dice-d20'                            = 'f6cf'
        'dice-d6'                             = 'f6d1'
        'dice-five'                           = 'f523'
        'dice-four'                           = 'f524'
        'dice-one'                            = 'f525'
        'dice-six'                            = 'f526'
        'dice-three'                          = 'f527'
        'dice-two'                            = 'f528'
        'digital-tachograph'                  = 'f566'
        'directions'                          = 'f5eb'
        'disease'                             = 'f7fa'
        'divide'                              = 'f529'
        'dizzy'                               = 'f567'
        'dna'                                 = 'f471'
        'dog'                                 = 'f6d3'
        'dollar-sign'                         = 'f155'
        'dolly'                               = 'f472'
        'dolly-flatbed'                       = 'f474'
        'donate'                              = 'f4b9'
        'door-closed'                         = 'f52a'
        'door-open'                           = 'f52b'
        'dot-circle'                          = 'f192'
        'dove'                                = 'f4ba'
        'download'                            = 'f019'
        'drafting-compass'                    = 'f568'
        'dragon'                              = 'f6d5'
        'draw-polygon'                        = 'f5ee'
        'drum'                                = 'f569'
        'drum-steelpan'                       = 'f56a'
        'drumstick-bite'                      = 'f6d7'
        'dumbbell'                            = 'f44b'
        'dumpster'                            = 'f793'
        'dumpster-fire'                       = 'f794'
        'dungeon'                             = 'f6d9'
        'edit'                                = 'f044'
        'egg'                                 = 'f7fb'
        'eject'                               = 'f052'
        'ellipsis-h'                          = 'f141'
        'ellipsis-v'                          = 'f142'
        'envelope'                            = 'f0e0'
        'envelope-open'                       = 'f2b6'
        'envelope-open-text'                  = 'f658'
        'envelope-square'                     = 'f199'
        'equals'                              = 'f52c'
        'eraser'                              = 'f12d'
        'ethernet'                            = 'f796'
        'euro-sign'                           = 'f153'
        'exchange-alt'                        = 'f362'
        'exclamation'                         = 'f12a'
        'exclamation-circle'                  = 'f06a'
        'exclamation-triangle'                = 'f071'
        'expand'                              = 'f065'
        'expand-alt'                          = 'f424'
        'expand-arrows-alt'                   = 'f31e'
        'external-link-alt'                   = 'f35d'
        'external-link-square-alt'            = 'f360'
        'eye'                                 = 'f06e'
        'eye-dropper'                         = 'f1fb'
        'eye-slash'                           = 'f070'
        'fan'                                 = 'f863'
        'fast-backward'                       = 'f049'
        'fast-forward'                        = 'f050'
        'faucet'                              = 'e005'
        'fax'                                 = 'f1ac'
        'feather'                             = 'f52d'
        'feather-alt'                         = 'f56b'
        'female'                              = 'f182'
        'fighter-jet'                         = 'f0fb'
        'file'                                = 'f15b'
        'file-alt'                            = 'f15c'
        'file-archive'                        = 'f1c6'
        'file-audio'                          = 'f1c7'
        'file-code'                           = 'f1c9'
        'file-contract'                       = 'f56c'
        'file-csv'                            = 'f6dd'
        'file-download'                       = 'f56d'
        'file-excel'                          = 'f1c3'
        'file-export'                         = 'f56e'
        'file-image'                          = 'f1c5'
        'file-import'                         = 'f56f'
        'file-invoice'                        = 'f570'
        'file-invoice-dollar'                 = 'f571'
        'file-medical'                        = 'f477'
        'file-medical-alt'                    = 'f478'
        'file-pdf'                            = 'f1c1'
        'file-powerpoint'                     = 'f1c4'
        'file-prescription'                   = 'f572'
        'file-signature'                      = 'f573'
        'file-upload'                         = 'f574'
        'file-video'                          = 'f1c8'
        'file-word'                           = 'f1c2'
        'fill'                                = 'f575'
        'fill-drip'                           = 'f576'
        'film'                                = 'f008'
        'filter'                              = 'f0b0'
        'fingerprint'                         = 'f577'
        'fire'                                = 'f06d'
        'fire-alt'                            = 'f7e4'
        'fire-extinguisher'                   = 'f134'
        'first-aid'                           = 'f479'
        'fish'                                = 'f578'
        'fist-raised'                         = 'f6de'
        'flag'                                = 'f024'
        'flag-checkered'                      = 'f11e'
        'flag-usa'                            = 'f74d'
        'flask'                               = 'f0c3'
        'flushed'                             = 'f579'
        'folder'                              = 'f07b'
        'folder-minus'                        = 'f65d'
        'folder-open'                         = 'f07c'
        'folder-plus'                         = 'f65e'
        'font'                                = 'f031'
        'football-ball'                       = 'f44e'
        'forward'                             = 'f04e'
        'frog'                                = 'f52e'
        'frown'                               = 'f119'
        'frown-open'                          = 'f57a'
        'funnel-dollar'                       = 'f662'
        'futbol'                              = 'f1e3'
        'gamepad'                             = 'f11b'
        'gas-pump'                            = 'f52f'
        'gavel'                               = 'f0e3'
        'gem'                                 = 'f3a5'
        'genderless'                          = 'f22d'
        'ghost'                               = 'f6e2'
        'gift'                                = 'f06b'
        'gifts'                               = 'f79c'
        'glass-cheers'                        = 'f79f'
        'glass-martini'                       = 'f000'
        'glass-martini-alt'                   = 'f57b'
        'glass-whiskey'                       = 'f7a0'
        'glasses'                             = 'f530'
        'globe'                               = 'f0ac'
        'globe-africa'                        = 'f57c'
        'globe-americas'                      = 'f57d'
        'globe-asia'                          = 'f57e'
        'globe-europe'                        = 'f7a2'
        'golf-ball'                           = 'f450'
        'gopuram'                             = 'f664'
        'graduation-cap'                      = 'f19d'
        'greater-than'                        = 'f531'
        'greater-than-equal'                  = 'f532'
        'grimace'                             = 'f57f'
        'grin'                                = 'f580'
        'grin-alt'                            = 'f581'
        'grin-beam'                           = 'f582'
        'grin-beam-sweat'                     = 'f583'
        'grin-hearts'                         = 'f584'
        'grin-squint'                         = 'f585'
        'grin-squint-tears'                   = 'f586'
        'grin-stars'                          = 'f587'
        'grin-tears'                          = 'f588'
        'grin-tongue'                         = 'f589'
        'grin-tongue-squint'                  = 'f58a'
        'grin-tongue-wink'                    = 'f58b'
        'grin-wink'                           = 'f58c'
        'grip-horizontal'                     = 'f58d'
        'grip-lines'                          = 'f7a4'
        'grip-lines-vertical'                 = 'f7a5'
        'grip-vertical'                       = 'f58e'
        'guitar'                              = 'f7a6'
        'h-square'                            = 'f0fd'
        'hamburger'                           = 'f805'
        'hammer'                              = 'f6e3'
        'hamsa'                               = 'f665'
        'hand-holding'                        = 'f4bd'
        'hand-holding-heart'                  = 'f4be'
        'hand-holding-medical'                = 'e05c'
        'hand-holding-usd'                    = 'f4c0'
        'hand-holding-water'                  = 'f4c1'
        'hand-lizard'                         = 'f258'
        'hand-middle-finger'                  = 'f806'
        'hand-paper'                          = 'f256'
        'hand-peace'                          = 'f25b'
        'hand-point-down'                     = 'f0a7'
        'hand-point-left'                     = 'f0a5'
        'hand-point-right'                    = 'f0a4'
        'hand-point-up'                       = 'f0a6'
        'hand-pointer'                        = 'f25a'
        'hand-rock'                           = 'f255'
        'hand-scissors'                       = 'f257'
        'hand-sparkles'                       = 'e05d'
        'hand-spock'                          = 'f259'
        'hands'                               = 'f4c2'
        'hands-helping'                       = 'f4c4'
        'hands-wash'                          = 'e05e'
        'handshake'                           = 'f2b5'
        'handshake-alt-slash'                 = 'e05f'
        'handshake-slash'                     = 'e060'
        'hanukiah'                            = 'f6e6'
        'hard-hat'                            = 'f807'
        'hashtag'                             = 'f292'
        'hat-cowboy'                          = 'f8c0'
        'hat-cowboy-side'                     = 'f8c1'
        'hat-wizard'                          = 'f6e8'
        'hdd'                                 = 'f0a0'
        'head-side-cough'                     = 'e061'
        'head-side-cough-slash'               = 'e062'
        'head-side-mask'                      = 'e063'
        'head-side-virus'                     = 'e064'
        'heading'                             = 'f1dc'
        'headphones'                          = 'f025'
        'headphones-alt'                      = 'f58f'
        'headset'                             = 'f590'
        'heart'                               = 'f004'
        'heart-broken'                        = 'f7a9'
        'heartbeat'                           = 'f21e'
        'helicopter'                          = 'f533'
        'highlighter'                         = 'f591'
        'hiking'                              = 'f6ec'
        'hippo'                               = 'f6ed'
        'history'                             = 'f1da'
        'hockey-puck'                         = 'f453'
        'holly-berry'                         = 'f7aa'
        'home'                                = 'f015'
        'horse'                               = 'f6f0'
        'horse-head'                          = 'f7ab'
        'hospital'                            = 'f0f8'
        'hospital-alt'                        = 'f47d'
        'hospital-symbol'                     = 'f47e'
        'hospital-user'                       = 'f80d'
        'hot-tub'                             = 'f593'
        'hotdog'                              = 'f80f'
        'hotel'                               = 'f594'
        'hourglass'                           = 'f254'
        'hourglass-end'                       = 'f253'
        'hourglass-half'                      = 'f252'
        'hourglass-start'                     = 'f251'
        'house-damage'                        = 'f6f1'
        'house-user'                          = 'e065'
        'hryvnia'                             = 'f6f2'
        'i-cursor'                            = 'f246'
        'ice-cream'                           = 'f810'
        'icicles'                             = 'f7ad'
        'icons'                               = 'f86d'
        'id-badge'                            = 'f2c1'
        'id-card'                             = 'f2c2'
        'id-card-alt'                         = 'f47f'
        'igloo'                               = 'f7ae'
        'image'                               = 'f03e'
        'images'                              = 'f302'
        'inbox'                               = 'f01c'
        'indent'                              = 'f03c'
        'industry'                            = 'f275'
        'infinity'                            = 'f534'
        'info'                                = 'f129'
        'info-circle'                         = 'f05a'
        'italic'                              = 'f033'
        'jedi'                                = 'f669'
        'joint'                               = 'f595'
        'journal-whills'                      = 'f66a'
        'kaaba'                               = 'f66b'
        'key'                                 = 'f084'
        'keyboard'                            = 'f11c'
        'khanda'                              = 'f66d'
        'kiss'                                = 'f596'
        'kiss-beam'                           = 'f597'
        'kiss-wink-heart'                     = 'f598'
        'kiwi-bird'                           = 'f535'
        'landmark'                            = 'f66f'
        'language'                            = 'f1ab'
        'laptop'                              = 'f109'
        'laptop-code'                         = 'f5fc'
        'laptop-house'                        = 'e066'
        'laptop-medical'                      = 'f812'
        'laugh'                               = 'f599'
        'laugh-beam'                          = 'f59a'
        'laugh-squint'                        = 'f59b'
        'laugh-wink'                          = 'f59c'
        'layer-group'                         = 'f5fd'
        'leaf'                                = 'f06c'
        'lemon'                               = 'f094'
        'less-than'                           = 'f536'
        'less-than-equal'                     = 'f537'
        'level-down-alt'                      = 'f3be'
        'level-up-alt'                        = 'f3bf'
        'life-ring'                           = 'f1cd'
        'lightbulb'                           = 'f0eb'
        'link'                                = 'f0c1'
        'lira-sign'                           = 'f195'
        'list'                                = 'f03a'
        'list-alt'                            = 'f022'
        'list-ol'                             = 'f0cb'
        'list-ul'                             = 'f0ca'
        'location-arrow'                      = 'f124'
        'lock'                                = 'f023'
        'lock-open'                           = 'f3c1'
        'long-arrow-alt-down'                 = 'f309'
        'long-arrow-alt-left'                 = 'f30a'
        'long-arrow-alt-right'                = 'f30b'
        'long-arrow-alt-up'                   = 'f30c'
        'low-vision'                          = 'f2a8'
        'luggage-cart'                        = 'f59d'
        'lungs'                               = 'f604'
        'lungs-virus'                         = 'e067'
        'magic'                               = 'f0d0'
        'magnet'                              = 'f076'
        'mail-bulk'                           = 'f674'
        'male'                                = 'f183'
        'map'                                 = 'f279'
        'map-marked'                          = 'f59f'
        'map-marked-alt'                      = 'f5a0'
        'map-marker'                          = 'f041'
        'map-marker-alt'                      = 'f3c5'
        'map-pin'                             = 'f276'
        'map-signs'                           = 'f277'
        'marker'                              = 'f5a1'
        'mars'                                = 'f222'
        'mars-double'                         = 'f227'
        'mars-stroke'                         = 'f229'
        'mars-stroke-h'                       = 'f22b'
        'mars-stroke-v'                       = 'f22a'
        'mask'                                = 'f6fa'
        'medal'                               = 'f5a2'
        'medkit'                              = 'f0fa'
        'meh'                                 = 'f11a'
        'meh-blank'                           = 'f5a4'
        'meh-rolling-eyes'                    = 'f5a5'
        'memory'                              = 'f538'
        'menorah'                             = 'f676'
        'mercury'                             = 'f223'
        'meteor'                              = 'f753'
        'microchip'                           = 'f2db'
        'microphone'                          = 'f130'
        'microphone-alt'                      = 'f3c9'
        'microphone-alt-slash'                = 'f539'
        'microphone-slash'                    = 'f131'
        'microscope'                          = 'f610'
        'minus'                               = 'f068'
        'minus-circle'                        = 'f056'
        'minus-square'                        = 'f146'
        'mitten'                              = 'f7b5'
        'mobile'                              = 'f10b'
        'mobile-alt'                          = 'f3cd'
        'money-bill'                          = 'f0d6'
        'money-bill-alt'                      = 'f3d1'
        'money-bill-wave'                     = 'f53a'
        'money-bill-wave-alt'                 = 'f53b'
        'money-check'                         = 'f53c'
        'money-check-alt'                     = 'f53d'
        'monument'                            = 'f5a6'
        'moon'                                = 'f186'
        'mortar-pestle'                       = 'f5a7'
        'mosque'                              = 'f678'
        'motorcycle'                          = 'f21c'
        'mountain'                            = 'f6fc'
        'mouse'                               = 'f8cc'
        'mouse-pointer'                       = 'f245'
        'mug-hot'                             = 'f7b6'
        'music'                               = 'f001'
        'network-wired'                       = 'f6ff'
        'neuter'                              = 'f22c'
        'newspaper'                           = 'f1ea'
        'not-equal'                           = 'f53e'
        'notes-medical'                       = 'f481'
        'object-group'                        = 'f247'
        'object-ungroup'                      = 'f248'
        'oil-can'                             = 'f613'
        'om'                                  = 'f679'
        'otter'                               = 'f700'
        'outdent'                             = 'f03b'
        'pager'                               = 'f815'
        'paint-brush'                         = 'f1fc'
        'paint-roller'                        = 'f5aa'
        'palette'                             = 'f53f'
        'pallet'                              = 'f482'
        'paper-plane'                         = 'f1d8'
        'paperclip'                           = 'f0c6'
        'parachute-box'                       = 'f4cd'
        'paragraph'                           = 'f1dd'
        'parking'                             = 'f540'
        'passport'                            = 'f5ab'
        'pastafarianism'                      = 'f67b'
        'paste'                               = 'f0ea'
        'pause'                               = 'f04c'
        'pause-circle'                        = 'f28b'
        'paw'                                 = 'f1b0'
        'peace'                               = 'f67c'
        'pen'                                 = 'f304'
        'pen-alt'                             = 'f305'
        'pen-fancy'                           = 'f5ac'
        'pen-nib'                             = 'f5ad'
        'pen-square'                          = 'f14b'
        'pencil-alt'                          = 'f303'
        'pencil-ruler'                        = 'f5ae'
        'people-arrows'                       = 'e068'
        'people-carry'                        = 'f4ce'
        'pepper-hot'                          = 'f816'
        'percent'                             = 'f295'
        'percentage'                          = 'f541'
        'person-booth'                        = 'f756'
        'phone'                               = 'f095'
        'phone-alt'                           = 'f879'
        'phone-slash'                         = 'f3dd'
        'phone-square'                        = 'f098'
        'phone-square-alt'                    = 'f87b'
        'phone-volume'                        = 'f2a0'
        'photo-video'                         = 'f87c'
        'piggy-bank'                          = 'f4d3'
        'pills'                               = 'f484'
        'pizza-slice'                         = 'f818'
        'place-of-worship'                    = 'f67f'
        'plane'                               = 'f072'
        'plane-arrival'                       = 'f5af'
        'plane-departure'                     = 'f5b0'
        'plane-slash'                         = 'e069'
        'play'                                = 'f04b'
        'play-circle'                         = 'f144'
        'plug'                                = 'f1e6'
        'plus'                                = 'f067'
        'plus-circle'                         = 'f055'
        'plus-square'                         = 'f0fe'
        'podcast'                             = 'f2ce'
        'poll'                                = 'f681'
        'poll-h'                              = 'f682'
        'poo'                                 = 'f2fe'
        'poo-storm'                           = 'f75a'
        'poop'                                = 'f619'
        'portrait'                            = 'f3e0'
        'pound-sign'                          = 'f154'
        'power-off'                           = 'f011'
        'pray'                                = 'f683'
        'praying-hands'                       = 'f684'
        'prescription'                        = 'f5b1'
        'prescription-bottle'                 = 'f485'
        'prescription-bottle-alt'             = 'f486'
        'print'                               = 'f02f'
        'procedures'                          = 'f487'
        'project-diagram'                     = 'f542'
        'pump-medical'                        = 'e06a'
        'pump-soap'                           = 'e06b'
        'puzzle-piece'                        = 'f12e'
        'qrcode'                              = 'f029'
        'question'                            = 'f128'
        'question-circle'                     = 'f059'
        'quidditch'                           = 'f458'
        'quote-left'                          = 'f10d'
        'quote-right'                         = 'f10e'
        'quran'                               = 'f687'
        'radiation'                           = 'f7b9'
        'radiation-alt'                       = 'f7ba'
        'rainbow'                             = 'f75b'
        'random'                              = 'f074'
        'receipt'                             = 'f543'
        'record-vinyl'                        = 'f8d9'
        'recycle'                             = 'f1b8'
        'redo'                                = 'f01e'
        'redo-alt'                            = 'f2f9'
        'registered'                          = 'f25d'
        'remove-format'                       = 'f87d'
        'reply'                               = 'f3e5'
        'reply-all'                           = 'f122'
        'republican'                          = 'f75e'
        'restroom'                            = 'f7bd'
        'retweet'                             = 'f079'
        'ribbon'                              = 'f4d6'
        'ring'                                = 'f70b'
        'road'                                = 'f018'
        'robot'                               = 'f544'
        'rocket'                              = 'f135'
        'route'                               = 'f4d7'
        'rss'                                 = 'f09e'
        'rss-square'                          = 'f143'
        'ruble-sign'                          = 'f158'
        'ruler'                               = 'f545'
        'ruler-combined'                      = 'f546'
        'ruler-horizontal'                    = 'f547'
        'ruler-vertical'                      = 'f548'
        'running'                             = 'f70c'
        'rupee-sign'                          = 'f156'
        'sad-cry'                             = 'f5b3'
        'sad-tear'                            = 'f5b4'
        'satellite'                           = 'f7bf'
        'satellite-dish'                      = 'f7c0'
        'save'                                = 'f0c7'
        'school'                              = 'f549'
        'screwdriver'                         = 'f54a'
        'scroll'                              = 'f70e'
        'sd-card'                             = 'f7c2'
        'search'                              = 'f002'
        'search-dollar'                       = 'f688'
        'search-location'                     = 'f689'
        'search-minus'                        = 'f010'
        'search-plus'                         = 'f00e'
        'seedling'                            = 'f4d8'
        'server'                              = 'f233'
        'shapes'                              = 'f61f'
        'share'                               = 'f064'
        'share-alt'                           = 'f1e0'
        'share-alt-square'                    = 'f1e1'
        'share-square'                        = 'f14d'
        'shekel-sign'                         = 'f20b'
        'shield-alt'                          = 'f3ed'
        'shield-virus'                        = 'e06c'
        'ship'                                = 'f21a'
        'shipping-fast'                       = 'f48b'
        'shoe-prints'                         = 'f54b'
        'shopping-bag'                        = 'f290'
        'shopping-basket'                     = 'f291'
        'shopping-cart'                       = 'f07a'
        'shower'                              = 'f2cc'
        'shuttle-van'                         = 'f5b6'
        'sign'                                = 'f4d9'
        'sign-in-alt'                         = 'f2f6'
        'sign-language'                       = 'f2a7'
        'sign-out-alt'                        = 'f2f5'
        'signal'                              = 'f012'
        'signature'                           = 'f5b7'
        'sim-card'                            = 'f7c4'
        'sink'                                = 'e06d'
        'sitemap'                             = 'f0e8'
        'skating'                             = 'f7c5'
        'skiing'                              = 'f7c9'
        'skiing-nordic'                       = 'f7ca'
        'skull'                               = 'f54c'
        'skull-crossbones'                    = 'f714'
        'slash'                               = 'f715'
        'sleigh'                              = 'f7cc'
        'sliders-h'                           = 'f1de'
        'smile'                               = 'f118'
        'smile-beam'                          = 'f5b8'
        'smile-wink'                          = 'f4da'
        'smog'                                = 'f75f'
        'smoking'                             = 'f48d'
        'smoking-ban'                         = 'f54d'
        'sms'                                 = 'f7cd'
        'snowboarding'                        = 'f7ce'
        'snowflake'                           = 'f2dc'
        'snowman'                             = 'f7d0'
        'snowplow'                            = 'f7d2'
        'soap'                                = 'e06e'
        'socks'                               = 'f696'
        'solar-panel'                         = 'f5ba'
        'sort'                                = 'f0dc'
        'sort-alpha-down'                     = 'f15d'
        'sort-alpha-down-alt'                 = 'f881'
        'sort-alpha-up'                       = 'f15e'
        'sort-alpha-up-alt'                   = 'f882'
        'sort-amount-down'                    = 'f160'
        'sort-amount-down-alt'                = 'f884'
        'sort-amount-up'                      = 'f161'
        'sort-amount-up-alt'                  = 'f885'
        'sort-down'                           = 'f0dd'
        'sort-numeric-down'                   = 'f162'
        'sort-numeric-down-alt'               = 'f886'
        'sort-numeric-up'                     = 'f163'
        'sort-numeric-up-alt'                 = 'f887'
        'sort-up'                             = 'f0de'
        'spa'                                 = 'f5bb'
        'space-shuttle'                       = 'f197'
        'spell-check'                         = 'f891'
        'spider'                              = 'f717'
        'spinner'                             = 'f110'
        'splotch'                             = 'f5bc'
        'spray-can'                           = 'f5bd'
        'square'                              = 'f0c8'
        'square-full'                         = 'f45c'
        'square-root-alt'                     = 'f698'
        'stamp'                               = 'f5bf'
        'star'                                = 'f005'
        'star-and-crescent'                   = 'f699'
        'star-half'                           = 'f089'
        'star-half-alt'                       = 'f5c0'
        'star-of-david'                       = 'f69a'
        'star-of-life'                        = 'f621'
        'step-backward'                       = 'f048'
        'step-forward'                        = 'f051'
        'stethoscope'                         = 'f0f1'
        'sticky-note'                         = 'f249'
        'stop'                                = 'f04d'
        'stop-circle'                         = 'f28d'
        'stopwatch'                           = 'f2f2'
        'stopwatch-20'                        = 'e06f'
        'store'                               = 'f54e'
        'store-alt'                           = 'f54f'
        'store-alt-slash'                     = 'e070'
        'store-slash'                         = 'e071'
        'stream'                              = 'f550'
        'street-view'                         = 'f21d'
        'strikethrough'                       = 'f0cc'
        'stroopwafel'                         = 'f551'
        'subscript'                           = 'f12c'
        'subway'                              = 'f239'
        'suitcase'                            = 'f0f2'
        'suitcase-rolling'                    = 'f5c1'
        'sun'                                 = 'f185'
        'superscript'                         = 'f12b'
        'surprise'                            = 'f5c2'
        'swatchbook'                          = 'f5c3'
        'swimmer'                             = 'f5c4'
        'swimming-pool'                       = 'f5c5'
        'synagogue'                           = 'f69b'
        'sync'                                = 'f021'
        'sync-alt'                            = 'f2f1'
        'syringe'                             = 'f48e'
        'table'                               = 'f0ce'
        'table-tennis'                        = 'f45d'
        'tablet'                              = 'f10a'
        'tablet-alt'                          = 'f3fa'
        'tablets'                             = 'f490'
        'tachometer-alt'                      = 'f3fd'
        'tag'                                 = 'f02b'
        'tags'                                = 'f02c'
        'tape'                                = 'f4db'
        'tasks'                               = 'f0ae'
        'taxi'                                = 'f1ba'
        'teeth'                               = 'f62e'
        'teeth-open'                          = 'f62f'
        'temperature-high'                    = 'f769'
        'temperature-low'                     = 'f76b'
        'tenge'                               = 'f7d7'
        'terminal'                            = 'f120'
        'text-height'                         = 'f034'
        'text-width'                          = 'f035'
        'th'                                  = 'f00a'
        'th-large'                            = 'f009'
        'th-list'                             = 'f00b'
        'theater-masks'                       = 'f630'
        'thermometer'                         = 'f491'
        'thermometer-empty'                   = 'f2cb'
        'thermometer-full'                    = 'f2c7'
        'thermometer-half'                    = 'f2c9'
        'thermometer-quarter'                 = 'f2ca'
        'thermometer-three-quarters'          = 'f2c8'
        'thumbs-down'                         = 'f165'
        'thumbs-up'                           = 'f164'
        'thumbtack'                           = 'f08d'
        'ticket-alt'                          = 'f3ff'
        'times'                               = 'f00d'
        'times-circle'                        = 'f057'
        'tint'                                = 'f043'
        'tint-slash'                          = 'f5c7'
        'tired'                               = 'f5c8'
        'toggle-off'                          = 'f204'
        'toggle-on'                           = 'f205'
        'toilet'                              = 'f7d8'
        'toilet-paper'                        = 'f71e'
        'toilet-paper-slash'                  = 'e072'
        'toolbox'                             = 'f552'
        'tools'                               = 'f7d9'
        'tooth'                               = 'f5c9'
        'torah'                               = 'f6a0'
        'torii-gate'                          = 'f6a1'
        'tractor'                             = 'f722'
        'trademark'                           = 'f25c'
        'traffic-light'                       = 'f637'
        'trailer'                             = 'e041'
        'train'                               = 'f238'
        'tram'                                = 'f7da'
        'transgender'                         = 'f224'
        'transgender-alt'                     = 'f225'
        'trash'                               = 'f1f8'
        'trash-alt'                           = 'f2ed'
        'trash-restore'                       = 'f829'
        'trash-restore-alt'                   = 'f82a'
        'tree'                                = 'f1bb'
        'trophy'                              = 'f091'
        'truck'                               = 'f0d1'
        'truck-loading'                       = 'f4de'
        'truck-monster'                       = 'f63b'
        'truck-moving'                        = 'f4df'
        'truck-pickup'                        = 'f63c'
        'tshirt'                              = 'f553'
        'tty'                                 = 'f1e4'
        'tv'                                  = 'f26c'
        'umbrella'                            = 'f0e9'
        'umbrella-beach'                      = 'f5ca'
        'underline'                           = 'f0cd'
        'undo'                                = 'f0e2'
        'undo-alt'                            = 'f2ea'
        'universal-access'                    = 'f29a'
        'university'                          = 'f19c'
        'unlink'                              = 'f127'
        'unlock'                              = 'f09c'
        'unlock-alt'                          = 'f13e'
        'upload'                              = 'f093'
        'user'                                = 'f007'
        'user-alt'                            = 'f406'
        'user-alt-slash'                      = 'f4fa'
        'user-astronaut'                      = 'f4fb'
        'user-check'                          = 'f4fc'
        'user-circle'                         = 'f2bd'
        'user-clock'                          = 'f4fd'
        'user-cog'                            = 'f4fe'
        'user-edit'                           = 'f4ff'
        'user-friends'                        = 'f500'
        'user-graduate'                       = 'f501'
        'user-injured'                        = 'f728'
        'user-lock'                           = 'f502'
        'user-md'                             = 'f0f0'
        'user-minus'                          = 'f503'
        'user-ninja'                          = 'f504'
        'user-nurse'                          = 'f82f'
        'user-plus'                           = 'f234'
        'user-secret'                         = 'f21b'
        'user-shield'                         = 'f505'
        'user-slash'                          = 'f506'
        'user-tag'                            = 'f507'
        'user-tie'                            = 'f508'
        'user-times'                          = 'f235'
        'users'                               = 'f0c0'
        'users-cog'                           = 'f509'
        'users-slash'                         = 'e073'
        'utensil-spoon'                       = 'f2e5'
        'utensils'                            = 'f2e7'
        'vector-square'                       = 'f5cb'
        'venus'                               = 'f221'
        'venus-double'                        = 'f226'
        'venus-mars'                          = 'f228'
        'vest'                                = 'e085'
        'vest-patches'                        = 'e086'
        'vial'                                = 'f492'
        'vials'                               = 'f493'
        'video'                               = 'f03d'
        'video-slash'                         = 'f4e2'
        'vihara'                              = 'f6a7'
        'virus'                               = 'e074'
        'virus-slash'                         = 'e075'
        'viruses'                             = 'e076'
        'voicemail'                           = 'f897'
        'volleyball-ball'                     = 'f45f'
        'volume-down'                         = 'f027'
        'volume-mute'                         = 'f6a9'
        'volume-off'                          = 'f026'
        'volume-up'                           = 'f028'
        'vote-yea'                            = 'f772'
        'vr-cardboard'                        = 'f729'
        'walking'                             = 'f554'
        'wallet'                              = 'f555'
        'warehouse'                           = 'f494'
        'water'                               = 'f773'
        'wave-square'                         = 'f83e'
        'weight'                              = 'f496'
        'weight-hanging'                      = 'f5cd'
        'wheelchair'                          = 'f193'
        'wifi'                                = 'f1eb'
        'wind'                                = 'f72e'
        'window-close'                        = 'f410'
        'window-maximize'                     = 'f2d0'
        'window-minimize'                     = 'f2d1'
        'window-restore'                      = 'f2d2'
        'wine-bottle'                         = 'f72f'
        'wine-glass'                          = 'f4e3'
        'wine-glass-alt'                      = 'f5ce'
        'won-sign'                            = 'f159'
        'wrench'                              = 'f0ad'
        'x-ray'                               = 'f497'
        'yen-sign'                            = 'f157'
        'yin-yang'                            = 'f6ad'
    }
    FontsMaterialIcon  = @(
        '3d-rotation'
        '500px'
        '8tracks'
        'account'
        'account-add'
        'account-box'
        'account-box-mail'
        'account-box-o'
        'account-box-phone'
        'account-calendar'
        'account-circle'
        'account-o'
        'accounts'
        'accounts-add'
        'accounts-alt'
        'accounts-list'
        'accounts-list-alt'
        'accounts-outline'
        'airline-seat-flat'
        'airline-seat-flat-angled'
        'airline-seat-individual-suite'
        'airline-seat-legroom-extra'
        'airline-seat-legroom-normal'
        'airline-seat-legroom-reduced'
        'airline-seat-recline-extra'
        'airline-seat-recline-normal'
        'airplane'
        'airplane-off'
        'airplay'
        'alarm'
        'alarm-check'
        'alarm-off'
        'alarm-plus'
        'alarm-snooze'
        'album'
        'alert-circle'
        'alert-circle-o'
        'alert-octagon'
        'alert-polygon'
        'alert-triangle'
        'amazon'
        'android'
        'android-alt'
        'apple'
        'apps'
        'archive'
        'arrow-left'
        'arrow-left-bottom'
        'arrow-merge'
        'arrow-missed'
        'arrow-right'
        'arrow-right-top'
        'arrows'
        'arrow-split'
        'aspect-ratio'
        'aspect-ratio-alt'
        'assignment'
        'assignment-account'
        'assignment-alert'
        'assignment-check'
        'assignment-o'
        'assignment-return'
        'assignment-returned'
        'attachment'
        'attachment-alt'
        'audio'
        'badge-check'
        'balance'
        'balance-wallet'
        'battery'
        'battery-alert'
        'battery-flash'
        'battery-unknown'
        'behance'
        'bike'
        'block'
        'block-alt'
        'blogger'
        'bluetooth'
        'bluetooth-connected'
        'bluetooth-off'
        'bluetooth-search'
        'bluetooth-setting'
        'blur'
        'blur-circular'
        'blur-linear'
        'blur-off'
        'boat'
        'book'
        'book-image'
        'bookmark'
        'bookmark-outline'
        'border-all'
        'border-bottom'
        'border-clear'
        'border-color'
        'border-horizontal'
        'border-inner'
        'border-left'
        'border-outer'
        'border-right'
        'border-style'
        'border-top'
        'border-vertical'
        'brightness-2'
        'brightness-3'
        'brightness-4'
        'brightness-5'
        'brightness-6'
        'brightness-7'
        'brightness-auto'
        'brightness-setting'
        'broken-image'
        'brush'
        'bug'
        'bus'
        'cake'
        'calendar'
        'calendar-alt'
        'calendar-check'
        'calendar-close'
        'calendar-note'
        'camera'
        'camera-add'
        'camera-alt'
        'camera-bw'
        'camera-front'
        'camera-mic'
        'camera-party-mode'
        'camera-rear'
        'camera-roll'
        'camera-switch'
        'car'
        'card'
        'card-alert'
        'card-giftcard'
        'card-membership'
        'card-off'
        'card-sd'
        'card-sim'
        'card-travel'
        'caret-down'
        'caret-down-circle'
        'caret-left'
        'caret-left-circle'
        'caret-right'
        'caret-right-circle'
        'caret-up'
        'caret-up-circle'
        'car-taxi'
        'car-wash'
        'case'
        'case-check'
        'case-download'
        'case-play'
        'cast'
        'cast-connected'
        'center-focus-strong'
        'center-focus-weak'
        'chart'
        'chart-donut'
        'check'
        'check-all'
        'check-circle'
        'check-circle-u'
        'check-square'
        'chevron-down'
        'chevron-left'
        'chevron-right'
        'chevron-up'
        'circle'
        'circle-o'
        'city'
        'city-alt'
        'close'
        'close-circle'
        'close-circle-o'
        'closed-caption'
        'cloud'
        'cloud-box'
        'cloud-circle'
        'cloud-done'
        'cloud-download'
        'cloud-off'
        'cloud-outline'
        'cloud-outline-alt'
        'cloud-upload'
        'cocktail'
        'code'
        'codepen'
        'code-setting'
        'code-smartphone'
        'coffee'
        'collection-bookmark'
        'collection-case-play'
        'collection-folder-image'
        'collection-image'
        'collection-image-o'
        'collection-item'
        'collection-item-1'
        'collection-item-2'
        'collection-item-3'
        'collection-item-4'
        'collection-item-5'
        'collection-item-6'
        'collection-item-7'
        'collection-item-8'
        'collection-item-9'
        'collection-item-9-plus'
        'collection-music'
        'collection-pdf'
        'collection-plus'
        'collection-speaker'
        'collection-text'
        'collection-video'
        'comment'
        'comment-alert'
        'comment-alt'
        'comment-alt-text'
        'comment-edit'
        'comment-image'
        'comment-list'
        'comment-more'
        'comment-outline'
        'comments'
        'comment-text'
        'comment-text-alt'
        'comment-video'
        'compare'
        'compass'
        'confirmation-number'
        'copy'
        'crop'
        'crop-16-9'
        'crop-3-2'
        'crop-5-4'
        'crop-7-5'
        'crop-din'
        'crop-free'
        'crop-landscape'
        'crop-portrait'
        'crop-square'
        'cutlery'
        'delete'
        'delicious'
        'desktop-mac'
        'desktop-windows'
        'developer-board'
        'device-hub'
        'devices'
        'devices-off'
        'dialpad'
        'disc-full'
        'disqus'
        'dns'
        'dock'
        'dot-circle'
        'dot-circle-alt'
        'download'
        'dribbble'
        'drink'
        'dropbox'
        'edit'
        'eject'
        'eject-alt'
        'email'
        'email-open'
        'equalizer'
        'evernote'
        'explicit'
        'exposure'
        'exposure-alt'
        'eye'
        'eyedropper'
        'eye-off'
        'face'
        'facebook'
        'facebook-box'
        'fast-forward'
        'fast-rewind'
        'favorite'
        'favorite-outline'
        'female'
        'file'
        'file-plus'
        'file-text'
        'filter-b-and-w'
        'filter-center-focus'
        'filter-frames'
        'filter-list'
        'filter-tilt-shift'
        'fire'
        'flag'
        'flare'
        'flash'
        'flash-auto'
        'flash-off'
        'flattr'
        'flickr'
        'flight-land'
        'flight-takeoff'
        'flip'
        'flip-to-back'
        'flip-to-front'
        'floppy'
        'flower'
        'flower-alt'
        'folder'
        'folder-outline'
        'folder-person'
        'folder-star'
        'folder-star-alt'
        'font'
        'format-align-center'
        'format-align-justify'
        'format-align-left'
        'format-align-right'
        'format-bold'
        'format-clear'
        'format-clear-all'
        'format-color-fill'
        'format-color-reset'
        'format-color-text'
        'format-indent-decrease'
        'format-indent-increase'
        'format-italic'
        'format-line-spacing'
        'format-list-bulleted'
        'format-list-numbered'
        'format-ltr'
        'format-rtl'
        'format-size'
        'format-strikethrough'
        'format-strikethrough-s'
        'format-subject'
        'format-underlined'
        'format-valign-bottom'
        'format-valign-center'
        'format-valign-top'
        'forward'
        'forward-10'
        'forward-30'
        'forward-5'
        'fullscreen'
        'fullscreen-alt'
        'fullscreen-exit'
        'functions'
        'gamepad'
        'gas-station'
        'gesture'
        'gif'
        'github'
        'github-alt'
        'github-box'
        'globe'
        'globe-alt'
        'globe-lock'
        'google'
        'google-drive'
        'google-earth'
        'google-glass'
        'google-maps'
        'google-old'
        'google-pages'
        'google-play'
        'google-plus'
        'google-plus-box'
        'gps'
        'gps-dot'
        'gps-off'
        'gradient'
        'graduation-cap'
        'grain'
        'graphic-eq'
        'grid'
        'grid-off'
        'group'
        'group-work'
        'hd'
        'hdr'
        'hdr-off'
        'hdr-strong'
        'hdr-weak'
        'headset'
        'headset-mic'
        'hearing'
        'help'
        'help-outline'
        'home'
        'hospital'
        'hospital-alt'
        'hotel'
        'hourglass'
        'hourglass-alt'
        'hourglass-outline'
        'hq'
        'http'
        'image'
        'image-alt'
        'image-o'
        'inbox'
        'info'
        'info-outline'
        'input-antenna'
        'input-composite'
        'input-hdmi'
        'input-power'
        'input-svideo'
        'instagram'
        'invert-colors'
        'invert-colors-off'
        'iridescent'
        'key'
        'keyboard'
        'keyboard-hide'
        'label'
        'label-alt'
        'label-alt-outline'
        'label-heart'
        'labels'
        'lamp'
        'landscape'
        'language-css3'
        'language-html5'
        'language-javascript'
        'language-python'
        'language-python-alt'
        'laptop'
        'laptop-chromebook'
        'laptop-mac'
        'lastfm'
        'layers'
        'layers-off'
        'leak'
        'leak-off'
        'library'
        'link'
        'linkedin'
        'linkedin-box'
        'lock'
        'lock-open'
        'lock-outline'
        'long-arrow-down'
        'long-arrow-left'
        'long-arrow-return'
        'long-arrow-right'
        'long-arrow-tab'
        'long-arrow-up'
        'looks'
        'loupe'
        'mail-reply'
        'mail-reply-all'
        'mail-send'
        'male'
        'male-alt'
        'male-female'
        'mall'
        'map'
        'markunread-mailbox'
        'memory'
        'menu'
        'mic'
        'mic-off'
        'mic-outline'
        'mic-setting'
        'minus'
        'minus-circle'
        'minus-circle-outline'
        'minus-square'
        'money'
        'money-box'
        'money-off'
        'mood'
        'mood-bad'
        'more'
        'more-vert'
        'mouse'
        'movie'
        'movie-alt'
        'n-1-square'
        'n-2-square'
        'n-3-square'
        'n-4-square'
        'n-5-square'
        'n-6-square'
        'nature'
        'nature-people'
        'navigation'
        'neg-1'
        'neg-2'
        'network'
        'network-alert'
        'network-locked'
        'network-off'
        'network-outline'
        'network-setting'
        'nfc'
        'notifications'
        'notifications-active'
        'notifications-add'
        'notifications-none'
        'notifications-off'
        'notifications-paused'
        'odnoklassniki'
        'open-in-browser'
        'open-in-new'
        'outlook'
        'palette'
        'panorama-horizontal'
        'panorama-vertical'
        'panorama-wide-angle'
        'parking'
        'pause'
        'pause-circle'
        'pause-circle-outline'
        'paypal'
        'paypal-alt'
        'phone'
        'phone-bluetooth'
        'phone-end'
        'phone-forwarded'
        'phone-in-talk'
        'phone-locked'
        'phone-missed'
        'phone-msg'
        'phone-paused'
        'phone-ring'
        'phone-setting'
        'phone-sip'
        'photo-size-select-large'
        'photo-size-select-small'
        'picture-in-picture'
        'pin'
        'pin-account'
        'pin-assistant'
        'pin-drop'
        'pin-help'
        'pin-off'
        'pinterest'
        'pinterest-box'
        'pizza'
        'plaster'
        'play'
        'play-circle'
        'play-circle-outline'
        'play-for-work'
        'playlist-audio'
        'playlist-plus'
        'playstation'
        'plus'
        'plus-1'
        'plus-2'
        'plus-circle'
        'plus-circle-o'
        'plus-circle-o-duplicate'
        'plus-square'
        'pocket'
        'polymer'
        'portable-wifi'
        'portable-wifi-changes'
        'portable-wifi-off'
        'power'
        'power-input'
        'power-setting'
        'present-to-all'
        'print'
        'puzzle-piece'
        'quote'
        'radio'
        'railway'
        'reader'
        'receipt'
        'reddit'
        'redo'
        'refresh'
        'refresh-alt'
        'refresh-sync'
        'refresh-sync-alert'
        'refresh-sync-off'
        'remote-control'
        'remote-control-alt'
        'repeat'
        'repeat-one'
        'replay'
        'replay-10'
        'replay-30'
        'replay-5'
        'roller'
        'rotate-ccw'
        'rotate-cw'
        'rotate-left'
        'rotate-right'
        'router'
        'rss'
        'ruler'
        'run'
        'satellite'
        'scanner'
        'scissors'
        'screen-rotation'
        'screen-rotation-lock'
        'search'
        'search-for'
        'search-in-file'
        'search-in-page'
        'search-replace'
        'seat'
        'sec-10'
        'sec-3'
        'select-all'
        'settings'
        'settings-square'
        'shape'
        'share'
        'shield-check'
        'shield-security'
        'shopping-basket'
        'shopping-cart'
        'shopping-cart-plus'
        'shuffle'
        'sign-in'
        'skip-next'
        'skip-previous'
        'skype'
        'slideshare'
        'slideshow'
        'smartphone'
        'smartphone-android'
        'smartphone-download'
        'smartphone-erase'
        'smartphone-info'
        'smartphone-iphone'
        'smartphone-landscape'
        'smartphone-landscape-lock'
        'smartphone-lock'
        'smartphone-portrait-lock'
        'smartphone-ring'
        'smartphone-setting'
        'smartphone-setup'
        'sort-amount-asc'
        'sort-amount-desc'
        'sort-asc'
        'sort-desc'
        'soundcloud'
        'space-bar'
        'speaker'
        'spellcheck'
        'spinner'
        'square-down'
        'square-o'
        'square-right'
        'stackoverflow'
        'star'
        'star-circle'
        'star-half'
        'star-outline'
        'steam'
        'steam-square'
        'stop'
        'storage'
        'store'
        'store-24'
        'subway'
        'sun'
        'surround-sound'
        'swap'
        'swap-alt'
        'swap-vertical'
        'swap-vertical-circle'
        'tab'
        'tablet'
        'tablet-android'
        'tablet-mac'
        'tab-unselected'
        'tag'
        'tag-close'
        'tag-more'
        'tap-and-play'
        'text-format'
        'texture'
        'thumb-down'
        'thumb-up'
        'thumb-up-down'
        'ticket-star'
        'time'
        'time-countdown'
        'time-interval'
        'timer'
        'time-restore'
        'time-restore-setting'
        'timer-off'
        'toll'
        'tonality'
        'toys'
        'traffic'
        'transform'
        'translate'
        'trending-down'
        'trending-flat'
        'trending-up'
        'triangle-down'
        'triangle-up'
        'truck'
        'tumblr'
        'tune'
        'turning-sign'
        'tv'
        'tv-alt-play'
        'tv-list'
        'tv-play'
        'twitch'
        'twitter'
        'twitter-box'
        'undo'
        'unfold-less'
        'unfold-more'
        'ungroup'
        'upload'
        'usb'
        'vibration'
        'videocam'
        'videocam-off'
        'videocam-switch'
        'view-agenda'
        'view-array'
        'view-carousel'
        'view-column'
        'view-comfy'
        'view-compact'
        'view-dashboard'
        'view-day'
        'view-headline'
        'view-list'
        'view-list-alt'
        'view-module'
        'view-quilt'
        'view-stream'
        'view-subtitles'
        'view-toc'
        'view-web'
        'view-week'
        'vignette'
        'vimeo'
        'vk'
        'voicemail'
        'volume-down'
        'volume-mute'
        'volume-off'
        'volume-up'
        'walk'
        'wallpaper'
        'washing-machine'
        'watch'
        'wb-auto'
        'whatsapp'
        'widgets'
        'wifi'
        'wifi-alt'
        'wifi-alt-2'
        'wifi-info'
        'wifi-lock'
        'wifi-off'
        'wifi-outline'
        'wikipedia'
        'window-maximize'
        'window-minimize'
        'window-restore'
        'windows'
        'wrap-text'
        'wrench'
        'xbox'
        'yahoo'
        'youtube'
        'youtube-play'
        'zero'
        'zoom-in'
        'zoom-out'
    )
    FontsSimple        = @(
        '1001tracklists'
        '1password'
        '3m'
        '42'
        '4d'
        '500px'
        'a-frame'
        'abbrobotstudio'
        'abbvie'
        'abletonlive'
        'about-dot-me'
        'abstract'
        'academia'
        'accenture'
        'acclaim'
        'accusoft'
        'acer'
        'acm'
        'actigraph'
        'activision'
        'adafruit'
        'adblock'
        'adblockplus'
        'addthis'
        'adguard'
        'adobe'
        'adobeacrobatreader'
        'adobeaftereffects'
        'adobeaudition'
        'adobecreativecloud'
        'adobedreamweaver'
        'adobefonts'
        'adobeillustrator'
        'adobeindesign'
        'adobelightroom'
        'adobelightroomclassic'
        'adobephonegap'
        'adobephotoshop'
        'adobepremierepro'
        'adobexd'
        'adonisjs'
        'aerlingus'
        'aeroflot'
        'aeromexico'
        'aerospike'
        'affinity'
        'affinitydesigner'
        'affinityphoto'
        'affinitypublisher'
        'aidungeon'
        'aiohttp'
        'aiqfome'
        'airasia'
        'airbnb'
        'airbus'
        'aircall'
        'aircanada'
        'airchina'
        'airfrance'
        'airplayaudio'
        'airplayvideo'
        'airtable'
        'alacritty'
        'alfaromeo'
        'algolia'
        'alibaba-dot-com'
        'alibabacloud'
        'aliexpress'
        'alipay'
        'alitalia'
        'alliedmodders'
        'allocine'
        'alltrails'
        'alpinedotjs'
        'alpinelinux'
        'altiumdesigner'
        'amazon'
        'amazonalexa'
        'amazonaws'
        'amazondynamodb'
        'amazonfiretv'
        'amazonlumberyard'
        'amazonpay'
        'amazonprime'
        'amazons3'
        'amd'
        'americanairlines'
        'americanexpress'
        'amp'
        'amul'
        'ana'
        'anaconda'
        'analogue'
        'anchor'
        'andela'
        'android'
        'androidauto'
        'androidstudio'
        'angellist'
        'angular'
        'angularjs'
        'angularuniversal'
        'anilist'
        'ansible'
        'ansys'
        'antdesign'
        'antena3'
        'anydesk'
        'aol'
        'apache'
        'apacheairflow'
        'apacheant'
        'apachecassandra'
        'apachecloudstack'
        'apachecordova'
        'apachedruid'
        'apacheecharts'
        'apacheflink'
        'apachegroovy'
        'apachehive'
        'apachejmeter'
        'apachekafka'
        'apachekylin'
        'apachemaven'
        'apachenetbeanside'
        'apacheopenoffice'
        'apachepulsar'
        'apacherocketmq'
        'apachesolr'
        'apachespark'
        'apachetomcat'
        'aparat'
        'apollographql'
        'apostrophe'
        'apple'
        'applearcade'
        'applemusic'
        'applepay'
        'applepodcasts'
        'appletv'
        'appsignal'
        'appstore'
        'appveyor'
        'aral'
        'arangodb'
        'archicad'
        'archiveofourown'
        'archlinux'
        'ardour'
        'arduino'
        'arkecosystem'
        'arlo'
        'artixlinux'
        'artstation'
        'arxiv'
        'asana'
        'asciidoctor'
        'asciinema'
        'aseprite'
        'askfm'
        'askubuntu'
        'assemblyscript'
        'asus'
        'at-and-t'
        'atari'
        'atlassian'
        'atom'
        'audacity'
        'audi'
        'audible'
        'audio-technica'
        'audioboom'
        'audiomack'
        'aurelia'
        'auth0'
        'authy'
        'autodesk'
        'autohotkey'
        'automatic'
        'automattic'
        'autotask'
        'aventrix'
        'awesomelists'
        'awesomewm'
        'awsamplify'
        'azureartifacts'
        'azuredataexplorer'
        'azuredevops'
        'azurefunctions'
        'azurepipelines'
        'b-and-rautomation'
        'babel'
        'badgr'
        'badoo'
        'baidu'
        'bamboo'
        'bancontact'
        'bandcamp'
        'bandlab'
        'bandsintown'
        'bankofamerica'
        'barclays'
        'baremetrics'
        'basecamp'
        'bata'
        'bathasu'
        'battle-dot-net'
        'bbc'
        'bbciplayer'
        'beatport'
        'beats'
        'beatsbydre'
        'behance'
        'beijingsubway'
        'bentley'
        'betfair'
        'bigbasket'
        'bigcartel'
        'bigcommerce'
        'bilibili'
        'bing'
        'bit'
        'bitbucket'
        'bitcoin'
        'bitcoincash'
        'bitcoinsv'
        'bitdefender'
        'bitly'
        'bitrise'
        'bitwarden'
        'bitwig'
        'blackberry'
        'blazemeter'
        'blazor'
        'blender'
        'blockchain-dot-com'
        'blogger'
        'bloglovin'
        'blueprint'
        'bluetooth'
        'bmcsoftware'
        'bmw'
        'boeing'
        'bookbub'
        'bookmeter'
        'bookstack'
        'boost'
        'bootstrap'
        'bosch'
        'bose'
        'bower'
        'box'
        'brand-dot-ai'
        'brandfolder'
        'brave'
        'breaker'
        'britishairways'
        'broadcom'
        'bt'
        'buddy'
        'buefy'
        'buffer'
        'bugatti'
        'bugcrowd'
        'bugsnag'
        'buildkite'
        'bulma'
        'bunq'
        'buymeacoffee'
        'buzzfeed'
        'byte'
        'c'
        'cachet'
        'cairometro'
        'cakephp'
        'campaignmonitor'
        'canonical'
        'canva'
        'capacitor'
        'carthrottle'
        'carto'
        'cashapp'
        'castbox'
        'castorama'
        'castro'
        'caterpillar'
        'cbs'
        'cdprojekt'
        'celery'
        'centos'
        'ceph'
        'cesium'
        'cevo'
        'chai'
        'chainlink'
        'chakraui'
        'chart-dot-js'
        'chartmogul'
        'chase'
        'chatbot'
        'checkio'
        'checkmarx'
        'chef'
        'chevrolet'
        'chinaeasternairlines'
        'chinasouthernairlines'
        'chocolatey'
        'chrysler'
        'chupachups'
        'cinema4d'
        'circle'
        'circleci'
        'cirrusci'
        'cisco'
        'citrix'
        'citroen'
        'civicrm'
        'ckeditor4'
        'claris'
        'clickup'
        'clion'
        'cliqz'
        'clockify'
        'clojure'
        'cloud66'
        'cloudbees'
        'cloudcannon'
        'cloudera'
        'cloudflare'
        'cloudsmith'
        'cloudways'
        'clubhouse'
        'clyp'
        'cmake'
        'cnn'
        'co-op'
        'cockroachlabs'
        'cocoapods'
        'cocos'
        'coda'
        'codacy'
        'codeberg'
        'codecademy'
        'codeceptjs'
        'codechef'
        'codeclimate'
        'codecov'
        'codefactor'
        'codeforces'
        'codeigniter'
        'codemagic'
        'codemirror'
        'codenewbie'
        'codepen'
        'codeproject'
        'codersrank'
        'coderwall'
        'codesandbox'
        'codeship'
        'codewars'
        'codingame'
        'codingninjas'
        'codio'
        'coffeescript'
        'cognizant'
        'coinbase'
        'commerzbank'
        'commonworkflowlanguage'
        'composer'
        'compropago'
        'concourse'
        'conda-forge'
        'conekta'
        'confluence'
        'consul'
        'contactlesspayment'
        'contentful'
        'convertio'
        'cookiecutter'
        'coronaengine'
        'coronarenderer'
        'corsair'
        'couchbase'
        'counter-strike'
        'countingworkspro'
        'coursera'
        'coveralls'
        'cpanel'
        'cplusplus'
        'craftcms'
        'creativecommons'
        'crehana'
        'crowdin'
        'crowdsource'
        'crunchbase'
        'crunchyroll'
        'cryengine'
        'crystal'
        'csharp'
        'css3'
        'csswizardry'
        'cucumber'
        'curl'
        'curseforge'
        'cycling74'
        'cypress'
        'd-wavesystems'
        'd3-dot-js'
        'dacia'
        'daf'
        'dailymotion'
        'daimler'
        'darkreader'
        'dart'
        'daserste'
        'dash'
        'dashlane'
        'dassaultsystemes'
        'databricks'
        'datacamp'
        'datadog'
        'datastax'
        'dataversioncontrol'
        'datocms'
        'datto'
        'dazn'
        'dblp'
        'dcentertainment'
        'debian'
        'deepin'
        'deepnote'
        'deezer'
        'delicious'
        'deliveroo'
        'dell'
        'delonghi'
        'delphi'
        'delta'
        'deno'
        'dependabot'
        'derspiegel'
        'designernews'
        'deutschebahn'
        'deutschebank'
        'dev-dot-to'
        'deviantart'
        'devpost'
        'devrant'
        'dgraph'
        'dhl'
        'diagrams-dot-net'
        'dialogflow'
        'diaspora'
        'digg'
        'digi-keyelectronics'
        'digitalocean'
        'dior'
        'directus'
        'discogs'
        'discord'
        'discourse'
        'discover'
        'disqus'
        'disroot'
        'django'
        'dlna'
        'docker'
        'docusign'
        'dogecoin'
        'dolby'
        'doordash'
        'dot-net'
        'douban'
        'draugiem-dot-lv'
        'dribbble'
        'drone'
        'drooble'
        'dropbox'
        'drupal'
        'dsautomobiles'
        'dtube'
        'duckduckgo'
        'dunked'
        'duolingo'
        'dwm'
        'dynamics365'
        'dynatrace'
        'ea'
        'eagle'
        'easyjet'
        'ebay'
        'eclipseche'
        'eclipseide'
        'eclipsejetty'
        'eclipsemosquitto'
        'eclipsevert-dot-x'
        'editorconfig'
        'edx'
        'egghead'
        'egnyte'
        'eightsleep'
        'elastic'
        'elasticcloud'
        'elasticsearch'
        'elasticstack'
        'electron'
        'element'
        'elementary'
        'eleventy'
        'elixir'
        'eljueves'
        'ello'
        'elm'
        'elsevier'
        'embarcadero'
        'ember-dot-js'
        'emby'
        'emirates'
        'emlakjet'
        'empirekred'
        'enpass'
        'envato'
        'epel'
        'epicgames'
        'epson'
        'erlang'
        'esea'
        'eslgaming'
        'eslint'
        'esphome'
        'espressif'
        'ethereum'
        'ethiopianairlines'
        'etihadairways'
        'etsy'
        'eventbrite'
        'eventstore'
        'evernote'
        'everplaces'
        'evry'
        'exercism'
        'expensify'
        'expertsexchange'
        'expo'
        'express'
        'eyeem'
        'f-droid'
        'f-secure'
        'facebook'
        'facebookgaming'
        'facebooklive'
        'faceit'
        'facepunch'
        'falcon'
        'fampay'
        'fandango'
        'fandom'
        'farfetch'
        'fastapi'
        'fastify'
        'fastlane'
        'fastly'
        'fathom'
        'favro'
        'feathub'
        'fedex'
        'fedora'
        'fedramp'
        'feedly'
        'ferrari'
        'ferrarin-dot-v-dot'
        'ffmpeg'
        'fiat'
        'fidoalliance'
        'fifa'
        'figma'
        'figshare'
        'fila'
        'files'
        'filezilla'
        'fing'
        'firebase'
        'firefox'
        'firefoxbrowser'
        'first'
        'fitbit'
        'fite'
        'fiverr'
        'flask'
        'flathub'
        'flattr'
        'flickr'
        'flipboard'
        'flipkart'
        'floatplane'
        'flood'
        'fluentd'
        'flutter'
        'fmod'
        'fnac'
        'folium'
        'fontawesome'
        'fontbase'
        'foodpanda'
        'ford'
        'forestry'
        'formstack'
        'fortinet'
        'fortran'
        'fossa'
        'fossilscm'
        'foursquare'
        'foxtel'
        'fozzy'
        'framer'
        'fraunhofer-gesellschaft'
        'freebsd'
        'freecodecamp'
        'freedesktop-dot-org'
        'freelancer'
        'freenas'
        'fujifilm'
        'fujitsu'
        'furaffinity'
        'furrynetwork'
        'futurelearn'
        'g2a'
        'gamejolt'
        'garmin'
        'gatling'
        'gatsby'
        'gauges'
        'geeksforgeeks'
        'generalelectric'
        'generalmotors'
        'genius'
        'gentoo'
        'geocaching'
        'gerrit'
        'ghost'
        'ghostery'
        'gimp'
        'giphy'
        'git'
        'gitbook'
        'gitea'
        'gitee'
        'gitextensions'
        'github'
        'githubactions'
        'githubsponsors'
        'gitkraken'
        'gitlab'
        'gitlfs'
        'gitpod'
        'gitter'
        'glassdoor'
        'glitch'
        'gmail'
        'gnome'
        'gnu'
        'gnubash'
        'gnuemacs'
        'gnuicecat'
        'gnuprivacyguard'
        'gnusocial'
        'go'
        'godotengine'
        'gofundme'
        'gog-dot-com'
        'goldenline'
        'goodreads'
        'google'
        'googleads'
        'googleadsense'
        'googleanalytics'
        'googleassistant'
        'googlecalendar'
        'googlecardboard'
        'googlecast'
        'googlechat'
        'googlechrome'
        'googleclassroom'
        'googlecloud'
        'googlecolab'
        'googledomains'
        'googledrive'
        'googleearth'
        'googlefit'
        'googlefonts'
        'googlehangouts'
        'googlekeep'
        'googlelens'
        'googlemaps'
        'googlemeet'
        'googlemessages'
        'googlemybusiness'
        'googlenearby'
        'googlenews'
        'googleoptimize'
        'googlepay'
        'googlephotos'
        'googleplay'
        'googlepodcasts'
        'googlescholar'
        'googlesearchconsole'
        'googlesheets'
        'googlestreetview'
        'googletagmanager'
        'googletranslate'
        'gotomeeting'
        'gov-dot-uk'
        'gradle'
        'grafana'
        'grammarly'
        'graphcool'
        'graphql'
        'grav'
        'gravatar'
        'graylog'
        'greensock'
        'griddotai'
        'gridsome'
        'groupon'
        'grubhub'
        'grunt'
        'guangzhoumetro'
        'gulp'
        'gumroad'
        'gumtree'
        'gutenberg'
        'habr'
        'hackaday'
        'hackclub'
        'hackerearth'
        'hackerone'
        'hackerrank'
        'hackhands'
        'hackster'
        'hackthebox'
        'handshake'
        'handshake_protocol'
        'happycow'
        'harbor'
        'hashnode'
        'haskell'
        'hasura'
        'hatenabookmark'
        'haveibeenpwned'
        'haxe'
        'hbo'
        'hcl'
        'headspace'
        'hellofresh'
        'hellyhansen'
        'helm'
        'helpdesk'
        'here'
        'heroku'
        'hexo'
        'hey'
        'hibernate'
        'highly'
        'hilton'
        'hipchat'
        'hitachi'
        'hive'
        'hive_blockchain'
        'hockeyapp'
        'homeadvisor'
        'homeassistant'
        'homeassistantcommunitystore'
        'homebrew'
        'homebridge'
        'homify'
        'honda'
        'hootsuite'
        'hoppscotch'
        'hotels-dot-com'
        'hotjar'
        'houdini'
        'houzz'
        'hp'
        'html5'
        'htmlacademy'
        'huawei'
        'hubspot'
        'hugo'
        'hulu'
        'humblebundle'
        'hungryjacks'
        'hurriyetemlak'
        'husqvarna'
        'hyper'
        'hyperledger'
        'hypothesis'
        'hyundai'
        'iata'
        'ibeacon'
        'ibm'
        'ibmwatson'
        'icinga'
        'icloud'
        'icomoon'
        'icon'
        'iconfinder'
        'iconify'
        'iconjar'
        'icq'
        'ideal'
        'ieee'
        'ifixit'
        'ifood'
        'ifttt'
        'iheartradio'
        'ikea'
        'imagej'
        'imdb'
        'imgur'
        'immer'
        'imou'
        'indeed'
        'infiniti'
        'influxdb'
        'informatica'
        'infosys'
        'ingress'
        'inkscape'
        'insomnia'
        'instacart'
        'instagram'
        'instapaper'
        'instructables'
        'integromat'
        'intel'
        'intellijidea'
        'intercom'
        'internetarchive'
        'internetexplorer'
        'intigriti'
        'invision'
        'invoiceninja'
        'iobroker'
        'ionic'
        'ios'
        'iota'
        'ipfs'
        'issuu'
        'itch-dot-io'
        'itunes'
        'iveco'
        'jabber'
        'jaguar'
        'jamboard'
        'jameson'
        'jamstack'
        'jasmine'
        'java'
        'javascript'
        'jbl'
        'jcb'
        'jeep'
        'jekyll'
        'jellyfin'
        'jenkins'
        'jenkinsx'
        'jest'
        'jet'
        'jetbrains'
        'jfrog'
        'jfrogbintray'
        'jinja'
        'jira'
        'jirasoftware'
        'jitsi'
        'johndeere'
        'joomla'
        'jpeg'
        'jquery'
        'jrgroup'
        'jsdelivr'
        'jsfiddle'
        'json'
        'jsonwebtokens'
        'jss'
        'julia'
        'junipernetworks'
        'junit5'
        'jupyter'
        'justeat'
        'justgiving'
        'kaggle'
        'kahoot'
        'kaios'
        'kakao'
        'kakaotalk'
        'kalilinux'
        'karlsruherverkehrsverbund'
        'kasasmart'
        'kashflow'
        'kaspersky'
        'katacoda'
        'katana'
        'kde'
        'kdenlive'
        'keepassxc'
        'kentico'
        'keras'
        'keybase'
        'keycdn'
        'khanacademy'
        'khronosgroup'
        'kia'
        'kibana'
        'kickstarter'
        'kik'
        'kirby'
        'kitsu'
        'klarna'
        'klm'
        'klook'
        'klout'
        'knowledgebase'
        'known'
        'ko-fi'
        'kodi'
        'koding'
        'kofax'
        'komoot'
        'kongregate'
        'kotlin'
        'krita'
        'ktm'
        'kubernetes'
        'kubuntu'
        'kyocera'
        'labview'
        'lada'
        'lamborghini'
        'landrover'
        'laragon'
        'laravel'
        'laravelhorizon'
        'laravelnova'
        'last-dot-fm'
        'lastpass'
        'latex'
        'launchpad'
        'lbry'
        'leaflet'
        'leanpub'
        'leetcode'
        'lenovo'
        'less'
        'letsencrypt'
        'letterboxd'
        'lg'
        'lgtm'
        'liberapay'
        'libraries-dot-io'
        'librarything'
        'libreoffice'
        'libuv'
        'lichess'
        'lifx'
        'lighthouse'
        'line'
        'lineageos'
        'linewebtoon'
        'linkedin'
        'linktree'
        'linode'
        'linux'
        'linuxcontainers'
        'linuxfoundation'
        'linuxmint'
        'lionair'
        'lit'
        'litecoin'
        'livechat'
        'livejournal'
        'livestream'
        'llvm'
        'lmms'
        'logitech'
        'logmein'
        'logstash'
        'looker'
        'loom'
        'loop'
        'lospec'
        'lotpolishairlines'
        'lua'
        'lubuntu'
        'lufthansa'
        'lumen'
        'lydia'
        'lyft'
        'maas'
        'macos'
        'macys'
        'magento'
        'magisk'
        'mail-dot-ru'
        'mailchimp'
        'majorleaguehacking'
        'makerbot'
        'man'
        'manageiq'
        'manjaro'
        'mapbox'
        'mariadb'
        'mariadbfoundation'
        'markdown'
        'marketo'
        'marriott'
        'maserati'
        'mastercard'
        'mastercomfig'
        'mastodon'
        'material-ui'
        'materialdesign'
        'materialdesignicons'
        'mathworks'
        'matomo'
        'matrix'
        'mattermost'
        'matternet'
        'max-planck-gesellschaft'
        'max'
        'maytag'
        'mazda'
        'mcafee'
        'mcdonalds'
        'mclaren'
        'mdnwebdocs'
        'mediafire'
        'mediatek'
        'mediatemple'
        'medium'
        'meetup'
        'mega'
        'mendeley'
        'mercedes'
        'mercurial'
        'messenger'
        'metabase'
        'metafilter'
        'meteor'
        'metro'
        'metrodelaciudaddemexico'
        'metrodemadrid'
        'metrodeparis'
        'mewe'
        'micro-dot-blog'
        'microbit'
        'microgenetics'
        'micropython'
        'microsoft'
        'microsoftacademic'
        'microsoftaccess'
        'microsoftazure'
        'microsoftedge'
        'microsoftexcel'
        'microsoftexchange'
        'microsoftoffice'
        'microsoftonedrive'
        'microsoftonenote'
        'microsoftoutlook'
        'microsoftpowerpoint'
        'microsoftsharepoint'
        'microsoftsqlserver'
        'microsoftteams'
        'microsoftvisio'
        'microsoftword'
        'microstrategy'
        'midi'
        'minds'
        'minecraft'
        'minetest'
        'mini'
        'minutemailer'
        'miro'
        'mitsubishi'
        'mix'
        'mixcloud'
        'mobx-state-tree'
        'mobx'
        'mocha'
        'modx'
        'mojangstudios'
        'moleculer'
        'momenteo'
        'monero'
        'mongodb'
        'monkeytie'
        'monogram'
        'monster'
        'monzo'
        'moo'
        'moscowmetro'
        'motorola'
        'mozilla'
        'mta'
        'mtr'
        'mumble'
        'musescore'
        'musicbrainz'
        'mxlinux'
        'myanimelist'
        'myob'
        'myspace'
        'mysql'
        'n26'
        'namebase'
        'namecheap'
        'nano'
        'nasa'
        'nationalgrid'
        'nativescript'
        'naver'
        'nba'
        'nbb'
        'ndr'
        'nec'
        'neo4j'
        'neovim'
        'nestjs'
        'netapp'
        'netflix'
        'netlify'
        'newjapanpro-wrestling'
        'newrelic'
        'newyorktimes'
        'next-dot-js'
        'nextcloud'
        'nextdoor'
        'nfc'
        'nginx'
        'ngrok'
        'niconico'
        'nim'
        'nintendo'
        'nintendo3ds'
        'nintendogamecube'
        'nintendonetwork'
        'nintendoswitch'
        'nissan'
        'nixos'
        'node-dot-js'
        'node-red'
        'nodemon'
        'nokia'
        'norwegian'
        'notepadplusplus'
        'notion'
        'notist'
        'nounproject'
        'npm'
        'nrwl'
        'nubank'
        'nucleo'
        'nuget'
        'nuke'
        'numba'
        'numpy'
        'nutanix'
        'nuxt-dot-js'
        'nvidia'
        'nx'
        'observable'
        'obsstudio'
        'ocaml'
        'octave'
        'octopusdeploy'
        'oculus'
        'odnoklassniki'
        'odysee'
        'okcupid'
        'okta'
        'oneplus'
        'onlyfans'
        'onnx'
        'onstar'
        'opel'
        'openaccess'
        'openai'
        'openaigym'
        'openapiinitiative'
        'openbadges'
        'openbsd'
        'openbugbounty'
        'opencollective'
        'opencontainersinitiative'
        'opencv'
        'openfaas'
        'opengl'
        'openid'
        'openlayers'
        'opennebula'
        'opensourceinitiative'
        'openssl'
        'openstack'
        'openstreetmap'
        'opensuse'
        'openvpn'
        'openwrt'
        'opera'
        'opnsense'
        'opsgenie'
        'opslevel'
        'oracle'
        'orcid'
        'org'
        'origin'
        'osano'
        'oshkosh'
        'osmc'
        'osu'
        'overcast'
        'overleaf'
        'ovh'
        'owasp'
        'oxygen'
        'oyo'
        'p5-dot-js'
        'packagist'
        'pagekit'
        'pagerduty'
        'pagespeedinsights'
        'pagseguro'
        'palantir'
        'paloaltosoftware'
        'pandas'
        'pandora'
        'pantheon'
        'paritysubstrate'
        'parse-dot-ly'
        'passport'
        'pastebin'
        'patreon'
        'payoneer'
        'paypal'
        'paytm'
        'pcgamingwiki'
        'peertube'
        'pegasusairlines'
        'pelican'
        'peloton'
        'pepsi'
        'perforce'
        'periscope'
        'perl'
        'peugeot'
        'pexels'
        'pfsense'
        'phabricator'
        'philipshue'
        'phonepe'
        'photobucket'
        'photocrowd'
        'photopea'
        'php'
        'phpstorm'
        'pi-hole'
        'picarto-dot-tv'
        'picnic'
        'picpay'
        'pimcore'
        'pinboard'
        'pingdom'
        'pingup'
        'pinterest'
        'pioneerdj'
        'pivotaltracker'
        'piwigo'
        'pixabay'
        'pixiv'
        'pjsip'
        'planet'
        'plangrid'
        'platzi'
        'playcanvas'
        'player-dot-me'
        'playerfm'
        'playstation'
        'playstation2'
        'playstation3'
        'playstation4'
        'playstation5'
        'playstationvita'
        'pleroma'
        'plesk'
        'plex'
        'plotly'
        'pluralsight'
        'plurk'
        'pluscodes'
        'pm2'
        'pnpm'
        'pocket'
        'pocketcasts'
        'podcastaddict'
        'podman'
        'pointy'
        'pokemon'
        'poly'
        'polymerproject'
        'pop_os'
        'porsche'
        'postcss'
        'postgresql'
        'postman'
        'postmates'
        'powerbi'
        'powers'
        'powershell'
        'pr-dot-co'
        'pre-commit'
        'premierleague'
        'prestashop'
        'presto'
        'prettier'
        'prezi'
        'prime'
        'primevideo'
        'prisma'
        'prismic'
        'privateinternetaccess'
        'probot'
        'processingfoundation'
        'processwire'
        'producthunt'
        'progate'
        'progress'
        'prometheus'
        'prosieben'
        'proto-dot-io'
        'protocols-dot-io'
        'protondb'
        'protonmail'
        'protonvpn'
        'protools'
        'proxmox'
        'publons'
        'pubmed'
        'pug'
        'puppet'
        'puppeteer'
        'purescript'
        'purgecss'
        'purism'
        'pusher'
        'pycharm'
        'pypi'
        'pypy'
        'python'
        'pytorch'
        'pytorchlightning'
        'pyup'
        'qantas'
        'qatarairways'
        'qemu'
        'qgis'
        'qi'
        'qiita'
        'qiskit'
        'qiwi'
        'qt'
        'qualcomm'
        'qualtrics'
        'quantcast'
        'quantconnect'
        'quantopian'
        'quarkus'
        'quasar'
        'qubesos'
        'quest'
        'quickbooks'
        'quicktime'
        'quip'
        'quora'
        'qwiklabs'
        'qzone'
        'r'
        'r3'
        'rabbitmq'
        'racket'
        'radar'
        'radiopublic'
        'rainmeter'
        'rakuten'
        'ram'
        'rancher'
        'raspberrypi'
        'razer'
        'react'
        'reactivex'
        'reactos'
        'reactrouter'
        'readthedocs'
        'realm'
        'reason'
        'reasonstudios'
        'redbubble'
        'reddit'
        'redhat'
        'redhatopenshift'
        'redis'
        'redux-saga'
        'redux'
        'redwoodjs'
        'relianceindustrieslimited'
        'renault'
        'renovatebot'
        'renpy'
        'renren'
        'repl-dot-it'
        'researchgate'
        'resurrectionremixos'
        'retroarch'
        'retropie'
        'reveal-dot-js'
        'reverbnation'
        'revolut'
        'revue'
        'rewe'
        'rezgo'
        'rhinoceros'
        'rider'
        'ring'
        'riotgames'
        'ripple'
        'riseup'
        'roamresearch'
        'roblox'
        'robotframework'
        'rocket-dot-chat'
        'roku'
        'rolls-royce'
        'rollup-dot-js'
        'roots'
        'rootsbedrock'
        'rootssage'
        'ros'
        'rottentomatoes'
        'roundcube'
        'rss'
        'rstudio'
        'rte'
        'rtl'
        'rtlzwei'
        'ruby'
        'rubygems'
        'rubyonrails'
        'runkeeper'
        'runkit'
        'rust'
        'rxdb'
        'ryanair'
        's7airlines'
        'safari'
        'sahibinden'
        'salesforce'
        'saltstack'
        'samsung'
        'samsungpay'
        'sanfranciscomunicipalrailway'
        'saopaulometro'
        'sap'
        'sass'
        'sat-dot-1'
        'saucelabs'
        'scala'
        'scaleway'
        'scania'
        'scikit-learn'
        'scipy'
        'scopus'
        'scratch'
        'screencastify'
        'scribd'
        'scrimba'
        'scrollreveal'
        'scrumalliance'
        'scrutinizerci'
        'seagate'
        'seat'
        'sefaria'
        'sega'
        'selenium'
        'sellfy'
        'semantic-release'
        'semanticuireact'
        'semanticweb'
        'semaphoreci'
        'semver'
        'sencha'
        'sennheiser'
        'sensu'
        'sentry'
        'sepa'
        'serverfault'
        'serverless'
        'sfml'
        'shanghaimetro'
        'sharp'
        'shazam'
        'shell'
        'shelly'
        'shenzhenmetro'
        'shields-dot-io'
        'shikimori'
        'shopify'
        'shopware'
        'shotcut'
        'showpad'
        'showtime'
        'shutterstock'
        'siemens'
        'signal'
        'simkl'
        'simpleanalytics'
        'simpleicons'
        'sinaweibo'
        'singlestore'
        'sitepoint'
        'sketch'
        'sketchfab'
        'sketchup'
        'skillshare'
        'skoda'
        'sky'
        'skyliner'
        'skype'
        'skypeforbusiness'
        'slack'
        'slackware'
        'slashdot'
        'slickpic'
        'slides'
        'slideshare'
        'smart'
        'smartthings'
        'smashingmagazine'
        'smrt'
        'smugmug'
        'snapchat'
        'snapcraft'
        'snowflake'
        'snyk'
        'society6'
        'socket-dot-io'
        'sogou'
        'solidity'
        'sololearn'
        'solus'
        'sonarcloud'
        'sonarlint'
        'sonarqube'
        'sonarsource'
        'songkick'
        'songoda'
        'sonicwall'
        'sonos'
        'soundcloud'
        'sourceengine'
        'sourceforge'
        'sourcegraph'
        'southwestairlines'
        'spacemacs'
        'spacex'
        'sparkar'
        'sparkasse'
        'sparkfun'
        'sparkpost'
        'spdx'
        'speakerdeck'
        'spectrum'
        'speedtest'
        'spinnaker'
        'spinrilla'
        'splunk'
        'spotify'
        'spotlight'
        'spreaker'
        'spring'
        'springboot'
        'sprint'
        'spyderide'
        'sqlite'
        'square'
        'squareenix'
        'squarespace'
        'ssrn'
        'stackbit'
        'stackexchange'
        'stackoverflow'
        'stackpath'
        'stackshare'
        'stadia'
        'staffbase'
        'starlingbank'
        'starship'
        'startrek'
        'starz'
        'statamic'
        'staticman'
        'statuspage'
        'statuspal'
        'steam'
        'steamdb'
        'steamworks'
        'steem'
        'steemit'
        'steinberg'
        'stellar'
        'stencyl'
        'stimulus'
        'stitcher'
        'stmicroelectronics'
        'storify'
        'storyblok'
        'storybook'
        'strapi'
        'strava'
        'streamlit'
        'stripe'
        'strongswan'
        'stubhub'
        'styled-components'
        'stylelint'
        'styleshare'
        'stylus'
        'subaru'
        'sublimetext'
        'substack'
        'subversion'
        'suckless'
        'sumologic'
        'supabase'
        'superuser'
        'surveymonkey'
        'suse'
        'suzuki'
        'svelte'
        'svg'
        'svgo'
        'swagger'
        'swarm'
        'swift'
        'swiggy'
        'swiper'
        'symantec'
        'symfony'
        'symphony'
        'sympy'
        'synology'
        't-mobile'
        'tableau'
        'tado'
        'tails'
        'tailwindcss'
        'talend'
        'tampermonkey'
        'taobao'
        'tapas'
        'tasmota'
        'tata'
        'taxbuzz'
        'teamcity'
        'teamspeak'
        'teamviewer'
        'ted'
        'teespring'
        'tekton'
        'tele5'
        'telegram'
        'telegraph'
        'tencentqq'
        'tencentweibo'
        'tensorflow'
        'teradata'
        'teratail'
        'terraform'
        'tesla'
        'testin'
        'testinglibrary'
        'textpattern'
        'theconversation'
        'theirishtimes'
        'themighty'
        'themodelsresource'
        'themoviedatabase'
        'theregister'
        'thesoundsresource'
        'thespritersresource'
        'thewashingtonpost'
        'thingiverse'
        'thinkpad'
        'three-dot-js'
        'threema'
        'thumbtack'
        'thunderbird'
        'thymeleaf'
        'ticketmaster'
        'tidal'
        'tide'
        'tiktok'
        'tile'
        'timescale'
        'tinder'
        'tinyletter'
        'tmux'
        'todoist'
        'toggl'
        'tokyometro'
        'tomorrowland'
        'topcoder'
        'toptal'
        'torbrowser'
        'torproject'
        'toshiba'
        'toyota'
        'tp-link'
        'tqdm'
        'trainerroad'
        'trakt'
        'transferwise'
        'transportforireland'
        'transportforlondon'
        'travisci'
        'treehouse'
        'trello'
        'trendmicro'
        'treyarch'
        'triller'
        'trino'
        'trip-dot-com'
        'tripadvisor'
        'trove'
        'truenas'
        'trulia'
        'trustedshops'
        'trustpilot'
        'tryhackme'
        'tryitonline'
        'ts-node'
        'tui'
        'tumblr'
        'tunein'
        'turbosquid'
        'turkishairlines'
        'tutanota'
        'tvtime'
        'twilio'
        'twitch'
        'twitter'
        'twoo'
        'typescript'
        'typo3'
        'uber'
        'ubereats'
        'ubiquiti'
        'ubisoft'
        'ublockorigin'
        'ubuntu'
        'udacity'
        'udemy'
        'ufc'
        'uikit'
        'ulule'
        'umbraco'
        'unacademy'
        'undertale'
        'unicode'
        'unilever'
        'unitedairlines'
        'unity'
        'unraid'
        'unrealengine'
        'unsplash'
        'untangle'
        'untappd'
        'upcloud'
        'uplabs'
        'uploaded'
        'ups'
        'upwork'
        'usps'
        'v'
        'v8'
        'vaadin'
        'vagrant'
        'valve'
        'vapor'
        'vault'
        'vauxhall'
        'vbulletin'
        'vectorlogozone'
        'vectorworks'
        'veeam'
        'veepee'
        'venmo'
        'vercel'
        'verdaccio'
        'veritas'
        'verizon'
        'vfairs'
        'viadeo'
        'viber'
        'vim'
        'vimeo'
        'vimeolivestream'
        'vine'
        'virb'
        'virtualbox'
        'virustotal'
        'visa'
        'visualstudio'
        'visualstudiocode'
        'vite'
        'vivaldi'
        'vivino'
        'vk'
        'vlcmediaplayer'
        'vmware'
        'vodafone'
        'volkswagen'
        'volvo'
        'vonage'
        'vox'
        'vsco'
        'vue-dot-js'
        'vuetify'
        'vulkan'
        'vultr'
        'w3c'
        'wagtail'
        'wakatime'
        'walkman'
        'wappalyzer'
        'warnerbros-dot'
        'wattpad'
        'waze'
        'wearos'
        'weasyl'
        'webassembly'
        'webauthn'
        'webcomponents-dot-org'
        'webdriverio'
        'webflow'
        'webgl'
        'webhint'
        'weblate'
        'webmin'
        'webmoney'
        'webpack'
        'webrtc'
        'webstorm'
        'wechat'
        'weights-and-biases'
        'wemo'
        'wetransfer'
        'whatsapp'
        'wheniwork'
        'whitesource'
        'wii'
        'wiiu'
        'wikidata'
        'wikimediacommons'
        'wikipedia'
        'wikiquote'
        'wikivoyage'
        'windows'
        'windows95'
        'windowsterminal'
        'windowsxp'
        'winmate'
        'wipro'
        'wire'
        'wireguard'
        'wireshark'
        'wish'
        'wistia'
        'wix'
        'wizzair'
        'wolfram'
        'wolframlanguage'
        'wolframmathematica'
        'woo'
        'woocommerce'
        'wordpress'
        'workplace'
        'worldhealthorganization'
        'wpengine'
        'wprocket'
        'write-dot-as'
        'wwe'
        'wwise'
        'x-dot-org'
        'x-pack'
        'xamarin'
        'xaml'
        'xampp'
        'xbox'
        'xcode'
        'xdadevelopers'
        'xero'
        'xfce'
        'xiaomi'
        'xilinx'
        'xing'
        'xmpp'
        'xrp'
        'xsplit'
        'xstate'
        'yahoo'
        'yale'
        'yamahacorporation'
        'yamahamotorcorporation'
        'yammer'
        'yandex'
        'yarn'
        'ycombinator'
        'yelp'
        'yoast'
        'yourtravel-dot-tv'
        'youtube'
        'youtubegaming'
        'youtubemusic'
        'youtubestudio'
        'youtubetv'
        'z-wave'
        'zalando'
        'zapier'
        'zdf'
        'zelle'
        'zend'
        'zendesk'
        'zendframework'
        'zenn'
        'zerodha'
        'zeromq'
        'zerply'
        'zhihu'
        'zigbee'
        'zillow'
        'zingat'
        'zoho'
        'zoiper'
        'zomato'
        'zoom'
        'zorin'
        'zotero'
        'zulip'
    )
}
function Remove-ConfigurationCSS {
    <#
    .SYNOPSIS
    Removes specified CSS properties or sections from a CSS configuration dictionary.
 
    .DESCRIPTION
    This function removes specified CSS properties or sections from a CSS configuration dictionary. It allows for targeted removal of individual properties or entire sections based on the provided parameters.
 
    .EXAMPLE
    Remove-ConfigurationCSS -CSS $CSS -Name 'Header' -Property 'Color'
    Removes the 'Color' property from the 'Header' section in the CSS configuration.
 
    .EXAMPLE
    Remove-ConfigurationCSS -CSS $CSS -Section @('Footer', 'Sidebar') -Not
    Removes all sections except 'Footer' and 'Sidebar' from the CSS configuration.
 
    #>

    [cmdletBinding()]
    param(
        [Parameter(ParameterSetName = 'Default')]
        [Parameter(ParameterSetName = 'Section')]
        [System.Collections.IDictionary] $CSS,

        [Parameter(ParameterSetName = 'Default')][string] $Name,

        [Parameter(ParameterSetName = 'Default')][string] $Property,

        [Parameter(ParameterSetName = 'Section')][string[]] $Section,

        [Parameter(ParameterSetName = 'Section')][switch] $Not
    )

    if ($CSS -and $Name -and $Property) {

        if ($CSS[$Name]) {
            if ($Property) {
                if ($CSS[$Name][$Property]) {
                    $CSS[$Name].Remove($Property)
                }
            }
        }
    }
    if ($Section -and -not $Not) {

        foreach ($S in $Section) {
            if ($CSS[$S]) {
                $CSS.Remove($S)
            }
        }
    } elseif ($Section) {

        foreach ($S in [string[]] $CSS.Keys) {
            if ($S -notin $Section) {
                $CSS.Remove($S)
            }
        }
    }
}
function Remove-DotsFromCssClass {
    <#
    .SYNOPSIS
    Removes dots from CSS class names.
 
    .DESCRIPTION
    This function removes dots from CSS class names by trimming the leading dot from each class name.
 
    .PARAMETER Css
    Specifies a dictionary of CSS class names.
 
    .EXAMPLE
    Remove-DotsFromCssClass -Css @{ '.class1' = 'value1'; '.class2' = 'value2' }
    Removes dots from the CSS class names in the provided dictionary.
 
    #>

    [cmdletBinding()]
    param(
        [System.Collections.IDictionary] $Css
    )
    foreach ($Key in [string[]] $Css.Keys) {
        $Css[$Key] = $Css[$Key].TrimStart('.')
    }
}
function Rename-Dictionary {
    <#
    .SYNOPSIS
    Renames keys in a dictionary based on a set of key-value pairs.
 
    .DESCRIPTION
    This function renames keys in a dictionary based on a set of key-value pairs provided. It iterates through each pair and updates the dictionary keys accordingly.
 
    .PARAMETER HashTable
    Specifies the dictionary whose keys are to be renamed.
 
    .PARAMETER Pair
    Specifies an array of dictionaries containing key-value pairs for renaming keys.
 
    .EXAMPLE
    $ExistingDictionary = @{
        Test = 'SomeValue'
        OtherKey = 'SomeOtherValue'
    }
 
    $ExpectedSheetsConfiguration = [ordered] @{
        Test = "defaultSection-$(Get-RandomStringName -Size 7)"
        OtherKey = "defaultSectionText-$(Get-RandomStringName -Size 7)"
    }
 
    Rename-Dictionary -HashTable $ExistingDictionary -Pair $ExpectedSheetsConfiguration
    Renames the keys in $ExistingDictionary based on the key-value pairs provided in $ExpectedSheetsConfiguration.
 
    #>

    [cmdletBinding()]
    param(
        [System.Collections.IDictionary] $HashTable,
        [System.Collections.IDictionary[]] $Pair
    )
    foreach ($P in $Pair) {
        foreach ($Key in [string[]] $P.Keys) {
            $HashTable[$($P[$Key])] = $HashTable[$Key]
            $HashTable.Remove($Key)
        }
    }
}

function Set-ConfigurationCSS {
    <#
    .SYNOPSIS
    Sets CSS configuration for a specific feature and type.
 
    .DESCRIPTION
    This function sets the CSS configuration for a specific feature and type in the script's current configuration. It allows customization of CSS properties for different features and types.
 
    .PARAMETER Feature
    Specifies the feature for which the CSS configuration is being set.
 
    .PARAMETER Type
    Specifies the type of CSS configuration being set.
 
    .PARAMETER CSS
    Specifies the CSS properties to be set for the specified feature and type.
 
    .EXAMPLE
    Set-ConfigurationCSS -Feature 'Header' -Type 'Color' -CSS @{ 'color' = 'blue' }
    Sets the 'color' property to 'blue' for the 'Header' feature in the CSS configuration.
 
    .EXAMPLE
    Set-ConfigurationCSS -Feature 'Footer' -Type 'Font' -CSS @{ 'font-family' = 'Arial', 'font-size' = '12px' }
    Sets the font family to 'Arial' and font size to '12px' for the 'Footer' feature in the CSS configuration.
    #>

    [cmdletBinding()]
    param(
        [string] $Feature,
        [string] $Type,
        [System.Collections.IDictionary] $CSS
    )
    $Script:CurrentConfiguration.Features.$Feature.$Type.CssInline = $CSS
}
function Set-Tag {
    <#
    .SYNOPSIS
    Sets HTML tags and attributes based on the provided HtmlObject.
 
    .DESCRIPTION
    This function generates HTML tags and attributes based on the provided HtmlObject. It constructs the HTML structure with the specified tag, attributes, and values.
 
    .PARAMETER HtmlObject
    Specifies the HtmlObject containing tag, attributes, and values to be set.
 
    .PARAMETER NewLine
    Specifies whether new lines are needed in the generated HTML code, useful for languages like JavaScript.
 
    .EXAMPLE
    $HtmlObject = @{
        Tag = 'div'
        Attributes = @{
            'class' = 'container'
            'style' = @{
                'color' = 'blue'
                'font-size' = '16px'
            }
        }
        Value = 'Hello, World!'
    }
 
    Set-Tag -HtmlObject $HtmlObject
    Generates the HTML tag <div class="container" style="color:blue;font-size:16px">Hello, World!</div>.
 
    .EXAMPLE
    $HtmlObject = @{
        Tag = 'ul'
        Attributes = @{
            'id' = 'list'
        }
        Value = @(
            @{
                Tag = 'li'
                Value = 'Item 1'
            }
            @{
                Tag = 'li'
                Value = 'Item 2'
            }
        )
    }
 
    Set-Tag -HtmlObject $HtmlObject -NewLine
    Generates the HTML structure:
    <ul id="list">
        <li>Item 1</li>
        <li>Item 2</li>
    </ul>
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $HtmlObject,
        [switch] $NewLine # This is needed if code requires new lines such as JavaScript
    )
    $HTML = [System.Text.StringBuilder]::new()
    [void] $HTML.Append("<$($HtmlObject.Tag)")
    foreach ($Property in $HtmlObject.Attributes.Keys) {
        $PropertyValue = $HtmlObject.Attributes[$Property]

        if ($PropertyValue -is [System.Collections.IDictionary]) {
            $OutputSubProperties = foreach ($SubAttributes in $PropertyValue.Keys) {
                $SubPropertyValue = $PropertyValue[$SubAttributes]

                if ($null -ne $SubPropertyValue -and $SubPropertyValue -ne '') {
                    "$($SubAttributes):$($SubPropertyValue)"
                }
            }
            $MyValue = $OutputSubProperties -join ';'
            if ($MyValue.Trim() -ne '') {
                [void] $HTML.Append(" $Property=`"$MyValue`"")
            }
        } else {

            if ($null -ne $PropertyValue -and $PropertyValue -ne '') {
                [void] $HTML.Append(" $Property=`"$PropertyValue`"")
            }
        }
    }
    if (($null -ne $HtmlObject.Value) -and ($HtmlObject.Value -ne '')) {
        [void] $HTML.Append(">")

        if ($HtmlObject.Value.Count -eq 1) {
            if ($HtmlObject.Value -is [System.Collections.IDictionary]) {
                [string] $NewObject = Set-Tag -HtmlObject ($HtmlObject.Value)
                [void] $HTML.Append($NewObject)
            } else {
                [void] $HTML.Append([string] $HtmlObject.Value)
            }
        } else {
            foreach ($Entry in $HtmlObject.Value) {
                if ($Entry -is [System.Collections.IDictionary]) {
                    [string] $NewObject = Set-Tag -HtmlObject ($Entry)
                    [void] $HTML.Append($NewObject)
                } else {

                    if ($NewLine) {
                        [void] $HTML.AppendLine([string] $Entry)
                    } else {
                        [void] $HTML.Append([string] $Entry)
                    }
                }
            }
        }
        [void] $HTML.Append("</$($HtmlObject.Tag)>")
    } else {
        if ($HtmlObject.SelfClosing) {
            [void] $HTML.Append("/>")
        } elseif ($HtmlObject.NoClosing) {
            [void] $HTML.Append(">")
        } else {
            [void] $HTML.Append("></$($HtmlObject.Tag)>")
        }
    }
    $HTML.ToString()
}
function Add-HTML {
    <#
    .SYNOPSIS
    Adds HTML content to the email body.
 
    .DESCRIPTION
    This function adds HTML content to the email body by executing the provided ScriptBlock containing the HTML code.
 
    .PARAMETER HTML
    Specifies the ScriptBlock containing the HTML code to be added to the email body.
 
    .EXAMPLE
    $HTMLContent = {
        Set-Tag -HtmlObject @{
            Tag = 'div'
            Attributes = @{
                'class' = 'container'
            }
            Value = 'Hello, World!'
        }
    }
 
    Add-HTML -HTML $HTMLContent
    Adds a <div class="container">Hello, World!</div> to the email body.
 
    .EXAMPLE
    $HTMLContent = {
        Set-Tag -HtmlObject @{
            Tag = 'ul'
            Attributes = @{
                'id' = 'list'
            }
            Value = @(
                @{
                    Tag = 'li'
                    Value = 'Item 1'
                }
                @{
                    Tag = 'li'
                    Value = 'Item 2'
                }
            )
        }
    }
 
    Add-HTML -HTML $HTMLContent
    Adds a list with items 'Item 1' and 'Item 2' to the email body.
    #>

    [alias('EmailHTML')]
    [CmdletBinding()]
    param(
        [ScriptBlock] $HTML
    )
    Invoke-Command -ScriptBlock $HTML
}
function Add-HTMLScript {
    <#
    .SYNOPSIS
    Adds JavaScript content to the HTML body.
 
    .DESCRIPTION
    This function adds JavaScript content to the HTML body by including JavaScript code from files, strings, or links. It allows customization of the placement of JavaScript content within the HTML document.
 
    .PARAMETER Placement
    Specifies where the JavaScript content should be placed in the HTML document. Valid values are 'Inline', 'Header', or 'Footer'. Default is 'Header'.
 
    .PARAMETER Resource
    Specifies the resource containing the JavaScript content.
 
    .PARAMETER ResourceComment
    Specifies a comment to be included with the JavaScript content.
 
    .PARAMETER Link
    Specifies an array of URLs to external JavaScript files to be included.
 
    .PARAMETER Content
    Specifies an array of JavaScript content as strings to be included.
 
    .PARAMETER FilePath
    Specifies an array of file paths containing JavaScript code to be included.
 
    .PARAMETER ReplaceData
    Specifies a dictionary of key-value pairs to replace in the JavaScript code.
 
    .PARAMETER AddComments
    Indicates whether to add comments around the JavaScript content. By default, comments are not added.
 
    .PARAMETER SkipTags
    Indicates whether to skip adding script tags around the JavaScript content. By default, script tags are added.
 
    .EXAMPLE
    Add-HTMLScript -Placement 'Header' -Resource 'MyScript' -ResourceComment 'Script for functionality X' -Link 'https://example.com/script.js'
    Adds an external JavaScript file 'script.js' to the header of the HTML document with a specified comment.
 
    .EXAMPLE
    Add-HTMLScript -Placement 'Footer' -Resource 'CustomScript' -ResourceComment 'Custom script for feature Y' -Content 'function myFunction() { // code here }'
    Adds custom JavaScript content to the footer of the HTML document with a specified comment.
 
    .EXAMPLE
    Add-HTMLScript -Placement 'Inline' -Resource 'InlineScript' -ResourceComment 'Inline script for page Z' -FilePath 'C:\Scripts\inline.js' -ReplaceData @{ 'placeholder' = 'replacement' }
    Adds JavaScript code from a file 'inline.js' to be included inline in the HTML document with specified replacements.
 
    #>

    [alias('Add-JavaScript', 'New-JavaScript', 'Add-JS')]
    [CmdletBinding()]
    param(
        [ValidateSet('Inline', 'Header', 'Footer')][string] $Placement = 'Header',
        [System.Collections.IDictionary] $Resource,
        [string] $ResourceComment,
        [string[]] $Link,
        [string[]] $Content,
        [string[]] $FilePath,
        [Parameter(DontShow)][System.Collections.IDictionary] $ReplaceData,
        [switch] $AddComments,
        [switch] $SkipTags
    )
    if (-not $ResourceComment) {
        $ResourceComment = "ResourceJS-$(Get-RandomStringName -Size 8 -LettersOnly)"
    }
    $Output = @(

        foreach ($File in $FilePath) {
            if ($File -ne '') {
                if (Test-Path -LiteralPath $File) {

                    $FileContent = Get-Content -LiteralPath $File -Raw -Encoding UTF8
                    if ($null -ne $ReplaceData) {
                        foreach ($_ in $ReplaceData.Keys) {
                            $FileContent = $FileContent -replace $_, $ReplaceData[$_]
                        }
                    }
                    if ($SkipTags) {
                        if ($Resource.InternalComment) {
                            "/* $($Resource.InternalComment)-start */" }
                        $FileContent
                        if ($Resource.InternalComment) {
                            "/* $($Resource.InternalComment)-end */" }
                    } else {
                        New-HTMLTag -Tag 'script' -Attributes @{ type = 'text/javascript'; comment = $Resource.InternalComment } {
                            if ($Resource.InternalComment) {
                                "/* $($Resource.InternalComment)-start */" }
                            $FileContent
                            if ($Resource.InternalComment) {
                                "/* $($Resource.InternalComment)-end */" }
                        } -NewLine
                    }
                } else {
                    Write-Warning "Add-HTMLScript - File $File not found. Unable to load JavaScript to HTML. HTML may be broken. Skipping."
                }
            }
        }

        if ($Content) {
            if ($SkipTags) {
                if ($Resource.InternalComment) {
                    "/* $($Resource.InternalComment)-start */" }
                $Content
                if ($Resource.InternalComment) {
                    "/* $($Resource.InternalComment)-end */" }
            } else {
                New-HTMLTag -Tag 'script' -Attributes @{ type = 'text/javascript' } {
                    if ($Resource.InternalComment) {
                        "/* $($Resource.InternalComment)-start */" }
                    $Content
                    if ($Resource.InternalComment) {
                        "/* $($Resource.InternalComment)-end */" }
                } -NewLine
            }
        }

        foreach ($L in $Link) {
            if ($L -ne '') {
                New-HTMLTag -Tag 'script' -Attributes @{ type = "text/javascript"; src = $L; comment = $Resource.InternalComment } -NewLine
            } else {
                return
            }
        }
    )
    if ($Output) {
        if ($AddComment) {
            $Output = @(
                "<!-- JS $ResourceComment START -->"
                $Output
                "<!-- JS $ResourceComment END -->"
            )
        }

        if ($Placement -eq 'Footer') {
            $Script:HTMLSchema.CustomFooterJS[$ResourceComment] = $Output
        } elseif ($Placement -eq 'Header') {
            $Script:HTMLSchema.CustomHeaderJS[$ResourceComment] = $Output
        } else {
            $Output
        }
    }
}
function Add-HTMLStyle {
    <#
    .SYNOPSIS
    Adds CSS styles to HTML documents.
 
    .DESCRIPTION
    The Add-HTMLStyle function allows you to add CSS styles to HTML documents in various ways such as inline styles, external stylesheets, and content from files or strings.
 
    .PARAMETER Placement
    Specifies where the CSS styles should be placed in the HTML document. Valid values are 'Inline', 'Header', or 'Footer'. Default is 'Header'.
 
    .PARAMETER Resource
    Specifies the resource to be added as CSS styles.
 
    .PARAMETER ResourceComment
    Specifies a comment for the resource being added.
 
    .PARAMETER Link
    Specifies an array of URLs to external CSS stylesheets to be linked in the HTML document.
 
    .PARAMETER Content
    Specifies an array of CSS content to be added directly to the HTML document.
 
    .PARAMETER FilePath
    Specifies an array of file paths containing CSS content to be added to the HTML document.
 
    .PARAMETER Css
    Specifies a hashtable of CSS styles to be converted and added to the HTML document.
 
    .PARAMETER ReplaceData
    Specifies a hashtable of data to be replaced in the CSS content.
 
    .PARAMETER AddComment
    Switch parameter to add comments around the added CSS styles.
 
    .PARAMETER RelType
    Specifies the relationship type for linked stylesheets. Valid values are 'dns-prefetch', 'preconnect', or 'preload'. Default is 'preload'.
 
    .PARAMETER SkipTags
    Switch parameter to skip adding HTML tags when inserting CSS content.
 
    .EXAMPLE
    Add-HTMLStyle -Placement 'Header' -Resource 'styles.css' -ResourceComment 'Custom styles' -Link 'https://example.com/styles.css'
 
    Adds a linked CSS stylesheet to the header of the HTML document with a custom comment.
 
    .EXAMPLE
    Add-HTMLStyle -Placement 'Inline' -Content 'body { background-color: lightblue; }' -ResourceComment 'Inline styles'
 
    Adds inline CSS styles directly to the HTML document body with a custom comment.
 
    .EXAMPLE
    Add-HTMLStyle -Placement 'Footer' -Css @{ 'body' = @{ 'background-color' = 'lightblue' } } -ResourceComment 'Custom CSS'
 
    Converts a hashtable of CSS styles to a stylesheet and adds it to the footer of the HTML document with a custom comment.
 
    #>

    [alias('Add-CSS')]
    [CmdletBinding()]
    param(
        [ValidateSet('Inline', 'Header', 'Footer')][string] $Placement = 'Header',
        [System.Collections.IDictionary] $Resource,
        [string] $ResourceComment,
        [string[]] $Link,
        [string[]] $Content,
        [string[]] $FilePath,
        [alias('CssInline')][System.Collections.IDictionary] $Css,
        [Parameter(DontShow)][System.Collections.IDictionary] $ReplaceData,
        [switch] $AddComment,
        [ValidateSet('dns-prefetch', 'preconnect', 'preload')][string] $RelType = 'preload',
        [switch] $SkipTags
    )
    if (-not $ResourceComment) {
        $ResourceComment = "ResourceCSS-$(Get-RandomStringName -Size 8 -LettersOnly)"
    }
    $Output = @(

        foreach ($File in $FilePath) {
            if ($File -ne '') {
                if (Test-Path -LiteralPath $File) {
                    Write-Verbose "Add-HTMLStyle - Reading file from $File"

                    $FileContent = Get-Content -LiteralPath $File -Raw -Encoding UTF8
                    if ($null -ne $ReplaceData) {
                        foreach ($_ in $ReplaceData.Keys) {
                            $FileContent = $FileContent -replace $_, $ReplaceData[$_]
                        }
                    }
                    $FileContent = $FileContent -replace '@charset "UTF-8";'

                    if ($SkipTags) {
                        if ($Resource.InternalComment) {
                            "/* $($Resource.InternalComment)-start */" }
                        $FileContent
                        if ($Resource.InternalComment) {
                            "/* $($Resource.InternalComment)-end */" }
                    } else {
                        New-HTMLTag -Tag 'style' -Attributes @{ type = 'text/css'; comment = $Resource.InternalComment } {
                            if ($Resource.InternalComment) {
                                "/* $($Resource.InternalComment)-start */" }
                            $FileContent
                            if ($Resource.InternalComment) {
                                "/* $($Resource.InternalComment)-end */" }
                        } -NewLine
                    }
                } else {
                    Write-Warning "Add-HTMLStyle - File $File not found. Unable to load CSS to HTML. HTML may be broken. Skipping."
                }
            }
        }

        if ($Content) {
            Write-Verbose "Add-HTMLStyle - Adding style from Content"
            if ($SkipTags) {
                if ($Resource.InternalComment) {
                    "/* $($Resource.InternalComment)-start */" }
                $Content
                if ($Resource.InternalComment) {
                    "/* $($Resource.InternalComment)-end */" }
            } else {
                New-HTMLTag -Tag 'style' -Attributes @{ type = 'text/css'; comment = $Resource.InternalComment } {
                    if ($Resource.InternalComment) {
                        "/* $($Resource.InternalComment)-start */" }
                    $Content
                    if ($Resource.InternalComment) {
                        "/* $($Resource.InternalComment)-end */" }
                } -NewLine
            }
        }

        foreach ($L in $Link) {
            if ($L -ne '') {
                Write-Verbose "Add-HTMLStyle - Adding link $L"
                New-HTMLTag -Tag 'link' -Attributes @{ rel = "stylesheet preload prefetch"; type = "text/css"; href = $L; as = 'style'; comment = $Resource.InternalComment } -SelfClosing -NewLine
            }
        }

        if ($Css) {
            ConvertTo-CascadingStyleSheets -Css $Css -WithTags:(-not $SkipTags.IsPresent) -Comment $Resource.InternalComment 
        }
    )
    if ($Output) {
        if ($AddComment) {
            $Output = @(
                "<!-- CSS $ResourceComment START -->"
                $Output
                "<!-- CSS $ResourceComment END -->"
            )
        }

        if ($Placement -eq 'Footer') {
            $Script:HTMLSchema.CustomFooterCSS[$ResourceComment] = $Output
        } elseif ($Placement -eq 'Header') {
            $Script:HTMLSchema.CustomHeaderCSS[$ResourceComment] = $Output
        } else {
            $Output
        }
    }
}
function ConvertTo-CascadingStyleSheets {
    <#
    .SYNOPSIS
    Converts a hashtable of CSS properties into Cascading Style Sheets (CSS) format.
 
    .DESCRIPTION
    The ConvertTo-CascadingStyleSheets function takes a hashtable of CSS properties and converts them into a CSS stylesheet format. It organizes the properties into selectors and declarations, ready to be used in styling HTML elements.
 
    .PARAMETER Css
    Specifies the hashtable of CSS properties to be converted.
 
    .PARAMETER Name
    Specifies the name of the CSS selector to be used in the stylesheet.
 
    .PARAMETER Comment
    Specifies an optional comment to be included in the CSS stylesheet.
 
    .PARAMETER WithTags
    Indicates whether to include CSS style tags around the generated CSS content. Default is false.
 
    .EXAMPLE
    $CssProperties = @{
        'color' = 'blue'
        'font-size' = '16px'
        'font-family' = 'Arial'
    }
 
    ConvertTo-CascadingStyleSheets -Css $CssProperties -Name '.myClass' -Comment 'Custom styles for myClass'
 
    Converts the hashtable of CSS properties into a CSS stylesheet with the selector '.myClass' and a custom comment.
 
    .EXAMPLE
    $CssProperties = @{
        'background-color' = 'lightblue'
        'border' = @{
            'style' = 'solid'
            'width' = '1px'
            'color' = 'black'
        }
    }
 
    ConvertTo-CascadingStyleSheets -Css $CssProperties -Name '#myDiv' -WithTags
 
    Converts the CSS properties into a CSS stylesheet with the selector '#myDiv' and includes CSS style tags.
 
    #>

    [alias('ConvertTo-CSS')]
    [cmdletBinding()]
    param(
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)][System.Collections.IDictionary] $Css,
        [string] $Name,
        [string] $Comment,
        [switch] $WithTags
    )
    Process {
        if ($Css) {
            Remove-EmptyValue -Hashtable $Css -Recursive
        }
        if ($Css.Count -eq 0) {

            return
        }

        $Output = foreach ($Key in $Css.Keys) {

            if ($Css[$Key] -is [System.Collections.IDictionary]) {
                "$Key {"
                foreach ($_ in $Css[$Key].Keys) {
                    if ($null -ne $Css[$Key][$_]) {

                        if ($Css[$Key][$_] -is [System.Collections.IDictionary]) {
                            "$_ {"
                            $Deep = ConvertTo-CascadingStyleSheets -Css $Css[$Key][$_]
                            $Deep
                            "}"
                        } else {
                            $Property = $_.Replace(' ', '')
                            " $Property`: $($Css[$Key][$_]);"
                        }
                    } else {
                        Write-Verbose ""
                    }
                }
                "}"
                ''
            } else {
                $Property = $Key.Replace(' ', '')
                " $Property`: $($Css[$Key]);"
            }

        }
        if ($WithTags) {
            New-HTMLTag -Tag 'style' -Attributes @{ type = 'text/css'; comment = $Comment } {
                if ($Name) {
                    "$Name {"
                    if ($Comment) {
                        "/* $($Comment)-start */" }
                    $Output
                    if ($Comment) {
                        "/* $($Comment)-end */" }
                    "}"
                } else {
                    if ($Comment) {
                        "/* $($Comment)-start */" }
                    $Output
                    if ($Comment) {
                        "/* $($Comment)-end */" }
                }
            }
        } else {
            if ($Name) {
                "$Name {"
                if ($Comment) {
                    "/* $($Comment)-start */" }
                $Output
                if ($Comment) {
                    "/* $($Comment)-end */" }
                "}"
            } else {
                if ($Comment) {
                    "/* $($Comment)-start */" }
                $Output
                if ($Comment) {
                    "/* $($Comment)-end */" }
            }
        }
    }
}

function Email {
    <#
    .SYNOPSIS
    Sends an email with specified parameters.
 
    .DESCRIPTION
    This function sends an email with the provided parameters including recipients, subject, content, and server settings.
 
    .PARAMETER Email
    Specifies the ScriptBlock containing the email content and additional parameters.
 
    .PARAMETER To
    Specifies the email addresses of the primary recipients.
 
    .PARAMETER CC
    Specifies the email addresses of the carbon copy recipients.
 
    .PARAMETER BCC
    Specifies the email addresses of the blind carbon copy recipients.
 
    .PARAMETER ReplyTo
    Specifies the email address to set as the reply-to address.
 
    .PARAMETER From
    Specifies the email address of the sender.
 
    .PARAMETER Subject
    Specifies the subject of the email.
 
    .PARAMETER AttachSelf
    Switch parameter to attach the email to itself.
 
    .PARAMETER AttachSelfName
    Specifies the name of the attached email.
 
    .PARAMETER Server
    Specifies the SMTP server for sending the email.
 
    .PARAMETER Username
    Specifies the username for authentication with the SMTP server.
 
    .PARAMETER Port
    Specifies the port number for the SMTP server. Default is 587.
 
    .PARAMETER Password
    Specifies the password for authentication with the SMTP server.
 
    .PARAMETER PasswordFromFile
    Switch parameter to indicate that the password is read from a file.
 
    .PARAMETER PasswordAsSecure
    Switch parameter to treat the password as a secure string.
 
    .PARAMETER SSL
    Switch parameter to enable SSL for the SMTP connection.
 
    .PARAMETER Priority
    Specifies the priority of the email. Valid values are 'Low', 'Normal', or 'High'. Default is 'Normal'.
 
    .PARAMETER DeliveryNotifications
    Specifies the type of delivery notifications. Valid values are 'None', 'OnSuccess', 'OnFailure', 'Delay', or 'Never'. Default is 'None'.
 
    .PARAMETER Encoding
    [Obsolete] Deprecated parameter. Encoding is set to UTF8 to prevent errors.
 
    .PARAMETER FilePath
    Specifies the file path for attachments.
 
    .PARAMETER Suppress
    Switch parameter to suppress sending the email.
 
    .PARAMETER Online
    Switch parameter to send the email online without saving it.
 
    .PARAMETER OutputHTML
    Switch parameter to output the email content as HTML.
 
    .PARAMETER WhatIf
    Switch parameter to show what would happen without actually sending the email.
 
    #>

    [CmdLetBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $Email,
        [string[]] $To,
        [string[]] $CC,
        [string[]] $BCC,
        [string] $ReplyTo,
        [string] $From,
        [string] $Subject,
        [alias('SelfAttach')][switch] $AttachSelf,
        [string] $AttachSelfName,
        [string] $Server,
        [string] $Username,
        [int] $Port = 587,
        [string] $Password,
        [switch] $PasswordFromFile,
        [switch] $PasswordAsSecure,
        [switch] $SSL,
        [ValidateSet('Low', 'Normal', 'High')] [string] $Priority = 'Normal',
        [ValidateSet('None', 'OnSuccess', 'OnFailure', 'Delay', 'Never')] $DeliveryNotifications = 'None',
        [Obsolete("Encoding is depracated. We're setting encoding to UTF8 always to prevent errors")]
        [Parameter(DontShow)][string] $Encoding,
        [string] $FilePath,
        [alias('Supress')][bool] $Suppress = $true,
        [switch] $Online,
        [switch] $OutputHTML,
        [switch] $WhatIf
    )
    $Script:EmailSchema = [ordered]@{}
    $Script:EmailSchema['AttachSelf'] = $AttachSelf.IsPresent
    $Script:EmailSchema['Online'] = $Online.IsPresent

    $StartTime = [System.Diagnostics.Stopwatch]::StartNew()
    $ServerParameters = [ordered] @{
        From                  = $From
        To                    = $To
        CC                    = $CC
        BCC                   = $BCC
        ReplyTo               = $ReplyTo
        Server                = $Server
        Login                 = $Username
        Password              = $Password
        PasswordAsSecure      = $PasswordAsSecure
        PasswordFromFile      = $PasswordFromFile
        Port                  = $Port
        EnableSSL             = $SSL

        Subject               = $Subject
        Priority              = $Priority
        DeliveryNotifications = $DeliveryNotifications
    }
    $Attachments = [System.Collections.Generic.List[string]]::new()

    [Array] $EmailParameters = Invoke-Command -ScriptBlock $Email

    foreach ($Parameter in $EmailParameters) {
        switch ( $Parameter.Type ) {
            HeaderTo {
                $ServerParameters.To = $Parameter.Addresses
            }
            HeaderCC {
                $ServerParameters.CC = $Parameter.Addresses
            }
            HeaderBCC {
                $ServerParameters.BCC = $Parameter.Addresses
            }
            HeaderFrom {
                $ServerParameters.From = $Parameter.Address
            }
            HeaderReplyTo {
                $ServerParameters.ReplyTo = $Parameter.Address
            }
            HeaderSubject {
                $ServerParameters.Subject = $Parameter.Subject
            }
            HeaderServer {
                $ServerParameters.Server = $Parameter.Server
                $ServerParameters.Port = $Parameter.Port
                $ServerParameters.Login = $Parameter.UserName
                $ServerParameters.Password = $Parameter.Password
                $ServerParameters.PasswordFromFile = $Parameter.PasswordFromFile
                $ServerParameters.PasswordAsSecure = $Parameter.PasswordAsSecure
                $ServerParameters.EnableSSL = $Parameter.SSL
                $ServerParameters.UseDefaultCredentials = $Parameter.UseDefaultCredentials
            }
            HeaderAttachment {
                foreach ($Attachment in  $Parameter.FilePath) {
                    $Attachments.Add($Attachment)
                }
            }
            HeaderOptions {
                $ServerParameters.DeliveryNotifications = $Parameter.DeliveryNotifications

                $ServerParameters.Priority = $Parameter.Priority
            }
            Default {
                $OutputBody = $Parameter
            }
        }
    }
    if ($OutputBody -is [System.Collections.IDictionary]) {
        $Body = $OutputBody.Body
        $AttachSelfBody = $OutputBody.AttachSelfBody
    } else {
        $Body = $OutputBody
    }

    if ($FilePath) {

        $SavedPath = Save-HTML -FilePath $FilePath -HTML $Body -Suppress $false
    }
    if ($OutputHTML) {

        $Body
    }
    if ($AttachSelf) {
        if ($AttachSelfName) {
            $TempFilePath = [System.IO.Path]::Combine([IO.path]::GetTempPath(), "$($AttachSelfName).html")
        } else {
            $TempFilePath = ''
        }
        if ($FilePath -and -not $AttachSelfName) {

            $Saved = $SavedPath
        } else {

            $Saved = Save-HTML -FilePath $TempFilePath -HTML $AttachSelfBody -Suppress $false
        }
        if ($Saved) {
            $Attachments.Add($Saved)
        }
    }

    $EmailOutput = Send-Email -EmailParameters $ServerParameters -Body $Body -Attachment $Attachments -WhatIf:$WhatIf
    if (-not $Suppress) {
        $EmailOutput
    }

    $EndTime = Stop-TimeLog -Time $StartTime -Option OneLiner
    Write-Verbose "Email - Time to send: $EndTime"
    $Script:EmailSchema = $null
}

function EmailAttachment {
    <#
    .SYNOPSIS
    Creates an email attachment object for header attachments.
 
    .DESCRIPTION
    This function creates an email attachment object specifically for header attachments. It takes an array of file paths and returns a custom object representing the attachment.
 
    .PARAMETER FilePath
    Specifies an array of file paths for the attachments.
 
    .EXAMPLE
    $Attachment = EmailAttachment -FilePath 'C:\Files\attachment1.txt', 'C:\Files\attachment2.pdf'
    Creates an email attachment object with two file paths for header attachments.
 
    .EXAMPLE
    $Files = 'C:\Files\attachment1.txt', 'C:\Files\attachment2.pdf'
    $Attachment = EmailAttachment -FilePath $Files
    Creates an email attachment object with multiple file paths for header attachments.
 
    #>

    [CmdletBinding()]
    param(
        [string[]] $FilePath
    )
    [PSCustomObject] @{
        Type     = 'HeaderAttachment'
        FilePath = $FilePath
    }
}
function EmailBCC {
    <#
    .SYNOPSIS
    Creates an email object for blind carbon copy (BCC) recipients.
 
    .DESCRIPTION
    This function creates an email object specifically for blind carbon copy (BCC) recipients. It takes an array of email addresses and returns a custom object representing the BCC recipients.
 
    .PARAMETER Addresses
    Specifies an array of email addresses for the BCC recipients.
 
    .EXAMPLE
    $BCCRecipients = EmailBCC -Addresses 'bcc1@example.com', 'bcc2@example.com'
    Creates an email object with two BCC recipients.
 
    .EXAMPLE
    $BCCList = 'bcc1@example.com', 'bcc2@example.com'
    $BCCRecipients = EmailBCC -Addresses $BCCList
    Creates an email object with multiple BCC recipients.
 
    #>

    [CmdletBinding()]
    param(
        [string[]] $Addresses
    )

    [PsCustomObject] @{
        Type      = 'HeaderBCC'
        Addresses = $Addresses
    }
}
function EmailBody {
    <#
    .SYNOPSIS
    Defines styling properties for an email body content.
 
    .DESCRIPTION
    This function defines styling properties for an email body content such as color, background color, font size, font weight, font style, font family, alignment, text decoration, text transform, direction, and more.
 
    .PARAMETER EmailBody
    Specifies the ScriptBlock containing the email body content.
 
    .PARAMETER Color
    Specifies the color of the text in the email body.
 
    .PARAMETER BackGroundColor
    Specifies the background color of the email body.
 
    .PARAMETER LineHeight
    Specifies the line height of the text in the email body.
 
    .PARAMETER Size
    Specifies the font size of the text in the email body.
 
    .PARAMETER FontWeight
    Specifies the weight of the font in the email body. Valid values are 'normal', 'bold', 'bolder', 'lighter', or numeric values from 100 to 900.
 
    .PARAMETER FontStyle
    Specifies the style of the font in the email body. Valid values are 'normal', 'italic', or 'oblique'.
 
    .PARAMETER FontVariant
    Specifies the variant of the font in the email body. Valid values are 'normal' or 'small-caps'.
 
    .PARAMETER FontFamily
    Specifies the font family of the text in the email body.
 
    .PARAMETER Alignment
    Specifies the alignment of the text in the email body. Valid values are 'left', 'center', 'right', or 'justify'.
 
    .PARAMETER TextDecoration
    Specifies the decoration of the text in the email body. Valid values are 'none', 'line-through', 'overline', or 'underline'.
 
    .PARAMETER TextTransform
    Specifies the transformation of the text in the email body. Valid values are 'uppercase', 'lowercase', or 'capitalize'.
 
    .PARAMETER Direction
    Specifies the direction of the text in the email body. Valid value is 'rtl'.
 
    .PARAMETER Online
    Switch parameter to indicate if the email body content is online.
 
    .PARAMETER Format
    Switch parameter to format the email body content.
 
    .PARAMETER Minify
    Switch parameter to minify the email body content.
 
    .PARAMETER Parameter
    Specifies additional parameters for styling the email body content.
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $EmailBody,
        [string] $Color,
        [string] $BackGroundColor,
        [string] $LineHeight,
        [alias('Size')][object] $FontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [string] $FontFamily ,
        [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [ValidateSet('rtl')][string] $Direction,
        [switch] $Online,
        [switch] $Format,
        [switch] $Minify,
        [System.Collections.IDictionary] $Parameter
    )

    $newHTMLSplat = @{}
    $newTableSplat = @{}
    if ($Alignment) {
        $newHTMLSplat.Alignment = $Alignment
        $newTableSplat.'text-align' = $Alignment
    }
    if ($FontSize) {
        $newHTMLSplat.FontSize = $FontSize
        $newTableSplat.'font-size' = ConvertFrom-Size -FontSize $FontSize
    }
    if ($TextTransform) {
        $newHTMLSplat.TextTransform = $TextTransform
        $newTableSplat.'text-transform' = $TextTransform
    }
    if ($Color) {
        $newHTMLSplat.Color = $Color
        $newTableSplat.'text-color' = ConvertFrom-Color -Color $Color
    }
    if ($FontFamily) {
        $newHTMLSplat.FontFamily = $FontFamily
        $newTableSplat.'font-family' = $FontFamily
    }
    if ($Direction) {
        $newHTMLSplat.Direction = $Direction
        $newTableSplat.'direction' = $Direction
    }
    if ($FontStyle) {
        $newHTMLSplat.FontStyle = $FontStyle
        $newTableSplat.'font-style' = $FontStyle
    }
    if ($TextDecoration) {
        $newHTMLSplat.TextDecoration = $TextDecoration
        $newTableSplat.'text-decoration' = $TextDecoration
    }
    if ($BackGroundColor) {
        $newHTMLSplat.BackGroundColor = $BackGroundColor
        $newTableSplat.'background-color' = ConvertFrom-Color -Color $BackGroundColor
    }
    if ($FontVariant) {
        $newHTMLSplat.FontVariant = $FontVariant
        $newTableSplat.'font-variant' = $FontVariant
    }
    if ($FontWeight) {
        $newHTMLSplat.FontWeight = $FontWeight
        $newTableSplat.'font-weight' = $FontWeight
    }
    if ($LineHeight) {
        $newHTMLSplat.LineHeight = $LineHeight
        $newTableSplat.'line-height' = $LineHeight
    }
    if ($newHTMLSplat.Count -gt 0) {
        $SpanRequired = $true
    } else {
        $SpanRequired = $false
    }

    if (-not $Online) {
        if ($Script:EmailSchema -and $Script:EmailSchema['Online']) {
            $HTMLOnline = $true
        } else {
            $HTMLOnline = $false
        }
    } else {
        $HTMLOnline = $true
    }

    $Body = New-HTML -Online:$HTMLOnline {

        $Script:HTMLSchema['Email'] = $true

        $Script:CurrentConfiguration['Features']['DefaultImage']['HeaderAlways']['CssInLine']['.logo']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DefaultHeadings']['HeaderAlways']['CssInLine']['h1']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DefaultHeadings']['HeaderAlways']['CssInLine']['h2']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DefaultHeadings']['HeaderAlways']['CssInLine']['h3']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DefaultHeadings']['HeaderAlways']['CssInLine']['h4']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DefaultHeadings']['HeaderAlways']['CssInLine']['h5']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DefaultHeadings']['HeaderAlways']['CssInLine']['h6']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DefaultText']['HeaderAlways']['CssInLine']['.defaultText']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DataTables']['HeaderAlways']['CssInLine']['div.dataTables_wrapper']['margin'] = '0px'
        $Script:CurrentConfiguration['Features']['DataTables']['HeaderAlways']['CssInLine']['div.dataTables_wrapper']['margin'] = '0px'

        if ($newTableSplat) {
            foreach ($Key in $newTableSplat.Keys) {
                $Script:CurrentConfiguration['Features']['DataTablesEmail']['HeaderAlways']['CssInLine']['table'][$Key] = $newTableSplat[$Key]
            }
        }
        if ($Parameter) {

            [Array] $ArrayParamerers = foreach ($Key in $Parameter.Keys) {
                if ($null -eq $Parameter[$Key]) {
                    , $null
                } else {
                    , $Parameter[$Key]
                }
            }
            $Template = Add-ParametersToScriptBlock -ScriptBlock $EmailBody -Parameter $Parameter
            if ($SpanRequired) {
                New-HTMLSpanStyle @newHTMLSplat {
                    Invoke-Command -ScriptBlock $Template -ArgumentList $ArrayParamerers
                }
            } else {
                Invoke-Command -ScriptBlock $Template -ArgumentList $ArrayParamerers
            }
        } else {
            if ($SpanRequired) {
                New-HTMLSpanStyle @newHTMLSplat {
                    Invoke-Command -ScriptBlock $EmailBody
                }
            } else {
                Invoke-Command -ScriptBlock $EmailBody
            }
        }
    } -Format:$Format -Minify:$Minify

    $options = [Text.RegularExpressions.RegexOptions] 'Singleline,IgnoreCase' 
    $OutputToCheck = [Regex]::Matches($Body, '(?<=<script)(.*?)(?=<\/script>)', $options) | Select-Object -ExpandProperty Value
    foreach ($Script in $OutputToCheck) {
        $Body = $Body.Replace("<script$Script</script>", '')
    }

    if ($Script:EmailSchema -and $Script:EmailSchema['AttachSelf']) {

        $AttachSelfBody = New-HTML -Online:$HTMLOnline {
            if ($Parameter) {

                [Array] $ArrayParamerers = foreach ($Key in $Parameter.Keys) {
                    , $Parameter[$Key]
                }
                $Template = Add-ParametersToScriptBlock -ScriptBlock $EmailBody -Parameter $Parameter
                if ($SpanRequired) {
                    New-HTMLSpanStyle @newHTMLSplat {
                        Invoke-Command -ScriptBlock $Template -ArgumentList $ArrayParamerers
                    }
                } else {
                    Invoke-Command -ScriptBlock $Template -ArgumentList $ArrayParamerers
                }
            } else {
                if ($SpanRequired) {
                    New-HTMLSpanStyle @newHTMLSplat {
                        Invoke-Command -ScriptBlock $EmailBody
                    }
                } else {
                    Invoke-Command -ScriptBlock $EmailBody
                }
            }
        } -Format:$Format -Minify:$Minify

        @{
            Body           = $Body
            AttachSelfBody = $AttachSelfBody
        }
    } else {

        $Body
    }

}

Register-ArgumentCompleter -CommandName EmailBody -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName EmailBody -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function EmailCC {
    <#
    .SYNOPSIS
    Creates an email object for carbon copy (CC) recipients.
 
    .DESCRIPTION
    This function creates an email object specifically for carbon copy (CC) recipients. It takes an array of email addresses and returns a custom object representing the CC recipients.
 
    .PARAMETER Addresses
    Specifies an array of email addresses for the CC recipients.
 
    .EXAMPLE
    $CCRecipients = EmailCC -Addresses 'cc1@example.com', 'cc2@example.com'
    Creates an email object with two CC recipients.
 
    .EXAMPLE
    $CCList = 'cc1@example.com', 'cc2@example.com'
    $CCRecipients = EmailCC -Addresses $CCList
    Creates an email object with multiple CC recipients.
    #>

    [CmdletBinding()]
    param(
        [string[]] $Addresses
    )

    [PsCustomObject] @{
        Type      = 'HeaderCC'
        Addresses = $Addresses
    }
}
function EmailFrom {
    <#
    .SYNOPSIS
    Creates an email object for the sender address.
 
    .DESCRIPTION
    This function creates an email object specifically for the sender address. It takes a single email address as input and returns a custom object representing the sender address.
 
    .PARAMETER Address
    Specifies the email address of the sender.
 
    .EXAMPLE
    $Sender = EmailFrom -Address 'sender@example.com'
    Creates an email object with the specified sender address.
 
    #>

    [CmdletBinding()]
    param(
        [string] $Address
    )

    [PsCustomObject] @{
        Type    = 'HeaderFrom'
        Address = $Address
    }
}
function EmailHeader {
    <#
    .SYNOPSIS
    Creates email headers based on the provided ScriptBlock.
 
    .DESCRIPTION
    This function creates email headers using the specified ScriptBlock. It allows customization of email headers before sending an email.
 
    .PARAMETER EmailHeader
    Specifies the ScriptBlock containing the email header content.
 
    .EXAMPLE
    $EmailHeader = {
        'From: sender@example.com'
        'To: recipient@example.com'
        'Subject: Hello World'
    }
    $Headers = EmailHeader -EmailHeader $EmailHeader
    Creates email headers with sender, recipient, and subject information.
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $EmailHeader
    )
    $EmailHeaders = Invoke-Command -ScriptBlock $EmailHeader
    $EmailHeaders
}
function EmailLayout {
    <#
    .SYNOPSIS
    Defines the layout structure for an email content.
 
    .DESCRIPTION
    This function defines the layout structure for an email content using a ScriptBlock. It generates an HTML table layout for the email content.
 
    .PARAMETER Layout
    Specifies the ScriptBlock containing the layout structure for the email content.
 
    .EXAMPLE
    $EmailLayout = {
        'Content goes here'
    }
    EmailLayout -Layout $EmailLayout
    Generates an HTML table layout for the email content with the specified content.
 
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $Layout
    )

    Enable-HTMLFeature -Feature EmailLayout

    if ($Layout) {
        $Output = & $Layout

        if ($Output) {
            New-HTMLTag -Tag 'table' -Attributes @{class = 'layoutTable'; width = '100%' } {
                New-HTMLTag -Tag 'tbody' -Attributes @{ class = 'layoutTableBody' } {
                    New-HTMLTag -Tag 'tr' -Attributes @{ class = 'layoutTableRow' } {
                        $Output
                    }
                }
            }
        }
    }
}
function EmailLayoutColumn {
    <#
    .SYNOPSIS
    Defines the layout structure for a column in an email content.
 
    .DESCRIPTION
    This function defines the layout structure for a column in an email content using specified parameters. It allows customization of column layout properties such as width, alignment, padding, and borders.
 
    .PARAMETER ColumnLayout
    Specifies the ScriptBlock containing the content of the column.
 
    .PARAMETER Width
    Specifies the width of the column.
 
    .PARAMETER Alignment
    Specifies the alignment of the content within the column. Valid values are 'left', 'center', 'right', or 'justify'.
 
    .PARAMETER Padding
    Specifies the padding for all sides of the column.
 
    .PARAMETER PaddingTop
    Specifies the padding for the top side of the column.
 
    .PARAMETER PaddingRight
    Specifies the padding for the right side of the column.
 
    .PARAMETER PaddingBottom
    Specifies the padding for the bottom side of the column.
 
    .PARAMETER PaddingLeft
    Specifies the padding for the left side of the column.
 
    .PARAMETER BorderTopStyle
    Specifies the style of the top border. Valid values are 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'.
 
    .PARAMETER BorderTopColor
    Specifies the color of the top border.
 
    .PARAMETER BorderTopWidthSize
    Specifies the width of the top border.
 
    .PARAMETER BorderBottomStyle
    Specifies the style of the bottom border. Valid values are 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'.
 
    .PARAMETER BorderBottomColor
    Specifies the color of the bottom border.
 
    .PARAMETER BorderBottomWidthSize
    Specifies the width of the bottom border.
 
    .PARAMETER BorderLeftStyle
    Specifies the style of the left border. Valid values are 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'.
 
    .PARAMETER BorderLeftColor
    Specifies the color of the left border.
 
    .PARAMETER BorderLeftWidthSize
    Specifies the width of the left border.
 
    .PARAMETER BorderRightStyle
    Specifies the style of the right border. Valid values are 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'.
 
    .PARAMETER BorderRightColor
    Specifies the color of the right border.
 
    .PARAMETER BorderRightWidthSize
    Specifies the width of the right border.
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $ColumnLayout,
        [string] $Width,
        [string] $Alignment,
        [string] $Padding,
        [string] $PaddingTop,
        [string] $PaddingRight,
        [string] $PaddingBottom,
        [string] $PaddingLeft,
        [Parameter()][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderTopStyle,
        [Parameter()][string] $BorderTopColor,
        [Parameter()][string] $BorderTopWidthSize = 0,
        [Parameter()][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderBottomStyle,
        [Parameter()][string] $BorderBottomColor,
        [Parameter()][string] $BorderBottomWidthSize = 0,
        [Parameter()][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderLeftStyle,
        [Parameter()][string] $BorderLeftColor,
        [Parameter()][string] $BorderLeftWidthSize = 0,
        [Parameter()][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderRightStyle,
        [Parameter()][string] $BorderRightColor,
        [Parameter()][string] $BorderRightWidthSize = 0
    )

    if ($ColumnLayout) {
        $Output = & $ColumnLayout

        $Style = @{
            'padding'             = ConvertFrom-Size -Size $Padding
            'padding-top'         = ConvertFrom-Size -Size $PaddingTop
            'padding-right'       = ConvertFrom-Size -Size $PaddingRight
            'padding-bottom'      = ConvertFrom-Size -Size $PaddingBottom
            'padding-left'        = ConvertFrom-Size -Size $PaddingLeft
            'max-width'           = ConvertFrom-Size -Size $Width
            "text-align"          = $Alignment
            'border-top-width'    = $BorderTopWidthSize
            'border-bottom-width' = $BorderBottomWidthSize
            'border-left-width'   = $BorderLeftWidthSize
            'border-right-width'  = $BorderRightWidthSize
            'border-top-color'    = $BorderTopColor
            'border-bottom-color' = $BorderBottomColor
            'border-left-color'   = $BorderLeftColor
            'border-right-color'  = $BorderRightColor
            'border-top-style'    = $BorderTopStyle
            'border-bottom-style' = $BorderBottomStyle
            'border-left-style'   = $BorderLeftStyle
            'border-right-style'  = $BorderRightStyle
        }
        New-HTMLTag -Tag 'td' -Attributes @{ class = 'layoutTableColumn'; width = $HTMLWidth; align = $Alignment; style = $Style } {
            $Output
        }
    }
}
function EmailLayoutRow {
    <#
    .SYNOPSIS
    Defines the layout structure for a row in an email content.
 
    .DESCRIPTION
    This function defines the layout structure for a row in an email content using a ScriptBlock. It generates an HTML table row layout for the email content.
 
    .PARAMETER RowLayout
    Specifies the ScriptBlock containing the content of the row.
 
    .PARAMETER Height
    Specifies the height of the row.
 
    .PARAMETER BorderTopStyle
    Specifies the style of the top border. Valid values are 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'.
 
    .PARAMETER BorderTopColor
    Specifies the color of the top border.
 
    .PARAMETER BorderTopWidthSize
    Specifies the width of the top border.
 
    .PARAMETER BorderBottomStyle
    Specifies the style of the bottom border. Valid values are 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'.
 
    .PARAMETER BorderBottomColor
    Specifies the color of the bottom border.
 
    .PARAMETER BorderBottomWidthSize
    Specifies the width of the bottom border.
 
    .PARAMETER BorderLeftStyle
    Specifies the style of the left border. Valid values are 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'.
 
    .PARAMETER BorderLeftColor
    Specifies the color of the left border.
 
    .PARAMETER BorderLeftWidthSize
    Specifies the width of the left border.
 
    .PARAMETER BorderRightStyle
    Specifies the style of the right border. Valid values are 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'.
 
    .PARAMETER BorderRightColor
    Specifies the color of the right border.
 
    .PARAMETER BorderRightWidthSize
    Specifies the width of the right border.
 
    .EXAMPLE
    $RowLayout = {
        'Column 1 content'
        'Column 2 content'
    }
    $Row = EmailLayoutRow -RowLayout $RowLayout -Height '50px' -BorderTopStyle 'solid' -BorderTopColor 'black' -BorderTopWidthSize '1px' -BorderBottomStyle 'dotted' -BorderBottomColor 'gray' -BorderBottomWidthSize '2px' -BorderLeftStyle 'dashed' -BorderLeftColor 'blue' -BorderLeftWidthSize '1px' -BorderRightStyle 'dotted' -BorderRightColor 'green' -BorderRightWidthSize '1px'
    Generates an HTML table row layout with specified content and border styles.
 
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $RowLayout,
        [string] $Height,
        [Parameter()][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderTopStyle,
        [Parameter()][string] $BorderTopColor,
        [Parameter()][string] $BorderTopWidthSize,
        [Parameter()][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderBottomStyle,
        [Parameter()][string] $BorderBottomColor,
        [Parameter()][string] $BorderBottomWidthSize,
        [Parameter()][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderLeftStyle,
        [Parameter()][string] $BorderLeftColor,
        [Parameter()][string] $BorderLeftWidthSize,
        [Parameter()][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderRightStyle,
        [Parameter()][string] $BorderRightColor,
        [Parameter()][string] $BorderRightWidthSize
    )
    if ($RowLayout) {
        $Output = & $RowLayout

        $HTMLHeight = ConvertFrom-Size -Size $Height
        $Style = @{
            'height'              = $HTMLHeight
            'border-top-width'    = $BorderTopWidthSize
            'border-bottom-width' = $BorderBottomWidthSize
            'border-left-width'   = $BorderLeftWidthSize
            'border-right-width'  = $BorderRightWidthSize
            'border-top-color'    = $BorderTopColor
            'border-bottom-color' = $BorderBottomColor
            'border-left-color'   = $BorderLeftColor
            'border-right-color'  = $BorderRightColor
            'border-top-style'    = $BorderTopStyle
            'border-bottom-style' = $BorderBottomStyle
            'border-left-style'   = $BorderLeftStyle
            'border-right-style'  = $BorderRightStyle
        }
        New-HTMLTag -Tag 'table' -Attributes @{ class = 'layoutTable' } {
            New-HTMLTag -Tag 'tbody' {
                New-HTMLTag -Tag 'tr' -Attributes @{class = 'layoutTableRow'; style = $Style } {
                    $Output
                }
            }
        }

    }
}
function EmailListItem {
    <#
    .SYNOPSIS
    Creates an HTML list item with customizable text styling.
 
    .DESCRIPTION
    The EmailListItem function generates an HTML list item with customizable text styling options such as color, font size, font weight, font style, alignment, text decoration, text transformation, and more.
 
    .PARAMETER Text
    Specifies the text content of the list item.
 
    .PARAMETER Color
    Specifies the color of the text. Default is an empty array.
 
    .PARAMETER BackGroundColor
    Specifies the background color of the list item. Default is an empty array.
 
    .PARAMETER FontSize
    Specifies the font size of the text. Default is an empty array.
 
    .PARAMETER FontWeight
    Specifies the font weight of the text. Valid values are 'normal', 'bold', 'bolder', 'lighter', and numeric values from 100 to 900. Default is an empty array.
 
    .PARAMETER FontStyle
    Specifies the font style of the text. Valid values are 'normal', 'italic', 'oblique'. Default is an empty array.
 
    .PARAMETER FontVariant
    Specifies the font variant of the text. Valid values are 'normal', 'small-caps'. Default is an empty array.
 
    .PARAMETER FontFamily
    Specifies the font family of the text. Default is an empty array.
 
    .PARAMETER Alignment
    Specifies the alignment of the text. Valid values are 'left', 'center', 'right', 'justify'. Default is an empty array.
 
    .PARAMETER TextDecoration
    Specifies the text decoration of the text. Valid values are 'none', 'line-through', 'overline', 'underline'. Default is an empty array.
 
    .PARAMETER TextTransform
    Specifies the text transformation of the text. Valid values are 'uppercase', 'lowercase', 'capitalize'. Default is an empty array.
 
    .PARAMETER Direction
    Specifies the direction of the text. Valid value is 'rtl'. Default is an empty array.
 
    .PARAMETER LineBreak
    Indicates whether a line break should be added after the list item.
 
    .EXAMPLE
    EmailListItem -Text "Example Text" -Color "red" -FontSize 14 -FontWeight "bold" -Alignment "center"
    Creates an HTML list item with the text "Example Text" in red color, font size 14, bold font weight, and centered alignment.
 
    #>

    [CmdletBinding()]
    param(
        [string[]] $Text,
        [string[]] $Color = @(),
        [string[]] $BackGroundColor = @(),
        [int[]] $FontSize = @(),
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string[]] $FontWeight = @(),
        [ValidateSet('normal', 'italic', 'oblique')][string[]] $FontStyle = @(),
        [ValidateSet('normal', 'small-caps')][string[]] $FontVariant = @(),
        [string[]] $FontFamily = @(),
        [ValidateSet('left', 'center', 'right', 'justify')][string[]] $Alignment = @(),
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string[]] $TextDecoration = @(),
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string[]] $TextTransform = @(),
        [ValidateSet('rtl')][string[]] $Direction = @(),
        [switch] $LineBreak
    )

    $newHTMLTextSplat = @{
        Alignment       = $Alignment
        FontSize        = $FontSize
        TextTransform   = $TextTransform
        Text            = $Text
        Color           = $Color
        FontFamily      = $FontFamily
        Direction       = $Direction
        FontStyle       = $FontStyle
        TextDecoration  = $TextDecoration
        BackGroundColor = $BackGroundColor
        FontVariant     = $FontVariant
        FontWeight      = $FontWeight
        LineBreak       = $LineBreak
    }

    New-HTMLListItem @newHTMLTextSplat
}
Register-ArgumentCompleter -CommandName EmailListItem -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName EmailListItem -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function EmailOptions {
    <#
    .SYNOPSIS
    Configures email options for sending notifications.
 
    .DESCRIPTION
    This function sets the priority and delivery notifications for email notifications.
 
    .PARAMETER Priority
    Specifies the priority of the email. Valid values are 'Low', 'Normal', or 'High'.
 
    .PARAMETER DeliveryNotifications
    Specifies when delivery notifications should be sent. Valid values are 'None', 'OnSuccess', 'OnFailure', 'Delay', or 'Never'.
 
    .EXAMPLE
    EmailOptions -Priority 'High' -DeliveryNotifications 'OnSuccess'
    Configures email options with High priority and delivery notifications on success.
 
    .EXAMPLE
    EmailOptions
    Configures email options with default settings.
 
    #>

    [CmdletBinding()]
    param(
        [ValidateSet('Low', 'Normal', 'High')] [string] $Priority = 'Normal',
        [ValidateSet('None', 'OnSuccess', 'OnFailure', 'Delay', 'Never')] $DeliveryNotifications = 'None',
        [Obsolete("Encoding is depracated. We're setting encoding to UTF8 always to prevent errors")]
        [Parameter(DontShow)][string] $Encoding
    )

    [PsCustomObject] @{
        Type                  = 'HeaderOptions'

        DeliveryNotifications = $DeliveryNotifications
        Priority              = $Priority
    }
}
function EmailReplyTo {
    <#
    .SYNOPSIS
    Defines the email address to which replies should be directed.
 
    .DESCRIPTION
    This function sets the email address to which replies should be directed when receiving notifications.
 
    .PARAMETER Address
    Specifies the email address to which replies should be directed.
 
    .EXAMPLE
    EmailReplyTo -Address "reply@example.com"
    Sets the email address to which replies should be directed to "reply@example.com".
 
    #>

    [CmdletBinding()]
    param(
        [string] $Address
    )

    [PsCustomObject] @{
        Type    = 'HeaderReplyTo'
        Address = $Address
    }
}
function EmailServer {
    <#
    .SYNOPSIS
    Defines the email server configuration for sending emails.
 
    .DESCRIPTION
    This function sets up the email server configuration including server address, port, authentication credentials, and SSL settings.
 
    .PARAMETER Server
    Specifies the address of the email server.
 
    .PARAMETER Port
    Specifies the port number of the email server. Default is 587.
 
    .PARAMETER UserName
    Specifies the username for authentication with the email server.
 
    .PARAMETER Password
    Specifies the password for authentication with the email server.
 
    .PARAMETER PasswordAsSecure
    Indicates that the password should be treated as a secure string.
 
    .PARAMETER PasswordFromFile
    Indicates that the password should be read from a file.
 
    .PARAMETER SSL
    Indicates whether SSL should be used for the connection.
 
    .PARAMETER UseDefaultCredential
    Specifies whether to use default credentials for authentication.
 
    .EXAMPLE
    EmailServer -Server "mail.example.com" -Port 587 -UserName "user@example.com" -Password "P@ssw0rd" -SSL
    Configures the email server with the specified server address, port, username, password, and SSL enabled.
 
    #>

    [CmdletBinding()]
    param(
        [string] $Server,
        [int] $Port = 587,
        [string] $UserName,
        [string] $Password,
        [switch] $PasswordAsSecure,
        [switch] $PasswordFromFile,
        [switch] $SSL,
        [alias('UseDefaultCredentials')][switch] $UseDefaultCredential
    )

    [PsCustomObject] @{
        Type                  = 'HeaderServer'
        Server                = $Server
        Port                  = $Port
        UserName              = $UserName
        Password              = $Password
        PasswordAsSecure      = $PasswordAsSecure
        PasswordFromFile      = $PasswordFromFile
        SSL                   = $SSL
        UseDefaultCredentials = $UseDefaultCredential
    }
}
function EmailSubject {
    <#
    .SYNOPSIS
    Defines the subject line for an email.
 
    .DESCRIPTION
    This function sets the subject line for an email message.
 
    .PARAMETER Subject
    Specifies the subject line of the email.
 
    .EXAMPLE
    EmailSubject -Subject "Important Notification"
    Sets the subject line of the email to "Important Notification".
 
    #>

    [CmdletBinding()]
    param(
        [string] $Subject
    )

    [PsCustomObject] @{
        Type    = 'HeaderSubject'
        Subject = $Subject
    }
}

function EmailTo {
    <#
    .SYNOPSIS
    Defines the email addresses to which the email should be sent.
 
    .DESCRIPTION
    This function sets the email addresses to which the email should be sent.
 
    .PARAMETER Addresses
    Specifies an array of email addresses to which the email should be sent.
 
    .EXAMPLE
    EmailTo -Addresses "user1@example.com", "user2@example.com"
    Sets the email addresses to which the email should be sent to "user1@example.com" and "user2@example.com".
 
    #>

    [CmdletBinding()]
    param(
        [string[]] $Addresses
    )

    [PsCustomObject] @{
        Type      = 'HeaderTo'
        Addresses = $Addresses
    }
}
function Enable-HTMLFeature {
    <#
    .SYNOPSIS
    Provides a way to enable existing feature or extending PSWriteHTML
 
    .DESCRIPTION
    Provides a way to enable existing feature or extending PSWriteHTML
 
    .PARAMETER Feature
    Choose one of the existing features or define them via extension
 
    .PARAMETER Configuration
    Provide hashtable with configuration of libraries
 
    .EXAMPLE
    Enable-HTMLFeature -Feature Raphael, Mapael, Jquery, JQueryMouseWheel, "MapaelMaps_$Map"
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [string[]] $Feature,
        [System.Collections.IDictionary] $Configuration
    )
    if (-not $Script:HTMLSchema) {
        Write-Warning -Message "Enable-HTMLFeature - no HTMLSchema defined. Using cmdlets outside of New-HTML will not generate missing CSS/JS content."
        return
    }
    foreach ($F in $Feature) {
        $Script:HTMLSchema.Features.$F = $true
    }
    if ($Configuration) {

        foreach ($Library in $Configuration.Keys) {
            $Script:CurrentConfiguration['Features'][$Library] = $Configuration[$Library]
        }
    }
}

Register-ArgumentCompleter -CommandName Enable-HTMLFeature -ParameterName Feature -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $Script:CurrentConfiguration.Features.Keys | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
}
function New-AccordionItem {
    <#
    .SYNOPSIS
    Creates a new accordion item with specified header text and panel text.
 
    .DESCRIPTION
    This function creates a new accordion item with the provided header text and panel text. The header text is displayed as a clickable button to toggle the visibility of the panel text.
 
    .PARAMETER HeaderText
    The text to be displayed as the header of the accordion item.
 
    .PARAMETER HeaderAlign
    The alignment of the header text. Accepted values are 'left', 'center', or 'right'.
 
    .PARAMETER PanelText
    The text to be displayed in the panel of the accordion item.
 
    .EXAMPLE
    New-AccordionItem -HeaderText "Header 1" -HeaderAlign "left" -PanelText "Panel 1 Text"
 
    Creates a new accordion item with the header text "Header 1" aligned to the left and panel text "Panel 1 Text".
 
    .EXAMPLE
    New-AccordionItem -HeaderText "Header 2" -HeaderAlign "center" -PanelText "Panel 2 Text"
 
    Creates a new accordion item with the header text "Header 2" aligned to the center and panel text "Panel 2 Text".
    #>

    [cmdletBinding()]
    param(
        [string] $HeaderText,
        [string] $HeaderAlign,
        [string] $PanelText
    )

    New-HTMLTag -Tag 'div' -Attributes @{ class = 'ac' } {
        New-HTMLTag -Tag 'h2' -Attributes @{ class = 'ac-header'; } {

            New-HTMLTag -Tag 'button' -Attributes @{ class = 'ac-trigger' } {
                $HeaderText
            }
        }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'ac-panel' } {
            New-HTMLTag -Tag 'p' -Attributes @{ class = 'ac-text' } {

                $PanelText
            }
        }
    }
}
function New-CalendarEvent {
    <#
    .SYNOPSIS
    Allows adding new calendar events to calendar
 
    .DESCRIPTION
    Allows adding new calendar events to calendar
 
    .PARAMETER Title
    The text that will appear on an event.
 
    .PARAMETER Description
    The text that will appear on an event when hovering over
 
    .PARAMETER StartDate
    When your event begins. If your event is explicitly allDay, hour, minutes, seconds and milliseconds will be ignored.
 
    .PARAMETER EndDate
    hen your event ends. If your event is explicitly allDay, hour, minutes, seconds and milliseconds will be ignored. If omitted, your events will appear to have the default duration.
 
    .PARAMETER Constraint
    A groupId belonging to other events, "businessHours" or "availableForMeeting"
 
    .PARAMETER Color
    An alias for specifying the backgroundColor and borderColor at the same time.
 
    .PARAMETER BackgroundColor
    Sets backround color. Only works if EndDate is provided.
 
    .PARAMETER BorderColor
    Sets border color. Only works if EndDate is provided.
 
    .PARAMETER TextColor
    Sets color of Text. Only works if EndDate is provided.
 
    .PARAMETER Url
    A URL that will be visited when this event is clicked by the user.
 
    .PARAMETER TargetName
    Specifies the name of the target frame or window. Default is _self
 
    .PARAMETER AllDayEvent
    Determines if the event is shown in the “all-day” section of the view, if applicable. Determines if time text is displayed in the event. If this value is not specified, it will be inferred by the start and end properties
 
    .EXAMPLE
    New-HTMLCalendar {
        New-CalendarEvent -Title 'Active Directory Meeting' -Description 'We will talk about stuff' -StartDate (Get-Date)
        # End date is required for the colors to work...
        New-CalendarEvent -Title 'Lunch' -StartDate (Get-Date).AddDays(2).AddHours(-3) -EndDate (Get-Date).AddDays(3) -Description 'Very long lunch' -TextColor DeepSkyBlue -BackgroundColor yellow -BorderColor Red
    }
 
    .NOTES
    Keep in mind that colors like background, border, text work only if there's EndDate. If there's no end date the color "color" is used only
    #>

    [alias('CalendarEvent')]
    [CmdletBinding()]
    param(
        [string] $Title,
        [string] $Description,
        [DateTime] $StartDate,
        [nullable[DateTime]] $EndDate,
        [string] $Constraint,
        [string] $Color,
        [string] $BackgroundColor,
        [string] $BorderColor,
        [string] $TextColor,
        [alias('Uri')][string] $Url,
        [switch] $AllDayEvent,
        [string] $TargetName
    )
    $Object = [PSCustomObject] @{
        Type     = 'CalendarEvent'
        Settings = [ordered] @{
            title           = $Title
            description     = $Description
            constraint      = $Constraint
            color           = ConvertFrom-Color -Color $Color
            backgroundColor = ConvertFrom-Color -Color $BackgroundColor
            borderColor     = ConvertFrom-Color -Color $BorderColor
            textColor       = ConvertFrom-Color -Color $TextColor
            url             = $Url
            targetName      = $targetName
        }
    }
    if ($AllDayEvent) {
        $Object.Settings.allDay = $true
    }
    if ($StartDate) {
        $Object.Settings.start = Get-Date -Date ($StartDate) -Format "yyyy-MM-ddTHH:mm:ss"
    }
    if ($EndDate) {
        $Object.Settings.end = Get-Date -Date ($EndDate) -Format "yyyy-MM-ddTHH:mm:ss"
    }

    Remove-EmptyValue -Hashtable $Object.Settings -Recursive 
    $Object
}
Register-ArgumentCompleter -CommandName New-CalendarEvent -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-CalendarEvent -ParameterName BorderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-CalendarEvent -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-CalendarEvent -ParameterName TextColor -ScriptBlock $Script:ScriptBlockColors
function New-CarouselSlide {
    <#
    .SYNOPSIS
    Creates a new carousel slide with specified content, background color, and height.
 
    .DESCRIPTION
    This function creates a new carousel slide with the provided content, background color, and height. The slide content is specified using a script block, the background color is a string representing a color, and the height is a numerical value.
 
    .PARAMETER SlideContent
    The script block containing the content to be displayed on the slide.
 
    .PARAMETER BackgroundColor
    The background color of the slide specified as a string.
 
    .PARAMETER Height
    The height of the slide.
 
    .EXAMPLE
    New-CarouselSlide -SlideContent { "This is the content of the slide" } -BackgroundColor 'blue' -Height 200
 
    Creates a new carousel slide with the specified content, background color 'blue', and height of 200 pixels.
 
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $SlideContent,
        [string] $BackgroundColor,
        $Height
    )
    $Style = @{
        'background-color' = ConvertFrom-Color -Color $BackgroundColor
        height             = $Height
    }
    Remove-EmptyValue -Hashtable $Style
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'slide'; style = $Style } {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'flexElement' } {
            if ($SlideContent) {
                & $SlideContent
            }
        }
    }
}
function New-ChartAxisX {
    <#
    .SYNOPSIS
    Creates a new X-axis for a chart with specified parameters.
 
    .DESCRIPTION
    This function creates a new X-axis for a chart with the provided parameters. It allows customization of the axis name, title, type, minimum and maximum values, and timezone offset.
 
    .PARAMETER Names
    An array of names for the X-axis categories.
 
    .PARAMETER TitleText
    The title text to be displayed for the X-axis.
 
    .PARAMETER Type
    The type of the X-axis data. Accepted values are 'datetime', 'category', or 'numeric'. Default is 'category'.
 
    .PARAMETER MinValue
    The minimum value of the X-axis.
 
    .PARAMETER MaxValue
    The maximum value of the X-axis.
 
    .PARAMETER TimeZoneOffset
    The timezone offset to adjust the X-axis values.
 
    .EXAMPLE
    New-ChartAxisX -Names @('Jan', 'Feb', 'Mar') -TitleText 'Months' -Type 'category'
 
    Creates a new X-axis with categories 'Jan', 'Feb', 'Mar' and title 'Months'.
 
    .EXAMPLE
    New-ChartAxisX -Names @('2020-01-01', '2020-02-01', '2020-03-01') -TitleText 'Dates' -Type 'datetime' -MinValue (Get-Date '2020-01-01') -MaxValue (Get-Date '2020-03-01') -TimeZoneOffset '+02:00'
 
    Creates a new X-axis with datetime values, title 'Dates', and timezone offset of +2 hours.
    #>

    [alias('ChartCategory', 'ChartAxisX', 'New-ChartCategory')]
    [CmdletBinding()]
    param(
        [alias('Name')][Array] $Names,
        [alias('Title')][string] $TitleText,
        [ValidateSet('datetime', 'category', 'numeric')][string] $Type = 'category',
        [object] $MinValue,
        [object] $MaxValue,

        [string] $TimeZoneOffset
        #[ValidateSet('top', 'topRight', 'left', 'right', 'bottom', '')][string] $LegendPosition = '',
        # [string[]] $Color
    )

    $offsetMilliseconds = 0
    if ($TimeZoneOffset) {
        $offsetMilliseconds = ([System.TimeSpan]::Parse($TimeZoneOffset)).TotalMilliseconds
    }

    if ($MinValue -is [DateTime] -or $MaxValue -is [DateTime]) {
        $Type = 'datetime'
    }
    switch ($Type) {
        'datetime' {
            if ($MinValue -is [System.DateTime]) {
                $MinValue = [int64]([System.DateTimeOffset]$MinValue).ToUnixTimeMilliseconds() + $offsetMilliseconds
            }

            if ($MaxValue -is [System.DateTime]) {
                $MaxValue = [int64]([System.DateTimeOffset]$MaxValue).ToUnixTimeMilliseconds() + $offsetMilliseconds
            }
        }
        Default {
            $MinValue = [int]$MinValue
            $MaxValue = [int]$MaxValue
        }
    }
    [PSCustomObject] @{
        ObjectType = 'ChartAxisX'
        ChartAxisX = @{
            Names     = $Names
            Type      = $Type
            TitleText = $TitleText
            Min       = $MinValue
            Max       = $MaxValue
        }

    }

}

function New-ChartAxisY {
    <#
    .SYNOPSIS
    Creates a new Y-axis for a chart with customizable parameters.
 
    .DESCRIPTION
    This function creates a new Y-axis for a chart with the provided parameters. It allows customization of the axis visibility, title, labels, range, style, and other options.
 
    .PARAMETER Show
    Indicates whether the Y-axis should be displayed.
 
    .PARAMETER ShowAlways
    Indicates whether the Y-axis should always be visible.
 
    .PARAMETER TitleText
    The text to be displayed as the title of the Y-axis.
 
    .PARAMETER TitleRotate
    The rotation angle of the Y-axis title. Accepted values are '90' or '270'.
 
    .PARAMETER TitleOffsetX
    The horizontal offset of the Y-axis title.
 
    .PARAMETER TitleOffsetY
    The vertical offset of the Y-axis title.
 
    .PARAMETER TitleFontWeight
    The font weight of the Y-axis title.
 
    .PARAMETER TitleColor
    The color of the Y-axis title.
 
    .PARAMETER TitleFontSize
    The font size of the Y-axis title.
 
    .PARAMETER TitleFontFamily
    The font family of the Y-axis title.
 
    .PARAMETER MinValue
    The minimum value of the Y-axis.
 
    .PARAMETER MaxValue
    The maximum value of the Y-axis.
 
    .PARAMETER LabelMinWidth
    The minimum width of the Y-axis labels.
 
    .PARAMETER LabelMaxWidth
    The maximum width of the Y-axis labels.
 
    .PARAMETER LabelAlign
    The alignment of the Y-axis labels. Accepted values are 'left', 'center', or 'right'.
 
    .PARAMETER LabelFontSize
    The font size of the Y-axis labels.
 
    .PARAMETER LabelFontFamily
    The font family of the Y-axis labels.
 
    .PARAMETER LabelFontWeight
    The font weight of the Y-axis labels.
 
    .PARAMETER LabelFontColor
    The color of the Y-axis labels.
 
    .PARAMETER Reversed
    Indicates whether the Y-axis should be reversed.
 
    .PARAMETER Opposite
    Indicates whether the Y-axis should be positioned on the opposite side.
 
    .PARAMETER Logarithmic
    Indicates whether the Y-axis should have a logarithmic scale.
 
    .PARAMETER ForceNiceScale
    Indicates whether the Y-axis should have a nice scale.
 
    .PARAMETER Floating
    Indicates whether the Y-axis should be floating.
 
    .PARAMETER SeriesName
    The name of the series associated with the Y-axis.
 
    .EXAMPLE
    New-ChartAxisY -Show -TitleText 'Sales' -MinValue 0 -MaxValue 1000 -LabelAlign 'center'
 
    Creates a new Y-axis with the title 'Sales', range from 0 to 1000, and center-aligned labels.
 
    .EXAMPLE
    New-ChartAxisY -Show -ShowAlways -TitleText 'Temperature' -TitleColor 'red' -LabelAlign 'right'
 
    Creates a new Y-axis with the title 'Temperature', always visible, red-colored title, and right-aligned labels.
    #>

    [alias('ChartAxisY')]
    [CmdletBinding()]
    param(
        [switch] $Show,
        [switch] $ShowAlways,
        [string] $TitleText,
        [ValidateSet('90', '270')][string] $TitleRotate,
        [int] $TitleOffsetX = 0,
        [int] $TitleOffsetY = 0,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $TitleFontWeight,
        [alias('TitleStyleColor')][string] $TitleColor,
        [alias('TitleStyleFontSize')][int] $TitleFontSize, # = 12,
        [alias('TitleStyleFontFamily')][string] $TitleFontFamily, # = 'Helvetica, Arial, sans-serif',
        [int] $MinValue,
        [int] $MaxValue,
        [int] $LabelMinWidth = -1,
        [int] $LabelMaxWidth,
        [ValidateSet('left', 'center', 'right')][string] $LabelAlign,
        [object] $LabelFontSize,
        [string] $LabelFontFamily,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $LabelFontWeight,
        [string[]] $LabelFontColor,
        [switch] $Reversed,
        [switch] $Opposite,
        [switch] $Logarithmic,
        [switch] $ForceNiceScale,
        [switch] $Floating,
        [string] $SeriesName
    )
    $Object = [PSCustomObject] @{
        ObjectType = 'ChartAxisY'
        ChartAxisY = @{
            show       = $Show.IsPresent
            showAlways = $ShowAlways.IsPresent
            seriesName = $SeriesName
        }
    }
    if ($Reversed) {
        $Object.ChartAxisY.reversed = $true
    }
    if ($Opposite) {
        $Object.ChartAxisY.opposite = $true
    }
    if ($Logarithmic) {
        $Object.ChartAxisY.logarithmic = $true
    }
    if ($ForceNiceScale) {
        $Object.ChartAxisY.forceNiceScale = $true
    }
    if ($Floating) {
        $Object.ChartAxisY.floating = $true
    }
    if ($MinValue) {
        $Object.ChartAxisY.min = $MinValue
    }
    if ($MaxValue) {
        $Object.ChartAxisY.max = $MaxValue
    }

    $Object.ChartAxisY.title = @{}
    if ($TitleText) {
        $Object.ChartAxisY.title.text = $TitleText
    }
    if ($TitleRotate) {
        $Object.ChartAxisY.title.rotate = [int] $TitleRotate
    }
    if ($TitleOffsetX) {
        $Object.ChartAxisY.title.offsetX = $TitleOffsetX
    }
    if ($TitleOffsetY) {
        $Object.ChartAxisY.title.offsetY = $TitleOffsetY
    }
    if ($TitleColor -or $TitleFontSize -or $TitleFontFamily) {
        $Object.ChartAxisY.title.style = @{}
        if ($TitleColor) {
            $Object.ChartAxisY.title.style.color = ConvertFrom-Color -Color $TitleColor
        }
        if ($TitleFontSize) {
            $Object.ChartAxisY.title.style.fontSize = ConvertFrom-Size -Size $TitleFontSize
        }
        if ($TitleFontFamily) {
            $Object.ChartAxisY.title.style.fontFamily = $TitleFontFamily
        }
        if ($TitleFontWeight) {
            $Object.ChartAxisY.title.style.fontWeight = $TitleFontWeight
        }
    }

    $Object.ChartAxisY.labels = @{}
    if ($LabelAlign) {
        $Object.ChartAxisY.labels.align = $LabelAlign
    }
    if ($LabelMinWidth -ne -1) {
        $Object.ChartAxisY.labels.minWidth = $LabelMinWidth
    }
    if ($LabelMaxWidth) {
        $Object.ChartAxisY.labels.maxWidth = $LabelMaxWidth
    }
    if ($LabelFontSize -or $LabelFontFamily -or $LabelFontWeight -or $LabelFontColor) {
        $Object.ChartAxisY.labels.style = @{}

        if ($LabelFontSize) {
            $Object.ChartAxisY.labels.style.fontSize = ConvertFrom-Size -Size $LabelFontSize
        }
        if ($LabelFontFamily) {
            $Object.ChartAxisY.labels.style.fontFamily = $LabelFontFamily
        }
        if ($LabelFontWeight) {
            $Object.ChartAxisY.labels.style.fontWeight = $LabelFontWeight
        }
        if ($LabelFontColor) {
            $Object.ChartAxisY.labels.style.colors = @($LabelFontColor)
        }
    }
    Remove-EmptyValue -Hashtable $Object.ChartAxisY -Recursive -Rerun 2
    $Object

}
Register-ArgumentCompleter -CommandName New-ChartAxisY -ParameterName TitleColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-ChartAxisY -ParameterName LabelFontColor -ScriptBlock $Script:ScriptBlockColors

function New-ChartBar {
    <#
    .SYNOPSIS
    Creates a new bar chart object with specified name, value, and color.
 
    .DESCRIPTION
    This function creates a new bar chart object with the provided name, value, and color. The name is used as the identifier for the bar, the value represents the data to be displayed, and the color determines the color of the bar.
 
    .PARAMETER Name
    The name of the bar chart object.
 
    .PARAMETER Value
    The value or data to be displayed in the bar chart.
 
    .PARAMETER Color
    The color of the bar. If not specified, a default color will be used.
 
    .EXAMPLE
    New-ChartBar -Name 'Sales' -Value 1000 -Color 'blue'
 
    Creates a new bar chart object named 'Sales' with a value of 1000 and a blue color.
 
    .EXAMPLE
    New-ChartBar -Name 'Expenses' -Value 750 -Color 'red'
 
    Creates a new bar chart object named 'Expenses' with a value of 750 and a red color.
    #>

    [alias('ChartBar')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $Name,
        [Parameter(Mandatory)][object] $Value,
        [string[]] $Color
    )
    $Object = [ordered] @{
        ObjectType = 'Bar'
        Name       = $Name
        Value      = $Value
        series     = [ordered]@{
            name = $Name
            type = 'column'
            data = $Value
        }
        Color      = if ($Color) {
            ConvertFrom-Color -Color $Color } else {
            $null }
    }
    Remove-EmptyValue -Hashtable $Object -Recursive
    $Object
}

Register-ArgumentCompleter -CommandName New-ChartBar -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartBarOptions {
    <#
    .SYNOPSIS
    Creates options for a bar chart with customizable parameters.
 
    .DESCRIPTION
    This function creates options for a bar chart with the provided parameters. It allows customization of the bar type, data labels, colors, patterns, gradients, and distribution.
 
    .PARAMETER Type
    The type of the bar chart. Accepted values are 'bar', 'barStacked', 'barStacked100Percent'. Default is 'bar'.
 
    .PARAMETER DataLabelsEnabled
    Indicates whether data labels are enabled. Default is $true.
 
    .PARAMETER DataLabelsOffsetX
    The horizontal offset of the data labels. Default is -6.
 
    .PARAMETER DataLabelsFontSize
    The font size of the data labels. Default is '12px'.
 
    .PARAMETER DataLabelsColor
    The color of the data labels.
 
    .PARAMETER Patterned
    Indicates whether patterned colors are used.
 
    .PARAMETER Gradient
    Indicates whether gradient colors are used.
 
    .PARAMETER Distributed
    Indicates whether the bars are distributed.
 
    .PARAMETER Vertical
    Indicates whether the bars are vertical.
 
    .EXAMPLE
    New-ChartBarOptions -Type 'barStacked' -DataLabelsEnabled $true -DataLabelsOffsetX -6 -DataLabelsFontSize '12px' -DataLabelsColor 'black' -Patterned -Distributed
 
    Creates options for a stacked bar chart with data labels enabled, offset of -6, font size '12px', black data labels, patterned colors, and distributed bars.
 
    .EXAMPLE
    New-ChartBarOptions -Type 'bar' -DataLabelsEnabled $false -Gradient -Vertical
 
    Creates options for a regular bar chart with data labels disabled, gradient colors, and vertical bars.
    #>

    [alias('ChartBarOptions')]
    [CmdletBinding()]
    param(
        [ValidateSet('bar', 'barStacked', 'barStacked100Percent')] $Type = 'bar',
        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [string] $DataLabelsColor,
        [alias('PatternedColors')][switch] $Patterned,
        [alias('GradientColors')][switch] $Gradient,
        [switch] $Distributed,
        [switch] $Vertical

    )

    if ($null -ne $PSBoundParameters.Patterned) {
        $PatternedColors = $Patterned.IsPresent
    } else {
        $PatternedColors = $null
    }
    if ($null -ne $PSBoundParameters.Gradient) {
        $GradientColors = $Gradient.IsPresent
    } else {
        $GradientColors = $null
    }

    [PSCustomObject] @{
        ObjectType         = 'BarOptions'
        Type               = $Type
        Horizontal         = -not $Vertical.IsPresent
        DataLabelsEnabled  = $DataLabelsEnabled
        DataLabelsOffsetX  = $DataLabelsOffsetX
        DataLabelsFontSize = $DataLabelsFontSize
        DataLabelsColor    = $DataLabelsColor
        PatternedColors    = $PatternedColors
        GradientColors     = $GradientColors
        Distributed        = $Distributed.IsPresent
    }
}

Register-ArgumentCompleter -CommandName New-ChartBarOptions -ParameterName DataLabelsColor -ScriptBlock $Script:ScriptBlockColors
function New-ChartDataLabel {
    <#
    .SYNOPSIS
    Configures DataLabels for Charts
 
    .DESCRIPTION
    Configures DataLabels for Charts
 
    .PARAMETER Enabled
    To determine whether to show dataLabels or not
 
    .PARAMETER TextAnchor
    The alignment of text relative to dataLabel’s drawing position. Accepted values: start, middle, end
 
    .PARAMETER Distributed
    Similar to plotOptions.bar.distributed, this option makes each data-label discrete. So, when you provide an array of colors in datalabels.style.colors, the index in the colors array correlates with individual data-label index of all series.
 
    .PARAMETER OffsetX
    Sets the left offset for dataLabels
 
    .PARAMETER OffsetY
    Sets the top offset for dataLabels
 
    .PARAMETER FontSize
    FontSize for the label
 
    .PARAMETER FontFamily
    FontFamily for the label
 
    .PARAMETER FontColor
    FontColors for the dataLabels. Accepts an array of string colors. Each index in the array corresponds to the series
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [switch] $Enabled,
        [ValidateSet('start', 'middle', 'end')][string] $TextAnchor,
        [switch] $Distributed,
        [alias('DataLabelsOffsetX')][nullable[int]] $OffsetX,
        [nullable[int]] $OffsetY,
        [alias('DataLabelsFontSize')][object] $FontSize,
        [string] $FontFamily,
        [alias('DataLabelsColor')][string[]] $FontColor

    )

    $Object = [PSCustomObject] @{
        ObjectType = 'DataLabel'
        DataLabel  = [ordered] @{
            enabled = $Enabled.IsPresent
        }
    }
    if ($TextAnchor) {
        $Object.DataLabel.textAnchor = $TextAnchor
    }
    if ($Distributed) {
        $Object.DataLabel.distributed = $Distributed.IsPresent
    }

    if ($null -ne $OffsetX) {
        $Object['DataLabel']['offsetX'] = $OffsetX
    }
    if ($null -ne $OffsetX) {
        $Object['DataLabel']['offsetX'] = $OffsetY
    }
    $Object.DataLabel.style = [ordered]@{}
    if ($FontSize) {
        $Object.DataLabel.style['fontSize'] = ConvertFrom-Size -Size $FontSize
    }
    if ($FontFamily) {
        $Object.DataLabel.style['fontFamily'] = $FontFamily
    }
    if ($DataLabelsColor.Count -gt 0) {
        $Object.DataLabel.style['colors'] = @(ConvertFrom-Color -Color $FontColor)
    }
    Remove-EmptyValue -Hashtable $Object.DataLabel -Recursive
    $Object
}

Register-ArgumentCompleter -CommandName New-ChartDataLabel -ParameterName DataLabelsColor -ScriptBlock $Script:ScriptBlockColors
function New-ChartDesign {
    <#
    .SYNOPSIS
    Configures charts gradient, image, pattern and dropShadow options
 
    .DESCRIPTION
    Long description
 
    .PARAMETER GradientShade
    Available options for gradient shade
 
    .PARAMETER GradientType
    Available options for gradient type
 
    .PARAMETER GradientShadeIntensity
    Intensity of the gradient shade
 
    .PARAMETER GradientGradientToColors
    Optional colors that ends the gradient to.
    The main colors array becomes the gradientFromColors and this array becomes the end colors of the gradient.
    Each index in the array corresponds to the series-index.
 
    .PARAMETER GradientInverseColors
    Inverse the start and end colors of the gradient.
 
    .PARAMETER GradientOpacityFrom
    Start color's opacity. If you want different opacity for different series, you can pass an array of numbers.
 
    .PARAMETER GradientOpacityTo
    End color's opacity. If you want different opacity for different series, you can pass an array of numbers.
 
    .PARAMETER GradientStops
    Stops defines the ramp of colors to use on a gradient
 
    .PARAMETER ImageSource
    Accepts an array of image paths which will correspond to each series.
 
    .PARAMETER ImageWidth
    Width of each image for all the series
 
    .PARAMETER ImageHeight
    Height of each image for all the series
 
    .PARAMETER PatternStyle
    Available pattern styles
 
    .PARAMETER PatternWidth
    Pattern width which will be repeated at this interval
 
    .PARAMETER PatternHeight
    Pattern height which will be repeated at this interval
 
    .PARAMETER PatternStrokeWidth
    Pattern lines width indicates the thickness of the stroke of pattern.
 
    .PARAMETER DropShadowEnabled
    Enable a dropshadow for paths of the SVG
 
    .PARAMETER DropShadowEnabledOnSries
    Provide series index on which the dropshadow should be enabled.
 
    .PARAMETER DropShadowTop
    Set top offset for shadow
 
    .PARAMETER DropShadowLeft
    Set left offset for shadow
 
    .PARAMETER DropShadowColor
    Give a color to the shadow. If array is provided, each series can have different shadow color
 
    .PARAMETER DropShadowBlur
    Set blur distance for shadow
 
    .PARAMETER DropShadowOpacity
    Set the opacity of shadow
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [alias('New-ChartFill')]
    [CmdletBinding()]
    param(
        [string][ValidateSet('light', 'dark')]$GradientShade,
        [string][ValidateSet('horizontal', 'vertical', 'diagonal1', 'diagonal2')]$GradientType,
        [nullable[float]] $GradientShadeIntensity,
        [string[]] $GradientGradientToColors,
        [switch] $GradientInverseColors,
        [float[]] $GradientOpacityFrom,
        [float[]] $GradientOpacityTo,
        $GradientStops,
        [string[]] $ImageSource,
        [string[]] $ImageWidth,
        [string[]] $ImageHeight,
        [string][ValidateSet('verticalLines', 'horizontalLines', 'slantedLines', 'squares', 'circles')] $PatternStyle,
        [string] $PatternWidth,
        [string] $PatternHeight,
        [string] $PatternStrokeWidth,
        [switch] $DropShadowEnabled,
        [Array] $DropShadowEnabledOnSries,
        [nullable[int]]$DropShadowTop,
        [string[]] $DropShadowColor,
        [nullable[int]] $DropShadowLeft,
        [nullable[int]] $DropShadowBlur,
        [nullable[float]] $DropShadowOpacity
    )

    $Object = [PSCustomObject] @{
        ObjectType = 'Fill'
        Design     = [ordered] @{
            fill       = [ordered] @{
                gradient = [ordered] @{

                }
                image    = [ordered] @{

                }
                pattern  = [ordered] @{

                }
            }
            dropShadow = [ordered] @{

            }
        }
    }

    if ($DropShadowEnabled) {
        $Object.Design.dropShadow.enabled = $true
    }
    if ($DropShadowEnabledOnSries) {
        $Object.Design.dropShadow.enabledOnSeries = $DropShadowEnabledOnSries
    }
    if ($null -ne $DropShadowTop) {
        $Object.Design.dropShadow.top = $DropShadowTop
    }
    if ($null -ne $DropShadowLeft) {
        $Object.Design.dropShadow.left = $DropShadowLeft
    }
    if ($DropShadowColor) {
        $Object.Design.dropShadow.color = $DropShadowColor
    }
    if ($null -ne $DropShadowBlur) {
        $Object.Design.dropShadow.blur = $DropShadowBlur
    }
    if ($null -ne $DropShadowOpacity) {
        $Object.Design.dropShadow.opacity = $DropShadowOpacity
    }

    if ($GradientShade) {
        $Object.Design.fill.gradient.shade = $GradientShade
    }
    if ($GradientType) {
        $Object.Design.fill.gradient.type = $GradientType
    }
    if ($null -ne $GradientShadeIntensity) {
        $Object.Design.fill.gradient.shadeIntensity = $GradientShadeIntensity
    }
    if ($GradientGradientToColors) {
        $Object.Design.fill.gradient.gradientToColors = @(ConvertFrom-Color -Color $GradientGradientToColors)
    }
    if ($GradientInverseColors) {
        $Object.Design.fill.gradient.inverseColors = $GradientInverseColors.IsPresent
    }
    if ($GradientOpacityFrom) {
        $Object.Design.fill.gradient.opacityFrom = $GradientOpacityFrom
    }
    if ($GradientOpacityTo) {
        $Object.Design.fill.gradient.opacityTo = $GradientOpacityTo
    }
    if ($GradientStops) {
        $Object.Design.fill.gradient.stops = $GradientStops
    }

    if ($ImageSource) {
        $Object.Design.fill.image.src = $ImageSource
    }
    if ($ImageWidth) {
        $Object.Design.fill.image.width = $ImageWidth
    }
    if ($ImageHeight) {
        $Object.Design.fill.image.height = $ImageHeight
    }

    if ($PatternStyle) {
        $Object.Design.fill.pattern.style = $PatternStyle
    }
    if ($PatternWidth) {
        $Object.Design.fill.pattern.width = $PatternWidth
    }
    if ($PatternHeight) {
        $Object.Design.fill.pattern.height = $PatternHeight
    }
    if ($PatternStrokeWidth) {
        $Object.Design.fill.pattern.strokeWidth = $PatternStrokeWidth
    }

    Remove-EmptyValue -Hashtable $Object.Design -Recursive -Rerun 2
    if ($Object.Design.Count -gt 0) {
        $Object
    } else {
        Write-Warning -Message "New-ChartDesign: No design options were specified."
    }
}

Register-ArgumentCompleter -CommandName New-ChartDesign -ParameterName DropShadowColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-ChartDesign -ParameterName GradientGradientToColors -ScriptBlock $Script:ScriptBlockColors

function New-ChartDonut {
    <#
    .SYNOPSIS
    Creates a new donut chart object with specified name, value, and color.
 
    .DESCRIPTION
    This function creates a new donut chart object with the provided name, value, and color. The name is used as the identifier for the donut, the value represents the data to be displayed, and the color determines the color of the donut.
 
    .PARAMETER Name
    The name of the donut chart object.
 
    .PARAMETER Value
    The value or data to be displayed in the donut chart.
 
    .PARAMETER Color
    The color of the donut. If not specified, a default color will be used.
 
    .EXAMPLE
    New-ChartDonut -Name 'Sales' -Value 1000 -Color 'blue'
 
    Creates a new donut chart object named 'Sales' with a value of 1000 and a blue color.
 
    .EXAMPLE
    New-ChartDonut -Name 'Expenses' -Value 750 -Color 'red'
 
    Creates a new donut chart object named 'Expenses' with a value of 750 and a red color.
    #>

    [alias('ChartDonut')]
    [CmdletBinding()]
    param(
        [string] $Name,
        [object] $Value,
        [string] $Color
    )

    [PSCustomObject] @{
        ObjectType = 'Donut'
        Name       = $Name
        Value      = $Value
        Color      = if ($Color) {
            ConvertFrom-Color -Color $Color } else {
            $null }
    }
}
Register-ArgumentCompleter -CommandName New-ChartDonut -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartEvent {
    <#
    .SYNOPSIS
    Creates a new chart event for a specified DataTable.
 
    .DESCRIPTION
    This function creates a new chart event for a specified DataTable. It allows for defining events like click, data point selection, and marker click for the chart.
 
    .PARAMETER DataTableID
    Specifies the ID of the DataTable for which the chart event is being created.
 
    .PARAMETER ColumnID
    Specifies the ID of the column in the DataTable for which the chart event is being created.
 
    .PARAMETER EscapeRegex
    Indicates whether to escape regular expressions in the chart event.
 
    .EXAMPLE
    New-ChartEvent -DataTableID "table1" -ColumnID 1 -EscapeRegex
    Creates a new chart event for DataTable with ID "table1" on column 1 with regular expression escaping enabled.
 
    .EXAMPLE
    New-ChartEvent -DataTableID "table2" -ColumnID 2
    Creates a new chart event for DataTable with ID "table2" on column 2 without regular expression escaping.
 
    #>

    [cmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $DataTableID,
        [Parameter(Mandatory)][int] $ColumnID,
        [switch] $EscapeRegex
    )
    if ($EscapeRegex) {
        $Script:HTMLSchema.Features.EscapeRegex = $true
        $Escape = 'true'
    } else {
        $Escape = 'false'
    }
    $Script:HTMLSchema.Features.ChartsEvents = $true

    $Output = @"
            events: {
                click: function (event, chartContext, config) {
                    chartEventClick('$DataTableID', $ColumnID, config.config, config.dataPointIndex, config.seriesIndex, $Escape);
                },
                dataPointSelection: function (event, chartContext, config) {
                    chartEventDataPointClick('$DataTableID', $ColumnID, config.w.config, config.dataPointIndex, $Escape);
                },
                markerClick: function (event, chartContext, { seriesIndex, dataPointIndex, config }) {
                    chartEventMarkerClick('$DataTableID', $ColumnID, chartContext.opts, dataPointIndex, seriesIndex, $Escape);
                }
            }
"@

    $DataTablesOutput = @"
    `$.fn.dataTable.ext.search.push(
        function (settings, searchData, index, rowData, counter) {
            return dataTablesSearchExtension('$DataTableID', settings, searchData, index, rowData, counter, true);
        }
    );
"@


    Add-HTMLScript -Placement Footer -Content $DataTablesOutput

    [PSCustomObject] @{
        ObjectType = 'ChartEvents'
        Event      = $Output
    }
}
function New-ChartGrid {
    <#
    .SYNOPSIS
    Creates a new chart grid with customizable options.
 
    .DESCRIPTION
    This function creates a new chart grid with the provided parameters. It allows customization of the grid visibility, colors, opacity, padding, and other styling options.
 
    .PARAMETER Show
    Indicates whether the grid is visible.
 
    .PARAMETER BorderColor
    The color of the grid border.
 
    .PARAMETER StrokeDash
    The style of the grid border. Default is 0.
 
    .PARAMETER Position
    The position of the grid. Accepted values are 'front', 'back', 'default'. Default is 'default'.
 
    .PARAMETER xAxisLinesShow
    Indicates whether the x-axis lines are visible.
 
    .PARAMETER yAxisLinesShow
    Indicates whether the y-axis lines are visible.
 
    .PARAMETER RowColors
    An array of colors for the rows in the grid.
 
    .PARAMETER RowOpacity
    The opacity of the row colors. Valid range is 0 to 1. Default is 0.5.
 
    .PARAMETER ColumnColors
    An array of colors for the columns in the grid.
 
    .PARAMETER ColumnOpacity
    The opacity of the column colors. Valid range is 0 to 1. Default is 0.5.
 
    .PARAMETER PaddingTop
    The top padding of the grid.
 
    .PARAMETER PaddingRight
    The right padding of the grid.
 
    .PARAMETER PaddingBottom
    The bottom padding of the grid.
 
    .PARAMETER PaddingLeft
    The left padding of the grid.
 
    .EXAMPLE
    New-ChartGrid -Show -BorderColor 'black' -StrokeDash 2 -Position 'front' -xAxisLinesShow -yAxisLinesShow -RowColors @('red', 'blue') -RowOpacity 0.7 -ColumnColors @('green', 'yellow') -ColumnOpacity 0.8 -PaddingTop 10 -PaddingRight 5 -PaddingBottom 15 -PaddingLeft 5
 
    Creates a new chart grid with specified options including visible grid, black border color, dashed border style, front position, visible x-axis and y-axis lines, red and blue row colors with 0.7 opacity, green and yellow column colors with 0.8 opacity, and padding values.
 
    .EXAMPLE
    New-ChartGrid -Show -BorderColor 'gray' -RowColors @('orange', 'purple') -ColumnColors @('pink', 'cyan') -PaddingTop 5 -PaddingRight 5 -PaddingBottom 5 -PaddingLeft 5
 
    Creates a new chart grid with visible grid, gray border color, orange and purple row colors, pink and cyan column colors, and equal padding values.
 
    #>

    [alias('ChartGrid')]
    [CmdletBinding()]
    param(
        [switch] $Show,
        [string] $BorderColor,
        [int] $StrokeDash, #: 0,
        [ValidateSet('front', 'back', 'default')][string] $Position = 'default',
        [switch] $xAxisLinesShow,
        [switch] $yAxisLinesShow,
        [string[]] $RowColors,
        [double] $RowOpacity = 0.5, # valid range 0 - 1
        [string[]] $ColumnColors,
        [double] $ColumnOpacity = 0.5, # valid range 0 - 1
        [int] $PaddingTop,
        [int] $PaddingRight,
        [int] $PaddingBottom,
        [int] $PaddingLeft
    )
    [PSCustomObject] @{
        ObjectType = 'ChartGrid'
        Grid       = @{
            Show           = $Show.IsPresent
            BorderColor    = $BorderColor
            StrokeDash     = $StrokeDash
            Position       = $Position
            xAxisLinesShow = $xAxisLinesShow.IsPresent
            yAxisLinesShow = $yAxisLinesShow.IsPresent
            RowColors      = $RowColors
            RowOpacity     = $RowOpacity
            ColumnColors   = $ColumnColors
            ColumnOpacity  = $ColumnOpacity
            PaddingTop     = $PaddingTop
            PaddingRight   = $PaddingRight
            PaddingBottom  = $PaddingBottom
            PaddingLeft    = $PaddingLeft
        }
    }

}
Register-ArgumentCompleter -CommandName New-ChartGrid -ParameterName BorderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-ChartGrid -ParameterName RowColors -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-ChartGrid -ParameterName ColumnColors -ScriptBlock $Script:ScriptBlockColors
function New-ChartLegend {
    <#
    .SYNOPSIS
    Creates a new chart legend with customizable options.
 
    .DESCRIPTION
    The New-ChartLegend function creates a new chart legend with various customization options such as position, alignment, font settings, and more.
 
    .PARAMETER Names
    Specifies an array of names for the legend items.
 
    .PARAMETER Color
    Specifies an array of colors for the legend items.
 
    .PARAMETER HideLegend
    Indicates whether to hide the legend.
 
    .PARAMETER LegendPosition
    Specifies the position of the legend (top, left, right, bottom).
 
    .PARAMETER HorizontalAlign
    Specifies the horizontal alignment of the legend labels (left, center, right).
 
    .PARAMETER Floating
    Indicates whether the legend should float.
 
    .PARAMETER InverseOrder
    Indicates whether to display legend items in reverse order.
 
    .PARAMETER OffsetX
    Specifies the horizontal offset of the legend.
 
    .PARAMETER OffsetY
    Specifies the vertical offset of the legend.
 
    .PARAMETER ItemMarginHorizontal
    Specifies the horizontal margin between legend items.
 
    .PARAMETER ItemMarginVertical
    Specifies the vertical margin between legend items.
 
    .PARAMETER FontSize
    Specifies the font size of the legend labels.
 
    .PARAMETER FontFamily
    Specifies the font family of the legend labels.
 
    .PARAMETER FontWeight
    Specifies the font weight of the legend labels.
 
    .PARAMETER DisableOnItemClickToggleDataSeries
    Indicates whether to disable toggling data series on legend item click.
 
    .PARAMETER DisableOnItemHoverHighlightDataSeries
    Indicates whether to disable highlighting data series on legend item hover.
 
    .PARAMETER UseSeriesColors
    Indicates whether to use series colors for legend items.
 
    .EXAMPLE
    New-ChartLegend -Names @('A', 'B', 'C') -Color @('red', 'blue', 'green') -LegendPosition 'top' -HorizontalAlign 'center' -FontSize 12
 
    Creates a new chart legend with three items ('A', 'B', 'C') in red, blue, and green colors positioned at the top with center alignment and font size of 12.
 
    #>

    [alias('ChartLegend')]
    [CmdletBinding()]
    param(
        [Array] $Names,
        [string[]] $Color,

        # real legend
        [switch] $HideLegend,
        [ValidateSet('top', 'left', 'right', 'bottom')][string] $LegendPosition,
        [ValidateSet('left', 'center', 'right')][string] $HorizontalAlign,
        [switch] $Floating,
        [switch] $InverseOrder,
        [nullable[int]] $OffsetX,
        [nullable[int]] $OffsetY,
        [nullable[int]] $ItemMarginHorizontal,
        [nullable[int]] $ItemMarginVertical,
        [object] $FontSize,
        [string] $FontFamily,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [switch] $DisableOnItemClickToggleDataSeries,
        [switch] $DisableOnItemHoverHighlightDataSeries,
        [switch] $UseSeriesColors
    )
    $Object = [PSCustomObject] @{
        ObjectType = 'Legend'
        Names      = $Names
        Color      = if ($Color) {
            ConvertFrom-Color -Color $Color } else {
            $null }

        legend     = [ordered] @{
            show            = -not $HideLegend.IsPresent
            position        = if ($LegendPosition) {
                $LegendPosition.ToLower() } else {
                $null }
            horizontalAlign = if ($HorizontalAlign) {
                $HorizontalAlign.ToLower() } else {
                $null }
            labels          = @{}
        }
    }
    if ($UseSeriesColors) {
        $Object.legend.labels.useSeriesColors = $true
    }
    if ($Floating) {
        $Object.legend.floating = $true
    }
    if ($InverseOrder) {
        $Object.legend.inverseOrder = $true
    }
    if ($OffsetX -ne $null) {
        $Object.legend.offsetX = $OffsetX
    }
    if ($OffsetY -ne $null) {
        $Object.legend.offsetY = $OffsetY
    }
    if ($ItemMarginHorizontal -ne $null -or $ItemMarginHorizontal -ne $null) {
        $Object.legend.itemMargin = @{}
        if ($ItemMarginHorizontal -ne $null) {
            $Object.legend.itemMargin.horizontal = $ItemMarginHorizontal
        }
        if ($ItemMarginVertical -ne $null) {
            $Object.legend.itemMargin.vertical = $ItemMarginVertical
        }
    }
    if ($DisableOnItemClickToggleDataSeries) {
        $Object.legend.onItemClick = @{
            toggleDataSeries = $false
        }
    }
    if ($DisableOnItemHoverHighlightDataSeries) {
        $Object.legend.onItemHover = @{
            highlightDataSeries = $false
        }
    }

    if ($FontSize) {
        $Object.legend.fontSize = ConvertFrom-Size -Size $FontSize
    }
    if ($FontFamily) {
        $Object.legend.fontFamily = $FontFamily
    }
    if ($FontWeight) {
        $Object.legend.fontWeight = $FontWeight
    }

    Remove-EmptyValue -Hashtable $Object.legend -Recursive -Rerun 2
    $Object
}
Register-ArgumentCompleter -CommandName New-ChartLegend -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartLine {
    <#
    .SYNOPSIS
    Add a line to a chart
 
    .DESCRIPTION
    Add a line to a chart
 
    .PARAMETER Name
    Name of the line
 
    .PARAMETER Value
    Values to display
 
    .PARAMETER Color
    Colors to fill the border for paths.
 
    .PARAMETER Curve
    Whether to draw smooth lines or straight lines
 
    .PARAMETER Width
      Sets the width of border for svg path
 
    .PARAMETER Cap
    For setting the starting and ending points of stroke
 
    .PARAMETER Dash
    Creates dashes in borders of svg path. Higher number creates more space between dashes in the border.
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [alias('ChartLine')]
    [CmdletBinding()]
    param(
        [string] $Name,
        [object] $Value,
        [string] $Color,
        [ValidateSet('straight', 'smooth', 'stepline')] $Curve,
        [System.Nullable[int]] $Width,
        [ValidateSet('butt', 'square', 'round')][string] $Cap,
        [System.Nullable[int]] $Dash
    )
    $Object = [PSCustomObject] @{
        ObjectType = 'Line'

        Name       = $Name
        Value      = $Value

        Color      = ConvertFrom-Color -Color $Color
        series     = [ordered] @{
            name = $Name
            type = 'line'
            data = $Value
        }
        stroke     = [ordered] @{
            curve     = $Curve
            lineCap   = $Cap

            width     = $Width
            dashArray = $Dash
        }
    }
    Remove-EmptyValue -Hashtable $Object.Series
    Remove-EmptyValue -Hashtable $Object.Stroke
    $Object
}

Register-ArgumentCompleter -CommandName New-ChartLine -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartMarker {
    <#
    .SYNOPSIS
    Creates a new chart marker with customizable options.
 
    .DESCRIPTION
    This function defines the properties of a chart marker, such as size, color, shape, and other visual attributes. It allows customization of the marker appearance in the chart.
 
    .PARAMETER Size
    Specifies the size of the marker point.
 
    .PARAMETER Color
    Sets the fill color(s) of the marker point.
 
    .PARAMETER StrokeColors
    Specifies the stroke color of the marker. Accepts a single color or an array of colors in a multi-series chart.
 
    .PARAMETER StrokeWidth
    Sets the stroke size of the marker.
 
    .PARAMETER StrokeOpacity
    Specifies the opacity of the border around the marker.
 
    .PARAMETER StrokeDashArray
    Defines the dashes in the border around the marker. A higher number creates more space between dashes in the border.
 
    .PARAMETER FillOpacity
    Specifies the opacity of the marker fill color.
 
    .PARAMETER Shape
    Defines the shape of the marker. Available options for shape are circle or square.
 
    .PARAMETER Radius
    Specifies the radius of the marker (applies to square shape).
 
    .PARAMETER OffsetX
    Sets the left offset of the marker.
 
    .PARAMETER OffsetY
    Sets the top offset of the marker.
 
    .PARAMETER ShowNullDataPoints
    Determines whether to show markers for null values in a line/area chart. If disabled, any null values present in line/area charts will not be visible.
 
    .PARAMETER HoverSize
    Specifies the fixed size of the marker when it is active.
 
    .PARAMETER HoverSizeOffset
    Unlike the fixed size, this option takes the original marker size and increases/decreases the value based on it. For example, if markers.size is 6 and markers.hover.sizeOffset is 3, the marker's size will be 9 when hovered.
 
    .EXAMPLE
    New-HTMLChart -Title 'Incidents Reported vs Solved' -TitleAlignment center {
        New-ChartMarker -Size 30 -Color red -Shape square -StrokeColors yellow
    }
 
    .NOTES
    Based on https://apexcharts.com/docs/options/markers/
    #>

    [cmdletBinding()]
    param(
        [nullable[int]] $Size,
        [string[]] $Color,
        [string[]] $StrokeColors,
        [int[]] $StrokeWidth,
        [int[]]$StrokeOpacity,
        [int[]]$StrokeDashArray,
        [int[]]$FillOpacity,
        [validateSet('circle', 'square')][string[]] $Shape,
        [int[]]$Radius,
        [int[]]$OffsetX,
        [int[]]$OffsetY,
        [switch] $ShowNullDataPoints,
        [int[]] $HoverSize,
        [int[]] $HoverSizeOffset
    )

    $Object = [PSCustomObject] @{
        ObjectType = 'Marker'
        markers    = [ordered]@{

        }
    }
    if ($Size) {
        $Object.markers.size = $Size
    }
    if ($Color) {
        $Object.markers.color = ConvertFrom-Color -Color $Color
    }
    if ($StrokeColors) {
        $Object.markers.strokeColors = ConvertFrom-Color -Color $StrokeColors
    }
    if ($null -ne $StrokeWidth) {
        $Object.markers.strokeWidth = $StrokeWidth
    }
    if ($null -ne $StrokeOpacity) {
        $Object.markers.strokeOpacity = $StrokeOpacity
    }
    if ($null -ne $StrokeDashArray) {
        $Object.markers.strokeDashArray = $StrokeDashArray
    }
    if ($null -ne $FillOpacity) {
        $Object.markers.fillOpacity = $FillOpacity
    }
    if ($Shape.Count -eq 1) {
        $Object.markers.shape = $Shape[0]
    } elseif ($Shape.Count -gt 1) {
        $Object.markers.shape = $Shape
    }
    if ($null -ne $Radius) {
        $Object.markers.radius = $Radius
    }
    if ($null -ne $OffsetX) {
        $Object.markers.offsetX = $OffsetX
    }
    if ($null -ne $OffsetY) {
        $Object.markers.offsetY = $OffsetY
    }
    if ($ShowNullDataPoints) {
        $Object.markers.showNullDataPoints = $ShowNullDataPoints.IsPresent
    }
    if ($null -ne $HoverSize -or $Null -ne $HoverSizeOffset) {
        $Object.markers.hover = [ordered]@{}
        if ($null -ne $HoverSize) {
            $Object.markers.hover.size = $HoverSize
        }
        if ($null -ne $HoverSizeOffset) {
            $Object.markers.hover.sizeOffset = $HoverSizeOffset
        }
    }
    Remove-EmptyValue -Hashtable $Object.markers -Recursive -Rerun 1
    $Object
}

function New-ChartPie {
    <#
    .SYNOPSIS
    Creates a new pie chart with customizable options.
 
    .DESCRIPTION
    This function creates a new pie chart with the specified name, value, and color. It allows customization of the pie chart appearance.
 
    .PARAMETER Name
    Specifies the name of the pie chart.
 
    .PARAMETER Value
    Specifies the value of the pie chart segment.
 
    .PARAMETER Color
    Specifies the color of the pie chart segment.
 
    .EXAMPLE
    New-ChartPie -Name "Slice 1" -Value 30 -Color "red"
    Creates a new pie chart segment named "Slice 1" with a value of 30 and colored in red.
 
    .EXAMPLE
    New-ChartPie -Name "Slice 2" -Value 20 -Color "blue"
    Creates a new pie chart segment named "Slice 2" with a value of 20 and colored in blue.
    #>

    [alias('ChartPie')]
    [CmdletBinding()]
    param(
        [string] $Name,
        [object] $Value,
        [string] $Color
    )
    if ($null -eq $Value) {
        $Value = 0
    }
    [PSCustomObject] @{
        ObjectType = 'Pie'
        Name       = $Name
        Value      = $Value
        Color      = if ($Color) {
            ConvertFrom-Color -Color $Color } else {
            $null }
    }
}

Register-ArgumentCompleter -CommandName New-ChartPie -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartRadial {
    <#
    .SYNOPSIS
    Creates a new radial chart object.
 
    .DESCRIPTION
    This function creates a new radial chart object with the specified Name, Value, and Color.
 
    .PARAMETER Name
    Specifies the name of the radial chart.
 
    .PARAMETER Value
    Specifies the value of the radial chart.
 
    .PARAMETER Color
    Specifies the color of the radial chart. If not provided, the default color is used.
 
    .EXAMPLE
    PS C:\> New-ChartRadial -Name "Chart1" -Value 75 -Color "Blue"
    Creates a new radial chart object named "Chart1" with a value of 75 and a blue color.
 
    .EXAMPLE
    PS C:\> New-ChartRadial -Name "Chart2" -Value 50
    Creates a new radial chart object named "Chart2" with a value of 50 using the default color.
 
    #>

    [alias('ChartRadial')]
    [CmdletBinding()]
    param(
        [string] $Name,
        [object] $Value,
        [string] $Color
    )
    [PSCustomObject] @{
        ObjectType = 'Radial'
        Name       = $Name
        Value      = $Value
        Color      = if ($Color) {
            ConvertFrom-Color -Color $Color } else {
            $null }
    }
}

Register-ArgumentCompleter -CommandName New-ChartRadial -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartRadialOptions {
    <#
    .SYNOPSIS
    Creates customizable options for a radial chart.
 
    .DESCRIPTION
    This function defines various customizable options for a radial chart, such as circle type, angles, colors, shadows, labels, gradients, and more.
 
    .PARAMETER CircleType
    Specifies the type of circle for the radial chart. Valid values are 'FullCircleTop', 'FullCircleBottom', 'FullCircleBottomLeft', 'FullCircleLeft', 'Speedometer', 'SemiCircleGauge'.
 
    .PARAMETER StartAngle
    Specifies the starting angle for the radial chart.
 
    .PARAMETER EndAngle
    Specifies the ending angle for the radial chart.
 
    .PARAMETER HollowBackGroundColor
    Specifies the color for the hollow background of the radial chart.
 
    .PARAMETER HollowMargin
    Specifies the margin for the hollow area of the radial chart.
 
    .PARAMETER HollowSize
    Specifies the size of the hollow area in the radial chart.
 
    .PARAMETER TrackDropShadow
    Specifies the drop shadow for the track of the radial chart.
 
    .PARAMETER TrackDropShadowTop
    Specifies the top shadow for the track of the radial chart.
 
    .PARAMETER TrackDropShadowLeft
    Specifies the left shadow for the track of the radial chart.
 
    .PARAMETER TrackDropShadowBlur
    Specifies the blur effect for the drop shadow of the track in the radial chart.
 
    .PARAMETER TrackDropShadowOpacity
    Specifies the opacity of the drop shadow for the track in the radial chart.
 
    .PARAMETER DataLabelNameOffsetY
    Specifies the Y-axis offset for the data label name in the radial chart.
 
    .PARAMETER DataLabelNameColor
    Specifies the color for the data label name in the radial chart.
 
    .PARAMETER DataLabelNameFontSize
    Specifies the font size for the data label name in the radial chart.
 
    .PARAMETER DataLabelValueShow
    Indicates whether to show the data label value in the radial chart.
 
    .PARAMETER DataLabelValueColor
    Specifies the color for the data label value in the radial chart.
 
    .PARAMETER DataLabelValueFontSize
    Specifies the font size for the data label value in the radial chart.
 
    .PARAMETER FillType
    Specifies the fill type for the radial chart.
 
    .PARAMETER GradientShade
    Specifies the shade for the gradient in the radial chart.
 
    .PARAMETER GradientType
    Specifies the type of gradient for the radial chart.
 
    .PARAMETER GradientToColors
    Specifies the colors for the gradient in the radial chart.
 
    .PARAMETER GraidentStops
    Specifies the gradient stops for the radial chart.
 
    .PARAMETER StrokeLineCap
    Specifies the line cap for the stroke in the radial chart.
 
    .PARAMETER Labels
    Specifies an array of labels for the radial chart.
 
    .EXAMPLE
    New-ChartRadialOptions -CircleType 'FullCircleTop' -StartAngle 0 -EndAngle 180 -HollowBackGroundColor 'white' -HollowMargin '10px' -HollowSize '50%' -TrackDropShadow 'black' -TrackDropShadowTop '2px' -TrackDropShadowLeft '2px' -TrackDropShadowBlur '5px' -TrackDropShadowOpacity '0.5' -DataLabelNameOffsetY '20px' -DataLabelNameColor 'black' -DataLabelNameFontSize '12px' -DataLabelValueShow -DataLabelValueColor 'red' -DataLabelValueFontSize '14px' -FillType 'solid' -GradientShade 'dark' -GradientType 'vertical' -GradientToColors @('red', 'blue') -GraidentStops @('0%', '100%') -StrokeLineCap 'round' -Labels @('Label1', 'Label2')
 
    Creates customizable options for a radial chart with specified parameters.
 
    #>

    [CmdletBinding()]
    param(
        [parameter(ParameterSetName = 'CircleType')][ValidateSet('FullCircleTop', 'FullCircleBottom', 'FullCircleBottomLeft', 'FullCircleLeft', 'Speedometer', 'SemiCircleGauge')] $CircleType,
        [parameter(ParameterSetName = 'CustomAngle')][nullable[int]] $StartAngle,
        [parameter(ParameterSetName = 'CustomAngle')][nullable[int]] $EndAngle,
        [string] $HollowBackGroundColor,
        [string] $HollowMargin,
        [string] $HollowSize,
        [string] $TrackDropShadow,
        [string] $TrackDropShadowTop,
        [string] $TrackDropShadowLeft,
        [string] $TrackDropShadowBlur,
        [string] $TrackDropShadowOpacity,
        [string] $DataLabelNameOffsetY,
        [string] $DataLabelNameColor,
        [string] $DataLabelNameFontSize,
        [switch] $DataLabelValueShow,
        [string] $DataLabelValueColor,
        [string] $DataLabelValueFontSize,
        [string] $FillType,
        [string] $GradientShade,
        [string] $GradientType,
        [string] $GradientToColors,
        [Array] $GraidentStops,
        [string] $StrokeLineCap,
        [string[]] $Labels
    )

    $Object = [PSCustomObject]@{
        ObjectType  = 'RadialOptions'
        plotOptions = @{
            radialBar = [ordered] @{

            }
        }
    }
    if ($null -ne $StartAngle -and $null -ne $EndAngle) {
        $Object.plotOptions.radialBar = [ordered] @{
            startAngle = $StartAngle
            endAngle   = $EndAngle
        }
    } else {
        if ($CircleType -eq 'SemiCircleGauge') {
            $Object.plotOptions.radialBar = [ordered] @{
                startAngle = -90
                endAngle   = 90
            }
        } elseif ($CircleType -eq 'FullCircleBottom') {
            $Object.plotOptions.radialBar = [ordered] @{
                startAngle = -180
                endAngle   = 180
            }
        } elseif ($CircleType -eq 'FullCircleLeft') {
            $Object.plotOptions.radialBar = [ordered] @{
                startAngle = -90
                endAngle   = 270
            }
        } elseif ($CircleType -eq 'FullCircleBottomLeft') {
            $Object.plotOptions.radialBar = [ordered] @{
                startAngle = -135
                endAngle   = 225
            }
        } elseif ($CircleType -eq 'Speedometer') {
            $Object.plotOptions.radialBar = [ordered] @{
                startAngle = -135
                endAngle   = 135
            }
        }
    }
    $Object
}

function New-ChartSpark {
    <#
    .SYNOPSIS
    Creates a new spark chart object.
 
    .DESCRIPTION
    This function creates a new spark chart object with the specified Name, Value, and Color.
 
    .PARAMETER Name
    Specifies the name of the spark chart.
 
    .PARAMETER Value
    Specifies the value of the spark chart.
 
    .PARAMETER Color
    Specifies the color of the spark chart.
 
    .EXAMPLE
    PS C:\> New-ChartSpark -Name "Spark1" -Value 100 -Color "Green"
    Creates a new spark chart object named "Spark1" with a value of 100 and a green color.
 
    .EXAMPLE
    PS C:\> New-ChartSpark -Name "Spark2" -Value 50
    Creates a new spark chart object named "Spark2" with a value of 50 using the default color.
    #>

    [alias('ChartSpark')]
    [CmdletBinding()]
    param(
        [string] $Name,
        [object] $Value,
        [string] $Color
    )

    [PSCustomObject] @{
        ObjectType = 'Spark'
        Name       = $Name
        Value      = $Value
        Color      = $Color
    }
}

Register-ArgumentCompleter -CommandName New-ChartSpark -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartTheme {
    <#
    .SYNOPSIS
    Creates a new chart theme with customizable options.
 
    .DESCRIPTION
    This function defines the properties of a chart theme, such as mode, palette, color, shade, and intensity. It allows customization of the theme appearance in the chart.
 
    .PARAMETER Mode
    Specifies the mode of the chart theme. Valid values are 'light' or 'dark'.
 
    .PARAMETER Palette
    Specifies the color palette for the chart theme. Valid values are 'palette1' through 'palette10'.
 
    .PARAMETER Monochrome
    Switch parameter to enable monochrome mode for the chart theme.
 
    .PARAMETER Color
    Sets the base color for the chart theme.
 
    .PARAMETER ShadeTo
    Specifies the shading direction for the chart theme. Valid values are 'light' or 'dark'.
 
    .PARAMETER ShadeIntensity
    Specifies the intensity of the shading effect for the chart theme.
 
    .EXAMPLE
    New-ChartTheme -Mode 'dark' -Palette 'palette3' -Monochrome -Color 'Red' -ShadeTo 'dark' -ShadeIntensity 0.8
    Creates a new chart theme with dark mode, palette 3, monochrome enabled, red base color, dark shading, and intensity of 0.8.
 
    .EXAMPLE
    New-ChartTheme -Mode 'light' -Palette 'palette7' -Color 'Blue' -ShadeTo 'light' -ShadeIntensity 0.6
    Creates a new chart theme with light mode, palette 7, blue base color, light shading, and intensity of 0.6.
    #>

    [alias('ChartTheme')]
    [CmdletBinding()]
    param(
        [ValidateSet('light', 'dark')][string] $Mode = 'light',
        [ValidateSet(
            'palette1',
            'palette2',
            'palette3',
            'palette4',
            'palette5',
            'palette6',
            'palette7',
            'palette8',
            'palette9',
            'palette10'
        )
        ][string] $Palette = 'palette1',
        [switch] $Monochrome,
        [string] $Color = "DodgerBlue",
        [ValidateSet('light', 'dark')][string] $ShadeTo = 'light',
        [double] $ShadeIntensity = 0.65
    )

    [PSCustomObject] @{
        ObjectType = 'Theme'
        Theme      = @{
            Mode           = $Mode
            Palette        = $Palette
            Monochrome     = $Monochrome.IsPresent
            Color          = $Color
            ShadeTo        = $ShadeTo
            ShadeIntensity = $ShadeIntensity
        }
    }
}

Register-ArgumentCompleter -CommandName New-ChartTheme -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartTimeLine {
    <#
    .SYNOPSIS
    Creates a new time series chart object.
 
    .DESCRIPTION
    This function creates a new time series chart object with the specified Name, DateFrom, DateTo, Color, TimeZoneOffset, and DateFormatPattern. It allows customization of the time series chart appearance.
 
    .PARAMETER Name
    Specifies the name of the time series chart.
 
    .PARAMETER DateFrom
    Specifies the starting date for the time series chart.
 
    .PARAMETER DateTo
    Specifies the ending date for the time series chart.
 
    .PARAMETER Color
    Specifies the color of the time series chart.
 
    .PARAMETER TimeZoneOffset
    Specifies the time zone offset for the time series chart.
 
    .PARAMETER DateFormatPattern
    Specifies the date format pattern for the time series chart. Default is "yyyy-MM-dd HH:mm:ss".
 
    .EXAMPLE
    New-ChartTimeLine -Name "TimeSeries1" -DateFrom (Get-Date) -DateTo (Get-Date).AddDays(7) -Color "Blue" -TimeZoneOffset "+3" -DateFormatPattern "MM/dd/yyyy HH:mm:ss"
    Creates a new time series chart object named "TimeSeries1" with a date range from the current date to 7 days ahead, colored in blue, with a time zone offset of +3, and using the date format pattern "MM/dd/yyyy HH:mm:ss".
 
    .EXAMPLE
    New-ChartTimeLine -Name "TimeSeries2" -DateFrom (Get-Date) -DateTo (Get-Date).AddDays(14) -Color "Green" -TimeZoneOffset "-5"
    Creates a new time series chart object named "TimeSeries2" with a date range from the current date to 14 days ahead, colored in green, with a time zone offset of -5, using the default date format pattern.
 
    #>

    [alias('ChartTimeLine')]
    [CmdletBinding()]
    param(
        [parameter(Mandatory)][string] $Name,
        [DateTime] $DateFrom,
        [DateTime] $DateTo,
        [string] $Color,
        [string] $TimeZoneOffset,
        [string] $DateFormatPattern = "yyyy-MM-dd HH:mm:ss"
        #[ValidateSet('straight', 'smooth', 'stepline')] $Curve = 'straight',
        #[int] $Width = 6,
        #[ValidateSet('butt', 'square', 'round')][string] $Cap = 'butt',
        #[int] $Dash = 0
    )

    $timezoneString = ""
    if ($TimeZoneOffset) {
        if ($TimeZoneOffset -Notlike "-*" -and $TimeZoneOffset -Notlike "+*") {
            $TimeZoneOffset = "+$TimeZoneOffset"
        }
        $timezoneString = " GMT$TimeZoneOffset"
    }

    $FormattedDateFrom = Get-Date -Date $DateFrom -Format $DateFormatPattern
    $FormattedDateTo = Get-Date -Date $DateTo -Format $DateFormatPattern

    $TimeLine = [ordered] @{
        x         = $Name
        y         = @(
            "new Date('$FormattedDateFrom$timezoneString').getTime()"
            "new Date('$FormattedDateTo$timezoneString').getTime()"
        )
        fillColor = ConvertFrom-Color -Color $Color
    }
    Remove-EmptyValue -Hashtable $TimeLine

    [PSCustomObject] @{
        ObjectType = 'TimeLine'
        TimeLine   = $TimeLine

    }
}

Register-ArgumentCompleter -CommandName New-ChartTimeLine -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartToolbar {
    <#
    .SYNOPSIS
    Creates a new chart toolbar object with customizable options.
 
    .DESCRIPTION
    This function defines a chart toolbar object with various tools such as download, selection, zoom, zoom in, zoom out, pan, and reset. It allows customization of the toolbar appearance and behavior.
 
    .PARAMETER Download
    Switch parameter to enable the download tool in the toolbar.
 
    .PARAMETER Selection
    Switch parameter to enable the selection tool in the toolbar.
 
    .PARAMETER Zoom
    Switch parameter to enable the zoom tool in the toolbar.
 
    .PARAMETER ZoomIn
    Switch parameter to enable the zoom in tool in the toolbar.
 
    .PARAMETER ZoomOut
    Switch parameter to enable the zoom out tool in the toolbar.
 
    .PARAMETER Pan
    Switch parameter to enable the pan tool in the toolbar.
 
    .PARAMETER Reset
    Switch parameter to enable the reset tool in the toolbar.
 
    .PARAMETER AutoSelected
    Specifies the default tool to be auto-selected when the toolbar is loaded. Valid values are 'zoom', 'selection', or 'pan'. Default is 'zoom'.
 
    .EXAMPLE
    New-ChartToolbar -Download -Selection -Zoom -ZoomIn -ZoomOut -Pan -Reset -AutoSelected 'zoom'
    Creates a new chart toolbar object with all tools enabled and the default tool set to zoom.
 
    .EXAMPLE
    New-ChartToolbar -Download -Zoom -ZoomOut -Pan -AutoSelected 'pan'
    Creates a new chart toolbar object with download, zoom, zoom out, and pan tools enabled, and the default tool set to pan.
    #>

    [alias('ChartToolbar')]
    [CmdletBinding()]
    param(
        [switch] $Download,
        [switch] $Selection,
        [switch] $Zoom,
        [switch] $ZoomIn,
        [switch] $ZoomOut,
        [switch] $Pan,
        [switch] $Reset,
        [ValidateSet('zoom', 'selection', 'pan')][string] $AutoSelected = 'zoom'
    )

    [PSCustomObject] @{
        ObjectType = 'Toolbar'
        Toolbar    = @{

            download     = $Download.IsPresent
            selection    = $Selection.IsPresent
            zoom         = $Zoom.IsPresent
            zoomin       = $ZoomIn.IsPresent
            zoomout      = $ZoomOut.IsPresent
            pan          = $Pan.IsPresent
            reset        = $Reset.IsPresent

            autoSelected = $AutoSelected
        }
    }
}
function New-ChartToolTip {
    <#
    .SYNOPSIS
    Creates a new chart tooltip object with customizable options.
 
    .DESCRIPTION
    This function defines a chart tooltip object with various options such as names, title text, type, min and max values, X and Y axis format patterns. It allows customization of the tooltip appearance and behavior.
 
    .PARAMETER Names
    Specifies an array of names for the chart tooltip.
 
    .PARAMETER TitleText
    Specifies the title text for the chart tooltip.
 
    .PARAMETER Type
    Specifies the type of the chart tooltip. Valid values are 'datetime', 'category', or 'numeric'.
 
    .PARAMETER MinValue
    Specifies the minimum value for the chart tooltip.
 
    .PARAMETER MaxValue
    Specifies the maximum value for the chart tooltip.
 
    .PARAMETER XAxisFormatPattern
    Specifies the format pattern for the X-axis in the chart tooltip.
 
    .PARAMETER YAxisFormatPattern
    Specifies the format pattern for the Y-axis in the chart tooltip. Default is "function (seriesName) { return ''; }".
 
    .EXAMPLE
    New-ChartToolTip -Names @("Name1", "Name2") -TitleText "Tooltip Title" -Type 'datetime' -MinValue 0 -MaxValue 100 -XAxisFormatPattern "HH:mm:ss" -YAxisFormatPattern "function (seriesName) { return seriesName + ': ' + value; }"
    Creates a new chart tooltip object with names "Name1" and "Name2", title "Tooltip Title", datetime type, min value of 0, max value of 100, X-axis format pattern "HH:mm:ss", and custom Y-axis format pattern.
 
    .EXAMPLE
    New-ChartToolTip -Names @("Data1", "Data2", "Data3") -TitleText "Data Tooltip" -Type 'numeric' -MinValue 50 -MaxValue 200 -XAxisFormatPattern "MM/dd/yyyy" -YAxisFormatPattern "function (seriesName) { return seriesName + ': ' + value; }"
    Creates a new chart tooltip object with names "Data1", "Data2", and "Data3", title "Data Tooltip", numeric type, min value of 50, max value of 200, X-axis format pattern "MM/dd/yyyy", and custom Y-axis format pattern.
    #>

    [CmdletBinding()]
    param(
        [alias('Name')][Array] $Names,
        [alias('Title')][string] $TitleText,
        [ValidateSet('datetime', 'category', 'numeric')][string] $Type = 'category',
        [object] $MinValue,
        [object] $MaxValue,
        [string] $XAxisFormatPattern, #"HH:mm:ss"
        [string] $YAxisFormatPattern = "function (seriesName) { return ''; }"

        #[ValidateSet('top', 'topRight', 'left', 'right', 'bottom', '')][string] $LegendPosition = '',
        # [string[]] $Color
    )

    [PSCustomObject] @{
        ObjectType   = 'ChartToolTip'
        ChartToolTip = @{
            enabled = $true
            y       = @{ title = @{ formatter = "$YAxisFormatPattern" } }
            x       = @{ format = "$XAxisFormatPattern" }
        }
    }
}
function New-DiagramEvent {
    <#
    .SYNOPSIS
    Allows to connect Diagrams with Tables using Events.
 
    .DESCRIPTION
    Allows to connect Diagrams with Tables using Events.
 
    .PARAMETER ID
    ID of the table to connect with the diagram.
 
    .PARAMETER ColumnID
    Column Number of the table to connect with the diagram.
 
    .EXAMPLE
    $Processes = Get-Process | Select-Object -First 3 -Property Name, ProcessName, Id, FileVersion, WorkingSet
    $TableID = 'RandomID'
 
    New-HTML -TitleText 'My Title' -Online -FilePath $PSScriptRoot\Example30-LinkedProcesses.html -ShowHTML {
        New-HTMLSection -Invisible {
            New-HTMLPanel {
                New-HTMLTable -DataTable $Processes -DataTableID $TableID
            }
            New-HTMLPanel {
                New-HTMLDiagram {
                    New-DiagramEvent -ID $TableID -ColumnID 2
                    New-DiagramNode -Label 'Processes' -IconBrands delicious
                    foreach ($_ in $Processes) {
                        New-DiagramNode -Label $_.ProcessName -Id $_.Id -To 'Processes'
                    }
                }
            }
        }
    }
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        #[switch] $FadeSearch,
        [string] $ID,
        [nullable[int]] $ColumnID
    )

    $Object = [PSCustomObject] @{
        Type     = 'DiagramEvent'
        Settings = @{

            ID       = $ID

            ColumnID = $ColumnID
        }
    }
    $Object
}
function New-DiagramLink {
    <#
    .SYNOPSIS
    Creates a new diagram link between two nodes.
 
    .DESCRIPTION
    Creates a new diagram link connecting two nodes in a visual representation.
 
    .PARAMETER From
    Specifies the starting node of the link.
 
    .PARAMETER To
    Specifies the ending node of the link.
 
    .PARAMETER Label
    Specifies the label text for the link.
 
    .PARAMETER ArrowsToEnabled
    Switch parameter to enable arrows at the end of the link.
 
    .PARAMETER ArrowsToScaleFactor
    Specifies the scale factor for the arrows at the end of the link.
 
    .PARAMETER ArrowsToType
    Specifies the type of arrows at the end of the link. Valid values are 'arrow', 'bar', or 'circle'.
 
    .PARAMETER ArrowsMiddleEnabled
    Switch parameter to enable arrows in the middle of the link.
 
    .PARAMETER ArrowsMiddleScaleFactor
    Specifies the scale factor for the arrows in the middle of the link.
 
    .PARAMETER ArrowsMiddleType
    Specifies the type of arrows in the middle of the link. Valid values are 'arrow', 'bar', or 'circle'.
 
    .PARAMETER ArrowsFromEnabled
    Switch parameter to enable arrows at the start of the link.
 
    .PARAMETER ArrowsFromScaleFactor
    Specifies the scale factor for the arrows at the start of the link.
 
    .PARAMETER ArrowsFromType
    Specifies the type of arrows at the start of the link. Valid values are 'arrow', 'bar', or 'circle'.
 
    .PARAMETER ArrowStrikethrough
    Switch parameter to enable a strikethrough effect for the link.
 
    .PARAMETER Chosen
    Switch parameter to mark the link as chosen.
 
    .PARAMETER Color
    Specifies the color of the link.
 
    .PARAMETER ColorHighlight
    Specifies the highlight color of the link.
 
    .PARAMETER ColorHover
    Specifies the hover color of the link.
 
    .PARAMETER ColorInherit
    Specifies how the link color should inherit. Valid values are 'true', 'false', 'from', 'to', or 'both'.
 
    .PARAMETER ColorOpacity
    Specifies the opacity of the link color. Range between 0 and 1.
 
    .PARAMETER Dashes
    Switch parameter to enable dashed line style for the link.
 
    .PARAMETER Length
    Specifies the length of the link.
 
    .PARAMETER FontColor
    Specifies the font color for the label.
 
    .PARAMETER FontSize
    Specifies the font size for the label.
 
    .PARAMETER FontName
    Specifies the font name for the label.
 
    .PARAMETER FontBackground
    Specifies the background color for the font.
 
    .PARAMETER FontStrokeWidth
    Specifies the stroke width for the font. (in pixels)
 
    .PARAMETER FontStrokeColor
    Specifies the stroke color for the font.
 
    .PARAMETER FontAlign
    Possible options: 'horizontal','top','middle','bottom'.
    The alignment determines how the label is aligned over the edge.
    The default value horizontal aligns the label horizontally, regardless of the orientation of the edge.
    When an option other than horizontal is chosen, the label will align itself according to the edge.
 
    .PARAMETER FontMulti
    Specifies whether the label supports multiple lines or markdown/html formatting.
 
    .PARAMETER FontVAdjust
    Specifies the vertical adjustment for the label.
 
    .PARAMETER WidthConstraint
    Specifies the width constraint for the link.
 
    .PARAMETER SmoothType
    Possible options: 'dynamic', 'continuous', 'discrete', 'diagonalCross', 'straightCross', 'horizontal', 'vertical', 'curvedCW', 'curvedCCW', 'cubicBezier'.
    Take a look at this example to see what these look like and pick the one that you like best! When using dynamic, the edges will have an invisible support node guiding the shape.
    This node is part of the physics simulation.
 
    .PARAMETER SmoothForceDirection
    Accepted options: ['horizontal', 'vertical', 'none'].
    This options is only used with the cubicBezier curves.
    When true, horizontal is chosen, when false, the direction that is larger (x distance between nodes vs y distance between nodes) is used.
    If the x distance is larger, horizontal. This is ment to be used with hierarchical layouts.
 
    .PARAMETER SmoothRoundness
    Accepted range: 0 .. 1.0. This parameter tweaks the roundness of the smooth curves for all types EXCEPT dynamic.
 
    .EXAMPLE
    An example of creating a new diagram link.
 
    .NOTES
    Additional notes
    #>

    [alias('DiagramEdge', 'DiagramEdges', 'New-DiagramEdge', 'DiagramLink')]
    [CmdletBinding()]
    param(
        [string[]] $From,
        [string[]] $To,
        [string] $Label,
        [switch] $ArrowsToEnabled,
        [nullable[int]] $ArrowsToScaleFacto,
        [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsToType,
        [switch] $ArrowsMiddleEnabled,
        [nullable[int]]$ArrowsMiddleScaleFactor,
        [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsMiddleType,
        [switch] $ArrowsFromEnabled,
        [nullable[int]] $ArrowsFromScaleFactor,
        [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsFromType,
        [switch] $ArrowStrikethrough,
        [switch] $Chosen,
        [string] $Color,
        [string] $ColorHighlight,
        [string] $ColorHover,
        [ValidateSet('true', 'false', 'from', 'to', 'both')][string]$ColorInherit,
        [nullable[double]] $ColorOpacity, # range between 0 and 1
        [switch] $Dashes,
        [string] $Length,
        [string] $FontColor,
        [object] $FontSize,
        [string] $FontName,
        [string] $FontBackground,
        [object] $FontStrokeWidth, #// px
        [string] $FontStrokeColor,
        [ValidateSet('horizontal', 'top', 'middle', 'bottom')][string] $FontAlign,
        [ValidateSet('false', 'true', 'markdown', 'html')][string]$FontMulti,
        [nullable[int]] $FontVAdjust,
        [nullable[int]] $WidthConstraint,

        [ValidateSet('dynamic', 'continuous', 'discrete', 'diagonalCross', 'straightCross', 'horizontal', 'vertical', 'curvedCW', 'curvedCCW', 'cubicBezier')][string] $SmoothType,
        [ValidateSet('horizontal', 'vertical', 'none')][string] $SmoothForceDirection,
        [string] $SmoothRoundness
    )
    $Object = [PSCustomObject] @{
        Type     = 'DiagramLink'
        Settings = @{
            from = $From
            to   = $To
        }
        Edges    = @{
            label              = $Label
            length             = $Length
            arrows             = [ordered]@{
                to     = [ordered]@{
                    enabled     = if ($ArrowsToEnabled) {
                        $ArrowsToEnabled.IsPresent } else {
                        $null }
                    scaleFactor = $ArrowsToScaleFactor
                    type        = $ArrowsToType
                }
                middle = [ordered]@{
                    enabled     = if ($ArrowsMiddleEnabled) {
                        $ArrowsMiddleEnabled.IsPresent } else {
                        $null }
                    scaleFactor = $ArrowsMiddleScaleFactor
                    type        = $ArrowsMiddleType
                }
                from   = [ordered]@{
                    enabled     = if ($ArrowsFromEnabled) {
                        $ArrowsFromEnabled.IsPresent } else {
                        $null }
                    scaleFactor = $ArrowsFromScaleFactor
                    type        = $ArrowsFromType
                }
            }
            arrowStrikethrough = if ($ArrowStrikethrough) {
                $ArrowStrikethrough.IsPresent } else {
                $null }
            chosen             = if ($Chosen) {
                $Chosen.IsPresent } else {
                $null }
            color              = [ordered]@{
                color     = ConvertFrom-Color -Color $Color
                highlight = ConvertFrom-Color -Color $ColorHighlight
                hover     = ConvertFrom-Color -Color $ColorHover
                inherit   = $ColorInherit
                opacity   = $ColorOpacity
            }
            font               = [ordered]@{
                color       = ConvertFrom-Color -Color $FontColor
                size        = ConvertFrom-Size -Size $FontSize
                face        = $FontName
                background  = ConvertFrom-Color -Color $FontBackground
                strokeWidth = ConvertFrom-Size -Size $FontStrokeWidth
                strokeColor = ConvertFrom-Color -Color $FontStrokeColor
                align       = $FontAlign
                multi       = $FontMulti
                vadjust     = $FontVAdjust
            }
            dashes             = if ($Dashes) {
                $Dashes.IsPresent } else {
                $null }
            widthConstraint    = $WidthConstraint
        }
    }
    if ($SmoothType -or $SmoothRoundness -or $SmoothForceDirection) {
        $Object.Edges['smooth'] = @{
            'enabled' = $true
        }
        if ($SmoothType) {
            $Object.Edges['smooth']['type'] = $SmoothType
        }
        if ($SmoothRoundness -ne '') {
            $Object.Edges['smooth']['roundness'] = $SmoothRoundness
        }
        if ($SmoothForceDirection) {
            $Object.Edges['smooth']['forceDirection'] = $SmoothForceDirection
        }
    }

    Remove-EmptyValue -Hashtable $Object.Settings -Recursive
    Remove-EmptyValue -Hashtable $Object.Edges -Recursive -Rerun 2
    $Object
}
Register-ArgumentCompleter -CommandName New-DiagramLink -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramLink -ParameterName ColorHighlight -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramLink -ParameterName ColorHover -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramLink -ParameterName FontColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramLink -ParameterName FontBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramLink -ParameterName FontStrokeColor -ScriptBlock $Script:ScriptBlockColors

function New-DiagramNode {
    <#
    .SYNOPSIS
    Creates nodes on a diagram
 
    .DESCRIPTION
    Creates nodes on a diagram
 
    .PARAMETER HtmlTextBox
    Experimental TextBox to put HTML instead of Image using SVG
 
    .PARAMETER Id
    Id of a node. If not set, label will be used as Id.
 
    .PARAMETER Label
    Label for a diagram
 
    .PARAMETER Title
    Label that shows up when hovering over node
 
    .PARAMETER To
    Parameter description
 
    .PARAMETER ArrowsToEnabled
    Parameter description
 
    .PARAMETER ArrowsMiddleEnabled
    Parameter description
 
    .PARAMETER ArrowsFromEnabled
    Parameter description
 
    .PARAMETER LinkColor
    Parameter description
 
    .PARAMETER Shape
    Parameter description
 
    .PARAMETER ImageType
    Parameter description
 
    .PARAMETER Image
    Parameter description
 
    .PARAMETER BorderWidth
    Parameter description
 
    .PARAMETER BorderWidthSelected
    Parameter description
 
    .PARAMETER BrokenImages
    Parameter description
 
    .PARAMETER Chosen
    Parameter description
 
    .PARAMETER ColorBorder
    Parameter description
 
    .PARAMETER ColorBackground
    Parameter description
 
    .PARAMETER ColorHighlightBorder
    Parameter description
 
    .PARAMETER ColorHighlightBackground
    Parameter description
 
    .PARAMETER ColorHoverBorder
    Parameter description
 
    .PARAMETER ColorHoverBackground
    Parameter description
 
    .PARAMETER FixedX
    Parameter description
 
    .PARAMETER FixedY
    Parameter description
 
    .PARAMETER FontColor
    Color of the label text.
 
    .PARAMETER FontSize
    Size of the label text.
 
    .PARAMETER FontName
    Font face (or font family) of the label text.
 
    .PARAMETER FontBackground
    When not undefined but a color string, a background rectangle will be drawn behind the label in the supplied color.
 
    .PARAMETER FontStrokeWidth
    As an alternative to the background rectangle, a stroke can be drawn around the text. When a value higher than 0 is supplied, the stroke will be draw
 
    .PARAMETER FontStrokeColor
    This is the color of the stroke assuming the value for stroke is higher than 0.
 
    .PARAMETER FontAlign
    This can be set to 'left' to make the label left-aligned. Otherwise, defaults to 'center'.
 
    .PARAMETER FontMulti
    If false, the label is treated as pure text drawn with the base font.
    If true or 'html' the label may be multifonted, with bold, italic and code markup, interpreted as html.
    If the value is 'markdown' or 'md' the label may be multifonted, with bold, italic and code markup, interpreted as markdown.
    The bold, italic, bold-italic and monospaced fonts may be set up under in the font.bold, font.ital, font.boldital and font.mono properties, respectively.
 
    .PARAMETER FontVAdjust
    Parameter description
 
    .PARAMETER Size
    The size is used to determine the size of node shapes that do not have the label inside of them.
    These shapes are: image, circularImage, diamond, dot, star, triangle, triangleDown, hexagon, square and icon
 
    .PARAMETER X
    Parameter description
 
    .PARAMETER Y
    Parameter description
 
    .PARAMETER IconAsImage
    Parameter description
 
    .PARAMETER IconColor
    Parameter description
 
    .PARAMETER IconBrands
    Parameter description
 
    .PARAMETER IconRegular
    Parameter description
 
    .PARAMETER IconSolid
    Parameter description
 
    .PARAMETER Level
    Parameter description
 
    .PARAMETER HeightConstraintMinimum
    Parameter description
 
    .PARAMETER HeightConstraintVAlign
    Parameter description
 
    .PARAMETER WidthConstraintMinimum
    Parameter description
 
    .PARAMETER WidthConstraintMaximum
    Parameter description
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [alias('DiagramNode')]
    [CmdLetBinding(DefaultParameterSetName = 'Shape')]
    param(
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][ScriptBlock] $HtmlTextBox,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $Id,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")] [string] $Label,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")] [string] $Title,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string[]] $To,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")]
        [switch] $ArrowsToEnabled,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")]
        [switch] $ArrowsMiddleEnabled,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")]
        [switch] $ArrowsFromEnabled,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")]
        [alias('EdgeColor')][string] $LinkColor,
        [parameter(ParameterSetName = "Shape")][string][ValidateSet(
            'circle', 'dot', 'diamond', 'ellipse', 'database', 'box', 'square', 'triangle', 'triangleDown', 'text', 'star', 'hexagon')] $Shape,
        [parameter(ParameterSetName = "Image")][ValidateSet('squareImage', 'circularImage')][string] $ImageType,
        [parameter(ParameterSetName = "Image")][uri] $Image,
        #[string] $BrokenImage,
        #[string] $ImagePadding,
        #[string] $ImagePaddingLeft,
        #[string] $ImagePaddingRight,
        #[string] $ImagePaddingTop,
        #[string] $ImagePaddingBottom,
        #[string] $UseImageSize,
        #[alias('BackgroundColor')][string] $Color,
        #[string] $Border,
        #[string] $HighlightBackground,
        #[string] $HighlightBorder,
        #[string] $HoverBackground,
        #[string] $HoverBorder,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $BorderWidth,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $BorderWidthSelected,
        [parameter(ParameterSetName = "Image")][string] $BrokenImages,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[bool]] $Chosen,
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $ColorBorder,
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $ColorBackground,
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $ColorHighlightBorder,
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $ColorHighlightBackground,
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $ColorHoverBorder,
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $ColorHoverBackground,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[bool]]$FixedX,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[bool]]$FixedY,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $FontColor,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $FontSize, #// px
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $FontName,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $FontBackground,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $FontStrokeWidth, #// px
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][string] $FontStrokeColor,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][ValidateSet('center', 'left')][string] $FontAlign,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][ValidateSet('false', 'true', 'markdown', 'html')][string]$FontMulti,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $FontVAdjust,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $Size,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $X,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $Y,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][switch] $IconAsImage,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconColor,
        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [string] $IconSolid,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $Level,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $HeightConstraintMinimum,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][ValidateSet('top', 'middle', 'bottom')][string] $HeightConstraintVAlign,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $WidthConstraintMinimum,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "Image")]
        [parameter(ParameterSetName = "Shape")][nullable[int]] $WidthConstraintMaximum
    )

    if (-not $Label) {
        Write-Warning 'New-DiagramNode - Label is required. Skipping node.'
        return
    }

    $Object = [PSCustomObject] @{
        Type     = 'DiagramNode'
        Settings = @{ }
        Edges    = @{ }
    }
    $Icon = @{ } 

    if (-not $ID) {
        $ID = $Label
    }

    if ($IconBrands -or $IconRegular -or $IconSolid) {
        $Script:HTMLSchema.Features.FontsAwesome = $true
        if ($IconBrands) {
            if (-not $IconAsImage) {

                $NodeShape = 'icon'
                $icon = @{
                    face   = '"Font Awesome 5 Brands"'
                    code   = -join ('\u', $Global:HTMLIcons.FontAwesomeBrands[$IconBrands])    
                    color  = ConvertFrom-Color -Color $IconColor
                    weight = 'bold'
                }
            } else {
                $NodeShape = 'image'
                $Image = -join ($Script:CurrentConfiguration.Features.FontsAwesome.Other.Link, 'brands/', $IconBrands, '.svg')
            }
        } elseif ($IconRegular) {
            if (-not $IconAsImage) {
                $NodeShape = 'icon'
                $icon = @{
                    face   = '"Font Awesome 5 Free"'
                    code   = -join ('\u', $Global:HTMLIcons.FontAwesomeRegular[$IconRegular])    
                    color  = ConvertFrom-Color -Color $IconColor
                    weight = 'bold'
                }
            } else {
                $NodeShape = 'image'
                $Image = -join ($Script:CurrentConfiguration.Features.FontsAwesome.Other.Link, 'regular/', $IconRegular, '.svg')
            }
        } else {
            if (-not $IconAsImage) {
                $NodeShape = 'icon'
                $icon = @{
                    face   = '"Font Awesome 5 Free"'
                    code   = -join ('\u', $Global:HTMLIcons.FontAwesomeSolid[$IconSolid])    
                    color  = ConvertFrom-Color -Color $IconColor
                    weight = 'bold'
                }
            } else {
                $NodeShape = 'image'
                $Image = -join ($Script:CurrentConfiguration.Features.FontsAwesome.Other.Link, 'solid/', $IconSolid, '.svg')
            }
        }
    } elseif ($Image) {
        if ($ImageType -eq 'squareImage') {
            $NodeShape = 'image'
        } else {
            $NodeShape = 'circularImage'
        }
    } elseif ($HtmlTextBox) {
        $OutputSVG = New-HTMLTag -Tag 'svg' -Attributes @{ xmlns = 'http://www.w3.org/2000/svg'; width = '390' ; height = '70' } {

            New-HTMLTag -Tag 'foreignObject' -Attributes @{x = "15"; y = "10"; width = "100%"; height = "100%"; } {
                New-HTMLTag -Tag 'div' -Attributes @{ xmlns = 'http://www.w3.org/1999/xhtml' } {
                    & $HtmlTextBox
                }
            }
        }
        $Image = ConvertTo-SVG -FileType 'svg+xml' -Content $OutputSVG
        $NodeShape = 'image'
    } else {
        $NodeShape = $Shape
    }

    if ($To) {
        $Object.Edges = [ordered] @{
            arrows = [ordered]@{
                to     = [ordered]@{
                    enabled = if ($ArrowsToEnabled) {
                        $ArrowsToEnabled.IsPresent } else {
                        $null }
                }
                middle = [ordered]@{
                    enabled = if ($ArrowsMiddleEnabled) {
                        $ArrowsMiddleEnabled.IsPresent } else {
                        $null }
                }
                from   = [ordered]@{
                    enabled = if ($ArrowsFromEnabled) {
                        $ArrowsFromEnabled.IsPresent } else {
                        $null }
                }
            }
            color  = [ordered]@{
                color = ConvertFrom-Color -Color $LinkColor
            }
            from   = if ($To) {
                $Id } else {
                '' }
            to     = if ($To) {
                $To } else {
                '' }
        }
    }
    $Object.Settings = [ordered] @{
        id                  = $Id
        label               = $Label
        title               = $Title
        shape               = $NodeShape

        image               = $Image
        icon                = $icon

        level               = $Level

        borderWidth         = $BorderWidth
        borderWidthSelected = $BorderWidthSelected
        brokenImage         = $BrokenImage

        chosen              = $Chosen
        color               = [ordered]@{
            border     = ConvertFrom-Color -Color $ColorBorder
            background = ConvertFrom-Color -Color $ColorBackground
            highlight  = [ordered]@{
                border     = ConvertFrom-Color -Color $ColorHighlightBorder
                background = ConvertFrom-Color -Color $ColorHighlightBackground
            }
            hover      = [ordered]@{
                border     = ConvertFrom-Color -Color $ColorHoverBorder
                background = ConvertFrom-Color -Color $ColorHoverBackground
            }
        }
        fixed               = [ordered]@{
            x = $FixedX
            y = $FixedY
        }
        font                = [ordered]@{
            color       = ConvertFrom-Color -Color $FontColor
            size        = $FontSize
            face        = $FontName
            background  = ConvertFrom-Color -Color $FontBackground
            strokeWidth = $FontStrokeWidth
            strokeColor = ConvertFrom-Color -Color $FontStrokeColor
            align       = $FontAlign
            multi       = $FontMulti
            vadjust     = $FontVAdjust
        }
        size                = $Size
        heightConstraint    = @{
            minimum = $HeightConstraintMinimum
            valign  = $HeightConstraintVAlign
        }
        widthConstraint     = @{
            minimum = $WidthConstraintMinimum
            maximum = $WidthConstraintMaximum
        }
        x                   = $X
        y                   = $Y
    }

    Remove-EmptyValue -Hashtable $Object.Settings -Recursive -Rerun 2
    Remove-EmptyValue -Hashtable $Object.Edges -Recursive -Rerun 2
    $Object
}
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName ColorBorder -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName ColorBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName ColorHighlightBorder -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName ColorHighlightBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName ColorHoverBorder -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName ColorHoverBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName FontColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName FontBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName FontStrokeColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName IconColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramNode -ParameterName LinkColor -ScriptBlock $Script:ScriptBlockColors

function New-DiagramOptionsInteraction {
    <#
    .SYNOPSIS
    Creates a new diagram options interaction object with customizable options.
 
    .DESCRIPTION
    This function defines a diagram options interaction object with various interactive features such as dragging nodes, dragging view, hiding edges on drag, hiding edges on zoom, hiding nodes on drag, hover effects, keyboard navigation, multiselect, navigation buttons, and selection options.
 
    .PARAMETER DragNodes
    Specifies whether dragging nodes is enabled.
 
    .PARAMETER DragView
    Specifies whether dragging the view is enabled.
 
    .PARAMETER HideEdgesOnDrag
    Specifies whether edges are hidden when dragging.
 
    .PARAMETER HideEdgesOnZoom
    Specifies whether edges are hidden when zooming.
 
    .PARAMETER HideNodesOnDrag
    Specifies whether nodes are hidden when dragging.
 
    .PARAMETER Hover
    Specifies whether hover effects are enabled.
 
    .PARAMETER HoverConnectedEdges
    Specifies whether connected edges are affected by hover.
 
    .PARAMETER KeyboardEnabled
    Specifies whether keyboard navigation is enabled.
 
    .PARAMETER KeyboardSpeedX
    Specifies the speed of movement in the X direction using the keyboard.
 
    .PARAMETER KeyboardSpeedY
    Specifies the speed of movement in the Y direction using the keyboard.
 
    .PARAMETER KeyboardSpeedZoom
    Specifies the speed of zooming using the keyboard.
 
    .PARAMETER KeyboardBindToWindow
    Specifies whether keyboard events are bound to the window.
 
    .PARAMETER Multiselect
    Specifies whether multiple selections are allowed.
 
    .PARAMETER NavigationButtons
    Specifies whether navigation buttons are displayed.
 
    .PARAMETER Selectable
    Specifies whether nodes and edges are selectable.
 
    .PARAMETER SelectConnectedEdges
    Specifies whether connected edges are selected along with nodes.
 
    .EXAMPLE
    New-DiagramOptionsInteraction -DragNodes -DragView -HideEdgesOnDrag -HideNodesOnDrag -Hover -KeyboardEnabled -Multiselect -Selectable -SelectConnectedEdges
    Creates a new diagram options interaction object with basic interactive features enabled.
 
    .EXAMPLE
    New-DiagramOptionsInteraction -DragNodes -DragView -HideEdgesOnDrag -HideNodesOnDrag -Hover -KeyboardEnabled -Multiselect -Selectable -SelectConnectedEdges -NavigationButtons
    Creates a new diagram options interaction object with navigation buttons added.
 
    .NOTES
    Based on options https://visjs.github.io/vis-network/docs/network/interaction.html#
 
    #>

    [alias('DiagramOptionsInteraction')]
    [CmdletBinding()]
    param(
        [nullable[bool]] $DragNodes,
        [nullable[bool]] $DragView,
        [nullable[bool]] $HideEdgesOnDrag,
        [nullable[bool]] $HideEdgesOnZoom,
        [nullable[bool]] $HideNodesOnDrag,
        [nullable[bool]] $Hover,
        [nullable[bool]] $HoverConnectedEdges,
        [nullable[bool]] $KeyboardEnabled,
        [nullable[int]] $KeyboardSpeedX,
        [nullable[int]] $KeyboardSpeedY,
        [nullable[decimal]] $KeyboardSpeedZoom,
        [nullable[bool]] $KeyboardBindToWindow,
        [nullable[bool]] $Multiselect,
        [nullable[bool]] $NavigationButtons,
        [nullable[bool]] $Selectable,
        [nullable[bool]] $SelectConnectedEdges,
        [nullable[int]] $TooltipDelay,
        [nullable[bool]] $ZoomView
    )

    $Object = [PSCustomObject] @{
        Type     = 'DiagramOptionsInteraction'
        Settings = @{
            interaction = [ordered] @{
                dragNodes            = $DragNodes
                dragView             = $DragView
                hideEdgesOnDrag      = $HideEdgesOnDrag
                hideEdgesOnZoom      = $HideEdgesOnZoom
                hideNodesOnDrag      = $HideNodesOnDrag
                hover                = $Hover
                hoverConnectedEdges  = $HoverConnectedEdges
                keyboard             = @{
                    enabled      = $KeyboardEnabled
                    speed        = @{
                        x    = $KeyboardSpeedX
                        y    = $KeyboardSpeedY
                        zoom = $KeyboardSpeedZoom
                    }
                    bindToWindow = $KeyboardBindToWindow
                }
                multiselect          = $Multiselect
                navigationButtons    = $NavigationButtons
                selectable           = $Selectable
                selectConnectedEdges = $SelectConnectedEdges
                tooltipDelay         = $TooltipDelay
                zoomView             = $ZoomView
            }
        }
    }
    Remove-EmptyValue -Hashtable $Object.Settings -Recursive -Rerun 2
    $Object
}

function New-DiagramOptionsLayout {
    <#
    .SYNOPSIS
    Defines layout options for hierarchical diagrams.
 
    .DESCRIPTION
    When enabling the hierarchical layout, it overrules some of the other options.
    The physics is set to the hierarchical repulsion solver and dynamic smooth edges are converted to static smooth edges.
 
    .PARAMETER RandomSeed
    When NOT using the hierarchical layout, the nodes are randomly positioned initially. This means that the settled result is different every time. If you provide a random seed manually, the layout will be the same every time. Ideally you try with an undefined seed, reload until you are happy with the layout and use the getSeed() method to ascertain the seed.
 
    .PARAMETER ImprovedLayout
    When enabled, the network will use the Kamada Kawai algorithm for initial layout. For networks larger than 100 nodes, clustering will be performed automatically to reduce the amount of nodes. This can greatly improve the stabilization times. If the network is very interconnected (no or few leaf nodes), this may not work and it will revert back to the old method. Performance will be improved in the future.
 
    .PARAMETER ClusterThreshold
    Cluster threshold to which improvedLayout applies.
 
    .PARAMETER HierarchicalEnabled
    When true, the layout engine positions the nodes in a hierarchical fashion using default settings.
    Toggle the usage of the hierarchical layout system. If this option is not defined, it is set to true if any of the properties in this object are defined.
 
    .PARAMETER HierarchicalLevelSeparation
    The distance between the different levels.
 
    .PARAMETER HierarchicalNodeSpacing
    Minimum distance between nodes on the free axis. This is only for the initial layout. If you enable physics, the node distance there will be the effective node distance.
 
    .PARAMETER HierarchicalTreeSpacing
    Distance between different trees (independent networks). This is only for the initial layout. If you enable physics, the repulsion model will denote the distance between the trees.
 
    .PARAMETER HierarchicalBlockShifting
    Method for reducing whitespace. Can be used alone or together with edge minimization. Each node will check for whitespace and will shift it's branch along with it for as far as it can, respecting the nodeSpacing on any level. This is mainly for the initial layout. If you enable physics, the layout will be determined by the physics. This will greatly speed up the stabilization time though!
 
    .PARAMETER HierarchicalEdgeMinimization
    Method for reducing whitespace. Can be used alone or together with block shifting. Enabling block shifting will usually speed up the layout process. Each node will try to move along its free axis to reduce the total length of it's edges. This is mainly for the initial layout. If you enable physics, the layout will be determined by the physics. This will greatly speed up the stabilization time though!
 
    .PARAMETER HierarchicalParentCentralization
    When true, the parents nodes will be centered again after the layout algorithm has been finished.
 
    .PARAMETER HierarchicalDirection
    The direction of the hierarchical layout. The available options are: UD, DU, LR, RL. To simplify: up-down, down-up, left-right, right-left.
 
    .PARAMETER HierarchicalSortMethod
    The algorithm used to ascertain the levels of the nodes based on the data. The possible options are: hubsize, directed.
    Hubsize takes the nodes with the most edges and puts them at the top. From that the rest of the hierarchy is evaluated.
    Directed adheres to the to and from data of the edges. A --> B so B is a level lower than A.
 
    .PARAMETER HierarchicalShakeTowards
    Controls whether in directed layout should all the roots be lined up at the top and their child nodes as close to their roots as possible (roots) or all the leaves lined up at the bottom and their parents as close to their children as possible (leaves, default).
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [alias('DiagramOptionsLayout')]
    [CmdletBinding()]
    param(
        [nullable[int]] $RandomSeed,
        [nullable[bool]] $ImprovedLayout,
        [nullable[int]] $ClusterThreshold ,
        [nullable[bool]] $HierarchicalEnabled,
        [nullable[int]] $HierarchicalLevelSeparation,
        [nullable[int]] $HierarchicalNodeSpacing,
        [nullable[int]] $HierarchicalTreeSpacing,
        [nullable[bool]] $HierarchicalBlockShifting,
        [nullable[bool]] $HierarchicalEdgeMinimization,
        [nullable[bool]] $HierarchicalParentCentralization,
        [ValidateSet('FromUpToDown', 'FromDownToUp', 'FromLeftToRight', 'FromRigthToLeft')][string] $HierarchicalDirection,
        [ValidateSet('hubsize', 'directed')][string] $HierarchicalSortMethod,
        [ValidateSet('roots', 'leaves')][string] $HierarchicalShakeTowards
    )
    $Direction = @{
        FromUpToDown    = 'UD'
        FromDownToUp    = 'DU'
        FromLeftToRight = 'LR'
        FromRigthToLeft = 'RL'
    }

    $Object = [PSCustomObject] @{
        Type     = 'DiagramOptionsLayout'
        Settings = [ordered] @{
            layout = [ordered] @{
                randomSeed       = $RandomSeed
                improvedLayout   = $ImprovedLayout
                clusterThreshold = $ClusterThreshold
                hierarchical     = [ordered]@{
                    enabled              = $HierarchicalEnabled
                    levelSeparation      = $HierarchicalLevelSeparation
                    nodeSpacing          = $HierarchicalNodeSpacing
                    treeSpacing          = $HierarchicalTreeSpacing
                    blockShifting        = $HierarchicalBlockShifting
                    edgeMinimization     = $HierarchicalEdgeMinimization
                    parentCentralization = $HierarchicalParentCentralization
                    direction            = $Direction[$HierarchicalDirection] 
                    sortMethod           = $HierarchicalSortMethod 
                    shakeTowards         = $HierarchicalShakeTowards
                }
            }
        }
    }
    Remove-EmptyValue -Hashtable $Object.Settings -Recursive -Rerun 2
    $Object
}

function New-DiagramOptionsLinks {
    <#
    .SYNOPSIS
    Creates a new set of options for diagram edges in a diagram.
 
    .DESCRIPTION
    This function creates a new set of options for diagram edges in a diagram. It allows customization of various properties such as arrows, colors, fonts, and constraints for diagram edges.
 
    .PARAMETER ArrowsToEnabled
    Specifies whether arrows pointing to the edge are enabled.
 
    .PARAMETER ArrowsToScaleFactor
    Specifies the scale factor for arrows pointing to the edge.
 
    .PARAMETER ArrowsToType
    Specifies the type of arrows pointing to the edge. Valid values are 'arrow', 'bar', 'circle'.
 
    .PARAMETER ArrowsMiddleEnabled
    Specifies whether arrows in the middle of the edge are enabled.
 
    .PARAMETER ArrowsMiddleScaleFactor
    Specifies the scale factor for arrows in the middle of the edge.
 
    .PARAMETER ArrowsMiddleType
    Specifies the type of arrows in the middle of the edge. Valid values are 'arrow', 'bar', 'circle'.
 
    .PARAMETER ArrowsFromEnabled
    Specifies whether arrows pointing from the edge are enabled.
 
    .PARAMETER ArrowsFromScaleFactor
    Specifies the scale factor for arrows pointing from the edge.
 
    .PARAMETER ArrowsFromType
    Specifies the type of arrows pointing from the edge. Valid values are 'arrow', 'bar', 'circle'.
 
    .PARAMETER ArrowStrikethrough
    Specifies whether the arrow should have a strikethrough effect.
 
    .PARAMETER Chosen
    Specifies whether the edge is chosen.
 
    .PARAMETER Color
    Specifies the color of the edge.
 
    .PARAMETER ColorHighlight
    Specifies the color of the edge when highlighted.
 
    .PARAMETER ColorHover
    Specifies the color of the edge when hovered over.
 
    .PARAMETER ColorInherit
    Specifies how the color of the edge should inherit. Valid values are 'true', 'false', 'from', 'to', 'both'.
 
    .PARAMETER ColorOpacity
    Specifies the opacity of the color of the edge. Range between 0 and 1.
 
    .PARAMETER Dashes
    Specifies whether the edge should be dashed.
 
    .PARAMETER Length
    Specifies the length of the edge.
 
    .PARAMETER FontColor
    Specifies the color of the font on the edge.
 
    .PARAMETER FontSize
    Specifies the font size in pixels.
 
    .PARAMETER FontName
    Specifies the font name for the edge.
 
    .PARAMETER FontBackground
    Specifies the background color of the font on the edge.
 
    .PARAMETER FontStrokeWidth
    Specifies the stroke width of the font in pixels.
 
    .PARAMETER FontStrokeColor
    Specifies the stroke color of the font on the edge.
 
    .PARAMETER FontAlign
    Specifies the alignment of the font. Valid values are 'center', 'left'.
 
    .PARAMETER FontMulti
    Specifies whether the font supports multiline. Valid values are 'false', 'true', 'markdown', 'html'.
 
    .PARAMETER FontVAdjust
    Specifies the vertical adjustment of the font.
 
    .PARAMETER WidthConstraint
    Specifies the width constraint of the edge.
 
    .EXAMPLE
    New-DiagramOptionsLinks -ArrowsToEnabled $true -ArrowsToScaleFactor 2 -ArrowsToType 'arrow' -ArrowsMiddleEnabled $false -ArrowsMiddleScaleFactor 1 -ArrowsMiddleType 'bar' -ArrowsFromEnabled $true -ArrowsFromScaleFactor 2 -ArrowsFromType 'circle' -ArrowStrikethrough $false -Chosen $true -Color 'blue' -ColorHighlight 'yellow' -ColorHover 'green' -ColorInherit 'from' -ColorOpacity 0.8 -Dashes $false -Length '100px' -FontColor 'black' -FontSize 12 -FontName 'Arial' -FontBackground 'white' -FontStrokeWidth 1 -FontStrokeColor 'gray' -FontAlign 'center' -FontMulti 'true' -FontVAdjust 0 -WidthConstraint 150
 
    Description
    -----------
    Creates a new set of diagram edge options with specific configurations.
    #>

    [alias('DiagramOptionsEdges', 'New-DiagramOptionsEdges', 'DiagramOptionsLinks')]
    [CmdletBinding()]
    param(
        [nullable[bool]] $ArrowsToEnabled,
        [nullable[int]] $ArrowsToScaleFactor,
        [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsToType,
        [nullable[bool]] $ArrowsMiddleEnabled,
        [nullable[int]] $ArrowsMiddleScaleFactor,
        [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsMiddleType,
        [nullable[bool]] $ArrowsFromEnabled,
        [nullable[int]] $ArrowsFromScaleFactor,
        [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsFromType,
        [nullable[bool]] $ArrowStrikethrough,
        [nullable[bool]] $Chosen,
        [string] $Color,
        [string] $ColorHighlight,
        [string] $ColorHover,
        [ValidateSet('true', 'false', 'from', 'to', 'both')][string]$ColorInherit,
        [nullable[double]] $ColorOpacity, # range between 0 and 1
        [nullable[bool]]  $Dashes,
        [string] $Length,
        [string] $FontColor,
        [nullable[int]] $FontSize, #// px
        [string] $FontName,
        [string] $FontBackground,
        [nullable[int]] $FontStrokeWidth, #// px
        [string] $FontStrokeColor,
        [ValidateSet('center', 'left')][string] $FontAlign,
        [ValidateSet('false', 'true', 'markdown', 'html')][string]$FontMulti,
        [nullable[int]] $FontVAdjust,
        [nullable[int]] $WidthConstraint
    )
    $Object = [PSCustomObject] @{
        Type     = 'DiagramOptionsEdges'
        Settings = @{
            edges = [ordered] @{

                arrows             = [ordered]@{
                    to     = [ordered]@{
                        enabled     = $ArrowsToEnabled
                        scaleFactor = $ArrowsToScaleFactor
                        type        = $ArrowsToType
                    }
                    middle = [ordered]@{
                        enabled     = $ArrowsMiddleEnabled
                        scaleFactor = $ArrowsMiddleScaleFactor
                        type        = $ArrowsMiddleType
                    }
                    from   = [ordered]@{
                        enabled     = $ArrowsFromEnabled
                        scaleFactor = $ArrowsFromScaleFactor
                        type        = $ArrowsFromType
                    }
                }
                arrowStrikethrough = $ArrowStrikethrough
                chosen             = $Chosen
                color              = [ordered]@{
                    color     = ConvertFrom-Color -Color $Color
                    highlight = ConvertFrom-Color -Color $ColorHighlight
                    hover     = ConvertFrom-Color -Color $ColorHover
                    inherit   = $ColorInherit
                    opacity   = $ColorOpacity
                }
                font               = [ordered]@{
                    color       = ConvertFrom-Color -Color $FontColor
                    size        = $FontSize
                    face        = $FontName
                    background  = ConvertFrom-Color -Color $FontBackground
                    strokeWidth = $FontStrokeWidth
                    strokeColor = ConvertFrom-Color -Color $FontStrokeColor
                    align       = $FontAlign
                    multi       = $FontMulti
                    vadjust     = $FontVAdjust
                }
                dashes             = $Dashes
                widthConstraint    = $WidthConstraint
            }
        }
    }
    Remove-EmptyValue -Hashtable $Object.Settings -Recursive -Rerun 2
    $Object
}
Register-ArgumentCompleter -CommandName New-DiagramOptionsLinks -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsLinks -ParameterName ColorHighlight -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsLinks -ParameterName ColorHover -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsLinks -ParameterName FontColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsLinks -ParameterName FontBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsLinks -ParameterName FontStrokeColor -ScriptBlock $Script:ScriptBlockColors

function New-DiagramOptionsManipulation {
    <#
    .SYNOPSIS
    Creates a new object for manipulating diagram options in a network visualization.
 
    .DESCRIPTION
    This function creates a new object that allows manipulation of diagram options in a network visualization. It provides options to control the initial state of manipulation tools and specify which manipulation actions are allowed.
 
    .PARAMETER InitiallyActive
    Specifies whether the manipulation tools are initially active upon loading the visualization.
 
    .PARAMETER AddNode
    Specifies whether the option to add nodes is enabled.
 
    .PARAMETER AddEdge
    Specifies whether the option to add edges is enabled.
 
    .PARAMETER EditNode
    Specifies whether the option to edit nodes is enabled.
 
    .PARAMETER EditEdge
    Specifies whether the option to edit edges is enabled.
 
    .PARAMETER DeleteNode
    Specifies whether the option to delete nodes is enabled.
 
    .PARAMETER DeleteEdge
    Specifies whether the option to delete edges is enabled.
 
    .EXAMPLE
    $options = New-DiagramOptionsManipulation -InitiallyActive $true -AddNode $true -AddEdge $true
    Creates a new object with manipulation options where adding nodes and edges is allowed and the manipulation tools are initially active.
 
    .NOTES
    This function is based on the official vis.js documentation for network manipulation: https://visjs.github.io/vis-network/docs/network/manipulation.html
    Please note that this implementation is incomplete and may require additional customization.
    #>

    [alias('DiagramOptionsManipulation')]
    [CmdletBinding()]
    param(
        [nullable[bool]] $InitiallyActive,
        [nullable[bool]] $AddNode,
        [nullable[bool]] $AddEdge,
        [nullable[bool]] $EditNode,
        [nullable[bool]] $EditEdge,
        [nullable[bool]] $DeleteNode,
        [nullable[bool]] $DeleteEdge
    )

    $Object = [PSCustomObject] @{
        Type     = 'DiagramOptionsManipulation'
        Settings = @{
            manipulation = [ordered] @{
                enabled         = $true
                initiallyActive = $InitiallyActive
                addNode         = $AddNode
                addEdge         = $AddEdge
                editNode        = $EditNode
                editEdge        = $EditEdge
                deleteNode      = $DeleteNode
                deleteEdge      = $DeleteEdge
            }
        }
    }
    Remove-EmptyValue -Hashtable $Object.Settings -Recursive
    $Object
}
function New-DiagramOptionsNodes {
    <#
    .SYNOPSIS
    Creates a custom object representing options for diagram nodes.
 
    .DESCRIPTION
    This function creates a custom object with various settings for diagram nodes, such as border width, colors, font styles, shape, constraints, and margins.
 
    .PARAMETER BorderWidth
    Specifies the width of the border around the node.
 
    .PARAMETER BorderWidthSelected
    Specifies the width of the border around the node when selected.
 
    .PARAMETER BrokenImage
    Specifies the image to display if the node image is broken.
 
    .PARAMETER Chosen
    Indicates whether the node is chosen.
 
    .PARAMETER ColorBorder
    Specifies the color of the node border.
 
    .PARAMETER ColorBackground
    Specifies the background color of the node.
 
    .PARAMETER ColorHighlightBorder
    Specifies the color of the node border when highlighted.
 
    .PARAMETER ColorHighlightBackground
    Specifies the background color of the node when highlighted.
 
    .PARAMETER ColorHoverBorder
    Specifies the color of the node border when hovered over.
 
    .PARAMETER ColorHoverBackground
    Specifies the background color of the node when hovered over.
 
    .PARAMETER FixedX
    Indicates whether the node's X position is fixed.
 
    .PARAMETER FixedY
    Indicates whether the node's Y position is fixed.
 
    .PARAMETER FontColor
    Specifies the color of the node's font.
 
    .PARAMETER FontSize
    Specifies the font size of the node text in pixels.
 
    .PARAMETER FontName
    Specifies the font family of the node text.
 
    .PARAMETER FontBackground
    Specifies the background color of the node's font.
 
    .PARAMETER FontStrokeWidth
    Specifies the stroke width of the node's font.
 
    .PARAMETER FontStrokeColor
    Specifies the stroke color of the node's font.
 
    .PARAMETER FontAlign
    Specifies the alignment of the node's text (center or left).
 
    .PARAMETER FontMulti
    Specifies the multi-line mode for the node's text (false, true, markdown, html).
 
    .PARAMETER FontVAdjust
    Specifies the vertical adjustment of the node's text.
 
    .PARAMETER Size
    Specifies the size of the node.
 
    .PARAMETER Shape
    Specifies the shape of the node (circle, dot, diamond, ellipse, database, box, square, triangle, triangleDown, text, star, hexagon).
 
    .PARAMETER HeightConstraintMinimum
    Specifies the minimum height constraint for the node.
 
    .PARAMETER HeightConstraintVAlign
    Specifies the vertical alignment of the node within its height constraint (top, middle, bottom).
 
    .PARAMETER WidthConstraintMinimum
    Specifies the minimum width constraint for the node.
 
    .PARAMETER WidthConstraintMaximum
    Specifies the maximum width constraint for the node.
 
    .PARAMETER Margin
    Specifies the margin around the node on all sides.
 
    .PARAMETER MarginTop
    Specifies the top margin around the node.
 
    .PARAMETER MarginRight
    Specifies the right margin around the node.
 
    .PARAMETER MarginBottom
    Specifies the bottom margin around the node.
 
    .PARAMETER MarginLeft
    Specifies the left margin around the node.
 
    .EXAMPLE
    New-DiagramOptionsNodes -BorderWidth 1 -BorderWidthSelected 2 -ColorBorder '#2B7CE9' -ColorBackground '#97C2FC' -Shape 'circle' -Size 50
    Creates a new diagram node with specified border widths, colors, shape, and size.
 
    .EXAMPLE
    New-DiagramOptionsNodes -FontColor '#000000' -FontSize 12 -FontName 'Arial' -Shape 'square' -Size 30
    Creates a new diagram node with specified font color, size, font family, shape, and size.
 
    #>

    [alias('DiagramOptionsNodes')]
    [CmdletBinding()]
    param(
        [nullable[int]] $BorderWidth,
        [nullable[int]] $BorderWidthSelected,
        [string] $BrokenImage,
        [nullable[bool]] $Chosen,
        [string] $ColorBorder,
        [string] $ColorBackground,
        [string] $ColorHighlightBorder,
        [string] $ColorHighlightBackground,
        [string] $ColorHoverBorder,
        [string] $ColorHoverBackground,
        [nullable[bool]] $FixedX,
        [nullable[bool]] $FixedY,
        [string] $FontColor,
        [nullable[int]] $FontSize, #// px
        [string] $FontName,
        [string] $FontBackground,
        [nullable[int]] $FontStrokeWidth, #// px
        [string] $FontStrokeColor,
        [ValidateSet('center', 'left')][string] $FontAlign,
        [ValidateSet('false', 'true', 'markdown', 'html')][string]$FontMulti,
        [nullable[int]] $FontVAdjust,
        [nullable[int]] $Size,
        [parameter(ParameterSetName = "Shape")][string][ValidateSet(
            'circle', 'dot', 'diamond', 'ellipse', 'database', 'box', 'square', 'triangle', 'triangleDown', 'text', 'star', 'hexagon')] $Shape,
        [nullable[int]] $HeightConstraintMinimum,
        [ValidateSet('top', 'middle', 'bottom')][string] $HeightConstraintVAlign,
        [nullable[int]] $WidthConstraintMinimum,
        [nullable[int]] $WidthConstraintMaximum,
        [nullable[int]] $Margin,
        [nullable[int]] $MarginTop,
        [nullable[int]] $MarginRight,
        [nullable[int]] $MarginBottom,
        [nullable[int]] $MarginLeft
    )
    $Object = [PSCustomObject] @{
        Type     = 'DiagramOptionsNodes'
        Settings = @{
            nodes = [ordered] @{
                borderWidth         = $BorderWidth
                borderWidthSelected = $BorderWidthSelected
                brokenImage         = $BrokenImage
                chosen              = $Chosen
                color               = [ordered]@{
                    border     = ConvertFrom-Color -Color $ColorBorder
                    background = ConvertFrom-Color -Color $ColorBackground
                    highlight  = [ordered]@{
                        border     = ConvertFrom-Color -Color $ColorHighlightBorder
                        background = ConvertFrom-Color -Color $ColorHighlightBackground
                    }
                    hover      = [ordered]@{
                        border     = ConvertFrom-Color -Color $ColorHoverBorder
                        background = ConvertFrom-Color -Color $ColorHoverBackground
                    }
                }
                fixed               = [ordered]@{
                    x = $FixedX
                    y = $FixedY
                }
                font                = [ordered]@{
                    color       = ConvertFrom-Color -Color $FontColor
                    size        = $FontSize 
                    face        = $FontName
                    background  = ConvertFrom-Color -Color $FontBackground
                    strokeWidth = $FontStrokeWidth 
                    strokeColor = ConvertFrom-Color -Color $FontStrokeColor
                    align       = $FontAlign
                    multi       = $FontMulti
                    vadjust     = $FontVAdjust
                }
                heightConstraint    = @{
                    minimum = $HeightConstraintMinimum
                    valign  = $HeightConstraintVAlign
                }
                size                = $Size
                shape               = $Shape
                widthConstraint     = @{
                    minimum = $WidthConstraintMinimum
                    maximum = $WidthConstraintMaximum
                }
            }
        }
    }

    if ($Margin) {
        $Object.Settings.nodes.margin = $Margin
    } else {
        $Object.Settings.nodes.margin = @{
            top    = $MarginTop
            right  = $MarginRight
            bottom = $MarginBottom
            left   = $MarginLeft
        }
    }

    Remove-EmptyValue -Hashtable $Object.Settings -Recursive -Rerun 2
    $Object
}

Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName ColorBorder -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName ColorBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName ColorHighlightBorder -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName ColorHighlightBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName ColorHoverBorder -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName ColorHoverBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName FontColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName FontBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-DiagramOptionsNodes -ParameterName FontStrokeColor -ScriptBlock $Script:ScriptBlockColors

function New-DiagramOptionsPhysics {
    <#
    .SYNOPSIS
    Configures the physics settings for a network visualization.
 
    .DESCRIPTION
    Configures the physics settings for a network visualization in a detailed manner, allowing customization of stabilization, velocity limits, timestep, and other parameters to optimize the behavior of the physics simulation.
 
    .PARAMETER Enabled
    Toggle the physics system on or off. This property is optional. If you define any of the options below and enabled is undefined, this will be set to true.
 
    .PARAMETER Solver
    You can select your own solver. Possible options: 'barnesHut', 'repulsion', 'hierarchicalRepulsion', 'forceAtlas2Based'. When setting the hierarchical layout, the hierarchical repulsion solver is automatically selected, regardless of what you fill in here.
 
    .PARAMETER StabilizationEnabled
    Toggle the stabilization. This is an optional property. If undefined, it is automatically set to true when any of the properties of this object are defined.
 
    .PARAMETER Stabilizationiterations
    The physics module tries to stabilize the network on load up til a maximum number of iterations defined here. If the network stabilized with less, you are finished before the maximum number.
 
    .PARAMETER StabilizationupdateInterval
    When stabilizing, the DOM can freeze. You can chop the stabilization up into pieces to show a loading bar for instance. The interval determines after how many iterations the stabilizationProgress event is triggered.
 
    .PARAMETER StabilizationonlyDynamicEdges
    If you have predefined the position of all nodes and only want to stabilize the dynamic smooth edges, set this to true. It freezes all nodes except the invisible dynamic smooth curve support nodes. If you want the visible nodes to move and stabilize, do not use this.
 
    .PARAMETER Stabilizationfit
    Toggle whether or not you want the view to zoom to fit all nodes when the stabilization is finished.
 
    .PARAMETER MaxVelocity
    The physics module limits the maximum velocity of the nodes to increase the time to stabilization. This is the maximum value.
 
    .PARAMETER MinVelocity
    Once the minimum velocity is reached for all nodes, we assume the network has been stabilized and the simulation stops.
 
    .PARAMETER Timestep
    The physics simulation is discrete. This means we take a step in time, calculate the forces, move the nodes and take another step. If you increase this number the steps will be too large and the network can get unstable. If you see a lot of jittery movement in the network, you may want to reduce this value a little.
 
    .PARAMETER AdaptiveTimestep
    If this is enabled, the timestep will intelligently be adapted (only during the stabilization stage if stabilization is enabled!) to greatly decrease stabilization times. The timestep configured above is taken as the minimum timestep. This can be further improved by using the improvedLayout algorithm.
    Layout: https://visjs.github.io/vis-network/docs/network/layout.html#layout
 
    .PARAMETER BarnesHutTheta
    This parameter determines the boundary between consolidated long range forces and individual short range forces. To oversimplify higher values are faster but generate more errors, lower values are slower but with less errors.
 
    .PARAMETER BarnesHutGravitationalConstant
    Gravity attracts. We like repulsion. So the value is negative. If you want the repulsion to be stronger, decrease the value (so -10000, -50000).
 
    .PARAMETER BarnesHutCentralGravity
    There is a central gravity attractor to pull the entire network back to the center.
 
    .PARAMETER BarnesHutSpringLength
    The edges are modelled as springs. This springLength here is the rest length of the spring.
 
    .PARAMETER BarnesHutSpringConstant
    This is how 'sturdy' the springs are. Higher values mean stronger springs.
 
    .PARAMETER BarnesHutDamping
    Accepted range: [0 .. 1]. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.
 
    .PARAMETER BarnesHutAvoidOverlap
    Accepted range: [0 .. 1]. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for both the gravity model. Value 1 is maximum overlap avoidance.
 
    .PARAMETER ForceAtlas2BasedTheta
    This parameter determines the boundary between consolidated long range forces and individual short range forces. To oversimplify higher values are faster but generate more errors, lower values are slower but with less errors.
 
    .PARAMETER ForceAtlas2BasedGravitationalConstant
    This is similar to the barnesHut method except that the falloff is linear instead of quadratic. The connectivity is also taken into account as a factor of the mass. If you want the repulsion to be stronger, decrease the value (so -1000, -2000).
 
    .PARAMETER ForceAtlas2BasedCentralGravity
    There is a central gravity attractor to pull the entire network back to the center. This is not dependent on distance.
 
    .PARAMETER ForceAtlas2BasedSpringLength
    The edges are modelled as springs. This springLength here is the rest length of the spring.
 
    .PARAMETER ForceAtlas2BasedSpringConstant
    This is how 'sturdy' the springs are. Higher values mean stronger springs.
 
    .PARAMETER ForceAtlas2BasedDamping
    Accepted range: [0 .. 1]. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.
 
    .PARAMETER ForceAtlas2BasedAvoidOverlap
    Accepted range: [0 .. 1]. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for both the gravity model. Value 1 is maximum overlap avoidance.
 
    .PARAMETER RepulsionNodeDistance
    This is the range of influence for the repulsion.
 
    .PARAMETER RepulsionCentralGravity
    There is a central gravity attractor to pull the entire network back to the center.
 
    .PARAMETER RepulsionSpringLength
    The edges are modelled as springs. This springLength here is the rest length of the spring.
 
    .PARAMETER RepulsionSpringConstant
    This is how 'sturdy' the springs are. Higher values mean stronger springs.
 
    .PARAMETER RepulsionDamping
    Accepted range: [0 .. 1]. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.
 
    .PARAMETER HierarchicalRepulsionNodeDistance
    This is the range of influence for the repulsion. Default (Number) Default 120
 
    .PARAMETER HierarchicalRepulsionCentralGravity
    There is a central gravity attractor to pull the entire network back to the center. Default (Number) 0.0
 
    .PARAMETER HierarchicalRepulsionSpringLength
    The edges are modelled as springs. This springLength here is the rest length of the spring.
 
    .PARAMETER HierarchicalRepulsionSpringConstant
    This is how 'sturdy' the springs are. Higher values mean stronger springs.
 
    .PARAMETER HierarchicalRepulsionDamping
    Accepted range: [0 .. 1]. The damping factor is how much of the velocity from the previous physics simulation iteration carries over to the next iteration.
 
    .PARAMETER HierarchicalRepulsionAvoidOverlap
    Accepted range: [0 .. 1]. When larger than 0, the size of the node is taken into account. The distance will be calculated from the radius of the encompassing circle of the node for both the gravity model. Value 1 is maximum overlap avoidance.
 
    .PARAMETER WindX
    The amount of force to be applied pushing non-fixed nodes to the right (positive value) or to the left (negative value).
 
    .PARAMETER WindY
    The amount of force to be applied pushing non-fixed nodes downwards (positive value) or upwards (negative value).
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [alias('DiagramOptionsPhysics')]
    [CmdletBinding(DefaultParameterSetName = 'BarnesHut')]
    param(
        [nullable[bool]] $Enabled,
        [validateSet('barnesHut', 'repulsion', 'hierarchicalRepulsion', 'forceAtlas2Based')][string] $Solver,

        [nullable[bool]] $StabilizationEnabled,
        [nullable[int]] $Stabilizationiterations,
        [nullable[int]] $StabilizationupdateInterval,
        [nullable[bool]] $StabilizationonlyDynamicEdges,
        [nullable[bool]] $Stabilizationfit,

        [nullable[int]] $MaxVelocity,
        [nullable[int]] $MinVelocity,
        [nullable[int]] $Timestep,
        [nullable[bool]] $AdaptiveTimestep,
        [Parameter(ParameterSetName = 'BarnesHut')][nullable[double]]$BarnesHutTheta,
        [Parameter(ParameterSetName = 'BarnesHut')][nullable[int]] $BarnesHutGravitationalConstant,
        [Parameter(ParameterSetName = 'BarnesHut')][nullable[double]] $BarnesHutCentralGravity,
        [Parameter(ParameterSetName = 'BarnesHut')][nullable[int]] $BarnesHutSpringLength,
        [Parameter(ParameterSetName = 'BarnesHut')][nullable[double]]$BarnesHutSpringConstant,
        [Parameter(ParameterSetName = 'BarnesHut')][nullable[double]] $BarnesHutDamping,
        [Parameter(ParameterSetName = 'BarnesHut')][nullable[int]] $BarnesHutAvoidOverlap,
        # Force2Atlas https://visjs.github.io/vis-network/docs/network/physics.html
        [Parameter(ParameterSetName = 'ForceAtlas2Based')][nullable[double]]$ForceAtlas2BasedTheta,
        [Parameter(ParameterSetName = 'ForceAtlas2Based')][nullable[int]] $ForceAtlas2BasedGravitationalConstant,
        [Parameter(ParameterSetName = 'ForceAtlas2Based')][nullable[double]] $ForceAtlas2BasedCentralGravity,
        [Parameter(ParameterSetName = 'ForceAtlas2Based')][nullable[int]] $ForceAtlas2BasedSpringLength,
        [Parameter(ParameterSetName = 'ForceAtlas2Based')][nullable[double]]$ForceAtlas2BasedSpringConstant,
        [Parameter(ParameterSetName = 'ForceAtlas2Based')][nullable[double]] $ForceAtlas2BasedDamping,
        [Parameter(ParameterSetName = 'ForceAtlas2Based')][nullable[int]] $ForceAtlas2BasedAvoidOverlap,
        # Repulsion https://visjs.github.io/vis-network/docs/network/physics.html
        [Parameter(ParameterSetName = 'Repulsion')][nullable[int]] $RepulsionNodeDistance,
        [Parameter(ParameterSetName = 'Repulsion')][nullable[double]]$RepulsionCentralGravity,
        [Parameter(ParameterSetName = 'Repulsion')][nullable[int]] $RepulsionSpringLength  ,
        [Parameter(ParameterSetName = 'Repulsion')][nullable[double]] $RepulsionSpringConstant,
        [Parameter(ParameterSetName = 'Repulsion')][nullable[double]] $RepulsionDamping,
        # Hierarchical Repulsion https://visjs.github.io/vis-network/docs/network/physics.html
        [Parameter(ParameterSetName = 'HierarchicalRepulsion')][nullable[int]] $HierarchicalRepulsionNodeDistance,
        [Parameter(ParameterSetName = 'HierarchicalRepulsion')][nullable[double]]$HierarchicalRepulsionCentralGravity,
        [Parameter(ParameterSetName = 'HierarchicalRepulsion')][nullable[int]] $HierarchicalRepulsionSpringLength  ,
        [Parameter(ParameterSetName = 'HierarchicalRepulsion')][nullable[double]] $HierarchicalRepulsionSpringConstant,
        [Parameter(ParameterSetName = 'HierarchicalRepulsion')][nullable[double]] $HierarchicalRepulsionDamping,
        [Parameter(ParameterSetName = 'HierarchicalRepulsion')][nullable[double]] $HierarchicalRepulsionAvoidOverlap,
        [nullable[int]] $WindX,
        [nullable[int]] $WindY
    )

    if (-not $Solver) {
        if ($PSCmdlet.ParameterSetName -eq 'Repulsion') {
            $Solver = 'repulsion'
        } elseif ($PSCmdlet.ParameterSetName -eq 'HierarchicalRepulsion') {
            $Solver = 'hierarchicalRepulsion'
        } elseif ($PSCmdlet.ParameterSetName -eq 'ForceAtlas2Based') {
            $Solver = 'forceAtlas2Based'
        } elseif ($PSCmdlet.ParameterSetName -eq 'BarnesHut') {
            $Solver = 'barnesHut'
        }
    }

    $Object = [PSCustomObject] @{
        Type     = 'DiagramOptionsPhysics'
        Settings = [ordered] @{
            physics = [ordered] @{
                enabled               = $Enabled
                solver                = $Solver
                barnesHut             = [ordered] @{
                    theta                 = $BarnesHutTheta
                    gravitationalConstant = $BarnesHutGravitationalConstant
                    centralGravity        = $BarnesHutCentralGravity
                    springLength          = $BarnesHutSpringLength
                    springConstant        = $BarnesHutSpringConstant
                    damping               = $BarnesHutDamping
                    avoidOverlap          = $BarnesHutAvoidOverlap
                }
                forceAtlas2Based      = [ordered] @{
                    theta                 = $ForceAtlas2BasedTheta                    
                    gravitationalConstant = $ForceAtlas2BasedGravitationalConstant    
                    centralGravity        = $ForceAtlas2BasedCentralGravity            
                    springLength          = $ForceAtlas2BasedSpringLength            
                    springConstant        = $ForceAtlas2BasedSpringConstant            
                    damping               = $ForceAtlas2BasedDamping                
                    avoidOverlap          = $ForceAtlas2BasedAvoidOverlap            
                }
                repulsion             = [ordered] @{
                    nodeDistance   = $RepulsionNodeDistance    
                    centralGravity = $RepulsionCentralGravity    
                    springLength   = $RepulsionSpringLength   
                    springConstant = $RepulsionSpringConstant    
                    damping        = $RepulsionDamping      
                }
                hierarchicalRepulsion = [ordered] @{
                    nodeDistance   = $HierarchicalRepulsionNodeDistance    
                    centralGravity = $HierarchicalRepulsionCentralGravity    
                    springLength   = $HierarchicalRepulsionSpringLength   
                    springConstant = $HierarchicalRepulsionSpringConstant    
                    damping        = $HierarchicalRepulsionDamping      
                    avoidOverlap   = $HierarchicalRepulsionAvoidOverlap  
                }
                stabilization         = [ordered] @{
                    enabled          = $StabilizationEnabled
                    iterations       = $Stabilizationiterations
                    updateInterval   = $StabilizationupdateInterval
                    onlyDynamicEdges = $StabilizationonlyDynamicEdges
                    fit              = $Stabilizationfit
                }
                maxVelocity           = $MaxVelocity
                minVelocity           = $MinVelocity
                timestep              = $Timestep
                adaptiveTimestep      = $AdaptiveTimestep
                wind                  = [ordered] @{
                    x = $WindX
                    y = $WindY
                }
            }
        }
    }
    Remove-EmptyValue -Hashtable $Object.Settings -Recursive -Rerun 2
    $Object
}

function New-GageSector {
    <#
    .SYNOPSIS
    Creates a new gauge sector with specified color, minimum value, and maximum value.
 
    .DESCRIPTION
    This function creates a new gauge sector with the provided color, minimum value, and maximum value. It is useful for visualizing data within a specific range.
 
    .PARAMETER Color
    Specifies the color of the gauge sector.
 
    .PARAMETER Min
    Specifies the minimum value of the gauge sector.
 
    .PARAMETER Max
    Specifies the maximum value of the gauge sector.
 
    .EXAMPLE
    $sector = New-GageSector -Color 'red' -Min 0 -Max 100
    Creates a new gauge sector with a red color, minimum value of 0, and maximum value of 100.
 
    .NOTES
    Additional notes
    #>

    [CmdletBinding()]
    param(
        [string] $Color,
        [int] $Min,
        [int] $Max
    )

    [ordered] @{
        color = ConvertFrom-Color -Color $Color
        lo    = $Min
        hi    = $Max
    }
}
Register-ArgumentCompleter -CommandName New-GageSection -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-HierarchicalTreeNode {
    <#
    .SYNOPSIS
    Creates a new hierarchical tree node object.
 
    .DESCRIPTION
    This function creates a new hierarchical tree node object with specified properties.
 
    .PARAMETER ID
    The unique identifier for the node.
 
    .PARAMETER Label
    The label or name of the node.
 
    .PARAMETER Type
    The type of the node. Default value is "Organism".
 
    .PARAMETER Description
    The description of the node.
 
    .PARAMETER To
    The parent node ID to which this node is connected.
 
    .EXAMPLE
    $node = New-HierarchicalTreeNode -ID "1" -Label "Node 1" -Description "This is node 1" -To "0"
    Creates a new hierarchical tree node with ID "1", label "Node 1", description "This is node 1", and connected to parent node with ID "0".
 
    .EXAMPLE
    $node = New-HierarchicalTreeNode -ID "2" -Label "Node 2" -Type "Animal" -Description "This is node 2" -To "1"
    Creates a new hierarchical tree node with ID "2", label "Node 2", type "Animal", description "This is node 2", and connected to parent node with ID "1".
    #>

    [alias('HierarchicalTreeNode')]
    [CmdletBinding()]
    param(
        [string] $ID,
        [alias('Name')][string] $Label,
        [string] $Type = "Organism",
        [string] $Description,
        [string] $To
    )

    if (-not $ID) {
        $ID = $Label
    }

    $Object = [PSCustomObject] @{
        Type     = 'TreeNode'
        Settings = [ordered] @{
            "id"          = $ID
            "parentId"    = $To
            "name"        = $Label

            "description" = $Description
        }
    }
    Remove-EmptyValue -Hashtable $Object.Settings -Recursive
    $Object
}

Function New-HTML {
    <#
    .SYNOPSIS
    Building block for creating HTML elements. A starting point for all other cmdlets in PSWriteHTML except Out-HtmlView.
 
    .DESCRIPTION
    Building block for creating HTML elements. A starting point for all other cmdlets in PSWriteHTML except Out-HtmlView.
 
    .PARAMETER HtmlData
    Provides ability to specify one or more elements within HTML. Using New-HTML without it, makes no larger sense.
 
    .PARAMETER Online
    Enables or Disables linking of CSS/JS. When Online is enabled that means that the CSS/JS files are loaded from the CDN. When Online is disabled that means that the CSS/JS files are all loaded from same HTML file (making it very large).
 
    .PARAMETER TitleText
    Title of the HTML page
 
    .PARAMETER Author
    Defines the author information in the HTML file (meta information). If not specified, the author information is skipped.
 
    .PARAMETER DateFormat
    Defines the date format in the HTML file (meta information). Default is 'yyyy-MM-dd HH:mm:ss'
 
    .PARAMETER AutoRefresh
    Defines if the page should be refreshed automatically every X seconds.
 
    .PARAMETER FilePath
    Provides the path to the file to be created.
 
    .PARAMETER ShowHTML
    Opens HTML in browser when generating of HTML is done. When no filepath is provided it uses temporary path instead. Good for testing.
 
    .PARAMETER Encoding
    Provides ability to choose encoding of the HTML file. Not really required to use, as UTF8 is the default. Options available: 'Unknown', 'String', 'Unicode', 'Byte', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'Ascii', 'Default', 'Oem', 'BigEndianUTF32'
 
    .PARAMETER FavIcon
    Provides ability to add a favicon to the HTML page. Can be a link or file path
 
    .PARAMETER Temporary
    Forces use of temporary file name. Doesn't work with -FilePath parameter.
 
    .PARAMETER AddComment
    Adds additional commands to the generated HTML file. This is useful for tracking or knowing what is what.
 
    .PARAMETER Format
    Formats HTML output (including CSS/JS). Requires PSParseHTML to be installed and available.
 
    .PARAMETER Minify
    Minifies HTML output (including CSS/JS). Requires PSParseHTML to be installed and available.
 
    .EXAMPLE
    New-HTML {
        New-HTMLSection -HeaderText 'Donut Charts - Defaults' -CanCollapse {
            New-HTMLPanel {
                New-HTMLChart {
                    New-ChartToolbar -Download
                    #New-ChartBarOptions -Gradient
                    New-ChartLegend -Name 'Time', 'Money', 'Taxes', 'test'
                    New-ChartBar -Name 'Test' -Value 1, 2, 3, 0
                    New-ChartBar -Name 'Test1' -Value 2, 5, 7, 0
                    New-ChartBar -Name 'Test2' -Value 3, 1, 50, 5
                }
            }
            New-HTMLPanel {
                New-HTMLChart {
                    New-ChartToolbar -Download
                    #New-ChartBarOptions -Gradient
                    New-ChartLegend -Name 'Time', 'Money', 'Taxes' -Color Red, Yellow, Green
                    New-ChartBar -Name 'Test' -Value 1, 2, 3
                    New-ChartBar -Name 'Test1' -Value 2, 5, 7
                    New-ChartBar -Name 'Test2' -Value 3, 1, 2
                }
            }
            New-HTMLPanel {
                New-HTMLChart {
                    New-ChartToolbar -Download
                    New-ChartBarOptions -Type barStacked
                    New-ChartLegend -Name 'Time', 'Money', 'Taxes' -Color Red, Yellow, Green
                    New-ChartBar -Name 'Test' -Value 1, 2, 3
                    New-ChartBar -Name 'Test1' -Value 2, 5, 7
                    New-ChartBar -Name 'Test2' -Value 3, 1, 2
                }
            }
        }
    } -ShowHTML -FilePath $PSScriptRoot\Example-ChartBarNext.html -Online -Format
 
    .EXAMPLE
    New-HTML -TitleText "Testing HideButtons" -Online -FilePath "$PSScriptRoot\Example7_06.html" {
        # Hide Buttons
        New-HTMLSection -HeaderText "Hide Buttons" -Content {
            New-HTMLTable -DataTable $Table -HideButtons
        }
        New-HTMLSection -Invisible {
            New-HTMLSection -HeaderText "Hide Buttons" -Content {
                New-HTMLTable -DataTable $Table -HideButtons -Transpose
            }
            New-HTMLSection -HeaderText 'Some chart' {
                New-HTMLChart {
                    New-ChartPie -Name 'Passed' -Value 5 -Color $ColorPassed
                    New-ChartPie -Name 'Failed' -Value 10 -Color $ColorFailed
                    New-ChartPie -Name 'Skipped' -Value 15 -Color $ColorSkipped
                }
            }
        }
        New-HTMLSection -HeaderText "Code used to generate above tables" -Content {
            New-HTMLCodeBlock {
                New-HTMLSection -HeaderText "Hide Buttons" -Content {
                    New-HTMLTable -DataTable $Table -HideButtons
                }
 
                New-HTMLSection -Invisible {
                    New-HTMLSection -HeaderText "Hide Buttons" -Content {
                        New-HTMLTable -DataTable $Table -HideButtons -Transpose
                    }
                    New-HTMLSection -HeaderText 'Some chart' {
                        New-HTMLChart {
                            New-ChartPie -Name 'Passed' -Value 5 -Color $ColorPassed
                            New-ChartPie -Name 'Failed' -Value 10 -Color $ColorFailed
                            New-ChartPie -Name 'Skipped' -Value 15 -Color $ColorSkipped
                        }
                    }
                }
            }
        }
    } -ShowHTML
 
    .NOTES
    General notes
    #>

    [alias('Dashboard')]
    [CmdletBinding()]
    param(
        [alias('Content')][Parameter(Position = 0)][ValidateNotNull()][ScriptBlock] $HtmlData = $(Throw "Have you put the open curly brace on the next line?"),
        [switch] $Online,
        [alias('Name', 'Title')][String] $TitleText,
        [string] $Author,
        [string] $DateFormat = 'yyyy-MM-dd HH:mm:ss',
        [int] $AutoRefresh,

        [Parameter(Mandatory = $false)][string]$FilePath,
        [alias('Show', 'Open')][Parameter(Mandatory = $false)][switch]$ShowHTML,
        [ValidateSet('Unknown', 'String', 'Unicode', 'Byte', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'Ascii', 'Default', 'Oem', 'BigEndianUTF32')] $Encoding = 'UTF8',
        [Uri] $FavIcon,

        [Parameter(DontShow)][switch] $UseCssLinks,
        [Parameter(DontShow)][switch] $UseJavaScriptLinks,
        [switch] $Temporary,
        [switch] $AddComment,
        [switch] $Format,
        [switch] $Minify,
        [string] $Language = 'en'
    )
    if ($UseCssLinks -or $UseJavaScriptLinks) {
        Write-Warning "New-HTML - UseCssLinks and UseJavaScriptLinks is depreciated. Use Online switch instead. Those switches will be removed in near future."
        $Online = $true
    }

    [string] $CurrentDate = (Get-Date).ToString($DateFormat)

    if (-not $FilePath -and ($Temporary -or $ShowHTML)) {

        $FilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).html")
    }
    if ($FilePath) {
        $DirectoryPath = [System.IO.Path]::GetDirectoryName($FilePath)

        $PagesPath = $DirectoryPath
        $FileName = [System.IO.Path]::GetFileNameWithoutExtension($FilePath)
        $HtmlExtension = [System.IO.Path]::GetExtension($FilePath)
    } else {

        $FileName = 'Temporary.html'
        $DirectoryPath = $null
        $PagesPath = $null
        $HtmlExtension = '.html'
    }

    $Script:CurrentConfiguration = Get-DefaultParameters
    $Script:GlobalSchema = @{
        Features           = [ordered] @{ } 
        StorageInformation = @{
            FileName  = $FileName
            Directory = $DirectoryPath
            PagesPath = $PagesPath
            Extension = $HtmlExtension
        }

        PagesCurrent       = $FileName
        Pages              = [ordered] @{

            $FileName = New-DefaultSettings
        }
    }

    $Script:HTMLSchema = $Script:GlobalSchema['Pages'][$FileName]

    [Array] $TempOutputHTML = Invoke-Command -ScriptBlock $HtmlData

    $Pages = [ordered]@{}

    $Pages[$FileName] = [ordered] @{
        Name     = $FileName
        Title    = $TitleText
        Main     = $null
        Primary  = $true
        Header   = $null
        Footer   = $null
        SavePath = $FilePath
        ShowHTML = $ShowHTML.IsPresent
    }
    $Pages[$FileName].Main = foreach ($ObjectTemp in $TempOutputHTML) {
        if ($ObjectTemp -is [PSCustomObject]) {
            if ($ObjectTemp.Type -eq 'Navigation') {
                $Navigation = foreach ($O in $ObjectTemp.Output) {

                    if ($O -isnot [System.Collections.IDictionary]) {
                        $O }
                }
            } elseif ($ObjectTemp.Type -eq 'Page') {
                foreach ($O in $ObjectTemp) {

                    if ($O.Output -isnot [System.Collections.IDictionary]) {
                        if ($O.FilePath) {
                            $SavePath = $O.FilePath
                        } else {
                            $Name = $O.Name.Replace(":", "_").Replace("/", "_").Replace("\", "_")
                            $SavePath = [io.path]::Combine($PagesPath, "$($FileName)_$($Name)$($HtmlExtension)")
                        }
                        $KeyName = $($O.Guid)
                        $Pages[$KeyName] = [ordered] @{
                            Name     = $KeyName
                            Title    = if ($O.Title) {
                                $O.Title } else {
                                $TitleText }
                            Main     = $null
                            Primary  = if ($Pages.Keys.Count -eq 0) {
                                $true } else {
                                $false }
                            Header   = $null
                            Footer   = $null
                            SavePath = $SavePath
                            ShowHTML = $false
                        }
                        $Pages[$KeyName].Main = foreach ($Object in $O.Output) {
                            if ($Object.Type -eq 'Footer') {
                                $Pages[$KeyName].Footer = foreach ($Sub in $Object.Output) {

                                    if ($Sub -isnot [System.Collections.IDictionary]) {
                                        $Sub }
                                }
                            } elseif ($Object.Type -eq 'Header') {
                                $Pages[$KeyName].Header = foreach ($Sub in $Object.Output) {

                                    if ($Sub -isnot [System.Collections.IDictionary]) {
                                        $Sub }
                                }
                            } else {
                                if ($Object -isnot [System.Collections.IDictionary]) {
                                    $Object }
                            }
                        }

                    }
                }
            } elseif ($ObjectTemp.Type -eq 'Footer') {
                $Pages[$FileName].Footer = foreach ($Sub in $ObjectTemp.Output) {

                    if ($Sub -isnot [System.Collections.IDictionary]) {
                        $Sub }
                }
            } elseif ($ObjectTemp.Type -eq 'Header') {
                $Pages[$FileName].Header = foreach ($Sub in $ObjectTemp.Output) {

                    if ($Sub -isnot [System.Collections.IDictionary]) {
                        $Sub }
                }
            } else {
                if ($ObjectTemp.Output) {

                    foreach ($SubObject in $ObjectTemp.Output) {
                        if ($SubObject -isnot [System.Collections.IDictionary]) {
                            $SubObject
                        }
                    }
                }
            }
        } else {

            if ($ObjectTemp -isnot [System.Collections.IDictionary]) {
                $ObjectTemp
            }
        }
    }
    foreach ($Page in $Pages.Keys) {

        $Script:HTMLSchema = $Script:GlobalSchema['Pages'][$Page]
        $Script:HTMLSchema.Features.Main = $true

        foreach ($_ in $Script:HTMLSchema.TabsHeadersNested) {
            $null = $Script:HTMLSchema.TabsHeaders.Remove($_)
        }

        $Features = Get-FeaturesInUse -PriorityFeatures 'Main', 'FontsAwesome', 'JQuery', 'Moment', 'DataTables', 'Tabs', 'Raphael' -Email:$Script:HTMLSchema['Email']

        [string] $HTML = @(

            '<!DOCTYPE html>' + [System.Environment]::NewLine
            New-HTMLTag -Tag 'html' -Attributes @{ lang = $Language } {
                if ($AddComment) {
                    '<!-- HEAD -->' }
                New-HTMLTag -Tag 'head' {

                    New-HTMLTag -Tag 'meta' -Attributes @{ charset = "utf-8" } -NoClosing

                    New-HTMLTag -Tag 'meta' -Attributes @{ name = 'viewport'; content = 'width=device-width, initial-scale=1' } -NoClosing
                    if ($Author) {
                        New-HTMLTag -Tag 'meta' -Attributes @{ name = 'author'; content = $Author } -NoClosing
                    }
                    New-HTMLTag -Tag 'meta' -Attributes @{ name = 'revised'; content = $CurrentDate } -NoClosing
                    New-HTMLTag -Tag 'title' { $Pages[$Page].Title }

                    if ($null -ne $FavIcon) {
                        $Extension = [System.IO.Path]::GetExtension($FavIcon)
                        if ($Extension -in @('.png', '.jpg', 'jpeg', '.svg', '.ico')) {
                            switch ($FavIcon.Scheme) {
                                "file" {
                                    if (Test-Path -Path $FavIcon.OriginalString) {
                                        $FavIcon = Get-Item -Path $FavIcon.OriginalString
                                        $FavIconImageBinary = Convert-ImageToBinary -ImageFile $FavIcon
                                        New-HTMLTag -Tag 'link' -Attributes @{rel = 'icon'; href = "$FavIconImageBinary"; type = 'image/x-icon' }
                                    } else {
                                        Write-Warning -Message "The path to the FavIcon image could not be resolved."
                                    }
                                }
                                "https" {
                                    $FavIcon = $FavIcon.OriginalString
                                    New-HTMLTag -Tag 'link' -Attributes @{rel = 'icon'; href = "$FavIcon"; type = 'image/x-icon' }
                                }
                                default {
                                    Write-Warning -Message "The path to the FavIcon image could not be resolved."
                                }
                            }
                        } else {
                            Write-Warning -Message "File extension `'$Extension`' is not supported as a FavIcon image.`nPlease use images with these extensions: '.png', '.jpg', 'jpeg', '.svg', '.ico'"
                        }
                    }

                    if ($Autorefresh -gt 0) {
                        New-HTMLTag -Tag 'meta' -Attributes @{ 'http-equiv' = 'refresh'; content = $Autorefresh } -SelfClosing
                    }

                    Get-Resources -Online:$Online.IsPresent -Location 'HeaderAlways' -Features Fonts 

                    if ($null -ne $Features) {

                        Get-Resources -Online:$Online.IsPresent -Location 'Header' -Features $Features -NoScript
                        Get-Resources -Online:$true -Location 'HeaderAlways' -Features $Features -NoScript
                        Get-Resources -Online:$false -Location 'HeaderAlways' -Features $Features -NoScript
                    }
                    if ($Script:HTMLSchema['Email'] -ne $true -and $Script:HTMLSchema.CustomHeaderJS) {
                        New-HTMLCustomJS -JS $Script:HTMLSchema.CustomHeaderJS
                    }
                    if ($Script:HTMLSchema.CustomHeaderCSS) {
                        New-HTMLCustomCSS -Css $Script:HTMLSchema.CustomHeaderCSS -AddComment:$AddComment
                    }
                }
                if ($AddComment) {
                    '<!-- END HEAD -->'
                    '<!-- BODY -->'
                }
                New-HTMLTag -Tag 'body' {
                    if ($null -ne $Features) {
                        Get-Resources -Online:$Online.IsPresent -Location 'Body' -Features $Features -NoScript
                        Get-Resources -Online:$true -Location 'BodyAlways' -Features $Features -NoScript
                        Get-Resources -Online:$false -Location 'BodyAlways' -Features $Features -NoScript
                    }
                    if ($Navigation) {
                        if ($AddComment) {
                            '<!-- NAVIGATION -->' }
                        $Navigation
                        if ($AddComment) {
                            '<!-- END NAVIGATION -->' }
                    }
                    New-HTMLTag -Tag 'div' -Attributes @{ class = 'main-section' } {
                        if ($Pages[$Page].Header) {
                            if ($AddComment) {
                                '<!-- HEADER -->' }
                            New-HTMLTag -Tag 'header' {
                                $Pages[$Page].Header
                            }
                            if ($AddComment) {
                                '<!-- END HEADER -->' }
                        }

                        if ($Script:HTMLSchema.Logos) {
                            $Script:HTMLSchema.Logos
                        }

                        if ($Script:HTMLSchema.TabsHeaders) {
                            New-HTMLTabHead -PageName $Page
                            New-HTMLTag -Tag 'div' -Attributes @{ 'data-panes' = 'true' } {

                                if ($Pages[$Page].Main) {
                                    $Pages[$Page].Main
                                }
                            }
                        } else {

                            if ($Pages[$Page].Main) {
                                $Pages[$Page].Main
                            }
                        }

                        foreach ($Chart in $Script:HTMLSchema.Charts) {
                            $Chart
                        }
                        foreach ($Diagram in $Script:HTMLSchema.Diagrams) {
                            $Diagram
                        }
                        foreach ($Carousel in $Script:HTMLSchema.Carousel) {
                            $Carousel
                        }
                    }
                    if ($AddComment) {
                        '<!-- FOOTER -->' }

                    [string] $Footer = @(
                        if ($Pages[$Page].Footer) {
                            $Pages[$Page].Footer
                        }
                        if ($null -ne $Features) {

                            Get-Resources -Online:$true -Location 'FooterAlways' -Features $Features -NoScript
                            Get-Resources -Online:$false -Location 'FooterAlways' -Features $Features -NoScript

                            Get-Resources -Online:$Online.IsPresent -Location 'Footer' -Features $Features -NoScript

                        }
                        if ($Script:HTMLSchema.CustomFooterCSS) {
                            New-HTMLCustomCSS -Css $Script:HTMLSchema.CustomFooterCSS -AddComment:$AddComment
                        }
                        if ($Script:HTMLSchema['Email'] -ne $true -and $Script:HTMLSchema.CustomFooterJS) {
                            New-HTMLCustomJS -JS $Script:HTMLSchema.CustomFooterJS
                        }
                    )
                    if ($Footer) {
                        New-HTMLTag -Tag 'footer' {
                            $Footer
                        }
                    }
                    if ($AddComment) {
                        '<!-- END FOOTER -->'
                        '<!-- END BODY -->'
                    }
                }
            }
        )
        if ($FilePath) {
            Save-HTML -HTML $HTML -FilePath $Pages[$Page].SavePath -Encoding $Encoding -Format:$Format -Minify:$Minify -ShowHTML:$Pages[$Page].ShowHTML
        } else {

            $HTML
        }
    }
}

function New-HTMLAccordion {
    <#
    .SYNOPSIS
    Creates a new HTML accordion element with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML accordion element with the specified accordion item content, duration of animation, and option to collapse on click.
 
    .PARAMETER AccordionItem
    Specifies the content of the accordion item as a script block.
 
    .PARAMETER Duration
    Specifies the duration of the accordion animation in milliseconds.
 
    .PARAMETER CollapseOnClick
    Indicates whether the accordion should collapse when clicked.
 
    .EXAMPLE
    New-HTMLAccordion -AccordionItem { "Accordion Content Here" } -Duration 500 -CollapseOnClick
    Creates a new HTML accordion with the specified content, animation duration of 500 milliseconds, and collapses on click.
 
    #>

    [cmdletBinding()]
    param(
        [scriptBlock] $AccordionItem,
        [int] $Duration,
        [switch] $CollapseOnClick
    )

    $Script:HTMLSchema.Features.AccordionFAQ = $true

    [string] $ID = "Accordion" + (Get-RandomStringName -Size 8)

    $Options = @{}
    if ($Duration) {
        $Options['duration'] = $Duration
    }
    if ($CollapseOnClick) {
        $Options['collapse'] = $true
    }
    $OptionsJSON = $Options | ConvertTo-Json

    if ($AccordionItem) {
        New-HTMLTag -Tag 'div' -Attributes @{ id = $Id ; class = "accordion-container flexElement" } {
            & $AccordionItem
        }
        New-HTMLTag -Tag 'script' {
            "new Accordion('#$Id', $OptionsJSON);"
        }
    }
}
function New-HTMLAnchor {
    <#
    .SYNOPSIS
    Creates a new HTML anchor element.
 
    .DESCRIPTION
    This function creates a new HTML anchor element (<a>) with specified attributes.
 
    .PARAMETER Name
    The name of the anchor element.
 
    .PARAMETER Id
    The ID of the anchor element.
 
    .PARAMETER Target
    Specifies where to open the linked document.
 
    .PARAMETER Class
    The CSS class of the anchor element.
 
    .PARAMETER HrefLink
    The URL of the linked document.
 
    .PARAMETER OnClick
    JavaScript code to execute when the anchor element is clicked.
 
    .PARAMETER Style
    The CSS styles to apply to the anchor element.
 
    .PARAMETER Text
    The text to display within the anchor element.
 
    .EXAMPLE
    New-HTMLAnchor -Target _parent
    Creates an anchor element with the target attribute set to "_parent".
 
    New-HTMLAnchor -Id "show_$RandomNumber" -Href '#' -OnClick "show('$RandomNumber');" -Style "color: #ffffff; display:none;" -Text 'Show'
    Creates an anchor element with specified attributes and text.
 
    Output:
    <a id="show_$RandomNumber" class="" target="_parent" href="#" onclick="show('$RandomNumber');" style="color: #ffffff; display:none;">Show</a>
 
    .NOTES
    This function is part of a set of functions for generating HTML elements.
    #>

    [alias('New-HTMLLink')]
    [cmdletBinding()]
    param(
        [alias('AnchorName')][string] $Name,
        [string] $Id,
        [string] $Target, # "_blank|_self|_parent|_top|framename"
        [string] $Class,
        [alias('Url', 'Link', 'UrlLink', 'Href')][string] $HrefLink,
        [string] $OnClick,
        [System.Collections.IDictionary] $Style,
        [alias('AnchorText', 'Value')][string] $Text
    )
    $Attributes = [ordered]@{
        'id'      = $Id
        'name'    = $Name
        'class'   = $Class
        'target'  = $Target
        'href'    = $HrefLink
        'onclick' = $OnClick
        'style'   = $Style
    }
    New-HTMLTag -Tag 'a' -Attributes $Attributes {
        $Text
    }
}
function New-HTMLCalendar {
    <#
    .SYNOPSIS
    Creates a new HTML calendar using FullCalendar library.
 
    .DESCRIPTION
    This function creates a new HTML calendar using the FullCalendar library. It allows customization of various calendar settings such as header elements, initial view, default date, event handling, and more.
 
    .PARAMETER CalendarSettings
    Specifies the script block containing settings for the calendar.
 
    .PARAMETER HeaderLeft
    Specifies the elements to display on the left side of the calendar header.
 
    .PARAMETER HeaderCenter
    Specifies the element to display in the center of the calendar header.
 
    .PARAMETER HeaderRight
    Specifies the elements to display on the right side of the calendar header.
 
    .PARAMETER DefaultDate
    Specifies the default date to display on the calendar.
 
    .PARAMETER NavigationLinks
    Indicates whether navigation links are enabled on the calendar.
 
    .PARAMETER NowIndicator
    Indicates whether the current time indicator is displayed on the calendar.
 
    .PARAMETER EventLimit
    Indicates whether to limit the number of events displayed on a day.
 
    .PARAMETER WeekNumbers
    Indicates whether week numbers are displayed on the calendar.
 
    .PARAMETER Selectable
    Indicates whether events can be selected on the calendar.
 
    .PARAMETER SelectMirror
    Indicates whether a mirror effect is applied when selecting events.
 
    .PARAMETER BusinessHours
    Indicates whether business hours are displayed on the calendar.
 
    .PARAMETER Editable
    Indicates whether events are editable on the calendar.
 
    .PARAMETER InitialView
    Specifies the initial view of the calendar.
 
    .PARAMETER UrlTarget
    Specifies the target URL for calendar events.
 
    .PARAMETER EventTimeFormat
    Specifies the format for displaying event times.
 
    .PARAMETER SlotLabelFormat
    Specifies the format for displaying slot labels.
 
    .EXAMPLE
    New-HTMLCalendar -CalendarSettings {
        # Define calendar settings here
    }
 
    Creates a new HTML calendar with default settings.
 
    .EXAMPLE
    New-HTMLCalendar -CalendarSettings {
        # Define calendar settings here
    } -HeaderLeft 'prev', 'next' -HeaderCenter 'title' -HeaderRight 'dayGridMonth', 'timeGridWeek' -DefaultDate (Get-Date) -NavigationLinks $true -NowIndicator $true -EventLimit $true -WeekNumbers $true -Selectable $true -SelectMirror $true -BusinessHours -Editable -InitialView 'dayGridMonth' -UrlTarget 'https://example.com' -EventTimeFormat @{
        hour = '2-digit'
        minute = '2-digit'
        omitZeroMinute = $false
        meridiem = $false
        hour12 = $false
    } -SlotLabelFormat @{
        hour = '2-digit'
        minute = '2-digit'
        omitZeroMinute = $false
        meridiem = $false
        hour12 = $false
    }
 
    Creates a new HTML calendar with custom settings.
 
    #>

    [alias('Calendar')]
    [CmdletBinding()]
    param(
        [ScriptBlock] $CalendarSettings,
        [ValidateSet(
            'prev', 'next', 'today', 'prevYear', 'nextYear', 'dayGridDay', 'dayGridWeek', 'dayGridMonth',
            'timeGridWeek', 'timeGridDay', 'listDay', 'listWeek', 'listMonth', 'title', 'listYear'
        )][string[]] $HeaderLeft = @('prev', 'next', 'today'),
        [ValidateSet(
            'prev', 'next', 'today', 'prevYear', 'nextYear', 'dayGridDay', 'dayGridWeek', 'dayGridMonth',
            'timeGridWeek', 'timeGridDay', 'listDay', 'listWeek', 'listMonth', 'title', 'listYear'
        )][string[]]$HeaderCenter = 'title',
        [ValidateSet(
            'prev', 'next', 'today', 'prevYear', 'nextYear', 'dayGridDay', 'dayGridWeek', 'dayGridMonth',
            'timeGridWeek', 'timeGridDay', 'listDay', 'listWeek', 'listMonth', 'title', 'listYear'
        )][string[]] $HeaderRight = @('dayGridMonth', 'timeGridWeek', 'timeGridDay', 'listMonth'),
        [DateTime] $DefaultDate = (Get-Date),
        [bool] $NavigationLinks = $true,
        [bool] $NowIndicator = $true,
        [bool] $EventLimit = $true,
        [bool] $WeekNumbers = $true,
        #[bool] $WeekNumbersWithinDays = $true,
        [bool] $Selectable = $true,
        [bool] $SelectMirror = $true,
        [switch] $BusinessHours,
        [switch] $Editable,
        [ValidateSet(
            'dayGridDay', 'dayGridWeek', 'dayGridMonth', 'timeGridDay', 'timeGridWeek', 'listDay', 'listWeek', 'listMonth', 'listYear'
        )][string] $InitialView,
        [string] $UrlTarget,
        [System.Collections.IDictionary] $EventTimeFormat = [ordered] @{
            hour           = '2-digit'
            minute         = '2-digit'
            #second = '2-digit'
            omitZeroMinute = $false
            meridiem       = $false
            hour12         = $false
        },
        [System.Collections.IDictionary] $SlotLabelFormat = [ordered] @{
            hour           = '2-digit'
            minute         = '2-digit'
            #second = '2-digit'
            omitZeroMinute = $false
            meridiem       = $false
            hour12         = $false
        }
    )
    if (-not $Script:HTMLSchema.Features) {
        Write-Warning 'New-HTMLCalendar - Creation of HTML aborted. Most likely New-HTML is missing.'
        Exit
    }
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.FullCalendar = $true
    $Script:HTMLSchema.Features.Popper = $true

    $CalendarEvents = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()

    [Array] $Settings = & $CalendarSettings
    foreach ($Object in $Settings) {
        if ($Object.Type -eq 'CalendarEvent') {
            $CalendarEvents.Add($Object.Settings)
        }
    }

    [string] $ID = "Calendar-" + (Get-RandomStringName -Size 8)

    $Calendar = [ordered] @{
        headerToolbar         = @{
            left   = $HeaderLeft -join ','
            center = $HeaderCenter -join ','
            right  = $HeaderRight -join ','
        }

        initialView           = $InitialView
        initialDate           = '{0:yyyy-MM-dd}' -f ($DefaultDate)
        eventDidMount         = 'eventDidMountReplaceMe'
        nowIndicator          = $NowIndicator

        navLinks              = $NavigationLinks 
        businessHours         = $BusinessHours.IsPresent 
        editable              = $Editable.IsPresent
        events                = $CalendarEvents
        dayMaxEventRows       = $EventLimit
        weekNumbers           = $WeekNumbers
        weekNumberCalculation = 'ISO'
        selectable            = $Selectable
        selectMirror          = $SelectMirror
        buttonIcons           = $false 

        eventTimeFormat       = $EventTimeFormat
        slotLabelFormat       = $SlotLabelFormat

        views                 = @{
            listDay   = @{ buttonText = 'list day' }
            listWeek  = @{ buttonText = 'list week' }
            listMonth = @{ buttonText = 'list month' }
            listYear  = @{ buttonText = 'list year' }
        }
    }
    $Calendar['eventClick'] = 'eventClickReplaceMe'

    Remove-EmptyValue -Hashtable $Calendar -Recursive
    $CalendarJSON = $Calendar | ConvertTo-Json -Depth 7

    $eventDidMount = @"
    eventDidMount: function(info) {
        var tooltip = new Tooltip(info.el, {
            title: info.event.extendedProps.description,
            placement: 'top',
            trigger: 'hover',
            container: 'body'
        });
    }
"@


    if ($UrlTarget) {

        $eventClick = @"
    eventClick: function (info) {
        var eventObj = info.event;
        if (eventObj.url) {
            window.open(eventObj.url, '$UrlTarget');
            info.jsEvent.preventDefault(); // prevents browser from following link in current tab.
        }
    }
"@

    } else {

        $eventClick = @"
    eventClick: function (info) {
        var eventObj = info.event;
        if (eventObj.extendedProps.targetName) {
            window.open(eventObj.url, eventObj.extendedProps.targetName);
            info.jsEvent.preventDefault(); // prevents browser from following link in current tab.
        }
    }
"@

    }

    if ($PSEdition -eq 'Desktop') {
        $TextToFind = '"eventDidMount": "eventDidMountReplaceMe"'
    } else {
        $TextToFind = '"eventDidMount": "eventDidMountReplaceMe"'
    }
    $CalendarJSON = $CalendarJSON.Replace($TextToFind, $eventDidMount)

    if ($PSEdition -eq 'Desktop') {
        $TextToFind = '"eventClick": "eventClickReplaceMe"'
    } else {
        $TextToFind = '"eventClick": "eventClickReplaceMe"'
    }
    $CalendarJSON = $CalendarJSON.Replace($TextToFind, $eventClick)

    $Div = New-HTMLTag -Tag 'div' -Attributes @{ id = $ID; class = 'calendarFullCalendar'; style = $Style }
    $Script = New-HTMLTag -Tag 'script' -Value {
        "document.addEventListener('DOMContentLoaded', function () {"
        "var calendarEl = document.getElementById('$ID');"
        'var calendar = new FullCalendar.Calendar(calendarEl,'
        $CalendarJSON
        ');'
        'calendar.render();'
        "calendarTracker['$ID'] = calendar;"
        '}); '
    } -NewLine

    $Div
    $Script
}
function New-HTMLCarousel {
    <#
    .SYNOPSIS
    Creates a new HTML carousel element with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML carousel element with the specified options such as slide content, mode (Horizontal/Vertical), alignment, number of slides per view, height, margin, looping, pagination, speed, starting slide, autoplay settings, arrow visibility, and more.
 
    .PARAMETER Slide
    Specifies the content of the carousel slide as a script block.
 
    .PARAMETER Mode
    Specifies the mode of the carousel (Horizontal/Vertical). Default is Horizontal.
 
    .PARAMETER Align
    Specifies the alignment of the carousel slides (center/start/end/justify).
 
    .PARAMETER PerView
    Specifies the number of slides per view.
 
    .PARAMETER Height
    Specifies the height of the carousel (adaptive/equal/auto).
 
    .PARAMETER Margin
    Specifies the margin between slides.
 
    .PARAMETER Loop
    Indicates whether the carousel should loop.
 
    .PARAMETER Pagination
    Indicates whether pagination should be enabled.
 
    .PARAMETER Speed
    Specifies the speed of the carousel animation.
 
    .PARAMETER MoveBy
    Specifies the number of slides to move at a time.
 
    .PARAMETER StartAt
    Specifies the index of the slide to start at.
 
    .PARAMETER MoveOnClick
    Indicates whether the carousel should move to the next slide on click.
 
    .PARAMETER AutoPlay
    Indicates whether autoplay is enabled.
 
    .PARAMETER AutoPlayEvery
    Specifies the interval for autoplay.
 
    .PARAMETER AutoPlayRewind
    Indicates whether autoplay should rewind.
 
    .PARAMETER AutoPlayPauseOnFocus
    Indicates whether autoplay should pause on focus.
 
    .PARAMETER AutoPlayPauseOnHover
    Indicates whether autoplay should pause on hover.
 
    .PARAMETER AutoPlaySyncID
    Specifies the ID to sync autoplay with.
 
    .PARAMETER DisableArrows
    Indicates whether the arrow navigation should be disabled.
 
    .PARAMETER ArrowTemplate
    Specifies custom arrow templates.
 
    .PARAMETER PaginationTemplate
    Specifies the pagination template ('default'/'index'/'custom').
 
    .PARAMETER Count
    Indicates whether slide count should be displayed.
 
    .PARAMETER CountTemplate
    Specifies the template for displaying slide count.
 
    .EXAMPLE
    New-HTMLCarousel -Slide { "Slide Content Here" } -Mode Horizontal -Align center -PerView 3 -Height adaptive -Margin '10px' -Loop -Pagination -Speed 500 -MoveBy 1 -StartAt 0 -MoveOnClick -AutoPlay -AutoPlayEvery 3000 -AutoPlayRewind -AutoPlayPauseOnFocus -AutoPlayPauseOnHover -AutoPlaySyncID 'carousel1' -DisableArrows -ArrowTemplate @('left-arrow', 'right-arrow') -PaginationTemplate 'index' -Count -CountTemplate 'Slide {index} of {total}'
 
    Creates a new HTML carousel with the specified options.
 
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $Slide,
        [validateSet('Horizontal', 'Vertical')][string] $Mode = 'Horizontal',
        [validateSet('center', 'start', 'end', 'justify')][string] $Align,
        [object] $PerView,
        [validateSet('adaptive', 'equal', 'auto')][string] $Height,
        [string] $Margin,
        [switch] $Loop,
        [switch] $Pagination,
        [nullable[int]] $Speed,
        [nullable[int]] $MoveBy,
        [nullable[int]] $StartAt,
        [switch] $MoveOnClick,
        [alias('Stream')][switch] $AutoPlay,
        [alias('StreamEvery')][nullable[int]] $AutoPlayEvery,
        [alias('StreamRewind')][switch] $AutoPlayRewind,
        [alias('StreamPauseOnFocus')][switch] $AutoPlayPauseOnFocus,
        [alias('StreamPauseOnHover')][switch] $AutoPlayPauseOnHover,
        [alias('StreamSyncID')][string] $AutoPlaySyncID,
        [switch] $DisableArrows,
        [string[]] $ArrowTemplate,
        [string] $PaginationTemplate,
        [switch] $Count,
        [string] $CountTemplate
    )
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.CarouselKineto = $true
    $Script:HTMLSchema.Features.jquery = $true

    if (-not $CarouselID) {
        $CarouselID = "Carousel-$(Get-RandomStringName -Size 8 -LettersOnly)"
    }

    $Options = [ordered] @{

        mode    = $Mode.ToLower()
        align   = $Align
        perView = $perView
        height  = $Height
        margin  = $Margin
        startAt = $StartAt
        moveBy  = $moveBy
        speed   = $Speed
    }
    if ($Pagination) {
        $Options['pagination'] = $true
    }
    if ($PaginationTemplate) {
        if ($PaginationTemplate -eq 'default') {
            $Options['paginationTemplate'] = $null
        } elseif ($PaginationTemplate -eq 'index') {
            $Options['paginationTemplate'] = '{{index}}'
        } else {
            $Options['paginationTemplate'] = $PaginationTemplate
        }
    }
    if ($Loop) {
        $Options['loop'] = $true
    }
    if ($MoveOnClick) {
        $Options['moveOnClick'] = $true
    }
    if ($AutoPlay) {
        $Options['stream'] = $true
        if ($AutoPlayEvery) {
            $Options['streamEvery'] = $AutoPlayEvery
        }
        if ($AutoPlayRewind) {
            $Options['streamRewind'] = $true
        }
        if ($AutoPlayPauseOnFocus) {
            $Options['pauseOnFocus'] = $true
        }
        if ($AutoPlayPauseOnHover) {
            $Options['pauseOnHover'] = $true
        }
        if ($AutoPlaySyncID) {
            $Options['syncId'] = $AutoPlaySyncID
        }
    }
    if ($DisableArrows) {
        $Options['arrows'] = $false
    }

    if ($ArrowTemplate) {
        $Options['arrows'] = $true
        if ($ArrowTemplate.Count -eq 1) {
            if ($ArrowTemplate -eq 'default') {
                $ArrowTemplate = $null
            } elseif ($ArrowTemplate -eq 'arrows') {
                $Options['arrowTemplate'] = @('←', '→')
            } else {
                $Options['arrowTemplate'] = $ArrowTemplate
            }
        } else {
            $Options['arrowTemplate'] = $ArrowTemplate
        }
    }
    if ($Count) {
        $Options['count'] = $true

        if ($CountTemplate) {
            if ($CountTemplate -eq 'default') {
                $CountTemplate = $null
            } elseif ($CountTemplate -eq 'count') {
                $CountTemplate = '{current}/{total}'
            } elseif ($CountTemplate -eq 'countBold') {
                $CountTemplate = '<em class="current">{{current}}</em> / {{total}}'
            } else {
                $CountTemplate = $CountTemplate
            }
            $Options['countTemplate'] = $CountTemplate
        }
    }

    Remove-EmptyValue -Hashtable $Options
    $Carousel = $Options | ConvertTo-JsonLiteral -Depth 1 -AdvancedReplace @{ '.' = '\.'; '$' = '\$' }

    New-HTMLTag -Tag 'div' -Attributes @{ ID = $CarouselID; class = "carousel" } {
        if ($Slide) {
            & $Slide
        }
    }
    $ScriptOutput = New-HTMLTag -Tag 'script' {
        "Kineto.create('#$CarouselID', $Carousel);"
    }

    $Script:HTMLSchema.Carousel.Add($ScriptOutput)
}

[scriptblock] $AutoCompleterCountTemplate = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    'default', 'count', 'countBold' | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
}
[scriptblock] $AutoCompleterArrowTemplate = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    'default', 'arrows' | Sort-Object | Where-Object { $_ -like "*$wordToComplete*" }
}

Register-ArgumentCompleter -CommandName New-HTMLCarousel -ParameterName CountTemplate -ScriptBlock $AutoCompleterCountTemplate
Register-ArgumentCompleter -CommandName New-HTMLCarousel -ParameterName ArrowTemplate -ScriptBlock $AutoCompleterArrowTemplate

function New-HTMLCarouselStyle {
    [cmdletBinding()]
    param(

    )
}
function New-HTMLChart {
    <#
    .SYNOPSIS
    Creates a new HTML chart with customizable settings.
 
    .DESCRIPTION
    This function creates a new HTML chart with customizable settings such as title, subtitle, dimensions, colors, and more.
 
    .PARAMETER ChartSettings
    Specifies the settings for the chart. This should be a ScriptBlock containing the chart configuration.
 
    .PARAMETER Title
    Specifies the title of the chart.
 
    .PARAMETER TitleAlignment
    Specifies the alignment of the chart title. Valid values are 'center', 'left', or 'right'.
 
    .PARAMETER TitleMargin
    Specifies the margin of the chart title.
 
    .PARAMETER TitleOffsetX
    Specifies the horizontal offset of the chart title.
 
    .PARAMETER TitleOffsetY
    Specifies the vertical offset of the chart title.
 
    .PARAMETER TitleFloating
    Specifies whether the chart title should float.
 
    .PARAMETER TitleFontSize
    Specifies the font size of the chart title.
 
    .PARAMETER TitleFontWeight
    Specifies the font weight of the chart title. Valid values are 'normal', 'bold', 'bolder', 'lighter', or numeric values from '100' to '900'.
 
    .PARAMETER TitleFontFamily
    Specifies the font family of the chart title.
 
    .PARAMETER TitleColor
    Specifies the color of the chart title.
 
    .PARAMETER SubTitle
    Specifies the subtitle of the chart.
 
    .PARAMETER SubTitleAlignment
    Specifies the alignment of the chart subtitle. Valid values are 'center', 'left', or 'right'.
 
    .PARAMETER SubTitleMargin
    Specifies the margin of the chart subtitle.
 
    .PARAMETER SubTitleOffsetX
    Specifies the horizontal offset of the chart subtitle.
 
    .PARAMETER SubTitleOffsetY
    Specifies the vertical offset of the chart subtitle.
 
    .PARAMETER SubTitleFloating
    Specifies whether the chart subtitle should float.
 
    .PARAMETER SubTitleFontSize
    Specifies the font size of the chart subtitle.
 
    .PARAMETER SubTitleFontWeight
    Specifies the font weight of the chart subtitle. Valid values are 'normal', 'bold', 'bolder', 'lighter', or numeric values from '100' to '900'.
 
    .PARAMETER SubTitleFontFamily
    Specifies the font family of the chart subtitle.
 
    .PARAMETER SubTitleColor
    Specifies the color of the chart subtitle.
 
    .PARAMETER Height
    Specifies the height of the chart. Default is 350.
 
    .PARAMETER Width
    Specifies the width of the chart.
 
    .PARAMETER Gradient
    Indicates whether to use gradient colors in the chart.
 
    .PARAMETER Patterned
    Indicates whether to use patterned colors in the chart.
 
    .PARAMETER Id
    Specifies the ID of the chart. If not provided, a random ID will be generated.
 
    .PARAMETER Group
    Specifies the group to which the chart belongs.
 
    .EXAMPLE
    New-HTMLChart -Title "Sales Data" -ChartSettings {
        // Chart configuration settings here
    }
 
    Creates a new HTML chart with the title "Sales Data" and custom chart settings.
 
    .EXAMPLE
    New-HTMLChart -Title "Sales Data" -SubTitle "Quarterly Sales" -Height 400 -Width 600 -Gradient -Id "SalesChart"
 
    Creates a new HTML chart with the title "Sales Data", subtitle "Quarterly Sales", height of 400, width of 600, gradient colors, and specified ID.
 
    #>

    [alias('Chart')]
    [CmdletBinding()]
    param(
        [ScriptBlock] $ChartSettings,
        [string] $Title,
        [ValidateSet('center', 'left', 'right')][string] $TitleAlignment,
        [nullable[int]] $TitleMargin,
        [nullable[int]] $TitleOffsetX,
        [nullable[int]] $TitleOffsetY,
        [nullable[int]] $TitleFloating,
        [object] $TitleFontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $TitleFontWeight,
        [string] $TitleFontFamily,
        [string] $TitleColor,

        [string] $SubTitle,
        [ValidateSet('center', 'left', 'right')][string] $SubTitleAlignment,
        [nullable[int]] $SubTitleMargin,
        [nullable[int]] $SubTitleOffsetX,
        [nullable[int]] $SubTitleOffsetY,
        [nullable[int]] $SubTitleFloating,
        [object] $SubTitleFontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $SubTitleFontWeight,
        [string] $SubTitleFontFamily,
        [string] $SubTitleColor,

        [Object] $Height = 350,
        [Object] $Width,
        [alias('GradientColors')][switch] $Gradient,
        [alias('PatternedColors')][switch] $Patterned,
        [string] $Id,
        [string] $Group
    )
    $Theme = $null
    $Events = $null
    $Script:HTMLSchema.Features.MainFlex = $true

    if (-not $Id) {
        $Id = "ChartID-" + (Get-RandomStringName -Size 8)
    }

    $Chart = [ordered] @{
        id     = $Id
        group  = $Group
        height = ConvertFrom-Size -Size $Height
        width  = ConvertFrom-Size -Size $Width
    }

    $TitleBlock = @{
        text     = $Title
        align    = $TitleAlignment
        margin   = $TitleMargin
        offsetX  = $TitleOffsetX
        offsetY  = $TitleOffsetY
        floating = $TitleFloating
        style    = @{
            fontSize   = ConvertFrom-Size -Size $TitleFontSize
            fontWeight = $TitleFontWeight
            fontFamily = $TitleFontFamily
            color      = ConvertFrom-Color -Color $TitleColor
        }
    }
    Remove-EmptyValue -Hashtable $TitleBlock -Recursive -Rerun 2

    $SubTitleBlock = @{
        text     = $SubTitle
        align    = $SubTitleAlignment
        margin   = $SubTitleMargin
        offsetX  = $SubTitleOffsetX
        offsetY  = $SubTitleOffsetY
        floating = $SubTitleFloating
        style    = @{
            fontSize   = ConvertFrom-Size -Size $SubTitleFontSize
            fontWeight = $SubTitleFontWeight
            fontFamily = $SubTitleFontFamily
            color      = ConvertFrom-Color -Color $SubTitleColor
        }
    }
    Remove-EmptyValue -Hashtable $SubTitleBlock -Recursive -Rerun 2

    $DataLabel = [ordered] @{
        enabled = $true
    }
    $Markers = [ordered] @{}

    $DataSet = [System.Collections.Generic.List[object]]::new()
    $DataName = [System.Collections.Generic.List[object]]::new()

    $DataSeries = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
    $LineStroke = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
    $DataSetChartTimeLine = [System.Collections.Generic.List[PSCustomObject]]::new()

    $Colors = [System.Collections.Generic.List[string]]::new()

    $ChartAxisY = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()

    [bool] $BarHorizontal = $true
    [bool] $BarDataLabelsEnabled = $true
    [int] $BarDataLabelsOffsetX = -6
    [string] $BarDataLabelsFontSize = '12px'
    [bool] $BarDistributed = $false

    [string] $Type = ''

    [Array] $Settings = & $ChartSettings
    foreach ($Setting in $Settings) {
        if ($Setting.ObjectType -eq 'Bar') {

            if (-not $Type) {

                $Type = $Setting.ObjectType
            }
            $DataSet.Add($Setting.Value)
            $DataName.Add($Setting.Name)
            $DataSeries.Add($Setting.series)

            if ($Setting.Color) {
                $Setting.Color | ForEach-Object {
                    $Colors.Add($_)
                }
            }

        } elseif ($Setting.ObjectType -eq 'Pie' -or $Setting.ObjectType -eq 'Donut') {

            $Type = $Setting.ObjectType
            $DataSet.Add($Setting.Value)
            $DataName.Add($Setting.Name)

            if ($Setting.Color) {
                $Setting.Color | ForEach-Object {
                    $Colors.Add($_)
                }
            }
        } elseif ($Setting.ObjectType -eq 'Spark') {

            $Type = $Setting.ObjectType
            $DataSet.Add(
                [ordered] @{
                    Name   = $Setting.Name
                    Values = $Setting.Value
                }
            )
            if ($Setting.Color) {
                $Setting.Color | ForEach-Object {
                    $Colors.Add($_)
                }
            }
        } elseif ($Setting.ObjectType -eq 'Radial') {
            $Type = $Setting.ObjectType
            $DataSet.Add($Setting.Value)
            $DataName.Add($Setting.Name)

            if ($Setting.Color) {
                $Setting.Color | ForEach-Object {
                    $Colors.Add($_)
                }
            }
        } elseif ($Setting.ObjectType -eq 'Legend') {

            $DataLegend = $Setting.Names
            if ($null -ne $Setting.Color) {
                $Setting.Color | ForEach-Object {
                    $Colors.Add($_)
                }
            }
            $Legend = $Setting.Legend
        } elseif ($Setting.ObjectType -eq 'BarOptions') {

            $Type = $Setting.Type
            $BarHorizontal = $Setting.Horizontal
            $BarDataLabelsEnabled = $Setting.DataLabelsEnabled
            $BarDataLabelsOffsetX = $Setting.DataLabelsOffsetX
            $BarDataLabelsFontSize = $Setting.DataLabelsFontSize
            $BarDataLabelsColor = $Setting.DataLabelsColor
            $BarDistributed = $Setting.Distributed

            if ($null -ne $Setting.PatternedColors) {
                $Patterned = $Setting.PatternedColors
            }
            if ($null -ne $Setting.GradientColors) {
                $Gradient = $Setting.GradientColors
            }
        } elseif ($Setting.ObjectType -eq 'Toolbar') {

            $Toolbar = $Setting.Toolbar
        } elseif ($Setting.ObjectType -eq 'Theme') {

            $Theme = $Setting.Theme
        } elseif ($Setting.ObjectType -eq 'Marker') {

            $Markers = $Setting.markers
        } elseif ($Setting.ObjectType -eq 'Line') {

            $Type = $Setting.ObjectType

            if ($Setting.series) {
                $DataSeries.Add($Setting.series)
            }
            if ($Setting.stroke.count -gt 0) {
                $LineStroke.Add($setting.stroke)
            }
            if ($Setting.Color) {
                $Colors.Add($Setting.Color)
            }
        } elseif ($Setting.ObjectType -eq 'ChartAxisX') {
            $ChartAxisX = $Setting.ChartAxisX
        } elseif ($Setting.ObjectType -eq 'ChartGrid') {
            $GridOptions = $Setting.Grid
        } elseif ($Setting.ObjectType -eq 'ChartAxisY') {
            $ChartAxisY.Add($Setting.ChartAxisY)
        } elseif ($Setting.ObjectType -eq 'TimeLine') {
            $Type = 'rangeBar'
            $DataSetChartTimeLine.Add($Setting.TimeLine)
        } elseif ($Setting.ObjectType -eq 'ChartToolTip') {
            $ChartToolTip = $Setting.ChartToolTip
        } elseif ($Setting.ObjectType -eq 'DataLabel') {
            $DataLabel = $Setting.DataLabel
        } elseif ($Setting.ObjectType -eq 'ChartEvents') {
            $Events = $Setting.Event
        } elseif ($Setting.ObjectType -eq 'RadialOptions') {
            $PlotOptions = $Setting.plotOptions
        } elseif ($Setting.ObjectType -eq 'Fill') {
            $Design = $Setting.Design
        }
    }

    if ($Type -in @('bar', 'barStacked', 'barStacked100Percent')) {

        $HashTable = [ordered] @{ }
        $ArrayCount = $DataSet[0].Count
        if ($ArrayCount -eq 1) {
            $HashTable.1 = $DataSet
        } else {
            for ($i = 0; $i -lt $ArrayCount; $i++) {
                $HashTable.$i = [System.Collections.Generic.List[object]]::new()
            }
            foreach ($Value in $DataSet) {
                for ($h = 0; $h -lt $Value.Count; $h++) {
                    $HashTable[$h].Add($Value[$h])
                }
            }
        }

        $SplatChart = [ordered] @{
            Data               = $($HashTable.Values)
            DataNames          = $DataName
            Colors             = $Colors
            DataLegend         = $DataLegend

            Type               = $Type
            Horizontal         = $BarHorizontal
            DataLabelsEnabled  = $BarDataLabelsEnabled
            DataLabelsOffsetX  = $BarDataLabelsOffsetX
            DataLabelsFontSize = $BarDataLabelsFontSize
            Distributed        = $BarDistributed
            DataLabelsColor    = $BarDataLabelsColor
            ChartAxisX         = $ChartAxisX
            ChartAxisY         = $ChartAxisY

            Legend             = $Legend
            Chart              = $Chart
            Theme              = $Theme
            Toolbar            = $Toolbar
            GridOptions        = $GridOptions
            PatternedColors    = $Patterned
            GradientColors     = $Gradient
            Events             = $Events
            Title              = $TitleBlock
            SubTitle           = $SubTitleBlock
            Design             = $Design
        }

        New-HTMLChartBar @SplatChart

    } elseif ($Type -eq 'Line') {
        if (-not $ChartAxisX) {
            Write-Warning -Message 'Chart Category (Chart Axis X) is missing.'
            return
        }
        $SplatChartLine = @{
            Chart           = $Chart
            Series          = $DataSeries
            Stroke          = $LineStroke
            DataLabel       = $DataLabel
            Legend          = $Legend
            Markers         = $Markers
            Colors          = $Colors

            ChartAxisX      = $ChartAxisX
            ChartAxisY      = $ChartAxisY

            Theme           = $Theme
            Toolbar         = $Toolbar
            GridOptions     = $GridOptions
            PatternedColors = $Patterned
            GradientColors  = $Gradient
            Events          = $Events
            Title           = $TitleBlock
            SubTitle        = $SubTitleBlock
            Design          = $Design
        }
        New-HTMLChartLine @SplatChartLine
    } elseif ($Type -eq 'Pie' -or $Type -eq 'Donut') {
        $SplatChart = @{
            Data            = $DataSet
            DataNames       = $DataName
            Colors          = $Colors

            Legend          = $Legend
            Chart           = $Chart
            Theme           = $Theme
            Toolbar         = $Toolbar
            GridOptions     = $GridOptions
            PatternedColors = $Patterned
            GradientColors  = $Gradient
            Events          = $Events
            Title           = $TitleBlock
            SubTitle        = $SubTitleBlock
            Design          = $Design
        }

        New-HTMLChartPie @SplatChart -Type $Type
    } elseif ($Type -eq 'Spark') {
        $SplatChart = @{
            Data            = $DataSet

            Colors          = $Colors

            ChartAxisX      = $ChartAxisX

            Legend          = $Legend
            Chart           = $Chart
            Theme           = $Theme
            Toolbar         = $Toolbar
            GridOptions     = $GridOptions
            PatternedColors = $Patterned
            GradientColors  = $Gradient
            Events          = $Events
            Title           = $TitleBlock
            SubTitle        = $SubTitleBlock
            Design          = $Design
        }
        New-HTMLChartSpark @SplatChart
    } elseif ($Type -eq 'Radial') {
        $SplatChart = @{
            Data            = $DataSet
            DataNames       = $DataName
            Colors          = $Colors

            PlotOptions     = $PlotOptions

            Legend          = $Legend
            Chart           = $Chart
            Theme           = $Theme
            Toolbar         = $Toolbar
            GridOptions     = $GridOptions
            PatternedColors = $Patterned
            GradientColors  = $Gradient
            Events          = $Events
            Title           = $TitleBlock
            SubTitle        = $SubTitleBlock
            Design          = $Design
        }
        New-HTMLChartRadial @SplatChart
    } elseif ($Type -eq 'rangeBar') {
        $SplatChart = @{
            Data            = $DataSetChartTimeLine
            ChartAxisX      = $ChartAxisX
            ChartAxisY      = $ChartAxisY
            ChartToolTip    = $ChartToolTip

            Legend          = $Legend
            Chart           = $Chart
            Theme           = $Theme
            Toolbar         = $Toolbar
            GridOptions     = $GridOptions
            PatternedColors = $Patterned
            GradientColors  = $Gradient
            Events          = $Events
            Title           = $TitleBlock
            SubTitle        = $SubTitleBlock
            Design          = $Design
        }
        New-HTMLChartTimeLine @SplatChart
    }
}

Register-ArgumentCompleter -CommandName New-HTMLChart -ParameterName TitleColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLChart -ParameterName SubTitleColor -ScriptBlock $Script:ScriptBlockColors
Function New-HTMLCodeBlock {
    <#
    .SYNOPSIS
    Creates a new HTML code block with customizable settings for syntax highlighting.
 
    .DESCRIPTION
    This function creates a new HTML code block with customizable settings for syntax highlighting. It allows you to specify the code language, theme, group, title, line numbers visibility, highlighted lines, and line numbering offset.
 
    .PARAMETER Code
    Specifies the code content to be displayed in the code block.
 
    .PARAMETER Style
    Specifies the language style for syntax highlighting. Default is 'powershell'.
 
    .PARAMETER Theme
    Specifies the theme for the code block. Available themes include 'enlighter', 'beyond', 'classic', and more.
 
    .PARAMETER Group
    Specifies the group to which the code block belongs.
 
    .PARAMETER Title
    Specifies the title of the code block.
 
    .PARAMETER Highlight
    Specifies specific lines to highlight in the code block.
 
    .PARAMETER ShowLineNumbers
    Indicates whether line numbers should be displayed in the code block.
 
    .PARAMETER LineOffset
    Specifies the starting line number offset for line numbering.
 
    .EXAMPLE
    New-HTMLCodeBlock -Code "function greet() { return 'Hello, World!'; }" -Style 'javascript' -Theme 'monokai' -Group 'Functions' -Title 'Greeting Function' -Highlight 2,4-6 -ShowLineNumbers $true -LineOffset 1
 
    Creates a new HTML code block displaying a JavaScript function with a custom theme, belonging to the 'Functions' group, titled 'Greeting Function', highlighting lines 2, 4 to 6, showing line numbers, and starting line numbering from 1.
 
    .EXAMPLE
    New-HTMLCodeBlock -Code "for ($i = 0; $i -lt 5; $i++) { Write-Host $i }" -Style 'powershell' -Group 'Loops' -Title 'For Loop Example' -ShowLineNumbers $false
 
    Creates a new HTML code block displaying a PowerShell for loop without line numbers, belonging to the 'Loops' group, titled 'For Loop Example'.
 
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)][String] $Code,
        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'assembly', 'asm', 'avrassembly', 'avrasm', 'c', 'cpp', 'c++', 'csharp',
            'css', 'cython', 'cordpro', 'diff', 'docker', 'dockerfile', 'generic', 'standard', 'groovy', 'go',
            'golang', 'html', 'ini', 'conf', 'java', 'js', 'javascript', 'jquery', 'mootools', 'ext.js', 'json',
            'kotlin', 'less', 'lua', 'gfm', 'md', 'markdown', 'octave', 'matlab', 'nsis', 'php', 'powershell', 'prolog',
            'py', 'python', 'raw', 'ruby', 'rust', 'scss', 'sass', 'shell', 'bash', 'sql',
            'squirrel', 'swift', 'typescript', 'vhdl', 'visualbasic', 'vb', 'xml', 'yaml'
        )][String] $Style = 'powershell',
        [Parameter(Mandatory = $false)]
        [ValidateSet(
            'enlighter', 'beyond', 'classic', 'godzilla', 'atomic', 'droide',
            'minimal', 'eclipse', 'mowtwo', 'rowhammer', 'bootstrap4', 'dracula', 'monokai'
        )][String] $Theme,
        [Parameter(Mandatory = $false)][String] $Group,
        [Parameter(Mandatory = $false)][String] $Title,
        [Parameter(Mandatory = $false)][String[]] $Highlight,
        [Parameter(Mandatory = $false)][nullable[bool]] $ShowLineNumbers,
        [Parameter(Mandatory = $false)][String] $LineOffset
    )
    $Script:HTMLSchema.Features.Main = $true
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.CodeBlocks = $true

    if ($null -eq $ShowLineNumbers -and $Highlight) {
        $ShowLineNumbers = $true
    }

    $Attributes = [ordered]@{
        'data-enlighter-language'    = "$Style".ToLower()
        'data-enlighter-theme'       = "$Theme".ToLower()
        'data-enlighter-group'       = "$Group".ToLower()
        'data-enlighter-title'       = "$Title"
        'data-enlighter-linenumbers' = "$ShowLineNumbers"
        'data-enlighter-highlight'   = "$Highlight"
        'data-enlighter-lineoffset'  = "$LineOffset".ToLower()
    }

    $ExtraCode = $Code.Split([System.Environment]::NewLine)
    [int] $Length = 5000
    $NewCode = foreach ($Line in $ExtraCode) {
        if ($Line.Trim() -ne '') {
            [int] $TempLength = $Line.Length - (($Line -replace '^(\s+)').Length)

            if ($TempLength -le $Length) {
                $Length = $TempLength
            }
            $Line
        }
    }
    $FixedCode = foreach ($Line in $NewCode) {
        $Line.Substring($Length)
    }
    $FinalCode = $FixedCode -join [System.Environment]::NewLine

    New-HTMLTag -Tag 'pre' -Attributes $Attributes {
        $FinalCode
    }
}

function New-HTMLContainer {
    <#
    .SYNOPSIS
    Creates a new HTML container element with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML container element with the specified options such as content, width, margin, and anchor name.
 
    .PARAMETER HTML
    Specifies the content to be placed inside the container as a ScriptBlock.
 
    .PARAMETER Width
    Specifies the width of the container. Default is '100%'.
 
    .PARAMETER Margin
    Specifies the margin of the container.
 
    .PARAMETER AnchorName
    Specifies the anchor name for the container. If not provided, a random anchor name will be generated.
 
    .EXAMPLE
    New-HTMLContainer -HTML {
        // HTML content here
    } -Width '50%' -Margin '10px' -AnchorName 'myAnchor'
 
    Creates a new HTML container with custom HTML content, width of 50%, margin of 10px, and anchor name 'myAnchor'.
 
    .EXAMPLE
    New-HTMLContainer -HTML {
        // More HTML content
    }
 
    Creates a new HTML container with additional HTML content and default width and margin.
 
    #>

    [alias('Container')]
    [CmdletBinding()]
    param(
        [alias('Content')][Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $HTML,
        [object] $Width = '100%',
        [string] $Margin,
        [string] $AnchorName
    )
    $Script:HTMLSchema.Features.MainFlex = $true
    if (-not $AnchorName) {
        $AnchorName = "anchor-$(Get-RandomStringName -Size 7)"
    }
    if ($Width -or $Margin) {
        [string] $ClassName = "flexElement$(Get-RandomStringName -Size 8 -LettersOnly)"
        $Attributes = @{
            'flex-basis' = ConvertFrom-Size -Size $Width
            'margin'     = if ($Margin) {
                $Margin }
        }
        $Css = ConvertTo-LimitedCSS -ClassName $ClassName -Attributes $Attributes -Group

        $Script:HTMLSchema.CustomHeaderCSS[$AnchorName] = $Css
        [string] $Class = "$ClassName overflowHidden"
    } else {
        [string] $Class = 'flexElement overflowHidden'
    }
    New-HTMLTag -Tag 'div' -Attributes @{ class = $Class } {
        if ($HTML) {
            Invoke-Command -ScriptBlock $HTML
        }
    }
}
function New-HTMLDiagram {
    <#
    .SYNOPSIS
    Creates a new HTML diagram with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML diagram with customizable options such as diagram data, height, width, image bundling, background image, background size, auto-resize, loading bar, filtering, and more.
 
    .PARAMETER Diagram
    Specifies the diagram data to be displayed. This should be a ScriptBlock containing the diagram configuration.
 
    .PARAMETER Height
    Specifies the height of the diagram.
 
    .PARAMETER Width
    Specifies the width of the diagram.
 
    .PARAMETER BundleImages
    Indicates whether to bundle images used in the diagram.
 
    .PARAMETER BackGroundImage
    Specifies the background image for the diagram.
 
    .PARAMETER BackgroundSize
    Specifies the size of the background image. Default is '100% 100%'.
 
    .PARAMETER NoAutoResize
    Indicates whether auto-resize functionality is disabled.
 
    .PARAMETER DisableLoader
    Indicates whether the loading bar should be disabled.
 
    .PARAMETER EnableFiltering
    Indicates whether filtering functionality is enabled.
 
    .PARAMETER MinimumFilteringChars
    Specifies the minimum number of characters required for filtering.
 
    .PARAMETER EnableFilteringButton
    Indicates whether a filtering button should be displayed.
 
    .EXAMPLE
    New-HTMLDiagram -Diagram {
        // Diagram configuration settings here
    } -Height 500 -Width 800 -BundleImages -BackGroundImage 'https://example.com/background.jpg' -BackgroundSize 'cover' -DisableLoader -EnableFiltering -MinimumFilteringChars 2 -EnableFilteringButton
 
    Creates a new HTML diagram with custom configuration settings including height, width, bundled images, background image, disabled loading bar, enabled filtering, and a filtering button.
 
    .EXAMPLE
    New-HTMLDiagram -Diagram {
        // More diagram configuration settings
    } -Height 600 -Width 1000
 
    Creates a new HTML diagram with additional configuration settings and default options for height and width.
 
    #>

    [alias('Diagram', 'New-Diagram')]
    [CmdletBinding()]
    param(
        [ScriptBlock] $Diagram,
        [object] $Height,
        [object] $Width,
        [switch] $BundleImages,
        [uri] $BackGroundImage,
        [string] $BackgroundSize = '100% 100%',
        [switch] $NoAutoResize, # Doesn't seem to do anything
        [alias('DisableLoadingBar')]
        [switch] $DisableLoader,
        [switch] $EnableFiltering,
        [int] $MinimumFilteringChars = 3,
        [switch] $EnableFilteringButton
    )
    if (-not $Script:HTMLSchema.Features) {
        Write-Warning 'New-HTMLDiagram - Creation of HTML aborted. Most likely New-HTML is missing.'
        Exit
    }
    $Script:HTMLSchema.Features.MainFlex = $true

    $DataEdges = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
    $DataNodes = [ordered] @{ }
    $DataEvents = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()

    [Array] $Settings = & $Diagram
    foreach ($Node in $Settings) {
        if ($Node.Type -eq 'DiagramNode') {
            $ID = $Node.Settings['id']
            $DataNodes[$ID] = $Node.Settings

            foreach ($From in $Node.Edges.From) {
                foreach ($To in $Node.Edges.To) {
                    $Edge = Copy-Dictionary -Dictionary $Node.Edges  
                    $Edge['from'] = $From
                    $Edge['to'] = $To
                    $DataEdges.Add($Edge)
                }
            }
        } elseif ($Node.Type -eq 'DiagramOptionsInteraction') {
            $DiagramOptionsInteraction = $Node.Settings
        } elseif ($Node.Type -eq 'DiagramOptionsManipulation') {
            $DiagramOptionsManipulation = $Node.Settings
        } elseif ($Node.Type -eq 'DiagramOptionsPhysics') {
            $DiagramOptionsPhysics = $Node.Settings
        } elseif ($Node.Type -eq 'DiagramOptionsLayout') {
            $DiagramOptionsLayout = $Node.Settings
        } elseif ($Node.Type -eq 'DiagramOptionsNodes') {
            $DiagramOptionsNodes = $Node.Settings
        } elseif ($Node.Type -eq 'DiagramOptionsEdges') {
            $DiagramOptionsEdges = $Node.Settings
        } elseif ($Node.Type -eq 'DiagramLink') {
            if ($Node.Settings.From -and $Node.Settings.To) {
                foreach ($From in $Node.Settings.From) {
                    foreach ($To in $Node.Settings.To) {
                        $Edge = $Node.Edges.Clone()
                        $Edge['from'] = $From
                        $Edge['to'] = $To
                        $DataEdges.Add($Edge)
                    }
                }
            }
            $DataEdges.Add($Node.Edges)
        } elseif ($Node.Type -eq 'DiagramEvent') {
            $DataEvents.Add($Node.Settings)
        }
    }

    $IconsAvailable = $false
    [Array] $Nodes = foreach ($_ in $DataNodes.Keys) {
        if ($DataNodes[$_]['image']) {
            if ($BundleImages) {
                $DataNodes[$_]['image'] = Convert-Image -Image $DataNodes[$_]['image']
            }
        }
        $NodeJson = $DataNodes[$_] | ConvertTo-JsonLiteral -Depth 5 -AdvancedReplace @{ '.' = '\.'; '$' = '\$' } 
        if ($DataNodes[$_].icon) {
            $IconsAvailable = $true
        }

        $Replace = @{
            '"\"Font Awesome 5 Solid\""'        = "'`"Font Awesome 5 Solid`"'"
            '"\"Font Awesome 5 Brands\""'       = "'`"Font Awesome 5 Brands`"'"
            '"\"Font Awesome 5 Regular\""'      = "'`"Font Awesome 5 Regular`"'"
            '"\"Font Awesome 5 Free\""'         = "'`"Font Awesome 5 Free`"'"
            '"\"Font Awesome 5 Free Regular\""' = "'`"Font Awesome 5 Free Regular`"'"
            '"\"Font Awesome 5 Free Solid\""'   = "'`"Font Awesome 5 Free Solid`"'"
            '"\"Font Awesome 5 Free Brands\""'  = "'`"Font Awesome 5 Free Brands`"'"
            '"\\u'                              = '"\u'
        }
        foreach ($R in $Replace.Keys) {
            $NodeJson = $NodeJson.Replace($R, $Replace[$R])
        }
        $NodeJson
    }
    [Array] $Edges = foreach ($_ in $DataEdges) {
        $_ | ConvertTo-JsonLiteral -Depth 5 -AdvancedReplace @{ '.' = '\.'; '$' = '\$' } 
    }

    $Options = @{
        autoResize = -not $NoAutoResize.IsPresent 
    }
    if ($DiagramOptionsInteraction) {
        if ($DiagramOptionsInteraction['interaction']) {
            $Options['interaction'] = $DiagramOptionsInteraction['interaction']
        }
    }
    if ($DiagramOptionsManipulation) {
        if ($DiagramOptionsManipulation['manipulation']) {
            $Options['manipulation'] = $DiagramOptionsManipulation['manipulation']
        }
    }
    if ($DiagramOptionsPhysics) {
        if ($DiagramOptionsPhysics['physics']) {
            $Options['physics'] = $DiagramOptionsPhysics['physics']
        }
    }
    if ($DiagramOptionsLayout) {
        if ($DiagramOptionsLayout['layout']) {
            $Options['layout'] = $DiagramOptionsLayout['layout']
        }
    }
    if ($DiagramOptionsEdges) {
        if ($DiagramOptionsEdges['edges']) {
            $Options['edges'] = $DiagramOptionsEdges['edges']
        }
    }
    if ($DiagramOptionsNodes) {
        if ($DiagramOptionsNodes['nodes']) {
            $Options['nodes'] = $DiagramOptionsNodes['nodes']
        }
    }

    if ($BundleImages -and $BackGroundImage) {
        $Image = Convert-Image -Image $BackGroundImage
    } else {
        $Image = $BackGroundImage
    }

    $newInternalDiagramSplat = @{
        Nodes                 = $Nodes
        Edges                 = $Edges
        Options               = $Options
        Width                 = $Width
        Height                = $Height
        BackgroundImage       = $Image
        Events                = $DataEvents
        IconsAvailable        = $IconsAvailable
        DisableLoader         = $DisableLoader
        EnableFiltering       = $EnableFiltering.IsPresent
        MinimumFilteringChars = $MinimumFilteringChars
    }
    if ($PSBoundParameters.ContainsKey('EnableFilteringButton')) {
        $newInternalDiagramSplat['EnableFilteringButton'] = $EnableFilteringButton.IsPresent
    }
    New-InternalDiagram @newInternalDiagramSplat
}

function New-HTMLFontIcon {
    <#
    .SYNOPSIS
    Creates an HTML font icon with specified parameters.
 
    .DESCRIPTION
    This function generates an HTML font icon based on the provided parameters such as icon size, color, and specific icon set (e.g., FontAwesomeBrands, FontAwesomeRegular, FontAwesomeSolid, FontMaterial).
 
    .PARAMETER IconSize
    Specifies the size of the icon.
 
    .PARAMETER IconColor
    Specifies the color of the icon.
 
    .PARAMETER IconBrands
    Specifies the icon from the FontAwesomeBrands set.
 
    .PARAMETER IconRegular
    Specifies the icon from the FontAwesomeRegular set.
 
    .PARAMETER IconSolid
    Specifies the icon from the FontAwesomeSolid set.
 
    .EXAMPLE
    New-HTMLFontIcon -IconSize 24 -IconColor "red" -IconBrands "fa-github"
    Generates an HTML font icon with a size of 24, red color, and the GitHub icon from the FontAwesomeBrands set.
 
    .EXAMPLE
    New-HTMLFontIcon -IconSize 32 -IconColor "blue" -IconRegular "fa-envelope"
    Generates an HTML font icon with a size of 32, blue color, and the envelope icon from the FontAwesomeRegular set.
    #>

    [cmdletBinding(DefaultParameterSetName = 'FontAwesomeSolid')]
    param(
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][int] $IconSize,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconColor,

        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")][string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")][string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconSolid,

        # FontsMaterialIcon
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontsMaterialIcon)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontsMaterialIcon))
            }
        )]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconMaterial,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Spinning,
        [parameter(ParameterSetName = "FontMaterial")][switch] $SpinningReverse,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Bordered,
        [parameter(ParameterSetName = "FontMaterial")][switch] $BorderedCircle,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullLeft,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullRight,
        <#
        [parameter(ParameterSetName = "FontMaterial")][switch] $Wobble,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FadeInLeft,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Pulse,
        #>

        [parameter(ParameterSetName = "FontMaterial")][validateSet('90', '180', '270')][string] $Rotate,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipVertical,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipHorizontal,
        # for using within UL/LI http://zavoloklom.github.io/material-design-iconic-font/examples.html
        [parameter(ParameterSetName = "FontMaterial")][switch] $ListIcons,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][switch] $FixedWidth,

        [switch] $AsCSS,
        [switch] $AsHashTable,
        [string] $Name
    )

    $StyleIcon = @{ }
    if ($IconSize -ne 0) {
        $StyleIcon.'font-size' = "$($IconSize)px"
    }

    if ($IconColor) {
        $StyleIcon.'color' = ConvertFrom-Color -Color $IconColor
    }

    if ($IconBrands -or $IconSolid -or $IconRegular) {

        Enable-HTMLFeature -Feature FontsAwesome

        if ($AsCSS) {
            [string] $Content = @(
                if ($IconBrands) {
                    $Face = '"Font Awesome 5 Brands"'
                    $Global:HTMLIcons['FontAwesomeBrands'][$IconBrands]
                } elseif ($IconRegular) {
                    $Face = '"Font Awesome 5 Free"'
                    $Global:HTMLIcons['FontAwesomeRegular'][$IconRegular]
                } elseif ($IconSolid) {
                    $Face = '"Font Awesome 5 Free"'
                    $Global:HTMLIcons['FontAwesomeSolid'][$IconSolid]
                }
            )
            $Css = [ordered] @{
                'font-family' = $Face
                'font-weight' = 'bold'
                'content'     = "`"\$Content`""
                'color'       = $StyleIcon.'color'
                'font-size'   = $StyleIcon.'font-size'
            }
            Remove-EmptyValue -Hashtable $Css
            if ($AsHashTable) {
                $Css
            } else {
                ConvertTo-CascadingStyleSheets -Css $CSS -Name $Name
            }
        } else {
            $Class = @(
                if ($IconBrands) {
                    "fab fa-$IconBrands".ToLower() 
                } elseif ($IconRegular) {
                    "far fa-$IconRegular".ToLower() 
                } elseif ($IconSolid) {
                    "fas fa-$IconSolid".ToLower() 
                }
                if ($FixedWidth) {
                    'fa-fw'
                }
            ) | Where-Object { $_ }
            New-HTMLTag -Tag 'i' -Attributes @{ class = $Class; style = $StyleIcon }
        }
    } elseif ($IconMaterial) {
        Enable-HTMLFeature -Feature FontsMaterialIcon

        $Class = @(
            'zmdi'
            "zmdi-$IconMaterial"
            if ($FixedWidth) {
                'zmdi-hc-fw'
            }
            if ($Spinning) {
                'zmdi-hc-spin'
            }
            if ($SpinningReverse) {
                'zmdi-hc-spin-reverse'
            }
            if ($PullLeft) {
                'pull-left'
            }
            if ($PullRight) {
                'pull-right'
            }
            if ($Bordered) {
                'zmdi-hc-border'
            }
            if ($BorderedCircle) {
                'zmdi-hc-border-circle'
            }

            if ($Rotate) {
                "zmdi-hc-rotate-$Rotate"
            }
            if ($FlipVertical) {
                'zmdi-hc-flip-vertical'
            }
            if ($FlipHorizontal) {
                'zmdi-hc-flip-horizontal'
            }
            if ($ListIcons) {

                'zmdi-hc-li'
            }
        ) | Where-Object { $_ }
        New-HTMLTag -Tag 'i' -Attributes @{ class = $Class; style = $StyleIcon }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLFontIcon -ParameterName IconColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLFooter {
    <#
    .SYNOPSIS
    Building block for use within New-HTML. Provides ability to define footer.
 
    .DESCRIPTION
    Building block for use within New-HTML. Provides ability to define footer. Additional way of managing how HTML content is displayed.
 
    .PARAMETER HTMLContent
    Define one or more HTML elements
 
    .EXAMPLE
    New-HTML -TitleText 'This is a test' -FilePath "$PSScriptRoot\Example34_01.html" {
        New-HTMLHeader {
            New-HTMLText -Text "Date of this report $(Get-Date)" -Color Blue -Alignment right
        }
        New-HTMLMain {
            New-HTMLTab -TabName 'Test' {
                New-HTMLSection -HeaderText '0 section' {
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter -Simplify
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                }
            }
            New-HTMLTab -TabName 'Test5' {
                New-HTMLSection -HeaderText '1 section' {
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                        # New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                }
            }
        }
        New-HTMLFooter {
            New-HTMLText -Text "Date of this report $(Get-Date)" -Color Blue -Alignment right
        }
    } -Online -ShowHTML
 
    .NOTES
    General notes
    #>

    [alias('Footer')]
    [CmdletBinding()]
    param(
        [scriptblock] $HTMLContent
    )
    if ($HTMLContent) {
        [PSCustomObject] @{
            Type   = 'Footer'
            Output = & $HTMLContent
        }
    }
}
function New-HTMLFrame {
    <#
    .SYNOPSIS
    Allows to inline other HTML files into the current HTML file.
 
    .DESCRIPTION
    Allows to inline other HTML files into the current HTML file. This can be useful if we want to display content from another file.
 
    .PARAMETER Id
    ID of the HTML element. By default it's auto-generated.
 
    .PARAMETER Name
    Name of the HTML element. By default it's empty.
 
    .PARAMETER SourcePath
    Path to a file with HTML file to display within iFrame
 
    .PARAMETER Height
    Set the height of the iFrame to static value. This should be used when not using iFrameResizer.
 
    .PARAMETER Scrolling
    The HTML <iframe> scrolling Attribute is used to specify that whether the scrollbar will be displayed or not in the <Iframe> Element. Basically the scrollbar is used when the content is large than the Iframe Element.
    Available options are:
    - auto: It has a default value. The scrollbar appears when needed.
    - yes: This value shows the scrollbar in the Iframe Element.
    - no: This value does not show the scrollbar in the Iframe Element.
 
    .PARAMETER FrameBorder
    Set the frameborder attribute of the <iframe> element. This attribute specifies whether the frame should have a border. The default value is 0.
 
    .PARAMETER UseiFrameResizer
    Forces HTML inline feature to use iFrameResizer instead of native functionality. For fully functional feature it requires modifying the source HTML file.
 
    .PARAMETER EnableLogging
    Enable logging to Console for debugging purposes when using iFrameResizer (requires UseiFrameResizer).
 
    .EXAMPLE
    New-HTML {
        New-HTMLSection {
            New-HTMLFrame -SourcePath "$PSSCriptRoot\GPOZaurr.html" -Scrolling Auto
        } -HeaderText 'Test'
        New-HTMLSection {
            New-HTMLFrame -SourcePath "$PSSCriptRoot\GPOZaurr.html" -Scrolling Auto -Height 1500px
        } -HeaderText 'Test'
        New-HTMLSection {
            New-HTMLFrame -SourcePath "C:\Support\GitHub\PSWriteHTML\Examples\Example-Maps\Example-Maps.html"
        } -HeaderText 'Test' -Height 100vh
    } -Online -TitleText 'Test Inline' -ShowHTML -FilePath "$PSScriptRoot\Example-InlineHTML01.html" -AddComment
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [string] $Id,
        [string] $Name,
        [string] $SourcePath,
        [validateSet('No', 'Yes', 'Auto')][string] $Scrolling = 'auto',
        [object] $Height,
        [object] $FrameBorder = 0,
        [switch] $UseiFrameResizer,
        [switch] $EnableLogging
    )
    $Script:HTMLSchema.Features.iFrame = $true
    if ($UseiFrameResizer) {
        $Script:HTMLSchema.Features.iFrameResizer = $true
    }

    if (-not $ID) {
        $ID = "Inline-$(Get-RandomStringName -Size 8 -LettersOnly)"
    }

    $Attributes = [ordered] @{
        id          = $Id
        src         = $SourcePath
        name        = $Name
        height      = ConvertFrom-Size -Size $Height
        frameborder = ConvertFrom-Size -Size $FrameBorder
        scrolling   = $Scrolling.ToLower()
    }
    Remove-EmptyValue -Hashtable $Attributes

    New-HTMLTag -Tag 'iframe' -Attributes $Attributes {

    }
    if ($UseiFrameResizer) {
        $InlineScript = @{
            log = $EnableLogging.IsPresent
        }
        $InlineScriptBody = $InlineScript | ConvertTo-Json -Depth 100
        $Script = New-HTMLTag -Tag 'script' -Value {

            "iFrameResize($InlineScriptBody, '#$Id')"
        } -NewLine
        $Script
    }
}
function New-HTMLGage {
    <#
    .SYNOPSIS
    Creates a new HTML gauge with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML gauge with various customizable options such as type, colors, fonts, values, and more.
 
    .PARAMETER GageContent
    The content of the gauge.
 
    .PARAMETER Type
    Specifies the type of gauge to create. Valid values are 'Gage' and 'Donut'. Default is 'Gage'.
 
    .PARAMETER BackgroundGaugageColor
    The background color of the gauge.
 
    .PARAMETER Value
    The value to display on the gauge. This parameter is mandatory.
 
    .PARAMETER ValueSymbol
    The symbol to display with the value.
 
    .PARAMETER ValueColor
    The color of the value displayed on the gauge.
 
    .PARAMETER ValueFont
    The font of the value displayed on the gauge.
 
    .PARAMETER MinValue
    The minimum value of the gauge.
 
    .PARAMETER MinText
    The text to display for the minimum value.
 
    .PARAMETER MaxValue
    The maximum value of the gauge.
 
    .PARAMETER MaxText
    The text to display for the maximum value.
 
    .PARAMETER Reverse
    Indicates whether the gauge should be displayed in reverse.
 
    .PARAMETER DecimalNumbers
    The number of decimal places to display.
 
    .PARAMETER GaugageWidth
    The width of the gauge.
 
    .PARAMETER Label
    The label to display on the gauge.
 
    .PARAMETER LabelColor
    The color of the label.
 
    .PARAMETER Counter
    Indicates whether a counter should be displayed.
 
    .PARAMETER ShowInnerShadow
    Indicates whether an inner shadow should be displayed.
 
    .PARAMETER NoGradient
    Indicates whether gradient should be disabled.
 
    .PARAMETER ShadowOpacity
    The opacity of the shadow.
 
    .PARAMETER ShadowSize
    The size of the shadow.
 
    .PARAMETER ShadowVerticalOffset
    The vertical offset of the shadow.
 
    .PARAMETER Pointer
    Indicates whether a pointer should be displayed.
 
    .PARAMETER PointerTopLength
    The length of the top of the pointer.
 
    .PARAMETER PointerBottomLength
    The length of the bottom of the pointer.
 
    .PARAMETER PointerBottomWidth
    The width of the bottom of the pointer.
 
    .PARAMETER StrokeColor
    The color of the stroke.
 
    .PARAMETER PointerStrokeWidth
    The width of the pointer stroke.
 
    .PARAMETER PointerStrokeLinecap
    The linecap style of the pointer stroke. Valid values are 'none', 'square', 'round'.
 
    .PARAMETER PointerColor
    The color of the pointer.
 
    .PARAMETER HideValue
    Indicates whether the value should be hidden.
 
    .PARAMETER HideMinMax
    Indicates whether the min/max values should be hidden.
 
    .PARAMETER FormatNumber
    Indicates whether the number should be formatted.
 
    .PARAMETER DisplayRemaining
    Indicates whether the remaining value should be displayed.
 
    .PARAMETER HumanFriendly
    Indicates whether the value should be displayed in a human-friendly format.
 
    .PARAMETER HumanFriendlyDecimal
    The number of decimal places to display in human-friendly format.
 
    .PARAMETER SectorColors
    An array of colors to use for different sectors of the gauge.
 
    .EXAMPLE
    New-HTMLGage -Value 75 -Label "Progress" -Type "Donut" -MinValue 0 -MaxValue 100 -SectorColors @("red", "yellow", "green")
 
    Creates a new donut gauge with a value of 75, labeled as "Progress", with a range from 0 to 100, and sector colors red, yellow, and green.
 
    #>

    [CmdletBinding()]
    param (
        [scriptblock] $GageContent,
        [validateSet('Gage', 'Donut')][string] $Type = 'Gage',
        [string] $BackgroundGaugageColor,
        [parameter(Mandatory)][decimal] $Value,
        [string] $ValueSymbol,
        [string] $ValueColor,
        [string] $ValueFont,
        [nullable[int]] $MinValue,
        [string] $MinText,
        [nullable[int]] $MaxValue,
        [string] $MaxText,
        [switch] $Reverse,
        [int] $DecimalNumbers,
        [decimal] $GaugageWidth,
        [string] $Label,
        [string] $LabelColor,
        [switch] $Counter,
        [switch] $ShowInnerShadow,
        [switch] $NoGradient,
        [nullable[decimal]] $ShadowOpacity,
        [nullable[int]] $ShadowSize,
        [nullable[int]] $ShadowVerticalOffset,
        [switch] $Pointer,
        [nullable[int]]  $PointerTopLength,
        [nullable[int]] $PointerBottomLength,
        [nullable[int]] $PointerBottomWidth,
        [string] $StrokeColor,
        #[validateSet('none')][string] $PointerStroke,
        [nullable[int]] $PointerStrokeWidth,
        [validateSet('none', 'square', 'round')] $PointerStrokeLinecap,
        [string] $PointerColor,
        [switch] $HideValue,
        [switch] $HideMinMax,
        [switch] $FormatNumber,
        [switch] $DisplayRemaining,
        [switch] $HumanFriendly,
        [int] $HumanFriendlyDecimal,
        [string[]] $SectorColors
    )

    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.Raphael = $true
    $Script:HTMLSchema.Features.JustGage = $true

    [string] $ID = "Gage" + (Get-RandomStringName -Size 8)
    $Gage = [ordered] @{
        id    = $ID
        value = $Value
    }

    $Gage.shadowSize = $ShadowSize
    $Gage.shadowOpacity = $ShadowOpacity
    $Gage.shadowVerticalOffset = $ShadowVerticalOffset

    if ($DecimalNumbers) {
        $Gage.decimals = $DecimalNumbers
    }
    if ($ValueColor) {
        $Gage.valueFontColor = $ValueColor
    }
    if ($ValueColor) {
        $Gage.valueFontFamily = $ValueFont
    }
    if ($MinText) {
        $Gage.minText = $MinText
    }
    if ($MaxText) {
        $Gage.maxText = $MaxText
    }

    $Gage.min = $MinValue
    $Gage.max = $MaxValue

    if ($Label) {
        $Gage.label = $Label
    }
    if ($LabelColor) {
        $Gage.labelFontColor = ConvertFrom-Color -Color $LabelColor
    }
    if ($Reverse) {
        $Gage.reverse = $Reverse.IsPresent
    }
    if ($Type -eq 'Donut') {
        $Gage.donut = $true
    }
    if ($GaugageWidth) {
        $Gage.gaugageWidthScale = $GaugageWidthScale
    }
    if ($Counter) {
        $Gage.counter = $Counter.IsPresent
    }
    if ($showInnerShadow) {
        $Gage.showInnerShadow = $ShowInnerShadow.IsPresent
    }
    if ($BackgroundGaugageColor) {
        $Gage.gaugeColor = ConvertFrom-Color -Color $BackgroundGaugageColor
    }
    if ($NoGradient) {
        $Gage.noGradient = $NoGradient.IsPresent
    }

    if ($HideMinMax) {
        $Gage.hideMinMax = $HideMinMax.IsPresent
    }
    if ($HideValue) {
        $Gage.hideValue = $HideValue.IsPresent
    }
    if ($FormatNumber) {
        $Gage.formatNumber = $FormatNumber.IsPresent
    }
    if ($DisplayRemaining) {
        $Gage.displayRemaining = $DisplayRemaining.IsPresent
    }
    if ($HumanFriendly) {
        $Gage.humanFriendly = $HumanFriendly.IsPresent
        if ($HumanFriendlyDecimal) {
            $Gage.humanFriendlyDecimal = $HumanFriendlyDecimal
        }
    }
    if ($ValueSymbol) {
        $Gage.symbol = $ValueSymbol
    }

    if ($GageContent) {
        [Array] $GageOutput = & $GageContent
        if ($GageOutput.Count -gt 0) {
            $Gage.customSectors = @{
                percents = $true
                ranges   = $GageOutput
            }
        }
    }

    if ($Pointer) {
        $Gage.pointer = $Pointer.IsPresent

        $Gage.pointerOptions = @{ }

        $Gage.pointerOptions.toplength = $PointerTopLength

        $Gage.pointerOptions.bottomlength = $PointerBottomLength

        $Gage.pointerOptions.bottomwidth = $PointerBottomWidth

        $Gage.pointerOptions.stroke_width = $PointerStrokeWidth

        $Gage.pointerOptions.stroke_linecap = $PointerStrokeLinecap

        $Gage.pointerOptions.color = ConvertFrom-Color -Color $PointerColor

        $Gage.pointerOptions.stroke = ConvertFrom-Color -Color $StrokeColor

    }
    $gage.relativeGaugeSize = $true
    Remove-EmptyValue -Hashtable $Gage -Rerun 1 -Recursive

    $Div = New-HTMLTag -Tag 'div' -Attributes @{ id = $Gage.id; }

    $Script = New-HTMLTag -Tag 'script' -Value {

        $JSON = $Gage | ConvertTo-Json -Depth 5 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) }
        "document.addEventListener(`"DOMContentLoaded`", function (event) {"
        "var g1 = new JustGage( $JSON );"
        "});"
    } -NewLine

    $Div
    $Script
}
Register-ArgumentCompleter -CommandName New-HTMLGage -ParameterName GaugageColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLGage -ParameterName LabelColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLGage -ParameterName ValueColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLGage -ParameterName PointerColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLGage -ParameterName StrokeColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLGage -ParameterName SectorColors -ScriptBlock $Script:ScriptBlockColors

function New-HTMLHeader {
    <#
    .SYNOPSIS
    Building block for use within New-HTML. Provides ability to define header.
 
    .DESCRIPTION
    Building block for use within New-HTML. Provides ability to define header. This allows to putting HTML elements above standard tabs such as images or texts.
 
    .PARAMETER HTMLContent
    Define one or more HTML elements
 
    .EXAMPLE
    New-HTML -TitleText 'This is a test' -FilePath "$PSScriptRoot\Example34_01.html" {
        New-HTMLHeader {
            New-HTMLText -Text "Date of this report $(Get-Date)" -Color Blue -Alignment right
        }
        New-HTMLMain {
            New-HTMLTab -TabName 'Test' {
                New-HTMLSection -HeaderText '0 section' {
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter -Simplify
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                }
            }
            New-HTMLTab -TabName 'Test5' {
                New-HTMLSection -HeaderText '1 section' {
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                        # New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                }
            }
        }
        New-HTMLFooter {
            New-HTMLText -Text "Date of this report $(Get-Date)" -Color Blue -Alignment right
        }
    } -Online -ShowHTML
 
    .NOTES
    General notes
    #>

    [alias('Header')]
    [CmdletBinding()]
    param(
        [scriptblock] $HTMLContent
    )

    if ($HTMLContent) {
        [PSCustomObject] @{
            Type   = 'Header'
            Output = & $HTMLContent
        }
    }
}
Function New-HTMLHeading {
    <#
    .SYNOPSIS
    Creates a new HTML heading with customizable options.
 
    .DESCRIPTION
    This function generates an HTML heading based on the provided parameters such as heading level, text content, underline option, and color.
 
    .PARAMETER Heading
    Specifies the level of the heading. Valid values are 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'.
 
    .PARAMETER HeadingText
    The text content to display within the heading.
 
    .PARAMETER Underline
    Indicates whether the heading should be underlined.
 
    .PARAMETER Color
    The color of the heading text.
 
    .EXAMPLE
    New-HTMLHeading -Heading 'h1' -HeadingText 'Main Title' -Underline -Color 'blue'
    Generates an HTML heading of level h1 with the text 'Main Title', underlined, and in blue color.
 
    .EXAMPLE
    New-HTMLHeading -Heading 'h3' -HeadingText 'Sub Title' -Color 'green'
    Generates an HTML heading of level h3 with the text 'Sub Title' in green color.
    #>

    [CmdletBinding()]
    Param (
        [validateset('h1', 'h2', 'h3', 'h4', 'h5', 'h6')][string] $Heading,
        [string] $HeadingText,
        [switch] $Underline,
        [string] $Color
    )
    $Script:HTMLSchema.Features.DefaultHeadings = $true
    if ($null -ne $Color) {
        $RGBcolor = ConvertFrom-Color -Color $Color
        $Attributes = @{
            style = @{ color = $RGBcolor }
        }
    } else {
        $Attributes = @{ }
    }
    if ($Underline) {
        $Attributes.Class = "$($Attributes.Class) underline"
    }
    New-HTMLTag -Tag $Heading -Attributes $Attributes {
        $HeadingText
    }
}
Register-ArgumentCompleter -CommandName New-HTMLHeading -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-HTMLHierarchicalTree {
    <#
    .SYNOPSIS
    Creates a hierarchical tree structure in HTML using D3.js.
 
    .DESCRIPTION
    This function generates an HTML hierarchical tree structure based on the provided tree view data using D3.js library.
 
    .PARAMETER TreeView
    Specifies the tree view data to be used for generating the hierarchical tree structure.
 
    .PARAMETER Autosize
    Indicates whether the SVG should be sized based on the nodes or use configured height/width for the SVG.
 
    .EXAMPLE
    New-HTMLHierarchicalTree -TreeView {
        @{
            Type = 'TreeNode'
            Settings = @{
                id = 1
                parentId = 0
                name = 'Root'
                description = 'Root Node'
            }
        }
    } -Autosize
    # Generates a hierarchical tree with a root node.
 
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $TreeView,
        [Switch]$Autosize
    )
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.D3Mitch = $true

    [string] $ID = "HierarchicalTree-" + (Get-RandomStringName -Size 8)

    $TreeNodes = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()

    [Array] $Settings = & $TreeView
    foreach ($Object in $Settings) {
        if ($Object.Type -eq 'TreeNode') {
            $TreeNodes.Add($Object.Settings)
        }
    }

    $Data = $TreeNodes | ConvertTo-Json -Depth 5

    if ($Autosize) {
        $SizingMode = 'nodeSize'  
    } else {
        $SizingMode = 'size'      
    }

    $Section = New-HTMLTag -Tag 'section' -Attributes @{ id = $ID; class = 'hierarchicalTree' }
    $Script = New-HTMLTag -Tag 'script' -Value { @"
        var data = $Data;
        var treePlugin = new d3.mitchTree.boxedTree()
        .setIsFlatData(true)
        .setData(data)
        .setElement(document.getElementById("$ID"))
        .setIdAccessor(function (data) {
            return data.id;
        })
        .setParentIdAccessor(function (data) {
            return data.parentId;
        })
        .setBodyDisplayTextAccessor(function (data) {
            return data.description;
        })
        .setTitleDisplayTextAccessor(function (data) {
            return data.name;
        })
        .getNodeSettings()
            .setSizingMode('${SizingMode}')
            .back()
        .initialize();
"@

    } -NewLine

    $Section
    $Script
}
function New-HTMLHorizontalLine {
    <#
    .SYNOPSIS
    Creates a horizontal line in HTML.
 
    .DESCRIPTION
    The New-HTMLHorizontalLine function generates a horizontal line (<hr>) tag in HTML.
 
    .EXAMPLE
    New-HTMLHorizontalLine
    Generates a horizontal line in HTML.
 
    #>

    [CmdletBinding()]
    param()
    New-HTMLTag -Tag 'hr' -SelfClosing
}
function New-HTMLImage {
    <#
    .SYNOPSIS
    Creates IMG tag with image link or image bundled inline
 
    .DESCRIPTION
    Creates IMG tag with image link or image bundled inline
 
    .PARAMETER Source
    Link to an image or file path to an image
 
    .PARAMETER UrlLink
    Specifies the URL of the page the link goes to when user clicks an image
 
    .PARAMETER AlternativeText
    Specifies an alternate text for the image, if the image for some reason cannot be displayed
 
    .PARAMETER Class
    Overwrites default CSS settings for links
 
    .PARAMETER Target
    The target attribute specifies where to open the linked document.
 
    - _blank Opens the linked document in a new window or tab
    - _self Opens the linked document in the same frame as it was clicked (this is default)
    - _parent Opens the linked document in the parent frame
    - _top Opens the linked document in the full body of the window
 
    Additionally framename can be given. Default is _blank
 
    .PARAMETER Width
    Width of an image (optional)
 
    .PARAMETER Height
    Height of an image (optional)
 
    .PARAMETER Inline
    Inserts given Image URL/File directly into HTML
 
    .EXAMPLE
    New-HTMLImage -Source 'https://evotec.pl/image.png' -UrlLink 'https://evotec.pl/' -AlternativeText 'My other text' -Class 'otehr' -Width '100%'
 
    .NOTES
    General notes
    #>

    [alias('Image', 'EmailImage')]
    [CmdletBinding()]
    param(
        [string] $Source,
        [Uri] $UrlLink = '',
        [string] $AlternativeText = '',
        [string] $Class = 'logo',
        [string] $Target = '_blank',
        [object] $Width,
        [object] $Height,
        [switch] $Inline,
        [Parameter(DontShow)][switch] $DisableCache
    )
    $Script:HTMLSchema.Features.DefaultImage = $true

    if ($Inline) {

        $BinaryImage = Convert-Image -Image $Source -Cache:(-not $DisableCache)
    }

    New-HTMLTag -Tag 'div' -Attributes @{ class = $Class.ToLower() } {
        $AAttributes = [ordered]@{
            'target' = $Target
            'href'   = $UrlLink.AbsoluteUri
        }
        New-HTMLTag -Tag 'a' -Attributes $AAttributes {
            if ($Inline) {
                $ImgAttributes = [ordered]@{
                    'src'    = "$BinaryImage"
                    'alt'    = "$AlternativeText"
                    'width'  = $Width 
                    'height' = $Height 
                }
            } else {
                $ImgAttributes = [ordered]@{
                    'src'    = "$Source"
                    'alt'    = "$AlternativeText"
                    'width'  = $Width 
                    'height' = $Height  
                }
            }
            New-HTMLTag -Tag 'img' -Attributes $ImgAttributes
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLImage -ParameterName Target -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    @('_blank', '_self', '_parent', '_top') | Where-Object { $_ -like "*$wordToComplete*" }
}
function New-HTMLList {
    <#
    .SYNOPSIS
    Creates an HTML list with customizable styling options.
 
    .DESCRIPTION
    The New-HTMLList function generates an HTML list with various styling options such as color, font size, font weight, alignment, and more.
 
    .PARAMETER ListItems
    Specifies the list items to be included in the HTML list.
 
    .PARAMETER Type
    Specifies the type of list to create. Valid values are 'Unordered' (default) and 'Ordered'.
 
    .PARAMETER Color
    Specifies the color of the text in the list.
 
    .PARAMETER BackGroundColor
    Specifies the background color of the list.
 
    .PARAMETER FontSize
    Specifies the font size of the text in the list.
 
    .PARAMETER FontWeight
    Specifies the weight of the font. Valid values include 'normal', 'bold', 'bolder', 'lighter', and numeric values from 100 to 900.
 
    .PARAMETER FontStyle
    Specifies the style of the font. Valid values are 'normal', 'italic', and 'oblique'.
 
    .PARAMETER FontVariant
    Specifies the variant of the font. Valid values are 'normal' and 'small-caps'.
 
    .PARAMETER FontFamily
    Specifies the font family for the text in the list.
 
    .PARAMETER Alignment
    Specifies the alignment of the text within the list. Valid values are 'left', 'center', 'right', and 'justify'.
 
    .PARAMETER TextDecoration
    Specifies the decoration of the text. Valid values are 'none', 'line-through', 'overline', and 'underline'.
 
    .PARAMETER TextTransform
    Specifies the transformation of the text. Valid values are 'uppercase', 'lowercase', and 'capitalize'.
 
    .PARAMETER Direction
    Specifies the direction of the text. Valid value is 'rtl' (right-to-left).
 
    .PARAMETER LineBreak
    Indicates whether to include line breaks between list items.
 
    .PARAMETER Reversed
    Indicates whether the list should be displayed in reverse order.
 
    .EXAMPLE
    New-HTMLList -ListItems { 'Item 1', 'Item 2', 'Item 3' } -Type Unordered -Color 'blue' -FontSize 12 -FontWeight 'bold' -Alignment 'center'
 
    Creates an unordered HTML list with three items ('Item 1', 'Item 2', 'Item 3') displayed in blue color, font size 12, bold font weight, and centered alignment.
 
    .EXAMPLE
    New-HTMLList -ListItems { 'Apple', 'Banana', 'Cherry' } -Type Ordered -FontFamily 'Arial' -TextDecoration 'underline' -Direction 'rtl'
 
    Creates an ordered HTML list with three items ('Apple', 'Banana', 'Cherry') using Arial font family, underlined text decoration, and right-to-left text direction.
 
    #>

    [alias('EmailList')]
    [CmdletBinding()]
    param(
        [Parameter(Position = 0)][ScriptBlock]$ListItems,
        [ValidateSet('Unordered', 'Ordered')] [string] $Type = 'Unordered',
        [string] $Color,
        [string] $BackGroundColor,
        [object] $FontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [string] $FontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [ValidateSet('rtl')][string] $Direction,
        [switch] $LineBreak,
        [switch] $Reversed
    )

    $newHTMLSplat = @{ }
    if ($Alignment) {
        $newHTMLSplat.Alignment = $Alignment
    }
    if ($FontSize) {
        $newHTMLSplat.FontSize = $FontSize
    }
    if ($TextTransform) {
        $newHTMLSplat.TextTransform = $TextTransform
    }
    if ($Color) {
        $newHTMLSplat.Color = $Color
    }
    if ($FontFamily) {
        $newHTMLSplat.FontFamily = $FontFamily
    }
    if ($Direction) {
        $newHTMLSplat.Direction = $Direction
    }
    if ($FontStyle) {
        $newHTMLSplat.FontStyle = $FontStyle
    }
    if ($TextDecoration) {
        $newHTMLSplat.TextDecoration = $TextDecoration
    }
    if ($BackGroundColor) {
        $newHTMLSplat.BackGroundColor = $BackGroundColor
    }
    if ($FontVariant) {
        $newHTMLSplat.FontVariant = $FontVariant
    }
    if ($FontWeight) {
        $newHTMLSplat.FontWeight = $FontWeight
    }
    if ($LineBreak) {
        $newHTMLSplat.LineBreak = $LineBreak
    }

    [bool] $SpanRequired = $false
    foreach ($Entry in $newHTMLSplat.GetEnumerator()) {
        if (($Entry.Value | Measure-Object).Count -gt 0) {
            $SpanRequired = $true
            break
        }
    }

    $ListAttributes = [ordered] @{}
    if ($Reversed) {
        $ListAttributes['reversed'] = 'reversed'
    }
    if ($ListItems) {
        [string] $List = @(
            if ($Type -eq 'Unordered') {
                New-HTMLTag -Tag 'ul' -Attributes $ListAttributes {
                    Invoke-Command -ScriptBlock $ListItems
                }
            } else {
                New-HTMLTag -Tag 'ol' -Attributes $ListAttributes {
                    Invoke-Command -ScriptBlock $ListItems
                }
            }
        )
        if ($SpanRequired) {
            New-HTMLSpanStyle @newHTMLSplat {
                $List
            }
        } else {
            $List
        }
    } else {
        Write-Warning "New-HTMLList - No content provided. Please use New-HTMLListItem inside New-HTMLList."
    }
}

Register-ArgumentCompleter -CommandName New-HTMLList -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLList -ParameterName BackGroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLListItem {
    <#
    .SYNOPSIS
    Creates a new HTML list item with customizable styling options.
 
    .DESCRIPTION
    The New-HTMLListItem function generates an HTML list item with various styling options such as text color, background color, font size, font weight, alignment, and more.
 
    .PARAMETER NestedListItems
    Specifies the nested list items to be included within the current list item.
 
    .PARAMETER Text
    Specifies the text content of the list item.
 
    .PARAMETER Color
    Specifies the color of the text in the list item.
 
    .PARAMETER BackGroundColor
    Specifies the background color of the list item.
 
    .PARAMETER FontSize
    Specifies the font size of the text in the list item.
 
    .PARAMETER FontWeight
    Specifies the weight of the font. Valid values include 'normal', 'bold', 'bolder', 'lighter', and numeric values from 100 to 900.
 
    .PARAMETER FontStyle
    Specifies the style of the font. Valid values are 'normal', 'italic', and 'oblique'.
 
    .PARAMETER FontVariant
    Specifies the variant of the font. Valid values are 'normal' and 'small-caps'.
 
    .PARAMETER FontFamily
    Specifies the font family for the text in the list item.
 
    .PARAMETER Alignment
    Specifies the alignment of the text within the list item. Valid values are 'left', 'center', 'right', and 'justify'.
 
    .PARAMETER TextDecoration
    Specifies the decoration of the text. Valid values are 'none', 'line-through', 'overline', and 'underline'.
 
    .PARAMETER TextTransform
    Specifies the transformation of the text. Valid values are 'uppercase', 'lowercase', and 'capitalize'.
 
    .PARAMETER Direction
    Specifies the direction of the text. Valid value is 'rtl' (right-to-left).
 
    .PARAMETER LineBreak
    Indicates whether to include a line break after the list item.
 
    .EXAMPLE
    New-HTMLListItem -Text 'Item 1' -Color 'blue' -FontSize '12px' -FontWeight 'bold' -Alignment 'center'
    Creates an HTML list item with the text 'Item 1' in blue color, font size of 12 pixels, bold font weight, and centered alignment.
 
    .EXAMPLE
    New-HTMLListItem -Text 'Item 2' -FontFamily 'Arial' -TextDecoration 'underline' -Direction 'rtl'
    Creates an HTML list item with the text 'Item 2' in Arial font family, underlined text decoration, and right-to-left direction.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0)][scriptblock] $NestedListItems,
        [Parameter(Position = 1)][string[]] $Text,
        [Parameter(Position = 2)][string[]] $Color = @(),
        [Parameter(Position = 3)][string[]] $BackGroundColor = @(),
        [object[]] $FontSize = @(),
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string[]] $FontWeight = @(),
        [ValidateSet('normal', 'italic', 'oblique')][string[]] $FontStyle = @(),
        [ValidateSet('normal', 'small-caps')][string[]] $FontVariant = @(),
        [string[]] $FontFamily = @(),
        [ValidateSet('left', 'center', 'right', 'justify')][string[]] $Alignment = @(),
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string[]] $TextDecoration = @(),
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string[]] $TextTransform = @(),
        [ValidateSet('rtl')][string[]] $Direction = @(),
        [switch] $LineBreak
    )

    $newHTMLTextSplat = @{
        Alignment       = $Alignment
        FontSize        = $FontSize
        TextTransform   = $TextTransform
        Text            = $Text
        Color           = $Color
        FontFamily      = $FontFamily
        Direction       = $Direction
        FontStyle       = $FontStyle
        TextDecoration  = $TextDecoration
        BackGroundColor = $BackGroundColor
        FontVariant     = $FontVariant
        FontWeight      = $FontWeight
        LineBreak       = $LineBreak
    }

    New-HTMLTag -Tag 'li' -Attributes $Style -Value {
        New-HTMLText @newHTMLTextSplat -SkipParagraph
        if ($NestedListItems) {
            & $NestedListItems
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLListItem -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLListItem -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLLogo {
    <#
    .SYNOPSIS
    Creates a new HTML logo with specified parameters.
 
    .DESCRIPTION
    This function creates a new HTML logo using the provided parameters. It allows customization of the left and right logo names, strings, and the option to hide logos.
 
    .PARAMETER LogoPath
    The path to the logo.
 
    .PARAMETER LeftLogoName
    The name of the left logo. Default is "Sample".
 
    .PARAMETER RightLogoName
    The name of the right logo. Default is "Alternate".
 
    .PARAMETER LeftLogoString
    The string for the left logo.
 
    .PARAMETER RightLogoString
    The string for the right logo.
 
    .PARAMETER HideLogos
    Switch parameter to hide logos.
 
    .EXAMPLE
    New-HTMLLogo -LogoPath "C:\Logos\logo.png" -LeftLogoName "CompanyA" -RightLogoName "CompanyB" -LeftLogoString "Company A Logo" -RightLogoString "Company B Logo"
 
    Creates a new HTML logo with custom left and right logo names and strings.
 
    .EXAMPLE
    New-HTMLLogo -LogoPath "C:\Logos\logo.png" -HideLogos
 
    Creates a new HTML logo with logos hidden.
 
    #>

    [CmdletBinding()]
    param(
        [String] $LogoPath,
        [string] $LeftLogoName = "Sample",
        [string] $RightLogoName = "Alternate",
        [string] $LeftLogoString,
        [string] $RightLogoString,
        [switch] $HideLogos
    )
    $Script:HTMLSchema.Features.MainImage = $true

    $LogoSources = Get-HTMLLogos -LogoPath $LogoPath -RightLogoName $RightLogoName -LeftLogoName $LeftLogoName -LeftLogoString $LeftLogoString -RightLogoString $RightLogoString

    $Options = [PSCustomObject] @{
        Logos        = $LogoSources
        ColorSchemes = $ColorSchemes
    }

    if ($HideLogos -eq $false) {
        $Leftlogo = $Options.Logos[$LeftLogoName]
        $Rightlogo = $Options.Logos[$RightLogoName]
        $Script:HTMLSchema.Logos = @(
            '<!-- START LOGO -->'
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'legacyLogo' } {
                New-HTMLTag -Tag 'div' -Attributes @{ class = 'legacyLeftLogo' } {
                    New-HTMLTag -Tag 'img' -Attributes @{ src = "$LeftLogo"; class = 'legacyImg' } -SelfClosing
                }
                New-HTMLTag -Tag 'div' -Attributes @{ class = 'legacyRightLogo' } {
                    New-HTMLTag -Tag 'img' -Attributes @{ src = "$RightLogo"; class = 'legacyImg' } -SelfClosing
                }
            }
            '<!-- END LOGO -->'
        ) -join ''
    }
}
function New-HTMLMain {
    <#
    .SYNOPSIS
    Defines the body HTML content. By default this is not required, but can be useful when header and footer are used to explicitly define the main content.
 
    .DESCRIPTION
    Defines the body HTML content. By default this is not required, but can be useful when header and footer are used to explicitly define the main content.
 
    .PARAMETER HTMLContent
    Provides ability to specify one or more elements within HTML. Using New-HTMLMain without it, makes no larger sense, as the content will be empty.
 
    .PARAMETER BackgroundColor
    Define a background color of the body element. You can choose from 800 defined colors or provide your own hex color
 
    .PARAMETER Color
    Choose a color of the body element. You can choose from 800 defined colors or provide your own hex color
 
    .PARAMETER FontFamily
    Choose a FontFamily for the body content
 
    .PARAMETER FontSize
    Choose a FontSize for the body content
 
    .EXAMPLE
    New-HTML -TitleText 'My title' -Online -FilePath $PSScriptRoot\Example40-Body.html -Show {
        New-HTMLMain {
            New-HTMLTabStyle -SlimTabs `
                -BorderBottomStyleActive solid -BorderBottomColorActive LightSkyBlue -BackgroundColorActive none `
                -TextColorActive Black -Align left -BorderRadius 0px -RemoveShadow -TextColor Grey -TextTransform capitalize #-FontSize 10pt
 
            New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey
            New-HTMLTab -Name 'First Level Tab - Test 1' -IconBrands acquisitions-incorporated {
                New-HTMLTab -Name '2nd Level Tab - Test 4/1' -IconBrands app-store {
                    New-HTMLSection -HeaderText 'Default Section Style' {
                        New-HTMLTableStyle -Type Header -TextAlign right -TextColor Blue
                        New-HTMLTableStyle -Type Row -TextAlign left -TextColor Grey
                        New-HTMLTable -DataTable $Test1 {
                            New-HTMLTableHeader -Names 'ID', 'HandleCount'
                        } -Filtering #-HideFooter -FilteringLocation Both
                    } -CanCollapse
                }
            }
            New-HTMLTab -Name 'Next' {
 
            }
        } -BackgroundColor Yellow -Color Red -FontSize 12px #-FontFamily 'Arial'
    }
    .EXAMPLE
    New-HTML -TitleText 'This is a test' -FilePath "$PSScriptRoot\Example34_01.html" {
        New-HTMLHeader {
            New-HTMLText -Text "Date of this report $(Get-Date)" -Color Blue -Alignment right
        }
        New-HTMLMain {
            New-HTMLTab -TabName 'Test' {
                New-HTMLSection -HeaderText '0 section' {
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter -Simplify
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                }
            }
            New-HTMLTab -TabName 'Test5' {
                New-HTMLSection -HeaderText '1 section' {
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                        # New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                }
            }
        }
        New-HTMLFooter {
            New-HTMLText -Text "Date of this report $(Get-Date)" -Color Blue -Alignment right
        }
    } -Online -ShowHTML
 
    .NOTES
    General notes
    #>

    [alias('Main')]
    [CmdletBinding()]
    param(
        [scriptblock] $HTMLContent,
        [string] $BackgroundColor,
        [string] $Color,
        [string] $FontFamily,
        [object] $FontSize
    )

    $CssConfiguration = Get-ConfigurationCss -Feature 'Main' -Type 'HeaderAlways'

    $BodyStyle = @{
        'background-color' = ConvertFrom-Color -Color $BackgroundColor
        'color'            = ConvertFrom-Color -Color $Color
        'font-family'      = $FontFamily
        'font-size'        = ConvertFrom-Size -TextSize $FontSize
    }
    Add-ConfigurationCSS -CSS $CssConfiguration -Name 'body' -Inject $BodyStyle

    $InputStyle = @{
        'font-size' = ConvertFrom-Size -TextSize $FontSize
    }
    Add-ConfigurationCSS -CSS $CssConfiguration -Name 'input' -Inject $InputStyle

    if ($HTMLContent) {

        [PSCustomObject] @{
            Type   = 'Main'
            Output = & $HTMLContent
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLMain -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLMain -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-HTMLMap {
    <#
    .SYNOPSIS
    Creates a new HTML map with specified settings.
 
    .DESCRIPTION
    This function creates a new HTML map with customizable settings such as map type, legend options, colors, and titles.
 
    .PARAMETER MapSettings
    A script block containing custom map settings to be applied.
 
    .PARAMETER Map
    Specifies the type of map to be displayed. Valid values are 'Poland', 'Usa_States', 'World_Countries', or 'European_Union'.
 
    .PARAMETER AnchorName
    Specifies the name of the HTML anchor element where the map will be rendered.
 
    .PARAMETER ShowAreaLegend
    Switch parameter to indicate whether to display the area legend on the map.
 
    .PARAMETER AreaTitle
    Specifies the title for the area legend.
 
    .PARAMETER PlotTitle
    Specifies the title for the plot legend.
 
    .PARAMETER ShowPlotLegend
    Switch parameter to indicate whether to display the plot legend on the map.
 
    .PARAMETER FillColor
    Specifies the color to fill the map areas.
 
    .PARAMETER StrokeColor
    Specifies the color of the map area borders.
 
    .PARAMETER StrokeWidth
    Specifies the width of the map area borders.
 
    .EXAMPLE
    New-HTMLMap -Map 'World_Countries' -AnchorName 'Map1' -ShowAreaLegend -AreaTitle 'Country Population' -PlotTitle 'Population Density' -FillColor 'blue' -StrokeColor 'black' -StrokeWidth 2
 
    Creates a new HTML map displaying world countries with area legend showing country population and plot legend showing population density. The map areas are filled with blue color, have black borders, and a border width of 2.
 
    .EXAMPLE
    $CustomSettings = {
        # Custom map settings here
    }
    New-HTMLMap -Map 'Usa_States' -AnchorName 'Map2' -ShowPlotLegend -PlotTitle 'Economic Data' -FillColor 'green' -StrokeColor 'gray' -StrokeWidth 1 -MapSettings $CustomSettings
 
    Creates a new HTML map displaying USA states with plot legend showing economic data. The map areas are filled with green color, have gray borders, and a border width of 1. Custom map settings are applied using the script block $CustomSettings.
 
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $MapSettings,
        [parameter(Mandatory)][ValidateSet('Poland', 'Usa_States', 'World_Countries', 'European_Union')][string] $Map,
        [string] $AnchorName,
        [switch] $ShowAreaLegend,
        [string] $AreaTitle,
        [string] $PlotTitle,
        [switch] $ShowPlotLegend,
        [alias('SliceColor')][string] $FillColor,
        [string] $StrokeColor,
        [nullable[int]] $StrokeWidth
    )
    Enable-HTMLFeature -Feature Raphael, Mapael, Jquery, JQueryMouseWheel, "MapaelMaps_$Map"
    if (-not $AnchorName) {
        $AnchorName = "MapContainer$(Get-RandomStringName -Size 8)"
    }
    $Options = [ordered] @{
        map    = [ordered] @{
            name        = $Map.ToLower()
            defaultArea = [ordered]@{
                attrs = [ordered]@{
                    fill           = ConvertFrom-Color -Color $FillColor
                    stroke         = ConvertFrom-Color -Color $StrokeColor
                    'stroke-width' = $StrokeWidth
                }
            }
        }
        legend = [ordered] @{
            area = [ordered] @{
                title  = $AreaTitle
                slices = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
            }
            plot = [ordered] @{
                title  = $PlotTitle
                slices = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
            }
        }
        areas  = [ordered] @{}
        plots  = [ordered] @{}
    }

    if ($MapSettings) {
        $ExecutedData = $MapSettings.Invoke()
        foreach ($Setting in $ExecutedData) {
            if ($Setting.Type -eq 'MapArea') {
                foreach ($MapArea in $Setting.Configuration.Keys ) {
                    $Options.areas[$MapArea] = $Setting.Configuration[$MapArea]
                }
            } elseif ($Setting.Type -eq 'MapPlot') {
                foreach ($MapPlot in $Setting.Configuration.Keys ) {
                    $Options.plots[$MapPlot] = $Setting.Configuration[$MapPlot]
                }
            } elseif ($Setting.Type -eq 'MapLegendOption') {
                if ($Setting.Configuration.default) {
                    foreach ($Option in $Setting.Configuration.default.Keys) {
                        $Options.legend.default[$Option] = $Setting.Configuration.default[$Option]
                    }
                }
                if ($Setting.Configuration.area) {
                    foreach ($Option in $Setting.Configuration.area.Keys) {
                        $Options.legend.area[$Option] = $Setting.Configuration.area[$Option]
                    }
                }
                if ($Setting.Configuration.plot) {
                    foreach ($Option in $Setting.Configuration.plot.Keys) {
                        $Options.legend.plot[$Option] = $Setting.Configuration.plot[$Option]
                    }
                }
            } elseif ($Setting.Type -eq 'MapLegendAreaSlice') {
                $Options.legend.area.slices.Add($Setting.Configuration)

            } elseif ($Setting.Type -eq 'MapLegendPlotSlice') {
                $Options.legend.plot.slices.Add($Setting.Configuration)
            }
        }
    }

    Remove-EmptyValue -Hashtable $Options -Recursive -Rerun 2

    $OptionsJSON = $Options | ConvertTo-JsonLiteral -Depth 5 -AdvancedReplace @{ '.' = '\.'; '$' = '\$' }

    New-HTMLTag -Tag 'script' {
        "`$(function () { `$(`".$AnchorName`").mapael($OptionsJSON); });"
    }

    New-HTMLTag -Tag 'div' -Attributes @{ class = $AnchorName } {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'map' } {
            $AlternateMapContent
        }
        if ($ShowAreaLegend) {
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'areaLegend' } {
                $AlternateAreaLegendContent
            }
        }
        if ($ShowPlotLegend) {
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'plotLegend' } {
                $AlternatePlotLegendContent
            }
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLMap -ParameterName FillColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLMap -ParameterName StrokeColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLMarkdown {
    <#
    .SYNOPSIS
    Creates HTML content from Markdown text with customizable options.
 
    .DESCRIPTION
    The New-HTMLMarkdown function converts Markdown text to HTML content with various customization options such as strikethrough, emojis, code block styling, table of contents, and more.
 
    .PARAMETER ScriptBlock
    Specifies a script block containing Markdown content to convert.
 
    .PARAMETER FilePath
    Specifies the path to a file containing Markdown content to convert.
 
    .PARAMETER Content
    Specifies an array of Markdown content to convert.
 
    .PARAMETER Id
    Specifies the ID attribute for the HTML container.
 
    .PARAMETER omitExtraWLInCodeBlocks
    Switch parameter to omit extra whitespace in code blocks.
 
    .PARAMETER EnableStrikethrough
    Switch parameter to enable strikethrough styling.
 
    .PARAMETER EnableEmoji
    Switch parameter to enable emoji support.
 
    .PARAMETER EnableBackslashEscapesHTMLTags
    Switch parameter to enable backslash escapes for HTML tags.
 
    .PARAMETER EnableMoreStyling
    Switch parameter to enable additional styling options.
 
    .PARAMETER HeaderLevelStart
    Specifies the starting header level for Markdown content.
 
    .PARAMETER EnableGithubCodeBlocks
    Switch parameter to enable GitHub-style code blocks.
 
    .PARAMETER EnableTaskLists
    Switch parameter to enable task lists in Markdown.
 
    .PARAMETER DisableTables
    Switch parameter to disable table rendering.
 
    .PARAMETER EnableSimpleLineBreaks
    Switch parameter to enable simple line breaks.
 
    .PARAMETER EnableRequireSpaceBeforeHeadingText
    Switch parameter to require space before heading text.
 
    .PARAMETER EnableEncodeEmails
    Switch parameter to enable email encoding.
 
    .PARAMETER EnableOpenLinksInNewWindow
    Switch parameter to open links in a new window.
 
    .PARAMETER EnableBackslashEscapes
    Switch parameter to enable backslash escapes.
 
    .PARAMETER SanitezeHTML
    Switch parameter to sanitize HTML content.
 
    .PARAMETER EnableTOC
    Switch parameter to enable table of contents generation.
 
    .PARAMETER Encoding
    Specifies the encoding for reading the Markdown content file. Default is 'UTF8'.
 
    .EXAMPLE
    New-HTMLMarkdown -FilePath "C:\Markdown\example.md" -EnableStrikethrough -EnableEmoji -EnableTOC
 
    Converts the Markdown content from the file "example.md" to HTML with strikethrough, emojis, and a table of contents.
 
    .EXAMPLE
    $MarkdownContent = @("## Title", "This is a **bold** text.")
    New-HTMLMarkdown -Content $MarkdownContent -EnableMoreStyling -EnableTaskLists
 
    Converts the Markdown content in the array with additional styling and task list support.
    #>

    [CmdletBinding(DefaultParameterSetName = 'FilePath')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ScriptBlock', Position = 0)][scriptblock] $ScriptBlock,
        [Parameter(Mandatory, ParameterSetName = 'FilePath')][string] $FilePath,
        [Parameter(Mandatory, ParameterSetName = 'Content')][Array] $Content,
        #[ValidateSet('original', 'vanilla', 'github')][string] $MarkdownFlavor,
        [string] $Id,
        [switch] $omitExtraWLInCodeBlocks,
        [switch] $EnableStrikethrough,
        [switch] $EnableEmoji,
        [switch] $EnableBackslashEscapesHTMLTags,
        [switch] $EnableMoreStyling,
        [int] $HeaderLevelStart,
        [switch] $EnableGithubCodeBlocks,
        [switch] $EnableTaskLists,
        [switch] $DisableTables,
        [switch] $EnableSimpleLineBreaks,
        [switch] $EnableRequireSpaceBeforeHeadingText,
        [switch] $EnableEncodeEmails,
        [switch] $EnableOpenLinksInNewWindow,
        [switch] $EnableBackslashEscapes,
        [switch] $SanitezeHTML,
        [switch] $EnableTOC,
        [string] $Encoding = 'UTF8'
    )

    $Script:HTMLSchema.Features.MarkdownShowdown = $true

    if ($FilePath) {
        [Array] $Content = Get-Content -LiteralPath $FilePath -Encoding $Encoding
    } elseif ($ScriptBlock) {
        [Array]  $Content = & $ScriptBlock
    }
    if (-not $Content) {
        Write-Warning -Message 'New-HTMLMarkdown - No content provided. Skipping'
        return
    }

    if (-not $Id) {
        $Id = "MarkdownContainer$(Get-RandomStringName -Size 8)"
    }

    $Var = foreach ($C in $Content) {
        $Value = $C.Replace("'", "\'")
        $Value + "\n"
    }
    $JoinedContent = $Var -join ''
    if ($EnableTOC) {
        $JoinedContent = $JoinedContent.Replace('[TOC]', '[toc]')
        $JoinedContent = $JoinedContent.Replace('[toc]', '<p>[toc]</p>')
    }

    $VariableOutput = "outputMarkdown$(Get-RandomStringName -Size 8)"
    $ConverterVariable = "converter$(Get-RandomStringName -Size 8)"
    $TextVariable = "text$(Get-RandomStringName -Size 8)"
    $HtmlVariable = "html$(Get-RandomStringName -Size 8)"
    $HtmlMDVariable = "htmlMD$(Get-RandomStringName -Size 8)"

    $Options = [ordered] @{}
    if ($PSBoundParameters.ContainsKey('omitExtraWLInCodeBlocks')) {
        $Options['omitExtraWLInCodeBlocks'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableStrikethrough')) {
        $Options['strikethrough'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableEmoji')) {
        $Options['emoji'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableMoreStyling')) {
        $Options['moreStyling'] = $true
    }
    if ($PSBoundParameters.ContainsKey('HeaderLevelStart')) {
        $Options['headerLevelStart'] = $HeaderLevelStart
    }
    if ($PSBoundParameters.ContainsKey('EnableGithubCodeBlocks')) {
        $Options['ghCodeBlocks'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableTaskLists')) {
        $Options['tasklists'] = $true
    }
    if ($PSBoundParameters.ContainsKey('DisableTables')) {
        $Options['tables'] = $false
    } else {
        $Options['tables'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableSimpleLineBreaks')) {
        $Options['simpleLineBreaks'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableRequireSpaceBeforeHeadingText')) {
        $Options['requireSpaceBeforeHeadingText'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableEncodeEmails')) {
        $Options['encodeEmails'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableOpenLinksInNewWindow')) {
        $Options['openLinksInNewWindow'] = $true
    }
    if ($PSBoundParameters.ContainsKey('EnableBackslashEscapes')) {
        $Options['backslashEscapesHTMLTags'] = $true
    }

    if ($EnableTOC) {
        $Script:HTMLSchema.Features.MarkdownShowdownTOC = $true
        $Options['extensions'] = @('tocReplaceMe')
    }

    $OptionsJSON = $Options | ConvertTo-Json
    $OptionsJSON = $OptionsJSON -replace '"tocReplaceMe"', 'showdownToc()'

    New-HTMLPanel -Invisible {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'markdown-body'; id = $Id } {}

        $Script = New-HTMLTag -Tag 'script' -Value {
            "let $TextVariable = '$JoinedContent';"
            "const $VariableOutput = document.getElementById(`"$Id`");"
            "var $ConverterVariable = new showdown.Converter($OptionsJSON),"
            "$HtmlVariable = $ConverterVariable.makeHtml($TextVariable);"

            if ($SanitezeHTML) {
                $Script:HTMLSchema.Features.JSXSS = $true
                "$HtmlMDVariable = filterXSS($HtmlVariable);"
                "$VariableOutput.innerHTML = $HtmlMDVariable;"
            } else {
                "$VariableOutput.innerHTML = $HtmlVariable;"
            }
            if ($MarkdownFlavor) {
                "$ConverterVariable.setFlavor('$MarkdownFlavor');"
            }
        } -NewLine
        $Script
    }
}

function New-HTMLMermeidChart {
    <#
    .SYNOPSIS
    Creates a new HTML Mermaid chart.
 
    .DESCRIPTION
    This function creates a new HTML Mermaid chart based on the provided Markdown scriptblock.
 
    .PARAMETER Markdown
    The Markdown scriptblock containing the Mermaid chart definition.
 
    .EXAMPLE
    New-HTMLMermeidChart -Markdown { @"
    graph TD;
        A-->B;
        A-->C;
        B-->D;
        C-->D;
    "@ }
 
    This example creates a simple flowchart with nodes A, B, C, and D connected by arrows.
 
    #>

    [alias('New-HTMLMermeid')]
    [CmdletBinding()]
    param(
        [scriptblock] $Markdown
    )

    $Script:HTMLSchema.Features.Mermaid = $true

    $Data = & $Markdown
    if ($Data -is [string]) {
        $Data = $Data
    } else {
        $Data = $Data -join "`r`n"
    }

    New-HTMLTag -Tag 'div' -Attributes @{ class = 'mermaid' } {
        $Data
    }
}
function New-HTMLNav {
    <#
    .SYNOPSIS
    Creates a new HTML navigation menu with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML navigation menu with various customization options such as navigation links, logo, colors, and menu settings.
 
    .PARAMETER NavigationLinks
    Specifies the script block containing navigation links to be displayed in the menu.
 
    .PARAMETER Logo
    Specifies the path to the logo image to be displayed in the menu.
 
    .PARAMETER LogoLink
    Specifies the URL to link the logo to.
 
    .PARAMETER LogoLinkHome
    Indicates whether the logo should link to the home page.
 
    .PARAMETER DisableBackgroundFading
    Switch to disable background fading when the side navigation drawer is open.
 
    .PARAMETER DisableClickToClose
    Switch to disable closing opened items when the user clicks outside of them.
 
    .PARAMETER DisableNavControls
    Switch to disable navigation controls for adjusting drawer width and height.
 
    .PARAMETER DisableStickyMenubar
    Switch to make the menubar static instead of sticky.
 
    .PARAMETER StartMenuOpen
    Switch to determine if the menu should start open.
 
    .PARAMETER FixedMenu
    Switch to make the menu fixed.
 
    .PARAMETER DisableHamburger
    Switch to disable the hamburger menu icon.
 
    .PARAMETER ResizeContent
    Switch to enable content resizing.
 
    .PARAMETER MenuWidth
    Specifies the width of the menu (default is 270).
 
    .PARAMETER MenuWidthExtended
    Specifies the extended width of the menu (default is 320).
 
    .PARAMETER TopBarBackGroundColor
    Specifies the background color of the top bar.
 
    .PARAMETER TopBarColor
    Specifies the text color of the top bar.
 
    .PARAMETER LeftMenuBackgroundColor
    Specifies the background color of the left menu.
 
    .PARAMETER LeftMenuColor
    Specifies the text color of the left menu.
 
    .EXAMPLE
    New-HTMLNav -NavigationLinks { 'Home', 'About', 'Services' } -Logo 'logo.png' -LogoLink 'https://example.com' -TopBarBackGroundColor 'blue' -TopBarColor 'white' -LeftMenuBackgroundColor 'black' -LeftMenuColor 'white'
 
    Creates a new HTML navigation menu with specified navigation links and custom logo, colors, and menu settings.
 
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $NavigationLinks,
        [string] $Logo,
        [string] $LogoLink,
        [switch] $LogoLinkHome,
        [switch] $DisableBackgroundFading,
        [switch] $DisableClickToClose,
        [switch] $DisableNavControls,
        [switch] $DisableStickyMenubar,
        [switch] $StartMenuOpen,
        [switch] $FixedMenu,
        [switch] $DisableHamburger,
        [switch] $ResizeContent,
        [int] $MenuWidth = 270,
        [int] $MenuWidthExtended = 320,
        [string] $TopBarBackGroundColor,
        [string] $TopBarColor,
        [string] $LeftMenuBackgroundColor,
        [string] $LeftMenuColor
    )
    $Script:HTMLSchema.Features.NavigationMenuHS = $true
    $Script:HTMLSchema.Features.JQuery = $true
    $Script:HTMLSchema.Features.FontsMaterialIcon = $true
    $Script:HTMLSchema.Features.FontsAwesome = $true

    $Script:GlobalSchema.Features.NavigationMenuHS = $true
    $Script:GlobalSchema.Features.JQuery = $true
    $Script:GlobalSchema.Features.FontsMaterialIcon = $true
    $Script:GlobalSchema.Features.FontsAwesome = $true

    $Script:CurrentConfiguration['Features']['Main']['HeaderAlways']['CssInLine']['.main-section']['margin-top'] = '55px'

    $Script:CurrentConfiguration['Features']['NavigationMenuHS']['HeaderAlways']['CssInLine']['.hs-menubar']['background-color'] = ConvertFrom-Color -Color $TopBarBackGroundColor
    $Script:CurrentConfiguration['Features']['NavigationMenuHS']['HeaderAlways']['CssInLine']['.hs-menubar']['color'] = ConvertFrom-Color -Color $TopBarColor
    $Script:CurrentConfiguration['Features']['NavigationMenuHS']['HeaderAlways']['CssInLine']['.hs-navigation']['background-color'] = ConvertFrom-Color -Color $LeftMenuBackgroundColor
    $Script:CurrentConfiguration['Features']['NavigationMenuHS']['HeaderAlways']['CssInLine']['.hs-navigation']['color'] = ConvertFrom-Color -Color $LeftMenuColor

    if ($LogoLinkHome) {
        $LogoLink = "$($Script:GlobalSchema.StorageInformation.FileName).html"
    }

    if ($NavigationLinks) {
        $Output = & $NavigationLinks
        $NavGridItems = [System.Collections.Generic.List[string]]::new()
        $NavLinks = [System.Collections.Generic.List[string]]::new()
        $NavGridMenu = [System.Collections.Generic.List[string]]::new()
        $TopMenu = [System.Collections.Generic.List[string]]::new()
        foreach ($Link in $Output) {
            if ($Link.Type -eq 'NavGridItem') {
                $NavGridItems.Add($Link.Value)
            } elseIf ($Link.Type -eq 'NavLinkItem') {
                $NavLinks.Add($Link.Value)
            } elseif ($Link.Type -eq 'NavGridMenu') {
                $NavGridMenu.Add($Link.Value)
            } elseif ($Link.Type -eq 'TopMenu') {
                $TopMenu.Add($Link.Value)
            }
        }
    }

    $Options = @{
        bgFading          = -not $DisableBackgroundFading.IsPresent 
        outClickToClose   = -not $DisableClickToClose.IsPresent 
        navControls       = -not $DisableNavControls.IsPresent 
        fixedMenubar      = -not $DisableStickyMenubar.IsPresent 
        startMenuOpen     = $StartMenuOpen.IsPresent
        fixedMenu         = $FixedMenu.IsPresent
        disableHamburger  = $DisableHamburger.IsPresent
        resizeContent     = $ResizeContent.IsPresent
        menuWidth         = $MenuWidth
        menuWidthExtended = $menuWidthExtended
    }
    $OptionsJSON = $Options | ConvertTo-Json

    $Navigation = @(

        New-HTMLTag -Tag 'nav' -Attributes @{ class = 'hs-navigation evotec-navigation' } {
            New-HTMLTag -Tag 'header' -Attributes @{ class = 'hs-menubar' } {

                New-HTMLTag -Tag 'div' -Attributes @{ class = 'menu-trigger' } {
                    New-HTMLTag -Tag 'i' -Attributes @{ class = 'zmdi zmdi-menu' }
                }

                New-HTMLTag -Tag 'div' -Attributes @{ class = 'brand-logo' } {
                    New-HTMLTag -Tag 'a' -Attributes @{ href = $LogoLink } {
                        New-HTMLTag -Tag 'img' -Attributes @{ src = $Logo; title = 'PSWriteHTML Logo'; alt = 'PSWriteHTML Logo' }
                    }
                }

                New-HTMLTag -Tag 'div' -Attributes @{ class = 'grid-trigger toggle'; 'data-reveal' = '.grid-items' } {
                    New-HTMLTag -Tag 'i' -Attributes @{ class = 'zmdi zmdi-view-module' }
                }
                New-HTMLTag -Tag 'div' -Attributes @{ class = 'hs-user toggle'; 'data-reveal' = '.user-info' } {
                    New-HTMLTag -Tag 'img' -Attributes @{ src = 'https://evotec.xyz/wp-content/uploads/2021/04/PrzemyslawKlysAndKulkozaurr.jpg'; alt = 'Evotec' } -NoClosing
                }
                New-HTMLTag -Tag 'div' -Attributes @{ class = 'more-trigger toggle'; 'data-reveal' = '.user-penal' } {
                    New-HTMLTag -Tag 'i' -Attributes @{ class = 'zmdi zmdi-more-vert' }
                }
            }

            New-HTMLTag -Tag 'section' -Attributes @{ class = 'box-model' } {
                New-HTMLTag -Tag 'ul' -Attributes @{ class = 'user-penal' } {
                    $NavGridMenu
                }
                if ($NavGridItems) {
                    New-HTMLTag -Tag 'ul' -Attributes @{ class = 'grid-items' } {
                        $NavGridItems
                    }
                }
            }

            New-HTMLTag -Tag 'ul' -Attributes @{ class = 'nav-links' } {
                if ($NavLinks) {
                    $NavLinks
                }
            }

            New-HTMLTag -Tag 'script' {
                "`$(document).ready(function () {"
                " `$('.hs-menubar').hsMenu($OptionsJSON);"
                "});"
            }
        }

    )
    [PSCustomObject] @{
        Type   = 'Navigation'
        Output = $Navigation
    }
}
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName TopBarBackGroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName TopBarColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName LeftMenuBackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName LeftMenuColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLNavFloat {
    <#
    .SYNOPSIS
    Creates a new HTML floating navigation menu with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML floating navigation menu with various customization options such as navigation links, title, tagline, colors, and button styling for hover effects.
 
    .PARAMETER NavigationLinks
    Specifies the script block containing navigation links to be displayed in the menu.
 
    .PARAMETER Title
    Specifies the title of the floating navigation menu.
 
    .PARAMETER SubTitle
    Specifies the subtitle or tagline of the floating navigation menu.
 
    .PARAMETER TitleColor
    Specifies the color of the title text.
 
    .PARAMETER TaglineColor
    Specifies the color of the tagline text.
 
    .PARAMETER ButtonLocationTop
    Specifies the top position of the floating navigation menu button.
 
    .PARAMETER ButtonLocationLeft
    Specifies the left position of the floating navigation menu button.
 
    .PARAMETER ButtonLocationRight
    Specifies the right position of the floating navigation menu button.
 
    .PARAMETER ButtonLocationBottom
    Specifies the bottom position of the floating navigation menu button.
 
    .PARAMETER ButtonColor
    Specifies the color of the floating navigation menu button.
 
    .PARAMETER ButtonColorBackground
    Specifies the background color of the floating navigation menu button.
 
    .PARAMETER ButtonColorOnHover
    Specifies the color of the floating navigation menu button on hover.
 
    .PARAMETER ButtonColorBackgroundOnHover
    Specifies the background color of the floating navigation menu button on hover.
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $NavigationLinks,
        [string] $Title,
        [alias('SubTitle')][string] $Tagline,
        [string] $TitleColor,
        [string] $TaglineColor,
        [object] $ButtonLocationTop,
        [object] $ButtonLocationLeft,
        [object] $ButtonLocationRight,
        [object] $ButtonLocationBottom,
        [string] $ButtonColor,
        [string] $ButtonColorBackground,
        [string] $ButtonColorOnHover,
        [string] $ButtonColorBackgroundOnHover
    )

    $Script:HTMLSchema.Features.NavigationFloat = $true
    $Script:HTMLSchema.Features.JQuery = $true
    $Script:HTMLSchema.Features.FontsMaterialIcon = $true
    $Script:HTMLSchema.Features.FontsAwesome = $true

    $Script:GlobalSchema.Features.NavigationFloat = $true
    $Script:GlobalSchema.Features.JQuery = $true
    $Script:GlobalSchema.Features.FontsMaterialIcon = $true
    $Script:GlobalSchema.Features.FontsAwesome = $true

    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-trigger']['top'] = $ButtonLocationTop
    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-trigger']['left'] = $ButtonLocationLeft
    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-trigger']['right'] = $ButtonLocationRight
    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-trigger']['bottom'] = $ButtonLocationBottom
    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-trigger']['background-color'] = ConvertFrom-Color -Color $ButtonColorBackground
    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-trigger']['color'] = ConvertFrom-Color -Color $ButtonColor

    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-widget.top-header h2']['color'] = ConvertFrom-Color -Color $TitleColor

    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.top-header .tagline']['color'] = ConvertFrom-Color -Color $TaglineColor

    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-trigger:hover']['background-color'] = ConvertFrom-Color -Color $ButtonColorBackgroundOnHover
    $Script:CurrentConfiguration['Features']['NavigationFloat']['HeaderAlways']['CssInLine']['.penal-trigger:hover']['color'] = ConvertFrom-Color -Color $ButtonColorOnHover

    if ($NavigationLinks) {
        $Output = & $NavigationLinks
    }

    $Navigation = @(
        New-HTMLTag -Tag 'nav' -Attributes @{ class = 'evotec-navigation' } {

            New-HTMLTag -Tag 'button' -Attributes @{ class = 'penal-trigger' }

            New-HTMLTag -Tag 'section' -Attributes @{ class = 'side-penal' } {
                New-HTMLTag -Tag 'div' -Attributes @{class = 'penal-widget top-header' } {
                    New-HTMLTag -Tag 'h2' {
                        $Title
                    }
                    New-HTMLTag -Tag 'i' -Attributes @{ class = 'tagline' } {
                        $Tagline
                    }

                    if ($Output) {
                        $Output
                    }
                }
            }
        }
    )

    [PSCustomObject] @{
        Type   = 'Navigation'
        Output = $Navigation
    }

}
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName ButtonColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName ButtonColorBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName ButtonColorBackgroundOnHover -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName ButtonColorOnHover -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName TitleColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavFloat -ParameterName TaglineColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLNavTop {
    <#
    .SYNOPSIS
    Creates a top navigation bar in HTML format with customizable options.
 
    .DESCRIPTION
    This function generates a top navigation bar in HTML format with customizable options such as navigation links, logo, colors, and more.
 
    .PARAMETER NavigationLinks
    Specifies the script block containing the navigation links to be displayed.
 
    .PARAMETER Logo
    Specifies the path to the logo image.
 
    .PARAMETER LogoLink
    Specifies the URL to link the logo to.
 
    .PARAMETER LogoLinkHome
    Indicates whether the logo should link to the home page.
 
    .PARAMETER MenuItemsWidth
    Specifies the width of the menu items.
 
    .PARAMETER MenuColor
    Specifies the color of the menu text.
 
    .PARAMETER MenuColorBackground
    Specifies the background color of the menu.
 
    .PARAMETER HomeColor
    Specifies the color of the home link text.
 
    .PARAMETER HomeColorBackground
    Specifies the background color of the home link.
 
    .PARAMETER HomeLink
    Specifies the URL for the home link.
 
    .PARAMETER HomeLinkHome
    Indicates whether the home link should link to the home page.
 
    .PARAMETER Convert
    Switch parameter to indicate if conversion is needed.
 
    .EXAMPLE
    New-HTMLNavTop -NavigationLinks {
        "Link 1", "Link 2", "Link 3"
    } -Logo "logo.png" -LogoLink "https://example.com" -LogoLinkHome -MenuItemsWidth '250px' -MenuColor 'Blue' -MenuColorBackground 'White' -HomeColor 'Red' -HomeColorBackground 'White' -HomeLink "https://example.com/home" -HomeLinkHome
    Creates a top navigation bar with specified navigation links, logo, colors, and home link.
 
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $NavigationLinks,
        [string] $Logo,
        [string] $LogoLink,
        [switch] $LogoLinkHome,

        [object] $MenuItemsWidth = '200px',

        [string] $MenuColor = 'PacificBlue',
        [string] $MenuColorBackground = 'Black',
        [string] $HomeColor = 'PacificBlue',
        [string] $HomeColorBackground = 'Black',
        [string] $HomeLink,
        [switch] $HomeLinkHome,
        [Parameter(DontShow)][switch] $Convert
    )
    if (-not $Convert) {
        $Script:HTMLSchema.Features.NavigationMenuDropdown = $true
        $Script:HTMLSchema.Features.AnimateToolkit = $true
        $Script:HTMLSchema.Features.JQuery = $true
        $Script:HTMLSchema.Features.FontsMaterialIcon = $true
        $Script:HTMLSchema.Features.FontsAwesome = $true

        $Script:GlobalSchema.Features.NavigationMenuDropdown = $true
        $Script:GlobalSchema.Features.AnimateToolkit = $true
        $Script:GlobalSchema.Features.JQuery = $true
        $Script:GlobalSchema.Features.FontsMaterialIcon = $true
        $Script:GlobalSchema.Features.FontsAwesome = $true

        $Script:CurrentConfiguration['Features']['Main']['HeaderAlways']['CssInLine']['.main-section']['margin-top'] = '55px'

        $Script:CurrentConfiguration['Features']['NavigationMenuDropdown']['HeaderAlways']['CssInLine']['@media only screen and (min-width: 480px)']['.menu-items']['width'] = $MenuItemsWidth
        $Script:CurrentConfiguration['Features']['NavigationMenuDropdown']['HeaderAlways']['CssInLine']['@media only screen and (min-width: 480px)']['.has-child ul']['width'] = $MenuItemsWidth

        if ($LogoLinkHome) {
            $LogoLink = "$($Script:GlobalSchema.StorageInformation.FileName)$($Script:GlobalSchema.StorageInformation.Extension)"
        }
        if ($HomeLinkHome) {
            $HomeHref = "$($Script:GlobalSchema.StorageInformation.FileName)$($Script:GlobalSchema.StorageInformation.Extension)"
        } elseif ($HomeLink) {
            $HomeHref = $HomeLink
        } else {
            $HomeHref = '#'
        }
    }

    if ($NavigationLinks) {
        $Output = & $NavigationLinks
        $TopMenu = [System.Collections.Generic.List[string]]::new()
        foreach ($Link in $Output) {
            if ($Link.Type -eq 'TopMenu') {
                $TopMenu.Add($Link.Value)
            }
        }
    }

    $Options = @{
        skin     = @{
            'color'            = ConvertFrom-Color -Color $MenuColor
            'background-color' = ConvertFrom-Color -Color $MenuColorBackground
        }
        skinHome = @{
            'color'            = ConvertFrom-Color -Color $HomeColor
            'background-color' = ConvertFrom-Color -Color $HomeColorBackground
        }
    }
    $OptionsJSON = $Options | ConvertTo-Json

    $IconSolid = 'home'
    $Navigation = @(

        New-HTMLTag -Tag 'nav' -Attributes @{ class = 'codehim-dropdown evotec-navigation' } {
            New-HTMLTag -Tag 'ul' -Attributes @{ class = 'dropdown-items' } {
                New-HTMLTag -Tag 'li' -Attributes @{ class = 'home-link' } {
                    New-HTMLTag -Tag 'a' -Attributes @{ href = $HomeHref } {
                        New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                    }
                }

                if ($TopMenu) {
                    $TopMenu
                }
            }
            if ($Logo) {

                New-HTMLTag -Tag 'div' -Attributes @{ class = 'home-logo' } {
                    New-HTMLTag -Tag 'a' -Attributes @{ href = $LogoLink } {
                        New-HTMLTag -Tag 'img' -Attributes @{ src = $Logo; title = 'PSWriteHTML Logo'; alt = 'PSWriteHTML Logo' } -NoClosing
                    }
                }
            }
            New-HTMLTag -Tag 'script' {
                "`$(document).ready(function () {"
                " `$('.codehim-dropdown').CodehimDropdown($OptionsJSON);"
                "});"
            }
        }
    )
    [PSCustomObject] @{
        Type   = 'Navigation'
        Output = $Navigation
    }
}

Register-ArgumentCompleter -CommandName New-HTMLNavTop -ParameterName HomeColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavTop -ParameterName HomeColorBackground -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavTop -ParameterName MenuColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavTop -ParameterName MenuColorBackground -ScriptBlock $Script:ScriptBlockColors
function New-HTMLOrgChart {
    <#
    .SYNOPSIS
    Creates an HTML organizational chart based on the provided nodes and options.
 
    .DESCRIPTION
    This function generates an interactive HTML organizational chart based on the specified nodes and customization options. Users can define the layout direction, initial visibility level, vertical alignment, and various interactive features like panning, zooming, dragging, and exporting.
 
    .PARAMETER ChartNodes
    Specifies the nodes to be displayed on the chart. This parameter should contain a script block defining the organizational structure.
 
    .PARAMETER Direction
    Specifies the layout direction of the chart. Available values are "TopToBottom" (default), "BottomToTop", "LeftToRight", and "RightToLeft".
 
    .PARAMETER VisileLevel
    Sets the initial visibility level of the org chart.
 
    .PARAMETER VerticalLevel
    Aligns the nodes vertically starting from the specified level.
 
    .PARAMETER ToggleSiblings
    Enables the ability to show/hide left and right sibling nodes by clicking arrows.
 
    .PARAMETER NodeTitle
    Sets the text content of the title section of each org chart node.
 
    .PARAMETER Pan
    Enables panning the org chart by mouse drag and drop.
 
    .PARAMETER Zoom
    Enables zooming in and out of the org chart using the mouse wheel.
 
    .PARAMETER ZoomInLimit
    Sets the maximum zoom-in limit.
 
    .PARAMETER ZoomOutLimit
    Sets the maximum zoom-out limit.
 
    .PARAMETER Draggable
    Enables dragging and dropping nodes within the org chart. Note: This feature is not supported on Internet Explorer.
 
    .PARAMETER AllowExport
    Enables the export button for the org chart.
 
    .PARAMETER ExportFileName
    Specifies the filename when exporting the org chart as an image.
 
    .PARAMETER ExportExtension
    Specifies the format for exporting the org chart. Available values are "png" and "pdf".
 
    .PARAMETER ChartID
    Specifies a custom ID for the org chart. If not provided, a random ID will be generated.
 
    .EXAMPLE
    New-HTML {
        New-HTMLOrgChart {
            New-OrgChartNode -Name 'CEO' -Title 'Chief Executive Officer' {
                New-OrgChartNode -Name 'Manager' -Title 'Manager'
                New-OrgChartNode -Name 'Supervisor' -Title 'Supervisor'
                New-OrgChartNode -Name 'Employee' -Title 'Employee' {
                    New-OrgChartNode -Name 'Assistant' -Title 'Assistant'
                }
            }
        } -AllowExport -ExportExtension pdf -Draggable
    } -FilePath $PSScriptRoot\OrgChart.html -ShowHTML -Online
 
    .NOTES
    This function requires the PSWriteHTML module to be installed. For more information, visit: https://github.com/EvotecIT/PSWriteHTML
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $ChartNodes,
        [ValidateSet("TopToBottom", "BottomToTop", "LeftToRight", "RightToLeft")][string] $Direction,
        [int] $VisileLevel,
        [int] $VerticalLevel,
        [string] $NodeTitle,
        [switch] $ToggleSiblings,
        [switch] $Pan,
        [switch] $Zoom,
        [double] $ZoomInLimit,
        [double] $ZoomOutLimit,
        [switch] $Draggable,
        [switch] $AllowExport,
        [string] $ExportFileName = 'PSWriteHTML-OrgChart',
        [ValidateSet('png', 'pdf')] $ExportExtension = 'png',
        [string] $ChartID
    )

    $DirectionDictionary = @{
        "TopToBottom" = 't2b'
        "BottomToTop" = 'b2t'
        "LeftToRight" = 'l2r'
        "RightToLeft" = 'r2l'
    }
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.Jquery = $true
    $Script:HTMLSchema.Features.ChartsOrg = $true
    if ($ExportExtension -eq 'png' -and $AllowExport) {
        $Script:HTMLSchema.Features.ES6Promise = $true
        $Script:HTMLSchema.Features.ChartsOrgExportPNG = $true
    }
    if ($ExportExtension -eq 'pdf' -and $AllowExport) {
        $Script:HTMLSchema.Features.ES6Promise = $true
        $Script:HTMLSchema.Features.ChartsOrgExportPDF = $true
        $Script:HTMLSchema.Features.ChartsOrgExportPNG = $true
    }

    if (-not $ChartID) {
        $ChartID = "OrgChart-$(Get-RandomStringName -Size 8 -LettersOnly)"
    }

    if ($ChartNodes) {
        $DataSource = & $ChartNodes
    }

    $OrgChart = [ordered] @{
        data                = $DataSource
        nodeContent         = 'title'
        exportButton        = $AllowExport.IsPresent
        exportFileName      = $ExportFileName
        exportFileextension = $ExportExtension
    }
    if ($NodeTitle) {
        $OrgChart['nodeTitle'] = $NodeTitle
    }
    if ($Direction) {
        $OrgChart['direction'] = $DirectionDictionary[$Direction]
    }
    if ($Draggable) {
        $OrgChart['draggable'] = $Draggable.IsPresent
    }
    if ($VisileLevel) {

        $OrgChart['visibleLevel'] = $VisileLevel
    }
    if ($VerticalLevel) {

        $OrgChart['verticalLevel'] = $VerticalLevel
    }
    if ($ToggleSiblings) {

        $OrgChart['toggleSiblingsResp'] = $ToggleSiblings.IsPresent
    }
    if ($Pan) {

        $OrgChart['pan'] = $Pan.IsPresent
    }
    if ($Zoom) {

        $OrgChart['zoom'] = $Zoom.IsPresent
        if ($ZoomInLimit) {
            $OrgChart['zoominLimit'] = $ZoomInLimit
        }
        if ($ZoomOutLimit) {
            $OrgChart['zoomoutLimit'] = $ZoomOutLimit
        }
    }
    $JsonOrgChart = $OrgChart | ConvertTo-Json -Depth 100

    New-HTMLTag -Tag 'script' {
        "`$(function () {"
        "`$(`"#$ChartID`").orgchart($JsonOrgChart);"
        "});"
    }
    New-HTMLTag -Tag 'div' -Attributes @{ id = $ChartID; class = 'orgchartWrapper flexElement' }
}
function New-HTMLPage {
    <#
    .SYNOPSIS
    Creates a new HTML page with specified content, name, title, file path, and ID.
 
    .DESCRIPTION
    This function creates a new HTML page with the provided content and metadata. It generates a unique GUID for the page and stores it in the global schema. The function returns a custom object representing the page.
 
    .PARAMETER PageContent
    The script block containing the HTML content of the page.
 
    .PARAMETER Name
    The name of the HTML page.
 
    .PARAMETER Title
    The title of the HTML page.
 
    .PARAMETER FilePath
    The file path where the HTML page will be saved.
 
    .PARAMETER ID
    The ID of the HTML page.
 
    .EXAMPLE
    New-HTMLPage -PageContent { "<html><body><h1>Hello, World!</h1></body></html>" } -Name "index" -Title "Welcome Page" -FilePath "C:\Pages\index.html" -ID "12345"
 
    Creates a new HTML page with a simple "Hello, World!" content, named "index", titled "Welcome Page", saved at "C:\Pages\index.html", and with ID "12345".
 
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $PageContent,
        [Parameter(Mandatory)][string] $Name,
        [string] $Title,
        [string] $FilePath,
        [string] $ID
    )

    if ($PageContent) {
        $GUID = "$Name_$([guid]::NewGuid().Guid)"
        $Script:GlobalSchema['Pages'][$GUID] = New-DefaultSettings

        $Script:HTMLSchema['PagesCurrent'] = $GUID
        $Script:HTMLSchema = $Script:GlobalSchema['Pages'][$GUID]

        [PSCustomObject] @{
            Type     = 'Page'
            Output   = & $PageContent
            Name     = $Name
            Guid     = $GUID
            FilePath = $FilePath
            Title    = $Title
            ID       = $ID
        }
    }
}
Function New-HTMLPanel {
    <#
    .SYNOPSIS
    Creates a new HTML panel with customizable styling options.
 
    .DESCRIPTION
    The New-HTMLPanel function creates a new HTML panel with various styling options such as background color, width, margin, alignment, and more.
 
    .PARAMETER Content
    Specifies the content of the HTML panel as a ScriptBlock.
 
    .PARAMETER BackgroundColor
    Specifies the background color of the HTML panel.
 
    .PARAMETER Invisible
    Indicates whether the panel should be invisible.
 
    .PARAMETER Width
    Specifies the width of the panel.
 
    .PARAMETER Margin
    Specifies the margin of the panel.
 
    .PARAMETER AlignContentText
    Specifies the alignment of the content within the panel.
 
    .PARAMETER BorderRadius
    Specifies the border radius of the panel.
 
    .PARAMETER AnchorName
    Specifies the anchor name of the panel.
 
    .PARAMETER StyleSheetsConfiguration
    Specifies the style sheets configuration for the panel.
 
    .EXAMPLE
    New-HTMLPanel -Content { "This is the content of the panel" } -BackgroundColor "lightblue" -Width "50%" -Margin "10px" -AlignContentText "center" -BorderRadius "5px" -AnchorName "myPanel" -StyleSheetsConfiguration @{ Panel = "customPanel" }
 
    Creates a new HTML panel with specified content, background color, width, margin, alignment, border radius, anchor name, and custom style sheet configuration.
 
    #>

    [alias('New-HTMLColumn', 'Panel')]
    [CmdletBinding()]
    param (
        [Parameter(Position = 0)][ValidateNotNull()][ScriptBlock] $Content,
        [alias('BackgroundShade')][string]$BackgroundColor,
        [switch] $Invisible,
        [alias('flex-basis')][string] $Width,
        [string] $Margin,

        [string][ValidateSet('center', 'left', 'right', 'justify')] $AlignContentText,
        [ValidateSet('0px', '5px', '10px', '15px', '20px', '25px')][string] $BorderRadius,
        [string] $AnchorName,
        [System.Collections.IDictionary] $StyleSheetsConfiguration
    )
    $Script:HTMLSchema.Features.Main = $true
    $Script:HTMLSchema.Features.MainFlex = $true

    if (-not $StyleSheetsConfiguration) {
        $Script:HTMLSchema.Features.DefaultPanel = $true
        $StyleSheetsConfiguration = [ordered] @{
            Panel = "defaultPanel"
        }
    }

    Remove-DotsFromCssClass -Css $StyleSheetsConfiguration
    if (-not $AnchorName) {
        $AnchorName = "anchor-$(Get-RandomStringName -Size 7)"
    }

    $PanelStyle = [ordered] @{
        "background-color" = ConvertFrom-Color -Color $BackgroundColor
        'border-radius'    = $BorderRadius
        'text-align'       = $AlignContentText 
    }
    if ($Invisible) {

        $StyleSheetsConfiguration.Panel = ''
        [string] $Class = "flexPanel overflowHidden $($StyleSheetsConfiguration.Panel)"
    } elseif ($Width -or $Margin) {
        $Attributes = @{
            'flex-basis' = if ($Width) {
                $Width } else {
                '100%' }
            'margin'     = if ($Margin) {
                $Margin }
        }
        [string] $ClassName = "defaultPanel$(Get-RandomStringName -Size 8 -LettersOnly)"
        $Css = ConvertTo-LimitedCSS -ClassName $ClassName -Attributes $Attributes -Group
        $Script:HTMLSchema.CustomHeaderCSS[$AnchorName] = $Css
        [string] $Class = "flexPanel overflowHidden $ClassName"
    } else {
        [string] $Class = "flexPanel overflowHidden $($StyleSheetsConfiguration.Panel)"
    }

    New-HTMLTag -Tag 'div' -Attributes @{ id = $AnchorName; class = $Class; style = $PanelStyle } {
        if ($Content) {
            Invoke-Command -ScriptBlock $Content
        }
    }

}

Register-ArgumentCompleter -CommandName New-HTMLPanel -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLPanelStyle {
    <#
    .SYNOPSIS
    Creates a new HTML panel style with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML panel style with the ability to set the border radius, remove shadow, and request configuration settings.
 
    .EXAMPLE
    New-HTMLPanelStyle -BorderRadius '10px' -RemoveShadow
    Creates a new HTML panel style with a border radius of 10px and removes the shadow.
 
    .EXAMPLE
    New-HTMLPanelStyle -BorderRadius '15px' -RequestConfiguration
    Creates a new HTML panel style with a border radius of 15px and requests the configuration settings.
 
    .PARAMETER BorderRadius
    Specifies the border radius for the HTML panel. Valid values are '0px', '5px', '10px', '15px', '20px', '25px'.
 
    .PARAMETER RemoveShadow
    Indicates whether to remove the shadow from the HTML panel.
 
    .PARAMETER RequestConfiguration
    Specifies whether to request the configuration settings for the HTML panel.
 
    .NOTES
    File Name : New-HTMLPanelStyle.ps1
    Prerequisite : This function requires the Get-ConfigurationCss and related functions.
    #>

    [alias('New-HTMLPanelOption', 'New-PanelOption', "PanelOption", 'New-PanelStyle', 'PanelStyle')]
    [cmdletBinding()]
    param(
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('0px', '5px', '10px', '15px', '20px', '25px')][string] $BorderRadius,
        [Parameter(ParameterSetName = 'Manual')][switch] $RemoveShadow,
        [Parameter(ParameterSetName = 'Manual')][switch] $RequestConfiguration
    )

    $CssConfiguration = Get-ConfigurationCss -Feature 'DefaultPanel' -Type 'HeaderAlways'

    $StyleSheetsConfiguration = [ordered] @{
        Panel = ".defaultPanel"
    }

    if ($RequestConfiguration) {
        $RequestedConfiguration = New-RequestCssConfiguration -Pair $StyleSheetsConfiguration -CSSConfiguration $CssConfiguration -Feature 'Inject' -Type 'HeaderAlways'
        $StyleSheetsConfiguration = $RequestedConfiguration.StyleSheetConfiguration
        $CssConfiguration = $RequestedConfiguration.CSSConfiguration
    }

    if ($RemoveShadow) {
        Remove-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.Panel -Property 'box-shadow'
    }
    Add-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.Panel -Inject @{ 'border-radius' = $BorderRadius }

    if ($RequestConfiguration) {

        return $StyleSheetsConfiguration
    }
}
function New-HTMLQRCode {
    <#
    .SYNOPSIS
    Creates an HTML QR code with specified parameters.
 
    .DESCRIPTION
    This function generates an HTML QR code based on the provided parameters. It allows customization of the QR code appearance and content.
 
    .PARAMETER Link
    The link or content to be encoded in the QR code.
 
    .PARAMETER Width
    The width of the QR code.
 
    .PARAMETER Height
    The height of the QR code.
 
    .PARAMETER Title
    The title or alt text for the QR code.
 
    .PARAMETER TitleColor
    The color of the title text.
 
    .PARAMETER Logo
    The path to the logo image to be embedded in the QR code.
 
    .PARAMETER LogoWidth
    The width of the logo image.
 
    .PARAMETER LogoHeight
    The height of the logo image.
 
    .PARAMETER LogoInline
    Indicates whether the logo should be embedded inline in the QR code.
 
    .EXAMPLE
    New-HTMLQRCode -Link "https://example.com" -Width 200 -Height 200 -Title "Example QR Code" -TitleColor "black" -Logo "C:\logo.png" -LogoWidth 50 -LogoHeight 50 -LogoInline
    Creates a QR code with a specified link, dimensions, title, color, and logo embedded inline.
 
    .EXAMPLE
    New-HTMLQRCode -Link "tel:1234567890" -Width 150 -Height 150 -Title "Contact Number" -TitleColor "blue" -Logo "C:\company_logo.png" -LogoWidth 30 -LogoHeight 30
    Generates a QR code for a phone number with custom dimensions, title, color, and logo.
 
    #>

    [cmdletBinding()]
    param(
        [string] $Link,
        [object] $Width,
        [object] $Height,
        [string] $Title,
        [string] $TitleColor,
        [string] $Logo,
        [object] $LogoWidth,
        [object] $LogoHeight,
        [switch] $LogoInline
    )
    $Script:HTMLSchema.Features.QR = $true

    if (-not $AnchorName) {
        $AnchorName = "QrCode$(Get-RandomStringName -Size 8)"
    }
    if ($LogoInline) {

        $LogoImage = Convert-Image -Image $Logo -Cache:(-not $DisableCache)
    } else {
        $LogoImage = $Logo
    }

    New-HTMLTag -Tag 'div' -Attributes @{ Id = $AnchorName; class = 'qrcode flexElement' }

    $Options = @{
        text       = $Link
        width      = ConvertTo-Size -Size $Width
        height     = ConvertTo-Size -Size $Height
        title      = $Title
        logo       = $LogoImage
        logoWidth  = ConvertTo-Size -Size $LogoWidth
        logoHeight = ConvertTo-Size -Size $LogoHeight
        titleColor = ConvertFrom-Color -Color $TitleColor
    }
    Remove-EmptyValue -Hashtable $Options -Recursive
    $OptionsJson = $Options | ConvertTo-Json

    $ScriptBottom = New-HTMLTag -Tag 'script' -Value {
        "var options = $OptionsJson;"
        "new QRCode(document.getElementById(`"$AnchorName`"), options);"
    }
    Add-HTMLScript -Placement Footer -Content $ScriptBottom -SkipTags
}

Register-ArgumentCompleter -CommandName New-HTMLQRCode -ParameterName TitleColor -ScriptBlock $Script:ScriptBlockColors
Function New-HTMLSection {
    <#
    .SYNOPSIS
    Creates a new HTML section with customizable styling options.
 
    .DESCRIPTION
    The New-HTMLSection function creates a new HTML section with various customizable styling options such as text alignment, background color, flexbox properties, and more.
 
    .PARAMETER Content
    Specifies the content of the HTML section as a ScriptBlock.
 
    .PARAMETER HeaderText
    Specifies the header text of the section.
 
    .PARAMETER HeaderTextColor
    Specifies the text color of the header.
 
    .PARAMETER HeaderTextSize
    Specifies the text size of the header.
 
    .PARAMETER HeaderTextAlignment
    Specifies the text alignment of the header. Valid values are 'center', 'left', 'right', 'justify'.
 
    .PARAMETER HeaderBackGroundColor
    Specifies the background color of the header.
 
    .PARAMETER BackgroundColor
    Specifies the background color of the section.
 
    .PARAMETER CanCollapse
    Indicates whether the section can be collapsed.
 
    .PARAMETER IsHidden
    Indicates whether the section is initially hidden.
 
    .PARAMETER Collapsed
    Indicates whether the section is collapsed.
 
    .PARAMETER Height
    Specifies the height of the section.
 
    .PARAMETER Width
    Specifies the width of the section. Default value is '100%'.
 
    .PARAMETER Invisible
    Indicates whether the section is invisible.
 
    .PARAMETER Margin
    Specifies the margin of the section.
 
    .PARAMETER Wrap
    Specifies the flex-wrap property. Valid values are 'wrap', 'nowrap', 'wrap-reverse'.
 
    .PARAMETER Direction
    Specifies the flex-direction property. Valid values are 'row', 'row-reverse', 'column', 'column-reverse'.
 
    .PARAMETER AlignContent
    Specifies the align-content property. Valid values are 'flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch'.
 
    .PARAMETER AlignItems
    Specifies the align-items property. Valid values are 'stretch', 'flex-start', 'flex-end', 'center', 'baseline'.
 
    .PARAMETER JustifyContent
    Specifies the justify-content property. Valid values are 'flex-start', 'flex-end', 'center'.
 
    .PARAMETER BorderRadius
    Specifies the border radius of the section. Valid values are '0px', '5px', '10px', '15px', '20px', '25px'.
 
    .PARAMETER AnchorName
    Specifies the anchor name for the section.
 
    .PARAMETER StyleSheetsConfiguration
    Specifies the configuration for style sheets.
 
    .EXAMPLE
    New-HTMLSection -Content { "This is the content of the section." } -HeaderText "Section Title" -HeaderTextColor "blue" -HeaderTextSize "20px" -HeaderTextAlignment "center" -HeaderBackGroundColor "lightgray" -BackgroundColor "white" -CanCollapse -Height "200px" -Width "50%" -Wrap "wrap" -Direction "row" -AlignContent "center" -AlignItems "center" -JustifyContent "flex-start" -BorderRadius "10px" -AnchorName "section1" -StyleSheetsConfiguration @{ Section = 'customSection'; SectionText = 'customSectionText' }
 
    .EXAMPLE
    $content = {
        "This is a sample content."
    }
    New-HTMLSection -Content $content -HeaderText "Sample Section" -HeaderTextColor "red" -HeaderTextSize "18px" -HeaderTextAlignment "left" -HeaderBackGroundColor "lightblue" -BackgroundColor "lightyellow" -Height "150px" -Wrap "nowrap" -Direction "column" -AlignContent "flex-start" -AlignItems "flex-start" -JustifyContent "center" -BorderRadius "5px" -AnchorName "sampleSection" -StyleSheetsConfiguration @{ Section = 'customSection'; SectionText = 'customSectionText' }
    #>

    [alias('New-HTMLContent', 'Section')]
    [CmdletBinding()]
    Param (
        [Parameter(Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace"),
        [alias('Name', 'Title')][Parameter(Mandatory = $false)][string]$HeaderText,
        [alias('TextColor')][string]$HeaderTextColor,
        [alias('TextSize')][string] $HeaderTextSize,
        [alias('TextAlignment')][string][ValidateSet('center', 'left', 'right', 'justify')] $HeaderTextAlignment,
        [alias('TextBackGroundColor')][string]$HeaderBackGroundColor,
        [alias('BackgroundShade')][string]$BackgroundColor,
        [alias('Collapsable')][Parameter(Mandatory = $false)][switch] $CanCollapse,
        [switch] $IsHidden,
        [switch] $Collapsed,
        [object] $Height,
        [object] $Width = '100%',
        [switch] $Invisible,
        [object] $Margin,

        [string][ValidateSet('wrap', 'nowrap', 'wrap-reverse')] $Wrap,
        [string][ValidateSet('row', 'row-reverse', 'column', 'column-reverse')] $Direction,
        [string][ValidateSet('flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch')] $AlignContent,
        [string][ValidateSet('stretch', 'flex-start', 'flex-end', 'center', 'baseline')] $AlignItems,
        [string][ValidateSet('flex-start', 'flex-end', 'center')] $JustifyContent,

        [ValidateSet('0px', '5px', '10px', '15px', '20px', '25px')][string] $BorderRadius,

        [string] $AnchorName,
        [System.Collections.IDictionary] $StyleSheetsConfiguration
    )
    $Script:HTMLSchema.Features.Main = $true
    $Script:HTMLSchema.Features.MainFlex = $true

    if (-not $StyleSheetsConfiguration) {
        $StyleSheetsConfiguration = [ordered] @{
            Section                 = 'defaultSection'
            SectionText             = 'defaultSectionText'
            SectionHead             = "defaultSectionHead"
            SectionContent          = 'defaultSectionContent'
            SectionContentInvisible = 'defaultSectionContentInvisible'
        }
    }

    Remove-DotsFromCssClass -Css $StyleSheetsConfiguration
    if (-not $AnchorName) {
        $AnchorName = "anchor-$(Get-RandomStringName -Size 7)"
    }

    if ($HeaderTextAlignment) {

        if ($HeaderTextAlignment -eq 'justify' -or $HeaderTextAlignment -eq 'center') {
            $HeaderAlignment = 'center'
        } elseif ($HeaderTextAlignment -eq 'left') {
            $HeaderAlignment = 'flex-start'
        } elseif ($HeaderTextAlignment -eq 'right') {
            $HeaderAlignment = 'flex-end'
        } else {

        }
    }
    $TextHeaderColorFromRGB = ConvertFrom-Color -Color $HeaderTextColor
    $TextSize = ConvertFrom-Size -Size $HeaderTextSize
    $HiddenDivStyle = [ordered] @{ }
    $AttributesTop = [ordered] @{}

    if ($StyleSheetsConfiguration.Section -eq 'defaultSection') {
        $Script:HTMLSchema.Features.DefaultSection = $true
        $CurrentFlexDirection = $Script:CurrentConfiguration['Features']['DefaultSection']['HeaderAlways']['CssInline'][".$($StyleSheetsConfiguration.Section)"]['flex-direction']
    } else {
        $CurrentFlexDirection = $Script:CurrentConfiguration['Features']['Inject']['HeaderAlways']['CssInline'][".$($StyleSheetsConfiguration.Section)"]['flex-direction']
    }

    if ($CanCollapse) {
        $Script:HTMLSchema.Features.HideSection = $true
        $Script:HTMLSchema.Features.RedrawObjects = $true
        if ($Collapsed) {

            $HideStyle = @{
                "color"   = $TextHeaderColorFromRGB;
                'display' = 'none'
            }

            $ShowStyle = @{
                "color" = $TextHeaderColorFromRGB
            }
            $HiddenDivStyle['display'] = 'none'

            if ($CurrentFlexDirection -eq 'Row') {
                $ClassTop = "sectionHide"
            }
        } else {

            $ShowStyle = @{
                "color"   = $TextHeaderColorFromRGB;
                'display' = 'none'
            }

            $HideStyle = @{
                "color" = $TextHeaderColorFromRGB
            }
            if ($CurrentFlexDirection -eq 'Row') {
                $ClassTop = "sectionShow"
            }
        }
    } else {

        $ShowStyle = @{
            "color"   = $TextHeaderColorFromRGB;
            'display' = 'none'
        }

        $HideStyle = @{
            "color"   = $TextHeaderColorFromRGB;
            'display' = 'none'
        }
        $ClassTop = ''
    }

    $AttributesTop['class'] = "$($StyleSheetsConfiguration.Section) overflowHidden $ClassTop"
    $AttributesTop['style'] = [ordered] @{
        "background-color" = ConvertFrom-Color -Color $BackgroundColor
        'border-radius'    = $BorderRadius
        'flex-basis'       = $Width
    }
    if ($PSBoundParameters.ContainsKey('Margin')) {
        $AttributesTop['style']['margin'] = ConvertFrom-Size -Size $Margin
    }
    if ($IsHidden) {
        $AttributesTop['style']["display"] = 'none'
    }

    if ($Invisible) {
        $AttributesTop['style']['height'] = ConvertFrom-Size -Size $Height
    } else {
        $HiddenDivStyle['height'] = ConvertFrom-Size -Size $Height
    }

    if ($Wrap -or $Direction) {
        [string] $ClassName = "flexParent$(Get-RandomStringName -Size 8 -LettersOnly)"
        $Attributes = @{
            'display'        = 'flex'
            'flex-wrap'      = if ($Wrap) {
                $Wrap } else {
            }
            'flex-direction' = if ($Direction) {
                $Direction } else {
            }
            'align-content'  = if ($AlignContent) {
                $AlignContent } else {
            }
            'align-items'    = if ($AlignItems) {
                $AlignItems } else {
            }
        }
        $Css = ConvertTo-LimitedCSS -ClassName $ClassName -Attributes $Attributes -Group

        $Script:HTMLSchema.CustomHeaderCSS[$AnchorName] = $Css
    } else {
        if ($Invisible) {
            [string] $ClassName = "flexParentInvisible flexElement overflowHidden $($StyleSheetsConfiguration.SectionContentInvisible)"
            [string] $ClassNameNested = "flexParent flexElement overflowHidden $($StyleSheetsConfiguration.SectionContentInvisible)"
        } else {
            [string] $ClassName = "flexParent flexElement overflowHidden $($StyleSheetsConfiguration.SectionContent)"
            [string] $ClassNameNested = "flexParent flexElement overflowHidden $($StyleSheetsConfiguration.SectionContent)"
        }
    }

    $ContentStyle = @{
        'justify-content' = $JustifyContent
    }

    $DivHeaderStyle = @{

        'justify-content'  = $HeaderAlignment
        'font-size'        = $TextSize
        "background-color" = ConvertFrom-Color -Color $HeaderBackGroundColor
    }
    $HeaderStyle = @{ "color" = $TextHeaderColorFromRGB }
    if ($Invisible) {
        New-HTMLTag -Tag 'div' -Attributes @{ class = $ClassName; style = $AttributesTop['style'] } -Value {
            New-HTMLTag -Tag 'div' -Attributes @{ class = $ClassNameNested; Style = $ContentStyle } -Value {
                $Object = Invoke-Command -ScriptBlock $Content
                if ($null -ne $Object) {
                    $Object
                }
            }
        }
    } else {
        New-HTMLTag -Tag 'div' -Attributes $AttributesTop -Value {
            New-HTMLTag -Tag 'div' -Attributes @{ class = $StyleSheetsConfiguration.SectionHead; style = $DivHeaderStyle } -Value {
                New-HTMLTag -Tag 'div' -Attributes @{ class = $StyleSheetsConfiguration.SectionText } {
                    New-HTMLAnchor -Name $HeaderText -Text "$HeaderText " -Style $HeaderStyle
                    "&nbsp;" 
                    New-HTMLAnchor -Id "show_$AnchorName" -Href 'javascript:void(0)' -OnClick "show('$AnchorName'); " -Style $ShowStyle -Text '(Show)'
                    New-HTMLAnchor -Id "hide_$AnchorName" -Href 'javascript:void(0)' -OnClick "hide('$AnchorName'); " -Style $HideStyle -Text '(Hide)'
                }
            }
            New-HTMLTag -Tag 'div' -Attributes @{ name = $AnchorName; class = $ClassName; id = $AnchorName; style = $HiddenDivStyle } -Value {
                New-HTMLTag -Tag 'div' -Attributes @{ class = "$ClassNameNested collapsable"; id = $AnchorName; style = $ContentStyle } -Value {
                    $Object = Invoke-Command -ScriptBlock $Content
                    if ($null -ne $Object) {
                        $Object
                    }
                }
            }
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLSection -ParameterName HeaderTextColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLSection -ParameterName HeaderBackGroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLSection -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLSectionScrolling {
    <#
    .SYNOPSIS
    Creates a new HTML section with scrolling functionality.
 
    .DESCRIPTION
    This function creates a new HTML section with scrolling functionality. It takes a script block as input, executes the script block, and generates HTML output for each SectionScrollingItem.
 
    .PARAMETER Content
    The script block containing the content to be displayed in the HTML section.
 
    .EXAMPLE
    New-HTMLSectionScrolling -Content {
        [PSCustomObject]@{
            Type = 'SectionScrollingItem'
            HTMLOutput = '<p>Section 1</p>'
            ListEntry = 'Section 1'
        }
        [PSCustomObject]@{
            Type = 'SectionScrollingItem'
            HTMLOutput = '<p>Section 2</p>'
            ListEntry = 'Section 2'
        }
    }
 
    .NOTES
    File Name : New-HTMLSectionScrolling.ps1
    Prerequisite : This function requires Enable-HTMLFeature to be run with the SectionScrolling feature enabled.
    #>

    [cmdletBinding()]
    param(
        [scriptblock]$Content
    )
    Enable-HTMLFeature -Feature SectionScrolling

    if ($Content) {
        $ContentExecuted = & $Content

        $HTMLListScrolling = [System.Collections.Generic.List[string]]::new()
        $HTMLData = foreach ($C in $ContentExecuted) {
            if ($C -is [PSCustomObject] -and $C.Type -eq 'SectionScrollingItem') {
                $C.HTMLOutput
                $HTMLListScrolling.Add($C.ListEntry)
            }
        }
        New-HTMLTag -Tag 'div' {
            New-HTMLTag -Tag 'div' {
                $HTMLData
            }
            New-HTMLTag -Tag 'nav' {
                New-HTMLTag -Tag 'ol' {
                    $HTMLListScrolling
                }
            } -Attributes @{ class = 'section-nav' }
        } -Attributes @{ class = 'sectionScrolling' }
    }
}
function New-HTMLSectionScrollingItem {
    <#
    .SYNOPSIS
    Creates a new HTML section with scrolling functionality.
 
    .DESCRIPTION
    This function creates a new HTML section with scrolling functionality. It allows you to define the content and title of the section.
 
    .PARAMETER Content
    The script block containing the content to be displayed within the section.
 
    .PARAMETER SectionTitle
    The title of the section.
 
    .EXAMPLE
    New-HTMLSectionScrollingItem -SectionTitle "Introduction" -Content {
        "This is the introduction section."
    }
    Creates a new HTML section with the title "Introduction" and the content "This is the introduction section."
 
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $Content,
        [string] $SectionTitle
    )

    $Name = "scrolling-$(Get-RandomStringName -Size 8 -LettersOnly)"
    $NestedScrollingList = [System.Collections.Generic.List[string]]::new()

    $HTMLOutput = New-HTMLTag -Tag 'section' {
        New-HTMLTag -Tag 'h2' {
            $SectionTitle
        }
        New-HTMLTag -Tag 'div' {

            $ExecutedContent = if ($Content) {
                & $Content
            }
            foreach ($C in $ExecutedContent) {
                if ($C -is [PSCustomObject] -and $C.Type -eq 'SectionScrollingItem') {
                    $C.HTMLOutput
                    $NestedScrollingList.Add($C.ListEntry)
                } else {
                    $C
                }
            }
        }
    } -Attributes @{ 'id' = $Name }

    if ($NestedScrollingList.Count -gt 0) {
        $ListEntry = New-HTMLTag -Tag 'li' {
            New-HTMLTag -Tag 'a' {
                $SectionTitle
            } -Attributes @{ href = "#$Name" }
            New-HTMLTag -Tag 'ul' {
                $NestedScrollingList
            }
        }
    } else {
        $ListEntry = New-HTMLTag -Tag 'li' {
            New-HTMLTag -Tag 'a' {
                $SectionTitle
            } -Attributes @{ href = "#$Name" }
        }
    }
    [PSCustomObject] @{
        Type       = 'SectionScrollingItem'
        HTMLOutput = $HTMLOutput
        ListEntry  = $ListEntry
        Name       = $Name
        Level      = $Level
    }
}
function New-HTMLSectionStyle {
    <#
    .SYNOPSIS
    Creates a new HTML section style with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML section style with various styling options such as border radius, text color, alignment, background color, and more.
 
    .EXAMPLE
    New-HTMLSectionStyle -BorderRadius '10px' -HeaderTextColor 'blue' -HeaderTextAlignment 'center' -HeaderBackGroundColor 'lightgray' -Wrap 'wrap' -Direction 'row' -Align 'center' -AlignItems 'flex-start' -Justify 'space-between' -Rotate '90deg' -BackgroundColorContent 'white' -WrapContent 'wrap' -DirectionContent 'column' -AlignContent 'flex-end' -AlignItemsContent 'center' -JustifyContent 'flex-start' -WritingMode 'horizontal-tb' -TextOrientation 'upright' -RequestConfiguration
    Creates a new HTML section style with specified options.
 
    .PARAMETER BorderRadius
    Specifies the border radius of the section.
 
    .PARAMETER HeaderTextColor
    Specifies the text color of the section header.
 
    .PARAMETER HeaderTextAlignment
    Specifies the text alignment of the section header.
 
    .PARAMETER HeaderBackGroundColor
    Specifies the background color of the section header.
 
    .PARAMETER Wrap
    Specifies the wrapping behavior of the section.
 
    .PARAMETER Direction
    Specifies the direction of the section layout.
 
    .PARAMETER Align
    Specifies the alignment of the section.
 
    .PARAMETER AlignItems
    Specifies the alignment of the items within the section.
 
    .PARAMETER Justify
    Specifies the justification of the section.
 
    .PARAMETER Rotate
    Specifies the rotation angle of the section.
 
    .PARAMETER BackgroundColorContent
    Specifies the background color of the section content.
 
    .PARAMETER WrapContent
    Specifies the wrapping behavior of the section content.
 
    .PARAMETER DirectionContent
    Specifies the direction of the section content layout.
 
    .PARAMETER AlignContent
    Specifies the alignment of the section content.
 
    .PARAMETER AlignItemsContent
    Specifies the alignment of the items within the section content.
 
    .PARAMETER JustifyContent
    Specifies the justification of the section content.
 
    .PARAMETER WritingMode
    Specifies the writing mode of the section.
 
    .PARAMETER TextOrientation
    Specifies the text orientation of the section.
 
    .PARAMETER RequestConfiguration
    Indicates whether to request configuration for the section styling.
 
    .NOTES
    File Name : New-HTMLSectionStyle.ps1
    Prerequisite : PowerShell V5
    #>

    [alias("New-HTMLSectionOptions", 'SectionOption', 'New-HTMLSectionOption')]
    [cmdletBinding()]
    param(
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('0px', '5px', '10px', '15px', '20px', '25px')][string] $BorderRadius,

        [Parameter(ParameterSetName = 'Manual')][switch] $RemoveShadow,

        [Parameter(ParameterSetName = 'Manual')][alias('TextColor')][string]$HeaderTextColor,
        [Parameter(ParameterSetName = 'Manual')][alias('TextAlignment')][string][ValidateSet('center', 'left', 'right', 'justify')] $HeaderTextAlignment,
        [Parameter(ParameterSetName = 'Manual')][alias('TextBackGroundColor')][string]$HeaderBackGroundColor,
        [Parameter(ParameterSetName = 'Manual')][switch] $HeaderRemovePadding,

        [Parameter(ParameterSetName = 'Manual')][string][ValidateSet('wrap', 'nowrap', 'wrap-reverse')] $Wrap,
        [Parameter(ParameterSetName = 'Manual')][string][ValidateSet('row', 'row-reverse', 'column', 'column-reverse')] $Direction,
        [Parameter(ParameterSetName = 'Manual')][string][ValidateSet('flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch')] $Align,
        [Parameter(ParameterSetName = 'Manual')][string][ValidateSet('stretch', 'flex-start', 'flex-end', 'center', 'baseline')] $AlignItems,
        [Parameter(ParameterSetName = 'Manual')][string][ValidateSet('flex-start', 'flex-end', 'center')] $Justify,
        [Parameter(ParameterSetName = 'Manual')][string][ValidateSet('-180deg', '-90deg', '90deg', '180deg')] $Rotate,

        [Parameter(ParameterSetName = 'Manual')][alias('BackgroundShade')][string]$BackgroundColorContent,
        [string][ValidateSet('wrap', 'nowrap', 'wrap-reverse')] $WrapContent,
        [string][ValidateSet('row', 'row-reverse', 'column', 'column-reverse')] $DirectionContent,
        [string][ValidateSet('flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch')] $AlignContent,
        [string][ValidateSet('stretch', 'flex-start', 'flex-end', 'center', 'baseline')] $AlignItemsContent,
        [string][ValidateSet('flex-start', 'flex-end', 'center')] $JustifyContent,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('vertical-rl', 'vertical-lr', 'horizontal-tb')][string] $WritingMode,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('mixed', 'upright')][string] $TextOrientation,

        [Parameter(ParameterSetName = 'Manual')][switch] $RequestConfiguration
    )

    $CssConfiguration = Get-ConfigurationCss -Feature 'DefaultSection' -Type 'HeaderAlways'
    $StyleSheetsConfiguration = [ordered] @{
        Section                 = ".defaultSection"
        SectionText             = ".defaultSectionText"
        SectionHead             = ".defaultSectionHead"
        SectionContent          = '.defaultSectionContent'
        SectionContentInvisible = '.defaultSectionContentInvisible'
    }

    if ($RequestConfiguration) {

        $RequestedConfiguration = New-RequestCssConfiguration -Pair $StyleSheetsConfiguration -CSSConfiguration $CssConfiguration -Feature 'Inject' -Type 'HeaderAlways'
        $StyleSheetsConfiguration = $RequestedConfiguration.StyleSheetConfiguration
        $CssConfiguration = $RequestedConfiguration.CSSConfiguration

    }

    if ($Wrap -or $Direction -or $Align -or $AlignItems) {

        Add-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.Section -Inject @{
            'display'        = 'flex'
            'flex-direction' = $Direction
        }
    }

    $SectionStyle = @{
        "background-color" = ConvertFrom-Color -Color $BackgroundColorContent
        'border-radius'    = $BorderRadius
    }
    Add-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.Section -Inject $SectionStyle

    if ($BackgroundColorContent -eq 'none') {
        Remove-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.Section -Property 'background-color'
    }
    if ($RemoveShadow) {
        Remove-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.Section -Property 'box-shadow'
    }

    $SectionHeadStyle = @{
        'flex-wrap'        = $Wrap
        'flex-direction'   = $Direction
        'align-content'    = $Align
        'align-items'      = $AlignItems
        'justify-content'  = $Justify
        "background-color" = ConvertFrom-Color -Color $HeaderBackGroundColor
        'border-radius'    = $BorderRadius
        "text-align"       = $HeaderTextAlignment
        "transform"        = ConvertFrom-Rotate -Rotate $Rotate
    }

    Add-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.SectionHead -Inject $SectionHeadStyle

    if ($HeaderBackGroundColor -eq 'none') {
        Remove-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.SectionHead -Property 'background-color'
    }
    if ($HeaderRemovePadding) {
        Remove-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.SectionHead -Property 'padding'
    }

    $SectionHeadTextStyle = @{
        'writing-mode'     = $WritingMode
        'text-orientation' = $TextOrientation
        'color'            = ConvertFrom-Color -Color $HeaderTextColor
    }
    Add-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.SectionText -Inject $SectionHeadTextStyle

    if ($HeaderTextColor -eq 'none') {
        Remove-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.SectionText -Property 'color'
    }

    if ($WrapContent -or $DirectionContent -or $AlignContent -or $AlignItemsContent -or $JustifyContent) {

        Add-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.SectionContent -Inject @{ display = 'flex' }
    }

    $SectionContentStyle = @{
        'flex-wrap'       = $WrapContent
        'flex-direction'  = $DirectionContent
        'align-content'   = $AlignContent
        'align-items'     = $AlignItemsContent
        'justify-content' = $JustifyContent
    }
    Add-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.SectionContent -Inject $SectionContentStyle
    Add-ConfigurationCSS -CSS $CssConfiguration -Name $StyleSheetsConfiguration.SectionContentInvisible -Inject $SectionContentStyle

    if ($RequestConfiguration) {

        return $StyleSheetsConfiguration
    }

}
function New-HTMLSpanStyle {
    <#
    .SYNOPSIS
    Creates an HTML <span> element with specified styling.
 
    .DESCRIPTION
    The New-HTMLSpanStyle function generates an HTML <span> element with customizable styling options such as color, background color, font size, font weight, font style, alignment, text decoration, text transformation, and more.
 
    .PARAMETER Content
    The content to be placed within the <span> element.
 
    .PARAMETER Color
    The color of the text.
 
    .PARAMETER BackGroundColor
    The background color of the <span> element.
 
    .PARAMETER FontSize
    The font size of the text.
 
    .PARAMETER LineHeight
    The height of each line of text.
 
    .PARAMETER FontWeight
    The weight of the font.
 
    .PARAMETER FontStyle
    The style of the font.
 
    .PARAMETER FontVariant
    The variant of the font.
 
    .PARAMETER FontFamily
    The font family to be used.
 
    .PARAMETER Alignment
    The alignment of the text within the <span> element.
 
    .PARAMETER TextDecoration
    The decoration to be applied to the text.
 
    .PARAMETER TextTransform
    The transformation to be applied to the text.
 
    .PARAMETER Direction
    The direction of the text.
 
    .PARAMETER Display
    The display style of the <span> element.
 
    .PARAMETER Opacity
    The opacity of the <span> element.
 
    .PARAMETER LineBreak
    Indicates whether to allow line breaks within the <span> element.
 
    .EXAMPLE
    New-HTMLSpanStyle -Content { "Hello, World!" } -Color 'red' -FontSize '12px' -FontWeight 'bold' -Alignment 'center'
    Creates a <span> element with red text, font size of 12 pixels, bold weight, and centered alignment containing the text "Hello, World!".
 
    .EXAMPLE
    New-HTMLSpanStyle -Content { "This is underlined text" } -Color 'blue' -TextDecoration 'underline'
    Creates a <span> element with blue text and underlined decoration containing the text "This is underlined text".
 
    #>

    [CmdletBinding()]
    param(
        [ScriptBlock] $Content,
        [string] $Color,
        [string] $BackGroundColor,
        [object] $FontSize,
        [string] $LineHeight,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [string] $FontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string]  $Alignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string]  $TextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string]  $TextTransform,
        [ValidateSet('rtl')][string] $Direction,
        [ValidateSet(
            'none', 'inline', 'block', 'inline-block', 'contents',
            'flex', 'grid', 'inline-flex', 'inline-grid', 'inline-table', 'list-item', 'run-in',
            'table', 'table-caption', 'table-column-group', 'table-header-group', 'table-footer-group',
            'table-row-group', 'table-cell', 'table-column', 'table-row'
        )][string] $Display,
        [double] $Opacity,
        [switch] $LineBreak
    )
    $Style = @{
        style = @{
            'color'            = ConvertFrom-Color -Color $Color
            'background-color' = ConvertFrom-Color -Color $BackGroundColor
            'font-size'        = ConvertFrom-Size -FontSize $FontSize
            'font-weight'      = $FontWeight
            'font-variant'     = $FontVariant
            'font-family'      = $FontFamily
            'font-style'       = $FontStyle
            'text-align'       = $Alignment
            'line-height'      = $LineHeight
            'text-decoration'  = $TextDecoration
            'text-transform'   = $TextTransform
            'direction'        = $Direction
            'display'          = $Display
            'opacity'          = $Opacity
        }
    }

    if ($Alignment) {
        $StyleDiv = @{ }
        $StyleDiv.Align = $Alignment

        New-HTMLTag -Tag 'div' -Attributes $StyleDiv {
            New-HTMLTag -Tag 'span' -Attributes $Style {
                if ($Content) {
                    Invoke-Command -ScriptBlock $Content
                }
            }
        }
    } else {
        New-HTMLTag -Tag 'span' -Attributes $Style {
            if ($Content) {
                Invoke-Command -ScriptBlock $Content
            }
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLSpanStyle -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLSpanStyle -ParameterName BackGroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLStatus {
    <#
    .SYNOPSIS
    Creates a new HTML status section with customizable content.
 
    .DESCRIPTION
    The New-HTMLStatus function creates an HTML status section with customizable content. It allows you to display status information with flexibility.
 
    .PARAMETER Content
    Specifies the content to be displayed within the status section as a ScriptBlock.
 
    .EXAMPLE
    New-HTMLStatus -Content {
        "Status: OK"
    }
    Creates a new HTML status section displaying "Status: OK".
 
    .EXAMPLE
    $content = {
        "Status: Error"
    }
    New-HTMLStatus -Content $content
    Creates a new HTML status section displaying "Status: Error".
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('')][ScriptBlock] $Content
    )
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.StatusButtonical = $true
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'buttonicalService' } {

        Invoke-Command -ScriptBlock $Content

    }

}
function New-HTMLStatusItem {
    <#
    <#
    .SYNOPSIS
    Creates a new HTML status item with customizable parameters.
 
    .DESCRIPTION
    This function creates a new HTML status item with customizable parameters such as service name, status, icon, percentage, font color, background color, and icon style.
 
    .PARAMETER Name
    Specifies the name of the service.
 
    .PARAMETER Status
    Specifies the status of the service.
 
    .PARAMETER Icon
    Specifies the icon style for the status item. Default value is 'Good'.
 
    .PARAMETER Percentage
    Specifies the percentage value for the status item. Default value is '100%'.
 
    .PARAMETER FontColor
    Specifies the font color for the status item. Default value is '#5f6982'.
 
    .PARAMETER BackgroundColor
    Specifies the background color for the status item. Default value is '#0ef49b'.
 
    .PARAMETER IconBrands
    Specifies the icon style for brands.
 
    .PARAMETER IconRegular
    Specifies the icon style for regular icons.
 
    .EXAMPLE
    New-HTMLStatusItem -Name "ServiceA" -Status "Good" -Icon "Good" -Percentage "100%" -FontColor "#5f6982" -BackgroundColor "#0ef49b" -IconBrands "BrandIcon"
 
    Creates a new HTML status item for ServiceA with a 'Good' status, using the 'Good' icon style, 100% percentage, font color '#5f6982', background color '#0ef49b', and 'BrandIcon' as the icon style for brands.
 
    .NOTES
    File Name : New-HTMLStatusItem.ps1
    Prerequisite : PowerShell V5
    #>

    [CmdletBinding(DefaultParameterSetName = 'Statusimo')]
    param(
        [alias('ServiceName')][string] $Name,
        [alias('ServiceStatus')][string] $Status,

        [ValidateSet('Dead', 'Bad', 'Good')]
        [Parameter(ParameterSetName = 'Statusimo')]
        $Icon = 'Good',

        [ValidateSet('0%', '10%', '30%', '70%', '100%')]
        [Parameter(ParameterSetName = 'Statusimo')]
        [string] $Percentage = '100%',

        [string]$FontColor = '#5f6982',

        [parameter(ParameterSetName = 'FontAwesomeBrands')]
        [parameter(ParameterSetName = 'FontAwesomeRegular')]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [Parameter(ParameterSetName = 'Hex')]
        [string]$BackgroundColor = '#0ef49b',

        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = 'FontAwesomeBrands')]
        [string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = 'FontAwesomeRegular')]
        [string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = 'FontAwesomeSolid')]
        [string] $IconSolid,

        [Parameter(ParameterSetName = 'Hex')]
        [ValidatePattern('^&#x[A-Fa-f0-9]{4,5}$')]
        [string]$IconHex
    )

    if ($PSCmdlet.ParameterSetName -eq 'Statusimo') {
        Write-Warning 'This parameter set has been deprecated. It will be removed in a future release. Look to move to the other parameter sets with customization options.'

        if ($Percentage -eq '100%') {
            $BackgroundColor = '#0ef49b'
        } elseif ($Percentage -eq '70%') {
            $BackgroundColor = '#d2dc69'
        } elseif ($Percentage -eq '30%') {
            $BackgroundColor = '#faa04b'
        } elseif ($Percentage -eq '10%') {
            $BackgroundColor = '#ff9035'
        } elseif ($Percentage -eq '0%') {
            $BackgroundColor = '#ff5a64'
        }

        if ($Icon -eq 'Dead') {
            $IconType = '&#x2620'
        } elseif ($Icon -eq 'Bad') {
            $IconType = '&#x2639'
        } elseif ($Icon -eq 'Good') {
            $IconType = '&#x2714'
        }
    } elseif ($PSCmdlet.ParameterSetName -like 'FontAwesome*') {
        $Script:HTMLSchema.Features.FontsAwesome = $true
        $BackgroundColor = ConvertFrom-Color -Color $BackgroundColor

        if ($IconBrands) {
            $IconClass = "fab fa-$IconBrands".ToLower()
        } elseif ($IconRegular) {
            $IconClass = "far fa-$IconRegular".ToLower()
        } elseif ($IconSolid) {
            $IconClass = "fas fa-$IconSolid".ToLower()
        }
    } elseif ($PSCmdlet.ParameterSetName -eq 'Hex') {
        $IconType = $IconHex
    }
    $FontColor = ConvertFrom-Color -Color $FontColor

    New-HTMLTag -Tag 'div' -Attributes @{ class = 'buttonical'; style = @{ "background-color" = $BackgroundColor } } -Value {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'label' } {
            New-HTMLTag -Tag 'span' -Attributes @{ class = 'performance'; style = @{ color = $FontColor } } {
                $Name
            }
        }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'middle' }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'status' } {
            New-HTMLTag -Tag 'input' -Attributes @{ name = Get-Random; type = 'radio'; value = 'other-item'; checked = 'true' } -SelfClosing
            New-HTMLTag -Tag 'span' -Attributes @{ class = "performance"; style = @{ color = $FontColor } } {
                $Status
                New-HTMLTag -Tag 'span' -Attributes @{ class = "icon $IconClass" } {
                    $IconType
                }
            }
        }
    }
}
Register-ArgumentCompleter -CommandName New-HTMLStatusItem -ParameterName FontColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLStatusItem -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLSummary {
    <#
    .SYNOPSIS
    Creates a new HTML summary section with customizable content.
 
    .DESCRIPTION
    The New-HTMLSummary function creates an HTML summary section with customizable content. It allows you to display a summary with a title, additional information, and summary items.
 
    .PARAMETER SummaryItems
    Specifies the script block containing the summary items to be displayed within the summary section.
 
    .PARAMETER Title
    Specifies the title of the summary section.
 
    .PARAMETER TitleRight
    Specifies the additional information to be displayed on the right side of the title.
 
    .EXAMPLE
    New-HTMLSummary -SummaryItems {
        "Summary Item 1"
        "Summary Item 2"
    } -Title "Summary Title" -TitleRight "Additional Info"
 
    Creates a new HTML summary section with the title "Summary Title", displaying "Summary Item 1" and "Summary Item 2" as summary items, and additional information "Additional Info" on the right side of the title.
 
    .NOTES
    Based on https://codepen.io/banik/pen/exjLzB by Tony Banik
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $SummaryItems,
        [string] $Title,
        [string] $TitleRight
    )

    Enable-HTMLFeature -Feature MainFlex, AccordionSummary

    New-HTMLTag -Tag 'container' {
        New-HTMLTag -Tag 'h2' {
            $Title
            New-HTMLTag -Tag 'span' {
                $TitleRight
            } -Attributes @{ 'class' = 'summary-end' }
        } -Attributes @{ class = 'summary-title' }
        & $SummaryItems
    }
}
function New-HTMLSummaryItem {
    <#
    .SYNOPSIS
    Creates a new HTML summary item with specified parameters.
 
    .DESCRIPTION
    This function creates a new HTML summary item with customizable text, icons, and other visual properties.
 
    .PARAMETER NestedItems
    Specifies the nested items to include within the summary item.
 
    .PARAMETER Text
    Specifies the text content of the summary item.
 
    .PARAMETER Open
    Indicates whether the summary item should be initially open or closed.
 
    .PARAMETER IconSize
    Specifies the size of the icon in the summary item.
 
    .PARAMETER IconColor
    Specifies the color of the icon in the summary item.
 
    .PARAMETER IconBrands
    Specifies the icon brand to use for the summary item. Valid values are retrieved from the available FontAwesome brands.
 
    .PARAMETER IconRegular
    Specifies the regular icon to use for the summary item. Valid values are retrieved from the available FontAwesome regular icons.
 
    .EXAMPLE
    New-HTMLSummaryItem -Text "Summary Item 1" -IconBrands "fa-windows" -IconSize 2 -IconColor "blue" -Open
 
    Creates a new HTML summary item with the text "Summary Item 1", using the Windows icon brand, a size of 2, blue color, and initially open.
 
    .EXAMPLE
    New-HTMLSummaryItem -Text "Summary Item 2" -IconRegular "fa-address-book" -IconSize 3 -IconColor "green"
 
    Creates a new HTML summary item with the text "Summary Item 2", using the address book regular icon, a size of 3, and green color.
 
    #>

    [cmdletBinding(DefaultParameterSetName = 'FontAwesomeSolid')]
    param(
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [Parameter(Position = 0)][scriptblock] $NestedItems,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [string] $Text,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [switch] $Open,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [int] $IconSize,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [string] $IconColor,

        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")][string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")][string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconSolid
    )

    $Name = "$(Get-RandomStringName -Size 8 -LettersOnly)"

    $IconChoice = @{
        IconSolid   = $IconSolid
        IconRegular = $IconRegular
        IconBrands  = $IconBrands
    }
    Remove-EmptyValue -Hashtable $IconChoice

    if ($IconChoice.Count -eq 0) {
        $IconChoice = @{
            IconSolid = 'check'
        }
    }

    if (-not $Script:SummaryNestedItem) {
        New-HTMLTag -Tag 'details' {
            New-HTMLTag -Tag 'summary' {
                $Text
            } -Attributes @{ class = $Name }
            New-HTMLTag -Tag 'ul' {
                if ($NestedItems) {
                    $Script:SummaryNestedItem = $true
                    & $NestedItems
                    $Script:SummaryNestedItem = $false
                }
            }
        } -Attributes @{ open = if ($Open) {
                'open' } else {
                '' } }
        $StyleNodeInformation = @{
            "details summary.$($Name):before" = New-HTMLFontIcon @IconChoice -AsCSS -AsHashTable -IconColor $IconColor -IconSize $IconSize
        }
    } else {
        New-HTMLTag -Tag 'li' {
            New-HTMLTag -Tag 'div' {
                $Text
                if ($NestedItems) {
                    & $NestedItems
                }
            } -Attributes @{ class = $Name }
        }
        $StyleNodeInformation = @{
            "details ul li div.$($Name):before" = New-HTMLFontIcon @IconChoice -AsCSS -AsHashTable -IconColor $IconColor -IconSize $IconSize
        }
    }

    Remove-EmptyValue -Hashtable $StyleNodeInformation -Recursive -Rerun 2
    if ($StyleNodeInformation) {
        Add-HTMLStyle -Placement Header -Css $StyleNodeInformation 
    }
}

Register-ArgumentCompleter -CommandName New-HTMLSummaryItem -ParameterName IconColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLSummaryItemData {
    <#
    .SYNOPSIS
    Creates a new HTML summary item data with specified text and information.
 
    .DESCRIPTION
    This function creates a new HTML summary item data with the provided text and information. It formats the output using New-HTMLText cmdlet.
 
    .PARAMETER Icon
    Specifies the icon for the summary item.
 
    .PARAMETER Text
    Specifies the text to be displayed in the summary item. This parameter is mandatory.
 
    .PARAMETER Information
    Specifies the information to be displayed in the summary item. This parameter is mandatory.
 
    .EXAMPLE
    New-HTMLSummaryItemData -Text "Total Sales" -Information "$1000"
    Creates a new HTML summary item with the text "Total Sales" and information "$1000".
 
    .EXAMPLE
    New-HTMLSummaryItemData -Text "Total Orders" -Information "50" -Icon "order.png"
    Creates a new HTML summary item with the text "Total Orders", information "50", and icon "order.png".
    #>

    [cmdletBinding()]
    param(
        [string] $Icon,
        [parameter(Mandatory)][string] $Text,
        [parameter(Mandatory)][alias('Value')][string] $Information
    )
    New-HTMLText -Text "$($Text): ", $Information -Color Grey, Black -Display inline -Opacity 0.3, 1 -FontWeight bold, normal
}
function New-HTMLTab {
    <#
    .SYNOPSIS
    Creates a new HTML tab with customizable content and icons.
 
    .DESCRIPTION
    The New-HTMLTab function creates a new HTML tab with the specified content, heading, and icon. It supports different parameter sets for FontAwesomeBrands, FontAwesomeRegular, and FontAwesomeSolid icons.
 
    .PARAMETER HtmlData
    The HTML content to be displayed within the tab. This should be provided as a ScriptBlock.
 
    .PARAMETER Heading
    The heading or title of the tab.
 
    .PARAMETER Name
    The name of the tab.
 
    .PARAMETER IconBrands
    The icon to be displayed for FontAwesomeBrands. Use tab completion to select from available options.
 
    .PARAMETER IconRegular
    The icon to be displayed for FontAwesomeRegular. Use tab completion to select from available options.
 
    .PARAMETER IconSolid
    The icon to be displayed for FontAwesomeSolid. Use tab completion to select from available options.
 
    .EXAMPLE
    New-HTMLTab -HtmlData { Get-Process } -Heading "Processes" -Name "ProcessTab" -IconBrands "windows"
 
    Creates a new tab displaying the list of processes with a Windows icon.
 
    .EXAMPLE
    New-HTMLTab -HtmlData { Get-Service } -Heading "Services" -Name "ServiceTab" -IconRegular building
 
    Creates a new tab displaying the list of services with a hard drive icon.
 
    .EXAMPLE
    New-HTMLTab -HtmlData { Get-EventLog -LogName System } -Heading "System Events" -Name "EventTab" -IconSolid "exclamation-triangle"
 
    Creates a new tab displaying the system events log with an exclamation triangle icon.
    #>

    [alias('Tab')]
    [CmdLetBinding(DefaultParameterSetName = 'FontAwesomeBrands')]
    param(
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $HtmlData = $(Throw "No curly brace?)"),
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [alias('TabHeading')][Parameter(Mandatory = $false, Position = 1)][String]$Heading,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [alias('TabName')][string] $Name = 'Tab',

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript( { $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys)) })]
        [string] $IconBrands,

        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript( { $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys)) })]
        [string] $IconRegular,

        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript( { $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys)) })]
        [string] $IconSolid,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][object] $TextSize,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $TextColor,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][object] $IconSize,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconColor,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform, 
        [string] $AnchorName
    )
    if (-not $Script:HTMLSchema.Features) {
        Write-Warning 'New-HTMLTab - Creation of HTML aborted. Most likely New-HTML is missing.'
        Exit
    }
    if ($IconBrands -or $IconRegular -or $IconSolid) {
        $Script:HTMLSchema.Features.FontsAwesome = $true
    }
    $Script:HTMLSchema.Features.MainFlex = $true
    if (-not $AnchorName) {
        $AnchorName = "Tab-$(Get-RandomStringName -Size 8)"
    }
    [string] $Icon = ''
    if ($IconBrands) {
        $Icon = "fab fa-$IconBrands".ToLower() 
    } elseif ($IconRegular) {
        $Icon = "far fa-$IconRegular".ToLower() 
    } elseif ($IconSolid) {
        $Icon = "fas fa-$IconSolid".ToLower() 
    }

    $StyleText = @{ }

    $StyleText['font-size'] = ConvertFrom-Size -Size $TextSize  

    if ($TextColor) {
        $StyleText.'color' = ConvertFrom-Color -Color $TextColor
    }

    $StyleText.'text-transform' = "$TextTransform"

    $StyleIcon = @{ }

    $StyleIcon.'font-size' = ConvertFrom-Size -Size $IconSize 

    if ($IconColor) {
        $StyleIcon.'color' = ConvertFrom-Color -Color $IconColor
    }

    if ($Script:HTMLSchema['TabPanelsList'].Count -eq 0) {

        foreach ($Tab in $Script:HTMLSchema.TabsHeaders) {
            $Tab.Current = $false
        }

        $Tab = [ordered] @{ }
        $Tab.ID = $AnchorName
        $Tab.Name = $Name
        $Tab.StyleIcon = $StyleIcon
        $Tab.StyleText = $StyleText
        $Tab.Current = $true

        if ($Script:HTMLSchema.TabsHeaders | Where-Object { $_.Active -eq $true }) {
            $Tab.Active = $false
        } else {
            $Tab.Active = $true
        }
        $Tab.Icon = $Icon

        if ($Tab.Active) {
            $Class = 'active'
        } else {
            $Class = ''
        }

        $Script:HTMLSchema.Features.Tabbis = $true
        $Script:HTMLSchema.Features.RedrawObjects = $true

        New-HTMLTag -Tag 'div' -Attributes @{ id = "$($Tab.ID)-Content"; class = $Class } {
            if (-not [string]::IsNullOrWhiteSpace($Heading)) {
                New-HTMLTag -Tag 'h7' {
                    $Heading
                }
            }
            $OutputHTML = Invoke-Command -ScriptBlock $HtmlData
            [Array] $TabsCollection = foreach ($_ in $OutputHTML) {
                if ($_ -is [System.Collections.IDictionary]) {
                    $_
                    $Script:HTMLSchema.TabsHeadersNested.Add($_)
                }
            }
            [Array] $HTML = foreach ($_ in $OutputHTML) {
                if ($_ -isnot [System.Collections.IDictionary]) {
                    $_
                }
            }
            if ($TabsCollection.Count -gt 0) {
                New-HTMLTabHead -TabsCollection $TabsCollection
                New-HTMLTag -Tag 'div' -Attributes @{ 'data-panes' = 'true' } {

                    $HTML
                }

            } else {
                $HTML
            }
        }
        $Script:HTMLSchema.TabsHeaders.Add($Tab)
        $Tab
    } else {

        if ($HtmlData) {
            $TabExecutedCode = & $HtmlData
        } else {
            $TabExecutedCode = ''
        }
        [PSCustomObject] @{
            Name      = $Name
            ID        = $AnchorName 
            Icon      = $Icon
            StyleIcon = $StyleIcon
            StyleText = $StyleText
            Content   = $TabExecutedCode
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLTab -ParameterName IconColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTab -ParameterName TextColor -ScriptBlock $Script:ScriptBlockColors

function New-HTMLTable {
    <#
    .SYNOPSIS
    Creates a new HTML table with various customization options.
 
    .DESCRIPTION
    This function generates an HTML table based on the provided data and allows for extensive customization using a wide range of parameters.
 
    .PARAMETER HTML
    The HTML content to be included before the table.
 
    .PARAMETER PreContent
    Additional content to be included before the table.
 
    .PARAMETER PostContent
    Additional content to be included after the table.
 
    .PARAMETER DataTable
    An array of objects containing the data to be displayed in the table.
 
    .PARAMETER Title
    The title of the HTML table.
 
    .PARAMETER Buttons
    An array of buttons to be displayed in the table for actions like copying, exporting, and printing.
 
    .PARAMETER PagingStyle
    The style of pagination to be used in the table.
 
    .PARAMETER PagingOptions
    An array of options for the number of items to display per page.
 
    .PARAMETER PagingLength
    The default number of items to display per page.
 
    .PARAMETER DisablePaging
    Disables pagination in the table.
 
    .PARAMETER DisableOrdering
    Disables column ordering in the table.
 
    .PARAMETER DisableInfo
    Disables information display at the bottom of the table.
 
    .PARAMETER HideFooter
    Hides the footer of the table.
 
    .PARAMETER HideButtons
    Hides all buttons in the table.
 
    .PARAMETER DisableProcessing
    Disables processing indicator in the table.
 
    .PARAMETER DisableResponsiveTable
    Disables responsiveness of the table.
 
    .PARAMETER DisableSelect
    Disables row selection in the table.
 
    .PARAMETER DisableStateSave
    Disables saving the state of the table.
 
    .PARAMETER DisableSearch
    Disables search functionality in the table.
 
    .PARAMETER OrderMulti
    Allows multiple column ordering in the table.
 
    .PARAMETER Filtering
    Enables filtering in the table.
 
    .PARAMETER FilteringLocation
    Specifies the location of the filter (Top, Bottom, Both).
 
    .PARAMETER Style
    An array of styles to apply to the table.
 
    .PARAMETER Simplify
    Simplifies the table layout.
 
    .PARAMETER TextWhenNoData
    The text to display when no data is available.
 
    .PARAMETER ScreenSizePercent
    The percentage of the screen width to occupy.
 
    .PARAMETER DefaultSortColumn
    An array of default columns to sort by.
 
    .PARAMETER DefaultSortIndex
    An array of default column indexes to sort by.
 
    .PARAMETER DefaultSortOrder
    The default sort order (Ascending, Descending).
 
    .PARAMETER DateTimeSortingFormat
    An array of date-time formats for sorting.
 
    .PARAMETER Find
    Search string to find in the table.
 
    .PARAMETER InvokeHTMLTags
    Enables HTML tags in the table.
 
    .PARAMETER DisableNewLine
    Disables new line characters in the table.
 
    .PARAMETER EnableKeys
    Enables keyboard navigation in the table.
 
    .PARAMETER EnableColumnReorder
    Enables column reordering in the table.
 
    .PARAMETER EnableRowReorder
    Enables row reordering in the table.
 
    .PARAMETER EnableAutoFill
    Enables auto-filling in the table.
 
    .PARAMETER EnableScroller
    Enables table scrolling.
 
    .PARAMETER ScrollX
    Enables horizontal scrolling.
 
    .PARAMETER ScrollY
    Enables vertical scrolling.
 
    .PARAMETER ScrollSizeY
    The height of the vertical scroll.
 
    .PARAMETER ScrollCollapse
    Collapses the table when scrolling.
 
    .PARAMETER FreezeColumnsLeft
    The number of columns to freeze on the left.
 
    .PARAMETER FreezeColumnsRight
    The number of columns to freeze on the right.
 
    .PARAMETER FixedHeader
    Fixes the header of the table.
 
    .PARAMETER FixedFooter
    Fixes the footer of the table.
 
    .PARAMETER ResponsivePriorityOrder
    The priority order for responsiveness.
 
    .PARAMETER ResponsivePriorityOrderIndex
    The index for responsive priority order.
 
    .PARAMETER PriorityProperties
    Properties to prioritize in the table.
 
    .PARAMETER IncludeProperty
    Properties to include in the table.
 
    .PARAMETER ExcludeProperty
    Properties to exclude from the table.
 
    .PARAMETER ImmediatelyShowHiddenDetails
    Shows hidden details immediately.
 
    .PARAMETER HideShowButton
    Displays a button to show/hide details.
 
    .PARAMETER AllProperties
    Displays all properties in the table.
 
    .PARAMETER SkipProperties
    Properties to skip in the table.
 
    .PARAMETER Compare
    Compares data in the table.
 
    .PARAMETER CompareNames
    Names to compare in the table.
 
    .PARAMETER HighlightDifferences
    Highlights differences in the table.
 
    .PARAMETER First
    Displays the first item in the table.
 
    .PARAMETER Last
    Displays the last item in the table.
 
    .PARAMETER CompareReplace
    Replaces data for comparison.
 
    .PARAMETER SearchRegularExpression
    Enables regular expression search.
 
    .PARAMETER WordBreak
    Enables word breaking in the table.
 
    .PARAMETER AutoSize
    Automatically sizes the table.
 
    .PARAMETER DisableAutoWidthOptimization
    Disables auto width optimization.
 
    .PARAMETER SearchPane
    Enables search pane in the table.
 
    .PARAMETER SearchPaneLocation
    Specifies the location of the search pane.
 
    .PARAMETER SearchBuilder
    Enables search builder in the table.
 
    .PARAMETER SearchBuilderLocation
    Specifies the location of the search builder.
 
    .PARAMETER DataStore
    Stores data for the table.
 
    .PARAMETER DataTableID
    The ID of the data table.
 
    .PARAMETER DataStoreID
    The ID of the data store.
 
    .PARAMETER Transpose
    Transpose table. This is useful when you have objects and you want to transpose them.
 
    .PARAMETER TransposeProperty
    Transpose table based on property. By default it's "Object X". This makes sense if you have unique value per object that you want to transpose table based on.
 
    .PARAMETER TransposeName
    Name of the column that will be used per object to transpose table. By default it's "Object X", "Object Y", "Object Z" etc.
 
    .PARAMETER TransposeLegacy
    Use old method of transposing table. This is useful when you have objects and you want to transpose them, using legacy method.
 
    .PARAMETER OverwriteDOM
    Overwrites the DOM structure.
 
    .PARAMETER SearchHighlight
    Highlights search results.
 
    .PARAMETER AlphabetSearch
    Enables alphabet search.
 
    .PARAMETER FuzzySearch
    Enables fuzzy search.
 
    .PARAMETER FuzzySearchSmartToggle
    Toggles smart fuzzy search.
 
    .PARAMETER FlattenObject
    Flattens the object for display.
 
    .PARAMETER FlattenDepth
    The depth to flatten the object.
 
    .PARAMETER PrettifyObject
    Forces object to be preprocessed before passing to HTML.
    This is useful when converting object with arrays or when there is a need to display DateTime in different format.
    This is mostly useful for email tables or when using DataStore HTML.
 
    .PARAMETER PrettifyObjectSeparator
    Define separator for prettified array object. Default is ", ".
 
    .PARAMETER PrettifyObjectDateTimeFormat
    DateTime format for prettified object.
 
    .EXAMPLE
    $data = @(
        [PSCustomObject]@{ Name = 'John'; Age = 30 },
        [PSCustomObject]@{ Name = 'Jane'; Age = 25 }
    )
    New-HTMLTable -DataTable $data -Title 'User Information' -Buttons @('copyHtml5', 'excelHtml5') -PagingStyle 'full_numbers' -PagingOptions @(10, 25, 50) -DefaultSortColumn @('Name') -DefaultSortOrder @('Ascending')
 
    .NOTES
    This function is designed to create customizable HTML tables for various data representation needs.
    #>

    [alias('Table', 'EmailTable')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $HTML,
        [Parameter(Mandatory = $false, Position = 1)][ScriptBlock] $PreContent,
        [Parameter(Mandatory = $false, Position = 2)][ScriptBlock] $PostContent,
        [alias('ArrayOfObjects', 'Object', 'Table')][Array] $DataTable,
        [string] $Title,
        [string[]][ValidateSet('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'pageLength', 'print', 'searchPanes', 'searchBuilder', 'columnVisibility')] $Buttons = @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'pageLength', 'searchBuilder'),
        [string[]][ValidateSet('numbers', 'simple', 'simple_numbers', 'full', 'full_numbers', 'first_last_numbers')] $PagingStyle = 'full_numbers',
        [int[]]$PagingOptions = @(15, 25, 50, 100),
        [int] $PagingLength,
        [switch]$DisablePaging,
        [switch]$DisableOrdering,
        [switch]$DisableInfo,
        [switch]$HideFooter,
        [alias('DisableButtons')][switch]$HideButtons,
        [switch]$DisableProcessing,
        [switch]$DisableResponsiveTable,
        [switch]$DisableSelect,
        [switch]$DisableStateSave,
        [switch]$DisableSearch,
        [switch]$OrderMulti,
        [switch]$Filtering,
        [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom',
        [string[]][ValidateSet('display', 'cell-border', 'compact', 'hover', 'nowrap', 'order-column', 'row-border', 'stripe')] $Style = @('display', 'compact'),
        [switch]$Simplify,
        [string]$TextWhenNoData = 'No data available to display.',
        [int] $ScreenSizePercent = 0,
        [string[]] $DefaultSortColumn,
        [int[]] $DefaultSortIndex,
        [ValidateSet('Ascending', 'Descending')][string[]] $DefaultSortOrder = 'Ascending',
        [string[]] $DateTimeSortingFormat,
        [alias('Search')][string]$Find,
        [switch] $InvokeHTMLTags,
        [switch] $DisableNewLine,
        [switch] $EnableKeys,
        [switch] $EnableColumnReorder,
        [switch] $EnableRowReorder,
        [switch] $EnableAutoFill,
        [switch] $EnableScroller,
        [switch] $ScrollX,
        [switch] $ScrollY,
        [int] $ScrollSizeY = 500,
        [switch]$ScrollCollapse,
        [int] $FreezeColumnsLeft,
        [int] $FreezeColumnsRight,
        [switch] $FixedHeader,
        [switch] $FixedFooter,
        [string[]] $ResponsivePriorityOrder,
        [int[]] $ResponsivePriorityOrderIndex,
        [string[]] $PriorityProperties,
        [string[]] $IncludeProperty,
        [string[]] $ExcludeProperty,
        [switch] $ImmediatelyShowHiddenDetails,
        [alias('RemoveShowButton')][switch] $HideShowButton,
        [switch] $AllProperties,
        [switch] $SkipProperties,
        [switch] $Compare,
        [Array] $CompareNames,
        [alias('CompareWithColors')][switch] $HighlightDifferences,
        [int] $First,
        [int] $Last,
        [alias('Replace')][Array] $CompareReplace,
        [alias('RegularExpression')][switch]$SearchRegularExpression,
        [ValidateSet('normal', 'break-all', 'keep-all', 'break-word')][string] $WordBreak = 'normal',
        [switch] $AutoSize,
        [switch] $DisableAutoWidthOptimization,
        [switch] $SearchPane,
        [ValidateSet('top', 'bottom')][string] $SearchPaneLocation = 'top',
        [switch] $SearchBuilder,
        [ValidateSet('top', 'bottom')][string] $SearchBuilderLocation = 'top',
        [ValidateSet('HTML', 'JavaScript', 'AjaxJSON')][string] $DataStore,
        [alias('DataTableName')][string] $DataTableID,
        [string] $DataStoreID,
        [switch] $Transpose,
        [string] $TransposeProperty,
        [string] $TransposeName,
        [switch] $TransposeLegacy,
        [string] $OverwriteDOM,
        [switch] $SearchHighlight,
        [switch] $AlphabetSearch,
        [switch] $FuzzySearch,
        [switch] $FuzzySearchSmartToggle,
        [switch] $FlattenObject,
        [switch] $PrettifyObject,
        [string] $PrettifyObjectSeparator = ", ",
        [string] $PrettifyObjectDateTimeFormat,
        [int] $FlattenDepth
    )
    if (-not $Script:HTMLSchema.Features) {
        Write-Warning 'New-HTMLTable - Creation of HTML aborted. Most likely New-HTML is missing.'
        Exit
    }
    $Script:HTMLSchema.Features.MainFlex = $true

    if (-not $DataTableID) {

        $DataTableID = "DT-$(Get-RandomStringName -Size 8 -LettersOnly)" 
    }

    if ($Simplify) {
        $Script:HTMLSchema['TableSimplify'] = $true
    } else {
        $Script:HTMLSchema['TableSimplify'] = $false
    }

    if ($HideFooter -and $Filtering -and $FilteringLocation -notin @('Both', 'Top')) {
        Write-Warning 'New-HTMLTable - Hiding footer while filtering is requested without specifying FilteringLocation to Top or Both.'
    }

    if ($DataStore -eq '') {
        if ($Script:HTMLSchema.TableOptions.DataStore) {

            $DataStore = $Script:HTMLSchema.TableOptions.DataStore
        } else {

            $DataStore = 'HTML'
        }
    }
    if ($DataStore -eq 'AjaxJSON') {
        if (-not $Script:HTMLSchema['TableOptions']['Folder']) {
            Write-Warning "New-HTMLTable - FilePath wasn't used in New-HTML. It's required for Hosted Solution."
            return
        }
    }
    if ($DataStoreID -and $DataStore -ne 'JavaScript') {
        Write-Warning "New-HTMLTable - Using DataStoreID is only supported if DataStore is JavaScript. It doesn't do anything without it"
    }

    $ConditionalFormatting = [System.Collections.Generic.List[PSCustomObject]]::new()
    $ConditionalFormattingInline = [System.Collections.Generic.List[PSCustomObject]]::new()
    $CustomButtons = [System.Collections.Generic.List[PSCustomObject]]::new()
    $HeaderRows = [System.Collections.Generic.List[PSCustomObject]]::new()
    $HeaderStyle = [System.Collections.Generic.List[PSCustomObject]]::new()
    $HeaderTop = [System.Collections.Generic.List[PSCustomObject]]::new()
    $HeaderResponsiveOperations = [System.Collections.Generic.List[PSCustomObject]]::new()
    $ContentRows = [System.Collections.Generic.List[PSCustomObject]]::new()
    $ContentStyle = [System.Collections.Generic.List[PSCustomObject]]::new()
    $ContentTop = [System.Collections.Generic.List[PSCustomObject]]::new()
    $ReplaceCompare = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
    $TableColumnOptions = [System.Collections.Generic.List[PSCustomObject]]::new()
    $TableEvents = [System.Collections.Generic.List[PSCustomObject]]::new()
    $TablePercentageBar = [System.Collections.Generic.List[PSCustomObject]]::new()
    $TableAlphabetSearch = [ordered]@{}
    $TableLanguage = [ordered]@{}

    $ColumnDefinitionList = [System.Collections.Generic.List[PSCustomObject]]::New()
    $RowGrouping = @{ }

    if ($DataTable.Count -gt 0) {
        if ($DataTable[0] -is [System.Collections.ICollection]) {
            $DataTable = foreach ($D in $DataTable) {
                $D
            }
        }
    }

    if ($FlattenObject) {
        if ($FlattenDepth) {
            $DataTable = ConvertTo-FlatObject -Objects $DataTable -Depth $FlattenDepth
        } else {
            $DataTable = ConvertTo-FlatObject -Objects $DataTable
        }
    }

    if ($PSBoundParameters.ContainsKey('PrettifyObject')) {
        if ($PrettifyObject) {
            $convertToPrettyObjectSplat = @{
                Object    = $DataTable
                ArrayJoin = $true
            }
            if ($PrettifyObjectDateTimeFormat) {
                $convertToPrettyObjectSplat['DateTimeFormat'] = $PrettifyObjectDateTimeFormat
            }
            if ($PrettifyObjectSeparator) {
                $convertToPrettyObjectSplat['ArrayJoinString'] = $PrettifyObjectSeparator
            }
            $DataTable = ConvertTo-PrettyObject @convertToPrettyObjectSplat
        }
    } elseif ($Script:HTMLSchema['TableOptions']['DataStoreOptions'].PrettifyObject) {
        $convertToPrettyObjectSplat = @{
            Object    = $DataTable
            ArrayJoin = $true
        }
        if ($Script:HTMLSchema['TableOptions']['DataStoreOptions'].PrettifyObjectDateTimeFormat) {
            $convertToPrettyObjectSplat['DateTimeFormat'] = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].PrettifyObjectDateTimeFormat
        }
        if ($Script:HTMLSchema['TableOptions']['DataStoreOptions'].PrettifyObjectSeparator) {
            $convertToPrettyObjectSplat['ArrayJoinString'] = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].PrettifyObjectSeparator
        }
        $DataTable = ConvertTo-PrettyObject @convertToPrettyObjectSplat
    }

    if ($HTML) {
        [Array] $Output = & $HTML

        if ($Output.Count -gt 0) {
            foreach ($Parameters in $Output) {
                if ($Parameters.Type -eq 'TableButtonPDF') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableButtonCSV') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableButtonPageLength') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableButtonExcel') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableButtonPDF') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableButtonPrint') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableButtonCopy') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableButtonSearchBuilder') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableButtonColumnVisibility') {
                    $CustomButtons.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableCondition') {
                    $ConditionalFormatting.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableConditionGroup') {
                    $ConditionalFormatting.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableHeaderMerge') {
                    $HeaderRows.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableHeaderStyle') {
                    $HeaderStyle.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableHeaderFullRow') {
                    $HeaderTop.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableContentMerge') {
                    $ContentRows.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableContentStyle') {
                    $ContentStyle.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableContentFullRow') {
                    $ContentTop.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableConditionInline') {
                    $ConditionalFormattingInline.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableConditionGroupInline') {
                    $ConditionalFormattingInline.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableHeaderResponsiveOperations') {
                    $HeaderResponsiveOperations.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableReplaceCompare') {
                    $ReplaceCompare.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableRowGrouping') {
                    $RowGrouping = $Parameters.Output
                } elseif ($Parameters.Type -eq 'TableColumnOption') {
                    $TableColumnOptions.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableEvent') {
                    $TableEvents.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TablePercentageBar') {
                    $TablePercentageBar.Add($Parameters.Output)
                } elseif ($Parameters.Type -eq 'TableAlphabetSearch') {
                    $TableAlphabetSearch = $Parameters.Output
                } elseif ($Parameters.Type -eq 'TableLanguage') {
                    $TableLanguage = $Parameters.Output
                }
            }
        }
    }

    if (-not $AutoSize) {
        [string] $Width = '100%'
    }

    if ($Compare) {
        $Splitter = "`r`n"

        if ($ReplaceCompare) {
            foreach ($R in $CompareReplace) {
                $ReplaceCompare.Add($R)
            }
        }

        $DataTable = Compare-MultipleObjects -Objects $DataTable -Summary -Splitter $Splitter -FormatOutput -AllProperties:$AllProperties -SkipProperties:$SkipProperties -Replace $ReplaceCompare -ExcludeProperty $ExcludeProperty -Property $IncludeProperty -ObjectsName $CompareNames

        if ($HighlightDifferences) {
            $Highlight = for ($i = 0; $i -lt $DataTable.Count; $i++) {
                if ($DataTable[$i].Status -eq $false) {

                    foreach ($DifferenceColumn in $DataTable[$i].Different) {
                        $DataSame = $DataTable[$i]."$DifferenceColumn-Same" -join $Splitter
                        $DataAdd = $DataTable[$i]."$DifferenceColumn-Add" -join $Splitter
                        $DataRemove = $DataTable[$i]."$DifferenceColumn-Remove" -join $Splitter

                        if ($DataSame -ne '') {
                            $DataSame = "$DataSame$Splitter"
                        }
                        if ($DataAdd -ne '') {
                            $DataAdd = "$DataAdd$Splitter"
                        }
                        if ($DataRemove -ne '') {
                            $DataRemove = "$DataRemove$Splitter"
                        }
                        $Text = New-HTMLText -Text $DataSame, $DataRemove, $DataAdd -Color Black, Red, Blue -TextDecoration none, line-through, none -FontWeight normal, bold, bold
                        New-TableContent -ColumnName "$DifferenceColumn" -RowIndex ($i + 1) -Text "$Text"
                    }
                } else {

                }
            }
        }
        $Properties = Select-Properties -Objects $DataTable -ExcludeProperty '*-Same', '*-Add', '*-Remove', 'Same', 'Different'
        $DataTable = $DataTable | Select-Object -Property $Properties

        if ($HighlightDifferences) {
            foreach ($Parameter in $Highlight.Output) {
                $ContentStyle.Add($Parameter)
            }
            $ConditionalFormatting.Add((New-TableCondition -Name "Status" -Operator eq -Value $true -ComparisonType bool -BackgroundColor MediumSpringGreen).Output)
            $ConditionalFormatting.Add((New-TableCondition -Name "Status" -Operator eq -Value $false -ComparisonType bool -BackgroundColor Salmon).Output)
            $ConditionalFormatting.Add((New-TableCondition -Name "Status" -Operator eq -Value '' -ComparisonType String -BackgroundColor Cumulus).Output)
        }
    }

    if ($null -ne $DataTable -and $null -eq $DataTable[0] -and $DataTable.Count -gt 0) {
        Write-Warning "New-HTMLTable - First element of array is null, but there are more elements in array. Reprocessing DataTable to remove null values."
        [Array] $DataTable = foreach ($D in $DataTable) {
            if ($null -ne $D) {
                $D
            }
        }
    }
    if ($null -eq $DataTable -or $DataTable.Count -eq 0) {
        $Filtering = $false 
        $HideFooter = $true
        if ($TableLanguage -and $TableLanguage.emptyTable) {

            [Array] $DataTable = $TableLanguage.emptyTable
            $TableLanguage.Remove('emptyTable')
        } else {
            [Array] $DataTable = $TextWhenNoData
        }
    }

    if ($DataTable[0] -isnot [System.Collections.IDictionary]) {
        if ($AllProperties) {
            $Properties = Select-Properties -Objects $DataTable -AllProperties:$AllProperties -Property $IncludeProperty -ExcludeProperty $ExcludeProperty
            if ($Properties -ne '*') {
                $DataTable = $DataTable | Select-Object -Property $Properties
            }
        } else {

            if ($DataStore -in 'JavaScript', 'AjaxJSON') {
                $Properties = Select-Properties -Objects $DataTable -Property $IncludeProperty -ExcludeProperty $ExcludeProperty
                if ($Properties -ne '*') {
                    $DataTable = $DataTable | Select-Object -Property $Properties
                }
            } else {
                if ($IncludeProperty -or $ExcludeProperty) {
                    $Properties = Select-Properties -Objects $DataTable -Property $IncludeProperty -ExcludeProperty $ExcludeProperty
                    if ($Properties -ne '*') {
                        $DataTable = $DataTable | Select-Object -Property $Properties
                    }
                }
            }
        }
    }

    if ($Transpose) {

        $formatTransposeTableSplat = @{
            AllObjects = $DataTable
        }
        if ($TransposeProperty) {
            $formatTransposeTableSplat['Property'] = $TransposeProperty
        }
        if ($TransposeName) {
            $formatTransposeTableSplat['Name'] = $TransposeName
        }
        if ($TransposeLegacy) {
            $formatTransposeTableSplat['Legacy'] = $true
        }
        $DataTable = Format-TransposeTable @formatTransposeTableSplat
    }

    if ($PriorityProperties) {
        if ($DataTable.Count -gt 0) {
            $Properties = $DataTable[0].PSObject.Properties.Name

            $RemainingProperties = foreach ($Property in $Properties) {
                if ($PriorityProperties -notcontains $Property) {
                    $Property
                }
            }
            $BoundedProperties = $PriorityProperties + $RemainingProperties
            $DataTable = $DataTable | Select-Object -Property $BoundedProperties
        }
    }

    $PagingOptions = $PagingOptions | Sort-Object -Unique

    $Options = [ordered] @{
        'dom'            = $null
        "searchFade"     = $false

        "scrollCollapse" = $ScrollCollapse.IsPresent
        "ordering"       = -not $DisableOrdering.IsPresent
        "order"          = @() 
        "rowGroup"       = ''
        "info"           = -not $DisableInfo.IsPresent
        "procesing"      = -not $DisableProcessing.IsPresent
        "select"         = -not $DisableSelect.IsPresent
        "searching"      = -not $DisableSearch.IsPresent
        "stateSave"      = -not $DisableStateSave.IsPresent
        "paging"         = -not $DisablePaging

        "pagingType"     = $PagingStyle
        "lengthMenu"     = @(
            , @($PagingOptions + (-1))
            , @($PagingOptions + "All")
        )
    }
    if ($PagingLength) {

        $Options['pageLength'] = $PagingLength
    } elseif ($PagingOptions[0] -ne 15) {

        $Options['pageLength'] = $PagingOptions[0]
    }

    if ($TableLanguage) {
        $Options['language'] = $TableLanguage
    }
    if ($AlphabetSearch -or $TableAlphabetSearch.Keys.Count -gt 0) {
        $Script:HTMLSchema.Features.DataTablesSearchAlphabet = $true
    }
    if ($SearchBuilder) {
        $SearchBuilderEnabled = $true
        $Script:HTMLSchema.Features.DataTablesDateTime = $true
        $Script:HTMLSchema.Features.DataTablesSearchBuilder = $true
    } else {
        $SearchBuilderEnabled = $false
    }
    if ($Buttons -contains 'searchBuilder') {

        $Script:HTMLSchema.Features.DataTablesDateTime = $true
        $Script:HTMLSchema.Features.DataTablesSearchBuilder = $true
        $Script:HTMLSchema.Features.DataTablesSearchPanesButton = $true
        if ($SearchBuilderEnabled) {
            Write-Warning -Message "New-HTMLTable - Using SearchBuilder option and SearchBuilder button won't work properly. Only one will work. Disabling SearchBuiler."
            $SearchBuilderEnabled = $false
        }
    }
    if ($FuzzySearch -or $FuzzySearchSmartToggle) {
        $Script:HTMLSchema.Features.DataTablesFuzzySearch = $true
        if ($FuzzySearch) {
            if ($FuzzySearchSmartToggle) {
                $Options['fuzzySearch'] = @{
                    toggleSmart = $true
                }
            } else {
                $Options['fuzzySearch'] = $true
            }
        } else {
            $Options['fuzzySearch'] = @{
                toggleSmart = $true
            }
        }
    }
    if ($SearchPane) {
        $Script:HTMLSchema.Features.DataTablesSearchPanes = $true
    }
    if ($OverwriteDOM) {

        $Options['dom'] = $OverwriteDOM
    } else {
        $DOM = 'Bfrtip'
        if ($AlphabetSearch -or $TableAlphabetSearch.Keys.Count -gt 0) {
            $DOM = "A$($Dom)"
        }
        if ($SearchBuilderEnabled -and $SearchPane) {
            if ($SearchPaneLocation -eq 'top' -and $SearchBuilderLocation -eq 'top') {
                $Options['dom'] = "QP$($DOM)"
            } elseif ($SearchPaneLocation -eq 'top' -and $SearchBuilderLocation -eq 'bottom') {
                $Options['dom'] = "P$($DOM)Q"
            } elseif ($SearchPaneLocation -eq 'bottom' -and $SearchBuilderLocation -eq 'top') {
                $Options['dom'] = "Q$($DOM)P"
            } elseif ($SearchPaneLocation -eq 'bottom' -and $SearchBuilderLocation -eq 'bottom') {
                $Options['dom'] = "$($DOM)QP"
            }
        } elseif ($SearchBuilderEnabled) {
            if ($SearchBuilderLocation -eq 'top') {
                $Options['dom'] = "Q$($DOM)"
            } else {
                $Options['dom'] = "$($DOM)Q"
            }
        } elseif ($SearchPane) {

            if ($SearchPaneLocation -eq 'top') {
                $Options['dom'] = "P$($DOM)"
            } else {
                $Options['dom'] = "$($DOM)P"
            }
        } else {
            $Options['dom'] = "$($DOM)"
        }
    }
    if ($Buttons -contains 'searchPanes') {

        $Script:HTMLSchema.Features.DataTablesSearchPanes = $true
        $Script:HTMLSchema.Features.DataTablesSearchPanesButton = $true
    }
    if ($EnableKeys) {
        $Script:HTMLSchema.Features.DataTablesKeyTable = $true
        $Options['keys'] = $true

    }
    if ($EnableAutoFill) {
        $Script:HTMLSchema.Features.DataTablesAutoFill = $true
        $Options['autoFill'] = $true
    }
    if ($SearchHighlight) {
        $Script:HTMLSchema.Features.DataTablesSearchHighlight = $true
        $Options['searchHighlight'] = $true
    }

    $Table = $null
    if ($DataTable[0] -is [System.Collections.IDictionary]) {
        [Array] $Table = foreach ($_ in $DataTable) {
            $_.GetEnumerator() | Select-Object Name, Value
        }
        $ObjectProperties = 'Name', 'Value'
    } elseif ($DataTable[0].GetType().Name -match 'bool|byte|char|datetime|decimal|double|ExcelHyperLink|float|int|long|sbyte|short|string|timespan|uint|ulong|URI|ushort') {
        [Array] $Table = $DataTable | ForEach-Object { [PSCustomObject]@{ 'Name' = $_ } }
        $ObjectProperties = 'Name'
    } else {
        [Array] $Table = $DataTable
        $ObjectProperties = $DataTable[0].PSObject.Properties.Name
    }

    if ($DataStore -eq 'HTML') {

        if ($Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoin -or $Script:HTMLSchema['TableOptions']['DataStoreOptions'].DateTimeFormat) {
            foreach ($Row in $Table) {
                foreach ($Name in $Row.PSObject.Properties.Name) {
                    if ($Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoin -and ($Row.$Name -is [System.Collections.IList] -or $Row.$Name -is [System.Collections.ReadOnlyCollectionBase])) {
                        $Row.$Name = $Row.$Name -join $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoinString
                    } elseif ($Script:HTMLSchema['TableOptions']['DataStoreOptions'].DateTimeFormat -and $Row.$Name -is [DateTime]) {
                        $Row.$Name = $($Row.$Name).ToString($Script:HTMLSchema['TableOptions']['DataStoreOptions'].DateTimeFormat)
                    }
                }
            }
        }
        $Table = $Table | ConvertTo-Html -Fragment | Select-Object -SkipLast 1 | Select-Object -Skip 2 
        [string] $Header = $Table | Select-Object -First 1 
        [string[]] $HeaderNames = $Header -replace '</th></tr>' -replace '<tr><th>' -split '</th><th>'
        if ($HeaderNames -eq '*') {

            $Header = $Header.Replace('*', $ObjectProperties)
            $HeaderNames = $ObjectProperties
        }

        if ($ContentRows.Capacity -gt 0 -or $ContentStyle.Count -gt 0 -or $ContentTop.Count -gt 0 -or $ConditionalFormattingInline.Count -gt 0) {
            $Table = Add-TableContent -ContentRows $ContentRows -ContentStyle $ContentStyle -ContentTop $ContentTop -ContentFormattingInline $ConditionalFormattingInline -Table $Table -HeaderNames $HeaderNames
        }
        $Table = $Table | Select-Object -Skip 1 
    } elseif ($DataStore -eq 'AjaxJSON') {

        [string] $Header = $Table | ConvertTo-Html -Fragment | Select-Object -Skip 2 -First 1
        [string[]] $HeaderNames = $Header -replace '</th></tr>' -replace '<tr><th>' -split '</th><th>'
        if ($HeaderNames -eq '*') {

            $Header = $Header.Replace('*', $ObjectProperties)
            $HeaderNames = $ObjectProperties
        }
        New-TableServerSide -DataTable $Table -DataTableID $DataTableID -Options $Options -HeaderNames $HeaderNames
        $Table = $null
    } elseif ($DataStore -eq 'JavaScript') {

        [string] $Header = $Table | ConvertTo-Html -Fragment | Select-Object -Skip 2 -First 1
        [string[]] $HeaderNames = $Header -replace '</th></tr>' -replace '<tr><th>' -split '</th><th>'
        if ($HeaderNames -eq '*') {

            $Header = $Header.Replace('*', $ObjectProperties)
            $HeaderNames = $ObjectProperties
        }
        New-TableJavaScript -HeaderNames $HeaderNames -Options $Options -NewLineFormat $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormatProperty

    }

    $AddedHeader = Add-TableHeader -HeaderRows $HeaderRows -HeaderNames $HeaderNames -HeaderStyle $HeaderStyle -HeaderTop $HeaderTop -HeaderResponsiveOperations $HeaderResponsiveOperations

    if ($TableAlphabetSearch.Keys.Count -gt 0) {
        $Options['alphabet'] = @{}
        if ($TableAlphabetSearch.caseSensitive) {
            $Options['alphabet']['caseSensitive'] = $true
        }
        if ($TableAlphabetSearch.numbers) {
            $Options['alphabet']['numbers'] = $true
        }
        if ($null -ne $TableAlphabetSearch.ColumnName) {
            $TableAlphabetSearch.ColumnID = ($HeaderNames).ToLower().IndexOf($TableAlphabetSearch.ColumnName.ToLower())
        }
        if ($null -ne $TableAlphabetSearch.ColumnID) {
            $Options['alphabet']['column'] = $TableAlphabetSearch.ColumnID
        }
    }

    if (-not $Script:HTMLSchema['TableSimplify'] -and -not $HideButtons) {
        $Script:HTMLSchema.Features.DataTablesButtons = $true
        $Options['buttons'] = @(
            if ($CustomButtons) {
                foreach ($Button in $CustomButtons) {
                    if (-not $Button.Title -and $Title) {
                        $Button.title = $Title
                    }
                    $Button
                }
            } else {
                foreach ($button in $Buttons) {
                    if ($button -eq 'pdfHtml5') {
                        $ButtonOutput = [ordered] @{
                            extend      = 'pdfHtml5'
                            pageSize    = 'A3'
                            orientation = 'landscape'
                            title       = $Title
                        }
                        $Script:HTMLSchema.Features.DataTablesButtonsPDF = $true
                    } elseif ($button -eq 'pageLength') {
                        if (-not $DisablePaging -and -not $ScrollY) {
                            $ButtonOutput = @{
                                extend = $button
                            }
                        } else {
                            $ButtonOutput = $null
                        }
                    } elseif ($button -eq 'excelHtml5') {
                        $Script:HTMLSchema.Features.DataTablesButtonsHTML5 = $true
                        $Script:HTMLSchema.Features.DataTablesButtonsExcel = $true
                        $ButtonOutput = [ordered] @{
                            extend        = $button
                            title         = $Title
                            exportOptions = @{

                                format = "findExportOptions"
                            }
                        }
                    } elseif ($button -eq 'copyHtml5') {
                        $Script:HTMLSchema.Features.DataTablesButtonsHTML5 = $true
                        $ButtonOutput = [ordered] @{
                            extend = $button
                            title  = $Title
                        }
                    } elseif ($button -eq 'csvHtml5') {
                        $Script:HTMLSchema.Features.DataTablesButtonsHTML5 = $true
                        $ButtonOutput = [ordered] @{
                            extend         = $button
                            title          = $Title
                            text           = 'CSV'
                            charset        = 'utf-8'
                            extension      = '.csv'
                            fieldSeparator = ';'
                            fieldBoundary  = ''

                            bom            = $true
                        }
                    } elseif ($button -eq 'searchPanes') {
                        $Script:HTMLSchema.Features.DataTablesSearchPanes = $true
                        $ButtonOutput = [ordered] @{
                            extend = $button
                            title  = $Title
                        }
                    } elseif ($button -eq 'print') {
                        $Script:HTMLSchema.Features.DataTablesButtonsPrint = $true
                        $ButtonOutput = [ordered] @{
                            extend = $button
                            title  = $Title
                        }
                    } elseif ($button -eq 'searchBuilder') {
                        $Script:HTMLSchema.Features.DataTablesDateTime = $true
                        $Script:HTMLSchema.Features.DataTablesSearchBuilder = $true
                        $ButtonOutput = [ordered] @{
                            extend = $button
                            title  = $Title

                        }
                    } elseif ($button -eq 'columnVisibility') {
                        $Script:HTMLSchema.Features.DataTablesButtonsColVis = $true
                        $Script:HTMLSchema.Features.DataTablesButtonsColVis = $true
                        $ButtonOutput = [ordered] @{
                            extend           = 'colvis'
                            title            = $Title
                            collectionLayout = 'dropdown columns'
                            collectionTitle  = 'Visibility control'
                        }
                    } else {
                        $ButtonOutput = [ordered] @{
                            extend = $button
                            title  = $Title
                        }
                    }
                    if ($ButtonOutput) {
                        Remove-EmptyValue -Hashtable $ButtonOutput
                        $ButtonOutput
                    }
                }
            }
        )
    } else {
        $Options['buttons'] = @()
    }
    if ($ScrollX) {
        $Options.'scrollX' = $true

        $DisableResponsiveTable = $true
    }
    if ($ScrollY -or $EnableScroller) {

        $Options.'scrollY' = "$($ScrollSizeY)px"
    }
    if (-not $Script:HTMLSchema['TableSimplify'] -and $EnableScroller) {
        $Script:HTMLSchema.Features.DataTablesScroller = $true
        $Options['scroller'] = @{
            loadingIndicator = $true
        }

    }
    if (-not $Script:HTMLSchema['TableSimplify'] -and $EnableRowReorder) {
        $Script:HTMLSchema.Features.DataTablesRowReorder = $true
        $Options['rowReorder'] = $true
    }

    if (-not $Script:HTMLSchema['TableSimplify'] -and ($FreezeColumnsLeft -or $FreezeColumnsRight)) {
        $Script:HTMLSchema.Features.DataTablesFixedColumn = $true
        $Options['fixedColumns'] = [ordered] @{ }
        if ($FreezeColumnsLeft) {
            $Options.fixedColumns.leftColumns = $FreezeColumnsLeft
        }
        if ($FreezeColumnsRight) {
            $Options.fixedColumns.rightColumns = $FreezeColumnsRight
        }
    }
    if (-not $Script:HTMLSchema['TableSimplify'] -and ($FixedHeader -or $FixedFooter)) {
        $Script:HTMLSchema.Features.DataTablesFixedHeader = $true

        $Options['fixedHeader'] = [ordered] @{
            header = $FixedHeader.IsPresent
            footer = $FixedFooter.IsPresent
        }
    }

    if (-not $Script:HTMLSchema['TableSimplify'] -and -not $DisableResponsiveTable) {
        $Script:HTMLSchema.Features.DataTablesResponsive = $true
        $Options["responsive"] = @{ }
        $Options["responsive"]['details'] = @{ }
        if ($ImmediatelyShowHiddenDetails) {
            $Options["responsive"]['details']['display'] = '$.fn.dataTable.Responsive.display.childRowImmediate'
        }
        if ($HideShowButton) {
            $Options["responsive"]['details']['type'] = 'none' 
        } else {
            $Options["responsive"]['details']['type'] = 'inline' 
        }
    } else {

    }

    if ($OrderMulti) {
        $Options.orderMulti = $OrderMulti.IsPresent
    }
    if ($Find -ne '') {
        $Options.search = @{
            search = $Find
        }
    }

    [int] $RowGroupingColumnID = -1
    if ($RowGrouping.Count -gt 0) {
        if ($RowGrouping.Name) {
            $RowGroupingColumnID = ($HeaderNames).ToLower().IndexOf($RowGrouping.Name.ToLower())
        } else {
            $RowGroupingColumnID = $RowGrouping.ColumnID
        }
        if ($RowGroupingColumnID -ne -1) {
            $ColumnsOrder = , @($RowGroupingColumnID, $RowGrouping.Sorting)
            if ($DefaultSortColumn.Count -gt 0 -or $DefaultSortIndex.Count -gt 0) {
                Write-Warning 'New-HTMLTable - Row grouping sorting overwrites default sorting.'
            }
        } else {
            Write-Warning 'New-HTMLTable - Row grouping disabled. Column name/id not found.'
        }
    } else {
        $SortingTranslate = [ordered] @{
            'Ascending'  = 'asc'
            'Descending' = 'desc'
        }
        [Array] $TranslatedDefaultSortOrder = foreach ($Order in $DefaultSortOrder) {
            $SortingTranslate[$Order]
        }

        $Sort = $TranslatedDefaultSortOrder[0]
        if ($DefaultSortColumn.Count -gt 0) {

            $ColumnsOrder = foreach ($Column in $DefaultSortColumn) {
                $DefaultSortingNumber = ($HeaderNames).ToLower().IndexOf($Column.ToLower())
                $ColumnSort = $Sort
                $ColumnSortIndex = $DefaultSortColumn.IndexOf( $Column )
                if ( $TranslatedDefaultSortOrder.count -ge 1 + $ColumnSortIndex ) {
                    $ColumnSort = $TranslatedDefaultSortOrder[ $ColumnSortIndex ]
                }
                if ($DefaultSortingNumber -ne -1) {
                    , @($DefaultSortingNumber, $ColumnSort)
                }
            }

        } elseif ($DefaultSortIndex.Count -gt 0) {

            $ColumnsOrder = foreach ($Column in $DefaultSortIndex) {
                $ColumnSort = $Sort
                $ColumnSortIndex = $DefaultSortIndex.IndexOf( $Column )
                if ( $TranslatedDefaultSortOrder.Count -ge 1 + $ColumnSortIndex ) {
                    $ColumnSort = $TranslatedDefaultSortOrder[ $ColumnSortIndex ]
                }
                if ($Column -ne -1) {
                    , @($Column, $ColumnSort)
                }
            }
        }
    }
    if ($ColumnsOrder.Count -gt 0) {
        $Options."order" = @($ColumnsOrder)

    }
    if (-not $Script:HTMLSchema['TableSimplify'] -and $EnableColumnReorder -and $ColumnsOrder.Count -eq 0) {
        $Script:HTMLSchema.Features.DataTablesColReorder = $true
        $Options["colReorder"] = $true
    }

    if ($ScreenSizePercent -gt 0) {
        $Options."scrollY" = "$($ScreenSizePercent)vh"
    }
    if ($null -ne $ConditionalFormatting -and $ConditionalFormatting.Count -gt 0) {
        $Options.createdRow = ''
    }

    if ($ResponsivePriorityOrderIndex -or $ResponsivePriorityOrder) {

        $PriorityOrder = 0

        [Array] $PriorityOrderBinding = @(
            foreach ($_ in $ResponsivePriorityOrder) {
                $Index = [array]::indexof($HeaderNames.ToUpper(), $_.ToUpper())
                if ($Index -ne -1) {
                    [pscustomobject]@{ responsivePriority = 0; targets = $Index }
                }
            }
            foreach ($_ in $ResponsivePriorityOrderIndex) {
                [pscustomobject]@{ responsivePriority = 0; targets = $_ }
            }
        )

        foreach ($_ in $PriorityOrderBinding) {
            $PriorityOrder++
            $_.responsivePriority = $PriorityOrder
            $ColumnDefinitionList.Add($_)
        }
    }

    If ($TableColumnOptions.Count -gt 0) {
        foreach ($_ in $TableColumnOptions) {
            $ColumnDefinitionList.Add($_)
        }
    }

    if ($TablePercentageBar.Count -gt 0) {
        $Script:HTMLSchema.Features.DataTablesPercentageBars = $true
        foreach ($Bar in $TablePercentageBar) {
            $ColumnDefinitionList.Add($(New-TablePercentageBarInternal @Bar))
        }
    }

    If ($ColumnDefinitionList.Count -gt 0) {
        $Options.columnDefs = $ColumnDefinitionList.ToArray()
    }

    If ($DisableAutoWidthOptimization) {
        $Options.autoWidth = $false
    }

    $Options = $Options | ConvertTo-Json -Depth 6 

    $Options = $Options -replace '"(\$\.fn\.dataTable\.Responsive\.display\.childRowImmediate)"', '$1'
    $Options = $Options -replace '"(\$\.fn\.dataTable\.render\.percentBar\(.+\))"', '$1'

    $ExportExcelOptions = @'
    {
        body: function (data, row, column, node) {
            data = $('<p>' + data + '</p>').text(); return $.isNumeric(data.replace(',', '.')) ? data.replace(',', '.') : data;
        }
    }
'@

    $Options = $Options.Replace('"findExportOptions"', $ExportExcelOptions)

    if ($DataStore -eq 'JavaScript') {

        if ($DataStoreID) {

            $Options = $Options.Replace('"markerForDataReplacement"', $DataStoreID)

            if (-not $Script:HTMLSchema.CustomFooterJS[$DataStoreID]) {
                $convertToPrettyObjectSplat = [ordered] @{

                    NumberAsString        = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NumberAsString
                    BoolAsString          = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].BoolAsString
                    DateTimeFormat        = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].DateTimeFormat
                    NewLineFormat         = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormat
                    NewLineFormatProperty = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormatProperty
                    Force                 = $true
                    ArrayJoin             = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoin
                    ArrayJoinString       = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoinString
                }
                $DataToInsert = $Table | ConvertTo-PrettyObject @convertToPrettyObjectSplat | ConvertTo-Json
                if ($DataToInsert.StartsWith('[')) {
                    $Script:HTMLSchema.CustomFooterJS[$DataStoreID] = "var $DataStoreID = $DataToInsert;"
                } else {
                    $Script:HTMLSchema.CustomFooterJS[$DataStoreID] = "var $DataStoreID = [$DataToInsert];"
                }
            }

        } else {
            $convertToPrettyObjectSplat = [ordered] @{

                NumberAsString        = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NumberAsString
                BoolAsString          = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].BoolAsString
                DateTimeFormat        = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].DateTimeFormat
                NewLineFormat         = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormat
                NewLineFormatProperty = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormatProperty
                Force                 = $true
                ArrayJoin             = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoin
                ArrayJoinString       = $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoinString
            }
            $DataToInsert = $Table | ConvertTo-PrettyObject @convertToPrettyObjectSplat | ConvertTo-Json
            if ($DataToInsert.StartsWith('[')) {
                $Options = $Options.Replace('"markerForDataReplacement"', $DataToInsert)
            } else {
                $Options = $Options.Replace('"markerForDataReplacement"', "[$DataToInsert]")
            }
        }

        $Table = $null
    }

    $Options = New-TableConditionalFormatting -Options $Options -ConditionalFormatting $ConditionalFormatting -Header $HeaderNames -DataStore $DataStore

    if ($RowGroupingColumnID -ne -1) {
        $Options = Convert-TableRowGrouping -Options $Options -RowGroupingColumnID $RowGroupingColumnID
        $RowGroupingTop = Add-TableRowGrouping -DataTableName $DataTableID -Top -Settings $RowGrouping
        $RowGroupingBottom = Add-TableRowGrouping -DataTableName $DataTableID -Bottom -Settings $RowGrouping
    }

    [Array] $Tabs = ($Script:HTMLSchema.TabsHeaders | Where-Object { $_.Current -eq $true })
    if ($Tabs.Count -eq 0) {

        $Tab = @{ Active = $true }
    } else {

        $Tab = $Tabs[0]
    }

    if (-not $Script:HTMLSchema['TableSimplify']) {
        $Script:HTMLSchema.Features.Jquery = $true
        $Script:HTMLSchema.Features.DataTables = $true
        $Script:HTMLSchema.Features.DataTablesEmail = $true
        $Script:HTMLSchema.Features.Moment = $true
        if (-not $HideButtons) {

        }

        $TableAttributes = @{ id = $DataTableID; class = "dataTables $($Style -join ' ')"; width = $Width }

        $SortingFormatDateTime = Add-CustomFormatForDatetimeSorting -DateTimeSortingFormat $DateTimeSortingFormat
        $FilteringOutput = Add-TableFiltering -Filtering $Filtering -FilteringLocation $FilteringLocation -DataTableName $DataTableID -SearchRegularExpression:$SearchRegularExpression
        $FilteringTopCode = $FilteringOutput.FilteringTopCode
        $FilteringBottomCode = $FilteringOutput.FilteringBottomCode
        $LoadSavedState = Add-TableState -DataTableName $DataTableID -Filtering $Filtering -FilteringLocation $FilteringLocation -SavedState (-not $DisableStateSave)
        if ($TableEvents.Count -gt 0) {
            $TableEventsCode = Add-TableEvent -Events $TableEvents -HeaderNames $HeaderNames -DataStore $DataStore

            $Script:HTMLSchema.Features.EscapeRegex = $true
        }

        if ($Tab.Active -eq $true) {
            New-HTMLTag -Tag 'script' {
                @"
                `$(document).ready(function() {
                    $SortingFormatDateTime
                    $RowGroupingTop
                    $LoadSavedState
                    $FilteringTopCode
                    // Table code
                    var table = `$('#$DataTableID').DataTable(
                        $($Options)
                    );
                    $FilteringBottomCode
                    $RowGroupingBottom
                    $TableEventsCode
                });
"@

                if ($FixedHeader -or $FixedFooter) {
                    "dataTablesFixedTracker['$DataTableID'] = true;"
                }
            }
        } else {
            [string] $TabName = $Tab.Id
            New-HTMLTag -Tag 'script' {
                @"
                    `$(document).ready(function() {
                        $SortingFormatDateTime
                        $RowGroupingTop
                        `$('.tabs').on('click', 'a', function (event) {
                            if (`$(event.currentTarget).attr("data-id") == "$TabName" && !$.fn.dataTable.isDataTable("#$DataTableID")) {
                                $LoadSavedState
                                $FilteringTopCode
                                // Table code
                                var table = `$('#$DataTableID').DataTable(
                                    $($Options)
                                );
                                $FilteringBottomCode
                            };
                        });
                        $RowGroupingBottom
                    });
"@

                if ($FixedHeader -or $FixedFooter) {
                    "dataTablesFixedTracker['$DataTableID'] = true;"
                }
            }
        }
    } else {
        $TableAttributes = @{ class = 'simplify' }
        $Script:HTMLSchema.Features.DataTablesSimplify = $true
    }

    if ($InvokeHTMLTags) {

        $Table = $Table -replace '&lt;', '<' -replace '&gt;', '>' -replace '&amp;', '&' -replace '&nbsp;', ' ' -replace '&quot;', '"' -replace '&#39;', "'"
    }
    if (-not $DisableNewLine) {

        $Table = $Table -replace '(?m)\s+$', "<BR>"
    }

    if ($OtherHTML) {
        $BeforeTableCode = Invoke-Command -ScriptBlock $OtherHTML
    } else {
        $BeforeTableCode = ''
    }

    if ($PreContent) {
        $BeforeTable = Invoke-Command -ScriptBlock $PreContent
    } else {
        $BeforeTable = ''
    }
    if ($PostContent) {
        $AfterTable = Invoke-Command -ScriptBlock $PostContent
    } else {
        $AfterTable = ''
    }

    if ($RowGrouping.Attributes.Count -gt 0) {
        $RowGroupingCSS = ConvertTo-LimitedCSS -ID $DataTableID -ClassName 'tr.dtrg-group td' -Attributes $RowGrouping.Attributes -Group
    } else {
        $RowGroupingCSS = ''
    }

    if ($Simplify) {
        $AttributeDiv = @{ class = 'flexElement overflowHidden' ; style = @{ display = 'flex' } }
    } else {
        $AttributeDiv = @{ class = 'flexElement overflowHidden' }
    }

    New-HTMLTag -Tag 'div' -Attributes $AttributeDiv -Value {
        $RowGroupingCSS
        $BeforeTableCode
        $BeforeTable

        if ($WordBreak -ne 'normal') {
            New-HTMLTag -Tag 'style' {
                ConvertTo-LimitedCSS -ClassName 'td' -ID $TableAttributes.id -Attributes @{ 'word-break' = $WordBreak } -Group
            }
        }

        New-HTMLTag -Tag 'table' -Attributes $TableAttributes {
            New-HTMLTag -Tag 'thead' {
                if ($AddedHeader) {
                    $AddedHeader
                } else {
                    $Header
                }
            }
            if ($Table) {
                New-HTMLTag -Tag 'tbody' {
                    $Table
                }
            }
            if (-not $HideFooter) {
                New-HTMLTag -Tag 'tfoot' {
                    $Header
                }
            }
        }
        $AfterTable
    }
}

function New-HTMLTableOption {
    <#
    .SYNOPSIS
    Configures New-HTMLTable options
 
    .DESCRIPTION
    Configures New-HTMLTable options
 
    .PARAMETER DataStore
    Choose how Data is stored for all tables HTML, JavaScript or AjaxJSON (external file)
 
    .PARAMETER BoolAsString
    When JavaScript or AjaxJSON is used, forces bool to string
 
    .PARAMETER NumberAsString
    When JavaScript or AjaxJSON is used, forces number to string
 
    .PARAMETER DateTimeFormat
    When JavaScript or AjaxJSON is used, one can configure DateTimeFormat (in PowerShell way)
 
    .PARAMETER NewLineCarriage
    When JavaScript or AjaxJSON is used, one can configure NewLineCarriage. Default NewLineCarriage = '<br>'
 
    .PARAMETER NewLine
    When JavaScript or AjaxJSON is used, one can configure NewLine. Default value for NewLine = "\n"
 
    .PARAMETER Carriage
    When JavaScript or AjaxJSON is used, one can configure Carriage. Default value for Carriage = "\r"
 
    .PARAMETER ArrayJoin
    When JavaScript or AjaxJSON is used, forces any array to be a string regardless of depth level
 
    .PARAMETER ArrayJoinString
    Uses defined string or char for array join. By default it uses comma with a space when used.
 
    .EXAMPLE
    New-HTML {
        New-HTMLTableOption -DateTimeFormat "yyyy-MM-dd HH:mm:ss" -BoolAsString
        New-HTMLSection -Invisible {
            New-HTMLSection -HeaderText 'Standard Table with PSCustomObjects' {
                New-HTMLTable -DataTable $DataTable1
            }
            New-HTMLSection -HeaderText 'Standard Table with PSCustomObjects' {
                New-HTMLTable -DataTable $DataTable1 -DataStore JavaScript
            }
        }
    } -ShowHTML
 
    .NOTES
    General notes
    #>

    [alias('New-TableOption', 'TableOption', 'EmailTableOption')]
    [cmdletBinding()]
    param(
        [ValidateSet('HTML', 'JavaScript', 'AjaxJSON')][string] $DataStore,
        [switch] $BoolAsString,
        [switch] $NumberAsString,
        [string] $DateTimeFormat,
        [string] $NewLineCarriage,
        [string] $NewLine,
        [string] $Carriage,
        [switch] $ArrayJoin,
        [string] $ArrayJoinString = ', ',
        [switch] $PrettifyObject,
        [string] $PrettifyObjectSeparator = ", ",
        [string] $PrettifyObjectDateTimeFormat
    )
    if ($Script:HTMLSchema) {
        if ($DataStore ) {
            $Script:HTMLSchema['TableOptions']['DataStore'] = $DataStore
        }
        if ($BoolAsString.IsPresent) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].BoolAsString = $BoolAsString.IsPresent
        }
        if ($NumberAsString.IsPresent) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NumberAsString = $NumberAsString.IsPresent
        }
        if ($DateTimeFormat) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].DateTimeFormat = $DateTimeFormat
        }
        if ($NewLineCarriage) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormat.NewLineCarriage = $NewLineCarriage
        }
        if ($NewLine) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormat.NewLine = $NewLine
        }
        if ($Carriage) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormat.Carriage = $Carriage
        }
        if ($NewLineCarriage) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormatProperty.NewLineCarriage = $NewLineCarriage
        }
        if ($NewLine) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormatProperty.NewLine = $NewLine
        }
        if ($Carriage) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].NewLineFormatProperty.Carriage = $Carriage
        }
        if ($ArrayJoin) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoin = $true
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoinString = $ArrayJoinString
        }
        if ($PSBoundParameters.ContainsKey('PrettifyObject')) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].PrettifyObject = $PrettifyObject.IsPresent
            if ($PrettifyObjectSeparator) {
                $Script:HTMLSchema['TableOptions']['DataStoreOptions'].PrettifyObjectSeparator = $PrettifyObjectSeparator
            }
            if ($PrettifyObjectDateTimeFormat) {
                $Script:HTMLSchema['TableOptions']['DataStoreOptions'].PrettifyObjectDateTimeFormat = $PrettifyObjectDateTimeFormat
            }
        }
    }
}
function New-HTMLTableStyle {
    <#
    .SYNOPSIS
    Apply new style for HTML Table
 
    .DESCRIPTION
    Apply new style for HTML Table. Currently only works with DataTables (javascript). It doesn't affect CSS only tables (emails, etc). Keep in mind this affects all tables, not just one.
 
    .PARAMETER Type
    Choose type to apply style on. You can choose from: 'Content', 'Table', 'Header', 'Row', 'Footer', 'RowOdd', 'RowEven', 'RowSelected', 'RowSelectedEven', 'RowSelectedOdd', 'RowHover', 'RowHoverSelected', 'Button'. Content is duplicate to Row.
 
    .PARAMETER FontSize
    Set font size for the text.
 
    .PARAMETER FontWeight
    Set font weight for the text. Allowed options: 'normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900'
 
    .PARAMETER FontStyle
    Set different font styles to be used for text. Allowed styles: 'normal', 'italic', 'oblique'
 
    .PARAMETER FontVariant
    Set different font variant to be used for text. Allowed variants: 'normal', 'small-caps'. In a small-caps font, all lowercase letters are converted to uppercase letters. However, the converted uppercase letters appears in a smaller font size than the original uppercase letters in the text.
 
    .PARAMETER FontFamily
    Specify the font to be used for text.
 
    .PARAMETER BackgroundColor
    Set the background color. Choose one of the 800 colors or provide a hex value.
 
    .PARAMETER TextColor
    Set the text color. Choose one of the 800 colors or provide a hex value.
 
    .PARAMETER TextDecoration
    Set different font decoration. Allowed options are: 'none', 'line-through', 'overline', 'underline'
 
    .PARAMETER TextTransform
    Set different text transform. Allowed options are: 'uppercase', 'lowercase', 'capitalize'
 
    .PARAMETER TextAlign
    Set the text alignment. Allowed options are: 'left', 'right', 'center', 'justify'
 
    .PARAMETER BorderTopStyle
    Set the border style for the top border. Allowed options are: 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'
 
    .PARAMETER BorderTopColor
    Set the border color for the top border
 
    .PARAMETER BorderTopWidthSize
    Set the border width for the top border
 
    .PARAMETER BorderBottomStyle
    Set the border style for the bottom border. Allowed options are: 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'
 
    .PARAMETER BorderBottomColor
    Set the border color for the bottom border
 
    .PARAMETER BorderBottomWidthSize
    Set the border width for the bottom border
 
    .PARAMETER BorderLeftStyle
    Set the border style for the left border. Allowed options are: 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'
 
    .PARAMETER BorderLeftColor
    Set the border color for the left border
 
    .PARAMETER BorderLeftWidthSize
    Set the border width for the left border
 
    .PARAMETER BorderRightStyle
    Set the border style for the right border. Allowed options are: 'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'
 
    .PARAMETER BorderRightColor
    Set the border color for the right border
 
    .PARAMETER BorderRightWidthSize
    Set the border width for the right border
 
    .EXAMPLE
    $Table = Get-Process | Select-Object -First 3
 
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor Yellow -TextColor Aquamarine -TextAlign center -Type RowOdd
    New-HTMLTableStyle -BackgroundColor Red -TextColor Aquamarine -Type Button
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor DarkSlateGray -TextColor Aquamarine -TextAlign center -Type RowEven
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor DarkSlateGray -TextColor Aquamarine -TextAlign center -Type Row
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor DarkSlateGray -TextColor Aquamarine -TextAlign center -Type Header
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor Orange -TextColor Aquamarine -TextAlign center -Type Footer
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor Orange -TextColor Aquamarine -TextAlign center -Type RowSelectedEven
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor Green -TextColor Aquamarine -TextAlign center -Type RowSelectedOdd
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor Yellow -TextColor Aquamarine -TextAlign center -Type RowHover
    New-HTMLTableStyle -FontFamily 'Calibri' -BackgroundColor Red -TextColor Aquamarine -TextAlign center -Type RowHoverSelected
    New-HTMLTableStyle -Type Header -BorderLeftStyle dashed -BorderLeftColor Red -BorderLeftWidthSize 1px
    New-HTMLTableStyle -Type Footer -BorderLeftStyle dotted -BorderLeftColor Red -BorderleftWidthSize 1px
    New-HTMLTableStyle -Type Footer -BorderTopStyle none -BorderTopColor Red -BorderTopWidthSize 5px -BorderBottomColor Yellow -BorderBottomStyle solid
    New-HTMLTableStyle -Type Footer -BorderTopStyle none -BorderTopColor Red -BorderTopWidthSize 5px -BorderBottomColor Yellow -BorderBottomStyle solid
    New-HTMLTableStyle -Type Footer -BorderTopStyle none -BorderTopColor Red -BorderTopWidthSize 5px -BorderBottomColor Yellow -BorderBottomStyle none
 
    New-HTML -ShowHTML -HtmlData {
        New-HTMLTable -DataTable $table -HideButtons {
 
        } -DisablePaging
    } -FilePath $PSScriptRoot\Example7_TableStyle.html -Online
 
    .NOTES
    General notes
    #>

    [alias('EmailTableStyle', 'TableStyle', 'New-TableStyle')]
    [cmdletBinding(DefaultParameterSetName = 'Manual')]
    param(
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('Content', 'Table', 'Header', 'Row', 'Footer', 'RowOdd', 'RowEven', 'RowSelected', 'RowSelectedEven', 'RowSelectedOdd', 'RowHover', 'RowHoverSelected', 'Button')][string] $Type = 'Table',
        [Parameter(ParameterSetName = 'Manual')][alias('TextSize')][string] $FontSize,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [Parameter(ParameterSetName = 'Manual')][string] $FontFamily,
        [Parameter(ParameterSetName = 'Manual')][string] $BackgroundColor,
        [Parameter(ParameterSetName = 'Manual')][string] $TextColor,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [Parameter(ParameterSetName = 'Manual')][alias('FontAlign', 'Align')][ValidateSet('left', 'right', 'center', 'justify')][string] $TextAlign,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderTopStyle,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderTopColor,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderTopWidthSize,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderBottomStyle,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderBottomColor,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderBottomWidthSize,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderLeftStyle,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderLeftColor,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderLeftWidthSize,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')] $BorderRightStyle,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderRightColor,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderRightWidthSize
    )
    $CssConfiguration = Get-ConfigurationCss -Feature 'DataTables' -Type 'HeaderAlways'

    $RowOdd = @(
        'table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd'
        'table.dataTable.display tbody tr.odd>.sorting_1, table.dataTable.order-column.stripe tbody tr.odd>.sorting_1'
        'table.dataTable.display tbody tr.odd>.sorting_2, table.dataTable.order-column.stripe tbody tr.odd>.sorting_2'
        'table.dataTable.display tbody tr.odd>.sorting_3, table.dataTable.order-column.stripe tbody tr.odd>.sorting_3'
    )
    $RowEven = @(
        'table.dataTable.stripe tbody tr.even, table.dataTable.display tbody tr.even'
        'table.dataTable.display tbody tr.even>.sorting_1, table.dataTable.order-column.stripe tbody tr.even>.sorting_1'
        'table.dataTable.display tbody tr.even>.sorting_2, table.dataTable.order-column.stripe tbody tr.even>.sorting_2'
        'table.dataTable.display tbody tr.even>.sorting_3, table.dataTable.order-column.stripe tbody tr.even>.sorting_3'
    )

    $RowSelectedOdd = @(
        'table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected'
        'table.dataTable.display tbody tr.odd.selected>.sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1'
        'table.dataTable.display tbody tr.odd.selected>.sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2'
        'table.dataTable.display tbody tr.odd.selected>.sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3'
    )
    $RowSelectedEven = @(
        'table.dataTable.stripe tbody tr.even.selected, table.dataTable.display tbody tr.even.selected'
        'table.dataTable.order-column tbody tr.selected>.sorting_1, table.dataTable.order-column tbody tr.selected>.sorting_2, table.dataTable.order-column tbody tr.selected>.sorting_3, table.dataTable.display tbody tr.selected>.sorting_1, table.dataTable.display tbody tr.selected>.sorting_2, table.dataTable.display tbody tr.selected>.sorting_3'
        'table.dataTable.display tbody tr.even.selected>.sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1'
        'table.dataTable.display tbody tr.even.selected>.sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2'
        'table.dataTable.display tbody tr.even.selected>.sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3'
    )
    $RowHover = @(
        'table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover'
        'table.dataTable.display tbody tr:hover>.sorting_1, table.dataTable.order-column.hover tbody tr:hover>.sorting_1'
        'table.dataTable.display tbody tr:hover>.sorting_2, table.dataTable.order-column.hover tbody tr:hover>.sorting_2'
        'table.dataTable.display tbody tr:hover>.sorting_3, table.dataTable.order-column.hover tbody tr:hover>.sorting_3'
    )
    $RowHoverSelected = @(
        'table.dataTable.hover tbody tr.odd:hover.selected, table.dataTable.display tbody tr.odd:hover.selected'
        'table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected'
        'table.dataTable.display tbody tr:hover.selected>.sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1'
        'table.dataTable.display tbody tr:hover.selected>.sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2'
        'table.dataTable.display tbody tr:hover.selected>.sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3'
    )

    $TableStyle = [ordered] @{
        'text-align'          = $TextAlign
        'text-transform'      = $TextTransform
        'font-size'           = ConvertFrom-Size -TextSize $FontSize
        'font-weight'         = $FontWeight
        'font-style'          = $FontStyle
        'font-variant'        = $FontVariant
        'font-family'         = $FontFamily
        'text-decoration'     = $TextDecoration

        'border-top-width'    = ConvertFrom-Size -TextSize $BorderTopWidth
        'border-top-style'    = $BorderTopStyle

        'border-bottom-width' = ConvertFrom-Size -TextSize $BorderBottomWidth
        'border-bottom-style' = $BorderBottomStyle

        'border-left-width'   = ConvertFrom-Size -TextSize $BorderLeftWidth
        'border-left-style'   = $BorderLeftStyle

        'border-right-width'  = ConvertFrom-Size -TextSize $BorderRightWidth
        'border-right-style'  = $BorderRightStyle
    }
    if ($TextColor) {
        $TableStyle['color'] = "$(ConvertFrom-Color -Color $TextColor) !important"
    }
    if ($BackgroundColor) {
        $TableStyle['background-color'] = "$(ConvertFrom-Color -Color $BackgroundColor) !important"
    }
    if ($BorderTopColor) {
        $TableStyle['border-top-color'] = "$(ConvertFrom-Color -Color $BorderTopColor) !important"
    }
    if ($BorderBottomColor) {
        $TableStyle['border-bottom-color'] = "$(ConvertFrom-Color -Color $BorderBottomColor) !important"
    }
    if ($BorderLeftColor) {
        $TableStyle['border-left-color'] = "$(ConvertFrom-Color -Color $BorderLeftColor) !important"
    }
    if ($BorderRightColor) {
        $TableStyle['border-right-color'] = "$(ConvertFrom-Color -Color $BorderRightColor) !important"
    }

    if ($Type -in 'Button') {
        $ButtonStyle = [ordered] @{}
        if ($TextColor) {
            $ButtonStyle['color'] = "$(ConvertFrom-Color -Color $TextColor) !important"
        }
        if ($BackgroundColor) {
            $ButtonStyle['background-color'] = "$(ConvertFrom-Color -Color $BackgroundColor) !important"
        }
        $ButtonCss = @(
            'td::before, td.sorting_1::before'
        )
        foreach ($Name in $ButtonCss) {
            Add-ConfigurationCSS -CSS $CssConfiguration -Name $Name -Inject $ButtonStyle
        }
    }
    if ($Type -in 'Header', 'Table') {
        Add-ConfigurationCSS -CSS $CssConfiguration -Name 'table.dataTable thead th, table.dataTable thead td' -Inject $TableStyle
    }
    if ($Type -in 'Footer', 'Table') {
        Add-ConfigurationCSS -CSS $CssConfiguration -Name 'table.dataTable tfoot th, table.dataTable tfoot td' -Inject $TableStyle
    }
    if ($Type -in 'RowOdd', 'Row', 'Table', 'Content') {
        foreach ($Name in $RowOdd) {
            Add-ConfigurationCSS -CSS $CssConfiguration -Name $Name -Inject $TableStyle
        }
    }
    if ($Type -in 'RowEven', 'Row', 'Table', 'Content') {
        foreach ($Name in $RowEven) {
            Add-ConfigurationCSS -CSS $CssConfiguration -Name $Name -Inject $TableStyle
        }
    }

    if ($Type -in 'RowSelected', 'RowSelectedOdd') {
        foreach ($Name in $RowSelectedOdd) {
            Add-ConfigurationCSS -CSS $CssConfiguration -Name $Name -Inject $TableStyle
        }
    }
    if ($Type -in 'RowSelected', 'RowSelectedEven') {
        foreach ($Name in $RowSelectedEven) {
            Add-ConfigurationCSS -CSS $CssConfiguration -Name $Name -Inject $TableStyle
        }
    }

    if ($Type -in 'RowHover') {
        foreach ($Name in $RowHover) {
            Add-ConfigurationCSS -CSS $CssConfiguration -Name $Name -Inject $TableStyle
        }
    }

    if ($Type -in 'RowHoverSelected') {
        foreach ($Name in $RowHoverSelected) {
            Add-ConfigurationCSS -CSS $CssConfiguration -Name $Name -Inject $TableStyle
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLTableStyle -ParameterName BorderBottomColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTableStyle -ParameterName BorderTopColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTableStyle -ParameterName BorderRightColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTableStyle -ParameterName BorderLeftColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTableStyle -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTableStyle -ParameterName TextColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLTabPanel {
    <#
    .SYNOPSIS
    Flexible and easy to implement Tab Panel with a lot of features, cool animation effects, event support, easy to customize.
 
    .DESCRIPTION
    Flexible and easy to implement Tab Panel with a lot of features, cool animation effects, event support, easy to customize.
 
    .PARAMETER Orientation
    Nav menu orientation. Default value is 'horizontal'.
 
    .PARAMETER DisableJustification
    Disable navigation menu justification
 
    .PARAMETER DisableBackButtonSupport
    Disable the back button support
 
    .PARAMETER DisableURLhash
    Disable selection of the tab based on url hash
 
    .PARAMETER TransitionAnimation
    Effect on navigation, none/fade/slide-horizontal/slide-vertical/slide-swing
 
    .PARAMETER TransitionSpeed
    Transion animation speed. Default 400
 
    .PARAMETER AutoProgress
    Enables auto navigation
 
    .PARAMETER AutoProgressInterval
    Auto navigate Interval (used only if "autoProgress" is enabled). Default 3500
 
    .PARAMETER DisableAutoProgressStopOnFocus
    Disable stop auto navigation on focus and resume on outfocus (used only if "autoProgress" is enabled)
 
    .EXAMPLE
    An example
 
    .NOTES
    Implementation based on: http://techlaboratory.net/jquery-smarttab
    License: MIT
 
    #>

    [cmdletBinding()]
    param(
        [ScriptBlock] $Tabs,
        [ValidateSet('horizontal', 'vertical')][string] $Orientation,
        [switch] $DisableJustification,
        [switch] $DisableBackButtonSupport,
        [switch] $DisableURLhash,
        [ValidateSet('none', 'fade', 'slide-horizontal', 'slide-vertical', 'slide-swing')][string] $TransitionAnimation, # 'none', // Effect on navigation, none/fade/slide-horizontal/slide-vertical/slide-swing
        [int] $TransitionSpeed,
        [switch] $AutoProgress,
        [int] $AutoProgressInterval,
        [switch] $DisableAutoProgressStopOnFocus,
        [ValidateSet('basic', 'elite', 'pills', 'brick', 'forge', 'blocks')] $Theme = 'basic'
    )
    $Script:HTMLSchema.Features.JQuery = $true
    $Script:HTMLSchema.Features.TabsInline = $true
    $Script:HTMLSchema.Features.RedrawObjects = $true

    $TabID = "TabPanel-$(Get-RandomStringName -Size 8 -LettersOnly)"
    if ($Tabs) {
        $Script:HTMLSchema['TabPanelsList'].Add($TabID)
        $TabContent = & $Tabs
        if ($TabContent) {
            if ($Orientation -eq 'vertical') {
                $ClassOrientation = 'st-vertical'
            }
            New-HTMLTag -Tag 'div' -Attributes @{ id = $TabID; class = "flexElement $ClassOrientation"; style = @{margin = '5px' } } {
                New-HTMLTag -Tag 'ul' -Attributes @{ class = 'nav' } {
                    foreach ($Tab in $TabContent) {
                        New-HTMLTag -Tag 'li' {
                            New-HTMLTag -Tag 'a' -Attributes @{ class = 'nav-link'; href = "#$($Tab.ID)" } {
                                if ($Tab.Icon) {
                                    New-HTMLTag -Tag 'span' -Attributes @{ class = $($Tab.Icon); style = $($Tab.StyleIcon) }
                                    '&nbsp;' 
                                }
                                New-HTMLTag -Tag 'span' -Attributes @{ style = $($Tab.StyleText ) } -Value { $Tab.Name }
                            }
                        }
                    }
                }
                New-HTMLTag -Tag 'div' -Attributes @{ class = 'tab-content flexElement' } {
                    foreach ($Tab in $TabContent) {
                        New-HTMLTag -Tag 'div' -Attributes @{ class = 'tab-pane flexElement'; id = $Tab.ID; role = 'tabpanel'; style = @{ padding = '0px' } } {
                            $Tab.Content
                        }
                    }
                }
            }
            $SmartTab = [ordered] @{
                autoAdjustHeight = $false 
                theme            = $Theme.ToLower()
            }
            if ($TransitionAnimation) {
                $SmartTab['transition'] = [ordered] @{}
                $SmartTab['transition']['animation'] = $TransitionAnimation
                if ($TransitionSpeed) {
                    $SmartTab['transition']['speed'] = $TransitionSpeed
                }
            }
            if ($DisableJustification) {
                $SmartTab['justified'] = $false
            }
            if ($DisableBackButtonSupport) {
                $SmartTab['backButtonSupport'] = $false
            }
            if ($DisableURLhash) {
                $SmartTab['enableURLhash'] = $false
            }
            if ($AutoProgress) {
                $SmartTab['autoProgress'] = [ordered] @{}
                $SmartTab['autoProgress']['enabled'] = $true
                if ($AutoProgressInterval) {
                    $SmartTab['autoProgress']['interval'] = $AutoProgressInterval
                }
                if ($DisableAutoProgressStopOnFocus) {
                    $SmartTab['autoProgress']['stopOnFocus'] = $false
                }
            }
            Remove-EmptyValue -Hashtable $SmartTab
            $SmartTabConfiguration = $SmartTab | ConvertTo-Json -Depth 2

            New-HTMLTag -Tag 'script' {
                @"
            `$(document).ready(function(){
                // SmartTab initialize
                `$('#$TabID').smartTab($SmartTabConfiguration);
                `$("#$TabID").on("showTab", function(e, anchorObject, tabIndex) {
                    //alert("You are on tab "+tabIndex+" now");
                    if (anchorObject[0].hash) {
                        var id = anchorObject[0].hash.replace('#', '');
                        findObjectsToRedraw(id);
                    };
                });
            });
"@

            }
        }
        $null = $Script:HTMLSchema['TabPanelsList'].Remove($TabID)
    }
}
function New-HTMLTabPanelColor {
    <#
    .SYNOPSIS
    Creates a new HTML Tab Panel with customizable colors.
 
    .DESCRIPTION
    This function allows you to define colors for various elements of an HTML Tab Panel, such as background color, border color, anchor colors, and loader colors.
 
    .PARAMETER BackgrounColor
    The background color of the tab panel.
 
    .PARAMETER BorderWidth
    The width of the border. Default is '1px'.
 
    .PARAMETER BorderStyle
    The style of the border. Choose from: 'solid', 'dotted', 'dashed', 'double', 'groove', 'ridge', 'inset', 'outset', 'none', 'hidden'. Default is 'solid'.
 
    .PARAMETER BorderColor
    The color of the border. Default is '#eeeeee'.
 
    .PARAMETER AnchorDefaultPrimaryColor
    The primary color of default anchor links.
 
    .PARAMETER AnchorDefaultSecondaryColor
    The secondary color of default anchor links.
 
    .PARAMETER AnchorActivePrimaryColor
    The primary color of active anchor links.
 
    .PARAMETER AnchorActiveSecondaryColor
    The secondary color of active anchor links.
 
    .PARAMETER AnchorDisabledPrimaryColor
    The primary color of disabled anchor links.
 
    .PARAMETER AnchorDisabledSecondaryColor
    The secondary color of disabled anchor links.
 
    .PARAMETER LoaderColor
    The color of the loader.
 
    .PARAMETER LoaderBackgroundColor
    The background color of the loader.
 
    .PARAMETER LoaderBackgroundWrapperColor
    The color of the loader background wrapper.
 
    .EXAMPLE
    Set-HTMLTabPanelColor -BackgrounColor '#f0f0f0' -BorderColor '#cccccc' -AnchorDefaultPrimaryColor '#333333'
 
    .NOTES
    This function allows customization of colors for an HTML Tab Panel.
    #>

    [CmdletBinding()]
    param(
        [string] $BackgrounColor,
        [string] $BorderWidth = '1px',
        [ValidateSet('solid', 'dotted', 'dashed', 'double', 'groove', 'ridge', 'inset', 'outset', 'none', 'hidden')][string] $BorderStyle = 'solid',
        [string] $BorderColor = '#eeeeee',
        [string] $AnchorDefaultPrimaryColor,
        [string] $AnchorDefaultSecondaryColor,
        [string] $AnchorActivePrimaryColor,
        [string] $AnchorActiveSecondaryColor,
        [string] $AnchorDisabledPrimaryColor,
        [string] $AnchorDisabledSecondaryColor,
        [string] $LoaderColor,
        [string] $LoaderBackgroundColor,
        [string] $LoaderBackgroundWrapperColor
    )
    Enable-HTMLFeature -Feature TabsInlineColor

    if ($Script:CurrentConfiguration -and $Script:CurrentConfiguration.Features.TabsInlineColor) {
        if ($PSBoundParameters.ContainsKey('BackgrounColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-background'] = ConvertFrom-Color -Color $BackgrounColor
        }
        if ($PSBoundParameters.ContainsKey('BorderColor') -or $PSBoundParameters.ContainsKey('BorderWidth') -or $PSBoundParameters.ContainsKey('BorderStyle')) {
            $BorderSizeConverted = ConvertFrom-Size -Size $BorderWidth
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-border'] = "$BorderSizeConverted $BorderStyle $(ConvertFrom-Color -Color $BorderColor)"
        }
        if ($PSBoundParameters.ContainsKey('AnchorDefaultPrimaryColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-anchor-default-primary-color'] = ConvertFrom-Color -Color $AnchorDefaultPrimaryColor
        }
        if ($PSBoundParameters.ContainsKey('AnchorDefaultSecondaryColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-anchor-default-secondary-color'] = ConvertFrom-Color -Color $AnchorDefaultSecondaryColor
        }
        if ($PSBoundParameters.ContainsKey('AnchorActivePrimaryColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-anchor-active-primary-color'] = ConvertFrom-Color -Color $AnchorActivePrimaryColor
        }
        if ($PSBoundParameters.ContainsKey('AnchorActiveSecondaryColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-anchor-active-secondary-color'] = ConvertFrom-Color -Color $AnchorActiveSecondaryColor
        }
        if ($PSBoundParameters.ContainsKey('AnchorDisabledPrimaryColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-anchor-disabled-primary-color'] = ConvertFrom-Color -Color $AnchorDisabledPrimaryColor
        }
        if ($PSBoundParameters.ContainsKey('AnchorDisabledSecondaryColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-anchor-disabled-secondary-color'] = ConvertFrom-Color -Color $AnchorDisabledSecondaryColor
        }
        if ($PSBoundParameters.ContainsKey('LoaderColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-loader-color'] = ConvertFrom-Color -Color $LoaderColor
        }
        if ($PSBoundParameters.ContainsKey('LoaderBackgroundColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-loader-background-color'] = ConvertFrom-Color -Color $LoaderBackgroundColor
        }
        if ($PSBoundParameters.ContainsKey('LoaderBackgroundWrapperColor')) {
            $Script:CurrentConfiguration.Features.TabsInlineColor.HeaderAlways.CssInline[':root']['--st-loader-background-wrapper-color'] = ConvertFrom-Color -Color $LoaderBackgroundWrapperColor
        }
    }
}
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName BackgrounColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName BorderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName AnchorDefaultPrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName AnchorDefaultSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName AnchorActivePrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName AnchorActiveSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName AnchorDisabledPrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName AnchorDisabledSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName LoaderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName LoaderBackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabPanelColor -ParameterName LoaderBackgroundWrapperColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLTabStyle {
    <#
    .SYNOPSIS
    Creates a new HTML tab style with customizable options.
 
    .DESCRIPTION
    This function allows you to define various styling options for HTML tabs, such as font size, color, weight, style, background color, border properties, and more.
 
    .PARAMETER FontSize
    Specifies the font size for the tabs.
 
    .PARAMETER FontSizeActive
    Specifies the font size for the active tab.
 
    .PARAMETER TextColor
    Specifies the text color for the tabs.
 
    .PARAMETER TextColorActive
    Specifies the text color for the active tab.
 
    .PARAMETER FontWeight
    Specifies the font weight for the tabs.
 
    .PARAMETER FontWeightActive
    Specifies the font weight for the active tab.
 
    .PARAMETER FontStyle
    Specifies the font style for the tabs.
 
    .PARAMETER FontStyleActive
    Specifies the font style for the active tab.
 
    .PARAMETER FontVariant
    Specifies the font variant for the tabs.
 
    .PARAMETER FontVariantActive
    Specifies the font variant for the active tab.
 
    .PARAMETER FontFamily
    Specifies the font family for the tabs.
 
    .PARAMETER FontFamilyActive
    Specifies the font family for the active tab.
 
    .PARAMETER TextDecoration
    Specifies the text decoration for the tabs.
 
    .PARAMETER TextDecorationActive
    Specifies the text decoration for the active tab.
 
    .PARAMETER BackgroundColor
    Specifies the background color for the tabs.
 
    .PARAMETER BackgroundColorActive
    Specifies the background color for the active tab.
 
    .PARAMETER BackgroundColorActiveTarget
    Specifies the background color for the active tab target.
 
    .PARAMETER BorderRadius
    Specifies the border radius for the tabs.
 
    .PARAMETER TextTransform
    Specifies the text transformation for the tabs.
 
    .PARAMETER TextTransformActive
    Specifies the text transformation for the active tab.
 
    .PARAMETER SlimTabs
    Indicates whether to use slim tabs.
 
    .PARAMETER Transition
    Indicates whether to apply transitions.
 
    .PARAMETER LinearGradient
    Indicates whether to use linear gradients.
 
    .PARAMETER RemoveShadow
    Indicates whether to remove shadows.
 
    .PARAMETER BorderStyle
    Specifies the border style for the tabs.
 
    .PARAMETER BorderColor
    Specifies the border color for the tabs.
 
    .PARAMETER BorderBottomWidth
    Specifies the bottom border width for the tabs.
 
    .PARAMETER BorderBottomStyle
    Specifies the bottom border style for the tabs.
 
    .PARAMETER BorderBottomColor
    Specifies the bottom border color for the tabs.
 
    .PARAMETER BorderBottomWidthActive
    Specifies the bottom border width for the active tab.
 
    .PARAMETER BorderBottomStyleActive
    Specifies the bottom border style for the active tab.
 
    .PARAMETER BorderBottomColorActive
    Specifies the bottom border color for the active tab.
 
    .PARAMETER Style
    Specifies a custom style for the tabs.
 
    .PARAMETER Align
    Specifies the alignment of the tabs.
 
    .PARAMETER Wrap
    Specifies the wrapping behavior of the tabs.
 
    .PARAMETER Direction
    Specifies the direction of the tabs.
 
    .EXAMPLE
    New-HTMLTabStyle -FontSize "14px" -FontSizeActive "16px" -TextColor "black" -TextColorActive "blue" -FontWeight "normal" -FontWeightActive "bold" -FontStyle "normal" -FontStyleActive "italic" -FontVariant "normal" -FontVariantActive "small-caps" -FontFamily "Arial" -FontFamilyActive "Verdana" -TextDecoration "none" -TextDecorationActive "underline" -BackgroundColor "#f0f0f0" -BackgroundColorActive "#e0e0e0" -BackgroundColorActiveTarget "#d0d0d0" -BorderRadius "5px" -TextTransform "uppercase" -TextTransformActive "lowercase" -SlimTabs -Transition -LinearGradient -RemoveShadow -BorderStyle "solid" -BorderColor "gray" -BorderBottomWidth "1px" -BorderBottomStyle "solid" -BorderBottomColor "gray" -BorderBottomWidthActive "2px" -BorderBottomStyleActive "dashed" -BorderBottomColorActive "blue" -Style "custom" -Align "center" -Wrap "wrap" -Direction "row"
    Creates a new HTML tab style with specified options.
 
    .EXAMPLE
    New-HTMLTabStyle -FontSize "12px" -FontSizeActive "14px" -TextColor "gray" -TextColorActive "black" -FontWeight "bold" -FontWeightActive "bolder" -FontStyle "italic" -FontStyleActive "oblique" -FontVariant "small-caps" -FontVariantActive "normal" -FontFamily "Verdana" -FontFamilyActive "Arial" -TextDecoration "underline" -TextDecorationActive "none" -BackgroundColor "#e0e0e0" -BackgroundColorActive "#f0f0f0" -BackgroundColorActiveTarget "#d0d0d0" -BorderRadius "10px" -TextTransform "capitalize" -TextTransformActive "uppercase" -SlimTabs -Transition -LinearGradient -RemoveShadow -BorderStyle "dotted" -BorderColor "black" -BorderBottomWidth "2px" -BorderBottomStyle "dashed" -BorderBottomColor "blue" -BorderBottomWidthActive "1px" -BorderBottomStyleActive "solid" -BorderBottomColorActive "gray" -Style "default" -Align "left" -Wrap "nowrap" -Direction "column"
    Creates another HTML tab style with different options.
    #>

    [alias('TabOptions', 'New-TabOption', 'New-HTMLTabOptions', 'TabOption', 'New-HTMLTabOption', 'TabStyle')]
    [CmdletBinding(DefaultParameterSetName = 'Manual')]
    param(
        [Parameter(ParameterSetName = 'Manual')][alias('TextSize')][string] $FontSize,
        [Parameter(ParameterSetName = 'Manual')][alias('TextSizeActive')][string] $FontSizeActive,
        [Parameter(ParameterSetName = 'Manual')][string] $TextColor,
        [Parameter(ParameterSetName = 'Manual')][string] $TextColorActive,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeightActive,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'italic', 'oblique')][string] $FontStyleActive,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('normal', 'small-caps')][string] $FontVariantActive,
        [Parameter(ParameterSetName = 'Manual')][string] $FontFamily,
        [Parameter(ParameterSetName = 'Manual')][string] $FontFamilyActive,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecorationActive,
        [Parameter(ParameterSetName = 'Manual')][string] $BackgroundColor,
        [Parameter(ParameterSetName = 'Manual')][alias('SelectorColor')][string] $BackgroundColorActive,
        [Parameter(ParameterSetName = 'Manual')][alias('SelectorColorTarget')][string] $BackgroundColorActiveTarget,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('0px', '5px', '10px', '15px', '20px', '25px')][string] $BorderRadius,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransformActive,

        [Parameter(ParameterSetName = 'Manual')][switch] $SlimTabs,
        [Parameter(ParameterSetName = 'Manual')][switch] $Transition,
        [Parameter(ParameterSetName = 'Manual')][switch] $LinearGradient,
        [Parameter(ParameterSetName = 'Manual')][switch] $RemoveShadow,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')][string] $BorderStyle,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderColor,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('medium', 'thin', 'thick')][string] $BorderBottomWidth,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')][string] $BorderBottomStyle,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderBottomColor,

        [Parameter(ParameterSetName = 'Manual')][ValidateSet('medium', 'thin', 'thick')][string] $BorderBottomWidthActive,
        [Parameter(ParameterSetName = 'Manual')][ValidateSet('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset')][string] $BorderBottomStyleActive,
        [Parameter(ParameterSetName = 'Manual')][string] $BorderBottomColorActive,

        [Parameter(ParameterSetName = 'Styled')][string] $Style,

        [Parameter(ParameterSetName = 'Styled')]
        [Parameter(ParameterSetName = 'Manual')]
        [alias('AlignTabs')][ValidateSet('left', 'right', 'center', 'justify')][string] $Align,

        [string][ValidateSet('wrap', 'nowrap', 'wrap-reverse')] $Wrap,
        [string][ValidateSet('row', 'row-reverse', 'column', 'column-reverse')] $Direction,
        [string][ValidateSet('flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch')] $AlignContent,
        [string][ValidateSet('stretch', 'flex-start', 'flex-end', 'center', 'baseline')] $AlignItems,
        [string][ValidateSet('flex-start', 'flex-end', 'center')] $JustifyContent,
        [int] $RowElements

    )
    if (-not $Script:HTMLSchema) {
        Write-Warning 'New-HTMLTabStyle - Creation of HTML aborted. Most likely New-HTML is missing.'
        Exit
    }
    if ($BackgroundColorActive -and $BackgroundColorActiveTarget -and (-not $LinearGradient)) {
        Write-Warning "New-HTMLTabStyle - Using BackgroundColorActiveTarget without LinearGradient switch doesn't apply any changes."
    }

    $TabbisCss = Get-ConfigurationCss -Feature 'Tabbis' -Type 'HeaderAlways'

    if (-not $BackgroundColorActive) {
        $BackgroundColorActive = "DodgerBlue"
    }
    if (-not $BackgroundColorActiveTarget) {
        $BackgroundColorActiveTarget = "MediumSlateBlue"
    }

    $BackGroundColorActiveSelector = ConvertFrom-Color -Color $BackgroundColorActive
    $BackGroundColorActiveSelectorTarget = ConvertFrom-Color -Color $BackgroundColorActiveTarget

    if ($SlimTabs.IsPresent) {
        $CssSlimTabs = @{
            'display' = 'inline-block'
        }

        Add-ConfigurationCSS -CSS $TabbisCss -Name '.tabsSlimmer' -Inject $CssSlimTabs
    }

    $CssTabsWrapper = [ordered] @{
        'text-align'      = $Align
        'text-transform'  = $TextTransform
        'font-size'       = ConvertFrom-Size -TextSize $FontSize
        'color'           = ConvertFrom-Color -Color $TextColor

        'font-weight'     = $FontWeight
        'font-style'      = $FontStyle
        'font-variant'    = $FontVariant
        'font-family'     = $FontFamily
        'text-decoration' = $TextDecoration
    }

    Add-ConfigurationCSS -CSS $TabbisCss -Name '.tabsWrapper' -Inject $CssTabsWrapper

    $CssTabsData = @{
        'border-radius'    = $BorderRadius
        'border-style'     = $BorderStyle
        'border-color'     = ConvertFrom-Color -Color $BorderColor
        'background-color' = ConvertFrom-Color -Color $BackgroundColor
        'justify-content'  = $JustifyContent
        'flex-wrap'        = $Wrap
        'flex-direction'   = $Direction
        'align-content'    = $AlignContent
        'align-items'      = $AlignItems
    }
    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs]' -Inject $CssTabsData

    $CssTabsActive = [ordered] @{
        'background'     = $BackGroundColorActiveSelector
        'color'          = '#fff'
        'border-radius'  = $BorderRadius
        'text-transform' = $TextTransformActive
    }

    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject $CssTabsActive

    if ($LinearGradient.IsPresent) {
        $CssTabbisGradient = [ordered] @{
            'background'   = "-moz-linear-gradient(45deg, $BackGroundColorActiveSelector 0%, $BackGroundColorActiveSelectorTarget 100%)"
            'background '  = "-webkit-linear-gradient(45deg, $BackGroundColorActiveSelector 0%, $BackGroundColorActiveSelectorTarget 100%)"
            'background ' = "linear-gradient(45deg, $BackGroundColorActiveSelector 0%, $BackGroundColorActiveSelectorTarget 100%)"
            'filter'       = "progid:DXImageTransform.Microsoft.gradient(startColorstr='$BackGroundColorActiveSelector', endColorstr='$BackGroundColorActiveSelectorTarget', GradientType=1)"
        }
        Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject $CssTabbisGradient
    }

    if ($Transition.IsPresent) {
        $CssTabbisTransition = [ordered] @{
            'transition-duration'        = '0.6s'
            'transition-timing-function' = 'cubic-bezier(0.68, -0.55, 0.265, 1.55)'
        }
        Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject $CssTabbisTransition
    }

    if ($BackgroundColorActive -eq 'none') {
        Remove-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Property 'background'
    }
    if ($RemoveShadow.IsPresent) {
        Remove-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs]' -Property 'box-shadow'
    }
    if ($PSBoundParameters.ContainsKey('TextSizeActive')) {
        Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'font-size' = ConvertFrom-Size -TextSize $FontSizeActive }
    }
    if ($PSBoundParameters.ContainsKey('TextColorActive')) {
        Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'color' = ConvertFrom-Color -Color $TextColorActive }
    }

    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'font-weight' = $FontWeightActive }
    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'font-style' = $FontStyleActive }
    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'font-variant' = $FontVariantActive }
    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'font-family' = $FontFamilyActive }
    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'text-decoration' = $TextDecorationActive }

    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'border-bottom-style' = $BorderBottomStyleActive }
    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'border-bottom-width' = $BorderBottomWidthActive }
    Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs] .active' -Inject @{ 'border-bottom-color' = ConvertFrom-Color -Color $BorderBottomColorActive }

    if ($BorderBottomStyle) {
        Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs]>*' -Inject @{ 'border-bottom-style' = $BorderBottomStyle }
        Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs]>*' -Inject @{ 'border-bottom-width' = $BorderBottomWidth }
        Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs]>*' -Inject @{ 'border-bottom-color' = ConvertFrom-Color -Color $BorderBottomColor }
    } elseif ($BorderBottomColor -or $BorderBottomWidth) {
        Write-Warning "New-HTMLTabStyle - You can't use BorderBottomColor or BorderBottomWidth without BorderBottomStyle."
    }

    if ($RowElements) {
        Add-ConfigurationCSS -CSS $TabbisCss -Name '[data-tabs]>*' -Inject @{ 'flex-basis' = "calc(80%/$RowElements)" }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLTabStyle -ParameterName TextColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabStyle -ParameterName TextColorActive -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabStyle -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabStyle -ParameterName BackgroundColorActive -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabStyle -ParameterName BackgroundColorActiveTarget -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabStyle -ParameterName BorderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabStyle -ParameterName BorderBottomColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTabStyle -ParameterName BorderBottomColorActive -ScriptBlock $Script:ScriptBlockColors
function New-HTMLTag {
    <#
    .SYNOPSIS
    Creates a new HTML tag with specified attributes and content.
 
    .DESCRIPTION
    The New-HTMLTag function creates a new HTML tag with the specified tag name, attributes, and content. It supports self-closing tags, custom attributes, and new line formatting.
 
    .PARAMETER Value
    Specifies the content of the HTML tag as a script block.
 
    .PARAMETER Tag
    Specifies the name of the HTML tag to create.
 
    .PARAMETER Attributes
    Specifies additional attributes for the HTML tag.
 
    .PARAMETER SelfClosing
    Indicates whether the HTML tag is self-closing.
 
    .PARAMETER NoClosing
    Indicates whether the HTML tag should not have a closing tag.
 
    .PARAMETER NewLine
    Indicates whether a new line should be added after the HTML tag.
 
    .EXAMPLE
    New-HTMLTag -Tag "div" -Attributes @{ class = "container" } -Value { "Hello, World!" }
 
    Creates a <div> tag with the class attribute set to "container" and the content "Hello, World!".
 
    .EXAMPLE
    New-HTMLTag -Tag "img" -Attributes @{ src = "image.jpg"; alt = "Image" } -SelfClosing
 
    Creates an <img> tag with the src and alt attributes set and is self-closing.
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('Content')][ScriptBlock] $Value,
        [Parameter(Mandatory = $true, Position = 1)][string] $Tag,
        [System.Collections.IDictionary] $Attributes,
        [switch] $SelfClosing,
        [switch] $NoClosing,
        [switch] $NewLine
    )

    try {
        $ScriptBlockResult = if ($null -eq $Value) {
            '' } else {
            Invoke-Command -ScriptBlock $Value -ErrorAction Stop }
    } catch {
        Write-Warning -Message "New-HTMLTag - Error: $($_.Exception.Message). Failed value: $($Value.ToString())"
        $StackTraceList = $_.ScriptStackTrace -split [System.Environment]::NewLine
        foreach ($S in $StackTraceList | Select-Object -First 4) {
            Write-Warning -Message "New-HTMLTag - Review StackTrace: $S"
        }
        $ScriptBlockResult = ''
    }
    $HTMLTag = [Ordered] @{
        Tag         = $Tag
        Value       = $ScriptBlockResult
        Attributes  = $Attributes
        SelfClosing = $SelfClosing
        NoClosing   = $NoClosing
    }
    Set-Tag -HtmlObject $HTMLTag -NewLine:$NewLine
}
function New-HTMLText {
    <#
    .SYNOPSIS
    This function provides ability to add new text to the HTML file.
 
    .DESCRIPTION
    This function provides ability to add new text to the HTML file, with colors, fonts and other styling features.
    It is used to add text to the HTML file with proper styling and formatting.
    Please keep in mind that if parameter is not provided the defaults will be used.
    The defaults can be from the body itself, or from section or other parts of HTML depending on where the text is added.
 
    .PARAMETER TextBlock
    Defines ability to use text block instead of array
 
    .PARAMETER Text
    Provide text or text array to be added to the HTML file.
 
    .PARAMETER Color
    Pick one of the 800 colors or provide a hex color code.
 
    .PARAMETER BackGroundColor
    Pick one of the 800 colors or provide a hex color code.
 
    .PARAMETER FontSize
    Provide font size. When skipped the default font size will be used.
 
    .PARAMETER LineHeight
    Provide line height. When skipped the default line height will be used.
 
    .PARAMETER FontWeight
    Provide font weight. When skipped the default font weight will be used. Options are: 'normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900'
 
    .PARAMETER FontStyle
    Provide font style. When skipped the default font style will be used. Options are: 'normal', 'italic', 'oblique'
 
    .PARAMETER FontVariant
    Provide font variant. When skipped the default font variant will be used. Options are: 'normal', 'small-caps'
 
    .PARAMETER FontFamily
    Provide font family. When skipped the default font family will be used.
 
    .PARAMETER Alignment
    Provide alignment. When skipped the default alignment will be used. Options are: 'left', 'right', 'center', 'justify'
 
    .PARAMETER TextDecoration
    Provide text decoration. When skipped the default text decoration will be used. Options are: 'none', 'line-through', 'overline', 'underline'
 
    .PARAMETER TextTransform
    Provide text transform. When skipped the default text transform will be used. Options are: 'uppercase', 'lowercase', 'capitalize'
 
    .PARAMETER Direction
    Provide direction. When skipped the direction will not be changed. Options are: 'rtl','ltr'. By default it's 'ltr'.
 
    .PARAMETER LineBreak
    Decides whether to add line break at the end of the text or not.
 
    .PARAMETER SkipParagraph
    Skips adding div tag to make sure text is not wrapped in it. By default it wraps all text in div tag.
 
    .PARAMETER Display
    Allows configuring CSS display property. The display property specifies the display behavior (the type of rendering box) of an element.
    Options are: 'none', 'inline', 'block', 'inline-block', 'contents','flex', 'grid', 'inline-flex', 'inline-grid', 'inline-table', 'list-item', 'run-in',
    'table', 'table-caption', 'table-column-group', 'table-header-group', 'table-footer-group', 'table-row-group', 'table-cell', 'table-column', 'table-row'
 
    .PARAMETER Opacity
    The opacity property sets the opacity level for an element. Value between 0 and 1. 1 is default.
 
    .EXAMPLE
    New-HTML -TitleText 'This is a test' -FilePath "$PSScriptRoot\Example34_01.html" {
        New-HTMLHeader {
            New-HTMLText -Text "Date of this report $(Get-Date)" -Color Blue -Alignment right
        }
        New-HTMLMain {
            New-HTMLTab -TabName 'Test' {
                New-HTMLSection -HeaderText '0 section' {
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter -Simplify
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                }
            }
            New-HTMLTab -TabName 'Test5' {
                New-HTMLSection -HeaderText '1 section' {
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                        # New-HTMLTable -DataTable $Processes -HideFooter
                    }
                    New-HTMLPanel {
                        New-HTMLTable -DataTable $Processes -HideFooter
                    }
                }
            }
        }
        New-HTMLFooter {
            New-HTMLText -Text "Date of this report $(Get-Date)" -Color Blue -Alignment right
        }
    } -Online -ShowHTML
 
    .EXAMPLE
 
    .NOTES
    General notes
    #>

    [alias('HTMLText', 'Text', 'EmailText')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $TextBlock,
        [string[]] $Text,
        [string[]] $Color = @(),
        [string[]] $BackGroundColor = @(),
        [alias('Size')][object[]] $FontSize = @(),
        [string[]] $LineHeight = @(),
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string[]] $FontWeight = @(),
        [ValidateSet('normal', 'italic', 'oblique')][string[]] $FontStyle = @(),
        [ValidateSet('normal', 'small-caps')][string[]] $FontVariant = @(),
        [string[]] $FontFamily = @(),
        [ValidateSet('left', 'center', 'right', 'justify')][string[]] $Alignment = @(),
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string[]] $TextDecoration = @(),
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string[]] $TextTransform = @(),
        [ValidateSet('rtl', 'ltr')][string[]] $Direction = @(),
        [switch] $LineBreak,
        [switch] $SkipParagraph,
        [ValidateSet(
            'none', 'inline', 'block', 'inline-block', 'contents',
            'flex', 'grid', 'inline-flex', 'inline-grid', 'inline-table', 'list-item', 'run-in',
            'table', 'table-caption', 'table-column-group', 'table-header-group', 'table-footer-group',
            'table-row-group', 'table-cell', 'table-column', 'table-row'
        )][string[]] $Display = @(),
        [double[]] $Opacity = @()#,
        #[string] $Margin = '5px'
    )
    $Script:HTMLSchema.Features.DefaultText = $true

    if ($TextBlock) {
        $Text = (Invoke-Command -ScriptBlock $TextBlock)
    }

    $DefaultColor = $Color[0]
    $DefaultFontSize = $FontSize[0]
    $DefaultFontWeight = if ($null -eq $FontWeight[0] ) {
        '' } else {
        $FontWeight[0] }
    $DefaultBackGroundColor = $BackGroundColor[0]
    $DefaultFontFamily = if ($null -eq $FontFamily[0] ) {
        '' } else {
        $FontFamily[0] }
    $DefaultFontStyle = if ($null -eq $FontStyle[0] ) {
        '' } else {
        $FontStyle[0] }
    $DefaultTextDecoration = if ($null -eq $TextDecoration[0]) {
        '' } else {
        $TextDecoration[0] }
    $DefaultTextTransform = if ($null -eq $TextTransform[0]) {
        '' } else {
        $TextTransform[0] }
    $DefaultFontVariant = if ($null -eq $FontVariant[0]) {
        '' } else {
        $FontVariant }
    $DefaultDirection = if ($null -eq $Direction[0]) {
        '' } else {
        $Direction[0] }
    $DefaultAlignment = if ($null -eq $Alignment[0]) {
        '' } else {
        $Alignment[0] }
    $DefaultLineHeight = if ($null -eq $LineHeight[0]) {
        '' } else {
        $LineHeight[0] }
    $DefaultDisplay = if ($null -eq $Display[0]) {
        '' } else {
        $Display[0] }
    $DefaultOpacity = if ($null -eq $Opacity[0]) {
        '' } else {
        $Opacity[0] }

    $Output = for ($i = 0; $i -lt $Text.Count; $i++) {
        if ($null -eq $FontWeight[$i]) {
            $ParamFontWeight = $DefaultFontWeight
        } else {
            $ParamFontWeight = $FontWeight[$i]
        }
        if ($null -eq $FontSize[$i]) {
            $ParamFontSize = $DefaultFontSize
        } else {
            $ParamFontSize = $FontSize[$i]
        }
        if ($null -eq $Color[$i]) {
            $ParamColor = $DefaultColor
        } else {
            $ParamColor = $Color[$i]
        }
        if ($null -eq $BackGroundColor[$i]) {
            $ParamBackGroundColor = $DefaultBackGroundColor
        } else {
            $ParamBackGroundColor = $BackGroundColor[$i]
        }
        if ($null -eq $FontFamily[$i]) {
            $ParamFontFamily = $DefaultFontFamily
        } else {
            $ParamFontFamily = $FontFamily[$i]
        }
        if ($null -eq $FontStyle[$i]) {
            $ParamFontStyle = $DefaultFontStyle
        } else {
            $ParamFontStyle = $FontStyle[$i]
        }

        if ($null -eq $TextDecoration[$i]) {
            $ParamTextDecoration = $DefaultTextDecoration
        } else {
            $ParamTextDecoration = $TextDecoration[$i]
        }

        if ($null -eq $TextTransform[$i]) {
            $ParamTextTransform = $DefaultTextTransform
        } else {
            $ParamTextTransform = $TextTransform[$i]
        }

        if ($null -eq $FontVariant[$i]) {
            $ParamFontVariant = $DefaultFontVariant
        } else {
            $ParamFontVariant = $FontVariant[$i]
        }
        if ($null -eq $Direction[$i]) {
            $ParamDirection = $DefaultDirection
        } else {
            $ParamDirection = $Direction[$i]
        }
        if ($null -eq $Alignment[$i]) {
            $ParamAlignment = $DefaultAlignment
        } else {
            $ParamAlignment = $Alignment[$i]
        }
        if ($null -eq $LineHeight[$i]) {
            $ParamLineHeight = $DefaultLineHeight
        } else {
            $ParamLineHeight = $LineHeight[$i]
        }
        if ($null -eq $Display[$i]) {
            $ParamDisplay = $DefaultDisplay
        } else {
            $ParamDisplay = $Display[$i]
        }
        if ($null -eq $Opacity[$i]) {
            $ParamOpacity = $DefaultOpacity
        } else {
            $ParamOpacity = $Opacity[$i]
        }

        $newSpanTextSplat = @{ }
        $newSpanTextSplat.Color = $ParamColor
        $newSpanTextSplat.BackGroundColor = $ParamBackGroundColor

        $newSpanTextSplat.FontSize = $ParamFontSize
        if ($ParamFontWeight -ne '') {
            $newSpanTextSplat.FontWeight = $ParamFontWeight
        }
        $newSpanTextSplat.FontFamily = $ParamFontFamily
        if ($ParamFontStyle -ne '') {
            $newSpanTextSplat.FontStyle = $ParamFontStyle
        }
        if ($ParamFontVariant -ne '') {
            $newSpanTextSplat.FontVariant = $ParamFontVariant
        }
        if ($ParamTextDecoration -ne '') {
            $newSpanTextSplat.TextDecoration = $ParamTextDecoration
        }
        if ($ParamTextTransform -ne '') {
            $newSpanTextSplat.TextTransform = $ParamTextTransform
        }
        if ($ParamDirection -ne '') {
            $newSpanTextSplat.Direction = $ParamDirection
        }
        if ($ParamAlignment -ne '') {
            $newSpanTextSplat.Alignment = $ParamAlignment
        }
        if ($ParamLineHeight -ne '') {
            $newSpanTextSplat.LineHeight = $ParamLineHeight
        }
        if ($ParamDisplay) {
            $newSpanTextSplat.display = $ParamDisplay
        }
        if ($ParamOpacity) {
            $newSpanTextSplat.opacity = $ParamOpacity
        }

        $newSpanTextSplat.LineBreak = $LineBreak
        New-HTMLSpanStyle @newSpanTextSplat {
            $FindMe = [regex]::Matches($Text[$i], "\[[^\]]+\]\(\S+\)")
            if ($FindMe) {
                foreach ($find in $FindMe) {
                    $LinkName = ([regex]::Match($Find.value, "[^\[]+(?=\])")).Value
                    $LinkURL = ([regex]::Match($Find.value, "(?<=\().+(?=\))")).Value
                    $Link = New-HTMLAnchor -HrefLink $LinkURL -Text $LinkName
                    $Text[$i] = $Text[$i].Replace($find.value, $Link)
                }
                $Text[$i]
            } else {

                $Text[$i]
            }

        }
    }

    if ($SkipParagraph) {
        $Output -join ''
    } else {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'defaultText' } {

            $Output
        }
    }
    if ($LineBreak) {
        New-HTMLTag -Tag 'br' -SelfClosing
    }
}

Register-ArgumentCompleter -CommandName New-HTMLText -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLText -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLTextBox {
    <#
    .SYNOPSIS
    Adds text to HTML where each line in TextBlock is treated as next line (adds BR to each line)
 
    .DESCRIPTION
    Adds text to HTML where each line in TextBlock is treated as next line (adds BR to each line).
    Automatic line breaks are main feature that differentiate New-HTMLTextBox from New-HTMLText
    where TextBlock is treated as single line of text unless LineBreak switch is used.
 
    .PARAMETER TextBlock
    ScriptBlock of one or more strings
 
    .PARAMETER Color
    Color of Text to set. Choose one or more colors from up to 800 defined colors. Alternatively provide your own Hex value
 
    .PARAMETER BackGroundColor
    Color of Background for a Text to set. Choose one or more colors from up to 800 defined colors. Alternatively provide your own Hex value
 
    .PARAMETER FontSize
    Choose FontSize. You can provide just int value which will assume pixels or string value with any other size value.
 
    .PARAMETER FontWeight
    Parameter description
 
    .PARAMETER FontStyle
    Parameter description
 
    .PARAMETER TextDecoration
    Parameter description
 
    .PARAMETER FontVariant
    Parameter description
 
    .PARAMETER FontFamily
    Parameter description
 
    .PARAMETER Alignment
    Chhoose Alignment
 
    .PARAMETER TextTransform
    Parameter description
 
    .PARAMETER Direction
    Parameter description
 
    .PARAMETER LineBreak
    Parameter description
 
    .EXAMPLE
    New-HTMLTextBox {
        "Hello $UserNotify,"
        ""
        "Your password is due to expire in $PasswordExpiryDays days."
        ""
        'To change your password: '
        '- press CTRL+ALT+DEL -> Change a password...'
        ''
        'If you have forgotten your password and need to reset it, you can do this by clicking here. '
        "In case of problems please contact the HelpDesk by visiting [Evotec Website](https://evotec.xyz) or by sending an email to Help Desk."
        ''
        'Alternatively you can always call Help Desk at +48 22 00 00 00'
        ''
        'Kind regards,'
        'Evotec IT'
    }
 
    .NOTES
    General notes
    #>

    [alias('EmailTextBox')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $TextBlock,
        [string[]] $Color = @(),
        [string[]] $BackGroundColor = @(),
        [alias('Size')][int[]] $FontSize = @(),
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string[]] $FontWeight = @(),
        [ValidateSet('normal', 'italic', 'oblique')][string[]] $FontStyle = @(),
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string[]] $TextDecoration = @(),
        [ValidateSet('normal', 'small-caps')][string[]] $FontVariant = @(),
        [string[]] $FontFamily = @(),
        [ValidateSet('left', 'center', 'right', 'justify')][string[]] $Alignment = @(),
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string[]] $TextTransform = @(),
        [ValidateSet('rtl')][string[]] $Direction = @(),
        [switch] $LineBreak
    )
    if ($TextBlock) {
        $Text = (Invoke-Command -ScriptBlock $TextBlock)
        if ($Text.Count) {
            $LineBreak = $true
        }
    }
    $Span = foreach ($T in $Text) {
        $newHTMLTextSplat = @{
            Alignment       = $Alignment
            FontSize        = $FontSize
            TextTransform   = $TextTransform
            Text            = $T
            Color           = $Color
            FontFamily      = $FontFamily
            Direction       = $Direction
            FontStyle       = $FontStyle
            TextDecoration  = $TextDecoration
            BackGroundColor = $BackGroundColor
            FontVariant     = $FontVariant
            FontWeight      = $FontWeight
            LineBreak       = $LineBreak
        }
        New-HTMLText @newHTMLTextSplat -SkipParagraph
    }
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'defaultText' } {
        $Span
    }
}
Register-ArgumentCompleter -CommandName New-HTMLTextBox -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLTextBox -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLTimeline {
    <#
    .SYNOPSIS
    Creates a new HTML timeline with customizable content.
 
    .DESCRIPTION
    This function creates an HTML timeline with the specified content. The timeline can be used to display a sequence of events or steps in a visually appealing manner.
 
    .PARAMETER Content
    Specifies the content of the HTML timeline as a script block.
 
    .EXAMPLE
    New-HTMLTimeline -Content {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'event' } -Value { "Event 1" }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'event' } -Value { "Event 2" }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'event' } -Value { "Event 3" }
    }
 
    Creates an HTML timeline with three events.
 
    #>

    [cmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('TimeLineItems')][ScriptBlock] $Content
    )
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.TimeLine = $true
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'timelineSimpleContainer' } {
        if ($null -eq $Value) {
            '' } else {
            Invoke-Command -ScriptBlock $Content }
    }
}
function New-HTMLTimelineItem {
    <#
    .SYNOPSIS
    Creates a new HTML timeline item with specified date, heading text, content text, and color.
 
    .DESCRIPTION
    This function generates an HTML timeline item with the provided information. It allows customization of the date, heading text, content text, and color of the timeline item.
 
    .PARAMETER Date
    Specifies the date for the timeline item. Defaults to the current date if not specified.
 
    .PARAMETER HeadingText
    Specifies the heading text for the timeline item.
 
    .PARAMETER Text
    Specifies the content text for the timeline item.
 
    .PARAMETER Color
    Specifies the color of the timeline item. If not provided, the default color is used.
 
    .EXAMPLE
    New-HTMLTimelineItem -Date "2022-01-01" -HeadingText "Project Kickoff" -Text "Started the project development phase." -Color "Blue"
    Creates a timeline item with a specific date, heading, content, and color.
 
    .EXAMPLE
    New-HTMLTimelineItem -HeadingText "Meeting with Client" -Text "Discussed project requirements." -Color "Green"
    Creates a timeline item with the current date, specified heading, content, and color.
 
    #>

    [CmdletBinding()]
    param(
        [DateTime] $Date = (Get-Date),
        [string] $HeadingText,
        [string] $Text,
        [string] $Color
    )
    $Attributes = @{
        class     = 'timelineSimple-item'
        "date-is" = $Date
    }

    if ($null -ne $Color) {
        $RGBcolor = ConvertFrom-Color -Color $Color
        $Style = @{
            color = $RGBcolor
        }
    } else {
        $Style = @{}
    }

    New-HTMLTag -Tag 'div' -Attributes $Attributes -Value {
        New-HTMLTag -Tag 'h1' -Attributes @{ class = 'timelineSimple'; style = $style } {
            $HeadingText
        }
        New-HTMLTag -Tag 'p' -Attributes @{ class = 'timelineSimple' } {
            $Text -Replace [Environment]::NewLine, '<br>' -replace '\n', '<br>'
        }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLTimelineItem -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-HTMLToast {
    <#
    .SYNOPSIS
    Creates a new HTML toast notification with customizable text, icons, and colors.
 
    .DESCRIPTION
    This function generates an HTML toast notification with options to set the text, text colors, icons, icon size, and various color settings for different elements of the notification.
 
    .PARAMETER TextHeader
    Specifies the header text of the notification.
 
    .PARAMETER TextHeaderColor
    Specifies the color of the header text.
 
    .PARAMETER Text
    Specifies the main text content of the notification.
 
    .PARAMETER TextColor
    Specifies the color of the main text.
 
    .PARAMETER IconSize
    Specifies the size of the icon in pixels.
 
    .PARAMETER IconColor
    Specifies the color of the icon.
 
    .PARAMETER BarColorLeft
    Specifies the color of the left bar in the notification.
 
    .PARAMETER BarColorRight
    Specifies the color of the right bar in the notification.
 
    .PARAMETER IconBrands
    Specifies the icon to be used from the Font Awesome Brands collection.
 
    .EXAMPLE
    New-HTMLToast -TextHeader "Notification" -Text "This is a sample notification" -IconBrands "fa-github" -BarColorRight "Red"
    Creates a new HTML toast notification with a header "Notification", main text "This is a sample notification", GitHub icon, and a red color for the right bar.
 
    .EXAMPLE
    New-HTMLToast -TextHeader "Alert" -Text "Alert message here" -IconBrands "fa-exclamation-triangle" -IconColor "Orange" -BarColorLeft "Yellow" -BarColorRight "Red"
    Creates a new HTML toast notification with a header "Alert", main text "Alert message here", warning triangle icon in orange color, yellow left bar, and red right bar.
 
    #>

    [CmdletBinding()]
    param(
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $TextHeader,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $TextHeaderColor,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $Text,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $TextColor,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][int] $IconSize = 30,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconColor = "Blue",

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $BarColorLeft = "Blue",

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $BarColorRight,

        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")][string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")][string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconSolid
    )

    [string] $Icon = ''
    if ($IconBrands) {
        $Icon = "fab fa-$IconBrands".ToLower() 
    } elseif ($IconRegular) {
        $Icon = "far fa-$IconRegular".ToLower() 
    } elseif ($IconSolid) {
        $Icon = "fas fa-$IconSolid".ToLower() 
    }
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.Toasts = $true
    $Script:HTMLSchema.Features.FontsAwesome = $true

    [string] $DivClass = "toast"

    $StyleText = @{ }
    if ($TextColor) {
        $StyleText.'color' = ConvertFrom-Color -Color $TextColor
    }

    $StyleTextHeader = @{ }
    if ($TextHeaderColor) {
        $StyleTextHeader.'color' = ConvertFrom-Color -Color $TextHeaderColor
    }

    $StyleIcon = @{ }
    if ($IconSize -ne 0) {
        $StyleIcon.'font-size' = "$($IconSize)px"
    }

    if ($IconColor) {
        $StyleIcon.'color' = ConvertFrom-Color -Color $IconColor
    }

    $StyleBarLeft = @{ }
    if ($BarColorLeft) {
        $StyleBarLeft.'background-color' = ConvertFrom-Color -Color $BarColorLeft
    }

    $StyleBarRight = @{ }
    if ($BarColorRight) {
        $StyleBarRight.'background-color' = ConvertFrom-Color -Color $BarColorRight
    }

    New-HTMLTag -Tag 'div' -Attributes @{ class = $DivClass } {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'toastBorderLeft'; style = $StyleBarLeft }
        New-HTMLTag -Tag 'div' -Attributes @{ class = "toastIcon $Icon"; style = $StyleIcon }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'toastContent' } {
            New-HTMLTag -Tag 'p' -Attributes @{ class = 'toastTextHeader'; style = $StyleTextHeader } {
                $TextHeader
            }
            New-HTMLTag -Tag 'p' -Attributes @{ class = 'toastText'; style = $StyleText } {
                $Text
            }
        }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'toastBorderRight'; style = $StyleBarRight }
    }
}

Register-ArgumentCompleter -CommandName New-HTMLToast -ParameterName TextHeaderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLToast -ParameterName TextColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLToast -ParameterName IconColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLToast -ParameterName BarColorLeft -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLToast -ParameterName BarColorRight -ScriptBlock $Script:ScriptBlockColors
function New-HTMLTree {
    <#
    .SYNOPSIS
    Creates a new HTML tree using FancyTree with various customization options.
 
    .DESCRIPTION
    This function creates a new HTML tree using FancyTree with the ability to customize the tree's appearance and behavior. It supports features like checkboxes, radio buttons, selection modes, icons, keyboard navigation, quick search, child counters, and more.
 
    .PARAMETER Data
    The script block that contains the data to populate the tree.
 
    .PARAMETER Checkbox
    Specifies the type of checkboxes to be displayed in the tree. Valid values are 'none', 'checkbox', or 'radio'. Default is 'none'.
 
    .PARAMETER SelectMode
    Specifies the selection mode for the tree. Valid values are 'none', '1', '2', or '3'. Default is '2'.
 
    .PARAMETER DisableIcons
    Disables icons in the tree if this switch is present.
 
    .PARAMETER DisableControl
    Disables control features in the tree if this switch is present.
 
    .PARAMETER DisableKeyboardNavigation
    Disables keyboard navigation in the tree if this switch is present.
 
    .PARAMETER AutoCollapseSibling
    Automatically collapses sibling nodes when a node is expanded.
 
    .PARAMETER AutoScroll
    Enables auto-scrolling when navigating the tree.
 
    .PARAMETER EnableQuickSearch
    Enables quick search functionality in the tree.
 
    .PARAMETER EnableChildCounter
    Enables child counter display in the tree.
 
    .PARAMETER WideSelection
    Enables wide selection mode in the tree.
 
    .PARAMETER MinimumExpandLevel
    Specifies the minimum level of nodes to expand in the tree.
 
    .EXAMPLE
    New-HTMLTree -Data {
        TreeNode -Node "Root" -Children {
            TreeNode -Node "Child 1"
            TreeNode -Node "Child 2"
        }
    } -Checkbox 'checkbox' -SelectMode '3' -EnableChildCounter -WideSelection
 
    Creates a new HTML tree with checkboxes, hierarchical selection mode, child counters, and wide selection enabled.
 
    #>

    [CmdletBinding()]
    param(
        [scriptblock] $Data,
        [ValidateSet('none', 'checkbox', 'radio')][string] $Checkbox = 'none',
        [ValidateSet('none', '1', '2', '3')] $SelectMode = '2',
        [switch] $DisableIcons,
        [switch] $DisableControl,
        [switch] $DisableKeyboardNavigation,
        [switch] $AutoCollapseSibling,
        [switch] $AutoScroll,
        [switch] $EnableQuickSearch,
        [switch] $EnableChildCounter,
        [switch] $WideSelection,
        [nullable[int]] $MinimumExpandLevel
    )
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.Jquery = $true
    $Script:HTMLSchema.Features.FancyTree = $true

    [string] $ID = "FancyTree" + (Get-RandomStringName -Size 8)

    if ($Data) {
        [Array] $Source = & $Data
    }

    $ChildCounter = [ordered] @{}
    $Nodes = [System.Collections.Generic.List[object]]::new()
    if ($Source.Count -gt 0) {
        foreach ($S in $Source) {
            if ($S.Type -eq 'TreeNode') {
                $Nodes.Add($S.Node)
            } elseif ($S.Type -eq 'ChildCounter') {
                $ChildCounter = $S.childcounter
            }
        }
    }

    $FancyTree = [ordered] @{}

    $FancyTree['extensions'] = @(
        "edit"
        "filter"
        if ($EnableChildCounter.IsPresent -or $ChildCounter.Count -gt 0) {
            "childcounter"
        }
        if ($WideSelection) {
            "wide"
        }
    )

    if ($Checkbox -eq 'none') {

    } elseif ($Checkbox -eq 'radio') {
        $FancyTree['checkbox'] = 'radio'
    } else {
        $FancyTree['checkbox'] = $true
    }

    if ($SelectMode -ne 'none') {
        $FancyTree['selectMode'] = $SelectMode 
    }
    if ($DisableIcons.IsPresent) {
        $FancyTree['icons'] = $false
    }
    if ($DisableControl.IsPresent) {
        $FancyTree['disabled'] = $true
    }
    if ($DisableKeyboardNavigation.IsPresent) {
        $FancyTree['keyboard'] = $false
    }
    if ($AutoCollapseSibling.IsPresent) {

        $FancyTree['autoCollapse'] = $true
    }
    if ($AutoScroll.IsPresent) {

        $FancyTree['autoScroll'] = $true
    }
    if ($EnableQuickSearch.IsPresent) {

        $FancyTree['quicksearch'] = $true
    }
    if ($ChildCounter.Count -gt 0) {
        $FancyTree['childcounter'] = $ChildCounter
    }
    if ($MinimumExpandLevel) {
        $FancyTree['minExpandLevel'] = $MinimumExpandLevel
    }
    if ($Nodes.Count -gt 0) {
        $FancyTree['source'] = $Nodes
    }
    Remove-EmptyValue -Hashtable $FancyTree -Rerun 1 -Recursive

    $Div = New-HTMLTag -Tag 'div' -Attributes @{ id = $ID; class = 'fancyTree' }

    $Activation = @"
    function (event, data) {
        var node = data.node;
        // Use <a> href and target attributes to load the content:
        if (node.data.href) {
            // Open target
            window.open(node.data.href, node.data.target);
            // or open target in iframe
            //$("[name=contentFrame]").attr("src", node.data.href);
        }
    },
"@


    $FancyTree['activate'] = "templateToReplace"

    $FancyTreeJSON = $FancyTree | ConvertTo-Json -Depth 100 

    $FancyTreeJSON = $FancyTreeJSON -replace '"templateToReplace"', $Activation

    $Script = New-HTMLTag -Tag 'script' -Value {
        $DivID = -join ('#', $ID)

        '$(function(){ // on page load'
        "`$(`"$DivID`").fancytree("
        $FancyTreeJSON
        ');'
        '});'
    } -NewLine

    $Div
    $Script
}
function New-HTMLTreeChildCounter {
    <#
    .SYNOPSIS
    Creates a new HTML tree child counter object with specified options.
 
    .DESCRIPTION
    This function creates a new HTML tree child counter object with the ability to customize its behavior using the provided parameters.
 
    .PARAMETER Deep
    Indicates whether to count child elements recursively.
 
    .PARAMETER HideZero
    Indicates whether to hide counters for child elements with a count of zero.
 
    .PARAMETER HideExpanded
    Indicates whether to hide counters for expanded child elements.
 
    .EXAMPLE
    PS C:\> New-HTMLTreeChildCounter -Deep
    Creates a new HTML tree child counter object that counts child elements recursively.
 
    .EXAMPLE
    PS C:\> New-HTMLTreeChildCounter -HideZero -HideExpanded
    Creates a new HTML tree child counter object that hides counters for child elements with a count of zero and expanded child elements.
 
    #>

    [CmdletBinding()]
    param(
        [switch] $Deep,
        [switch] $HideZero,
        [switch] $HideExpanded
    )

    $Counter = [ordered] @{
        Type         = 'ChildCounter'
        childcounter = [ordered] @{
            deep         = $Deep.IsPresent
            hideZeros    = $HideZero.IsPresent
            hideExpanded = $HideExpanded.IsPresent
        }
    }
    $Counter
}
function New-HTMLTreeNode {
    <#
    .SYNOPSIS
    Creates a new HTML tree node with specified properties.
 
    .DESCRIPTION
    This function creates a new HTML tree node with the specified properties. It allows for customization of various attributes such as title, ID, folder structure, tooltip, icon, selection status, expansion status, etc.
 
    .PARAMETER Children
    Specifies the script block that defines the children nodes of the tree node.
 
    .PARAMETER Title
    Specifies the title of the tree node.
 
    .PARAMETER Id
    Specifies the unique identifier of the tree node.
 
    .PARAMETER Folder
    Indicates whether the tree node represents a folder.
 
    .PARAMETER Tooltip
    Specifies the tooltip text for the tree node.
 
    .PARAMETER Icon
    Specifies the icon for the tree node.
 
    .PARAMETER IconTooltip
    Specifies the tooltip text for the icon.
 
    .PARAMETER IsSelected
    Indicates whether the tree node is selected.
 
    .PARAMETER IsExpanded
    Indicates whether the tree node is expanded.
 
    .PARAMETER Unselectable
    Indicates whether the tree node is unselectable.
 
    .PARAMETER Checkbox
    Specifies the type of checkbox for the tree node. Valid values are 'none', 'checkbox', or 'radio'.
 
    .PARAMETER HrefLink
    Specifies the URL link for the tree node.
 
    .PARAMETER Target
    Specifies the target window for the URL link. Valid values are "_blank", "_self", "_parent", "_top", or a frame name.
 
    .EXAMPLE
    New-HTMLTreeNode -Title "Folder 1" -Id "folder1" -Folder -Children {
        New-HTMLTreeNode -Title "Item 1" -Id "item1"
        New-HTMLTreeNode -Title "Item 2" -Id "item2"
    }
 
    .EXAMPLE
    New-HTMLTreeNode -Title "Document" -Id "doc1" -HrefLink "https://example.com" -Target "_blank"
 
    #>

    [CmdletBinding()]
    param(
        [scriptblock] $Children,
        [string] $Title,
        [string] $Id,
        [switch] $Folder,
        [string] $Tooltip,
        [string] $Icon,
        [string] $IconTooltip,
        [switch] $IsSelected,
        [alias('Expanded')][switch] $IsExpanded,
        [switch] $Unselectable,
        [ValidateSet('none', 'checkbox', 'radio')][string] $Checkbox,
        [alias('Url', 'Link', 'UrlLink', 'Href')][string] $HrefLink,
        [string] $Target # "_blank|_self|_parent|_top|framename"
    )

    if ($Children) {
        [Array] $SourceChildren = & $Children
        $NestedChildren = $SourceChildren.Node
    }

    $Node = [ordered] @{
        title       = $Title
        key         = $Id
        tooltip     = $Tooltip
        iconTooltip = $IconTooltip
    }
    if ($Checkbox -eq 'radio') {
        $Node['checkbox'] = 'radio'
    } elseif ($Checkbox -eq 'checkbox') {
        $Node['checkbox'] = $true
    }
    if ($Folder) {
        $Node['folder'] = $true
    }
    if ($IsSelected.IsPresent) {
        $Node['selected'] = $true
    }
    if ($IsExpanded.IsPresent) {
        $Node['expanded'] = $true
    }
    if ($SourceChildren.Count) {
        $Node['children'] = @( $NestedChildren )
    }
    if ($Unselectable.IsPresent) {
        $Node['unselectable'] = $true
    }
    if ($HrefLink -and $Target) {
        $Node['data'] = [ordered] @{
            href   = $HrefLink
            target = $Target
        }
    } elseif ($Href) {
        $Node['data'] = [ordered] @{
            href   = $HrefLink
            target = "_blank"
        }
    }
    if ($Icon) {
        $Node['icon'] = $Icon
    }
    Remove-EmptyValue -Hashtable $Node
    [ordered] @{
        Type = 'TreeNode'
        Node = $Node
    }
}
function New-HTMLWinBox {
    <#
    .SYNOPSIS
    Creates a customizable HTML window box with various options for styling and functionality.
 
    .DESCRIPTION
    This function allows you to create an HTML window box with customizable features such as title, background color, initial position, size, URL content, modal display, theme, and more.
 
    .PARAMETER HTML
    Specifies the HTML content to be displayed within the window box.
 
    .PARAMETER Title
    Specifies the title of the window box.
 
    .PARAMETER BackgroundColor
    Specifies the background color of the window box title.
 
    .PARAMETER Index
    Set the initial z-index of the window to this value (could be increased automatically when unfocused/focused).
 
    .PARAMETER Border
    Set the border width of the window (supports all css units, like px, %, em, rem, vh, vmax).
 
    .PARAMETER Height
    Set the initial width/height of the window (supports units "px" and "%").
 
    .PARAMETER Width
    Set the initial width/height of the window (supports units "px" and "%").
 
    .PARAMETER X
    Set the initial position of the window (supports: "right" for x-axis, "bottom" for y-axis, "center" for both, units "px" and "%" for both).
 
    .PARAMETER Y
    Set the initial position of the window (supports: "right" for x-axis, "bottom" for y-axis, "center" for both, units "px" and "%" for both).
 
    .PARAMETER Top
    Set or limit the viewport of the window's available area (supports units "px" and "%").
 
    .PARAMETER Right
    Set or limit the viewport of the window's available area (supports units "px" and "%").
 
    .PARAMETER Bottom
    Set or limit the viewport of the window's available area (supports units "px" and "%").
 
    .PARAMETER Left
    Set or limit the viewport of the window's available area (supports units "px" and "%").
 
    .PARAMETER Url
    Specifies the URL content to be loaded inside the window box via iframe.
 
    .PARAMETER Modal
    Indicates whether the window box should be displayed as a modal window.
 
    .PARAMETER Maximize
    Automatically maximizes the window box when created.
 
    .PARAMETER Theme
    Specifies the theme of the window box.
 
    .PARAMETER NoAnimation
    Disables transition animations for the window box.
 
    .PARAMETER NoShadow
    Disables the drop shadow effect for the window box.
 
    .PARAMETER NoHeader
    Hides the header of the window box, including the title and toolbar.
 
    .PARAMETER NoMinmizeIcon
    Hides the minimize icon in the window box.
 
    .PARAMETER NoMaximizeIcon
    Hides the maximize icon in the window box.
 
    .PARAMETER NoFullScreenIcon
    Hides the fullscreen icon in the window box.
 
    .PARAMETER NoCloseIcon
    Hides the close icon in the window box.
 
    .PARAMETER NoResizeCapability
    Disables the resizing capability of the window box.
 
    .PARAMETER NoMoveCapability
    Disables the moving capability of the window box.
 
    .EXAMPLE
    $Data = Get-Process | Select-Object -First 3
 
    New-HTML -TitleText 'This is a test' -FilePath "$PSScriptRoot\Example-WinBox01.html" {
        New-HTMLWinBox -Title 'This is a test Window' -BackgroundColor Red {
            New-HTMLText -Text 'This is a text within modal dialog'
            New-HTMLTable -DataTable $Data
        } -Width 50% -Height 50% -X center -Y center
    } -Online -ShowHTML
 
    .NOTES
    This function provides a flexible way to create interactive and styled window boxes in HTML.
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $HTML,
        [string] $Title,
        [string] $BackgroundColor,
        [nullable[int]] $Index,
        [string] $Border,
        [string] $Height,
        [string] $Width,
        [string] $X,
        [string] $Y,
        [string] $Top,
        [string] $Right,
        [string] $Bottom,
        [string] $Left,
        [alias('Uri')][uri] $Url,
        [switch] $Modal,
        [switch] $Maximize,
        [ValidateSet('modern', 'white')][string] $Theme,

        [switch] $NoAnimation,
        [switch] $NoShadow,
        [switch] $NoHeader,
        [switch] $NoMinmizeIcon,
        [switch] $NoMaximizeIcon,
        [switch] $NoFullScreenIcon,
        [switch] $NoCloseIcon,
        [switch] $NoResizeCapability,
        [switch] $NoMoveCapability
    )
    $WinBoxHidden = "WinBoxModal-$(Get-RandomStringName -Size 8 -LettersOnly)"
    $WinBoxID = "WinBox-$(Get-RandomStringName -Size 8 -LettersOnly)"

    $Script:HTMLSchema.Features.WinBox = $true
    $Script:HTMLSchema.Features.MainFlex = $true

    [Array] $Class = @(
        if ($NoAnimation) {
            'no-animation'
        }
        if ($NoShadow) {
            'no-shadow'
        }
        if ($NoHeader) {
            'no-header'
        }
        if ($NoMinmizeIcon) {
            'no-min'
        }
        if ($NoMaximizeIcon) {
            'no-max'
        }
        if ($NoFullScreenIcon) {
            'no-full'
        }
        if ($NoCloseIcon) {
            'no-close'
        }
        if ($NoResizeCapability) {
            'no-resize'
        }
        if ($NoMoveCapability) {
            'no-move'
        }
    )

    $Options = [ordered] @{
        id         = $WinBoxID
        class      = if ($Class.Count -gt 0) {
            $Class } else {
            $null }
        title      = $Title
        background = ConvertFrom-Color -Color $BackgroundColor
        height     = $Height
        width      = $Width
        top        = $Top
        left       = $Left
        right      = $Right
        bottom     = $Bottom
        x          = $X
        y          = $Y
        url        = $Url
        index      = $Index
        theme      = $Theme
    }
    if ($Maximize) {
        $Options['max'] = $true
    }
    if ($Modal) {
        $Options['modal'] = $true
    }

    if ($HTML) {
        $Options['mount'] = 'replaceHTMLme'
        New-HTMLTag -Tag 'div' -Attributes @{ style = @{ "display" = "none" } } {
            New-HTMLTag -Tag 'div' -Attributes @{ id = $WinBoxHidden; } {
                & $HTML
            }
        }
    }

    Remove-EmptyValue -Hashtable $Options
    $OptionsJSON = $Options | ConvertTo-JsonLiteral

    if ($HTML) {
        $OptionsJSON = $OptionsJSON.Replace('"replaceHTMLme"', "document.getElementById('$WinBoxHidden')")
    }

    New-HTMLTag -Tag 'script' {
        "new WinBox($OptionsJSON);"
    }
}

Register-ArgumentCompleter -CommandName New-HTMLWinbox -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLWizard {
    <#
    .SYNOPSIS
    Provides a simple way to build wizard
 
    .DESCRIPTION
    Provides a simple way to build wizard
 
    .PARAMETER Theme
    Choose a theme to display wizard in
 
    .PARAMETER DisableCycleSteps
    Disables the navigation cycle through
 
    .PARAMETER ToolbarPosition
    Position of the toolbar (none, top, bottom, both)
 
    .PARAMETER ToolbarButtonPosition
    Button alignment of the toolbar (left, right, center)
 
    .PARAMETER HideNextButton
    Hide next button
 
    .PARAMETER HidePreviousButton
    Hide previous button
 
    .PARAMETER DiableAnchorClickable
    Disable anchor navigation
 
    .PARAMETER EnableAllAnchors
    Activates all anchors clickable all times
 
    .PARAMETER DisableMarkDoneStep
    Disable done state on navigation
 
    .PARAMETER DisableMarkAllPreviousStepsAsDone
    Disable when a step selected by url hash, all previous steps are marked done
 
    .PARAMETER RemoveDoneStepOnNavigateBack
    While navigate back done step after active step will be cleared
 
    .PARAMETER DisableAnchorOnDoneStep
    Disable the done steps navigation
 
    .PARAMETER DisableJustification
    Disable navigation menu justification
 
    .PARAMETER DisableBackButtonSupport
    Disable the back button support
 
    .PARAMETER DisableURLhash
    Disable selection of the tab based on url hash
 
    .PARAMETER TransitionAnimation
    Effect on navigation, none/fade/slide-horizontal/slide-vertical/slide-swing
 
    .PARAMETER TransitionSpeed
    Transion animation speed. Default 400
 
    .EXAMPLE
    An example
 
    .NOTES
    Implementation based on: http://techlaboratory.net/jquery-smartwizard
    License: MIT
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $WizardSteps,
        [ValidateSet('basic', 'arrows', 'square', 'round', 'dots')][string] $Theme,
        [switch] $DisableCycleSteps,

        [ValidateSet('bottom', 'top', 'both', 'none')][string] $ToolbarPosition,
        [ValidateSet('right', 'left', 'center')][string] $ToolbarButtonPosition,
        [switch] $HideNextButton,
        [switch] $HidePreviousButton,

        [switch] $DiableAnchorClickable, #: true, // Enable/Disable anchor navigation
        [switch] $EnableAllAnchors, #: false, // Activates all anchors clickable all times
        [switch] $DisableMarkDoneStep, #: true, // Add done state on navigation
        [switch] $DisableMarkAllPreviousStepsAsDone, #: true, // When a step selected by url hash, all previous steps are marked done
        [switch] $RemoveDoneStepOnNavigateBack, #: false, // While navigate back done step after active step will be cleared
        [switch] $DisableAnchorOnDoneStep, #: true // Enable/Disable the done steps navigation

        [switch] $DisableJustification,
        [switch] $DisableBackButtonSupport,
        [switch] $DisableURLhash,
        [ValidateSet('none', 'fade', 'slide-horizontal', 'slide-vertical', 'slide-swing')][string] $TransitionAnimation, # 'none', // Effect on navigation, none/fade/slide-horizontal/slide-vertical/slide-swing
        [int] $TransitionSpeed
    )
    $Script:HTMLSchema.Features.MainFlex = $true
    $Script:HTMLSchema.Features.JQuery = $true
    $Script:HTMLSchema.Features.Wizard = $true
    $Script:HTMLSchema.Features.RedrawObjects = $true

    $WizardID = "TabPanel-$(Get-RandomStringName -Size 8 -LettersOnly)"
    if ($WizardSteps) {
        $Script:HTMLSchema['WizardList'].Add($WizardID)
        $WizardContent = & $WizardSteps
        if ($WizardContent) {
            New-HTMLTag -Tag 'div' -Attributes @{ id = $WizardID; class = 'flexElement defaultWizard' } {
                New-HTMLTag -Tag 'ul' -Attributes @{ class = 'nav' } {
                    foreach ($Step in $WizardContent) {
                        New-HTMLTag -Tag 'li' {
                            New-HTMLTag -Tag 'a' -Attributes @{ class = 'nav-link'; href = "#$($Step.ID)" } {
                                if ($Tab.Icon) {
                                    New-HTMLTag -Tag 'span' -Attributes @{ class = $($Step.Icon); style = $($Step.StyleIcon) }
                                    '&nbsp;' 
                                }
                                New-HTMLTag -Tag 'span' -Attributes @{ style = $($Step.StyleText ) } -Value { $Step.Name }
                            }
                        }
                    }
                }
                New-HTMLTag -Tag 'div' -Attributes @{ class = 'tab-content flexElement' } {
                    foreach ($Step in $WizardContent) {
                        New-HTMLTag -Tag 'div' -Attributes @{ class = 'tab-pane flexElement'; id = $Step.ID; role = 'tabpanel'; style = @{ padding = '0px' } } {
                            $Step.Content
                        }
                    }
                }
                $SmartWizard = [ordered] @{
                    orientation      = $Orientation
                    autoAdjustHeight = $false 
                }
                if ($Theme) {
                    $SmartWizard['theme'] = $Theme
                }
                if ($TransitionAnimation) {
                    $SmartWizard['transition'] = [ordered] @{}
                    $SmartWizard['transition']['animation'] = $TransitionAnimation
                    if ($TransitionSpeed) {
                        $SmartWizard['transition']['speed'] = $TransitionSpeed
                    }
                }
                if ($ToolbarPosition -or $ToolbarButtonPosition -or $HideNextButton -or $HidePreviousButton) {
                    $SmartWizard['toolbarSettings'] = [ordered]@{}
                    if ($ToolbarPosition) {
                        $SmartWizard['toolbarSettings']['toolbarPosition'] = $ToolbarPosition
                    }
                    if ($ToolbarButtonPosition) {
                        $SmartWizard['toolbarSettings']['toolbarButtonPosition'] = $ToolbarButtonPosition
                    }
                    if ($HideNextButton) {
                        $SmartWizard['toolbarSettings']['showNextButton'] = $false
                    }
                    if ($HidePreviousButton) {
                        $SmartWizard['toolbarSettings']['showPreviousButton'] = $false
                    }
                }

                if ($DiableAnchorClickable -or $EnableAllAnchors -or $DisableMarkDoneStep -or $DisableMarkAllPreviousStepsAsDone -or $RemoveDoneStepOnNavigateBack -or $DisableAnchorOnDoneStep) {
                    $SmartWizard['anchorSettings'] = [ordered]@{}
                    if ($DiableAnchorClickable) {
                        $SmartWizard['anchorSettings']['anchorClickable'] = $false
                    }
                    if ($EnableAllAnchors) {
                        $SmartWizard['anchorSettings']['enableAllAnchors'] = $true
                    }
                    if ($DisableMarkDoneStep) {
                        $SmartWizard['anchorSettings']['markDoneStep'] = $false
                    }
                    if ($DisableMarkAllPreviousStepsAsDone) {
                        $SmartWizard['anchorSettings']['markAllPreviousStepsAsDone'] = $false
                    }
                    if ($RemoveDoneStepOnNavigateBack) {
                        $SmartWizard['anchorSettings']['removeDoneStepOnNavigateBack'] = $true
                    }
                    if ($DisableAnchorOnDoneStep) {
                        $SmartWizard['anchorSettings']['enableAnchorOnDoneStep'] = $false
                    }
                }

                if ($DisableCycleSteps) {
                    $SmartWizard['cycleSteps'] = $false
                }
                if ($DisableJustification) {
                    $SmartWizard['justified'] = $false
                }
                if ($DisableBackButtonSupport) {
                    $SmartWizard['backButtonSupport'] = $false
                }
                if ($DisableURLhash) {
                    $SmartWizard['enableURLhash'] = $false
                }

                Remove-EmptyValue -Hashtable $SmartWizard
                $SmartWizardConfiguration = $SmartWizard | ConvertTo-Json -Depth 2

                New-HTMLTag -Tag 'script' {
                    @"
                `$(document).ready(function(){
                    // SmartWizard initialize
                    `$('#$WizardID').smartWizard($SmartWizardConfiguration);
                });
                // Initialize the stepContent event
                `$("#$WizardID").on("showStep", function (e, anchorObject, stepIndex, stepDirection) {
                    if (anchorObject[0].hash) {
                        var id = anchorObject[0].hash.replace('#', '');
                        findObjectsToRedraw(id);
                    };
                });
"@

                }
            }
            $null = $Script:HTMLSchema['WizardList'].Remove($TabID)
        }
    }
}
function New-HTMLWizardColor {
    <#
    .SYNOPSIS
    Creates a new HTML Wizard Color configuration.
 
    .DESCRIPTION
    This function creates a new HTML Wizard Color configuration with customizable color settings for various elements in the wizard interface.
 
    .PARAMETER BorderColor
    Specifies the color for the border of the wizard interface.
 
    .PARAMETER ToolbarBtnColor
    Specifies the color for the toolbar button text.
 
    .PARAMETER ToolbarBtnBackgroundColor
    Specifies the background color for the toolbar buttons.
 
    .PARAMETER AnchorDefaultPrimaryColor
    Specifies the primary color for default anchor elements.
 
    .PARAMETER AnchorDefaultSecondaryColor
    Specifies the secondary color for default anchor elements.
 
    .PARAMETER AnchorActivePrimaryColor
    Specifies the primary color for active anchor elements.
 
    .PARAMETER AnchorActiveSecondaryColor
    Specifies the secondary color for active anchor elements.
 
    .PARAMETER AnchorDonePrimaryColor
    Specifies the primary color for completed anchor elements.
 
    .PARAMETER AnchorDoneSecondaryColor
    Specifies the secondary color for completed anchor elements.
 
    .PARAMETER AnchorDisabledPrimaryColor
    Specifies the primary color for disabled anchor elements.
 
    .PARAMETER AnchorDisabledSecondaryColor
    Specifies the secondary color for disabled anchor elements.
 
    .PARAMETER AnchorErrorPrimaryColor
    Specifies the primary color for anchor elements in error state.
 
    .PARAMETER AnchorErrorSecondaryColor
    Specifies the secondary color for anchor elements in error state.
 
    .PARAMETER AnchorWarningPrimaryColor
    Specifies the primary color for anchor elements in warning state.
 
    .PARAMETER AnchorWarningSecondaryColor
    Specifies the secondary color for anchor elements in warning state.
 
    .PARAMETER ProgressColor
    Specifies the color for the progress indicator.
 
    .PARAMETER ProgressBackgroundColor
    Specifies the background color for the progress indicator.
 
    .PARAMETER LoaderColor
    Specifies the color for the loader animation.
 
    .PARAMETER LoaderBackgroundColor
    Specifies the background color for the loader animation.
 
    .PARAMETER LoaderBackgroundWrapperColor
    Specifies the background color for the loader animation wrapper.
 
    .EXAMPLE
    New-HTMLWizardColor -BorderColor "#FF0000" -ToolbarBtnColor "#00FF00" -ToolbarBtnBackgroundColor "#0000FF"
    Creates a new HTML Wizard Color configuration with specified border, toolbar button text, and toolbar button background colors.
 
    #>

    [CmdletBinding()]
    param(
        [string] $BorderColor,
        [string] $ToolbarBtnColor,
        [string] $ToolbarBtnBackgroundColor,
        [string] $AnchorDefaultPrimaryColor,
        [string] $AnchorDefaultSecondaryColor,
        [string] $AnchorActivePrimaryColor,
        [string] $AnchorActiveSecondaryColor,
        [string] $AnchorDonePrimaryColor,
        [string] $AnchorDoneSecondaryColor,
        [string] $AnchorDisabledPrimaryColor,
        [string] $AnchorDisabledSecondaryColor,
        [string] $AnchorErrorPrimaryColor,
        [string] $AnchorErrorSecondaryColor,
        [string] $AnchorWarningPrimaryColor,
        [string] $AnchorWarningSecondaryColor,
        [string] $ProgressColor,
        [string] $ProgressBackgroundColor,
        [string] $LoaderColor,
        [string] $LoaderBackgroundColor,
        [string] $LoaderBackgroundWrapperColor
    )
    Enable-HTMLFeature -Feature WizardColor

    if ($Script:CurrentConfiguration -and $Script:CurrentConfiguration.Features.WizardColor) {
        if ($Script:CurrentConfiguration -and $Script:CurrentConfiguration.Features.WizardColor) {
            if ($PSBoundParameters.ContainsKey('BorderColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-border-color'] = ConvertFrom-Color -Color $BorderColor
            }
            if ($PSBoundParameters.ContainsKey('ToolbarBtnColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-toolbar-btn-color'] = ConvertFrom-Color -Color $ToolbarBtnColor
            }
            if ($PSBoundParameters.ContainsKey('ToolbarBtnBackgroundColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-toolbar-btn-background-color'] = ConvertFrom-Color -Color $ToolbarBtnBackgroundColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorDefaultPrimaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-default-primary-color'] = ConvertFrom-Color -Color $AnchorDefaultPrimaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorDefaultSecondaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-default-secondary-color'] = ConvertFrom-Color -Color $AnchorDefaultSecondaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorActivePrimaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-active-primary-color'] = ConvertFrom-Color -Color $AnchorActivePrimaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorActiveSecondaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-active-secondary-color'] = ConvertFrom-Color -Color $AnchorActiveSecondaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorDonePrimaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-done-primary-color'] = ConvertFrom-Color -Color $AnchorDonePrimaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorDoneSecondaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-done-secondary-color'] = ConvertFrom-Color -Color $AnchorDoneSecondaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorDisabledPrimaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-disabled-primary-color'] = ConvertFrom-Color -Color $AnchorDisabledPrimaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorDisabledSecondaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-disabled-secondary-color'] = ConvertFrom-Color -Color $AnchorDisabledSecondaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorErrorPrimaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-error-primary-color'] = ConvertFrom-Color -Color $AnchorErrorPrimaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorErrorSecondaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-error-secondary-color'] = ConvertFrom-Color -Color $AnchorErrorSecondaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorWarningPrimaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-warning-primary-color'] = ConvertFrom-Color -Color $AnchorWarningPrimaryColor
            }
            if ($PSBoundParameters.ContainsKey('AnchorWarningSecondaryColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-anchor-warning-secondary-color'] = ConvertFrom-Color -Color $AnchorWarningSecondaryColor
            }
            if ($PSBoundParameters.ContainsKey('ProgressColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-progress-color'] = ConvertFrom-Color -Color $ProgressColor
            }
            if ($PSBoundParameters.ContainsKey('ProgressBackgroundColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-progress-background-color'] = ConvertFrom-Color -Color $ProgressBackgroundColor
            }
            if ($PSBoundParameters.ContainsKey('LoaderColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-loader-color'] = ConvertFrom-Color -Color $LoaderColor
            }
            if ($PSBoundParameters.ContainsKey('LoaderBackgroundColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-loader-background-color'] = ConvertFrom-Color -Color $LoaderBackgroundColor
            }
            if ($PSBoundParameters.ContainsKey('LoaderBackgroundWrapperColor')) {
                $Script:CurrentConfiguration.Features.WizardColor.HeaderAlways.CssInline[':root']['--sw-loader-background-wrapper-color'] = ConvertFrom-Color -Color $LoaderBackgroundWrapperColor
            }
        }
    }
}
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName BorderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName ToolbarBtnColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName ToolbarBtnBackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorDefaultPrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorDefaultSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorActivePrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorActiveSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorDonePrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorDoneSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorDisabledPrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorDisabledSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorErrorPrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorErrorSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorWarningPrimaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName AnchorWarningSecondaryColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName ProgressColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName ProgressBackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName LoaderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName LoaderBackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLWizardColor -ParameterName LoaderBackgroundWrapperColor -ScriptBlock $Script:ScriptBlockColors
function New-HTMLWizardStep {
    <#
    .SYNOPSIS
    Creates a new HTML wizard step with customizable options.
 
    .DESCRIPTION
    This function creates a new HTML wizard step with various customization options such as setting the HTML data, tab heading, tab name, and icons.
 
    .PARAMETER HtmlData
    Specifies the HTML data to be displayed in the wizard step.
 
    .PARAMETER Heading
    Specifies the heading text for the tab.
 
    .PARAMETER Name
    Specifies the name of the tab.
 
    .PARAMETER IconBrands
    Specifies the icon for the tab from the FontAwesome Brands collection.
 
    .PARAMETER IconRegular
    Specifies the icon for the tab from the FontAwesome Regular collection.
 
    .PARAMETER IconSolid
    Specifies the icon for the tab from the FontAwesome Solid collection.
 
    .EXAMPLE
    New-HTMLWizardStep -HtmlData { Get-Content -Path "C:\example.html" } -Heading "Step 1" -Name "First Tab" -IconBrands "fa fa-facebook"
 
    Creates a new HTML wizard step with the specified HTML data, tab heading, tab name, and FontAwesome Brands icon.
 
    .EXAMPLE
    New-HTMLWizardStep -HtmlData { Get-Content -Path "C:\another.html" } -Heading "Step 2" -Name "Second Tab" -IconRegular "fa fa-github"
 
    Creates a new HTML wizard step with the specified HTML data, tab heading, tab name, and FontAwesome Regular icon.
    #>

    [CmdLetBinding(DefaultParameterSetName = 'FontAwesomeBrands')]
    param(
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $HtmlData = $(Throw "No curly brace?)"),
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [alias('TabHeading')][Parameter(Mandatory = $false, Position = 1)][String]$Heading,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [alias('TabName')][string] $Name = 'Tab',

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript( { $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys)) })]
        [string] $IconBrands,

        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript( { $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys)) })]
        [string] $IconRegular,

        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript( { $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys)) })]
        [string] $IconSolid,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][object] $TextSize,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $TextColor,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][object] $IconSize,
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconColor,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [string] $AnchorName
    )
    if (-not $Script:HTMLSchema.Features) {
        Write-Warning 'New-HTMLWizardStep - Creation of HTML aborted. Most likely New-HTML is missing.'
        Exit
    }
    if (-not $AnchorName) {
        $AnchorName = "WizardStep-$(Get-RandomStringName -Size 8)"
    }
    [string] $Icon = ''
    if ($IconBrands) {
        $Icon = "fab fa-$IconBrands".ToLower() 
    } elseif ($IconRegular) {
        $Icon = "far fa-$IconRegular".ToLower() 
    } elseif ($IconSolid) {
        $Icon = "fas fa-$IconSolid".ToLower() 
    }

    $StyleText = @{ }

    $StyleText['font-size'] = ConvertFrom-Size -Size $TextSize  

    if ($TextColor) {
        $StyleText.'color' = ConvertFrom-Color -Color $TextColor
    }

    $StyleText.'text-transform' = "$TextTransform"

    $StyleIcon = @{ }

    $StyleIcon.'font-size' = ConvertFrom-Size -Size $IconSize 

    if ($IconColor) {
        $StyleIcon.'color' = ConvertFrom-Color -Color $IconColor
    }

    if ($HtmlData) {
        $WizardExecutedCode = & $HtmlData
    } else {
        $WizardExecutedCode = ''
    }
    [PSCustomObject] @{
        Name      = $Name
        ID        = $AnchorName
        Icon      = $Icon
        StyleIcon = $StyleIcon
        StyleText = $StyleText
        Content   = $WizardExecutedCode
    }
}
function New-MapArea {
    <#
    .SYNOPSIS
    Creates a new MapArea object with specified parameters.
 
    .DESCRIPTION
    This function creates a new MapArea object with the specified area, href, value, and optional tooltip content.
 
    .PARAMETER Area
    Specifies the area for the MapArea object. This parameter is mandatory.
 
    .PARAMETER Href
    Specifies the href attribute for the MapArea object.
 
    .PARAMETER Value
    Specifies the value attribute for the MapArea object. This parameter is mandatory.
 
    .PARAMETER Tooltip
    Specifies the tooltip content for the MapArea object. This parameter can be provided as a script block.
 
    .EXAMPLE
    New-MapArea -Area "North" -Href "north.html" -Value "North Region" -Tooltip { "This is the North region." }
 
    Creates a new MapArea object for the North region with the specified href, value, and tooltip content.
 
    .EXAMPLE
    New-MapArea -Area "South" -Href "south.html" -Value "South Region"
 
    Creates a new MapArea object for the South region with the specified href and value.
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $Area,
        [string] $Href,
        [Parameter(Mandatory)][string] $Value,
        [alias('Tooltip')][scriptblock] $TooltipContent
    )

    $OutputObject = [ordered] @{
        Type          = 'MapArea'
        Configuration = [ordered] @{
            $Area = [ordered] @{
                href    = $Href
                value   = $Value
                tooltip = @{
                    content = if ($TooltipContent) {
                        $ValueExecuted = & $TooltipContent
                        $ValueExecuted -join ""
                    } else {
                        $null
                    }
                }
            }
        }
    }
    Remove-EmptyValue -Hashtable $OutputObject.Configuration -Recursive -Rerun 2
    $OutputObject
}
function New-MapLegendOption {
    <#
    .SYNOPSIS
    Creates a new map legend option with specified parameters.
 
    .DESCRIPTION
    This function creates a new map legend option with the provided parameters. It allows customization of the legend title, redraw behavior on resize, and display mode.
 
    .PARAMETER Type
    Specifies the type of the legend option. Valid values are 'area' and 'plot'.
 
    .PARAMETER Title
    Specifies the title of the legend option.
 
    .PARAMETER RedrawOnResize
    Specifies whether the legend should redraw on resize. Default is $null.
 
    .PARAMETER Mode
    Specifies the display mode of the legend option. Valid values are 'horizontal' and 'vertical'.
 
    .EXAMPLE
    New-MapLegendOption -Type "area" -Title "Area Legend" -RedrawOnResize $true -Mode "horizontal"
    Creates a new map legend option for an area with the title "Area Legend", redraws on resize, and displays horizontally.
 
    .EXAMPLE
    New-MapLegendOption -Type "plot" -Title "Plot Legend" -Mode "vertical"
    Creates a new map legend option for a plot with the title "Plot Legend" and displays vertically.
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][ValidateSet('area', 'plot')]$Type,
        [string] $Title,
        [nullable[bool]] $RedrawOnResize,
        [ValidateSet('horizontal', 'vertical')][string] $Mode
    )

    $OutputObject = [ordered] @{
        Type          = 'MapLegendOption'
        Configuration = [ordered] @{
            default = [ordered] @{
                redrawOnResize = $RedrawOnResize
            }
            area    = [ordered] @{}
            plot    = [ordered] @{}
        }
    }
    if ($Type -eq 'area') {
        $OutputObject.Configuration.area = [ordered] @{
            title = $Title
            mode  = $Mode
        }
    } elseif ($Type -eq 'plot') {
        $OutputObject.Configuration.plot = [ordered] @{
            title = $Title
            mode  = $Mode
        }
    }
    Remove-EmptyValue -Hashtable $OutputObject.Configuration -Recursive -Rerun 2
    $OutputObject
}
function New-MapLegendSlice {
    <#
    .SYNOPSIS
    Creates a new map legend slice.
 
    .DESCRIPTION
    This function creates a new map legend slice with specified parameters.
 
    .PARAMETER Label
    The label of the slice for the legend.
 
    .PARAMETER FillColor
    The color to fill the slice with.
 
    .PARAMETER MinimumValue
    The minimum value for the interval defining the slice.
 
    .PARAMETER MaximumValue
    The maximum value for the interval defining the slice.
 
    .PARAMETER Value
    The fixed value for the slice. Use this instead of 'MinimumValue' and 'MaximumValue' to set a specific value.
 
    .PARAMETER DisplayInLegend
    Specifies whether to display the slice in the legend. Default is true.
 
    .PARAMETER InitializeClicked
    Set to true to initialize the legend item in the 'clicked' state on map load. Default is false.
 
    .EXAMPLE
    New-MapLegendSlice -Label "Slice 1" -FillColor "Blue" -MinimumValue 0 -MaximumValue 100 -DisplayInLegend $true
 
    .NOTES
    This function is used to create slices for map legends.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][ValidateSet('area', 'plot')]$Type,
        [string] $Label,
        [alias('SliceColor')][string] $FillColor,
        [alias('HoverSliceColor')][string] $HoverFillColor,
        [nullable[int]] $StrokeWidth,
        [string] $Transform,
        [string] $HoverTransform,
        [nullable[int]] $HoverStrokeWidth,
        [nullable[float]] $MinimumValue,
        [nullable[float]] $MaximumValue,
        [nullable[float]] $Value,
        [nullable[bool]] $DisplayInLegend,
        [nullable[bool]] $InitializeClicked,
        [nullable[int]] $Size
    )
    $OutputObject = [ordered] @{
        Type          = if ($Type -eq 'area') {
            'MapLegendAreaSlice' } else {
            'MapLegendPlotSlice' }
        Configuration = [ordered] @{
            label      = $Label
            attrs      = @{
                fill           = ConvertFrom-Color -Color $FillColor
                'transform'    = $Transform
                'stroke-width' = $StrokeWidth
            }
            attrsHover = @{
                'fill'         = ConvertFrom-Color -Color $HoverFillColor
                'transform'    = $HoverTransform
                'stroke-width' = $HoverStrokeWidth
            }
            min        = $MinimumValue
            max        = $MaximumValue
            sliceValue = $Value
            display    = $DisplayInLegend
            clicked    = $InitializeClicked
            size       = $Size
        }
    }
    Remove-EmptyValue -Hashtable $OutputObject.Configuration -Recursive -Rerun 2
    $OutputObject
}

Register-ArgumentCompleter -CommandName New-MapLegendSlice -ParameterName FillColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-MapLegendSlice -ParameterName HoverFillColor -ScriptBlock $Script:ScriptBlockColors
function New-MapPlot {
    <#
    .SYNOPSIS
    Creates a new MapPlot object with specified latitude, longitude, and other optional parameters.
 
    .DESCRIPTION
    This function creates a new MapPlot object with the provided latitude, longitude, and other optional parameters like href, value, and tooltip content.
 
    .PARAMETER Plot
    Specifies the name of the plot.
 
    .PARAMETER Latitude
    Specifies the latitude coordinate for the plot.
 
    .PARAMETER Longitude
    Specifies the longitude coordinate for the plot.
 
    .PARAMETER Href
    Specifies the hyperlink reference for the plot.
 
    .PARAMETER Value
    Specifies the value associated with the plot.
 
    .PARAMETER TooltipContent
    Specifies the content of the tooltip associated with the plot.
 
    .EXAMPLE
    New-MapPlot -Plot "Sample Plot" -Latitude 40.7128 -Longitude -74.0060 -Href "https://example.com" -Value "Sample Value" -TooltipContent { "This is a sample tooltip." }
 
    Creates a new MapPlot object named "Sample Plot" with latitude 40.7128, longitude -74.0060, a hyperlink reference to "https://example.com", a value of "Sample Value", and a tooltip content of "This is a sample tooltip."
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $Plot,
        [Parameter(Mandatory)][double] $Latitude,
        [Parameter(Mandatory)][double] $Longitude,
        [string] $Href,
        [Parameter(Mandatory)][string] $Value,
        [alias('Tooltip')][scriptblock] $TooltipContent
    )

    $OutputObject = [ordered] @{
        Type          = 'MapPlot'
        Configuration = [ordered] @{
            $Plot = [ordered] @{
                latitude  = $Latitude
                longitude = $Longitude
                href      = $Href
                value     = $Value
                tooltip   = @{
                    content = if ($TooltipContent) {
                        $ValueExecuted = & $TooltipContent
                        $ValueExecuted -join ""
                    } else {
                        $null
                    }
                }
            }
        }
    }
    Remove-EmptyValue -Hashtable $OutputObject.Configuration -Recursive -Rerun 2
    $OutputObject
}
function New-NavFloatWidget {
    <#
    .SYNOPSIS
    Creates a new navigation float widget with customizable options for different types of widgets.
 
    .DESCRIPTION
    The New-NavFloatWidget function creates a new navigation float widget with various customizable options such as Dots, SelectBox, List, Toggle, and Text. Each type of widget has specific styling and functionality.
 
    .PARAMETER Items
    Specifies the script block defining the items to be included in the widget.
 
    .PARAMETER Type
    Specifies the type of the widget. Valid values are: Dots, SelectBox, List, Toggle, Text.
 
    .PARAMETER Title
    Specifies the title of the widget.
 
    .EXAMPLE
    New-NavFloatWidget -Items { New-NavFloatWidgetItem -Name "Home" -Href "/home" -IconBrands "fab fa-home" -IconColor "blue" } -Type "Dots"
    Creates a new navigation float widget with Dots type, displaying a single item named "Home" with a home icon in blue color.
 
    .EXAMPLE
    New-NavFloatWidget -Items { New-NavFloatWidgetItem -Name "About" -Href "/about" -IconRegular "far fa-address-card" -IconColor "green" } -Type "List" -Title "About Us"
    Creates a new navigation float widget with List type, displaying a single item named "About" with an address card icon in green color and a title "About Us".
 
    .EXAMPLE
    New-NavFloatWidget -Items { New-NavFloatWidgetItem -Name "Toggle 1" -Href "/toggle1" } -Type "Toggle" -Title "Toggle Items"
    Creates a new navigation float widget with Toggle type, displaying a single item named "Toggle 1" linking to "/toggle1" and a title "Toggle Items".
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $Items,
        [ValidateSet('Dots', 'SelectBox', 'List', 'Toggle', 'Text')][string] $Type,
        [string] $Title
    )
    if ($Items) {
        if ($Type -eq 'Dots') {
            $Script:GlobalSchema['NavFloatWidget'] = 'Dots'
            New-HTMLTag -Tag 'menu' -Attributes @{ class = 'top-links' } {
                & $Items
            }
            $Script:GlobalSchema['NavFloatWidget'] = $null
        } elseif ($Type -eq 'SelectBox') {
            $Script:GlobalSchema['NavFloatWidget'] = 'SelectBox'
            if ($Title) {
                New-HTMLTag -Tag 'h3' { $Title } }
            New-HTMLTag -Tag 'select' -Attributes @{ class = 'penal-select' } {
                & $Items
            }
            $Script:GlobalSchema['NavFloatWidget'] = $null
        } elseif ($Type -eq 'List') {
            $Script:GlobalSchema['NavFloatWidget'] = 'list'

            if ($Title) {
                New-HTMLTag -Tag 'h3' { $Title } }
            New-HTMLTag -Tag 'ul' -Attributes @{ class = "penal-list" } {
                & $Items
            }

            $Script:GlobalSchema['NavFloatWidget'] = $null
        } elseif ($Type -eq 'Toggle') {
            $Script:GlobalSchema['NavFloatWidget'] = 'toggle'
            if ($Title) {
                New-HTMLTag -Tag 'h3' { $Title } }
            New-HTMLTag -Tag 'div' -Attributes @{ class = "toggle-switch" } {
                New-HTMLTag -Tag 'ul' -Attributes @{ class = "toggle-buttons" } {
                    & $Items
                }
            }
            $Script:GlobalSchema['NavFloatWidget'] = $null
        } elseif ($Type -eq 'Text') {
            $Script:GlobalSchema['NavFloatWidget'] = 'about'
            if ($Title) {
                New-HTMLTag -Tag 'h3' { $Title } }
            New-HTMLTag -Tag 'div' -Attributes @{ class = "penal-widget about" } {

                & $Items
            }
            $Script:GlobalSchema['NavFloatWidget'] = $null
        }
    }
}
function New-NavFloatWidgetItem {
    <#
    .SYNOPSIS
    Creates a new navigation float item with specified parameters.
 
    .DESCRIPTION
    This function creates a new navigation float item with the provided parameters. It allows customization of the item's name, link, icon, color, and other properties.
 
    .PARAMETER Name
    Specifies the name of the navigation float item.
 
    .PARAMETER Href
    Specifies the URL link for the navigation float item.
 
    .PARAMETER InternalPageID
    Specifies the internal page ID for the navigation float item.
 
    .PARAMETER LinkHome
    Indicates whether the navigation float item should link to the home page.
 
    .PARAMETER IconColor
    Specifies the color of the icon for the navigation float item.
 
    .PARAMETER IconBrands
    Specifies the icon brand for the navigation float item. Valid values are: $($Global:HTMLIcons.FontAwesomeBrands.Keys).
 
    .PARAMETER IconRegular
    Specifies the regular icon for the navigation float item. Valid values are: $($Global:HTMLIcons.FontAwesomeRegular.Keys).
 
    .EXAMPLE
    PS C:\> New-NavFloatWidgetItem -Name "Home" -Href "https://www.example.com" -IconBrands "fab fa-home" -IconColor "blue"
 
    Creates a new navigation float item named "Home" that links to "https://www.example.com" with a blue home icon.
 
    .EXAMPLE
    PS C:\> New-NavFloatWidgetItem -Name "About" -InternalPageID "about" -LinkHome
 
    Creates a new navigation float item named "About" that links to the internal page with ID "about" and links to the home page.
 
    #>

    [cmdletBinding(DefaultParameterSetName = 'FontAwesomeSolid')]
    param(
        [parameter(Mandatory, ParameterSetName = "FontAwesomeBrands")]
        [parameter(Mandatory, ParameterSetName = "FontAwesomeRegular")]
        [parameter(Mandatory, ParameterSetName = "FontAwesomeSolid")]
        [parameter(Mandatory, ParameterSetName = "FontMaterial")][string] $Name,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $Href,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $InternalPageID,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][switch] $LinkHome,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconColor,

        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")][string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")][string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconSolid,

        # FontsMaterialIcon
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontsMaterialIcon)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontsMaterialIcon))
            }
        )]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconMaterial,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Spinning,
        [parameter(ParameterSetName = "FontMaterial")][switch] $SpinningReverse,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Bordered,
        [parameter(ParameterSetName = "FontMaterial")][switch] $BorderedCircle,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullLeft,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullRight,
        [parameter(ParameterSetName = "FontMaterial")][validateSet('90', '180', '270')][string] $Rotate,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipVertical,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipHorizontal
    )
    if ($InternalPageID) {
        $Href = "$($Script:GlobalSchema.StorageInformation.FileName)_$InternalPageID.html"
    }
    if ($LinkHome) {
        $Href = "$($Script:GlobalSchema.StorageInformation.FileName).html"
    }
    if ($Script:GlobalSchema['NavFloatWidget'] -eq 'dots') {
        New-HTMLTag -Tag 'menuitem' {
            New-InternalNavLink -FloatItem -Name $Name -Href $Href -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
        }
    } elseif ($Script:GlobalSchema['NavFloatWidget'] -eq 'selectBox') {
        New-HTMLTag -Tag 'option' { $Name }
    } elseif ($Script:GlobalSchema['NavFloatWidget'] -eq 'list') {
        New-InternalNavLink -ListItem -Name $Name -Href $Href -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
    } elseif ($Script:GlobalSchema['NavFloatWidget'] -eq 'toggle') {
        New-HTMLTag -Tag 'li' { $Name }
    } elseif ($Script:GlobalSchema['NavFloatWidget'] -eq 'about') {
        New-HTMLText -Text $Name
    }
}
function New-NavItem {
    <#
    .SYNOPSIS
    Creates a new navigation item with specified parameters.
 
    .DESCRIPTION
    The New-NavItem function creates a new navigation item with the specified parameters, such as name, href, type, icon, and color.
 
    .EXAMPLE
    New-NavItem -Name "Home" -Href "/" -Type "Menu" -IconBrands "fa fa-home" -IconColor "blue"
    Creates a new navigation item named "Home" with a link to the root ("/"), type "Menu", using a home icon from FontAwesome Brands with blue color.
 
    .EXAMPLE
    New-NavItem -Name "About" -Href "/about" -Type "Grid" -IconRegular "far fa-address-card" -IconColor "green"
    Creates a new navigation item named "About" with a link to "/about", type "Grid", using an address card icon from FontAwesome Regular with green color.
 
    .EXAMPLE
    New-NavItem -Name "Contact" -Href "/contact" -Type "Menu" -FontMaterial "person" -IconColor "red"
    Creates a new navigation item named "Contact" with a link to "/contact", type "Menu", using a person icon from Material Design Icons with red color.
    #>

    [alias('New-HTMLNavItem')]
    [cmdletBinding(DefaultParameterSetName = 'FontAwesomeSolid')]
    param(
        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")]
        [alias('Text')][string] $Name,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $Href,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $InternalPageID,

        [parameter(ParameterSetName = "FontAwesomeBrands", Mandatory)]
        [parameter(ParameterSetName = "FontAwesomeRegular", Mandatory)]
        [parameter(ParameterSetName = "FontAwesomeSolid", Mandatory)]
        [parameter(ParameterSetName = "FontMaterial", Mandatory)][ValidateSet('Grid', 'Menu')][string] $Type,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconColor,

        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")][string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")][string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconSolid,

        # FontsMaterialIcon
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontsMaterialIcon)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontsMaterialIcon))
            }
        )]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconMaterial,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Spinning,
        [parameter(ParameterSetName = "FontMaterial")][switch] $SpinningReverse,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Bordered,
        [parameter(ParameterSetName = "FontMaterial")][switch] $BorderedCircle,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullLeft,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullRight,
        [parameter(ParameterSetName = "FontMaterial")][validateSet('90', '180', '270')][string] $Rotate,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipVertical,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipHorizontal
    )

    if ($InternalPageID) {
        $Href = "$($Script:GlobalSchema.StorageInformation.FileName)_$($InternalPageID)$($Script:GlobalSchema.StorageInformation.Extension)"
    }

    if ($Type -eq 'Grid') {
        $GridItem = New-HTMLTag -Tag 'li' -Attributes @{ class = 'grid' } {

            New-HTMLTag -Tag 'a' -Attributes @{ href = $Href } {
                New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                if ($Name) {
                    $Name
                }
            }
        }
        [PSCustomObject] @{
            Type  = 'NavGridItem'
            Value = $GridItem
        }
    } elseif ($Type -eq 'Menu') {
        $GridItem = New-HTMLTag -Tag 'li' {
            New-HTMLTag -Tag 'a' -Attributes @{ href = $Href } {
                New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                if ($Name) {
                    $Name
                }
            }
        }
        [PSCustomObject] @{
            Type  = 'NavGridMenu'
            Value = $GridItem
        }
    }
}
Register-ArgumentCompleter -CommandName New-HTMLNavItem -ParameterName IconColor -ScriptBlock $Script:ScriptBlockColors
function New-NavLink {
    <#
    .SYNOPSIS
    Creates a new navigation link with customizable options for nested links.
 
    .DESCRIPTION
    The New-NavLink function creates a new navigation link with various customizable options such as nested links, name, name color, href, internal page ID, and icon color.
 
    .PARAMETER NestedLinks
    Specifies the script block defining the nested link properties.
 
    .PARAMETER Name
    Specifies the name of the navigation link. This parameter is mandatory.
 
    .PARAMETER NameColor
    Specifies the color of the navigation link name.
 
    .PARAMETER Href
    Specifies the URL to navigate to when the navigation link is clicked.
 
    .PARAMETER InternalPageID
    Specifies the internal page ID associated with the navigation link.
 
    .PARAMETER IconColor
    Specifies the color of the icon for the navigation link.
 
    .EXAMPLE
    New-NavLink -NestedLinks { New-NavLink -Name "Sublink 1" -Href "/sublink1" } -Name "Main Link" -NameColor "blue" -Href "/mainlink" -InternalPageID "main" -IconColor "green"
    Creates a new navigation link named "Main Link" with a nested link named "Sublink 1" in blue name color, green icon color, navigating to "/mainlink" with internal page ID "main".
 
    .EXAMPLE
    New-NavLink -NestedLinks { New-NavLink -Name "Sublink 2" -Href "/sublink2" } -Name "Another Link" -NameColor "red" -Href "/anotherlink" -InternalPageID "another" -IconColor "yellow"
    Creates a new navigation link named "Another Link" with a nested link named "Sublink 2" in red name color, yellow icon color, navigating to "/anotherlink" with internal page ID "another".
    #>

    [alias('New-HTMLNavLink')]
    [cmdletBinding(DefaultParameterSetName = 'FontAwesomeSolid')]
    param(
        [parameter(Position = 0, ParameterSetName = "FontAwesomeBrands")]
        [parameter(Position = 0, ParameterSetName = "FontAwesomeRegular")]
        [parameter(Position = 0, ParameterSetName = "FontAwesomeSolid")]
        [parameter(Position = 0, ParameterSetName = "FontMaterial")]
        [ScriptBlock] $NestedLinks,

        [parameter(Mandatory, ParameterSetName = "FontAwesomeBrands")]
        [parameter(Mandatory, ParameterSetName = "FontAwesomeRegular")]
        [parameter(Mandatory, ParameterSetName = "FontAwesomeSolid")]
        [parameter(Mandatory, ParameterSetName = "FontMaterial")][string] $Name,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")]
        [string] $NameColor,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $Href,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $InternalPageID,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconColor,

        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")][string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")][string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconSolid,

        # FontsMaterialIcon
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontsMaterialIcon)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontsMaterialIcon))
            }
        )]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconMaterial,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Spinning,
        [parameter(ParameterSetName = "FontMaterial")][switch] $SpinningReverse,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Bordered,
        [parameter(ParameterSetName = "FontMaterial")][switch] $BorderedCircle,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullLeft,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullRight,
        [parameter(ParameterSetName = "FontMaterial")][validateSet('90', '180', '270')][string] $Rotate,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipVertical,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipHorizontal
    )

    if ($InternalPageID) {
        $Href = "$($Script:GlobalSchema.StorageInformation.FileName)_$InternalPageID.html"
    }
    if ($NestedLinks) {
        [Array] $OutputLinks = & $NestedLinks
    }

    if (-not $Script:GlobalSchema['TopMenu']) {

        $NavLink = @(
            if ($OutputLinks) {
                New-HTMLTag -Tag 'li' -Attributes @{ class = 'has-child' } {
                    New-InternalNavLink -Nested -Name $Name -NameColor $NameColor -Href $Href -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                    New-HTMLTag -Tag 'ul' -Attributes @{ class = 'its-children' } {
                        $OutputLinks.Value
                    }
                }
            } else {
                New-InternalNavLink -Name $Name -NameColor $NameColor -Href $Href -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
            }
        )
        [PSCustomObject] @{
            Type  = 'NavLinkItem'
            Value = $NavLink
        }
    } else {

        $NavLink = @(
            if ($OutputLinks) {
                New-HTMLTag -Tag 'li' -Attributes @{ class = 'has-child' } {
                    New-InternalNavLink -MenuItems -Nested -Name $Name -NameColor $NameColor -Href $Href -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                    New-HTMLTag -Tag 'ul' -Attributes @{ class = 'menu-subitems' } {
                        $OutputLinks.Value
                    }
                }
            } else {
                New-InternalNavLink -MenuItems -Name $Name -NameColor $NameColor -Href $Href -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
            }
        )
        [PSCustomObject] @{
            Type  = 'NavLinkItem'
            Value = $NavLink
        }
    }
}
Register-ArgumentCompleter -CommandName New-HTMLNavLink -ParameterName IconColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-HTMLNavLink -ParameterName NameColor -ScriptBlock $Script:ScriptBlockColors
function New-NavTopMenu {
    <#
    .SYNOPSIS
    Creates a new navigation top menu item with customizable options.
 
    .DESCRIPTION
    The New-NavTopMenu function creates a new navigation top menu item with various customizable options such as icon, name, href, and more.
 
    .PARAMETER MenuItem
    Specifies the script block defining the menu item properties.
 
    .PARAMETER Name
    Specifies the name of the menu item. This parameter is mandatory.
 
    .PARAMETER Href
    Specifies the URL to navigate to when the menu item is clicked.
 
    .PARAMETER InternalPageID
    Specifies the internal page ID associated with the menu item.
 
    .PARAMETER IconColor
    Specifies the color of the icon.
 
    .PARAMETER IconBrands
    Specifies the icon for the menu item from the Font Awesome Brands collection.
 
    .PARAMETER IconRegular
    Specifies the icon for the menu item from the Font Awesome Regular collection.
 
    .EXAMPLE
    New-NavTopMenu -Name "Home" -Href "/home" -IconBrands "fa fa-home" -IconColor "blue"
    Creates a new navigation top menu item named "Home" with a home icon from Font Awesome Brands in blue color.
 
    .EXAMPLE
    New-NavTopMenu -Name "About" -Href "/about" -IconRegular "fa fa-info-circle" -IconColor "green"
    Creates a new navigation top menu item named "About" with an info circle icon from Font Awesome Regular in green color.
    #>

    [alias('New-HTMLNavTopMenu')]
    [cmdletBinding(DefaultParameterSetName = 'FontAwesomeSolid')]
    param(
        [parameter(Position = 0, ParameterSetName = "FontAwesomeBrands")]
        [parameter(Position = 0, ParameterSetName = "FontAwesomeRegular")]
        [parameter(Position = 0, ParameterSetName = "FontAwesomeSolid")]
        [parameter(Position = 0, ParameterSetName = "FontMaterial")]
        [ScriptBlock] $MenuItem,

        [parameter(Mandatory, ParameterSetName = "FontAwesomeBrands")]
        [parameter(Mandatory, ParameterSetName = "FontAwesomeRegular")]
        [parameter(Mandatory, ParameterSetName = "FontAwesomeSolid")]
        [parameter(Mandatory, ParameterSetName = "FontMaterial")][string] $Name,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $Href,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $InternalPageID,

        [parameter(ParameterSetName = "FontAwesomeBrands")]
        [parameter(ParameterSetName = "FontAwesomeRegular")]
        [parameter(ParameterSetName = "FontAwesomeSolid")]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconColor,

        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")][string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter( {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeRegular.Keys) }
        )]
        [ValidateScript( { $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys)) }        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")][string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconSolid,

        # FontsMaterialIcon
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
                ($Global:HTMLIcons.FontsMaterialIcon)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontsMaterialIcon))
            }
        )]
        [parameter(ParameterSetName = "FontMaterial")][string] $IconMaterial,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Spinning,
        [parameter(ParameterSetName = "FontMaterial")][switch] $SpinningReverse,
        [parameter(ParameterSetName = "FontMaterial")][switch] $Bordered,
        [parameter(ParameterSetName = "FontMaterial")][switch] $BorderedCircle,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullLeft,
        [parameter(ParameterSetName = "FontMaterial")][switch] $PullRight,
        [parameter(ParameterSetName = "FontMaterial")][validateSet('90', '180', '270')][string] $Rotate,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipVertical,
        [parameter(ParameterSetName = "FontMaterial")][switch] $FlipHorizontal
    )

    if ($MenuItem) {

        $Script:GlobalSchema['TopMenu'] = $true
        $MenuExecuted = & $MenuItem
        $Menu = New-HTMLTag -Tag 'li' {
            New-HTMLTag -Tag 'span' -Attributes @{class = 'dropdown-heading' } {
                New-HTMLTag -Tag 'span' -Attributes @{ style = @{ "padding-right" = "5px" } } {

                    New-InternalNavIcon -IconBrands $IconBrands -IconRegular $IconRegular -IconSolid $IconSolid -IconMaterial $IconMaterial -Spinning:$Spinning.IsPresent -SpinningReverse:$SpinningReverse.IsPresent -IconColor $IconColor -Bordered:$Bordered.IsPresent -BorderedCircle:$BorderedCircle.IsPresent -PullLeft:$PullLeft.IsPresent -PullRight:$PullRight.IsPresent -Rotate $Rotate -FlipVertical:$FlipVertical.IsPresent -FlipHorizontal:$FlipHorizontal.IsPresent
                }
                $Name
            }
            New-HTMLTag -Tag 'ul' -Attributes @{ class = 'menu-items' } {
                $MenuExecuted.Value
            }
        }
        [PSCustomObject] @{
            Type  = 'TopMenu'
            Value = $Menu
        }
        $Script:GlobalSchema['TopMenu'] = $false
    }
}

Register-ArgumentCompleter -CommandName New-HTMLNavTopMenu -ParameterName IconColor -ScriptBlock $Script:ScriptBlockColors
function New-OrgChartNode {
    <#
    .SYNOPSIS
    Creates a new organizational chart node.
 
    .DESCRIPTION
    This function creates a new node for an organizational chart. It allows defining children nodes by specifying nested nodes for self-nesting.
 
    .PARAMETER Children
    Specifies the children of the node by defining nested nodes for self-nesting.
 
    .PARAMETER Name
    Specifies the name of the node.
 
    .PARAMETER Title
    Specifies the title of the node.
 
    .PARAMETER ClassName
    Specifies the class name for styling the node.
 
    .EXAMPLE
    New-HTML {
        New-HTMLOrgChart {
            New-OrgChartNode -Name 'Test' -Title 'Test2' {
                New-OrgChartNode -Name 'Test' -Title 'Test2'
                New-OrgChartNode -Name 'Test' -Title 'Test2'
                New-OrgChartNode -Name 'Test' -Title 'Test2' {
                    New-OrgChartNode -Name 'Test' -Title 'Test2'
                }
            }
        } -AllowExport -ExportExtension pdf -Draggable
    } -FilePath $PSScriptRoot\Example-OrgChart01.html -ShowHTML -Online
 
    .NOTES
    Additional information
    #>

    [cmdletBinding()]
    param(
        [scriptblock] $Children,
        [string] $Name,
        [string] $Title,
        # [string] $ClassName,
        [string] $TitleBackgroundColor,
        [string] $TitleBorderColor,
        [string] $TitleColor,
        [string] $ContentBackgroundColor,
        [string] $ContentBorderColor,
        [string] $ContentColor
    )

    $ClassName = "orgchartColoring$(Get-RandomStringName -Size 8 -LettersOnly)"
    $StyleNodeInformation = @{
        ".orgchart .$ClassName .title"   = @{
            'color'            = ConvertFrom-Color -Color $TitleColor
            'border-color'     = ConvertFrom-Color -Color $TitleBorderColor
            'background-color' = ConvertFrom-Color -Color $TitleBackgroundColor
        }
        ".orgchart .$ClassName .content" = @{
            'color'            = ConvertFrom-Color -Color $ContentColor
            'border-color'     = ConvertFrom-Color -Color $ContentBorderColor
            'background-color' = ConvertFrom-Color -Color $ContentBackgroundColor
        }
    }
    Remove-EmptyValue -Hashtable $StyleNodeInformation -Recursive -Rerun 2
    if ($StyleNodeInformation) {
        Add-HTMLStyle -Placement Header -Css $StyleNodeInformation 
    }
    $ChartNode = [ordered] @{
        name      = $Name
        title     = $Title
        className = $ClassName
        nodeId    = $Name
    }
    if ($Children) {
        $ChildrenOutput = & $Children
        $ChartNode['children'] = @($ChildrenOutput)
    }
    Remove-EmptyValue -Hashtable $ChartNode
    $ChartNode
}

Register-ArgumentCompleter -CommandName New-OrgChartNode -ParameterName TitleBackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-OrgChartNode -ParameterName TitleColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-OrgChartNode -ParameterName BorderColor -ScriptBlock $Script:ScriptBlockColors
function New-TableAlphabetSearch {
    <#
    .SYNOPSIS
    Creates a custom object for Table Alphabet Search with specified parameters.
 
    .DESCRIPTION
    This function creates a custom object for Table Alphabet Search with the specified parameters. It allows for customization of the search behavior by enabling case sensitivity and adding numbers to the search.
 
    .PARAMETER ColumnID
    Specifies the ID of the column to search.
 
    .PARAMETER ColumnName
    Specifies the name of the column to search.
 
    .PARAMETER AddNumbers
    Switch parameter to include numbers in the search.
 
    .PARAMETER CaseSensitive
    Switch parameter to perform a case-sensitive search.
 
    .EXAMPLE
    New-TableAlphabetSearch -ColumnID 1 -ColumnName "Name" -AddNumbers -CaseSensitive
    Creates a Table Alphabet Search object for column ID 1 with the name "Name", including numbers in the search and performing a case-sensitive search.
 
    .EXAMPLE
    New-TableAlphabetSearch -ColumnID 2 -ColumnName "ID" -CaseSensitive
    Creates a Table Alphabet Search object for column ID 2 with the name "ID", performing a case-sensitive search.
 
    #>

    [alias('TableAlphabetSearch', 'New-HTMLTableAlphabetSearch')]
    [CmdletBinding(DefaultParameterSetName = 'ID')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ID')][int] $ColumnID,
        [Parameter(Mandatory, ParameterSetName = 'Name')][string] $ColumnName,
        [switch] $AddNumbers,
        [switch] $CaseSensitive
    )
    $Output = [PSCustomObject]@{
        Type   = 'TableAlphabetSearch'
        Output = @{
            ColumnID   = $ColumnID
            ColumnName = $ColumnName
        }
    }
    if ($CaseSensitive) {
        $Output.Output['caseSensitive'] = $true
    }
    if ($AddNumbers) {
        $Output.Output['numbers'] = $true
    }
    Remove-EmptyValue -Hashtable $Output.Output
    $Output
}
function New-TableButtonColumnVisibility {
    <#
    .SYNOPSIS
    Creates a new table button for column visibility control.
 
    .DESCRIPTION
    This function creates a new table button for controlling the visibility of columns in a table. It allows customization of the button text, layout, and other properties.
 
    .PARAMETER ButtonName
    Specifies the text to display on the button.
 
    .PARAMETER CollectionTitle
    Specifies the title for the column visibility control collection. Default is 'Visibility control'.
 
    .PARAMETER CollectionPosition
    Specifies the position of the collection (fixed or dropdown). Default is 'dropdown'.
 
    .PARAMETER CollectionLayout
    Specifies the layout of the collection (columns, two-column, three-column, four-column). Default is 'columns'.
 
    .PARAMETER DropUp
    Indicates whether the collection should be displayed as a drop-up menu.
 
    .PARAMETER Fade
    Specifies the fade effect duration when toggling visibility.
 
    .EXAMPLE
    New-TableButtonColumnVisibility -ButtonName "Toggle Columns" -CollectionTitle "Column Visibility" -CollectionPosition "fixed" -CollectionLayout "two-column" -DropUp -Fade 500
    Creates a new table button with the specified properties.
 
    #>

    [alias('TableButtonColumnVisibility', 'New-HTMLTableButtonColumnVisibility')]
    [CmdletBinding()]
    param(
        # [string] $Title,
        [string] $ButtonName,
        #[string[]] $Columns,
        [string] $CollectionTitle = 'Visibility control',
        [ValidateSet('fixed', 'dropdown')][string] $CollectionPosition = 'dropdown',
        [ValidateSet('columns', 'two-column', 'three-column', 'four-column')][string] $CollectionLayout = 'columns',
        [switch] $DropUp,
        [int] $Fade
    )
    $Script:HTMLSchema.Features.DataTablesButtons = $true
    $Script:HTMLSchema.Features.DataTablesButtonsColVis = $true
    $Output = @{
        extend = 'colvis'
    }
    if ($ButtonName) {
        $Output['text'] = $ButtonName
    }
    $Output['collectionLayout'] = "$CollectionPosition $CollectionLayout"
    $Output['popoverTitle'] = $CollectionTitle
    if ($DropUp) {
        $Output['dropup'] = $true
    }
    if ($PSBoundParameters.ContainsKey('Fade')) {
        $Output['fade'] = $Fade
    }
    if ($Columns) {

        $Output['columns'] = @(
            foreach ($Column in $Columns) {
                @{ name = $Column }
            }
        )
    }
    [PSCustomObject] @{
        Type   = 'TableButtonColumnVisibility'
        Output = $Output
    }
}
function New-TableButtonCopy {
    <#
    .SYNOPSIS
    Creates a new table button for copying data with customizable options.
 
    .DESCRIPTION
    This function creates a new table button for copying data with customizable options. It allows users to specify the button title and display text.
 
    .PARAMETER Title
    The title to be displayed when hovering over the button.
 
    .PARAMETER ButtonName
    The text to be displayed on the button.
 
    .EXAMPLE
    New-TableButtonCopy -Title "Copy Data" -ButtonName "Copy"
 
    Description
    -----------
    Creates a new table button with the title "Copy Data" and button name "Copy".
 
    #>

    [alias('TableButtonCopy', 'EmailTableButtonCopy', 'New-HTMLTableButtonCopy')]
    [CmdletBinding()]
    param(
        [string] $Title,
        [string] $ButtonName
    )
    if (-not $Script:HTMLSchema['TableSimplify']) {
        $Script:HTMLSchema.Features.DataTablesButtons = $true
        $Script:HTMLSchema.Features.DataTablesButtonsHTML5 = $true
    }
    $Output = [ordered]@{}
    $Output['extend'] = 'copyHtml5'
    if ($ButtonName) {
        $Output['text'] = $ButtonName
    }
    if ($Title) {
        $Output['title'] = $title
    }
    [PSCustomObject] @{
        Type   = 'TableButtonCopy'
        Output = $Output
    }
}
function New-TableButtonCSV {
    <#
    .SYNOPSIS
    Creates a new table button for exporting data to CSV with customizable options.
 
    .DESCRIPTION
    This function creates a new table button for exporting data to CSV with customizable options. It allows users to specify the button title, file name, and other settings for CSV export.
 
    .PARAMETER Title
    The title to be displayed when hovering over the button.
 
    .PARAMETER ButtonName
    The text to be displayed on the button. Default value is 'CSV'.
 
    .PARAMETER Extension
    The file extension for the exported CSV file. Default value is '.csv'.
 
    .PARAMETER FileName
    The name of the exported CSV file.
 
    .PARAMETER DisableBOM
    Switch to disable Byte Order Mark (BOM) in the exported CSV file.
 
    .PARAMETER FieldSeparator
    The separator character for fields in the CSV file. Default value is ';'.
 
    .PARAMETER FieldBoundary
    The character used to enclose fields in the CSV file. Default value is '"'.
 
    .EXAMPLE
    New-TableButtonCSV -Title "Export to CSV" -FileName "data" -FieldSeparator "," -FieldBoundary "'"
 
    Description
    -----------
    Creates a new table button with the title "Export to CSV", exports data to a CSV file named "data.csv" with comma as the field separator and single quote as the field boundary.
 
    #>

    [alias('TableButtonCSV', 'EmailTableButtonCSV', 'New-HTMLTableButtonCSV')]
    [CmdletBinding()]
    param(
        [string] $Title,
        [string] $ButtonName = 'CSV',
        [string] $Extension = '.csv',
        [string] $FileName,
        [switch] $DisableBOM,
        [string] $FieldSeparator = ';',
        [string] $FieldBoundary = '"'
    )
    if (-not $Script:HTMLSchema['TableSimplify']) {
        $Script:HTMLSchema.Features.DataTablesButtons = $true
        $Script:HTMLSchema.Features.DataTablesButtonsHTML5 = $true
    }
    if ($FileName) {
        $Split = $FileName.Split('.')
        if ($Split.Count -gt 1) {
            $Extension = '.' + $Split[-1]
        }
        $FileName = $Split[0]
    }
    $Output = [ordered]@{
        extend         = 'csvHtml5'
        text           = $ButtonName
        charset        = 'utf-8'
        extension      = $Extension
        fieldSeparator = $FieldSeparator
        fieldBoundary  = $FieldBoundary
        bom            = -not $DisableBOM.IsPresent
    }
    if ($FileName) {
        $Output['filename'] = $FileName
    }
    if ($Title) {
        $Output['title'] = $title
    }
    [PSCustomObject] @{
        Type   = 'TableButtonCSV'
        Output = $Output
    }
}
function New-TableButtonExcel {
    <#
    .SYNOPSIS
    Creates a new table button for exporting data to Excel with customizable options.
 
    .DESCRIPTION
    This function creates a new table button for exporting data to Excel with customizable options. It allows users to specify the button title and display text.
 
    .PARAMETER Title
    The title to be displayed when hovering over the button.
 
    .PARAMETER ButtonName
    The text to be displayed on the button.
 
    .EXAMPLE
    New-TableButtonExcel -Title "Export to Excel" -ButtonName "Export"
 
    Description
    -----------
    Creates a new table button with the title "Export to Excel" and button name "Export".
 
    #>

    [alias('TableButtonExcel', 'EmailTableButtonExcel', 'New-HTMLTableButtonExcel')]
    [CmdletBinding()]
    param(
        [string] $Title,
        [string] $ButtonName
    )
    $Script:HTMLSchema.Features.DataTablesButtons = $true
    $Script:HTMLSchema.Features.DataTablesButtonsHTML5 = $true
    $Script:HTMLSchema.Features.DataTablesButtonsExcel = $true
    $Output = @{}
    $Output['extend'] = 'excelHtml5'
    if ($ButtonName) {
        $Output['text'] = $ButtonName
    }
    if ($Title) {
        $Output['title'] = $title
    }
    [PSCustomObject] @{
        Type   = 'TableButtonExcel'
        Output = $Output
    }
}
function New-TableButtonPageLength {
    <#
    .SYNOPSIS
    Creates a new table button for setting the page length in a DataTable.
 
    .DESCRIPTION
    This function creates a new table button for setting the page length in a DataTable. It allows users to customize the button title and display text.
 
    .PARAMETER Title
    The title to be displayed when hovering over the button.
 
    .PARAMETER ButtonName
    The text to be displayed on the button.
 
    .EXAMPLE
    New-TableButtonPageLength -Title "Set Page Length" -ButtonName "Page Length"
 
    Description
    -----------
    Creates a new table button with the title "Set Page Length" and button name "Page Length".
 
    #>

    [alias('TableButtonPageLength', 'EmailTableButtonPageLength', 'New-HTMLTableButtonPageLength')]
    [CmdletBinding()]
    param(
        [string] $Title,
        [string] $ButtonName
    )
    if (-not $Script:HTMLSchema['TableSimplify']) {
        $Script:HTMLSchema.Features.DataTablesButtons = $true
    }
    $Output = @{}
    $Output['extend'] = 'pageLength'
    if ($ButtonName) {
        $Output['text'] = $ButtonName
    }
    if ($Title) {
        $Output['title'] = $title
    }
    [PSCustomObject] @{
        Type   = 'TableButtonPageLength'
        Output = $Output
    }
}
function New-TableButtonPDF {
    <#
    .SYNOPSIS
    Allows more control when adding buttons to Table
 
    .DESCRIPTION
    Allows more control when adding buttons to Table. Works only within Table or New-HTMLTable scriptblock.
 
    .PARAMETER Title
    Document title (appears above the table in the generated PDF). The special character * is automatically replaced with the value read from the host document's title tag.
 
    .PARAMETER DisplayName
    The button's display text. The text can be configured using this option
 
    .PARAMETER MessageBottom
    Message to be shown at the bottom of the table, or the caption tag if displayed at the bottom of the table.
 
    .PARAMETER MessageTop
    Message to be shown at the top of the table, or the caption tag if displayed at the top of the table.
 
    .PARAMETER FileName
    File name to give the created file (plus the extension defined by the extension option). The special character * is automatically replaced with the value read from the host document's title tag.
 
    .PARAMETER Extension
    The extension to give the created file name. (default .pdf)
 
    .PARAMETER PageSize
    Paper size for the created PDF. This can be A3, A4, A5, LEGAL, LETTER or TABLOID. Other options are available.
 
    .PARAMETER Orientation
    Paper orientation for the created PDF. This can be portrait or landscape
 
    .PARAMETER Header
    Indicate if the table header should be included in the exported data or not.
 
    .PARAMETER Footer
    Indicate if the table footer should be included in the exported data or not.
 
    .EXAMPLE
    Dashboard -Name 'Dashimo Test' -FilePath $PSScriptRoot\DashboardEasy05.html -Show {
        Section -Name 'Test' -Collapsable {
            Container {
                Panel {
                    Table -DataTable $Process {
                        TableButtonPDF
                        TableButtonCopy
                        TableButtonExcel
                    } -Buttons @() -DisableSearch -DisablePaging -HideFooter
                }
                Panel {
                    Table -DataTable $Process -Buttons @() -DisableSearch -DisablePaging -HideFooter
                }
                Panel {
                    Table -DataTable $Process {
                        TableButtonPDF -PageSize A10 -Orientation landscape
                        TableButtonCopy
                        TableButtonExcel
                    } -Buttons @() -DisableSearch -DisablePaging -HideFooter
                }
            }
        }
    }
 
    .NOTES
    Options are based on this URL: https://datatables.net/reference/button/pdfHtml5
 
    #>


    [alias('TableButtonPDF', 'EmailTableButtonPDF', 'New-HTMLTableButtonPDF')]
    [CmdletBinding()]
    param(
        [string] $Title,
        [alias('DisplayName')][string] $ButtonName,
        [string] $MessageBottom,
        [string] $MessageTop,
        [string] $FileName,
        [string] $Extension,
        [string][ValidateSet('4A0', '2A0', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10',
            'B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10',
            'C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10',
            'RA0', 'RA1', 'RA2', 'RA3', 'RA4',
            'SRA0', 'SRA1', 'SRA2', 'SRA3', 'SRA4',
            'EXECUTIVE', 'FOLIO', 'LEGAL', 'LETTER', 'TABLOID')] $PageSize = 'A3',
        [string][ValidateSet('portrait', 'landscape')] $Orientation = 'landscape',
        [switch] $Header,
        [switch] $Footer
    )
    if (-not $Script:HTMLSchema['TableSimplify']) {
        $Script:HTMLSchema.Features.DataTablesButtons = $true
        $Script:HTMLSchema.Features.DataTablesButtonsPDF = $true
        $Script:HTMLSchema.Features.DataTablesButtonsHTML5 = $true
    }
    $Button = @{ }
    $Button.extend = 'pdfHtml5'
    $Button.pageSize = $PageSize
    $Button.orientation = $Orientation
    if ($MessageBottom) {
        $Button.messageBottom = $MessageBottom
    }
    if ($MessageTop) {
        $Button.messageTop = $MessageTop
    }
    if ($ButtonName) {
        $Button.text = $ButtonName
    }
    if ($Title) {
        $Button.title = $Title
    }
    if ($FileName) {
        $Button.filename = $FileName
    }
    if ($Extension) {
        $Button.extension = $Extension
    }
    if ($Header) {
        $Button.header = $Header.IsPresent
    }
    if ($Footer) {
        $Button.footer = $Footer.IsPresent
    }

    [PSCustomObject] @{
        Type   = 'TableButtonPDF'
        Output = $Button
    }
}

function New-TableButtonPrint {
    <#
    .SYNOPSIS
    Creates a new table button for printing with optional title and button name.
 
    .DESCRIPTION
    This function creates a new table button for printing with optional title and button name. It can be used to add a print button to HTML tables.
 
    .EXAMPLE
    New-TableButtonPrint -Title "Print Table" -ButtonName "Print"
 
    Description
    -----------
    Creates a new table button with the title "Print Table" and button name "Print".
 
    .PARAMETER Title
    The title to be displayed when hovering over the button.
 
    .PARAMETER ButtonName
    The text to be displayed on the button.
 
    #>

    [alias('TableButtonPrint', 'EmailTableButtonPrint', 'New-HTMLTableButtonPrint')]
    [CmdletBinding()]
    param(
        [string] $Title,
        [string] $ButtonName
    )
    if (-not $Script:HTMLSchema['TableSimplify']) {
        $Script:HTMLSchema.Features.DataTablesButtons = $true
    }
    $Output = @{}
    $Output['extend'] = 'print'
    if ($ButtonName) {
        $Output['text'] = $ButtonName
    }
    if ($Title) {
        $Output['title'] = $title
    }
    [PSCustomObject] @{
        Type   = 'TableButtonPrint'
        Output = $Button
    }
}
function New-TableButtonSearchBuilder {
    <#
    .SYNOPSIS
    Creates a configuration object for a table button search builder.
 
    .DESCRIPTION
    This function creates a configuration object for a table button search builder with customizable options.
 
    .PARAMETER ButtonName
    The name of the button to be displayed.
 
    .PARAMETER DepthLimit
    The depth limit for the search builder.
 
    .PARAMETER DefaultLogic
    The default logic to be applied.
 
    .PARAMETER GreyScale
    Indicates if the search builder should be displayed in greyscale.
 
    .EXAMPLE
    New-TableButtonSearchBuilder -ButtonName "Search" -DepthLimit 3 -DefaultLogic "AND" -GreyScale
    Creates a search builder button named "Search" with a depth limit of 3, default logic set to "AND", and displayed in greyscale.
 
    .EXAMPLE
    New-TableButtonSearchBuilder -ButtonName "Filter" -DefaultLogic "OR"
    Creates a search builder button named "Filter" with default logic set to "OR".
 
    #>

    [alias('TableButtonSearchBuilder', 'EmailTableButtonSearchBuilder', 'New-HTMLTableButtonSearchBuilder')]
    [CmdletBinding()]
    param(
        [string] $ButtonName,
        [int] $DepthLimit = 2,
        [string] $DefaultLogic,
        [switch] $GreyScale
    )
    $Script:HTMLSchema.Features.DataTablesButtons = $true
    $Script:HTMLSchema.Features.DataTablesDateTime = $true
    $Script:HTMLSchema.Features.DataTablesSearchBuilder = $true
    $Output = @{
        config = @{}
    }
    $Output['extend'] = 'searchBuilder'
    if ($GreyScale) {
        $Output['config']['greyscale'] = $true
    }
    if ($ButtonName) {
        $Output['text'] = $ButtonName
    }
    if ($DepthLimit) {
        $Output['config']['depthLimit'] = $DepthLimit
    }
    if ($DefaultLogic) {
        $Output['config']['logic'] = $DefaultLogic
    }
    [PSCustomObject] @{
        Type   = 'TableButtonSearchBuilder'
        Output = $Output
    }
}
function New-TableColumnOption {
    <#
    .SYNOPSIS
        Allows for certain modification of options within DataTable's columnDefs parameter. You can set visibility, searchability, sortability, and width for specific columns
    .DESCRIPTION
        Allows for certain modification of options within DataTable's columnDefs parameter.
        See: https://datatables.net/reference/option/columnDefs
 
        New-HTMLTable has parameters for -ResponsivePriorityOrder and -ResponsivePriorityOrderIndex and these are set at a higher precedent than options specified here.
        See the DataTable reference section for conflict resolution.
 
        The New-TableColumnOption cmdlet will add entries to the columnDefs parameter in the order in which the cmdlets are ordered.
        If you use 2 or more New-TableColumnOption, the first cmdlet takes priority over the second cmdlet if they specify the same targets or overriding property values
        With this in mind, you should almost always specify -AllColumns last, since that will take priority over any commands issued later
 
    .EXAMPLE
        New-TableColumnOption -ColumnIndex (0..4) -Width 50
        The first 5 columns with have a width defined as 50, this may not be exact.
        See: https://datatables.net/reference/option/columns.width
    .EXAMPLE
        New-TableColumnOption -ColumnIndex 0,1,2 -Hidden $false
        New-TableColumnOption -ColumnIndex 1 -Sortable $true
        New-TableColumnOption -AllColumns -Hidden $true -Searchable $false -Sortable $false
        All columns will be hidden, not searchable, and not sortable
        However, since there is a option specified higher up, the first 3 columns will be visible
        Additionally the 2nd column will be sortable
    .INPUTS
        None. You cannot pipe objects to New-TableColumnOption
    .OUTPUTS
        PSCustomObject
    .NOTES
        The New-HTMLTable cmdlet has -ResponsivePriorityOrder and -ResponsivePriorityOrderIndex that also modifes the columnDefs option in DataTable
    #>

    [CmdletBinding(DefaultParameterSetName = 'ColumnIndex', SupportsShouldProcess = $false, PositionalBinding = $false, HelpUri = '', ConfirmImpact = 'Medium')]
    [Alias('EmailTableColumnOption', 'TableColumnOption', 'New-HTMLTableColumnOption')]
    [OutputType([PSCustomObject])]
    param(
        # Identifies specific columns that the properties should apply to
        [Parameter(ParameterSetName = "ColumnIndex", Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [alias('Targets')]
        [int[]]
        $ColumnIndex,

        # Uses the columnDef Target "_ALL" to indicate all columns / remaining columns
        [Parameter(ParameterSetName = "AllColumns", Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [alias('AllTargets', 'TargetAll')]
        [switch]
        $AllColumns,

        # Width for the column as a string
        [Parameter(ParameterSetName = "ColumnIndex")]
        [Parameter(ParameterSetName = "AllColumns")]
        [string]
        $Width,

        # Defines if a column is hidden. A hidden column can still be used by Conditional Formatting and can still be searchable
        [Parameter(ParameterSetName = "ColumnIndex")]
        [Parameter(ParameterSetName = "AllColumns")]
        [boolean]$Hidden,

        # Defines if a column is able to be searched
        [Parameter(ParameterSetName = "ColumnIndex")]
        [Parameter(ParameterSetName = "AllColumns")]
        [boolean]$Searchable,

        # Defines if a column can be sorted
        [Parameter(ParameterSetName = "ColumnIndex")]
        [Parameter(ParameterSetName = "AllColumns")]
        [boolean]$Sortable
    )

    $TableColumnOptionProperty = @{
        targets = if ($AllColumns) {
            "_all" } else {
            $ColumnIndex };
    }

    If ($null -ne $PSBoundParameters['Width']) {
        $TableColumnOptionProperty.width = $Width }
    If ($null -ne $PSBoundParameters['Hidden']) {
        $TableColumnOptionProperty.visible = !$Hidden }
    If ($null -ne $PSBoundParameters['Searchable']) {
        $TableColumnOptionProperty.searchable = $Searchable }
    If ($null -ne $PSBoundParameters['Sortable']) {
        $TableColumnOptionProperty.orderable = $Sortable }

    If ($TableColumnOptionProperty.Keys.Count -gt 1) {
        [PSCustomObject] @{
            Type   = 'TableColumnOption'
            Output = [PSCustomObject]$TableColumnOptionProperty
        }
    } Else {
        Write-Warning "New-TableColumnOption did not have any additional arguments listed"
    }

}

function New-TableCondition {
    <#
    .SYNOPSIS
    Creates a new table condition with specified parameters and styling options for pass and fail conditions.
 
    .DESCRIPTION
    This function creates a new table condition with the specified parameters and allows customization of the styling options for both pass and fail conditions.
 
    .PARAMETER Name
    Specifies the name of the condition.
 
    .PARAMETER HighlightHeaders
    Specifies an array of headers to highlight based on the condition.
 
    .PARAMETER ComparisonType
    Specifies the type of comparison to be performed. Valid values are 'number', 'string', 'bool', or 'date'.
 
    .PARAMETER Operator
    Specifies the comparison operator to be used.
 
    .PARAMETER Value
    Specifies the value to compare against.
 
    .PARAMETER Row
    Switch parameter to indicate if the styling should be applied to the entire row.
 
    .PARAMETER Inline
    Switch parameter to indicate if the styling should be applied inline.
 
    .PARAMETER CaseSensitive
    Switch parameter to indicate if the comparison should be case-sensitive.
 
    .PARAMETER DateTimeFormat
    Specifies the format for date and time comparisons.
 
    .PARAMETER ReverseCondition
    Switch parameter to reverse the comparison logic.
 
    .PARAMETER Color
    Specifies the text color for pass conditions.
 
    .PARAMETER BackgroundColor
    Specifies the background color for pass conditions.
 
    .PARAMETER FontSize
    Specifies the font size for pass conditions.
 
    .PARAMETER FontWeight
    Specifies the font weight for pass conditions.
 
    .PARAMETER FontStyle
    Specifies the font style for pass conditions.
 
    .PARAMETER FontVariant
    Specifies the font variant for pass conditions.
 
    .PARAMETER FontFamily
    Specifies the font family for pass conditions.
 
    .PARAMETER Alignment
    Specifies the alignment for pass conditions.
 
    .PARAMETER TextDecoration
    Specifies the text decoration for pass conditions.
 
    .PARAMETER TextTransform
    Specifies the text transformation for pass conditions.
 
    .PARAMETER Direction
    Specifies the text direction for pass conditions.
 
    .PARAMETER FailColor
    Specifies the text color for fail conditions.
 
    .PARAMETER FailBackgroundColor
    Specifies the background color for fail conditions.
 
    .PARAMETER FailFontSize
    Specifies the font size for fail conditions.
 
    .PARAMETER FailFontWeight
    Specifies the font weight for fail conditions.
 
    .PARAMETER FailFontStyle
    Specifies the font style for fail conditions.
 
    .PARAMETER FailFontVariant
    Specifies the font variant for fail conditions.
 
    .PARAMETER FailFontFamily
    Specifies the font family for fail conditions.
 
    .PARAMETER FailAlignment
    Specifies the alignment for fail conditions.
 
    .PARAMETER FailTextDecoration
    Specifies the text decoration for fail conditions.
 
    .PARAMETER FailTextTransform
    Specifies the text transformation for fail conditions.
 
    .PARAMETER FailDirection
    Specifies the text direction for fail conditions.
 
    .EXAMPLE
    An example of how to use this function.
 
    .NOTES
    Additional notes about the function.
    #>

    [alias('EmailTableCondition', 'TableConditionalFormatting', 'New-HTMLTableCondition', 'TableCondition')]
    [CmdletBinding()]
    param(
        [parameter(Mandatory)][alias('ColumnName')][string] $Name,
        [string[]] $HighlightHeaders,
        [alias('Type')][ValidateSet('number', 'string', 'bool', 'date')][string] $ComparisonType = 'string',
        [ValidateSet('lt', 'le', 'eq', 'ge', 'gt', 'ne', 'contains', 'like', 'notlike', 'notcontains', 'between', 'betweenInclusive', 'in', 'notin')][string] $Operator = 'eq',
        [parameter(Mandatory)][Object] $Value,
        [switch] $Row,
        [switch] $Inline,
        [switch] $CaseSensitive,
        [string] $DateTimeFormat,
        [switch] $ReverseCondition,
        # Style for PASS
        [string]$Color,
        [string]$BackgroundColor,
        [object] $FontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [string] $FontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [ValidateSet('rtl')][string] $Direction,
        # Style for FAIL
        [string]$FailColor,
        [string]$FailBackgroundColor,
        [object] $FailFontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FailFontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FailFontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FailFontVariant,
        [string] $FailFontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string] $FailAlignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $FailTextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $FailTextTransform,
        [ValidateSet('rtl')][string] $FailDirection
    )

    $Script:HTMLSchema.Features.DataTablesConditions = $true

    $Style = @{
        Color           = $Color
        BackGroundColor = $BackGroundColor
        FontSize        = $FontSize
        FontWeight      = $FontWeight
        FontStyle       = $FontStyle
        FontVariant     = $FontVariant
        FontFamily      = $FontFamily
        Alignment       = $Alignment
        TextDecoration  = $TextDecoration
        TextTransform   = $TextTransform
        Direction       = $Direction
    }
    Remove-EmptyValue -Hashtable $Style

    $FailStyle = @{
        Color           = $FailColor
        BackGroundColor = $FailBackGroundColor
        FontSize        = $FailFontSize
        FontWeight      = $FailFontWeight
        FontStyle       = $FailFontStyle
        FontVariant     = $FailFontVariant
        FontFamily      = $FailFontFamily
        Alignment       = $FailAlignment
        TextDecoration  = $FailTextDecoration
        TextTransform   = $FailTextTransform
        Direction       = $FailDirection
    }
    Remove-EmptyValue -Hashtable $FailStyle

    $TableCondition = [PSCustomObject] @{
        ConditionType    = 'Condition'
        Row              = $Row
        Type             = $ComparisonType
        Name             = $Name
        Operator         = $Operator
        Value            = $Value
        Style            = ConvertTo-HTMLStyle @Style
        FailStyle        = ConvertTo-HTMLStyle @FailStyle
        HighlightHeaders = $HighlightHeaders
        CaseSensitive    = $CaseSensitive.IsPresent
        DateTimeFormat   = $DateTimeFormat
        ReverseCondition = $ReverseCondition.IsPresent
    }
    [PSCustomObject] @{
        Type   = if ($Inline) {
            'TableConditionInline' } else {
            'TableCondition' }
        Output = $TableCondition
    }
}

Register-ArgumentCompleter -CommandName New-TableCondition -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableCondition -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableCondition -ParameterName FailColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableCondition -ParameterName FailBackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-TableConditionGroup {
    <#
    .SYNOPSIS
    Creates a new table condition group with specified conditions and styling options.
 
    .DESCRIPTION
    This function creates a new table condition group with the specified conditions and allows customization of the styling options for both pass and fail conditions.
 
    .PARAMETER Conditions
    Specifies the conditions to be applied within the condition group.
 
    .PARAMETER Logic
    Specifies the logic to be used for combining conditions. Valid values are 'AND', 'OR', or 'NONE'.
 
    .PARAMETER HighlightHeaders
    Specifies an array of headers to highlight based on the conditions.
 
    .PARAMETER Row
    Switch parameter to indicate if the styling should be applied to the entire row.
 
    .PARAMETER Inline
    Switch parameter to indicate if the styling should be applied inline.
 
    .PARAMETER Color
    Specifies the text color for pass conditions.
 
    .PARAMETER BackgroundColor
    Specifies the background color for pass conditions.
 
    .PARAMETER FontSize
    Specifies the font size for pass conditions.
 
    .PARAMETER FontWeight
    Specifies the font weight for pass conditions.
 
    .PARAMETER FontStyle
    Specifies the font style for pass conditions.
 
    .PARAMETER FontVariant
    Specifies the font variant for pass conditions.
 
    .PARAMETER FontFamily
    Specifies the font family for pass conditions.
 
    .PARAMETER Alignment
    Specifies the alignment for pass conditions.
 
    .PARAMETER TextDecoration
    Specifies the text decoration for pass conditions.
 
    .PARAMETER TextTransform
    Specifies the text transformation for pass conditions.
 
    .PARAMETER Direction
    Specifies the text direction for pass conditions.
 
    .PARAMETER FailColor
    Specifies the text color for fail conditions.
 
    .PARAMETER FailBackgroundColor
    Specifies the background color for fail conditions.
 
    .PARAMETER FailFontSize
    Specifies the font size for fail conditions.
 
    .PARAMETER FailFontWeight
    Specifies the font weight for fail conditions.
 
    .PARAMETER FailFontStyle
    Specifies the font style for fail conditions.
 
    .PARAMETER FailFontVariant
    Specifies the font variant for fail conditions.
 
    .PARAMETER FailFontFamily
    Specifies the font family for fail conditions.
 
    .PARAMETER FailAlignment
    Specifies the alignment for fail conditions.
 
    .PARAMETER FailTextDecoration
    Specifies the text decoration for fail conditions.
 
    .PARAMETER FailTextTransform
    Specifies the text transformation for fail conditions.
 
    .PARAMETER FailDirection
    Specifies the text direction for fail conditions.
 
    .EXAMPLE
    New-TableConditionGroup -Conditions {
        New-HTMLTableCondition -Name 'Test1' -Value 1 -ComparisonType number
        New-HTMLTableCondition -Name 'Test2' -Value 2 -ComparisonType number
    } -Logic 'OR' -HighlightHeaders 'Test1', 'Test2', 'DisplayName', 'DomainName' -Color 'Green' -BackgroundColor 'LightGreen' -FontSize 12 -FontWeight 'bold' -Alignment 'center'
 
    Description:
    Creates a new table condition group with specified conditions, logic, highlighted headers, and styling options for pass conditions.
 
    .NOTES
    Additional notes about the function.
    #>

    [alias('EmailTableConditionGroup', 'TableConditionGroup', 'New-HTMLTableConditionGroup')]
    [CmdletBinding()]
    param(
        [scriptblock] $Conditions,
        [ValidateSet('AND', 'OR', 'NONE')][string] $Logic = 'AND',
        [string[]] $HighlightHeaders,
        [switch] $Row,
        [switch] $Inline,
        # Style for PASS
        [string]$Color,
        [string]$BackgroundColor,
        [object] $FontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [string] $FontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [ValidateSet('rtl')][string] $Direction,
        # Style for FAIL
        [string]$FailColor,
        [string]$FailBackgroundColor,
        [object] $FailFontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FailFontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FailFontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FailFontVariant,
        [string] $FailFontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string] $FailAlignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $FailTextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $FailTextTransform,
        [ValidateSet('rtl')][string] $FailDirection
    )
    if ($Conditions) {
        $Script:HTMLSchema.Features.DataTablesConditions = $true

        $Style = @{
            Color           = $Color
            BackGroundColor = $BackGroundColor
            FontSize        = $FontSize
            FontWeight      = $FontWeight
            FontStyle       = $FontStyle
            FontVariant     = $FontVariant
            FontFamily      = $FontFamily
            Alignment       = $Alignment
            TextDecoration  = $TextDecoration
            TextTransform   = $TextTransform
            Direction       = $Direction
        }
        Remove-EmptyValue -Hashtable $Style

        $FailStyle = @{
            Color           = $FailColor
            BackGroundColor = $FailBackGroundColor
            FontSize        = $FailFontSize
            FontWeight      = $FailFontWeight
            FontStyle       = $FailFontStyle
            FontVariant     = $FailFontVariant
            FontFamily      = $FailFontFamily
            Alignment       = $FailAlignment
            TextDecoration  = $FailTextDecoration
            TextTransform   = $FailTextTransform
            Direction       = $FailDirection
        }
        Remove-EmptyValue -Hashtable $FailStyle

        $TableConditionGroup = [PSCustomObject] @{
            ConditionType    = 'ConditionGroup'
            Style            = ConvertTo-HTMLStyle @Style
            FailStyle        = ConvertTo-HTMLStyle @FailStyle
            Conditions       = & $Conditions
            Row              = $Row
            Logic            = $Logic
            HighlightHeaders = $HighlightHeaders
            DateTimeFormat   = $DateTimeFormat
        }
        [PSCustomObject] @{
            Type   = if ($Inline) {
                'TableConditionGroupInline' } else {
                'TableConditionGroup' }
            Output = $TableConditionGroup
        }
    }
}

Register-ArgumentCompleter -CommandName New-TableConditionGroup -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableConditionGroup -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableConditionGroup -ParameterName FailColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableConditionGroup -ParameterName FailBackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-TableContent {
    <#
    .SYNOPSIS
    Provide a way to style or overwrite the table content with new content or style
 
    .DESCRIPTION
    Provide a way to style or overwrite the table content with new content or style
 
    .PARAMETER ColumnName
    Define column name to search where to replace the content or style. Conflicts with ColumnIndex. Choose one or the other.
 
    .PARAMETER ColumnIndex
    Define column index to search where to replace the content or style. Conflicts with ColumnName. Choose one or the other.
 
    .PARAMETER RowIndex
    Define row index to search where to replace the content or style.
 
    .PARAMETER Text
    Overwrite the text content of the cell. If not defined the cell will be styled only.
 
    .PARAMETER Color
    Pick one of the 800 colors or provide a hex color code.
 
    .PARAMETER BackGroundColor
    Pick one of the 800 colors or provide a hex color code.
 
    .PARAMETER FontSize
    Provide new font size. When skipped the font size will not be changed.
 
    .PARAMETER FontWeight
    Provide new font weight. When skipped the font weight will not be changed. Options are: 'normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900'
 
    .PARAMETER FontStyle
    Provide new font style. When skipped the font style will not be changed. Options are: 'normal', 'italic', 'oblique'
 
    .PARAMETER FontVariant
    Provide new font variant. When skipped the font variant will not be changed. Options are: 'normal', 'small-caps'
 
    .PARAMETER FontFamily
    Provide new font family. When skipped the font family will not be changed.
 
    .PARAMETER Alignment
    Provide new alignment. When skipped the alignment will not be changed. Options are: 'left', 'center', 'right', 'justify'
 
    .PARAMETER TextDecoration
    Provide new text decoration. When skipped the text decoration will not be changed. Options are: 'none', 'line-through', 'overline', 'underline'
 
    .PARAMETER TextTransform
    Provide new text transform. When skipped the text transform will not be changed. Options are: 'uppercase', 'lowercase', 'capitalize'
 
    .PARAMETER Direction
    Provide new direction. When skipped the direction will not be changed. Options are: 'rtl','ltr'. By default it's 'ltr'.
 
    .PARAMETER WordBreak
    Provide new word break. When skipped the word break will not be changed. Options are: 'normal', 'break-all', 'keep-all', 'break-word'
 
    .EXAMPLE
    New-HTML -TitleText "Example37 - Word Breaking" -FilePath "$PSScriptRoot\Example37.html" {
        New-HTMLSection -HeaderText "Word Break for whole table" -HeaderTextAlignment center -Content {
            New-HTMLTable -DataTable $(Get-Process | Select-Object -First 5) -WordBreak 'break-word'
        }
        New-HTMLSection -HeaderText "Word Break per column" -HeaderTextAlignment center -Content {
            New-HTMLTable -DataTable $(Get-Process | Select-Object -First 5) {
                New-TableContent -WordBreak break-all -ColumnName 'Path'
            }
        }
        New-HTMLSection -HeaderText "No word break" -HeaderTextAlignment center -Content {
            New-HTMLTable -DataTable $(Get-Process | Select-Object -First 5)
        }
    } -Online -ShowHTML
 
    .EXAMPLE
    $Values = @(
        [PSCustomObject] @{
            Test1 = 1
            Test2 = 2
            Test3 = 3
            Test4 = 1
        }
        [PSCustomObject] @{
            Test1 = 1
            Test2 = 2
            Test3 = 3
            Test4 = 1
        }
        [PSCustomObject] @{
            Test1 = 1
            Test2 = 2
            Test3 = 3
            Test4 = 2
        }
        [PSCustomObject] @{
            Test1 = 1
            Test2 = 2
            Test3 = 3
            Test4 = 1
        }
        [PSCustomObject] @{
            Test1 = 1
            Test2 = 2
            Test3 = 3
            Test4 = 1
        }
        [PSCustomObject] @{
            Test1 = 1
            Test2 = 2
            Test3 = 3
            Test4 = 2
        }
    )
 
    New-HTML -TitleText "Example41 - Table" -FilePath "$PSScriptRoot\Example41.html" {
        New-HTMLSection -HeaderText "Testing" -HeaderTextAlignment center -Content {
            New-HTMLTable -DataTable $Values {
                for ($i = 0; $i -le $Values.Count; $i++) {
                    if ($Values[$i].Test1 -ne $Values[$i].Test4) {
                        New-TableContent -BackGroundColor Red -ColumnName 'Test4' -RowIndex ($i+1)
                    }
                }
            }
        }
    } -Online -ShowHTML
 
    .NOTES
    General notes
    #>

    [alias('TableContent', 'EmailTableContent', 'New-HTMLTableContent')]
    [CmdletBinding()]
    param(
        [alias('ColumnNames', 'Names', 'Name')][string[]] $ColumnName,
        [int[]] $ColumnIndex,
        [int[]] $RowIndex,
        [string[]] $Text,
        [string] $Color,
        [string] $BackGroundColor,
        [object] $FontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [string] $FontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [ValidateSet('rtl', 'ltr')][string] $Direction,
        [ValidateSet('normal', 'break-all', 'keep-all', 'break-word')][string] $WordBreak
    )
    if ($WordBreak -eq '' -or $WordBreak -eq 'normal') {
        $WordBreakStyle = ''
    } else {
        $WordBreakStyle = $WordBreak
    }

    $Style = @{
        Color           = $Color
        BackGroundColor = $BackGroundColor
        FontSize        = $FontSize
        FontWeight      = $FontWeight
        FontStyle       = $FontStyle
        FontVariant     = $FontVariant
        FontFamily      = $FontFamily
        Alignment       = $Alignment
        TextDecoration  = $TextDecoration
        TextTransform   = $TextTransform
        Direction       = $Direction
        WordBreak       = $WordBreakStyle
    }
    Remove-EmptyValue -Hashtable $Style

    [PSCustomObject]@{
        Type   = 'TableContentStyle'
        Output = @{
            Name        = $ColumnName
            Text        = $Text
            RowIndex    = $RowIndex | Sort-Object
            ColumnIndex = $ColumnIndex | Sort-Object
            Style       = ConvertTo-HTMLStyle @Style
            Used        = $false
        }
    }
}

Register-ArgumentCompleter -CommandName New-TableContent -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableContent -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-TableEvent {
    <#
    .SYNOPSIS
    Creates a new table event object.
 
    .DESCRIPTION
    This function creates a new table event object with the specified parameters.
 
    .PARAMETER TableID
    Specifies the ID of the table.
 
    .PARAMETER SourceColumnName
    Specifies the name of the source column.
 
    .PARAMETER TargetColumnID
    Specifies the ID of the target column.
 
    .PARAMETER SourceColumnID
    Specifies the ID of the source column.
 
    .EXAMPLE
    New-TableEvent -TableID "Table1" -SourceColumnName "Column1" -TargetColumnID 1 -SourceColumnID 2
 
    Creates a new table event object for Table1 with SourceColumnName "Column1", TargetColumnID 1, and SourceColumnID 2.
 
    #>

    [cmdletBinding()]
    param(
        [alias('ID')][string] $TableID,
        [string] $SourceColumnName,
        [nullable[int]] $TargetColumnID,
        [nullable[int]] $SourceColumnID
    )
    if (($null -ne $SourceColumnID -or $SourceColumnName) -and $null -ne $TargetColumnID) {
        $Object = [PSCustomObject] @{
            Type   = 'TableEvent'
            Output = @{
                TableID          = $TableID
                SourceColumnID   = $SourceColumnID
                SourceColumnName = $SourceColumnName
                TargetColumnID   = $TargetColumnID
            }
        }
        $Object
    } else {
        Write-Warning "New-TableEvent - SourceColumnId or SourceColumnName and TargetColumnID are required for events to work."
    }
}
function New-TableHeader {
    <#
    .SYNOPSIS
    Creates a new table header with specified styling options.
 
    .DESCRIPTION
    The New-TableHeader function creates a new table header with customizable styling options such as font size, color, alignment, and more.
 
    .EXAMPLE
    New-TableHeader -Names "Name", "Age", "Email" -Title "Employee Information" -Color "black" -BackGroundColor "lightgray" -FontSize 14 -FontWeight "bold" -FontStyle "normal" -FontVariant "normal" -FontFamily "Arial" -Alignment "center" -TextDecoration "none" -TextTransform "uppercase" -Direction "ltr" -AddRow -ColumnCount 3 -Display "all"
    Creates a table header with specified column names, title, and styling options.
 
    .PARAMETER Names
    Specifies an array of column names for the table header.
 
    .PARAMETER Title
    Specifies the title of the table header.
 
    .PARAMETER Color
    Specifies the text color of the table header.
 
    .PARAMETER BackGroundColor
    Specifies the background color of the table header.
 
    .PARAMETER FontSize
    Specifies the font size of the table header.
 
    .PARAMETER FontWeight
    Specifies the font weight of the table header.
 
    .PARAMETER FontStyle
    Specifies the font style of the table header.
 
    .PARAMETER FontVariant
    Specifies the font variant of the table header.
 
    .PARAMETER FontFamily
    Specifies the font family of the table header.
 
    .PARAMETER Alignment
    Specifies the alignment of the table header.
 
    .PARAMETER TextDecoration
    Specifies the text decoration of the table header.
 
    .PARAMETER TextTransform
    Specifies the text transformation of the table header.
 
    .PARAMETER Direction
    Specifies the text direction of the table header.
 
    .PARAMETER AddRow
    Switch parameter to add an additional row to the table header.
 
    .PARAMETER ColumnCount
    Specifies the number of columns in the table header.
 
    .PARAMETER Display
    Specifies the display settings for the table header.
 
    #>

    [alias('TableHeader', 'EmailTableHeader', 'New-HTMLTableHeader')]
    [CmdletBinding()]
    param(
        [string[]] $Names,
        [string] $Title,
        [string] $Color,
        [string] $BackGroundColor,
        [object] $FontSize,
        [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight,
        [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle,
        [ValidateSet('normal', 'small-caps')][string] $FontVariant,
        [string] $FontFamily,
        [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment,
        [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration,
        [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform,
        [ValidateSet('rtl')][string] $Direction,
        [switch] $AddRow,
        [int] $ColumnCount,
        [ValidateSet(
            'all',
            'none',
            'never',
            'desktop',
            'not-desktop',
            'tablet-l',
            'tablet-p',
            'mobile-l',
            'mobile-p',
            'min-desktop',
            'max-desktop',
            'tablet',
            'not-tablet',
            'min-tablet',
            'max-tablet',
            'not-tablet-l',
            'min-tablet-l',
            'max-tablet-l',
            'not-tablet-p',
            'min-tablet-p',
            'max-tablet-p',
            'mobile',
            'not-mobile',
            'min-mobile',
            'max-mobile',
            'not-mobile-l',
            'min-mobile-l',
            'max-mobile-l',
            'not-mobile-p',
            'min-mobile-p',
            'max-mobile-p'
        )][string] $ResponsiveOperations

    )
    if ($AddRow) {
        Write-Warning "New-HTMLTableHeader - Using AddRow switch is deprecated. It's not nessecary anymore. Just use Title alone. It will be removed later on."
    }

    $Style = @{
        Color           = $Color
        BackGroundColor = $BackGroundColor
        FontSize        = $FontSize
        FontWeight      = $FontWeight
        FontStyle       = $FontStyle
        FontVariant     = $FontVariant
        FontFamily      = $FontFamily
        Alignment       = $Alignment
        TextDecoration  = $TextDecoration
        TextTransform   = $TextTransform
        Direction       = $Direction
    }
    Remove-EmptyValue -Hashtable $Style

    if (($AddRow -and $Title) -or ($Title -and -not $Names)) {
        $Type = 'TableHeaderFullRow'
    } elseif ((-not $AddRow -and $Title) -or ($Title -and $Names)) {
        $Type = 'TableHeaderMerge'
    } elseif ($Names -and $ResponsiveOperations) {
        $Type = 'TableHeaderResponsiveOperations'
    } elseif ($ResponsiveOperations) {
        Write-Warning 'New-HTMLTableHeader - ResponsiveOperations require Names (ColumnNames) to apply operation to.'
        return
    } else {
        $Type = 'TableHeaderStyle'
    }

    [PSCustomObject]@{
        Type   = $Type
        Output = @{
            Names                = $Names
            ResponsiveOperations = $ResponsiveOperations
            Title                = $Title
            Style                = ConvertTo-HTMLStyle @Style
            ColumnCount          = $ColumnCount
        }
    }
}

Register-ArgumentCompleter -CommandName New-TableHeader -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableHeader -ParameterName BackGroundColor -ScriptBlock $Script:ScriptBlockColors
function New-TableLanguage {
    <#
    .SYNOPSIS
    Provides ability to overwrite texts available in the table.
 
    .DESCRIPTION
    Provides ability to overwrite texts available in the table. This is useful for translating to different languages or choosing different naming.
 
    .PARAMETER Info
    Overwrites information about the table. Default value is: "Showing _START_ to _END_ of _TOTAL_ entries"
 
    .PARAMETER InfoFiltered
    Overwrites information about the table when filtered. Default value is: "(filtered from _MAX_ total entries)"
 
    .PARAMETER Search
    Overwrites the search text. Default value is: "Search:"
 
    .PARAMETER EmptyTable
    Overwrites the text when the table is empty. Default value is: "No data available in table"
 
    .PARAMETER ZeroRecords
    Overwrites the text when no records match the search. Default value is: "No matching records found"
 
    .PARAMETER PaginateFirst
    Overwrites the text for the first page button. Default value is: "First"
 
    .PARAMETER PaginateLast
    Overwrites the text for the last page button. Default value is: "Last"
 
    .PARAMETER PaginateNext
    Overwrites the text for the next page button. Default value is: "Next"
 
    .PARAMETER PaginatePrevious
    Overwrites the text for the previous page button. Default value is: "Previous"
 
    .EXAMPLE
    New-HTML -TitleText "Example41 - Table" -FilePath "$PSScriptRoot\Example41.html" {
        New-HTMLSection -HeaderText "Testing" -HeaderTextAlignment center -Content {
            New-HTMLTable -DataTable $Values {
                New-TableLanguage -Search 'Find' -PaginateFirst 'First Option' -EmptyTable 'No data in the table'
                New-HTMLTableConditionGroup {
                    New-HTMLTableCondition -Name 'Test1' -Value 1 -ComparisonType number
                    New-HTMLTableCondition -Name 'Test2' -Value 2 -ComparisonType number
                } -BackgroundColor Salmon -FailBackgroundColor Goldenrod -Logic OR -HighlightHeaders 'Test1', 'Test2', 'DisplayName', 'DomainName'
            } -DataStore JavaScript
        }
    } -ShowHTML -Online
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        [string] $Info,
        [string] $InfoFiltered,
        [string] $Search,
        [string] $EmptyTable,
        [string] $ZeroRecords,
        [string] $PaginateFirst,
        [string] $PaginateLast,
        [string] $PaginateNext,
        [string] $PaginatePrevious
    )

    $Object = [PSCustomObject] @{
        Type   = 'TableLanguage'
        Output = [ordered] @{
            "search"       = $Search
            "info"         = $Info
            "infoFiltered" = $InfoFiltered

            "emptyTable"   = $EmptyTable
            "zeroRecords"  = $ZeroRecords
            paginate       = [ordered] @{
                "first"    = $PaginateFirst
                "last"     = $PaginateLast
                "next"     = $PaginateNext
                "previous" = $PaginatePrevious
            }
        }
    }
    Remove-EmptyValue -Hashtable $Object.Output -Recursive
    $Object
}
function New-TablePercentageBar {
    <#
    .SYNOPSIS
    Creates a new table percentage bar object with specified attributes.
 
    .DESCRIPTION
    This function creates a new table percentage bar object with the provided attributes. It allows customization of the appearance of the percentage bar within a table.
 
    .PARAMETER ColumnID
    Specifies the ID of the column where the percentage bar will be displayed.
 
    .PARAMETER ColumnName
    Specifies the name of the column where the percentage bar will be displayed.
 
    .PARAMETER Type
    Specifies the shape of the percentage bar. Valid values are 'square' or 'round'.
 
    .PARAMETER TextColor
    Specifies the color of the text within the percentage bar.
 
    .PARAMETER BorderColor
    Specifies the color of the border of the percentage bar.
 
    .PARAMETER BorderStyle
    Specifies the style of the border of the percentage bar. Valid values are 'solid', 'outset', 'groove', or 'ridge'.
 
    .PARAMETER BarColor
    Specifies the color of the percentage bar itself.
 
    .PARAMETER BackgroundColor
    Specifies the background color of the percentage bar.
 
    .PARAMETER RoundValue
    Specifies the decimal places to round the percentage value to.
 
    .PARAMETER ConditionalFormatting
    Specifies any conditional formatting rules for the percentage bar.
 
    .EXAMPLE
    New-TablePercentageBar -ColumnID 1 -ColumnName "Progress" -Type 'round' -TextColor "#ffffff" -BorderColor "#000000" -BorderStyle 'solid' -BarColor "#00ff00" -BackgroundColor "#ffffff" -RoundValue 2
 
    Description:
    Creates a new table percentage bar object for the column "Progress" with specified attributes.
 
    .NOTES
    Additional notes about the function.
    #>

    [alias('TablePercentageBar', 'New-HTMLTablePercentageBar')]
    [CmdletBinding()]
    param(
        [scriptblock] $ConditionalFormatting,
        [int] $ColumnID,
        [string] $ColumnName,
        [ValidateSet('square', 'round')][string] $Type,
        [string] $TextColor,
        [string] $BorderColor,
        [ValidateSet('solid', 'outset', 'groove', 'ridge')][string] $BorderStyle,
        [string] $BarColor,
        [string] $BackgroundColor,
        [int] $RoundValue
    )

    if ($ConditionalFormatting) {
        $OutputConditions = & $ConditionalFormatting
        [Array] $Conditions = foreach ($OutputCondition in $OutputConditions) {
            if ($OutputCondition.Type -eq 'TablePercentageBarCondition') {
                $OutputCondition.Output
            }
        }
    }

    $Output = [PSCustomObject]@{
        Type   = 'TablePercentageBar'
        Output = @{
            ColumnID              = $ColumnID
            ColumnName            = $ColumnName
            Type                  = $Type
            TextColor             = ConvertFrom-Color -Color $TextColor
            BorderColor           = ConvertFrom-Color -Color $BorderColor
            BarColor              = ConvertFrom-Color -Color $BarColor
            BackgroundColor       = ConvertFrom-Color -Color $BackgroundColor
            RoundValue            = $RoundValue
            BorderStyle           = $BorderStyle
            ConditionalFormatting = $Conditions
        }
    }
    Remove-EmptyValue -Hashtable $Output.Output
    $Output
}

Register-ArgumentCompleter -CommandName New-TablePercentageBar -ParameterName TextColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TablePercentageBar -ParameterName BorderColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TablePercentageBar -ParameterName BarColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TablePercentageBar -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function New-TablePercentageBarCondition {
    <#
    .SYNOPSIS
    Creates a new table percentage bar condition object with specified attributes.
 
    .DESCRIPTION
    This function creates a new table percentage bar condition object with the provided attributes. It defines the minimum and maximum values for the percentage range, along with the colors for the background, text, and bar.
 
    .PARAMETER Minimum
    Specifies the minimum value for the percentage range.
 
    .PARAMETER Maximum
    Specifies the maximum value for the percentage range.
 
    .PARAMETER BackgroundColor
    Specifies the background color for the percentage bar.
 
    .PARAMETER TextColor
    Specifies the text color for the percentage bar.
 
    .PARAMETER BarColor
    Specifies the color of the percentage bar.
 
    .EXAMPLE
    New-TablePercentageBarCondition -Minimum 0 -Maximum 10 -BackgroundColor "#ff0000" -TextColor "#ffffff" -BarColor "#ff0000"
    Creates a new table percentage bar condition object for the range 0-10 with specified colors.
 
    .EXAMPLE
    New-TablePercentageBarCondition -Minimum 11 -Maximum 20 -BackgroundColor "#00ff00" -BarColor "#00ff00"
    Creates a new table percentage bar condition object for the range 11-20 with specified colors.
 
    .EXAMPLE
    New-TablePercentageBarCondition -Minimum 21 -Maximum 23 -BackgroundColor "#0000ff" -BarColor "#0000ff"
    Creates a new table percentage bar condition object for the range 21-23 with specified colors.
 
    .EXAMPLE
    New-TablePercentageBarCondition -Minimum 24 -Maximum 100 -BackgroundColor "#00FFFF" -BarColor "#00FFFF"
    Creates a new table percentage bar condition object for the range 24-100 with specified colors.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][int] $Minimum,
        [Parameter(Mandatory)][int] $Maximum,
        [string] $BackgroundColor,
        [string] $TextColor,
        [string] $BarColor
    )

    $Output = [PSCustomObject]@{
        Type   = 'TablePercentageBarCondition'
        Output = [ordered] @{
            min             = $Minimum
            max             = $Maximum
            backgroundColor = ConvertFrom-Color -Color $BackgroundColor
            textColor       = ConvertFrom-Color -Color $TextColor
            barColor        = ConvertFrom-Color -Color $BarColor
        }
    }
    $Output
}

Register-ArgumentCompleter -CommandName New-TablePercentageBarCondition -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TablePercentageBarCondition -ParameterName TextColor -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TablePercentageBarCondition -ParameterName BarColor -ScriptBlock $Script:ScriptBlockColors

function New-TableReplace {
    <#
    .SYNOPSIS
    Replaces values in a specified field of a table.
 
    .DESCRIPTION
    This function replaces values in a specified field of a table with the provided replacements.
 
    .PARAMETER FieldName
    The name of the field in the table where replacements will be made.
 
    .PARAMETER Replacements
    An array of strings containing the values to replace in the specified field.
 
    .EXAMPLE
    New-TableReplace -FieldName "Status" -Replacements @("Active", "Inactive")
 
    Description:
    Replaces values in the "Status" field with "Active" or "Inactive".
 
    .EXAMPLE
    New-TableReplace -FieldName "Priority" -Replacements @("High", "Medium", "Low")
 
    Description:
    Replaces values in the "Priority" field with "High", "Medium", or "Low".
    #>

    [alias('TableReplace', 'EmailTableReplace', 'New-HTMLTableReplace')]
    [CmdletBinding()]
    param(
        [string] $FieldName,
        [string[]] $Replacements

    )
    [PSCustomObject]@{
        Type   = 'TableReplaceCompare'
        Output = @{
            $FieldName = $Replacements
        }
    }
}
function New-TableRowGrouping {
    <#
    .SYNOPSIS
    Creates a new table row grouping object with specified attributes.
 
    .DESCRIPTION
    This function creates a new table row grouping object with the provided attributes. It enables the feature for DataTablesRowGrouping in the HTML schema.
 
    .PARAMETER Name
    Specifies the name of the row grouping.
 
    .PARAMETER ColumnID
    Specifies the ID of the column to group rows by. Default value is -1.
 
    .PARAMETER SortOrder
    Specifies the sort order for the grouped rows. Valid values are 'Ascending' or 'Descending'. Default is 'Ascending'.
 
    .PARAMETER Color
    Specifies the color for the row grouping.
 
    .PARAMETER BackgroundColor
    Specifies the background color for the row grouping.
 
    .EXAMPLE
    New-TableRowGrouping -Name "Group A" -ColumnID 1 -SortOrder 'Ascending' -Color 'Red' -BackgroundColor 'LightGray'
    Creates a new table row grouping object for Group A, grouping by column ID 1 in ascending order with red text and light gray background.
 
    .EXAMPLE
    New-TableRowGrouping -Name "Group B" -ColumnID 2 -SortOrder 'Descending' -Color 'Blue' -BackgroundColor 'White'
    Creates a new table row grouping object for Group B, grouping by column ID 2 in descending order with blue text and white background.
    #>

    [alias('TableRowGrouping', 'EmailTableRowGrouping', 'New-HTMLTableRowGrouping')]
    [CmdletBinding()]
    param(
        [alias('ColumnName')][string] $Name,
        [int] $ColumnID = -1,
        [ValidateSet('Ascending', 'Descending')][string] $SortOrder = 'Ascending',
        [string] $Color,
        [string] $BackgroundColor
    )
    $Script:HTMLSchema.Features.DataTablesRowGrouping = $true

    $Object = [PSCustomObject] @{
        Type   = 'TableRowGrouping'
        Output = [ordered] @{
            Name       = $Name
            ColumnID   = $ColumnID
            Sorting    = if ('Ascending') {
                'asc' } else {
                'desc' }
            Attributes = @{
                'color'            = ConvertFrom-Color -Color $Color
                'background-color' = ConvertFrom-Color -Color $BackgroundColor
            }
        }
    }
    Remove-EmptyValue -Hashtable $Object.Output
    $Object
}

Register-ArgumentCompleter -CommandName New-TableRowGrouping -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
Register-ArgumentCompleter -CommandName New-TableRowGrouping -ParameterName BackgroundColor -ScriptBlock $Script:ScriptBlockColors
function Out-HtmlView {
    <#
    .SYNOPSIS
    Small function that allows to send output to HTML
 
    .DESCRIPTION
    Small function that allows to send output to HTML. When displaying in HTML it allows data to output to EXCEL, CSV and PDF. It allows sorting, searching and so on.
 
    .PARAMETER Table
    Data you want to display
 
    .PARAMETER Title
    Title of HTML Window
 
    .PARAMETER DefaultSortColumn
    Sort by Column Name
 
    .PARAMETER DefaultSortIndex
    Sort by Column Index
 
    .EXAMPLE
    Get-Process | Select-Object -First 5 | Out-HtmlView
 
    .NOTES
    General notes
    #>

    [alias('Out-GridHtml', 'ohv')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $HTML,
        [Parameter(Mandatory = $false, Position = 1)][ScriptBlock] $PreContent,
        [Parameter(Mandatory = $false, Position = 2)][ScriptBlock] $PostContent,
        [alias('ArrayOfObjects', 'Object', 'DataTable')][Parameter(ValueFromPipeline = $true, Mandatory = $false)] $Table,
        [string] $FilePath,
        [string] $Title = 'Out-HTMLView',
        [switch] $PassThru,
        [string[]][ValidateSet('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'pageLength', 'print', 'searchPanes', 'searchBuilder')] $Buttons = @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'pageLength', 'searchBuilder'),
        [string[]][ValidateSet('numbers', 'simple', 'simple_numbers', 'full', 'full_numbers', 'first_last_numbers')] $PagingStyle = 'full_numbers',
        [int[]]$PagingOptions = @(15, 25, 50, 100),
        [int] $PagingLength,
        [switch]$DisablePaging,
        [switch]$DisableOrdering,
        [switch]$DisableInfo,
        [switch]$HideFooter,
        [alias('DisableButtons')][switch]$HideButtons,
        [switch]$DisableProcessing,
        [switch]$DisableResponsiveTable,
        [switch]$DisableSelect,
        [switch]$DisableStateSave,
        [switch]$DisableSearch,
        [switch]$OrderMulti,
        [switch]$Filtering,
        [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom',
        [string[]][ValidateSet('display', 'cell-border', 'compact', 'hover', 'nowrap', 'order-column', 'row-border', 'stripe')] $Style = @('display', 'compact'),
        [switch]$Simplify,
        [string]$TextWhenNoData = 'No data available to display.',
        [int] $ScreenSizePercent = 0,
        [string[]] $DefaultSortColumn,
        [int[]] $DefaultSortIndex,
        [ValidateSet('Ascending', 'Descending')][string] $DefaultSortOrder = 'Ascending',
        [string[]] $DateTimeSortingFormat,
        [alias('Search')][string]$Find,
        [switch] $InvokeHTMLTags,
        [switch] $DisableNewLine,
        [switch] $EnableKeys,
        [switch] $EnableColumnReorder,
        [switch] $EnableRowReorder,
        [switch] $EnableAutoFill,
        [switch] $EnableScroller,
        [switch] $ScrollX,
        [switch] $ScrollY,
        [int] $ScrollSizeY = 500,
        [switch]$ScrollCollapse,
        [int] $FreezeColumnsLeft,
        [int] $FreezeColumnsRight,
        [switch] $FixedHeader,
        [switch] $FixedFooter,
        [string[]] $ResponsivePriorityOrder,
        [int[]] $ResponsivePriorityOrderIndex,
        [string[]] $PriorityProperties,
        [string[]] $IncludeProperty,
        [string[]] $ExcludeProperty,
        [switch] $ImmediatelyShowHiddenDetails,
        [alias('RemoveShowButton')][switch] $HideShowButton,
        [switch] $AllProperties,
        [switch] $SkipProperties,
        [switch] $Compare,
        [alias('CompareWithColors')][switch] $HighlightDifferences,
        [Array] $CompareNames,
        [int] $First,
        [int] $Last,
        [alias('Replace')][Array] $CompareReplace,
        [alias('RegularExpression')][switch]$SearchRegularExpression,
        [ValidateSet('normal', 'break-all', 'keep-all', 'break-word')][string] $WordBreak = 'normal',
        [switch] $AutoSize,
        [switch] $DisableAutoWidthOptimization,
        [switch] $SearchPane,
        [ValidateSet('top', 'bottom')][string] $SearchPaneLocation = 'top',
        [switch] $SearchBuilder,
        [ValidateSet('top', 'bottom')][string] $SearchBuilderLocation = 'top',
        [ValidateSet('HTML', 'JavaScript', 'AjaxJSON')][string] $DataStore,
        [switch] $Transpose,
        [string] $TransposeProperty,
        [string] $TransposeName,
        [switch] $TransposeLegacy,
        [switch] $PreventShowHTML,
        [switch] $Online,
        [string] $OverwriteDOM,
        [switch] $SearchHighlight,
        [switch] $AlphabetSearch,
        [switch] $FuzzySearch,
        [switch] $FuzzySearchSmartToggle,
        [switch] $FlattenObject,
        [switch] $PrettifyObject,
        [string] $PrettifyObjectSeparator = ", ",
        [string] $PrettifyObjectDateTimeFormat
    )
    Begin {
        $DataTable = [System.Collections.Generic.List[Object]]::new()
        if ($FilePath -eq '') {
            $FilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).html")
        }
    }
    Process {
        if ($null -ne $Table) {
            foreach ($T in $Table) {
                $DataTable.Add($T)
            }
        }
    }
    End {
        if ($null -ne $DataTable) {

            New-HTML -FilePath $FilePath -Online:($Online.IsPresent) -TitleText $Title -ShowHTML:(-not $PreventShowHTML) {
                $newHTMLTableSplat = @{
                    DataTable                    = $DataTable
                    HideFooter                   = $HideFooter
                    Buttons                      = $Buttons
                    PagingStyle                  = $PagingStyle
                    PagingOptions                = $PagingOptions
                    DisablePaging                = $DisablePaging
                    DisableOrdering              = $DisableOrdering
                    DisableInfo                  = $DisableInfo
                    DisableProcessing            = $DisableProcessing
                    DisableResponsiveTable       = $DisableResponsiveTable
                    DisableSelect                = $DisableSelect
                    DisableStateSave             = $DisableStateSave
                    DisableSearch                = $DisableSearch
                    ScrollCollapse               = $ScrollCollapse
                    Style                        = $Style
                    TextWhenNoData               = $TextWhenNoData
                    ScreenSizePercent            = $ScreenSizePercent
                    HTML                         = $HTML
                    PreContent                   = $PreContent
                    PostContent                  = $PostContent
                    DefaultSortColumn            = $DefaultSortColumn
                    DefaultSortIndex             = $DefaultSortIndex
                    DefaultSortOrder             = $DefaultSortOrder
                    DateTimeSortingFormat        = $DateTimeSortingFormat
                    Find                         = $Find
                    OrderMulti                   = $OrderMulti
                    Filtering                    = $Filtering
                    FilteringLocation            = $FilteringLocation
                    InvokeHTMLTags               = $InvokeHTMLTags
                    DisableNewLine               = $DisableNewLine
                    ScrollX                      = $ScrollX
                    ScrollY                      = $ScrollY
                    ScrollSizeY                  = $ScrollSizeY
                    FreezeColumnsLeft            = $FreezeColumnsLeft
                    FreezeColumnsRight           = $FreezeColumnsRight
                    FixedHeader                  = $FixedHeader
                    FixedFooter                  = $FixedFooter
                    ResponsivePriorityOrder      = $ResponsivePriorityOrder
                    ResponsivePriorityOrderIndex = $ResponsivePriorityOrderIndex
                    PriorityProperties           = $PriorityProperties
                    AllProperties                = $AllProperties
                    SkipProperties               = $SkipProperties
                    Compare                      = $Compare
                    HighlightDifferences         = $HighlightDifferences
                    First                        = $First
                    Last                         = $Last
                    ImmediatelyShowHiddenDetails = $ImmediatelyShowHiddenDetails
                    Simplify                     = $Simplify
                    HideShowButton               = $HideShowButton
                    CompareReplace               = $CompareReplace
                    SearchRegularExpression      = $SearchRegularExpression
                    WordBreak                    = $WordBreak
                    AutoSize                     = $AutoSize
                    DisableAutoWidthOptimization = $DisableAutoWidthOptimization
                    Title                        = $Title
                    SearchPane                   = $SearchPane
                    SearchPaneLocation           = $SearchPaneLocation
                    EnableScroller               = $EnableScroller
                    EnableKeys                   = $EnableKeys
                    EnableAutoFill               = $EnableAutoFill
                    EnableRowReorder             = $EnableRowReorder
                    EnableColumnReorder          = $EnableColumnReorder
                    HideButtons                  = $HideButtons
                    PagingLength                 = $PagingLength
                    DataStore                    = $DataStore
                    DisableColumnReorder         = $DisableColumnReorder
                    AlphabetSearch               = $AlphabetSearch
                    SearchBuilder                = $SearchBuilder
                    SearchBuilderLocation        = $SearchBuilderLocation
                    OverwriteDOM                 = $OverwriteDOM
                    IncludeProperty              = $IncludeProperty
                    ExcludeProperty              = $ExcludeProperty
                    FuzzySearch                  = $FuzzySearch
                    FuzzySearchSmartToggle       = $FuzzySearchSmartToggle
                    FlattenObject                = $FlattenObject
                    CompareNames                 = $CompareNames
                }
                if ($Transpose) {
                    $newHTMLTableSplat['Transpose'] = $Transpose.IsPresent
                    if ($TransposeProperty) {
                        $newHTMLTableSplat['TransposeProperty'] = $TransposeProperty
                    }
                    if ($TransposeName) {
                        $newHTMLTableSplat['TransposeName'] = $TransposeName
                    }
                    if ($TransposeLegacy) {
                        $newHTMLTableSplat['TransposeLegacy'] = $TransposeLegacy
                    }
                }
                if ($PrettifyObject) {
                    $newHTMLTableSplat['PrettifyObject'] = $PrettifyObject.IsPresent
                    if ($PrettifyObjectSeparator) {
                        $newHTMLTableSplat['PrettifyObjectSeparator'] = $PrettifyObjectSeparator
                    }
                    if ($PrettifyObjectDateTimeFormat) {
                        $newHTMLTableSplat['PrettifyObjectDateTimeFormat'] = $PrettifyObjectDateTimeFormat
                    }
                }
                Remove-EmptyValue -Hashtable $newHTMLTableSplat
                New-HTMLTable @newHTMLTableSplat

            }
            if ($PassThru) {

                $DataTable
            }
        } else {
            Write-Warning 'Out-HtmlView - No data available.'
        }
    }
}

Function Save-HTML {
    <#
    .SYNOPSIS
    Saves HTML (text) to a file. Can be used with conjucncton with New-HTML.
 
    .DESCRIPTION
    Saves HTML (text) to a file. Can be used with conjucncton with New-HTML, although New-HTML has Save-HTML built-in already when providing FilePath parameter.
 
    .PARAMETER FilePath
    Provides the path to the file to be created.
 
    .PARAMETER HTML
    HTML (text) to be saved.
 
    .PARAMETER ShowHTML
    Opens HTML in browser when generating of HTML is done. When no filepath is provided it uses temporary path instead. Good for testing.
 
    .PARAMETER Encoding
    Provides ability to choose encoding of the HTML file. Not really required to use, as UTF8 is the default. Options available: 'Unknown', 'String', 'Unicode', 'Byte', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'Ascii', 'Default', 'Oem', 'BigEndianUTF32'
 
    .PARAMETER Suppress
    If Suppress is used then no output is shown. If Suppress is set to false then FilePath is returned.
 
    .PARAMETER Format
    Formats HTML output (including CSS/JS). Requires PSParseHTML to be installed and available.
 
    .PARAMETER Minify
    Minifies HTML output (including CSS/JS). Requires PSParseHTML to be installed and available.
 
    .EXAMPLE
    $Company = 'Evotec'
 
    $Text = @"
    This email is from Evotec. It may contain confidential information. It is intended for the addressee only and may not be copied or disclosed to any third party without our permission. If you are not the intended recipient please contact the sender as soon as possible and delete the material from any computer. If this email has been sent as a personal message to the addressee, the sender is not acting in his/her capacity as an employee or officer of $Company Limited and no liability is accepted for the content of any such email. Outgoing email may be monitored for the purpose of ensuring compliance with our email policy and relevant laws
    "@
 
    $HTML = New-HTMLText -FontSize 10 -FontFamily 'Source Sans Pro' -Text $Text
    Save-HTML -FilePath $PSScriptRoot\Disclaimer.html -HTML $HTML -ShowHTML
 
    .EXAMPLE
    $Test = EmailBody {
        EmailText -LineBreak
        EmailText -Text 'Z poważaniem' -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray -LineBreak
        EmailText -Text $DisplayName -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        EmailText -Text $Title -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        EmailText -Text $Department -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        EmailText -LineBreak
        if ($Mobile) {
            EmailText -Text "m: $Mobile" -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        }
        EmailText -Text "e: $Email" -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        EmailText -LineBreak
        EmailText -Text $CompanyName -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        EmailText -Text $StreetAddress -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        EmailText -Text "$PostalCode $City" -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        EmailText -LineBreak
        EmailText -Text "[www.aliorleasing.pl](https://www.aliorleasing.pl)" -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray
        EmailText -LineBreak
        EmailLayoutRow {
            EmailLayoutColumn {
                EmailImage -Source 'https://aliorleasing.pl/images/logoAL2.jpg' -UrlLink '' -AlternativeText 'Alior Leasing Logo' -Width '134' -Inline -Height '90'
            }
            EmailLayoutColumn {
 
            }
            EmailLayoutColumn {
                EmailImage -Source 'https://aliorleasing.pl/images/akcia-nnw.jpg' -UrlLink '' -AlternativeText 'Alior Leasing Logo' -Width '300' -Inline -Height '90'
            }
        }
        EmailText -LineBreak
        EmailText -FontWeight bold, normal -FontSize 8pt -FontFamily 'Verdana,sans-serif' -Color Gray -Text "Alior Leasing", `
            " Spółka z ograniczoną odpowiedzialnością z siedzibą w Warszawie, ul. Łopuszańska 38D, 02-232 Warszawa, wpisana do rejestru przedsiębiorców Krajowego Rejestru Sądowego pod numerem KRS: 0000554171, której dokumentacja przechowywana jest w Sądzie Rejonowym dla M. St. Warszawy w Warszawie, XIV Wydział Gospodarczy Krajowego Rejestru Sądowego, NIP: 5223027866, REGON: 361335353, o kapitale zakładowym i wpłaconym: 15 009 000,00 PLN."
        EmailText -LineBreak
    }
 
    Save-HTML -FilePath $PSScriptRoot\Output\TestBody2.html -ShowHTML -HTML $Test
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $false)][string]$FilePath,
        [Parameter(Mandatory = $true)][string] $HTML,
        [alias('Show', 'Open')][Parameter(Mandatory = $false)][switch]$ShowHTML,
        [ValidateSet('Unknown', 'String', 'Unicode', 'Byte', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'Ascii', 'Default', 'Oem', 'BigEndianUTF32')] $Encoding = 'UTF8',
        [alias('Supress')][bool] $Suppress = $true,
        [switch] $Format,
        [switch] $Minify
    )
    if ([string]::IsNullOrEmpty($FilePath)) {
        $FilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).html")
        Write-Verbose "Save-HTML - FilePath parameter is empty, using Temporary $FilePath"
    } else {
        if (Test-Path -LiteralPath $FilePath) {
            Write-Verbose "Save-HTML - Path $FilePath already exists. Report will be overwritten."
        }
    }
    Write-Verbose "Save-HTML - Saving HTML to file $FilePath"

    if ($Format -or $Minify) {
        $Commands = Get-Command -Name 'Format-HTML' -ErrorAction SilentlyContinue
        if ($Commands -and $Commands.Source -eq 'PSParseHTML') {
            if ($Format) {
                $HTML = Format-HTML -Content $HTML
            }
            if ($Minify) {
                $HTML = Optimize-HTML -Content $HTML
            }
        } else {
            Write-Warning "Save-HTML - Minify or Format functionality requires PSParseHTML module. Please install it using Install-Module PSParseHTML -Force."
        }
    }
    try {
        $HTML | Set-Content -LiteralPath $FilePath -Force -Encoding $Encoding -ErrorAction Stop
        if (-not $Suppress) {
            $FilePath
        }
    } catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        $FilePath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).html")
        Write-Warning "Save-HTML - Failed with error: $ErrorMessage"
        Write-Warning "Save-HTML - Saving HTML to file $FilePath"
        try {
            $HTML | Set-Content -LiteralPath $FilePath -Force -Encoding $Encoding -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            Write-Warning "Save-HTML - Failed with error: $ErrorMessage`nPlease define a different path for the `'-FilePath`' parameter."
        }
    }
    if ($ShowHTML) {
        try {
            Invoke-Item -LiteralPath $FilePath -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            Write-Verbose "Save-HTML - couldn't open file $FilePath in a browser. Error: $ErrorMessage"
        }
    }
}

Export-ModuleMember -Function @('Add-HTML', 'Add-HTMLScript', 'Add-HTMLStyle', 'ConvertTo-CascadingStyleSheets', 'Email', 'EmailAttachment', 'EmailBCC', 'EmailBody', 'EmailCC', 'EmailFrom', 'EmailHeader', 'EmailLayout', 'EmailLayoutColumn', 'EmailLayoutRow', 'EmailListItem', 'EmailOptions', 'EmailReplyTo', 'EmailServer', 'EmailSubject', 'EmailTo', 'Enable-HTMLFeature', 'New-AccordionItem', 'New-CalendarEvent', 'New-CarouselSlide', 'New-ChartAxisX', 'New-ChartAxisY', 'New-ChartBar', 'New-ChartBarOptions', 'New-ChartDataLabel', 'New-ChartDesign', 'New-ChartDonut', 'New-ChartEvent', 'New-ChartGrid', 'New-ChartLegend', 'New-ChartLine', 'New-ChartMarker', 'New-ChartPie', 'New-ChartRadial', 'New-ChartRadialOptions', 'New-ChartSpark', 'New-ChartTheme', 'New-ChartTimeLine', 'New-ChartToolbar', 'New-ChartToolTip', 'New-DiagramEvent', 'New-DiagramLink', 'New-DiagramNode', 'New-DiagramOptionsInteraction', 'New-DiagramOptionsLayout', 'New-DiagramOptionsLinks', 'New-DiagramOptionsManipulation', 'New-DiagramOptionsNodes', 'New-DiagramOptionsPhysics', 'New-GageSector', 'New-HierarchicalTreeNode', 'New-HTML', 'New-HTMLAccordion', 'New-HTMLAnchor', 'New-HTMLCalendar', 'New-HTMLCarousel', 'New-HTMLCarouselStyle', 'New-HTMLChart', 'New-HTMLCodeBlock', 'New-HTMLContainer', 'New-HTMLDiagram', 'New-HTMLFontIcon', 'New-HTMLFooter', 'New-HTMLFrame', 'New-HTMLGage', 'New-HTMLHeader', 'New-HTMLHeading', 'New-HTMLHierarchicalTree', 'New-HTMLHorizontalLine', 'New-HTMLImage', 'New-HTMLList', 'New-HTMLListItem', 'New-HTMLLogo', 'New-HTMLMain', 'New-HTMLMap', 'New-HTMLMarkdown', 'New-HTMLMermeidChart', 'New-HTMLNav', 'New-HTMLNavFloat', 'New-HTMLNavTop', 'New-HTMLOrgChart', 'New-HTMLPage', 'New-HTMLPanel', 'New-HTMLPanelStyle', 'New-HTMLQRCode', 'New-HTMLSection', 'New-HTMLSectionScrolling', 'New-HTMLSectionScrollingItem', 'New-HTMLSectionStyle', 'New-HTMLSpanStyle', 'New-HTMLStatus', 'New-HTMLStatusItem', 'New-HTMLSummary', 'New-HTMLSummaryItem', 'New-HTMLSummaryItemData', 'New-HTMLTab', 'New-HTMLTable', 'New-HTMLTableOption', 'New-HTMLTableStyle', 'New-HTMLTabPanel', 'New-HTMLTabPanelColor', 'New-HTMLTabStyle', 'New-HTMLTag', 'New-HTMLText', 'New-HTMLTextBox', 'New-HTMLTimeline', 'New-HTMLTimelineItem', 'New-HTMLToast', 'New-HTMLTree', 'New-HTMLTreeChildCounter', 'New-HTMLTreeNode', 'New-HTMLWinBox', 'New-HTMLWizard', 'New-HTMLWizardColor', 'New-HTMLWizardStep', 'New-MapArea', 'New-MapLegendOption', 'New-MapLegendSlice', 'New-MapPlot', 'New-NavFloatWidget', 'New-NavFloatWidgetItem', 'New-NavItem', 'New-NavLink', 'New-NavTopMenu', 'New-OrgChartNode', 'New-TableAlphabetSearch', 'New-TableButtonColumnVisibility', 'New-TableButtonCopy', 'New-TableButtonCSV', 'New-TableButtonExcel', 'New-TableButtonPageLength', 'New-TableButtonPDF', 'New-TableButtonPrint', 'New-TableButtonSearchBuilder', 'New-TableColumnOption', 'New-TableCondition', 'New-TableConditionGroup', 'New-TableContent', 'New-TableEvent', 'New-TableHeader', 'New-TableLanguage', 'New-TablePercentageBar', 'New-TablePercentageBarCondition', 'New-TableReplace', 'New-TableRowGrouping', 'Out-HtmlView', 'Save-HTML') -Alias @('Add-CSS', 'Add-JavaScript', 'Add-JS', 'Calendar', 'CalendarEvent', 'Chart', 'ChartAxisX', 'ChartAxisY', 'ChartBar', 'ChartBarOptions', 'ChartCategory', 'ChartDonut', 'ChartGrid', 'ChartLegend', 'ChartLine', 'ChartPie', 'ChartRadial', 'ChartSpark', 'ChartTheme', 'ChartTimeLine', 'ChartToolbar', 'Container', 'ConvertTo-CSS', 'Dashboard', 'Diagram', 'DiagramEdge', 'DiagramEdges', 'DiagramLink', 'DiagramNode', 'DiagramOptionsEdges', 'DiagramOptionsInteraction', 'DiagramOptionsLayout', 'DiagramOptionsLinks', 'DiagramOptionsManipulation', 'DiagramOptionsNodes', 'DiagramOptionsPhysics', 'EmailHTML', 'EmailImage', 'EmailList', 'EmailTable', 'EmailTableButtonCopy', 'EmailTableButtonCSV', 'EmailTableButtonExcel', 'EmailTableButtonPageLength', 'EmailTableButtonPDF', 'EmailTableButtonPrint', 'EmailTableButtonSearchBuilder', 'EmailTableColumnOption', 'EmailTableCondition', 'EmailTableConditionGroup', 'EmailTableContent', 'EmailTableHeader', 'EmailTableOption', 'EmailTableReplace', 'EmailTableRowGrouping', 'EmailTableStyle', 'EmailText', 'EmailTextBox', 'Footer', 'Header', 'HierarchicalTreeNode', 'HTMLText', 'Image', 'Main', 'New-ChartCategory', 'New-ChartFill', 'New-Diagram', 'New-DiagramEdge', 'New-DiagramOptionsEdges', 'New-HTMLColumn', 'New-HTMLContent', 'New-HTMLLink', 'New-HTMLMermeid', 'New-HTMLNavItem', 'New-HTMLNavLink', 'New-HTMLNavTopMenu', 'New-HTMLPanelOption', 'New-HTMLSectionOption', 'New-HTMLSectionOptions', 'New-HTMLTableAlphabetSearch', 'New-HTMLTableButtonColumnVisibility', 'New-HTMLTableButtonCopy', 'New-HTMLTableButtonCSV', 'New-HTMLTableButtonExcel', 'New-HTMLTableButtonPageLength', 'New-HTMLTableButtonPDF', 'New-HTMLTableButtonPrint', 'New-HTMLTableButtonSearchBuilder', 'New-HTMLTableColumnOption', 'New-HTMLTableCondition', 'New-HTMLTableConditionGroup', 'New-HTMLTableContent', 'New-HTMLTableHeader', 'New-HTMLTablePercentageBar', 'New-HTMLTableReplace', 'New-HTMLTableRowGrouping', 'New-HTMLTabOption', 'New-HTMLTabOptions', 'New-JavaScript', 'New-PanelOption', 'New-PanelStyle', 'New-TableOption', 'New-TableStyle', 'New-TabOption', 'ohv', 'Out-GridHtml', 'Panel', 'PanelOption', 'PanelStyle', 'Section', 'SectionOption', 'Tab', 'Table', 'TableAlphabetSearch', 'TableButtonColumnVisibility', 'TableButtonCopy', 'TableButtonCSV', 'TableButtonExcel', 'TableButtonPageLength', 'TableButtonPDF', 'TableButtonPrint', 'TableButtonSearchBuilder', 'TableColumnOption', 'TableCondition', 'TableConditionalFormatting', 'TableConditionGroup', 'TableContent', 'TableHeader', 'TableOption', 'TablePercentageBar', 'TableReplace', 'TableRowGrouping', 'TableStyle', 'TabOption', 'TabOptions', 'TabStyle', 'Text')
# SIG # Begin signature block
# MIItqwYJKoZIhvcNAQcCoIItnDCCLZgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBvMkbRg9f1Leop
# 7LMpFJGh9yMjpMOQAJgP1O8ZcBDeLqCCJq4wggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggWQMIIDeKADAgECAhAFmxtXno4hMuI5B72nd3VcMA0GCSqG
# SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIw
# aTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLK
# EdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4Tm
# dDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembu
# d8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnD
# eMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1
# XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVld
# QnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTS
# YW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSm
# M9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzT
# QRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kx
# fgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
# VR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzANBgkq
# hkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNkaA9Wz3eucPn9mkqZucl4
# XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjSPMFDQK4dUPVS/JA7u5iZ
# aWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK7VB6fWIhCoDIc2bRoAVg
# X+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eBcg3AFDLvMFkuruBx8lbk
# apdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp5aPNoiBB19GcZNnqJqGL
# FNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msgdDDS4Dk0EIUhFQEI6FUy
# 3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vriRbgjU2wGb2dVf0a1TD9u
# KFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ79ARj6e/CVABRoIoqyc54
# zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5nLGbsQAe79APT0JsyQq8
# 7kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3i0objwG2J5VT6LaJbVu8
# aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0HEEcRrYc9B9F1vM/zZn4w
# ggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1
# c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqG
# SIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbS
# g9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9
# /UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXn
# HwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0
# VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4f
# sbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40Nj
# gHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0
# QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvv
# mz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T
# /jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk
# 42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5r
# mQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
# FgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5n
# P+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcG
# CCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
# Y29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
# aUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8v
# Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNV
# HSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIB
# AH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxp
# wc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIl
# zpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQ
# cAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfe
# Kuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+j
# Sbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJsh
# IUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6
# OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDw
# N7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR
# 81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2
# VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGsDCCBJigAwIBAgIQ
# CK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjEwNDI5MDAw
# MDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zrPYGXcMW7xIUmMJ+k
# jmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHMgQM+TXAkZLON4gh9
# NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8IrgnQnAZaf6mIBJNYc9
# URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyCEUhSaN4QvRRXXegY
# E2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0p6MDDnSlrzm2q2AS
# 4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQakhCBj7A7CdfHmzJa
# wv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0XLyTRSiDNipmKF+w
# c86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960IHnWmZcy740hQ83eR
# Gv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2FKZbS110YU0/EpF2
# 3r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBHX8mBUHOFECMhWWCK
# ZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q27IwyCQLMbDwMVhEC
# AwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGg34Ou2
# O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB3BggrBgEFBQcB
# AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr
# BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwHAYDVR0gBBUwEzAH
# BgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIBADojRD2NCHbuj7w6
# mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6jfCbVN7w6XUhtldU/
# SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmImoqKwba9oUgYftzY
# gBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtfJqGVWEjVGv7XJz/9
# kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrxoj7bQ7gzyE84FJKZ
# 9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3LIU/Gs4m6Ri+kAew
# Q3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx4b6cpwoG1iZnt5Lm
# Tl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9Oj9FpsToFpFSi0HA
# SIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+ICw2/O/TOHnuO77Xr
# y7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug0wcCampAMEhLNKhR
# ILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5Vzu0nAPthkX0tGFu
# v2jiJmCG6sivqf6UHedjGzqGVnhOMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnp
# BOMzBDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5
# NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEy
# NTIzNTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYD
# VQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQAD
# ggIPADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBD
# Er4IxHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo
# 76EO7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rO
# H3bpLEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9R
# eNZ8hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgX
# j3o5WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTV
# DSupWJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16J
# idj5XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/C
# acBqU0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93N
# Rxvd1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1X
# CB+1rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMB
# AAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1s
# BwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9X
# LAN3DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
# U3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZU
# aW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQS
# R9lDkfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWB
# b0HvqT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDC
# zFzUy34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1
# UruJKlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3Wp
# ByXtgVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGE
# sshJmLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8
# a1u7cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNF
# YagLDBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7Q
# EY7MhKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgE
# deoHNHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/J
# ceENc2Sg8h3KeFUCS7tpFk7CrDqkMIIHXzCCBUegAwIBAgIQB8JSdCgUotar/iTq
# F+XdLjANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTIzMDQxNjAwMDAwMFoX
# DTI2MDcwNjIzNTk1OVowZzELMAkGA1UEBhMCUEwxEjAQBgNVBAcMCU1pa2/FgsOz
# dzEhMB8GA1UECgwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMSEwHwYDVQQDDBhQ
# cnplbXlzxYJhdyBLxYJ5cyBFVk9URUMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQCUmgeXMQtIaKaSkKvbAt8GFZJ1ywOH8SwxlTus4McyrWmVOrRBVRQA
# 8ApF9FaeobwmkZxvkxQTFLHKm+8knwomEUslca8CqSOI0YwELv5EwTVEh0C/Daeh
# vxo6tkmNPF9/SP1KC3c0l1vO+M7vdNVGKQIQrhxq7EG0iezBZOAiukNdGVXRYOLn
# 47V3qL5PwG/ou2alJ/vifIDad81qFb+QkUh02Jo24SMjWdKDytdrMXi0235CN4Rr
# W+8gjfRJ+fKKjgMImbuceCsi9Iv1a66bUc9anAemObT4mF5U/yQBgAuAo3+jVB8w
# iUd87kUQO0zJCF8vq2YrVOz8OJmMX8ggIsEEUZ3CZKD0hVc3dm7cWSAw8/FNzGNP
# lAaIxzXX9qeD0EgaCLRkItA3t3eQW+IAXyS/9ZnnpFUoDvQGbK+Q4/bP0ib98XLf
# QpxVGRu0cCV0Ng77DIkRF+IyR1PcwVAq+OzVU3vKeo25v/rntiXCmCxiW4oHYO28
# eSQ/eIAcnii+3uKDNZrI15P7VxDrkUIc6FtiSvOhwc3AzY+vEfivUkFKRqwvSSr4
# fCrrkk7z2Qe72Zwlw2EDRVHyy0fUVGO9QMuh6E3RwnJL96ip0alcmhKABGoIqSW0
# 5nXdCUbkXmhPCTT5naQDuZ1UkAXbZPShKjbPwzdXP2b8I9nQ89VSgQIDAQABo4IC
# AzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYE
# FHrxaiVZuDJxxEk15bLoMuFI5233MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
# BggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEz
# ODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j
# cmwwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3
# dy5kaWdpY2VydC5jb20vQ1BTMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEB
# CwUAA4ICAQC3EeHXUPhpe31K2DL43Hfh6qkvBHyR1RlD9lVIklcRCR50ZHzoWs6E
# BlTFyohvkpclVCuRdQW33tS6vtKPOucpDDv4wsA+6zkJYI8fHouW6Tqa1W47YSrc
# 5AOShIcJ9+NpNbKNGih3doSlcio2mUKCX5I/ZrzJBkQpJ0kYha/pUST2CbE3JroJ
# f2vQWGUiI+J3LdiPNHmhO1l+zaQkSxv0cVDETMfQGZKKRVESZ6Fg61b0djvQSx51
# 0MdbxtKMjvS3ZtAytqnQHk1ipP+Rg+M5lFHrSkUlnpGa+f3nuQhxDb7N9E8hUVev
# xALTrFifg8zhslVRH5/Df/CxlMKXC7op30/AyQsOQxHW1uNx3tG1DMgizpwBasrx
# h6wa7iaA+Lp07q1I92eLhrYbtw3xC2vNIGdMdN7nd76yMIjdYnAn7r38wwtaJ3KY
# D0QTl77EB8u/5cCs3ShZdDdyg4K7NoJl8iEHrbqtooAHOMLiJpiL2i9Yn8kQMB6/
# Q6RMO3IUPLuycB9o6DNiwQHf6Jt5oW7P09k5NxxBEmksxwNbmZvNQ65Zn3exUAKq
# G+x31Egz5IZ4U/jPzRalElEIpS0rgrVg8R8pEOhd95mEzp5WERKFyXhe6nB6bSYH
# v8clLAV0iMku308rpfjMiQkqS3LLzfUJ5OHqtKKQNMLxz9z185UCszGCBlMwggZP
# AgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEw
# PwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2
# IFNIQTM4NCAyMDIxIENBMQIQB8JSdCgUotar/iTqF+XdLjANBglghkgBZQMEAgEF
# AKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgor
# BgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3
# DQEJBDEiBCCxqiBzlhj88zHGaafkVFk872k24sB7ddBLg8XZT2lhODANBgkqhkiG
# 9w0BAQEFAASCAgAJkhvCxB1l3eck+t5fD6sfHgTPPjh5B2Pd3Trfk1zs0UiJh4BN
# +dQpoTgLyZn7qAxhJag89IP7UhEB5cA4qqmOE9dFqBUzAYWjpLNoRXTBOjRWLZeS
# MMc3gvQSBwp00UA4SbialDyl/jRBD5pN+YLfSai39TbnfLso1uFEfMkfz4lgIGwR
# crM2PdkjZsW9pJSsQ8zWihgzm5B5mmSQF7DJBk8MxHjyDkJqdKdbGo+G9G0r20bI
# 860anxEZiuWMwOC5ASZhPR1Be+MHL3LqONuKeWcU3qp+JRbgVuRMaR00LXk3Ljn6
# MD9bJ6o/XiXmAsMOOybzK7/J2fr/C03WJfHmPzyBUe9BKLAWHH0ubRTveKSpA0wh
# oApWoDBkgll0Oxl/FJqSqFXxJ4sTdocT+3sLGONGOY/nso8D0AUzZD/dXUkjemat
# Nf2byXu48eF5G3Ch48hqcBFIMfCZzBhFc18RydHmykVjkM02uRbDWvH62hzfAZrj
# 7DhOWUmjL/h93aFvQa9KLx9wny4hk1KlFCAfOYjmlPCaaAPTB9khTdohK1iwjDYA
# PNNSDC0eSUdwCPXBTORlNSjbX73XT7mjaQmsGvdvT5oGNqkysSaXvgdDqpfQoonr
# adBlCvCwwU24Qwdug0/oJHkEmoMChw1731NKUwZ4tE+i3Qoe/BTkhUmkE6GCAyAw
# ggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYD
# VQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBH
# NCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTj
# MwQwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwG
# CSqGSIb3DQEJBTEPFw0yNDEyMTcxOTQ5MTVaMC8GCSqGSIb3DQEJBDEiBCAwBKBC
# IxQVSpkej2Zuo+Qi61YVD2wpK41lXzkXSNuT+zANBgkqhkiG9w0BAQEFAASCAgBQ
# uBUwRf5Ra+9UaipTHHdAivCQgwmxXPJdHjc83u6d8mPfUME9jJtbMx5bzAeVZdaK
# k5xHEmX+Wrzv3FDRGk1DfXJov9juX/YIFKH2CN6wez65F5lgbydg/8hWyqJySdv+
# sIxdO/Um8l669+3dPRKsO5Bxwu7CckavGQoEiRb/7DHIDQdTagvUX8IQSP2DzSGA
# knZCh63XoeCkWA1ctySS7GgOMvLvYLocribzkiCVAPGBssPHzN+adlVpUNBJDqiw
# MMe2smdn1BB0qOryk9y2ICgbc4VClaR/x0NZNBmFzVNdYiULLiZOLZqivwGDEaYT
# 6sWz0fkktceL+W6CkteCmt33j4KQeG2ixcZFjwLP0mPv2M5V7lUdYgE8ifgkKqAZ
# DXxnwkik9D1DZ0fzhTSNgduvOrlzcCrS1Z2n+7j09u3T4Zwib06oe+eiM7nkVpOF
# fD4W0+Q5rYFXnxVMbC8ZDvS86bBwCS0H/rwvHBli51LIY5Uwd9pM3W9oOQd+9V7L
# CwhBWLSuOH08MrfC1BNI1ImHAtkavK1B/CzlIIMTp+fExWxtm1n3N/xl4395T5sJ
# sKyuQLjbhstK5mba6CGzjITaRZTZXcr/Rd+vw9esvhLFybTfKHlHVCUZu3eOdvx/
# sp1Iy85oxeAd7o6k+kV/LKnHUXucbti6zBKHHtd6dw==
# SIG # End signature block