TFS Avatars

Detects locally-running image server to use for replacements of TFS avatars. Use http-server (https://www.npmjs.com/package/http-server) for node.js for simple image hosting. Recommend image size of 100x100.

// ==UserScript==
// @name			TFS Avatars
// @namespace		DorkForce
// @version			4.4
// @description		Detects locally-running image server to use for replacements of TFS avatars. Use http-server (https://www.npmjs.com/package/http-server) for node.js for simple image hosting. Recommend image size of 100x100.
// @author			Dan Overlander
// @include			*tfs2*
// @require			https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @require         https://greasyfork.org/scripts/23115-tampermonkey-support-library/code/Tampermonkey%20Support%20Library.js?version=730858
// @require		    https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js
// ==/UserScript==

// Since v04.3: Replaced properName method with that from Gitlab script. TODO: extract script to tm library.
// Since v04.1: Tamperlibrary link updated. Removes Tamper Global script dependency
// Since v04.0: moving out setTamperIcon
// Since v03.0: Changed avatarHost to localhost
// Since v02.0: Changed avatarHost IP. Decreased TIMEOUT. Added history-image-icon class
// Since v01.0: Added missing classes from workitem history and links
// Since v00.0: Initial


(function() {
    'use strict';

    const TIMEOUT = 250;
    var toggle = {
            isMouseMoved: false,
            areClassesAdded: false
        },
        global = {
            triggerElement: '.menu-icon',
            scriptName: 'TFS Avatars',
            prefsName: 'TFSAvatarsPrefs',
            prefs: {},
            handlePrefsLocally: true,
            animationSpeed: (TIMEOUT/2),
            avatarHost: 'localhost:8080/', // for setPhoto
            pingPhoto: 'none', // pinging for setPhoto
            imageExt: '.png'
        },
        page = {
            initialize: function () {
                setTimeout(function () {
                    page.addClasses();
                    tm.addClasses();
                    tm.setTamperIcon(global);
                    page.setAvatars();
                }, TIMEOUT);
            },
            addClasses: function () {
                if (!toggle.areClassesAdded) {
                    toggle.areClassesAdded = true;

                    tm.addGlobalStyle('.identity-view-control { font-size:9px; line-height:10px; }');

                }
            },
            setAvatars: function () {
                tm.ping(global.avatarHost + global.pingPhoto + global.imageExt, function callback (response) {
                    if (response === 'responded') {
                        var avatarArray = [],
                            thisName = 'none',
                            thisImg;

                        tm.getContainer({
                            'el': '.user-picture-resolved'
                        }).then(function($container){
                            _.each($('.user-picture-resolved'), function (el) {
                                thisName = $(el).next().text();
                                thisImg = el;
                                utils.setProp(thisImg, thisName);
                            });
                        });

                        tm.getContainer({
                            'el': '.identity-picker-resolved'
                        }).then(function($container){
                            _.each($('.identity-picker-resolved'), function (el) {
                                thisName = $(el).text().replace('Focus on the selected item to change the value via a combo box', '');
                                thisImg = el.firstChild;
                                utils.setProp(thisImg, thisName);
                            });
                        });

                        tm.getContainer({
                            'el': '.history-identity-icon'
                        }).then(function($container){
                            _.each($('.history-identity-icon'), function (el) {
                                thisName = $(el).prop('alt');
                                thisName = utils.removeBrackets(thisName);
                                utils.setProp($(el), thisName);
                            });
                        });

                        tm.getContainer({
                            'el': '.la-user-icon'
                        }).then(function($container){
                            _.each($('.la-user-icon'), function (el) {
                                thisImg = $(el).find('img');
                                thisName = thisImg.prop('alt');
                                thisName = utils.removeBrackets(thisName);
                                utils.setProp(thisImg, thisName);
                            });
                        });

                        tm.getContainer({
                            'el': '.history-image-icon'
                        }).then(function($container){
                            _.each($('.history-image-icon'), function (el) {
                                thisImg = $(el);
                                thisName = thisImg.prop('alt');
                                thisName = utils.removeBrackets(thisName);
                                utils.setProp(thisImg, thisName);
                            });
                        });

                        tm.getContainer({
                            'el': '.ms-Persona'
                        }).then(function($container){
                            _.each($('.ms-Persona'), function (el) {
                                thisName = $(el).find('.persona-main-text-primary').text();
                                if (thisName == null || thisName === '') { // look for this value elsewhere in the DOM; in this case, the person's manager's element
                                    thisName = $(el).find('.persona-list-element-text-primary').text();
                                }
                                thisImg = $(el).find('img');
                                utils.setProp(thisImg, thisName);
                            });
                        });

                        tm.getContainer({
                            'el': '.profile-image'
                        }).then(function($container){
                            _.each($('.profile-image'), function (el) {
                                thisName = $(el).parent().parent().prop('outerHTML');
                                if (thisName.indexOf('aria-label') > -1) {
                                    var propStartIndex = thisName.indexOf('aria-label') + 12,
                                        propLength = thisName.indexOf('(', propStartIndex) - propStartIndex;
                                    thisName = thisName.substr(propStartIndex, propLength);
                                } else {
                                    thisName = '';
                                }

                                if (thisName == null || thisName === '') { // look for this value elsewhere in the DOM; in this case, the person's manager's element
                                    thisName = $(el).next().text();
                                }
                                thisImg = el;
                                utils.setProp(thisImg, thisName);
                            });
                        });

                        tm.getContainer({
                            'el': '.identity-picture'
                        }).then(function($container){
                            $('.identity-picture').each( function (key, value) {
                                var propStartIndex,
                                    propLength;
                                thisName = value.outerHTML; // generally, this is on the taskboard
                                if (thisName.indexOf('data-sip') > -1) {
                                    propStartIndex = thisName.indexOf('data-sip') + 10;
                                    propLength = thisName.indexOf('"', propStartIndex) - propStartIndex;
                                    thisName = thisName.substr(propStartIndex, propLength).replace('@Dell.com', '');
                                } else {
                                    thisName = '';
                                }

                                if (thisName == null || thisName === '') { // look for this value elsewhere in the DOM; in this case, within a dropdown
                                    thisName = $(this).parent().find('.title').text();
                                }

                                if (thisName == null || thisName === '') { // look for this value elsewhere in the DOM; in this case, in work-item history list
                                    thisName = $(this).parent().next().find('.discussion-messages-user').text();
                                }

                                if (thisName == null || thisName === '') { // look for this value elsewhere in the DOM; in this case, in team dashboard
                                    thisName = $(this).prop('alt');
                                    thisName = utils.removeBrackets(thisName);
                                }

                                thisImg = this;
                                utils.setProp(thisImg, thisName);
                            });
                        });

                        // On Query pages, in rows where Assigned- fields are shown. #AssignedFields
                        tm.getContainer({
                            'el': '.identity-grid-cell'
                        }).then(function($container){
                            var targetSpans = $('.identity-grid-cell').find('span');
                            targetSpans.each( function (key, value) {
                                thisName = utils.noTeam($(value).prop('innerText'));
                                if( (thisName.length > 2) && $(value).prop('innerHTML').indexOf('replacedName') === -1 ) {
                                    $(value).parent().parent().css('padding', '0px');
                                    $(value).parent().prop('innerHTML', '<span title="' + thisName + '" class="replacedName" style="background-image: url(\'http://' + global.avatarHost + thisName + global.imageExt + '\');">&nbsp;</span>' + thisName.replace(', ', '<br>').replace(' - Dell Team', '').replace(/([0-9])\w+/, '').replace(/([0-9])/, ''));
                                    $('.replacedName').css(
                                        {
                                            'top': '-2px',
                                            'position': 'relative',
                                            'height': '27px',
                                            'width': '27px',
                                            'display': 'block',
                                            'background-size': '27px 27px',
                                            'float': 'left'
                                        }
                                    );
                                }
                            });
                            setTimeout(page.setAvatars, (TIMEOUT*5));
                        });


                    }
                });
            }
        },
        utils = {
            noTeam: function(thisName) {
                return thisName.replace(' - Dell Team', '');
            },
            setProp: function(thisImg, thisName) {
                if (thisName != null && thisName != '' && thisName != 'undefined') {
                    thisName = utils.noTeam(thisName);
                    thisName = utils.properName(thisName);
                    $(thisImg).prop('src', 'http://' + global.avatarHost + thisName + global.imageExt);
                }
            },
            properName: function(thisName) {
                var firstName = '',
                    lastName = '',
                    midName = '';

                thisName = thisName
                    .replace('https://gitlab.dell.com/', '')
                    .replace(' - Dell Team', '')
                    .replace('\'s avatar', '')
                    .replace('Assigned to ', '')
                    .replace('Avatar for ', '')
                    .replace('@', '')
                    .replace(/@/g, '')
                    .replace(/\//g, '')
                    .replace(/_/g, '-');
                firstName = thisName.substring(0, thisName.indexOf('-'));
                lastName = thisName.substring(thisName.indexOf('-')+1, thisName.length);
                if ((firstName.length === 0 && lastName.length === 0)) {
                    return;
                }
                if (firstName.length > 0 && lastName.length > 0 && thisName.indexOf(',') < 0) {
                    thisName = lastName + ', ' + firstName;
                }
                if (thisName.indexOf('-') > 0) {
                    midName = thisName.substring(0, thisName.indexOf('-'));
                    thisName = thisName.substring(thisName.indexOf('-')+1, thisName.length);
                    thisName = thisName + ' ' + midName;
                }
                while (thisName.indexOf('  ') > 0) {
                    thisName = thisName.replace(/\s\s/, ''); // no double spaces
                }
                thisName = thisName
                    .replace(/(\r\n\t|\n|\r\t)/gm,'') // no line breaks or tabs
                    .replace(/ ,/, ',') // no spaces before commas
                    .replace(/\%20/, '') // no %20 characters
                    .replace(/Americas/g, '')
                    .trim(); // seriously, no extra spaces
                thisName = thisName.replace(',', ', ').replace('  ', ' '); // there's probably a less-stupid way of REALLY making sure it's always "COMMA SPACE"
                return thisName;
            },
            removeBrackets(thisName) {
                if (thisName == null) {
                    return;
                }
                var propLength = thisName.indexOf('<') - 1;
                thisName = thisName.substr(0, propLength);
                return thisName;
            }
        };

    /*
     * Global functions
     */


    function initScript () {
        tm.getContainer({
            'el': global.triggerElement,
            'max': 100,
            'spd': 1000
        }).then(function($container){
            page.initialize();
        });
    }
    initScript();

    $(document).mousemove(function(e) {
        if (!global.isMouseMoved) {
            global.isMouseMoved = true;
            setTimeout(function() {
                global.isMouseMoved = false;
            }, TIMEOUT * 2);
            initScript();
        }
    });

})();