Stack Pop

Adds a the first StackOverflow answer to your search query

Versione datata 25/12/2022. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Stack Pop
// @namespace    https://codeberg.org/happybits/stack-pop
// @version      1.0
// @license      MIT 
// @description  Adds a the first StackOverflow answer to your search query
// @author       happybits 
// @match        https://www.google.com/search*
// @icon
// @grant        GM_xmlhttpRequest
// ==/UserScript==

try {

    go()

    async function go() {

        // Wait for the google search to appear in the DOM

        const rso = await waitFor("#rso")

        // Inject StackPop before the google search result

        const stackDiv = document.createElement("div")
        stackDiv.id = "stack-pop"
        rso.before(stackDiv)
        
        // Find the first search result that leads to Stack Overflow
        
        const googleResults = Array.from(rso.children)

        const firstStackOverflowHit = googleResults

            .filter(result => result.querySelector("a")?.href)
            .map(result => result.querySelector("a").href)
            .find(link => link.startsWith("https://stackoverflow.com"))

        // If any, get the answer inject it to the search page

        if (firstStackOverflowHit) {

            // HTTP call using Tampermonkey's built in function

            GM_xmlhttpRequest({
                method: "GET",
                url: firstStackOverflowHit,
                onload: function (response) {

                    // Build a DOM from the text-response and parse the question and the first answer

                    const stackOverFlowPage = new DOMParser().parseFromString(response.responseText, 'text/html');
                    const question = stackOverFlowPage.querySelector("h1").textContent
                    const firstAnswerContent = stackOverFlowPage.querySelector(".answer .s-prose").innerHTML

                    // Styling for the StackOverflow answer 

                    const stackPopStyling =  `
                    #stack-pop {
                        width: 700px; 
                    }
                    
                    #stack-pop li {
                        margin-left: 1.5em;
                    }
                    
                    #stack-pop pre {
                        background-color: #eee;
                        padding: 1em;
                        width: fit-content;
                    }
                    
                    #stack-pop code {
                        background-color: #eee;
                    }
                    
                    #stack-pop img {
                        max-width: 100%;
                    }
                    `

                    // Create the StackPop widget
                    // Yes I know, it's inline styling etc, but it's by design, I think it's easy to read and compact!
                    // Or am I wrong ;) ?

                    stackDiv.innerHTML = `
                    
                    <style>${stackPopStyling}</style>

                    <div style="border:solid 2px #f48225; margin: 1em 0; overflow: auto;">

                        <a href="${firstStackOverflowHit}" style="text-decoration:none">
                            <div style="cursor:pointer; background-color:#f48225; color:white; padding:0.5em; font-size: 1.5em">
                                ${question}
                            </div>
                        </a>

                        <div style="padding:0 1.5em 1.5em 1.5em;">
                            ${firstAnswerContent}
                        </div>

                    </div>
                    `

                }
            });
        }


    }

    // This is a generic method that can be used to select element that may take some time to appear in the DOM
    // The second parameter "scope" is optional, of you want to limit the query

    function waitFor(selector, scope) {

        const pause = 10
        let maxTime = 10000

        return new Promise(resolve => {

            function inner() {
                if (maxTime <= 0) {
                    throw "Timeout for select " + selector
                }
                const element = (scope ?? document).querySelector(selector)

                if (element) {
                    resolve(element)
                    return
                }
                maxTime -= pause
                setTimeout(inner)
            }

            inner()
        })
    }

    // A simple log function which shows with a TAMPER-prefix

    function log(...message) {
        console.log('%c TAMPER ', 'color: white; background-color: #61dbfb', ...message);
    }

}

// Unexpected errors is shown in red

catch (exception) {
    console.log('%c TAMPER ', 'color: white; background-color: red', exception);
}