Dribbble Extender

Shows who follows you on your following list

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Dribbble Extender
// @description  Shows who follows you on your following list
// @author       Kos
// @namespace    http://tampermonkey.net/
// @version      0.5
// @license      CC BY-SA 2.0
// @homepage     https://greasyfork.org/scripts/22003-dribbble-extender
// @include      https://dribbble.com/*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js
// @grant        GM_addStyle
// ==/UserScript==

;(function() {
    'use strict';
    
    // styles for checkmark
    GM_addStyle(
        '.us-follows-you-mark {color:#b3e3bd;font-weight:200}\
        .us-fetch-ready {color:#2cff5a;font-weight:900}'
    );

    var ACCESS_TOKEN = '9eafaa85aba0e22cdd7b7ddaa23181b59c29989dbd27000c78db21f5609110f4',
        followers = [],
        // username
        username = $('.has-sub .profile-name').parent().attr('href').replace('/', ''),
        cacheKey = username,
        curPage = 1,
        perPage = 100,
        repaintDelay = 500,
        waitForAllFetch = false,
        requestPerIteration = 15,
        requestsResponseLeftCount = 0,
        mainIntervalStopped = true,
        lastIntervalFoundFollowers = null,
        justCache = false;

    // if not recognized logged in user, exit
    if (username == '')
    {
        return;
    }
    
    // functions for working with localStorage

    function lsTest()
    {
        var test = 'test';
        try
        {
            localStorage.setItem(test, test);
            localStorage.removeItem(test);
            return true;
        }
        catch(e)
        {
            return false;
        }
    }

    function getLocalStorageArray(name)
    {
        var list = localStorage.getItem(name);
        if (!list)
        {
            return [];
        }
        return JSON.parse(list);
    }

    function saveUpdatedFollowersList(list)
    {
        localStorage.setItem(cacheKey+'_followers_latest', JSON.stringify(list));
    }

    function addFollowerLocalStorage(id)
    {
        var list = getLocalStorageArray(cacheKey+'_followers_latest');

        if (list.indexOf(id) == -1)
        {
            list.push(id);
        }

        saveUpdatedFollowersList(list);
    }

    function saveFinishedAmountOfFollowers()
    {
        var list = getLocalStorageArray(cacheKey+'_followers_latest');

        localStorage.setItem(cacheKey+'_followers', JSON.stringify(list));
        saveUpdatedFollowersList([]);

        // update on page
        followers = list;
        paintFollowed();
    }

    var localStorageAvailable = lsTest();

    $(document).ready(function(){

        if (localStorageAvailable)
        {
            followers = getLocalStorageArray(cacheKey+'_followers');

            // if have previously saved followers, increase delay,
            // update on page only when all users parsed
            if (followers.length)
            {
                waitForAllFetch = true;
                
                // cache results for 5 minutes
                if (localStorageAvailable)
                {
                    var lastFetchTime = localStorage.getItem(cacheKey+'_last_followers_fetch');
                    if (lastFetchTime && new Date().getTime() - lastFetchTime < 5*60*1000)
                    {
                        justCache = true;
                        waitForAllFetch = false;
                    }
                }
            }

            // clear previously parsed data, to parse all new
            saveUpdatedFollowersList([]);
        }

        function finishParse()
        {
            if (mainIntervalStopped)
            {
                return;
            }

            clearInterval(mainInterval);
            mainIntervalStopped = true;
            
            if (localStorageAvailable)
            {
                localStorage.setItem(cacheKey+'_last_followers_fetch', new Date().getTime());
            }
        }
        
        if (!justCache)
        {
            mainIntervalStopped = false;
            
            var mainInterval = setInterval(function(){
                // if there are request we wait to finish, exit
                if (requestsResponseLeftCount > 0)
                {
                    return;
                }
                // if not found any users on last interval
                if (lastIntervalFoundFollowers !== null && lastIntervalFoundFollowers === 0)
                {
                    finishParse();
                    return;
                }
                requestsResponseLeftCount = requestPerIteration;
                lastIntervalFoundFollowers = 0;
                for (var i = 0; i < requestPerIteration; i++)
                {
                    $.ajax({
                        type: 'GET',
                        url: 'https://api.dribbble.com/v1/users/'+username+'/followers/?page='+(curPage++)+'&per_page='+perPage,
                        beforeSend: function(jqxhr)
                        {
                            jqxhr.setRequestHeader('Authorization', 'Bearer ' + ACCESS_TOKEN);
                        },
                        success: function(res)
                        {
                            if (res.length === 0)
                            {
                                finishParse();
                                return;
                            }

                            for (var i = 0; i < res.length; i++)
                            {
                                setFollowed(res[i].follower.id);
                                lastIntervalFoundFollowers++;
                            }

                            // if not full page, assume it last one
                            if (res.length < perPage)
                            {
                                finishParse();
                            }
                        },
                        complete: function(){
                            requestsResponseLeftCount--;
                            // if this is last response of last iteration, save ids for page
                            // in this 'if' fetch ends
                            if (requestsResponseLeftCount === 0 && mainIntervalStopped)
                            {
                                if (localStorageAvailable)
                                {
                                    saveFinishedAmountOfFollowers();
                                }
                            }
                        }
                    });
                }
            }, 100);
        }

        // paint then set repaint with delay
        paintFollowed();
        setInterval(paintFollowed, repaintDelay);
    });
    
    function setFollowed(id)
    {
        id = Math.round(id);

        if (!waitForAllFetch && followers.indexOf(id) == -1)
        {
            followers.push(id);
        }

        if (localStorageAvailable)
        {
            addFollowerLocalStorage(id);
        }
    }

    function paintFollowed()
    {
        paintOnFollowingPage();
        paintOnShotPage();
        paintOnShotsListPage();
        paintOnCommentsPage();
        paintOnUserPage();
    }
    
    function paintOnFollowingPage()
    {
        var following = $('ol.list-of-scrolling-rows').find('.scrolling-row');
        
        if (following.length == 0)
        {
            return;
        }

        for (var i = 0; i < following.length; i++)
        {
            var userId = parseInt(following[i].className.match(/.*?user-row-(\d+).*/)[1]),
                userBlock = $(following[i]),
                title = userBlock.find('.hover-card-parent');

            // if not followed, remove mark if has one
            if (followers.indexOf(userId) == -1)
            {
                userBlock.removeClass('us-follows-you');
                userBlock.find('.us-follows-you-mark').remove();
                continue;
            }

            // if already marked as follower do nothing until all fetched
            if (userBlock.hasClass('us-follows-you'))
            {
                if (mainIntervalStopped)
                {
                    userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
                }
                continue;
            }

            // set mark of follower
            userBlock.addClass('us-follows-you');
            title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
        }
    }
    
    function paintOnShotsListPage()
    {
        var dribbbles = $('ol.dribbbles > li');
        
        if (dribbbles.length == 0)
        {
            return;
        }

        for (var i = 0; i < dribbbles.length; i++)
        {
            var userBlock = $(dribbbles[i]);
            if (userBlock.find('.attribution-team').length)
            {
                continue;
            }
            
            var avatar = userBlock.find('.attribution-user img');

            // avatar is not loaded yet
            if (!avatar || !avatar.attr('src'))
            {
                return;
            }
            
            
            var userId = parseInt(avatar.attr('src').match(/users\/(\d+)/)[1]),
                title = userBlock.find('.attribution-user a').first();

            // if not followed, remove mark if has one
            if (followers.indexOf(userId) == -1)
            {
                userBlock.removeClass('us-follows-you');
                userBlock.find('.us-follows-you-mark').remove();
                continue;
            }

            // if already marked as follower do nothing until all fetched
            if (userBlock.hasClass('us-follows-you'))
            {
                if (mainIntervalStopped)
                {
                    userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
                }
                continue;
            }

            // set mark of follower
            userBlock.addClass('us-follows-you');
            title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
        }
    }
    
    function paintOnCommentsPage()
    {
        var dribbbles = $('ol.comments > li');
        
        if (dribbbles.length == 0)
        {
            return;
        }

        for (var i = 0; i < dribbbles.length; i++)
        {
            var userBlock = $(dribbbles[i]),
                userId = parseInt(userBlock.attr('data-user-id')),
                title = userBlock.find('h2');

            // if not followed, remove mark if has one
            if (followers.indexOf(userId) == -1)
            {
                userBlock.removeClass('us-follows-you');
                userBlock.find('.us-follows-you-mark').remove();
                continue;
            }

            // if already marked as follower do nothing until all fetched
            if (userBlock.hasClass('us-follows-you'))
            {
                if (mainIntervalStopped)
                {
                    userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
                }
                continue;
            }

            // set mark of follower
            userBlock.addClass('us-follows-you');
            title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
        }
    }
    
    function paintOnShotPage()
    {
        var userBlock = $('.user');
        
        if (userBlock.length == 0)
        {
            return;
        }

        var userId = parseInt($('.user > a[rel=contact] img.photo').attr('src').replace(/.*users\/(\d+).*/, '$1')),
            title = userBlock.find('.shot-byline-user a');

        // if not followed, remove mark if has one
        if (followers.indexOf(userId) == -1)
        {
            userBlock.removeClass('us-follows-you');
            userBlock.find('.us-follows-you-mark').remove();
            return;
        }

        // if already marked as follower do nothing until all fetched
        if (userBlock.hasClass('us-follows-you'))
        {
            if (mainIntervalStopped)
            {
                userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
            }
            return;
        }

        // set mark of follower
        userBlock.addClass('us-follows-you');
        title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
    }
    
    function paintOnUserPage()
    {
        var userBlock = $('.profile-head');
        
        if (userBlock.length == 0)
        {
            return;
        }

        var avatar = $($('.profile-head img.photo')[0]);
        
        // avatar is not loaded yet
        if (!avatar || !avatar.attr('src'))
        {
            return;
        }
        
        var userId = parseInt(avatar.attr('src').replace(/.*users\/(\d+).*/, '$1')),
            title = userBlock.find('.profile-name a');

        // if not followed, remove mark if has one
        if (followers.indexOf(userId) == -1)
        {
            userBlock.removeClass('us-follows-you');
            userBlock.find('.us-follows-you-mark').remove();
            return;
        }

        // if already marked as follower do nothing until all fetched
        if (userBlock.hasClass('us-follows-you'))
        {
            if (mainIntervalStopped)
            {
                userBlock.find('.us-follows-you-mark').addClass('us-fetch-ready');
            }
            return;
        }

        // set mark of follower
        userBlock.addClass('us-follows-you');
        title.html('<span title="Follows you" class="us-follows-you-mark'+(mainIntervalStopped ? ' us-fetch-ready' : '')+'">✓ </span>'+title.html());
    }
})();