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