PSWriteHTML.psm1

function Add-HTMLHead {
    [CmdletBinding()]
    param(
        [string] $TitleText,
        [switch] $UseStyleLinks,
        [switch] $UseCssLinks,
        [switch] $AddAuthor,
        [switch] $HideTitle,
        $Options
    )
    [string] $CurrentDate = (Get-Date).ToString($DateFormat)

    # 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)
            }
        }
    }

    $HTML = New-GenericList -Type [string]
    $HTML.Add("<!DOCTYPE HTML>")
    if ($AddAuthor) {
        if ([String]::IsNullOrWhiteSpace($Author)) {
            $Author = $env:USERNAME
        }
        $HTML.Add("<!--- This page was autogenerated $CurrentDate By $Author -->")
    }
    $HTML.Add("<html>")
    $HTML.Add('<!-- Header -->')
    $HTML.Add('<head>')
    if (-not $HideTitle) {
        $HTML.Add("<Title>$TitleText</Title>")
    }
    $HTML.Add("<!-- Styles -->")
    $HTML.Add("$($Options.StyleContent)")
    $HTML.Add('<!-- Scripts -->')
    $HTML.Add("$($Options.ScriptContent)")
    $HTML.Add('</head>')
    $HTML.Add('')
    return $HTML
}

function Add-HTMLBodyOpening {
    [CmdletBinding()]
    param(
        $Options,
        [string] $TitleText,
        [switch] $HideTitle,
        [switch] $HideLogos,
        [switch] $HideDate,
        [string] $PrimaryColorHex,
        [string] $CurrentDate
    )
    $HTML = New-GenericList -Type [string]
    $HTML.Add('<!-- Body -->')
    #$HTML.Add('<body onload="hide();">')
    $HTML.Add('<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>
"@

    }

    if (-not $HideTitle) {
        $HTML.Add("<!-- Report Header -->")
        $HTML.Add($LogoContent)
        $HTML.Add("<div class=`"pageTitle`">$TitleText</div>")
        $HTML.Add("<hr />")
    }
    if (-not $HideDate) {
        $HTML.Add("<div class=`"ReportCreated`">Report created on $($CurrentDate)</div>")
    }

    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"'
        }
    }
    return $HTML
}
function Add-HTMLHorizontalLine {
    [CmdletBinding()]
    param(

    )
    return '<hr />'
}
function Add-Image {
    <#
    .SYNOPSIS
 
 
    .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
    Add-Image -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
    )
    [System.Collections.IDictionary] $Image = [Ordered] @{
        Tag        = 'img'
        Attributes = [ordered]@{
            'src'    = "$Source"
            'alt'    = "$AlternativeText"
            'width'  = "$Height"
            'height' = "$Width"
        }
        Value      = '' # if value is blank it will not be used
    }

    [System.Collections.IDictionary] $A = [Ordered] @{
        Tag        = 'a'
        Attributes = [ordered]@{
            'target' = $Target
            'href'   = $UrlLink
        }
        Value      = $Image
    }

    [System.Collections.IDictionary] $Div = [Ordered] @{
        Tag        = 'div'
        Attributes = [ordered]@{
            'class' = "$Class".ToLower()
        }
        Value      = $A
    }
    $HTML = Set-Tag -HtmlObject $Div
    return $HTML

}
function Format-AnchorTag {
    param (
        $Object
    )

    $AnchorTag = ''
    try {
        $href = $Object.url
        if($null -eq $href)
        {
            throw "Missing parameter 'url' (href)"
        }
        # using default target if none is set
        $target = '_self'
        if($null -ne $Object.target)
        {
            $target = $Object.target
        }

        # using url if no content is set
        $Content = $href
        if($null -ne $Object.Content)
        {
            $Content = $Object.Content
            if($Content -is [System.Management.Automation.PSCustomObject])
            {
                # could be an image
                $Content = Format-Html -Object $Content
            }
        }

        $AnchorTag = '<a href="{0}" target="{1}">{2}</a>' -f $href, $target, $Content
    }
    catch {
        Write-Warning "Failed to format anchor tag, error was $_"
    }

    $AnchorTag
}
function Format-Html {
    param (
        $Object
    )

    switch ($Object) {
        {$_._type -eq 'img'} { Format-ImageTag -Object $Object}
        {$_._type -eq 'link'} { Format-AnchorTag -Object $Object }
        Default {
            throw "Unknown type, $($_._type)"
        }
    }    
}
function Format-ImageTag {
    param (
        $Object
    )
    
    $imgtag = ''
    try {
        # ex. <img src="smiley.gif" alt="Smiley face" height="42" width="42">
        $src = $Object.src
        $alt = $Object.alt
        [string]$width = $Object.width
        [string]$height = $Object.height   

        $imgtag = '<img src="{0}" alt="{1}" height="{2}" width="{3}">' -f $src, $alt, $width, $height

        if([string]::IsNullOrEmpty($alt))
        {
            $imgtag = $imgtag -replace 'alt=""'
        }

        if([string]::IsNullOrEmpty($width))
        {
            $imgtag = $imgtag -replace 'width=""'
        }        

        if([string]::IsNullOrEmpty($height))
        {
            $imgtag = $imgtag -replace 'height=""'
        }        
    }
    catch {
        Write-Warning "Failed to format image tag, error was $_"
    }

    $imgtag
}
Function Get-Functions {
    PAram (
        $pattern,
        $path = "$pwd\*",
        [switch]$Recurse = $false
    )
    $parser = [System.Management.Automation.PSParser]
    Get-ChildItem (join-Path $path '\*') -Recurse:$Recurse -Include *.ps1, *.psm1 | ForEach {
        $content = [IO.File]::ReadAllText($_.FullName)
        $tokens = $parser::Tokenize($content, [ref] $null)
        $count = $tokens.Count
        $(
            for ($idx = 0; $idx -lt $count; $idx += 1) {
                if ($tokens[$idx].Content -eq 'function') {
                    $targetToken = $tokens[$idx + 1]
                    [PSCustomObject] @{
                        FileName     = $_.FullName
                        FunctionName = $targetToken.Content

                        Line         = $targetToken.StartLine
                    } | Select FunctionName, FileName, Line
                }
            }
        ) #| Where {$_.FunctionName -match $pattern}
    }
}
Function Get-HTMLAnchor {
    <#
    .SYNOPSIS
        Creates an Anchor
    .PARAMETER AnchorName
        The Actual Anchor
#>

    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $AnchorName
    )

    $Anchor = '<a name="' + $AnchorName + '"></a>'
    Write-Output $Anchor
}
Function Get-HTMLAnchorLink {
    <#
    .SYNOPSIS
        creates Hyperlink for an Anchor
        .PARAMETER AnchorName
            The Actual name of the Anchor (Hidden)
        .PARAMETER AnchorText
            The HyperLink text. Will default to $AnchorNname if not specified
#>

    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $false)]
        [String]
        $AnchorName,
        [Parameter(Mandatory = $true)]
        [String]
        $AnchorText
    )
    if ($AnchorText -eq $null -or $AnchorText -eq '') {$AnchorText = $AnchorName}
    $report = '<a class="alink" href="#' + $AnchorName + '">' + $AnchorText + '</a>'

    Write-Output $report
}
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     = -join ((65..90) + (97..122) | Get-Random -Count 12 | ForEach-Object {[char]$_})
        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-HTMLContentDataTable {
    <#
    .SYNOPSIS
        Creates an HTML 5 Data table from an array of objects
        .PARAMETER ArrayOfObjects
            An array of objects
        .PARAMETER Paging
        .PARAMETER PagingOptions
        .PARAMETER Ordering
        .PARAMETER Info
        .PARAMETER HideFooter
 
#>

    param
    (
        [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
        [alias('ArrayOfObjects', 'Object', 'Table')][Array]$DataTable,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false)]
        [switch]$DisablePaging,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false)]
        [string]$PagingOptions = '15,25, 50, 100,',
        [Parameter(Mandatory = $false, ValueFromPipeline = $false)]
        [switch]$DisableOrdering,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false)]
        [switch]$DisableInfo,
        [Parameter(Mandatory = $false, ValueFromPipeline = $false)]
        [switch]$HideFooter,
        [switch]$DisableColumnReorder,
        [switch]$DisableResponsiveTable,
        [switch]$DisableSelect

    )
    if ($DisablePaging)    {$Paging = 'false'} else {$Paging = 'true'}
    if ($DisableOrdering) {$Ordering = 'false'} else {$Ordering = 'true'}
    if ($DisableInfo) {$Info = 'false'} else {$Info = 'true'}
    if ($DisableColumnReorder) { $ColumnReorder = 'false' } else { $ColumnReorder = 'true' }
    if ($DisableResponsiveTable) { $ResponsiveTable = 'false' } else { $ResponsiveTable = 'true' }
    if ($DisableSelect) { $Select = 'false' } else { $Select = 'true' }

    [string] $DTInstance = Get-RandomStringName -Size 8

    $TableHeader = @'
<script>
 $(document).ready(function() {
     $('#
'@


    $TableHeader += $DTInstance
    $TableHeader += @"
').DataTable({
        dom: 'Bfrtip',
        buttons: [
            'copyHtml5',
            'excelHtml5',
            'csvHtml5',
            'pdfHtml5'
        ],
        "paging": $($Paging),
        "pagingType": "full_numbers",
        "lengthMenu": [[$PagingOptions -1], [$PagingOptions "All"]],
        "ordering": $($Ordering),
        "info": $($Info),
        "colReorder": $($ColumnReorder),
        "responsive": { details: $($ResponsiveTable) },
        "select": $($Select),
        "columns": [
"@

    $ArraryHeader = $DataTable | ConvertTo-Html -Fragment
    $HeadersText = ($ArraryHeader[2] -replace '<tr>', '' -replace '<th>', '' -replace '</tr>', '')
    $ColumnHeaders = ($HeadersText.substring(0, $HeadersText.Length - 5)) -split '</th>'

    foreach ($ColumnHeader in $ColumnHeaders ) {
        $TableHeader += '{ "data": "' + $ColumnHeader + '" },'
    }
    $TableHeader += @'
]
     });
 } );
</script>
'@

    $TableHeader = $TableHeader.Replace(',]', ']')

    $NumberOfColumns = ($DataTable | Get-Member -MemberType NoteProperty  | Select-Object Name).Count

    $Report = $DataTable | ConvertTo-Html -Fragment
    <# Sample view of output
 
    <table>
    <colgroup><col/><col/><col/><col/></colgroup>
    <tr><th>ForestMode</th><th>Name</th><th>RootDomain</th><th>SchemaMaster</th></tr>
    <tr><td>Windows2012R2Forest</td><td>ad.evotec.xyz</td><td>ad.evotec.xyz</td><td>AD1.ad.evotec.xyz</td></tr>
    </table>
 
    When formatted:
    <table>
        <colgroup>
            <col />
            <col />
            <col />
            <col />
        </colgroup>
        <tr>
            <th>ForestMode</th>
            <th>Name</th>
            <th>RootDomain</th>
            <th>SchemaMaster</th>
        </tr>
        <tr>
            <td>Windows2012R2Forest</td>
            <td>ad.evotec.xyz</td>
            <td>ad.evotec.xyz</td>
            <td>AD1.ad.evotec.xyz</td>
        </tr>
    </table>
    #>


    # Removes colgroup and col that is empty
    $Report = $Report -replace '<col/>', "" -replace '<colgroup>', "" -replace '</colgroup>', ""

    # Connects Table Header with Table by replacing Table Tag with new tag and thead
    $Report = $Report -replace '<table>', ('<table id="' + $DTInstance + '" class="display compact"><thead>')

    # This splits 1st row from the rest of the table making it table header, closes theader and opens up tbody
    $Report = $Report -replace '</th></tr>', '</th></tr></thead><tbody>'

    # This closes body instead of table
    $Report = $Report -replace "</table>", "</tbody>"
    if (-not $HideFooter) {
        # this fixes footer (column names at the bottom of table)
        $Footer = '<tfoot><tr>'
        foreach ($Header in $ColumnHeaders) {
            $Footer += '<th>' + $Header + '</th>'
        }
        $Footer += '</tr></tfoot>'
        $Report += $Footer  # $Report = $Report -replace "LoadFooterHere", $Footer
    }

    #$Report = $Report -replace 'URL01NEW', '<a target="_blank" href="'
    #$Report = $Report -replace 'URL01', '<a href="'
    #$Report = $Report -replace 'URL02', '">'
    #$Report = $Report -replace 'URL03', '</a>'

    $Report += "</table>"
    return ($TableHeader + $Report)
}
Function Get-HTMLContentTable {
    <#
    .SYNOPSIS
        Creates an HTML table from an array of objects
        .PARAMETER ArrayOfObjects
            An array of objects
        .PARAMETER Fixed
            fixes the html column width by the number of columns
        .PARAMETER GroupBy
            The column to group the data. make sure this is first in the array
        .PARAMETER Column Totals
            an Array of headers from that exist in the array of objects to be summed up
#>

    [CmdletBinding()]
    param(
        [alias('ArrayOfObjects', 'Object', 'Table')][Array]$DataTable,
        [String]$GroupBy,
        [Array]$ColumnCounts,
        [Switch]$Fixed,
        [Array]$ColumnAverages,
        [Switch]$NoSortableHeader,
        [Array]$ColumnTotals

    )

    # save the advanced properties (objects) to a hashtable for later use
    $AdvancedTypes = @{}
    $ArrayOfObjects | ForEach-Object {
        $obj = $_
        $obj.PSObject.Properties | Where-Object {$_.TypeNameOfValue -eq 'System.Management.Automation.PSCustomObject'} | ForEach-Object {
            $Key = [guid]::NewGuid().Guid -replace '-',''
            $PropertyName = $_.Name
            $o = $obj.$PropertyName
            $AdvancedTypes[$Key] = $o
            # the key will be put into a table which we can then later replace using the content of the object we just saved to that key
            $obj.$PropertyName = $Key
        }
    }

    if ($GroupBy -eq '') {
        Write-Verbose "Get-HTMLContentTable - Running this option 1"
        $Report = $DataTable | ConvertTo-Html -Fragment
        $Report = $Report -replace '<col/>', "" -replace '<colgroup>', "" -replace '</colgroup>', ""
        $Report = $Report -replace "<tr>(.*)<td>Green</td></tr>", "<tr class=`"green`">`$+</tr>"
        $Report = $Report -replace "<tr>(.*)<td>Yellow</td></tr>", "<tr class=`"yellow`">`$+</tr>"
        $Report = $Report -replace "<tr>(.*)<td>Red</td></tr>", "<tr class=`"red`">`$+</tr>"
        $Report = $Report -replace "<tr>(.*)<td>Odd</td></tr>", "<tr class=`"odd`">`$+</tr>"
        $Report = $Report -replace "<tr>(.*)<td>Even</td></tr>", "<tr class=`"even`">`$+</tr>"
        $Report = $Report -replace "<tr>(.*)<td>None</td></tr>", "<tr>`$+</tr>"
        $Report = $Report -replace '<th>RowColor</th>', ''

        if ($Fixed) {
            $Report = $Report -replace '<table>', '<table class="fixed">'
        } else {
            if (!($NoSortableHeader)) {
                $Report = $Report -replace '<table>', '<table class="sortable">'
            }
        }
    } else {
        Write-Verbose "Get-HTMLContentTable - Running this option 2"
        $NumberOfColumns = ($DataTable | Get-Member -MemberType NoteProperty  | Select-Object Name).Count
        $Groupings = @()
        $DataTable | Select-Object $GroupBy -Unique  | Sort-Object $GroupBy | ForEach-Object { $Groupings += [String]$_.$GroupBy}
        if ($Fixed) {
            $Report = '<table class="fixed">'
        } else {
            $Report = '<table>'
        }
        $GroupHeader = $DataTable | ConvertTo-Html -Fragment
        $GroupHeader = $GroupHeader -replace '<col/>', "" -replace '<colgroup>', "" -replace '</colgroup>', "" -replace '<table>', "" -replace '</table>', "" -replace "<td>.+?</td>" -replace "<tr></tr>", ""
        $GroupHeader = $GroupHeader -replace '<th>RowColor</th>', ''
        $Report += $GroupHeader
        foreach ($Group in $Groupings) {
            $Report += "<tr><td colspan=`"$NumberOfColumns`" class=`"groupby`">$Group</td></tr>"
            $GroupBody = $DataTable | Where-Object { [String]$($_.$GroupBy) -eq $Group } | Select-Object * -ExcludeProperty $GroupBy | ConvertTo-Html -Fragment
            $GroupBody = $GroupBody -replace '<col/>', "" -replace '<colgroup>', "" -replace '</colgroup>', "" -replace '<table>', "" -replace '</table>', "" -replace "<th>.+?</th>" -replace "<tr></tr>", "" -replace '<tr><td>', "<tr><td></td><td>"
            $GroupBody = $GroupBody -replace "<tr>(.*)<td>Green</td></tr>", "<tr class=`"green`">`$+</tr>"
            $GroupBody = $GroupBody -replace "<tr>(.*)<td>Yellow</td></tr>", "<tr class=`"yellow`">`$+</tr>"
            $GroupBody = $GroupBody -replace "<tr>(.*)<td>Red</td></tr>", "<tr class=`"red`">`$+</tr>"
            $GroupBody = $GroupBody -replace "<tr>(.*)<td>Odd</td></tr>", "<tr class=`"odd`">`$+</tr>"
            $GroupBody = $GroupBody -replace "<tr>(.*)<td>Even</td></tr>", "<tr class=`"even`">`$+</tr>"
            $GroupBody = $GroupBody -replace "<tr>(.*)<td>None</td></tr>", "<tr>`$+</tr>"
            $Report += $GroupBody
        }

    }

    # replace the guids with html formatted tags
    $AdvancedTypes.Keys | ForEach-Object{
        $Key = $_
        $Object = $AdvancedTypes[$Key]
        $Report = $Report -replace $Key, (Format-Html -Object $Object)
    }

    # Not sure what that is for, must be something that ReportHTML guy was using - will be removed
    # spaelling: it is because ConvertTo-Html will botch any existing html. Below is a very clumsy solution
    # I have put my solution for this into this file.
    $Report = $Report -replace 'URL01NEW', '<a target="_blank" href="'
    $Report = $Report -replace 'URL01', '<a href="'
    $Report = $Report -replace 'URL02', '">'
    $Report = $Report -replace 'URL03', '</a>'

    if ($Report -like "*<tr>*" -and $report -like "*odd*" -and $report -like "*even*") {
        $Report = $Report -replace "<tr>", '<tr class="header">'
    }

    if ($ColumnTotals.count -gt 0 -or $ColumnAverages.count -gt 0 -or $ColumnCounts.count -gt 0 ) {
        Write-Verbose "Get-HTMLContentTable - Running this option 3"

        $TableFooter = $DataTable | ConvertTo-Html -Fragment
        $TableFooter = $TableFooter -replace '<col/>', "" -replace '<colgroup>', "" -replace '</colgroup>', "" -replace '<table>', "" -replace '</table>', "" -replace "<td>.+?</td>" -replace "<tr></tr>", ""
        $TableFooter = $TableFooter -replace '<th>RowColor</th>', ''
        #$ColumnTotal
        foreach ($ColumnTotal in $ColumnTotals) {
            $TableFooter = $TableFooter -replace $ColumnTotal, ("sum:" + ($DataTable | measure $ColumnTotal -Sum ).sum)
        }
        #ColumnAverage
        foreach ($ColumnAverage in $ColumnAverages) {
            $TableFooter = $TableFooter -replace $ColumnAverage, ("avg:" + ($DataTable | measure $ColumnAverage -Average ).average)
        }
        #ColumnCount
        foreach ($ColumnCount in $ColumnCounts) {
            $TableFooter = $TableFooter -replace $ColumnCount, ("count:" + ($DataTable | measure $ColumnCount).count)
        }
        #Cleanup
        foreach ($Column in ($DataTable | Get-Member )) {
            $TableFooter = $TableFooter -replace ("<th>" + $Column.Name + "</th>"), '<td></td>'
        }

        $TableFooter = $TableFooter -replace '<th>', '<td class="totalrow">' -replace "</th>", '</td>'
        $TableFooter = $TableFooter -replace '<tr>', '<tr class="totalrow">'

        $Report = $Report -replace "</table>", ""
        $Report += "<tfoot>"
        $Report += $TableFooter
        $Report += "</tfoot>"
        # We need to close the table because we removed closing to add TableFooters.
        $Report += "</table>"
    }
    return $Report
}
Function Get-HTMLContentTableAdvanced {
    <#
        .SYNOPSIS
            Code borrowed from https://www.powershellgallery.com/packages/EnhancedHTML2
 
 
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
        [alias('ArrayOfObjects', 'Object', 'Table')][Array]$DataTable
    )

    $out = ''
    $out += "<table>"

    foreach ($object in $DataTable) {
        $datarow = ''
        $headerrow = ''

        $properties = $object | Get-Member -MemberType Properties | Select-Object -ExpandProperty Name

        foreach ($prop in $properties) {
            #$prop = $properties[0]
            $name = $null
            $value = $null

            if ($prop -is [string]) {
                $name = $Prop
                $value = $object.($prop)
            } else {
                Write-Warning "Unhandled property $prop"
            }

            $headerrow += "<th>$name</th>"
            $datarow += "<td>$value</td>"
        }

        if (-not $wrote_first_line ) {
            $out += "<tr>$headerrow</tr><tbody>"
            $wrote_first_line = $true
        }
        $out += "<tr>$datarow</tr>"
    }

    $out += "</table>"


    return $out
}
Function Get-HTMLContentText {
    <#
    .SYNOPSIS
        Creates an HTML entry with heading and detail
        .PARAMETER Heading
            The type of logo
        .PARAMETER Detail
             Some additional pish
#>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false)]
        [String]
        $Heading,
        [Parameter(Mandatory = $false)]
        [String]
        $Detail
    )

    $Report = @"
<table><tbody>
    <tr>
    <th class="content">$Heading</th>
    <td class="content">$($Detail)</td>
    </tr>
</tbody></table>
"@

    $Report = $Report -replace 'URL01NEW', '<a target="_blank" href="'
    $Report = $Report -replace 'URL01', '<a href="'
    $Report = $Report -replace 'URL02', '">'
    $Report = $Report -replace 'URL03', '</a>'
    Return $Report
}
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"
            )
            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"
        )
        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 = GenerateRandomColorScheme -numberofschemes $DataCount
        }
    }
    else
    {
        $ChartColorScheme = GenerateRandomColorScheme -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
}
Function Get-HTMLLineChartObject
{
<#
    .SYNOPSIS
        create a Line chart object for use with Get-HTMLLineChart
#>

    [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 = New-Object PSObject -Property @{`
        Width = $Width
        Height = $Height
    }
    
    if($DataSetNames.Count -eq 0)
    {
        $DataSetNames = @($DataSetName)
    }

    $DataDefinition = New-Object PSObject -Property @{
        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 = New-Object PSObject -Property @{`
        ChartType = $ChartType
        ColorSchemeName = "$thisColorScheme"
        Showlabels = $true
        fill = $false
        borderWidth = "1"
        responsive = 'false'
        animateScale = 'true'
        animateRotate = 'true'
        legendPosition = 'bottom'
    }
    
    $ChartObject = New-Object PSObject -Property @{`
        ObjectName = -join ((65..90) + (97..122) | Get-Random -Count 12 | ForEach-Object {[char]$_})
        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
}
Function Get-HTMLPieChartObject {
    <#
    .SYNOPSIS
        create a Bar chart object for use with Get-HTMLBarChart
#>

    [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     = -join ((65..90) + (97..122) | Get-Random -Count 12 | ForEach-Object {[char]$_})
        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-Parameters {
    Param (
        $Cmdlet,
        [switch]$ShowCommon,
        [switch]$Full
    )
    $command = Get-Command $Cmdlet -ea silentlycontinue

    # resolve aliases (an alias can point to another alias)
    while ($command.CommandType -eq "Alias") {
        $command = Get-Command ($command.definition)
    }
    if (-not $command) { return }

    foreach ($paramset in $command.ParameterSets) {
        $Output = @()
        foreach ($param in $paramset.Parameters) {
            if ( ! $ShowCommon ) {
                if ($param.aliases -match "vb|db|ea|wa|ev|wv|ov|ob|wi|cf") { continue }
            }
            $process = "" | Select-Object Name, Type, ParameterSet, Aliases, Position, IsMandatory,
            Pipeline, PipelineByPropertyName
            $process.Name = $param.Name
            if ( $param.ParameterType.Name -eq "SwitchParameter" ) {
                $process.Type = "Boolean"
            } else {
                switch -regex ( $param.ParameterType ) {
                    "Nullable``1\[(.+)\]" { $process.Type = $matches[1].Split('.')[-1] + " (nullable)" ; break }
                    default { $process.Type = $param.ParameterType.Name }
                }
            }
            if ( $paramset.name -eq "__AllParameterSets" ) { $process.ParameterSet = "Default" }
            else { $process.ParameterSet = $paramset.Name }
            $process.Aliases = $param.aliases
            if ( $param.Position -lt 0 ) { $process.Position = $null }
            else { $process.Position = $param.Position }
            $process.IsMandatory = $param.IsMandatory
            $process.Pipeline = $param.ValueFromPipeline
            $process.PipelineByPropertyName = $param.ValueFromPipelineByPropertyName
            $output += $process
        }
        if ( ! $Full ) {
            $Output | Select-Object Name, Type, ParameterSet, IsMandatory, Pipeline
        } else { Write-Output $Output }
    }
}

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

    param(
        [int]$Min = 0,
        [int]$max = 255
    )
    Write-Output ([string](Get-Random -Maximum $max -Minimum $Min) + ',' )
}
Function Get-RandomColorScheme {
    <#
.SYNOPSIS
    Generate a colour scheme
.PARAMETER $NumberOfSchemes
#>

    param
    (
        [Parameter(Mandatory = $false)]
        [int]
        $NumberOfSchemes = 1
    )

    $Hover = '0.3)'
    $color = '0.6)'
    $border = '1)'
    $background = '0.7)'
    $ColorSwing = 8

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

    $ColorScheme = @()
    $i = 0
    while ($i -ne $NumberOfSchemes ) {
        switch ($ColorReference) {
            1 {$base = 'rgba(' + (Get-RandomColor -min  $BCMin -max $BCMax) + (Get-RandomColor) + (Get-RandomColor) }
            2 {$base = 'rgba(' + (Get-RandomColor) + (Get-RandomColor -min  $BCMin -max $BCMax) + (Get-RandomColor) }
            3 {$base = 'rgba(' + (Get-RandomColor) + (Get-RandomColor) + (Get-RandomColor -min  $BCMin -max $BCMax) }
        }

        $Scheme = '' | select Colour, Background, Hover , Border
        $Scheme.Background = $base + $background
        $Scheme.Border = $base + $border
        $Scheme.Colour = $base + $color
        $Scheme.Hover = $base + $Hover
        $ColorScheme += $Scheme
        $i++
    }

    Write-Output $ColorScheme
}
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] $HideTitle,
        [switch] $NoScript,
        [string] $PrimaryColorHex,
        [switch] $AddAuthor,
        [string] $Author,
        [switch] $HideDate,
        [string] $DateFormat = 'yyyy-MM-dd HH:mm:ss',
        [String] $FooterText
    )
    Begin {
        [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

    }
    Process {
        $HTML = "<HTML>"
        $HTML += Add-HTMLHead `
            -Options $Options `
            -UseStyleLinks:$UseStyleLinks `
            -UseCssLinks:$UseCssLinks `
            -TitleText $TitleText `
            -AddAuthor:$AddAuthor `
            -HideTitle:$HideTitle
        $HTML += Add-HTMLBodyOpening -Options $Options -TitleText $TitleText -HideLogos:$HideLogos -HideTitle:$HideTitle -HideDate:$HideDate -PrimaryColorHex $PrimaryColorHex -CurrentDate $CurrentDate
        $HTML += Invoke-Command -ScriptBlock $HtmlData
        $HTML += Get-JavaScript -FilePath "$PSScriptRoot\Resources\JS\Additional\LoadEnlighter.js"

    }
    End {
        $HTML += "</BODY>"
        if ($null -ne $FooterText) {
            $FooterText = "Copyright &#169; $([DateTime]::Now.Year). All Rights Reserved."
        }
        $HTML += Add-HTMLHorizontalLine
        $HTML += @"
        <!-- FOOTER -->
        <div class="footer">$FooterText</div>
        <!-- END BODY -->
"@


        $HTML += "</HTML>"
        return $HTML
    }
}
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
    #>


    [System.Collections.IDictionary] $Builder = [Ordered] @{
        Tag        = 'pre'
        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()
        }
        Value      = $Code
    }
    $HTML = Set-Tag -HtmlObject $Builder

    return $HTML
}
Function New-HTMLColumn {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace with Content"),
        [int]$ColumnNumber = 1,
        [int]$ColumnCount = 1
    )
    Begin {
        $HTML = New-GenericList -Type [string]
    }
    Process {
        $ColumnItem = [string]$ColumnNumber + "of" + [string]$ColumnCount
        Write-Verbose $ColumnItem
        $ColumnItem = $ColumnItem.replace('1', 'one').replace('2', 'two').replace('3', 'three').replace('4', 'four').replace('5', 'five').replace('6', 'six')
        Write-Verbose $ColumnItem
        $HTML.Add('<div class="' + $ColumnItem + ' column">')

        $HTML.Add((Invoke-Command -ScriptBlock $Content))
    }
    End {
        $HTML.Add('</div>')
        return $HTML
    }
}
Function New-HTMLContent {
    <#
    .SYNOPSIS
        Creates a section in HTML
        .PARAMETER HeaderText
            The heading for the section
        .PARAMETER IsHidden
            Switch parameter to define if the section can collapse
        .PARAMETER BackgroundShade
            An int for 1 to 6 that defines background shading
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace with Content"),
        [Parameter(Mandatory = $false)][string]$HeaderText,
        [Parameter(Mandatory = $false)][switch]$IsHidden,
        [Parameter(Mandatory = $false)][string]$Anchor,
        [Parameter(Mandatory = $false)][validateset(1, 2, 3, 4, 5, 6)][int]$BackgroundShade,
        [Parameter(Mandatory = $false)][switch] $CanCollapse
    )
    Begin {
        $HTML = New-GenericList -Type [string]
    }
    Process {
        switch ($BackgroundShade) {
            1 { $bgColorCode = "#F8F8F8" }
            2 { $bgColorCode = "#D0D0D0" }
            3 { $bgColorCode = "#A8A8A8" }
            4 { $bgColorCode = "#888888" }
            5 { $bgColorCode = "#585858" }
            6 { $bgColorCode = "#282828" }
            default { $bgColorCode = "#ffffff" }
        }

        if ([String]::IsNullOrEmpty($Anchor)) {
            $InsertAnchor = 'name="' + $Anchor + '"'
        }
        $RandomNumber = Get-Random
        if ($IsHidden) {
            $HTML.Add(@"
<div class="section">
<div class="header">
    <a name="$($HeaderText)">$($HeaderText)</a> (<a id="show_$RandomNumber" href="#" onclick="show('$RandomNumber');" style="color: #ffffff;">Show</a>
    <a id="hide_$RandomNumber" href="#" onclick="hide('$RandomNumber');" style="color: #ffffff; display:none;">Hide</a>)
</div>
<div class="content" id="$RandomNumber" style="display:none;background-color:$($bgColorCode);">
"@
)
        } elseif ($CanCollapse) {
            $HTML.Add(@"
<div class="section">
<div class="header">
    <a name="$($HeaderText)">$($HeaderText)</a> (<a id="show_$RandomNumber" href="#" onclick="show('$RandomNumber');" style="color: #ffffff; display:none;">Show</a>
    <a id="hide_$RandomNumber" href="#" onclick="hide('$RandomNumber');" style="color: #ffffff; ">Hide</a>)
</div>
<div class="content" id="$RandomNumber" style="background-color:$($bgColorCode);">
"@
)
        } else {
            $HTML.Add(@"
<div class="section">
<div class="header">
    <a name="$($HeaderText)">$($HeaderText)</a>
</div>
<div class="content" style="background-color:$($bgColorCode);">
"@
)
        }

        $HTML.Add((Invoke-Command -ScriptBlock $Content))
    }
    End {
        $HTML.Add('</div></div>')
        return $HTML
    }
}
Function New-HTMLHeading {
    Param (
        [validateset('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7')][string]$Heading,
        [string]$HeadingText
    )
    [System.Collections.IDictionary] $Builder = [Ordered] @{
        Tag   = $Heading
        Value = $HeadingText
    }
    $HTML = Set-Tag -HtmlObject $Builder
    return $HTML
}
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
    }
    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-HtmlTab {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 1)][ValidateNotNull()][ScriptBlock] $HtmlData = $(Throw "No curly brace?)"),
        [Parameter(Mandatory, Position = 0)][String] $TabName,
        [Parameter(Mandatory = $false, Position = 2)][String]$TabHeading

    )
    Begin {}
    Process {
        $HTML = New-GenericList -Type [string]
        $HTML.Add('<div id="' + $TabName + '" class="tabcontent">')
        if (-not [string]::IsNullOrWhiteSpace($TabHeading)) {
            $HTML.Add("<h7>$TabHeading</h7>")
        }
        $HTML.Add((Invoke-Command -ScriptBlock $HtmlData))
    }
    End {
        $HTML.Add('</div>')
        <#
        $HTML.Add(@"
                <script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
</script>
"@
        )
#>

        $HTML.Add((Get-JavaScript -FilePath "$PSScriptRoot\Resources\JS\Additional\LoadDefaultOpen.js"))
        return $HTML
    }
}
Function New-HTMLTabHeader {
    [alias('Get-HTMLTabHeader')]
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)][Array] $TabNames
    )

    $TabHeader = @()
    $TabHeader += '<ul class="tab">'
    $FirstTab = $true
    Foreach ($TabName in $TabNames) {
        if ($FirstTab) {
            $TabHeader += ' <li><a href="javascript:void(0)" class="tablinks" onclick="openTab(event, ''' + $TabName + ''')" id="defaultOpen">' + $TabName + '</a></li>'
            $FirstTab = $false
        } else {
            $TabHeader += ' <li><a href="javascript:void(0)" class="tablinks" onclick="openTab(event, ''' + $TabName + ''')">' + $TabName + '</a></li>'
        }

    }
    $TabHeader += '</ul>'
    Set-Variable -Scope global -Name TabHeaderCreated -Value $true
    Write-output $TabHeader
}
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
    )

    if ([string]::IsNullOrEmpty($FilePath)) {
        Write-Warning "Save-HTMLReport - FilePath parameter $ReportPath is empty, using Temporary"
        $FilePath = Get-FileName -Temporary -Extension 'html'
    } else {
        if (Test-Path -Path $FilePath) {
            Write-Warning 'Save-HTMLReport - Path already exists. Report will be overwritten.'
        }
    }
    Write-Verbose "Saving HTML to file $FilePath"


    $HTML | Set-Content -Path $FilePath -Force
    Write-Verbose $FilePath
    if ($ShowHTML) {
        #Start-Sleep -Seconds 1
        Invoke-Item $FilePath
    }
    Write-Output $FilePath
}
$Script:Tracking = @{
    Head = @{

    }
    Body = @{

    }
    Table = @{

    }

}
Function Set-TableRowColor {
    <#
    .SYNOPSIS
        adds a row colour field to the array of object for processing with htmltable
        .PARAMETER ArrayOfObjects
            The type of logo
        .PARAMETER Green
             Some additional pish
        .PARAMETER Yellow
             Some additional pish
        .PARAMETER Red
            use $this and an expression to measure the value
        .PARAMETER Alertnating
            a switch the will define Odd and Even Rows in the rowcolor column
#>

    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $false)]
        [Object[]]$ArrayOfObjects,
        [Parameter(Mandatory = $false)]
        [string]$Green = '$this -eq $false',
        [Parameter(Mandatory = $false)]
        [string]$Yellow = '$this -eq $false',
        [Parameter(Mandatory = $false)]
        [string]$Red = '$this -eq $false',
        [Parameter(Mandatory = $false)]
        [switch]$Alternating
    )
    if ($Alternating) {
        $ColoredArray = $ArrayOfObjects | Add-Member -MemberType ScriptProperty -Name RowColor -Value {
            if ((([array]::indexOf($ArrayOfObjects, $this)) % 2) -eq 0) {'Odd'}
            if ((([array]::indexOf($ArrayOfObjects, $this)) % 2) -eq 1) {'Even'}
        } -PassThru -Force | Select-Object *
    } else {
        $ColoredArray = $ArrayOfObjects | Add-Member -MemberType ScriptProperty -Name RowColor -Value {
            if (Invoke-Expression $Green) {'Green'}
            elseif (Invoke-Expression $Red) {'Red'}
            elseif (Invoke-Expression $Yellow) {'Yellow'}
            else {'None'}
        } -PassThru -Force | Select-Object *
    }

    return $ColoredArray
}
function Set-Tag {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $HtmlObject
    )
    $HTML = New-GenericList -Type [string]
    $HTML.Add("<$($HtmlObject.Tag)")
    foreach ($Property in $HtmlObject.Attributes.Keys) {
        $PropertyValue = $HtmlObject.Attributes[$Property]
        # skip adding properties that are empty
        if ($PropertyValue -ne '') {
            $HTML.Add("$Property = `"$PropertyValue`"")
        }
    }
    if (($null -ne $HtmlObject.Value) -and ($HtmlObject.Value -ne '')) {
        $HTML.Add(">")
        if ($HtmlObject.Value -is [System.Collections.IDictionary]) {
            [string] $NewObject = Set-Tag -HtmlObject ($HtmlObject.Value)
            $HTML.Add($NewObject)
        } else {
            $HTML.Add($HtmlObject.Value)
        }
        $HTML.Add("</$($HtmlObject.Tag)>")
    } else {
        $HTML.Add("/>")
    }
    $HTML
}


Export-ModuleMember `
    -Function @('Add-HTMLHorizontalLine','Add-Image','Get-HTMLAnchor','Get-HTMLAnchorLink','Get-HTMLBarChart','Get-HTMLBarChartObject','Get-HTMLContentDataTable','Get-HTMLContentTable','Get-HTMLContentTableAdvanced','Get-HTMLContentText','Get-HTMLLineChart','Get-HTMLLineChartObject','Get-HTMLPieChart','Get-HTMLPieChartObject','New-Html','New-HTMLCodeBlock','New-HTMLColumn','New-HTMLContent','New-HTMLHeading','New-HtmlTab','New-HTMLTabHeader','Save-HTML','Set-TableRowColor') `
    -Alias @('Get-HTMLTabHeader')