CryptalEquine / Rankings Assistant

// ==UserScript==
// @name          Rankings Assistant
// @namespace     ra
// @description   For Howrse: Adds buttons to the horse rankings to load more at a time
// @author        Cryptal
// @include       *www.howrse.com/classements/chevaux*
// @include       *au.howrse.com/classements/chevaux*
// @include       *ca.howrse.com/classements/chevaux*
// @include       *us.howrse.com/classements/chevaux*
// @include       *.howrse.co.uk/classements/chevaux*
// @version       1.0.2
// @run-at        document-end
// @noframes      true
// @grant         unsafeWindow
// @grant         DOMAttrModified
// @grant         GM_log
// @grant         GM_info
// @grant         GM_openInTab
// @grant         GM_getValue
// @grant         GM_setValue
// @grant         GM_deleteValue
// @grant         GM_listValues
// @grant         GM_xmlhttpRequest
// @grant         GM_addStyle
// @grant         GM_setClipboard
// @grant         GM_getResourceText
// @grant         window.close
// @require       https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js
// @updateURL     https://openuserjs.org/meta/CryptalEquine/Rankings_Assistant.meta.js
// ==/UserScript==

var requests = [];

Wait("#chevauxContent table[id*='overall']", function(e)
{
   var breed, breedScript, breedID = 0;
   
   if ($('#chevauxContent').find('div.message-content').find("strong[dir='ltr']").length > 0)
   {
      breed = $('#chevauxContent').find('div.message-content').find("strong[dir='ltr']").html().trim();
      breedScript = $('#table-0').find("a[rel*='nofollow']:contains('" + breed + "')").find('script');
      breedID = breedScript.html().match(/\([0-9]+/g)[0].replace('(', '');
   }
   
   var headings = $('#chevauxContent').find('h3');
   var tables = $('#chevauxContent').find('table.ranking');
   
   for (var i = 0; i < headings.length; i++)
   {
      var name = tables.eq(i).attr('id').trim();
      
      headings.eq(i).append('<span>&nbsp;&nbsp;&nbsp;</span>');
      headings.eq(i).append('<input type="button" id="' + name + '-100" value="Load Next 100">');
      (function(n) { $('#' + n + '-100').click( function() { begin(100, n, 'race', breedID); } ); })(name);
      
      headings.eq(i).append('<span>&nbsp;&nbsp;&nbsp;</span>');
      headings.eq(i).append('<input type="button" id="' + name + '-500" value="Load Next 500">');
      (function(n) { $('#' + n + '-500').click( function() { begin(500, n, 'race', breedID); } ); })(name);
      
      headings.eq(i).append('<span>&nbsp;&nbsp;&nbsp;</span>');
      headings.eq(i).append('<span id="ra_loading_' + name + '"></span>');
   }
});

function begin(levels, name, type, race)
{
   var current = $('#slide-' + name).find('tr').length;
   requests = [];
   
   doRequests(current, levels, name, type, race);
   waitForRequests(levels, name, type, race);
}

function doRequests(current, levels, name, type, race)
{
   var requestCount = Math.ceil(levels / 10);
   
   for (var i = 0; i < requestCount; i++)
   {
      Log('Querying for position ' + (current + (i * 10)));
      
      var _ajax = $.ajax({
         url      : 'https://' + location.host + '/classements/suivant'
         ,method  : 'POST'
         ,data    : {
            qName: name
            ,position: (current + (i * 10))
            ,type: type
            ,race: race
         }
      });
      
      requests.push(_ajax);
   }
   
   function doLoading()
   {
      var loaded = 0;
      
      for (var i = 0; i < requests.length; i++)
      {
         if (requests[i].statusText == 'OK')
            loaded++;
      }
      
      if (loaded < requestCount)
      {
         $('#ra_loading_' + name).html('Loading ... (' + (loaded * 10) + '/' + (requestCount * 10) + ')');
         setTimeout(doLoading, 100);
      }
      else
         $('#ra_loading_' + name).html('');
   }
   
   doLoading();
}

function waitForRequests(levels, name, type, race)
{
   $.when.apply($, requests).done(function()
   {
      var outputs = [];
      
      for (var i = 0; i < requests.length; i++)
      {
         var obj = JSON.parse(requests[i].responseText);
         outputs.push(obj.content);
      }
      
      $('#slide-' + name).html( $('#slide-' + name).html() + outputs.join('') );
   });
}

function Log(s)
{
   GM_log(s);
}

function Wait ( selectorTxt, actionFunction, bWaitOnce, iframeSelector) 
{
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
        targetNodes     = $(selectorTxt);
    else
        targetNodes     = $(iframeSelector).contents ()
                                           .find (selectorTxt);

    if (targetNodes  &&  targetNodes.length > 0) {
        btargetsFound   = true;
        
        targetNodes.each ( function () {
            var jThis        = $(this);
            var alreadyFound = jThis.data ('alreadyFound')  ||  false;

            if (!alreadyFound) {
                //--- Call the payload function.
                var cancelFound     = actionFunction (jThis);
                if (cancelFound)
                    btargetsFound   = false;
                else
                    jThis.data ('alreadyFound', true);
            }
        } );
    }
    else {
        btargetsFound   = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj      = Wait.controlObj  ||  {};
    var controlKey      = selectorTxt.replace (/[^\w]/g, "_");
    var timeControl     = controlObj [controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound  &&  bWaitOnce  &&  timeControl) {
        //--- The only condition where we need to clear the timer.
        clearInterval (timeControl);
        delete controlObj [controlKey];
    }
    else {
        //--- Set a timer, if needed.
        if ( ! timeControl) {
            timeControl = setInterval ( function () {
                    Wait (    selectorTxt,
                                            actionFunction,
                                            bWaitOnce,
                                            iframeSelector
                                        );
                },
                300
            );
            controlObj [controlKey] = timeControl;
        }
    }
    Wait.controlObj   = controlObj;
}