GreasyFork Code: Syntax Highlight by PrismJS

To syntax highlight GreasyFork Code by PrismJS

Versão de: 20/12/2023. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         GreasyFork Code: Syntax Highlight by PrismJS
// @namespace    Violentmonkey Scripts
// @grant        none
// @version      0.2.5
// @author       CY Fung
// @description  To syntax highlight GreasyFork Code by PrismJS
// @run-at       document-start
// @inject-into  page
// @unwrap
// @license      MIT
// @match        https://greasyfork.org/*
// @match        https://sleazyfork.org/*
// ==/UserScript==


(() => {
    let byPass = true;

    const cdn = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0';
    const resoruces = {
        'prism-core.js': `${cdn}/components/prism-core.js`,
        'prism-clike.js': `${cdn}/components/prism-clike.min.js`,
        'prism-javascript.js': `${cdn}/components/prism-javascript.min.js`,
        'prism-css.js': `${cdn}/components/prism-css.min.js`,
        'prism-stylus.js': `${cdn}/components/prism-stylus.min.js`,
        'prism.css': `${cdn}/themes/prism.min.css`,
        'prism-dark.css': `${cdn}/themes/prism-dark.min.css`,
    }

    function selectAllWithinElement(element) {
        // Clear any current selections
        window.getSelection().removeAllRanges();

        // Create a new range
        let range = document.createRange();

        // Check if the element exists
        if (element) {
            // Select all content within the element
            range.selectNodeContents(element);

            // Add the range to the selection
            window.getSelection().addRange(range);
        } else {
            console.error('Element not found with ID:', element);
        }
    }
    document.addEventListener('keydown', (e) => {
        if (e && e.code === 'KeyA' && e.isTrusted && (e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {

            const target = e.target;
            const container = target ? target.closest('div.code-container') : null;
            const code = container ? container.querySelector('code') : null;

            if (container && code) {

                e.preventDefault();
                e.stopPropagation();
                e.stopImmediatePropagation();

                setTimeout(() => {
                    selectAllWithinElement(code);
                }, 1)

            }

        }
    }, true)


    const Promise = (async function () { })().constructor;


    const PromiseExternal = ((resolve_, reject_) => {
        const h = (resolve, reject) => { resolve_ = resolve; reject_ = reject };
        return class PromiseExternal extends Promise {
            constructor(cb = h) {
                super(cb);
                if (cb === h) {
                    /** @type {(value: any) => void} */
                    this.resolve = resolve_;
                    /** @type {(reason?: any) => void} */
                    this.reject = reject_;
                }
            }
        };
    })();

    const documentReady = new Promise(resolve => {
        Promise.resolve().then(() => {
            if (document.readyState !== 'loading') {
                resolve();
            } else {
                window.addEventListener("DOMContentLoaded", resolve, false);
            }
        });
    });


    async function doAction() {

        await new Promise(r => setTimeout(r, 1));

        document.head.appendChild(document.createElement('style')).textContent = `

            .code-container{
                height:100vh;
            }
            .code-container .CodeMirror, .code-container textarea{
                height:100%;
            }
        `;

        if (window.requestIdleCallback) await new Promise(r => !!window.requestIdleCallback(r));
        else {
            await new Promise(r => !!window.requestAnimationFrame(r));
            await new Promise(r => !!window.setTimeout(r, 170));
            await new Promise(r => !!window.requestAnimationFrame(r));
        }

        byPass = false;

    }


    let mgg = 0;
    async function mTz() {
        if (mgg) return;
        mgg = 1;
        documentReady.then(doAction);
    }

    function getElementsByTagName(tag) {
        if (byPass) {
            if (tag === 'pre' || tag === 'code' || tag === 'xmp') {
                if (location.pathname.endsWith('/code')) {
                    setTimeout(mTz, 100)
                    return [];
                }
            }
        }
        return this.getElementsByTagName331(tag);
    }

    async function onBodyHeadReadyAsync() {

        if (document.body && document.head) {


        } else {

            const promiseBegin = new Promise(resolve => {

                let mo = new MutationObserver(() => {
                    if (document.body && document.head) {
                        mo.disconnect();
                        mo.takeRecords();
                        mo = null;
                        resolve();
                    }

                });
                mo.observe(document, { subtree: true, childList: true });

            });

            await promiseBegin.then();

        }

    }


    // Load CSS
    function loadJS(href) {

        return new Promise(resolve => {

            const script = document.createElement('script');
            script.src = href;
            script.onload = () => {
                resolve(script);
            };
            document.head.appendChild(script);

        });

    }

    // Load CSS
    function loadCSS(href) {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = href;
        document.head.appendChild(link);
        return link;
    }

    const global_css = `

        html {
            line-height: 1.5;
            -webkit-text-size-adjust: 100%;
            -moz-tab-size: 4;
            -o-tab-size: 4;
            tab-size: 4;
            font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
            font-feature-settings: normal;
            font-variation-settings: normal
        }
            /*
                body {
                    margin: 0;
                    line-height: inherit
                }

                hr {
                    height: 0;
                    color: inherit;
                    border-top-width: 1px
                }

                abbr:where([title]) {
                    -webkit-text-decoration: underline dotted;
                    text-decoration: underline dotted
                }

                h1,h2,h3,h4,h5,h6 {
                    font-size: inherit;
                    font-weight: inherit
                }


                b,strong {
                    font-weight: bolder
                }
            */
        .code-container code, .code-container kbd, .code-container pre, .code-container samp {
            font-family: ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;
            font-size: 1em
        }
            /*
                small {
                    font-size: 80%
                }

                sub,sup {
                    font-size: 75%;
                    line-height: 0;
                    position: relative;
                    vertical-align: baseline
                }

                sub {
                    bottom: -.25em
                }

                sup {
                    top: -.5em
                }

                table {
                    text-indent: 0;
                    border-color: inherit;
                    border-collapse: collapse
                }

                button,input,optgroup,select,textarea {
                    font-family: inherit;
                    font-feature-settings: inherit;
                    font-variation-settings: inherit;
                    font-size: 100%;
                    font-weight: inherit;
                    line-height: inherit;
                    color: inherit;
                    margin: 0;
                    padding: 0
                }

                button,select {
                    text-transform: none
                }

                :-moz-focusring {
                    outline: auto
                }

                :-moz-ui-invalid {
                    box-shadow: none
                }

                progress {
                    vertical-align: baseline
                }

                ::-webkit-inner-spin-button,::-webkit-outer-spin-button {
                    height: auto
                }

                [type=search] {
                    -webkit-appearance: textfield;
                    outline-offset: -2px
                }

                ::-webkit-search-decoration {
                    -webkit-appearance: none
                }

                ::-webkit-file-upload-button {
                    -webkit-appearance: button;
                    font: inherit
                }

                summary {
                    display: list-item
                }

                blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre {
                    margin: 0
                }

                fieldset {
                    margin: 0
                }

                fieldset,legend {
                    padding: 0
                }

                menu,ol,ul {
                    list-style: none;
                    margin: 0;
                    padding: 0
                }
                textarea {
                    resize: vertical
                }

                input::-moz-placeholder,textarea::-moz-placeholder {
                    opacity: 1;
                    color: #9ca3af
                }

                input::placeholder,textarea::placeholder {
                    opacity: 1;
                    color: #9ca3af
                }
            */

        #script-content > .code-container[class] {
            width: 100%;
        }

        .code-container[class] {
            border-radius: 0;
        }

        .code-container[class] {
            border-radius: 0;
        }

        .code-container > pre:only-child{
            padding:0;
        }

        code.syntax-highlighted[class] {
            /*
                font-family: ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace !important;
                font-size: 9pt;
            */
            font-family: monospace;
            font-size: 13px;
            font-variant-ligatures: contextual;
            line-height: 1.15rem;
            text-shadow: none !important;
        }





        .hljs-comment[class], .hljs-quote[class] {
            font-style: inherit;
            color: #259789;
        }

        .hljs-add-marker-width .marker-fixed-width[class] {
            user-select: none !important;
            width: calc(var(--hljs-marker-width, 0em) + 16px);
            background: #f4f4f4;
            padding-right: 6px;
            margin-right: 4px;
            contain: paint style;
        }


        [dark] .hljs-add-marker-width .marker-fixed-width[class] {

            background: #242424;
            color: #b6b2b2;
        }

    `;


    const cssForCodePage = /\/scripts\/\d+[^\s\/\\]*\/code(\/|$)/.test(location.href) ? `

        html:not([dkkfv]) div.code-container {
            position: absolute !important;
            display: none;
        }

        html:not([dkkfv]) div.code-container > pre{
            display:none;
        }


        html:not([dkkfv]) code:only-child {
            display:none;
        }


        .code-container,
        .code-container pre:only-child,
        .code-container pre:only-child code:only-child {
            max-height: calc(100vh + 4px);
            max-width: calc(100vw + 4px);
        }
    ` : '';


    const cssAdd = `

        ${global_css}

        ${cssForCodePage}

        .code-container {
            max-width: 100%;
            display: inline-flex;
            flex-direction: column;
            overflow: auto;
            border-radius: 8px;
            max-height: 100%;
            overflow: visible;
        }
        .code-container > pre:only-child {
            max-width: 100%;
            display: inline-flex;
            flex-direction: column;
            flex-grow: 1;
            height: 0;
        }
        .code-container > pre:only-child > code:only-child {
            max-width: 100%;
            flex-grow: 1;
            height: 0;
        }
        .code-container pre code {
            padding: 0;
            font-family: Consolas;
            cursor: text;
            overflow: auto;
        }
        .code-container pre code .marker {
            display: inline-block;
            color: #636d83;
            text-align: right;
            padding-right: 20px;
            user-select: none;
            cursor: auto;
        }

        .code-container[contenteditable]{
          outline: 0 !important;
          contain: strict;
        }

        .code-container[contenteditable]>pre[contenteditable="false"]{
          contain: strict;
        }




html {

  --token-color-keyword: #07a;
  --token-color-punctuation: #1415ec;
  --token-color-comment: #259789;
  --token-color-function: #da204f;
}

[dark] {

  --token-color-keyword: #898af2;
  --token-color-punctuation: #fadbdb;
  --token-color-comment:#59c6b9;
  --token-color-function: #e98aa2;

}


        body .token.comment {
    color: var(--token-color-comment);
}

body .token.atrule, body .token.attr-value, body .token.keyword {
    color: #1415ec;
    color: var(--token-color-keyword);
}


.language-stylus .token.atrule, .language-stylus .token.attr-value, .language-stylus .token.keyword {
    color: #700d0d;
}


body .token.punctuation{
  color: var(--token-color-punctuation);
}


body .token.variable-declaration,
body  .token.variable {
    color: #0d10cd;
}

body .token.selector{
color: #1373bb;
}


body .token.function {
color:var(--token-color-function);
}



.language-stylus .token.variable-declaration,
.language-stylus .token.variable {
    color: #0d10cd;
}

.language-stylus .token.selector{
color: #1373bb;
}

.language-stylus .token.punctuation{
color:#700d0d;
}


.language-stylus .token.function {
color:#da204f
}




    `;




    HTMLElement.prototype.getElementsByTagName331 = HTMLElement.prototype.getElementsByTagName
    Document.prototype.getElementsByTagName331 = Document.prototype.getElementsByTagName

    HTMLElement.prototype.getElementsByTagName = getElementsByTagName
    Document.prototype.getElementsByTagName = getElementsByTagName


    const pScript = new PromiseExternal();

    onBodyHeadReadyAsync().then(async () => {

        if (!location.pathname.endsWith('/code')) {
            return;
        }

        document.head.appendChild(document.createElement('style')).textContent = `${cssAdd}`;

        self.Prism = self.Prism || {};
        self.Prism.manual = true;
        await loadJS(resoruces['prism-core.js']);
        await loadJS(resoruces['prism-clike.js']);
        await loadJS(resoruces['prism-javascript.js']);
        await loadJS(resoruces['prism-css.js']);
        await loadJS(resoruces['prism-stylus.js']);

        if (document.documentElement.hasAttribute('dark')) {

            loadCSS(resoruces['prism-dark.css']);
        } else {

            loadCSS(resoruces['prism.css']);
        }

        pScript.resolve();




    });


    /** @param {HTMLElement} pre */
    async function prepareCodeAreaAsync(pre) {

        for (const li of pre.querySelectorAll('li')) {
            li.append(document.createTextNode('\n'));
        }

        const codeElement = document.createElement('code');
        // codeElement.classList.add('language-javascript');
        codeElement.innerHTML = pre.innerHTML;

        // Clearing the original code container and appending the new one
        // pre.classList = '';
        pre.innerHTML = '';
        // pre.appendChild(codeElement);

        // if (pre.querySelector('code')) return;
        const code = codeElement;

        const codeContainer = pre.closest('.code-container');
        if (codeContainer && codeContainer.querySelector('.code-container>pre:only-child')) {
            // avoid selection to the outside by mouse dragging
            codeContainer.setAttribute('contenteditable', '');
            codeContainer.querySelector('.code-container>pre:only-child').setAttribute('contenteditable', 'false');
        }


        // let parentNode = code.parentNode;
        // let nextNode = code.nextSibling;

        // code.remove();
        let parentNode = pre;
        let nextNode = null;
        await Promise.resolve().then();

        // preset language
        /*
        const text = codeElement.textContent;
        if(/(^|\n)\s*\/\/\s+==UserScript==\s*\n/.test(text)){
            codeElement.classList.add('language-javascript');
        }else if(/(^|\n)\s*\/\*\s+==UserStyle==\s*\n/.test(text)){
            codeElement.classList.add('language-css');
        }
        */
        let className = '';
        if (pre.classList.contains('lang-js')) {
            className = 'language-javascript';
            codeElement.classList.add();
        } else if (pre.classList.contains('lang-css')) {

            const text = codeElement.textContent;
            let m = /\n\@preprocessor\s+([-_a-zA-Z]{3,8})\s*\n/.exec(text);
            className = 'language-css'
            if (m) {
                const preprocessor = m[1];
                if (preprocessor === 'stylus') {
                    className = 'language-stylus';
                } else if (preprocessor === 'uso') {
                    className = 'language-stylus';
                } else if (preprocessor === 'less') {
                    className = 'language-less';
                } else if (preprocessor === 'default') {
                    className = 'language-stylus';
                } else {
                    className = 'language-stylus';
                }
            }


        }


        if (!className) return;




        codeElement.classList.add(className);
        codeElement.classList.add('syntax-highlighted')

        let htmlCode = '';

        if (className === 'language-javascript') {


            htmlCode = Prism.highlight(code.textContent, Prism.languages.javascript, 'javascript');
        } else if (className === 'language-stylus') {

            htmlCode = Prism.highlight(code.textContent, Prism.languages.stylus, 'stylus');
        } else if (className === 'language-less') {

            htmlCode = Prism.highlight(code.textContent, Prism.languages.less, 'less');

        } else {

            htmlCode = Prism.highlight(code.textContent, Prism.languages.css, 'css');
        }



        // Adding line numbers
        htmlCode = htmlCode || '';
        const htmlSplit = htmlCode ? htmlCode.split('\n') : [];
        const totalLines = htmlSplit.length;

        let mwStyle = '';

        if (totalLines >= 1) {
            code.classList.add('hljs-add-marker-width');
            const len = `${totalLines}`.length * 0.5;
            mwStyle = `${len}em`;
            htmlCode = htmlSplit.map((n, i) => `<span class="marker marker-fixed-width">${i + 1}</span>${n}`).join('\n');

        } else {

            code.classList.remove('hljs-add-marker-width');
        }

        let targetCode = code;

        if (htmlCode) {

            const template = document.createElement('template');

            template.innerHTML = `<code>${htmlCode}</code>`;
            const code2 = template.content.firstChild;
            for (const className of code.classList) {
                code2.classList.add(className);
            }

            targetCode = code2;
        } else {
            code.innerHTML = "";
            targetCode = code;
        }

        targetCode.style.setProperty('--hljs-marker-width', mwStyle);

        parentNode.insertBefore(targetCode, nextNode);



    }
    documentReady.then(async () => {

        if (!location.pathname.endsWith('/code')) {
            byPass = false;
            return;
        }

        await pScript.then();

        // Code highlighting
        const promises = [...document.querySelectorAll('.code-container pre.lang-js, .code-container pre.lang-css')].map(prepareCodeAreaAsync)
        Promise.all(promises).then(() => {
            setTimeout(() => {
                document.documentElement.setAttribute('dkkfv', '');
            }, 1);
        })

    });


})();