PSWriteHTML.psm1

function Add-TableFiltering {
    [CmdletBinding()]
    param(
        [bool] $Filtering,
        [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom'
    )
    $Output = @{}
    if ($Filtering) {
        # https://datatables.net/examples/api/multi_filter.html

        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).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).draw();
                        }
                    });
                });
"@


        } else {
            # top headers
            $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 = '' # assign multiple same values trick
    }
    return $Output
}
function Convert-ImagesToBinary {
    [CmdLetBinding()]
    param(
        [string[]] $Content,
        [string] $Search,
        [string] $ReplacePath
    )
    if ($Content -like "*$Search*") {
        $Replace = "data:image/$FileType;base64," + [Convert]::ToBase64String((Get-Content -LiteralPath $ReplacePath -Encoding Byte))
        $Content = $Content.Replace($Search, $Replace)        
    }
    $Content
}
function Convert-StyleContent {
    [CmdLetBinding()]
    param(
        [string[]] $CSS,
        [string] $ImagesPath,
        [string] $SearchPath
    )

    #Get-ObjectType -Object $CSS -VerboseOnly -Verbose

    $ImageFiles = Get-ChildItem -Path (Join-Path $ImagesPath '\*') -Include *.jpg, *.png, *.bmp #-Recurse
    foreach ($Image in $ImageFiles) {
        #$Image.FullName
        #$Image.Name
        $CSS = Convert-ImagesToBinary -Content $CSS -Search "$SearchPath$($Image.Name)" -ReplacePath $Image.FullName
    }
    return $CSS
}

#

#Convert-StyleContent -ImagesPath "$PSScriptRoot\Resources\Images\DataTables" -SearchPath "DataTables-1.10.18/images/"
function Convert-StyleContent1 {
    param(
        [PSCustomObject] $Options
    )
    # Replace PNG / JPG files in Styles
    if ($null -ne $Options.StyleContent) {
        Write-Verbose "Logos: $($Options.Logos.Keys -join ',')"
        foreach ($Logo in $Options.Logos.Keys) {
            $Search = "../images/$Logo.png", "DataTables-1.10.18/images/$Logo.png"
            $Replace = $Options.Logos[$Logo]
            foreach ($S in $Search) {
                Write-Verbose "Logos - replacing $S with binary representation"
                $Options.StyleContent = ($Options.StyleContent).Replace($S, $Replace)
            }
        }
    }    
}
function Get-FeaturesInUse {
    [CmdletBinding()]
    param()
    foreach ($Key in $Script:HTMLSchema.Features.Keys) {
        if ($Script:HTMLSchema.Features[$Key]) {
            $Key
        }
    }
}
Function Get-HTMLColorSchemes {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false)][String]$SchemePath
    )
    if ([String]::IsNullOrEmpty($SchemePath)) {
        $SchemePath = "$PSScriptRoot\Resources\ColorSchemas"
    }
    $Schemes = @{}
    Write-Verbose "Retrieving *.rcs from $SchemePath"
    $SchemeFiles = @(Get-ChildItem $SchemePath -Filter '*.rcs' -Recurse )
    foreach ($SchemeFile in $SchemeFiles) {
        $SchemeContent = Import-Csv -Delimiter ';' -Path $SchemeFile.FullName
        $Schemes.Add($SchemeFile.BaseName, $SchemeContent)
    }
    $Schemes.add('Generated1', (Get-RandomColorScheme -NumberOfSchemes 80))
    $Schemes.add('Generated2', (Get-RandomColorScheme -NumberOfSchemes 80))
    $Schemes.add('Generated3', (Get-RandomColorScheme -NumberOfSchemes 80))
    $Schemes.add('Generated4', (Get-RandomColorScheme -NumberOfSchemes 80))
    $Schemes.add('Generated5', (Get-RandomColorScheme -NumberOfSchemes 80))
    $Schemes.add('Generated6', (Get-RandomColorScheme -NumberOfSchemes 80))
    $Schemes.add('Generated7', (Get-RandomColorScheme -NumberOfSchemes 80))
    $Schemes.add('Generated8', (Get-RandomColorScheme -NumberOfSchemes 80))
    Write-Output $Schemes
}
Function Get-HTMLLogos {
    <#
         .SYNOPSIS
            Get Base64 HTML
 
    #>

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

    )
    $LogoSources = @{}
    $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 {
        $LogoPath += "$PSScriptRoot\Resources\Images\Other"
    }
    $LogoPath += "$PSScriptRoot\Resources\Images\DataTables"

    $ImageFiles = Get-ChildItem -Path (join-path $LogoPath '\*') -Include *.jpg, *.png, *.bmp -Recurse
    foreach ($ImageFile in $ImageFiles) {
        if ($ImageFile.Extension -eq '.jpg') {
            $FileType = 'jpeg'
        } else {
            $FileType = $ImageFile.Extension.Replace('.', '')
        }
        Write-Verbose "Converting $($ImageFile.FullName) to base64 ($FileType)"
        $LogoSources.Add($ImageFile.BaseName, "data:image/$FileType;base64," + [Convert]::ToBase64String((Get-Content $ImageFile.FullName -Encoding Byte)))
    }
    Write-Output $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)   
    
    #Write-Color $NrStart, $NrEnd, $Type -Color White, Yellow, Blue

    if ($Type -eq 'Between') {
        if ($NrStart -eq -1) {
            # return nothing
            return
        }
        $Content[$NrStart..$NrEnd]
    } 
    if ($Type -eq 'After') {
        if ($NrStart -eq -1) {
            # Returns untouched content
            return $Content
        }
        $Content[($NrEnd + 1)..($Content.Count - 1)]

    }
    if ($Type -eq 'Before') {
        if ($NrStart -eq -1) {
            # return nothing
            return
        }
        $Content[0..$NrStart]
    }
}

Function Get-RandomColor {
    <#
    .SYNOPSIS
        Random colour Function
    #>

    param(
        [int]$RMin = 0,
        [int]$RMax = 255,
        [int]$GMin = 0,
        [int]$GMax = 255,
        [int]$BMin = 0,
        [int]$BMax = 255
    )
    $R = (Get-Random -Maximum $RMax -Minimum $RMin)
    $G = (Get-Random -Maximum $GMax -Minimum $GMin)
    $B = (Get-Random -Maximum $BMax -Minimum $BMin)

    @($R, $G, $B)
}
Function Get-RandomColorScheme {
    <#
    .SYNOPSIS
    Generate a colour scheme
 
     
    .DESCRIPTION
    Generate a colour scheme
     
    .PARAMETER NumberOfSchemes
    Parameter description
     
    .EXAMPLE
    An example
     
    .NOTES
    General notes
    #>

    
    param
    (
        [Parameter(Mandatory = $false)][int]$NumberOfSchemes = 1,
        [int] $ColorSwing = 8,        
        [string] $Hover = 0.3,
        [string] $color = 0.6,
        [string] $border = 1,
        [string] $background = 0.7
    )

    $ColorReference = Get-Random -Minimum 1 -Maximum 3
    $BaseColor = (Get-Random -Maximum (200 - $ColorSwing) -Minimum (50 + $ColorSwing))
    $BCMax = $BaseColor + $ColorSwing
    $BCMin = $BaseColor - $ColorSwing

    $i = 0
    while ($i -ne $NumberOfSchemes) {       
        switch ($ColorReference) {
            1 { [int[]] $base = Get-RandomColor -rmin $BCMin -rmax $BCMax }
            2 { [int[]] $base = Get-RandomColor -gmin $BCMin -gmax $BCMax }
            3 { [int[]] $base = Get-RandomColor -bmin $BCMin -bcmax $BCMax }
        }

        [PSCustomObject] @{
            Background = "rgba($($base + $background -join ','))"
            Border     = "rgba($($base + $border -join ','))"
            Colour     = "rgba($($base + $color -join ','))"
            Hover      = "rgba($($base + $Hover -join ','))"
        }
        $i++
    }


}
function Get-Resources {
    [CmdLetBinding()]
    param(
        [switch] $UseCssLinks,
        [switch] $UseJavaScriptLinks,
        [switch] $NoScript,
        [ValidateSet('Header', 'Footer', 'HeaderAlways', 'FooterAlways')][string] $Location
    )
    DynamicParam {
        # Defines Features Parameter Dynamically
        $Names = $Script:Configuration.Features.Keys
        $ParamAttrib = New-Object System.Management.Automation.ParameterAttribute
        $ParamAttrib.Mandatory = $true
        $ParamAttrib.ParameterSetName = '__AllParameterSets'

        $ReportAttrib = New-Object  System.Collections.ObjectModel.Collection[System.Attribute]
        $ReportAttrib.Add($ParamAttrib)
        $ReportAttrib.Add((New-Object System.Management.Automation.ValidateSetAttribute($Names)))
        $ReportRuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Features', [string[]], $ReportAttrib)
        $RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $RuntimeParamDic.Add('Features', $ReportRuntimeParam)
        return $RuntimeParamDic
    }
    Process {
        [string[]] $Features = $PSBoundParameters.Features

        foreach ($Feature in $Features) {

            Write-Verbose "Get-Resources - Location: $Location - Feature: $Feature UseCssLinks: $UseCssLinks UseJavaScriptLinks: $UseJavaScriptLinks"
            if ($UseCssLinks) {
                New-HTMLResourceCSS -Link $Script:Configuration.Features.$Feature.$Location.'CssLink' -ResourceComment $Script:Configuration.Features.$Feature.Comment
            } else {
                $CSSOutput = New-HTMLResourceCSS -FilePath $Script:Configuration.Features.$Feature.$Location.'Css' -ResourceComment $Script:Configuration.Features.$Feature.Comment
                Convert-StyleContent -CSS $CSSOutput -ImagesPath "$PSScriptRoot\Resources\Images\DataTables" -SearchPath "DataTables-1.10.18/images/"
            }
            if ($UseJavaScriptLinks) {
                New-HTMLResourceJS -Link $Script:Configuration.Features.$Feature.$Location.'JsLink' -ResourceComment $Script:Configuration.Features.$Feature.Comment
            } else {
                New-HTMLResourceJS -FilePath $Script:Configuration.Features.$Feature.$Location.'Js' -ResourceComment $Script:Configuration.Features.$Feature.Comment
            }

            if ($NoScript) {
                [Array] $Output = @(
                    if ($UseCssLinks) {
                        New-HTMLResourceCSS -Link $Script:Configuration.Features.$Feature.$Location.'CssLinkNoScript' -ResourceComment $Script:Configuration.Features.$Feature.Comment
                    } else {
                        $CSSOutput = New-HTMLResourceCSS -FilePath $Script:Configuration.Features.$Feature.$Location.'CssNoScript' -ResourceComment $Script:Configuration.Features.$Feature.Comment
                        Convert-StyleContent -CSS $CSSOutput -ImagesPath "$PSScriptRoot\Resources\Images\DataTables" -SearchPath "DataTables-1.10.18/images/"
                    }
                )
                if (($Output.Count -gt 0) -and ($null -ne $Output[0])) {
                    New-HTMLTag -Tag 'noscript' {
                        $Output
                    }
                }
            }
        }
    }
}
function New-ApexChart {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [ValidateSet('', 'central')][string] $Positioning
    )
    $Script:HTMLSchema.Features.ChartsApex = $true
    [string] $ID = "ChartID-" + (Get-RandomStringName -Size 8)
    $Div = New-HTMLTag -Tag 'div' -Attributes @{ id = $ID; class = $Positioning }
    $Script = New-HTMLTag -Tag 'script' -Value {
        # Convert Dictionary to JSON and return chart within SCRIPT tag
        # Make sure to return with additional empty string
        $JSON = $Options | ConvertTo-Json -Depth 5 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) }
        "var options = $JSON"
        ""
        "var chart = new ApexCharts(document.querySelector('#$ID'),
            options
        );"

        "chart.render();"
    }
    $Div
    # we need to move it to the end of the code therefore using additional vesel
    $Script:HTMLSchema.Charts.Add($Script)
}
function New-ChartArea {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,

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

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

        #$Type
    )
    # Chart defintion type, size
    $Options.chart = @{
        type = 'area'
    }

    $Options.series = @( New-HTMLChartDataSet -Data $Data -DataNames $DataNames )

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

}
function New-ChartAxisX {
    param(
        [System.Collections.IDictionary] $Options,
        [string] $Title,
        [int] $MinValue,
        [int] $MaxValue,
        [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category',
        $DataCategories
    )

    if (-not $Options.Contains('xaxis')) {
        $Options.xaxis = @{}
    }
    if ($Title -ne '') {
        $Options.xaxis.title = @{}
        $Options.xaxis.title.text = $Title
    }
    if ($MinValue -gt 0) {
        $Options.xaxis.min = $MinValue
    }
    if ($MinValue -gt 0) {
        $Options.xaxis.max = $MaxValue
    }
    if ($DataCategoriesType -ne '') {
        $Options.xaxis.type = $DataCategoriesType
    }
    if ($DataCategories.Count -gt 0) {
        $Options.xaxis.categories = $DataCategories
    }
}
function New-ChartAxisY {
    param(
        [System.Collections.IDictionary] $Options,
        [string] $Title,
        [int] $MinValue,
        [int] $MaxValue
    )
    if (-not $Options.Contains('yaxis')) {
        $Options.yaxis = @{}
    }
    if ($Title -ne '') {
        $Options.yaxis.title = @{}
        $Options.yaxis.title.text = $Title
    }
    if ($MinValue -gt 0) {
        $Options.yaxis.min = $MinValue
    }
    if ($MinValue -gt 0) {
        $Options.yaxis.max = $MaxValue
    }
}
Function New-ChartBar {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $Horizontal = $true,
        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [RGBColors[]] $DataLabelsColor,

        [string] $Title,
        [ValidateSet('center', 'left', 'right', '')][string] $TitleAlignment = '',

        [string] $Formatter,
        [ValidateSet('bar', 'barStacked', 'barStacked100Percent')] $Type = 'bar',

        [RGBColors[]] $Colors,

        [switch] $PatternedColors,
        [switch] $Distributed,

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

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

    if ($Colors.Count -gt 0) {
        $RGBColor = ConvertFrom-Color -Color $Colors
        $Options.colors = @($RGBColor)
    }

    $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-HTMLChartDataSet -Data $Data -DataNames $DataLegend)

    # X AXIS - CATEGORIES
    $Options.xaxis = [ordered] @{}
    # if ($DataCategoriesType -ne '') {
    # $Options.xaxis.type = $DataCategoriesType
    #}
    if ($DataNames.Count -gt 0) {
        $Options.xaxis.categories = $DataNames
        # Need to figure out how to conver to json and leave function without ""
        #if ($Formatter -ne '') {
        #$Options.xaxis.labels = @{
        #formatter = "function(val) { return val + `"$Formatter`" }"
        #}
        #}
    }

    New-ChartTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment

     if ($PatternedColors) {
        $Options.fill = @{
            type    = 'pattern'
            opacity = 1
            pattern = @{
                style = @('circles', 'slantedLines', 'verticalLines', 'horizontalLines')
            }
        }
    }
}
function New-ChartDataLabels {
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [RGBColors[]] $DataLabelsColor
    )

    $Options.dataLabels = [ordered] @{
        enabled = $DataLabelsEnabled
        offsetX = $DataLabelsOffsetX
        style   = @{
            fontSize = $DataLabelsFontSize
        }
    }
    if ($DataLabelsColor.Count -gt 0) {
        $Options.dataLabels.style.colors = @(ConvertFrom-Color -Color $DataLabelsColor)
    }
}
function New-ChartGridColors {
    param(
        [System.Collections.IDictionary] $Options,
        [RGBColors[]] $GridColors,
        [double] $GridOpacity
    )
    $Options.grid = @{}
    $Options.grid.row = @{}

    if ($GridColors.Count -gt 0) {
        $Options.grid.row.colors = @(ConvertFrom-Color -Color $GridColors)
    }
    if ($GridOpacity -ne 0) {
        $Options.grid.row.opacity = $GridOpacity
    }
}

function New-ChartLegend {
    param(
        [System.Collections.IDictionary] $Options,
        [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', '')][string] $LegendPosition = ''
    )
    # legend
    if ($LegendPosition -eq '' -or $LegendPosition -eq 'bottom') {
        # Do nothing
    } 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-ChartLine {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,

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

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

        # $Type
    )
    # Chart defintion type, size
    $Options.chart = @{
        type = 'line'
    }

    $Options.series = @( New-HTMLChartDataSet -Data $Data -DataNames $DataNames )

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

}
function New-ChartMarker {
    param(
        [System.Collections.IDictionary] $Options,
        [int] $MarkerSize
    )
    if ($MarkerSize -gt 0) {
        $Options.markers = @{
            size = $MarkerSize
        }
    }
}
function New-ChartRadial {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [Array] $Values,
        [Array] $Names,
        $Type
    )
    # Chart defintion type, size
    $Options.chart = @{
        type = 'radialBar'
    }

    if ($Type -eq '1') {
        New-ChartRadialType1 -Options $Options
    } elseif ($Type -eq '2') {
        New-ChartRadialType2 -Options $Options
    }

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


}
function New-ChartRadialCircleType {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [ValidateSet('FullCircleTop', 'FullCircleBottom', 'FullCircleBottomLeft', 'FullCircleLeft', 'Speedometer', 'SemiCircleGauge')] $CircleType
    )
    if ($CircleType -eq 'SemiCircleGauge') {
        $Options.plotOptions.radialBar = [ordered] @{
            startAngle = -90
            endAngle   = 90
        }
    } elseif ($CircleType -eq 'FullCircleBottom') {
        $Options.plotOptions.radialBar = [ordered] @{
            startAngle = -180
            endAngle   = 180
        }
    } elseif ($CircleType -eq 'FullCircleLeft') {
        $Options.plotOptions.radialBar = [ordered] @{
            startAngle = -90
            endAngle   = 270
        }
    } elseif ($CircleType -eq 'FullCircleBottomLeft') {
        $Options.plotOptions.radialBar = [ordered] @{
            startAngle = -135
            endAngle   = 225
        }
    } elseif ($CircleType -eq 'Speedometer') {
        $Options.plotOptions.radialBar = [ordered] @{
            startAngle = -135
            endAngle   = 135
        }
    } else {
        #FullCircleTop
    }
}
function New-ChartRadialDataLabels {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [string] $LabelAverage = 'Average'
    )
    if ($LabelAverage -ne '') {
        $Options.plotOptions.radialBar.dataLabels = @{
            showOn = 'always'

            name   = @{
                # fontSize = '16px'
                # color = 'undefined'
                #offsetY = 120
            }
            value  = @{
                #offsetY = 76
                # fontSize = '22px'
                # color = 'undefined'
                # formatter = 'function (val) { return val + "%" }'
            }

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

        }
    }
}

function New-ChartRadialType1 {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [Array] $Values,
        [Array] $Names
    )

    $Options.plotOptions = @{
        radialBar = [ordered] @{
            #startAngle = -135
            #endAngle = 225

            #startAngle = -135
            #endAngle = 135


            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  #// margin is in pixels
                dropShadow  = [ordered] @{
                    enabled = $true
                    top     = -3
                    left    = 0
                    blur    = 4
                    opacity = 0.35
                }
            }
            <#
            dataLabels = @{
                showOn = 'always'
 
                name = @{
                    # fontSize = '16px'
                    # color = 'undefined'
                    #offsetY = 120
                }
                value = @{
                    #offsetY = 76
                    # fontSize = '22px'
                    # color = 'undefined'
                    # formatter = 'function (val) { return val + "%" }'
                }
 
                total = @{
                    show = $false
                    label = 'Average'
                }
            }
            #>

        }
    }

    $Options.fill = [ordered] @{
        type     = 'gradient'
        gradient = [ordered] @{
            shade            = 'dark'
            type             = 'horizontal'
            shadeIntensity   = 0.5
            gradientToColors = @('#ABE5A1')
            inverseColors    = true
            opacityFrom      = 1
            opacityTo        = 1
            stops            = @(0, 100)
        }
    }
    <# Gradient
        $Options.stroke = @{
        lineCap = 'round'
    }
    #>

    <#
    $Options.fill = @{
        type = 'gradient'
        gradient = @{
            shade = 'dark'
            shadeIntensity = 0.15
            inverseColors = false
            opacityFrom = 1
            opacityTo = 1
            stops = @(0, 50, 65, 91)
        }
    }
    #>

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

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

            #startAngle = -135
            #endAngle = 225

            #startAngle = -135
            #endAngle = 135


            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 = @{
                background = '#fff'
                strokeWidth = '70%'
                margin = 0 #// margin is in pixels
                dropShadow = @{
                    enabled = $true
                    top = -3
                    left = 0
                    blur = 4
                    opacity = 0.35
                }
            }
            dataLabels = @{
                showOn = 'always'
 
                name = @{
                    # fontSize = '16px'
                    # color = 'undefined'
                    offsetY = 120
                }
                value = @{
                    offsetY = 76
                    # fontSize = '22px'
                    # color = 'undefined'
                    # formatter = 'function (val) { return val + "%" }'
                }
 
                total = @{
                    show = $false
                    label = 'Average'
                }
            }
            #>

        }
    }
}
function New-ChartSize {
    [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-ChartSpark {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [nullable[RGBColors]] $Color,
        [string] $Title,
        [string] $SubTitle,
        [int] $FontSizeTitle = 24,
        [int] $FontSizeSubtitle = 14,
        [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 = [ordered] @{
        type      = 'area'
        sparkline = @{
            enabled = $true
        }
    }
    $Options.stroke = @{
        curve = 'straight'
    }
    $Options.title = [ordered] @{
        text    = $Title
        offsetX = 0
        style   = @{
            fontSize = "$($FontSizeTitle)px"
            cssClass = 'apexcharts-yaxis-title'
        }
    }
    $Options.subtitle = [ordered] @{
        text    = $SubTitle
        offsetX = 0
        style   = @{
            fontSize = "$($FontSizeSubtitle)px"
            cssClass = 'apexcharts-yaxis-title'
        }
    }
    $Options.yaxis = @{
        min = 0
    }
    $Options.fill = @{
        opacity = 0.3
    }
    $Options.series = @(
        # Checks if it's multiple array passed or just one. If one it will draw one line, if more then one it will draw line per each array
        if ($Values[0] -is [Array]) {
            foreach ($Value in $Values) {
                @{
                    data = @($Value)
                }
            }
        } else {
            @{
                data = @($Values)
            }
        }
    )
}
function New-ChartStrokeDefinition {
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $LineShow = $true,
        [ValidateSet('straight', 'smooth', 'stepline')] $LineCurve = 'straight',
        [int] $LineWidth,
        [RGBColors[]] $LineColor
    )
    # LINE Definition
    $Options.stroke = [ordered] @{
        show  = $LineShow
        curve = $LineCurve
    }
    if ($LineWidth -ne 0) {
        $Options.stroke.width = $LineWidth
    }
    if ($LineColor.Count -gt 0) {
        $Options.stroke.colors = @(ConvertFrom-Color -Color $LineColor)
    }
}
function New-ChartTitle {
    param(
        [System.Collections.IDictionary] $Options,
        [string] $Title,
        [ValidateSet('center', 'left', 'right', '')][string] $TitleAlignment = ''
    )
    # title
    $Options.title = [ordered] @{}
    if ($TitleText -ne '') {
        $Options.title.text = $Title
    }
    if ($TitleAlignment -ne '') {
        $Options.title.align = $TitleAlignment
    }
}
function New-ChartToolbar {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [bool] $Show = $true,
        [bool] $Download = $true,
        [bool] $Selection = $true,
        [bool] $Zoom = $true,
        [bool] $ZoomIn = $true,
        [bool] $ZoomOut = $true,
        [bool] $Pan = $true,
        [bool] $Reset = $true
    )
    $Options.chart.toolbar = [ordered] @{
        show         = $show
        tools        = [ordered] @{
            download  = $Download
            selection = $Selection
            zoom      = $Zoom
            zoomin    = $ZoomIn
            zoomout   = $ZoomOut
            pan       = $Pan
            reset     = $Reset
        }
        autoSelected = 'zoom'
    }
}
function New-ChartZoom {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Options,
        [switch] $Enabled
    )
    if ($Enabled) {
        $Options.chart.zoom = @{
            type    = 'x'
            enabled = $Enabled.IsPresent
        }
    }
}
Function New-HTML {
    [CmdletBinding()]
    param(
        [Parameter(Position = 0)][ValidateNotNull()][ScriptBlock] $HtmlData = $(Throw "Have you put the open curly brace on the next line?"),
        [switch] $UseCssLinks,
        [switch] $UseJavaScriptLinks,
        [String] $TitleText,
        [String] $ColorSchemePath,
        [string] $PrimaryColorHex,
        [string] $Author,
        [string] $DateFormat = 'yyyy-MM-dd HH:mm:ss',

        # save HTML options
        [Parameter(Mandatory = $false)][string]$FilePath,
        [Parameter(Mandatory = $false)][switch]$ShowHTML,
        [int] $AutoRefresh,
        [ValidateSet('Unknown', 'String', 'Unicode', 'Byte', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'Ascii', 'Default', 'Oem', 'BigEndianUTF32')] $Encoding = 'UTF8'
    )

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

    <#
    $Options = New-HTMLReportOptions `
        -RightLogoName $RightLogoName `
        -LeftLogoName LeftLogoName `
        -LeftLogoString $LeftLogoString `
        -RightLogoString $RightLogoString `
        -CSSName $CSSName `
        -CSSPath $CSSPath `
        -ScriptPath $ScriptPath `
        -ColorSchemePath $ColorSchemePath `
        -UseCssLinks:$UseCssLinks `
        -UseStyleLinks:$UseStyleLinks
    #>

    $Script:HTMLSchema = @{
        TabsHeaders = [System.Collections.Generic.List[HashTable]]::new() # tracks / stores headers
        Features    = @{} # tracks features for CSS/JS implementation
        Charts      = [System.Collections.Generic.List[string]]::new()
    }
    $OutputHTML = Invoke-Command -ScriptBlock $HtmlData
    $Logo = Get-HTMLPartContent -Content $OutputHTML -Start '<!-- START LOGO -->' -End '<!-- END LOGO -->' -Type Between
    $OutputHTML = Get-HTMLPartContent -Content $OutputHTML -Start '<!-- START LOGO -->' -End '<!-- END LOGO -->' -Type After
    $Features = Get-FeaturesInUse

    $HTML = @(
        '<!DOCTYPE html>'
        New-HTMLTag -Tag 'html' {
            '<!-- HEADER -->'
            New-HTMLTag -Tag 'head' {
                New-HTMLTag -Tag 'meta' -Attributes @{ charset = "utf-8" } -SelfClosing
                New-HTMLTag -Tag 'meta' -Attributes @{ name = 'viewport'; content = 'width=device-width, initial-scale=1' } -SelfClosing
                New-HTMLTag -Tag 'meta' -Attributes @{ name = 'author'; content = $Author } -SelfClosing
                New-HTMLTag -Tag 'meta' -Attributes @{ name = 'revised'; content = $CurrentDate } -SelfClosing
                New-HTMLTag -Tag 'title' { $TitleText }
                if ($Autorefresh -gt 0) {
                    New-HTMLTag -Tag 'meta' -Attributes @{ 'http-equiv' = 'refresh'; content = $Autorefresh } -SelfClosing
                }

                Get-Resources -UseCssLinks:$true -UseJavaScriptLinks:$true -Location 'HeaderAlways' -Features Default, DefaultHeadings, Fonts, FontsAwesome
                Get-Resources -UseCssLinks:$false -UseJavaScriptLinks:$false -Location 'HeaderAlways' -Features Default, DefaultHeadings
                if ($null -ne $Features) {
                    Get-Resources -UseCssLinks:$true -UseJavaScriptLinks:$true -Location 'HeaderAlways' -Features $Features
                    Get-Resources -UseCssLinks:$false -UseJavaScriptLinks:$false -Location 'HeaderAlways' -Features $Features
                    Get-Resources -UseCssLinks:$true -UseJavaScriptLinks:$true -Location 'HeaderAlways' -Features $Features -NoScript
                    Get-Resources -UseCssLinks:$false -UseJavaScriptLinks:$false -Location 'HeaderAlways' -Features $Features -NoScript
                    Get-Resources -UseCssLinks:$UseCssLinks -UseJavaScriptLinks:$UseJavaScriptLinks -Location 'Header' -Features $Features
                }
            }
            '<!-- END HEADER -->'
            '<!-- BODY -->'
            #'<body onload="hide();">'
            New-HTMLTag -Tag 'body' {

                <#
            if ($HideLogos -eq $false) {
                $Leftlogo = $Options.Logos[$LeftLogoName]
                $Rightlogo = $Options.Logos[$RightLogoName]
                $LogoContent = @"
                <table><tbody>
                <tr>
                    <td class="clientlogo"><img src="$Leftlogo" /></td>
                    <td class="MainLogo"><img src="$Rightlogo" /></td>
                </tr>
                </tbody></table>
"@
                $LogoContent
            }
            #>

                <# Not sure what for
            if (!([string]::IsNullOrEmpty($PrimaryColorHex))) {
                if ($PrimaryColorHex.Length -eq 7) {
                    $HTML = $HTML -replace '#337E94', $PrimaryColorHex
                } else {
                    Write-Warning '$PrimaryColorHex must be 7 characters with hash eg "#337E94"'
                }
            }
            #>


                # Add logo if there is one
                $Logo

                # Add tabs header if there is one
                if ($Script:HTMLSchema.TabsHeaders) {
                    New-HTMLTabHead
                }
                # Add remaining data
                $OutputHTML

                # Add charts scripts if those are there
                foreach ($Chart in $Script:HTMLSchema.Charts) {
                    $Chart
                }
            }
            '<!-- END BODY -->'
            '<!-- FOOTER -->'
            New-HTMLTag -Tag 'footer' {
                if ($null -ne $Features) {
                    # FooterAlways means we're not able to provide consistent output with and without links and we prefer those to be included
                    # either as links or from file per required features
                    Get-Resources -UseCssLinks:$true -UseJavaScriptLinks:$true -Location 'FooterAlways' -Features $Features
                    Get-Resources -UseCssLinks:$false -UseJavaScriptLinks:$false -Location 'FooterAlways' -Features $Features
                    # standard footer features
                    Get-Resources -UseCssLinks:$UseCssLinks -UseJavaScriptLinks:$UseJavaScriptLinks -Location 'Footer' -Features $Features
                }
            }
            '<!-- END FOOTER -->'
        }
    )
    if ($FilePath -ne '') {
        Save-HTML -HTML $HTML -FilePath $FilePath -ShowHTML:$ShowHTML -Encoding $Encoding
    } else {
        $HTML
    }
}
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
    #>

    

    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,
        [string] $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-HTMLAnchorLink {
    <#
    .SYNOPSIS
    Creates Hyperlink for an Anchor
 
    .DESCRIPTION
    Long description
 
    .PARAMETER AnchorName
    The Actual name of the Anchor (Hidden)
 
    .PARAMETER AnchorText
    The HyperLink text. Will default to $AnchorNname if not specified
 
    .EXAMPLE
    Get-HTMLAnchorLink -AnchorName 'test'
 
    .NOTES
    General notes
    #>


    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true)][String] $AnchorName,
        [Parameter(Mandatory = $false)][String] $AnchorText
    )
    if ($AnchorText -eq $null -or $AnchorText -eq '') {
        $AnchorText = $AnchorName
    }
    New-HTMLAnchor -Name $AnchorName -HrefLink '#' -Class 'alink' -Text $AnchorText
}
Function New-HTMLAnchorName {
    <#
    .SYNOPSIS
    Creates an anchor
 
    .DESCRIPTION
    Long description
 
    .PARAMETER AnchorName
    Parameter description
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)][String] $AnchorName
    )
    New-HTMLAnchor -Name $AnchorName
}
function New-HTMLCanvas {
    [CmdletBinding()]
    param(
        [alias('Value')][Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace with Content"),
        [string] $ID,
        [string] $Width,
        [string] $Height,
        [string] $Style
    )
    $Canvas = [Ordered] @{
        Tag         = 'canvas'
        Attributes  = [ordered]@{
            'id'     = $Id
            'width'  = $Width
            'height' = $Height
            'style'  = $Style
        }
        SelfClosing = $false
        Value       = Invoke-Command -ScriptBlock $Content
    }
    $HTML = Set-Tag -HtmlObject $Canvas
    return $HTML
}
function New-HTMLChart {
    [CmdletBinding()]
    param(
        [nullable[int]] $Height = 350,
        [nullable[int]] $Width,
        [string] $Type = 'bar',
        [bool] $Horizontal = $true,
        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [RGBColors] $DataLabelsColor,
        [Array] $Data = @(),
        [Array] $DataNames = @(),
        [Array] $DataCategories = @(),
        [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category',
        [string] $TitleText,
        [ValidateSet('center', 'left', 'right', '')][string] $TitleAlignment = '',

        [bool] $LineShow = $true,
        [ValidateSet('straight', 'smooth')] $LineCurve = 'straight',
        $LineWidth = 2,
        [RGBColors[]] $LineColor,
        [ValidateSet('', 'central')][string] $Positioning
    )




    $Options = [ordered] @{}

    # Chart defintion type, size
    $Options.chart = @{
        type = $Type
    }


    New-ChartSize -Options $Options -Height $Height -Width $Width
    New-ChartToolbar -Options $Options


    $Options.plotOptions = @{
        bar = @{
            horizontal = $Horizontal
        }
    }
    $Options.dataLabels = [ordered] @{
        enabled = $DataLabelsEnabled
        offsetX = $DataLabelsOffsetX
        style   = @{
            fontSize = $DataLabelsFontSize
            colors   = @($DataLabelsColor)
        }
    }
    if ('bar', 'line' -contains $Type) {
        # Some types require a more complicated dataset
        $Options.series = @( New-HTMLChartDataSet -Data $Data -DataNames $DataNames )
    } else {
        # Some types of charts require simple data sets - in particular just array
        $Options.series = $Data
        if ($null -ne $DataCategories) {
            $Options.labels = $DataCategories
        } else {
            $Options.labels = $DataNames
        }
    }
    # X AXIS - CATEGORIES
    $Options.xaxis = [ordered] @{}
    if ($DataCategoriesType -ne '') {
        $Options.xaxis.type = $DataCategoriesType
    }
    if ($DataCategories.Count -gt 0) {
        $Options.xaxis.categories = $DataCategories
    }

    # LINE Definition
    $Options.stroke = [ordered] @{
        show   = $LineShow
        curve  = $LineCurve
        width  = $LineWidth
        colors = @(ConvertFrom-Color -Color $LineColor)
    }


    $Options.legend = @{
        position = 'right'
        offsetY  = 100
        height   = 230
    }

    # title
    $Options.title = [ordered] @{}
    if ($TitleText -ne '') {
        $Options.title.text = $TitleText
    }
    if ($TitleAlignment -ne '') {
        $Options.title.align = $TitleAlignment
    }


    New-ChartSize -Options $Options -Height $Height -Width $Width
    New-ChartToolbar -Options $Options
    New-ApexChart -Positioning $Positioning -Options $Options
}
function New-HTMLChartArea {
    [CmdletBinding()]
    param(
        [nullable[int]] $Height = 350,
        [nullable[int]] $Width,
        [ValidateSet('', 'central')][string] $Positioning,

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

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

        [RGBColors[]] $GridColors,
        [double] $GridOpacity,

        [string] $Title,
        [ValidateSet('center', 'left', 'right', '')][string] $TitleAlignment = '',

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

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

        [int] $MarkerSize,

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

        [switch] $Zoom
    )

    $Options = [ordered] @{}

    New-ChartArea -Options $Options -Data $Data -DataNames $DataNames

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

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

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

    New-ChartAxisY -Options $Options `
        -Title $TitleY

    New-ChartMarker -Options $Options -MarkerSize $MarkerSize

    New-ChartTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment
    New-ChartGridColors -Options $Options -GridColors $GridColors -GridOpacity $GridOpacity

    New-ChartLegend -Options $Options -LegendPosition $LegendPosition

    New-ChartSize -Options $Options -Height $Height -Width $Width

    New-ChartZoom -Options $Options -Enabled:$Zoom
    New-ChartToolbar -Options $Options
    New-ApexChart -Positioning $Positioning -Options $Options
}
function New-HTMLChartBar {
    [CmdletBinding()]
    param(
        [nullable[int]] $Height = 350,
        [nullable[int]] $Width,
        [ValidateSet('', 'central')][string] $Positioning,
        [ValidateSet('bar', 'barStacked', 'barStacked100Percent')] $Type = 'bar',
        <#
 
        [Array] $DataCategories = @(),
        [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category',
 
 
        [bool] $LineShow = $true,
        [ValidateSet('straight', 'smooth')] $LineCurve = 'straight',
        $LineWidth = 2,
        [RGBColors[]] $LineColor,
        #>


        [RGBColors[]] $Colors,

        [switch] $PatternedColors,

        [string] $Title,
        [ValidateSet('center', 'left', 'right', '')][string] $TitleAlignment = '',

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

        [switch] $Distributed,

        #[string] $Formatter,

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

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

    )




    $Options = [ordered] @{}
    New-ChartBar -Options $Options -Horizontal $Horizontal -DataLabelsEnabled $DataLabelsEnabled `
        -DataLabelsOffsetX $DataLabelsOffsetX -DataLabelsFontSize $DataLabelsFontSize -DataLabelsColor $DataLabelsColor `
        -Data $Data -DataNames $DataNames -DataLegend $DataLegend -Title $Title -TitleAlignment $TitleAlignment `
        -Type $Type -Colors $Colors `
        -PatternedColors:$PatternedColors -Distributed:$Distributed


    New-ChartLegend -Options $Options -LegendPosition $LegendPosition
    #-Formatter $Formatter


    <#
    if ('bar', 'line' -contains $Type) {
        # Some types require a more complicated dataset
        $Options.series = @( New-HTMLChartDataSet -Data $Data -DataNames $DataNames )
    } else {
        # Some types of charts require simple data sets - in particular just array
        $Options.series = $Data
        if ($null -ne $DataCategories) {
            $Options.labels = $DataCategories
        } else {
            $Options.labels = $DataNames
        }
    }
 
    # X AXIS - CATEGORIES
    $Options.xaxis = [ordered] @{}
    if ($DataCategoriesType -ne '') {
        $Options.xaxis.type = $DataCategoriesType
    }
    if ($DataCategories.Count -gt 0) {
        $Options.xaxis.categories = $DataCategories
    }
 
    # LINE Definition
    $Options.stroke = [ordered] @{
        show = $LineShow
        curve = $LineCurve
        width = $LineWidth
        colors = @(ConvertFrom-Color -Color $LineColor)
    }
 
 
    $Options.legend = @{
        position = 'right'
        offsetY = 100
        height = 230
    }
 
 
 
#>

    New-ChartSize -Options $Options -Height $Height -Width $Width
    New-ChartToolbar -Options $Options
    New-ApexChart -Positioning $Positioning -Options $Options
}
function New-HTMLChartDataSet {
    [CmdletBinding()]
    param(
        [Array] $Data,
        [Array] $DataNames
    )

    if ($null -ne $Data -and $null -ne $DataNames) {
        if ($Data[0] -is [Array]) {
            # If it's array of Arrays
            if ($Data[0].Count -eq $DataNames.Count) {
                for ($a = 0; $a -lt $Data.Count; $a++) {
                    @{
                        name = $DataNames[$a]
                        data = $Data[$a]
                    }
                }
            } elseif ($Data.Count -eq $DataNames.Count) {
                for ($a = 0; $a -lt $Data.Count; $a++) {
                    @{
                        name = $DataNames[$a]
                        data = $Data[$a]
                    }
                }
            } else {
                # rerun with just data (so it checks another if)
                New-HTMLChartDataSet -Data $Data
            }

        } else {
            if ($null -ne $DataNames) {
                # If it's just int in Array
                @{
                    name = $DataNames
                    data = $Data
                }
            } else {
                @{
                    data = $Data
                }
            }
        }

    } elseif ($null -ne $Data) {
        # No names given
        if ($Data[0] -is [Array]) {
            # If it's array of Arrays
            foreach ($D in $Data) {
                @{
                    data = $D
                }
            }
        } else {
            # If it's just int in Array
            @{
                data = $Data
            }
        }
    } else {
        Write-Warning -Message "New-HTMLChartDataSet - No Data provided. Unabled to create dataset."
        return @{}
    }
}
function New-HTMLChartLine {
    [CmdletBinding()]
    param(
        [nullable[int]] $Height = 350,
        [nullable[int]] $Width,
        [ValidateSet('', 'central')][string] $Positioning,

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

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

        [RGBColors[]] $GridColors,
        [double] $GridOpacity,

        [string] $Title,
        [ValidateSet('center', 'left', 'right', '')][string] $TitleAlignment = '',

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

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

        [int] $MarkerSize,

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

    $Options = [ordered] @{}

    New-ChartLine -Options $Options -Data $Data -DataNames $DataNames

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

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

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

    New-ChartAxisY -Options $Options `
        -Title $TitleY

    New-ChartMarker -Options $Options -MarkerSize $MarkerSize

    New-ChartTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment
    New-ChartGridColors -Options $Options -GridColors $GridColors -GridOpacity $GridOpacity

    New-ChartLegend -Options $Options -LegendPosition $LegendPosition

    New-ChartSize -Options $Options -Height $Height -Width $Width
    New-ChartToolbar -Options $Options
    New-ApexChart -Positioning $Positioning -Options $Options
}
function New-HTMLChartRadial {
    [CmdletBinding()]
    param(
        [nullable[int]] $Height = 350,
        [nullable[int]] $Width,
        [ValidateSet('', 'central')][string] $Positioning,

        <#
        [bool] $Horizontal = $true,
        [bool] $DataLabelsEnabled = $true,
        [int] $DataLabelsOffsetX = -6,
        [string] $DataLabelsFontSize = '12px',
        [nullable[RGBColors]] $DataLabelsColor,
        [Array] $Data = @(),
        [Array] $DataNames = @(),
        [Array] $DataCategories = @(),
        [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category',
        [string] $TitleText,
        [ValidateSet('center', 'left', 'right', '')][string] $TitleAlignment = '',
 
        [bool] $LineShow = $true,
        [ValidateSet('straight', 'smooth')] $LineCurve = 'straight',
        $LineWidth = 2,
        [RGBColors[]] $LineColor,
 
#>


        [Array] $Names,
        [Array] $Values,
        $Type,
        [ValidateSet('FullCircleTop', 'FullCircleBottom', 'FullCircleBottomLeft', 'FullCircleLeft', 'Speedometer', 'SemiCircleGauge')] $CircleType = 'FullCircleTop',
        [string] $LabelAverage
    )

    $Options = [ordered] @{}

    New-ChartRadial -Options $Options -Names $Names -Values $Values -Type $Type
    # This controls how the circle starts / left , right and so on
    New-ChartRadialCircleType -Options $Options -CircleType $CircleType
    # This added label. It's useful if there's more then one data
    New-ChartRadialDataLabels -Options $Options -Label $LabelAverage
    New-ChartSize -Options $Options -Height $Height -Width $Width
    New-ChartToolbar -Options $Options
    New-ApexChart -Positioning $Positioning -Options $Options
}
function New-HTMLChartSpark {
    [CmdletBinding()]
    param(
        [nullable[int]] $Height = 350,
        [nullable[int]] $Width,
        [ValidateSet('', 'central')][string] $Positioning,

        # Data to display in Spark
        [Array] $Data,
        [string] $TitleText,
        [string] $SubTitleText,
        [int] $FontSizeTitle = 24,
        [int] $FontSizeSubtitle = 14,
        [nullable[RGBColors]] $Color
    )

    $Options = [ordered] @{}
    New-ChartSpark -Options $Options -Color $Color -Title $TitleText -SubTitle $SubTitleText -FontSizeTitle $FontSizeTitle -FontSizeSubtitle $FontSizeSubtitle -Values $Data
    New-ChartSize -Options $Options -Height $Height -Width $Width
    New-ChartToolbar -Options $Options

    New-ApexChart -Positioning $Positioning -Options $Options
}


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',
            'standard',
            'classic',
            'bootstrap4',
            'beyond',
            'godzilla',
            'eclipse',
            'mootwo',
            'droide',
            'minimal',
            'atomic',
            'dracula',
            'rowhammer'
        )][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.CodeBlocks = $true
    <# Explanation to fields:
        data-enlighter-language (string) - The language of the codeblock - overrides the global default setting | Block+Inline Content option
        data-enlighter-theme (string) - The theme of the codeblock - overrides the global default setting | Block+Inline Content option
        data-enlighter-group (string) - The identifier of the codegroup where the codeblock belongs to | Block Content option
        data-enlighter-title (string) - The title/name of the tab | Block Content option
        data-enlighter-linenumbers (boolean) - Show/Hide the linenumbers of a codeblock (Values: "true", "false") | Block Content option
        data-enlighter-highlight (string) - A List of lines to point out, comma seperated (ranges are supported) e.g. "2,3,6-10" | Block Content option
        data-enlighter-lineoffset (number) - Start value of line-numbering e.g. "5" to start with line 5 - attribute start of the ol tag is set | Block Content option
    #>


    $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()
    }
    New-HTMLTag -Tag 'pre' -Attributes $Attributes {
        $Code
    }
}
Function New-HTMLContent {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace with Content"),
        [alias('ColumnCount', 'Columns')][ValidateSet('1', '2', '3', '4', '5 ', '6', '7', '8', '9', '10', '11', '12')][string] $Count = 1,
        [Parameter(Mandatory = $false)][string]$HeaderText,
        [RGBColors]$HeaderTextColor = [RGBColors]::White,
        [string][ValidateSet('center', 'left', 'right', 'justify')] $HeaderTextAlignment = 'center',
        [alias('BackgroundShade')][RGBColors]$BackgroundColor,
        [Parameter(Mandatory = $false)][switch] $CanCollapse,
        [Parameter(Mandatory = $false)][switch] $IsHidden,
        [switch] $Collapsed,
        [int] $Height,
        [switch] $Invisible
    )
    $RandomNumber = Get-Random
    $TextHeaderColorFromRGB = ConvertFrom-Color -Color $HeaderTextColor
    if ($null -ne $BackgroundColor) {
        $BackGroundColorFromRGB = ConvertFrom-Color -Color $BackgroundColor
        $BGStyleColor = "background-color:$BackGroundColorFromRGB;"
    } else {
        $BGStyleColor = ''
    }
    if ($CanCollapse) {
        $Script:HTMLSchema.Features.HideSection = $true
        if ($IsHidden) {
            $ShowStyle = "color: $TextHeaderColorFromRGB;" # shows Show button
            $HideStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Hide button
        } else {
            if ($Collapsed) {
                $HideStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Show button
                $ShowStyle = "color: $TextHeaderColorFromRGB;" # shows Hide button
                $HiddenDivStyle = 'display:none;'
            } else {
                $ShowStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Show button
                $HideStyle = "color: $TextHeaderColorFromRGB;" # shows Hide button
            }
        }
    } else {
        if ($IsHidden) {
            $ShowStyle = "color: $TextHeaderColorFromRGB;" # shows Show button
            $HideStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Hide button
        } else {
            $ShowStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Show button
            $HideStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Show button
        }
    }

    if ($Height -ne 0) { $StyleHeight = "height: $($Height)px" }
    $StyleWidth = "width: calc(100% / $Count - 15px)"

    if ($IsHidden) {
        $DivContentStyle = "display:none; $StyleWidth; $StyleHeight; $BGStyleColor"
    } else {
        $DivContentStyle = "$StyleWidth; $StyleHeight; $BGStyleColor"
    }

    $DivHeaderStyle = "text-align: $HeaderTextAlignment;"
    $HeaderStyle = "color: $TextHeaderColorFromRGB;"

    if ($Invisible) {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'defaultContainerOther' } -Value {
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'defaultContainerOther defaultPanelOther' } -Value {
                Invoke-Command -ScriptBlock $Content
            }
        }
    } else {
        # return this HTML
        New-HTMLTag -Tag 'div' -Attributes @{ 'class' = "defaultSection defaultCard"; 'style' = $DivContentStyle } -Value {
            New-HTMLTag -Tag 'div' -Attributes @{ 'class' = "defaultHeader"; 'style' = $DivHeaderStyle  } -Value {
                New-HTMLAnchor -Name $HeaderText -Text $HeaderText -Style $HeaderStyle
                New-HTMLAnchor -Id "show_$RandomNumber" -Href 'javascript:void(0)' -OnClick "show('$RandomNumber');" -Style $ShowStyle -Text '(Show)'
                New-HTMLAnchor -Id "hide_$RandomNumber" -Href 'javascript:void(0)' -OnClick "hide('$RandomNumber');" -Style $HideStyle -Text '(Hide)'
            }
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'defaultContainerOther'; id = $RandomNumber; Style = $HiddenDivStyle } -Value {
                New-HTMLTag -Tag 'div' -Attributes @{ class = 'defaultContainer defaultPanelOther collapsable'; id = $RandomNumber; } -Value {
                    Invoke-Command -ScriptBlock $Content
                }
            }
        }
    }
}
Function New-HTMLHeading {
    [CmdletBinding()]
    Param (
        [validateset('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7')][string]$Heading,
        [string]$HeadingText,
        [validateset('', 'central')][string] $Type,
        [switch] $Underline,
        [nullable[RGBColors]] $Color
    )
    if ($null -ne $Color) {
        $RGBcolor = ConvertFrom-Color -Color $Color
        $Attributes = @{
            style = "color: $RGBcolor;"
        }
    } else {
        $Attributes = @{}
    }
    if ($Type -eq 'central') {
        $Attributes.Class = 'central'
    }
    if ($Underline) {
        $Attributes.Class = "$($Attributes.Class) underline"
    }

    New-HTMLTag -Tag $Heading -Attributes $Attributes {
        $HeadingText
    }
}
function New-HTMLHorizontalLine {
    [CmdletBinding()]
    param()
    New-HTMLTag -Tag 'hr' -SelfClosing
}
function New-HTMLImage {
    <#
    .SYNOPSIS
    Short description
     
    .DESCRIPTION
    Long description
     
    .PARAMETER Source
    Parameter description
     
    .PARAMETER UrlLink
    Parameter description
     
    .PARAMETER AlternativeText
    Parameter description
     
    .PARAMETER Class
    Parameter description
     
    .PARAMETER Target
    Parameter description
     
    .PARAMETER Width
    Parameter description
     
    .PARAMETER Height
    Parameter description
     
    .EXAMPLE
    New-HTMLImage -Source 'https://evotec.pl/image.png' -UrlLink 'https://evotc.pl/' -AlternativeText 'My other text' -Class 'otehr' -Width '100%'
     
    .NOTES
    General notes
    #>

    
    [CmdletBinding()]
    param(
        [string] $Source,
        [Uri] $UrlLink = '',
        [string] $AlternativeText = '',
        [string] $Class = 'Logo',
        [string] $Target = '_blank',
        [string] $Width,
        [string] $Height
    )

    New-HTMLTag -Tag 'div' -Attributes @{ class = $Class.ToLower() } {
        $AAttributes = [ordered]@{
            'target' = $Target
            'href'   = $UrlLink
        }        
        New-HTMLTag -Tag 'a' -Attributes $AAttributes {
            $ImgAttributes = [ordered]@{
                'src'    = "$Source"
                'alt'    = "$AlternativeText"
                'width'  = "$Height"
                'height' = "$Width"
            }
            New-HTMLTag -Tag 'img' -Attributes $ImgAttributes
        }        
    }
}
function New-HTMLList {
    [CmdletBinding()]
    param(
        [ScriptBlock]$ListItems,
        [ValidateSet('Unordered', 'Ordered')] [string] $Type = 'Unordered',
        [RGBColors] $Color,
        [RGBColors] $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
    )

    $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 ((Get-ObjectCount -Object $Entry.Value) -gt 0) {
            $SpanRequired = $true
            break
        }
    }

    if ($SpanRequired) {
        New-HTMLSpanStyle @newHTMLSplat {
            if ($Type -eq 'Unordered') {
                New-HTMLTag -Tag 'ul' {
                    Invoke-Command -ScriptBlock $ListItems
                }
            } else {
                New-HTMLTag -Tag 'ol' {
                    Invoke-Command -ScriptBlock $ListItems
                }
            }
        }
    } else {
        if ($Type -eq 'Unordered') {
            New-HTMLTag -Tag 'ul' {
                Invoke-Command -ScriptBlock $ListItems
            }
        } else {
            New-HTMLTag -Tag 'ol' {
                Invoke-Command -ScriptBlock $ListItems
            }
        }
    }
}
function New-HTMLListItem {
    [CmdletBinding()]
    param(
        [string] $Text,
        [RGBColors[]] $Color = @(),
        [RGBColors[]] $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
    }

    if (($FontSize.Count -eq 0) -or ($FontSize -eq 0)) {
        $Size = ''
    } else {
        $size = "$($FontSize)px"
    }
    $Style = @{
        style = @{
            'color'            = ConvertFrom-Color -Color $Color
            'background-color' = ConvertFrom-Color -Color $BackGroundColor
            'font-size'        = $Size
            'font-weight'      = $FontWeight
            'font-variant'     = $FontVariant
            'font-family'      = $FontFamily
            'font-style'       = $FontStyle
            'text-align'       = $Alignment


            'text-decoration'  = $TextDecoration
            'text-transform'   = $TextTransform
            'direction'        = $Direction
        }
    }

    New-HTMLTag -Tag 'li' -Attributes $Style -Value {
        New-HTMLText @newHTMLTextSplat -SkipParagraph
    }
}
function New-HTMLLogo {
    param(
        [String] $LogoPath,
        [string] $LeftLogoName = "Sample",
        [string] $RightLogoName = "Alternate",
        [string] $LeftLogoString,
        [string] $RightLogoString,
        [switch] $HideLogos
    )

    $LogoSources = Get-HTMLLogos `
        -RightLogoName $RightLogoName `
        -LeftLogoName $LeftLogoName  `
        -LeftLogoString $LeftLogoString `
        -RightLogoString $RightLogoString

    Convert-StyleContent1 -Options $Options


    $Options = [PSCustomObject] @{
        Logos        = $LogoSources
        ColorSchemes = $ColorSchemes
    }

    if ($HideLogos -eq $false) {
        $Leftlogo = $Options.Logos[$LeftLogoName]
        $Rightlogo = $Options.Logos[$RightLogoName]
        '<!-- START LOGO -->'
        $LogoContent = @"
        <table><tbody>
        <tr>
            <td class="clientlogo"><img src="$Leftlogo" /></td>
            <td class="MainLogo"><img src="$Rightlogo" /></td>
        </tr>
        </tbody></table>
"@

        $LogoContent
        '<!-- END LOGO -->'
     
    }       
}
function New-HTMLMessage {
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('')][ScriptBlock] $Content,
        $Text
    )
    $Script:HTMLSchema.Features.Message = $true
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'message green' } {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'message-icon' } {
            New-HTMLTag -Tag 'i' -Attributes @{ class = 'fa fa-bell-o'} 
        }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'message-body' } {
            New-HTMLTag -Tag 'p' -Attributes @{ class = '' } {
                $Text
            }
        }
    }
}
Function New-HTMLPanel {
    [alias('New-HTMLColumn')]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace with Content"),
        [alias('ColumnCount', 'Columns')][ValidateSet('1', '2', '3', '4', '5 ', '6', '7', '8', '9', '10', '11', '12')][string] $Count = 1,
        [alias('BackgroundShade')][RGBColors]$BackgroundColor,
        [switch] $Invisible,
        [int] $Height
    )
    if ($Height -ne 0) { 
        $StyleHeight = "height: $($Height)px" 
    }    
    $StyleWidth = "width: calc(100% / $Count - 10px)"

    if ($null -ne $BackgroundColor) {
        $BackGroundColorFromRGB = ConvertFrom-Color -Color $BackgroundColor
        $DivColumnStyle = "background-color:$BackGroundColorFromRGB; $StyleWidth; $StyleHeight;"
    } else {
        $DivColumnStyle = "$StyleWidth; $StyleHeight;"
    }
    if ($Invisible) {
        $DivColumnStyle = "$DivColumnStyle box-shadow: unset !important;"
    }
    New-HTMLTag -Tag 'div' -Attributes @{ class = "defaultPanel defaultCard"; style = $DivColumnStyle } {
        Invoke-Command -ScriptBlock $Content
    }
}
function New-HTMLResourceCSS {
    [alias('New-ResourceCSS', 'New-CSS')]
    [CmdletBinding()]
    param(
        [alias('ScriptContent')][Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content,
        [string] $Link,
        [string] $ResourceComment,
        [string] $FilePath
    
    )
    $Output = @(
        "<!-- CSS $ResourceComment START -->"
        foreach ($File in $FilePath) {
            if ($File -ne '') {
                if (Test-Path -LiteralPath $File) {                
                    New-HTMLTag -Tag 'style' -Attributes @{ type = 'text/css' } {    
                        Write-Verbose "New-HTMLResourceCSS - Reading file from $File"        
                        Get-Content -LiteralPath $File           
                    }
                } 
            }
        }
        foreach ($L in $Link) {   
            if ($L -ne '') {
                Write-Verbose "New-HTMLResourceCSS - Adding link $L"   
                New-HTMlTag -Tag 'link' -Attributes @{ rel = "stylesheet"; type = "text/css"; href = $L } -SelfClosing
            }
        }
        "<!-- CSS $ResourceComment END -->"
    )
    if ($Output.Count -gt 2) {
        # Outputs only if more than comments
        $Output
    } 
}
function New-HTMLResourceJS {
    [alias('New-ResourceJS', 'New-JavaScript')]
    [CmdletBinding()]
    param(
        [alias('ScriptContent')][Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content,
        [string[]] $Link,
        [string] $ResourceComment,
        [string[]] $FilePath
    )   
    $Output = @(
        "<!-- JS $ResourceComment START -->"
        foreach ($File in $FilePath) {
            if ($File -ne '') {
                if (Test-Path -LiteralPath $File) {
                    New-HTMLTag -Tag 'script' -Attributes @{ type = 'text/javascript' } {            
                        Get-Content -LiteralPath $File          
                    }
                } else {
                    return
                }
            } 
        }
        foreach ($L in $Link) {        
            if ($L -ne '') {
                New-HTMlTag -Tag 'script' -Attributes @{ type = "text/javascript"; src = $L }
            } else {
                return
            }
        }
        "<!-- JS $ResourceComment END -->"
    )
    if ($Output.Count -gt 2) {
        # Outputs only if more than comments
        $Output
    } 
}
function New-HTMLSpanStyle {
    [CmdletBinding()]
    param(
        [ScriptBlock] $Content,
        [nullable[RGBColors]] $Color,
        [nullable[RGBColors]] $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
    )
    Write-Verbose 'New-SpanStyle - BEGIN'
    if ($FontSize -eq 0) {
        $Size = ''
    } else {
        $size = "$($FontSize)px"
    }
    $Style = @{
        style = @{
            'color'            = ConvertFrom-Color -Color $Color
            'background-color' = ConvertFrom-Color -Color $BackGroundColor
            'font-size'        = $Size
            'font-weight'      = $FontWeight
            'font-variant'     = $FontVariant
            'font-family'      = $FontFamily
            'font-style'       = $FontStyle
            'text-align'       = $Alignment


            'text-decoration'  = $TextDecoration
            'text-transform'   = $TextTransform
            'direction'        = $Direction
        }
    }

    if ($Alignment) {
        $StyleDiv = @{}
        $StyleDiv.Align = $Alignment

        New-HTMLTag -Tag 'div' -Attributes $StyleDiv {
            New-HTMLTag -Tag 'span' -Attributes $Style {
                Invoke-Command -ScriptBlock $Content
            }
        }
    } else {
        New-HTMLTag -Tag 'span' -Attributes $Style {
            Invoke-Command -ScriptBlock $Content
        }
    }
    # if ($LineBreak) {
    # Write-Verbose 'New-SpanStyle - BR'
    # New-HTMLTag -Tag 'br' -SelfClosing
    # }


}
function New-HTMLStatus {
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('')][ScriptBlock] $Content
    )
    $Script:HTMLSchema.Features.StatusButtonical = $true
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'buttonicalService' } {
        #New-HTMLTag -Tag 'div' -Attributes @{ class = 'buttonical-align' } {
        Invoke-Command -ScriptBlock $Content
        # }
    }
    
}
function New-HTMLStatusItem {
    param(
        $ServiceName,
        $ServiceStatus,
        [ValidateSet('Dead', 'Bad', 'Good')]$Icon = 'Good',
        [ValidateSet('0%', '10%', '30%', '70%', '100%')][string] $Percentage = '100%'
    )
    #$Script:HTMLSchema.Features.StatusButtonical = $true
    if ($Icon -eq 'Dead') {
        $IconType = 'performanceDead'
    } elseif ($Icon -eq 'Bad') { 
        $IconType = 'performanceProblem'
    } elseif ($Icon -eq 'Good') {
        #$IconType = 'performance'
    }

    if ($Percentage -eq '100%') {
        $Colors = 'background-color: #0ef49b;'
    } elseif ($Percentage -eq '70%') {
        $Colors = 'background-color: #d2dc69;'
    } elseif ($Percentage -eq '30%') {
        $Colors = 'background-color: #faa04b;'
    } elseif ($Percentage -eq '10%') {
        $Colors = 'background-color: #ff9035;'
    } elseif ($Percentage -eq '0%') {
        $Colors = 'background-color: #ff5a64;'
    }

    New-HTMLTag -Tag 'div' -Attributes @{ class = 'buttonical'; style = $Colors } -Value {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'label' } {
            New-HTMLTag -Tag 'span' -Attributes @{ class = 'performance' } {
                $ServiceName
            }
        }
        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 $IconType" } {
                $ServiceStatus
            }            
        }
    }
}
function New-HTMLTab {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $HtmlData = $(Throw "No curly brace?)"),
        [Parameter(Mandatory = $false, Position = 1)][String]$TabHeading,
        [alias('Name')][string] $TabName
    )
    $Script:HTMLSchema.Features.Tabs = $true
    $Script:HTMLSchema.Features.JQuery = $true

    # Reset all Tabs Headers to make sure there are no Current Tab Set
    # This is required for New-HTMLTable
    foreach ($Tab in $Script:HTMLSchema.TabsHeaders) {
        $Tab.Current = $false
    }

    # Start Tab Tracking
    $Tab = @{}
    $Tab.ID = "Tab-$(Get-RandomStringName -Size 8)"
    $Tab.Name = $TabName
    #$Tab.Used = $true
    $Tab.Current = $true
    if ($Script:HTMLSchema.TabsHeaders | Where-Object { $_.Active -eq $true }) {
        $Tab.Active = $false
    } else {
        $Tab.Active = $true
    }
    $Script:HTMLSchema.TabsHeaders.Add($Tab)
    # End Tab Tracking


    # This is building HTML
    if ($Tab.Active) {
        $Class = 'tabs-content active'
    } else {
        $Class = 'tabs-content'
    }

    New-HTMLTag -Tag 'div' -Attributes @{ id = $Tab.ID; class = $Class } {
        if (-not [string]::IsNullOrWhiteSpace($TabHeading)) {
            New-HTMLTag -Tag 'h7' {
                $TabHeading
            }
        }
        Invoke-Command -ScriptBlock $HtmlData
    }
}
function New-HTMLTabHead {
    [CmdletBinding()]
    Param (
        
    )    
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'tabsWrapper' } {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'tabs' } {
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'selector' }
            foreach ($Tab in $Script:HTMLSchema.TabsHeaders) {
           
                if ($Tab.Active) {
                    $AttributesA = @{
                        'class'   = 'active'
                        'href'    = 'javascript:void(0)'
                        'data-id' = "$($Tab.Id)"
                    }

                    New-HTMLTag -Tag 'a' -Attributes $AttributesA {
                        New-HTMLTag -Tag 'i' -Attributes @{ class = 'fas fa-bomb' } -Value { $Tab.Name }
                    }
                    #New-HTMLAnchor -Class 'active' -HrefLink "#$($Tab.Id)" -Text $Tab.Name
                } else {
                    # New-HTMLAnchor -Class '' -HrefLink "#" -Text $Tab.Name

                    $AttributesA = @{
                        'class'   = ''
                        'href'    = 'javascript:void(0)'
                        'data-id' = "$($Tab.Id)"
                    }

                    New-HTMLTag -Tag 'a' -Attributes $AttributesA {
                        New-HTMLTag -Tag 'i' -Attributes @{ class = 'fas fa-bomb' } -Value { $Tab.Name }
                    }
                }
            }            
        }
    }
}
function New-HTMLTable {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $ConditionalFormatting,
        [alias('ArrayOfObjects', 'Object', 'Table')][Array] $DataTable,
        #[Array] $ConditionalFormatting,
        [string[]][ValidateSet('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5')] $Buttons = @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5'),
        [string[]][ValidateSet('numbers', 'simple', 'simple_numbers', 'full', 'full_numbers', 'first_last_numbers')] $PagingStyle = 'full_numbers',
        [int[]]$PagingOptions = @(15, 25, 50, 100),
        [switch]$DisablePaging,
        [switch]$DisableOrdering,
        [switch]$DisableInfo,
        [switch]$HideFooter,
        [switch]$DisableColumnReorder,
        [switch]$DisableProcessing,
        [switch]$DisableResponsiveTable,
        [switch]$DisableSelect,
        [switch]$DisableStateSave,
        [switch]$DisableSearch,
        [switch]$ScrollCollapse,
        [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.',
        [int] $ScreenSizePercent = 0,
        [string[]] $DefaultSortColumn,
        [int[]] $DefaultSortIndex,
        [ValidateSet('Ascending', 'Descending')][string] $DefaultSortOrder = 'Ascending',
        [alias('Search')][string]$Find
    )
    # Theme creator https://datatables.net/manual/styling/theme-creator

    [string] $DataTableName = "DT-$(Get-RandomStringName -Size 8 -LettersOnly)" # this builds table ID
    if ($null -eq $DataTable -or $DataTable.Count -eq 0) {
        #return ''
        $DataTable = $TextWhenNoData
    }
    if ($DataTable[0] -is [System.Collections.IDictionary]) {
        Write-Verbose 'New-HTMLTable - Working with IDictionary'
        [Array ] $Table = $($DataTable).GetEnumerator() | Select-Object Name, Value | ConvertTo-Html -Fragment | Select-Object -SkipLast 1 | Select-Object -Skip 2 # This removes table tags (open/closing)
    } elseif ($DataTable[0] -is [string]) {
        [Array] $Table = $DataTable | ForEach-Object { [PSCustomObject]@{ 'Name' = $_ } } | ConvertTo-Html -Fragment | Select-Object -SkipLast 1 | Select-Object -Skip 2
    } else {
        Write-Verbose 'New-HTMLTable - Working with Objects'
        [Array] $Table = $DataTable | ConvertTo-Html -Fragment | Select-Object -SkipLast 1 | Select-Object -Skip 2 # This removes table tags (open/closing)
    }
    [string] $Header = $Table | Select-Object -First 1 # this gets header
    [string[]] $HeaderNames = $Header -replace '</th></tr>' -replace '<tr><th>' -split '</th><th>'
    $Table = $Table | Select-Object -Skip 1 # this gets actuall table content

    $Options = [ordered] @{
        <# DOM Definition: https://datatables.net/reference/option/dom
            l - length changing input control
            f - filtering input
            t - The table!
            i - Table information summary
            p - pagination control
            r - processing display element
            B - Buttons
            S - Select
        #>

        dom              = 'Bfrtip'
        buttons          = @($Buttons)
        "colReorder"     = -not $DisableColumnReorder.IsPresent


        # https://datatables.net/examples/basic_init/scroll_y_dynamic.html
        "paging"         = -not $DisablePaging
        "scrollCollapse" = $ScrollCollapse.IsPresent

        <# Paging Type
            numbers - Page number buttons only
            simple - 'Previous' and 'Next' buttons only
            simple_numbers - 'Previous' and 'Next' buttons, plus page numbers
            full - 'First', 'Previous', 'Next' and 'Last' buttons
            full_numbers - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
            first_last_numbers - 'First' and 'Last' buttons, plus page numbers
        #>

        "pagingType"     = $PagingStyle
        "lengthMenu"     = @(
            @($PagingOptions, -1)
            @($PagingOptions, "All")
        )
        "ordering"       = -not $DisableOrdering.IsPresent
        "order"          = @() # this makes sure there's no default ordering upon start (usually it would be 1st column)
        "info"           = -not $DisableInfo.IsPresent
        "procesing"      = -not $DisableProcessing.IsPresent
        "responsive"     = @{
            details = -not $DisableResponsiveTable.IsPresent
        }
        "select"         = -not $DisableSelect.IsPresent
        "searching"      = -not $DisableSearch.IsPresent
        "stateSave"      = -not $DisableStateSave.IsPresent
    }

    if ($OrderMulti) {
        $Options.orderMulti = $OrderMulti.IsPresent
    }
    if ($Find -ne '') {
        $Options.search = @{
            search = $Find
        }
    }

    # Sorting
    if ($DefaultSortOrder -eq 'Ascending') {
        $Sort = 'asc'
    } else {
        $Sort = 'dsc'
    }
    if ($DefaultSortColumn.Count -gt 0) {
        $ColumnsOrder = foreach ($Column in $DefaultSortColumn) {
            $DefaultSortingNumber = ($HeaderNames).ToLower().IndexOf($Column.ToLower())
            if ($DefaultSortingNumber -ne -1) {
                , @($DefaultSortingNumber, $Sort)
            }
        }

    }
    if ($DefaultSortIndex.Count -gt 0 -and $DefaultSortColumn.Count -eq 0) {
        $ColumnsOrder = foreach ($Column in $DefaultSortIndex) {
            if ($Column -ne -1) {
                , @($Column, $Sort)
            }
        }
        $Options."order" = @($ColumnsOrder)
        # there seems to be a bug in ordering and colReorder plugin
        # Disabling colReorder
        $Options.colReorder = $false
    }

    # Overwriting table size - screen size in percent. With added Section/Panels it shouldn't be more than 90%
    if ($ScreenSizePercent -gt 0) {
        $Options."scrollY" = "$($ScreenSizePercent)vh"
    }
    if ($null -ne $ConditionalFormatting) {
        #.Count -gt 0) {
        $Options.columnDefs = ''
    }
    $Options = $Options | ConvertTo-Json -Depth 6

    if ($null -ne $ConditionalFormatting) {
        $Conditional = Invoke-Command -ScriptBlock $ConditionalFormatting
    }
    # Process Conditional Formatting. Ugly JS building
    $Options = New-TableConditionalFormatting -Options $Options -ConditionalFormatting $Conditional -Header $HeaderNames


    [Array] $Tabs = ($Script:HTMLSchema.TabsHeaders | Where-Object { $_.Current -eq $true })
    if ($Tabs.Count -eq 0) {
        # There are no tabs in use, pretend there is only one Active Tab
        $Tab = @{ Active = $true }
    } else {
        # Get First Tab
        $Tab = $Tabs[0]
    }

    # return data
    if (-not $Simplify) {
        $Script:HTMLSchema.Features.DataTables = $true
        $Script:HTMLSchema.Features.DataTablesPDF = $true
        $Script:HTMLSchema.Features.DataTablesExcel = $true

        $TableAttributes = @{ id = $DataTableName; class = "$($Style -join ' ')" }

        $FilteringOutput = Add-TableFiltering -Filtering $Filtering -FilteringLocation $FilteringLocation
        $FilteringTopCode = $FilteringOutput.FilteringTopCode
        $FilteringBottomCode = $FilteringOutput.FilteringBottomCode

        if ($Tab.Active -eq $true) {
            New-HTMlTag -Tag 'script' {
                @"
                `$(document).ready(function() {
                    $FilteringTopCode
                    // Table code
                    var table = `$('#$DataTableName').DataTable(
                        $($Options)
                    );
                    $FilteringBottomCode
                });
"@

            }
        } else {
            [string] $TabName = $Tab.Id
            New-HTMlTag -Tag 'script' {
                @"
                    `$(document).ready(function() {
                        `$('.tabs').on('click', 'a', function (event) {
                            if (`$(event.currentTarget).attr("data-id") == "$TabName" && !$.fn.dataTable.isDataTable("#$DataTableName")) {
                                $FilteringTopCode
                                // Table code
                                var table = `$('#$DataTableName').DataTable(
                                    $($Options)
                                );
                                $FilteringBottomCode
                            };
                        });
                    });
"@

            }
        }
    } else {
        $TableAttributes = @{ class = 'sortable' }
    }
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'defaultPanelOther' } -Value {
        # Build HTML TABLE
        New-HTMLTag -Tag 'table' -Attributes $TableAttributes {
            New-HTMLTag -Tag 'thead' {
                $Header
            }
            New-HTMLTag -Tag 'tbody' {
                $Table
            }
            if (-not $HideFooter) {
                New-HTMLTag -Tag 'tfoot' {
                    $Header
                }
            }
        }
    }
}
function New-HTMLTableCondition {
    [CmdletBinding()]
    param(
        [alias('ColumnName')][string] $Name,
        [ValidateSet('number', 'string')][string] $Type,
        [ValidateSet('lt', 'le', 'eq', 'ge', 'gt')][string] $Operator,
        [Object] $Value,
        [switch] $Row,
        [nullable[RGBColors]] $Color,
        [nullable[RGBColors]] $BackgroundColor
    )
    return [PSCustomObject] @{ Row = $Row; Type = $Type ; Name = $Name; Operator = $Operator; Value = $Value; Color = $Color; BackgroundColor = $BackgroundColor }
}
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
    )
    $HTMLTag = [Ordered] @{
        Tag         = $Tag
        Attributes  = $Attributes
        Value       = if ($null -eq $Value) { '' } else { Invoke-Command -ScriptBlock $Value }
        SelfClosing = $SelfClosing
    }
    $HTML = Set-Tag -HtmlObject $HTMLTag
    return $HTML
}
function New-HTMLText {
    [CmdletBinding()]
    param(
        [string[]] $Text,
        [RGBColors[]] $Color = @(),
        [RGBColors[]] $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,
        [switch] $SkipParagraph
    )
    #Write-Verbose 'New-HTMLText - Processing...'
    $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] }


    $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]
        }

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

        $newSpanTextSplat.LineBreak = $LineBreak
        New-HTMLSpanStyle @newSpanTextSplat {
            if ($Text[$i] -match "\[([^\[]*)\)") {
                # Covers markdown LINK "[somestring](https://evotec.xyz)"
                $RegexBrackets1 = [regex] "\[([^\[]*)\]" # catch 'sometstring'
                $RegexBrackets2 = [regex] "\(([^\[]*)\)" # catch link
                $RegexBrackets3 = [regex] "\[([^\[]*)\)" # catch both somestring and link
                $Text1 = $RegexBrackets1.match($Text[$i]).Groups[1].value
                $Text2 = $RegexBrackets2.match($Text[$i]).Groups[1].value
                $Text3 = $RegexBrackets3.match($Text[$i]).Groups[0].value
                if ($Text1 -ne '' -and $Text2 -ne '') {
                    $Link = New-HTMLAnchor -HrefLink $Text2 -Text $Text1
                    $Text[$i].Replace($Text3, $Link)
                }
            } else {
                # Default
                $Text[$i]
            }
        }
    }

    if ($SkipParagraph) {
        $Output -join ''
    } else {
        New-HTMLTag -Tag 'div' {
            $Output
        }
    }
    if ($LineBreak) {
        New-HTMLTag -Tag 'br' -SelfClosing
    }
}
function New-HTMLTimeline {
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('TimeLineItems')][ScriptBlock] $Content
    )
    $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,
        [nullable[RGBColors]] $Color
    )
    $Attributes = @{
        class     = 'timelineSimple-item'
        "date-is" = $Date
    }

    if ($null -ne $Color) {
        $RGBcolor = ConvertFrom-Color -Color $Color
        $Style = "color: $RGBcolor;"
    } else {
        $Style = ''
    }
    # $Script:HTMLSchema.Features.TimeLine = $true
    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>'
        }
    }
}
function New-HTMLToast {
    [CmdletBinding()]
    param(
        [string] $TextHeader,
        [string] $Text,
        [ValidateSet('Green', 'Blue', 'Orange')] $Color = "Green",
        [ValidateSet('Success', 'Information', 'Exclamation')][string] $Icon = 'Success'
    )
    $Script:HTMLSchema.Features.Toast = $true
    $DivClass = "toast $($Color.ToLower()) central" 
    New-HTMLTag -Tag 'div' -Attributes @{ class = $DivClass } {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'toast__icon' } {
            if ($Icon -eq 'Success') {
                New-IconSuccess
            } elseif ($Icon -eq 'Information') {
                New-IconInfo
            } elseif ($Icon -eq 'Exclamation') {
                New-IconExclamation
            }
        }
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'toast__content' } {
            New-HTMLTag -Tag 'p' -Attributes @{ class = 'toast__type' } {
                $TextHeader
            }
            New-HTMLTag -Tag 'p' -Attributes @{ class = 'toast__message' } {
                $Text
            }
        }
    }
}
function New-IconExclamation {
    <#
    .SYNOPSIS
    This function is used for New-HTMLToast
     
    .DESCRIPTION
    Long description
     
    .EXAMPLE
    An example
     
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param()
    $SvgAttributes = [ordered] @{
        version       = "1.1"
        class         = "toast__svg"
        xmlns         = "http://www.w3.org/2000/svg"
        'xmlns:xlink' = "http://www.w3.org/1999/xlink"
        x             = "0px"
        y             = "0px"
        viewBox       = "0 0 301.691 301.691"
        style         = "enable-background:new 0 0 301.691 301.691;"
        'xml:space'   = "preserve"
    }
    New-HTMLTag -Tag 'svg' -Attributes $SvgAttributes {
        $Points = @{
            points = "119.151,0 129.6,218.406 172.06,218.406 182.54,0 "
        }
        New-HTMLTag -Tag 'polygon' -Attributes $Points 
        $React = @{
            x      = "130.563"
            y      = "261.168"
            width  = "40.525"
            height = "40.523"
        }
        New-HTMLTag -Tag 'react' -Attributes $React
    }
}
function New-IconInfo {
    <#
    .SYNOPSIS
    This function is used for New-HTMLToast
     
    .DESCRIPTION
    Long description
     
    .EXAMPLE
    An example
     
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param()
    $SvgAttributes = [ordered] @{
        version       = "1.1"
        class         = "toast__svg"
        xmlns         = "http://www.w3.org/2000/svg"
        'xmlns:xlink' = "http://www.w3.org/1999/xlink"
        x             = "0px"
        y             = "0px"
        viewBox       = "0 0 32 32"
        style         = "enable-background:new 0 0 32 32;"
        'xml:space'   = "preserve"
    }
    New-HTMLTag -Tag 'svg' -Attributes $SvgAttributes {
        $PathAttributes = @{
            d = "M10,16c1.105,0,2,0.895,2,2v8c0,1.105-0.895,2-2,2H8v4h16v-4h-1.992c-1.102,0-2-0.895-2-2L20,12H8 v4H10z"        
        }
        New-HTMLTag -Tag 'path' -Attributes $PathAttributes
        New-HTMLTag -Tag 'circle' -Attributes @{ cx = "16"; cy = "4"; r = "4"; }
    }
}
function New-IconSuccess {
    <#
    .SYNOPSIS
    This function is used for New-HTMLToast
     
    .DESCRIPTION
    Long description
     
    .EXAMPLE
    An example
     
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param()
    $SvgAttributes = [ordered] @{
        version       = "1.1"
        class         = "toast__svg"
        xmlns         = "http://www.w3.org/2000/svg"
        'xmlns:xlink' = "http://www.w3.org/1999/xlink"
        x             = "0px"
        y             = "0px"
        viewBox       = "0 0 512 512"
        style         = "enable-background:new 0 0 512 512;"
        'xml:space'   = "preserve"
    }
    New-HTMLTag -Tag 'svg' -Attributes $SvgAttributes {
        $PathAttributes = @{
            d = "M504.502,75.496c-9.997-9.998-26.205-9.998-36.204,0L161.594,382.203L43.702,264.311c-9.997-9.998-26.205-9.997-36.204,0 c-9.998,9.997-9.998,26.205,0,36.203l135.994,135.992c9.994,9.997,26.214,9.99,36.204,0L504.502,111.7 C514.5,101.703,514.499,85.494,504.502,75.496z"
        }
        New-HTMLTag -Tag 'path' -Attributes $PathAttributes
    }
}
function New-TableConditionalFormatting {
    [CmdletBinding()]
    param(
        [string] $Options,
        [Array] $ConditionalFormatting,
        [string[]] $Header
    )

    if ($ConditionalFormatting.Count -gt 0) {
        # Conditional - changes PowerShellOperator into JS operator
        foreach ($Formatting in $ConditionalFormatting) {
            if ($Formatting.Operator -eq 'gt') {
                $Formatting.Operator = '>'
            } elseif ($Formatting.Operator -eq 'lt') {
                $Formatting.Operator = '<'
            } elseif ($Formatting.Operator -eq 'eq') {
                $Formatting.Operator = '=='
            } elseif ($Formatting.Operator -eq 'le') {
                $Formatting.Operator = '<='
            } elseif ($Formatting.Operator -eq 'ge') {
                $Formatting.Operator = '>='
            }
        }
        <#
        $ConditionHeaderNr = $CleanHeader.IndexOf('Name')
 
        $Condition1 = "columnDefs: [{targets: $ConditionHeaderNr, render: function ( data, type, row ) {"
        $Condition2 = "var color = 'black'; var backgroundColor = undefined;"
 
        $Condition3 = foreach ($Condition in $ConditionalFormatting) {
            "if (data $($Condition.Operator) $($Condition.Value)) {"
            if ($null -ne $Condition.Color) {
                $RGBColor = (ConvertFrom-Color -Color $Condition.Color)
                "color = '$RGBColor';"
            }
            if ($null -ne $Condition.BackgroundColor) {
                $RGBBackgroundColor = (ConvertFrom-Color -Color $Condition.BackgroundColor)
                "backgroundColor = '$RGBBackgroundColor';"
            }
            "}"
 
        }
        if ($null -ne $Condition.Color -and $null -ne $Condition.BackgroundColor) {
            # Background and Color Both
            $Condition4 = "return '<span style=`"color:' + color + ';background-color:' + backgroundColor + '`">' + data + '</span>';"
        } elseif ($null -ne $Condition.Color) {
            # Color Only
            $Condition4 = "return '<span style=`"color:' + color + '`">' + data + '</span>';"
        } else {
            # Background Color Only
            $Condition4 = "return '<span style=`"background-color:' + backgroundColor + '`">' + data + '</span>';"
        }
 
        $Condition5 = '},}]'
        #>


        $Condition1 = '"createdRow": function (row, data, dataIndex, column) {'

        $Condition3 = foreach ($Condition in $ConditionalFormatting) {
            $ConditionHeaderNr = $Header.ToLower().IndexOf($($Condition.Name.ToLower()))
            # reset to defaults
            [string] $ColorJSDefinition = ''
            [string] $ColorBackgroundJSDefinition = ''

            if ($null -ne $Condition.Color) {
                $RGBColor = (ConvertFrom-Color -Color $Condition.Color)
                $C = @{ "color" = $RGBColor } | ConvertTo-Json
                $ColorJSDefinition = ".css($C)"
            }
            if ($null -ne $Condition.BackgroundColor) {
                $RGBBackgroundColor = (ConvertFrom-Color -Color $Condition.BackgroundColor)
                $BG = @{ "background-color" = $RGBBackgroundColor } | ConvertTo-Json
                $ColorBackgroundJSDefinition = ".css($BG)"
            }

            if ($null -eq $Condition.Type -or $Condition.Type -eq 'number') {
                "if (data[$ConditionHeaderNr] $($Condition.Operator) $($Condition.Value)) {"
            } elseif ($Condition.Type -eq 'string') {
                "if (data[$ConditionHeaderNr] $($Condition.Operator) '$($Condition.Value)') {"
            } elseif ($Condition.Type -eq 'date') {
                "if (new Date(data[$ConditionHeaderNr]) $($Condition.Operator) new Date('$($Condition.Value)')) {"
            }




            if ($null -ne $Condition.Row -and $Condition.Row -eq $true) {
                "`$(column)$($ColorJSDefinition)$($ColorBackgroundJSDefinition);"
            } else {
                "`$(column[$ConditionHeaderNr])$($ColorJSDefinition)$($ColorBackgroundJSDefinition);"
            }



            "}"
        }

        $Condition5 = '}'

        $Test = $Condition1 + $Condition3 + $Condition5
        $Options = $Options -Replace ('"columnDefs": ""', $Test)
    }

    return $Options
}

<# Best solution
"createdRow": function (row, data, dataIndex, column) {
    if (data[2] == 'Normal') {
        $(column[2]).css({ "color": "red"}).css({'background-color':'green' })
    }
}
# Other
columnDefs: [{
    targets: 0,
    render: function (data, type, row) {
        var color = 'black';
        var backgroundColor = undefined;
        if (data > 10000) {
            color = '#ff0000';
            backgroundColor = '#008000';
        }
        if (data < 10000) {
            color = '#ffff00';
            backgroundColor = '#008000';
        }
        if (data > 20000) {
            color = '#008000';
            backgroundColor = '#008000';
        }
        return '<span style="color:' + color +
            ';background-color:' + backgroundColor + '">' +
            data + '</span>';
    },
}]
#>

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(ValueFromPipeline = $true, Mandatory = $true)] $Table,
        [string[]] $PriorityProperties,
        [string] $Title = 'Out-HTMLView',
        [string[]] $DefaultSortColumn,
        [int[]] $DefaultSortIndex,
        [string] $FilePath,
        [switch] $DisablePaging,
        [switch] $PassThru,
        [switch]$Filtering,
        [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom',
        [alias('Search')][string]$Find
    )
    Begin {
        $DataTable = [System.Collections.Generic.List[Object]]::new()
        if ($FilePath -eq '') {
            $FilePath = Get-FileName -Extension 'html' -Temporary
        }
    }
    Process {
        foreach ($T in $Table) {
            $DataTable.Add($T)
        }
    }
    End {
        # Code responsible for Priority Properties
        if ($DataTable.Count -gt 0) {
            $Properties = $DataTable[0].PSObject.Properties.Name
            $RemainingProperties = foreach ($Property in $Properties) {
                if ($PriorityProperties -notcontains $Property) {
                    $Property
                }
            }
            $AllProperties = $PriorityProperties + $RemainingProperties
            $DataTable = $DataTable | Select-Object -Property $AllProperties
        }
        # HTML generation part
        New-HTML -FilePath $FilePath -UseCssLinks -UseJavaScriptLinks -TitleText $Title -ShowHTML {
            New-HTMLTable -DataTable $DataTable -DefaultSortColumn $DefaultSortColumn -DefaultSortIndex $DefaultSortIndex `
                -DisablePaging:$DisablePaging -Filtering:$Filtering -FilteringLocation $FilteringLocation `
                -Find $Find
        }
        if ($PassThru) {
            # This isn't really real PassThru but just passing final object further down the pipe when needed
            # real PassThru requires significant work - if you're up to it, let me know.
            $DataTable
        }
    }
}
Function Save-HTML {
    <#
    .SYNOPSIS
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER FilePath
    Parameter description
 
    .PARAMETER HTML
    Parameter description
 
    .PARAMETER ShowReport
    Parameter description
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>


    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $false)][string]$FilePath,
        [Parameter(Mandatory = $true)][Array] $HTML,
        [Parameter(Mandatory = $false)][switch]$ShowHTML,
        [ValidateSet('Unknown', 'String', 'Unicode', 'Byte', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'Ascii', 'Default', 'Oem', 'BigEndianUTF32')] $Encoding = 'UTF8',
        [bool] $Supress = $true
    )
    if ([string]::IsNullOrEmpty($FilePath)) {
        $FilePath = Get-FileName -Temporary -Extension 'html'
        Write-Warning "Save-HTML - FilePath parameter $FilePath is empty, using Temporary $FilePath"
    } else {
        if (Test-Path -LiteralPath $FilePath) {
            Write-Warning "Save-HTML - Path $FilePath already exists. Report will be overwritten."
        }
    }
    Write-Verbose "Save-HTML - Saving HTML to file $FilePath"
    try {
        $HTML | Set-Content -LiteralPath $FilePath -Force -Encoding $Encoding
        if (-not $Supress) {
            $FilePath
        }
    } catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        Write-Warning "Save-HTML - Failed with error: $ErrorMessage"
    }
    if ($ShowHTML) {
        try {
            Invoke-Item -LiteralPath $FilePath -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            Write-Warning "Save-HTML - couldn't open file $FilePath in a browser. Error: $ErrorMessage"
        }
    }

}
$Script:Configuration = [ordered] @{
    Features = [ordered] @{
        Default          = @{
            Comment      = 'Always Required Default Visual Settings'
            HeaderAlways = @{
                Css = "$PSScriptRoot\Resources\CSS\default.css"
            }
        }
        DefaultHeadings  = @{
            Comment      = 'Always Required Default Headings'
            HeaderAlways = @{
                Css = "$PSScriptRoot\Resources\CSS\headings.css"
            }
        }
        Accordion        = @{
            Comment      = 'Accordion'
            HeaderAlways = @{
                Css = "$PSScriptRoot\Resources\CSS\accordion-1.css"
            }
        }
        CodeBlocks       = @{
            Comment      = 'EnlighterJS CodeBlocks'
            Header       = @{
                CssLink = 'https://evotec.xyz/wp-content/uploads/pswritehtml/enlighterjs.min.css'
                Css     = "$PSScriptRoot\Resources\CSS\enlighterjs.min.css"
                JsLink  = 'https://evotec.xyz/wp-content/uploads/pswritehtml/enlighterjs.min.js'
                JS      = "$PSScriptRoot\Resources\JS\enlighterjs.min.js"
            }
            Footer       = @{

            }
            HeaderAlways = @{

            }
            FooterAlways = @{
                JS = "$PSScriptRoot\Resources\JS\enlighterjs-footer.js"
            }
        }
        Charts           = @{
            Comment = 'Charts JS'
            Header  = @{
                JsLink = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.bundle.min.js'
            }
        }
        ChartsApex       = @{
            Comment      = 'Apex Charts'
            Header       = @{
                JsLink = 'https://cdn.jsdelivr.net/npm/apexcharts@latest'
                JS     = "$PSScriptRoot\Resources\JS\apexcharts.min.js"
            }
            HeaderAlways = @{
                #Css = "$PSScriptRoot\Resources\CSS\apexcharts.css"
            }
        }
        Jquery           = @{
            Comment = 'Jquery'
            Header  = @{
                JsLink = 'http://code.jquery.com/jquery-3.3.1.min.js'
                Js     = "$PSScriptRoot\Resources\JS\jquery-3.3.1.min.js"
            }
        }
        DataTables       = @{
            Comment      = 'DataTables'
            HeaderAlways = @{
                Css         = "$PSScriptRoot\Resources\CSS\datatables.css"
                CssNoscript = "$PSScriptRoot\Resources\CSS\datatables.noscript.css"
            }
            Header       = @{
                CssLink = 'https://cdn.datatables.net/v/dt/jq-3.3.1/dt-1.10.18/af-2.3.2/b-1.5.4/b-colvis-1.5.4/b-html5-1.5.4/b-print-1.5.4/cr-1.5.0/fc-3.2.5/fh-3.1.4/kt-2.5.0/r-2.2.2/rg-1.1.0/rr-1.2.4/sc-1.5.0/sl-1.2.6/datatables.min.css'
                Css     = "$PSScriptRoot\Resources\CSS\datatables.min.css"
                JsLink  = "https://cdn.datatables.net/v/dt/jq-3.3.1/dt-1.10.18/af-2.3.2/b-1.5.4/b-colvis-1.5.4/b-html5-1.5.4/b-print-1.5.4/cr-1.5.0/fc-3.2.5/fh-3.1.4/kt-2.5.0/r-2.2.2/rg-1.1.0/rr-1.2.4/sc-1.5.0/sl-1.2.6/datatables.min.js"
                JS      = "$PSScriptRoot\Resources\JS\datatables.min.js"
            }
        }
        DataTablesPDF    = @{
            Comment = 'DataTables PDF Features'
            Header  = @{
                JsLink = 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/pdfmake.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/vfs_fonts.js'
                Js     = "$PSScriptRoot\Resources\JS\pdfmake.min.js", "$PSScriptRoot\Resources\JS\vfs_fonts.min.js"
            }
        }
        DataTablesExcel  = @{
            Comment = 'DataTables Excel Features'
            Header  = @{
                JsLink = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js'
                JS     = "$PSScriptRoot\Resources\JS\jszip.min.js"
            }
        }
        Fonts            = @{
            Comment      = 'Default fonts'
            HeaderAlways = @{
                CssLink = 'https://fonts.googleapis.com/css?family=Roboto|Hammersmith+One|Questrial|Oswald'
            }
        }
        FontsAwesome     = @{
            Comment      = 'Default fonts icons'
            HeaderAlways = @{
                CssLink = 'https://use.fontawesome.com/releases/v5.7.2/css/all.css'
            }
        }
        HideSection      = @{
            Comment      = 'Hide Section Code'
            HeaderAlways = @{
                JS = "$PSScriptRoot\Resources\JS\HideSection.js"
            }
        }
        Tabs             = @{
            Comment      = 'Elastic Tabs'
            HeaderAlways = @{
                Css = "$PSScriptRoot\Resources\CSS\tabs-elastic.css"
            }
            FooterAlways = @{
                JS = "$PSScriptRoot\Resources\JS\tabs-elastic.js"
            }
        }
        TimeLine         = @{
            Comment      = 'Timeline Simple'
            HeaderAlways = @{
                Css = "$PSScriptRoot\Resources\CSS\timeline-simple.css"
            }
        }
        StatusButtonical = @{
            Comment      = 'Status Buttonical'
            HeaderAlways = @{
                Css = "$PSScriptRoot\Resources\CSS\status.css"
            }
        }
        Toast            = @{
            Comment      = 'Toasts Looking Messages'
            HeaderAlways = @{
                Css = "$PSScriptRoot\Resources\CSS\toasts.css"
            }
        }
        TuiGrid          = @{
            Comment = 'Tui Grid'
            Header  = @{
                Css     = "$PSScriptRoot\Resources\CSS\tuigrid.css"
                CssLink = 'https://cdn.jsdelivr.net/npm/tui-grid@3.5.0/dist/tui-grid.css'
            }
        }
    }
}
function Set-Tag {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $HtmlObject
    )
    $HTML = [System.Text.StringBuilder]::new()
    [void] $HTML.Append("<$($HtmlObject.Tag)")
    foreach ($Property in $HtmlObject.Attributes.Keys) {
        $PropertyValue = $HtmlObject.Attributes[$Property]
        # This checks if property has any subproperties such as style having multiple options
        if ($PropertyValue -is [System.Collections.IDictionary]) {
            $OutputSubProperties = foreach ($SubAttributes in $PropertyValue.Keys) {
                $SubPropertyValue = $PropertyValue[$SubAttributes]
                # skip adding properties that are empty
                if ($null -ne $SubPropertyValue -and $SubPropertyValue -ne '') {
                    "$($SubAttributes):$($SubPropertyValue)"
                }
            }
            $MyValue = $OutputSubProperties -join ';'
            if ($MyValue.Trim() -ne '') {
                [void] $HTML.Append(" $Property=`"$MyValue`"")
            }
        } else {
            # skip adding properties that are empty
            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 {
                    [void] $HTML.AppendLine([string] $Entry)
                }
            }
        }
        [void] $HTML.Append("</$($HtmlObject.Tag)>")
    } else {
        if ($HtmlObject.SelfClosing) {
            [void] $HTML.Append("/>")
        } else {
            [void] $HTML.Append("></$($HtmlObject.Tag)>")
        }
    }
    $HTML.ToString()
}


Export-ModuleMember `
    -Function @('New-HTML','New-HTMLAnchor','New-HTMLAnchorLink','New-HTMLAnchorName','New-HTMLChart','New-HTMLChartArea','New-HTMLChartBar','New-HTMLChartDataSet','New-HTMLChartLine','New-HTMLChartRadial','New-HTMLChartSpark','New-HTMLCodeBlock','New-HTMLContent','New-HTMLHeading','New-HTMLHorizontalLine','New-HTMLImage','New-HTMLList','New-HTMLListItem','New-HTMLLogo','New-HTMLMessage','New-HTMLPanel','New-HTMLResourceCSS','New-HTMLResourceJS','New-HTMLSpanStyle','New-HTMLStatus','New-HTMLStatusItem','New-HTMLTab','New-HTMLTabHead','New-HTMLTable','New-HTMLTableCondition','New-HTMLTag','New-HTMLText','New-HTMLTimeline','New-HTMLTimelineItem','New-HTMLToast','Out-HtmlView','Save-HTML') `
    -Alias @('New-CSS','New-HTMLColumn','New-JavaScript','New-ResourceCSS','New-ResourceJS','ohv','Out-GridHtml')