Hi Ray, please update the script with the following version. Major change is now it'll also work on romeo.com. Other thing the 'cosmetic' fixed were improper implemented and never showed any effects. I changed that now. see function xhrPoorMensPlus() Best regards Djamana
P.s. EDIT: Only copy&paste the lines till // ---- Settings UI ---- And keep the 'old part' I didn't changed anything beyond that. However the HTML gets interpreted, instead of just showing it, what messes up things. ...or just use this: https://pastebin.com/ijxZGVPD
// ==UserScript==
// @name Romeo Additions
// @namespace https://greasyfork.org/en/users/723211-ray/
// @version 2.2
// @description Allows to hide users, display their information on tiles, and enhances the Radar.
// @description:de Ermöglicht das Verstecken von Benutzern, die Anzeige ihrer Details auf Kacheln, und verbessert den Radar.
// @author -Ray-, Djamana
// @include *://*.romeo.com/*
// @grant GM_addStyle
// @require https://code.jquery.com/git/jquery-git.slim.min.js
// @_require https://code.jquery.com/jquery-3.6.0.slim.min.js
// ==/UserScript==
// ==== Dependencies ====
/*! waitForKeyElements | https://gist.github.com/BrockA/2625891 */
/*--- waitForKeyElements(): A utility function, for Greasemonkey scripts,
that detects and handles AJAXed content.
Usage example:
waitForKeyElements (
"div.comments"
, commentCallbackFunction
);
//--- Page-specific function to do what we want when the node is found.
function commentCallbackFunction (jNode) {
jNode.text ("This comment changed by waitForKeyElements().");
}
IMPORTANT: This function requires your script to have loaded jQuery.
*/
function waitForKeyElements(
selectorTxt, /* Required: The jQuery selector string that
specifies the desired element(s).
*/
actionFunction, /* Required: The code to run when elements are
found. It is passed a jNode to the matched
element.
*/
bWaitOnce, /* Optional: If false, will continue to scan for
new elements even after the first match is
found.
*/
iframeSelector /* Optional: If set, identifies the iframe to
search.
*/
) {
var targetNodes, btargetsFound;
if (typeof iframeSelector == "undefined")
targetNodes = $(selectorTxt);
else
targetNodes = $(iframeSelector).contents()
.find(selectorTxt);
if (targetNodes && targetNodes.length > 0) {
btargetsFound = true;
/*--- Found target node(s). Go through each and act if they
are new.
*/
targetNodes.each(function () {
var jThis = $(this);
var alreadyFound = jThis.data('alreadyFound') || false;
if (!alreadyFound) {
//--- Call the payload function.
var cancelFound = actionFunction(jThis);
if (cancelFound)
btargetsFound = false;
else
jThis.data('alreadyFound', true);
}
});
}
else {
btargetsFound = false;
}
//--- Get the timer-control variable for this selector.
var controlObj = waitForKeyElements.controlObj || {};
var controlKey = selectorTxt.replace(/[^\w]/g, "_");
var timeControl = controlObj[controlKey];
//--- Now set or clear the timer as appropriate.
if (btargetsFound && bWaitOnce && timeControl) {
//--- The only condition where we need to clear the timer.
clearInterval(timeControl);
delete controlObj[controlKey]
}
else {
//--- Set a timer, if needed.
if (!timeControl) {
timeControl = setInterval(function () {
waitForKeyElements(selectorTxt,
actionFunction,
bWaitOnce,
iframeSelector
);
},
300
);
controlObj[controlKey] = timeControl;
}
}
waitForKeyElements.controlObj = controlObj;
}
// ==== CSS ====
GM_addStyle(`
#visits > .layer__container--wider { width:unset; max-width:1227px; }
div[class*='tile--loading--'] .tile__image { background-image:url(/assets/05c2dc53b86dcd7abdb1d8a50346876b.svg); }
.tile__bar { position:absolute; bottom:0; right:0; visibility:hidden; }
.tile__bar_action { background:rgba(0,0,0,0.4); backdrop-filter: blur(3px); display: inline-block; color:white; margin-left: 1px; padding: 0.25rem 0.45rem; }
.tile__bar_action:hover { background-color:#00A3E4; }
.tile__bar_action:active { background-color:#06648B; }
.tile__link:hover .tile__bar { visibility:visible; }
`);
// ==== Script ====
(function () {
'use strict';
proxyXhr();
})();
// ---- Language ----
const _strings = {
"display": {
"de": "Anzeige",
"en": "Display"
},
"enhancedTiles": {
"de": "Erweiterte Kacheln",
"en": "Enhanced tiles"
},
"enhancedTilesDesc": {
"de": "Zeige alle Benutzerdetails auf den Kacheln. Im Radar wird dies Benutzer als 'Plus'-Abonnenten mit großen Kacheln darstellen.",
"en": "Shows all user details on tiles. The radar will claim users to be 'Plus' subscribers in large tiles."
},
"extensionTitle": {
"en": "Romeo Additions"
},
"hiddenUsers": {
"de": "Ausgeblendete Benutzer",
"en": "Hidden users"
},
"hideUser": {
"de": "Benutzer ausblenden",
"en": "Hide user"
},
"maxAge": {
"de": "Maximales Alter",
"en": "Maximal age"
},
"minAge": {
"de": "Minimales Alter",
"en": "Minimal age"
},
"viewFullImage": {
"de": "Bild vergrößern",
"en": "View full image"
},
}
function getString(key) {
const lang = document.documentElement.getAttribute("lang") || "en";
const translations = _strings[key];
if (translations)
return translations[lang] || translations["en"] || "%" + key + "%";
return "%" + key + "%";
}
// ---- Settings ----
const settingNs = "RA_SETTINGS:";
function getEnhancedTiles() {
return localStorage.getItem(settingNs + "enhancedTiles") == "true";
}
function getHiddenMaxAge() {
return parseInt(localStorage.getItem(settingNs + "hiddenMaxAge")) || 99;
}
function getHiddenMinAge() {
return parseInt(localStorage.getItem(settingNs + "hiddenMinAge")) || 18;
}
function getHiddenUsers() {
return JSON.parse(localStorage.getItem(settingNs + "hiddenUsers")) || [];
}
function setEnhancedTiles(value) {
localStorage.setItem(settingNs + "enhancedTiles", value);
}
function setHiddenMaxAge(value) {
localStorage.setItem(settingNs + "hiddenMaxAge", value);
}
function setHiddenMinAge(value) {
localStorage.setItem(settingNs + "hiddenMinAge", value);
}
function setUserHidden(username, hide) {
let hiddenUsers = getHiddenUsers();
if (hide) {
if (hiddenUsers.length < hiddenUsers.push(username)) {
hiddenUsers.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
localStorage.setItem(settingNs + "hiddenUsers", JSON.stringify(hiddenUsers));
}
} else {
const prevLength = hiddenUsers.length;
hiddenUsers = hiddenUsers.filter(e => e != username);
if (prevLength > hiddenUsers.length)
localStorage.setItem(settingNs + "hiddenUsers", JSON.stringify(hiddenUsers));
}
}
// ---- XHR ----
function ReMaskDash(RE_WithDashes) {
return RE_WithDashes
.replaceAll("/","\\/")
}
function isApiRequest(url, verb) {
// Request must start with "/api/v?/" or "/api/+/" followed by the given verb.
//const matches = url.match(/\/api\/(v[0-9]|\+)\//);
const matches = url.match(ReMaskDash("/api/(v[0-9]|\\+)/"));
if (matches && matches.length)
return url.startsWith(verb, matches[0].length);
return false;
}
function filterUser(user, hiddenMaxAge, hiddenMinAge, hiddenNames) {
return user.personal.age >= hiddenMinAge
&& user.personal.age <= hiddenMaxAge
&& !hiddenNames.includes(user.name)
}
function proxyXhr() {
// Intercept XHR queries and replies by hooking the XHR open method.
const realOpen = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this.addEventListener("load", () => {
//console.log("[RA] XHR reply: method=" + method + ", url=" + url);
try {
// Parse data.
const isString = typeof this.response === "string";
reply = isString ?
JSON.parse(this.response) :
this.response;
// Modify interesting data.
if ( isApiRequest(url, "notifications") ) {
reply = xhrHideNotifications(reply);
}
if (
isApiRequest(url, "profiles") ||
isApiRequest(url, "visitors") ||
isApiRequest(url, "visits")
) {
reply = xhrRestorePlusVisit( reply );
reply = xhrEnhanceUsers ( reply );
}
if ( isApiRequest(url, "session") ) {
reply = xhrPoorMensPlus( reply );
}
// Write back possibly modified data.
Object.defineProperty(this, "responseText", { writable: true });
this.responseText = isString ? JSON.stringify(reply) : reply;
} catch (e) {
//console.log("[RA] XHR handler failed: " + e)
}
});
// Forward to client.
return realOpen.apply(this, arguments);
}
}
function xhrHideNotifications(reply) {
// Remove manually hidden users.
const hiddenMaxAge = getHiddenMaxAge();
const hiddenMinAge = getHiddenMinAge();
const hiddenNames = getHiddenUsers();
return reply.filter(x => filterUser(x.partner, hiddenMaxAge, hiddenMinAge, hiddenNames));
}
function xhrEnhanceUsers(reply) {
// Remove manually hidden users.
const enhancedTiles = getEnhancedTiles();
const hiddenMaxAge = getHiddenMaxAge();
const hiddenMinAge = getHiddenMinAge();
const hiddenNames = getHiddenUsers();
let newItems = [];
for (let item of reply.items) {
if (filterUser(item, hiddenMaxAge, hiddenMinAge, hiddenNames)) {
// Show as "large tiles" to display user details everywhere.
if (enhancedTiles)
item.display.large_tile = true;
newItems.push(item);
}
}
reply.items = newItems;
return reply;
}
function xhrPoorMensPlus(reply) {
// Cosmetic patch #1
if (!reply.is_plus) {
reply.is_plus = true
reply.is_free_plus = true // maybe not needed
reply.payment_group = "PLUS"
}
// Cosmetic patch #2
if (reply.inferface) {
reply.show_plus_badge = true // maybe not needed
reply.show_ads = false // maybe not needed
}
// Cosmetic patch #3
if (reply.show_plus_badge) {
reply.show_plus_badge = true // maybe not needed
}
return reply;
}
function xhrRestorePlusVisit(reply) {
// Restore PLUS-visible visitors.
reply.items_limited = 0;
return reply;
}
// ---- Tile UI ----
waitForKeyElements(
"a.tile__link, " +
"div.js-profiles a.listresult, " +
"div.js-wrapper a.tile__link > div.tile__image, " +
"#visits a.listresult", jNode => {
// Determine tile properties.
const tile = jNode.parent(".tile");
const tileLink = tile.children(".tile__link").first();
if (!tileLink) // ignore placeholders
return;
// Add full headline as tooltip.
const tileInfo = tileLink.children(".tile__info").first();
const tileHeadline = tileInfo.children(".tile__headline").first();
tileHeadline.attr("title", tileHeadline.text());
// Add action bar.
const tileImage = tileLink.children(".tile__image").first();
const username = tileImage.attr("aria-label");
const tileBar = $("").appendTo(tileLink);
addShowImageAction(tileBar, tileImage);
addHideUserAction(tileBar, tile, username);
});
function addShowImageAction(tileBar, tileImage) {
const style = tileImage.attr("style");
if (!style)
return;
const url = style.substring(style.lastIndexOf("/") + 1, style.lastIndexOf(")"));
if (url.endsWith(".svg")) // ignore "no photo" placeholders
return;
const origUrl = "/img/usr/original/0x0/" + url;
$("")
.on("click", e => {
e.preventDefault();
window.open(origUrl, "_blank");
})
.appendTo(tileBar);
}
function addHideUserAction(tileBar, tile, username) {
$("")
.on("click", e => {
e.preventDefault();
setUserHidden(username, true);
tile.css("display", "none");
})
.appendTo(tileBar);
}
// ---- Settings UI ----
waitForKeyElements("li.js-settings > div.accordion > ul", jNode => {
let itemClass = jNode.find("a").attr("class");
$("
")
.on("click", e => {
// Force open the setting pane and clear any existing contents.
$("#offcanvas-nav > .js-layer-content").addClass("is-open");
const pane = $(".js-side-content");
pane.empty();
// Add pane and list.
pane.append(`
` + getString("extensionTitle") + `
` + getString("enhancedTiles") + `
` + getString("enhancedTilesDesc") + `
` + getString("hiddenUsers") + `
` + getString("minAge") + `
` + getString("maxAge") + `
`);
// Handle enhanced user tiles.
let inEnhancedTiles = $("#ra_enhancedTiles");
inEnhancedTiles.prop("checked", getEnhancedTiles());
inEnhancedTiles.on("change", e => {
setEnhancedTiles(e.target.checked);
});
// Handle hidden age.
let minAge = getHiddenMinAge();
let maxAge = getHiddenMaxAge();
let inMinAge = $("#ra_hiddenMinAge");
let inMaxAge = $("#ra_hiddenMaxAge");
inMinAge.val(minAge);
inMaxAge.val(maxAge);
inMinAge.on("change", e => {
minAge = parseInt(e.target.value);
setHiddenMinAge(minAge);
if (minAge > maxAge) {
maxAge = minAge;
setHiddenMaxAge(maxAge);
inMaxAge.val(maxAge);
}
});
inMaxAge.on("change", e => {
maxAge = parseInt(e.target.value);
setHiddenMaxAge(maxAge);
if (maxAge < minAge) {
minAge = maxAge;
setHiddenMinAge(minAge);
inMinAge.val(minAge);
}
});
// Handle hidden user list.
const ul = $("#ra_hiddenUsers");
for (const item of getHiddenUsers()) {
const li = $("
Hi Ray,
please update the script with the following version.
Major change is now it'll also work on romeo.com.
Other thing the 'cosmetic' fixed were improper implemented and never showed any effects.
I changed that now. see function xhrPoorMensPlus()
Best regards
Djamana
P.s.
EDIT: Only copy&paste the lines till
// ---- Settings UI ----
And keep the 'old part'
I didn't changed anything beyond that.
However the HTML gets interpreted, instead of just showing it, what messes up things.
...or just use this:
https://pastebin.com/ijxZGVPD