RomeoEnhancer

Ergänzungen für die Romeo-Website

当前为 2022-03-29 提交的版本,查看 最新版本

// ==UserScript==
// @name         RomeoEnhancer
// @version      1.9.1
// @author       braveguy (Romeo: braveguy / Gruppe RomeoEnhancer)
// @description  Ergänzungen für die Romeo-Website
// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @include      https://*.romeo.com/*
// @include      https://83.98.143.20/*
// @include      https://www.hunqz.com/*
// @run-at       document-start
// @copyright    braveguy 12.10.2016 / 29.03.2022
// @namespace    https://gf.qytechs.cn/users/139428
// @grant        GM_addStyle
// ==/UserScript==


/**
 * Copyright(c) braveguy (Romeo: braveguy / Gruppe 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://gf.qytechs.cn/scripts/31282-romeoenhancer
 *
 *
 * ****** English version *****
 *
 * Copyright(c) by braveguy (Romeo: braveguy / group 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://gf.qytechs.cn/scripts/31282-romeoenhancer

*/


// ***** GM polyfill *****
if (typeof GM == 'undefined') {
  this.GM = {};
}

if (typeof GM_addStyle == 'undefined') {
  this.GM_addStyle = (aCss) => {
    'use strict';
    let head = document.getElementsByTagName('head')[0];
    if (head) {
      let style = document.createElement('style');
      style.setAttribute('type', 'text/css');
      style.textContent = aCss;
      head.appendChild(style);
      return style;
    }
    return null;
  };
}


// ***** CSS *****

//background colors
let color1 = '', color2 = '', color3 = '', color4 = '', color5 = '', color6 = '', color7 = '', color8 = '', color9 = '';
//localStorage.setItem('REcolorScheme', 'retroBlue')
switch (localStorage.getItem('REcolorScheme')) {
    case 'sexyDark':
        //Romeo default - Sexy Dark
        break;
    case 'lighterGrey':
        color1 = '#1f1f1f', color2 = '#1f1f1f', color3 = '#000', color4 = '#101010', color5 = '#1a1a1a', color6 = '#f0f0f0', color7 = '#cc2d2d', color8 = '#2a2a2a', color9 = '#2a2a2a'; // Lighter Grey
        break;
    case 'retroBlue':
        color1 = '#102c54', color2 = '#102c54', color3 = '#000', color4 = '#101010', color5 = '#122646', color6 = '#f0f0f0', color7 = '#f0f0f0', color8 = 'rgba(255,255,255,.08)', color9 = '#30415d'; // Retro Blue
        break;
    default: //fineGrey
        color1 = '#1a1a1a', color2 = '#171717', color3 = '#000', color4 = '#101010', color5 = '#232323', color6 = '#f0f0f0', color7 = '#cc2d2d', color8 = '#2a2a2a', color9 = '#2a2a2a'; // Fine Grey
        break;
}


GM_addStyle (

    //tweak dark design
    `html.js body, #activity-stream .layer__container, .content-nav__actions, .layer__column--background, .layer__column--detail, #group-preview .layer__container, .profile {background-color:${color1} !important}` +
    `div.js-wrapper section[class^="Section"], div.js-wrapper div[class^="slider_wrapper-"]:before, div.js-wrapper div[class^="slider_wrapper-"]:after {background-color:${color1} !important}` +
    `.ui-navbar--app {background-color:${color2}}` +
    `.listitem--selected > div {background-color:${color9} !important}` +
	`li[class^="stat-bar__item--"] {background-color:${color2}}` +
    `.listresult {background-color:${color2}}` +
    `.listresult:hover, :is(.js-chat, .js-contacts) .reactView > div:hover {background-color:${color8} !important}` +
    `.listresult .list-stat-bar__item, .icon-circular.icon-u {background-color:transparent}` +
    `.js-groups .reactView > button {background-color:${color9} !important}` +
    `:is(.js-post-edit, .js-post-list, #post, #group-post) textarea {background-color:${color4} !important}` +
    `.js-post-edit button[class*="ActionButton"] {background-color:${color1}}` +
    `.js-post-edit div[class*="Wrapper-"] {background-color:${color5} !important}` +
    `.js-post-list div[class^="Container"], :is(#group-post, #post) :is(.js-post, .js-post div[class^="Container"], div.js-comment) {background-color:${color5} !important}` +
    `section.js-main-stage div[class^="Grid-"] div[class*="Wrapper-"], section.js-main-stage div[class^="Grid-"] + div[class^="Wrapper-"] {background-color:${color5} !important}` +
    `.message__text {background-color:${color6}}` +
    `:is(.js-groups, .js-main .js-navigation li) svg[class^="Icon__Svg-sc-"] {color:${color7}}` +

    //bigger text in messages, groups, profiles; wrap long links
    ':is(#messages-list div.reactView, div[class^="TruncateBlock__Content-"]) > p[class^="BaseText-sc-"],' +
    '.profile section.profile__stats section > div > p[class^="BaseText-sc-"] {font-size:.9375rem; line-height:1.375; padding-bottom:.125rem; word-break:break-word}' +

	//icons, links
	'a.re-icon {position:relative; top:.1em; font-size:.9em}' +
    '.listresult__infos a.re-icon {top:inherit; font-size:.9rem}' +
    'a.re-idle {color:unset}' +
	'a.re-icon:hover, a.re-idle:hover {color:rgba(102,215,255,.8)}' +
    '.touchevents a.re-icon {padding:.25em .5em}' +
    '.touchevents a[class*="Tabbed-nav-link"] span {padding:0 .5em}' +
    'a.re-link, a.re-link-no-hover {color:#00bdff}' +
    '[class*="hunqz"] a.re-link {color:#fc193c}' +
	'a.re-link-idle {color:unset !important}' +
    ':is(a.re-link, a.re-link-idle, .js-report-icon svg):hover {color:#66d7ff !important}' +
    '[class*="hunqz"] :is(a.re-link, a.re-link-idle, a.re-icon, .js-report-icon svg):hover {color:#fd5871 !important}' +

    //filter icon, bookmark name
    '.cmuYcO, .edfjQb, .cpJGzS, .fILOaS {padding-top:.33em!important; border:0!important}' +
    '.re-filter-bookmark-name {position:absolute; right:0}' +
    '.is-filter-collapsed .re-filter-bookmark-name {display:none!important}' +
    '.is-filter-opened .re-filter-options-text, .is-filter-opened div.js-filter-button svg + span + span:not(.re-filter-bookmark-name) {display:none!important}' +

	//text
	'span.re-posts-view {font-family: "Gibson Bold"; font-weight:500; text-transform:uppercase; background-color:transparent; color:#fff; height:auto; cursor:pointer; padding-left:2rem;}' +
	'span.re-list-head, span.re-list-view, span.re-list-load {font-family:"Helvetica Neue",Helvetica,Arial,"Open Sans",sans-serif; text-transform:uppercase; color:rgb(255,255,255,.5); height:auto}' +
	'span.re-list-view, span.re-list-load {font-family: "Gibson Bold"; font-weight:500; color:rgb(250,250,250); cursor:pointer;}' +
	'span.re-list-load:before {display:inline-block; transform:scaleX(-1)}' +

    //expand truncated location names on hover
    '.typo-small.txt-truncate:hover {white-space:normal; word-break:break-all}' +

    //expand headline in big tiles
    '.tile__headline {font-size:.875rem !important; word-break:break-word}' +
    '.tile__headline:hover {white-space:normal}' +

	//more dense list view, more headline text
	'.list-stat-bar__item {width:inherit; color:rgba(255,255,255,.75)}' +
    'blockquote.listresult__quote {white-space:normal; color:rgba(255,255,255,.625)}' +

    //messenger
    '#messenger .message__content--attachment {max-width:80%}' +
    '#messenger .message--sent .message__content--attachment {max-width:15rem}' +
    '#messenger .message__content a {word-break:break-word}' +
    '#messenger :is(div.js-header-region, div.js-header) div.reactView > div {grid-template-columns:1fr 4fr 1fr}' +
    '#messenger :is(div.js-header-region, div.js-header) div.reactView a > img[style="margin-top: 0.375em;"] {margin-top:0!important}' +
    '#messenger .re-location {font-size:.75rem; font-weight:500; letter-spacing:0; line-height:1.25; text-transform:none}' +
    ':is(.js-chat, .js-contacts, #manage) svg[class*="OnlineStatus__"] {font-size:1em}' +
    ':is(.js-chat, .js-contacts, #manage) svg[class*="OnlineStatus__Sex"] {font-size:1.125em}' +
    '.js-chat .js-scrollable {max-height:1270px !important}' +
    '.js-chat .typo-headline-small {font-size:inherit}' +
    '.fQIYNa {font-weight:550}' +
    '.online-favourites__item {margin:0 .475rem !important}' +
    '.online-favourites__itemName {width:4rem; margin:0 -.125rem}' +
    'section.emoji-mart button.emoji-mart-anchor:hover {color:rgba(255,255,255,.5)}' +

    //stream
    'div.stream__content .tile__onlinestate {position:absolute; left:-3px; top:-3px}' +
    'div.stream__content .tile__onlinestate span.icon {display:flex; align-items:center; justify-content:center; background-color:#1f1f1f; border-radius:50%; height:19px; width:19px; margin-top:0!important}' +
    '.listitem .emoji:hover {font-size:1.5rem; line-height:1.33rem; margin-left:-.4rem; position:relative; right:-.2rem; padding-bottom:.33rem}' +

	//profile stats, separators, icons
    '.profile__info svg[aria-hidden="false"] {font-size:1.1em}' +
    '.profile div[aria-label] > div[role="figure"] {border-color:rgba(255,255,255,0.33)}' +
    '.profile .re-profile-stats {color:rgba(255,255,255,0.6); font-size:.875rem !important}' +
    '.js-report-icon div {margin-left:.25rem}' +
    '.js-report-icon a {border-width:.125rem}' +
    '.js-report-icon svg {font-size:1.75em}' +
    '.profile .bg-raise {background:linear-gradient(transparent,rgba(0,0,0,.375))}' +
    '.profile .js-authenticity button {display:flex; align-items:center; flex-direction:columns; justify-content:center}' +
    '.profile .js-authenticity button span {position:absolute; top:19px; font-size:.75rem; font-weight:500; color:rgba(0,0,0,.8)}' +
    '.profile .js-authenticity button span.white {top:18px; color:rgba(250,250,250,1)}' +
    '.profile .js-authenticity svg {filter:none}' +
    '.re-img-count {position:absolute; bottom:.5rem; left:.5rem; display:flex; align-items:center; justify-items:center}' +
    '.re-img-count div {color:rgba(255,255,255,.87); background-color:rgb(18,18,18); border-radius:.125rem; padding:0 .25rem; height:1.125rem; line-height:1rem}' +
    '.profile__image-strip .re-img-count {margin-left:.5rem}' +

    //slide show
	'#photos div[class^="Description"] {font-size:.85rem}' +
    '#photos div.js-username + div.txt-quiet {color:rgba(255,255,255,0.5)}' +
    '#photos div[class^="SecondaryActions"], #photos .js-actions .js-context-menu-trigger {padding-bottom:2rem}' +

    //group tiles
    '.js-members-count > div {background-color:rgba(0,0,0,.66)!important}' +
    '.re-group-tile > div > p + div {position:absolute; bottom:2rem; left:.5rem}' +
    '.re-group-tile > div > p {font-size:.925rem; width:auto; background:linear-gradient(transparent 12%,rgba(0,0,0,.175),transparent)}' +
    //'.re-group-tile > div > p {text-shadow: rgba(0, 0, 0, 0.24) 0px 1px 1px, rgba(0, 0, 0, 0.24) 1px 1px 1px, rgba(0, 0, 0, 0.32) 1px 1px 3px;}' +
    '.re-common-group span.icon-group-members, .re-common-group p:last-child {color:#6ddc00}' +
    '.js-members-count + div > span {margin-bottom:0}' +
    'div[class*="NameAndOfficialBadgeWrapper"] {flex-flow:unset}' +
    '.re-icon-official {display:inline; margin:0 .175rem .175rem 0; vertical-align:middle; height:1.125em; width:1.125em}' +

	//groups
	'.js-date .reactView span {font-size:.85em}' +
	'div.Container--3h4tt div.js-groups > div.pt- {display:none}' +
	'.Content--YvjYm a {overflow-wrap:break-word}' +
    'button.js-show-more.ShowMoreButton--2xOu8.is-hidden {display:block!important}' +
    'div[class^="CommentsArea"] div[class^="TruncateBlock__Content"] {-webkit-line-clamp:unset}' +
    'div[class^="CommentsArea"] div[class^="TruncateBlock__Container"] p button {display:none}' +
    '.js-main .js-sidebar {max-height:inherit !important}' +
    '.js-main .js-sidebar .js-groups {padding-bottom:1rem}' +

    //groups list
    '.re-groups-listitem {display:flex; align-items:center; border:0; border-radius:.25em; padding:.5em; width:100%}' +
    `.re-groups-listitem:hover {background-color:${color8}}` +
    '.re-groups-tile {display:flex; flex-shrink:0; margin-right:1em; background-size:cover; border-radius:50%; height:3em; width:3em}' +
    '.re-groups-entry {flex-grow:1; flex-shrink:1; min-width:0; display:flex; align-items:center}' +
    '.re-groups-text {font-family:"Helvetica Neue",Helvetica,Arial,"Open Sans",sans-serif; font-size:1em; font-weight:500; letter-spacing:.5px; line-height:1.375em; word-break:break-word; margin-right:.25em; display:block; overflow:hidden}' +
    '.re-groups-name {color:rgb(0,163,228); font-weight:500; display:inline-block; overflow:hidden; text-overflow:ellipsis; word-wrap:normal}' +
    '.re-groups-time {color:rgba(250,250,250,.625); font-size:.85em}' +
    '.re-groups-admin {display:flex; align-items:center; color:rgb(0,0,0,.8); background-color:rgb(0,189,255); padding:0 .25rem; border-radius:.25rem; font-size:0.625rem}' +
    '.re-groups-new {display:flex; align-items:center; color:rgb(250,250,250,.8); font-size:0.8em}' +
    '.re-groups-new svg {display:inline-block; height:1em; width:1.125em; vertical-align:middle}' +
    '.re-groups-new svg path {fill:currentcolor}' +
    '.re-groups-selected .re-groups-name {color:rgba(255,255,255,.925)}' +
    '.re-groups-visited :is(.re-groups-name, .re-groups-new) {color:rgba(250,250,250,.425)}' +
    '.re-groups-visited .re-groups-time {color:rgba(250,250,250,.375)}' +


    //travel
    '.re-member-travel, .re-radar-travel, .re-edit-travel {color:#00bdff; text-transform:initial}' +
    '.re-member-travel:hover, .re-radar-travel:hover, .re-edit-travel:hover {color:#66d7ff}' +
    '.re-member-travel.re-selected, .re-radar-travel.re-selected {color:#fff}' +

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


    //*** desktop ***
	'@media screen and (min-width:768px) {' +

    //general
    '.re-mobi {display:none!important}' +

    '}' +


    //*** desktop >= 1024px ***
    '@media screen and (min-width: 1024px) {' +

    //stream
    '.stream {width:18.5rem!important}'+

    '}' +


	//*** mobile ***
	'@media screen and (max-width:767px) {' +

    //general
    '.re-desk {display:none!important}' +

    //tweak dark design
    `.ui-navbar--app, .ui-navbar--app ul, .ui-navbar.content-nav ul {background-color:#000 !important}` +

    //main menu
    'header.js-header {height:4.25rem; padding-bottom:.5rem}' +
    '.layer--nav-primary {bottom:4.25rem}' +

    //stream
    'div.stream__content {top:3.75rem}' +

    //bigger icons
	'a.re-icon {font-size:1em}' +

    //no block icon in message thread
    'div.js-correspondence div.js-header-region div.reactView > div > span > div > a {display:none}' +

    //back arrow in group post
    '#group-post .js-post button.js-back {margin:1rem 1rem 0 -.5rem}' +

    '}'

);


// ***** Clean up old settings *****
localStorage.removeItem('contactsSelection');
localStorage.removeItem('REgroupPostsView');
localStorage.removeItem('reRatingMax');


// ***** Viewport *****
var mobile = window.matchMedia("(max-width:767px)");


// **************************************************************
// ***** 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];
    var timerInterval   = (document.visibilityState === "visible" ? 500 : 1500);

	//--- 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
                                   );
            },
                                       timerInterval
                                      );
            controlObj [controlKey] = timeControl;
        }
    }
    waitForKeyElements.controlObj   = controlObj;
}

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


// ***** Set AJAX headers *****
var key0 = window.atob('WC1TZXNzaW9uLUlk');
var key1 = window.atob('WC1BcGktS2V5');
var val1 = window.atob('OEpOdlBiVjNJOUtMaU5xUFNxc2NOVFllZzd1aXd2TUk=');
function ajaxHead(){
	return {[key0]: JSON.parse(localStorage.getItem('PR_SETTINGS:SESSION_ID')), [key1]: val1};
}


// ***** Replace URLs with links *****
// ***** Adopted from https://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links/37687#37687 *****
function linkify(text) {

	//Email addresses
	var pattern1 = /(^|\s|<br>)(([-\w\.\+])+@[-\w]+?(\.[A-Z]{2,6})+)/gim;
	text = text.replace(pattern1, '$1<a href="mailto:$2" class="plain-text-link">$2</a>');

	//URLs starting with http:// or https://
	var pattern2 = /(^|\s|<br>)(https?:\/\/[-\w+&@#\/\(\)%?=~|!:,.;]*[-\w+&@#\/\(\)%=~|])/gim;
	text = text.replace(pattern2, '$1<a target="_blank" href="$2" rel="noreferrer noopener" class="plain-text-link">$2</a>');

	//URLs without http:// or https://
	var pattern3 = /(^|\s|<br>)([-\w]+\.[a-z][-\w+&#\/\(\)%?=~|!:,.;]*[-\w+&#\/\(\)%=~|])/gm;
	text = text.replace(pattern3, '$1<a target="_blank" href="http://$2" rel="noreferrer noopener" class="plain-text-link re-link-idle">$2</a>');

	//keep internal links in same window
	var pattern4 = /(target="_blank" )(href="https?:\/\/(www\.)?((planet)?romeo|hunqz)\.com)/gim;
	text = text.replace(pattern4, '$2');

	return text;
}


// ***** Format date/time *****
function dateTime(timeStamp, dateOnly) {
    // 1s...59s; 1min...59min; hh:mm; gestern hh:mm; tt.mm.jjjj, hh:mm
    var timeNow = new Date();
    var timeNow1 = new Date (timeNow - 1000*60*60*24);
    var timeTag = "";
    if (timeStamp) {
        if (timeNow.toLocaleDateString() == timeStamp.toLocaleDateString()) {
            timeTag = 'heute ' + timeStamp.toLocaleTimeString('de-DE').slice(0,-3);
        } else if (timeNow1.toLocaleDateString() == timeStamp.toLocaleDateString()) {
            timeTag = 'gestern ' + timeStamp.toLocaleTimeString('de-DE').slice(0,-3);
        } else {
            timeTag = dateOnly ? timeStamp.toLocaleDateString('de-DE') : timeStamp.toLocaleString('de-DE').slice(0,-3);
        }
    }
    return timeTag;
}


// ***** Change URL without reloading the page *****
function changeUrl(url) {
	history.pushState({}, document.title, url);
	history.pushState({}, document.title, url);
	history.back();
}


// ***** Open message thread or contact info by profile name *****
// (not working for blocked/blocking/deactivated profiles)
function openByName(baseUrl, profileLink, profileName) {
	var profileType = 'profiles';
	if (profileLink.match(/^\/hunq\//)) {
		profileType = 'hunqz/profiles';
	/*} else if (profileLink.match(/^\/groups?\//)) {
		profileType = 'groups';*/
	}
	$.ajax({url: `/api/v4/${profileType}?pick=items.*.(id,name)&lang=de&length=1&filter[fulltext_search_mode]=EXACT&filter[fulltext]=@${profileName}`,
			headers: ajaxHead()
		   })
	.done(function (data) {
		if (data.items[0] && data.items[0].name == profileName) {
			changeUrl(baseUrl + data.items[0].id);
        } else {
            $.ajax({headers: ajaxHead(), url: `/api/v4/${profileType}/${profileName}/full?lang=de&lookup_type=NAME`})
            .done(function (data) {
                if (data.id) {
                    changeUrl(baseUrl + data.id);
                }
            });
        }
	});
}


// ***** Hide visit *****
function hideVisit(profileId) {
	if (profileId > 0) {
		$.ajax({url: `/api/v4/visits/${profileId}`,
				headers: ajaxHead(),
				method: 'DELETE'
			   })
		.done(function () {
			$('div.profile-topnav__hide').hide();
		})
	}
}


// ***** Search profile id, open profile name directly *****
function searchInput(jNode) {
	$('#search a.re-add').off('click').remove();
	$(jNode).on('input click', function() {
        var inputValue = $(jNode).val();
		$('#search a.re-add').off('click').remove();
		if (inputValue.match(/^[1-9]\d{2,}$/)) {
			$('#search div.js-input').after(
                `<a style="font-size:1rem" class="re-add re-link-no-hover mr-"><span class="re-desk">Profil-ID ${inputValue} anzeigen</span><span class="re-mobi">ID anzeigen</span></a>`
			).next('.re-add').click(function() {
				openById(inputValue);
				return false;
			});
        } else if (inputValue.match(/^@?[-\w\/]{3,}$/)) {
            inputValue = inputValue.replace(/^@/, '');
            $('#search div.js-input').after(
                `<a style="font-size:1rem" class="re-add re-link-no-hover mr-"><span>${inputValue}</span><span class="re-desk"> anzeigen</span></a>`
            ).next('.re-add').click(function() {
                changeUrl(((inputValue.match(/^\//)) ? '' : '/profile/') + inputValue);
                return false;
            });
        }
    });
}


// ***** Open profile by ID *****
function openById(id) {
    $.ajax({headers: ajaxHead(), url: `/api/v4/profiles/${id}?expand=partner&lang=de`})

	.done(function (data) {
		var type = (data.type) ? data.type : '';
		var name = (data.name) ? data.name : '';
        var profileType = 'profile';
        if (type == 'ESCORT') {
            profileType = 'hunq';
        } else if (type == 'CLUB') {
            profileType = 'group';
        }
        changeUrl(`/${profileType}/${name}`);
    })

   	.fail(function (data) {
		changeUrl('/profile/-');
	});
}


// ***** Picture upload month/year *****

//init
var imgTable = [
    0x00000000,0x00000000,0x000076f1,0x0000c0c2,0x000113ac,0x000193c7,0x00022adb,0x0002e59b,0x0003c274,0x0004bdda,0x0005cb8f,0x0006f792, //2003
    0x00083008,0x0009e166,0x000bc399,0x000dc70d,0x000fcad1,0x001219f5,0x0014e794,0x00180a61,0x001b7a83,0x001eeaaa,0x0022a968,0x0026aaf2, //2004
    0x002b6a16,0x00313036,0x003679aa,0x003c51e7,0x00421898,0x00489386,0x004f0bb9,0x00563896,0x005d5196,0x006425db,0x006bc70e,0x0072db1e, //2005
    0x007a330c,0x00827630,0x0089c239,0x00920af4,0x009ae36a,0x00a3fd57,0x00ad0670,0x00b72f32,0x00c1ea95,0x00cb8a91,0x00d5b890,0x00df6c5b, //2006
    0x00e8faad,0x00f42dc0,0x00fe1ca0,0x0108f04e,0x01140217,0x011feaa7,0x012b87da,0x0137a5e8,0x014368b3,0x014eaa9b,0x015ae9b2,0x01665596, //2007
    0x01721afe,0x017ecdd9,0x018a8ee3,0x0197bf46,0x01a3fca7,0x01b1bb65,0x01bf6f5a,0x01ce39de,0x01dd1952,0x01eb6283,0x01fa1c2d,0x020876c2, //2008
    0x021703cc,0x0226d437,0x0234d31f,0x02552e25,0x02676bf0,0x027aa2a8,0x028d92c6,0x02a15073,0x02b5f39d,0x02c9adb5,0x02ddb5e1,0x02f0ec81, //2009
    0x0304f1c0,0x031b41a8,0x032e68b1,0x0342c994,0x03570786,0x036c9395,0x0381b90a,0x03985a47,0x03b09c6d,0x03c663e5,0x03dd7e4e,0x03f43921, //2010
    0x040ae598,0x0423dced,0x0439c138,0x0452752e,0x046b1bac,0x0485592a,0x04a06d43,0x04bcc54e,0x04d9b67e,0x04f52906,0x0511e53d,0x052df075, //2011
    0x054a0d31,0x0568a266,0x0584c09c,0x05a25176,0x05c0ba75,0x05df7ee8,0x05fe3605,0x061e623c,0x063f5c2e,0x065e0e92,0x067e22b7,0x069dbc58, //2012
    0x06c0304b,0x06e63d7d,0x0707d372,0x072e0941,0x0751b8f1,0x077862c9,0x079c0d65,0x07c1d1fd,0x07e86b65,0x080da891,0x08320879,0x08558d00, //2013
    0x087b78bc,0x08a21715,0x08c4f43a,0x08eaa130,0x0911cb58,0x0939ac93,0x0960c758,0x098a963b,0x09b76d90,0x09e02d40,0x0a0906c0,0x0a3047ac, //2014
    0x0a810e27,0x0adaffd0,0x0b295113,0x0b7d3b5d,0x0bcf0d62,0x0c249600,0x0c755b5b,0x0ccd27fa,0x0d23c7df,0x0d7376dc,0x0dc57294,0x0e140406, //2015
    0x0e65e5c9,0x0ebde62a,0x0f0cd756,0x0f603ba6,0x0fb3653a,0x100b35e6,0x105e79ed,0x10b8b874,0x1115ce52,0x116ae973,0x11be384e,0x121244b8, //2016
    0x1267dfa9,0x12c42272,0x1315847b,0x136e2e12,0x13c61e4f,0x141f5987,0x147b12a6,0x14ddb823,0x154191ae,0x159e77ae,0x1602066a,0x16657d11, //2017
    0x16cb7cc6,0x1734ec50,0x179299df,0x17fa28e5,0x185ec78c,0x18c9c40a,0x193229c7,0x19a1e789,0x1a12bcdd,0x1a797ec2,0x1ae1e760,0x1b47eede, //2018
    0x1bb4bbe1,0x1c23cf3d,0x1c84c7c3,0x1cf2f6a5,0x1d61d7f8,0x1ddb56d8,0x1e596a40,0x1ede97da,0x1f66d255,0x1fe21e6b,0x205eafbb,0x20d26c98, //2019
    0x21484431,0x21c24ba6,0x2230d2f9,0x22a6cd0e,0x2312ebbd,0x23844c83,0x23f45e42,0x246d38c1,0x24eb9338,0x255dcbe7,0x25d30c82,0x264389a4, //2020
    0x26b9cb56,0x27310205,0x279833c7,0x28072e59,0x2870c5b3,0x28e20ee3,0x294ea8bd,0x29bdfadb,0x2a2f2276,0x2a987389,0x2b026a37,0x2b66207d, //2021
    0x2bcd3a33,0x2c38480b,0x2c96002d,0x2cfb3a0f,0x2d5fe6e2,0x2dc7eecd,0x2e2c9ba0,0x2e94a38b,0x2efcab76,0x2f615849,0x2fc983ff,0x302e30d2, //2022
    0xffffffff];

function uploadDate(imgName) {
    const monthYearIndex = (element) => element >= parseInt(imgName, 16);
    var index = imgTable.findIndex(monthYearIndex) - 1;
    var year = 2003 + Math.trunc(index / 12);
    var month = 1 + index % 12;
    if (imgTable[index] == 0x00000000) {
        return '';
    } else {
        return `≈ ${month}.${year}${imgTable[index + 1] == 0xffffffff ? '+' : ''}`;
    }
}


// ***** Copy message thread to clipboard *****
function copyMessageThread() {
	var profileId = location.pathname.match(/\d{3,}/);
	var loadTime = new Date().toLocaleString('de-DE').slice(0,-3);
	var msgCount = '', header = '', msgThread = '', msgTime = '', name = '';
	var profileName = $('#messenger div.js-correspondence').find('a > span[lang], div span[disabled], a > img').attr('title').trim();
	var ownName = $('img.avatar__image').attr('alt');
	var jsonParam = `lang=de&length=10000&filter[folders][]=SENT&filter[folders][]=RECEIVED&filter[partner_id]=${profileId}`;
	$.ajax({headers: ajaxHead(), url: `/api/v4/messages?${jsonParam}`})

	.done(function (data) {

		//gather data
		if (data.items_total) {
			msgCount = data.items_total;
			header = 'Message-Verlauf von ' + ownName + ' mit ' + profileName + ' (Profil-ID ' + profileId + ') vom ' + loadTime + '\n\n\n';
			for (var i = msgCount-1; i >= 0; i--) {
				msgTime = new Date(data.items[i].date.slice(0,-5)+'.000Z');
				name = (data.items[i].folder == 'SENT') ? ownName : profileName;
				msgThread += msgCount-i + '. ' + name + '  •  ' + msgTime.toLocaleString('de-DE').slice(0,-3);
				msgThread += (data.items[i].unread == true) ? ' [ungelesen]' : '';
				msgThread += (data.items[i].locked == true) ? ' [gespeichert]' : '';
				msgThread += (data.items[i].spam == true) ? ' [Spam]' : '';
				msgThread += '\n-------------------------------------------------------------------';
				msgThread += '\n' + data.items[i].text+ '\n\n\n';
			}
			msgThread += '\n====================\n\n';
		}

		//confirm copy
		$('#messenger div.js-correspondence').after(
`<div class="re-bubble layout layout--h-center">
<div class="ui-bubble ui-bubble--dark" style="width:auto; top:72px">
<div class="ui-bubble__content [ js-content ] [ js-scrollable ] scrollable">
<div class="confirm-box">
<div class="txt-right" style="margin:-.5rem -.5rem -.75rem">
<a class="re-copy-cancel re-link icon icon-larger icon-cross"></a>
</div>
<div class="confirm-box__label" style="margin-bottom:1.125rem">
<p>Message-Verlauf kopieren</p>
</div>
<div class="confirm-box__actions">
<button class="re-copy-email ui-button ui-button--transparent">E-Mail-Entwurf</button>
<button class="re-copy-confirm ui-button ui-button--primary">Zwischenablage</button>
</div>
</div>
</div>
</div>
</div>
<div class="layer ui-bubble__overlay l-fancy"></div>`
		);
		$('button.re-copy-confirm').focus().click(function() {
            copyToClipboard(header + msgThread);
			$('div.re-bubble, div.ui-bubble__overlay').hide();
		});
		$('button.re-copy-email').click(function() {
            location.href = `mailto:?subject=${encodeURIComponent(header)}&body=${encodeURIComponent(msgThread)}`;
			$('div.re-bubble, div.ui-bubble__overlay').hide();
		});
		$('a.re-copy-cancel, div.ui-bubble__overlay').click(function() {
			$('div.re-bubble, div.ui-bubble__overlay').hide();
		});
	});
}


// ***** copy data to clipboard *****
const copyToClipboard = msgThread => {
	const el = document.createElement('textarea');
	el.value = msgThread;
	el.setAttribute('readonly', '');
	el.style.position = 'absolute';
	el.style.left = '-9999px';
	document.body.appendChild(el);
	const selected =
		  document.getSelection().rangeCount > 0
	? document.getSelection().getRangeAt(0)
	: false;
	el.select();
	document.execCommand('copy');
	document.body.removeChild(el);
	if (selected) {
		document.getSelection().removeAllRanges();
		document.getSelection().addRange(selected);
	}
}


// ***** Fix scrolling on larger screens for messages list *****
function fixScroll (jNode) {
    $(jNode).parents('.js-chat .js-scrollable').attr('style', 'max-height:inherit !important');
}


// ***** Add double tap to menu items, change behavior for mobile *****
function menuLinks (jNode) {
    $(jNode).has('#radar-menu').off('dblclick').dblclick(function() { changeUrl('/search/romeos'); });
    //$(jNode).filter('[href="/search"]').off('dblclick').dblclick(function() { changeUrl('/search/romeos'); });
    $(jNode).has('#visitors-menu').off('dblclick').dblclick(function() { changeUrl('/visitors/me'); });
    $(jNode).has('#messages-menu').off('dblclick').dblclick(function() { changeUrl('/messenger/contacts'); });
    $(jNode).has('[href="/me"]').off('dblclick').dblclick(function() { changeUrl('/stream'); });
    //$(jNode).has('[href="/stream"]').off('click').click(function() { (location.pathname.match(/^\/(visitors|messenger)/)) ? history.back(); return false; : return true; });
    if (mobile.matches) {
        //$(jNode).has('#visitors-menu').off('click').click(function() { changeUrl('/visitors'); return false; });
        //$(jNode).has('#messages-menu').off('click').click(function() { changeUrl('/messenger/chat'); return false; });
        $(jNode).has('#groups-menu').off('click').click(function() { changeUrl('/groups/member'); return false; });
    }

    /*var status = $('aside .avatar__status').html();
    $('nav li[class*="Hamburger"]').not('.is-selected').find('a').not(':has(span.js-badge)').append(
        `<span class="js-badge">${status}</span>`
        //`<span class="avatar__status avatar__status--small">${status}</span>`
    );*/
}


// ***** Add contacts etc. to navigation bar *****
function navLinks (jNode) {

	//icons on radar and guycandy
	//$('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/guycandy/"]').find('li.re-add').remove();
    if (!mobile.matches) {
        $('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/guycandy/"]').not(':has(li.re-add > span)').append(
            '<li class="Tabbed-nav-item--2iOWw mr pr- re-add"><span class="re-add re-radar-travel icon icon-airplane" style="cursor:pointer" title="Travel"><span class="pl-"></span></span></li>'
        );
        $('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/guycandy/"]').not(':has(li.re-add > a)').append(
            '<li class="Tabbed-nav-item--2iOWw re-add"><a style="cursor:default">|</a></li>' +
            '<li class="Tabbed-nav-item--2iOWw re-add"><a href="/messenger/contacts" class="js-nav-item Tabbed-nav-link--xyQY6" title="Kontakte"><span class="icon icon-save-contact"></span></a></li>' +
            '<li class="Tabbed-nav-item--2iOWw re-add"><a href="/groups/member" class="js-nav-item Tabbed-nav-link--xyQY6" title="Meine Gruppen"><span class="icon icon-group-members"></span></a></li>'
        );
    } else {
        $('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/guycandy/"]').not(':has(li.re-add)').prepend(
            '<li class="Tabbed-nav-item--2iOWw mr pr- re-add"><span class="re-add re-radar-travel icon icon-airplane" style="cursor:pointer" title="Travel"><span class="pl-"></span></span></li>'
        );
    }

    //travel
    if ($('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/guycandy/"]')) {
    	var viewMode = localStorage.getItem('REradarTravelLocation');
    	var toggleTravel = 'span.re-radar-travel';
    	var refreshClick = 'li.Tabbed-nav-item--2iOWw a.is-selected';
    	handleTravelLocation(viewMode, toggleTravel, refreshClick);
    	$('span.re-radar-travel').click(function() {
			var viewMode = localStorage.getItem('REradarTravelLocation');
	  	    var toggleTravel = 'span.re-radar-travel';
	   	    var refreshClick = 'li.Tabbed-nav-item--2iOWw a.is-selected';
			viewMode = (viewMode == 'travel') ? 'default' : 'travel';
			localStorage.setItem('REradarTravelLocation', viewMode);
			handleTravelLocation(viewMode, toggleTravel, refreshClick);
		});
    }

    //icons on groups
    if (!mobile.matches) {
        $('div.js-navigation ul').has('a[href="/groups/discover"]').not(':has(li.re-add)').append(
            '<li class="Tabbed-nav-item--2iOWw re-add"><a style="cursor:default">|</a></li>' +
            '<li class="Tabbed-nav-item--2iOWw re-add"><a href="/messenger/contacts" class="js-nav-item Tabbed-nav-link--xyQY6" title="Kontakte"><span class="icon icon-save-contact"></span></a></li>'
        );
    }
    $('div.js-navigation ul li').not('.re-add').find('a[href="/groups/member"]').attr('title', `${commonGroupsList.length.toLocaleString()} Gruppen abonniert`);

    //remove GR-Tools hint
    $('li.re-grtools').remove();
}


// ***** Show versions and online users in menu, selected bookmark in filter icon title and text *****
function handleVersionBookmark (jNode) {

    //menu
    if ($('#offcanvas-nav div[class^="Version"]').length) {
        var reVersion = `${(typeof GM_info != 'undefined') ? `${GM_info.script.name} ${GM_info.script.version}` : 'RomeoEnhancer'}`;
        var onlineAll = '';
        $.ajax({url: `/api/services/landing/online-count`}).done(function (data) {
            if (data.online_count) {
                onlineAll = `${data.online_count.toLocaleString()} Profile online`;
            }
            $('#offcanvas-nav div[class^="Version"] .re-add').remove();
            $('#offcanvas-nav div[class^="Version"]').addClass('txt-preserve').append(`<div class="re-add">${reVersion}\n${onlineAll}</div>`);
            var profileType = ($('li.is-selected, li[class*="list__item--active"]').find('a[href*="/hunqz"]').length) ? '/hunqz/profiles' : '/profiles';
            var users = '', online = '';
            $.ajax({headers: ajaxHead(), url: `/api/v4${profileType}?pick=items_total&lang=de&length=1`}).done(function (data) {
                if (data.items_total) {
                    users = `${data.items_total.toLocaleString()} User`;
                }
                $.ajax({headers: ajaxHead(), url: `/api/v4${profileType}?pick=items_total&lang=de&length=1&filter[online_status][]=ONLINE&filter[online_status][]=DATE&filter[online_status][]=SEX`}).done(function (data) {
                    if (data.items_total) {
                        online = `${data.items_total.toLocaleString()} online`;
                    }
                    $('#offcanvas-nav div[class^="Version"]').off().on('dblclick', function() {
                        $('#offcanvas-nav div[class^="Version"] div').replaceWith(`<div class="re-add">${reVersion}\n${onlineAll}\n${users} • ${online}</div>`);
                    });
                });
            });
        });

        //display
        /*if (location.pathname == '/me/display') {
            console.log('Display menu');
            $('#offcanvas-nav div.settings__key:first-child').not('.re-add').clone().insertBefore('#offcanvas-nav div.settings__key:first-child').first().addClass('re-add');
        }*/

    } else {

    //filter icon title and text
        var selectedBookmark = $('div[class^="js-bookmarks-list"] div[class*="item__is-selected"] a').text().trim();
        var searchOptions = $('div.js-filter-button button svg').attr('label');
        if (selectedBookmark) {
            $('div.js-filter-button button').attr('style', 'color:rgb(250,250,250); background-color:transparent');
            $('div.js-filter-button button title').text(`Lesezeichen: ${selectedBookmark}\r${searchOptions}`);
            $('div.js-filter-button button span').not('.re-add').addClass('re-filter-options-text');
            $('div.js-filter-button button').not(':has(.re-add)').append(`<span class="re-add re-filter-bookmark-name mr">${selectedBookmark}</span>`);
            $('div.js-filter-button button span').attr('title', `Lesezeichen: ${selectedBookmark}\r${searchOptions}`);
            $('button.ui-navbar__button--bookmarks').attr('title', 'Lesezeichen auswählen');
        } else {
            $('div.js-filter-button button span.re-filter-options-text').removeClass('re-filter-options-text');
            $('div.js-filter-button button span.re-add').remove();
        }
    }
}


// ***** Preview unread messages in title tag, delete by clicking blue badge *****
function previewMessage (jNode) {
	var profileId = $(jNode).parent().parent().attr('href').match(/\d{3,}/);
    if (profileId) {
	    $(jNode).parent().parent().addClass('re-id' + profileId);
	    var msgCount = parseInt($(jNode).text());
	    var jsonParam = 'lang=de&length=' + msgCount + '&filter[folders][]=RECEIVED&filter[partner_id]=' + profileId;
	    var msgText = '';
	    var thisId = '.re-id' + profileId;
	    $.ajax({headers: ajaxHead(), url: '/api/v4/messages?' + jsonParam}).done(function (data) {

    		//preview
	    	for (var i = msgCount-1; i >= 0; i--) {
		    	msgText += '\r' + data.items[i].text + '\r';
    		}
	    	$(thisId).attr('title', msgText);
            $(thisId).find('img').parent('div').attr('title', msgText);

		    //delete unread
    		$(jNode).attr('title', 'Löschen').click(function () {
	    		if (confirm(msgCount + ' Message(s) ungelesen löschen?')) {
		    		for (var i = msgCount-1; i >= 0; i--) {
			    		$.ajax({url: '/api/v4/messages/' + data.items[i].id + '?expand=from',
                                headers: ajaxHead(),
                                method: 'DELETE'
                               })
                        .done(function (data) {
                            //$('#messenger li.Tabbed-nav-item--8Ou8b a').first().get(0).click();
                        })
                    }
                    //changeUrl('/messenger/chat');
                    //$('#messenger li.Tabbed-nav-item--8Ou8b.is-selected a').get(0).click();
    			}
	    	})
	    });
    }
}


// ***** Show id, last login, and location in message thread and contact info *****
function showLoginLocation (jNode) {
	var profileId = location.pathname.match(/\d{3,}$/);
	//var id = `<span style="opacity:.5" title="Profil-ID ${profileId}"><span class="re-desk">Profil-ID</span><span class="re-mobi">#</span> ${profileId}</span>`;
	var id = `<span class="re-desk" style="opacity:.5" title="Profil-ID"># ${profileId} • </span>`;
	var name = '', country = '', distance = '', sensor = '';
	$.ajax({headers: ajaxHead(), url: '/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,-5)+'.000Z');
            var loginLocalTime = dateTime(loginTime);
			var timeTag = dateTime(loginTime, !mobile.matches);
			$(jNode).find('a svg[class*="OnlineStatus__"] title').text(`Online seit ${loginLocalTime}`);
			$(jNode).find('a div > svg').removeAttr('title').parent().attr('title', `Zuletzt online ${loginLocalTime}`);
            $(jNode).find('a').not(':has(svg[class*="OnlineStatus__"], div > svg)').find('span, img').after(
                `<span class="icon icon-last-login-hours mh--" style="color:rgba(250,250,250,.75)" title="Zuletzt online ${loginLocalTime}"> ${timeTag}</span>`
			);
		}

		//location, distance
		if (data.location.name) {
			name = `<a target="_blank" href="https://google.com/maps/place/${data.location.name}" rel="noreferrer noopener" title="Ort in Google Maps anzeigen">${data.location.name.trim()}</a>`;
		}
        if (data.location.country && (data.location.distance > 50000 || data.location.distance == null)) {
            country = ` - ${data.location.country}`;
        }
		if (data.location.distance >= 0) {
			if (data.location.distance < 1000) {
				distance = ' • ' + data.location.distance + 'm';
			} else {
				distance = ' • ' + (Math.round(data.location.distance/100)/10).toLocaleString() + ' km';
			}
		}
		if (data.location.sensor) {
			sensor = '<span class="icon icon-gps-needle icon-badge ml--" style="font-size:.85em"></span>';
		}
	})

	.always(function (data) {
		$(jNode).not(':has(.re-add)').find('a').has('span[lang]').not(':has(a)').after(
			`<div class="re-location re-add">${id}${name}${country}${distance}${sensor}</div>`
		);
	});
}


// ***** Add copy to message thread options menu *****
function threadOptionsMenu (jNode) {
	$(jNode).find('li').first().clone().prependTo($(jNode)).click(function() {
        copyMessageThread();
		return false;
	}).attr('id', 'options_copy').find('span').text('Kopieren');
}


// ***** Link contact icons in messages list entries *****
function contactsMessage (jNode) {
	var profileId = new Array;
    profileId = $(jNode).attr('href').match(/\d{3,}/);
	if (! profileId) profileId = location.pathname.match(/\d{3,}/);
    $(jNode).not(':has(a.re-add)').find('svg.eesqBN, svg.gClpgj').wrap(
		`<a href="/messenger/contacts/all/${profileId}" class="re-add"></a>`
	);
	$(jNode).find('svg.BqJLH').wrap(
		`<a href="/messenger/contacts/blocked/${profileId}" class="re-add"></a>`
	);

    //fix hunqz readability
    $(jNode).has('svg[label="HUNQZ"]').find('span[lang]').first().attr('style', 'font-weight:550');
}


// ***** Add message icons to contact list entries *****
function messageContacts (jNode) {
	var profileId = new Array;
    profileId = $(jNode).attr('href').match(/\d{3,}$/);
	if (! profileId) profileId = location.pathname.match(/\d{3,}$/);
	$(jNode).find('span[title]').last().not(':has(a)').append(
		`<a class="icon icon-chat re-icon ml- mr--" title="Messages" href="/messenger/chat/${profileId}"></a>`
	);

    //fix hunqz readability
    $(jNode).has('svg[label="HUNQZ"]').find('span[lang]').first().attr('style', 'font-weight:550');
}


// ***** Link user names on discover page to messages *****
function handleContactStrip (jNode) {
	var replacePattern = /(.*\/(profile|hunq|group)\/)([-\w]*)(.*)/;
	var profileLink = $(jNode).attr('href');
    $(jNode).attr('href', profileLink.replace(/\/group\//, '/profile/'));
	var profileName = profileLink.replace(replacePattern, '$3');
	var baseUrl = '/messenger/chat/';
	$(jNode).find('div[class^="Name-"]').attr('title', profileName)/*.wrapInner(
		`<a class="re-link-idle" title="${profileName}  |  Messages" href="${baseUrl}${profileName}"></a>`
	).children()*/.click(function() {
		openByName(baseUrl, profileLink, profileName);
		return false;
	}).attr('style', `${profileLink.match(/^\/hunq\//) ? 'color:#fc193c' : ''}`);
}


// ***** Mark common groups *****
function handleGroupTiles (jNode) {
    if ($(jNode).attr('href').match(/^\/group\//)) {
        $(jNode).addClass('re-group-tile');
        const profileName = $(jNode).attr('href').match(/[-\w]+$/);
        const nameIndex = (item) => item.name == profileName;
        if (commonGroupsList.findIndex(nameIndex) > -1) {
            $(jNode).find('span.icon-group-members').parent('div[class^="Container"]').addClass('re-common-group');
            $(jNode).addClass('re-common-group');
            $(jNode).find('span.icon-group-members, p').last().attr('title', 'Du bist Mitglied in dieser Gruppe').attr('aria-label', 'Du bist Mitglied in dieser Gruppe');
        }

        //title
        $(jNode).find('p').first().attr('title', $(jNode).find('p').first().text().trim());

        //official group
        const imgSrc = $(jNode).find('.js-is-official img').attr('src');
        if (imgSrc) $(jNode).find('.js-members-count > div').prepend(
            `<img src="${imgSrc}" class="re-icon-official" title="Offizielle Gruppe"</img>`
        );
    }
}


// ***** Add message icons to discover, visitor list, search results, and group member list, link contact icons *****
function handleTiles (jNode) {
    //var jNodeTiles = $(jNode).parentsUntil('a').parent();
    var jNodeTiles = $(jNode).hasClass('listresult__age') ? $(jNode).parent().parent().parent().parent('a') : $(jNode).parent('a');
	var replacePattern = /(.*\/(profile|hunq|group)\/)([-\w]*)(.*)/;
	var profileLink = jNodeTiles.attr('href');
	var profileName = profileLink.replace(replacePattern, '$3');

    //add + to distance if travel
    if (travelMode && location.pathname.match(/^\/(radar|groups|explore)\//)) {
        if (jNodeTiles.find('div.typo-small.txt-truncate').text().match(/(\d[\d\.,]*\s?(km|m|mi|ft))/)) {
            var distance = jNodeTiles.find('div.typo-small.txt-truncate').html().replace(/^\s+/, '+');
            jNodeTiles.find('div.typo-small.txt-truncate').html(distance);
        }
    }

    //add visitor icon
    var visitTime, visitedTime = '';
    const nameIndex = (item) => item.name == profileName;
    if (location.pathname == '/visitors/me') {
        if (visitorsList.findIndex(nameIndex) > -1) {
            var index1 = visitorsList.findIndex(nameIndex);
            visitTime = new Date(visitorsList[index1].date_visited);
            jNodeTiles.find('div.tile__visit').append(  // 'div.info, div.Name--3cum4 .info__username'
                `<a class="icon icon-visitor re-icon re-idle ml--" style="top:-1px" title="Besucher ${dateTime(visitTime)}" href="/visitors"></a>`
            );
        }
    }
    if (location.pathname == '/visitors') {
        if (visitsList.findIndex(nameIndex) > -1) {
            var index2 = visitsList.findIndex(nameIndex);
            visitedTime = new Date(visitsList[index2].date_visited);
            jNodeTiles.find('div.tile__visit').prepend(  // 'div.info, div.Name--3cum4 .info__username'
                `<a class="icon icon-visitor re-icon re-idle mr--" style="top:-1px" title="Besucht ${dateTime(visitedTime)}" href="/visitors/me"></a>`
            );
        }
    }


	//add message icon
	var baseUrl = '/messenger/chat/';
	jNodeTiles.find('div.tile__info > div.info, div.listresult__header').append(
		`<a class="icon icon-chat re-icon re-idle ml-" title="Messages" href="${baseUrl}${profileName}"></a>`
	).children().last().click(function() {
		openByName(baseUrl, profileLink, profileName);
		return false;
	});

    //link contact icon
	jNodeTiles.find('span.icon-save-contact').replaceWith(
		`<a class="tile__badge icon icon-save-contact re-idle" title="Kontakt bearbeiten" href="/messenger/contacts/all/${profileName}"></a>`
	);
	jNodeTiles.find('a.icon-save-contact').click(function() {
		openByName('/messenger/contacts/all/', profileLink, profileName);
		return false;
	});
	jNodeTiles.find('span.icon-block-contact').replaceWith(
		`<a class="tile__badge icon icon-block-contact" title="Kontakt bearbeiten" href="/messenger/contacts/blocked/${profileName}"></a>`
	);
	jNodeTiles.find('a.icon-block-contact').click(function() {
		openByName('/messenger/contacts/blocked/', profileLink, profileName);
		return false;
	});
}


// ***** Add message icons and preview links to radar entries, link contact icons *****
function handleRadar (jNode) {
    var jNodeRadar = $(jNode).hasClass('listresult__age') ? $(jNode).parent().parent().parent().parent('a') : $(jNode).parent('a');
	var replacePattern = /(.*\/(profile|hunq|group)\/)([-\w]*)(.*)/;
	var profileLink = jNodeRadar.attr('href');
	var profileName = profileLink.replace(replacePattern, '$3');

    // TEST
    if ($('#profiles div.search-results__item').length > 200) {
        //$('#profiles div.search-results__item').eq(0).remove();
    }

    //add visitor icon
    const nameIndex = (element) => element.name == profileName;
    if (visitorsList.findIndex(nameIndex) > -1) {
        var index = visitorsList.findIndex(nameIndex);
        var visitTime = new Date(visitorsList[index].date);
        jNodeRadar.find('div.info, span.info__body').append(
            `<a class="icon icon-visitor re-icon re-idle ml-" title="Besucher ${dateTime(visitTime)}" href="/visitors"></a>`
        );
    }

	//add message icon
	var baseUrl = '/messenger/chat/';
	jNodeRadar.find('div.tile__info > div.info, div.listresult__header').append(  // ('div.info, span.info__body')
		`<a class="icon icon-chat re-icon re-idle ml-" title="Messages" href="${baseUrl}${profileName}"></a>`
	).children().last().click(function() {
		openByName(baseUrl, profileLink, profileName);
		return false;
	});

    //add + to distance if travel
    if (travelMode || location.pathname.match(/^\/explore\//)) {
        if (location.pathname.match(/^\/(radar|groups|explore|guycandy)\//)) {
            if (jNodeRadar.find('div.typo-small.txt-truncate').text().match(/(\d[\d\.,]*\s?(km|m|mi|ft))/)) {
                var distance = jNodeRadar.find('div.typo-small.txt-truncate').html().replace(/^\s+/, '+');
                jNodeRadar.find('div.typo-small.txt-truncate').html(distance);
            }
        }
    }

    //link username to preview
	jNodeRadar.find('div.info__username, span.listresult__info').wrapInner(
		`<a class="re-link-idle" title="${profileName}  |  Vorschau" href="${profileLink.replace(replacePattern, '$1$3/preview')}"></a>`
	);

	//link contact icon
	jNodeRadar.find('span.icon-save-contact').replaceWith(
		`<a class="tile__badge icon icon-save-contact re-idle" title="Kontakt bearbeiten" href="/messenger/contacts/all/${profileName}"></a>`
	);
	jNodeRadar.find('a.icon-save-contact').click(function() {
		openByName('/messenger/contacts/all/', profileLink, profileName);
		return false;
	});
	jNodeRadar.find('span.icon-block-contact').replaceWith(
		`<a class="tile__badge icon icon-block-contact" title="Kontakt bearbeiten" href="/messenger/contacts/blocked/${profileName}"></a>`
	);
	jNodeRadar.find('a.icon-block-contact').click(function() {
		openByName('/messenger/contacts/blocked/', profileLink, profileName);
		return false;
	});

	//show count of profiles, Plus, travelling, and GPS in title tag of selected tab
	var loadTime = new Date().toLocaleTimeString() + ' aktualisiert';
	var profiles = $('#profiles div.grid-tile, #profiles div.listresult__tile').length;
	var profilesPlus = '';
	var percentPlus = '';
	if ($('#profiles .search-results--small-tiles').length == 0) {
		profilesPlus = $('#profiles div.js-romeo-badge span img, #profiles div[class*="badge--plus-"]').length;
		percentPlus = (profiles > 0 ? ' (' + (Math.round(profilesPlus/profiles*1000)/10).toLocaleString() + ' %)' : '');
		profilesPlus =  ' • ' + profilesPlus + ' Plus' + percentPlus;
	}
	var profilesTravel = '';
	var percentTravel = '';
	profilesTravel = $('#profiles .js-search-results .travelIcon__container svg').length;
	percentTravel = (profiles > 0 ? ' (' + (Math.round(profilesTravel/profiles*1000)/10).toLocaleString() + ' %)' : '');
	profilesTravel =  `${profilesTravel} ${(profilesTravel == 1 ? 'Reisender' : 'Reisende')}${percentTravel}`;
	var profilesGPS = '';
	var percentGPS = '';
	profilesGPS = $('#profiles span.icon-gps-needle').length;
	percentGPS = (profiles > 0 ? ' (' + (Math.round(profilesGPS/profiles*1000)/10).toLocaleString() + ' %)' : '');
	profilesGPS =  ' • ' + profilesGPS + ' GPS' + percentGPS;
    profiles += ` ${(profiles == 1 ? 'Profil' : 'Profile')} geladen`;
	$('ul.Tabbed-nav--2c5l9 li.is-selected, div.js-nav-item').attr(
		'title', `${loadTime}\r${profiles}${profilesPlus}\r${profilesTravel}${profilesGPS}`
	);
}


// ***** Add message icons to non-message Activity Stream entries; handle group related entries; add icon to settings *****
function handleActivity (jNode) {
	var bodyLink = $(jNode).attr('href');
	if (bodyLink != undefined && ! bodyLink.match('/messenger/chat/')) {
		var replacePattern = /(.*\/(profile|hunq|groups)\/)([A-Za-z0-9\-_]*)(.*)/;
		var profileLink = '' + $(jNode).parent().find('a').attr('href');
		var profileName = profileLink.replace(replacePattern, '$3');
		var baseUrl = '/messenger/chat/';

		//add message icon
        if (profileName) {
            $(jNode).find('div.layout.layout--consume span, .Text-sc-1whxy9k-0').last().not(':has(a,img)').append(
                `<a class="icon icon-chat re-icon ml- mr--" title="Messages" href="${baseUrl}${profileName}"></a>`
            ).children().last().click(function() {
                openByName(baseUrl, profileLink, profileName);
                return false;
            });
        }

		//italic if system text
		$(jNode).find('span.listitem__text, span.ui-status-description').attr('style', 'font-style:italic');

		//add icon if group picture, hide group pictures in small stream
        var jNodeGroupPicture = $(jNode).has('img.thumbnail').not(':has(span.icon-heart, span.icon-camera-icon, .re-add)');
        jNodeGroupPicture.find('span.listitem__timestamp').after(
            `<span class="clr-ui-text listitem__highlight--expanded re-add"><span class="icon icon-group-members"></span></span>`
        );
        if (! location.pathname.match(/^\/stream/)) {
            /*jNodeGroupPicture.find('span.listitem__text').first().hide();
            jNodeGroupPicture.attr('title', jNodeGroupPicture.find('span.listitem__text').first().text().trim());
            $(jNodeGroupPicture).parent().addClass('re-group-item');*/
            $(jNodeGroupPicture).parent().addClass('re-group-item').hide();
        } else {
            //hide group pictures of blocked users
            $(jNodeGroupPicture).has('svg.BqJLH').parent().addClass('re-group-item').hide();
        }
	}

    //add enlarge and settings
    $('div.stream__content div.js-list').not(':has(div.re-add)').prepend(`
<div class="re-add mt-- mb- mh" style="display:flex; justify-content:space-between; align-items:center; height:.875rem">
<div class="layout">${(!location.pathname.match(/^\/stream$/)) ? `<a href="/stream" class="icon icon-back re-icon re-link p0 re-stream-group-items" title="Erweitern"></a>` : ``}</div>
<a href="/me/notifications" class="icon icon-settings re-icon re-link p0"><span class="ml--">Anpassen</span></a>
</div>`
    );
    var groupItems = $('section.js-stream .re-group-item').not(':has(svg.BqJLH)').length;
    if (groupItems) {
        $('.re-stream-group-items').html(`<span class="ml-">${groupItems}<span class="icon icon-group-members" style="margin-left:.15rem"></span></span>`).attr('title', `Erweitern • Neue Bilder in ${groupItems} Gruppe${(groupItems == 1) ? '' : 'n'}`);
    }

    //fix hunqz readability
    $(jNode).filter('div.stream__content a.listitem__body').has('svg[label="HUNQZ"]').find('span[lang]').first().attr('style', 'font-weight:550');
}


// ***** Profiles: show profile id, visits since; link URLs *****
function handleProfile (jNode) {
    var profileName = $('div.profile__info h1').last().text().trim().match(/[-\w]+$/);
    var profileType = ($('div.is-profile-loaded').hasClass('profile--hunqz')) ? '/hunqz/profiles' : '/profiles';
    var profilePath = ($('div.is-profile-loaded').hasClass('profile--hunqz')) ? 'hunq' : 'profile';
	var profileId = '', profileIdInfo = '', visits = '', since = '', known = '', known1st = '', known2nd = '', verified = '';
	var albums = 0, profilePictures = 0, albumPictures = 0, quickSharePictures = 0;
    var name = '', country = '', distance = '', sensor = '', headline = '';
    var sinceMonthYear = $('section.profile__stats section > p').last().contents().filter(function(){ return this.TEXT_NODE; }).first().text();
    $.ajax({headers: ajaxHead(), url: `/api/v4${profileType}/${profileName}/full?lang=de&lookup_type=NAME&expand=albumsV2.items.*.pictures`})

	.done(function (data) {
		if (data.id) {
			profileId = data.id;
            profileIdInfo = `Profil-ID: ${data.id}`;
		}
		if (data.visits_count) { //  not 0 or undefined
			visits = `<br>Besucher: ${data.visits_count.toLocaleString()}`;
		}
		if (data.creation_date) {
			since = new Date(data.creation_date.slice(0,-5)+'.000Z').toLocaleDateString();
			since = 'Mitglied seit: ' + since;
		} else {
			since = sinceMonthYear;
		}

        //known by
        if (data.known_by && data.known_by.first_degree > 0) {
            known1st = data.known_by.first_degree;
            known2nd = data.known_by.second_degree;
            known = `Bekannt bei ${known1st.toLocaleString()} ${known1st == 1 ? 'Nutzer (dieser' : 'Nutzern (diese'} bekannt bei ${known2nd.toLocaleString()})`;
        } else {
            known = `Noch nicht bei anderen bekannt`;
        }

        //authenticity
        if (known1st) {
            verified = (known1st > 9 && known2nd > 99) ? 'white' : '';
            $('.profile .js-authenticity svg path.tick').attr('transform', 'translate(0 -5) scale(.925)');
            $('.profile .js-authenticity button').append(`<span class="${verified}">${known1st <= 30 ? `${known1st.toLocaleString()}` : '30+'}</span>`);
        } else {
            $('.profile .js-authenticity button').append(`<span>...</span>`);
        }

        //show pictures count
		if (data.albumsV2.items_total) {
            for (var item of data.albumsV2.items) {
                if (item.id == 'PROFILE') {
                    if (item.pictures) {
                        profilePictures += item.pictures.items_total;
                    }
                } else if (item.access_policy == 'SHARED') {
                    quickSharePictures += item.items_total;
                } else {
                    if (item.pictures) {
                        albumPictures += item.pictures.items_total;
                        //albums += (item.items_total) ? 1 : 0;
                    }
                }
            }
            if (profilePictures > 1) {
                $('section.profile__image-strip').not(':has(.re-add)').append(
                    `<div class="re-add re-img-count"><div><p>${profilePictures}</p></div></div>`
                ).children().last().click(function(){
                    changeUrl(`/${profilePath}/${profileName}/gallery`);
                }).attr('style', 'cursor:pointer').attr('title', 'Alle Alben anzeigen');
            }
            if (quickSharePictures) {
                $('section.js-profile-stats section > div > a > div > div').first().has('svg').not(':has(.re-add)').append(
                    `<div class="re-add re-img-count"><div><p>${quickSharePictures}</p></div></div>`
                );
            }
            if (quickSharePictures || albumPictures) {
                $('section.profile__stats section > div[class^="InfoText-"] h4').first().not(':has(.re-add)').append(
                    `<span class="re-add" style="color:rgba(255,255,255,.6)"> • ${quickSharePictures + albumPictures} Bilder</span>`
                );
            }
		}

        //link location name to maps
        if (data.location.name) {
			name = `<a target="_blank" href="https://google.com/maps/place/${data.location.name}" rel="noreferrer noopener" class="re-link-idle" title="Ort in Google Maps anzeigen">${data.location.name.trim()}</a>`;
		}
        if (data.location.country && (data.location.distance > 50000 || data.location.distance == null)) {
            country = `, ${data.location.country}`;
        }
        if (data.location.distance != null) {
			if (data.location.distance < 1000) {
				distance = ' • ' + data.location.distance + 'm';
			} else {
				distance = ' • ' + (Math.round(data.location.distance/100)/10).toLocaleString() + ' km';
			}
		}
		if (data.location.sensor) {
			sensor = '<span class="icon icon-small icon-gps-needle icon-badge ml--"></span>';
		}

        $('div.profile__info svg + p[class^="BodyText"]').first().html(name + country + distance);
    })

	.fail(function (data) {
		profileId = $('#spotlight-container div.layer.layer--spotlight').attr('id').match(/\d{3,}/);
		since = sinceMonthYear;
	})

	.always(function (data) {

		//since, visits, known by, id
		$('section.profile__stats section > p').last().addClass('re-profile-stats').html(`${since}<br>${known}${visits}<br>${profileIdInfo}`);

        //authenticity
        var title = $('.profile__info .js-authenticity button').attr('title');
        $('.profile__info .js-authenticity button').not('.re-add').addClass('re-add').attr('title', `${title}\n${sinceMonthYear}`);

		//hide visit
		$('div.profile__info h1').last().attr('title', 'Profilbesuch verstecken').attr('aria-label', 'Profilbesuch verstecken').click(function(){
			hideVisit(profileId);
		});

        //link to albums
        $('section.profile__stats section').first().find('div[class^="InfoText-"] h4').click(function(){
            changeUrl(`/${profilePath}/${profileName}/gallery`);
        }).attr('style', 'cursor:pointer').attr('title', 'Alle Alben anzeigen');

        //complete headline
        if (data.headline && data.headline.length > 50) {
            $('div.profile__info p[class^="BodyText"]').last().text(data.headline).attr('style', 'margin-right:3.5rem');
        }

        //URLs in headline and profile text
        $('div.profile__info div.reactView > div > p[class^="BodyText"], section.profile__stats section > div > p[class^="BaseText-sc-"]').each(function() {
            var replacedText = linkify($(this).html());
            $(this).html(replacedText);
        });

        //link travel locations to maps
        $('section#travel-list div > p:first-child').each(function() {
            $(this).wrapInner(
                `<a target="_blank" href="https://google.com/maps/place/${$(this).text().trim()}" rel="noreferrer noopener" class="re-link-idle" title="Ort in Google Maps anzeigen"></a>`
            )
        });
    });


    //move Travel Date and Looking For to top, keeping Albums and B&B at top
    $('section.profile__stats section').not('.re-add').has('h4:contains("Ich suche"), h4:contains("Looking For"), h4:contains("Recherche")').each(function() {
        $(this).addClass('re-add').prependTo('section.profile__stats > div > div.reactView > div');
    });
    $('section.profile__stats section#travel-list').not('.re-add').addClass('re-add').prependTo('section.profile__stats > div > div.reactView > div');
    $('section.profile__stats section').not('.re-add').has('img + div > p').addClass('re-add').prependTo('section.profile__stats > div > div.reactView > div');
    $('section.profile__stats section').not('.re-add').first().has('div[class^="InfoText-"]').addClass('re-add').prependTo('section.profile__stats > div > div.reactView > div');
}


// ***** Handle error page *****
function linksError (jNode) {
    //...
	$(jNode).append(
		`<div style="font-size:0.9em"><br/>...</div>`
	);
}


// ***** Sort groups list by active posts *****

//init
var groupsList = [];
var counter = 0;
var groupsCount = 0;
var refresh = false;
var groupsListloadTime = 0;
var lastGroupUrl = '';
var atoz = false;
var linkTextActive = 'Aktive Beiträge', linkTextDefault = 'Standard';

function recentPosts (jNode) {
//console.log('recentPosts');

	//insert menu
	var viewMode = localStorage.getItem('REgroupsListView');
	//var linkTextActive = 'Aktive Beiträge', linkTextDefault = 'Standard';
	var linkText = (viewMode == 'active') ? linkTextActive : linkTextDefault;
	$('div.Container--3h4tt div.js-groups').not(':has(span.re-list-head)').prepend(`
<div class="mt- mb- pv-" style="font-size:.85rem; border-bottom:1px solid rgba(255,255,255,.125)">
<span class="re-list-head">Sortierung</span>
<span class="re-list-view ml-" title="Umschalten" role="button" aria-label="Sortierung ändern">${linkText}</span>
<span class="re-list-load icon icon-stathistory pl- pr-" title="Aktualisieren">
<span class="re-list-head ml--"></span>
</span>
</div>`
    );

    //toggle sort mode (recent posts <-> a-z)
    $('.re-list-head').off().on('dblclick', function() {
        if (viewMode == 'active') {
            atoz = !atoz;
            linkText = linkTextActive = (atoz) ? 'A - Z' : 'Aktive Beiträge';
            $('.re-list-view').text(linkText);
            refresh = true;
            insertPostsList (groupsList);
        }
    });

    if (mobile.matches) {
		$('div.Container--3h4tt div.js-header').removeClass('l-hidden-sm');
		$('div.Container--xbtQn').hide();
	}
	if (viewMode !== 'active') {
		$('span.re-list-load').hide();
	}

	//register toggle menu click
	$('span.re-list-view').click(function() {
        if (counter == groupsCount) {  //not while building groupsList
            var oldViewMode = localStorage.getItem('REgroupsListView');
            if (oldViewMode == 'active') {
                linkText = linkTextDefault;
                viewMode = 'default';
                $('span.re-list-load').hide();
                lastGroupUrl = '';
            } else {
                linkText = linkTextActive;
                viewMode = 'active';
                $('span.re-list-load').show();
            }
            localStorage.setItem('REgroupsListView', viewMode);
            $('span.re-list-view').text(linkText);
            handlePostsList(viewMode);
        }
    });

	//register refresh list click
	$('span.re-list-load').click(function() {
        if (counter == groupsCount) {  //not while building groupsList
            groupsList.length = 0;
            counter = 0;
            refresh = true;
            groupsListloadTime = new Date().toLocaleTimeString();
            $('div.Container--3h4tt div.js-groups div.re-add').remove();
            $('div.Container--3h4tt div.js-groups > div.pt-').hide();
            handlePostsList(viewMode);
        }
    });
    handlePostsList(viewMode);
}


// ***** Handle posts list *****
function handlePostsList (viewMode) {

	if (viewMode !== 'active') {
		$('div.Container--3h4tt div.js-groups div.re-add').remove();
		$('div.Container--3h4tt div.js-groups > div.pt-').show();
        var highlighted = 'div.Container--3h4tt div.js-groups button';
        if ($(highlighted).length) {
            if (typeof $(highlighted)[0].scrollIntoViewIfNeeded === 'function') {
                $(highlighted)[0].scrollIntoViewIfNeeded(true);
            } else {
                $(highlighted)[0].scrollIntoView({block: 'center'});
            }
        }
    } else {
		$('div.Container--3h4tt div.js-groups > div.pt-').hide();
		if (groupsList.length > 0) {
			insertPostsList(groupsList);
		} else {
            refresh = true;
            groupsListloadTime = new Date().toLocaleTimeString('de-DE');
			$('div.Container--3h4tt div.js-groups').not(':has(.re-add)').append(
				'<div class="spinner-container re-add"><div class="spinner"></div></div>'
			);

            $.ajax({headers: ajaxHead(), url: '/api/v4/profiles/me/groups?expand=items.*.(membership,activity)&lang=de&length=1000&pick=items.*.(id,name,display_name,membership.is_admin,is_forum_enabled,activity.posts.*,activity.photos.*,preview_pic.url_token),items_total'})

			.done(function (data) {
				groupsList = [];
				counter = 0;
				groupsCount = data.items_total;
				var ajaxUrl = '', name = '', displayName = '', isAdmin = false, picToken = '', actPosts = 0, countPosts = 0, actPhotos = 0, countPhotos = 0;

				for (var item of data.items) {

                    // loop "posts" for all groups
                    if (item) {
                        name = item.name;
                        displayName = item.display_name;
                        isAdmin = item.membership.is_admin;
                        picToken = (item.preview_pic) ? item.preview_pic.url_token : '';
                        if (item.activity) {
                            actPosts = (item.activity.posts) ? item.activity.posts.last_accessed : 0;
                            countPosts = (item.activity.posts) ? item.activity.posts.count : 0;
                            actPhotos = (item.activity.photos) ? item.activity.photos.last_accessed : 0;
                            countPhotos = (item.activity.photos) ? item.activity.photos.count : 0;
                        } else {
                            actPosts = 0;
                            countPosts = 0;
                            actPhotos = 0;
                            countPhotos = 0;
                        }
                        if (item.is_forum_enabled) {
                            ajaxUrl = `/api/v4/groups/${item.id}/posts?pick=items.*.(date_created,comments.items.*.(date_created))&expand=items.*.(comments)&lang=de&length=5&sort_criteria=COMMENTED_AT_DESC`;
                        } else {
                            ajaxUrl = `/api/v4/groups/${item.id}/posts?pick=items.*.(date_created)&lang=de&length=5&sort_criteria=COMMENTED_AT_DESC`;
                        }

                        $.ajax({headers: ajaxHead(),
                                url: ajaxUrl,
                                custom: {name: name, displayName: displayName, isAdmin: isAdmin, picToken: picToken, actPosts: actPosts, countPosts: countPosts, actPhotos: actPhotos, countPhotos: countPhotos} })

                        .done(function (data) {

                            //save name, timestamp, etc. to array (most recent post or comment)
                            var timeList = [], name = '', displayName = '', time = 0, isComment = false, picToken = '', actPosts = 0, countPosts = 0, actPhotos = 0, countPhotos = 0;
                            for (var item of data.items) {
                                if (item.comments && item.comments.items[0]) {
                                    time = item.comments.items[0].date_created;
                                    isComment = true;
                                } else {
                                    time = item.date_created;
                                    isComment = false;
                                }
                                if (time != 0) time = new Date(time.slice(0,-2) + ':00');
                                timeList.push({time: time, isComment: isComment});
                            }

                            //sort by most recent post or comment
                            timeList.sort(function (a,b) {
                                return b.time - a.time;
                            });

                            if (timeList[0]) time = timeList[0].time;
                            if (timeList[0]) isComment = timeList[0].isComment;
                            name = this.custom.name;
                            displayName = this.custom.displayName;
                            isAdmin = this.custom.isAdmin;
                            picToken = this.custom.picToken;
                            actPosts = this.custom.actPosts;
                            countPosts = this.custom.countPosts;
                            actPhotos = this.custom.actPhotos;
                            countPhotos = this.custom.countPhotos;
                            if (actPosts != 0) actPosts = new Date(actPosts);
                            if (actPhotos != 0) actPhotos = new Date(actPhotos);
                            //console.log(displayName, timeList);

                            //add to array
                            groupsList.push({time: time, name: name, displayName: displayName, isAdmin: isAdmin, picToken: picToken, isComment: isComment, actPosts: actPosts, countPosts: countPosts, actPhotos: actPhotos, countPhotos: countPhotos, visited: false});
                        })

                        .always(function (data) {
                            counter++;
                            if (counter >= groupsCount) {
                                insertPostsList(groupsList);
                                return;
                            }
                        });

                    } else {
                        counter++;
                        if (counter >= groupsCount) {
                            insertPostsList(groupsList);
                            return;
                        }
                    }
                }
            });
        }
        $('span.re-list-load span').text(groupsListloadTime.slice(0,-3));
        $('ul.Container--1I9Gx button').click();
	}
}


// ***** Insert posts list *****
function insertPostsList (groupsList) {

	//sort by most recent
	groupsList.sort(function (a,b) {
        if (atoz) {
            return b.isAdmin - a.isAdmin || b.displayName.toLowerCase() < a.displayName.toLowerCase();
        } else {
            return b.time - a.time;
        }
    });
	//console.log(groupsList);

	//insert list elements on top of MY GROUPS
	$('div.Container--3h4tt div.js-groups div.re-add').remove();
	$('div.Container--3h4tt div.js-groups > div.pt-').hide();
    $('div.Container--3h4tt div.js-groups').append('<div class="mb re-add"></div>');
	var thumbnail = '';
	for (var item of groupsList) {
		thumbnail = (item.picToken == '') ? '/assets/f3b077168d98a575e8960e70c59f5d78.svg' : `/img/usr/squarish/212x212/${item.picToken}.jpg`;
		$('div.Container--3h4tt div.js-groups').append(`
<div class="reactView re-add">
<a href="/groups/member/${item.name}" class="re-groups-listitem">
<div class="re-groups-tile" style="background-image:url(${thumbnail})"></div>
<div class="re-groups-entry">
<span class="re-groups-text">
<span>
<span lang="" title="${item.displayName}" class="re-groups-name">${item.displayName}<br>
<span class="re-groups-time" title="${(item.isComment) ? 'Letzter Kommentar' : 'Letzter Beitrag'} vom ${item.time.toLocaleString('de-DE').slice(0,-3)}">${dateTime(item.time, !mobile.matches)}</span>
</span>
</span>
</span>
</div>
</a>
</div>`
        );
        if (item.isComment) {
            $('div.Container--3h4tt div.js-groups div.re-add a').last().append(`
<div class="re-groups-new ml-" style="margin-right:.1em" title="Aktueller Kommentar vom ${item.time.toLocaleString('de-DE').slice(0,-3)}">
<svg label="Aktueller Kommentar" role="img" aria-hidden="false" viewBox="0 0 1024 1024">
<path d="M943.3 408.7c-49.9-49.9-118.9-80.7-195-80.7H222.7l.1-183.5L0 367.4l222.8 222.8.1-183.5h525.4c54.4 0 103.6 22.1 139.3 57.7 35.6 35.7 57.6 84.9 57.6 139.3 0 54.4-22 103.6-57.7 139.3-35.7 35.7-84.9 57.7-139.3 57.7H616.3c-14.2 0-25.7 11.5-25.7 25.7v27.5c0 14.2 11.5 25.7 25.7 25.7h131.9c76.2 0 145.1-30.9 195-80.7 49.9-49.9 80.7-118.9 80.7-195 .1-76.3-30.8-145.3-80.6-195.2z"></path>
</svg>
</div>`
             );
        }
        if (item.actPosts) {
            $('div.Container--3h4tt div.js-groups div.re-add a').last().append(`
<div class="re-groups-new ml-" title="${item.countPosts} ${(item.countPosts == 1) ? 'neuer Beitrag' : 'neue Beiträge'} seit ${item.actPosts.toLocaleString('de-DE').slice(0,-3)}">
<span style="margin-right:.25em">${item.countPosts}</span>
<svg label="Neue Beiträge" style="font-size:.675em" role="img" aria-hidden="false" viewBox="0 0 100 100">
<path d="M50 0a50 50 0 100 100A50 50 0 1050 0z"></path>
</svg>
</div>`
            );
        }
        if (item.actPhotos) {
            $('div.Container--3h4tt div.js-groups div.re-add a').last().append(`
<div class="re-groups-new ml-" title="${item.countPhotos} ${(item.countPhotos == 1) ? 'neues Bild' : 'neue Bilder'} seit ${item.actPhotos.toLocaleString('de-DE').slice(0,-3)}">
<span style="margin-right:.25em">${item.countPhotos}</span>
<svg label="Neue Bilder" role="img" aria-hidden="false" viewBox="0 0 1024 1024">
<path d="M656 579c0 80-64 145-144 145s-144-65-144-145 64-145 144-145 144 65 144 145zM512 808c-63 0-120-26-161-68s-67-98-67-162c0-63 26-120 67-162s98-67 161-67 120 25 161 67 67 99 67 162c0 64-26 120-67 162s-98 68-161 68zm449-589H762L644 107c-7-7-16-11-27-11H407c-11 0-20 4-27 11L262 219H63c-35 0-63 28-63 63v583c0 35 28 63 63 63h898c35 0 63-28 63-63V282c0-35-28-63-63-63z"></path>
</svg>
</div>`
            );
        }
        if (item.isAdmin) {
            $('div.Container--3h4tt div.js-groups div.re-add a').last().append(`
<div class="re-groups-admin ml-" title="Du verwaltest diese Gruppe" aria-label="Du verwaltest diese Gruppe"s>
<span>ADMIN</span>
</div>`
            );
        }
	}

    //highlight selected item, dim visited items
    $('div.Container--3h4tt div.js-groups div.re-add a').each(function() {
        var index = $('div.Container--3h4tt div.js-groups div.re-add a').index(this);
        if (location.href.match(this.href) || (mobile.matches && lastGroupUrl.match(this.href))) {
            if (!refresh) {
                if (typeof $(this)[0].scrollIntoViewIfNeeded === 'function') {
                    $(this)[0].scrollIntoViewIfNeeded(true);
                } else {
                    $(this)[0].scrollIntoView({block: 'center'});
                }
            }
            $(this).addClass('re-groups-selected');
            $(this).attr('style', `background-color:${color9}`);
            groupsList[index].visited = true;
            //if (mobile.matches) $(this).find('div.re-groups-new').remove();
        } else if (groupsList[index].visited == true) {
            $(this).addClass('re-groups-visited');
        }
    });

    //refresh content on re-click, remove indicators on first click
    $('div.Container--3h4tt div.js-groups div.re-add a').click(function() {
        lastGroupUrl = this.href;
        if (location.href.match(this.href)) {
            $('ul.Container--1I9Gx button').click();
            return false;
        } else {
            return true;
        }
    });
    refresh = false;
}


// ***** Toggle group manage mode *****
function groupManageMode (jNode) {
    if (resigned) $(jNode).prepend('<span class="re-add">!</span>');
    $(jNode).off().on('dblclick', function() {
        $(this).find('.re-add').remove();
        resigned = !resigned;
        if (resigned) $(this).prepend('<span class="re-add">!</span>');
    });
}


// ***** Compact view for group posts *****
function groupPostsViewMode (jNode) {
	var viewMode = localStorage.getItem('REgroupPostsView');
	var linkTextCompact = 'Kompakt', linkTextDefault = 'Mit Kommentaren';
	var linkText = (viewMode == 'compact') ? linkTextCompact : linkTextDefault;
	$('div.Container--1cJVr').append(
		'<span class="re-posts-view" title="Umschalten">' + linkText + '</span>' +
		'<span class="layout-item icon icon-dropdown dropdown__arrow"></span>'
	);
	$('span.re-posts-view').click(function() {
		var oldViewMode = localStorage.getItem('REgroupPostsView');
		var linkText = linkTextCompact, viewMode = 'compact';
		if (oldViewMode == 'compact') {
			linkText = linkTextDefault;
			viewMode = 'default';
		}
		localStorage.setItem('REgroupPostsView', viewMode);
		$('span.re-posts-view').text(linkText);
		$('span.CommentCount--3uCNR').each(function() {
			handleGroupComment(this);
		});
	});
}


// ***** Show/hide group comments *****
function refreshGroupComments (jNode) {
	var thisNode = $(jNode).find('span.CommentCount--3uCNR');  // span.CommentCount--1Eef2
	handleGroupComment(thisNode);
}


// ***** Handle a single group post *****
function handleGroupComment (thisNode) {
	var viewMode = localStorage.getItem('REgroupPostsView');
	var timestamp = $(thisNode).parent().parent().nextAll().find('span.PostDate--2LVzF').last().text();
	if (viewMode == 'compact') {
		$(thisNode).parent().parent().nextAll().hide();
		if (timestamp == '') {
			$(thisNode).text('Kommentar schreiben');
		} else {
			$(thisNode).after(
				'<span class="ml--"> • ' + timestamp + '</span>'
			);
		}
		$(thisNode).not(':has(a)').wrapInner('<a></a>').off('click').click(function() {
			$(thisNode).parent().parent().nextAll().toggle();
		});
	} else {
		$('div.CommentsArea--3iSoQ, div.js-add-comment').show();
		$(thisNode).find('a').contents().unwrap('a');
		$(thisNode).off('click');
		$(thisNode).next('span').remove();
		if (timestamp == '') {
			$(thisNode).text('0 Kommentare');
		}
	}
}


// ***** Group posts *****
function handleGroupPosts (jNode) {
    //...
}


// ***** Sort group members by distance from travel location *****

//init
var travelName = '';
sessionStorage.setItem('PR_SETTINGS:groups:members:sorting', localStorage.getItem('PR_SETTINGS:groups:members:sorting'));

function groupTravelLocation (jNode) {
	var viewMode = localStorage.getItem('REgroupTravelLocation');
    var toggleTravel = 'span.re-member-travel';
    var refreshClick = 'ul.Container--1I9Gx button';
    $('div.Container--e53RO div.Container--13Nkp .re-add').remove();
	$('div.Container--e53RO div.Container--13Nkp').has('div:contains("Entf"), div:contains("Dist")').append(
        `<span class="re-add re-member-travel icon icon-airplane ml- pl" style="cursor:pointer" title="Travel" role="img" aria-label="Travel"><span class="pl-"></span></span>`
	);
    handleTravelLocation(viewMode, toggleTravel, refreshClick);
    $('span.re-member-travel').click(function() {
		var viewMode = localStorage.getItem('REgroupTravelLocation');
        var toggleTravel = 'span.re-member-travel';
        var refreshClick = 'ul.Container--1I9Gx button';
		viewMode = (viewMode == 'travel') ? 'default' : 'travel';
		localStorage.setItem('REgroupTravelLocation', viewMode);
		handleTravelLocation(viewMode, toggleTravel, refreshClick);
	});

    //save sorting permanently
    localStorage.setItem('PR_SETTINGS:groups:members:sorting', sessionStorage.getItem('PR_SETTINGS:groups:members:sorting'));
}


function handleTravelLocation (viewMode, toggleTravel, refreshClick) {
    if (viewMode == 'travel') {
        $(toggleTravel).addClass('re-selected');
        if (xhrTravelLat) {
            $.ajax({headers: ajaxHead(), url: `/api/geocoder/private/name?lat=${xhrTravelLat}&lon=${xhrTravelLong}&lang=de`})

            .done(function (data) {
                if (data[0].name) {
                    travelName = data[0].name;
                }
                var travelEdit = '<a href="/explore/edit" class="re-edit-travel icon icon-pen pl-" title="Reiseziel in TRAVEL ändern"></a>';
                $(toggleTravel).find('span').html(travelName).attr('title', travelName);
                $(toggleTravel).next('a.re-edit-travel').remove();
                $(toggleTravel).after(travelEdit);
                travelMode = true;
                $(refreshClick).get(0).click();
            });

        } else {
            $.ajax({headers: ajaxHead(), url: `/api/v4/locations/travel`})

            .done(function (data) {
                var travelEdit = '';
                if (data.length) {
                    var last = data.length -1;
                    xhrTravelLat = data[last].lat;
                    xhrTravelLong = data[last].long;
                    travelName = data[last].name;
                    travelMode = true;
                    travelEdit = '<a href="/explore/edit" class="re-edit-travel icon icon-pen pl-" title="Reiseziel in TRAVEL ändern"></a>';
                } else {
                    travelMode = false;
                    travelEdit = '<a href="/explore/new" class="re-edit-travel">Füge in TRAVEL dein Reiseziel hinzu!</a>';
                }
                $(toggleTravel).find('span').html(travelName).attr('title', travelName);
                $(toggleTravel).next('a.re-edit-travel').remove();
                $(toggleTravel).after(travelEdit);
                $(refreshClick).get(0).click();
            });

        }

    } else {
        $(toggleTravel).removeClass('re-selected');
        $(toggleTravel).find('span').text('').removeAttr('title');
        $(toggleTravel).next('a.re-edit-travel').remove();
        travelMode = false;
        $(refreshClick).get(0).click();
   }
}


// ***** Show picture info in slide show *****
function imgInfo (jNode) {
    $('#photos .re-add').remove();
	var eq = ($('#photos div[class*=swipe__element]').length == 1) ? 0 : 1;
	var imgName = $('#photos div[class*=swipe__element]').eq(eq).find('div.fit img').attr('src');
    var profileLink = $('#photos div.reactView a');
    var profileName = '';
	if (imgName && imgName.match(/^\/img\//)) {
		//var imgNameTxt = `${imgName.substr(imgName.lastIndexOf('/') + 1, 5)}...`;
        var imgNameTxt = uploadDate(`${imgName.substr(imgName.lastIndexOf('/') + 1, 8)}`);
        var info = `<a style="color:rgba(255,255,255,.5); cursor:default" title="Upload-Datum" href="${imgName}">${imgNameTxt}</a>`;
        var date = `<span class="re-add re-mobi ml+" style="color:rgba(255,255,255,.5)">${$('div.js-sidebar div.txt-quiet').text().trim()}</span>`;
        if (profileLink.length) {
            profileName = `<a class="re-add re-mobi re-link ml+" style="z-index:1000" href="${profileLink.attr('href')}">${$(profileLink).find('[title]').attr('title').trim()}</a>`;
        }

        //desktop
        if(profileLink.length) {
            $('#photos div[class*="js-counter"] div[class*="Container"]').not(':has(a)').prepend(`<span class="re-desk mr+">${info}</span>`);
        } else {
            $('#photos div.js-actions').not(':has(.re-desk)').append(`<div class="re-add re-desk" style="position:absolute; bottom:1rem; right:1rem; white-space:nowrap">${info}</div>`);
        }

        //mobile
        $('#photos div.js-actions').not(':has(.re-mobi)').append(`<div class="re-add re-mobi" style="position:absolute; bottom:1rem; right:1rem; white-space:nowrap">${info}</div>`);
        $('#photos div[class*="js-counter"] div[class*="Container"]').not(':has(.re-add)').append(date + profileName);
	}

	//URLs in picture caption
	$('#photos div[class^="Description"]').each(function() {
		var replacedText = linkify($(this).text());
		$(this).html(replacedText);
	});

    //fix for arrow and esc keys
    $('#photos button').first().focus();

    //remove false blurred layers
    $('#photos.layer.layer--centered').slice(1).remove();
}


// ***** Show picture info in picture rating *****
function ratingInfo (jNode) {
    $('#picture-rating .re-add').remove();
    $('#picture-rating button').blur();
	var imgName = $('#picture-rating img').attr('src');
    if (imgName) {
        var imgNameTxt = `${imgName.substr(imgName.lastIndexOf('/') + 1, 5)}...`;
        var imgDate = uploadDate(`${imgName.substr(imgName.lastIndexOf('/') + 1, 8)}`);
        var imgNameMax = sessionStorage.getItem('REratingMax');
        imgNameMax = (imgNameMax ? imgNameMax : imgNameTxt);
        if (imgNameTxt >= imgNameMax) {
            sessionStorage.setItem('REratingMax', imgNameTxt);
        }
        var color = (parseInt(imgNameTxt,16) + 1 < parseInt(imgNameMax,16)) ? 'rgba(255,0,0,0.8)' : 'rgba(255,255,255,0.375)';
        $('#picture-rating div > span > span').append(
            `<a style="color:${color}; font-size:0.85em; cursor:default" class="ml mr- re-add" href="${imgName}">${imgDate}</a>`
        );
    }

    //set focus on skip or reload button
    if ($('.no-touchevents').length) {
        $('#picture-rating p + button, #picture-rating button[class^="TertiaryButton__Element-"]').focus();
    }
}


// ***** Remove getting location spinner in Gear browser *****

//init
var cleanDone = false;

function removeFeedbackLayer (jNode) {
    if (cleanDone == false && $(jNode).has('div.spinner.spinner--centered').length) {
        $(jNode).has('div.spinner.spinner--centered').fadeOut(1000, function() { $(jNode).remove(); });
        cleanDone = true;
    }
}


// ***** Relogin after timeout *****
function reLogin (jNode) {
    if ($(jNode).siblings('div.ui-dimmer__buttons').find('span.ui-button--primary').filter(':contains("Erneut einloggen"), :contains("Log in again"), :contains("Reconnexion")').length) {
        $(jNode).hide();
        $(jNode).parent().not(':has(.re-add)').append('<div class="spinner-container re-add"><div class="spinner"></div></div>');
        $(jNode).siblings('div.ui-dimmer__buttons').hide();
        $(jNode).siblings('div.ui-dimmer__text').text('Erneut einloggen ...');
        location.reload();
    }
}


// ***** XHR *****

//init
var xhrTravelLat = '', xhrTravelLong = '';
var travelMode = false;
var resigned = false;

(function() {
    let oldXHROpen = window.XMLHttpRequest.prototype.open;
    window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {

        if (url.match(/v4\/messages\//)) {
            //...
        }

        if (url.match(/v4\/messages\/conversations\?/)) {
            //url = url.replace('&length=15', '&length=120');
        }

        if (url.match(/v4\/contacts\?/)) {
            if ($('#contacts-custom-tags a.ui-tag--selected span.ui-tag__label').text().trim() == '[ A-Z ]') {
                url += '&sort_criteria=NAME_ASC';
                url = url.replace(/&filter%5Btags%5D%5B%5D\=\d+/, ``);
            }
            if ($('#contacts-custom-tags a.ui-tag--selected span.ui-tag__label').text().trim() == '[ Login ]') {
                url += '&sort_criteria=LAST_LOGIN_DESC';
                url = url.replace(/&filter%5Btags%5D%5B%5D\=\d+/, ``);
            }
            if ($('#contacts-custom-tags a.ui-tag--selected span.ui-tag__label').text().trim() == '[ Online ]') {
                url += '&filter[online]=true';
                url = url.replace(/&filter%5Btags%5D%5B%5D\=\d+/, ``);
            }
            //url += '&sort_criteria=LAST_LOGIN_DESC';
            //url += '&sort_criteria=NAME_ASC';
            //url += '&filter[online]=true';
            url = url.replace('/contacts?length=100&pick=items.*.profile&lang=de', '/contacts?length=999&pick=items.*.profile&lang=de');
        }

        if (location.pathname.match(/^\/messenger\/contacts\/name/)) {
            if (url.match(/filter%5Busername%5D\=/)) {
                url += '&sort_criteria=NAME_ASC';
                url = url.replace(/filter%5Busername%5D\=\*/, '');
            }
        }

        if (location.pathname.match(/^\/radar\//)) {
            if (travelMode) {
                url = url.replace(/filter%5Blocation%5D%5Blat%5D\=[-0-9\.]+/, `filter[location][lat]=${xhrTravelLat}`);
                url = url.replace(/filter%5Blocation%5D%5Blong%5D\=[-0-9\.]+/, `filter[location][long]=${xhrTravelLong}`);
            }
            //url = url.replace(/filter%5Btravellers_filter%5D\=EXCLUDED/, '');
            //url = url.replace(/filter%5Btravellers_filter%5D\=INCLUDED/, 'filter%5Btravellers_filter%5D=EXCLUDED');
            //url = url.replace(/sort_criteria\=NEARBY_ASC/, 'sort_criteria=NEARBY_DESC');
        }

        if (location.pathname.match(/^\/(radar|hunqz)\//) && url.match(/filter%5Blocation%5D/)) {
            var radius = $('.js-distance-radius .noUi-handle').attr('aria-valuenow');
            if (radius >= 115500 && radius < 116500) {  //116
                url = url.replace(/filter%5Blocation%5D%5Bradius%5D\=[0-9\.]+/, `filter[location][radius]=${1000000}`);
            }
            if (radius >= 116500 && radius < 117500) {  //117
                url = url.replace(/filter%5Blocation%5D%5Bradius%5D\=[0-9\.]+/, `filter[location][radius]=${2000000}`);
            }
            if (radius >= 117500 && radius < 118500) {  //118
                url = url.replace(/filter%5Blocation%5D%5Bradius%5D\=[0-9\.]+/, `filter[location][radius]=${3000000}`);
            }
            if (radius >= 118500 && radius < 119500) {  //119
                url = url.replace(/filter%5Blocation%5D%5Bradius%5D\=[0-9\.]+/, `filter[location][radius]=${4000000}`);
            }
        }

        if (url.match(/v4\/groups\//)) {
            if (travelMode) {
                url = url.replace(/filter%5Blocation%5D%5Blat%5D\=[-0-9\.]+/, `filter[location][lat]=${xhrTravelLat}`);
                url = url.replace(/filter%5Blocation%5D%5Blong%5D\=[-0-9\.]+/, `filter[location][long]=${xhrTravelLong}`);
            }
            if (resigned) {
                url = url.replace('statuses[]=REJECTED', 'statuses[]=NONE');
            }
        }

        if (url.match(/v4\/profiles\/popular/)) {
            if (travelMode) {
                url = url.replace('sort_criteria=LAST_LOGIN_DESC', 'sort_criteria=NEARBY_ASC');
                url = url.replace(/filter%5Blocation%5D%5Blat%5D\=[-0-9\.]+/, `filter[location][lat]=${xhrTravelLat}`);
                url = url.replace(/filter%5Blocation%5D%5Blong%5D\=[-0-9\.]+/, `filter[location][long]=${xhrTravelLong}`);
            }
            //url = url.replace('sort_criteria=LAST_LOGIN_DESC', 'sort_criteria=SIGNUP_DESC');
        }

        if (location.pathname.match(/^\/explore\//) && url.match(/v4\/profiles\?lang/)) {
            var loc = location.pathname.match(/[-0-9\.]+/g);
            xhrTravelLat = loc[0];
            xhrTravelLong = loc[1];

            //var radius = $('.js-distance-radius .noUi-handle').attr('aria-valuenow');
            //url = url.replace('sort_criteria=NEARBY_ASC', `sort_criteria=LAST_LOGIN_DESC&filter[location][radius]=${radius}`);
            //url = url.replace('sort_criteria=NEARBY_ASC', `sort_criteria=SIGNUP_DESC&filter[location][radius]=${radius}`);
        }

        return oldXHROpen.apply(this, arguments);
    }
})();


// ***** Run at login *****

//init
var commonGroupsList = [], visitorsList = [], visitsList = [];

function runAtLogin (jNode) {

    //init common groups
    commonGroupsList = [];
    $.ajax({headers: ajaxHead(), url: '/api/v4/profiles/me/groups?lang=de&length=10000&pick=items.*.(id,name,display_name)'})
    .done(function (data) {
        for (var item of data.items) {
            commonGroupsList.push(item);
        }
        //console.log(commonGroupsList);
        waitForKeyElements ('li[class*="list__item--group-tile-"] a, .profile .js-profile-stats section > div > a, .profile .js-profile-groups ul > a, .js-list li[class*="Tile--"] a, #search div[class^="GroupThird-"] a', handleGroupTiles);
        //DYNAMIC_TWEAKS.push({selector: 'li[class*="list__item--group-tile-"] a, .profile .js-profile-stats section > div > a, .profile .js-profile-groups ul > a, .js-list li[class*="Tile--"] a, #search div[class^="GroupThird-"] a', tweak: handleGroupTiles});

    });

    //visitors, visits
    $.ajax({headers: ajaxHead(), url: '/api/v4/visitors?lang=de&length=10000&pick=items.*.(name,date_visited)'})
    .done(function (data) {
        for (var item of data.items) {
            //visitorsList.push(item);
        }
        //console.log(visitorsList);
    });
    $.ajax({headers: ajaxHead(), url: '/api/v4/visits?lang=de&length=10000&pick=items.*.(name,date_visited)'})
    .done(function (data) {
        for (var item of data.items) {
            //visitsList.push(item);
        }
        //console.log(visitsList);
    });
}



// ***** MutationObserver *****

/*** Functions for accessibility tweaks ***/

function makeHeading(el, level) {
    el.setAttribute("role", "heading");
    el.setAttribute("aria-level", level);
}

function makeRegion(el, label) {
    el.setAttribute("role", "main");
    el.setAttribute("aria-label", label);
}

function makeButton(el, label) {
    el.setAttribute("role", "button");
    if (label) el.setAttribute("aria-label", label);
}

function makeButtonFromText(el, label) {
    el.setAttribute("role", "button");
    if (!label) label = '';
    el.setAttribute("aria-label", `${label}${el.textContent.trim()}`);
}

function makeIcon(el, label) {
    el.setAttribute("role", "img");
    el.setAttribute("aria-label", label);
}

function makePresentational(el) {
    el.setAttribute("role", "presentation");
}

function setLabel(el, label) {
    el.setAttribute("aria-label", label);
}

function setLabelFromTitle(el, label) {
    if (!label) label = '';
    el.setAttribute("aria-label", `${label}${el.getAttribute('title')}`);
}

function setLabelFromText(el) {
    el.setAttribute("aria-label", el.textContent.trim());
}


/*** Code to apply the tweaks when appropriate ***/

function applyTweaks(root, tweaks) {
    for (let tweak of tweaks) {
        for (let el of root.querySelectorAll(tweak.selector)) {
            if (Array.isArray(tweak.tweak)) {
                let [func, ...args] = tweak.tweak;
                func(el, ...args);
            } else {
                tweak.tweak(el);
            }
        }
    }
}

function initMutationObserver() {
    applyTweaks(document, LOAD_TWEAKS);
    applyTweaks(document, DYNAMIC_TWEAKS);
}

let observer = new MutationObserver(function(mutations) {
    for (let mutation of mutations) {
        try {
            if (mutation.type === "childList") {
                for (let node of mutation.addedNodes) {
                    if (node.nodeType != Node.ELEMENT_NODE) {
                        continue;
                    }
                    applyTweaks(node, DYNAMIC_TWEAKS);
                }
            /*} else if (mutation.type === "attributes") {
                if (mutation.attributeName == "class") {
                    //onClassModified(mutation.target);
                    //applyTweaks(node, DYNAMIC_ATTRIBUTE_TWEAKS);
                    applyTweaks(mutation.target, DYNAMIC_TWEAKS);
                }*/
            }
        } catch (e) {
            // Catch exceptions for individual mutations so other mutations are still handled
            console.log(`Exception while handling mutation: ${e}`);
        }
    }
});
observer.observe(document, {childList: true,/* attributes: true,*/
                            subtree: true/*, attributeFilter: ["class"]*/});

/*** Define the actual tweaks ***/

// Tweaks to be applied on load
const LOAD_TWEAKS = [
]

// Tweaks to be applied whenever a node is added
const DYNAMIC_TWEAKS = [

    //RomeoEnhancer (see also waitForKeyElements below)
    //{selector: 'div#marionette.is-logged-in', tweak: runAtLogin},
    {selector: '#search input', tweak: searchInput},
    {selector: 'header li', tweak: menuLinks},
    //{selector: 'div[class*="Central--"] ul.Tabbed-nav--2c5l9 li.is-selected', tweak: navLinks},
    {selector: '#offcanvas-nav div[class^="Version"], div.js-filter-button button', tweak: handleVersionBookmark},
    //{selector: '#messenger:not(.is-hidden) span.sc-idbldl-0', tweak: previewMessage},
    {selector: '#messenger div:is(.js-correspondence, .js-header) div.reactView > div', tweak: showLoginLocation},
    //{selector: '#messenger div.js-correspondence div.js-header-region div[class*="ContextMenu__"] ul', tweak: threadOptionsMenu},
    {selector: '#messenger div.js-chat .reactView a[class^="Box-sc-"]', tweak: contactsMessage},
    {selector: '#messenger div.js-contacts .reactView a[class^="Box-sc-"]', tweak: messageContacts},
    {selector: ':is(div.js-wrapper, div.js-admins, #group-preview) a.js-contact', tweak: handleContactStrip},
    {selector: ':is(div.js-wrapper, #visits, #search, .js-profiles) :is(a.tile__link div.tile__info, span.listresult__age)', tweak: handleTiles},
    {selector: '#profiles :is(a.tile__link div.tile__info, span.listresult__age)', tweak: handleRadar},
    {selector: 'div.stream__content a.listitem__body, div.stream__content div.js-list', tweak: handleActivity},
    {selector: 'section.js-profile-stats div.js-social-links', tweak: handleProfile},
    //{selector: 'div.js-groups', tweak: recentPosts},
    {selector: '#manage div[class^="FilterBar__Container"] > div > div', tweak: groupManageMode},
    {selector: 'div.Container--e53RO div.Container--13Nkp button', tweak: groupTravelLocation},
    {selector: '#photos div[class*="swipe__element"] img', tweak: imgInfo},
    {selector: '#picture-rating img, #picture-rating p[class^="Text-sc-"]', tweak: ratingInfo},
    //{selector: 'div.js-chat .js-scrollable div[class="js-paging-spinner spinner-container l-fancy"]', tweak: fixScroll},
    //{selector: '#spotlight-container > div.layer-container-clickable', tweak: removeFeedbackLayer},
    {selector: 'div.layer--error div.icon-warning-triangle', tweak: reLogin},

    // Navigation items with badges
    {selector: '.icon-search', tweak: [makeIcon, "Suchen"]},
    {selector: '.icon-visitor', tweak: [makeIcon, "Besucher"]},
    {selector: '.icon-chat', tweak: [makeIcon, "Messages"]},
    {selector: '.icon-notification-bell', tweak: [makeIcon, "Activity Stream"]},
    {selector: '.ui-status--online', tweak: [makeIcon, "online"]},
    {selector: '.icon-airplane', tweak: [makeIcon, "Travel"]},
    {selector: '.icon-save-contact', tweak: [makeIcon, "Kontakte"]},
    {selector: '.js-nav-item .icon-group-members', tweak: [makeIcon, "Meine Gruppen"]},

    // The Profile screen
    {selector: '.profile--romeo', tweak: [makeRegion, "Romeo-Profil"]},
    {selector: '.profile--hunqz', tweak: [makeRegion, "Hunqz-Profil"]},
    {selector: '.icon-add-footprint', tweak: [makeIcon, "Fußtaps vergeben"]},
    {selector: '.js-given-footprint', tweak: [setLabelFromTitle, "Fußtaps: "]},
    {selector: '.js-profile-footprints a:not(.is-selected)', tweak: [makeButton]},
    {selector: '.js-remove-footprint, .js-profile-footprints .is-selected .footprint-remove', tweak: [makeButton, "Fußtaps entfernen"]},
    {selector: '.js-quickshare-trigger', tweak: [setLabel, "QuickShare-Album teilen"]},
    {selector: '.icon-default-contact', tweak: [makeIcon, "Nutzer speichern"]},
    {selector: '[id^="profile-"] .icon-save-contact', tweak: [makeIcon, "Nutzer speichern"]},
    {selector: '#visits > div', tweak: [makeRegion, "Besucher"]},
    {selector: '.icon-back', tweak: [makeIcon, "Zurück"]},
    {selector: '.icon-next', tweak: [makeIcon, "Weiter"]},
    {selector: '.icon-open-menu-ver', tweak: [makeIcon, "Menü öffnen"]},
    {selector: '.icon-open-stats', tweak: [makeIcon, "Profildetails"]},
    {selector: '.js-attach-pictures', tweak: [setLabel, "Bilder anhängen"]},
    {selector: '.js-submit', tweak: [setLabel, "Abschicken"]},
    {selector: 'button[class^="Close--"]', tweak: [setLabel, "Schließen"]},
    {selector: '.js-close-spotlight', tweak: [makeButton, "Schließen"]},
    {selector: '.js-hide.js-plus', tweak: [setLabel, "Profilbesuch verstecken"]},

    // other
    {selector: '.messages-send__select-button', tweak: [setLabel, "Templates, Standort, Bilder senden"]},
    {selector: ':not(.js-nav-item) > .icon-group-members', tweak: [makeIcon, "Gruppe"]},
]

// Tweaks to be applied on attribute changes
const DYNAMIC_ATTRIBUTE_TWEAKS = [
    //...
]

initMutationObserver();



// ***** waitForKeyElements (for nodes not handled by MutationObserver) *****

waitForKeyElements ('div#marionette.is-logged-in', runAtLogin, true);
//waitForKeyElements ('#search input', searchInput);
//waitForKeyElements ('header li', menuLinks);
waitForKeyElements ('div[class*="Central--"] ul.Tabbed-nav--2c5l9 li.is-selected', navLinks);
//waitForKeyElements ('#offcanvas-nav div[class^="Version"], div.js-filter-button', handleVersionBookmark); // #offcanvas-nav div.settings__key,
waitForKeyElements ('#messenger[class!="layer layer--nav-primary is-hidden"] span.sc-idbldl-0', previewMessage);
//waitForKeyElements ('#messenger div:is(.js-correspondence, .js-header) div.reactView > div', showLoginLocation);
waitForKeyElements ('#messenger div.js-correspondence div.js-header-region div[class*="ContextMenu__"] ul', threadOptionsMenu);
//waitForKeyElements ('#messenger div.js-chat .reactView a[class^="Box-sc-"]', contactsMessage);
//waitForKeyElements ('#messenger div.js-contacts .reactView a[class^="Box-sc-"]', messageContacts);
//waitForKeyElements ('div.js-wrapper a.tile__link > div.tile__image, :is(#visits, #search, .js-profiles) :is(a.tile__link, a.listresult)', handleTiles);
//waitForKeyElements (':is(div.js-wrapper, div.js-admins, #group-preview) a.js-contact', handleContactStrip);
//waitForKeyElements ('#profiles a.tile__link, #profiles a.listresult', handleRadar);
//waitForKeyElements ('div.stream__content a.listitem__body, div.stream__content div.js-list', handleActivity);
//waitForKeyElements ('div.is-profile-loaded', handleProfile);
//waitForKeyElements ('div.profile__container--error', linksError);
waitForKeyElements ('div.js-groups', recentPosts);
//waitForKeyElements ('#manage div[class^="FilterBar__Container"] > div > div', groupManageMode);
//waitForKeyElements ('div.Sorting--3E8ZW', groupPostsViewMode);
//waitForKeyElements ('div.Main--W3X4g div.js-post-list p.js-content div.reactView button', handleGroupPosts);
//waitForKeyElements ('div.Container--e53RO div.Container--13Nkp button', groupTravelLocation);
//waitForKeyElements ('div.js-post-list div.Container--3__0Z', refreshGroupComments);
//waitForKeyElements ('#photos div[class*=swipe__element]', imgInfo);
//waitForKeyElements ('#picture-rating img, #picture-rating p[class^="Text-sc-"]', ratingInfo);
waitForKeyElements ('div.js-chat .js-scrollable div[class="js-paging-spinner spinner-container l-fancy"]', fixScroll, true);
waitForKeyElements ('#spotlight-container > div.layer-container-clickable', removeFeedbackLayer, true);
//waitForKeyElements ('div.layer--error div.icon-warning-triangle', reLogin);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址