powershell-cli-menu-helpers.psm1

function Select-ItemFromList {
    <#
        .Synopsis
        Offers the possibility to select multible values in a cli based menu list.
 
        .DESCRIPTION
        Offers the possibility to select multible values in a cli based menu list.
        A filter, cancel, multiselect and empty return option is available.
        The function will return the selected items.
 
        .PARAMETER ListTitle
        The title of the list.
 
        .PARAMETER Items
        The items to show in the list.
 
        .PARAMETER PropertyName
        The name of the property to render in the list.
 
        .PARAMETER MultiselectOption
        Enables the multiselect option.
 
        .PARAMETER CancelOption
        Enables the cancel option.
 
        .PARAMETER FilterOption
        Enables the filter option.
 
        .PARAMETER AllowEmptyReturn
        Enables the empty return option.
 
        .INPUTS
        None. You cannot pipe objects to Select-ItemFromList.
 
        .OUTPUTS
        System.Management.Automation.PSObject[]
        Select-ItemFromList returns the selected items as an object in the following format.
 
        [PSCustomObject]@{
                Items = $null # Array of the items
                State = $null # %OK% / %CANCEL%
            }
 
        .EXAMPLE
        # Select an applicaiton to start
        [System.Array]$Applications = @(
            [PSCustomObject]@{Name = 'Notepad'; Path = "$($env:windir)\System32\notepad.exe" },
            [PSCustomObject]@{Name = 'Calc'; Path = "$($env:windir)\System32\calc.exe" },
            [PSCustomObject]@{Name = 'Explorer'; Path = "$($env:windir)\System32\Explorer.exe" }
        )
 
        [PSCustomObject]$Application = Select-ItemFromList -ListTitle 'Please select an applicaiton to start' -Items $Applications -PropertyName 'Name'
        Start-Process -FilePath $Application.Items.Path
 
        .EXAMPLE
        # Select multiple processes to get furhter detailes later
        $Processes = Select-ItemFromList -ListTitle "Select process, press 'd' if done" -Items (Get-Process | Select-Object -First 10) -PropertyName 'ProcessName' -MultiselectOption -CancelOption -FilterOption
        if ($Processes.State -eq '%OK%')
        {
            foreach ($Process in $Processes.Items)
            {
                $Process | Select-Object -Property Id, ProcessName
            }
        }
 
        .LINK
        https://github.com/netcloud/powershell-cli-menu-helpers
    #>

    [OutputType([System.Management.Automation.PSObject[]])]
    Param(
        [System.String]$ListTitle,
        [System.Management.Automation.PSObject[]]$Items,
        [Parameter(Mandatory = $true)]
        [System.String]$PropertyName,
        [System.Management.Automation.SwitchParameter]$MultiselectOption,
        [System.Management.Automation.SwitchParameter]$CancelOption,
        [System.Management.Automation.SwitchParameter]$FilterOption,
        [System.Management.Automation.SwitchParameter]$AllowEmptyReturn = $false
    )
    Begin {
        # Prepare the output object
        [PSCustomObject]$ReturnObj = [PSCustomObject]@{
            Items = $null
            State = $null # %OK% / %CANCEL%
        }

        # Define the possible options
        [System.Array]$Options = @(
            [PSCustomObject]@{display = $MultiselectOption; letter = 'a'; description = 'Select all' },
            [PSCustomObject]@{display = $MultiselectOption; letter = 'u'; description = 'Unselect all' },
            [PSCustomObject]@{display = $FilterOption; letter = 'f'; description = 'Apply filter' },
            [PSCustomObject]@{display = $FilterOption; letter = 'r'; description = 'Reset filter' },
            [PSCustomObject]@{display = ($MultiselectOption -or $AllowEmptyReturn); letter = 'd'; description = 'Done selecting' },
            [PSCustomObject]@{display = $CancelOption; letter = 'c'; description = 'Cancel' }
        )

        # Add internal needed properties to the items
        for ($i = 1; $i -le $Items.Count; $i ++) {
            Add-Member -InputObject $Items[$i - 1] -MemberType NoteProperty -Name '_id' -Value $i -Force
            Add-Member -InputObject $Items[$i - 1] -MemberType NoteProperty -Name '_selected' -Value $false -Force
        }

        # Set filters
        filter Get-SelectedItem {
            if ($_._selected) {
                $_
            }
        }

        filter Get-FilteredItem {
            if ($_.$PropertyName -like "*$Filter*") {
                $_
            }
        }
    }
    Process {
        # Display the list in a loop
        [System.Boolean]$RunMenu = $true
        [System.String]$Filter = ''

        Clear-Host
        Write-Host -Object ''
        Write-Host -Object ''

        while ($RunMenu) {
            # Render the list title
            if ($ListTitle -ne '') {
                Write-Host -Object $ListTitle
            }

            # Display filter if applied
            if ($Filter -ne '') {
                Write-Host -Object "Filter: $Filter"
                Write-Host -Object ''
            }

            # Render the list items
            foreach ($Item in ($Items | Get-FilteredItem)) {
                if ($Item._selected) {
                    Write-Host -Object "$($Item._id)*) $($Item.$PropertyName)" -ForegroundColor Green
                }
                else {
                    Write-Host -Object "$($Item._id)) $($Item.$PropertyName)"
                }
            }

            Write-Host -Object ''
            Write-Host -Object ''

            # Render the list options
            foreach ($Option in $Options) {
                if ($Option.display) {
                    Write-Host -Object "$($Option.letter)" -ForegroundColor Yellow -NoNewline
                    Write-Host -Object ": $($Option.description)"
                }
            }

            Write-Host -Object ''
            Write-Host -Object ''

            # Get user input
            [System.String]$UserInputPrompt = 'Enter selection'
            if ($MultiselectOption) {
                $UserInputPrompt = "Enter selection ($(($Items | Get-SelectedItem | Measure-Object).Count) selections)"
            }

            [System.String]$UserInput = Read-Host -Prompt $UserInputPrompt

            # Process the user input
            switch ($UserInput) {
                # Add filter if option is enabled
                { ($_ -eq 'f') -and ($FilterOption) } {
                    $Filter = Read-Host -Prompt 'Enter filter'
                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Reset filter if option is enabled
                { ($_ -eq 'r') -and ($FilterOption) } {
                    $Filter = ''
                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Select item in multi select mode / return item in single select mode
                { ($_ -match '^\d+$') -and (($Items | Get-FilteredItem)._id -contains $_) } {
                    # Save the item id
                    $ItemId = $_

                    # Get the item and set selected to true
                    $Item = $Items | Where-Object -FilterScript { $_._id -eq $ItemId }
                    $Item._selected = !$Item._selected

                    # Return the item if single select mode
                    if (!$MultiselectOption) {
                        if (((($Items | Get-SelectedItem).Count -eq 0) -and $AllowEmptyReturn) -or (($Items | Get-SelectedItem).Count -ne 0)) {
                            $ReturnObj.State = '%OK%'
                            return
                        }
                        else {
                            Clear-Host
                            Write-Host -Object "Please select an item!" -ForegroundColor Yellow
                            Write-Host -Object ''
                        }
                    }

                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Select all items if multi select is enabled
                { ($_ -eq 'a') -and $MultiselectOption } {
                    foreach ($Item in ($Items | Get-FilteredItem)) {
                        $Item._selected = $true
                    }

                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Unselect all items if multi select is enabled
                { ($_ -eq 'u') -and $MultiselectOption } {
                    foreach ($Item in ($Items | Get-FilteredItem)) {
                        $Item._selected = $false
                    }

                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Cancel the selection if option is activated
                { ($_ -eq 'c' -and $CancelOption) } {
                    $ReturnObj.State = '%CANCEL%'
                    $RunMenu = $false
                }

                # Return all selected items if multi select mode is activated or empty return is allowed
                { ($_ -eq 'd' -and $MultiselectOption) -or ($_ -eq 'd' -and $AllowEmptyReturn) } {
                    if (((($Items | Get-SelectedItem).Count -eq 0) -and $AllowEmptyReturn) -or (($Items | Get-SelectedItem).Count -ne 0)) {
                        $ReturnObj.State = '%OK%'
                        return
                    }
                    else {
                        Clear-Host
                        Write-Host -Object "Please select at least one item!" -ForegroundColor Yellow
                        Write-Host -Object ''
                    }
                }

                # If user input is not valid
                default {
                    Clear-Host
                    Write-Host -Object "Input '$_' is not valid!" -ForegroundColor Yellow
                    Write-Host -Object ''
                }
            }
        }
    }
    End {
        # Return the selected items only if run menu equals true
        if ($RunMenu) {
            $ReturnObj.Items = @() + $Items | Get-SelectedItem
        }

        return $ReturnObj
    }
}
function Select-ItemFromTable {
    <#
        .Synopsis
        Offers the possibility to select multible values in a cli based menu table.
 
        .DESCRIPTION
        Offers the possibility to select multible values in a cli based menu table.
        A filter, cancel, multiselect and empty return option is available.
        The function will return the selected items.
 
        .PARAMETER TableTitle
        The title of the table.
 
        .PARAMETER Items
        The items to show in the table.
 
        .PARAMETER PropertyName
        The name of the property to render in the table.
 
        .PARAMETER MultiselectOption
        Enables the multiselect option.
 
        .PARAMETER CancelOption
        Enables the cancel option.
 
        .PARAMETER FilterOption
        Enables the filter option.
 
        .PARAMETER HideTableHeaders
        To hide the table headers in the output.
 
        .PARAMETER AllowEmptyReturn
        Enables the empty return option.
 
        .INPUTS
        None. You cannot pipe objects to Select-ItemFromTable.
 
        .OUTPUTS
        System.Management.Automation.PSObject[]
        Select-ItemFromTable returns the selected items as an object in the following format.
 
        [PSCustomObject]@{
                Items = $null # Array of the items
                State = $null # %OK% / %CANCEL%
            }
 
        .EXAMPLE
        # Select an applicaiton to start
        [System.Array]$Applications = @(
            [PSCustomObject]@{Name = 'Notepad'; Path = "$($env:windir)\System32\notepad.exe" },
            [PSCustomObject]@{Name = 'Calc'; Path = "$($env:windir)\System32\calc.exe" },
            [PSCustomObject]@{Name = 'Explorer'; Path = "$($env:windir)\System32\Explorer.exe" }
        )
 
        [PSCustomObject]$Application = Select-ItemFromTable -TableTitle 'Please select an applicaiton to start' -Items $Applications -PropertyName 'Name'
        Start-Process -FilePath $Application.Items.Path
 
        .EXAMPLE
        # Select multiple processes to get furhter detailes later
        $Processes = Select-ItemFromTable -TableTitle "Select process, press 'd' if done" -Items (Get-Process | Select-Object -First 10) -PropertyName 'Id', 'Handles', 'ProcessName' -MultiselectOption -CancelOption -FilterOption
        if ($Processes.State -eq '%OK%')
        {
            foreach ($Process in $Processes.Items)
            {
                $Process
            }
        }
 
        .LINK
        https://github.com/netcloud/powershell-cli-menu-helpers
    #>

    [OutputType([System.Management.Automation.PSObject[]])]
    Param(
        [System.String]$TableTitle,
        [System.Management.Automation.PSObject[]]$Items,
        [Parameter(Mandatory = $true)]
        [System.String[]]$PropertyName,
        [System.Management.Automation.SwitchParameter]$MultiselectOption,
        [System.Management.Automation.SwitchParameter]$CancelOption,
        [System.Management.Automation.SwitchParameter]$FilterOption,
        [System.Management.Automation.SwitchParameter]$HideTableHeaders,
        [System.Management.Automation.SwitchParameter]$AllowEmptyReturn = $false
    )
    Begin {
        # Prepare the output object
        [PSCustomObject]$ReturnObj = [PSCustomObject]@{
            Items = $null
            State = $null # %OK% / %CANCEL%
        }

        # Define the possible options
        [System.Array]$Options = @(
            [PSCustomObject]@{display = $MultiselectOption; letter = 'a'; description = 'Select all' },
            [PSCustomObject]@{display = $MultiselectOption; letter = 'u'; description = 'Unselect all' },
            [PSCustomObject]@{display = $FilterOption; letter = 'f'; description = 'Apply filter' },
            [PSCustomObject]@{display = $FilterOption; letter = 'r'; description = 'Reset filter' },
            [PSCustomObject]@{display = ($MultiselectOption -or $AllowEmptyReturn); letter = 'd'; description = 'Done selecting' },
            [PSCustomObject]@{display = $CancelOption; letter = 'c'; description = 'Cancel' }
        )

        # Add internal needed properties to the items
        for ($i = 1; $i -le $Items.Count; $i ++) {
            Add-Member -InputObject $Items[$i - 1] -MemberType NoteProperty -Name '_id' -Value $i -Force
            Add-Member -InputObject $Items[$i - 1] -MemberType NoteProperty -Name '_selected' -Value $false -Force
        }

        # Get lengt of the longest id
        [System.Int32]$IdLengt = ($Items._id | Sort-Object -Descending | Select-Object -First 1).Length + 2

        # Set the table properties
        $TableProperty = @(@{Name = (' ' * $IdLengt); Expression = { if ($_._selected) { "$($_._id)*)" } else { "$($_._id))" } } }) + $PropertyName

        # Set filters
        filter Get-SelectedItem {
            if ($_._selected) {
                $_
            }
        }

        filter Get-FilteredItem {
            if (($_ | Select-Object -Property $TableProperty | ConvertTo-Json -Compress | Out-String) -like "*$Filter*") {
                $_
            }
        }
    }
    Process {
        # Display the table in a loop
        [System.Boolean]$RunMenu = $true
        [System.String]$Filter = ''

        Clear-Host
        Write-Host -Object ''
        Write-Host -Object ''

        while ($RunMenu) {
            # Render the table title
            if ($TableTitle -ne '') {
                Write-Host -Object $TableTitle
                Write-Host -Object ''
            }

            # Display filter if applied
            if ($Filter -ne '') {
                Write-Host -Object "Filter: $Filter"
                Write-Host -Object ''
            }

            # Render the items as a table
            if ($Items.Count -gt 0) {
                if (!$HideTableHeaders) {
                    # Get the formated table with header as string
                    [System.String]$Table = $Items | Get-FilteredItem | Select-Object -Property $TableProperty | Format-Table -AutoSize | Out-String

                    # Get each line of the table
                    $Rows = $Table.Split([System.Environment]::NewLine) | Where-Object -FilterScript { $_ -ne '' }

                    # Print the table header
                    Write-Host -Object $Rows[0]
                    Write-Host -Object $Rows[1]

                    # Print each row
                    for ($i = 2; $i -lt $Rows.Count; $i++) {
                        if (($Items | Get-FilteredItem)[$i - 2]._selected) {
                            Write-Host -Object $Rows[$i] -ForegroundColor Green
                        }
                        else {
                            Write-Host -Object $Rows[$i]
                        }
                    }
                }
                else {
                    # Write blank line for header
                    Write-Host -Object ''

                    # Get the formated table without header as string
                    [System.String]$Table = $Items | Get-FilteredItem | Select-Object -Property $TableProperty | Format-Table -HideTableHeaders -AutoSize | Out-String

                    # Get each line of the table
                    [System.Array]$Rows = @()
                    $Rows += $Table.Split([System.Environment]::NewLine) | Where-Object -FilterScript { $_ -ne '' }

                    # Print each row
                    for ($i = 0; $i -lt $Rows.Count; $i++) {
                        if (($Items | Get-FilteredItem)[$i]._selected) {
                            Write-Host -Object $Rows[$i] -ForegroundColor Green
                        }
                        else {
                            Write-Host -Object $Rows[$i]
                        }
                    }
                }
            }

            Write-Host -Object ''
            Write-Host -Object ''

            # Render the table options
            foreach ($Option in $Options) {
                if ($Option.display) {
                    Write-Host -Object "$($Option.letter)" -ForegroundColor Yellow -NoNewline
                    Write-Host -Object ": $($Option.description)"
                }
            }

            Write-Host -Object ''
            Write-Host -Object ''

            # Get user input
            [System.String]$UserInputPrompt = 'Enter selection'
            if ($MultiselectOption) {
                $UserInputPrompt = "Enter selection ($(($Items | Get-SelectedItem | Measure-Object).Count) selections)"
            }

            [System.String]$UserInput = Read-Host -Prompt $UserInputPrompt

            # Process the user input
            switch ($UserInput) {
                # Add filter if option is enabled
                { ($_ -eq 'f') -and ($FilterOption) } {
                    $Filter = Read-Host -Prompt 'Enter filter'
                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Reset filter if option is enabled
                { ($_ -eq 'r') -and ($FilterOption) } {
                    $Filter = ''
                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Select item in multi select mode / return item in single select mode
                { ($_ -match '^\d+$') -and (($Items | Get-FilteredItem)._id -contains $_) } {
                    # Save the item id
                    $ItemId = $_

                    # Get the item and set selected to true
                    $Item = $Items | Where-Object -FilterScript { $_._id -eq $ItemId }
                    $Item._selected = !$Item._selected

                    # Return the item if single select mode
                    if (!$MultiselectOption) {
                        if (((($Items | Get-SelectedItem).Count -eq 0) -and $AllowEmptyReturn) -or (($Items | Get-SelectedItem).Count -ne 0)) {
                            $ReturnObj.State = '%OK%'
                            return
                        }
                        else {
                            Clear-Host
                            Write-Host -Object "Please select an item!" -ForegroundColor Yellow
                            Write-Host -Object ''
                        }
                    }

                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Select all items if multi select is enabled
                { ($_ -eq 'a') -and $MultiselectOption } {
                    foreach ($Item in ($Items | Get-FilteredItem)) {
                        $Item._selected = $true
                    }

                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Unselect all items if multi select is enabled
                { ($_ -eq 'u') -and $MultiselectOption } {
                    foreach ($Item in ($Items | Get-FilteredItem)) {
                        $Item._selected = $false
                    }

                    Clear-Host
                    Write-Host -Object ''
                    Write-Host -Object ''
                }

                # Cancel the selection if option is activated
                { ($_ -eq 'c' -and $CancelOption) } {
                    $ReturnObj.State = '%CANCEL%'
                    $RunMenu = $false
                }

                # Return all selected items if multi select mode is activated or empty return is allowed
                { ($_ -eq 'd' -and $MultiselectOption) -or ($_ -eq 'd' -and $AllowEmptyReturn) } {
                    if (((($Items | Get-SelectedItem).Count -eq 0) -and $AllowEmptyReturn) -or (($Items | Get-SelectedItem).Count -ne 0)) {
                        $ReturnObj.State = '%OK%'
                        return
                    }
                    else {
                        Clear-Host
                        Write-Host -Object "Please select at least one item!" -ForegroundColor Yellow
                        Write-Host -Object ''
                    }
                }

                # If user input is not valid
                default {
                    Clear-Host
                    Write-Host -Object "Input '$_' is not valid!" -ForegroundColor Yellow
                    Write-Host -Object ''
                }
            }
        }
    }
    End {
        # Return the selected items only if run menu equals true
        if ($RunMenu) {
            $ReturnObj.Items = @() + $Items | Get-SelectedItem
        }

        return $ReturnObj
    }
}
function Select-MenuEntryFromList {
    <#
        .Synopsis
        Offers the possibility to render a menu inside the cli and to select one entry.
 
        .DESCRIPTION
        Offers the possibility to render a menu inside the cli and to select one entry.
        The function will return the selected menu entry.
 
        .PARAMETER MenuTitle
        The title of the menu.
 
        .PARAMETER MenuEntries
        The entries to show inside the menu.
 
        .PARAMETER BackOption
        Enables the back option.
 
        .PARAMETER ExitOption
        Enables the exit option.
 
        .INPUTS
        None. You cannot pipe objects to Select-MenuEntryFromList.
 
        .OUTPUTS
        System.String
        Select-MenuEntryFromList returns the selected menu item as string in the following format.
 
        [PSCustomObject]@{
            MenuEntry = $null # System.String
            State = $null # %OK% / %BACK% / %EXIT%
        }
 
        .EXAMPLE
        # Render a menu with back and exit option
 
        # Set the menu title and the menu entries
        [System.String[]]$MenuEntries = @('Menu 1', 'Menu 2', 'Menu 3')
        [System.String]$MenuTitle = "=============== Test Menu ==============="
 
        # Ask user for the menu entry
        [PSCustomObject]$SelectedMenuEntry = Select-MenuEntryFromList -MenuTitle $MenuTitle -MenuEntries $MenuEntries -ExitOption -BackOption
 
        # Do the action based on the selection
        switch ($SelectedMenuEntry.State)
        {
            '%OK%'
            {
                Write-Host -Object "Selected menu entry: $($SelectedMenuEntry.MenuEntry)"
            }
 
            '%BACK%'
            {
                Write-Host -Object "Back option selected"
            }
 
            '%EXIT%'
            {
                Write-Host -Object "Exit option selected"
            }
        }
 
        .LINK
        https://github.com/netcloud/powershell-cli-menu-helpers
    #>

    [OutputType([System.Management.Automation.PSObject[]])]
    Param(
        [System.String]$MenuTitle,
        [System.String[]]$MenuEntries,
        [System.Management.Automation.SwitchParameter]$BackOption,
        [System.Management.Automation.SwitchParameter]$ExitOption
    )
    Begin {
        # Prepare the output object
        [PSCustomObject]$ReturnObj = [PSCustomObject]@{
            MenuEntry = $null
            State     = $null # %OK% / %BACK% / %EXIT%
        }

        # Define the possible options
        [System.Array]$Options = @(
            [PSCustomObject]@{display = $BackOption; letter = 'b'; description = 'Back' },
            [PSCustomObject]@{display = $ExitOption; letter = 'e'; description = 'Exit' }
        )
    }
    Process {
        # Display the list in a loop
        [System.Boolean]$RunMenu = $true

        Clear-Host
        Write-Host -Object ''
        Write-Host -Object ''

        while ($RunMenu) {
            # Render the menu title
            if ($MenuTitle -ne '') {
                Write-Host -Object $MenuTitle
            }

            # Render the menu entries
            for ($i = 1; $i -le $MenuEntries.Count; $i ++) {
                Write-Host -Object "$i) $($MenuEntries[$i - 1])"
            }

            Write-Host -Object ''
            Write-Host -Object ''

            # Render the list options
            foreach ($Option in $Options) {
                if ($Option.display) {
                    Write-Host -Object "$($Option.letter)" -NoNewline -ForegroundColor Yellow
                    Write-Host -Object ": $($Option.description)"
                }
            }

            Write-Host -Object ''
            Write-Host -Object ''

            # Get user input
            [System.String]$UserInput = Read-Host -Prompt 'Enter selection'

            # Get all menu entry id's
            [System.Int32[]]$MenuEntryIDs = @()
            if ($MenuEntries.Count -ne 0) {
                $MenuEntryIDs = (1..$MenuEntries.Count)
            }

            # Process the user input
            switch ($UserInput) {
                # Return selected menu entry
                { ($_ -match '^\d+$') -and ($MenuEntryIDs -contains $_) } {
                    # Add the item to the return object
                    $ReturnObj.State = '%OK%'
                    $ReturnObj.MenuEntry = $MenuEntries[$_ - 1]
                    return
                }

                # Return back if the option is activated
                { ($_ -eq 'b' -and $BackOption) } {
                    $ReturnObj.State = '%BACK%'
                    return
                }

                # Return exit if the option is activated
                { ($_ -eq 'e' -and $ExitOption) } {
                    $ReturnObj.State = '%EXIT%'
                    return
                }

                # If user input is not valid
                default {
                    Clear-Host
                    Write-Host -Object "Input '$_' is not valid!" -ForegroundColor Yellow
                    Write-Host -Object ''
                }
            }
        }
    }
    End {
        # Return the object
        return $ReturnObj
    }
}

# SIG # Begin signature block
# MIISiwYJKoZIhvcNAQcCoIISfDCCEngCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU1l7Wql4MgCPZ/eNXZyLPbgSN
# /Zmggg5XMIIEHDCCAwSgAwIBAgIKIn/hjAADAAAm0zANBgkqhkiG9w0BAQsFADBM
# MRIwEAYKCZImiZPyLGQBGRYCY2gxGDAWBgoJkiaJk/IsZAEZFghuZXRjbG91ZDEc
# MBoGA1UEAxMTbmV0Y2xvdWQuY2ggUm9vdCBDQTAeFw0yMTAxMDYxMjE2MDJaFw0y
# MzEyMDkwODEzMzBaMHsxCzAJBgNVBAYTAkNIMRMwEQYDVQQHEwpXaW50ZXJ0aHVy
# MRQwEgYDVQQKEwtOZXRjbG91ZCBBRzEOMAwGA1UECxMFbmNEZXYxDjAMBgNVBAMT
# BW5jRGV2MSEwHwYJKoZIhvcNAQkBFhJuY19kZXZAbmV0Y2xvdWQuY2gwgZ8wDQYJ
# KoZIhvcNAQEBBQADgY0AMIGJAoGBAOZN3BE6WdimqN9zMlsEnhxijWS/vvvXfnNj
# 9om0FWFxvPY8MDq5kg1nSPilnGGjrKw42Uzswo3FjH18RNu9xXFP2BNdpbSG9/00
# OyYEMs3i8d0bgrcYN4glbYLIQfE5o83OwjgekwXZHRFYRLAZqH37zkiNoE6suQBi
# Rsv/3I8ZAgMBAAGjggFTMIIBTzALBgNVHQ8EBAMCB4AwPQYJKwYBBAGCNxUHBDAw
# LgYmKwYBBAGCNxUIheWYYYOrnmaEiYsRh4H/CYPQoX4qh7P+Q4a7zQgCAWUCARQw
# HQYDVR0OBBYEFKkBjB/3JfydHBCXjC8v1zaANJB0MB8GA1UdIwQYMBaAFG4vOrQ2
# gTM4WQh4BBTACZlkDJX6MEEGA1UdHwQ6MDgwNqA0oDKGMGh0dHA6Ly9wa2kubmV0
# Y2xvdWQuY2gvQ0ExMy9uZXRjbG91ZFJvb3QyMDEzLmNybDBMBggrBgEFBQcBAQRA
# MD4wPAYIKwYBBQUHMAKGMGh0dHA6Ly9wa2kubmV0Y2xvdWQuY2gvQ0ExMy9uZXRj
# bG91ZFJvb3QyMDEzLmNydDATBgNVHSUEDDAKBggrBgEFBQcDAzAbBgkrBgEEAYI3
# FQoEDjAMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAGuuUeP7gOF1oB
# 1wzPepJyVZA28VhJATscbldy256atj30MH4RyVl6zxFaEh2icgUbRt+UPANBkIMT
# s8BLWLLD829eW9JXcZRy3HYQkFW7aPfDYOLGI3CQcDuBE3bEXv4EPm6SCnoik5sF
# v04zVM1Qp4kFtmRGuignjdOl/6Hs3v8AlXFrQh5jhFgBDgCFYYGMkNbWKCw/usgJ
# AHN2Jxejoopj6G6PUNiJ6AwUqZbSpNcc7Yz+KQ+hqwNc5VTg3/n0WHkKIQs+WU/C
# 1VfrTy+Qo5f4kgsVsmOZQu0ZVVyoDSKpxv+C0plBqqAGN+LrIJF3TgZe8kYLZRPL
# +FefSoNEMIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TANBgkqhkiG9w0B
# AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAwMFoXDTMxMDEw
# NjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFCvQp1dU2UtAxQ
# tSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in43Stwhd4CGPN4
# bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6t2HWc+xObTOK
# fF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0TXhG4wODMSlK
# XAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK02/xWVLwfoYer
# vnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEAAaOCAbgwggG0
# MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG
# AQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYBBQUHAgEWG2h0
# dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0tuEgHf4prtLk
# YaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNqerwwcQYDVR0f
# BGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJl
# ZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz
# c3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6
# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NB
# LmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5nRv1CclF0CiNH
# o6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2uHDPYuj1UUp4
# eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmDgvoaU+2QzI2h
# F3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QRcucbZEnYIpp1
# FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4bTxMd90oNcX6X
# t/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aKLzCCBTEwggQZ
# oAMCAQICEAqhJdbWMht+QeQF2jaXwhUwDQYJKoZIhvcNAQELBQAwZTELMAkGA1UE
# BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj
# ZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4X
# DTE2MDEwNzEyMDAwMFoXDTMxMDEwNzEyMDAwMFowcjELMAkGA1UEBhMCVVMxFTAT
# BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEx
# MC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBD
# QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3QMu5LzY9/3am6gpnF
# OVQoV7YjSsQOB0UzURB90Pl9TWh+57ag9I2ziOSXv2MhkJi/E7xX08PhfgjWahQA
# OPcuHjvuzKb2Mln+X2U/4Jvr40ZHBhpVfgsnfsCi9aDg3iI/Dv9+lfvzo7oiPhis
# EeTwmQNtO4V8CdPuXciaC1TjqAlxa+DPIhAPdc9xck4Krd9AOly3UeGheRTGTSQj
# MF287DxgaqwvB8z98OpH2YhQXv1mblZhJymJhFHmgudGUP2UKiyn5HU+upgPhH+f
# MRTWrdXyZMt7HgXQhBlyF/EXBu89zdZN7wZC/aJTKk+FHcQdPK/P2qwQ9d2srOlW
# /5MCAwEAAaOCAc4wggHKMB0GA1UdDgQWBBT0tuEgHf4prtLkYaWyoiWyyBc1bjAf
# BgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzASBgNVHRMBAf8ECDAGAQH/
# AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB5BggrBgEF
# BQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBD
# BggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2Ny
# bDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDig
# NoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
# dENBLmNybDBQBgNVHSAESTBHMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYc
# aHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sBwEwDQYJKoZI
# hvcNAQELBQADggEBAHGVEulRh1Zpze/d2nyqY3qzeM8GN0CE70uEv8rPAwL9xafD
# DiBCLK938ysfDCFaKrcFNB1qrpn4J6JmvwmqYN92pDqTD/iy0dh8GWLoXoIlHsS6
# HHssIeLWWywUNUMEaLLbdQLgcseY1jxk5R9IEBhfiThhTWJGJIdjjJFSLK8pieV4
# H9YLFKWA1xJHcLN11ZOFk362kmf7U2GJqPVrlsD0WGkNfMgBsbkodbeZY4UijGHK
# eZR+WfyMD+NvtQEmtmyl7odRIeRYYJu6DC0rbaLEfrvEJStHAgh8Sa4TtuF8QkIo
# xhhWz0E0tmZdtnR79VYzIi8iNrJLokqV2PWmjlIxggOeMIIDmgIBATBaMEwxEjAQ
# BgoJkiaJk/IsZAEZFgJjaDEYMBYGCgmSJomT8ixkARkWCG5ldGNsb3VkMRwwGgYD
# VQQDExNuZXRjbG91ZC5jaCBSb290IENBAgoif+GMAAMAACbTMAkGBSsOAwIaBQCg
# eDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEE
# AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJ
# BDEWBBSWmZ2NoRhR1u0WzOkMZ1Vu5QAIeDANBgkqhkiG9w0BAQEFAASBgA5gY0px
# BtXnRTmwpMCx3jD5z32pzQ1h7nvBsUyKKGqeDwWABRuMB9LqKJpUZsf6J6xbF7Ss
# Cit8Kt3CGPu5geB6hV0TsDQ42oNx6I86VeU4aX90mS30L3tmv3Ddf1L0CNP4Dkmx
# /BNIWt77PTxeyqM9fgJKu0I+rsQbJCyfw0o1oYICIDCCAhwGCSqGSIb3DQEJBjGC
# Ag0wggIJAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0
# IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0ECEA1CSuC+Ooj/YEAhzhQA
# 8N0wCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZI
# hvcNAQkFMQ8XDTIxMDEwNzA3NTYwM1owIwYJKoZIhvcNAQkEMRYEFOVgxPg9ZUpe
# 1KhUl6Y8h8oC9KRbMA0GCSqGSIb3DQEBAQUABIIBAGolr7oEPEFYAM78wHlqN7d1
# GgHILimA0aOY9ZU4vFOOQ8+TXw6IebkmPSKP919ZBLPWYXdV8LAh0kFYVu34c9Xc
# TOXY6qxahYkHZd9+xilWylSNCbdcdt5yrzdQL8IA/GJeh+EhfX+RO935Yjjxqi4N
# jBH3B3TxCfJzWWgpUWmE41OQhrSedUFSWYEhDPbaWbYbsmeE67A+Sjy+yT82Qpjc
# PKe4QFVpmDHjaSWqkaLvugFc8i64IUcC3GRl6zLNaRlhuq2t4CCqNxX5ZjwHQQk9
# Ejzf1z8ja/S9FPtCRSGVutJweEN79y4bfbtItAVhaRCC1g17PeCyBnVUT4Z9JIM=
# SIG # End signature block