Public/Add-HTMLTableColor.ps1

function Add-HTMLTableColor {
    <#
    .SYNOPSIS
    Colorize cells or rows in an HTML table, or add other inline CSS.
 
    .DESCRIPTION
    Colorize cells or rows in an HTML table, or add other inline CSS.
 
    .PARAMETER HTML
    HTML string to analyze.
 
    .PARAMETER Column
    If specified, the column you want to analyze. This is case sensitive.
 
    .PARAMETER SecondColumn
    If specified, the column you want to use as a reference column to analyze against Column. This is case sensitive.
 
    .PARAMETER Argument
    If Column is specified, argument is used as a comparison value for Column.
 
    .PARAMETER ScriptBlock
    If Column is specified, used to evaluate whether to add the CSS or not. If the ScriptBlock returns $true, the specified CSSAtribute and CSSAtributeValue will be added.
 
    $args[0] is the existing cell value
    $args[1] is the argument parameter
 
    Examples:
        {[string]$args[0] -eq [string]$args[1]} # specified Column value equals Argument. This is the default.
        {[double]$args[0] -gt [double]$args[1]} # specified Column value is greater than Argument.
 
    .PARAMETER CSSAttribute
    If Column is specified, the attribute to change if the ScriptBlock returns true. Default: style
 
    .PARAMETER CSSAttributeValue
    If Column is specified, the attribute value to change if the ScriptBlock returns true.
 
    Example: "background-color:#FFCC99;"
 
    .PARAMETER HighlightRow
    If specified, and Column is specified, and the ScriptsBlock returns true, CSSAttribute and CSSAttributeValue is applied to the entire row, not just the cell.
 
    .PARAMETER HighlightReferenceCell
    If specified, and Column and SecondColumn are specified, and the ScriptsBlock returns true, CSSAttribute and CSSAttributeValue is applied to both Column and SecondColumn cells.
 
    .PARAMETER ApplyFormat
    If specified, format the cell data post ScriptBlock evaluation. This should be added to the last evaluation as it will change the data and possibly make further evaluations fail.
 
    .PARAMETER StringFormat
    If specified, uses the -f parameter to format the cell data. For example, "{0:n2)" will format data as 2 decimal digits, 89.2349862972638476 will become 89.23.
 
    .PARAMETER CommandFormat
    If specified, passes a locally defined command to be used to format the cell data.
 
    Examples:
        ${function:Format-Bytes}
 
        Where Format-Bytes is defined as:
 
        function Format-Bytes ($Bytes) {
            # Do Stuff
            return $results
        }
 
    .EXAMPLE
    Sample scripts can be found in the "Examples" folder off of the module's root path
 
    .NOTES
    Author: brandon said
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [String]$HTML,

        [Parameter(Mandatory = $false)]
        [String]$Column = "Name",

        [Parameter(Mandatory = $false)]
        [String]$SecondColumn = "",

        [Parameter(Mandatory = $false)]
        $Argument = 0,

        [Parameter(Mandatory = $false)]
        [ScriptBlock]$ScriptBlock = {[string]$args[0] -eq [string]$args[1]},

        [Parameter(Mandatory = $false)]
        [Alias('Attr')]
        [String]$CSSAttribute = "style",

        [Parameter(Mandatory = $false)]
        [Alias('AttrValue')]
        [String]$CSSAttributeValue,

        [Parameter(Mandatory = $false)]
        [Alias('WholeRow')]
        [Switch]$HighlightRow = $false,

        [Parameter(Mandatory = $false)]
        [Switch]$HighlightReferenceCell = $false,

        [Parameter(Mandatory = $false)]
        [Switch]$ApplyFormat = $false,

        [Parameter(Mandatory = $false)]
        [String]$StringFormat = "",

        [Parameter(Mandatory = $false)]
        [ScriptBlock]$CommandFormat = {}
    )

    begin {
        #Requires -Version 2.0
        Add-Type -AssemblyName System.Xml.Linq | Out-Null
    }

    process {
        # Convert our data to x(ht)ml
        if ($HTML.StartsWith("<table>`r`n") -and $HTML.EndsWith("`r`n</table>")) {
            $Xml = [System.Xml.Linq.XDocument]::Parse($HTML)
        } else {
            $NestedTable = $true
            $HTML = "<table>$HTML</table>"
            $Xml = [System.Xml.Linq.XDocument]::Parse($HTML)
        }

        # Get Column index. Try th with no namespace first, then default namespace provided by ConvertTo-Html.
        try {
            $ColumnIndex = (($Xml.Descendants("th") | Where-Object { $_.Value -eq $Column }).NodesBeforeSelf() | Measure-Object).Count
        }
        catch {
            try {
                $ColumnIndex = (($Xml.Descendants("{http://www.w3.org/1999/xhtml}th") | Where-Object { $_.Value -eq $Column }).NodesBeforeSelf() | Measure-Object).Count
            }
            catch {
                throw "Error: Invalid Column Name ($Column)."
            }
        }
        # Get SecondColumn index. Try th with no namespace first, then default namespace provided by ConvertTo-Html.
        if ($SecondColumn -ne "") {
            try {
                $SecondColumnIndex = (($Xml.Descendants("th") | Where-Object { $_.Value -eq $SecondColumn }).NodesBeforeSelf() | Measure-Object).Count
            }
            catch {
                try {
                    $SecondColumnIndex = (($Xml.Descendants("{http://www.w3.org/1999/xhtml}th") | Where-Object { $_.Value -eq $SecondColumn }).NodesBeforeSelf() | Measure-Object).Count
                }
                catch {
                    throw "Error: Invalid Column Name ($SecondColumn)."
                }
            }
        }
        # If we found the specified column index and no second column exists
        if (($ColumnIndex -as [double] -ge 0) -and ($SecondColumnIndex -as [double] -eq 0)) {
            # Take action on td descendants matching that index
            switch ($Xml.Descendants("td") | Where-Object {($_.NodesBeforeSelf() | Measure-Object).Count -eq $ColumnIndex}) {
                # Run the script block. If it is true, set attributes
                {Invoke-Command $ScriptBlock -ArgumentList @($_.Value, $Argument)} {
                    # Mark the whole row or just the column
                    if ($HighlightRow)  {
                        $_.Parent.SetAttributeValue($CSSAttribute, $CSSAttributeValue)
                    } else {
                        $_.SetAttributeValue($CSSAttribute, $CSSAttributeValue)
                        if ($ApplyFormat) {
                            if ($StringFormat -ne "") {
                                $_.Value = $StringFormat -f $([double]$_.Value)
                            } elseif ($CommandFormat -ne "") {
                                $_.Value = Invoke-Command -ScriptBlock $CommandFormat -ArgumentList $_.Value
                            }
                        }
                    }
                }
                default {
                    if ($ApplyFormat) {
                        if (-not ($StringFormat -ne "" -and $CommandFormat.Ast.Extent.Text -ne '{}')) {
                            if ($StringFormat -ne "") {
                                $_.Value = $StringFormat -f $([double]$_.Value)
                            } elseif ($CommandFormat -ne "") {
                                $_.Value = Invoke-Command -ScriptBlock $CommandFormat -ArgumentList $_.Value
                            }
                        } else {
                            throw "Error: Multiple format methods specified on Column ($Column)."
                        }
                    }
                }
            }
        # If we found both column and second column indexes
        } else {
            # Iterate each table row
            foreach ($XmlTr in $($Xml.Descendants("tr"))) {
                # Take action on td descendants matching column index
                switch ($XmlTr.Descendants("td") | Where-Object {($_.NodesBeforeSelf() | Measure-Object).Count -eq $ColumnIndex}) {
                    # Run the script block. If it is true, set attributes
                    {$(Invoke-Command $ScriptBlock -ArgumentList @(@($($XmlTr.Descendants("td")))[$ColumnIndex].Value, @($($XmlTr.Descendants("td")))[$SecondColumnIndex].Value))} {
                        # Mark the whole row or just the column
                        if ($HighlightRow)  {
                            $_.Parent.SetAttributeValue($CSSAttribute, $CSSAttributeValue)
                            # Remove attribute if previously set by another condition
                            $_.SetAttributeValue($CSSAttribute, $null)
                        } else {
                            $_.SetAttributeValue($CSSAttribute, $CSSAttributeValue)
                            if ($HighlightReferenceCell) {
                                @($($XmlTr.Descendants("td")))[$SecondColumnIndex].SetAttributeValue($CSSAttribute, $CSSAttributeValue)
                            }
                        }
                    }
                }
            }
        }
        # Return the XML
        $XmlString = $Xml.Document.ToString()
        if ($NestedTable -and $XmlString.StartsWith("<table>`r`n") -and $XmlString.EndsWith("`r`n</table>")) {
            $XmlString.Substring(11, $XmlString.Length - 21)
        } else {
            $XmlString
        }
    }
}