Greasy Fork is available in English.

RomeoEnhancer

Ergänzungen für die neue Romeo-Seite

Ajankohdalta 12.1.2018. Katso uusin versio.

// ==UserScript==
// @name         RomeoEnhancer
// @version      1.0.1
// @author       braveguy (Romeo: braveguy / Romeo-Club: RomeoEnhancer)
// @description  Ergänzungen für die neue Romeo-Seite
// @include      https://www.planetromeo.com/*
// @include      https://www.hunqz.com/*
// @include      https://bluebird.planetromeo.com/*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js
// @grant        GM_addStyle
// @copyright    braveguy 12.10.2016 / 12.01.2018
// @namespace    https://greasyfork.org/users/139428
// ==/UserScript==

/* The @grant directive is needed to work around a design change
   introduced in GM 1.0. It restores the sandbox. */


/**
 * Copyright(c) braveguy (Romeo: braveguy / Romeo-Club: RomeoEnhancer)
 *
 * Änderungen oder die Wiederverwendung von Code von RomeoEnhancer
 * erfordern meine ausdrückliche Zustimmung. Es ist nicht gestattet,
 * geänderte Versionen von RomeoEnhancer zu veröffentlichen.
 *
 * Das Skript wurde mit Tampermonkey in aktuellen Versionen von Safari, Chrome
 * und Firefox getestet. Dennoch geschieht die Benutzung auf eigenes Risiko.
 *
 * ** Datenschutz **
 * RomeoEnhancer enthält keinerlei Code, um Nutzer zu identifizieren
 * oder Daten auszuspähen. Es besteht keinerlei geschäftliches Interesse.
 *
 * Die aktuelle Version von RomeoEnhancer ist verfügbar unter:
 * https://greasyfork.org/scripts/31282-romeoenhancer
 *
 *
 * ****** English version *****
 *
 * Copyright(c) by braveguy (Romeo: braveguy / Romeo-Club: RomeoEnhancer)
 *
 * Modifications and/or reuse of RomeoEnhancer code require my explicit consent.
 * You are NOT allowed to publish any changed version of RomeoEnhancer!
 *
 * All code has been tested with Tampermonkey in a recent Safari, Chrome,
 * and Firefox browser. However, the use of this script is at your own risk.
 *
 * ** Privacy **
 * RomeoEnhancer does NOT and never will include any code to identify you
 * or spy on your data. There is no commercial interest.
 *
 * The latest version of RomeoEnhancer is available on:
 * https://greasyfork.org/scripts/31282-romeoenhancer

*/


// ***** (Try to) catch app version *****
// For this to work: (1) script must @run-at document-start, (2) log event would have to be in same scope
var romeoVersion = '';
var consoleLog = console.log;
console.log = function (message) {
    console.info('1. Typ: ' + typeof message); //test
	if (message.match(/PLANETROMEO v/)) {
        romeoVersion = '\r' + message;
		console.info('2. Typ: ' + typeof message);
		console.info('romeoVersion = ' + romeoVersion);
    }
    consoleLog.call(this, arguments);
};
//console.log('PLANETROMEO v.xxx'); //test


// ***** CSS *****
GM_addStyle (
	//icons
	'a.re-icon {margin-left:0.25em; font-size:0.85em; color:rgba(255,255,255,0.8)}' +
	'a:hover.re-icon {color:rgba(102,215,255,0.8)}' +
	'a.re-link {color:rgba(255,255,255,1)}' +

	//position online state icon in thumbs
	'div.tile__onlinestate {left:.5rem; top:.5rem}' +
	'div.tile--small div.tile__onlinestate {left:.25rem; top:.3rem}' +

	//separators
	'.separator--dark, .gallery__info, .footprint-separator, .grid__nav-caption {color:rgba(255,255,255,0.33)}' +

	//more big tiles per page
	'@media screen and (min-width:60rem){' +
	'.is-filter-opened .search-results--big-tiles .search-results__item, ' +
	'.is-stream-opened .search-results--big-tiles .search-results__item {padding-bottom:33.33333% !important; width:33.33333% !important}' +
	'main.is-filter-opened.is-stream-opened div.search-results--big-tiles div.search-results__item {padding-bottom:50% !important; width:50% !important} }' +
	'@media screen and (min-width:80rem){' +
	'.is-filter-opened .search-results--big-tiles .search-results__item, ' +
	'.is-stream-opened .search-results--big-tiles .search-results__item {padding-bottom:25% !important; width:25% !important}' +
	'main.is-filter-opened.is-stream-opened div.search-results--big-tiles div.search-results__item {padding-bottom:33.33333% !important; width:33.33333% !important} }' +
	'@media screen and (min-width:100rem){' +
	'.is-filter-opened .search-results--big-tiles .search-results__item, ' +
	'.is-stream-opened .search-results--big-tiles .search-results__item {padding-bottom:20% !important; width:20% !important}' +
	'main.is-filter-opened.is-stream-opened div.search-results--big-tiles div.search-results__item {padding-bottom:25% !important; width:25% !important} }' +
	'@media screen and (min-width:120rem){' +
	'.is-filter-opened .search-results--big-tiles .search-results__item, ' +
	'.is-stream-opened .search-results--big-tiles .search-results__item {padding-bottom:16.66666% !important; width:16.66666% !important}' +
	'main.is-filter-opened.is-stream-opened div.search-results--big-tiles div.search-results__item {padding-bottom:20% !important; width:20% !important} }' +
	'@media screen and (min-width:140rem){' +
	'.is-filter-opened .search-results--big-tiles .search-results__item, ' +
	'.is-stream-opened .search-results--big-tiles .search-results__item {padding-bottom:12.5% !important; width:12.5% !important}' +
	'main.is-filter-opened.is-stream-opened div.search-results--big-tiles div.search-results__item {padding-bottom:16.66666% !important; width:16.66666% !important} }'
);


// **************************************************************
// ***** The following function is taken from:
// ***** https://gist.github.com/BrockA
// ***** https://gist.github.com/raw/2625891/waitForKeyElements.js


/*--- 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;
}

// ***** end
// **************************************************************


// ***** Replace URLs with links *****
// ***** Taken from https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links/37687#37687 *****
function linkify(inputText) {
    var replacedText, replacePattern1, replacePattern2, replacePattern3;

    //URLs starting with http://, https://, or ftp://
    replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank" class="plain-text-link">$1</a>');

    //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
    replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank" class="plain-text-link">$2</a>');

    //Change email addresses to mailto:: links.
    replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\-\_]+?(\.[a-zA-Z]{2,6})+)/gim;
    replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1" class="plain-text-link">$1</a>');

    return replacedText;
}


// ***** Insert layer *****
function iframeLoad(iframeId, iframeUrl) {
	$('#spotlight-container').append(
		'<div id="' + iframeId + '" class="layer layer--spotlight"><div class="js-layer-content layer__container layer__container--spotlight  l-fancy">' +
		'<div class="js-profile-error"><div><iframe style="border:0;width:928px;height:688px;" src="' + iframeUrl + '"></iframe>' +
		'<a class="js-close-spotlight icon icon-large icon-cross layer__closingx l-tappable--big l-hidden-sm"></a>' +
		'</div></div></div></div>'
	);
}


// ***** Calculate time *****
function timeDiff(loginTime) {
	var timeTag = loginTime;
	// 1s...59s; 1min...59min; hh:mm; gestern hh:mm; tt.mm.jjjj, hh:mm
	return timeTag;
}


// ***** Open last selected contacts instead of favourites *****
function keepContacts (jNode) {
	if (window.location.hash.match(/#\/module\/contacts\/\w*$/)) {
		localStorage.setItem('contactsSelection', window.location.hash);
	}
	var contactsPath = localStorage.getItem('contactsSelection');
	contactsPath = (contactsPath ? contactsPath : '#/module/contacts/favourites');
	$('#contacts-menu, a.contacts__search-close').attr('href', contactsPath);
}


// ***** Display selected bookmark in bookmark icon title, other data in header logo title *****
function bookmarkTitle (jNode) {

	//bookmark title
	var selectedBookmark = $('li.js-bookmarks li.is-selected a').text();
	$('li.js-bookmarks').attr('title', selectedBookmark);
	//$('header.site-header h1').attr('title','RomeoEnhancer ' + GM_info.script.version);
	//$('header.site-header h1').attr('title','PlanetRomeo ' + APP_VERSION + '\rRomeoEnhancer ' + GM_info.script.version);

	//users, online, version
	var users, online, version;
	$.get('/api/v4/profiles?lang=de&length=1').done(function (data) {
		if (data.items_total) {
			users = data.items_total.toLocaleString() + ' Nutzer | ';
		}
		$.get('/api/v4/profiles?lang=de&length=1&filter%5Bonline_status%5D%5B%5D=ONLINE&filter%5Bonline_status%5D%5B%5D=DATE&filter%5Bonline_status%5D%5B%5D=SEX').done(function (data) {
			if (data.items_total) {
				online = data.items_total.toLocaleString() + ' online';
			}
			version = romeoVersion + '\rRomeoEnhancer ' + GM_info.script.version;
			if (users && online) {
				$('header.js-header h1').attr('title', users + online + version);
			}
		});
	});
}


// ***** Add forums and classic to radar navigation bar *****
function forumsLink (jNode) {
	$('main div.js-navigation ul:has(a[href^="/#/radar/"])').append(
		'<li class="Tabbed-nav-item--6apqr"><a class="c" style="cursor:default">|</a></li>' +
		'<li class="Tabbed-nav-item--6apqr"><a id="re-forums-link" href="/#/profile/-forums" class="js-nav-item Tabbed-nav-link--2-Muf">Foren</a></li>' +
		'<li class="Tabbed-nav-item--6apqr"><a target="romeoclassic" href="https://classic.planetromeo.com" class="js-nav-item Tabbed-nav-link--2-Muf">Classic</a></li>'
	);
	/*$('#re-forums-link').click(function() {
		iframeLoad('re-forums','https://classic.planetromeo.com/myuser/?page=club&amp;subPage=forum');
	});*/
}


// ***** Add statistics to visitors navigation bar *****
function statisticsLink (jNode) {
	$('#visits div.js-navigation ul').append(
		'<li class="Tabbed-nav-item--6apqr"><a class="Tabbed-nav-link--2-Muf" style="cursor:default">|</a></li>' +
		'<li class="Tabbed-nav-item--6apqr"><a href="/#/profile/-statistics" class="Tabbed-nav-link--2-Muf">Statistik</a></li>'
	);
}


// ***** Display classic forums page in a layer *****
function loadForum (jNode) {
	var frameW = 'width:' + $('#profile--forums section').width() + 'px;';
	var frameH = 'height:' + $('#profile--forums section').height() + 'px;';
	$('#profile--forums section').replaceWith(
		'<iframe style="border:0;' + frameW + frameH + '" src="https://classic.planetromeo.com/myuser/?page=club&amp;subPage=forum"></iframe>'
	);
	$('ul.search-nav__vers-list li a[href="/#/profile/-forums"]').parent().removeClass('is-selected');
}


// ***** Display classic statistics page in a layer *****
function loadStatistics (jNode) {
	var frameW = 'width:' + $('#profile--statistics section').width() + 'px;';
	var frameH = 'height:' + $('#profile--statistics section').height() + 'px;';
	$('#profile--statistics section').replaceWith(
		'<iframe style="border:0;' + frameW + frameH + '" src="https://classic.planetromeo.com/settings/visitorStatistics.php"></iframe>'
	);
	$('ul.search-nav__vers-list li a[href="/#/profile/-statistics"]').parent().removeClass('is-selected');
}


// ***** Preview unread messages in title tag *****
function previewMessage (jNode) {
	var profileId, jsonParam;
	$('#messenger span.txt-pill--mini').each(function(){
		profileId = $(this).parent().parent().parent().attr('href').match(/\d{3,}/);
		$(this).parent().parent().parent().addClass('re-id' + profileId);
		var msgCount = parseInt($(this).text());
		jsonParam = 'lang=de&length=' + msgCount + '&filter%5Bfolders%5D%5B%5D=RECEIVED&filter%5Bpartner_id%5D=' + profileId;
		var msgText = '';
		var thisId = '.re-id' + profileId;
		$.get('/api/v4/messages?' + jsonParam).done(function (data) {
			for (var i = msgCount-1; i >= 0; i--) {
				msgText = msgText + '\r' + data.items[i].text+ '\r';
			}
			$(thisId).attr('title', msgText);
		});
	});
}


// ***** Show last login and location in message thread and contact info *****
function showLoginLocation (jNode) {
	var profileId, thisId;
	$('#messenger div.layer-header__title div.info, #messenger div.js-contacts div.layer-header__title div.info').each(function(){
		profileId = $(this).find('a').attr('href').match(/\d{3,}/);
		thisId = this;
		$.get('/api/v4/profiles/' + profileId + '?expand=partner&lang=de').done(function (data) {

			//last login
			if (data.last_login) {
				var loginTime = new Date(data.last_login.slice(0,19)).toLocaleString();
				var timeTag = timeDiff(loginTime);
				//var timeTag = loginTime;
				$(thisId).not(':has(span.txt-raise)').find('span.icon').removeAttr('title').parent().attr(
					{'title': 'Online seit ' + loginTime, 'style': 'font-size:0.85em'}
				);
				$(thisId).find('span.icon-last-login-hours').removeAttr('title').parent().attr(
					{'title': 'Zuletzt online ' + loginTime, 'style': 'font-size:0.85em'}
				);
				$(thisId).not(':has(span.icon)').append(
					'<span class="icon icon-last-login-hours txt-raise" style="font-size:0.85em" title="Zuletzt online ' + loginTime + '"> ' + timeTag + '</span>'
				);
			}

			//location
			var name = '', distance = '', sensor = '';
			if (data.location.name) {
				name = data.location.name;
			}
			if (data.location.distance >= 0) {
				if (data.location.distance < 1000) {
					distance = ' – ' + data.location.distance + 'm';
				} else {
					distance = data.location.distance.toString();
					distance = ' – ' + distance.slice(0, -3) + ',' + distance.slice(-3, -2) + ' km';
				}
			}
			if (data.location.sensor) {
				sensor = ' ' + '<span class="icon icon-gps-needle icon-badge"></span>';
			}
			$(thisId).after(
				'<div style="text-align:center" class="typo-small lh-heading txt-truncate">' + name + distance + sensor + '</div>'
			);

		});

		//profile id
		$(thisId).find('a.plain-text-link').attr(
			{'title': 'Profil-Nr. ' + profileId}
		);
	});
}


// ***** Add select all to attach picture list *****
function messageAllPictures (jNode) {
	/* //alert('Select');
	$('#messenger div.js-attachment-region div.ui-navbar').append(
		'<span style="text-align:right">Alle</span>'
	).click(function() {
		$('div.pictures-list div.tile').addClass('tile--selected');
	}); */
}


// ***** Link contact icons in messages list entries *****
function contactsMessage (jNode) {
	var profileId;
	$('#messenger a.js-preview.listitem__body').each(function() {
		profileId = $(this).attr('href').match(/\d{3,}/);
		if (!profileId) profileId = location.hash.match(/\d{3,}/);
		$(this).find('span.icon-save-contact').replaceWith(
			'<a class="tile__badge icon icon-save-contact" title="Kontakt bearbeiten" href="/#/messenger/contacts/all/' + profileId + '"</a>'
		);
		$(this).find('span.icon-block-contact').replaceWith(
			'<a class="tile__badge icon icon-block-contact" title="Kontakt bearbeiten" href="/#/messenger/contacts/blocked/' + profileId + '"</a>'
		);

	});
}


// ***** Add message icons to contact list entries *****
function messageContacts (jNode) {
	var profileId;
	$('#messenger div.js-contacts a.js-preview.listitem__body').each(function() {
		profileId = $(this).attr('href').match(/\d{3,}/);
		if (!profileId) profileId = location.hash.match(/\d{3,}/);
		$(this).find('span.plain-text-link').not(':has(a)').append(
			'<a class="icon icon-chat ml- re-icon" title="Messages" href="/#/messenger/chat/'+ profileId + '"></a>'
		);
	});
}


// ***** Add message icons to visitor list entries, link contact icons *****
function handleVisitors (jNode) {
	var profileId;
	$('#visits a.tile__link, #visits a.listresult, #search a.tile__link').each(function() {
		profileId = $(this).attr('href').match(/\d{3,}/);

		//add message icon
		$(this).find('div.info').not(':has(a)').append(
			'<a class="icon icon-chat re-icon" title="Messages" href="/#/messenger/chat/' + profileId + '"></a>'
		);

		//link contact icon
		$(this).find('span.icon-save-contact').replaceWith(
			'<a class="tile__badge icon icon-save-contact" title="Kontakt bearbeiten" href="/#/messenger/contacts/all/' + profileId + '"</a>'
		);
		$(this).find('span.icon-block-contact').replaceWith(
			'<a class="tile__badge icon icon-block-contact" title="Kontakt bearbeiten" href="/#/messenger/contacts/blocked/' + profileId + '"</a>'
		);
	});
}


// ***** Add message icons and preview links to radar entries, link contact icons *****
function handleRadar (jNode) {
	var profileId;
	$('#profiles a.tile__link, #profiles a.listresult').each(function() {
		profileId = $(this).attr('href').match(/\d{3,}/);

		//add message icon
		$(this).find('div.info, span.info__body').not(':has(a)').append(
			'<a class="icon icon-chat re-icon" title="Messages" href="/#/messenger/chat/' + profileId + '"></a>'
		);

		//link headline to preview
		$(this).find('div.info__username, span.listresult__name').not(':has(a)').wrapInner(
			'<a class="re-link" title="Vorschau" href="/#/profile/' + profileId + '/preview"></a>'
		);

		//link contact icon
		$(this).find('span.icon-save-contact').replaceWith(
			'<a class="tile__badge icon icon-save-contact" title="Kontakt bearbeiten" href="/#/messenger/contacts/all/' + profileId + '"</a>'
		);
		$(this).find('span.icon-block-contact').replaceWith(
			'<a class="tile__badge icon icon-block-contact" title="Kontakt bearbeiten" href="/#/messenger/contacts/blocked/' + profileId + '"</a>'
		);
	});
}


// ***** Add message icons to non-message Activity Stream entries *****
function handleActivity (jNode) {
	var bodyLink, profileId;
	$('div.stream div.listitem').each(function() {
		bodyLink = $(this).find('a.listitem__body').attr('href');
		if (! bodyLink.match('/messenger/chat/')) {
			profileId = $(this).find('a.tile__link').attr('href').match(/\d{3,}/);
			$(this).find('div.layout-item').not(':has(a)').find('span.listitem__text, span.ui-status-description').last().append(
				'<a class="icon icon-chat re-icon" title="Messages" href="/#/messenger/chat/' + profileId + '"></a>'
			);
			$(this).find('span.listitem__text, span.ui-status-description').attr('style','font-style:italic');
		}
	});
}


// ***** Profiles: add links to clubs, guestbook, market, chat, save user; show profile id, visits since; link URLs *****
function handleProfile (jNode) {
	var profileId = $('#spotlight-container div.layer.layer--spotlight').attr('id').match(/\d{3,}/);

	//links
	var linkClubs = '<a target="_blank" href="https://classic.planetromeo.com/gemeinsam/php/links/index.php?set=' + profileId + '">Clubs</a>';
	var linkGuestbook = '<a target="_blank" href="https://classic.planetromeo.com/gemeinsam/php/guestbook/lesen.php?empfaenger=' + profileId + '">Gästebuch</a>';
	var linkMarket = '<a target="_blank" href="https://classic.planetromeo.com/market/view.php?userId=' + profileId + '">Anzeigen</a>';
	var linkChat = '<a target="_blank" href="https://classic.planetromeo.com/msg/history.php?uid=' + profileId + '">Chat</a>';
	var linkSaveUser = '<a target="_blank" href="https://classic.planetromeo.com/gemeinsam/php/myuser/index.php?partnerId=' + profileId + '">User speichern</a>';
	$('section.profile__stats > div').prepend(
		'<div style="padding-left:1rem;font-size:0.9em"><br/>Classic:&nbsp' + linkClubs + '&nbsp;|&nbsp;' + linkGuestbook + '&nbsp;|&nbsp;' + linkMarket + '&nbsp;|&nbsp;' + linkChat + '&nbsp;|&nbsp;' + linkSaveUser + '</div>'
	);

	//profile id
	$('section.profile__stats > div').append(
		'<div class="re-profile-id mb" style="text-align:center;font-size:0.9em;font-weight:500;opacity:0.5">Profil-Nr. ' + profileId + '</div>'
	);

	//visits since
	$.get('/api/v4/profiles/' + profileId + '/full?expand=partner&lang=de').done(function (data) {
		var visits = '', since = '';
		if (data.visits_count) {
			visits = data.visits_count.toLocaleString() + ' Besucher';
		}
		if (data.creation_date) {
			since = new Date(data.creation_date.slice(0,19)).toLocaleString().split(',');
			since = 'seit ' + since[0];
		}
		if (visits && since) $('div.re-profile-id').before(
			'<div style="text-align:center">' + visits + ' ' + since + '</div>'
		);
	});

	//URLs in headline and profile text
	$('div.profile__container blockquote.profile__headline, section.profile__stats span.txt-preserve').each(function() {
		var replacedText = linkify($(this).html());
		$(this).html(replacedText);
	});
}


// ***** Add links to error page: save profile etc. *****
function linksError (jNode) {
	var profileId = $('#spotlight-container div.layer.layer--spotlight').attr('id').match(/\d{3,}/);
	var linkClubs = '<a target="_blank" href="https://classic.planetromeo.com/gemeinsam/php/links/index.php?set=' + profileId + '">Clubs</a>';
	var linkGuestbook = '<a target="_blank" href="https://classic.planetromeo.com/gemeinsam/php/guestbook/lesen.php?empfaenger=' + profileId + '">Gästebuch</a>';
	var linkMarket = '<a target="_blank" href="https://classic.planetromeo.com/market/view.php?userId=' + profileId + '">Anzeigen</a>';
	var linkAlbum = '<a target="_blank" href="https://classic.planetromeo.com/auswertung/album/?set=' + profileId + '&user=">Fotoalbum</a>';
	var linkChat = '<a target="_blank" href="https://classic.planetromeo.com/msg/history.php?uid=' + profileId + '">Chat</a>';
	var linkSaveUser = '<a target="_blank" href="https://classic.planetromeo.com/gemeinsam/php/myuser/index.php?partnerId=' + profileId + '">Profil speichern</a>';
	$('div.profile__container--error').append(
		'<div style="font-size:0.9em"><br/>' + linkClubs + '&nbsp;|&nbsp;' + linkGuestbook + '&nbsp;|&nbsp;' + linkMarket + '&nbsp;|&nbsp;' + linkAlbum + '&nbsp;|&nbsp;' + linkChat + '&nbsp;|&nbsp;' + linkSaveUser + '</div>'
	);
}


// ***** Insert BMI to profile, big radar tiles, and list view *****
function insertBMI (jNode) {
	// ;
}


// ***** Show picture info in slide show *****
function imgInfo (jNode) {
	var imgName;
	var eq = ($('div.layout.picture').length == 1) ? 0 : 1;
	imgName = $('div.layout.picture:eq(' + eq + ') img.picture__image').attr('src');
	if (imgName) {
		var imgNameTxt = imgName.substr(imgName.lastIndexOf('/')+1, 5) + '...';
		$('#swipe div.js-header div.js-counter').not(':has(a)').append(
			'<a target="_blank" style="color:rgba(255,255,255,0.5); font-size:0.85em" class="ml" href="' + imgName + '">' + imgNameTxt + '</a>'
		);
	}
}


// ***** Show picture info in picture rating *****
function ratingInfo (jNode) {
	var color;
	var imgName = $('#picture-rating img.picture-rating__image').attr('src');
	var imgNameTxt = imgName.substr(imgName.lastIndexOf('/')+1, 5) + '...';
	var imgNameMax = localStorage.getItem('reRatingMax');
	imgNameMax = (imgNameMax ? imgNameMax : imgNameTxt);
	if (imgNameTxt >= imgNameMax) {
		localStorage.setItem('reRatingMax', imgNameTxt);
	}
	if (parseInt(imgNameTxt,16) + 1 >= parseInt(imgNameMax,16)) {
		color = 'rgba(255,255,255,0.5)';
	} else {
		color = 'rgba(255,0,0,0.8)';
	}
	$('#picture-rating div.layer-header__title').append(
		'<a target="_blank" style="color:' + color + '; font-size:0.85em" class="ml" href="' + imgName + '">' + imgNameTxt + '</a>'
	);
}


// ***** Picture search *****
function pictureSearch (jNode) {
	// ;
}


// ***** Relogin after timeout *****
function reLogin (jNode) {
	if (document.cookie.match(/PHPSESSID\=/)) {
		location.reload();
	} else {
		location.replace('https://www.planetromeo.com/#/auth/login');
	}
}



waitForKeyElements ('#contacts-menu, #trigger-region div div, a.contacts__search-close', keepContacts);
waitForKeyElements ('li.js-bookmarks li.is-selected, header.site-header h1', bookmarkTitle);
waitForKeyElements ('main div.js-navigation ul li:first', forumsLink);
waitForKeyElements ('#visits div.js-navigation ul li:first', statisticsLink);
waitForKeyElements ('#profile--forums section', loadForum);
waitForKeyElements ('#profile--statistics section', loadStatistics);
waitForKeyElements ('#messenger[class!="layer layer--nav-primary is-hidden"] span.txt-pill--mini', previewMessage);
waitForKeyElements ('#messenger div.layer-header__title, #messenger div.js-contacts div.layer-header__title', showLoginLocation);
waitForKeyElements ('#messenger div.js-pictures.pictures-list', messageAllPictures);
waitForKeyElements ('#messenger a.js-preview.listitem__body', contactsMessage); //span.plain-text-link
waitForKeyElements ('#messenger div.js-contacts span.plain-text-link', messageContacts);
waitForKeyElements ('#visits div.tile__info, #search div.tile__info', handleVisitors); //and search
waitForKeyElements ('#profiles div.info__username, #profiles a.listresult', handleRadar);
waitForKeyElements ('div.stream a.listitem__body', handleActivity);
waitForKeyElements ('div.is-profile-loaded', handleProfile);
waitForKeyElements ('div.profile__container--error', linksError);
waitForKeyElements ('#swipe div.layout.picture', imgInfo);
waitForKeyElements ('#picture-rating img.picture-rating__image', ratingInfo);
waitForKeyElements ('div.layer--error', reLogin);