Hummingbird User Compare

Adds a button that compares the anime list of a hummingbird user against yours

Version vom 29.04.2015. Aktuellste Version

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Hummingbird User Compare
// @version      3.0.2
// @description  Adds a button that compares the anime list of a hummingbird user against yours
// @author       fuzetsu
// @match        https://hummingbird.me/*
// @match        https://forums.hummingbird.me/*
// @grant        none
// @require      https://greasyfork.org/scripts/5679-wait-for-elements/code/Wait%20For%20Elements.js?version=46106
// @noframes
// @namespace https://greasyfork.org/users/649
// ==/UserScript==
var SCRIPT_NAME = 'Hummingbird User Compare';
var BTN_ID = 'us-compare-button';
var COMPAT_ID = 'us-compat-table';
var USERNAME_SELECTOR = 'h2.username,h1.username';

var Util = {
  log: function() {
    var args = [].slice.call(arguments);
    args.unshift('%c' + SCRIPT_NAME + ':', 'font-weight: bold');
    console.log.apply(console, args);
  },
  q: function(query, context) {
    return (context || document).querySelector(query);
  },
  qq: function(query, context) {
    return [].slice.call((context || document).querySelectorAll(query));
  },
  shallowTextContent: function(elem) {
    var child = elem.firstChild,
        texts = [];

    while (child) {
      if (child.nodeType == 3) {
        texts.push(child.data);
      }
      child = child.nextSibling;
    }

    return texts.join("");
  },
  arrToTable: function(arr, opts) {
    // takes an object and converts to an html compliant list of attributes
    var makeAttrs = function(obj) {
      var str = '';
      var key;
      for(key in obj) {
        if(obj.hasOwnProperty(key)) {
          str += key + '="' + obj[key] + '" ';
        }
      }
      return str;
    };

    // creates an object of the specified type
    // either takes a config object with attributes and a value, or just the value directly
    var createElement = function(type, data) {
      if(typeof data === 'object') {
        return '<' + type +' ' + makeAttrs(data.attrs || {}) + '>' + data.value + '</' + type + '>';
      }
      return '<' + type + '>' + data + '</' + type + '>';
    };

    opts = opts || {};

    var headers = '';

    // if the table is going to have a head use the first row
    if(opts.hasHead) {
      headers = arr[0].map(function(header) {
        return createElement('th', header);
      }).join('');
      headers = '<thead>' + headers + '</thead>';
      arr = arr.slice(1);
    }

    // create the body of the array
    var rows = arr.map(function(row) {
      return '<tr>' + row.map(function(cell) {
        return createElement('td', cell);
      }).join('') + '</tr>';
    }).join('');

    // create and return the actual table all put together
    return createElement('table', {
      attrs: opts.attrs,
      value: (headers + '<tbody>' + rows + '</tbody>')
    });

  },
  getJSON: function(url, load, error) {
    var xhr = new XMLHttpRequest();
    xhr.open('get', url);
    xhr.responseType = 'json';
    xhr.onload = function() {
      load(xhr.response);
    };
    xhr.onerror = error;
    xhr.send();
  }
};

var hb = {
  getCompareUrl: function() {
    var profileUrl = Util.q('.dropdown-menu > li > a').href;
    var you = profileUrl.slice(profileUrl.lastIndexOf('/') + 1);
    var them = Util.shallowTextContent(Util.q(USERNAME_SELECTOR)).trim();
    return {
      you: you,
      them: them,
      url: 'http://fuzetsu.github.io/hummingbird-user-compare/?user1=' + you + '&user2=' + them
    };
  },
  getCompatibility: function(callback) {
    var buffer = [];

    var process = function(data) {
      buffer.push(data);
      if(buffer.length === 2) { 
        var anime, manga;
        if (buffer[0].type === 'anime') {
          anime = buffer[0];
          manga = buffer[1];
        } else {
          anime = buffer[1];
          manga = buffer[0];
        }
        callback(anime, manga);
      }
    };

    var profileUrl = Util.q('.dropdown-menu > li > a').href;
    var you = profileUrl.slice(profileUrl.lastIndexOf('/') + 1);
    var them = Util.shallowTextContent(Util.q(USERNAME_SELECTOR)).trim();

    // get anime compat
    Util.getJSON('https://hbird-cmp-node.herokuapp.com/compatibility/anime?user1=' + you + '&user2=' + them, process);
    // get manga compat
    Util.getJSON('https://hbird-cmp-node.herokuapp.com/compatibility/manga?user1=' + you + '&user2=' + them, process);
  }
};

var lastUser;

Util.log('Started, waiting for user page...');

waitForUrl(/https:\/\/(forums\.)?hummingbird\.me\/users\/.+/, function() {
  Util.log('Found user page, waiting for button area...');
  waitForElems('.user-cover-options .follow-button:not(.' + BTN_ID + ')', function(btnFollow) {
    var forumButton = Util.q('.account-info .inline-list a');
    var btn = Util.q('#' + BTN_ID);
    var compatTable = Util.q('#' + COMPAT_ID);
    var compare = hb.getCompareUrl();

    var compatibilityCallback = function(anime, manga) {
      var animeMessage = anime.phrase;
      if (anime.percent) {
        animeMessage += ' (' + anime.percent +  ')';
      }
      var mangaMessage = manga.phrase;
      if (manga.percent) {
        mangaMessage += ' (' + manga.percent +  ')';
      }
      var table = [];
      table.push([{value: 'Anime Compatability:', attrs: {style: 'text-align: right; padding-right: 5px;'}}, {value: animeMessage, attrs: {style: 'font-weight:bold'}}]);
      table.push([{value: 'Manga Compatability:', attrs: {style: 'text-align: right; padding-right: 5px;'}}, {value: mangaMessage, attrs: {style: 'font-weight:bold'}}]);
      btnFollow.parentNode.innerHTML += Util.arrToTable(table, {
        hasHead: false,
        attrs: {
          style: 'position: absolute; bottom: -25px; right: 0;',
          id: COMPAT_ID
        }
      });
    };

    // exit early if you're on your own profile
    if(compare.them === compare.you) return;
    Util.log('Found button area, inserting compare button');
    if(forumButton) {
      forumButton.title = '';
      forumButton.target = '_blank';
      forumButton.href = compare.url;
    } else {
      if(!btn) {
        btn = document.createElement('a');
        btn.className = btnFollow.className;
        btn.id = BTN_ID;
        btn.textContent = 'Compare';
        btn.target = '_blank';
        btn.setAttribute('style', 'right: ' + (btnFollow.clientWidth + 10) + 'px; background: rgb(236, 134, 97); color: white;');
      }
      if(compare.them === lastUser) {
        // if the current user is the same as last but the user url changed then wait for the username to update
        if(location.href.indexOf("/users/" + lastUser) === -1) {
          if (compatTable) {
            compatTable.remove();
          }
          Util.q(USERNAME_SELECTOR).addEventListener('DOMSubtreeModified', function(e) {
            this.removeEventListener('DOMSubtreeModified', arguments.callee);
            compare = hb.getCompareUrl();
            btn.href = compare.url;
            btnFollow.parentNode.appendChild(btn);
            hb.getCompatibility(compatibilityCallback);

          });
        }
      } else {
        if (compatTable) {
          compatTable.remove();
        }
        btn.href = compare.url;
        btnFollow.parentNode.appendChild(btn);
        hb.getCompatibility(compatibilityCallback);
      }
    }
    lastUser = compare.them;
  }, true);
});