Pansies.psm1

#Region '.\_init.ps1' 0
using namespace ColorMine.ColorSpaces
using namespace PoshCode.Pansies
using namespace ColorMine.Palettes
using namespace PoshCode.Pansies.Palettes
using namespace System.Collections.Generic

# On first import, if HostPreference doesn't exist, set it and strongly type it
if(!(Test-Path Variable:HostPreference) -or $HostPreference -eq $null) {
    [System.Management.Automation.ActionPreference]$global:HostPreference = "Continue"
}

Set-Variable HostPreference -Description "Dictates the action taken when a host message is delivered" -Visibility Public -Scope Global

if(-not $IsLinux -and -not $IsMacOS) {
    [PoshCode.Pansies.Console.WindowsHelper]::EnableVirtualTerminalProcessing()
}

if(Get-Command Add-MetadataConverter -ErrorAction SilentlyContinue) {
    Add-MetadataConverter @{
        RgbColor = { [PoshCode.Pansies.RgbColor]$args[0] }
        [PoshCode.Pansies.RgbColor] = { "RgbColor '$_'" }
    }
}
#EndRegion '.\_init.ps1' 23
#Region '.\Private\ConvertToCssColor.ps1' 0
function ConvertToCssColor {
    [CmdletBinding()]
    param(
        [Parameter(ParameterSetName="PListColorDictionary", Mandatory, Position = 0)]
        [Dictionary[string,object]]$colors,

        [Parameter(ParameterSetName="ColorValue", Mandatory, Position = 0)]
        [string]$color
    )
    end {
        if($PSCmdlet.ParameterSetName -eq "PListColorDictionary") {
            [int]$r = 255 * $colors["Red Component"]
            [int]$g = 255 * $colors["Green Component"]
            [int]$b = 255 * $colors["Blue Component"]
            [PoshCode.Pansies.RgbColor]::new($r, $g, $b).ToString()
        }
        if($PSCmdlet.ParameterSetName -eq "ColorValue") {
            [PoshCode.Pansies.RgbColor]::new($color).ToString()
        }
    }
}
#EndRegion '.\Private\ConvertToCssColor.ps1'
#Region '.\Private\ExportTheme.ps1' 0
function ExportTheme {
    <#
        .SYNOPSIS
            Imports themes by name
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        # The name of the theme
        [Parameter(Position = 0, Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(ValueFromPipeline, Position = 1)]
        $InputObject,

        [switch]$Force,

        [switch]$Update,

        [switch]$PassThru,

        [ValidateSet("User", "Machine")]
        [string]$Scope = "User"
    )
    process {
        $NativeThemePath = Join-Path $(Get-ConfigurationPath -Scope $Scope) "$Name.theme.psd1"

        if(Test-Path -LiteralPath $NativeThemePath) {
            if($Update) {
                Write-Verbose "Updating $NativeThemePath"
                $Theme = Import-Metadata $NativeThemePath -ErrorAction Stop
                Update-Object -InputObject $Theme -UpdateObject $InputObject | Export-Metadata $NativeThemePath
            } elseif($Force -or $PSCmdlet.ShouldContinue("Overwrite $($NativeThemePath)?", "$Name Theme exists")) {
                Write-Verbose "Exporting to $NativeThemePath"
                $InputObject | Export-Metadata $NativeThemePath
            }
        } else {
            Write-Verbose "Exporting to $NativeThemePath"
            $InputObject | Export-Metadata $NativeThemePath
        }

        if($PassThru) {
            $InputObject | Add-Member NoteProperty Name $Name -Passthru |
                           Add-Member NoteProperty PSPath $NativeThemePath -Passthru
        }
    }
}
#EndRegion '.\Private\ExportTheme.ps1'
#Region '.\Private\FindVSCodeTheme.ps1' 0
function FindVsCodeTheme {
    [CmdletBinding()]
    param($Name)

    $VSCodeExtensions = @(
        # VS Code themes are in one of two places: in the app, or in your profile folder:
        Convert-Path "~\.vscode*\extensions\"
        # If `code` is in your path, we can guess where that is...
        Get-Command Code-Insiders, Code -ErrorAction Ignore |
            Split-Path | Split-Path | Join-Path -ChildPath "resources\app\extensions\"
    )
    $Warnings = @()

    $Themes = @(
        # If they passed a file path that exists, use just that one file
        if ($Specific = Test-Path -LiteralPath $Name) {
            $File = Convert-Path $Name
            $(
                if ($File.EndsWith(".json")) {
                    try {
                        # Write-Debug "Parsing json file: $File"
                        ConvertFrom-Json (Get-Content -Path $File -Raw -Encoding utf8) -ErrorAction SilentlyContinue
                    } catch {
                        Write-Error "Couldn't parse '$File'. $(
                        if($PSVersionTable.PSVersion.Major -lt 6) {
                            'You could try again with PowerShell Core, the JSON parser there works much better!'
                        })"

                    }
                } else {
                    # Write-Debug "Parsing PList file: $File"
                    Import-PList -Path $File
                }
            ) | Select-Object @{ Name = "Name"
                                 Expr = {
                                    if ($_.name) {
                                        $_.name
                                    } else {
                                        [IO.Path]::GetFileNameWithoutExtension($File)
                                    }
                                }
                           }, @{ Name = "Path"
                                 Expr = {$File}
                           }
        } else {
            $VSCodeExtensions  = $VSCodeExtensions | Join-Path -ChildPath "\*\package.json" -Resolve
            foreach ($File in $VSCodeExtensions) {
                # Write-Debug "Considering VSCode Extention $([IO.Path]::GetFileName([IO.Path]::GetDirectoryName($File)))"
                $JSON = Get-Content -Path $File -Raw -Encoding utf8
                try {
                    $Extension = ConvertFrom-Json $JSON -ErrorAction Stop
                    # if ($Extension.contributes.themes) {
                    # Write-Debug "Found $($Extension.contributes.themes.Count) themes"
                    # }
                    $Extension.contributes.themes |
                        Select-Object @{Name="Name" ; Expr={$_.label}},
                                      @{Name="Style"; Expr={$_.uiTheme}},
                                      @{Name="Path" ; Expr={Join-Path (Split-Path $File) $_.path -resolve}}
                } catch {
                    $Warning = "Couldn't parse some VSCode extensions."
                }
            }
        }
    )
    if($Themes.Count -eq 0) {
        throw "Could not find any VSCode themes. Please use a full path."
    }

    if($Specific -and $Themes.Count -eq 1) {
        $Themes
    }

    # Make sure we're comparing the name to a name
    $Name = [IO.Path]::GetFileName(($Name -replace "\.json$|\.tmtheme$"))
    Write-Verbose "Testing theme names for '$Name'"

    # increasingly fuzzy search: (eq -> like -> match)
    if(!($Theme = $Themes.Where{$_.name -eq $Name})) {
        if (!($Theme = $Themes.Where{$_.name -like $Name})) {
            if (!($Theme = $Themes.Where{$_.name -like "*$Name*"})) {
                foreach($Warning in $Warnings) {
                    Write-Warning $Warning
                }
                Write-Error "Couldn't find the theme '$Name', please try another: $(($Themes.name | Select-Object -Unique) -join ', ')"
            }
        }
    }
    if(@($Theme).Count -gt 1) {
        $Dupes = $(if(@($Theme.Name | Get-Unique).Count -gt 1) {$Theme.Name} else {$Theme.Path}) -join ", "
        Write-Warning "Found more than one theme for '$Name'. Using '$(@($Theme)[0].Path)', but you could try again for one of: $Dupes)"
    }
    @($Theme)[0]
}
#EndRegion '.\Private\FindVSCodeTheme.ps1'
#Region '.\Private\GetColorProperty.ps1' 0
function GetColorProperty{
    <#
        .SYNOPSIS
            Search the colors for a matching theme color name and returns the foreground
    #>

    param(
        # The array of colors
        [Array]$colors,

        # An array of (partial) scope names in priority order
        # The foreground color of the first matching scope in the tokens will be returned
        [string[]]$name
    )
    # Since we loaded the themes in order of prescedence, we take the first match that has a foreground color
    foreach ($pattern in $name) {
        # Normalize color
        if($foreground = @($colors.$pattern).Where{$_}[0]) {
            ConvertToCssColor $foreground
            return
        }
    }
}
#EndRegion '.\Private\GetColorProperty.ps1'
#Region '.\Private\GetColorScopeForeground.ps1' 0
function GetColorScopeForeground {
    <#
        .SYNOPSIS
            Search the tokens for a scope name with a foreground color
    #>

    param(
        # The array of tokens
        [Array]$tokens,

        # An array of (partial) scope names in priority order
        # The foreground color of the first matching scope in the tokens will be returned
        [string[]]$name
    )
    # Since we loaded the themes in order of prescedence, we take the first match that has a foreground color
    foreach ($pattern in $name) {
        foreach ($token in $tokens) {
            if (($token.scope -split "\s*,\s*" -match $pattern) -and $token.settings.foreground) {
                ConvertToCssColor $token.settings.foreground
                return
            }
        }
    }
}
#EndRegion '.\Private\GetColorScopeForeground.ps1'
#Region '.\Private\ImportJsonIncludeLast.ps1' 0
function ImportJsonIncludeLast {
    <#
        .SYNOPSIS
            Import VSCode json themes, including any included themes
    #>

    [CmdletBinding()]
    param([string[]]$Path)

    # take the first
    $themeFile, $Path = $Path
    $theme = Get-Content $themeFile | ConvertFrom-Json

    # Output all the colors or token colors
    if ($theme.colors) {
        $theme.colors
    }
    if ($theme.tokenColors) {
        $theme.tokenColors
    }

    # Recurse includes
    if ($theme.include) {
        $Path += $themeFile | Split-Path | Join-Path -Child $theme.include | convert-path
    }
    if ($Path) {
        ImportJsonIncludeLast $Path
    }
}
#EndRegion '.\Private\ImportJsonIncludeLast.ps1'
#Region '.\Private\ImportTheme.ps1' 0
function ImportTheme {
    <#
        .SYNOPSIS
            Imports themes by name
    #>

    [CmdletBinding()]
    param(
        # The name of the theme
        [Parameter(Position=0)]
        [string]$Name
    )

    $FileName = $Name -replace "((\.theme)?\.psd1)?$" -replace '$', ".theme.psd1"

    $Path = if (!(Test-Path -LiteralPath $FileName)) {
        Get-Theme $Name | Select-Object -First 1 -ExpandProperty PSPath
    } else {
        Convert-Path $FileName
    }
    if(!$Path) {
        $Themes = @(Get-Theme "$Name*")
        if($Themes.Count -gt 1) {
            Write-Warning "No exact match for $Name. Using $($Themes[0]), but also found $($Themes[1..$($Themes.Count-1)] -join ', ')"
            $Path = $Themes[0].PSPath
        } elseif($Themes) {
            Write-Warning "No exact match for $Name. Using $($Themes[0])"
            $Path = $Themes[0].PSPath
        } else {
            $Themes = @(Get-Theme "*$Name*")
            if($Themes.Count -gt 1) {
                Write-Warning "No exact match for $Name. Using $($Themes[0]), but also found $($Themes[1..$($Themes.Count-1)] -join ', ')"
                $Path = $Themes[0].PSPath
            } elseif($Themes) {
                Write-Warning "No exact match for $Name. Using $($Themes[0])"
                $Path = $Themes[0].PSPath
            }
        }
        if(!$Path) {
            Write-Error "No theme '$Name' found. Try Get-Theme to see available themes."
            return
        }
    }

    Write-Verbose "Importing $Name theme from $Path"
    $Theme = Import-Metadata $Path -ErrorAction Stop

    # Cast the ConsoleColors here
    if($Theme['ConsoleColors']) {
        $Theme['ConsoleColors'] = $Theme['ConsoleColors'].ForEach([RgbColor])
    }
    # Convert colors to escape sequences for PSReadline.Colors
    if ($Colors = $Theme.PSReadline.Colors) {
        foreach ($color in @($Colors.Keys)) {
            # If it doesn't start with ESC
            if ($Colors[$color] -notmatch "^$([char]27)") {
                try {
                    # Use the RGBColor
                    $Colors[$color] = ([RgbColor]$Colors[$color]).ToVtEscapeSequence() <# |
                        Add-Member -MemberType NoteProperty -Name Color -Value ([RgbColor]$Colors[$color]) -PassThru #>

                } catch {
                    Write-Warning "Skipped 'PSReadLine.$color', because '$($Colors[$color])' is neither a color nor an escape sequence"
                    $null = $Colors.Remove($color)
                }
            }
        }
        $Theme['PSReadline']['Colors'] = $Colors
    }

    $Theme
}
#EndRegion '.\Private\ImportTheme.ps1'
#Region '.\Private\ShowPreview.ps1' 0
function ShowPreview {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, Position = 0)]
        $Palette,

        [Parameter(Position = 1)]
        $Syntax,

        $Name,

        [Switch]$Tiny,

        [Switch]$MoreText,

        [Switch]$NoCodeSample
    )
    if($null -eq $Syntax) {
        $Syntax  = Get-PSReadLineOption | Select-Object -Property @(
                @{Name = "ContinuationPrompt"; Expression = { $_.ContinuationPromptColor }}
                @{Name = "Parameter"; Expression = { $_.ParameterColor }}
                @{Name = "Member"; Expression = { $_.MemberColor }}
                @{Name = "Command"; Expression = { $_.CommandColor }}
                @{Name = "Operator"; Expression = { $_.OperatorColor }}
                @{Name = "Emphasis"; Expression = { $_.EmphasisColor }}
                @{Name = "Selection"; Expression = { $_.SelectionColor }}
                @{Name = "Variable"; Expression = { $_.VariableColor }}
                @{Name = "Type"; Expression = { $_.TypeColor }}
                @{Name = "Keyword"; Expression = { $_.KeywordColor }}
                @{Name = "String"; Expression = { $_.StringColor }}
                @{Name = "Error"; Expression = { $_.ErrorColor }}
                @{Name = "Number"; Expression = { $_.NumberColor }}
                @{Name = "DefaultToken"; Expression = { $_.DefaultTokenColor }}
                @{Name = "Comment"; Expression = { $_.CommentColor }}
            )
    }
    $e = [char]27

    -join $(
        "$($Name)`n"
        if($Palette) {
            if($Palette.Count -lt 16) {
                throw "Invalid theme: missing ConsoleColors (there should be 16, but are only $($Palette.Count))"
            }

            if (!$Tiny) {
                $ansi = 30
                $bold = $false
                " Black Red Green Yellow Blue Magenta Cyan White Gray Dark Gray `n"
                " 49m 40m 41m 42m 43m 44m 45m 46m 47m 100m 107m `n"
                " 39m"

                foreach($fg in @($null) + $Palette[0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]) {
                    if($fg) {
                        "$(if($bold){"1;"}else{" "})$($ansi)m"
                        $ansi += $bold
                        $bold = !$bold
                    }
                    foreach($bg in @($null) + $Palette[0, 4, 2, 6, 1, 5, 3, 15, 7, 8]) {
                        $(if($null -ne $bg) { $bg.ToVtEscapeSequence($true) })
                        $(if($null -ne $fg) { $fg.ToVtEscapeSequence() })
                        " gYw $([char]27)[0m "
                    }
                    "`n"
                }
                "`n"
            }

            0..7 | ForEach-Object {
                $Dark = $Palette[$_]
                $Lite = $Palette[($_ + 8)]

                New-Text (" $([ConsoleColor]$_)".PadRight(12) + " $Dark ") -Fore (Get-Complement $Dark -Force) -Back $Dark -LeaveColor
                New-Text (" $([ConsoleColor]$_+8)".PadRight(9) + " $Lite ") -Fore (Get-Complement $Lite -Force) -Back $Lite
                "`n"
            }
        }

        if($Syntax -and !$NoCodeSample) {
            "$e[8A$e[45G$($Syntax.Keyword)function $($Syntax.DefaultToken)Test-Syntax $($Syntax.DefaultToken){"
            "$e[B$e[45G $($Syntax.Comment)# Demo Syntax Highlighting"
            "$e[B$e[45G $($Syntax.DefaultToken)[$($Syntax.Type)CmdletBinding$($Syntax.DefaultToken)()]"
            "$e[B$e[45G $($Syntax.Keyword)param$($Syntax.DefaultToken)( [$($Syntax.Type)IO.FileInfo$($Syntax.DefaultToken)]$($Syntax.Variable)`$Path $($Syntax.DefaultToken))"
            "$e[B"
            "$e[B$e[45G $($Syntax.Command)Write-Verbose $($Syntax.String)`"Testing in $($Syntax.Variable)`$($($Syntax.Command)Split-Path $($Syntax.Variable)`$PSScriptRoot $($Syntax.Parameter)-Leaf$($Syntax.Variable))$($Syntax.String)`" $($Syntax.Parameter)-Verbose"
            "$e[B$e[45G $($Syntax.Variable)`$Env:PSModulePath $($Syntax.Operator)-split $($Syntax.String)';' $($Syntax.Operator)-notcontains $($Syntax.Variable)`$Path$($Syntax.DefaultToken).$($Syntax.Member)FullName"
            "$e[B$e[45G$($Syntax.DefaultToken)}$e[39m$e[B"
        }

        if($Palette -and $MoreText) {
            0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 | ForEach-Object {
                $Color = $Palette[$_]
                New-Text " This is a test " -Back Black -Fore $Color -LeaveColor
                New-Text " showing more " -Back DarkGray -Fore $Color -LeaveColor
                New-Text " sample text on " -Back Gray -Fore $Color -LeaveColor
                New-Text " common backgrounds: " -Back White -Fore $Color -LeaveColor
                New-Text " $Color $([ConsoleColor]$_) ".PadRight(22) -Fore $(if($_ -le 8 -and $_ -ne 7){"White"}else{"Black"}) -Back $Color
                "`n"
            }
            "`n"
        }
    )
}
#EndRegion '.\Private\ShowPreview.ps1'
#Region '.\Public\Convert-ConsolePalette.ps1' 0
function Convert-ConsolePalette {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact="Medium", DefaultParameterSetName = "Dark")]
    param(
        # How much to shift the dark colors. Positive values make the colors brighter, negative values make them darker
        [Parameter(Mandatory, ParameterSetName = "Dark", Position = 0)]
        [int]$DarkShift,

        # How much to shift the bright colors. Positive values make the colors brighter, negative values make them darker
        [Parameter(Mandatory, ParameterSetName = "Bright")]
        [int]$BrightShift,

        # By default, the colors are modified in-place. If copy is set:
        # - the dark colors start with the value of the bright colors
        # - the light colors start at the value of the dark colors
        [switch]$Copy
    )
    $Palette = Get-ConsolePalette
    for($i=0;$i -lt 8; $i++){
        $Dark  = $Palette[$i].ToHunterLab()
        $Light = $Palette[$i+8].ToHunterLab()

        if ($BrightShift) {
            if ($Copy) {
                $Light = $Dark.ToHunterLab()
            }
            $Light.L  += $BrightShift
        }

        if ($DarkShift) {
            if ($Copy) {
                $Dark  = $Light.ToHunterLab()
            }
            $Dark.L   += $DarkShift
        }

        $Palette[$i]   = [PoshCode.Pansies.RgbColor]$Dark.ToRgb()
        $Palette[$i+8] = [PoshCode.Pansies.RgbColor]$Light.ToRgb()
    }

    ShowPreview $Palette -Tiny

    if($PSCmdlet.ShouldProcess("Save Theme")) {
        Set-ConsolePalette $Palette
    }
}
#EndRegion '.\Public\Convert-ConsolePalette.ps1'
#Region '.\Public\ConvertFrom-iTermColors.ps1' 0
function ConvertFrom-iTermColors {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding(SupportsShouldProcess)]
    param(
        # The name of (or full path to) an XML PList itermcolors scheme
        # If you provide just a name, will search for an .itermcolors file in the current folder and the theme
        [Alias("PSPath", "Name")]
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$Theme,

        [switch]$Force,

        [switch]$Update,

        [switch]$Passthru,

        [ValidateSet("User", "Machine")]
        [string]$Scope = "User"
    )

    begin {
        # In Windows Color Table order
        $PListColorNames = @(
            "Ansi 0 Color"   # DARK_BLACK
            "Ansi 4 Color"   # DARK_BLUE
            "Ansi 2 Color"   # DARK_GREEN
            "Ansi 6 Color"   # DARK_CYAN
            "Ansi 1 Color"   # DARK_RED
            "Ansi 5 Color"   # DARK_MAGENTA
            "Ansi 3 Color"   # DARK_YELLOW
            "Ansi 7 Color"   # DARK_WHITE
            "Ansi 8 Color"   # BRIGHT_BLACK
            "Ansi 12 Color"  # BRIGHT_BLUE
            "Ansi 10 Color"  # BRIGHT_GREEN
            "Ansi 14 Color"  # BRIGHT_CYAN
            "Ansi 9 Color"   # BRIGHT_RED
            "Ansi 13 Color"  # BRIGHT_MAGENTA
            "Ansi 11 Color"  # BRIGHT_YELLOW
            "Ansi 15 Color"  # BRIGHT_WHITE
        )
    }

    process {
        if (!(Test-Path $Theme)) {
            throw [System.IO.FileNotFoundException]::new("Palette file not found", $Theme)
        }

        if (!($pList = Import-PList $Theme)) {
            return
        }

        $ThemeOutput = @{
            Name = [IO.Path]::GetFileNameWithoutExtension($Theme)
            ConsoleColors = @(
                foreach($color in $PListColorNames) {
                    if(!$pList[$color]) {
                        Wait-Debugger
                        Write-Warning "Missing color $color"
                        $null
                    } else {
                        ConvertToCssColor $pList[$color]
                    }
                }
            )
        }
        if ($pList.ContainsKey("Foreground Color") -and $pList.ContainsKey("Background Color")) {
            $ThemeOutput.ConsoleForeground = ConvertToCssColor $pList["Foreground Color"]
            $ThemeOutput.ConsoleBackground = ConvertToCssColor $pList["Background Color"]
        }
        if ($pList.ContainsKey("Bold Color")) {
            if (!$ThemeOutput['PSReadLine']) {
                $ThemeOutput['PSReadLine'] = @{}
            }
            if (!$ThemeOutput['PSReadLine']['Colors']) {
                $ThemeOutput['PSReadLine']['Colors'] = @{}
            }
            $ThemeOutput['PSReadLine']['Colors']['Emphasis'] = ConvertToCssColor $pList["Bold Color"]
        }
        if ($pList.ContainsKey("Selection Color")) {
            if (!$ThemeOutput['PSReadLine']) {
                $ThemeOutput['PSReadLine'] = @{}
            }
            if (!$ThemeOutput['PSReadLine']['Colors']) {
                $ThemeOutput['PSReadLine']['Colors'] = @{}
            }
            $ThemeOutput['PSReadLine']['Colors']['Selection'] = ConvertToCssColor $pList["Selection Color"]
        }
        if(!($Name = $pList["name"])) {
            $Name = ([IO.Path]::GetFileNameWithoutExtension($Theme))
        }

        $ThemeOutput | ExportTheme -Name $Name -Passthru:$Passthru -Scope:$Scope -Force:$Force -Update:$Update
    }
}
#EndRegion '.\Public\ConvertFrom-iTermColors.ps1'
#Region '.\Public\ConvertFrom-VSCodeTheme.ps1' 0
function ConvertFrom-VSCodeTheme {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding(SupportsShouldProcess)]
    param(
        # The name of (or full path to) a vscode json theme
        # E.g. 'Dark+' or 'Monokai'
        [Alias("PSPath", "Name")]
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$Theme,

        # Overwrite any existing theme
        [switch]$Force,

        [switch]$Update,

        # Output the theme after importing it
        [switch]$Passthru,

        [ValidateSet("User", "Machine")]
        [string]$Scope = "User"
    )
    $ThemeSource = FindVsCodeTheme $Theme -ErrorAction Stop

    Write-Verbose "Importing $($ThemeSource.Path)"
    if($PSCmdlet.ShouldProcess($ThemeSource.Path, "Convert to $($ThemeSource.Name).theme.psd1")) {
        # Load the theme file and split the output into colors and tokencolors
        if($ThemeSource.Path.endswith(".json")) {
            $colors, $tokens = (ImportJsonIncludeLast $ThemeSource.Path).Where( {!$_.scope}, 'Split', 2)
        } else {
            $colors, $tokens = (Import-PList $ThemeSource.Path).settings.Where( {!$_.scope}, 'Split', 2)
            $colors = $colors.settings
        }

        # these should come from the colors, rather than the token scopes
        $DefaultTokenColor = GetColorProperty $colors 'editor.foreground', 'foreground', 'terminal.foreground'
        $SelectionColor = GetColorProperty $colors 'editor.selectionBackground', 'editor.selectionHighlightBackground', 'selection'
        $ErrorColor = @(@(GetColorProperty $colors 'errorForeground', 'editorError.foreground') + @(GetColorScopeForeground $tokens 'invalid'))[0]

        # I'm going to need some help figuring out what the best mappings are
        $CommandColor = GetColorScopeForeground $tokens 'support.function'
        $CommentColor = GetColorScopeForeground $tokens 'comment'
        $ContinuationPromptColor = GetColorScopeForeground $tokens 'constant.character'
        $EmphasisColor = GetColorScopeForeground $tokens 'markup.bold','markup.italic','emphasis','strong','constant.other.color', 'markup.heading'
        $KeywordColor = GetColorScopeForeground $tokens '^keyword.control$', '^keyword$', 'keyword.control', 'keyword'
        $MemberColor = GetColorScopeForeground $tokens 'variable.other.object.property', 'member', 'type.property', 'support.function.any-method', 'entity.name.function'
        $NumberColor = GetColorScopeForeground $tokens 'constant.numeric'
        $OperatorColor = GetColorScopeForeground $tokens 'keyword.operator$', 'keyword'
        $ParameterColor = GetColorScopeForeground $tokens 'parameter'
        $StringColor = GetColorScopeForeground $tokens '^string$'
        $TypeColor = GetColorScopeForeground $tokens '^storage.type$','^support.class$', '^entity.name.type.class$', '^entity.name.type$'
        $VariableColor = GetColorScopeForeground $tokens '^variable$', '^entity.name.variable$', '^variable.other$'


        $ThemeOutput = [Ordered]@{
            Name       = $ThemeSource.Name
            PSReadLine = @{
                Colors = @{
                    Command =            $CommandColor
                    Comment =            $CommentColor
                    ContinuationPrompt = $ContinuationPromptColor
                    DefaultToken =       $DefaultTokenColor
                    Emphasis =           $EmphasisColor
                    Error =              $ErrorColor
                    Keyword =            $KeywordColor
                    Member =             $MemberColor
                    Number =             $NumberColor
                    Operator =           $OperatorColor
                    Parameter =          $ParameterColor
                    Selection =          $SelectionColor
                    String =             $StringColor
                    Type =               $TypeColor
                    Variable =           $VariableColor
                }
            }
        }

        # If the VSCode Theme has terminal colors, export those
        if ($colors.'terminal.ansiBrightYellow') {
            Write-Verbose "Exporting ConsoleColors"
            $ThemeOutput['ConsoleColors'] = @(
                    GetColorProperty $colors "terminal.ansiBlack"
                    GetColorProperty $colors "terminal.ansiRed"
                    GetColorProperty $colors "terminal.ansiGreen"
                    GetColorProperty $colors "terminal.ansiYellow"
                    GetColorProperty $colors "terminal.ansiBlue"
                    GetColorProperty $colors "terminal.ansiMagenta"
                    GetColorProperty $colors "terminal.ansiCyan"
                    GetColorProperty $colors "terminal.ansiWhite"
                    GetColorProperty $colors "terminal.ansiBrightBlack"
                    GetColorProperty $colors "terminal.ansiBrightRed"
                    GetColorProperty $colors "terminal.ansiBrightGreen"
                    GetColorProperty $colors "terminal.ansiBrightYellow"
                    GetColorProperty $colors "terminal.ansiBrightBlue"
                    GetColorProperty $colors "terminal.ansiBrightMagenta"
                    GetColorProperty $colors "terminal.ansiBrightCyan"
                    GetColorProperty $colors "terminal.ansiBrightWhite"
                )
            if ($colors."terminal.background") {
                $ThemeOutput['ConsoleBackground'] = GetColorProperty $colors "terminal.background"
            }
            if ($colors."terminal.foreground") {
                $ThemeOutput['ConsoleForeground'] = GetColorProperty $colors "terminal.foreground"
            }
        }

        if (GetColorProperty $colors 'editorWarning.foreground') {
            $ThemeOutput['Host'] = @{
                'PrivateData' = @{
                    WarningForegroundColor  = GetColorProperty $colors 'editorWarning.foreground'
                    ErrorForegroundColor = GetColorProperty $Colors 'editorError.foreground'
                    VerboseForegroundColor = GetColorProperty $Colors 'editorInfo.foreground'
                    ProgressForegroundColor = GetColorProperty $Colors 'notifications.foreground'
                    ProgressBackgroundColor = GetColorProperty $Colors 'notifications.background'
                }
            }
        }

        if ($DebugPreference -in "Continue", "Inquire") {
            $global:colors = $colors
            $global:tokens = $tokens
            $global:Theme = $ThemeOutput
            ${function:global:Get-VSColorScope} = ${function:GetColorScopeForeground}
            ${function:global:Get-VSColor} = ${function:GetColorProperty}
            Write-Debug "For debugging, `$Theme, `$Colors, `$Tokens were copied to global variables, and Get-VSColor and Get-VSColorScope exported."
        }

        if ($ThemeOutput.PSReadLine.Colors.Values -contains $null) {
            Write-Warning "Some PSReadLine color values not set in '$($ThemeSource.Path)'"
        }

        $ThemeOutput | ExportTheme -Name $ThemeSource.Name -Passthru:$Passthru -Scope:$Scope -Force:$Force -Update:$Update
    }
}
#EndRegion '.\Public\ConvertFrom-VSCodeTheme.ps1'
#Region '.\Public\Export-PList.ps1' 0
function Export-PList {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding(SupportsShouldProcess)]
    param(
        # The object(s) to convert
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias("io")]
        [Object[]]$InputObject,

        # The path to an XML or binary plist file (e.g. a .tmTheme file)
        [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0)]
        [Alias("PSPath")]
        [string]$Path,

        [switch]$Binary
    )

    begin {
        $Output = @()
    }
    process {
        $Output += $InputObject
    }
    end {
        $Parent = if($Parent = Split-Path $Path) {
            if(!(Test-Path -LiteralPath $Parent -PathType Container)) {
                New-Item -ItemType Directory -Path $Parent -Force
            }
            Convert-Path $Parent
        } else {
            Convert-Path (Get-Location -PSProvider FileSystem)
        }
        $Path = Join-Path $Parent -ChildPath (Split-Path $Path -Leaf)

        if($PSCmdlet.ShouldProcess($Path,"Export InputObject as PList $(if($Binary){'Binary'}else{'Xml'})") ) {
            if ($Binary) {
                [PoshCode.Pansies.Parsers.Plist]::WriteBinary($Output, $Path)
            } else {
                [PoshCode.Pansies.Parsers.Plist]::WriteXml($Output, $Path)
            }
        }
    }
}
#EndRegion '.\Public\Export-PList.ps1'
#Region '.\Public\Export-Theme.ps1' 0
function Export-Theme {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding()]
    param(
        # The name of the theme to export the current settings to
        [Parameter(Position = 0, Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [string]$Update,

        [switch]$Force,

        [switch]$Passthru,

        [ValidateSet("User", "Machine")]
        [string]$Scope = "User"
    )
    end {
        if($Update) {
            $Theme = ImportTheme $Name -ErrorAction SilentlyContinue
        }
        if(!$Theme) {
            $Theme = @{}
        }
        $Theme = $Theme | Update-Object @{
            ConsoleColors = (Get-ConsolePalette).ForEach({$_.ToString()})
        }

        $Theme = $Theme | Update-Object @{
            PSReadLine = @{
                # TODO: convert escape sequences to RGBColor values (e.g. 30's and 90's to colors, including 38;2;R;G;B;)
                Colors = ConvertFrom-Metadata (Get-PSReadLineOption | Select-Object *Color | ConvertTo-Metadata -AsHashtable)
            }
        }

        if($Host.Name -eq "ConsoleHost") {

            $Theme = $Theme | Update-Object @{
                Host = @{
                    PrivateData = ConvertFrom-Metadata ($Host.PrivateData | Select-Object * | ConvertTo-Metadata -AsHashtable)
                }
            }
        }

        $Theme | ExportTheme -Name $Name -Passthru:$Passthru -Scope:$Scope -Force:$Force
    }
}
#EndRegion '.\Public\Export-Theme.ps1'
#Region '.\Public\Get-Complement.ps1' 0
function Get-Complement {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding()]
    [OutputType([PoshCode.Pansies.RgbColor])]
    param(
        # The source color to calculate the complement of
        [Parameter(ValueFromPipeline, Mandatory)]
        [PoshCode.Pansies.RgbColor]$Color,

        # Force the luminance to have "enough" contrast
        [switch]$ForceContrast,

        # Assume there are only 16 colors
        [switch]$ConsoleColor,

        # If set, output the input $Color before the complement
        [switch]$Passthru
    )
    process {
        if($ConsoleColor) {
            $Color = $Color.ConsoleColor
        }

        if($Passthru) { $Color }

        if($ConsoleColor) {
            if($Color.ToHunterLab().L -lt 50) {
                [PoshCode.Pansies.RgbColor][ConsoleColor]::White
            } else {
                [PoshCode.Pansies.RgbColor][ConsoleColor]::Black
            }
        } else {
            $Hsl = $Color.ToHsl()
            $Hsl.H = ($Hsl.H + 180) % 360

            if($ForceContrast) {
                $Lab = $Hsl.ToHunterLab()
                $Source = $Color.ToHunterLab()
                $Lab.L = ($Source.L + 50) % 100
                [PoshCode.Pansies.RgbColor]$Lab
            } else {
                [PoshCode.Pansies.RgbColor]$Hsl
            }
        }
    }
}
#EndRegion '.\Public\Get-Complement.ps1'
#Region '.\Public\Get-Gradient.ps1' 0
function Get-Gradient {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding()]
    [OutputType([PoshCode.Pansies.RgbColor[][]],[PoshCode.Pansies.RgbColor[]])]
    param(
        [Parameter(Mandatory, Position=0)]
        [PoshCode.Pansies.RgbColor]$StartColor,

        [Parameter(Mandatory, Position=1)]
        [PoshCode.Pansies.RgbColor]$EndColor,

        [Parameter(Position=2)]
        [Alias("Length","Count","Steps")]
        [int]$Width = $Host.UI.RawUI.WindowSize.Width,

        [Parameter(Position=3)]
        [int]$Height = 1,
        [ValidateSet("CMY","CMYK","LAB","LCH","LUV","HunterLAB","HSL","HSV","HSB","RGB","XYZ","YXY")]
        $ColorSpace = "HunterLab",

        [switch]$Reverse,

        [switch]$Flatten
    )

    $Height = [Math]::Max(1, $Height)
    $Width = [Math]::Max(1, $Width)
    $Colors = new-object PoshCode.Pansies.RgbColor[][] $Height, $Width

    # Simple pythagorean distance
    $Size = [Math]::Sqrt(($Height - 1) * ($Height - 1) + ($Width - 1) * ($Width - 1))

    $Left = $StartColor."To$ColorSpace"()
    $Right = $EndColor."To$ColorSpace"()
    $StepSize = New-Object "${ColorSpace}Color" -Property @{
        Ordinals = $(
            foreach ($i in 0..($Left.Ordinals.Count-1)) {
                ($Right.Ordinals[$i] - $Left.Ordinals[$i]) / $Size
            }
        )
    }
    Write-Verbose "Size: $('{0:N2}' -f $Size) ($Width x $Height) ($($Colors.Length) x $($Colors[0].Length))"
    Write-Verbose "Diff: {$StepSize}"
    Write-Verbose "From: {$Left} $($StartColor.Ordinals)"
    Write-Verbose "To: {$Right} $($EndColor.Ordinals)"
    # For colors based on hue rotation, the math is slightly more complex:
    [bool]$RotatingColor = $StepSize | Get-Member H

    if($RotatingColor) {
        [Bool]$Change = [Math]::Abs($StepSize.H) -ge 180 / $Size
        if ($Reverse) { $Change = !$Change }
        if ($Change) {
            $StepSize.H = if ($StepSize.H -gt 0) {
                $StepSize.H - 360 / $Size
            } else {
                $StepSize.H + 360 / $Size
            }
        }
        $Ceiling = 360
    }

    for ($Line = 1; $Line -le $Height; $Line++) {
        for ($Column = 1; $Column -le $Width; $Column++) {
            $D = [Math]::Sqrt(($Line - 1) * ($Line - 1) + ($Column - 1) * ($Column - 1))

            $StepColor = New-Object "${ColorSpace}Color" -Property @{
                Ordinals = $(
                    foreach ($i in 0..$Left.Ordinals.Count) {
                        $Left.Ordinals[$i] + $StepSize.Ordinals[$i] * $D
                    }
                )
            }

            # For colors based on hue rotation, the math is slightly more complex:
            if($RotatingColor) {
                if($StepColor.H -lt 0) {
                    $StepColor.H += 360
                }
                $StepColor.H %= $Ceiling
            }
            $Ordinals1 = $StepColor.Ordinals

            $Colors[$Line - 1][$Column - 1] = $StepColor.ToRgb()
            $Ordinals2 = $Colors[$Line - 1][$Column - 1].Ordinals
            Write-Debug ("Step ${Line},${Column}: {0:N2}, {1:N2}, {2:N2} => {3:N2}, {4:N2}, {5:N2}" -f $Ordinals1[0], $Ordinals1[1], $Ordinals1[2], $Ordinals2[0], $Ordinals2[1], $Ordinals2[2] )
        }
    }

    if ($Flatten) {
        $Colors.GetEnumerator().GetEnumerator()
    } else {
        ,$Colors
    }

}
#EndRegion '.\Public\Get-Gradient.ps1'
#Region '.\Public\Get-Theme.ps1' 0
function Get-Theme {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding()]
    param(
        # The name of the theme(s) to show. Supports wildcards, and defaults to * everything.
        [string]$Name = "*",

        # If set, only returns themes that include ConsoleColor
        [switch]$ConsoleColors,

        # If set, only returns themes that include PSReadline Colors
        [switch]$PSReadline
    )

    $Name = $Name -replace "((\.theme)?\.psd1)?$" -replace '$', ".theme.psd1"

    foreach($Theme in Join-Path $(
            Get-ConfigurationPath -Scope User -SkipCreatingFolder
            Get-ConfigurationPath -Scope Machine -SkipCreatingFolder
        ) -ChildPath $Name -Resolve -ErrorAction Ignore ) {
            if ($ConsoleColors -or $PSReadline) {
                $ThemeData = Import-Metadata -Path $Theme
                if($ConsoleColors -and !$ThemeData.ConsoleColors) {
                    continue
                }
                if($PSReadline -and !$ThemeData.PSReadline) {
                    continue
                }
            }
            $Name = if($ThemeData.Name) {
                $ThemeData.Name
            } else {
                [IO.Path]::GetFileName($Theme) -replace "\.theme\.psd1$"
            }
            $Name | Add-Member NoteProperty PSPath $Theme -PassThru |
                    Add-Member NoteProperty Name $Name -PassThru
    }
}
#EndRegion '.\Public\Get-Theme.ps1'
#Region '.\Public\Import-PList.ps1' 0
function Import-PList {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding()]
    param(
        # The path to an XML or binary plist file (e.g. a .tmTheme file)
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)]
        [Alias("PSPath")]
        [string]$Path
    )

    process {
        $Path = Convert-Path $Path

        [PoshCode.Pansies.Parsers.Plist]::ReadPlist($Path)
    }
}
#EndRegion '.\Public\Import-PList.ps1'
#Region '.\Public\Import-Theme.ps1' 0
function Import-Theme {
    # .EXTERNALHELP Pansies-help.xml
    [CmdletBinding()]
    param(
        # A theme to import (can be the name of an installed PANSIES theme, or the full path to a psd1 file)
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string]$Name,

        # By default, imported themes will update the default console colors (if they define console colors)
        # If SkipDefault is set, Import-Theme will leave the default Console Colors alone
        [switch]$SkipDefault
    )
    $Theme = ImportTheme $Name

    if ($ConsoleColors = $Theme.ConsoleColors) {
        Write-Verbose "Setting the console palette"
        Set-ConsolePalette -Colors $ConsoleColors -Default:(!$SkipDefault)
    }

    if ($PSReadLine = $Theme.PSReadLine) {
        Write-Verbose "Applying PSReadLineOptions"
        Set-PSReadLineOption @PSReadLine
    }

    if ($HostSettings = $Theme.Host) {
        function Update-Host {
            param($HostObject, $Settings)
            foreach ($key in $Settings.Keys) {
                try {
                    Write-Verbose "Applying Host settings: $key"
                    if ($HostObject.$key -is [ConsoleColor]) {
                        Write-Verbose "Converting '$($Settings.$Key)' to ConsoleColor"
                        $HostObject.$key = [ConsoleColor]([RgbColor]::ConsolePalette.FindClosestColorIndex([RgbColor]::new($Settings.$Key)))
                    } elseif ($Settings.$key -is [hashtable]) {
                        Update-Host $HostObject.$key $Settings.$Key
                    } else {
                        $HostObject.$key = $Settings.$Key
                    }
                } catch {
                    Write-Warning "Failed to apply Host '$key' = '$($Settings.$Key)' (it should have been a $($HostObject.$key.GetType().FullName)"
                }
            }
        }
        Update-Host $Host $HostSettings
    }
}
#EndRegion '.\Public\Import-Theme.ps1'
#Region '.\Public\Show-Theme.ps1' 0
function Show-Theme {
    # .EXTERNALHELP Pansies-help.xml
    [OutputType([string])]
    [CmdletBinding(DefaultParameterSetName="CurrentTheme")]
    param(
        [Alias("Theme","PSPath")]
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$Name,

        [Switch]$Tiny,

        [Switch]$MoreText,

        [Switch]$NoCodeSample
    )
    process {
        if(!$Name) {
            $Palette = Get-ConsolePalette
            ShowPreview $Palette @PSBoundParameters
        } else {
            foreach($Theme in Get-Theme $Name) {
                $Theme = ImportTheme $Theme.PSPath | Add-Member -Type NoteProperty -Name Name -Value $Theme.Name -Passthru -Force
                $Palette = $Theme.ConsoleColors
                $Syntax  = [PSCustomObject]$Theme.PSReadLine.Colors

                $PSBoundParameters['Name'] = $Theme.Name
                ShowPreview $Palette $Syntax @PSBoundParameters
            }
        }
    }
}
#EndRegion '.\Public\Show-Theme.ps1'

# SIG # Begin signature block
# MIIXzgYJKoZIhvcNAQcCoIIXvzCCF7sCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUPKGvVrNUPtSnR0rId1xHdDdS
# N1mgghMBMIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B
# AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG
# A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh
# d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg
# Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV
# UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu
# dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q
# WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC
# i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4
# ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3
# +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI
# fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd
# BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG
# CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro
# YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV
# HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y
# MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf
# plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y
# 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq
# IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3
# DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh
# dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD
# QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE
# BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT
# eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow
# mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0
# jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu
# ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh
# d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz
# C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB
# o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO
# BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw
# Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90
# cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx
# oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy
# bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV
# HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa
# 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH
# bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73
# BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR
# EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW
# yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu
# e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw
# ggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9v
# dCBDQTAeFw0xMzEwMjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYT
# AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy
# dC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNp
# Z25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4R
# r2d3B9MLMUkZz9D7RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrw
# nIal2CWsDnkoOn7p0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnC
# wlLyFGeKiUXULaGj6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8
# y5Kh5TsxHM/q8grkV7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM
# 0SAlI+sIZD5SlsHyDxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6f
# pjOp/RnfJZPRAgMBAAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1Ud
# DwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGsw
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcw
# AoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElE
# Um9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNl
# cnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDov
# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBP
# BgNVHSAESDBGMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93
# d3cuZGlnaWNlcnQuY29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoK
# o6XqcQPAYPkt9mV1DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8w
# DQYJKoZIhvcNAQELBQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+
# C2D9wz0PxK+L/e8q3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119E
# efM2FAaK95xGTlz/kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR
# 4pwUR6F6aGivm6dcIFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4v
# cn4c10lFluhZHen6dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwH
# gfqL2vmCSfdibqFT+hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggUwMIIEGKADAgEC
# AhALDZkX0sdOvwJhwzQTbV+7MA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25p
# bmcgQ0EwHhcNMTgwNzEyMDAwMDAwWhcNMTkwNzE2MTIwMDAwWjBtMQswCQYDVQQG
# EwJVUzERMA8GA1UECBMITmV3IFlvcmsxFzAVBgNVBAcTDldlc3QgSGVucmlldHRh
# MRgwFgYDVQQKEw9Kb2VsIEguIEJlbm5ldHQxGDAWBgNVBAMTD0pvZWwgSC4gQmVu
# bmV0dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMJb3Cf3n+/pFJiO
# hQqN5m54FpyIktMRWe5VyF8465BnAtzw3ivMyN+3k8IoXQhMxpCsY1TJbLyydNR2
# QzwEEtGfcTVnlAJdFFlBsgIdK43waaML5EG7tzNJKhHQDiN9bVhLPTXrit80eCTI
# RpOA7435oVG8erDpxhJUK364myUrmSyF9SbUX7uE09CJJgtB7vqetl4G+1j+iFDN
# Xi3bu1BFMWJp+TtICM+Zc5Wb+ZaYAE6V8t5GCyH1nlAI3cPjqVm8y5NoynZTfOhV
# bHiV0QI2K5WrBBboR0q6nd4cy6NJ8u5axi6CdUhnDMH20NN2I0v+2MBkgLAzxPrX
# kjnaEGECAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZl
# dQ5YMB0GA1UdDgQWBBTiwur/NVanABEKwjZDB3g6SZN1mTAOBgNVHQ8BAf8EBAMC
# B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDov
# L2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGG
# L2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3Js
# MEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYw
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcw
# AoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3Vy
# ZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL
# BQADggEBADNNHuRAdX0ddONqaUf3H3pwa1K016C02P90xDIyMvw+hiUb4Z/xewnY
# jyplspD0NQB9ca2pnNIy1KwjJryRgq8gl3epSiWTbViVn6VDK2h0JXm54H6hczQ8
# sEshCW53znNVUUUfxGsVM9kMcwITHYftciW0J+SsGcfuuAIuF1g47KQXKWOMcUQl
# yrP5t0ywotTVcg/1HWAPFE0V0sFy+Or4n81+BWXOLaCXIeeryLYncAVUBT1DI6lk
# peRUj/99kkn+hz1q4hHTtfNpMTOApP64EEFGKICKkJdvhs1PjtGa+QdAkhcInTxk
# t/hIJPUb1nO4CsKp1gaVsRkkbcStJ2kxggQ3MIIEMwIBATCBhjByMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBT
# aWduaW5nIENBAhALDZkX0sdOvwJhwzQTbV+7MAkGBSsOAwIaBQCgeDAYBgorBgEE
# AYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG
# CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBReVLGy
# 00oWCmOcdPtivZ35lhKEajANBgkqhkiG9w0BAQEFAASCAQCLalbUa6a17aIuHXSy
# c2aO8tvZJtlwzelHuQTb08n/+0eOOboUFZ+2R7b1iXuWwjnpdmvD8YWKqjLBzap1
# bYeZ8dCUdFhERTE0TScW+CThqEC/cZ3vR36bqH3o9+plq4El/nWxaixsVFIR/lgj
# ZefARmtc/RmiS4Uv1MW1L0lQpg3CFrQLCoBgCfpA2jRBE/FHuyM/XHMCQ4p5rIZJ
# voShbh3+gJ/qEn4/jpUlsuhxaZMHaIv2YQUXNxAYCpUhgShk6Fa6MAs+5CfXzaiO
# +ZPFlFhGdpf6TGCwqtue2zeMhXGvUd9V0we3tmLZryUaHHRR81m1wMS/teOjZEol
# R8uDoYICCzCCAgcGCSqGSIb3DQEJBjGCAfgwggH0AgEBMHIwXjELMAkGA1UEBhMC
# VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTAwLgYDVQQDEydTeW1h
# bnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIENBIC0gRzICEA7P9DjI/r81bgTY
# apgbGlAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJ
# KoZIhvcNAQkFMQ8XDTE4MDkwNjA2MjU1OFowIwYJKoZIhvcNAQkEMRYEFKvqH8Rw
# 3Q7mxsPx09/wFBf7vlg2MA0GCSqGSIb3DQEBAQUABIIBAFQcrgVvtP/HGzKT5Ov7
# ZnersbX0Va0Ljp9xeLSOFTbiUdUukPJvvpDvtvP4G+EpQv9rIB1SOixkh9+tEh/M
# YUQIUxq15q8Dgpout4/updSBwSZuagcCelrqaLPwiLDua4oJEq4/UpnEndOCLC1m
# HEiLoXmUDWODt3DI9VB1iGsJd6ZeMmFoj++/veRXF2wuD8K1iQE4i3bt03rFfI1F
# Z8NSgE0vq/RsqUJ0gd5tWkSsONDbnFAUVzjT8KaQY1b+s2QXuXRbSYUzwUu69f5l
# aNK8mLMNTMu8AcPF7aoocRCL35Yx2v5aRdeeHXLAh2BQ/XgGqi47kt+1cUxXeqdb
# haI=
# SIG # End signature block