GreasyFork Code: Syntax Highlight by PrismJS

To syntax highlight GreasyFork Code by PrismJS

Versión del día 14/12/2023. Echa un vistazo a la versión más reciente.

// ==UserScript==
// @name         GreasyFork Code: Syntax Highlight by PrismJS
// @namespace    Violentmonkey Scripts
// @grant        none
// @version      0.2.0
// @author       CY Fung
// @description  To syntax highlight GreasyFork Code by PrismJS
// @run-at       document-start
// @inject-into  page
// @unwrap
// @license      MIT
// @match        https://greatest.deepsurf.us/*
// @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`
    }

    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);
    }

    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;
        }





        .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;
        }

    `;


    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;
        }



        body .token.comment {
    color: #259789;
}




body .token.punctuation{
color:#700d0d;
}


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

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


body .token.function {
color:#da204f
}



.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 () => {

        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']);
        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;
        }

        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);
        })

    });


})();