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 ) $Name = $Name -replace "((\.theme)?\.psd1)?$" -replace '$', ".theme.psd1" $Path = if (!(Test-Path -LiteralPath $Name)) { Get-Theme $Name | Select-Object -First 1 -ExpandProperty PSPath } else { Convert-Path $Name } 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 '.\Public\ConvertFrom-iTermColors.ps1' 0 function ConvertFrom-iTermColors { <# .SYNOPSIS Convert a .itermcolors XML file into a partial theme .DESCRIPTION Generate a PANSIES PowerShell theme from iTermColors. For a collection of iTermColors files you can use, visit https:#ithub.com/mbadolato/iTerm2-Color-Schemes .EXAMPLE ConvertFrom-iTermColors Argonaut Will find the Argonaut.itermcolors file in the furrent directory (or in the module storage path) #> [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 { <# .SYNOPSIS Convert a VSCode Theme file into a partial theme .DESCRIPTION Attempts to generate a PANSIES Powershell theme from a VSCode Theme. This feature is still experimental, and so far, includes theming: - The ConsoleColor palette (the base 16 ConsoleColors) from the `terminal.ansi*` colors in the VSCode theme - The PSReadLine colors (requires PSReadline 2.0.0 beta 2) from various named scopes - The PrivateData ConsoleColors (used for foreground of the output streams: Verbose, Error, Warning, Debug, Progress) from various named colors. NOTE: for now, even if everything works, there may be some colors for PSReadLine that aren't set, or that are set incorrectly (depending on the theme). If so, please let me know of themes you want to use or of colors that are wrong in the issues at https://GitHub.com/PoshCode/PANSIES/issues .EXAMPLE ConvertFrom-VSCodeTheme Dark+ Import-Theme Dark+ # This example shows how to convert the VSCode Dark+ default theme and then use it in your console. #> [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 { <# .SYNOPSIS Convert an object to an XML or Binary PList file. #> [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 { <# .SYNOPSIS List available PANSIES themes, optionally filtering #> [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 { <# .SYNOPSIS Convert an XML or Binary PList (property list) file to objects (arrays, string->object dictionaries, etc). #> [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 { <# .SYNOPSIS Import a theme file to style your console window and PowerShell session #> [CmdletBinding()] param( # A theme to import (can be the name of an installed PANSIES theme, or the full path to a psd1 file) [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 { [OutputType([string])] [CmdletBinding(DefaultParameterSetName="CurrentTheme")] param( [Alias("Theme","PSPath")] [Parameter(ValueFromPipelineByPropertyName)] [string]$Name, [Switch]$Tiny, [Switch]$MoreText, [Switch]$NoCodeSample ) process { $e = [char]27 foreach($Theme in Get-Theme $Name) { $Theme = ImportTheme $Theme.PSPath | Add-Member -Type NoteProperty -Name Name -Value $Theme.Name -Passthru -Force $Palette = $Theme.ConsoleColors -join $( "$($Theme.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]$_) $Dark ".PadRight(22) -Fore (Get-Complement $Dark -Force) -Back $Dark -LeaveColor New-Text " $([ConsoleColor]$_+8) $Lite ".PadRight(18) -Fore (Get-Complement $Lite -Force) -Back $Lite "`n" } } if(($Syntax = $Theme.PSReadLine.Colors) -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 '.\Public\Show-Theme.ps1' # SIG # Begin signature block # MIIXzgYJKoZIhvcNAQcCoIIXvzCCF7sCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUkPdNUPcIbqW6IiDM8Pdo518v # VnCgghMBMIID7jCCA1egAwIBAgIQfpPr+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 # CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBTMzM4P # 0LoIP298Onabo8qBoPhEdzANBgkqhkiG9w0BAQEFAASCAQAAtfpfUhtupURfTlkt # hRPAwwbvyZOiBy6G1UkiFRNsHvB0B++QpdZcMiPISvfD38xJF5Iq9OFCLNyizq5M # WDL8e6+ip4w6/httXEWRoVa9MxbrCrJOti5sHrEHvpvo6o4O+b+dzZn2Ckbm3Tht # XQ7ZDV691JPVvVTdJfoFg823krtXub+m+VAh95laaC7PtHejXwZGYA6M/xUvKkdS # QYmr3FnBDJqpoyfFHgGzk/sAQupqEG0TRzo2eVUzgB0sMyllfrHeypLtYLKXCg86 # HXHnnp2ET5ti/y0md+RDxNL7mwelgSZht28iK0ILAarK4d72aMebB1mreF+fGAgF # +Tl3oYICCzCCAgcGCSqGSIb3DQEJBjGCAfgwggH0AgEBMHIwXjELMAkGA1UEBhMC # VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTAwLgYDVQQDEydTeW1h # bnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIENBIC0gRzICEA7P9DjI/r81bgTY # apgbGlAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJ # KoZIhvcNAQkFMQ8XDTE4MDkwNDA2MDE1OVowIwYJKoZIhvcNAQkEMRYEFNgHZ81q # KoagBBhJQlsPyKyJnMmSMA0GCSqGSIb3DQEBAQUABIIBAI1vQ33F3uNKruXdFyuS # OcFwpCOS1LoSkBHHAEEb29+iGffyhMLNiUNN6rO3Kd64n0u1i12gxOEIyDUJ6VTv # 3uXu8oY4vmAueQsFEOtzVxfYOvYr9jIGRcbU7yi/V/1YGlr9VTqeJzMMz5CCEemC # G88jM+Uu/nJOApvDxWOPygU59VEHeYG1XzR2cjeYzW6VO+htB9UaiWjrmsU6KOh3 # 0ivf9MNZT4LcYNWP1p0Dm4FettnD/UKa3NjmjFxlviI8qqyP1iVS1zQTwfoMtGKS # 6i+AHE3u94nLSClNZ7ZaEGqKoYeecOROUXdowYKcFlYgpo54VnKWv9xWzE9z7GEx # 1x8= # SIG # End signature block |