PSWriteHTML.psm1

function Convert-StyleContent {
    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-HTMLBarChart {
    <#
    .SYNOPSIS
 
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        $ChartObject,
        [Parameter(Mandatory = $true)]
        [Array]
        $DataSet,
        [Parameter(Mandatory = $false)]
        $Options
    )

    $DataCount = $DataSet.Count
    Write-Verbose "Data Set counnt is $DataCount"
    if ($ChartObject.ChartStyle.ColorSchemeName -ne 'Random') {
        if ($Options -eq $null) {
            #Write-Verbose "Default Colour Schemes selected, selecting $($ChartObject.ChartStyle.ColorSchemeName)"
            #$ColorSchemes = Get-HTMLColorSchemes
            $ChartColorScheme = $GlobalColorSchemes.($ChartObject.ChartStyle.ColorSchemeName) | Select-Object -First $DataCount
        } else {
            Write-Verbose "Options Colour Schemes selected, selecting $($ChartObject.ChartStyle.ColorSchemeName)"
            $ChartColorScheme = $Options.ColorSchemes.($ChartObject.ChartStyle.ColorSchemeName) | Select-Object -First $DataCount
        }
        if ($ChartColorScheme.Count -lt $DataCount) {
            Write-Warning ("Colorscheme " + $ChartObject.ChartStyle.ColorSchemeName + " only has " + $ChartColorScheme.Count + " schemes, you have $DataCount Records. Generating one for you" )
            $ChartColorScheme = Get-RandomColorScheme -numberofschemes $DataCount
        }
    } else {
        $ChartColorScheme = Get-RandomColorScheme -numberofschemes $DataCount
    }

    $ofs = ','
    $CJSHeader = @()
    $CJSHeader += '<canvas id="' + $ChartObject.ObjectName + '" width="' + $ChartObject.Size.Width + '" height="' + $ChartObject.Size.Height + '"></canvas>'
    $CJSHeader += '<script>'
    $CJSHeader += 'var ctx = document.getElementById("' + $ChartObject.ObjectName + '");'
    $CJSHeader += 'var ' + $ChartObject.ObjectName + ' = new Chart(ctx, {'
    $CJSHeader += " type: '$($ChartObject.ChartStyle.ChartType)',"


    $CJSData = @()
    $CJSData = " data: {" + "`n"
    if ($ChartObject.ChartStyle.Showlabels) {
        $ofs = '","'
        $CJSData += ' labels: ["' + "$($DataSet.($ChartObject.DataDefinition.DataNameColumnName))" + '"],' + "`n"
    }

    $CJSData += " datasets: [{" + "`n"
    $CJSData += " label: '$($chartobject.DataDefinition.datasetname)'," + "`n"
    $ofs = ","
    $CJSData += " data: [" + "$($DataSet | % {$_.($ChartObject.DataDefinition.DataValueColumnName)})" + "]," + "`n"
    $ofs = "','"
    $CJSData += " backgroundColor: ['" + "$($ChartColorScheme.Background)" + "']," + "`n"
    $CJSData += " borderColor: ['" + "$($ChartColorScheme.border)" + "']," + "`n"
    $CJSData += " hoverBackgroundColor: ['" + "$($ChartColorScheme.border)" + "']," + "`n"
    $CJSData += " borderWidth: $($ChartObject.ChartStyle.borderWidth)" + "`n"
    $CJSData += " }]" + "`n"
    $CJSData += " },"
    $ofs = ""

    $CJSOptions = @()
    $cjsOptions += ' options: {'
    #responsive
    $cjsOptions += " responsive: $($ChartObject.ChartStyle.responsive),"
    #legend
    $cjsOptions += " legend: {
                position: '$($ChartObject.ChartStyle.legendposition)',
            },"

    #title
    if ($ChartObject.Title -ne '') {
        $cjsOptions += " title: {
                display: true,
                text: '$($ChartObject.Title)'
            },"

    }
    #scale & Labels
    $XAxisLabel = $ChartObject.DataDefinition.AxisXTitle
    if ([string]::IsNullOrEmpty($XAxisLabel)) {
        $displayXAxisLabel = 'false'
    } else {
        $displayXAxisLabel = 'true'
    }

    $YAxisLabel = $ChartObject.DataDefinition.AxisYTitle
    if ([string]::IsNullOrEmpty($YAxisLabel)) {
        $displayYAxisLabel = 'false'
    } else {
        $displayYAxisLabel = 'true'
    }

    $CJSOptions += "scales: {
                        xAxes: [{
                            display: $displayXAxisLabel,
                            scaleLabel: {
                                display: $displayXAxisLabel,
                                labelString: '$XAxisLabel'
                            }
                        }],
                        yAxes: [{
                            display: $displayYAxisLabel,
                            scaleLabel: {
                                display: $displayYAxisLabel,
                                labelString: '$YAxisLabel'
                            },
                            ticks: {
                                beginAtZero:true
                            }
                        }]
                    },
 
    "


    $cjsOptions += " }" + "`n"
    $CJSOptions += "}); "

    $CJSFooter = " </script>"



    $CJS = @()
    $CJS += $CJSHeader
    $CJS += $CJSData
    $CJS += $CJSOptions
    $CJS += $CJSFooter

    write-output $CJS
}
Function Get-HTMLBarChartObject {
    <#
    .SYNOPSIS
        create a Bar chart object for use with Get-HTMLBarChart
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        $ColorScheme
    )


    $ChartSize = [PSCustomObject] @{
        Width  = 500
        Height = 400
    }

    $DataDefinition = [PSCustomObject] @{
        DataSetName         = "Data"
        AxisXTitle          = ""
        AxisYTitle          = ""
        DataNameColumnName  = "name"
        DataValueColumnName = "count"

    }

    if ($ColorScheme -eq "Generated") {
        $thisColorScheme = 'Generated' + [string](Get-Random -Minimum 1 -Maximum 8)
    } elseif ($ColorScheme -eq "Random") {
        $thisColorScheme = 'Random'
    } else {
        $thisColorScheme = 'ColorScheme1'
    }

    $ChartStyle = [PSCustomObject] @{
        ChartType       = "bar"
        ColorSchemeName = "$thisColorScheme"
        Showlabels      = $true
        borderWidth     = "1"
        responsive      = 'false'
        legendPosition  = 'bottom'

    }

    $ChartObject = [PSCustomObject] @{
        ObjectName     = Get-RandomStringName -Size 12
        Title          = ""
        Size           = $ChartSize
        DataDefinition = $DataDefinition
        ChartStyle     = $ChartStyle
    }

    return $ChartObject
}
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-HTMLCSS {
    <#
        .SYNOPSIS
            Get's HTML Cascading Style Sheet
        .PARAMETER CSSName
            Name of the CSS
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false)][String] $CSSPath,
        [Parameter(Mandatory = $false)][String] $CSSName,

        [switch] $Builtin,
        [switch] $UseLinks
    )
    $CSSHeaders = New-GenericList -Type [string]

    if ($Builtin) {
        if ($UseLinks) {
            $Links = @(
                "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"
                "https://evotec.xyz/wp-content/uploads/pswritehtml/enlighterjs.min.css"
                "https://cdn.jsdelivr.net/npm/tui-grid@3.5.0/dist/tui-grid.css"
             # "https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css"
            )
            foreach ($Link in $Links) {
                [string] $HTMLLink = '<link rel="stylesheet" type="text/css" href="**Link**"/>'.Replace('**Link**', $Link)
                $CSSHeaders.Add($HTMLLink)
                $CSSHeaders.Add("`r`n")
            }
            return $CSSHeaders

        } else {
            $CSSPath = "$PSScriptRoot\Resources\CSS\StylesAlways"
        }
    } else {
        if ([String]::IsNullOrEmpty($CSSPath)) {
            $CSSPath = "$PSScriptRoot\Resources\CSS\Styles"
        }
    }
    Write-Verbose "Retrieving *.css from $CSSPath"

    $CSSFiles = @((get-childitem $CSSPath -Filter '*.css' -Recurse))
    if (-not $Builtin) {
        # By builtin we mean CSS that is part of JS Packages. This is limiting choice to single CSS to make sure reports look properly
        $CSSFiles =    $CSSFiles | Where-Object { $_.BaseName -eq $CSSName }
    }


    foreach ($CssFile in $CSSFiles) {
        $CSSHeaders.Add('<style type="text/css">')
        $CSSHeaders.Add("`r`n")
        if ($CssFile -like '*.min.*') {
            Write-Verbose "Generating Style Header from - $($CssFile.FullName) (minified file)"
            $CSSHeaders.Add((Get-Content -Path $CssFile.FullName))
        } else {
            Write-Verbose "Generating Style Header from - $($CssFile.FullName) (from non-minified file (adding delimiter))"
            $CSSHeaders.Add((Get-Content -Path $CssFile.FullName -Delimiter "`r`n"))
        }
        $CSSHeaders.Add("</style>")
        $CSSHeaders.Add("`r`n")

    }
    Write-Output $CSSHeaders
}
Function Get-HTMLJavaScripts {
    <#
    .SYNOPSIS
        Get's Script File from module directory
#>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false)][String]$ScriptPath,
        [switch] $UseLinks
    )
    $ScriptHeaders = New-GenericList -Type [string]
    if ($UseLinks) {
        $Links = @(
            "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"
            "https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js"
            "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"
            "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.bundle.min.js"
            "https://evotec.xyz/wp-content/uploads/pswritehtml/enlighterjs.min.js"
            "https://cdn.jsdelivr.net/npm/apexcharts@latest"
            "https://use.fontawesome.com/releases/v5.3.1/js/all.js"
        )
        foreach ($Link in $Links) {
            [string] $HTMLLink = '<script type="text/javascript" src="**Link**"></script>'.Replace('**Link**', $Link)
            $ScriptHeaders.Add($HTMLLink)
            $ScriptHeaders.Add("`r`n")
        }
        # Only some links are available online, so some static resources need to be put in. Maybe there is a way to put on CDN.
        # For now we need to reload this function again.
        $ScriptHeaders.Add((Get-HTMLJavaScripts -ScriptPath "$PSScriptRoot\Resources\JS\Static"))

    } else {
        if ([String]::IsNullOrEmpty($ScriptPath)) {
            $ScriptPath = "$PSScriptRoot\Resources\JS"
        }
        Write-Verbose "Retrieving *.js from $ScriptPath"
        $ScriptFiles = @((get-childitem $ScriptPath -Filter '*.js' -Recurse))

        foreach ($ScriptFile in $ScriptFiles) {
            $ScriptHeaders.Add("`r`n")
            $ScriptHeaders.Add('<script type="text/javascript">')
            $ScriptHeaders.Add("`r`n")
            if ($ScriptFile -like '*.min.*') {
                Write-Verbose "Generating Script Header from minified file - $($ScriptFile.Fullname)"
                $ScriptHeaders.Add((Get-Content -Path $ScriptFile.Fullname))
            } else {
                Write-Verbose "Generating Script Header from non-minified file (adding delimiter) $($ScriptFile.Fullname)"
                $ScriptHeaders.Add((Get-Content -Path $ScriptFile.Fullname -Delimiter "`r`n"))
                <#
                $ScriptHeaders = New-Object System.Collections.Generic.List[System.Object]
                foreach ($ScriptFile in $ScriptFiles) {
                    Write-Verbose "Generating Script Header from $ScriptFile"
                    $ScriptHeaders.Add(("`r`n" + '<script type="text/javascript"> '+ "`r`n"))
                    $Content = Get-Content $ScriptFile
                    foreach ($item in $Content) {
                        $ScriptHeaders.Add("$item`r`n")
                    }
                    $ScriptHeaders.Add('</script> ')
                }
                $ScriptHeaders = $ScriptHeaders.ToArray()
                #>

            }
            $ScriptHeaders.Add('</script>')
            $ScriptHeaders.Add("`r`n")
        }
    }
    Write-Output $ScriptHeaders
}
Function Get-HTMLLineChart {
    <#
    .SYNOPSIS
     
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        $ChartObject,
        [Parameter(Mandatory = $true)]
        [Array]
        $DataSet,
        [Parameter(Mandatory = $false)]
        $Options
    )
    
    $DataCount = $ChartObject.DataDefinition.DataSetNames.Count
    Write-Verbose "Data Set count is $DataCount"

    if ($ChartObject.ChartStyle.ColorSchemeName -ne 'Random') {
        if ($null -eq $Options) {
            $ChartColorScheme = $GlobalColorSchemes.($ChartObject.ChartStyle.ColorSchemeName) | Select-Object -First $DataCount
        } else {
            Write-Verbose "Options Colour Schemes selected, selecting $($ChartObject.ChartStyle.ColorSchemeName)"
            $ChartColorScheme = $Options.ColorSchemes.($ChartObject.ChartStyle.ColorSchemeName) | Select-Object -First $DataCount
        }
        if ($ChartColorScheme.Count -lt $DataCount) {
            Write-Warning ("Colorscheme " + $ChartObject.ChartStyle.ColorSchemeName + " only has " + $ChartColorScheme.Count + " schemes, you have $DataCount Records. Generating one for you" )            
            $ChartColorScheme = Get-RandomColorScheme -numberofschemes $DataCount
        }
    } else {
        $ChartColorScheme = Get-RandomColorScheme -numberofschemes $DataCount
    }

    $Labels = ""
    if ($ChartObject.ChartStyle.Showlabels) {
        $Labels = ' labels: [{0}],' -f ($DataSet.($ChartObject.DataDefinition.DataCategoryName) -join ',')
    }
    $ChartObjectObjectName = $ChartObject.ObjectName
    # to display the title or not
    $displayTitle = ([string](-not [string]::IsNullOrEmpty($ChartObject.Title))).tolower()

    # a template for the dataset, notice we escape the {}
    $sDataset = @'
    {{
        label: '{0}',
        data: [{1}],
        borderColor: '{2}',
        borderWidth: {3}
    }}
'@


    $CJS = @"
    <canvas id="$ChartObjectObjectName" width="$($ChartObject.Size.Width)" height="$($ChartObject.Size.Height)"></canvas>
    <script>
    var ctx = document.getElementById("$ChartObjectObjectName");
    var $ChartObjectObjectName = new Chart(ctx, {
        type: '$($ChartObject.ChartStyle.ChartType)',
        data: {
            $Labels
            datasets: [
                $(
                    $i = 0
                    ($ChartObject.DataDefinition.DataSetNames | ForEach-Object {
                        $data = $DataSet.$_ -join ','
                        $sDataset -f $_, $data, $ChartColorScheme.border[$i], $ChartObject.ChartStyle.borderWidth
                        $i += 1
                    }) -join ','
                     
                )
            ]
        },
        options: {
            responsive: $($ChartObject.ChartStyle.responsive),
            legend: {
                    position: '$($ChartObject.ChartStyle.legendposition)',
                },
            title: {
                    display: $displayTitle,
                    text: '$($ChartObject.Title)'
                },
        },
        animation: {
                    animateScale: $($ChartObject.ChartStyle.animateScale),
                    animateRotate: $($ChartObject.ChartStyle.animateRotate)
                }
    });
    </script>
"@
    

    write-output $CJS
}
<#
 
$dataSet = @()
$dataSet += [ordered] @{ label = 'My'; data = 10, 30, 1, 5; borderColor = 'green'; borderWidth = 1}
$dataSet += [ordered] @{ label = 'My'; data = 20, 50; borderColor = 'green'; borderWidth = 1}
$dataSet += [ordered] @{ label = 'My'; data = 30, 5, 30; borderColor = 'green'; borderWidth = 1}
 
$Test = [ordered] @{
    type = 'line'
    data = @{
        labels = @()
        datasets = $dataSet
    }
    options = [ordered] @{
        responsive = $false
        legend = @{
            position = 'bottom'
        }
        title = @{
            display = $true
            text = "Title"
        }
         
 
    }
    animation = [ordered] @{
        animateScale = $true
        animateRoate = $true
    }
}
 
$test | ConvertTo-Json -Depth 5
#>

#>

<# Result
<canvas
    id="LineChartlr8hfamk9c37"
    width="500"
    height="400"
></canvas>
<script>
    var ctx = document.getElementById(
        "LineChartlr8hfamk9c37"
    );
    var LineChartlr8hfamk9c37 = new Chart(ctx, {
        type: "line",
        data: {
            labels: ["2011", "2012", "2013"],
            datasets: [
                {
                    label: "My",
                    data: [10, 30, 1, 5],
                    borderColor: "rgba(66,182,115,1)",
                    backgroundColor: [
                        "yellow"
                    ],
                    borderWidth: 1
                },
                {
                    label: "New",
                    data: [20, 50],
                    borderColor: "rgba(72,40,230,1)",
                    borderWidth: 1
                },
                {
                    label: "Hello",
                    data: [30, 5, 30],
                    borderColor: "rgba(64,126,176,1)",
                    borderWidth: 1
                }
            ]
        },
        options: {
            responsive: false,
            legend: {
                position: "bottom"
            },
            title: {
                display: true,
                text: "Group Membership"
            }
        },
        animation: {
            animateScale: true,
            animateRotate: true
        }
    });
</script>
#>

Function Get-HTMLLineChartObject {
    [CmdletBinding()]
    param(
        [ValidateSet("line")][String] $ChartType = 'line',
        [Parameter(Mandatory = $false)]$ColorScheme,
        [Parameter(Mandatory = $false)][int]$Width = 500,
        [Parameter(Mandatory = $false)][int]$Height = 400,
        [Parameter(Mandatory = $false)][string]$DataCategoryName = 'x',
        [Parameter(Mandatory = $false)][string]$DataSetName = 'y',
        [Parameter(Mandatory = $false)][Array]$DataSetNames = @()
    )

    $ChartSize = [PSCustomObject] @{
        Width  = $Width
        Height = $Height
    }

    if ($DataSetNames.Count -eq 0) {
        $DataSetNames = @($DataSetName)
    }

    $DataDefinition = [PSCustomObject] @{
        DataCategoryName = $DataCategoryName
        DataSetName      = $DataSetName
        DataSetNames     = $DataSetNames
    }

    if ($ColorScheme -eq "Generated") {
        $thisColorScheme = 'Generated' + [string](Get-Random -Minimum 1 -Maximum 8)
    } elseif ($ColorScheme -eq "Random") {
        $thisColorScheme = 'Random'
    } else {
        $thisColorScheme = 'ColorScheme2'
    }

    $ChartStyle = [PSCustomObject]@{
        ChartType       = $ChartType
        ColorSchemeName = "$thisColorScheme"
        Showlabels      = $true
        fill            = $false
        borderWidth     = "1"
        responsive      = 'false'
        animateScale    = 'true'
        animateRotate   = 'true'
        legendPosition  = 'bottom'
    }

    $ChartObject = [PSCustomObject] @{
        ObjectName     = "LineChart$(Get-RandomStringName -Size 12)"
        Title          = ""
        Size           = $ChartSize
        DataDefinition = $DataDefinition
        ChartStyle     = $ChartStyle
    }

    return $ChartObject
}
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-HTMLPieChart {
    <#
    .SYNOPSIS
 
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        $ChartObject,
        [Parameter(Mandatory = $true)]
        [Array]
        $DataSet,
        [Parameter(Mandatory = $false)]
        $Options
    )

    $DataCount = $DataSet.Count
    Write-Verbose "Data Set counnt is $DataCount"

    if ($ChartObject.ChartStyle.ColorSchemeName -ne 'Random') {
        if ($Options -eq $null) {
            #Write-Verbose "Default Colour Schemes selected, selecting $($ChartObject.ChartStyle.ColorSchemeName)"
            #$ColorSchemes = Get-HTMLColorSchemes
            $ChartColorScheme = $GlobalColorSchemes.($ChartObject.ChartStyle.ColorSchemeName) | select -First $DataCount
        } else {
            Write-Verbose "Options Colour Schemes selected, selecting $($ChartObject.ChartStyle.ColorSchemeName)"
            $ChartColorScheme = $Options.ColorSchemes.($ChartObject.ChartStyle.ColorSchemeName) | select -First $DataCount
        }
        if ($ChartColorScheme.Count -lt $DataCount) {
            Write-Warning ("Colorscheme " + $ChartObject.ChartStyle.ColorSchemeName + " only has " + $ChartColorScheme.Count + " schemes, you have $DataCount Records. Generating one for you" )
            $ChartColorScheme = Get-RandomColorScheme -numberofschemes $DataCount
        }
    } else {
        $ChartColorScheme = Get-RandomColorScheme -numberofschemes $DataCount
    }

    $ofs = ','
    $CJSHeader = @()
    $CJSHeader += '<canvas id="' + $ChartObject.ObjectName + '" width="' + $ChartObject.Size.Width + '" height="' + $ChartObject.Size.Height + '"></canvas>'
    $CJSHeader += '<script>'
    $CJSHeader += 'var ctx = document.getElementById("' + $ChartObject.ObjectName + '");'
    $CJSHeader += 'var ' + $ChartObject.ObjectName + ' = new Chart(ctx, {'
    $CJSHeader += " type: '$($ChartObject.ChartStyle.ChartType)',"


    $CJSData = @()
    $CJSData = " data: {" + "`n"
    if ($ChartObject.ChartStyle.Showlabels) {
        $ofs = '","'
        $CJSData += ' labels: ["' + "$($DataSet.($ChartObject.DataDefinition.DataNameColumnName))" + '"],' + "`n"
    }

    $CJSData += " datasets: [{" + "`n"
    $CJSData += " label: '$($chartobject.DataDefinition.datasetname)'," + "`n"
    $ofs = ","
    $CJSData += " data: [" + "$($DataSet | Foreach-Object {$_.($ChartObject.DataDefinition.DataValueColumnName)})" + "]," + "`n"
    $ofs = "','"
    $CJSData += " backgroundColor: ['" + "$($ChartColorScheme.Background)" + "']," + "`n"
    $CJSData += " hoverBackgroundColor: ['" + "$($ChartColorScheme.border)" + "']," + "`n"
    $CJSData += " borderWidth: $($ChartObject.ChartStyle.borderWidth)" + "`n"
    $CJSData += " }]" + "`n"
    $CJSData += " },"
    $ofs = ""


    $CJSOptions = @()
    $cjsOptions += ' options: {'
    #responsive
    $cjsOptions += " responsive: $($ChartObject.ChartStyle.responsive),"
    #legend
    $cjsOptions += " legend: {
                position: '$($ChartObject.ChartStyle.legendposition)',
            },"

    #Title
    if ($ChartObject.Title -ne '') {
        $cjsOptions += " title: {
                display: true,
                text: '$($ChartObject.Title)'
            },"

    }
    $cjsOptions += " },"
    #animation
    $cjsOptions += " animation: {
                animateScale: $($ChartObject.ChartStyle.animateScale),
                animateRotate: $($ChartObject.ChartStyle.animateRotate)
            }"

    $CJSOptions += "}); "



    $CJSFooter = " </script>"



    $CJS = @()
    $CJS += $CJSHeader
    $CJS += $CJSData
    $CJS += $CJSOptions
    $CJS += $CJSFooter

    write-output $CJS
}

<#
<canvas
    id="Piexy1fwn4hqjsa"
    width="250"
    height="250"
></canvas>
<script>
    var ctx = document.getElementById(
        "Piexy1fwn4hqjsa"
    );
    var Piexy1fwn4hqjsa = new Chart(ctx, {
        type: "doughnut",
        data: {
            labels: ["Members", "No Members"],
            datasets: [
                {
                    label: "Data",
                    data: [10, 5],
                    backgroundColor: [
                        "rgba(161,194,145,0.7)",
                        "rgba(156,215,85,0.7)"
                    ],
                    hoverBackgroundColor: [
                        "rgba(161,194,145,1)",
                        "rgba(156,215,85,1)"
                    ],
                    borderWidth: 1
                }
            ]
        },
        options: {
            responsive: false,
            legend: {
                position: "bottom"
            },
            title: {
                display: true,
                text: "Group Membership"
            }
        },
        animation: {
            animateScale: true,
            animateRotate: true
        }
    });
</script>
 
#>

Function Get-HTMLPieChartObject {
    [CmdletBinding()]
    param(
        [ValidateSet("pie", "doughnut")]
        [String]
        $ChartType = 'pie',
        [Parameter(Mandatory = $false)]
        $ColorScheme
    )

    $ChartSize = [PSCustomObject] @{
        Width  = 500
        Height = 400
    }

    $DataDefinition = [PSCustomObject] @{
        DataSetName         = "Data"
        DataNameColumnName  = "name"
        DataValueColumnName = "count"
    }

    if ($ColorScheme -eq "Generated") {
        $thisColorScheme = 'Generated' + [string](Get-Random -Minimum 1 -Maximum 8)
    } elseif ($ColorScheme -eq "Random") {
        $thisColorScheme = 'Random'
    } else {
        $thisColorScheme = 'ColorScheme2'
    }

    $ChartStyle = [PSCustomObject] @{
        ChartType       = $ChartType
        ColorSchemeName = "$thisColorScheme"
        Showlabels      = $true
        borderWidth     = "1"
        responsive      = 'false'
        animateScale    = 'true'
        animateRotate   = 'true'
        legendPosition  = 'bottom'
    }

    $ChartObject = [PSCustomObject] @{
        ObjectName     = "Pie$(Get-RandomStringName -Size 12)"
        Title          = ""
        Size           = $ChartSize
        DataDefinition = $DataDefinition
        ChartStyle     = $ChartStyle
    }

    return $ChartObject
}
Function Get-HTMLStackedChart
{
<#
    .SYNOPSIS
     
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        $ChartObject,
        [Parameter(Mandatory=$true)]
        [Array]
        $DataSet,        
        [Parameter(Mandatory=$false)]
        $Options
    )


    $DataCount = $DataSet.Count
    Write-Verbose "Data Set count is $DataCount"

    if ($ChartObject.ChartStyle.ColorSchemeName -ne 'Random')
    {
        if ($Options -eq $null) {
            #Write-Verbose "Default Colour Schemes selected, selecting $($ChartObject.ChartStyle.ColorSchemeName)"
            #$ColorSchemes = Get-HTMLColorSchemes
            $ChartColorScheme = $GlobalColorSchemes.($ChartObject.ChartStyle.ColorSchemeName) | select -First $DataCount
        } else {
            Write-Verbose "Options Colour Schemes selected, selecting $($ChartObject.ChartStyle.ColorSchemeName)"
            $ChartColorScheme = $Options.ColorSchemes.($ChartObject.ChartStyle.ColorSchemeName) | select -First $DataCount
        }
        if ($ChartColorScheme.Count -lt $DataCount) {
            Write-Warning ("Colorscheme " +  $ChartObject.ChartStyle.ColorSchemeName + " only has " + $ChartColorScheme.Count + " schemes, you have $DataCount Records. Generating one for you" )            
            $ChartColorScheme = Get-RandomColorScheme -numberofschemes $DataCount -ColorSwing (($DataCount/2)+8)
        }
    }
    else
    {
        $ChartColorScheme = Get-RandomColorScheme -numberofschemes $DataCount -ColorSwing (($DataCount/2)+8)
    }
        
    $ofs = ','
    $CJSHeader  = @()
    $CJSHeader += '<canvas id="' + $ChartObject.ObjectName + '" width="'+ $ChartObject.Size.Width + '" height="' + $ChartObject.Size.Height +'"></canvas>'
    $CJSHeader += '<script>'
    $CJSHeader += 'var ctx = document.getElementById("' + $ChartObject.ObjectName + '");'
    $CJSHeader += 'var ' + $ChartObject.ObjectName + ' = new Chart(ctx, {'
    $CJSHeader += " type: '$($ChartObject.ChartStyle.ChartType)',"
    
    
    $CJSData = @()
    $CJSData = " data: {"+ "`n"
    if ($ChartObject.ChartStyle.Showlabels) {
        $ofs ='","'
        $CJSData += ' labels: ["' +"$([string]($DataSet.($ChartObject.DataDefinition.DataSetArrayData).($ChartObject.DataDefinition.DataSetArrayXLabels) | select -Unique))" + '"],' + "`n"
    }
    

    $rowCount = 0
    $CJSData += " datasets: ["
    #$Set = $DataSet[4]
    Foreach ($Set in $DataSet)
    {
        $CJSData += " {" + "`n"
        $CJSData += " label: '$($Set.($ChartObject.DataDefinition.DataSetArrayName))'," + "`n"
        $ofs =","
        $CJSData += " fill: true," + "`n"
        $CJSData += " data: [" + "$([string]($Set.($ChartObject.DataDefinition.DataSetArrayData).($chartObject.DataDefinition.DataSetArrayDataColumn)))" +"]," + "`n"
        $ofs ="','"
        $CJSData += " borderCapStyle: 'butt'," + "`n"        
        $CJSData += " backgroundColor: '$($ChartColorScheme[$rowcount].Background)'," + "`n"
        $CJSData += " pointBackgroundColor: '$($ChartColorScheme[$rowcount].Border)'," + "`n"
        $CJSData += " pointHighlightStroke: '$($ChartColorScheme[$rowcount].Border)'," + "`n"
        $CJSData += " borderColor: '$($ChartColorScheme[$rowcount].Border)'," + "`n"
        
        if ($rowCount -ne ($DataSet.Count -1)) 
        {
            $CJSData += " },"
        }
        else
        {
            $CJSData += " }"
        }
        $rowCount++
    }

        $CJSData += " ]"+ "`n"
        $CJSData += " },"    
        $ofs =""
       

    
    
    $CJSOptions = @()
    $cjsOptions += ' options: {'
    #responsive
    $cjsOptions += " responsive: $($ChartObject.ChartStyle.responsive),"
    #legend
    $cjsOptions += " legend: {
                position: '$($ChartObject.ChartStyle.legendposition)',
            },"

    #Title
    if ($ChartObject.Title -ne '') {
        $cjsOptions += " title: {
                display: true,
                text: '$($ChartObject.Title)'
            },"

    }
    #Scales
    $cjsOptions += "scales: {
               yAxes: [{
               stacked: $($ChartObject.ChartStyle.yAxesStacked),
                }]
        },"

    #Tooltips
        $cjsOptions += "tooltips: {
               mode: '$($ChartObject.ChartStyle.tooltips.mode)',
               intersect: $($ChartObject.ChartStyle.tooltips.intersect),
        },"


    #Hover
        $cjsOptions += "hover: {
                 mode: '$($ChartObject.ChartStyle.hover.mode)',
               intersect: $($ChartObject.ChartStyle.hover.intersect),
        },"

    #animation
    $cjsOptions += " animation: {
                animateScale: $($ChartObject.ChartStyle.animateScale),
                animateRotate: $($ChartObject.ChartStyle.animateRotate),
                duration: 750,
            },"

    $cjsOptions += "}"
    $CJSOptions += "}); "
    
    
    $CJSFooter = " </script>"
    


    $CJS  = @()
    $CJS += $CJSHeader
    $CJS += $CJSData
    $CJS += $CJSOptions 
    $CJS += $CJSFooter

    write-output $CJS
}
Function Get-HTMLStackedChartObject
{
<#
    .SYNOPSIS
        create a Stacked chart object for use with Get-HTMLStackedChart
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        $ColorScheme,
        #[Parameter(Mandatory=$true)]
        [String]
        $DataSetArrayXLabels='Date',
        #[Parameter(Mandatory=$true)]
        [String]
        $DataSetArrayName='OwnerSolution',
        #[Parameter(Mandatory=$true)]
        [String]
        $DataSetArrayData='usagedata',
        #[Parameter(Mandatory=$true)]
        [String]
        $DataSetArrayDataColumn='cost'
)

    $ChartSize = New-Object PSObject -Property @{`
        Width = 1000
        Height = 400
    }
    
    $DataDefinition = New-Object PSObject -Property @{`
        DataSetArrayData = $DataSetArrayData
        DataSetArrayName = $DataSetArrayName
        DataSetArrayXLabels = $DataSetArrayXLabels
        DataSetArrayDataColumn = $DataSetArrayDataColumn
    }
    
    if ($ColorScheme -eq "Generated") {$thisColorScheme = 'Generated' + [string](Get-Random -Minimum 1 -Maximum 8)}
    elseif ($ColorScheme -eq "Random") {$thisColorScheme = 'Random' }
    else {$thisColorScheme = 'ColorScheme2'}

    $ChartStyle = New-Object PSObject -Property @{`
        ChartType = 'line' # bar
        ColorSchemeName = "$thisColorScheme"
        Showlabels= $true
        yAxesStacked= 'true'
        borderWidth = "1"
        responsive = 'false'
        animateScale = 'true'
        animateRotate = 'true'
        legendPosition = 'bottom'
        tooltips = New-Object PSObject -Property @{`
            mode = 'dataset'
            intersect = 'true'
        }
        hover  = New-Object PSObject -Property @{`
            mode = 'point'
            intersect = 'true'
        }

    }
    
    $ChartObject = New-Object PSObject -Property @{`
        ObjectName     = "StackedChart$(Get-RandomStringName -Size 12)"
        Title = ""
        Size = $ChartSize
        DataDefinition = $DataDefinition
        ChartStyle = $ChartStyle
    }
    
    return $ChartObject
}
function Get-JavaScript {
    param(
        [string[]] $FilePath
    )
    $ScriptHeaders = New-GenericList -Type [string]
    foreach ($ScriptFile in $FilePath) {
        $ScriptHeaders.Add("`r`n")
        $ScriptHeaders.Add('<script type="text/javascript">')
        $ScriptHeaders.Add("`r`n")
        if ($ScriptFile -like '*.min.*') {
            Write-Verbose "Generating Script Header from minified file - $($ScriptFile)"
            $ScriptHeaders.Add((Get-Content -Path $ScriptFile))
        } else {
            Write-Verbose "Generating Script Header from non-minified file (adding delimiter) $($ScriptFile)"
            $ScriptHeaders.Add((Get-Content -Path $ScriptFile -Delimiter "`r`n"))
        }
        $ScriptHeaders.Add('</script>')
        $ScriptHeaders.Add("`r`n")
    }
    return $ScriptHeaders
}

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
    )

    [string] $Hover = 0.3
    [string] $color = 0.6
    [string] $border = 1
    [string] $background = 0.7
    [int] $ColorSwing = 8

    $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-ResourceContent {
    param(
        [string[]]$FilePath
    )
    foreach ($File in $FilePath) {
        if ($File -ne '' -and (Test-Path -LiteralPath $FilePath)) {
          # if ($File -like '*.min.*') {
        # Write-Verbose "Generating Style Header from - $($File) (minified file)"
            # Get-Content -Path $File
          # } else {
                Write-Verbose "Generating Style Header from - $($File) (from non-minified file (adding delimiter))"
                Get-Content -Path $File #-Delimiter "`r`n"
           # }
        }
    }
}
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] $UseStyleLinks,
        [String] $TitleText,
        [String] $CSSPath,
        [String] $CSSName = "default",
        [String] $ScriptPath,
        [String] $ColorSchemePath,
        [String] $LogoPath,
        [string] $LeftLogoName = "Sample",
        [string] $RightLogoName = "Alternate",
        [string] $LeftLogoString,
        [string] $RightLogoString,
        [switch] $HideLogos,
        [switch] $NoScript,
        [string] $PrimaryColorHex,
        [switch] $AddAuthor,
        [string] $Author,
        [string] $DateFormat = 'yyyy-MM-dd HH:mm:ss'
    )

    [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 = @()
    }

    '<!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 'title' { $TitleText }
            
            if ($AddAuthor) {
                if ([String]::IsNullOrWhiteSpace($Author)) { $Author = $env:USERNAME }
                
                "<!--- This page was autogenerated $CurrentDate By $Author -->"
            }

            "<!-- STYLES -->"       
            $Options.StyleContent
            #New-HTMLResourceCSS -FilePath "$PSScriptRoot\Resources\CSS\Styles\timeline.css" -ResourceComment 'Timeline'
            #New-HTMLResourceCSS -FilePath "$PSScriptRoot\Resources\CSS\Styles\timeline-ygc.css" -ResourceComment 'Timeline YGC'
            #New-HTMLResourceCSS -FilePath "$PSScriptRoot\Resources\CSS\Styles\timeline-scrible.css" -ResourceComment 'Timeline Scrible'
            New-HTMLResourceCSS -FilePath "$PSScriptRoot\Resources\CSS\Styles\timeline-simple.css" -ResourceComment 'Timeline Simple'
            New-HTMLResourceCSS -FilePath "$PSScriptRoot\Resources\CSS\Styles\skeleton-tabs.css" -ResourceComment 'Skeleton Tabs'
            New-HTMLResourceCSS -FilePath "$PSScriptRoot\Resources\CSS\Styles\status.css" -ResourceComment 'Status Buttonical'
            New-HTMLResourceCSS -FilePath "$PSScriptRoot\Resources\CSS\Styles\message.css" -ResourceComment 'Message Notifications'
            New-HTMLResourceCSS -FilePath "$PSScriptRoot\Resources\CSS\Styles\toasts.css" -ResourceComment 'Toast Notifications'
            '<!-- END STYLES -->'
            '<!-- SCRIPTS -->'
            $Options.ScriptContent
            '<!-- END SCRIPTS -->'
        }
        '<!-- END HEADER -->'
        '<!-- BODY -->'
        #'<body onload="hide();">'
        New-HTMLTag -Tag 'body' {
            #Add-HTMLBodyOpening -Options $Options -TitleText $TitleText -HideLogos:$HideLogos -HideTitle:$HideTitle -HideDate:$HideDate -PrimaryColorHex $PrimaryColorHex -CurrentDate $CurrentDate
            
            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
            }         
            #if (-not $HideDate) {
            # New-HTMLTag -Tag 'div' -Attributes @{ class = 'ReportCreated' } -Value { "Report created on $($CurrentDate)" }
            #}

            <# 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"'
                }
            }
            #>
 
            Invoke-Command -ScriptBlock $HtmlData
            
            '<!-- FOOTER -->'
            New-HTMLTag -Tag 'footer' { 
                #New-HTMLTag -Tag 'div' -Attributes @{ class = 'defaultFooter' } {
                # if ($null -ne $FooterText) {
                # $FooterText = "Copyright &#169; $([DateTime]::Now.Year). All Rights Reserved."
                # }
                # $FooterText
                #}
                New-HTMLResourceJS {

                    @'
                    // INIT CODE - simple page-wide initialization based on css selectors
                    // - highlight all pre + code tags (CSS3 selectors)
                    // - use javascript as default language
                    // - use theme "enlighter" as default theme
                    // - replace tabs with 2 spaces
                    EnlighterJS.init("pre", "code", {
                            language: "powershell",
                            theme: "enlighter",
                            indent: 2
                        });
'@

                }                
            }
            '<!-- END FOOTER -->'
        }
        '<!-- END BODY -->'
    }     
}
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
    )

    $ID = "ChartID-" + (Get-RandomStringName -Size 8)

    $Div = New-HTMLTag -Tag 'div' -Attributes @{ id = $ID; class = $Positioning } 
    $Script = New-HTMLTag -Tag 'script' -Value {

        $Options = [ordered] @{}

        # Chart defintion type, size
        $Options.chart = @{
            type = $Type
        }
        if ($null -ne $Height) {
            $Options.chart.height = $Height
        }
        if ($null -ne $Width) {
            $Options.chart.width = $Width
        }  
        $Options.chart.toolbar = [ordered] @{
            show         = $true
            tools        = [ordered] @{
                download  = $true
                selection = $true
                zoom      = $true
                zoomin    = $true
                zoomout   = $true
                pan       = $true
                reset     = $true
            }
            autoSelected = 'zoom' 
        }
        
        

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

        # Convert Dictionary to JSON and return chart within SCRIPT tag
        # Make sure to return with additional empty string
        $JSON = $Options | ConvertTo-Json -Depth 5
        "var options = $JSON"
        ""
        "var chart = new ApexCharts(document.querySelector('#$ID'),
            options
        );"

        "chart.render();"
    }
    $Div
    $Script
}
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.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-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
    )

    <# 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-HTMLColumn {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace with Content"),
        [alias('ColumnCount')][ValidateSet('1', '2', '3', '4', '5 ', '6', '7', '8', '9', '10', '11', '12')][string] $Columns = 1,
        [alias('BackgroundShade')][RGBColors]$BackgroundColor,
        [switch] $Invisible
    )
    if ($null -ne $BackgroundColor) {
        $BackGroundColorFromRGB = ConvertFrom-Color -Color $BackgroundColor
        $DivColumnStyle = "background-color:$BackGroundColorFromRGB; width: calc(100% / $Columns - 10px);"
    } else {
        $DivColumnStyle = "width: calc(100% / $Columns - 10px);"
    }
    if ($Invisible) {
        $DivColumnStyle = "$DivColumnStyle box-shadow: unset !important;"
    }
    New-HTMLTag -Tag 'div' -Attributes @{ class = "defaultColumn defaultCard"; style = $DivColumnStyle } {
        Invoke-Command -ScriptBlock $Content
    }
}
Function New-HTMLContent {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace with Content"),
        [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
    )
    $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) {
        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;" # 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 ($IsHidden) {
        $DivContentStyle = "display:none;$BGStyleColor"
    } else { 
        $DivContentStyle = $BGStyleColor
    }

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

    # 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 '#' -OnClick "show('$RandomNumber');" -Style $ShowStyle -Text '(Show)' 
            New-HTMLAnchor -Id "hide_$RandomNumber" -Href '#' -OnClick "hide('$RandomNumber');" -Style $HideStyle -Text '(Hide)' 
        }

        New-HTMLTag -Tag 'div' -Attributes @{ class = 'collapsable'; id = $RandomNumber; } -Value {
            Invoke-Command -ScriptBlock $Content
        }
    }
}
Function New-HTMLHeading {
    Param (
        [validateset('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7')][string]$Heading,
        [string]$HeadingText,
        [validateset('', 'central')][string] $Type,
        [switch] $Underline
    )  
    $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-HTMLMessage {
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('')][ScriptBlock] $Content,
        $Text
    )
    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-HTMLReportOptions {
    [CmdletBinding(DefaultParameterSetName = 'NoSave')]
    param
    (
        [Parameter(Mandatory = $false, ParameterSetName = 'NoSave')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        [System.Collections.Hashtable]
        $LogoSources,
        [Parameter(Mandatory = $false, ParameterSetName = 'NoSave')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        [System.Collections.Hashtable]
        $ColorSchemes,
        [Parameter(Mandatory = $false, ParameterSetName = 'NoSave')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        $CSSName = "default",
        [Parameter(Mandatory = $false, ParameterSetName = 'NoSave')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        [String]
        $CSSPath,
        [Parameter(Mandatory = $false, ParameterSetName = 'NoSave')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        [String]
        $ScriptPath,
        [Parameter(Mandatory = $false, ParameterSetName = 'NoSave')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        [String]
        $ColorSchemePath,
        [Parameter(Mandatory = $false, ParameterSetName = 'NoSave')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        [String]
        $LogoPath,
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        [String]$SaveOptionsPath,
        [Parameter(Mandatory = $false, ParameterSetName = 'NoSave')]
        [Parameter(Mandatory = $false, ParameterSetName = 'Save')]
        [String]
        $ReportPath,

        [switch] $UseCssLinks,
        [switch] $UseStyleLinks,

        [string]$LeftLogoName = "Sample",
        [string]$RightLogoName = "Alternate",
        [string]$LeftLogoString,
        [string]$RightLogoString

    )
    if ($ColorSchemes -eq $null) {
        $ColorSchemes = Get-HTMLColorSchemes -SchemePath $ColorSchemePath
    }

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

    $ScriptHeaderContent = New-GenericList -Type [string]
    if ($UseCssLinks) {
        $ScriptHeaderContent.Add((Get-HTMLJavaScripts -UseLinks:$UseCssLinks -ScriptPath $ScriptPath))
    } else {
        $ScriptHeaderContent.Add((Get-HTMLJavaScripts -ScriptPath $ScriptPath))
    }

    $StyleHeaderContent = New-GenericList -Type [string]
    $StyleHeaderContent.Add((Get-HTMLCSS -Builtin -UseLinks:$UseStyleLinks))
    $StyleHeaderContent.Add((Get-HTMLCSS -CSSPath $CSSPath -CSSName $CSSName))

    $Options = [PSCustomObject] @{
        Logos         = $LogoSources
        ScriptContent = $ScriptHeaderContent
        StyleContent  = $StyleHeaderContent
        ColorSchemes  = $ColorSchemes
    }
    # Converts Styles to their binary representation
    Convert-StyleContent -Options $Options

    set-variable -Name GlobalColorSchemes -Value $ColorSchemes -Scope Global
    if ([string]::IsNullOrEmpty($SaveOptionsPath)) {
        Write-Output $Options
    } else {
        Write-Verbose "Saving Report CSS to $SaveOptionsPath"
        $StyleHeaderContent | Set-Content -Path (Join-Path $SaveOptionsPath default.css)
        Write-Verbose "Saving Report Color Schemes to $SaveOptionsPath"
        foreach ($SchemeName in $ColorSchemes.Keys) {
            $ColorSchemes[$SchemeName] | ConvertTo-Csv -NoTypeInformation -Delimiter ';' | ForEach-Object {$_.Replace('"', '')} | Out-File (Join-Path $SaveOptionsPath "$schemeName.rcs")
        }
        foreach ($LogoSource in $LogoSources.keys) {
            [IO.File]::WriteAllBytes((Join-Path $SaveOptionsPath "$LogoSource.jpg"), [Convert]::FromBase64String($LogoSources[$LogoSource].split(',')[1]))
        }
        foreach ($CSSFile in $CSSFiles) {
            get-content $CSSFile.FullName | set-content (Join-Path $SaveOptionsPath $CSSFile.name)
        }

    }
}
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
    )
    "<!-- CSS $ResourceComment -->"
    if ($Content -eq '') {
        New-HTMlTag -Tag 'link' -Attributes @{ rel = "stylesheet"; type = "text/css"; href = $Link } 
    } elseif ($FilePath -ne '') {
        New-HTMLTag -Tag 'style' -Attributes @{ type = 'text/css' } {
            Get-ResourceContent -FilePath $FilePath
        }
    } else {
        New-HTMLTag -Tag 'style' -Attributes @{ type = 'text/css' } {
            $Content
        }
    }
    "<!-- CSS $ResourceComment END -->"
}
function New-HTMLResourceJS {
    [alias('New-ResourceJS', 'New-JavaScript')]
    [CmdletBinding()]
    param(
        [alias('ScriptContent')][Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content,
        [string] $Link
    )   
    New-HTMlTag -Tag 'script' -Attributes @{ type = "text/javascript"; src = $Link } -Value $Content
}
function New-HTMLStatus {
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('')][ScriptBlock] $Content
    )
    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%'
    )
    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
    )
    if ($TabName -ne '') {
        # Start - Just in case you prefer to use old way - can be useful with tabs created out of order
        [Array] $Tabs = ($Script:HTMLSchema.TabsHeaders | Where-Object { $_.Name -eq $TabName})
        if ($Tabs.Count -gt 0) {
            $Tab = $Tabs[0]
            $Tab.Used = $true
            $Tab.Current = $true
        } else {
            Write-Warning "New-HTMLTab - Tab name $TabName is not defined in TabHeaders. Terminating!"
            Exit
        }
        # End
    } else {
        # Start Tabs Tracking - This code gets tab names from TabHeaders and uses its data. No need to define Tab Names for New-HTMLTab
        $Script:HTMLSchema.TabsHeaders | ForEach-Object { $_.Current = $false }
        [Array] $Tabs = ($Script:HTMLSchema.TabsHeaders | Where-Object { $_.Used -eq $false })
        if ($Tabs.Count -gt 0) {
            $Tab = $Tabs[0]
            $Tab.Used = $true
            $Tab.Current = $true
        } else {
            Write-Warning "New-HTMLTab - There are no unused tabs. Either TableHeaders are not defined or there are not enough of them. Terminating!"
            Exit
        }
        # End Tabs Tracking
    }
    if ($Tab.Active) {
        $Class = 'tab-pane active'
    } else {
        $Class = 'tab-pane'
    }

    # This is building HTML
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'tab-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-HTMLTabHeader {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)][string[]] $TabNames,
        [string] $DefaultTab,
        [string] $TabsType
    )
    if ($DefaultTab -ne '') {
        if ($TabNames -notcontains $DefaultTab) {
            Write-Warning 'New-HTMLTabHeader - DefaultTab is not part of TabNames. Unable to set DefaultTab'
        }
    }
    # Tabs Tracking Start - This creates Tabs Tracking Code that's used for New-HTMLTab and New-HTMLTable
    [int] $TabNumber = 0
    $Script:HTMLSchema.TabsHeaders = foreach ($Tab in $TabNames) {
        [string] $TabID = "Tab-$(Get-RandomStringName -Size 8)"

        $Information = @{
            Name    = $Tab
            Id      = $TabID
            Number  = $TabNumber++
            Used    = $false
            Active  = $false 
            Current = $false
        }
        if ($DefaultTab -ne '' -and $Tab -eq $DefaultTab) {
            $Information.Active = $true
        }
        $Information
    }
    if ($Script:HTMLSchema.TabsHeaders.Active -notcontains $true) {
        $Script:HTMLSchema.TabsHeaders[0].Active = $true
    }
    # Tabs Tracking End
    
    New-HTMLTag -Tag 'div' -Attributes @{ class = '' } {
        New-HTMLTag -Tag 'ul' -Attributes @{ class = 'tab-nav'} {
            foreach ($Tab in $Script:HTMLSchema.TabsHeaders) {
                New-HTMLTag -Tag 'li' {
                    if ($Tab.Active) {
                        New-HTMLAnchor -Class 'button active' -HrefLink "#$($Tab.Id)" -Text $Tab.Name
                    } else {
                        New-HTMLAnchor -Class 'button' -HrefLink "#$($Tab.Id)" -Text $Tab.Name
                    }
                }
            }
        }
    }
}
function New-HTMLTable {
    [CmdletBinding()]
    param(
        [alias('ArrayOfObjects', 'Object', 'Table')][Array] $DataTable,
        [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,
        [string[]][ValidateSet('display', 'cell-border', 'compact', 'hover', 'nowrap', 'order-column', 'row-border', 'stripe')] $Style = @('display', 'compact'),
        [switch]$Simplify
    )
    # Theme creator https://datatables.net/manual/styling/theme-creator

    [string] $DataTableName = "DT-$(Get-RandomStringName -Size 8)" # this builds table ID
    [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
    $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

        "paging"     = -not $DisablePaging

        <# 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
        "info"       = -not $DisableInfo
        "procesing"  = -not $DisableProcessing
        "responsive" = @{ 
            details = -not $DisableResponsiveTable 
        }
        "select"     = -not $DisableSelect
        "searching"  = -not $DisableSearch
        "stateSave"  = -not $DisableStateSave

    } | ConvertTo-Json -Depth 6


    [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) {
        $TableAttributes = @{ id = $DataTableName; class = ($Style -join ' ') }
        if ($Tab.Active -eq $true) {
            New-HTMlTag -Tag 'script' {
                @"
                `$(document).ready(function() {
                    `$('#$DataTableName').DataTable($($Options));
                });
"@

            }
        } else {
            [string] $TabName = $Tab.Id
            New-HTMlTag -Tag 'script' {
                @"
                    `$(document).ready(function() {
                        `$('a').on('click', function() {
                            if (`$(this).attr("href") == "#$TabName" && !$.fn.dataTable.isDataTable("#$DataTableName")) {
                                `$('#$DataTableName').DataTable($($Options));
                            };
                        });
                    });
"@

            }
        }
    } else {
        $TableAttributes = @{ class = 'sortable' }
    }
    # 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-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-HTMLTimeline {
    param(
        [Parameter(Mandatory = $false, Position = 0)][alias('TimeLineItems')][ScriptBlock] $Content
    )
    New-HTMLTag -Tag 'div' -Attributes @{ class = 'timelineSimpleContainer' } {
        if ($null -eq $Value) { '' } else { Invoke-Command -ScriptBlock $Content }
    }
}
function New-HTMLTimelineItem {
    param(
        $Date = (Get-Date),
        $HeadingText,
        $Text
    )
    $Attributes = @{
        class     = 'timelineSimple-item'
        "date-is" = $Date
    }
    New-HTMLTag -Tag 'div' -Attributes $Attributes -Value {
        New-HTMLTag -Tag 'h1' -Attributes @{ class = 'timelineSimple' } {
            $HeadingText
        }
        New-HTMLTag -Tag 'p' -Attributes @{ class = 'timelineSimple'} {
            $Text
        }
    }
}
function New-HTMLToast {
    [CmdletBinding()]
    param(
        [string] $TextHeader,
        [string] $Text,
        [ValidateSet('Green', 'Blue', 'Orange')] $Color = "Green",
        [ValidateSet('Success', 'Information', 'Exclamation')][string] $Icon = 'Success'
    )
    $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 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,
        [switch] $Supress
    )
    if ([string]::IsNullOrEmpty($FilePath)) {
        Write-Warning "Save-HTML - FilePath parameter $ReportPath is empty, using Temporary"
        $FilePath = Get-FileName -Temporary -Extension 'html'
    } else {
        if (Test-Path -LiteralPath $FilePath) {
            Write-Warning 'Save-HTML - Path already exists. Report will be overwritten.'
        }
    }
    Write-Verbose "Save-HTML - Saving HTML to file $FilePath"


    $HTML | Set-Content -Path $FilePath -Force
    Write-Verbose $FilePath
    if ($ShowHTML) {
        #Start-Sleep -Seconds 1
        Invoke-Item $FilePath
    }
    if (-not $Supress) {
        $FilePath
    }
}
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]
        # skip adding properties that are empty
        if ($PropertyValue -ne '') {
            [void] $HTML.Append(" $Property=`"$PropertyValue`"")
        }
    }
    if (($null -ne $HtmlObject.Value) -and ($HtmlObject.Value -ne '')) {
        [void] $HTML.Append(">")
        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 @('Get-HTMLBarChart','Get-HTMLBarChartObject','Get-HTMLLineChart','Get-HTMLLineChartObject','Get-HTMLPieChart','Get-HTMLPieChartObject','Get-HTMLStackedChart','Get-HTMLStackedChartObject','New-HTML','New-HTMLAnchor','New-HTMLAnchorLink','New-HTMLAnchorName','New-HTMLChart','New-HTMLChartDataSet','New-HTMLCodeBlock','New-HTMLColumn','New-HTMLContent','New-HTMLHeading','New-HTMLHorizontalLine','New-HTMLImage','New-HTMLMessage','New-HTMLResourceCSS','New-HTMLResourceJS','New-HTMLStatus','New-HTMLStatusItem','New-HTMLTab','New-HTMLTabHeader','New-HTMLTable','New-HTMLTag','New-HTMLTimeline','New-HTMLTimelineItem','New-HTMLToast','Save-HTML') `
    -Alias @('New-CSS','New-JavaScript','New-ResourceCSS','New-ResourceJS')