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-InternalFormatWithUglify {
    [CmdletBinding()]
    param(
        [string] $Content,
        [string] $Indent = ' ',
        [NUglify.BlockStart] $BlockStartLine = [NUglify.BlockStart]::SameLine,
        [switch] $RemoveOptionalTags,
        [switch] $OutputTextNodesOnNewLine,
        [switch] $RemoveEmptyAttributes,
        [switch] $AlphabeticallyOrderAttributes,
        [switch] $RemoveEmptyBlocks,
        [switch] $RemoveComments,
        [switch] $IsFragment
    )
    $Settings = [NUglify.Html.HtmlSettings]::new()
    # HTML Settings
    if ($IsFragment) {
        $Settings.IsFragmentOnly = $true
    }
    # Keep first comment
    # $Pattern = "<!-- saved from url=\(0014\)about:internet -->"
    # $MOTW = [System.Text.RegularExpressions.Regex]::new($Pattern) #, [System.Text.RegularExpressions.RegexOptions]::MultiLine)
    # $Settings.KeepCommentsRegex.Add($MOTW)

    $Settings.RemoveOptionalTags = $RemoveOptionalTags.IsPresent
    $Settings.PrettyPrint = $true
    $Settings.Indent = $Indent
    $Settings.OutputTextNodesOnNewLine = $OutputTextNodesOnNewLine.IsPresent # option to not indent textnodes when theyre the only child
    $Settings.RemoveEmptyAttributes = $RemoveEmptyAttributes.IsPresent
    $Settings.AlphabeticallyOrderAttributes = $AlphabeticallyOrderAttributes.IsPresent
    $Settings.RemoveComments = $RemoveComments
    $Settings.RemoveQuotedAttributes = $false
    #$Settings.LineTerminator = [System.Environment]::NewLine
    # JS Settings
    $Settings.JsSettings.MinifyCode = $true
    $Settings.JsSettings.OutputMode = [NUglify.OutputMode]::MultipleLines
    $Settings.JsSettings.Indent = $Indent
    $Settings.JsSettings.BlocksStartOnSameLine = $BlockStartLine
    $Settings.JsSettings.PreserveFunctionNames = $true
    $Settings.JsSettings.LocalRenaming = [NUglify.JavaScript.LocalRenaming]::KeepAll
    #$Settings.JsSettings.EvalTreatment = [NUglify.JavaScript.EvalTreatment]::Ignore
    #$Settings.JsSettings.Format = [NUglify.JavaScript.JavaScriptFormat]::Normal
    $Settings.JsSettings.NoAutoRenameList = $true
    $Settings.JsSettings.PreserveFunctionNames = $true
    #$Settings.JsSettings.CollapseToLiteral = $true
    #$Settings.JsSettings.ConstStatementsMozilla = $true
    #$Settings.JsSettings.LineBreakThreshold = 50
    $Settings.JsSettings.ReorderScopeDeclarations = $false
    #$Settings.JsSettings.RenamePairs = $false
    #$Settings.JsSettings.QuoteObjectLiteralProperties = $true
    $Settings.JsSettings.TermSemicolons = $true
    #$Settings.JsSettings.Format = [NUglify.JavaScript.JavaScriptFormat]::Normal
    $Settings.JsSettings.RemoveUnneededCode = $false;
    $Settings.JsSettings.RemoveFunctionExpressionNames = $false;
    # $Settings.NoAutoRenameCollection # ReadOnly
    #$Settings.JsSettings.LineTerminator = "`r`n"
    # CSS Settings
    $Settings.CssSettings.OutputMode = [NUglify.OutputMode]::MultipleLines
    $Settings.CssSettings.Indent = $Indent
    $Settings.CssSettings.BlocksStartOnSameLine = $BlockStartLine
    $Settings.CssSettings.RemoveEmptyBlocks = $RemoveEmptyBlocks
    $Settings.CssSettings.DecodeEscapes = $false
    #$Settings.CssSettings.LineTerminator = "`r`n"
    [NUglify.Uglify]::Html($Content, $Settings).Code
}

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 Format-InternalUglifyJS {
    [CmdletBinding()]
    param(
        [string] $Content
    )
    $Settings = [NUglify.JavaScript.CodeSettings]::new()
    $Settings.MinifyCode = $true
    $Settings.OutputMode = [NUglify.OutputMode]::MultipleLines
    $Settings.Indent = $Indent
    $Settings.LocalRenaming = [NUglify.JavaScript.LocalRenaming]::KeepAll
    #$Settings.EvalTreatment = [NUglify.JavaScript.EvalTreatment]::Ignore
    #$Settings.Format = [NUglify.JavaScript.JavaScriptFormat]::Normal
    $Settings.Indent = ' '
    $Settings.NoAutoRenameList = $true
    $Settings.PreserveFunctionNames = $true
    # $Settings.NoAutoRenameCollection # ReadOnly
    [NUglify.Uglify]::Js($Content, $Code).Code
}
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
    )
    $Settings = [NUglify.Css.CssSettings]::new()
    $Settings.DecodeEscapes = $false
    [NUglify.Uglify]::Css($Content, $Settings).Code
}
function Optimize-InternalUglifyHTML {
    [CmdletBinding()]
    param(
        [string] $Content,
        [switch] $CSSDecodeEscapes
    )

    $Settings = [NUglify.Html.HtmlSettings]::new()
    $Settings.RemoveOptionalTags = $false
    $Settings.CssSettings.DecodeEscapes = $CSSDecodeEscapes.IsPresent
    # Keep first comment
    #$Pattern = "<!-- saved from url=\(0014\)about:internet -->"
    #$Pattern = "^\ssaved\sfrom\url="
    #$MOTW = [System.Text.RegularExpressions.Regex]::new($Pattern, [System.Text.RegularExpressions.RegexOptions]::MultiLine)
    #$Settings.KeepCommentsRegex.Add($MOTW)

    if ($Content -like "*<!-- saved from url=(0014)about:internet -->*") {
        $MOTW = "<!-- saved from url=(0014)about:internet -->"
    } else {
        $MOTW = ''
    }
    $Settings.RemoveComments = $true
    $Output = [NUglify.Uglify]::Html($Content, $Settings).Code
    if ($MOTW) {
        $MOTW + [System.Environment]::NewLine + $Output
    } else {
        $Output
    }
}

<# $Settings
AttributesCaseSensitive : False
CollapseWhitespaces : True
RemoveComments : True
RemoveOptionalTags : False
RemoveInvalidClosingTags : True
RemoveEmptyAttributes : True
RemoveQuotedAttributes : True
DecodeEntityCharacters : True
AttributeQuoteChar :
RemoveScriptStyleTypeAttribute : True
ShortBooleanAttribute : True
IsFragmentOnly : False
MinifyJs : True
JsSettings : NUglify.JavaScript.CodeSettings
MinifyCss : True
MinifyCssAttributes : True
CssSettings : NUglify.Css.CssSettings
PrettyPrint : False
RemoveJavaScript : False
InlineTagsPreservingSpacesAround : {[a, True], [abbr, True], [acronym, True], [b, True]...}
KeepOneSpaceWhenCollapsing : False
TagsWithNonCollapsableWhitespaces : {[pre, True], [textarea, True]}
KeepCommentsRegex : {^!, ^/?ko(?:[\s\-]|$)}
KeepTags : {}
RemoveAttributes : {}
AlphabeticallyOrderAttributes : False
#>


<# $Settings.JsSettings
 
HasRenamePairs : False
RenamePairs :
NoAutoRenameCollection : {$super}
NoAutoRenameList : $super
KnownGlobalCollection : {}
KnownGlobalNamesList :
DebugLookupCollection : {}
DebugLookupList :
AlwaysEscapeNonAscii : False
AmdSupport : False
CollapseToLiteral : True
ConstStatementsMozilla : False
ErrorIfNotInlineSafe : False
EvalLiteralExpressions : True
EvalTreatment : Ignore
Format : Normal
IgnoreConditionalCompilation : False
IgnorePreprocessorDefines : False
InlineSafeStrings : True
LocalRenaming : CrunchAll
MacSafariQuirks : True
MinifyCode : True
ManualRenamesProperties : True
PreprocessOnly : False
PreserveFunctionNames : False
PreserveImportantComments : True
QuoteObjectLiteralProperties : False
ReorderScopeDeclarations : True
RemoveFunctionExpressionNames : True
RemoveUnneededCode : True
ScriptVersion : None
SourceMode : Program
StrictMode : False
StripDebugStatements : True
SymbolsMap :
WarningLevel : 0
AllowEmbeddedAspNetBlocks : False
BlocksStartOnSameLine : NewLine
IgnoreAllErrors : False
IndentSize : 4
LineBreakThreshold : 2147482647
OutputMode : SingleLine
TermSemicolons : False
KillSwitch : 0
LineTerminator :
 
IgnoreErrorCollection : {}
IgnoreErrorList :
PreprocessorValues : {}
PreprocessorDefineList :
ResourceStrings : {}
ReplacementTokens : {}
ReplacementFallbacks : {}
#>


<# $Settings.CssSettings
ColorNames : Hex
CommentMode : Important
MinifyExpressions : True
CssType : FullStyleSheet
RemoveEmptyBlocks : True
FixIE8Fonts : True
ExcludeVendorPrefixes : {}
IgnoreRazorEscapeSequence : False
DecodeEscapes : True
WarningLevel : 0
AllowEmbeddedAspNetBlocks : False
BlocksStartOnSameLine : NewLine
IgnoreAllErrors : False
IndentSize : 4
LineBreakThreshold : 2147482647
OutputMode : SingleLine
TermSemicolons : False
KillSwitch : 0
LineTerminator :
 
IgnoreErrorCollection : {}
IgnoreErrorList :
PreprocessorValues : {}
PreprocessorDefineList :
ResourceStrings : {}
ReplacementTokens : {}
ReplacementFallbacks : {}
#>

function Optimize-InternalUglifyJS {
    [CmdletBinding()]
    param(
        [string] $Content,
        [string] $Indent = ' '
    )
    #$Settings = [NUglify.JavaScript.CodeSettings]::new()
    #$Settings.MinifyCode = $true
    #$Settings.OutputMode = [NUglify.OutputMode]::MultipleLines
    #$Settings.Indent = $Indent
    #$Settings.JsSettings.MinifyCode = $true
    #$Settings.JsSettings.OutputMode = [NUglify.OutputMode]::MultipleLines
    #$Settings.JsSettings.Indent = $Indent
    #$Settings.JsSettings.BlocksStartOnSameLine = $BlockStartLine
    [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, ParameterSetName = 'Content', ValueFromPipeline, ValueFromPipelineByPropertyName)][string]$Content,
        [alias('Uri')][Parameter(Mandatory, 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
    #$Content = "<style>$Content</style>"
    #$Output = Format-InternalFormatWithUglify -Content $Content -IsFragment

    # 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,
        [string] $Indent = ' ',
        [NUglify.BlockStart] $BlockStartLine = [NUglify.BlockStart]::SameLine,
        [switch] $RemoveHTMLComments,
        [switch] $RemoveOptionalTags,
        [switch] $OutputTextNodesOnNewLine,
        [switch] $RemoveEmptyAttributes,
        [switch] $AlphabeticallyOrderAttributes,
        [switch] $RemoveEmptyBlocks
    )

    # 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
    $formatInternalFormatWithUglifySplat = @{
        Content                       = $Content
        Indent                        = $Indent
        BlockStartLine                = $BlockStartLine
        OutputTextNodesOnNewLine      = $OutputTextNodesOnNewLine
        RemoveOptionalTags            = $RemoveOptionalTags
        RemoveEmptyAttributes         = $RemoveEmptyAttributes
        AlphabeticallyOrderAttributes = $AlphabeticallyOrderAttributes
        RemoveEmptyBlocks             = $RemoveEmptyBlocks
        RemoveComments                = $RemoveHTMLComments
        #IsFragment = $true
    }
    $Output = Format-InternalFormatWithUglify @formatInternalFormatWithUglifySplat

    # 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 = Format-InternalUglifyJS -Content $Content
    if ($OutputFile) {
        [IO.File]::WriteAllText($OutputFile, $Output)
    } else {
        $Output
    }

    <#
    $IndentLenght = $Indent.Length
    $Content = "<script>$Content</script>"
    $Output = Format-InternalFormatWithUglify -Content $Content -IsFragment
    $SplitOutput = ($Output.Split("`n"))
    $NewOutput = for ($i = 1; $i -lt $SplitOutput.Count - 1; $i++) {
        $SplitOutput[$i].SubString($IndentLenght)
    }
    $FinalOutput = $NewOutput -join "`n"
    # Output to file or to text
    if ($OutputFile) {
        [IO.File]::WriteAllText($OutputFile, $FinalOutput)
    } else {
        $FinalOutput
    }
    #>

}
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
    #$Output = Optimize-InternalCSS -Content $Content
    $Output = Optimize-InternalUglifyCSS -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,
        [switch] $CSSDecodeEscapes
    )
    # 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
    }

    # Do magic
    $Output = Optimize-InternalUglifyHTML -Content $Content -CSSDecodeEscapes:$CSSDecodeEscapes

    # 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-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
# MIIdWQYJKoZIhvcNAQcCoIIdSjCCHUYCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUokawoxxRrMqA84arHTe9CU2C
# gsigghhnMIIDtzCCAp+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
# 8jCCBP4wggPmoAMCAQICEA1CSuC+Ooj/YEAhzhQA8N0wDQYJKoZIhvcNAQELBQAw
# cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk
# IElEIFRpbWVzdGFtcGluZyBDQTAeFw0yMTAxMDEwMDAwMDBaFw0zMTAxMDYwMDAw
# MDBaMEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEgMB4G
# A1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjEwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQDC5mGEZ8WK9Q0IpEXKY2tR1zoRQr0KdXVNlLQMULUmEP4d
# yG+RawyW5xpcSO9E5b+bYc0VkWJauP9nC5xj/TZqgfop+N0rcIXeAhjzeG28ffnH
# bQk9vmp2h+mKvfiEXR52yeTGdnY6U9HR01o2j8aj4S8bOrdh1nPsTm0zinxdRS1L
# sVDmQTo3VobckyON91Al6GTm3dOPL1e1hyDrDo4s1SPa9E14RuMDgzEpSlwMMYpK
# jIjF9zBa+RSvFV9sQ0kJ/SYjU/aNY+gaq1uxHTDCm2mCtNv8VlS8H6GHq756Wwog
# L0sJyZWnjbL61mOLTqVyHO6fegFz+BnW/g1JhL0BAgMBAAGjggG4MIIBtDAOBgNV
# HQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
# CDBBBgNVHSAEOjA4MDYGCWCGSAGG/WwHATApMCcGCCsGAQUFBwIBFhtodHRwOi8v
# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHwYDVR0jBBgwFoAU9LbhIB3+Ka7S5GGlsqIl
# ssgXNW4wHQYDVR0OBBYEFDZEho6kurBmvrwoLR1ENt3janq8MHEGA1UdHwRqMGgw
# MqAwoC6GLGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMu
# Y3JsMDKgMKAuhixodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVk
# LXRzLmNybDCBhQYIKwYBBQUHAQEEeTB3MCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
# cC5kaWdpY2VydC5jb20wTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jYWNlcnRzLmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURUaW1lc3RhbXBpbmdDQS5jcnQw
# DQYJKoZIhvcNAQELBQADggEBAEgc3LXpmiO85xrnIA6OZ0b9QnJRdAojR6OrktIl
# xHBZvhSg5SeBpU0UFRkHefDRBMOG2Tu9/kQCZk3taaQP9rhwz2Lo9VFKeHk2eie3
# 8+dSn5On7UOee+e03UEiifuHokYDTvz0/rdkd2NfI1Jpg4L6GlPtkMyNoRdzDfTz
# ZTlwS/Oc1np72gy8PTLQG8v1Yfx1CAB2vIEO+MDhXM/EEXLnG2RJ2CKadRVC9S0y
# OIHa9GCiurRS+1zgYSQlT7LfySmoc0NR2r1j1h9bm/cuG08THfdKDXF+l7f0P4Tr
# weOjSaH6zqe/Vs+6WXZhiV9+p7SOZ3j5NpjhyyjaW4emii8wggUwMIIEGKADAgEC
# AhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEw
# MjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV
# BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEi
# MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7
# RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p
# 0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj
# 6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grk
# V7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHy
# DxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMB
# AAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAT
# BgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGG
# GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2Nh
# Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCB
# gQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNl
# cnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgG
# CmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu
# Y29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1
# DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQEL
# BQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q
# 3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/
# kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dc
# IFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6
# dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT
# +hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggUxMIIEGaADAgECAhAKoSXW1jIbfkHk
# Bdo2l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE
# aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMT
# G0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0z
# MTAxMDcxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0
# IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEB
# AQUAA4IBDwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5
# fU1ofue2oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb
# 6+NGRwYaVX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU
# 46gJcWvgzyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM/fDqR9mI
# UF79Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfx
# FwbvPc3WTe8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAd
# BgNVHQ4EFgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SS
# y4IxLVGLp6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMC
# AYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j
# cnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYDVR0gBEkw
# RzA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj
# ZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLp
# UYdWac3v3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQd
# aq6Z+CeiZr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC
# 4HLHmNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3CzddWThZN+
# tpJn+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6H
# USHkWGCbugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIv
# IjayS6JKldj1po5SMIIFPTCCBCWgAwIBAgIQBNXcH0jqydhSALrNmpsqpzANBgkq
# hkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT
# SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDYyNjAwMDAwMFoX
# DTIzMDcwNzEyMDAwMFowejELMAkGA1UEBhMCUEwxEjAQBgNVBAgMCcWabMSFc2tp
# ZTERMA8GA1UEBxMIS2F0b3dpY2UxITAfBgNVBAoMGFByemVteXPFgmF3IEvFgnlz
# IEVWT1RFQzEhMB8GA1UEAwwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7KB3iyBrhkLUbbFe9qxhKKPBYqD
# Bqlnr3AtpZplkiVjpi9dMZCchSeT5ODsShPuZCIxJp5I86uf8ibo3vi2S9F9AlfF
# jVye3dTz/9TmCuGH8JQt13ozf9niHecwKrstDVhVprgxi5v0XxY51c7zgMA2g1Ub
# +3tii0vi/OpmKXdL2keNqJ2neQ5cYly/GsI8CREUEq9SZijbdA8VrRF3SoDdsWGf
# 3tZZzO6nWn3TLYKQ5/bw5U445u/V80QSoykszHRivTj+H4s8ABiforhi0i76beA6
# Ea41zcH4zJuAp48B4UhjgRDNuq8IzLWK4dlvqrqCBHKqsnrF6BmBrv+BXQIDAQAB
# o4IBxTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0O
# BBYEFBixNSfoHFAgJk4JkDQLFLRNlJRmMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE
# DDAKBggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2Ny
# bDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUw
# QzA3BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNl
# cnQuY29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNp
# Z25pbmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAmr1s
# z4lsLARi4wG1eg0B8fVJFowtect7SnJUrp6XRnUG0/GI1wXiLIeow1UPiI6uDMsR
# XPHUF/+xjJw8SfIbwava2eXu7UoZKNh6dfgshcJmo0QNAJ5PIyy02/3fXjbUREHI
# NrTCvPVbPmV6kx4Kpd7KJrCo7ED18H/XTqWJHXa8va3MYLrbJetXpaEPpb6zk+l8
# Rj9yG4jBVRhenUBUUj3CLaWDSBpOA/+sx8/XB9W9opYfYGb+1TmbCkhUg7TB3gD6
# o6ESJre+fcnZnPVAPESmstwsT17caZ0bn7zETKlNHbc1q+Em9kyBjaQRcEQoQQNp
# ezQug9ufqExx6lHYDjGCBFwwggRYAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYD
# VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAv
# BgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EC
# EATV3B9I6snYUgC6zZqbKqcwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAI
# oAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB
# CzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFCVP6I65QeKk1TI4orhy
# fV1MuvGXMA0GCSqGSIb3DQEBAQUABIIBAFAuLiA52ciuyvlsQ6JR5JI27IhEdEz+
# 4GbQJyoOBRuCkMW+Xg4czw8nu5QHwcJizag6w1RNikOUqxn8TINGEZ1SqDEj+BGP
# qkC+vS4K7XnGrtN+xEhpChEjDqZe6G8jassq5f46BvljpWCkzX0+XKqukCKf/aXy
# RIvZsyCF+Xspqtqqje+RU+2W+nb2R6NkEymqxSSwROuU+kmDKTycktvGFNCYg2kT
# mlRS25tK4+Xffok3uZVby7Luz8OVgCzU+CSIHMgA11zwxULwrw4xSEG01V9HbzVm
# gVG1gIBtE4Jqn0NLdaUjTMSNN6R6a6uUV1en0CuNq/tfSYgWcQdjlu6hggIwMIIC
# LAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNV
# BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8G
# A1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQQIQ
# DUJK4L46iP9gQCHOFADw3TANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIxMDMxNDE0MDYwMFowLwYJKoZI
# hvcNAQkEMSIEIDwhLIUlBOYDAxUWLph4Yue89YOM0s5m6sI8UAE9IdQvMA0GCSqG
# SIb3DQEBAQUABIIBAFb0OhDSm7gvqLrt5MuO/bOSPNil0pSLqzY/HrV33SRFfUq4
# g1edbfmIvopLpTZ4hQ7gZanlpQX757JaF+susKka0o1+kzd0PLfo9pbPl8SQzitU
# JKCqZX1QPWdHPIeXbStM8kNd3RdmNnQ7tIiNkpyt+9TjTqzsm6g/v5KcaSwJDJ+9
# y9V2cn+f6GTmd0TLnyJtW3mkT9AsqDiTXssb7HxrG/C/a1b+XyiyKyf/ummvNfCl
# +QDPFH9dZiPPw+gs/i7AzDG9rVpgE2Z3BOl+hxKmXsRR7qZoOiQmxKsFuRDI7uP9
# QyZlI38VFMfJod7A98tUazPqqoNx8nmpLQZNElI=
# SIG # End signature block