Functions/GenXdev.Queries.Webbrowser/Open-StackOverflowQuery.ps1

################################################################################
<#
.SYNOPSIS
Opens Stack Overflow search queries in a web browser.
 
.DESCRIPTION
Opens one or more Stack Overflow search queries in a web browser with
comprehensive browser and display configuration options. This function provides
an advanced wrapper around Stack Overflow search functionality with extensive
options for window positioning, browser selection, and behavior customization.
 
Key features:
- Multiple search query support with pipeline input
- Smart browser detection and selection (Edge, Chrome, Firefox, all browsers)
- Advanced window positioning (left, right, top, bottom, centered, fullscreen)
- Multi-monitor support with automatic or manual monitor selection
- Private/incognito browsing mode support
- Application mode for distraction-free browsing
- Language localization support for international search results
- Extension and popup blocking options
- Focus management and window manipulation
- Keystroke automation to browser windows
- URL return options for automation workflows
 
The function automatically detects system capabilities and adjusts behavior
accordingly. For browsers not installed on the system, operations are silently
skipped without errors.
 
.PARAMETER Queries
The search queries to perform on Stack Overflow. Accepts pipeline input and
supports multiple queries in a single operation. Each query is URL-encoded
and submitted as a separate Stack Overflow search.
 
.PARAMETER Language
The language of the returned search results. Accepts a comprehensive list of
languages and automatically sets the appropriate Accept-Language header for
localized search results.
 
.PARAMETER Monitor
The monitor to display results on:
- 0 = Primary monitor
- -1 = Discard positioning (no window positioning)
- -2 = Configured secondary monitor (uses $Global:DefaultSecondaryMonitor)
- 1+ = Specific monitor number
 
.PARAMETER Width
The initial width of the browser window in pixels. When not specified, uses
the monitor's working area width or half-width for side positioning.
 
.PARAMETER Height
The initial height of the browser window in pixels. When not specified, uses
the monitor's working area height or half-height for top/bottom positioning.
 
.PARAMETER X
The initial X position of the browser window. When not specified, uses the
monitor's left edge. Can be specified relative to the selected monitor.
 
.PARAMETER Y
The initial Y position of the browser window. When not specified, uses the
monitor's top edge. Can be specified relative to the selected monitor.
 
.PARAMETER AcceptLang
Set the browser accept-lang http header. Overrides the Language parameter
when both are specified.
 
.PARAMETER KeysToSend
Keystrokes to send to the browser window after opening. Uses the same format
as the GenXdev.Windows\Send-Key cmdlet for automation scenarios.
 
.PARAMETER Private
Opens in incognito/private browsing mode. Uses InPrivate for Edge and
incognito for Chrome.
 
.PARAMETER Force
Force enable debugging port, stopping existing browsers if needed. Useful
when browser debugging features are required.
 
.PARAMETER Edge
Opens in Microsoft Edge browser specifically.
 
.PARAMETER Chrome
Opens in Google Chrome browser specifically.
 
.PARAMETER Chromium
Opens in Microsoft Edge or Google Chrome, depending on what the default
browser is. Prefers Chromium-based browsers.
 
.PARAMETER Firefox
Opens in Firefox browser specifically.
 
.PARAMETER All
Opens in all registered modern browsers simultaneously.
 
.PARAMETER FullScreen
Opens in fullscreen mode using F11 key simulation.
 
.PARAMETER Left
Place browser window on the left side of the screen.
 
.PARAMETER Right
Place browser window on the right side of the screen.
 
.PARAMETER Top
Place browser window on the top side of the screen.
 
.PARAMETER Bottom
Place browser window on the bottom side of the screen.
 
.PARAMETER Centered
Place browser window in the center of the screen.
 
.PARAMETER ApplicationMode
Hide the browser controls for a distraction-free search experience.
 
.PARAMETER NoBrowserExtensions
Prevent loading of browser extensions during the search session.
 
.PARAMETER DisablePopupBlocker
Disable the popup blocker to allow search-related popups.
 
.PARAMETER FocusWindow
Focus the browser window after opening the search.
 
.PARAMETER SetForeground
Set the browser window to foreground after opening.
 
.PARAMETER Maximize
Maximize the window after positioning for full-screen search experience.
 
.PARAMETER RestoreFocus
Restore PowerShell window focus after opening the browser.
 
.PARAMETER NewWindow
Don't re-use existing browser window, instead, create a new one for each
search query.
 
.PARAMETER PassThru
Returns a [System.Diagnostics.Process] object of the browser process for
further automation or monitoring.
 
.PARAMETER ReturnURL
Don't open webbrowser, just return the formatted Stack Overflow search URL
for the query without launching any browser.
 
.PARAMETER ReturnOnlyURL
After opening webbrowser, return the formatted Stack Overflow search URL.
Useful for logging or further processing.
 
.PARAMETER SendKeyEscape
Escape control characters when sending keystrokes to the browser window.
 
.PARAMETER SendKeyHoldKeyboardFocus
Prevent returning keyboard focus to PowerShell after sending keystrokes to
the browser window.
 
.PARAMETER SendKeyUseShiftEnter
Send Shift+Enter instead of regular Enter for line breaks when sending
keystrokes to the browser.
 
.PARAMETER SendKeyDelayMilliSeconds
Delay between sending different key sequences in milliseconds. Useful for
slower systems or complex automation scenarios.
 
.EXAMPLE
Open-StackOverflowQuery -Queries "powershell array" -Monitor 0
 
Opens a Stack Overflow search for "powershell array" on the primary monitor.
 
.EXAMPLE
qso "powershell array" -mon 0
 
Opens a Stack Overflow search using the alias with monitor positioning.
 
.EXAMPLE
"powershell", "array manipulation" | Open-StackOverflowQuery -Language "English" -Chrome
 
Opens multiple Stack Overflow searches in Chrome with English language preference.
 
.EXAMPLE
Open-StackOverflowQuery -Queries "c# linq" -ReturnURL
 
Returns the Stack Overflow search URL without opening a browser.
#>

################################################################################
function Open-StackOverflowQuery {

    [CmdletBinding()]
    [Alias('qso')]
    param(
        ########################################################################
        [Alias('q', 'Name', 'Text', 'Query')]
        [Parameter(
            Mandatory = $true,
            Position = 0,
            ValueFromRemainingArguments = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'The search queries to perform on Stack Overflow'
        )]
        [string[]] $Queries,
        ########################################################################
        [ValidateSet(
            'Afrikaans',
            'Akan',
            'Albanian',
            'Amharic',
            'Arabic',
            'Armenian',
            'Azerbaijani',
            'Basque',
            'Belarusian',
            'Bemba',
            'Bengali',
            'Bihari',
            'Bork, bork, bork!',
            'Bosnian',
            'Breton',
            'Bulgarian',
            'Cambodian',
            'Catalan',
            'Cherokee',
            'Chichewa',
            'Chinese (Simplified)',
            'Chinese (Traditional)',
            'Corsican',
            'Croatian',
            'Czech',
            'Danish',
            'Dutch',
            'Elmer Fudd',
            'English',
            'Esperanto',
            'Estonian',
            'Ewe',
            'Faroese',
            'Filipino',
            'Finnish',
            'French',
            'Frisian',
            'Ga',
            'Galician',
            'Georgian',
            'German',
            'Greek',
            'Guarani',
            'Gujarati',
            'Hacker',
            'Haitian Creole',
            'Hausa',
            'Hawaiian',
            'Hebrew',
            'Hindi',
            'Hungarian',
            'Icelandic',
            'Igbo',
            'Indonesian',
            'Interlingua',
            'Irish',
            'Italian',
            'Japanese',
            'Javanese',
            'Kannada',
            'Kazakh',
            'Kinyarwanda',
            'Kirundi',
            'Klingon',
            'Kongo',
            'Korean',
            'Krio (Sierra Leone)',
            'Kurdish',
            'Kurdish (Soranî)',
            'Kyrgyz',
            'Laothian',
            'Latin',
            'Latvian',
            'Lingala',
            'Lithuanian',
            'Lozi',
            'Luganda',
            'Luo',
            'Macedonian',
            'Malagasy',
            'Malay',
            'Malayalam',
            'Maltese',
            'Maori',
            'Marathi',
            'Mauritian Creole',
            'Moldavian',
            'Mongolian',
            'Montenegrin',
            'Nepali',
            'Nigerian Pidgin',
            'Northern Sotho',
            'Norwegian',
            'Norwegian (Nynorsk)',
            'Occitan',
            'Oriya',
            'Oromo',
            'Pashto',
            'Persian',
            'Pirate',
            'Polish',
            'Portuguese (Brazil)',
            'Portuguese (Portugal)',
            'Punjabi',
            'Quechua',
            'Romanian',
            'Romansh',
            'Runyakitara',
            'Russian',
            'Scots Gaelic',
            'Serbian',
            'Serbo-Croatian',
            'Sesotho',
            'Setswana',
            'Seychellois Creole',
            'Shona',
            'Sindhi',
            'Sinhalese',
            'Slovak',
            'Slovenian',
            'Somali',
            'Spanish',
            'Spanish (Latin American)',
            'Sundanese',
            'Swahili',
            'Swedish',
            'Tajik',
            'Tamil',
            'Tatar',
            'Telugu',
            'Thai',
            'Tigrinya',
            'Tonga',
            'Tshiluba',
            'Tumbuka',
            'Turkish',
            'Turkmen',
            'Twi',
            'Uighur',
            'Ukrainian',
            'Urdu',
            'Uzbek',
            'Vietnamese',
            'Welsh',
            'Wolof',
            'Xhosa',
            'Yiddish',
            'Yoruba',
            'Zulu')]
        [parameter(
            Mandatory = $false,
            Position = 1,
            HelpMessage = ('The language of the returned search results for ' +
                'localized Stack Overflow content')
        )]
        [string] $Language,
        ########################################################################
        [Alias('m', 'mon')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('The monitor to use, 0 = default, -1 is discard, ' +
                '-2 = Configured secondary monitor, defaults to -1, no positioning')
        )]
        [int] $Monitor = -1,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'The initial width of the webbrowser window'
        )]
        [int] $Width = -1,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'The initial height of the webbrowser window'
        )]
        [int] $Height = -1,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'The initial X position of the webbrowser window'
        )]
        [int] $X = -999999,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'The initial Y position of the webbrowser window'
        )]
        [int] $Y = -999999,
        ########################################################################
        [Alias('lang', 'locale')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Set the browser accept-lang http header'
        )]
        [string] $AcceptLang,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Keystrokes to send to the Browser window, ' +
                'see documentation for cmdlet GenXdev.Windows\Send-Key')
        )]
        [string[]] $KeysToSend,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Opens in incognito/private browsing mode'
        )]
        [Alias('incognito', 'inprivate')]
        [switch] $Private,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Force enable debugging port, stopping existing ' +
                'browsers if needed')
        )]
        [switch] $Force,
        ########################################################################
        [Alias('e')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Opens in Microsoft Edge'
        )]
        [switch] $Edge,
        ########################################################################
        [Alias('ch')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Opens in Google Chrome'
        )]
        [switch] $Chrome,
        ########################################################################
        [Alias('c')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Opens in Microsoft Edge or Google Chrome, ' +
                'depending on what the default browser is')
        )]
        [switch] $Chromium,
        ########################################################################
        [Alias('ff')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Opens in Firefox'
        )]
        [switch] $Firefox,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Opens in all registered modern browsers'
        )]
        [switch] $All,
        ########################################################################
        [Alias('fs', 'f')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Opens in fullscreen mode'
        )]
        [switch] $FullScreen,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Place browser window on the left side of the screen'
        )]
        [switch] $Left,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Place browser window on the right side of the screen'
        )]
        [switch] $Right,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Place browser window on the top side of the screen'
        )]
        [switch] $Top,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Place browser window on the bottom side of the screen'
        )]
        [switch] $Bottom,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Place browser window in the center of the screen'
        )]
        [switch] $Centered,
        ########################################################################
        [Alias('a', 'app', 'appmode')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Hide the browser controls'
        )]
        [switch] $ApplicationMode,
        ########################################################################
        [Alias('de', 'ne', 'NoExtensions')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Prevent loading of browser extensions'
        )]
        [switch] $NoBrowserExtensions,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Disable the popup blocker'
        )]
        [Alias('allowpopups')]
        [switch] $DisablePopupBlocker,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Focus the browser window after opening'
        )]
        [Alias('fw','focus')]
        [switch] $FocusWindow,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Set the browser window to foreground after opening'
        )]
        [Alias('fg')]
        [switch] $SetForeground,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Maximize the window after positioning'
        )]
        [switch] $Maximize,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Restore PowerShell window focus'
        )]
        [Alias('rf', 'bg')]
        [switch] $RestoreFocus,
        ########################################################################
        [Alias('nw', 'new')]
        [Parameter(
            Mandatory = $false,
            HelpMessage = ("Don't re-use existing browser window, instead, " +
                'create a new one')
        )]
        [switch] $NewWindow,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Returns a [System.Diagnostics.Process] object of ' +
                'the browserprocess')
        )]
        [Alias('pt')]
        [switch] $PassThru,
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = "Don't open webbrowser, just return the url"
        )]
        [switch] $ReturnURL,
        ########################################################################
        [parameter(
            Mandatory = $false,
            HelpMessage = 'After opening webbrowser, return the url'
        )]
        [switch] $ReturnOnlyURL,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Escape control characters when sending keys'
        )]
        [Alias('Escape')]
        [switch] $SendKeyEscape,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Prevent returning keyboard focus to PowerShell ' +
                'after sending keys')
        )]
        [Alias('HoldKeyboardFocus')]
        [switch] $SendKeyHoldKeyboardFocus,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Send Shift+Enter instead of regular Enter for ' +
                'line breaks')
        )]
        [Alias('UseShiftEnter')]
        [switch] $SendKeyUseShiftEnter,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Delay between sending different key sequences ' +
                'in milliseconds')
        )]
        [Alias('DelayMilliSeconds')]
        [int] $SendKeyDelayMilliSeconds,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Removes the borders of the browser window.'
        )]
        [Alias('nb')]
        [switch] $NoBorders,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Use alternative settings stored in session for ' +
                'Stack Overflow search preferences')
        )]
        [switch] $SessionOnly,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Clear alternative settings stored in session for ' +
                'Stack Overflow search preferences')
        )]
        [switch] $ClearSession,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = ('Store settings only in persistent preferences without ' +
                'affecting session for Stack Overflow search')
        )]
        [Alias('FromPreferences')]
        [switch] $SkipSession,
        ########################################################################
        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Position browser window either fullscreen on different monitor than PowerShell, or side by side with PowerShell on the same monitor.'
        )]
        [Alias('sbs')]
        [switch] $SideBySide
        ########################################################################
    )

    begin {

        # copy identical parameter values from this function to open-webbrowser
        $invocationArguments = GenXdev.Helpers\Copy-IdenticalParamValues `
            -BoundParameters $PSBoundParameters `
            -FunctionName 'GenXdev.Webbrowser\Open-Webbrowser' `
            -DefaultValues (Microsoft.PowerShell.Utility\Get-Variable `
                -Scope Local `
                -ErrorAction SilentlyContinue)

        # process language parameter if specified by user
        if (-not [string]::IsNullOrWhiteSpace($Language)) {

            # retrieve the language code from the language dictionary
            $code = (GenXdev.Helpers\Get-WebLanguageDictionary)[$Language]

            # set accept-lang header if not already explicitly specified
            if (-not $PSBoundParameters.ContainsKey('AcceptLang')) {

                $null = $invocationArguments.AcceptLang = $code
            }
        }
    }


    process {

        # process each search query provided by the user
        foreach ($query in $Queries) {

            # output verbose information about the query being processed
            Microsoft.PowerShell.Utility\Write-Verbose "Processing query: $query"

            # construct the complete stack overflow search url with encoded query
            $invocationArguments.'Url' = 'https://stackoverflow.com/search?q=' +
            [Uri]::EscapeUriString($query)

            # handle return url only scenario without opening browser
            if ($ReturnOnlyURL) {

                # return the constructed url directly to the output stream
                Microsoft.PowerShell.Utility\Write-Output (
                    $invocationArguments.Url
                )

                # skip to next query without opening browser
                continue
            }

            # launch browser with the constructed search url
            GenXdev.Webbrowser\Open-Webbrowser @invocationArguments

            # return url if requested by user after opening browser
            if ($ReturnURL) {

                # output the constructed url to the output stream
                Microsoft.PowerShell.Utility\Write-Output (
                    $invocationArguments.Url
                )
            }
        }
    }

    end {
    }
}
################################################################################