GayRomeo Optimizer

Optimize GayRomeo (and PlanetRomeo) pages

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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]