cam4 Optimizer

By marvinate Last update Mar 15, 2012 — Installed 8,167 times.

There are 17 previous versions of this script.

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name           cam4.com Optimizer
// @namespace      http://marvinate.wordpress.com/
// @description    Optimize cam4 pages
// @include        http*://cam4.com/*
// @include        http*://*.cam4.com/*
// @version        2.4.1
// ==/UserScript==
//
// Version 1.1  [2010-12-27]
// [-] Bugfix: Adjusted to new cam4 layout
//
// Version 1.1a [2010-12-29]
// [-] Bugfix: Reenabled list enhancements
//
// Version 1.1b [2010-12-29]
// [+] Removed additional advertisments
//
// Version 1.2  [2011-04-12]
// [*] New cam4 layout for start page
//
// Version 1.2a [2011-05-22]
// [+] Include https:// URLs
//
// Version 1.2b [2011-06-08]
// [+] Include *.cam4.com
//
// Version 1.2c [2011-07-09]
// [+] Small enhancements
//
// Version 1.2d [2011-10-02]
// [-] Bugfix for preview generation
//
// Version 2.0 [2011-10-29]
// [*] Profile/Archive: Reformatted archive image list (include series marker)
// [+] Profile: Add optimized list of preview images for user
// [+] Profile/Image: Optimized layout of preview image detail page
// [+] List: Optimized bottom navigation
// [+] List: Show full status message in link hover
//
// Version 2.1 [2011-11-12]
// [+] Allow popup creation from video window
//
// Version 2.2 [2011-11-21]
// [+] Automatically load next pages in same document after click
// [+] Better placement of friends list
//
// Version 2.2.1 [2011-12-15]
// [*] Minor enhancements in profile detail image display
// [*] Allow reloads in detached video window
// [*] Added reload links to list pagination
//
// Version 2.2.2 [2012-01-17]
// [*] Tipping area can now be re-enabled
// [+] Remove "Send private tip" div
//
// Version 2.3 [2012-01-20]
// [+] Preferences Screen
//
// Version 2.4 [2012-02-02]
// [*] Completely rewritten codebase
// [*] Optimized layout
// [+] Highlight users from a specific country (editable in preferences)
//
// Version 2.4.1 [2012-03-15]
// [-] Bugfix: Archite wasn't rendered correctly


document.title                          = window.location.href;
document.optimizerVersion               = "2.4.1";


// =============================================================================
// ===  Global :: Additional CSS  ==============================================
// =============================================================================

GM_addStyle(".marvinateButton { display: inline-block; font-size: 14px; font-weight: bold; background-color: #FC531D; padding: 4px 8px 4px 8px; margin: 0px 10px 0px 0px; color: white; border: 1px solid #FC531D; } ")
GM_addStyle(".marvinateButton:hover { cursor: pointer; text-decoration: none; background-color: #FFE1D7; color: #FC531D; } ")
GM_addStyle(".marvinateButtonSmall { display: inline-block; font-weight: bold; background-color: #FFE1D7; padding: 4px 8px 4px 8px; margin: -4px 5px -4px 10px; color: #FC531D; border: 0; }");
GM_addStyle(".marvinateButtonSmall:hover { cursor: pointer; text-decoration: none; background-color: #FC531D; color: white; } ")
GM_addStyle(".marvinateSelf { padding: 9px 0px 0px 250px; line-height: 1; position: absolute; left: 0px; top: 0px; width: 550px; }");
GM_addStyle(".marvinateSelf small { font-size: 10px; }");
GM_addStyle(".marvinateSelfTitle { font-size: 12px; font-weight: bold; color: #000000; }");
GM_addStyle(".marvinatePrefs { width: 500px; margin: 20px auto 0px auto; padding: 10px 15px 10px 15px; border: 5px solid #FC531D; background-color: #FFE1D7; }");
GM_addStyle(".marvinatePrefsTitle { font-weight: bold; font-size: 15px; margin: 0px 0px 10px 0px; }");
GM_addStyle(".marvinatePrefsWrapper { position: absolute; left: 0; top: 0; width: 100%; height: 100%; }");
GM_addStyle(".marvinatePrefsTable { width: 100%; }");
GM_addStyle(".marvinatePrefsTable td.title { width: 200px; padding: 1px 2px 1px 0px; }");
GM_addStyle(".marvinatePrefsTable td.content { padding: 1px 0px 1px 2px; }");
GM_addStyle(".marvinatePrefsTable td.content input[type=text] { width: 150px; }");
GM_addStyle(".marvinatePrefsButtons { margin: 10px 0px 0px 0px; }");
GM_addStyle(".marvinatePager .pagerLinkActive { background-color: #FC531D; color: white; }");
GM_addStyle(".marvinateNextPageArea { font-size: 12px; cursor: pointer; margin: 5px 10px 5px 10px; padding: 10px 10px 10px 10px; border: 5px solid #FC531D; font-weight: bold; background-color: #FFE1D7; }");
GM_addStyle(".marvinateNextPageAreaWrapper { clear: both; padding: 10px 0px 0px 0px; }");
GM_addStyle(".marvinateNextPageContent { font-size: 12px; margin: 5px 10px 5px 10px; padding: 13px 10px 13px 10px; border: 2px solid #FC531D; }");
GM_addStyle(".marvinateNextPageContentWrapper { clear: both; border: 5px solid blue; }");
GM_addStyle(".marvinateDetailTitle { clear: left; margin: 5px 0px 5px 0px; padding: 0px 5px 0px 5px; font-size: 12px; font-weight: bold; }");
GM_addStyle(".marvinateDetailActions { margin: 5px 0px 5px 0px; text-align: left; }");
GM_addStyle(".marvinateDetailArea { clear: both; display: block; margin: 0px 10px 10px 10px; padding: 5px 5px 5px 5px; border: 2px solid #FFE1D7; }");
GM_addStyle(".marvinateDetailAreaPhotos { display: inline-block; }");
GM_addStyle(".marvinateDetailAreaArchive { max-height: 200px; overflow: auto; }");
GM_addStyle(".marvinateImagesTitle { font-weight: bold; font-size: 16px; margin: 0px 0px 5px 0px; }");
GM_addStyle(".marvinateImagesList { margin: 0px 0px 0px 0px; padding: 5px 5px 5px 5px; border: 2px solid #FFE1D7; }");
GM_addStyle(".marvinateImagesList img.active { border: 4px solid #FC531D; margin: -4px; }");
GM_addStyle(".marvinateImagesContent { margin: 10px 0px 0px 0px; padding: 5px 5px 5px 5px; border: 2px solid #FFE1D7; }");


// =============================================================================
// ===  Global :: Remove advertisments  ========================================
// =============================================================================

MV_removeElementsByPath("//a[@class='upgrade']");
MV_removeElementsByPath("//a[@href='/gold']");
MV_removeElementsByPath("//a[@href='/profiles/tokens.jsp']");
MV_removeElementsByPath("//a[@href='/contest']");
MV_removeElementsByPath("//a[@href='/contest/month/']");
MV_removeElementsByPath("//a[@href='/super_shows']");
MV_removeElementsByPath("//a[@href='/videos']");
MV_removeElementsByPath("//a[contains(@href, 'cam4bucks.com')]");
MV_removeElementsByPath("//a[contains(@href, 'cam4premium.com')]");
MV_removeElementsByPath("//a[contains(@href, 'theater.aebn.net')]");
MV_removeElementsByPath("//a[contains(@href, 'naughtyreviews.com')]");
MV_removeElementsByPath("//body/h2");
MV_removeElementsByPath("//div[@class='ads']");
MV_removeElementsByPath("//div[@class='bottom_footer']");
MV_removeElementsByPath("//div[@class='c4_socials']");
MV_removeElementsByPath("//div[@class='innerTube']");
MV_removeElementsByPath("//div[@class='newjoin']");
MV_removeElementsByPath("//div[@id='footer']");
MV_removeElementsByPath("//div[@id='headerBanner']");
MV_removeElementsByPath("//div[@id='myAccount']");
MV_removeElementsByPath("//div[@id='options']");
MV_removeElementsByPath("//div[@id='right-content']");
MV_removeElementsByPath("//div[@id='right-content-top']");
MV_removeElementsByPath("//div[@id='right-content-bottom']");
MV_removeElementsByPath("//div[@id='socialBookmarkProfile']");
MV_removeElementsByPath("//div[@id='subfoot']");
MV_removeElementsByPath("//div[@id='tippingJarDiv']");
MV_removeElementsByPath("//div[@id='topHeader']");
MV_removeElementsByPath("//li[@class='right search']");
MV_removeElementsByPath("//iframe");
MV_removeElementsByPath("//h3");


// =============================================================================
// ===  Global :: Self identification and header  ==============================
// =============================================================================

var logoDiv                             = document.getElementById('C4Logo');
if(logoDiv != null) {

  var openPreferencesLink               = MV_createElement("div", { "class": "marvinateButtonSmall" }, "Open Preferences");
  openPreferencesLink.addEventListener("click", preferencesOpen, true);

  var titleLink                         = MV_createElement("div", { "class": "marvinateSelfTitle" });
  titleLink.appendChild(document.createTextNode("Optimizations by marvin - Version " + document.optimizerVersion + " "));
  titleLink.appendChild(openPreferencesLink);

  var authorDiv                         = MV_createElement("div", { "class": "marvinateSelf" });
  authorDiv.appendChild(titleLink);
  authorDiv.appendChild(MV_createElement("div", { "style": "padding: 3px 0px 0px 0px;"}, "<small><a style=\"color: #000000;\" target=\"_blank\" href=\"http://marvinate.wordpress.com\">http://marvinate.wordpress.com</a></small>"));
  authorDiv.appendChild(MV_createElement("div", { "style": "padding: 10px 0px 0px 0px;"}, "<small><i>\"I think,\" Marvin murmured at last, from deep within his corroding rattling thorax, \"I feel good about it.\"</i></small>"));
  logoDiv.insertBefore(authorDiv, null);

}


// =============================================================================
// ===  Global :: Preferences  =================================================
// =============================================================================

function preferencesRow(pTitle, pContent) {
  var tableRow                          = MV_createElement("tr");
  var titleCell                         = MV_createElement("td", { "class": "title" }, pTitle);
  var contentCell                       = MV_createElement("td", { "class": "content" });
  contentCell.appendChild(pContent);
  tableRow.appendChild(titleCell);
  tableRow.appendChild(contentCell);
  return tableRow;
}

function preferencesOpen() {

  var preferencesInnerDiv               = MV_createElement("div", { "class": "marvinatePrefs"});
  var preferencesOuterDiv               = MV_createElement("div", { "class": "marvinatePrefsWrapper"});
  preferencesOuterDiv.close             = function() { preferencesOuterDiv.parentNode.removeChild(preferencesOuterDiv); }
  preferencesOuterDiv.appendChild(preferencesInnerDiv);

  var showTippingAreaCheckbox           = MV_createElement("input", { "type": "checkbox", "value": "true" });
  if(GM_getValue("marvinateShowTippingArea")) {
    showTippingAreaCheckbox.checked     = true;
  }
  var showTippingAreaCheckboxInfo       = MV_createElement("div");
  showTippingAreaCheckboxInfo.appendChild(showTippingAreaCheckbox);
  showTippingAreaCheckboxInfo.appendChild(document.createTextNode(" Show tipping area on user page"));

  var highlightLanguages                = GM_getValue("marvinateHighlightLanguages");
  var highlightLanguagesField           = MV_createElement("input", { "type": "text", "value": highlightLanguages == null ? "" : highlightLanguages});

  var prefsTable                        = MV_createElement("table", { "class": "marvinatePrefsTable"});
  prefsTable.appendChild(preferencesRow("Tipping area", showTippingAreaCheckboxInfo));
  prefsTable.appendChild(preferencesRow("Highlight users from countries", highlightLanguagesField));

  var saveFunction                      = function() {
    GM_setValue("marvinateShowTippingArea", showTippingAreaCheckbox.checked);
    GM_setValue("marvinateHighlightLanguages", highlightLanguagesField.value);
    alert("Preferences saved! (Reload the current page to make sure they're applied)");
    preferencesOuterDiv.close();
  }

  var prefsButtonArea                   = MV_createElement("div", { "class": "marvinatePrefsButtons"});
  prefsButtonArea.appendChild(MV_createButton({ "class": "marvinateButton", "value": "Save Preferences" }, saveFunction));
  prefsButtonArea.appendChild(MV_createButton({ "class": "marvinateButton", "value": "Cancel" }, function() { preferencesOuterDiv.close(); }));

  preferencesInnerDiv.appendChild(MV_createElement("div", { "class": "marvinatePrefsTitle" }, "Marvinate Preferences"));
  preferencesInnerDiv.appendChild(prefsTable);
  preferencesInnerDiv.appendChild(prefsButtonArea);
  document.body.appendChild(preferencesOuterDiv);

}


// =============================================================================
// ===  Userlist  ::  Variables  ===============================================
// =============================================================================

var optimizeUserListListeners           = [];

function getCurrentPageInfo() {
  var currentPagePrefix                 = null;
  var currentPageIndex                  = 1;
  var lastSlashIndex                    = window.location.href.lastIndexOf("/");
  if(lastSlashIndex == window.location.href.length - 1) {
    currentPagePrefix                   = window.location.href.substring(0, lastSlashIndex);
  } else {
    var lastUrlPart                     = parseInt(window.location.href.substring(lastSlashIndex + 1));
    if(lastUrlPart) {
      currentPageIndex                  = lastUrlPart;
      currentPagePrefix                 = window.location.href.substring(0, lastSlashIndex);
    } else {
      currentPagePrefix                 = window.location.href;
    }
  }
  return { prefix: currentPagePrefix, index: currentPageIndex, minIndex: Math.max(1, currentPageIndex - 7), maxIndex: Math.max(15, currentPageIndex + 7) };
}


// =============================================================================
// ===  Userlist :: Highlight specified countries  =============================
// =============================================================================

optimizeUserListListeners.push(function() {
  var profileNodes                      = document.evaluate("//div[@class='profileBox']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  var highlightCountriesPrefsValue      = GM_getValue("marvinateHighlightLanguages");
  var highlightCountries                = highlightCountriesPrefsValue == null ? null : highlightCountriesPrefsValue.split(",");
  if(profileNodes != null && highlightCountries != null) {
    for(var i=0; i < profileNodes.snapshotLength; i++) {
      var profileNode                   = profileNodes.snapshotItem(i);
      var imageNode                     = document.evaluate("div[@class='profileDataBox']//span[@class='country']//img", profileNode, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);
      for(var j=0; j < highlightCountries.length; j++) {
        if(highlightCountries[j].trim() == imageNode.getAttribute("alt")) {
          profileNode.setAttribute("style", "border: 2px solid green; margin: 1px 1px 1px 1px !important; background-color: #dddddd; position: relative; ");
        }
      }
    }
  }
});


// =============================================================================
// ===  Userlist :: Optimize boxes  ============================================
// =============================================================================

optimizeUserListListeners.push(function() {
  var profileDetailBoxList              = document.evaluate("//div[@class='profileBox']/div[@class='profileDataBox']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  for(var i=0; i < profileDetailBoxList.snapshotLength; i++) {
    var profileDetailElement            = profileDetailBoxList.snapshotItem(i);
    var statusMessageLink               = document.evaluate("div[@class='profileDetailBox']//a", profileDetailElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    if(statusMessageLink != null && statusMessageLink.snapshotLength > 1) {
      var statusMessage                 = statusMessageLink.snapshotItem(1).innerHTML;
      if(statusMessage != null && statusMessage.length > 0) {
        for(var j=0; j < statusMessageLink.snapshotLength; j++) {
          statusMessageLink.snapshotItem(j).setAttribute("title", statusMessage);
        }
      }
    }
  }
});


// =============================================================================
// ==  Userlist :: Optimize pager  =============================================
// =============================================================================

var pagerElementList                    = document.evaluate("//div[@class='pager']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
if(pagerElementList.snapshotLength > 0) {

  var currentPageInfo                   = getCurrentPageInfo();
  var newPagerElement                   = MV_createElement("div", { "class": "marvinatePager" });
  newPagerElement.appendPageLink        = function(pPageIndex, pActive) {
    var targetPageLink                  = currentPageInfo.prefix + "/" + String(pPageIndex);
    this.appendChild(MV_createElement("a", { "href": targetPageLink, "class": pActive ? "pagerLink pagerLinkActive" : "pageLink" }, pPageIndex));
  }

  newPagerElement.appendChild(MV_createElement("a", { "style": "border-left: 0", "href": "JavaScript:window.location.href = window.location.href;" }, "Reload page"));
  if(currentPageInfo.minIndex > 1) {
    newPagerElement.appendPageLink(1);
    if(currentPageInfo.minIndex > 2) {
      newPagerElement.appendChild(MV_createElement("span", { "style": "color: #FC531D;" }, "..."));
    }
  }
  for(var i=currentPageInfo.minIndex; i < currentPageInfo.maxIndex; i++) {
    newPagerElement.appendPageLink(i, i == currentPageInfo.index);
  }
  newPagerElement.appendChild(MV_createElement("span", { "style": "color: #FC531D" }, "..."));
  newPagerElement.appendChild(MV_createElement("a", { "href": "JavaScript:window.location.href = window.location.href;" }, "Reload page"));

  var pagerParentNode                   = pagerElementList.snapshotItem(0);
  pagerParentNode.innerHTML             = "";
  pagerParentNode.appendChild(newPagerElement);

}


// =============================================================================
// ===  Userlist :: Optimize user list  ========================================
// =============================================================================

function optimizeUserList() {
  for(var i=0; i < optimizeUserListListeners.length; i++) {
    optimizeUserListListeners[i]();
  }
}
optimizeUserList(); // When loading we call all the listeners


// =============================================================================
// ==  Userlist :: Integrate loading of further pages  =========================
// =============================================================================

var pagerElementList                    = document.evaluate("//div[@class='pager']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
if(pagerElementList.snapshotLength > 0) {

  var currentPageInfo                   = getCurrentPageInfo();

  function appendSearchResultSensitiveArea(pPageIndex) {
    var loadNextPageInnerDiv            = MV_createElement("div", { "class": "marvinateNextPageArea" }, "Load next page (Page " + pPageIndex + ")");
    loadNextPageInnerDiv.addEventListener("click", function() { appendSearchResultPage(pPageIndex, loadNextPageInnerDiv); }, true);
    var loadNextPageOuterDiv            = MV_createElement("div", { "class" : "marvinateNextPageAreaWrapper" });
    loadNextPageOuterDiv.appendChild(loadNextPageInnerDiv);
    var pagerElement                    = pagerElementList.snapshotItem(0);
    pagerElement.parentNode.insertBefore(loadNextPageOuterDiv, pagerElement);
  }

  function appendLoadedPageError(pRequest, pResponse, pException) {
    pRequest.pageDiv.innerHTML                        = "Cannot load next page (Page " + pRequest.pageIndex + ")" + (pException == null ? "" : (" [" + pException + "]"));
    pRequest.pageDiv.setAttribute("style", "background-color: #FC531D; color: #ffffff;");
  }

  function appendLoadedPageContent(pElement, pRequest, pResponse) {

    var directoryDivHeader              = MV_createElement("div", { "class": "marvinateNextPageContent" });
    directoryDivHeader.appendChild(document.createTextNode("Results from page " + pRequest.pageIndex))
    directoryDivHeader.appendChild(MV_createElement("a", { "class": "marvinateButtonSmall", "href": currentPageInfo.prefix + "/" + pRequest.pageIndex }, "Reload page directly"));
    directoryDivHeader.appendChild(MV_createElement("a", { "class": "marvinateButtonSmall", "href": "JavaScript:window.location.href = window.location.href;" }, "Reload original page"));

    var directoryDivWrapper             = document.createElement("div", { "class": "marvinateNextPageContentWrapper" });
    directoryDivWrapper.appendChild(directoryDivHeader);
    directoryDivWrapper.appendChild(pElement);
    pRequest.pageDiv.parentNode.insertBefore(directoryDivWrapper, pRequest.pageDiv);
    pRequest.pageDiv.parentNode.removeChild(pRequest.pageDiv);

    optimizeUserList();
    appendSearchResultSensitiveArea(pRequest.pageIndex + 1);

  }

  function appendSearchResultPage(pPageIndex, pDiv) {
    pDiv.innerHTML                      = "";
    pDiv.appendChild(MV_createElement("div", { "class": "marvinateNextPageWaiting" }, "Loading next page (Page " + pPageIndex + ")"));
    MV_sendRequest({
      url: currentPageInfo.prefix + "/" + pPageIndex,
      xpath: "//div[@id='directoryDiv']",
      onComplete: appendLoadedPageContent,
      onError: appendLoadedPageError,
      pageIndex: pPageIndex,
      pageDiv: pDiv
    });
  }

  // Inital next page area
  appendSearchResultSensitiveArea(currentPageInfo.index + 1);

}


// =============================================================================
// ===  Profile Detail :: Reformat video detail area  ==========================
// =============================================================================

function openUserInPopup() {

  var lastSlash                         = window.location.href.lastIndexOf("/");
  var profileName                       = window.location.href.substring(lastSlash + 1);
  var rootUrl                           = window.location.href.substring(lastSlash);

  var newWindowBodyContent              = "<div class=\"marvinateDetailTitle\" style=\"margin: 0px 0px 10px 0px\">";
  newWindowBodyContent                 += "Video for: <a target=\"_blank\" href=\"" + window.location.href + "\">" + profileName + "</a> &nbsp; <input class=\"marvinateButtonSmall\" type=\"button\" value=\"Reload video area\" id=\"marvinateReload\" /></div>";
  newWindowBodyContent                 += document.getElementById('Cam4VChat').innerHTML;

  var newWindowContent                  = "<html><head><title>cam4 [" + profileName + "]</title></head>";
  newWindowContent                     += "<body id=\"marvinateBody\">" + newWindowBodyContent + "</body></html>";

  var newWindow                         = window.open("about:blank", "", "width=950,height=550");
  newWindow.document.writeln(newWindowContent);
  newWindow.document.close();

  function makeReloadable() {
    var newWindowBodyElement            = newWindow.document.getElementById('marvinateBody');
    var reloadLink                      = newWindow.document.getElementById('marvinateReload');
    reloadLink.addEventListener("click", function() {
      newWindowBodyElement.innerHTML      = newWindowBodyContent;
      makeReloadable();
    }, true);
  }
  makeReloadable();

}

if(document.getElementById('profile_page') != null) {

  var nicknameResult                    = document.evaluate("//div[@class='nickname']", document, null, XPathResult.STRING_TYPE, null);
  if(nicknameResult != null) {
    document.title                      = nicknameResult.stringValue + " [cam4.com]";
  }

  var addFriendDiv                      = document.getElementById('addFriendFavoriteDiv');
  if(addFriendDiv != null) {
    addFriendDiv.setAttribute("style", "clear: both");
  }

  var camPanel                          = document.getElementById('camPaneBig') == null ? document.getElementById('camPaneSmall') : document.getElementById('camPaneBig');
  var flashObjectElement                = document.getElementById('Cam4VChat');
  if(flashObjectElement != null && camPanel != null) {

    var actionsPanel                    = MV_createElement("div", { "class": "marvinateDetailActions" });

    var tippingDiv                      = document.getElementById('tippingDiv');
    if(tippingDiv != null) {
      tippingDiv.visible                = GM_getValue("marvinateShowTippingArea");
      tippingDiv.setAttribute("style", tippingDiv.visible ? "display: inline-block; " : "display: none; ");
      tippingDiv.showOrHide             = function() {
        if(tippingDiv.visible) {
          document.getElementById('tippingDiv').setAttribute("style", "display: none; ");
          tippingDiv.visible            = false;
        } else {
          document.getElementById('tippingDiv').setAttribute("style", "display: inline-block; ");
          tippingDiv.visible            = true;
        }
      }
      actionsPanel.appendChild(MV_createButton({ "class": "marvinateButton", "value": "Show/Hide tipping area" }, tippingDiv.showOrHide));
    }
    actionsPanel.appendChild(MV_createButton({ "class": "marvinateButton", "value": "Open broadcast in popup window" }, openUserInPopup));

    camPanel.setAttribute("style", "display: block; padding: 5px; margin: 2px 4px 8px 10px; border: 2px solid #FFE1D7; height: auto; ");
    camPanel.appendChild(actionsPanel);

  }

  var profileContainerResult            = document.evaluate("//div[@class='profile-container']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  var profileContainerDiv               = profileContainerResult.snapshotItem(0);
  var friendsDiv                        = document.getElementById('friendsDiv');
  if(friendsDiv != null) {
    friendsDiv.parentNode.removeChild(friendsDiv);
    profileContainerDiv.parentNode.appendChild(friendsDiv);
  }

}


// =============================================================================
// ===  Profile Detail :: Reformat preview images  =============================
// =============================================================================

var showPhotosElement                   = document.getElementById('showPhotos');
var previewImages                       = showPhotosElement == null ? null : document.evaluate("//div[@class='imagesDiv']/a", showPhotosElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if(previewImages != null && previewImages.snapshotLength > 0) {
  var profileImagesDiv                  = MV_createElement("div", { "class": "marvinateDetailArea marvinateDetailAreaPhotos"});
  profileImagesDiv.appendChild(MV_createElement("div", { "class": "marvinateDetailTitle"}, "Profile photos"));
  for(var i=0; i < previewImages.snapshotLength; i++) {
    var linkElement                     = previewImages.snapshotItem(i);
    var imageElement                    = linkElement.firstChild;
    var profileImageLink                = MV_createElement("a", { "href": linkElement.href });
    profileImageLink.appendChild(MV_createElement("img", { "height": "90", "src": imageElement.getAttribute('onmouseover').replace(/setBigIMG\('(.*?)\/\d+x\d+\/(.*?)'.*/g, '$1/120x90/$2') }));
    profileImagesDiv.appendChild(profileImageLink);
  }
  var profileElement                    = document.getElementById('profile_page');
  profileElement.parentNode.insertBefore(profileImagesDiv, profileElement);
  if(showPhotosElement != null) {
    showPhotosElement.innerHTML         = "";
  }
}


// =============================================================================
// ===  Profile Detail :: Image display  =======================================
// =============================================================================

if(window.location.href.indexOf('/pp.jsp?userId') > -1) {
  var imageList                         = document.evaluate("//a/img", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  if(imageList.snapshotLength > 0) {

    var userId                          = window.location.href.replace(/.*?userId=(\d+)&.*/g, '$1');
    var previewListTitle                = document.evaluate("//h2", document, null, XPathResult.STRING_TYPE, null).stringValue;

    var firstFullImageUrl               = null;
    var previewListContent              = "<div class=\"marvinateImagesTitle\">" + previewListTitle + "</div>";
    previewListContent                 += "<div class=\"marvinateImagesList\">";
    for(var i=0; i < imageList.snapshotLength; i++) {
      var imageElement                  = imageList.snapshotItem(i);
      var imageMouseoverValue           = imageElement.getAttribute("onmouseover");
      var targetImage                   = imageMouseoverValue.replace(/setBigIMG\('(.*?)'.*/g, '$1');
      var imageIdValue                  = imageMouseoverValue.replace(/.*?\/(\d+)\..*'.*/g, '$1');
      var imageActive                   = window.location.href.indexOf("&id=" + imageIdValue) > 0;
      previewListContent               += "<a style=\"display: inline-block; margin: 0px 5px 5px 0px;\" ";
      previewListContent               += " href=\"/profiles/pp.jsp?userId=" + userId + "&id=" + imageIdValue + "#marvinateFullImage\"";
      previewListContent               += "><img height=\"90\" src=\"" + imageElement.src + "\"";
      if(imageActive) {
        previewListContent             += " class=\"active\""
      }
      if(firstFullImageUrl == null || imageActive) {
        firstFullImageUrl               = targetImage;
      }
      previewListContent               += " /></a>";
    }
    previewListContent                 += "</div>";
    previewListContent                 += "<div class=\"marvinateImagesContent\">";
    previewListContent                 += "<a name=\"marvinateFullImage\" href=\"" + firstFullImageUrl + "\">";
    previewListContent                 += "<img id=\"marvinateFullImage\" src=\"" + firstFullImageUrl + "\" /></a>";
    previewListContent                 += "</div>";

    var targetBodyElement               = MV_getElementByPath("//body");
    targetBodyElement.innerHTML         = previewListContent;

  }
}


//=============================================================================
//===  Profile Detail :: Display preview images  ==============================
//=============================================================================

var archiveContainerPath                = document.evaluate("//div[@class='archive-container']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
var scriptNodeWithSnapshots             = document.evaluate("//script[contains(text(), 'snapshots[0]')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if(scriptNodeWithSnapshots.snapshotLength > 0 && archiveContainerPath.snapshotLength <= 0) {
  var profileName                       = window.location.pathname.substring(1);
  var scriptElementContent              = scriptNodeWithSnapshots.snapshotItem(0).innerHTML;
  var imageIdentifiers                  = archiveCollectImageIdentifiers(scriptElementContent);
  var imageSeries                       = archiveListImages(imageIdentifiers, profileName, null);
  if(imageSeries.series > 0) {

    var previewImagesDiv                = MV_createElement("div", { "class": "marvinateDetailArea marvinateDetailAreaArchive" });
    previewImagesDiv.appendChild(MV_createElement("div", { "class": "marvinateDetailTitle" }, "Previous Images (Latest images appear first)"));
    previewImagesDiv.appendChild(MV_createElement("div", {}, imageSeries.content));

    var mainTabsPath                    = document.evaluate("//div[@class='maintabs']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
    var mainTabsElement                 = mainTabsPath.snapshotItem(0);
    mainTabsElement.parentNode.insertBefore(previewImagesDiv, mainTabsElement.nextSibling);
    var showBroadcastPanelPath          = document.evaluate("//div[@id='showBroadcastImages']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
    var showBroadcastPanel              = showBroadcastPanelPath.snapshotItem(0);
    showBroadcastPanel.parentNode.removeChild(showBroadcastPanel);

  }
}


// =============================================================================
// ===  Profile Archive :: Generation script  ==================================
// =============================================================================

function archiveCollectImageIdentifiers(pScriptElementContent) {
  var imageIdentifiers                  = new Array();
  var imageIdentifiers                  = new Array();
  var needle                            = "snapshots[";
  var needleIndex                       = pScriptElementContent.indexOf(needle);
  var declarationEndIndex               = needleIndex < 0 ? -1 : pScriptElementContent.indexOf("var base", needleIndex);
  while(needleIndex > 0 && needleIndex < declarationEndIndex) {
    var valueStartIndex                 = pScriptElementContent.indexOf("=", needleIndex);
    if(valueStartIndex > -1) {
      var valueEndIndex                 = pScriptElementContent.indexOf(";", valueStartIndex);
      if(valueEndIndex > -1) {
        var imageId                     = pScriptElementContent.substring(valueStartIndex + 2, valueEndIndex);
        imageIdentifiers.push(imageId);
      }
    }
    needleIndex                         = valueStartIndex < 0 ? -1 : pScriptElementContent.indexOf(needle, valueStartIndex);
  }
  return imageIdentifiers;
}

function archiveListImages(pImageIdentifiers, pProfileName, pBigpicUrl) {

  // Now we loop through all the images currently available
  var imageSeriesDivContent             = "";
  var imageSeriesIndex                  = 0;
  var imageSeriesCurrentIndex           = -1;
  for(var i=0; i < pImageIdentifiers.length; i++) {

    // A great difference between the image identifiers signalizes a break
    // between the broadcast, meaning the user has gone offline and online
    // again. For an optimized presentation we mark this as a new line, so
    // we can clearly see when the user has started and ended it's show
    var selectedImage                   = pBigpicUrl != null && pBigpicUrl.stringValue.indexOf(pImageIdentifiers[i]) > -1;
    if(i == 0 || (pImageIdentifiers[i-1] - pImageIdentifiers[i]) > 5000) {
      if(i > 0) {
        imageSeriesDivContent          += "</div>";
      }
      imageSeriesDivContent            += "<a name=\"marvinateSeries" + imageSeriesIndex + "\"></a>";
      imageSeriesDivContent            += "<div style=\"";
      imageSeriesDivContent            += imageSeriesIndex++ % 2 == 0 ? "padding: 0px 10px 0px 10px;" : "padding: 10px 10px 5px 10px; margin: 5px 0px 10px 0px; background-color: #dddddd; ";
      imageSeriesDivContent            += "\">";
    }
    if(selectedImage) {
      imageSeriesCurrentIndex           = imageSeriesIndex;
    }

    // Now print the image for the current identifier
    imageSeriesDivContent              += "<a name=\"marvinateImage" + pImageIdentifiers[i] + "\" href=\"\http://www.cam4.com/" + pProfileName + "/archive/" + pImageIdentifiers[i] + "\" style=\"display: inline-block; padding: 0px 2px 2px 2px; \">";
    imageSeriesDivContent              += "<img src=\"http://content.codelnet.com/0/cam4/" + pImageIdentifiers[i] + ".120x90.snap\" style=\"margin: 0 !important; padding: 0 !important; " + (selectedImage ? 'border: 5px solid red; width: 112px; height: 82px;' : 'border: 1px solid black; width: 120px; height: 90px;') + "\" />";
    imageSeriesDivContent              += "</a>";

  }
  imageSeriesDivContent                += "</div>"; // Last series

  return {
    content: imageSeriesDivContent,
    series: imageSeriesIndex,
    currentSeries: imageSeriesCurrentIndex
  };

}

// =============================================================================
// ===  Profile Archive :: Archive Listing  ====================================
// =============================================================================

var archiveContainerElement             = document.evaluate("//div[@class='archive-container']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
var scriptElement                       = archiveContainerElement.snapshotLength <= 0 ? null : document.evaluate("//div[@class='archive-container']/script", document, null, XPathResult.STRING_TYPE, null);
var scriptElementContent                = scriptElement == null ? null : scriptElement.stringValue;
if(scriptElementContent != null && scriptElementContent.length > 0) {

  var imageCounter                      = 0;
  var resultDivContent                  = "";
  var bigpicURL                         = document.evaluate("//div[@class='bigpic']/img/@src", document, null, XPathResult.STRING_TYPE, null);
  var profileNameEndIndex               = window.location.pathname.indexOf("/", 1);
  var profileName                       = window.location.pathname.substring(1, profileNameEndIndex);
  var imageIdentifiers                  = archiveCollectImageIdentifiers(scriptElementContent);
  var imageSeries                       = archiveListImages(imageIdentifiers, profileName, bigpicURL);

  // Show the requested large picture
  resultDivContent                     += "<div style=\"margin: -10px -20px 0px 0px\">";
  resultDivContent                     += "<table style=\"border-spacing: 0; border-collapse: collapse; margin: 0px 0px 10px 10px; \">";
  resultDivContent                     += "<tr>";
  resultDivContent                     += "<td style=\"width: 500px; vertical-align: top; \">";
  resultDivContent                     += "<a href=\"http://www.cam4.com/" + profileName + "\"><img style=\"border: 1px solid black; width: 480px; height: 400px; padding: 0 !important; margin: 0 !important;\" src=\"" + bigpicURL.stringValue + "\" /></a>";
  resultDivContent                     += "</td>";
  resultDivContent                     += "<td style=\"vertical-align: top;\">";
  resultDivContent                     += "<div style=\"padding: 0px 0px 5px 0px; font-weight: bold; font-size: 16px; \"><a href=\"http://www.cam4.com/" + profileName + "\">" + profileName + "</a></div>";
  resultDivContent                     += "<div><a href=\"http://www.cam4.com/" + profileName + "\">http://www.cam4.com/" + profileName + "</a></div>";
  resultDivContent                     += "<div style=\"padding: 10px 0px 0px 0px; font-size: 12px; color: #000000; \">Images total: " + imageIdentifiers.length + "</div>";
  resultDivContent                     += "<div style=\"padding: 5px 0px 0px 0px; font-size: 12px; color: #000000; \">Series total: " + imageSeries.series + "</div>";
  if(imageSeries.currentSeries > 0) {
    resultDivContent                   += "<div style=\"padding: 5px 0px 0px 0px; font-size: 12px; color: #000000; \">Series for current image: " + imageSeries.currentSeries;
    resultDivContent                   += " <a class=\"marvinateButtonSmall\" href=\"#marvinateSeries" + (imageSeries.currentSeries - 1) + "\">Goto series</a>";
    resultDivContent                   += "</div>";
  }
  resultDivContent                     += "</td>";
  resultDivContent                     += "</tr>";
  resultDivContent                     += "</table>";
  resultDivContent                     += imageSeries.content;
  resultDivContent                     += "</div>";

  // Now we write the generated HTML content that contains the list of all
  // available pictures to the result document
  var targetDivResult                   = document.evaluate("id('showBroadcastImages')/div/div", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE , null);
  var targetDiv                         = targetDivResult.snapshotItem(0);
  targetDiv.innerHTML                   = resultDivContent;
  targetDiv.setAttribute("style", "width: 97%; padding: 10px;");

}


// =============================================================================
// ===  Includes  ==============================================================
// =============================================================================
//
// 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) {
  var pathResult                        = document.evaluate(pPath, document, 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]
// 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"); // Invalid response 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"); // Invalid response 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, pResponse, null);
  }

}
// Include end [xmlhttpUtil.js]