Microsoft.Windows.Setting.Accessibility.psm1

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest

enum Ensure {
    Absent
    Present
}

enum TextSize {
    KeepCurrentValue
    Small
    Medium
    Large
    ExtraLarge
}

enum MagnificationValue {
    KeepCurrentValue
    None
    Low
    Medium
    High
}

enum PointerSize {
    KeepCurrentValue
    Normal
    Medium
    Large
    ExtraLarge
}

[Flags()] enum StickyKeysOptions {
    # https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-stickykeys
    None = 0x00000000 # 0
    Active = 0x00000001 # 1
    Available = 0x00000002 # 2
    HotkeyActive = 0x00000004 # 4
    ConfirmHotkey = 0x00000008 # 8
    HotkeySound = 0x00000010# 16
    VisualIndicator = 0x00000020 # 32
    AudibleFeedback = 0x00000040 # 64
    TriState = 0x00000080 # 128
    TwoKeysOff = 0x00000100 # 256
}

[Flags()] enum ToggleKeysOptions {
    # https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-togglekeys
    None = 0x00000000 # 0
    Active = 0x00000001 # 1
    Available = 0x00000002 # 2
    HotkeyActive = 0x00000004 # 4
    ConfirmHotkey = 0x00000008 # 8
    HotkeySound = 0x00000010# 16
    VisualIndicator = 0x00000020 # 32
}

[Flags()] enum FilterKeysOptions {
    # https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-filterkeys
    None = 0x00000000 # 0
    Active = 0x00000001 # 1
    Available = 0x00000002 # 2
    HotkeyActive = 0x00000004 # 4
    ConfirmHotkey = 0x00000008 # 8
    HotkeySound = 0x00000010# 16
    VisualIndicator = 0x00000020 # 32
    AudibleFeedback = 0x00000040 # 64
}

if ([string]::IsNullOrEmpty($env:TestRegistryPath)) {
    $global:AccessibilityRegistryPath = 'HKCU:\Software\Microsoft\Accessibility\'
    $global:MagnifierRegistryPath = 'HKCU:\Software\Microsoft\ScreenMagnifier\'
    $global:PointerRegistryPath = 'HKCU:\Control Panel\Cursors\'
    $global:ControlPanelAccessibilityRegistryPath = 'HKCU:\Control Panel\Accessibility\'
    $global:AudioRegistryPath = 'HKCU:\Software\Microsoft\Multimedia\Audio\'
    $global:PersonalizationRegistryPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\'
    $global:NTAccessibilityRegistryPath = 'HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Accessibility\'
    $global:CursorIndicatorAccessibilityRegistryPath = 'HKCU:\Software\Microsoft\Accessibility\CursorIndicator\'
    $global:ControlPanelDesktopRegistryPath = 'HKCU:\Control Panel\Desktop'
    $global:StickyKeysRegistryPath = 'HKCU:\Control Panel\Accessibility\StickyKeys'
    $global:ToggleKeysRegistryPath = 'HKCU:\Control Panel\Accessibility\ToggleKeys'
    $global:FilterKeysRegistryPath = 'HKCU:\Control Panel\Accessibility\Keyboard Response'
    $global:EyeControlRegistryPath = 'HKCU:\Software\Microsoft\input\EC\'
} else {
    $global:AccessibilityRegistryPath = $global:MagnifierRegistryPath = $global:PointerRegistryPath = $global:ControlPanelAccessibilityRegistryPath = $global:AudioRegistryPath = $global:PersonalizationRegistryPath = $global:NTAccessibilityRegistryPath = $global:CursorIndicatorAccessibilityRegistryPath = $global:ControlPanelDesktopRegistryPath = $global:StickyKeysRegistryPath = $global:ToggleKeysRegistryPath = $global:FilterKeysRegistryPath = $global:EyeControlRegistryPath = $env:TestRegistryPath
}

[DSCResource()]
class Text {
    [DscProperty(Key)] [TextSize] $Size = [TextSize]::KeepCurrentValue
    [DscProperty(NotConfigurable)] [int] $SizeValue

    hidden [string] $TextScaleFactor = 'TextScaleFactor'

    [Text] Get() {
        $currentState = [Text]::new()

        if (-not(DoesRegistryKeyPropertyExist -Path $global:AccessibilityRegistryPath -Name $this.TextScaleFactor)) {
            $currentState.Size = [TextSize]::Small
            $currentState.SizeValue = 96
        } else {
            $currentState.SizeValue = [int](Get-ItemPropertyValue -Path $global:AccessibilityRegistryPath -Name $this.TextScaleFactor)
            $currentSize = switch ($currentState.sizeValue) {
                96 { [TextSize]::Small }
                120 { [TextSize]::Medium }
                144 { [TextSize]::Large }
                256 { [TextSize]::ExtraLarge }
            }

            if ($null -ne $currentSize) {
                $currentState.Size = $currentSize
            }
        }

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()
        if ($this.Size -ne [TextSize]::KeepCurrentValue -and $this.Size -ne $currentState.Size) {
            return $false
        }

        return $true
    }

    [void] Set() {
        if ($this.Size -ne [TextSize]::KeepCurrentValue) {
            $desiredSize = switch ([TextSize]($this.Size)) {
                Small { 96 }
                Medium { 120 }
                Large { 144 }
                ExtraLarge { 256 }
            }

            Set-ItemProperty -Path $global:AccessibilityRegistryPath -Name $this.TextScaleFactor -Value $desiredSize -Type DWORD
        }
    }
}

[DSCResource()]
class Magnifier {
    [DscProperty(Key)] [MagnificationValue] $Magnification = [MagnificationValue]::KeepCurrentValue
    [DscProperty(Mandatory)] [int] $ZoomIncrement = 25
    [DscProperty()] [bool] $StartMagnify = $false
    [DscProperty(NotConfigurable)] [int] $MagnificationLevel
    [DscProperty(NotConfigurable)] [int] $ZoomIncrementLevel

    hidden [string] $MagnificationProperty = 'Magnification'
    hidden [string] $ZoomIncrementProperty = 'ZoomIncrement'

    [Magnifier] Get() {
        $currentState = [Magnifier]::new()

        if (-not(DoesRegistryKeyPropertyExist -Path $global:MagnifierRegistryPath -Name $this.MagnificationProperty)) {
            $currentState.Magnification = [MagnificationValue]::None
            $currentState.MagnificationLevel = 0
        } else {
            $currentState.MagnificationLevel = (Get-ItemProperty -Path $global:MagnifierRegistryPath -Name $this.MagnificationProperty).Magnification
            $currentMagnification = switch ($currentState.MagnificationLevel) {
                0 { [MagnificationValue]::None }
                100 { [MagnificationValue]::Low }
                200 { [MagnificationValue]::Medium }
                300 { [MagnificationValue]::High }
                default { [MagnificationValue]::KeepCurrentValue }
            }

            $currentState.Magnification = $currentMagnification
        }

        if (-not(DoesRegistryKeyPropertyExist -Path $global:MagnifierRegistryPath -Name $this.ZoomIncrementProperty)) {
            $currentState.ZoomIncrement = 25
            $currentState.ZoomIncrementLevel = 25
        } else {
            $currentState.ZoomIncrementLevel = (Get-ItemProperty -Path $global:MagnifierRegistryPath -Name $this.ZoomIncrementProperty).ZoomIncrement
            $currentState.ZoomIncrement = $currentState.ZoomIncrementLevel
        }

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()
        if ($this.Magnification -ne [MagnificationValue]::KeepCurrentValue -and $this.Magnification -ne $currentState.Magnification) {
            return $false
        }
        if ($this.ZoomIncrement -ne $currentState.ZoomIncrement) {
            return $false
        }

        return $true
    }

    [void] Set() {
        if ($this.Test()) {
            return
        }

        if (-not (Test-Path -Path $global:MagnifierRegistryPath)) {
            New-Item -Path $global:MagnifierRegistryPath -Force | Out-Null
        }

        if ($this.Magnification -ne [MagnificationValue]::KeepCurrentValue) {
            $desiredMagnification = switch ([MagnificationValue]($this.Magnification)) {
                None { 0 }
                Low { 100 }
                Medium { 200 }
                High { 300 }
            }

            Set-ItemProperty -Path $global:MagnifierRegistryPath -Name $this.MagnificationProperty -Value $desiredMagnification -Type DWORD
        }

        $currentState = $this.Get()

        if ($this.ZoomIncrement -ne $currentState.ZoomIncrement) {
            Set-ItemProperty -Path $global:MagnifierRegistryPath -Name $this.ZoomIncrementProperty -Value $this.ZoomIncrement -Type DWORD
        }

        if (($this.StartMagnify) -and (($null -eq (Get-Process -Name 'Magnify' -ErrorAction SilentlyContinue)))) {
            Start-Process 'C:\Windows\System32\Magnify.exe'
        }
    }
}

[DSCResource()]
class MousePointer {
    [DscProperty(Key)] [PointerSize] $PointerSize = [PointerSize]::KeepCurrentValue
    [DscProperty(NotConfigurable)] [string] $PointerSizeValue

    hidden [string] $PointerSizeProperty = 'CursorBaseSize'

    [MousePointer] Get() {
        $currentState = [MousePointer]::new()

        if (-not(DoesRegistryKeyPropertyExist -Path $global:PointerRegistryPath -Name $this.PointerSizeProperty)) {
            $currentState.PointerSize = [PointerSize]::Normal
            $currentState.PointerSizeValue = '32'
        } else {
            $currentState.PointerSizeValue = (Get-ItemProperty -Path $global:PointerRegistryPath -Name $this.PointerSizeProperty).CursorBaseSize
            $currentSize = switch ($currentState.PointerSizeValue) {
                '32' { [PointerSize]::Normal }
                '96' { [PointerSize]::Medium }
                '144' { [PointerSize]::Large }
                '256' { [PointerSize]::ExtraLarge }
                default { [PointerSize]::KeepCurrentValue }
            }

            $currentState.PointerSize = $currentSize
        }

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()
        if ($this.PointerSize -ne [PointerSize]::KeepCurrentValue -and $this.PointerSize -ne $currentState.PointerSize) {
            return $false
        }

        return $true
    }

    [void] Set() {
        if ($this.PointerSize -ne [PointerSize]::KeepCurrentValue) {
            $desiredSize = switch ([PointerSize]($this.PointerSize)) {
                Normal { '32' }
                Medium { '96' }
                Large { '144' }
                ExtraLarge { '256' }
            }

            if (-not (Test-Path -Path $global:PointerRegistryPath)) {
                New-Item -Path $global:PointerRegistryPath -Force | Out-Null
            }

            Set-ItemProperty -Path $global:PointerRegistryPath -Name $this.PointerSizeProperty -Value $desiredSize
        }
    }
}

[DSCResource()]
class VisualEffect {
    # Key required. Do not set.
    [DscProperty(Key)] [string] $SID
    [DscProperty()] [nullable[bool]] $AlwaysShowScrollbars
    [DscProperty()] [nullable[bool]] $TransparencyEffects
    [DscProperty()] [int] $MessageDurationInSeconds

    static hidden [string] $DynamicScrollbarsProperty = 'DynamicScrollbars'
    static hidden [string] $TransparencySettingProperty = 'EnableTransparency'
    static hidden [string] $MessageDurationProperty = 'MessageDuration'

    static [bool] GetShowDynamicScrollbarsStatus() {
        if (-not(DoesRegistryKeyPropertyExist -Path $global:ControlPanelAccessibilityRegistryPath -Name ([VisualEffect]::DynamicScrollbarsProperty))) {
            return $false
        } else {
            $dynamicScrollbarsValue = (Get-ItemProperty -Path $global:ControlPanelAccessibilityRegistryPath -Name ([VisualEffect]::DynamicScrollbarsProperty)).DynamicScrollbars
            return ($dynamicScrollbarsValue -eq 0)
        }
    }

    static [bool] GetTransparencyStatus() {
        if (-not(DoesRegistryKeyPropertyExist -Path $global:PersonalizationRegistryPath -Name ([VisualEffect]::TransparencySettingProperty))) {
            return $false
        } else {
            $TransparencySetting = (Get-ItemProperty -Path $global:PersonalizationRegistryPath -Name ([VisualEffect]::TransparencySettingProperty)).EnableTransparency
            return ($TransparencySetting -eq 0)
        }
    }

    static [int] GetMessageDuration() {
        if (-not(DoesRegistryKeyPropertyExist -Path $global:ControlPanelAccessibilityRegistryPath -Name ([VisualEffect]::MessageDurationProperty))) {
            return 5
        } else {
            $MessageDurationSetting = (Get-ItemProperty -Path $global:ControlPanelAccessibilityRegistryPath -Name ([VisualEffect]::MessageDurationProperty)).MessageDuration
            return $MessageDurationSetting
        }
    }

    [VisualEffect] Get() {
        $currentState = [VisualEffect]::new()
        $currentState.AlwaysShowScrollbars = [VisualEffect]::GetShowDynamicScrollbarsStatus()
        $currentState.TransparencyEffects = [VisualEffect]::GetTransparencyStatus()
        $currentState.MessageDurationInSeconds = [VisualEffect]::GetMessageDuration()

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()
        if (($null -ne $this.AlwaysShowScrollbars) -and ($this.AlwaysShowScrollbars -ne $currentState.AlwaysShowScrollbars)) {
            return $false
        }
        if (($null -ne $this.TransparencyEffects) -and ($this.TransparencyEffects -ne $currentState.TransparencyEffects)) {
            return $false
        }
        if ((0 -ne $this.MessageDurationInSeconds) -and ($this.MessageDurationInSeconds -ne $currentState.MessageDurationInSeconds)) {
            return $false
        }

        return $true
    }

    [void] Set() {
        if (-not $this.Test()) {
            if (-not (Test-Path -Path $global:ControlPanelAccessibilityRegistryPath)) {
                New-Item -Path $global:ControlPanelAccessibilityRegistryPath -Force | Out-Null
            }
            if ($null -ne $this.AlwaysShowScrollbars) {
                $dynamicScrollbarValue = if ($this.AlwaysShowScrollbars) { 0 } else { 1 }
                Set-ItemProperty -Path $global:ControlPanelAccessibilityRegistryPath -Name ([VisualEffect]::DynamicScrollbarsProperty) -Value $dynamicScrollbarValue
            }
            if ($null -ne $this.TransparencyEffects) {
                $transparencyValue = if ($this.TransparencyEffects) { 0 } else { 1 }

                if (-not (DoesRegistryKeyPropertyExist -Path $global:PersonalizationRegistryPath -Name ([VisualEffect]::TransparencySettingProperty))) {
                    New-ItemProperty -Path $global:PersonalizationRegistryPath -Name ([VisualEffect]::TransparencySettingProperty) -Value $transparencyValue -PropertyType DWord
                }
                Set-ItemProperty -Path $global:PersonalizationRegistryPath -Name ([VisualEffect]::TransparencySettingProperty) -Value $transparencyValue
            }
            if (0 -ne $this.MessageDurationInSeconds) {
                $min = 5
                $max = 300
                if ($this.MessageDurationInSeconds -notin $min..$max) {
                    throw "MessageDurationInSeconds must be between $min and $max. Value $($this.MessageDurationInSeconds) was provided."
                }
                Set-ItemProperty -Path $global:ControlPanelAccessibilityRegistryPath -Name ([VisualEffect]::MessageDurationProperty) -Value $this.MessageDurationInSeconds
            }
        }
    }
}

[DSCResource()]
class Audio {
    # Key required. Do not set.
    [DscProperty(Key)] [string] $SID
    [DscProperty()] [bool] $EnableMonoAudio = $false

    static hidden [string] $EnableMonoAudioProperty = 'AccessibilityMonoMixState'

    static [bool] GetEnableMonoAudioStatus() {
        if (-not(DoesRegistryKeyPropertyExist -Path $global:AudioRegistryPath -Name ([Audio]::EnableMonoAudioProperty))) {
            return $false
        } else {
            $AudioMonoSetting = (Get-ItemProperty -Path $global:AudioRegistryPath -Name ([Audio]::EnableMonoAudioProperty)).AccessibilityMonoMixState
            return ($AudioMonoSetting -eq 0)
        }
    }

    [Audio] Get() {
        $currentState = [Audio]::new()
        $currentState.EnableMonoAudio = [Audio]::GetEnableMonoAudioStatus()

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()
        if ($this.EnableMonoAudio -ne $currentState.EnableMonoAudio) {
            return $false
        }

        return $true
    }

    [void] Set() {
        if (-not $this.Test()) {
            if (-not (Test-Path -Path $global:AudioRegistryPath)) {
                New-Item -Path $global:AudioRegistryPath -Force | Out-Null
            }

            $monoAudioValue = if ($this.EnableMonoAudio) { 0 } else { 1 }

            Set-ItemProperty -Path $global:AudioRegistryPath -Name ([Audio]::EnableMonoAudioProperty) -Value $monoAudioValue
        }
    }
}

[DSCResource()]
class TextCursor {
    # Key required. Do not set.
    [DscProperty(Key)] [string] $SID
    [DscProperty()] [nullable[bool]] $IndicatorStatus
    [DscProperty()] [int] $IndicatorSize
    [DscProperty()] [int] $IndicatorColor
    [DscProperty()] [int] $Thickness

    static hidden [string] $IndicatorStatusProperty = 'Configuration'
    static hidden [string] $IndicatorStatusValue = 'cursorindicator'
    static hidden [string] $IndicatorSizeProperty = 'IndicatorType'
    static hidden [string] $IndicatorColorProperty = 'IndicatorColor'
    static hidden [string] $ThicknessProperty = 'CaretWidth'


    static [bool] GetIndicatorStatus() {
        $indicatorStatusArgs = @{  Path = $global:NTAccessibilityRegistryPath; Name = ([TextCursor]::IndicatorStatusProperty) }
        if (-not(DoesRegistryKeyPropertyExist @indicatorStatusArgs)) {
            return $false
        } else {
            $textCursorSetting = (Get-ItemProperty @indicatorStatusArgs).Configuration
            return ($textCursorSetting -eq ([TextCursor]::IndicatorStatusValue))
        }
    }

    static [int] GetIndicatorSize() {
        $indicatorSizeArgs = @{  Path = $global:CursorIndicatorAccessibilityRegistryPath; Name = ([TextCursor]::IndicatorSizeProperty) }
        if (-not(DoesRegistryKeyPropertyExist @indicatorSizeArgs)) {
            return 1
        } else {
            $textCursorSetting = (Get-ItemProperty @indicatorSizeArgs).IndicatorType
            return $textCursorSetting
        }
    }

    static [int] GetIndicatorColor() {
        $indicatorColorArgs = @{  Path = $global:CursorIndicatorAccessibilityRegistryPath; Name = ([TextCursor]::IndicatorColorProperty) }
        if (-not(DoesRegistryKeyPropertyExist @indicatorColorArgs)) {
            return $false
        } else {
            $textCursorSetting = (Get-ItemProperty @indicatorColorArgs).IndicatorColor
            return $textCursorSetting
        }
    }

    static [int] GetThickness() {
        $thicknessArgs = @{ Path = $global:ControlPanelDesktopRegistryPath; Name = ([TextCursor]::ThicknessProperty); }
        if (-not(DoesRegistryKeyPropertyExist @thicknessArgs)) {
            return 1
        } else {
            $textCursorSetting = (Get-ItemProperty @thicknessArgs).CaretWidth
            return $textCursorSetting
        }
    }

    [TextCursor] Get() {
        $currentState = [TextCursor]::new()
        $currentState.IndicatorStatus = [TextCursor]::GetIndicatorStatus()
        $currentState.IndicatorSize = [TextCursor]::GetIndicatorSize()
        $currentState.IndicatorColor = [TextCursor]::GetIndicatorColor()
        $currentState.Thickness = [TextCursor]::GetThickness()

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()
        if (($null -ne $this.IndicatorStatus) -and ($this.IndicatorStatus -ne $currentState.IndicatorStatus)) {
            return $false
        }
        if ((0 -ne $this.IndicatorSize) -and ($this.IndicatorSize -ne $currentState.IndicatorSize)) {
            return $false
        }
        if ((0 -ne $this.IndicatorColor) -and ($this.IndicatorColor -ne $currentState.IndicatorColor)) {
            return $false
        }
        if ((0 -ne $this.Thickness) -and ($this.Thickness -ne $currentState.Thickness)) {
            return $false
        }

        return $true
    }

    [void] Set() {
        if (-not $this.Test()) {
            if ($null -ne $this.IndicatorStatus) {
                $indicatorStatusArgs = @{ Path = $global:NTAccessibilityRegistryPath; Name = ([TextCursor]::IndicatorStatusProperty); }
                $textCursorValue = if ($this.IndicatorStatus) { ([TextCursor]::IndicatorStatusValue) } else { '' }
                Set-ItemProperty @indicatorStatusArgs -Value $textCursorValue
            }

            if (0 -ne $this.IndicatorSize) {
                $indicatorSizeArgs = @{  Path = $global:CursorIndicatorAccessibilityRegistryPath; Name = ([TextCursor]::IndicatorSizeProperty) }
                $min = 1
                $max = 20
                if ($this.IndicatorSize -notin $min..$max) {
                    throw "IndicatorSize must be between $min and $max. Value $($this.IndicatorSize) was provided."
                }
                if (-not (DoesRegistryKeyPropertyExist @indicatorSizeArgs)) {
                    New-ItemProperty @indicatorSizeArgs -Value $this.IndicatorSize -PropertyType DWord
                }
                Set-ItemProperty @indicatorSizeArgs -Value $this.IndicatorSize
            }

            if (0 -ne $this.IndicatorColor) {
                $indicatorColorArgs = @{  Path = $global:CursorIndicatorAccessibilityRegistryPath; Name = ([TextCursor]::IndicatorColorProperty) }
                $min = 1
                $max = 99999999
                if ($this.IndicatorColor -notin $min..$max) {
                    throw "IndicatorColor must be between $min and $max. Value $($this.IndicatorColor) was provided."
                }
                if (-not (DoesRegistryKeyPropertyExist @indicatorColorArgs)) {
                    New-ItemProperty @indicatorColorArgs -Value $this.IndicatorColor -PropertyType DWord
                }
                Set-ItemProperty @indicatorColorArgs -Value $this.IndicatorColor
            }

            if (0 -ne $this.Thickness) {
                $thicknessArgs = @{ Path = $global:ControlPanelDesktopRegistryPath; Name = ([TextCursor]::ThicknessProperty); }
                $min = 1
                $max = 20
                if ($this.Thickness -notin $min..$max) {
                    throw "Thickness must be between $min and $max. Value $($this.Thickness) was provided."
                }
                Set-ItemProperty @thicknessArgs -Value $this.Thickness
            }
        }
    }
}

[DSCResource()]
class StickyKeys {
    # Key required. Do not set.
    [DscProperty(Key)] [string] $SID
    [DscProperty()] [nullable[bool]] $Active
    [DscProperty()] [nullable[bool]] $Available
    [DscProperty()] [nullable[bool]] $HotkeyActive
    [DscProperty()] [nullable[bool]] $ConfirmOnHotkeyActivation
    [DscProperty()] [nullable[bool]] $HotkeySound
    [DscProperty()] [nullable[bool]] $VisualIndicator
    [DscProperty()] [nullable[bool]] $AudibleFeedback
    [DscProperty()] [nullable[bool]] $TriState
    [DscProperty()] [nullable[bool]] $TwoKeysOff

    static hidden [string] $SettingsProperty = 'Flags'

    static [System.Enum] GetCurrentFlags() {
        if (-not(DoesRegistryKeyPropertyExist -Path $global:StickyKeysRegistryPath -Name ([StickyKeys]::SettingsProperty))) {
            return [StickyKeysOptions]::None
        } else {
            $StickyKeysFlags = [System.Enum]::Parse('StickyKeysOptions', (Get-ItemPropertyValue -Path $global:StickyKeysRegistryPath -Name ([StickyKeys]::SettingsProperty)))
            return $StickyKeysFlags
        }
    }

    [StickyKeys] Get() {
        $currentFlags = [StickyKeys]::GetCurrentFlags()

        $currentState = [StickyKeys]::new()
        $currentState.Active = $currentFlags.HasFlag([StickyKeysOptions]::Active)
        $currentState.Available = $currentFlags.HasFlag([StickyKeysOptions]::Available)
        $currentState.HotkeyActive = $currentFlags.HasFlag([StickyKeysOptions]::HotkeyActive)
        $currentState.ConfirmOnHotkeyActivation = $currentFlags.HasFlag([StickyKeysOptions]::ConfirmHotkey)
        $currentState.HotkeySound = $currentFlags.HasFlag([StickyKeysOptions]::HotkeySound)
        $currentState.VisualIndicator = $currentFlags.HasFlag([StickyKeysOptions]::VisualIndicator)
        $currentState.AudibleFeedback = $currentFlags.HasFlag([StickyKeysOptions]::AudibleFeedback)
        $currentState.TriState = $currentFlags.HasFlag([StickyKeysOptions]::TriState)
        $currentState.TwoKeysOff = $currentFlags.HasFlag([StickyKeysOptions]::TwoKeysOff)

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()

        if (($null -ne $this.Active) -and ($this.Active -ne $currentState.Active)) {
            return $false
        }

        if (($null -ne $this.Available) -and ($this.Available -ne $currentState.Available)) {
            return $false
        }

        if (($null -ne $this.HotkeyActive) -and ($this.HotkeyActive -ne $currentState.HotkeyActive)) {
            return $false
        }

        if (($null -ne $this.ConfirmOnHotkeyActivation) -and ($this.ConfirmOnHotkeyActivation -ne $currentState.ConfirmOnHotkeyActivation)) {
            return $false
        }

        if (($null -ne $this.HotkeySound) -and ($this.HotkeySound -ne $currentState.HotkeySound)) {
            return $false
        }

        if (($null -ne $this.VisualIndicator) -and ($this.VisualIndicator -ne $currentState.VisualIndicator)) {
            return $false
        }

        if (($null -ne $this.AudibleFeedback) -and ($this.AudibleFeedback -ne $currentState.AudibleFeedback)) {
            return $false
        }

        if (($null -ne $this.TriState) -and ($this.TriState -ne $currentState.TriState)) {
            return $false
        }

        if (($null -ne $this.TwoKeysOff) -and ($this.TwoKeysOff -ne $currentState.TwoKeysOff)) {
            return $false
        }

        return $true
    }

    [void] Set() {
        # Only make changes if changes are needed
        if (-not $this.Test()) {
            # If a value isn't set in the DSC, it should remain unchanged, to do this we need the current flags
            $flags = [StickyKeys]::GetCurrentFlags()

            if ($null -ne $this.Active) {
                $flags = $this.Active ? $flags -bor [StickyKeysOptions]::Active : $flags -band (-bnot [StickyKeysOptions]::Active)
            }

            if ($null -ne $this.Available) {
                $flags = $this.Available ? $flags -bor [StickyKeysOptions]::Available : $flags -band (-bnot [StickyKeysOptions]::Available)
            }

            if ($null -ne $this.HotkeyActive) {
                $flags = $this.HotkeyActive ? $flags -bor [StickyKeysOptions]::HotkeyActive : $flags -band (-bnot [StickyKeysOptions]::HotkeyActive)
            }

            if ($null -ne $this.ConfirmOnHotkeyActivation) {
                $flags = $this.ConfirmOnHotkeyActivation ? $flags -bor [StickyKeysOptions]::ConfirmHotkey : $flags -band (-bnot [StickyKeysOptions]::ConfirmHotkey)
            }

            if ($null -ne $this.HotkeySound) {
                $flags = $this.HotkeySound ? $flags -bor [StickyKeysOptions]::HotkeySound : $flags -band (-bnot [StickyKeysOptions]::HotkeySound)
            }

            if ($null -ne $this.VisualIndicator) {
                $flags = $this.VisualIndicator ? $flags -bor [StickyKeysOptions]::VisualIndicator : $flags -band (-bnot [StickyKeysOptions]::VisualIndicator)
            }

            if ($null -ne $this.AudibleFeedback) {
                $flags = $this.AudibleFeedback ? $flags -bor [StickyKeysOptions]::AudibleFeedback : $flags -band (-bnot [StickyKeysOptions]::AudibleFeedback)
            }

            if ($null -ne $this.TriState) {
                $flags = $this.TriState ? $flags -bor [StickyKeysOptions]::TriState : $flags -band (-bnot [StickyKeysOptions]::TriState)
            }

            if ($null -ne $this.TwoKeysOff) {
                $flags = $this.TwoKeysOff ? $flags -bor [StickyKeysOptions]::TwoKeysOff : $flags -band (-bnot [StickyKeysOptions]::TwoKeysOff)
            }

            # Set the value in the registry
            Set-ItemProperty -Path $global:StickyKeysRegistryPath -Name ([StickyKeys]::SettingsProperty) -Value $flags.GetHashCode()
        }
    }
}

[DSCResource()]
class ToggleKeys {
    # Key required. Do not set.
    [DscProperty(Key)] [string] $SID
    [DscProperty()] [nullable[bool]] $Active
    [DscProperty()] [nullable[bool]] $Available
    [DscProperty()] [nullable[bool]] $HotkeyActive
    [DscProperty()] [nullable[bool]] $ConfirmOnHotkeyActivation
    [DscProperty()] [nullable[bool]] $HotkeySound
    [DscProperty()] [nullable[bool]] $VisualIndicator

    static hidden [string] $SettingsProperty = 'Flags'

    static [System.Enum] GetCurrentFlags() {
        if (-not(DoesRegistryKeyPropertyExist -Path $global:ToggleKeysRegistryPath -Name ([ToggleKeys]::SettingsProperty))) {
            return [ToggleKeysOptions]::None
        } else {
            $ToggleKeysFlags = [System.Enum]::Parse('ToggleKeysOptions', (Get-ItemPropertyValue -Path $global:StickyKeysRegistryPath -Name ([StickyKeys]::SettingsProperty)))
            return $ToggleKeysFlags
        }
    }

    [ToggleKeys] Get() {
        $currentFlags = [ToggleKeys]::GetCurrentFlags()

        $currentState = [ToggleKeys]::new()
        $currentState.Active = $currentFlags.HasFlag([ToggleKeysOptions]::Active)
        $currentState.Available = $currentFlags.HasFlag([ToggleKeysOptions]::Available)
        $currentState.HotkeyActive = $currentFlags.HasFlag([ToggleKeysOptions]::HotkeyActive)
        $currentState.ConfirmOnHotkeyActivation = $currentFlags.HasFlag([ToggleKeysOptions]::ConfirmHotkey)
        $currentState.HotkeySound = $currentFlags.HasFlag([ToggleKeysOptions]::HotkeySound)
        $currentState.VisualIndicator = $currentFlags.HasFlag([ToggleKeysOptions]::VisualIndicator)

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()

        if (($null -ne $this.Active) -and ($this.Active -ne $currentState.Active)) {
            return $false
        }

        if (($null -ne $this.Available) -and ($this.Available -ne $currentState.Available)) {
            return $false
        }

        if (($null -ne $this.HotkeyActive) -and ($this.HotkeyActive -ne $currentState.HotkeyActive)) {
            return $false
        }

        if (($null -ne $this.ConfirmOnHotkeyActivation) -and ($this.ConfirmOnHotkeyActivation -ne $currentState.ConfirmOnHotkeyActivation)) {
            return $false
        }

        if (($null -ne $this.HotkeySound) -and ($this.HotkeySound -ne $currentState.HotkeySound)) {
            return $false
        }

        if (($null -ne $this.VisualIndicator) -and ($this.VisualIndicator -ne $currentState.VisualIndicator)) {
            return $false
        }

        return $true
    }

    [void] Set() {
        # Only make changes if changes are needed
        if (-not $this.Test()) {
            # If a value isn't set in the DSC, it should remain unchanged, to do this we need the current flags
            $flags = [ToggleKeys]::GetCurrentFlags()

            if ($null -ne $this.Active) {
                $flags = $this.Active ? $flags -bor [ToggleKeysOptions]::Active : $flags -band (-bnot [ToggleKeysOptions]::Active)
            }

            if ($null -ne $this.Available) {
                $flags = $this.Available ? $flags -bor [ToggleKeysOptions]::Available : $flags -band (-bnot [ToggleKeysOptions]::Available)
            }

            if ($null -ne $this.HotkeyActive) {
                $flags = $this.HotkeyActive ? $flags -bor [ToggleKeysOptions]::HotkeyActive : $flags -band (-bnot [ToggleKeysOptions]::HotkeyActive)
            }

            if ($null -ne $this.ConfirmOnHotkeyActivation) {
                $flags = $this.ConfirmOnHotkeyActivation ? $flags -bor [ToggleKeysOptions]::ConfirmHotkey : $flags -band (-bnot [ToggleKeysOptions]::ConfirmHotkey)
            }

            if ($null -ne $this.HotkeySound) {
                $flags = $this.HotkeySound ? $flags -bor [ToggleKeysOptions]::HotkeySound : $flags -band (-bnot [ToggleKeysOptions]::HotkeySound)
            }

            if ($null -ne $this.VisualIndicator) {
                $flags = $this.VisualIndicator ? $flags -bor [ToggleKeysOptions]::VisualIndicator : $flags -band (-bnot [ToggleKeysOptions]::VisualIndicator)
            }

            # Set the value in the registry
            Set-ItemProperty -Path $global:ToggleKeysRegistryPath -Name ([ToggleKeys]::SettingsProperty) -Value $flags.GetHashCode()
        }
    }
}

[DSCResource()]
class FilterKeys {
    # Key required. Do not set.
    [DscProperty(Key)] [string] $SID
    [DscProperty()] [nullable[bool]] $Active
    [DscProperty()] [nullable[bool]] $Available
    [DscProperty()] [nullable[bool]] $HotkeyActive
    [DscProperty()] [nullable[bool]] $ConfirmOnHotkeyActivation
    [DscProperty()] [nullable[bool]] $HotkeySound
    [DscProperty()] [nullable[bool]] $VisualIndicator
    [DscProperty()] [nullable[bool]] $AudibleFeedback

    static hidden [string] $SettingsProperty = 'Flags'

    static [System.Enum] GetCurrentFlags() {
        if (-not(DoesRegistryKeyPropertyExist -Path $global:FilterKeysRegistryPath -Name ([FilterKeys]::SettingsProperty))) {
            return [FilterKeysOptions]::None
        } else {
            $FilterKeysFlags = [System.Enum]::Parse('FilterKeysOptions', (Get-ItemPropertyValue -Path $global:FilterKeysRegistryPath -Name ([FilterKeys]::SettingsProperty)))
            return $FilterKeysFlags
        }
    }

    [FilterKeys] Get() {
        $currentFlags = [FilterKeys]::GetCurrentFlags()

        $currentState = [FilterKeys]::new()
        $currentState.Active = $currentFlags.HasFlag([FilterKeysOptions]::Active)
        $currentState.Available = $currentFlags.HasFlag([FilterKeysOptions]::Available)
        $currentState.HotkeyActive = $currentFlags.HasFlag([FilterKeysOptions]::HotkeyActive)
        $currentState.ConfirmOnHotkeyActivation = $currentFlags.HasFlag([FilterKeysOptions]::ConfirmHotkey)
        $currentState.HotkeySound = $currentFlags.HasFlag([FilterKeysOptions]::HotkeySound)
        $currentState.VisualIndicator = $currentFlags.HasFlag([FilterKeysOptions]::VisualIndicator)
        $currentState.AudibleFeedback = $currentFlags.HasFlag([FilterKeysOptions]::AudibleFeedback)

        return $currentState
    }

    [bool] Test() {
        $currentState = $this.Get()

        if (($null -ne $this.Active) -and ($this.Active -ne $currentState.Active)) {
            return $false
        }

        if (($null -ne $this.Available) -and ($this.Available -ne $currentState.Available)) {
            return $false
        }

        if (($null -ne $this.HotkeyActive) -and ($this.HotkeyActive -ne $currentState.HotkeyActive)) {
            return $false
        }

        if (($null -ne $this.ConfirmOnHotkeyActivation) -and ($this.ConfirmOnHotkeyActivation -ne $currentState.ConfirmOnHotkeyActivation)) {
            return $false
        }

        if (($null -ne $this.HotkeySound) -and ($this.HotkeySound -ne $currentState.HotkeySound)) {
            return $false
        }

        if (($null -ne $this.VisualIndicator) -and ($this.VisualIndicator -ne $currentState.VisualIndicator)) {
            return $false
        }

        if (($null -ne $this.AudibleFeedback) -and ($this.AudibleFeedback -ne $currentState.AudibleFeedback)) {
            return $false
        }

        return $true
    }

    [void] Set() {
        # Only make changes if changes are needed
        if (-not $this.Test()) {
            # If a value isn't set in the DSC, it should remain unchanged, to do this we need the current flags
            $flags = [FilterKeys]::GetCurrentFlags()

            if ($null -ne $this.Active) {
                $flags = $this.Active ? $flags -bor [FilterKeysOptions]::Active : $flags -band (-bnot [FilterKeysOptions]::Active)
            }

            if ($null -ne $this.Available) {
                $flags = $this.Available ? $flags -bor [FilterKeysOptions]::Available : $flags -band (-bnot [FilterKeysOptions]::Available)
            }

            if ($null -ne $this.HotkeyActive) {
                $flags = $this.HotkeyActive ? $flags -bor [FilterKeysOptions]::HotkeyActive : $flags -band (-bnot [FilterKeysOptions]::HotkeyActive)
            }

            if ($null -ne $this.ConfirmOnHotkeyActivation) {
                $flags = $this.ConfirmOnHotkeyActivation ? $flags -bor [FilterKeysOptions]::ConfirmHotkey : $flags -band (-bnot [FilterKeysOptions]::ConfirmHotkey)
            }

            if ($null -ne $this.HotkeySound) {
                $flags = $this.HotkeySound ? $flags -bor [FilterKeysOptions]::HotkeySound : $flags -band (-bnot [FilterKeysOptions]::HotkeySound)
            }

            if ($null -ne $this.VisualIndicator) {
                $flags = $this.VisualIndicator ? $flags -bor [FilterKeysOptions]::VisualIndicator : $flags -band (-bnot [FilterKeysOptions]::VisualIndicator)
            }

            if ($null -ne $this.AudibleFeedback) {
                $flags = $this.AudibleFeedback ? $flags -bor [FilterKeysOptions]::AudibleFeedback : $flags -band (-bnot [FilterKeysOptions]::AudibleFeedback)
            }

            # Set the value in the registry
            Set-ItemProperty -Path $global:FilterKeysRegistryPath -Name ([FilterKeys]::SettingsProperty) -Value $flags.GetHashCode()
        }
    }
}

[DscResource()]
class EyeControl {
    [DscProperty(Key)] [Ensure] $Ensure
    hidden [string] $SettingsProperty = 'Enabled'

    [EyeControl] Get() {
        $currentState = [EyeControl]::new()

        if (-not(DoesRegistryKeyPropertyExist -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty)) {
            $currentState.Ensure = [Ensure]::Absent
        } else {
            $currentState.Ensure = [int]((Get-ItemPropertyValue -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty) -eq 1)
        }

        return $currentState
    }

    [bool] Test() {
        return $this.Get().Ensure -eq $this.Ensure
    }

    [void] Set() {
        # Only make changes if changes are needed
        if ($this.Test()) { return }
        if (-not (DoesRegistryKeyPropertyExist -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty)) {
            New-ItemProperty -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty -PropertyType DWord
        }
        Set-ItemProperty -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty -Value $([int]$this.Ensure)
    }
}

#region Functions
function DoesRegistryKeyPropertyExist {
    param (
        [Parameter(Mandatory)]
        [string]$Path,

        [Parameter(Mandatory)]
        [string]$Name
    )

    # Get-ItemProperty will return $null if the registry key property does not exist.
    $itemProperty = Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue
    return $null -ne $itemProperty
}
#endregion Functions

# SIG # Begin signature block
# MIIoUgYJKoZIhvcNAQcCoIIoQzCCKD8CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBrdZoT+FMQnFFK
# yP6hzsNR2nJWHjD/vW8hO7sobwiDL6CCDYUwggYDMIID66ADAgECAhMzAAAEA73V
# lV0POxitAAAAAAQDMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTEzWhcNMjUwOTExMjAxMTEzWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCfdGddwIOnbRYUyg03O3iz19XXZPmuhEmW/5uyEN+8mgxl+HJGeLGBR8YButGV
# LVK38RxcVcPYyFGQXcKcxgih4w4y4zJi3GvawLYHlsNExQwz+v0jgY/aejBS2EJY
# oUhLVE+UzRihV8ooxoftsmKLb2xb7BoFS6UAo3Zz4afnOdqI7FGoi7g4vx/0MIdi
# kwTn5N56TdIv3mwfkZCFmrsKpN0zR8HD8WYsvH3xKkG7u/xdqmhPPqMmnI2jOFw/
# /n2aL8W7i1Pasja8PnRXH/QaVH0M1nanL+LI9TsMb/enWfXOW65Gne5cqMN9Uofv
# ENtdwwEmJ3bZrcI9u4LZAkujAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU6m4qAkpz4641iK2irF8eWsSBcBkw
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwMjkyNjAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AFFo/6E4LX51IqFuoKvUsi80QytGI5ASQ9zsPpBa0z78hutiJd6w154JkcIx/f7r
# EBK4NhD4DIFNfRiVdI7EacEs7OAS6QHF7Nt+eFRNOTtgHb9PExRy4EI/jnMwzQJV
# NokTxu2WgHr/fBsWs6G9AcIgvHjWNN3qRSrhsgEdqHc0bRDUf8UILAdEZOMBvKLC
# rmf+kJPEvPldgK7hFO/L9kmcVe67BnKejDKO73Sa56AJOhM7CkeATrJFxO9GLXos
# oKvrwBvynxAg18W+pagTAkJefzneuWSmniTurPCUE2JnvW7DalvONDOtG01sIVAB
# +ahO2wcUPa2Zm9AiDVBWTMz9XUoKMcvngi2oqbsDLhbK+pYrRUgRpNt0y1sxZsXO
# raGRF8lM2cWvtEkV5UL+TQM1ppv5unDHkW8JS+QnfPbB8dZVRyRmMQ4aY/tx5x5+
# sX6semJ//FbiclSMxSI+zINu1jYerdUwuCi+P6p7SmQmClhDM+6Q+btE2FtpsU0W
# +r6RdYFf/P+nK6j2otl9Nvr3tWLu+WXmz8MGM+18ynJ+lYbSmFWcAj7SYziAfT0s
# IwlQRFkyC71tsIZUhBHtxPliGUu362lIO0Lpe0DOrg8lspnEWOkHnCT5JEnWCbzu
# iVt8RX1IV07uIveNZuOBWLVCzWJjEGa+HhaEtavjy6i7MIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGiMwghofAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAQDvdWVXQ87GK0AAAAA
# BAMwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIDRK
# i8Va9CnqSjvpVL/0guEkwVqZXwfe3hJb97gr7+UoMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAFybm5KEF7m+qAl0UusT9iksVv2DmiZw7Coy8
# swSP8hB9zXXJrVjYUi9lc2xBE+ovWOp1paYw0f74xVieIIAi/ut5B7Oe/bxeYNJW
# a6gNRcP+Y33+vVtELeuxWYr/qeqiGqye8LL+jqI0SW9XNUVhDSzxr0N7DAZ5/H/5
# h9YsQQAZx6ftXv97BEgCyZKNQsx05nTfIIQvdeglUCwB1laPgd+gnNqCvqV78IOD
# 92bLfNZh519gTpGtBxLCG1T0P0+iY1+EUa/3xMolzoe1GkbLmGG2Wzc8+Fp4TxUt
# b+NMbdCHLUpHum0CK9RYSsdE1u8edWqoSklMzqCIGY3G5dgNAKGCF60wghepBgor
# BgEEAYI3AwMBMYIXmTCCF5UGCSqGSIb3DQEHAqCCF4YwgheCAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCB565RmRY8hGmQNpmlvlQ/xc+nyyFfpF1qZ
# 1u+G5GYR7gIGZuszQSxGGBMyMDI0MTExMjE3MjMwMi40OTRaMASAAgH0oIHZpIHW
# MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT
# Hm5TaGllbGQgVFNTIEVTTjoyRDFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEfswggcoMIIFEKADAgECAhMzAAAB/XP5
# aFrNDGHtAAEAAAH9MA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMB4XDTI0MDcyNTE4MzExNloXDTI1MTAyMjE4MzExNlowgdMxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jv
# c29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVs
# ZCBUU1MgRVNOOjJEMUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# oWWs+D+Ou4JjYnRHRedu0MTFYzNJEVPnILzc02R3qbnujvhZgkhp+p/lymYLzkQy
# G2zpxYceTjIF7HiQWbt6FW3ARkBrthJUz05ZnKpcF31lpUEb8gUXiD2xIpo8YM+S
# D0S+hTP1TCA/we38yZ3BEtmZtcVnaLRp/Avsqg+5KI0Kw6TDJpKwTLl0VW0/23sK
# ikeWDSnHQeTprO0zIm/btagSYm3V/8zXlfxy7s/EVFdSglHGsUq8EZupUO8XbHzz
# 7tURyiD3kOxNnw5ox1eZX/c/XmW4H6b4yNmZF0wTZuw37yA1PJKOySSrXrWEh+H6
# ++Wb6+1ltMCPoMJHUtPP3Cn0CNcNvrPyJtDacqjnITrLzrsHdOLqjsH229Zkvndk
# 0IqxBDZgMoY+Ef7ffFRP2pPkrF1F9IcBkYz8hL+QjX+u4y4Uqq4UtT7VRnsqvR/x
# /+QLE0pcSEh/XE1w1fcp6Jmq8RnHEXikycMLN/a/KYxpSP3FfFbLZuf+qIryFL0g
# EDytapGn1ONjVkiKpVP2uqVIYj4ViCjy5pLUceMeqiKgYqhpmUHCE2WssLLhdQBH
# dpl28+k+ZY6m4dPFnEoGcJHuMcIZnw4cOwixojROr+Nq71cJj7Q4L0XwPvuTHQt0
# oH7RKMQgmsy7CVD7v55dOhdHXdYsyO69dAdK+nWlyYcCAwEAAaOCAUkwggFFMB0G
# A1UdDgQWBBTpDMXA4ZW8+yL2+3vA6RmU7oEKpDAfBgNVHSMEGDAWgBSfpxVdAF5i
# XYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jv
# c29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENB
# JTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRp
# bWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1Ud
# JQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF
# AAOCAgEAY9hYX+T5AmCrYGaH96TdR5T52/PNOG7ySYeopv4flnDWQLhBlravAg+p
# jlNv5XSXZrKGv8e4s5dJ5WdhfC9ywFQq4TmXnUevPXtlubZk+02BXK6/23hM0TSK
# s2KlhYiqzbRe8QbMfKXEDtvMoHSZT7r+wI2IgjYQwka+3P9VXgERwu46/czz8IR/
# Zq+vO5523Jld6ssVuzs9uwIrJhfcYBj50mXWRBcMhzajLjWDgcih0DuykPcBpoTL
# lOL8LpXooqnr+QLYE4BpUep3JySMYfPz2hfOL3g02WEfsOxp8ANbcdiqM31dm3vS
# heEkmjHA2zuM+Tgn4j5n+Any7IODYQkIrNVhLdML09eu1dIPhp24lFtnWTYNaFTO
# fMqFa3Ab8KDKicmp0AthRNZVg0BPAL58+B0UcoBGKzS9jscwOTu1JmNlisOKkVUV
# kSJ5Fo/ctfDSPdCTVaIXXF7l40k1cM/X2O0JdAS97T78lYjtw/PybuzX5shxBh/R
# qTPvCyAhIxBVKfN/hfs4CIoFaqWJ0r/8SB1CGsyyIcPfEgMo8ceq1w5Zo0JfnyFi
# 6Guo+z3LPFl/exQaRubErsAUTfyBY5/5liyvjAgyDYnEB8vHO7c7Fg2tGd5hGgYs
# +AOoWx24+XcyxpUkAajDhky9Dl+8JZTjts6BcT9sYTmOodk/SgIwggdxMIIFWaAD
# AgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv
# ZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIy
# MjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5
# vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64
# NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhu
# je3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl
# 3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPg
# yY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I
# 5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2
# ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/
# TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy
# 16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y
# 1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6H
# XtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMB
# AAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQW
# BBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30B
# ATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYB
# BAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMB
# Af8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBL
# oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr
# BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS
# b29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1Vffwq
# reEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27
# DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pv
# vinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9Ak
# vUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWK
# NsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2
# kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+
# c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep
# 8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+Dvk
# txW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1Zyvg
# DbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/
# 2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDVjCCAj4CAQEwggEBoYHZpIHW
# MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT
# Hm5TaGllbGQgVFNTIEVTTjoyRDFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAoj0WtVVQUNSK
# oqtrjinRAsBUdoOggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx
# MDANBgkqhkiG9w0BAQsFAAIFAOrdh98wIhgPMjAyNDExMTIwNzUzMDNaGA8yMDI0
# MTExMzA3NTMwM1owdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA6t2H3wIBADAHAgEA
# AgIiSDAHAgEAAgISYjAKAgUA6t7ZXwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor
# BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUA
# A4IBAQAIkXts31e/A7X4VkNA7bus2rUsNpmtuo1QAnazkk9TZCgrzp/ZEU8RctPU
# PX5tFfjojKvDi1pfdF92zwcwKOWZnFUvubIDiuGs+9WY/v7/SBUphzD7jCTUatGn
# cWXrwJhrZrqK7atUKwAtb/nHgCvEeG155XYheCVTi+CWHlWC/CO5/QKoefSEf96U
# RZ1M4L4ZCr37LzariIJHCghRXmVLfQ4YTUqTmP9L6Mfy57D/2e5tcFHEAlkOpUQX
# A/ECcgbv8IesZWaiNCGkg7IuucchEEcxu79vEO34dfmi/engCLS0Yc+99RY4+M9S
# vuNkwUC3l2SHbbrsWVXwR+5c+GL1MYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAH9c/loWs0MYe0AAQAAAf0wDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQgRFllWqFdpzYc9mGTnynmS/fMeqMILvUA77VXAmXZpQcwgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCCAKEgNyUowvIfx/eDfYSupHkeF1p6GFwjKBs8l
# RB4NRzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# /XP5aFrNDGHtAAEAAAH9MCIEIN63ory/oSqdzAAkC84gWzleUStbJYjnVI5ruVjd
# Uee7MA0GCSqGSIb3DQEBCwUABIICAGwutZXhQXFV9U9N6W6Eewy8VNlG3gs6bSva
# vKg9N87J4WmJq0tiE3sxQKpfIDWBS0FJ2RrZO+2psG9LTBmDBVhwhvnuz8AL9NbM
# +KPwWHSCcIhIwWu1hyf8X4X9E8Lp5taUzmJlOTkM5eUxCPQWsE3IPb0YvT9peTfJ
# u9zJbnQ7iJYOrlzHhZV+RjXAvc4B9S07K34Llouu4gQ+bNB5TXKkUVIkomyqvNIn
# aoFc5ivxUK0fhm/zm5MEJLsuNjK6YiNsxjIg5od/KkpwR9itg4mxJdLRCxSBXVgS
# ijCldqLSpSUANneJTPPzdBRwOpAndFgXr7BOs+DaeUsDuDLkkH2AQnCQjf5OHcEQ
# 9+Go7GGkLtzlLuR+TZvwciwcP6OalcfzoxQZCKILt4yoIH3oj6t4oHNFNFXpWPMq
# qZudC7o+PIVsFIDa+UpMyVwkq4WRwczyPe1qIQxo1EEaP+5rwrrwKyNDjbMR6fPh
# EG+PWCCBG4ai9I/EWRZNwthu1lEYlDJW66CqkSLkhQun6Loesgj3Rq3iJiT3OC4i
# 5IK3VQ8Fx8waxZD1IP+YPLP8KscdKW9jdbWgZbNovYuQUHRAkHQT1B5hioXxksc9
# CAITs4NgU9Nfo/wQsmTScnBl3PD0Jtz8MnpFWQHMdjXf7hbX6a5jURoXwYEVkRmd
# csbP06hL
# SIG # End signature block