GreasyFork Search

To search scripts using Google Search

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

Bạn sẽ cần cài đặt một tiện ích mở rộng như Tampermonkey hoặc Violentmonkey để cài đặt kịch bản này.

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

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

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

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         GreasyFork Search
// @namespace    http://tampermonkey.net/
// @version      0.6.9
// @description  To search scripts using Google Search
// @author       CY Fung
// @match        https://greatest.deepsurf.us/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=greatest.deepsurf.us
// @require      https://fastly.jsdelivr.net/npm/[email protected]/dist/jstat.min.js
// @grant        none
// @license MIT
// ==/UserScript==



(() => {

    function jacobi(a) {
        var n = a.length;
        var trial = n * n * 2;
        var e = jStat.identity(n, n);
        var ev = [];
        var i, j, p, q, maxim, s;
        let vaildResult = false;
        outer: while (trial-- > 0) {
            maxim = a[0][1];
            p = 0;
            q = 1;
            for (i = 0; i < n; i++) {
                for (j = 0; j < n; j++) {
                    if (i != j) {
                        let t = Math.abs(a[i][j]);
                        if (maxim < t) {
                            maxim = t;
                            p = i;
                            q = j
                        }
                    }
                }
            }

            s = jStat.identity(n, n);

            let tanValue = 2 * a[p][q] / (a[p][p] - a[q][q]);

            let cosTwoTheta = Math.sqrt(1 / (1 + tanValue * tanValue));
            let cosTheta = Math.sqrt(.5 * (1 + cosTwoTheta));
            let sinTheta = Math.sqrt(.5 * (1 - cosTwoTheta));

            s[p][p] = cosTheta;
            s[p][q] = -sinTheta;
            s[q][p] = sinTheta;
            s[q][q] = cosTheta;

            e = jStat.multiply(e, s);
            a = jStat.multiply(jStat.multiply(jStat.transpose(s), a), s);

            for (i = 0; i < n; i++) {
                for (j = i + 1; j < n; j++) {
                    if (Math.abs(a[i][j]) > .0004) {
                        continue outer;
                    }
                }
            }

            vaildResult = true;
            break;
        }
        if (!vaildResult) {
            console.warn("The matrix is not symmetric.")
            return null;
        }
        for (i = 0; i < n; i++) ev.push(a[i][i]);
        //returns both the eigenvalue and eigenmatrix
        return [e, ev];
    }



    function getVN(A) {
        // normalized the matrix values such that det(A) will be a finite value close to 1.0
        // vn = sqrt( ( column_vector_1 ^2 + column_vector_2 ^2 + ... + column_vector_n ^2 ) / n )
        let vn = 0;
        const AT = jStat.transpose(A);
        let N = AT.length;
        for (let i = 0; i < N; i++) {
            vn += jStat.dot(AT[i], AT[i]);
        }
        vn = Math.sqrt(vn / N);
        return vn;
    }

    function subtractLambdaFromDiagonal(matrix, lambda) {
        // A - lambda I
        return matrix.map((row, rowIndex) => row.map((val, colIndex) => rowIndex === colIndex ? val - lambda : val));
    }


    function eigenvalueNewton(A, lambda0) {
        const N = A.length;
        const epsilon = 1e-5; // epsilon is applied on the normalized scale of lambda
        const maxTrial = 8;

        function f(lambda) {
            return jStat.det(subtractLambdaFromDiagonal(A, lambda));
        }

        function fPrime(lambda) {
            return (f(lambda + epsilon) - f(lambda)) / epsilon;
        }

        let x_k = lambda0;
        let positiveSign = 0;
        let negativeSign = 0;
        for (let i = 0; i < maxTrial; i++) {
            const fx = f(x_k);
            const fxPrime = fPrime(x_k);
            const diff = fx / fxPrime;
            if (isNaN(diff)) return x_k; // ignore f/f'
            const x_k1 = x_k - diff;
            if ((diff > 0 ? diff : -diff) < epsilon) {
                return x_k1;
            }
            x_k = x_k1;
            if (fx > 0) positiveSign = 1;
            else if (fx < 0) negativeSign = 1;
        }
        return positiveSign && negativeSign ? x_k : lambda0; // avoid diverging iterations
    }

    function vectorNorm(v) {
        // Math.sqrt(v dot v), same as jStat.norm(jStat.transpose(v))
        let s = 0;
        for (const k of v) s += k[0] * k[0];
        return Math.sqrt(s);
    }

    function isUnitVector(v, tol = 0.01) {
        // Check if it is likely a unit vector
        let s = 0;
        for (const k of v) {
            s += k[0] * k[0];
            if (s > 1 + tol) return false;
        }
        return s > 1 - tol;
    }

    jStat.jacobiOri = jStat.jacobi;
    // https://www.statskingdom.com/pca-calculator.html
    jStat.jacobi = function (C) {

        const vn = getVN(C);
        C = jStat.multiply(C, 1 / vn);
        let r1 = jacobi(C);
        // let r0 = JSON.parse(JSON.stringify(r1))
        // r0[1] = r0[1].map(v => vn * v);
        let A = C;
        let eigenvectors = r1[0];
        let eigenvalues = r1[1];
        const iterationCount = 4;

        for (let i = 0; i < eigenvalues.length; i++) {
            let q, m;
            q = jStat.transpose(eigenvectors[i]);
            if (!isUnitVector(q)) break;
            eigenvalues[i] = eigenvalueNewton(A, eigenvalues[i]); // refine eigenvalues obtained in jacobiOri

            // inverse power method (A-lambda I) y_k = b_{k-1}
            // b_k = y_k / norm(y_k)
            let M = subtractLambdaFromDiagonal(A, eigenvalues[i]);
            for (let j = 0; j < iterationCount; j++) {
                m = jStat.transpose(jStat.gauss_elimination(M, q));
                m = jStat.multiply(m, 1 / vectorNorm(m))
                if (!isUnitVector(m)) break; // avoid Inf / NaN error
                q = m;
            }
            eigenvectors[i] = jStat.transpose(q);

        }
        r1[1] = r1[1].map(v => vn * v);
        return r1;
    };



})();


jStat.PCA = function PCA(X) {
    var m = X.length;
    var n = X[0].length;
    var i = 0;
    var j, temp1;
    var u = [];
    var D = [];
    var result = [];
    var temp2 = [];
    var Y = [];
    var Bt = [];
    var B = [];
    var C = [];
    var V = [];
    var Vt = [];
    for (i = 0; i < m; i++) {
        u[i] = jStat.sum(X[i]) / n;
    }
    for (i = 0; i < n; i++) {
        B[i] = [];
        for (j = 0; j < m; j++) {
            B[i][j] = X[j][i] - u[j];
        }
    }
    B = jStat.transpose(B);
    for (i = 0; i < m; i++) {
        C[i] = [];
        for (j = 0; j < m; j++) {
            C[i][j] = (jStat.dot([B[i]], [B[j]])) / (n - 1);
        }
    }
    result = jStat.jacobi(C);
    V = result[0];
    D = result[1];

    Vt = jStat.transpose(V);


    let vd = [];
    for (i = 0; i < D.length; i++) {
        vd[i] = {
            Vt: Vt[i],
            D: D[i],
            k: D[i] * D[i]
        };
    }

    vd.sort((a, b) => {
        return b.k - a.k
    })

    Vt = vd.map(e => e.Vt);
    D = vd.map(e => e.D);



    V = null;


    Bt = jStat.transpose(B);

    let pcs_11 = [];
    let pt_11 = [1, 1];
    for (i = 0; i < m; i++) {


        pcs_11[i] = jStat.dot([Vt[i]], [pt_11]);
        if (pcs_11[i] < 0) Vt[i] = jStat.multiply(Vt[i], -1);
        pcs_11[i] = jStat.dot([Vt[i]], [pt_11]);
    }





    for (i = 0; i < m; i++) {
        Y[i] = [];
        for (j = 0; j < Bt.length; j++) {
            Y[i][j] = jStat.dot([Vt[i]], [Bt[j]]);
        }
    }
    return [X, D, Vt, Y];
};

(function () {
    'use strict';

    let input = document.querySelector('form input[name="q"]');
    if (!(input instanceof HTMLInputElement)) return;
    let form = input.closest('form');
    if (!(form instanceof HTMLFormElement)) return;


    let locales = [...document.querySelectorAll('select#language-selector-locale > option')].map(x => x.value)

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


    @keyframes rs1tmAnimation {
        0% {
            background-position-x: 3px;
        }
        100% {
            background-position-x: 4px;
        }
    }

    form.rs1tm{
        position: fixed;
        top:-300px;
        left:-300px;
        width: 1px;
        height: 1px;
        contain: strict;
        display: flex;
        overflow: hidden;
        animation: rs1tmAnimation 1ms linear 1ms 1 normal forwards;
    }

    `
    document.addEventListener('animationstart', (evt) => {

        if (evt.animationName === 'rs1tmAnimation') {
            const target = evt.target;
            target && target.parentNode && target.remove();
        }

    }, true);

    window.callback947 = function (rainijpolynomialRegressionJs) {
        if (!rainijpolynomialRegressionJs) return;
        const { PolynomialFeatures, PolynomialRegressor, RegressionError } = rainijpolynomialRegressionJs;
        if (!PolynomialFeatures || !PolynomialRegressor || !RegressionError) return;

        console.log(rainijpolynomialRegressionJs)
    }

    form.addEventListener('submit', function (evt) {

        try {


            let form = evt.target;
            if (!(form instanceof HTMLFormElement)) return;
            let input = form.querySelector('input[name="q"]');
            if (!(input instanceof HTMLInputElement)) return;

            if (form.classList.contains('rs1tm')) return;

            let value = input.value;
            const lang = document.documentElement.lang || '';

            let useLang = false;


            let u = 0;
            let isGoogleSearch = false;

            let sites = [];

            const split = value.split(/\s+/);
            let forceLang = 'all';
            let reformedSplit = [];
            for (const s of split) {

                if (!isGoogleSearch && /^[a-z][a-z0-9_-]{2,}(\.[a-z][a-z0-9_-]{2,})*(\.[a-z-]{2,4})+$/.test(s)) {
                    if (/\.(js|css|html|htm|xml|img|svg|txt|php|cgi|xhtml|ini|vue|xhr|ajax)$/.test(s)) {
                        reformedSplit.push(s);
                    } else {
                        sites.push(s);
                    }
                } else if (s === 'js') {
                    forceLang = 'js'; reformedSplit.push(s);
                } else if (s === 'css') {
                    forceLang = 'css'; reformedSplit.push(s);
                } else if (s === 'user.js') {
                    forceLang = 'js';
                } else if (s === 'user.css') {
                    forceLang = 'css';
                } else if (s === '"js"') {
                    reformedSplit.push('js');
                } else if (s === '"css"') {
                    reformedSplit.push('css');
                } else if (u === 0 && s === 'g') {
                    isGoogleSearch = true;
                } else if (locales.indexOf(s) >= 0 || s === lang) {
                    useLang = s;
                } else {
                    reformedSplit.push(s);
                }
                u++;
            }
            console.log(sites)

            value = reformedSplit.join(' ')

            let onlySite = '';

            if (sites.length === 1 && sites[0]) {
                onlySite = sites[0];
            }

            /*
              if (!isGoogleSearch && onlySite && /\.\w+\.\w+/.test(onlySite)) {
                  alert('Greasy Fork only lists eTLD+1.');
                      evt.preventDefault();
                  evt.stopImmediatePropagation();
                  evt.stopPropagation();
                  return;
              }
              */


            if (isGoogleSearch && value) {
                let q = value.replace('g ', '');

                let m = "-inurl%3A%22%2Fusers%2F%22+-inurl%3A%22%2Fdiscussions%22-inurl%3A%22%2Fstats%22+-inurl%3A%22%2Ffeedback%22+-inurl%3A%22%2Fcode%22+-inurl%3A%22q%3D%22+-inurl%3A%22%2Fby-site%2F%22+inurl%3A%22%2Fscripts%2F%22+site%3Agreatest.deepsurf.us";



                let lr = useLang ? `&lr=lang_${useLang}` : '';
                evt.preventDefault();
                evt.stopImmediatePropagation();
                evt.stopPropagation();
                location.href = `https://www.google.com/search?q=${encodeURIComponent(q)}+${m}${lr}`

            } else if (!isGoogleSearch && (value || onlySite)) {


                let newForm = document.createElement('form');
                newForm.className = 'rs1tm';
                const copyAttr = (x) => {
                    let t = form.getAttribute(x);
                    if (typeof t === 'string') newForm.setAttribute(x, t);
                }
                copyAttr('action');
                copyAttr('accept-charset');
                copyAttr('method');
                newForm.innerHTML = `<input name="q" type="hidden" value="" /><input name="site" type="hidden" /><input name="language" type="hidden" value="all" /><input name="sort" type="hidden" /><input name="vl" type="hidden" value="">`


                const nq = newForm.querySelector('input[name="q"]');
                const language = newForm.querySelector('input[name="language"]');
                const site = newForm.querySelector('input[name="site"]');
                const sort = newForm.querySelector('input[name="sort"]');

                value = value.replace(/\s+/g, ' ');
                site.value = onlySite;

                if (form.getAttribute('action') === `/${lang}/scripts` && useLang && useLang !== lang) {
                    form.setAttribute('action', `/${useLang}/scripts`)
                }


                if (site.value === '') site.remove();

                nq.value = value;

                language.value = forceLang;

                if (language.value === '') language.remove();


                sort.value = 'updated';

                let sorting = document.querySelector('#script-list-sort');
                if (sorting) {
                    let sorts1 = {
                        nil: 0,
                        daily_installs: 0,
                        total_installs: 0,
                        ratings: 0,
                        created: 0,
                        updated: 0,
                        name: 0
                    }
                    let sorts2 = {
                        daily_installs: 0,
                        total_installs: 0,
                        ratings: 0,
                        created: 0,
                        updated: 0,
                        name: 0
                    }
                    const allOptions = sorting.querySelectorAll('.list-option');
                    const sorts = allOptions.length === 6 ? (sorts2) : (sorts1);
                    const keys = Object.keys(sorts)

                    if (allOptions.length === keys.length) {


                        for (const key of keys) {
                            let e = `.list-option:not(.list-current) a[href$="sort=${key}"]`
                            if (key === 'nil') {
                                e = `.list-option:not(.list-current) a[href]:not([href*="sort="])`
                                e = sorting.querySelector(e)
                            } else {
                                e = sorting.querySelector(e)
                            }

                            if (e) {
                                sorts[key] = 1;
                            }

                        }



                        let p = Object.entries(sorts).filter(r => !r[1])
                        if (p.length === 1) {
                            sort.value = p[0][0]
                        }

                    }

                }




                if (sort.value === '') sort.remove();

                evt.preventDefault();
                evt.stopImmediatePropagation();
                evt.stopPropagation();

                form.parentNode.insertBefore(newForm, form);
                newForm.submit();
                Promise.resolve().then(() => {
                    newForm.remove();
                })


            } else {
                evt.preventDefault();
                evt.stopImmediatePropagation();
                evt.stopPropagation();
            }

        } catch (e) {
            console.log(e);

            evt.preventDefault();
            evt.stopImmediatePropagation();
            evt.stopPropagation();
        }

    })

    // Your code here...
})();

(() => {

    function prettyMatrix(A) {
        let w = '';
        for (let i = 0; i < A.length; i++) {
            for (let j = 0; j < A[i].length; j++) {
                w += A[i][j].toFixed(4) + '\t'
            }
            w += '\n\t';
        }
        return '[\n\t' + w.trim() + '\n]';
    }

    const getY = (data) => {

        const daily = data.map(d => d.daily * 100 + Math.round(25 + 50 * Math.random()));
        const total = data.map(d => d.total * 100 + Math.round(25 + 50 * Math.random()));
        const dailyMean = jStat.mean(daily);
        const dailySD = jStat.stdev(daily, true);
        const totalMean = jStat.mean(total);
        const totalSD = jStat.stdev(total, true);

        const uDaily = jStat.multiply(jStat.subtract(daily, dailyMean), 1 / dailySD);
        const uTotal = jStat.multiply(jStat.subtract(total, totalMean), 1 / totalSD);

        const dataA = data.map((d, i) => [uDaily[i], uTotal[i]]);

        // dataA = dataA.slice(0, 4)
        // console.log(dataA)

        const matrixA = jStat.transpose(dataA)


        const result = jStat.PCA(matrixA);
        const [X, D, Vt, Y] = result;

        return Y;

    }

    class AvgMap {
        constructor() {
            this.map = new Map();
        }
        add(key, val) {
            let m = this.map.get(key);
            if (!m) this.map.set(key, (m = [0, 0]));
            m[0] += val;
            m[1]++;
        }
        avg(key) {
            let m = this.map.get(key);
            if (!m) return 0;
            return m[0] / m[1];
        }
    }


    requestAnimationFrame(() => {

        setTimeout(() => {

            if ((location.search.includes('sort=updated') || location.search.includes('sort=created')) && location.pathname.endsWith('/scripts')) { } else return;
            if (!/[?&]vl=/.test(location.search)) return;
            let items = document.querySelectorAll('[data-script-id][data-script-daily-installs][data-script-total-installs]');

            let data = [...items].map(e => ({
                id: parseInt(e.getAttribute('data-script-id')),
                daily: parseInt(e.getAttribute('data-script-daily-installs')),
                total: parseInt(e.getAttribute('data-script-total-installs'))
            })).filter(e => e.id && !isNaN(e.daily) && !isNaN(e.total));
            const Y = getY(data);



            let q = null;
            let qSet = null;
            if (location.search.includes('q=')) {
                q = new URLSearchParams(location.search)
                q = q.get('q')

            }

            function makeQA(q) {
                let qSet = new Set();
                q.replace(/_-/g, ' ').replace(/\b\S+\b/g, (_) => {
                    qSet.add(_.toLowerCase())
                });
                return qSet;
            }

            if (q) {
                qSet = makeQA(q);
            }

            let mr = new Map();
            let u = 0;

            const pcaScoreMap = new AvgMap();
            for (const d of data) {
                const k = `${d.daily}|${d.total}`;
                pcaScoreMap.add(k, Y[0][u++]);
            }

            for (const d of data) {
                const k = `${d.daily}|${d.total}`;
                d.pcaScore = pcaScoreMap.avg(k);
                let elm = document.querySelector(`[data-script-id="${d.id}"]`);
                if (elm) {

                    let order = 0;
                    order -= Math.floor(d.pcaScore * 1000);

                    let u1 = new Set(), u2 = new Set();

                    if (qSet) {

                        const pSet = qSet;

                        let elp = elm.querySelector('.script-link')
                        if (elp) {
                            let t = elp.textContent

                            t.replace(/_-/g, ' ').replace(/\b\S+\b/g, (_) => {
                                if (pSet.has(_.toLowerCase())) u1.add(_.toLowerCase());
                            });


                        }



                        let elq = elm.querySelector('.script-description')

                        if (elq) {
                            let t = elq.textContent

                            t.replace(/_-/g, ' ').replace(/\b\S+\b/g, (_) => {
                                if (pSet.has(_.toLowerCase())) u2.add(_.toLowerCase());
                            });


                        }


                    }
                    u1 = u1.size;
                    u2 = u2.size;

                    if (u1 > 0 && u2 > 0 && u1 > 3 * u2 + 2) order -= 58000
                    else if (u1 > 0 && u2 > 0 && u2 > 3 * u1 + 2) order -= 54000
                    else if (u1 > 0 && u2 > 0 && u1 > 2 * u2 + 1) order -= 48000
                    else if (u1 > 0 && u2 > 0 && u2 > 2 * u1 + 1) order -= 44000
                    else if (u1 > 0 && u2 > 0 && u1 > u2) order -= 38000
                    else if (u1 > 0 && u2 > 0 && u2 > u1) order -= 34000
                    else if (u1 && u2) order -= 30000
                    else if (u1) order -= 20000
                    else if (u2) order -= 10000


                    mr.set(d.id, order);
                    // elm.style.order = order;
                    // elm.parentNode.style.display = 'flex';


                    // elm.parentNode.style.flexDirection = 'column';


                }
            }


            let lists = [...new Set([...document.querySelectorAll(`[data-script-id]`)].map(p => p.parentNode))];
            for (const list of lists) {

                let m = [...list.childNodes].map((e, originalIdx) => ({
                    originalIdx,
                    element: e,
                    order: mr.get(e instanceof HTMLElement ? (+e.getAttribute('data-script-id') || '') : '') || 0
                }));

                m.sort((a, b) => {
                    return a.order - b.order || a.originalIdx - b.originalIdx
                });
                let newNodes = m.map(e => e.element);

                list.replaceChildren(...newNodes);
            }


            // console.log(prettyMatrix(X))

            // console.log(prettyMatrix(Y))





        }, 300);

    });



})();