Greasy Fork is available in English.

GayRomeo Optimizer

Optimize GayRomeo (and PlanetRomeo) pages

// ==UserScript==
// @name           GayRomeo Optimizer
// @namespace      http://marvinate.wordpress.com
// @description    Optimize GayRomeo (and PlanetRomeo) pages
// @include        http*://*.gayromeo.com/*
// @include        http*://*.planetromeo.com/*
// @include        http*://83.98.143.20/*
// @grant          GM_xmlhttpRequest
// @version        1.4
// ==/UserScript==

// Version 1.1 [2011-11-14]
// [*] Changed internal system to use real objects instead of procedural stuff
//
// Version 1.1a [2011-11-23]
// [*] Optimized JavaScript code - yes, back to procedural stuff... just shut up ;-)
//
// Version 1.2 [2012-01-14]
// [+] Integrated callback to GR-Tools
//
// Version 1.2.1 [2012-02-01]
// [*] Added GayRomeo IP address as default include
// [*] Special handling for club pages (thanks to gymnazein)
//
// Version 1.2.1a [2012-02-02]
// [*] Correct useof window.location instead of document.location
//
// Version 1.2.2 [2012-10-31]
// [*] Small DOM adjustments (thanks to gymnazein)
//
// Version 1.3 [2012-11-24]
// [*] Detect last page (thanks to djamana)
// [*] Optimized generated DOM (thanks to djamana)
// [*] Refactored internal logics
//
// Version 1.3a [2012-12-17]
// [*] Small source code enhancements
//
// Version 1.4 [2013-02-10]
// [*] Compatibility to latest GR-Tools

// -----------------------------------------------------------------------------
// ---  Search list enhancements  ----------------------------------------------
// -----------------------------------------------------------------------------

if(window.location.href.indexOf("/search/index.php") > -1 || window.location.href.indexOf("/search/?") > -1) {

  // We're in a list page, which means that we can create the list optimizer
  // that is being used to alter the current pages content
  function GrListOptimizer() {

    var currentPageRegexArray = /.*?resultPage=(\d+)/.exec(window.location.href);
    this.currentPageIndex = currentPageRegexArray == null ? 1 : parseInt(currentPageRegexArray[1]);

    var lastPageLink = MV_getElementByPath("//table[@class='searchLayout']//tr[last()]/td[last()]/a[last()]");
    this.lastPageIndex = lastPageLink == null ? -1 : parseInt(lastPageLink.innerHTML);

    var nextPageUrl = MV_getElementByPath("//body/table[2]/tbody/tr/td/a[last()]");
    this.nextPageLink = nextPageUrl.getAttribute("href");

    this.navigationTableElement = MV_getElementByPath("//body/table[2]");

  }

  // Append the given element to the currently displayed content on the screen.
  // The content is always added before the pagination table
  GrListOptimizer.prototype.appendContent = function(pElement) {
    this.navigationTableElement.parentNode.insertBefore(pElement, this.navigationTableElement);
  }

  // Append the link to the next page (if there is a next page, otherwise, we'll
  // do nothing)
  GrListOptimizer.prototype.appendNextPageLink = function(pNextPageIndex) {
    var targetPageIndex = pNextPageIndex ? pNextPageIndex : this.currentPageIndex + 1;
    if(this.lastPageIndex > 1 && targetPageIndex <= this.lastPageIndex) {

      var thisOptimizer = this;
      var nextPageLink = MV_createElement("a", {
        style: "display: block; text-align: left; border: 1px solid #ffffff; padding: 5px 5px 5px 5px;",
        name: "gayromeooptimizer_" + targetPageIndex,
        href: "#gayromeooptimizer_" + targetPageIndex
      }, "Load next page (page " + targetPageIndex + ")...");
      nextPageLink.addEventListener("click", function() { thisOptimizer.appendResultPage(nextPageLink, targetPageIndex); }, true);
      nextPageLink.addEventListener("mouseover", function() { thisOptimizer.appendResultPage(nextPageLink, targetPageIndex); }, true);
      this.appendContent(nextPageLink);

    }
  }

  // Load the content for the page with the specified index and append it to
  // the current page
  GrListOptimizer.prototype.appendResultPage = function(pSourceLinkElement, pPageIndex) {

    var loadingMessageDiv = MV_createElement("div", { style: "border: 1px solid #ffffff; padding: 5px 5px 5px 5px;" }, "Loading next page (page " + pPageIndex + ")...");
    pSourceLinkElement.parentElement.insertBefore(loadingMessageDiv, pSourceLinkElement);
    pSourceLinkElement.parentElement.removeChild(pSourceLinkElement);

    var thisOptimizer = this;
    var thisPageParameterStart = window.location.href.indexOf("?");
    var thisPageBaseUrl = thisPageParameterStart < 0 ? window.location.href : window.location.href.substring(0, thisPageParameterStart);
    var nextPageOriginalUrl = thisPageBaseUrl + this.nextPageLink;
    var nextPageMatchedArray = /(.*?)resultPage=\d+(.*)/.exec(nextPageOriginalUrl);
    var nextPageUrl = nextPageMatchedArray == null ? nextPageOriginalUrl : (nextPageMatchedArray[1] + "&resultPage=" + pPageIndex + nextPageMatchedArray[2]);

    MV_sendRequest({
      url: nextPageUrl,
      xpath: "//body/table[1]",
      onComplete: function(pElement, pRequest, pResponse) {
        thisOptimizer.appendResultFromResponse(loadingMessageDiv, pElement, pRequest, pResponse);
      },
      onError: function(pRequest, pResponse, pException) {
        thisOptimizer.appendResultFromError(loadingMessageDiv, pRequest, pResponse, pException);
      },
      pageIndex: pPageIndex
    });

  }

  GrListOptimizer.prototype.appendResultFromResponse = function(pLoadingElement, pElement, pRequest, pResponse) {

    // Replace the "loading" message with the title of the current page
    var nextPageTableHeader = MV_createElement("div", { style: "padding: 5px 5px 5px 5px; border: 1px solid #ffffff" }, "Result list (page " + pRequest.pageIndex + ")");
    pLoadingElement.parentElement.insertBefore(nextPageTableHeader, pLoadingElement);
    pLoadingElement.parentElement.removeChild(pLoadingElement);

    // Append the content we've received for the next page
    this.appendContent(pElement);
    this.appendNextPageLink(pRequest.pageIndex + 1);

    // retrigger GRT-Tools if installed
    // (http://userscripts.org/scripts/show/33184)
    var grtEvent = document.createEvent('Event');
    grtEvent.initEvent('GRT_RETRIGGER', false, false);
    document.dispatchEvent(grtEvent);

  }

  GrListOptimizer.prototype.appendResultFromError = function(pLoadingElement, pRequest, pResponse, pException) {

    var errorElement = MV_createElement("div", { style: "border: 1px solid red; padding: 5px 5px 5px; font-weight: bold;" }, "Cannot load page " + pRequest.pageIndex);
    if(pException != null) {
      errorElement.appendChild(document.createTextNode(" [" + pException + "]"))
    }

    var thisOptimizer = this;
    var retryLink = MV_createElement("a", null, "Retry loading page");
    retryLink.addEventListener("click", function() { thisOptimizer.appendResultPage(errorElement, pRequest.pageIndex); }, true);
    errorElement.appendChild(document.createTextNode(" ("));
    errorElement.appendChild(retryLink);
    errorElement.appendChild(document.createTextNode(")"));

    pLoadingElement.parentElement.insertBefore(errorElement, pLoadingElement);
    pLoadingElement.parentElement.removeChild(pLoadingElement);

  }

  // Initialize the Optimizer
  var listOptimizer = new GrListOptimizer();
  listOptimizer.appendNextPageLink();

}


// -----------------------------------------------------------------------------
// ---  Close a picture in a popup Window by clicking on it. -------------------
// -----------------------------------------------------------------------------

if(window.location.pathname.indexOf("/auswertung/pix/popup") > -1) {
  var imageElementsResult = document.evaluate("//img", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  for(var i = 0; i < imageElementsResult.snapshotLength; i++) {
    imageElementsResult.snapshotItem(i).addEventListener("click", function() {
      window.close();
    }, false);
  }
}


// -----------------------------------------------------------------------------
// ---  Includes  --------------------------------------------------------------
// -----------------------------------------------------------------------------

// Include start [xmlhttpUtil.js]
/**
 * Sends the request to the remote system and evaluates the response which
 * must be valid HTML and contain a specified element identifiable by an XPath
 * expression
 *
 * Expected properties in the request are:
 * url
 *   the URL to which the request will be made
 * xpath
 *   the XPath expression that must evaluate to an element that will be
 *   extracted from the response received by the remote system
 * onComplete
 *   a function that will be called when the result has been received and the
 *   content should be displayed
 * onError
 *   a function that will be called if the request cannot be sent or the
 *   response received is invalid
 */
function MV_sendRequest(pRequest) {

  var processResponse = function(pResponse) {
    var responseHtmlStart = pResponse.responseText.indexOf("<html");
    var responseHtmlEnd = pResponse.responseText.indexOf("</html>");
    if(responseHtmlStart < 0 || responseHtmlEnd < 0) {
      pRequest.onError(pRequest, pResponse, "Invalid HTML document received");
    } else {
      var responseHtmlElement = document.createElement("html");
      responseHtmlElement.innerHTML = pResponse.responseText.substring(responseHtmlStart, responseHtmlEnd + "</html>".length);
      var responseXpathResult = document.evaluate(pRequest.xpath, responseHtmlElement, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
      if(responseXpathResult.snapshotLength <= 0) {
        pRequest.onError(pRequest, pResponse, "Invalid HTML document received");
      } else {
        pRequest.onComplete(responseXpathResult.snapshotItem(0), pRequest, pResponse);
      }
    }
  };

  try {
    GM_xmlhttpRequest({
      method: "GET",
      url: pRequest.url,
      onload: processResponse,
      onerror: function(pResponse) { pRequest.onError(pRequest, pResponse, null); }
    });
  } catch(e) {
    pRequest.onError(pRequest, null, e);
  }

}
// Include end [xmlhttpUtil.js]
// Include start [domUtil.js]
function MV_removeElementsByPath(pPath) {
  var pathResult = document.evaluate(pPath, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  if(pathResult.snapshotLength > 0) {
    for(var i=0; i < pathResult.snapshotLength; i++) {
      var pathNode = pathResult.snapshotItem(i);
      pathNode.parentNode.removeChild(pathNode);
    }
  }
}

function MV_getElementByPath(pPath, pRoot) {
  var pathResult = document.evaluate(pPath, pRoot == null ? document : pRoot, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  return pathResult.snapshotLength <= 0 ? null : pathResult.snapshotItem(0);
}

function MV_createButton(pAttributes, pClickListener) {
  pAttributes.type = "button";
  var resultElement = MV_createElement("input", pAttributes);
  if(pClickListener != null) {
    resultElement.addEventListener("click", pClickListener, true);
  }
  return resultElement;
}

function MV_createElement(pElementName, pAttributes, pInnerHtml) {
  var resultElement = document.createElement(pElementName);
  for(attributeName in pAttributes) {
    resultElement.setAttribute(attributeName, pAttributes[attributeName]);
  }
  if(pInnerHtml != null) {
    resultElement.innerHTML = pInnerHtml;
  }
  return resultElement;
}
// Include end [domUtil.js]