مدیاویکی:Gadget-PageContributions.js

از ویکی‌گفتاورد

نکته: پس از انتشار ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.

  • فایرفاکس / سافاری: کلید Shift را نگه دارید و روی دکمهٔ Reload کلیک کنید، یا کلید‌های Ctrl-F5 یا Ctrl-R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-R)
  • گوگل کروم: کلیدهای Ctrl+Shift+R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-Shift-R)
  • اینترنت اکسپلورر/ Edge: کلید Ctrl را نگه‌دارید و روی دکمهٔ Refresh کلیک کنید، یا کلید‌های Ctrl-F5 را با هم فشار دهید
  • اپرا: Ctrl-F5 را بفشارید.
(function(mw, $) {
 
    var query = {
        action: 'query',
        prop: 'revisions',
        titles: mw.config.get('wgPageName'),
        rvlimit:10,
        rvprop: 'ids|timestamp|user|size|content',
        format: 'json'
    };
 
    var inProgress = false;
 
    var isOpen = false;
 
    var revisions = [];
 
    var str = null;
 
    var hidden = 0;
 
    function toHex(n) {
        if (n < 0) n = 0xFFFFFFFF + n + 1;
        return n.toString(16);
    };
 
    function fnv(str) {
        // this is an implementation of the Fowler-Noll-Vo hash algorithm
        var hash = 2166136261; // the 32 bit offset
        for (var i = 0, l = str.length; i < l; i++) {
            hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
            hash = hash ^ str.charCodeAt(i);
        }
        return hash & 0x0ffffffff;
    };
 
    var perm = [ // permutations for the lsh algorithm
        1, 14, 110, 25, 97, 174, 132, 119, 138, 170, 125, 118, 27, 233, 140, 51,
        87, 197, 177, 107, 234, 169, 56, 68, 30, 7, 173, 73, 188, 40, 36, 65,
        49, 213, 104, 190, 57, 211, 148, 223, 48, 115, 15, 2, 67, 186, 210, 28,
        12, 181, 103, 70, 22, 58, 75, 78, 183, 167, 238, 157, 124, 147, 172, 144,
        176, 161, 141, 86, 60, 66, 128, 83, 156, 241, 79, 46, 168, 198, 41, 254,
        178, 85, 253, 237, 250, 154, 133, 88, 35, 206, 95, 116, 252, 192, 54, 221,
        102, 218, 255, 240, 82, 106, 158, 201, 61, 3, 89, 9, 42, 155, 159, 93,
        166, 80, 50, 34, 175, 195, 100, 99, 26, 150, 16, 145, 4, 33, 8, 189,
        121, 64, 77, 72, 208, 245, 130, 122, 143, 55, 105, 134, 29, 164, 185, 194,
        193, 239, 101, 242, 5, 171, 126, 11, 74, 59, 137, 228, 108, 191, 232, 139,
        6, 24, 81, 20, 127, 17, 91, 92, 251, 151, 225, 207, 21, 98, 113, 112,
        84, 226, 18, 214, 199, 187, 13, 32, 94, 220, 224, 212, 247, 204, 196, 43,
        249, 236, 45, 244, 111, 182, 153, 136, 129, 90, 217, 202, 19, 165, 231, 71,
        230, 142, 96, 227, 62, 179, 246, 114, 162, 53, 160, 215, 205, 180, 47, 109,
        44, 38, 31, 149, 135, 0, 216, 52, 63, 23, 37, 69, 39, 117, 146, 184,
        163, 200, 222, 235, 248, 243, 219, 10, 152, 131, 123, 229, 203, 76, 120, 209 ];
 
     function lsh(str) {
        // this is an implementation of a lsh algorithm, loosley similar to nilsimsa
        var arr = Array();
        for (var i = 0; i <= 0x0ff; i++) {
            arr[i] = 0;
        }
        for (var i = 0, l = str.length-2; i < l; i++) {
             // first we do a Pearson hash of trigram from the text
             var hash = 0;
             hash = perm[ (hash ^ str.charCodeAt(i)) & 0x0ff ];
             hash = perm[ (hash ^ str.charCodeAt(i+1)) & 0x0ff ];
             hash = perm[ (hash ^ str.charCodeAt(i+2)) & 0x0ff ];
             // then we accumulate in the array at indices given by the hash
             arr[hash]++;
        }
        // fold the array
        for (var i = 0; i <= 0x01f; i++) {
            arr[i] += arr[i+0x020] + arr[i+0x040] + arr[i+0x060] + arr[i+0x080] + arr[i+0x0a0] + arr[i+0x0c0] + arr[i+0x0e0];
        }
        var acc = 0;
        var lim = arr.slice(0, 0x01f).sort(function(a,b){return a-b})[0x00f];
        for (var i = 0; i <= 0x01f; i++) {
            acc |= (((lim < arr[i]) ? 0 : 1) << i);
        }
        return acc & 0x0ffffffff;
    };
 
    function rvec(str) {
        // this is an implementation of a subspace reduction algorithm based upon the Pearson hash
        var arr = new Array();
        for (var i = 0; i <= 0x0ff; i++) {
            arr[i] = 0;
        }
        for (var i = 0, l = str.length-2; i < l; i++) {
             // first we do a Pearson hash of trigram from the text
             var hash = 0;
             hash = perm[ (hash ^ str.charCodeAt(i)) & 0x0ff ];
             hash = perm[ (hash ^ str.charCodeAt(i+1)) & 0x0ff ];
             hash = perm[ (hash ^ str.charCodeAt(i+2)) & 0x0ff ];
             // then we accumulate in the array at indices given by the hash
             arr[hash]++;
        }
        return arr;
    };
 
    function beans(n) {
        // implementation of a bean counter
        // make an unsigned 32bits int
        if (n < 0) n = 0xFFFFFFFF + n + 1;
        // From Hacker's Delight, p. 66, Figure 5-2
        n = n - ((n >> 1) & 0x55555555);
        n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
        n = (n + (n >> 4)) & 0x0F0F0F0F;
        n = n + (n >> 8);
        n = n + (n >> 16);
        return n & 0x0000003F;
    };
 
    function cosine(vec1, vec2) {
        // really just a difference measure for the trigram vectors
        var combined = Object();
        for (var x in vec1)
            combined[x] = vec1[x];
        for (var x in vec2) {
            if (combined[x] == null) combined[x] = 0;
            combined[x] -= vec2[x];
        }
        var acc = 0;
        for (var x in combined)
            acc += combined[x] * combined[x];
        return Math.sqrt(acc);
    };
 
    function rcos(vec1, vec2) {
        // really just a difference measure for the trigram vectors
        var acc = 0;
        for (var i = 0; i <= 0x0ff; i++)
            acc += Math.pow((vec1 ? vec1[i] : 0) - (vec2 ? vec2[i] : 0), 2);
        return Math.sqrt(acc);
    };
 
    function onclick() {
        // just an onclick handler to start processing
        if (str === null) {
            if (!inProgress) {
                inProgress = true;
                isOpen = true;
                jsMsg('<h3>فهرست مشارکت‌کنندگان اصلی</h3><small id="progress" />', 'page-authors' );
                $('#mw-js-message.mw-js-message-page-authors h3').each(function(i, el) { injectSpinner( el, 'page-authors' ); });
                getRevisions();
            }
            else {
                alert('Already in progress!');
            }
        }
        else {
            if (!isOpen) {
                isOpen = true;
                jsMsg('<h3>فهرست مشارکت‌کنندگان اصلی</h3>' + str, 'page-authors' );
                $('#mw-js-message.mw-js-message-page-authors').show().find('ul').slideDown(1000);
 
            }
            else {
                isOpen = false;
                $('#mw-js-message.mw-js-message-page-authors').slideUp(1000);
            }
        }
        return false;
    };
 
    function getRevisions() {
        // ajax object
        $.ajax({
            url: mw.util.wikiScript( 'api' ),
            dataType: 'json',
            data: query,
            context: document.body,
            success: function(data, textStatus) {
                if (textStatus != 'success') {
                    alert('Success, but with "' + textStatus + '"');
                    $('#mw-js-message.mw-js-message-page-authors').slideUp(1000);
                }
                var r = data['query'].pages[parseInt(mw.config.get('wgArticleId'))].revisions;
                for (var x = 0; x < r.length; x++) {
                    if (typeof r[x]['*'] == 'undefined') {
                        hidden++;
                        continue;
                    }
                    r[x].lsh = lsh(r[x]['*']);
                    r[x].fnv = toHex(fnv(r[x]['*']));
                    if(r[x].size == 'undefined') r[x].size = r[x]['*'].length;
                    r[x].vec = rvec(r[x]['*']);
                    r[x]['*'] = null; // conserve space
                    revisions.push(r[x]);
                }
                if (data['query-continue']) {
                    $('#mw-js-message.mw-js-message-page-authors #progress').html( String(revisions.length).replace(/1/g, "۱").replace(/2/g, "۲").replace(/3/g, "۳").replace(/4/g, "۴").replace(/5/g, "۵").replace(/,/g, ".").replace(/6/g, "۶").replace(/7/g, "۷").replace(/8/g, "۸").replace(/9/g, "۹").replace(/0/g, "۰") + ' ویرایش دریافت شد&hellip;' );
                    query.rvstartid = data['query-continue'].revisions.rvcontinue;
                    getRevisions();
                }
                else {
                    setResult();
                }
            },
            error: function(jqXHR, textStatus, errorThrown) {
                alert('Error, but with "' + textStatus + '"');
                $('#mw-js-message.mw-js-message-page-authors').slideUp(1000);
            }
        });
    };
 
    function setResult() {
        // final handler after all ajax calls has completed
        $('#mw-js-message.mw-js-message-page-authors #progress').html( 'All revisions loaded&hellip;' );
 
        // variables    
        var digests = Object();
        var users = Object();
        var revids = Object();
 
        // short form to get the revisions
        var revs = revisions;
 
        // get the individual revisions through a revid-key
        for (var i = 0; i < revs.length; i++) revids[revs[i].revid] = revs[i];
 
        // get the individual revisions through the fnv-digest
        $('#mw-js-message.mw-js-message-page-authors #progress').html( 'Calculate FNV-digests&hellip;' );
        var shunted = 0;
        for (var i = revs.length-1; 0<=i; i--) {
            if (digests[revs[i].fnv]) {
                revs[i].previousid = digests[revs[i].fnv].revid;
                shunted++;
            }
            digests[revs[i].fnv] = revs[i];
        }
 
        // cache the cosine measure and set previousid to reflect the span
        $('#mw-js-message.mw-js-message-page-authors #progress').html(  'Calculate cosine measure&hellip;' );
        var i = 0;
        while (i<revs.length) {
            var j = i;
            if (revs[i].previousid) {
                revs[i].cosine = rcos(revs[i].vec, revids[revs[i].previousid].vec);
                var previousid = revs[i].previousid;
                while (revs[i].revid > previousid && i<revs.length) i++;
            }
            else if (revs[i].parentid) {
                revs[i].cosine = rcos(revs[i].vec, revids[revs[i].parentid].vec);
                var parentid = revs[i].parentid;
                while (revs[i].revid > parentid && i<revs.length) i++;
            }
            else if (i+1<revs.length) {
                revs[i].cosine = rcos(revs[i].vec, revs[i+1].vec);
                i++;
            }
            else if (i+1==revs.length) {
                revs[i].cosine = rcos(revs[i].vec);
                revs[j].previousid = 0;
                break;                                                        
            }
            if (i != j) revs[j].previousid = revs[i].revid;
        }
 
        // adjust the cosine measure if lsh is to far off
        $('#mw-js-message.mw-js-message-page-authors #progress').html( 'Adjust cosine measure&hellip;' );
        var similar = 0;
        var revision = revs[0];
        while (revision) {
            var previous = revids[revision.previousid];
            if (previous /* && revision.size > 100 */ && 64<revision.cosine) {
                var p=previous;
                var lsh=2147483647;
                var keep = null;
                for (var j = 0; j < 16; j++) {
                    if (p) {
                        var tmp = rcos(revision.vec, p.vec);
                        if (lsh > tmp) {
                            lsh = tmp;
                            keep = p;
                        }
                        p = revids[p.previousid];
                    }
                    else {
                        break;
                    }
                }
                if (keep && keep != previous) {
                    previous = keep;
                    similar++;
                }
            }
            if (previous) {
                revision.previousid = previous.revid;
                revision.cosine = rcos(revision.vec, previous.vec);
            }
            revision = previous;
        }
 
        // accumulate the cosine measure for each user
        $('#mw-js-message.mw-js-message-page-authors #progress').html( 'Accumulate cosine measure&hellip;' );
        var revision = revs[0];
        var num = 0;
        while (revision) {
            if (!users[revision.user]) users[revision.user] = { cosine: 0 };
            users[revision.user].cosine += revision.cosine;
            revision = revids[revision.previousid];
            num++;
        }
        // accumulate the total cosine measure
        var acc = { cosine: 0 };
		for (var x in users) acc.cosine += users[x].cosine;
 
        //for (var x = 0; x < users.length; x++) acc.cosine += users[x].cosine;
 
        // sort out the main contributors
        $('#mw-js-message.mw-js-message-page-authors #progress').html( 'Sort out main contributors&hellip;' );
        var authors = Array();
        var pseudonyms = Array();
        var count = 0;
        for (var x in users) pseudonyms.push(x);
		var sorted=pseudonyms.sort(function(a, b){ return users[b].cosine - users[a].cosine });
        for (var x = 0; x < sorted.length; x++ ) {
            var name = sorted[x];
            var cosine = Math.round(100*users[name].cosine/acc.cosine);
            if (count++ < 5 && 0 < cosine) authors.push(name);
            else if (5 <= cosine ) authors.push(name);
            else if (revs[revs.length-1].user == name) authors.push(name);
        }
 
        // print the cosine measure for each user
        $('#mw-js-message.mw-js-message-page-authors #progress').html( 'Create presentation&hellip;' );
        str = '<p>این فهرست نشان دهندهٔ آمار ساده شده‌ٔ میزان مشارکت کاربرهای اصلی در این صفحه است.</p>';
        str += 'تعداد ' + String(num) .replace(/1/g, "۱").replace(/2/g, "۲").replace(/3/g, "۳").replace(/4/g, "۴").replace(/5/g, "۵").replace(/,/g, ".").replace(/6/g, "۶").replace(/7/g, "۷").replace(/8/g, "۸").replace(/9/g, "۹").replace(/0/g, "۰")+ ' از کل ' + String(revisions.length) .replace(/1/g, "۱").replace(/2/g, "۲").replace(/3/g, "۳").replace(/4/g, "۴").replace(/5/g, "۵").replace(/,/g, ".").replace(/6/g, "۶").replace(/7/g, "۷").replace(/8/g, "۸").replace(/9/g, "۹").replace(/0/g, "۰")+ ' نسخه در محاسبات درنظر گرفته‌شده‌است. ';
        if (0<hidden)
            str += 'در میان نسخه‌ها ' + String(hidden).replace(/1/g, "۱").replace(/2/g, "۲").replace(/3/g, "۳").replace(/4/g, "۴").replace(/5/g, "۵").replace(/,/g, ".").replace(/6/g, "۶").replace(/7/g, "۷").replace(/8/g, "۸").replace(/9/g, "۹").replace(/0/g, "۰") + ' ویرایش مخفی‌شده وجود دارد. ';
        if (0<shunted || 0<similar)
            str += ' از میان این نسخه‌ها ' + String(shunted).replace(/1/g, "۱").replace(/2/g, "۲").replace(/3/g, "۳").replace(/4/g, "۴").replace(/5/g, "۵").replace(/,/g, ".").replace(/6/g, "۶").replace(/7/g, "۷").replace(/8/g, "۸").replace(/9/g, "۹").replace(/0/g, "۰") + ' نسخه بی‌تاثیر بودند و ' + String(similar).replace(/1/g, "۱").replace(/2/g, "۲").replace(/3/g, "۳").replace(/4/g, "۴").replace(/5/g, "۵").replace(/,/g, ".").replace(/6/g, "۶").replace(/7/g, "۷").replace(/8/g, "۸").replace(/9/g, "۹").replace(/0/g, "۰") + ' نسخه ویرایش مشابه با نسخه‌های قبلی یافت شد.';
        else
            str += 'مورد خاصی یافت نشد.';
        str += '<ul style="display:none">';
        var part = 0;
        for (var x = 0; x < authors.length; x++) {
            var name = authors[x];
			//document.write (acc.cosine);
            part += users[name].cosine;
            str += '<li>میزان مشارکت کاربر ';
            str += '<a href="' + mw.util.getUrl('User:' + name) + '">' + name + '</a>';
            str += ' (';
            str += '<a href="' + mw.util.getUrl('User_talk:' + name) + '">بحث</a>';
            str += ' | ';
            str += '<a href="' + mw.util.getUrl('Special:Contributions/' + name) + '">مشارکت‌ها</a>';
            str += ')  ';
            str += ' <em>';
            str += String((0.1<users[name].cosine/acc.cosine ? Math.round(100*users[name].cosine/acc.cosine) : Math.round(1000*users[name].cosine/acc.cosine)/10)).replace(/1/g, "۱").replace(/2/g, "۲").replace(/3/g, "۳").replace(/4/g, "۴").replace(/5/g, "۵").replace(/,/g, ".").replace(/6/g, "۶").replace(/7/g, "۷").replace(/8/g, "۸").replace(/9/g, "۹").replace(/0/g, "۰")+' ٪ ' + ' از کل مشارکت‌ها ';
            if (revs[revs.length-1].user == name) str += ' و <B>شروع کنندهٔ مقاله</B> ';
            str += 'است.</em>';
            str += '</li>';
        }
 
        str += '<li>مشارکت ویرایش‌گران دیگر  ' + String(Math.round(100*(acc.cosine-part)/acc.cosine)).replace(/1/g, "۱").replace(/2/g, "۲").replace(/3/g, "۳").replace(/4/g, "۴").replace(/5/g, "۵").replace(/,/g, ".").replace(/6/g, "۶").replace(/7/g, "۷").replace(/8/g, "۸").replace(/9/g, "۹").replace(/0/g, "۰") + '٪ از کل مشارکت‌های مقاله است.</li>';
        str += '</ul>';
        $('#mw-js-message.mw-js-message-page-authors #progress').remove();
        $('#mw-js-message.mw-js-message-page-authors h3').each(function(i, el) { removeSpinner( 'page-authors' ); });
        $('#mw-js-message.mw-js-message-page-authors').append(str);
        $('#mw-js-message.mw-js-message-page-authors ul').slideDown(1000);
        inProgress = false;
    };
 
    // the strange test (2147483647 <= -1 >>> 1) is to verify that we have at least 32 bit ints
    // note that 32 bit ints are according to standard, while it can be longer but not shorter to respect the standard
    var nsnum = parseInt(mw.config.get('wgNamespaceNumber'));
    if (0 <= nsnum && 0 == nsnum%2 && mw.config.get('wgIsArticle') && (2147483647 <= -1 >>> 1)) {
        $( function() {
            mw.util.addPortletLink(
                "p-cactions",
                '#',
                "میزان مشارکت کاربرها",
                "t-article-authors",
                "نمایش میزان مشارکت کاربرها در این صفحه",
                null,
                null
            );
            $('#t-article-authors').click(onclick);
        });
    }
})(mediaWiki, jQuery);