PSParseHTML.psm1
function Convert-InternalHTMLToText { [CmdletBinding()] param( [string] $Content ) $Output = [NUglify.Uglify]::HtmlToText($Content) if ($Output.HasErrors) { Write-Warning "Convert-HTMLToText -Errors: $($Output.Errors)" } $Output.Code } function ConvertFrom-HTMLTableAgilityPack { [cmdletbinding()] param( [Uri] $Url, [string] $Content, [System.Collections.IDictionary] $ReplaceContent, [System.Collections.IDictionary] $ReplaceHeaders, [switch] $ReverseTable ) Begin { } Process { if ($Content) { [HtmlAgilityPack.HtmlDocument] $HtmlDocument = [HtmlAgilityPack.HtmlDocument]::new() $HtmlDocument.LoadHtml($Content) } else { [HtmlAgilityPack.HtmlWeb] $HtmlWeb = [HtmlAgilityPack.HtmlWeb]::new() [HtmlAgilityPack.HtmlDocument] $HtmlDocument = $HtmlWeb.Load($url) } [Array] $Tables = $HtmlDocument.DocumentNode.SelectNodes("//table") [Array] $OutputTables = :table foreach ($table in $Tables) { $Rows = $table.SelectNodes('.//tr') if ($ReverseTable) { $Count = 0 [Array] $TableContent = @( $obj = [ordered] @{ } $TableContent = foreach ($Row in $Rows) { $Count++ #for ($x = 0; $x -lt $headers.count; $x++) { # if ($($headers[$x])) { # $obj["$($headers[$x])"] = $row.SelectNodes("th|td")[$x].InnerText.Trim() [string] $CellHeader = $row.SelectNodes("th").InnerText [string] $CellContent = $row.SelectNodes("td").InnerText $CellContent = $CellContent.Trim() if ($ReplaceContent) { foreach ($Key in $ReplaceContent.Keys) { $CellContent = $CellContent -replace $Key, $ReplaceContent.$Key } } if ($CellHeader) { $obj["$($CellHeader)"] = $CellContent } else { $obj["$Count"] = $CellContent } # } else { # $obj["$x"] = $row.SelectNodes("th|td")[$x].InnerText.Trim() # } #} } #[PSCustomObject] $obj $obj ) } else { $Headers = foreach ($Row in $Rows[0]) { foreach ($Cell in $row.SelectNodes("th|td")) { $CellContent = $Cell.InnerText.Trim() if ($ReplaceHeaders) { foreach ($Key in $ReplaceHeaders.Keys) { $CellContent = $CellContent -replace $Key, $ReplaceHeaders.$Key } } $CellContent } } $TableContent = foreach ($Row in $Rows | Select-Object -Skip 1) { $obj = [ordered] @{ } for ($x = 0; $x -lt $headers.count; $x++) { if ($($headers[$x])) { # $obj["$($headers[$x])"] = $row.SelectNodes("th|td")[$x].InnerText.Trim() [string] $CellContent = $row.SelectNodes("th|td")[$x].InnerText $CellContent = $CellContent.Trim() if ($ReplaceContent) { foreach ($Key in $ReplaceContent.Keys) { $CellContent = $CellContent -replace $Key, $ReplaceContent.$Key } } $obj["$($headers[$x])"] = $CellContent } else { $obj["$x"] = $row.SelectNodes("th|td")[$x].InnerText.Trim() } } [PSCustomObject] $obj } } @(, $TableContent) } $OutputTables } End { } } function ConvertFrom-HTMLTableAngle { [cmdletbinding()] param( [Uri] $Url, [string] $Content, [System.Collections.IDictionary] $ReplaceContent, [System.Collections.IDictionary] $ReplaceHeaders ) Begin { } Process { if ($Url) { $Content = (Invoke-WebRequest -Uri $Url).Content } if (-not $Content) { return } # Initialize the parser $HTMLParser = [AngleSharp.Html.Parser.HtmlParser]::new() # Load the html $ParsedDocument = $HTMLParser.ParseDocument($Content) # Get all the tables [Array] $Tables = $ParsedDocument.GetElementsByTagName('table') # For each table :table foreach ($table in $tables) { [Array] $headers = foreach ($_ in $Table.Rows[0].Cells) { $CellContent = $_.TextContent.Trim() if ($ReplaceHeaders) { foreach ($Key in $ReplaceHeaders.Keys) { $CellContent = $CellContent -replace $Key, $ReplaceHeaders.$Key } } $CellContent } # if headers have value if ($Headers.Count -ge 1) { [Array] $output = foreach ($row in $table.Rows | Select-Object -Skip 1) { $obj = [ordered]@{ } # add all the properties, one per row for ($x = 0; $x -lt $headers.count; $x++) { if ($($headers[$x])) { if ($row.Cells[$x].TextContent) { $CellContent = $row.Cells[$x].TextContent.Trim() if ($ReplaceContent) { foreach ($Key in $ReplaceContent.Keys) { $CellContent = $CellContent -replace $Key, $ReplaceContent.$Key } } $obj["$($headers[$x])"] = $CellContent } else { $obj["$($headers[$x])"] = $row.Cells[$x].TextContent } } else { $obj["$x"] = $row.Cells[$x].TextContent #.Trim() } } [PSCustomObject] $obj } # if there are any rows, output if ($output.count -ge 1) { @(, $output) } else { Write-Verbose 'ConvertFrom-HtmlTable - Table has no rows. Skipping' } } } } End { } } function Format-InternalCSS { [CmdletBinding()] param( [string] $Content ) $CssParser = [AngleSharp.Css.Parser.CssParser]::new() $ParsedDocument = $CssParser.ParseStyleSheet($Content) $StringWriter = [System.IO.StringWriter]::new() $PrettyMarkupFormatter = [AngleSharp.Css.CssStyleFormatter]::new() $ParsedDocument.ToCss($StringWriter, $PrettyMarkupFormatter) $StringWriter.ToString() } function Format-InternalHTML { [CmdletBinding()] param( [string] $Content ) $HTMLParser = [AngleSharp.Html.Parser.HtmlParser]::new() $ParsedDocument = $HTMLParser.ParseDocument($Content) $StringWriter = [System.IO.StringWriter]::new() $PrettyMarkupFormatter = [AngleSharp.Html.PrettyMarkupFormatter]::new() $ParsedDocument.ToHtml($StringWriter, $PrettyMarkupFormatter) $StringWriter.ToString() } function Format-InternalJS { [CmdletBinding()] param( [string] $Content, [int] $IndentSize = 4, [string] $IndentChar = ' ', [bool] $IndentWithTabs = $false, [bool] $PreserveNewlines = $true, [double] $MaxPreserveNewlines = 10.0, [bool] $JslintHappy = $false, [Jsbeautifier.BraceStyle] $BraceStyle = [Jsbeautifier.BraceStyle]::Collapse, [bool] $KeepArrayIndentation = $false, [bool] $KeepFunctionIndentation = $false, [bool] $EvalCode = $false, #[int] $WrapLineLength = 0, [bool] $BreakChainedMethods = $false ) $Jsbeautifier = [Jsbeautifier.Beautifier]::new() $Jsbeautifier.Opts.IndentSize = $IndentSize $Jsbeautifier.Opts.IndentChar = $IndentChar $Jsbeautifier.Opts.IndentWithTabs = $IndentWithTabs $Jsbeautifier.Opts.PreserveNewlines = $PreserveNewlines $Jsbeautifier.Opts.MaxPreserveNewlines = $MaxPreserveNewlines $Jsbeautifier.Opts.JslintHappy = $JslintHappy $Jsbeautifier.Opts.BraceStyle = $BraceStyle $Jsbeautifier.Opts.KeepArrayIndentation = $KeepArrayIndentation $Jsbeautifier.Opts.KeepFunctionIndentation = $KeepFunctionIndentation $Jsbeautifier.Opts.EvalCode = $EvalCode #$Jsbeautifier.Opts.WrapLineLength = $WrapLineLength $Jsbeautifier.Opts.BreakChainedMethods = $BreakChainedMethods #$Jsbeautifier.Flags <# public BeautifierFlags(string mode) { PreviousMode = "BLOCK"; Mode = mode; VarLine = false; VarLineTainted = false; VarLineReindented = false; InHtmlComment = false; IfLine = false; ChainExtraIndentation = 0; InCase = false; InCaseStatement = false; CaseBody = false; IndentationLevel = 0; TernaryDepth = 0; } #> $FormattedJS = $Jsbeautifier.Beautify($Content) $FormattedJS } function Optimize-InternalCSS { [CmdletBinding()] param( [string] $Content ) $CSSParser = [AngleSharp.Css.Parser.CssParser]::new() $ParsedDocument = $CSSParser.ParseStyleSheet($Content) $StringWriter = [System.IO.StringWriter]::new() $PrettyMarkupFormatter = [AngleSharp.Css.MinifyStyleFormatter]::new() $ParsedDocument.ToCss($StringWriter, $PrettyMarkupFormatter) $StringWriter.ToString() } function Optimize-InternalUglifyCSS { [CmdletBinding()] param( [string] $Content ) [NUglify.Uglify]::Css($Content).Code } function Optimize-InternalUglifyHTML { [CmdletBinding()] param( [string] $Content ) $Settings = [NUglify.Html.HtmlSettings]::new() $Settings.RemoveOptionalTags = $false [NUglify.Uglify]::Html($Content, $Settings).Code } function Optimize-InternalUglifyJS { [CmdletBinding()] param( [string] $Content ) [NUglify.Uglify]::Js($Content).Code } function Convert-HTMLToText { [CmdletBinding()] param( [string] $File, [string] $OutputFile, [string] $Content ) # Load from file or text if ($File) { if (Test-Path -LiteralPath $File) { $Content = [IO.File]::ReadAllText($File) } else { Write-Warning "Convert-HTMLToText - File doesn't exists" return } } elseif ($Content) { } else { Write-Warning 'Convert-HTMLToText - No choice file or Content. Termninated.' return } $Output = Convert-InternalHTMLToText -Content $Content # Output to file or to text if ($OutputFile) { [IO.File]::WriteAllText($OutputFile, $Output) } else { $Output } } function ConvertFrom-HTMLAttributes { [alias('ConvertFrom-HTMLTag', 'ConvertFrom-HTMLClass')] [cmdletbinding()] param ( [Parameter(Mandatory = $true)][Array] $Content, [string] $Tag, [string] $Class, [string] $Id, [string] $Name, [switch] $ReturnObject ) Begin { # Initialize the parser $HTMLParser = [AngleSharp.Html.Parser.HtmlParser]::new() } Process { # Load the html $ParsedDocument = $HTMLParser.ParseDocument($content) # Get all the tables if ($Tag) { [Array] $OutputContent = $ParsedDocument.GetElementsByTagName($Tag) } elseif ($Class) { [Array] $OutputContent = $ParsedDocument.GetElementsByClassName($Class) } elseif ($Id) { [Array] $OutputContent = $ParsedDocument.GetElementById($Id) } elseif ($Name) { [Array] $OutputContent = $ParsedDocument.GetElementsByName($Name) } if ($OutputContent) { if ($ReturnObject) { $OutputContent } else { $OutputContent.TextContent } } } End { } } Function ConvertFrom-HtmlTable { [cmdletbinding()] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Content')][string]$Content, [alias('Uri')][Parameter(Mandatory = $true, ParameterSetName = 'Uri')][Uri] $Url, [System.Collections.IDictionary] $ReplaceContent, [System.Collections.IDictionary] $ReplaceHeaders, [ValidateSet('AngleSharp', 'AgilityPack')] $Engine, [switch] $ReverseTable ) Begin { # This fixes an issue https://github.com/PowerShell/PowerShell/issues/11287 for ConvertTo-HTML $HeadersReplacement = [ordered] @{ '\*' = ''; } if (-not $ReplaceHeaders) { $ReplaceHeaders = [ordered] @{ } } foreach ($Key in $HeadersReplacement.Keys) { $ReplaceHeaders["$Key"] = $HeadersReplacement.$Key } } Process { if ($Engine -eq 'AngleSharp' -and -not $ReverseTable) { ConvertFrom-HTMLTableAngle -Url $Url -Content $Content -ReplaceHeaders $ReplaceHeaders -ReplaceContent $ReplaceContent } else { ConvertFrom-HTMLTableAgilityPack -Url $url -Content $Content -ReplaceHeaders $ReplaceHeaders -ReplaceContent $ReplaceContent -ReverseTable:$ReverseTable } } End { } } function Format-CSS { [CmdletBinding()] param( [string] $File, [string] $OutputFile, [string] $Content ) # Load from file or text if ($File) { if (Test-Path -LiteralPath $File) { $Content = [IO.File]::ReadAllText($File) } else { Write-Warning "Format-CSS - File doesn't exists" return } } elseif ($Content) { } else { Write-Warning 'Format-CSS - No choice file or Content. Termninated.' return } $Output = Format-InternalCSS -Content $Content # Output to file or to text if ($OutputFile) { [IO.File]::WriteAllText($OutputFile, $Output) } else { $Output } } function Format-HTML { [CmdletBinding()] param( [string] $File, [string] $OutputFile, [string] $Content ) # Load from file or text if ($File) { if (Test-Path -LiteralPath $File) { $Content = [IO.File]::ReadAllText($File) } else { Write-Warning "Format-HTML - File doesn't exists" return } } elseif ($Content) { } else { Write-Warning 'Format-HTML - No choice file or Content. Termninated.' return } # Do the magic $Output = Format-InternalHTML -Content $Content # Output to file or to text if ($OutputFile) { [IO.File]::WriteAllText($OutputFile, $Output) } else { $Output } } function Format-JavaScript { [alias('Format-JS')] [CmdletBinding()] param( [string] $File, [string] $OutputFile, [alias('FileContent')][string] $Content ) # Load from file or text if ($File) { if (Test-Path -LiteralPath $File) { $Content = [IO.File]::ReadAllText($File) } else { Write-Warning "Format-JavaScript - File doesn't exists" return } } elseif ($Content) { } else { Write-Warning 'Format-JavaScript - No choice file or Content. Termninated.' return } # For now don't want to give this as an option [int] $IndentSize = 4 [string] $IndentChar = ' ' [bool] $IndentWithTabs = $false [bool] $PreserveNewlines = $true [double] $MaxPreserveNewlines = 10.0 [bool] $JslintHappy = $false [Jsbeautifier.BraceStyle] $BraceStyle = [Jsbeautifier.BraceStyle]::Collapse [bool] $KeepArrayIndentation = $false [bool] $KeepFunctionIndentation = $false [bool] $EvalCode = $false #[int] $WrapLineLength = 0 [bool] $BreakChainedMethods = $false # do the magic $SplatJS = @{ IndentSize = $IndentSize IndentChar = $IndentChar IndentWithTabs = $IndentWithTabs PreserveNewlines = $PreserveNewlines MaxPreserveNewlines = $MaxPreserveNewlines JslintHappy = $JslintHappy BraceStyle = $BraceStyle KeepArrayIndentation = $KeepArrayIndentation KeepFunctionIndentation = $KeepFunctionIndentation EvalCode = $EvalCode #WrapLineLength = $WrapLineLength BreakChainedMethods = $BreakChainedMethods } $Output = Format-InternalJS -Content $Content @SplatJS # Output to file or to text if ($OutputFile) { [IO.File]::WriteAllText($OutputFile, $Output) } else { $Output } } function Optimize-CSS { [CmdletBinding()] param( [string] $File, [string] $OutputFile, [string] $Content ) # Load from file or text if ($File) { if (Test-Path -LiteralPath $File) { $Content = [IO.File]::ReadAllText($File) } else { Write-Warning "Optimize-CSS - File doesn't exists" return } } elseif ($Content) { } else { Write-Warning 'Optimize-CSS - No choice file or Content. Termninated.' return } # Do magic #if ($Engine -eq 'AngleSharp') { $Output = Optimize-InternalCSS -Content $Content #} else { # $Output = Optimize-InternalYahoo -Content $Content # } # Output to file or to text if ($OutputFile) { [IO.File]::WriteAllText($OutputFile, $Output) } else { $Output } } function Optimize-HTML { [CmdletBinding()] param( [string] $File, [string] $OutputFile, [string] $Content ) # Load from file or text if ($File) { if (Test-Path -LiteralPath $File) { $Content = [IO.File]::ReadAllText($File) } else { Write-Warning "Optimize-HTML - File doesn't exists" return } } elseif ($Content) { } else { Write-Warning 'Optimize-HTML - No choice file or Content. Termninated.' return } # for now don't want to give this as option [bool] $ShouldKeepAttributeQuotes = $true [bool] $ShouldKeepComments = $true [bool] $ShouldKeepEmptyAttributes = $true [bool] $ShouldKeepImpliedEndTag = $true [bool] $ShouldKeepStandardElements = $true # Do magic #$Output = Optimize-InternalHTML -Content $Content -ShouldKeepAttributeQuotes $ShouldKeepAttributeQuotes -ShouldKeepComments $ShouldKeepComments -ShouldKeepEmptyAttributes $ShouldKeepEmptyAttributes -ShouldKeepImpliedEndTag $ShouldKeepImpliedEndTag -ShouldKeepStandardElements $ShouldKeepStandardElements $Output = Optimize-InternalUglifyHTML -Content $Content # Output to file or to text if ($OutputFile) { [IO.File]::WriteAllText($OutputFile, $Output) } else { $Output } } function Optimize-JavaScript { [CmdletBinding()] param( [string] $File, [string] $OutputFile, [string] $Content ) # Load from file or text if ($File) { if (Test-Path -LiteralPath $File) { $Content = [IO.File]::ReadAllText($File) } else { Write-Warning "Optimize-JavaScript - File doesn't exists" return } } elseif ($Content) { } else { Write-Warning 'Optimize-JavaScript - No choice file or Content. Termninated.' return } #$Output = Optimize-InternalYahoo -Content $Content $Output = Optimize-InternalUglifyJS -Content $Content # Output to file or to text if ($OutputFile) { [IO.File]::WriteAllText($OutputFile, $Output) } else { $Output } } if ($PSEdition -eq 'Core') { Add-Type -Path $PSScriptRoot\Lib\Standard\AngleSharp.Css.dll Add-Type -Path $PSScriptRoot\Lib\Standard\AngleSharp.dll Add-Type -Path $PSScriptRoot\Lib\Standard\HtmlAgilityPack.dll Add-Type -Path $PSScriptRoot\Lib\Standard\Jsbeautifier.dll Add-Type -Path $PSScriptRoot\Lib\Standard\NUglify.dll Add-Type -Path $PSScriptRoot\Lib\Standard\System.Text.Encoding.CodePages.dll } else { Add-Type -Path $PSScriptRoot\Lib\Standard\AngleSharp.Css.dll Add-Type -Path $PSScriptRoot\Lib\Standard\AngleSharp.dll Add-Type -Path $PSScriptRoot\Lib\Standard\HtmlAgilityPack.dll Add-Type -Path $PSScriptRoot\Lib\Standard\Jsbeautifier.dll Add-Type -Path $PSScriptRoot\Lib\Standard\NUglify.dll Add-Type -Path $PSScriptRoot\Lib\Standard\System.Text.Encoding.CodePages.dll } Export-ModuleMember -Function @('ConvertFrom-HTMLAttributes', 'ConvertFrom-HtmlTable', 'Convert-HTMLToText', 'Format-CSS', 'Format-HTML', 'Format-JavaScript', 'Optimize-CSS', 'Optimize-HTML', 'Optimize-JavaScript') -Alias @('ConvertFrom-HTMLClass', 'ConvertFrom-HTMLTag', 'Format-JS') # SIG # Begin signature block # MIIgQAYJKoZIhvcNAQcCoIIgMTCCIC0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUu5Ns4PYr0Q6fyDEGbIN79kPl # wP2gghtvMIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0B # AQUFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg # Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg # +XESpa7cJpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lT # XDGEKvYPmDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5 # a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g # 0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1 # roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf # GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G # A1UdDgQWBBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLL # gjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3 # cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmr # EthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+ # fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5Q # Z7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu # 838fYxAe+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw # 8jCCBTAwggQYoAMCAQICEAQJGBtf1btmdVNDtW+VUAgwDQYJKoZIhvcNAQELBQAw # ZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBS # b290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcjELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg # U2lnbmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPjTsxx/ # DhGvZ3cH0wsxSRnP0PtFmbE620T1f+Wondsy13Hqdp0FLreP+pJDwKX5idQ3Gde2 # qvCchqXYJawOeSg6funRZ9PG+yknx9N7I5TkkSOWkHeC+aGEI2YSVDNQdLEoJrsk # acLCUvIUZ4qJRdQtoaPpiCwgla4cSocI3wz14k1gGL6qxLKucDFmM3E+rHCiq85/ # 6XzLkqHlOzEcz+ryCuRXu0q16XTmK/5sy350OTYNkO/ktU6kqepqCquE86xnTrXE # 94zRICUj6whkPlKWwfIPEvTFjg/BougsUfdzvL2FsWKDc0GCB+Q4i2pzINAPZHM8 # np+mM6n9Gd8lk9ECAwEAAaOCAc0wggHJMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD # VR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHkGCCsGAQUFBwEBBG0w # azAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUF # BzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk # SURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3Js # ME8GA1UdIARIMEYwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczov # L3d3dy5kaWdpY2VydC5jb20vQ1BTMAoGCGCGSAGG/WwDMB0GA1UdDgQWBBRaxLl7 # KgqjpepxA8Bg+S32ZXUOWDAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823I # DzANBgkqhkiG9w0BAQsFAAOCAQEAPuwNWiSz8yLRFcgsfCUpdqgdXRwtOhrE7zBh # 134LYP3DPQ/Er4v97yrfIFU3sOH20ZJ1D1G0bqWOWuJeJIFOEKTuP3GOYw4TS63X # X0R58zYUBor3nEZOXP+QsRsHDpEV+7qvtVHCjSSuJMbHJyqhKSgaOnEoAjwukaPA # JRHinBRHoXpoaK+bp1wgXNlxsQyPu6j4xRJon89Ay0BEpRPw5mQMJQhCMrI2iiQC # /i9yfhzXSUWW6Fkd6fp0ZGuy62ZD2rOwjNXpDd32ASDOmTFjPQgaGLOBm0/GkxAG # /AeB+ova+YJJ92JuoVP6EpQYhS6SkepobEQysmah5xikmmRR7zCCBT0wggQloAMC # AQICEATV3B9I6snYUgC6zZqbKqcwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2ln # bmluZyBDQTAeFw0yMDA2MjYwMDAwMDBaFw0yMzA3MDcxMjAwMDBaMHoxCzAJBgNV # BAYTAlBMMRIwEAYDVQQIDAnFmmzEhXNraWUxETAPBgNVBAcTCEthdG93aWNlMSEw # HwYDVQQKDBhQcnplbXlzxYJhdyBLxYJ5cyBFVk9URUMxITAfBgNVBAMMGFByemVt # eXPFgmF3IEvFgnlzIEVWT1RFQzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC # ggEBAL+ygd4sga4ZC1G2xXvasYSijwWKgwapZ69wLaWaZZIlY6YvXTGQnIUnk+Tg # 7EoT7mQiMSaeSPOrn/Im6N74tkvRfQJXxY1cnt3U8//U5grhh/CULdd6M3/Z4h3n # MCq7LQ1YVaa4MYub9F8WOdXO84DANoNVG/t7YotL4vzqZil3S9pHjaidp3kOXGJc # vxrCPAkRFBKvUmYo23QPFa0Rd0qA3bFhn97WWczup1p90y2CkOf28OVOOObv1fNE # EqMpLMx0Yr04/h+LPAAYn6K4YtIu+m3gOhGuNc3B+MybgKePAeFIY4EQzbqvCMy1 # iuHZb6q6ggRyqrJ6xegZga7/gV0CAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrE # uXsqCqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBQYsTUn6BxQICZOCZA0CxS0TZSU # ZjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAw # bjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1j # cy1nMS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtY3MtZzEuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYB # BQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGE # BggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0 # LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQC # MAAwDQYJKoZIhvcNAQELBQADggEBAJq9bM+JbCwEYuMBtXoNAfH1SRaMLXnLe0py # VK6el0Z1BtPxiNcF4iyHqMNVD4iOrgzLEVzx1Bf/sYycPEnyG8Gr2tnl7u1KGSjY # enX4LIXCZqNEDQCeTyMstNv931421ERByDa0wrz1Wz5lepMeCqXeyiawqOxA9fB/ # 106liR12vL2tzGC62yXrV6WhD6W+s5PpfEY/chuIwVUYXp1AVFI9wi2lg0gaTgP/ # rMfP1wfVvaKWH2Bm/tU5mwpIVIO0wd4A+qOhEia3vn3J2Zz1QDxEprLcLE9e3Gmd # G5+8xEypTR23NavhJvZMgY2kEXBEKEEDaXs0LoPbn6hMcepR2A4wggZqMIIFUqAD # AgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTAeFw0xNDEw # MjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVTMREwDwYDVQQK # EwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1wIFJlc3BvbmRl # cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8s+CCNeDg9sYq # 5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7dy4XpX6X51Id0 # iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGfrvP9Enh1DqZb # FP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5uHzu5uc0LzF3 # gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYuRhDIjegEYNu8 # c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJCkawCwO+k8IkR # j3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG # A1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgB # hv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v # Q1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAA # dABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQA # dQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQA # aQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIA # ZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcA # aABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQA # IABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4A # IABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSME # GDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpNJLZJMp1KKnka # g0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0dHA6Ly9jcmw0 # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMHcGCCsGAQUF # BwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEG # CCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRB # c3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+GzNNsiaBXJuG # ziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94GAYw3+puxnSR+ # /iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFBpr1i2fAnPTgd # KG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dxnSHdFMoVXZJB # 2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bIo4sKHOWV2q7E # LlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQKOhvjjz3Kr2qN # e9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJKoZIhvcNAQEF # BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE # CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ # RCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFowYjELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMIIB # IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKVpYBzQHDSnlZU # XKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqvy15r7a2wcTHr # zzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/2DDD/6b3+6LN # b3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE7h6z8rwMK5nQ # xl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKvmPv2zkBdXPao # 8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIUYJX9BwSiCQID # AQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIGCCsGAQUFBwMB # BggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCDCCAdIGA1Ud # IASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6 # Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggr # BgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAA # QwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAA # YQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUA # cgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4A # ZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAA # bABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAA # aQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIA # ZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB/wQIMAYBAf8C # AQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaG # NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e36K+Vw0rZwLN # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA # A4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90OPKyXGGinJXD # UOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann4+erYs37iy2Q # wsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qqq8+cdkvtX8JL # FuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD7eeSDY2xaYxP # +1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38KiGtSTGDR5V3 # cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcCAQEwgYYwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2ln # bmluZyBDQQIQBNXcH0jqydhSALrNmpsqpzAJBgUrDgMCGgUAoHgwGAYKKwYBBAGC # NwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQU5LN6bTok # h5emx2fBrbu2i3QOrqMwDQYJKoZIhvcNAQEBBQAEggEAQqXvmRRcAcpKxuc6GCk/ # 56ZLFZ+3P5mfv68EpTtSGBNB48V6lbo/Rc8HP3pRl7o6tZuW+0KZsQ4TsTSWySNV # JhHYVn2m21VhYo8Aqy5xyYk2hWc1AdClS6a8tlo+qKCBWBu17I5WcwRmOUznSacr # dP9uODVIr9i+hiI4KRRMdcngcgrbFRG+mn1KA+600yKml9Rvka1fGoReU5M6ZcJ5 # QllCAFQwVpJEip8YrMwW0Guhpo8t1QSglC47p3EImeMWXDLgwGxS4VqQ2ZujhK40 # P6LzXtwDDjIT8Epc1zikz1ovwENCAKhSPYOYBFU/l3fcmWQjyLI8vNgMhbjmPT8J # 9KGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr # 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc # BgkqhkiG9w0BCQUxDxcNMjAwODIxMTk0ODU1WjAjBgkqhkiG9w0BCQQxFgQUVnsh # DIvmVdUhLKHHF6/8yFypzQswDQYJKoZIhvcNAQEBBQAEggEAdNuVf8puheUYeSAg # 38+xvDeowyDjA4M+rXWX0u7rTguj8Q6Daz820JCPADRBEEvJuceTNHKz1S6V8rDc # PXIr4Ad5WWvaDm9M9kAfLVbsqPDPxA3Xi9qCF+Y3B8NQ53kH48q6Q+SdK1kBP44T # mzrLYWY08bmAcI7xE/jkR8T2alK3rfIgwPaJJX52R+jQ3RloHQWvqpYiY8vFTZtl # k/Wogu/WwQaQT6NtziDXyXOkRt+3b5u8paiGFPEqxLNuX3hfwDdsT4PzinuQYAnB # +JPAF/XBT59PxWEYSXGvB/3KQiC3yEjXW1PfWx6xUriKakrwyIaozlyYtAb6JLqb # 23BU6Q== # SIG # End signature block |