PSWriteHTML.psm1

function Compare-MultipleObjects { 
    [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 { 
    [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
                    } 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, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                            $NewObject[$DisplayProperty] = "$Value"
                        } else {

                            $Value = "$Value".Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.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, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.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
                    } 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, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage)
                            $NewObject[$DisplayProperty] = "$Value"
                        } else {

                            $Value = "$Value".Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.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, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.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 { 
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][System.Collections.ICollection] $Object,
        [ValidateSet("ASC", "DESC", "NONE")][String] $Sort = 'NONE'
    )
    process {
        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 { 
    [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 { 
    [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 { 
    [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 { 
    [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 { 
    [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 { 
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [int] $MarkerSize
    )
    if ($MarkerSize -gt 0) {
        $Options.markers = @{
            size = $MarkerSize
        }
    }
}
function New-ChartInternalPattern {
    [CmdletBinding()]
    param(

    )
    $Options.fill = [ordered]@{
        type    = 'pattern'
        opacity = 1
        pattern = [ordered]@{
            style = @('circles', 'slantedLines', 'verticalLines', 'horizontalLines')
        }
    }
}
function New-ChartInternalPie {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [switch] $Enabled
    )
    if ($Enabled) {
        $Options.chart.zoom = @{
            type    = 'x'
            enabled = $Enabled.IsPresent
        }
    }
}
function New-HTMLChartArea {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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
 
    .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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [CmdletBinding()]
    param(
        [string] $Image,
        [switch] $Cache
    )

    $ImageFile = Get-ImageFile -Image $Image -Cache:$Cache
    if ($ImageFile) {
        Convert-ImageToBinary -ImageFile $ImageFile
    }
}
function Convert-ImagesToBinary {
    [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 {
    [CmdletBinding()]
    param(
        [System.IO.FileInfo] $ImageFile
    )

    if ($ImageFile.Extension -eq '.jpg') {
        $FileType = 'jpeg'
    } 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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [CmdLetBinding()]
    param(
        [string] $Content,
        [string] $FileType
    )
    if ($Content) {
        $Replace = "data:image/$FileType;charset=utf-8," + [uri]::EscapeDataString($Content)
        $Replace
    }
}
function Get-ConfigurationCSS {
    [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 {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        [string]$LeftLogoName = "Sample",
        [string]$RightLogoName = "Alternate",
        [string]$LeftLogoString,
        [string]$RightLogoString

    )
    $LogoSources = [ordered] @{ }
    $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 {
    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 {
    [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
                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 {
    [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 {
    [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"
                }
            }
            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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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
    )
    $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

    if ($Options.physics -and $Options.physics.enabled -eq $false) {
        $DisableLoader = $true
    }
    if (-not $DisableLoader) {
        $Script:HTMLSchema.Features.VisNetworkLoadingBar = $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 {
                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 {
            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 ($_ in $Events) {
                New-DiagramInternalEvent -ID $_.ID -ColumnID $_.ColumnID
            }
            '});'
        )
    }
    if ($DisableLoader) {
        $LoadingBarEvent = ''
    } else {
        $LoadingBarEvent = @"
            network.on("stabilizationProgress", function (params) {
                var maxWidth = 496;
                var minWidth = 20;
                var widthFactor = params.iterations / params.total;
                var width = Math.max(minWidth, maxWidth * widthFactor);
 
                document.getElementById("$ID-diagramBar").style.width = width + "px";
                document.getElementById("$ID-diagramText").innerHTML = Math.round(widthFactor * 100) + "%";
            });
            network.once("stabilizationIterationsDone", function () {
                document.getElementById("$ID-diagramText").innerHTML = "100%";
                document.getElementById("$ID-diagramBar").style.width = "496px";
                document.getElementById("$ID-diagramLoadingBar").style.opacity = 0;
                // really clean the dom element
                setTimeout(function () {
                    document.getElementById("$ID-diagramLoadingBar").style.display = "none";
                }, 500);
            });
            //window.addEventListener("load", () => {
            // draw();
            //});
"@

    }

    $FunctionInclude = @"
    function loadDiagram(container, data, options) {
        var network = new vis.Network(container, data, options);
        $PreparedEvents
        $LoadingBarEvent
    }
"@


    $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"

    } -NewLine

    $Div
    $Script:HTMLSchema.Diagrams.Add($Script)
}
function New-InternalNavIcon {
    [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 {
    [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 {
    [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 {
    [cmdletBinding()]
    param(
        [string[]] $HeaderNames,
        [System.Collections.IDictionary] $Options
    )
    $Options['data'] = "markerForDataReplacement"
    [Array] $Options['columns'] = foreach ($Property in $HeaderNames) {
        @{ data = $Property.Replace('.', '\.') }
    }
    $Options['deferRender'] = $true
}
function New-TablePercentageBarInternal {
    [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 {
    [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 {
    [CmdletBinding()]
    param(

    )
    $ConfigurationURL = 'https://cdn.jsdelivr.net/gh/evotecit/cdn@0.0.22'
    $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] @{

                        '@media all and (-ms-high-contrast:active)' = @{
                            '.defaultSection' = @{
                                'display' = 'flex'
                            }
                        }
                        '.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/promise-polyfill@8/dist/polyfill.min.js' 
                        'https://cdn.jsdelivr.net/npm/eligrey-classlist-js-polyfill' 
                        'https://cdn.jsdelivr.net/npm/findindex_polyfill_mdn' 

                        'https://cdn.jsdelivr.net/npm/apexcharts@3.41.0/dist/apexcharts.min.js'
                    )
                    JS     = @(
                        "$PSScriptRoot\Resources\JS\polyfill.min.js"
                        "$PSScriptRoot\Resources\JS\classList.min.js"
                        "$PSScriptRoot\Resources\JS\findIndex.min.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.6/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.6/peer/umd/vis-network.min.js'
                    Js      = "$PSScriptRoot\Resources\JS\vis-network.min.js"
                    CssLink = 'https://cdn.jsdelivr.net/npm/vis-network@9.1.6/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-networkFunctions.min.js"
                    JS     = "$PSScriptRoot\Resources\JS\vis-networkFunctions.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-networkLoadDiagram.min.js"
                    JS     = "$PSScriptRoot\Resources\JS\vis-networkLoadDiagram.js"
                }
                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.2/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.2/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 {
    [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
    Remove dot from .class
 
    .DESCRIPTION
    Long description
 
    .PARAMETER Css
    Parameter description
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [System.Collections.IDictionary] $Css
    )
    foreach ($Key in [string[]] $Css.Keys) {
        $Css[$Key] = $Css[$Key].TrimStart('.')
    }
}
function Rename-Dictionary {
    [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 {
    [cmdletBinding()]
    param(
        [string] $Feature,
        [string] $Type,
        [System.Collections.IDictionary] $CSS
    )
    $Script:CurrentConfiguration.Features.$Feature.$Type.CssInline = $CSS
}
function Set-Tag {
    [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 {
    [alias('EmailHTML')]
    [CmdletBinding()]
    param(
        [ScriptBlock] $HTML
    )
    Invoke-Command -ScriptBlock $HTML
}
function Add-HTMLScript {
    [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 {
    [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 {
    [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 {
    [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 {
    [CmdletBinding()]
    param(
        [string[]] $FilePath
    )
    [PSCustomObject] @{
        Type     = 'HeaderAttachment'
        FilePath = $FilePath
    }
}
function EmailBCC {
    [CmdletBinding()]
    param(
        [string[]] $Addresses
    )

    [PsCustomObject] @{
        Type      = 'HeaderBCC'
        Addresses = $Addresses
    }
}
function EmailBody {
    [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) {
                , $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 {
    [CmdletBinding()]
    param(
        [string[]] $Addresses
    )

    [PsCustomObject] @{
        Type      = 'HeaderCC'
        Addresses = $Addresses
    }
}
function EmailFrom {
    [CmdletBinding()]
    param(
        [string] $Address
    )

    [PsCustomObject] @{
        Type    = 'HeaderFrom'
        Address = $Address
    }
}
function EmailHeader {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $EmailHeader
    )
    $EmailHeaders = Invoke-Command -ScriptBlock $EmailHeader
    $EmailHeaders
}
function EmailLayout {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [CmdletBinding()]
    param(
        [string] $Address
    )

    [PsCustomObject] @{
        Type    = 'HeaderReplyTo'
        Address = $Address
    }
}
function EmailServer {
    [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 {
    [CmdletBinding()]
    param(
        [string] $Subject
    )

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

function EmailTo {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [alias('ChartDonut')]
    [CmdletBinding()]
    param(
        [string] $Name,
        [object] $Value,
        [string] $Color
    )

    [PSCustomObject] @{
        ObjectType = 'Donut'
        Name       = $Name
        Value      = $Value
        Color      = $Color
    }
}
Register-ArgumentCompleter -CommandName New-ChartDonut -ParameterName Color -ScriptBlock $Script:ScriptBlockColors
function New-ChartEvent {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER Size
    Size of the marker point.
 
    .PARAMETER Color
    Sets the fill color(s) of the marker point.
 
    .PARAMETER StrokeColors
    Stroke Color of the marker. Accepts a single color or an array of colors in a multi-series chart.
 
    .PARAMETER StrokeWidth
    Stroke Size of the marker.
 
    .PARAMETER StrokeOpacity
    Opacity of the border around marker.
 
    .PARAMETER StrokeDashArray
    Dashes in the border around marker. Higher number creates more space between dashes in the border.
 
    .PARAMETER FillOpacity
    Opacity of the marker fill color.
 
    .PARAMETER Shape
    Shape of the marker. Available Options for shape circle or square
 
    .PARAMETER Radius
    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
    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
    Fixed size of the marker when it is active
 
    .PARAMETER HoverSizeOffset
    Unlike the fixed size, this option takes the original markers.size and increases/decreases the value based on it.
    So, if markers.size: 6, markers.hover.sizeOffset: 3 will make the marker’s size 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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER From
    Parameter description
 
    .PARAMETER To
    Parameter description
 
    .PARAMETER Label
    Parameter description
 
    .PARAMETER ArrowsToEnabled
    Parameter description
 
    .PARAMETER ArrowsToScaleFacto
    Parameter description
 
    .PARAMETER ArrowsToType
    Parameter description
 
    .PARAMETER ArrowsMiddleEnabled
    Parameter description
 
    .PARAMETER ArrowsMiddleScaleFactor
    Parameter description
 
    .PARAMETER ArrowsMiddleType
    Parameter description
 
    .PARAMETER ArrowsFromEnabled
    Parameter description
 
    .PARAMETER ArrowsFromScaleFactor
    Parameter description
 
    .PARAMETER ArrowsFromType
    Parameter description
 
    .PARAMETER ArrowStrikethrough
    Parameter description
 
    .PARAMETER Chosen
    Parameter description
 
    .PARAMETER Color
    Parameter description
 
    .PARAMETER ColorHighlight
    Parameter description
 
    .PARAMETER ColorHover
    Parameter description
 
    .PARAMETER ColorInherit
    Parameter description
 
    .PARAMETER ColorOpacity
    Parameter description
 
    .PARAMETER Dashes
    Parameter description
 
    .PARAMETER Length
    Parameter description
 
    .PARAMETER FontColor
    Parameter description
 
    .PARAMETER FontSize
    Parameter description
 
    .PARAMETER FontName
    Parameter description
 
    .PARAMETER FontBackground
    Parameter description
 
    .PARAMETER FontStrokeWidth
    Parameter description
 
    .PARAMETER FontStrokeColor
    Parameter description
 
    .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
    Parameter description
 
    .PARAMETER FontVAdjust
    Parameter description
 
    .PARAMETER WidthConstraint
    Parameter description
 
    .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
 
    .NOTES
    General 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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER DragNodes
    Parameter description
 
    .PARAMETER DragView
    Parameter description
 
    .PARAMETER HideEdgesOnDrag
    Parameter description
 
    .PARAMETER HideEdgesOnZoom
    Parameter description
 
    .PARAMETER HideNodesOnDrag
    Parameter description
 
    .PARAMETER Hover
    Parameter description
 
    .PARAMETER HoverConnectedEdges
    Parameter description
 
    .PARAMETER KeyboardEnabled
    Parameter description
 
    .PARAMETER KeyboardSpeedX
    Parameter description
 
    .PARAMETER KeyboardSpeedY
    Parameter description
 
    .PARAMETER KeyboardSpeedZoom
    Parameter description
 
    .PARAMETER KeyboardBindToWindow
    Parameter description
 
    .PARAMETER Multiselect
    Parameter description
 
    .PARAMETER NavigationButtons
    Parameter description
 
    .PARAMETER Selectable
    Parameter description
 
    .PARAMETER SelectConnectedEdges
    Parameter description
 
    .PARAMETER TooltipDelay
    Parameter description
 
    .PARAMETER ZoomView
    Parameter description
 
    .EXAMPLE
    An example
 
    .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
    Short description
 
    .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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER InitiallyActive
    Parameter description
 
    .PARAMETER AddNode
    Parameter description
 
    .PARAMETER AddEdge
    Parameter description
 
    .PARAMETER EditNode
    Parameter description
 
    .PARAMETER EditEdge
    Parameter description
 
    .PARAMETER DeleteNode
    Parameter description
 
    .PARAMETER DeleteEdge
    Parameter description
 
    .EXAMPLE
    An example
 
    .NOTES
    Based on https://visjs.github.io/vis-network/docs/network/manipulation.html#
    It's incomplete
 
    #>


    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .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 {
    [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 {
    [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
    )
    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
        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

                            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 = @(

            '<!-- saved from url=(0014)about:internet -->' + [System.Environment]::NewLine
            '<!DOCTYPE html>' + [System.Environment]::NewLine
            New-HTMLTag -Tag 'html' {
                if ($AddComment) {
                    '<!-- HEAD -->' }
                New-HTMLTag -Tag 'head' {
                    New-HTMLTag -Tag 'meta' -Attributes @{ 'http-equiv' = "Content-Type"; content = "text/html; 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' { $TitleText }

                    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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER Name
    Parameter description
 
    .PARAMETER Id
    Parameter description
 
    .PARAMETER Target
    Parameter description
 
    .PARAMETER Class
    Parameter description
 
    .PARAMETER HrefLink
    Parameter description
 
    .PARAMETER OnClick
    Parameter description
 
    .PARAMETER Style
    Parameter description
 
    .PARAMETER Text
    Parameter description
 
    .EXAMPLE
    New-HTMLAnchor -Target _parent
 
    New-HTMLAnchor -Id "show_$RandomNumber" -Href '#' -OnClick "show('$RandomNumber');" -Style "color: #ffffff; display:none;" -Text 'Show'
 
    Output:
    <a target = "_parent" />
 
    .NOTES
    General notes
    #>

    [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 {
    [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
    )
    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 

        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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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
        [switch] $DisableLoader
    )
    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
    }

    New-InternalDiagram -Nodes $Nodes -Edges $Edges -Options $Options -Width $Width -Height $Height -BackgroundImage $Image -Events $DataEvents -IconsAvailable:$IconsAvailable -DisableLoader:$DisableLoader
}

function New-HTMLFontIcon {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [CmdletBinding()]
    param(
        [String] $LogoPath,
        [string] $LeftLogoName = "Sample",
        [string] $RightLogoName = "Alternate",
        [string] $LeftLogoString,
        [string] $RightLogoString,
        [switch] $HideLogos
    )
    $Script:HTMLSchema.Features.MainImage = $true

    $LogoSources = Get-HTMLLogos -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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER ChartNodes
    Define nodes to be shown on the chart
 
    .PARAMETER Direction
    The available values are "top to bottom" (default value), "bottom to top", "left to right" and "right to left"
 
    .PARAMETER VisileLevel
    It indicates the level that at the very beginning orgchart is expanded to.
 
    .PARAMETER VerticalLevel
    Users can make use of this option to align the nodes vertically from the specified level.
 
    .PARAMETER ToggleSiblings
    Once enable this option, users can show/hide left/right sibling nodes respectively by clicking left/right arrow.
 
    .PARAMETER NodeTitle
    It sets one property of datasource as text content of title section of orgchart node. In fact, users can create a simple orghcart with only nodeTitle option.
 
    .PARAMETER Pan
    Users could pan the orgchart by mouse drag&drop if they enable this option.
 
    .PARAMETER Zoom
    Users could zoomin/zoomout the orgchart by mouse wheel if they enable this option.
 
    .PARAMETER ZoomInLimit
    Users are allowed to set a zoom-in limit.
 
    .PARAMETER ZoomOutLimit
    Users are allowed to set a zoom-out limit.
 
    .PARAMETER Draggable
    Users can drag & drop the nodes of orgchart if they enable this option. **Note**: this feature doesn't work on IE due to its poor support for HTML5 drag & drop API.
 
    .PARAMETER AllowExport
    It enable the export button for orgchart.
 
    .PARAMETER ExportFileName
    It's filename when you export current orgchart as a picture.
 
    .PARAMETER ExportExtension
    Available values are png and pdf.
 
    .PARAMETER ChartID
    Forces ChartID to be set to known value rather than having it autogenerated
 
    .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
    General notes
    #>

    [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 {
    [cmdletBinding()]
    param(
        [scriptblock] $PageContent,
        [Parameter(Mandatory)][string] $Name,
        [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
            ID       = $ID
        }
    }
}
Function New-HTMLPanel {
    [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 {
    [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 {
    [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 {
    [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 {
    [cmdletBinding()]
    param(
        [scriptblock]$Content
    )

    Enable-HTMLFeature -Feature SectionScrolling

    if ($Content) {
        $ContentExecuted = & $Content

        $HTMLList = [System.Collections.Generic.List[string]]::new()
        $HTMLData = foreach ($C in $ContentExecuted) {
            if ($C -is [PSCustomObject] -and $C.Type -eq 'SectionScrollingItem') {
                $C.HTMLOutput
                $HTMLList.Add($C.ListEntry)
            }
        }
        New-HTMLTag -Tag 'div' {
            New-HTMLTag -Tag 'div' {
                $HTMLData
            }
            New-HTMLTag -Tag 'nav' {
                New-HTMLTag -Tag 'ol' {
                    $HTMLList
                }
            } -Attributes @{ class = 'section-nav' }
        } -Attributes @{ class = 'sectionScrolling' }
    }
}
function New-HTMLSectionScrollingItem {
    [cmdletBinding()]
    param(
        [scriptblock] $Content,
        [string] $SectionTitle
    )

    $Name = "scrolling-$(Get-RandomStringName -Size 8 -LettersOnly)"

    if (-not $Script:HTMLSectionScrollingItem) {
        $HTMLOutput = New-HTMLTag -Tag 'section' {
            New-HTMLTag -Tag 'h2' {
                $SectionTitle
            }
            New-HTMLTag -Tag 'div' {
                $Script:HTMLSectionScrollingItem = $true
                if ($Content) {
                    & $Content
                }
                $Script:HTMLSectionScrollingItem = $false
            }
        } -Attributes @{ 'id' = $Name }
        $Level = 0

        $ListEntry = New-HTMLTag -Tag 'li' {
            New-HTMLTag -Tag 'a' {
                $SectionTitle
            } -Attributes @{ href = "#$Name" }
        }
    } else {
        $Link
        New-HTMLTag -Tag 'section' {
            if ($Content) {
                & $Content
            }
            $Script:HTMLSectionScrollingItem = $false
        } -Attributes @{ 'id' = $Name }
        $Level = 1

        $ListEntry = New-HTMLTag -Tag 'li' {
            New-HTMLTag -Tag 'a' {
                $SectionTitle
            } -Attributes @{ href = "#$Name" }
            New-HTMLTag -Tag 'ul' {

            }
        }

    }
    [PSCustomObject] @{
        Type       = 'SectionScrollingItem'
        HTMLOutput = $HTMLOutput
        ListEntry  = $ListEntry
        Name       = $Name
        Level      = $Level
    }
}
function New-HTMLSectionStyle {
    [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 {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER SummaryItems
    Parameter description
 
    .PARAMETER Title
    Parameter description
 
    .PARAMETER TitleRight
    Parameter description
 
    .EXAMPLE
    An example
 
    .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 {
    [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 {
    [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 {
    [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 {
    [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] $OverwriteDOM,
        [switch] $SearchHighlight,
        [switch] $AlphabetSearch,
        [switch] $FuzzySearch,
        [switch] $FuzzySearchSmartToggle,
        [switch] $FlattenObject,
        [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 ($Transpose) {

        $DataTable = Format-TransposeTable -Object $DataTable
    }
    if ($FlattenObject) {
        if ($FlattenDepth) {
            $DataTable = ConvertTo-FlatObject -Objects $DataTable -Depth $FlattenDepth
        } else {
            $DataTable = ConvertTo-FlatObject -Objects $DataTable
        }
    }

    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 ($First -or $Last) {
        $DataTable = $DataTable | Select-Object -First $First -Last $Last
    }

    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 ($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

    }

    $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
                    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
                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]")
            }
        }
        $Options = $Options -replace '\\\\r', '\r' -replace "\\\\n", "\n"

        $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 = ', '
    )
    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 ($ArrayJoin) {
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoin = $true
            $Script:HTMLSchema['TableOptions']['DataStoreOptions'].ArrayJoinString = $ArrayJoinString
        }
    }
}
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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER HTML
    Parameter description
 
    .PARAMETER Title
    The window title.
 
    .PARAMETER BackgroundColor
    Set the background color for the 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
    Open URL inside the window (loaded via iframe).
 
    .PARAMETER Modal
    Shows the window as modal.
 
    .PARAMETER Maximize
    Automatically toggles the window into maximized state when created.
 
    .PARAMETER Theme
 
    .PARAMETER NoAnimation
    Disables the windows transition animation
 
    .PARAMETER NoShadow
    Disables the windows drop shadow
 
    .PARAMETER NoHeader
    Hide the window header incl. title and toolbar
 
    .PARAMETER NoMinmizeIcon
    Hide the minimize icon
 
    .PARAMETER NoMaximizeIcon
    Hide the maximize icon
 
    .PARAMETER NoFullScreenIcon
    Hide the fullscreen icon
 
    .PARAMETER NoCloseIcon
    Hide the close icon
 
    .PARAMETER NoResizeCapability
    Disables the window resizing capability
 
    .PARAMETER NoMoveCapability
    Disables the window moving capability
 
    .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
    General notes
    #>

    [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 {
    [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 {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER Label
    The label of the slice for the legend
 
    .PARAMETER FillColor
    Parameter description
 
    .PARAMETER MinimumValue
    The minimal value for the interval defining the slice
 
    .PARAMETER MaximumValue
    The maximal value for the interval defining the slice
 
    .PARAMETER Value
    The value for the slice. This option can be used instead of the 'min' and 'max' options in order to set a fixed value instead of an interval of values for the slice.
 
    .PARAMETER DisplayInLegend
    Display the slice in the legend (Boolean, default : true)
 
    .PARAMETER InitializeClicked
    Set to true in order to initialize the legend item in the 'clicked' state on the map load. (Boolean, default : false)
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER Children
    Define children of the node by specifying nested nodes (self-nesting)
 
    .PARAMETER Name
    Name of the node
 
    .PARAMETER Title
    Title of the node
 
    .PARAMETER ClassName
    Parameter description
 
    .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
    General notes
    #>

    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER Name
    Parameter description
 
    .PARAMETER HighlightHeaders
    By default Name parameter is used as column to be highlighted. In case you want to specify different header(s) to be highlighted you can use this parameter.
 
    .PARAMETER ComparisonType
    Parameter description
 
    .PARAMETER Operator
    Parameter description
 
    .PARAMETER Value
    Parameter description
 
    .PARAMETER Row
    Parameter description
 
    .PARAMETER Inline
    Parameter description
 
    .PARAMETER CaseSensitive
    Parameter description
 
    .PARAMETER DateTimeFormat
    Parameter description
 
    .PARAMETER ReverseCondition
    By default ColumnValue (left side) is being compared to Condition Value (right side). This switch reverses the comparison
 
    .PARAMETER Color
    Parameter description
 
    .PARAMETER BackgroundColor
    Parameter description
 
    .PARAMETER FontSize
    Parameter description
 
    .PARAMETER FontWeight
    Parameter description
 
    .PARAMETER FontStyle
    Parameter description
 
    .PARAMETER FontVariant
    Parameter description
 
    .PARAMETER FontFamily
    Parameter description
 
    .PARAMETER Alignment
    Parameter description
 
    .PARAMETER TextDecoration
    Parameter description
 
    .PARAMETER TextTransform
    Parameter description
 
    .PARAMETER Direction
    Parameter description
 
    .PARAMETER FailColor
    Parameter description
 
    .PARAMETER FailBackgroundColor
    Parameter description
 
    .PARAMETER FailFontSize
    Parameter description
 
    .PARAMETER FailFontWeight
    Parameter description
 
    .PARAMETER FailFontStyle
    Parameter description
 
    .PARAMETER FailFontVariant
    Parameter description
 
    .PARAMETER FailFontFamily
    Parameter description
 
    .PARAMETER FailAlignment
    Parameter description
 
    .PARAMETER FailTextDecoration
    Parameter description
 
    .PARAMETER FailTextTransform
    Parameter description
 
    .PARAMETER FailDirection
    Parameter description
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [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 {
    [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 {
    [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 {
    [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
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER ColumnID
    Parameter description
 
    .PARAMETER ColumnName
    Parameter description
 
    .PARAMETER Type
    Parameter description
 
    .PARAMETER TextColor
    Parameter description
 
    .PARAMETER BorderColor
    Parameter description
 
    .PARAMETER BorderStyle
    Parameter description
 
    .PARAMETER BarColor
    Parameter description
 
    .PARAMETER BackgroundColor
    Parameter description
 
    .PARAMETER RoundValue
    Parameter description
 
    .PARAMETER ConditionalFormatting
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [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 {
    [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 {
    [alias('TableReplace', 'EmailTableReplace', 'New-HTMLTableReplace')]
    [CmdletBinding()]
    param(
        [string] $FieldName,
        [string[]] $Replacements

    )
    [PSCustomObject]@{
        Type   = 'TableReplaceCompare'
        Output = @{
            $FieldName = $Replacements
        }
    }
}
function New-TableRowGrouping {
    [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,
        [switch] $PreventShowHTML,
        [switch] $Online,
        [string] $OverwriteDOM,
        [switch] $SearchHighlight,
        [switch] $AlphabetSearch,
        [switch] $FuzzySearch,
        [switch] $FuzzySearchSmartToggle,
        [switch] $FlattenObject
    )
    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
                    Transpose                    = $Transpose
                    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
                }
                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
# MIItsQYJKoZIhvcNAQcCoIItojCCLZ4CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDcu2oJuRgLTA9m
# RnFhbqNReKoMJJO9gLD2Jh37KrbYbaCCJrQwggWNMIIEdaADAgECAhAOmxiO+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
# v2jiJmCG6sivqf6UHedjGzqGVnhOMIIGwjCCBKqgAwIBAgIQBUSv85SdCDmmv9s/
# X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5
# NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0MTAx
# MzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZIhvcN
# AQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwvSKOX
# ejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsvlmbj
# aedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4ZGIX7
# ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RChG4PB
# uOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDaY7Mu
# 6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S769Sg
# LDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0rRUG
# FOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQfqZc
# ClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7UjipmAmh
# cbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1Pt7U2
# 0clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM604qD
# y0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAW
# BgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglg
# hkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0O
# BBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6
# Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEy
# NTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZT
# SEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa1t6g
# qbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1Ott5s
# 1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4JZ0q
# BXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvPnPp4
# 4pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3NG6w
# QSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7MmJ4Z
# iQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOGv6wn
# LEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5Adza
# ROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2nyMpqy
# 0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0eaHDA
# dwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9nR2Xl
# G3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMIIHXzCCBUegAwIBAgIQB8JSdCgU
# otar/iTqF+XdLjANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UE
# ChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQg
# Q29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTIzMDQxNjAw
# MDAwMFoXDTI2MDcwNjIzNTk1OVowZzELMAkGA1UEBhMCUEwxEjAQBgNVBAcMCU1p
# a2/FgsOzdzEhMB8GA1UECgwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMSEwHwYD
# VQQDDBhQcnplbXlzxYJhdyBLxYJ5cyBFVk9URUMwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCUmgeXMQtIaKaSkKvbAt8GFZJ1ywOH8SwxlTus4McyrWmV
# OrRBVRQA8ApF9FaeobwmkZxvkxQTFLHKm+8knwomEUslca8CqSOI0YwELv5EwTVE
# h0C/Daehvxo6tkmNPF9/SP1KC3c0l1vO+M7vdNVGKQIQrhxq7EG0iezBZOAiukNd
# GVXRYOLn47V3qL5PwG/ou2alJ/vifIDad81qFb+QkUh02Jo24SMjWdKDytdrMXi0
# 235CN4RrW+8gjfRJ+fKKjgMImbuceCsi9Iv1a66bUc9anAemObT4mF5U/yQBgAuA
# o3+jVB8wiUd87kUQO0zJCF8vq2YrVOz8OJmMX8ggIsEEUZ3CZKD0hVc3dm7cWSAw
# 8/FNzGNPlAaIxzXX9qeD0EgaCLRkItA3t3eQW+IAXyS/9ZnnpFUoDvQGbK+Q4/bP
# 0ib98XLfQpxVGRu0cCV0Ng77DIkRF+IyR1PcwVAq+OzVU3vKeo25v/rntiXCmCxi
# W4oHYO28eSQ/eIAcnii+3uKDNZrI15P7VxDrkUIc6FtiSvOhwc3AzY+vEfivUkFK
# RqwvSSr4fCrrkk7z2Qe72Zwlw2EDRVHyy0fUVGO9QMuh6E3RwnJL96ip0alcmhKA
# BGoIqSW05nXdCUbkXmhPCTT5naQDuZ1UkAXbZPShKjbPwzdXP2b8I9nQ89VSgQID
# AQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYD
# VR0OBBYEFHrxaiVZuDJxxEk15bLoMuFI5233MA4GA1UdDwEB/wQEAwIHgDATBgNV
# HSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3Js
# My5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQw
# OTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAy
# MUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEFBQcCARYbaHR0
# cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGUBggrBgEFBQcBAQSBhzCBhDAkBggr
# BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBo
# dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2Rl
# U2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqG
# SIb3DQEBCwUAA4ICAQC3EeHXUPhpe31K2DL43Hfh6qkvBHyR1RlD9lVIklcRCR50
# ZHzoWs6EBlTFyohvkpclVCuRdQW33tS6vtKPOucpDDv4wsA+6zkJYI8fHouW6Tqa
# 1W47YSrc5AOShIcJ9+NpNbKNGih3doSlcio2mUKCX5I/ZrzJBkQpJ0kYha/pUST2
# CbE3JroJf2vQWGUiI+J3LdiPNHmhO1l+zaQkSxv0cVDETMfQGZKKRVESZ6Fg61b0
# djvQSx510MdbxtKMjvS3ZtAytqnQHk1ipP+Rg+M5lFHrSkUlnpGa+f3nuQhxDb7N
# 9E8hUVevxALTrFifg8zhslVRH5/Df/CxlMKXC7op30/AyQsOQxHW1uNx3tG1DMgi
# zpwBasrxh6wa7iaA+Lp07q1I92eLhrYbtw3xC2vNIGdMdN7nd76yMIjdYnAn7r38
# wwtaJ3KYD0QTl77EB8u/5cCs3ShZdDdyg4K7NoJl8iEHrbqtooAHOMLiJpiL2i9Y
# n8kQMB6/Q6RMO3IUPLuycB9o6DNiwQHf6Jt5oW7P09k5NxxBEmksxwNbmZvNQ65Z
# n3exUAKqG+x31Egz5IZ4U/jPzRalElEIpS0rgrVg8R8pEOhd95mEzp5WERKFyXhe
# 6nB6bSYHv8clLAV0iMku308rpfjMiQkqS3LLzfUJ5OHqtKKQNMLxz9z185UCszGC
# BlMwggZPAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBS
# U0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQB8JSdCgUotar/iTqF+XdLjANBglghkgB
# ZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJ
# AzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8G
# CSqGSIb3DQEJBDEiBCDoiZe1oYPyM/3GD/VG3coMFqhkY5UgD3MHyAr0JS7/LTAN
# BgkqhkiG9w0BAQEFAASCAgBGcE3w5mOdbJzRAnWOLbn7qxo1bbHIvp08eryDtX4R
# TwNi2xIrnzVBnOoPdMk3IP7REv4vrAC9PP7bSAPMknIFZmWp3JlPBHLkDVuxtKgR
# DA17gvAAtwhV1Eth4StvfEzWojyC4XhQdvGNzyAeH1Kw2YoYfphWfqE/sAurTNDF
# REfFQwhkHR1LnQ5jPcwi8/fWidGlhTKXXzSLcRh6cBYzMIUMpWG7erjbjEPMRAy1
# WgSZiuHYUlcGvxxF6PgciDsaP//noIiiByY/NmOaSV1+rmpFbYy+kKUirjnZJT3P
# NarlcDGt/87nvxyyhlfkE/A+r4cjKzRBcCgT6QMo+ugSBrzYxxl4M4x38KR+huLb
# NWIaI7UzP2PnZaG7vj3gtpJLe0JyzeFqsPCacJTZshyuZr3efLl6QWg8pb4WkbIs
# d4xI8Hzq/OgvPW+6CUN8zCBt2Oe8c0xp2KU2GbHLUvWV5B1FXMG2tQ4TBdKKNhbu
# nGPh440nPAUTyfzcj2F04m90yq9GfeR1J6P5Z5AjuIqBF7uI1Sxp1oY8iPMdOtwF
# H6zwLPWIuJ7QyS/qSxrs0BxHrF0pGdNl+OAHJWazsBigcGxhM2TmHvEWfBZwPe8T
# u6NziF3Kkd4PQGjqLERtfxg3zFzofZniFaLsI73AfS3O6qxCMaBXlwCDpsmTBeO0
# /KGCAyAwggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1
# c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAVEr/OUnQg5
# pr/bP1/lYRYwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN
# AQcBMBwGCSqGSIb3DQEJBTEPFw0yNDAxMTUxNDA3NDhaMC8GCSqGSIb3DQEJBDEi
# BCAojbtZZLS1cvAlKCia7XXD/X3Nrg6mzmHePrOfN1bGYTANBgkqhkiG9w0BAQEF
# AASCAgAWlqtZoxPfghVOsU6jhSv23DErFRsbTL3uWfmuzsqvy20yi5FkdexEjy/F
# AiGr6vMWH4i4GpaPVpo1LldMb44zhIeIXJEXtrMeWuKwQG1pw8iMnrjCH48yPkrO
# 6iq/FmnDMeU6uosHyUtg0sQEBXYpjiqbAXojFeWonGW3kmobw9fwcftLz9Gx4B01
# HNkRAZRMF5Kj1bufKQ5+YsAcXHbcSuq0c1amQVO5VT/NyteJMJKdfDfkU7Cu9VBL
# uS0FPjQModsTMfPzIgL70En8k1qMKYShnlgZ68U3SYaj8tY/zTKUGV7c2E67oLL4
# FRp+7YjmHkrfEZxDonrI1KlVteXfXSErY+WxYn52s0i7mNWBMdJjL2oP7L+4DBTL
# SlW78F3BjRaGzS/lltTMKo9QDYCpITgSfTOlYW2ahmFqKUuMr0fIPYGSDmQG+6QA
# HFoMBAUWWGK0UN/Xm9dC1DLrNNxY4tgpURRNr59zHrKZ4yCiL0YEh9Ml6Fvxc1pF
# RLSwQDFcPKCHguacca4bKcEGEys+5RBAYBedsLh44fr5icXOWqrH2q/Nmh9U+K01
# iF32kP/wJxT/ZA6OGQH0lW75RWYaMv+OMFujphFOjY7wEmj+dlbxs5ycNA8NGtow
# SpsCV50ZtbFmnjYbO+YhIjPEuZH9CPsdDDKRFAkSPsaX/tGNug==
# SIG # End signature block