Change Stash's default avatar to username initials

If you are using Atlassian's Stash at work, you must be annoyed by so many co-workers using the default avatar, which makes it so different to recognize different users. And you just couldn't ask them all to upload an avatar!

So here I write a small tampermonkey script to change the default avatar to their name initials.

const styleEl = document.createElement('style');
document.head.appendChild(styleEl);
styleEl.sheet.insertRule('.aui-avatar-inner { background-color: lightgray; color: white; font-weight: 700; line-height: 32px; }', 0);
styleEl.sheet.insertRule('.aui-avatar-small .aui-avatar-inner { line-height: 24px; }', 0);

const observer = new MutationObserver(function(mutations) {
    document.querySelectorAll('img[src^="https://secure.gravatar.com"]').forEach((img) => {
        const parent = img.parentElement;
        const initials = img.alt.split(' ').map(s=>s[0]).join('');
        parent.textContent = initials;
    })
})
observer.observe(document, { childList: true, subtree: true });

Before:
image
After:
image

Surely it would be much better if we could just use css without javascript. However, :has() pseudo-class is still can not be used within stylesheets but only with functions like document.querySelector().

And the MutationObserver used here is much faster than the conventional DOMNodeInserted event. Just a reminder, in real life, a proper debounce to the callback is much appropriate here!


Update:

Just saw a brilliant way to detect DOM Node Insertion without using MutationObserver: Detect DOM Node Insertions with JavaScript and CSS Animations. So I updated the script as this:

let insertionObserver = function(selector, callback) {
  const styleEl = document.createElement('style');
  document.head.appendChild(styleEl);
  const KeyframeName = 'insertionObserver';
  const keyframesRule = `@keyframes ${KeyframeName} { from { opacity: 0.99; } to { opacity: 1; } }`;
  const styleRule = `${selector} { animation-duration: 0.001s; animation-name: ${KeyframeName}; }`;

  styleEl.sheet.insertRule(keyframesRule);
  styleEl.sheet.insertRule(styleRule);

  document.addEventListener('animationstart', (event) => {
    if (event.animationName === KeyframeName) {
      callback(event);
    }
  }, false);
}

insertionObserver('.aui-avatar-inner img[src^="https://secure.gravatar.com"]', (event) => {
  let img = event.target;
  const parent = img.parentElement;
  const initials = img.alt.split(' ').map(s=>s[0]).join('');
  const height = getComputedStyle(parent).height;
  parent.setAttribute("style", `background-color: lightgray; color: white; line-height: ${height}`);
  parent.textContent = initials;
});

Maybe it's time to create a repo to for these little helper functions, as my arsenal..