melon / GameFAQs Show Icon

// ==UserScript==
// @name         GameFAQs Show Icon
// @namespace    https://gamefaqs.gamespot.com/
// @version      0.5
// @description  Shows user icon next to posts on GameFAQs
// @license      MIT
// @author       melon (TeaMilk)
// @match        https://gamefaqs.gamespot.com/boards/*/*
// @exclude      https://gamefaqs.gamespot.com/boards/*/*/*
// @grant        none
//
// ==/UserScript==
(function() {
  if (document.querySelector('.userIcon') !== null) {
    return; //prevent duplicate loading icons when revisiting page in Firefox
  }
  
	'use strict';
  var size = 64;
  var userInfo = {}; //indexed by username, has name, and (msg id, user id) OR icon url
  var postInfo = []; //each entry has reference to userInfo object, reference to icon element, (undefined if a deleted post)
  
  var rand = new Date().getTime(); //unique val to append to each img request to avoid cache
  
  if(document.querySelector("#login_dialog")){ //user is not logged in
  	var key = document.querySelector("#login_dialog").querySelector('input[name=key]').getAttribute("value");
  }
  else{
    var key = document.querySelector(".sticky_dialog").querySelector(".body").querySelector('input[type=hidden]').getAttribute("value");
  }
  var board = window.location.href.split('/')[4].split('-')[0];
  var posts = document.getElementsByClassName("msg"); //actual post elements on the page

  //if author is positioned to left or top of post
  var authorLeft = posts[0].querySelector('.msg_infobox').offsetWidth <= 150;

  //author position may change with window resize
  window.addEventListener("resize", positionIcons);

  //put blank icons in place (reduce cascading effect), and push info into postInfo/add to userInfo
  for (var i = 0; i < posts.length; i++) {
    if (!posts[i].firstChild.classList.contains('deleted')) {
      var icon = makeBlankIcon();
      var box = posts[i].querySelector('.msg_infobox');
      positionIcon(icon, box);
      var menu = posts[i].querySelector('.user_submenu');
      var user = menu.getAttribute("data-username");
      if (!userInfo.hasOwnProperty(user)) {
        userInfo[user] = {
          msg: menu.getAttribute("data-msgid"),
          uid: menu.getAttribute("data-userid"),
          name: user
        };
      }
      postInfo.push({
        user: userInfo[user],
        icon: icon
      });
    }
    else {
      postInfo.push(undefined); //in order for posts index to match postInfo index (some will be blank)
    }
  }

  for (var j = 0; j < postInfo.length; j++) {
    if (postInfo[j]) {
      if (!postInfo[j].user.hasOwnProperty("url")) { //only make request if this user's icon URL is not already saved
        iconRequest(j);
      }
      else {
        fillIcon(postInfo[j].icon, postInfo[j].user);
      }
    }
  }

  //takes post # and makes request for that icon. Sets user URL and fills icon
  function iconRequest(num) {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'https://gamefaqs.gamespot.com/ajax/user_basic_info', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    xhr.onreadystatechange = function () {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        postInfo[num].user.url = JSON.parse(xhr.responseText).avatar;
        fillIcon(postInfo[num].icon, postInfo[num].user);
        delete postInfo[num].user.msg;
        delete postInfo[num].user.uid;
      }
    }
    xhr.send('b=' + board + '&m=' + postInfo[num].user.msg + '&u=' + postInfo[num].user.uid + '&key=' + key);
  }

  //takes reference to icon and user object and fills image and href
  function fillIcon(icon, user) {
    icon.href = "/community/" + user.name;
    icon.firstChild.src = user.url + "?" + rand;
  }

  //update the position of an icon
  function positionIcon(icon, box) {
    if (authorLeft) {
      icon.style.padding = '5px';
      icon.style.display = 'block';
      icon.style.margin = 'auto';
      box.insertBefore(icon, box.firstChild.nextSibling.nextSibling);
    }
    else {
      icon.style.padding = '0px 5px 0px 0px';
      box.insertBefore(icon, box.firstChild);
      icon.style.display = 'inline-block';
    }
  }

  //reposition all icons if author position changes
  function positionIcons() {
    var prev = authorLeft;
    authorLeft = posts[0].querySelector('.msg_infobox').offsetWidth <= 150;

    if ((!prev && authorLeft) || (prev && !authorLeft)) {
      for (var i = 0; i < posts.length; i++) {
        if (!posts[i].firstChild.classList.contains('deleted')) {
          var box = posts[i].querySelector('.msg_infobox');
          positionIcon(postInfo[i].icon, box);
        }
      }
    }
  }

  //makes a blank icon
  function makeBlankIcon() {
    var icon = document.createElement('a');
    icon.className = "userIcon";
    icon.style.height = size + 'px';
    icon.style.width = size + 'px';
    var pic = document.createElement('img');
    pic.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAAOUlEQVR42u3OIQEAAAACIP1/2hkWWEBzVgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAYF3YDicAEE8VTiYAAAAAElFTkSuQmCC";
    pic.classList.add("imgboxart");
    icon.appendChild(pic);
    return icon;
  }
})();