RomeoEnhancer

Ergänzungen für die Romeo-Website

目前為 2024-02-05 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         RomeoEnhancer
// @version      1.11.2
// @author       braveguy (Romeo: braveguy / Gruppe RomeoEnhancer)
// @description  Ergänzungen für die Romeo-Website
// @require      https://code.jquery.com/jquery-3.7.0.min.js
// @match        https://*.romeo.com/*
// @match        https://83.98.143.20/*
// @match        https://www.hunqz.com/*
// @run-at       document-body
// @copyright    braveguy 12.10.2016 / 04.02.2024
// @namespace    https://greasyfork.org/users/139428
// ==/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://greasyfork.org/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://greasyfork.org/scripts/31282-romeoenhancer

*/


// ***** Use RE_addStyle instead of GM_addStyle to work with both Gear Browser and Userscripts *****
if (typeof RE_addStyle == 'undefined') {
  this.RE_addStyle = (aCss) => {
    'use strict';
    let head = document.querySelector('head');
    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':  // Lighter Grey
        color1 = '#1f1f1f', color2 = '#1f1f1f', color3 = '#000', color4 = '#101010', color5 = '#1a1a1a', color6 = '#f0f0f0', color7 = '#cc2d2d', color8 = '#2a2a2a', color9 = '#2a2a2a';
        break;
    case 'retroBlue':  // Retro Blue
        color1 = '#102c54', color2 = '#102c54', color3 = '#000', color4 = '#101010', color5 = '#122646', color6 = '#f0f0f0', color7 = '#f0f0f0', color8 = 'rgba(255,255,255,.08)', color9 = '#30415d';
        break;
    case 'fineGrey':  // RE default - Fine Grey
    default:
        color1 = '#1a1a1a', color2 = '#171717', color3 = '#000', color4 = '#101010', color5 = '#232323', color6 = '#f0f0f0', color7 = '#cc2d2d', color8 = '#2a2a2a', color9 = '#2a2a2a';
        break;
}


RE_addStyle (

    //tweak dark design
    `html.js body, .js-main-stage, .stream__content, .layer__column--background, .layer__column--detail, #group-preview .layer__container, .profile .below-fold, .profile-section {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}` +
    `.Xcontent-nav, .Xcontent-nav__actions, .ui-navbar--app, .filter-container {background-color:${color2} !important}` +
    `.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}` +
    `.js-profile-footprints section {background-color:${color8}}` +
    `.listresult .list-stat-bar__item, .icon-circular.icon-u {background-color:transparent}` +
    `.WGgGs .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}` +
    `.messages-list__content, .messages-send {background-color:${color2}!important}` +
    `:is(.js-profile-contact, #messenger .js-detail) > div > div, :is(.js-profile-contact, .js-detail) > div > div > div > div:last-child {background-color:${color2}!important}` +
    `.messages-send__form-input {background-color:${color3}!important}` +
    `:is(.WGgGs, .js-main .js-navigation li) svg[class^="Icon__Svg-sc-"] {color:${color7}}` +
    `:is(.search-results--list-style > div, .grouped-tiles-list .refreshable) > div:nth-child(odd) .reactView > div {background-color:${color5}}` +
    `:is(.search-results--list-style > div, .grouped-tiles-list .refreshable) > div:nth-child(even) .reactView > div {background-color:${color1}}` +

    //bigger text in messages, groups, profiles; wrap long links
    ':is(#messages-list div.reactView, :is(.js-post-list, .js-post, .js-profile-text, .js-description) div[class^="TruncateBlock__Content-"]) > p[class^="BaseText-sc-"],' +
    '.profile section.profile__stats details > div > p[class^="BaseText-sc-"],' +
    ':is(.stream__content, .js-chat, .js-correspondence, .js-contacts, .js-username, .WGgGs, .js-actions, #manage, .js-profile-reviews) [class^="BodyText-sc-"],' +
    '[class^="ResponsiveBodyText-"] {font-size:.9375rem !important; letter-spacing:normal !important; line-height:1.375 !important; word-break:break-word !important}' +

    //avatar icon
    ':is(.layer-left-navigation, header li) div[class^="OnlineStatus"] svg[viewBox="0 0 10 12"] {width:.75rem; height:.75rem}' +

    //icons, links
	'a.re-icon {position:relative; top:.1em; font-size:.9em}' +
    ':is(div.BIG, div.SMALL, div.LIST, .js-contacts) a.re-icon {position:relative; top:0}' +
    ':is(div.LIST, .js-contacts, .js-username) a.re-icon {font-size:.825em}' +
    'a.re-idle, a.re-idle-no-hover, a.re-idle-no-hover:hover {color:inherit !important}' +
	':is(a.re-icon:hover, a.re-idle, Xa.re-icon-visitor, Xa.re-icon visited):hover {color:rgba(102,215,255,.8) !important}' +
    '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}' +
    'a[class*="Tabbed-nav-link-"] {font-size:1.05rem}' +

    //discover page
    ':is(a.re-eyecandy-active, a.re-eyecandy-active:hover) span {display:none; margin: 0 .25rem 0 .5rem; font-size:.875rem; color:rgba(255,255,255,1) !important; text-transform:none; font-family:Inter; font-weight:normal}' +
    'a.re-eyecandy-active.is-selected span:first-child, a.re-eyecandy-active:not(.is-selected) span:nth-child(2) {display:inline}' +
    'a.re-eyecandy-active svg {margin-bottom:.125rem}' +
    'a.re-eyecandy-active svg path {fill:currentcolor}' +

    //filter icon, bookmark name
    //'.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}' +

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

    //big tiles
    'div.tile--plus span[class^="SpecialText"] {background-color:rgb(38,38,38)}' +
    'div.tile p[class^="SpecialText"] {word-break:break-word}' +
    'div.tile p[class^="SpecialText"]:hover {white-space:normal}' +

    //list view
    'div:has(> .LIST) + div {margin-top:.5rem}' +

    //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}' +
    '.message:not(.message--sent) .message__text {background:hsla(0,0%,100%,.125)}' +
    '.message__status {color:rgba(255,255,255,.5)!important}' +
    ':is(.js-chat, #manage) div[class^="OnlineStatus"] > div > svg {height:13px; width:13px}' +
    ':is(.js-chat, #manage) div[class^="OnlineStatus"] > div > svg[viewBox="0 0 10 12"] {height:15px; width:15px}' +
    'p[class*="OnlineStatus__TimeString"] {line-height:1}' +
    '.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)}' +
    '.js-chat p[class^="BodyText-sc-"] {opacity:.87; column-gap:.25rem}' +
    '.js-chat a[href^="/messenger"] > div > span {opacity:.87}' +
    '.js-chat a > span > span svg[viewBox="0 0 9 7"] {height:9px; width:11px; opacity:.66}' +
    '.js-chat a > span > span svg[viewBox="0 0 12 12"] {height:15px; width:15px}' +
    '#messenger .js-header-region > div > h1 {padding-top:1.2rem}' +
    '#messenger .js-header-region > div > h1 a:has(span) {padding-bottom:.85rem}' +
    '#messenger .js-header-region .reactView > div > div > :is(div, a) {position:absolute; top:-1.2rem}' +
    '#messenger .re-login-location {position:absolute; top:0; width:100%}' +
    '#messenger .re-login-location > div {position:absolute; font-size:.75rem; font-weight:500; opacity:.8; background-color:transparent}' +
    '#messenger .re-msg-location {top:0; width:100%; padding:.25rem 0 0 3rem}' +
    '#messenger .re-msg-location a {padding-top:3px}' +
    '#messenger .re-msg-login {top:2.7rem; left:4rem; line-height:.85rem; min-width:10rem; padding-left:3rem; margin-right:5rem}' +
    '#messenger .re-msg-offline {top:2.7rem; left:3rem; line-height:.85rem}' +
    '.messages-send__form textarea.input--chat {padding-right:.5rem}' +
    ':is(.js-correspondence .js-header-region, #manage) div[class*="ContextMenu__"] ul {width:15.25rem; max-width:15.25rem !important}' +
    '.js-contacts div > div > button > svg {height:21px; width:21px}' +
    'ul[class^="TagCloud__UnstyledList"] button {font-size:.875rem; line-height:1.25}' +
    ':is(.js-detail, .js-profile-contact) p[class^="ResponsiveBodyText-"] + :is(span, p) {font-size:.875rem !important; padding-bottom:.25rem}' +
    '.js-chat .listitem--selected {border-right:5px solid #fff}' +
    '.js-contacts .listitem--selected > div {border-left:5px solid #fff}' +

    //stream
    'div.stream__content div.tile div[class^="OnlineStatus"] {position:absolute; left:-8px; top:-8px}' +
    'div.stream__content div.tile div[class^="OnlineStatus"] > div {display:flex; align-items:center; justify-content:center; background-color:#1f1f1f; border-radius:50%; height:19px; width:19px; margin-top:0!important}' +
    'div.stream__content div.tile div[class^="OnlineStatus"] svg {height:12px; width:12px}' +
    'div.stream__content div.tile div[class^="OnlineStatus"] svg[viewBox="0 0 10 12"] {height:13px; width:13px}' +
    '.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 p {word-break:break-word}' +
    '.profile div[aria-label] > div[role="figure"] {border-color:rgba(255,255,255,0.33)}' +
    '.profile .re-profile-stats {font-size:.825rem; color:rgba(255,255,255,0.6)}' +
    '.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}' +
    '.js-profile-footprints section:last-child {padding-top:7.5rem}' +
    '.js-profile-footprints section + section:last-child {padding-top:0}' +
    '.js-profile-footprints.re-zoom section:last-child {grid-template-columns:repeat(3,33%)}' +
    '.js-profile-footprints.re-zoom section:last-child button {background-size:72px 72px; height:77px; width:72px; border-bottom-width:5px}' +
    '.js-profile-footprints.re-zoom section:last-child label p {max-width:14ch}' +

    //slideshow
    'div.swiper-wrapper {margin-top:2rem}' +
    'div.swiper-zoom-container > img {min-height:75%}' +
    '#metadata-bar > div > div > a {margin-left:0}' +
    '#metadata-bar > div > div a + p > a {margin-left:.33em; display:inline}' +
    '#metadata-bar > div:first-child > div + p {margin:0 0 .5rem 0; font-size:.8125rem !important; color:rgba(255,255,255,.66); text-transform:uppercase}' +
    '#metadata-bar > div:first-child > div:first-child + p[class^="SpecialText"] {text-transform:none}' +
    '#metadata-bar > div > div > p[class^="ResponsiveBodyText"] {font-size:.875rem !important}' +
	'#metadata-bar > div > div:nth-last-child(-n+2) {flex-direction:row}' +
    '#metadata-bar > div > div:nth-last-child(-n+2) button > svg + p {display:none}' +
    '#metadata-bar > div > div:nth-last-child(-n+2) > button {margin:0 .25rem .25rem 0; order:2}' +
    '#metadata-bar > div > div:nth-last-child(-n+2) > p {margin:.25rem 0; order:3}' +
    '#metadata-bar > div > div:last-child {margin-bottom:.25rem}' +
    '#metadata-bar > div > div:last-child > p {color:rgb(255,255,255,.66)}' +

    //slideshow in portrait mode (metadata at bottom)
    '@media screen and (orientation:portrait) {' +
    'div.swiper-wrapper {margin-top:0}' +
    '#metadata-bar:not(.gtvwGw) {background:linear-gradient(transparent,rgba(0,0,0,.4) 2.5rem,rgba(0,0,0,.8))}' +
    '#metadata-bar:not(.gtvwGw) {backdrop-filter:blur(0px); -webkit-backdrop-filter:blur(0px)}' +
    '#metadata-bar:not(.gtvwGw) > div > div {margin:0 0 .25rem 0}' +
    '#metadata-bar:not(.gtvwGw) > div:first-child > div:first-child {margin-bottom:0}' +
    '#metadata-bar:not(.gtvwGw) > div:first-child > div + p {margin-bottom:0}' +
    '#metadata-bar:not(.gtvwGw) > div > div:nth-last-child(-n+2) > button {margin-bottom:0}' +
    '#metadata-bar:not(.gtvwGw) > div > div:last-child {margin-bottom:0}' +
    'aside .media-viewer-footer {height:2rem; padding-bottom:.5rem}' +
    'aside .media-viewer-footer p {padding-bottom:.5rem}' +
    '}' +

    //group tiles
    'div[class*="Tile__BaseTile-"] > p + div {position:absolute; bottom:2rem; left:.475rem}' +
    'div[class*="Tile__BaseTile-"] > p + div > p {background-color:#121212 !important}' +
    'div[class*="Tile__BaseTile-"] > p {margin:0 0 -.125rem}' +
    '.profile div[class*="Tile__BaseTile-"] > p {margin:0; font-size:.925rem}' +
    '.re-common-group p:last-child {color:#6ddc00}' +

	//groups
	'nav.re-groups-def, nav.re-groups-enh {position:relative; padding:0 1rem 1rem; height:calc(100% - 3rem); overflow-y:auto}' +
	'nav.re-groups-def {display:none}' +
	'.js-date .reactView span {font-size:.85em}' +
    '.js-post-list button[class*="ShowMoreButton"].is-hidden {display:block!important}' +
    '.js-post-list p[class^="BaseText"] > button {margin-top:.25rem}' +
    '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 .WGgGs {padding-bottom:1rem}' +
    '.js-blurred-image > div {z-index:0}' +

    //groups list
	'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:Inter,Helvetica,Arial,"Open Sans",sans-serif; text-transform:uppercase; user-select:none; 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)}' +
    '.re-groups-listitem {font-family:Inter,Helvetica,Arial,"Open Sans",sans-serif; 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-size:1em; font-weight:500; line-height:1.375em; word-break:break-word; margin-right:.25em; display:block; overflow:hidden}' +
    '.re-groups-name {font-size:.9375rem; color:rgb(0,163,228); font-weight:500; display:inline-block; overflow:hidden; text-overflow:ellipsis; word-wrap:normal}' +
    '.re-groups-time {font-size:.825em; color:rgba(250,250,250,.625)}' +
    '.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 {background-color:${color9}}` +
    '.re-groups-selected .re-groups-name {color:rgba(255,255,255,.925)}' +
    '.re-groups-selected .re-groups-new {color:rgba(250,250,250,.425)}' +
    '.re-groups-selected .re-groups-admin {background-color:rgba(255,255,255,.87)}' +
    '.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)}' +
    '.re-groups-visited .re-groups-admin {background-color:rgba(250,250,250,.425)}' +

    //travel
    '.re-member-travel, .re-radar-travel, .re-edit-travel {color:#00bdff; text-transform:initial; vertical-align: middle; line-height:inherit !important; cursor:pointer}' +
    '.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}' +

    //visitor icons
    'a.re-icon-visitor, a.re-icon-visited {font-size:1.1em; color:#fff}' +
    'a.re-icon-visitor:before, a.re-icon-visited:before {position:relative; top:1px}' +
    'a.re-icon-visited:before {transform:scaleX(-1)}' +

    //picture rating
    '.re-rating-date {position:absolute; top:2.5rem; font-size:.85em; cursor:default}' +

    //relogin
    '.re-relogin-frame {background-color:transparent !important}' +
    '.re-relogin-frame:before {border:none !important}' +

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


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

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

    //slideshow in portrait window
    '.ReactModal__Overlay.ReactModal__Overlay--after-open > .ReactModal__Content.ReactModal__Content--after-open > main {background:none}' +
    '.container-button-next, .container-button-prev {display:flex !important}' +
    '.swiper-zoom-container > img {height:initial}' +

    //fix for version number and social buttons
    '#offcanvas-nav > div {height: calc(100% - 4rem)}' +

    //fix for chat navigation on smaller screens
    '#messenger ul[class^="Tabbed-nav-"] {width:100vw}' +

    '}' +


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

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

    '}' +


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

    //top right navigation
    'nav.js-top-right-navigation {display:none}' +

    '}' +


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

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

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

    //main menu
    'header.js-header {height:4rem; padding-bottom:.25rem}' +
    '.layer--nav-primary {bottom:4rem}' +
    'a[class*="Tabbed-nav-link-"] {font-size:.875rem}' +

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

    //messages
    '#messenger .js-header-region > div > h1 {padding-top:1.7rem}' +
    '#messenger .re-msg-location {padding-top:.75rem}' +
    '#messenger .re-msg-login {top:3.2rem}' +
    '#messenger .re-msg-offline {top:3.2rem}' +
    '.messages-send__form textarea.input--chat {font-size:.9375rem; letter-spacing:normal}' +

    //slideshow
    'div.swiper-wrapper {height:96%; margin-top:0}' +

    //group tiles
    'div[class*="Tile__BaseTile-"] > p {margin:0; font-size:.925rem}' +

    //picture rating
    '.re-rating-date {top:3rem}' +

    //hide groups list when group selected, wrap long group names
    // 'div[class*="withNav-"] > div[class*="ReactContainer-"] {overflow-x:hidden !important}' +
    'div[class*="withNav-"] > div[class*="ReactContainer-"]:not(:has(.re-groups-def)) {flex:0; width:0}' +
    'div[class*="withNav-"] > div[class*="ReactContainer-"]:not(:has(.re-groups-def)) > :is(div, nav) {display:none}' +
    'div[class*="withNav-"] > div[class*="ReactContainer-"]:has(.re-groups-def) + div.js-main {display:none}' +
    'div[class^="NameAndOfficialBadgeWrapper-"] {white-space:unset}' +

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

    //fix for flickering bottom and top right navigation
    'header nav > ul > li:not(:has(path[d^="M20.3943"], path[d^="m20.507"], path[d^="M23.102"], path[d^="M13.493"], path[d^="M6.99 10"], div[style^="background-image"])) {display:none}' +
    'nav.js-top-right-navigation {display:none}' +

    //fix for navbar
    '.ui-navbar {height:3.5rem}' +
    'div.contacts__filters {top:3.5rem}' +
    'div.js-nav + div {margin-top:3.5rem}' +

    '}' +


    //*** TOUCH SCREEN ***
	'@media screen and (hover:none) {' +

    //general
    '.re-touch {visibility:visible!important}' +

    //icons, links
    'a.re-icon {padding:.25em 0 .25rem .5em; font-size:1em}' +
    'div.stream__content a.re-icon {padding:.25em .5em .25em 0}' +
    'a.re-eyecandy-active {padding:.25em .5em}' +

    '}'

);


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


// ***** Test for mobile or touch device *****
// usage: if (mobile.matches) ...; if (touch.matches) ...
var mobile = window.matchMedia("(max-width:767px)");
var touch = window.matchMedia("(hover:none)");


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


// ***** Add title and aria-label
jQuery.fn.titleLabel = function(text) {
    return $(this).attr({'title': text, 'aria-label': text});
};


// ***** Open message thread or contact info by profile name *****
// (not working for blocked, blocking, or 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').attr('style', 'visibility:hidden');
            $('.profile .top-info-header button').attr('style', 'color:rgb(168,168,168)');
		})
	}
}


// ***** 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
const 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
    0x002b6a0f,0x00313036,0x00367804,0x003c4ae9,0x00421916,0x0048a2b6,0x004f0ba9,0x00563896,0x005d4ff9,0x00642853,0x006bc702,0x0072dc7c, //2005
    0x007a2388,0x0082636e,0x0089c239,0x00921763,0x009ae5e4,0x00a3f472,0x00acfdc3,0x00b73469,0x00c1e024,0x00cb8a2a,0x00d5c06a,0x00df6215, //2006
    0x00e909cb,0x00f428f1,0x00fe1934,0x0108f04e,0x01141330,0x011ffb48,0x012b9597,0x0137a3d7,0x0143666a,0x014eba63,0x015aea4f,0x01665b6d, //2007
    0x01721df4,0x017ecdd9,0x018a872f,0x0197cf93,0x01a3fca7,0x01b1bb65,0x01bf7fca,0x01ce1b0e,0x01dd3271,0x01eb50f3,0x01fa29a3,0x02086e83, //2008
    0x0216e0ea,0x0226de08,0x0234eda5,0x02552d86,0x02677f82,0x027aae57,0x028d94f9,0x02a17765,0x02b5de6e,0x02c9adb5,0x02ddba93,0x02f0dfe7, //2009
    0x0304f979,0x031b41a8,0x032e6143,0x0342c960,0x0356cd19,0x036cb45e,0x0381a103,0x039883d7,0x03b0829f,0x03c679e3,0x03dd820b,0x03f432a3, //2010
    0x040af127,0x0423ddbb,0x0439c102,0x045272f4,0x046b187c,0x048570e5,0x04a07ac1,0x04bcdec4,0x04d9b4fa,0x04f52906,0x05120af3,0x052df05d, //2011
    0x054a17b7,0x0568a266,0x0584c8a7,0x05a25176,0x05c0b41b,0x05df82c6,0x05fe3605,0x061e623c,0x063f4240,0x065e2c95,0x067e22b7,0x069dbc58, //2012
    0x06c0432b,0x06e65935,0x0707d372,0x072df9eb,0x0751ca91,0x077860fb,0x079c0f3e,0x07c1e771,0x07e86366,0x080da8ce,0x0831ee79,0x08558d00, //2013
    0x087b94b3,0x08a21c54,0x08c4f55c,0x08eac6fe,0x09119caa,0x0939b66c,0x0960b60a,0x098a9925,0x09b76f87,0x09e01456,0x0a0906c0,0x0a305fc4, //2014
    0x0a80e9b0,0x0adaf553,0x0b2947ce,0x0b7d3b5d,0x0bcf0d62,0x0c24958c,0x0c755b5b,0x0ccd27fa,0x0d23c304,0x0d7376dc,0x0dc5b8fb,0x0e13f084, //2015
    0x0e65d6e6,0x0ebdf2c6,0x0f0ce2ec,0x0f607bf5,0x0fb37151,0x100aaf7e,0x105e79ed,0x10b8bbbf,0x1115d90f,0x116ae5ac,0x11be74a4,0x12120bad, //2016
    0x1267dfa9,0x12c42272,0x1315847b,0x136e2e9c,0x13c5fdbd,0x141f2328,0x147b0f3e,0x14ddb823,0x154194f4,0x159e4a36,0x1601fff2,0x16657d11, //2017
    0x16cb8a49,0x1734ece1,0x1792be94,0x17fa28e5,0x185ec78c,0x18c99770,0x193230d2,0x19a225bd,0x1a12bd1e,0x1a79217a,0x1ae1e760,0x1b4790fd, //2018
    0x1bb4f607,0x1c23ce64,0x1c84c7c3,0x1cf2f646,0x1d61d7f8,0x1ddb56d8,0x1e59a281,0x1edf03d3,0x1f66d255,0x1fe26f82,0x205ec456,0x20d2bded, //2019
    0x21484431,0x21c24ba6,0x2230c76f,0x22a6cd0e,0x2312ebbd,0x23844c83,0x23f45e42,0x246d38c1,0x24eb9338,0x255dc81a,0x25d30c39,0x264389a4, //2020
    0x26b9cb56,0x2730f2c2,0x279824a7,0x28072e59,0x2870b46c,0x28e20cee,0x294ea8da,0x29bdfadb,0x2a2f2276,0x2a987389,0x2b026a37,0x2b6628b7, //2021
    0x2bcd3a33,0x2c3819e9,0x2c95c80f,0x2cf681ad,0x2d57afa7,0x2dc09fc1,0x2e283430,0x2e94df63,0x2f016036,0x2f63046c,0x2fc3e726,0x301fada4, //2022
    0x307f54f1,0x30e474f9,0x313bba85,0x3199f875,0x31f91c99,0x3259674f,0x32b90ef5,0x3320f954,0x3386b446,0x33e58f10,0x34461539,0x34a15fd7, //2023
    0x35019fb1,0x3561df8c,0x35bbe9bb,0x361c0877,0x36792d7b,0x36d96d55,0x3736925a,0x3796d234,0x37f7120e,0x38543713,0x38b4980b,0x3911bd10, //2024
    0xffffffff];

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


// ***** Copy message thread to clipboard *****
function copyMessageThread(profileId, profileName) {
    let loadTime = new Date().toLocaleString('de-DE').slice(0,-3);
    let ownName = 'unknown';
    let msgThread = '- keine Nachrichten -';
    let msgCount = '', header = '', msgTime = '', name = '';
    $.ajax({headers: ajaxHead(), url: `/api/v4/session?lang=de`}).done(function (data) {
        if (data.username) {
            ownName = data.username;
        }
        let 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
            header = 'Nachrichtenverlauf von ' + ownName + ' mit ' + profileName + ' (Profil-ID ' + profileId + ') vom ' + loadTime + '\n\n\n';
            if (data.items_total) {
                msgThread = '';
                msgCount = data.items_total;
                for (let 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.trim() + '\n\n\n';

                    //handle links
                    if (data.items[i].attachments) {
                        for (let item of data.items[i].attachments) {
                            if (item.type == 'COMMAND') {
                                msgThread += `[${item.index + 1}] ${item.params?.url?.match(/^\//) ? 'https://www.romeo.com' : ''}${item.params?.url}\n`;
                                msgThread = msgThread.replace(`[[${item.index}]]`, `${item.params?.text} [${item.index + 1}]`);
                            }
                        }
                        msgThread += '\n\n'
                    }
                }
                msgThread += '\n====================\n\n';
            }

            //confirm copy
            $('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>Nachrichtenverlauf 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() {
                navigator.clipboard.writeText(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();
            });
        });
    });
}


// ***** 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 handleMainMenu (jNode) {
    // let groupsPath = (lastGroupName) ? `/groups/member/${lastGroupName}` : `/groups/member`;
    if (!mobile.matches) {
        // $(jNode).filter('a[href^="/radar"]').off().click(function() { changeUrl('/radar'); return false; });
        // $(jNode).has('path[d^="m20.507"]').off('dblclick').dblclick(function() { changeUrl('/search/romeos'); return false; });
        // $(jNode).has('path[d^="M13.493"]').off().has('p[class^="SpecialText"]').click(function() { changeUrl('/messenger/chat'); return false; });
        // $(jNode).filter('a[href^="/messenger"]').attr('style', 'pointer-events:auto').filter('a[isactive="true"]').off().click(function() { $('#messenger').addClass('is-hidden'); return false; });
        // $(jNode).has('path[d^="M6.99 10"]').off().click(function() { changeUrl('/groups/discover'); return false; });
        $(jNode).has('path[d^="M6.99 10"]').find('button').off('click').click(function() {
            let groupsPath = (lastGroupName) ? `/groups/member/${lastGroupName}` : `/groups/member`;
            changeUrl(groupsPath); return false;
        });
        // $(jNode).filter('a[href^="/hunqz"]').off().click(function() { changeUrl('/hunqz'); return false; });
        // $(jNode).has('path[d^="M972.78"]').off().click(function() { changeUrl('/eyecandy'); return false; });
    } else {
        $(jNode).has('path[d^="M20.3943"], path[d^="m20.507"]').off('dblclick').dblclick(function() { changeUrl('/stream'); return false; });
        $(jNode).has('path[d^="M23.102"]').off('dblclick').dblclick(function() { changeUrl('/visitors/me'); return false; });
        $(jNode).has('path[d^="M13.493"]').off('dblclick').dblclick(function() { changeUrl('/messenger/contacts'); return false; });
        $(jNode).has('path[d^="M6.99 10"]').off('click').click(function() { changeUrl('/groups/member'); return false; });
        // $(jNode).find('button').has('div[style^="background-image"]').off('dblclick').dblclick(function() { changeUrl('/stream'); return false; });
    }
}


// ***** Add contacts etc. to tab navigation *****
function handleTabMenu (jNode) {

    //get url for last group
    // if (localStorage.getItem('REgroupsListView')?.match(/"groups":"member"/)) {
        //let groupsPath = (commonGroupsList?.display_name[0]) ? commonGroupsList?.display_name[0] : `/groups/member`;
        let groupsPath = (lastGroupName) ? `/groups/member/${lastGroupName}` : `/groups/member`;
    // }

    //icons on radar and eyecandy
    if (!mobile.matches) {
        $('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/eyecandy/"]').not(':has(li.re-add > span)').append(
            '<li class="Tabbed-nav-item--TCdwx re-add"><span class="re-add re-radar-travel icon icon-airplane" title="Reiseziel"><span></span></span></li>'
        );
        $('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/eyecandy/"], a[href^="/hunqz"]').not(':has(li.re-add > a)').append(
            '<li class="Tabbed-nav-item--TCdwx re-add"><a style="cursor:default">|</a></li>' +
            '<li class="Tabbed-nav-item--TCdwx re-add"><a href="/messenger/contacts" class="js-nav-item Tabbed-nav-link--x9FLb" title="Kontakte"><span class="icon icon-save-contact"></span></a></li>' +
            `<li class="Tabbed-nav-item--TCdwx re-add"><a href="${groupsPath}" class="js-nav-item Tabbed-nav-link--x9FLb" title="Meine Gruppen (${groupsPath})"><span class="icon icon-group-members"></span></a></li>`
        );
    } else {
        $('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/eyecandy/"]').not(':has(li.re-add)').prepend(
            '<li class="Tabbed-nav-item--TCdwx re-add"><span class="re-add re-radar-travel icon icon-airplane" title="Reiseziel"><span></span></span></li>'
        );
    }

    //travel
    if ($('main nav.js-navigation ul').has('a[href^="/radar/"], a[href^="/eyecandy/"]').length) {
    	let viewMode = localStorage.getItem('REradarTravelLocation');
    	let toggleTravel = 'span.re-radar-travel';
        handleTravelLocation(viewMode, toggleTravel);
    	$('span.re-radar-travel').off().click(function() {
			let viewMode = localStorage.getItem('REradarTravelLocation');
	  	    let toggleTravel = 'span.re-radar-travel';
	   	    let refreshClick = 'li.Tabbed-nav-item--TCdwx 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--TCdwx re-add"><a style="cursor:default">|</a></li>' +
            '<li class="Tabbed-nav-item--TCdwx re-add"><a href="/messenger/contacts" class="js-nav-item Tabbed-nav-link--x9FLb" title="Kontakte"><span class="icon icon-save-contact"></span></a></li>'
        );
    }
    $('div.js-navigation ul li').not('.re-add').find('a[href="/groups/member"]').titleLabel(`${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();
        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}`);
            $('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}`);
            //$('nav.js-navigation li.js-filter-button div.js-filter-button button').attr('title', `Lesezeichen: ${selectedBookmark}`);
            $('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) {
    //console.log('preview message 0');
	var profileId = $(jNode).closest('a').attr('href').match(/\d{3,}/);
    if (profileId) {
        //console.log('preview message 1');
	    $(jNode).closest('a').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
            //console.log('preview message 2');
	    	for (var i = msgCount-1; i >= 0; i--) {
		    	msgText += '\r' + data.items[i].text + '\r';

                //handle links
                if (data.items[i].attachments) {
                    for (var item of data.items[i].attachments) {
                        if (item.type == 'COMMAND') {
                            //console.log('attachments COMMAND');
                            msgText = msgText.replace(`[[${item.index}]]`, `[${item.params?.text}]`);
                        }
                    }
                }

    		}
	    	$(thisId).attr('title', msgText);
            $(thisId).find('img').parent('div').attr('title', msgText);

		    //delete unread
    		$(jNode).attr('title', 'Löschen')/*.text(` ${msgCount} `)*/.off().click(function () {
	    		if (confirm(msgCount + ' Nachrichten 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();
    			}
	    	})
	    });

        //highlight profile name
        $(jNode).closest('a').find('p[class^="BodyText"]').attr('style', 'color:rgba(255,255,255,1); opacity:1');
    }
}


// ***** Show last login and location in message thread *****
function showLoginLocation (jNode) {

    //placeholder for login and location
    $('section.js-detail > div').not(':has(.re-login-location)').append(
        `<div class="re-login-location re-add"></div>`
    );

    if ($('.js-correspondence > div').filter('.re-add').length) return;

    var profileId = location.pathname.match(/\d{3,}$/);
	var name = '', country = '', distance = '', sensor = '';
    var loginTime = '', loginLocalTime = '', timeTag = '', lastSince = '', offlineSince ='';
	$.ajax({headers: ajaxHead(), url: '/api/v4/profiles/' + profileId + '?expand=partner&lang=de'})

	.done(function (data) {
        $('.js-correspondence > div').addClass('re-add');

		//last login
		if (data.last_login && data.online_status) {
			loginTime = new Date(data.last_login.slice(0,-5)+'.000Z');
            loginLocalTime = dateTime(loginTime);
			timeTag = dateTime(loginTime, !mobile.matches);
            lastSince = `${data.online_status == 'OFFLINE' ? 'Zuletzt online:' : 'Online seit:'}`;

            if (($(jNode).closest('div').parent().find('[class*="OnlineStatus"]')).length == 0) {
                offlineSince = `<span class="icon icon-last-login-hours mr-- re-add" style="font-size:.75rem; color:rgba(250,250,250,.75)" title="${lastSince} ${loginLocalTime}"> ${touch.matches ? '' : timeTag}</span>`;
            };

		}

		//location, distance
		if (data.location && data.location.name) {
			name = `<a class="re-link-idle" 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 && data.location.country && (data.location.distance > 50000 || data.location.distance == null)) {
            country = `, ${data.location.country}`;
        }
        if (data.location && data.location.distance >= 0) {
            if (data.location.distance < 1000) {
                distance = ' • ' + data.location.distance + 'm';
            } else if (data.location.distance < 50000) {
                distance = ' • ' + (Math.round(data.location.distance/100)/10).toLocaleString() + ' km';
            } else {
                distance = ' • ' + (Math.round(data.location.distance/1000)) + ' km';
            }
        }
        if (data.location && data.location.sensor) {
            sensor = '<span class="icon icon-gps-needle icon-badge mr-" style="font-size:.85em"></span>';
		}
	})

	.always(function (data) {
        $('section.js-detail div.re-login-location').html(`
            <div class="re-msg-location">${sensor}${name}${country}${distance}</div>
            <div class="re-msg-login" title="${lastSince} ${loginLocalTime}">
            <span class="re-touch">${lastSince} ${loginLocalTime}</span></div>
            <div class="re-msg-offline">${offlineSince}</div>`
        );
    });
}


// ***** Add copy to message thread options menu *****
function threadOptionsMenu (jNode) {
	$(jNode).find('li').first().not('.re-done').addClass('re-done').clone().appendTo($(jNode)).click(function() {
        let profileId = '', profileName = '';
        if ($(this).closest('#messenger').length) {
            profileId = location.pathname.match(/\d{3,}/);
            profileName = $('#messenger div.js-correspondence').find('a > p[class^="BodyText"]').first().text().trim();
        } else {
            profileId = xhrFull.id;
            profileName = xhrFull.name;
        }
        copyMessageThread(profileId, profileName);
		return false;
	}).attr('id', 'options_copy').find('span').text('Kopieren');
}


// ***** Zoom footprints list *****

//init
let setFootprintZoom = (localStorage.getItem('REfootprintZoom') === 'true');

function handleFootprints (jNode) {
    setFootprintZoom ? $('.js-profile-footprints').addClass('re-zoom') : $('.js-profile-footprints').removeClass('re-zoom');
    $('.js-profile-footprints h1 > span').last().not(':has(.re-add)').wrapInner(
        `<a class="re-link mr re-add" style="font-size:1.2rem" title="Vergrößern" href="">- +</a>`
    ).click(function() {
        setFootprintZoom = !setFootprintZoom;
        localStorage.setItem('REfootprintZoom', setFootprintZoom);
        $('.js-profile-footprints').toggleClass('re-zoom');
        return false;
    });
}


// ***** Filter by "no entry" *****
function handleFilterNoEntry (jNode) {

    //insert button
    $(jNode).not('.re-add').addClass('re-add').after(`
        <div class="re-add layout mt mh++">
        <a class="re-button-no-entry ui-tag ui-tag--center" href="">+ keine Angabe
        <span class="icon icon-small icon-info ml-" title="Zeigt auch Profile, die zu den ausgewählten Filterkategorien keine Angabe enthalten"
        aria-label="Zeigt auch Profile, die zu den ausgewählten Filterkategorien keine Angabe enthalten"></span>
        </a>
        </div>`
    ).next('div.re-add').off().click(function() {
        filterNoEntry = !filterNoEntry;
        localStorage.setItem('REfilterNoEntry', filterNoEntry);

        //toggle button
        $('.re-button-no-entry').toggleClass('ui-tag--selected');

        //reload radar tab
        $('section.js-main-stage div.js-navigation').find('a.is-selected, div.js-nav-item').get(0).click();
        return false;
    });
    if (filterNoEntry) $('.re-button-no-entry').addClass('ui-tag--selected');
}


// ***** Link contact icons in messages list entries *****
function handleMessage (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').has('path[d^="M4 8a4"]').wrap(
		`<a href="/messenger/contacts/all/${profileId}" class="re-add"></a>`
	);
	$(jNode).not(':has(a.re-add)').find('svg').has('path[d^="M8.039"]').wrap(
		`<a href="/messenger/contacts/blocked/${profileId}" class="re-add"></a>`
	);
    // $(jNode).find('span > div > p[class^="SpecialText"]:contains("Gesendet")').text('Gesendet …');
    $(jNode).find('span > div > p[class^="SpecialText"]:contains("Gelesen")').text('Gelesen ✓');

    //add hide function to left navigation
    //$('.layer-left-navigation a[href^="/messenger"]').attr('style', 'pointer-events:auto').off().click(function() { $('#messenger').addClass('is-hidden'); return false; });
}


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


// ***** 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');
    //console.log(profileName);
	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()*/.off().click(function() {
		openByName(baseUrl, profileLink, profileName);
		return false;
	}).attr('style', `${profileLink.match(/^\/hunq\//) ? 'color:#fc193c' : ''}`);
}


// ***** Mark common groups *****
function handleGroupTiles (jNode) {
    var jNodeGroupTiles = $(jNode).closest('a');
    if (jNodeGroupTiles.attr('href') && jNodeGroupTiles.attr('href').match(/^\/group\//)) {
        const profileName = jNodeGroupTiles.attr('href').match(/[-\w]+$/);
        const nameIndex = (item) => item.name == profileName;
        if (commonGroupsList.findIndex(nameIndex) > -1) {
            jNodeGroupTiles.addClass('re-common-group');
            jNodeGroupTiles.find('p').last().attr('title', 'Du bist Mitglied in dieser Gruppe');
        }

        //show full group name in title
        jNodeGroupTiles.find('p').first().attr('title', $(jNode).find('p').first().text().trim());
    }
}


// ***** Sort Eyecandy on startpage by Nearby or Activity  *****
function handleDiscoverEyecandy (jNode) {
    $(jNode).not('.re-done').addClass('re-done').append(
        `<a class="re-link re-eyecandy-active ml" title="Umschalten" href="">
        <span>${$('a[href^="/radar/distance"]').first().text().trim()}</span>
        <span>${$('a[href^="/radar/login"]').first().text().trim()}</span>
        <svg aria-hidden="true" viewBox="0 0 10 6" height="6" width="10">
        <path fill-rule="evenodd" d="M5.707 5.707a1 1 0 0 1-1.414 0l-4-4A1 1 0 0 1 1.707.293L5 3.586 8.293.293a1 1 0 0 1 1.414 1.414l-4 4Z" clip-rule="evenodd"></path>
        </svg>
        </a>`
    ).children().off().click(function() {
        eyecandyActive = !eyecandyActive;
        localStorage.setItem('REeyecandyActive', eyecandyActive);
        $('.re-eyecandy-active').toggleClass('is-selected');
        $('li[class^="Tabbed-nav-item-"] a.is-selected')[0].click();
        return false;
    });
    if (eyecandyActive) $('.re-eyecandy-active').addClass('is-selected');
}


// ***** Add message icons to discover, visitor list, search results, and group member list, link contact icons *****
function handleTiles (jNode) {
    let jNodeTiles = $(jNode).closest('a, button');
    let profileName = $(jNode).text().trim();
    let profileLink = `/${(location.pathname.match(/^\/hunqz\//) ? 'hunq' : 'profile')}/${profileName}`;

    //add + to distance if travel
    if ((travelMode && location.pathname.match(/^\/(radar|groups|explore)\//)) || $('.ui-navbar').has('path[d^="M6 8c-1"]').length) {
        if (jNodeTiles.find('svg + p[class^="SpecialText"]').not('.re-add').text().match(/(\d[\d\.,]*\s?(km|m|mi|ft))/)) {
            var distance = `+${jNodeTiles.find('svg + p[class^="SpecialText"]').html()}`;
            jNodeTiles.find('svg + p[class^="SpecialText"]').html(distance).addClass('re-add');
        }
    }

    //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 > div > div + div > p').first().not('.re-add').addClass('re-add').append(
                `<a class="icon icon-visitor re-icon re-icon-visitor ml--" 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 > div > div + div > p').first().not('.re-add').addClass('re-add').prepend(
                `<a class="icon icon-visitor re-icon re-icon-visited mr--" title="Besucht ${dateTime(visitedTime)}" href="/visitors/me"></a>`
            );
        }
    }

    //link username
    if ($(jNode).closest('button').length) {
        $(jNode).not(':has(.re-link-idle)').wrapInner(
            `<a class="re-link-idle" title="${profileName}" href="${profileLink}"></a>`
        ).off().click(function() {
            changeUrl(`${profileLink}`);
            return false;
        });
    }

	//add message icon
	var baseUrl = '/messenger/chat/';
    $(jNode).parent().not(':has(.re-icon)').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('svg').has('path[d^="M4 8a4"]').not('.re-contact').addClass('re-contact').wrap(
		`<a class="re-idle-no-hover re-contact" title="Kontakt bearbeiten" href="/messenger/contacts/all/${profileName}"></a>`
	);
	jNodeTiles.find('a.re-contact').off().click(function() {
		openByName('/messenger/contacts/all/', profileLink, profileName);
        //console.log(profileLink, profileName);
		return false;
	});
	jNodeTiles.find('svg').has('path[d^="M8.039"]').not('.re-contact-blocked').addClass('re-contact-blocked').wrap(
		`<a class="re-idle-no-hover re-contact-blocked" title="Kontakt bearbeiten" href="/messenger/contacts/blocked/${profileName}"></a>`
	);
	jNodeTiles.find('a.re-contact-blocked').off().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) {
    let jNodeRadar = $(jNode).closest('a, button');
	let profileName = $(jNode).text().trim();
    let profileLink = `/${(location.pathname.match(/^\/hunqz\//) ? 'hunq' : 'profile')}/${profileName}`;

    // 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_visited);
        jNodeRadar.find('div > div > div + div').first().not('.re-add').addClass('re-add').append(
            `<a class="icon icon-visitor re-icon re-icon-visitor ml--" title="Besucher ${dateTime(visitTime)}" href="/visitors"></a>`
        );
    }

	//add message icon
	var baseUrl = '/messenger/chat/';
    //jNodeRadar.find('p[class^="SpecialText"]').first().not(':has(.re-icon)').append(
    $(jNode).parent().not(':has(.re-icon)').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;
	});

    //add + to distance if travel
    if (travelMode || location.pathname.match(/^\/explore\//) || $('.ui-navbar').has('path[d^="M6 8c-1"]').length) {
        if (location.pathname.match(/^\/(radar|groups|explore|eyecandy|hunqz)\//)) {
            if (jNodeRadar.find('svg + p[class^="SpecialText"]').not('.re-add').text().match(/(\d[\d\.,]*\s?(km|m|mi|ft))/)) {
                var distance = `+${jNodeRadar.find('svg + p[class^="SpecialText"]').html()}`;
                jNodeRadar.find('svg + p[class^="SpecialText"]').html(distance).addClass('re-add');
            }
        }
    }

    //link username to preview
    //if ($(jNode).closest('.search-results--big-tiles, .search-results--list-style').length) {
        $(jNode).not(':has(.re-link-idle)').wrapInner(
            `<a class="re-link-idle" title="${profileName}  |  Vorschau" href="${profileLink}"></a>`
        ).off().click(function() {
            changeUrl(`${profileLink}/preview`);
            return false;
        });
    //}

	//link contact icon
	jNodeRadar.find('svg').has('path[d^="M4 8a4"]').not('.re-contact').addClass('re-contact').wrap(
		`<a class="re-idle-no-hover re-contact" title="Kontakt bearbeiten" href="/messenger/contacts/all/${profileName}"></a>`
	);
	jNodeRadar.find('a.re-contact').off().click(function() {
		openByName('/messenger/contacts/all/', profileLink, profileName);
		return false;
	});
	jNodeRadar.find('svg').has('path[d^="M8.039"]').not('.re-contact-blocked').addClass('re-contact-blocked').wrap(
		`<a class="re-idle-no-hover re-contact-blocked" title="Kontakt bearbeiten" href="/messenger/contacts/blocked/${profileName}"></a>`
	);
	jNodeRadar.find('a.re-contact-blocked').off().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 :is(div.BIG, div.SMALL, div.LIST)').length;
	var profilesPlus = '';
	var percentPlus = '';
	if ($('#profiles .search-results--mixed-tiles').length) {
		profilesPlus = $('#profiles .search-results--mixed-tiles div.tile--plus, #profiles .search-results--big-tiles').length;
		percentPlus = (profiles > 0 ? ' (' + (Math.round(profilesPlus/profiles*1000)/10).toLocaleString() + ' %)' : '');
		profilesPlus =  ' • ' + profilesPlus + ' Plus' + percentPlus;
	}
	var profilesTravel = '';
	var percentTravel = '';
	profilesTravel = $('#profiles svg > path[d^="M10.0452"]').length;
	percentTravel = (profiles > 0 ? ' (' + (Math.round(profilesTravel/profiles*1000)/10).toLocaleString() + ' %)' : '');
	profilesTravel =  `${profilesTravel} ${(profilesTravel == 1 ? 'Reisender' : 'Reisende')}${percentTravel}`;
	var profilesGPS = '';
	var percentGPS = '';
	profilesGPS = $('#profiles svg > path[d^="M11.94"]').length;
	percentGPS = (profiles > 0 ? ' (' + (Math.round(profilesGPS/profiles*1000)/10).toLocaleString() + ' %)' : '');
	profilesGPS =  ' • ' + profilesGPS + ' GPS' + percentGPS;
    profiles += ` ${(profiles == 1 ? 'Profil' : 'Profile')} geladen`;
	$('ul[class^="Tabbed-nav--"] li.is-selected, ul[class^="list--"] li[class*="list__item--active--"], 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 handleStream (jNode) {
    var jNodeStream = $(jNode).closest('a');
	var bodyLink = jNodeStream.attr('href');
    if (bodyLink != undefined) {
        var replacePattern = /(.*\/(profile|hunq|groups)\/)([-\w]*)(.*)/;
        var profileLink = '' + jNodeStream.parent().find('a').attr('href');
        var profileName = profileLink.replace(replacePattern, '$3');
        var baseUrl = '/messenger/chat/';

        //link contact icon
        jNodeStream.find('svg').has('path[d^="M4 8a4"]').not('.re-contact').addClass('re-contact').wrap(
            `<a class="re-idle-no-hover re-contact" title="Kontakt bearbeiten" href="/messenger/contacts/all/${profileName}"></a>`
        );
        jNodeStream.find('a.re-contact').off().click(function() {
            openByName('/messenger/contacts/all/', profileLink, profileName);
            return false;
        });
        jNodeStream.find('svg').has('path[d^="M8.039"]').not('.re-contact-blocked').addClass('re-contact-blocked').wrap(
            `<a class="re-idle-no-hover re-contact-blocked" title="Kontakt bearbeiten" href="/messenger/contacts/blocked/${profileName}"></a>`
        );
        jNodeStream.find('a.re-contact-blocked').off().click(function() {
            openByName('/messenger/contacts/blocked/', profileLink, profileName);
            return false;
        });
    }
    if (bodyLink != undefined && ! bodyLink.match('/messenger/chat/')) {

        //add message icon
        if (profileName) {
            jNodeStream.find('p[class^="BodyText-"]').not(':has(a,img)').prepend(
                `<a class="icon icon-chat re-icon mr-" title="Messages" href="${baseUrl}${profileName}"></a>`
            ).children().last().click(function() {
                openByName(baseUrl, profileLink, profileName);
                return false;
            });
        }

        //italic if system text
        jNodeStream.find('span.listitem__text').not('.thumbnail__info').attr('style', 'font-style:italic');

        //decode html entities in group names
        let msgText = jNodeStream.find('span.listitem__text').not('.thumbnail__info').text().trim();
        jNodeStream.find('span.listitem__text').not('.thumbnail__info').html(msgText);

        //add icon if group picture, hide group pictures in small stream
        var jNodeGroupPicture = jNodeStream.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('path[d^="M8.039"]').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>`
                                                                        );
    let groupItems = $('section.js-stream .re-group-item').not(':has(path[d^="M8.039"])').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'}`);
    }

    //set radar tile size if stream is opened
/*     if ($('.js-top-right-navigation .js-stream').has('div.stream') && !location.pathname.match(/^\/stream$/)) {
        $('section.js-main-stage').addClass('is-stream-opened');
    } else {
        $('section.js-main-stage').removeClass('is-stream-opened');
    }
    $('.js-top-right-navigation li > button').off().click(function() {
        $('section.js-main-stage').removeClass('is-stream-opened');
    }); */
}


// ***** Profiles: show profile id, visits since; link URLs *****
function handleProfile (jNode) {
    var profileName = $('div.top-info-header p[class^="BodyText"]').first().text().trim();
    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 = '';
    var loginTime = '', loginLocalTime = '', timeTag = '', lastSince ='';
    var sinceMonthYear = $('section.profile__stats section > p').last().contents().filter(function(){ return this.TEXT_NODE; }).first().text();

    var data = xhrFull;
    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`;
    }

    //online time
    if (data.last_login && data.online_status) {
        loginTime = new Date(data.last_login.slice(0,-5)+'.000Z');
        loginLocalTime = dateTime(loginTime);
        timeTag = dateTime(loginTime, 'dateOnly');
        lastSince = `${data.online_status == 'OFFLINE' ? 'Zuletzt online:' : 'Online seit:'}`;
    }

    //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').titleLabel('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-"] p[class^="BaseText"]').first().not(':has(.re-add)').append(
                `<span class="re-add" style="font-size:.925em; 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 if (data.location.distance < 50000) {
            distance = ' • ' + (Math.round(data.location.distance/100)/10).toLocaleString() + ' km';
        } else {
            distance = ' • ' + (Math.round(data.location.distance/1000)) + ' km';
        }
    }

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

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

    //authenticity
    var title = $('.top-info-header p + div > button').attr('title');
    $('.top-info-header p + div > button').not('.re-add').addClass('re-add').find('title').text(`${title}${(known1st) ? ` (${(known1st <= 30) ? known1st : '30+'})` : ''}\n${sinceMonthYear}\n${lastSince} ${timeTag}`);

    //hide visit
    $('.profile--romeo .top-info-header p[class^="BodyText"]').first().attr('title', 'Profilbesuch verstecken').off().click(function(){
        hideVisit(profileId);
    });

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

    //complete headline
    if (data.headline && data.headline.length > 50) {
        $('div.profile__info p[class^="BodyText"]').last().text(data.headline);
    }

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

    //link travel locations to maps
    $('section.profile__stats details').has('#travel-list').find('div:not([role="heading"]) > p:first-child').not(':has(.re-add)').each(function() {
        $(this).wrapInner(
            `<a target="_blank" href="https://google.com/maps/place/${$(this).text().trim()}" rel="noreferrer noopener" class="re-link-idle re-add" 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 details').not('.re-add').has('summary :contains("Ich suche"), summary :contains("Looking For"), summary :contains("Recherche")').each(function() {
        $(this).addClass('re-add').prependTo('section.profile__stats > div > div.reactView:last-child > div');
    });
    $('section.profile__stats details').has('#travel-list').not('.re-add').addClass('re-add').prependTo('section.profile__stats > div > div.reactView:last-child > div');
    $('section.profile__stats section').not('.re-add').has('img + div > p').addClass('re-add').prependTo('section.profile__stats > div > div.reactView:last-child > div');
    $('section.profile__stats section').not('.re-add').first().has('a[href*="/gallery"]').addClass('re-add').prependTo('section.profile__stats > div > div.reactView:last-child > div');
}


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


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

//init
let groupsList = [];
let counter = 0;
let groupsCount = 0;
let refresh = false;
let groupsListloadTime = 0;
//let lastGroupName = localStorage.getItem('RElastGroupName');
let lastGroupName = '';
let atoz = false;
let linkTextActive = 'Aktive Beiträge', linkTextDefault = 'Standard';

function recentPosts (jNode) {
    //console.log('recentPosts');
    //if (mobile.matches) return; // not for mobile yet

    $(jNode).not('.re-groups-def').addClass('re-groups-def').after(
        `<nav class="re-groups-enh" style="display:none"></nav>`
    );

	//insert menu
	let viewMode = localStorage.getItem('REgroupsListView');
	let linkText = (viewMode == 'active') ? linkTextActive : linkTextDefault;
	$('nav.re-groups-def').closest('div[class*="ReactContainer"]').not(':has(span.re-list-head)').prepend(`
<div class="mh mt- 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.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();
                //lastGroupName = '';
            } 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();
            // $('nav.re-groups-def div.re-add').remove();
            // $('nav.re-groups-def > div.pt-').hide();
            $('nav.re-groups-enh').find('a.re-add, button, h4').remove();
            $('nav.re-groups-def').hide();
            handlePostsList(viewMode);
        }
    });
    handlePostsList(viewMode);
}


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

	if (viewMode !== 'active') {
        $('nav.re-groups-enh').prev('nav').not('.re-groups-def').addClass('re-groups-def, re-fix');  // fix after reload/relogin
		$('nav.re-groups-enh').hide();
		$('nav.re-groups-def').show();
        let highlighted = 'nav.re-groups-def button';
        if ($(highlighted).length) {
            $(highlighted)[0].scrollIntoView({block: 'center'});
        }
    } else {
		$('nav.re-groups-def').hide();
		$('nav.re-groups-enh').show();
		if (groupsList.length > 0) {
			insertPostsList(groupsList);
		} else {
            refresh = true;
            groupsListloadTime = new Date().toLocaleTimeString('de-DE');
			$('nav.re-groups-enh').not(':has(.re-add)').prepend(
				'<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 (let 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 (let 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.trim();
                            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 || a.displayName.localeCompare(b.displayName);
        } else {
            return b.time - a.time;
        }
    });
	//console.log(groupsList);

	//insert list elements on top of MY GROUPS
	$('nav.re-groups-enh').find('a.re-add, button, div, h4').remove();
	$('nav.re-groups-def').hide();
    let thumbnail = '';
	for (let item of groupsList) {
		thumbnail = (item.picToken == '') ? '/assets/76d319082f701e66be89.svg' : `/img/usr/squarish/212x212/${item.picToken}.jpg`;
		$('nav.re-groups-enh').append(`
<a href="/groups/member/${item.name}" class="re-add 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, !touch.matches)}</span>
</span>
</span>
</span>
</div>
</a>`
        ).children('a').last().off().click(function() {
            $('nav.re-groups-enh a.re-groups-selected').removeClass('re-groups-selected').addClass('re-groups-visited');
            $(this).removeClass('re-groups-visited').addClass('re-groups-selected');
            let index = $('nav.re-groups-enh a.re-add').index(this);
            lastGroupName = `${groupsList[index].name}`;
            //localStorage.setItem('RElastGroupName', lastGroupName);
        });
        if (item.isComment) {
            $('nav.re-groups-enh a.re-add').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) {
            $('nav.re-groups-enh a.re-add').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) {
            $('nav.re-groups-enh a.re-add').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) {
            $('nav.re-groups-enh a.re-add').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
    $('nav.re-groups-enh a.re-add').each(function() {
        let index = $('nav.re-groups-enh a.re-add').index(this);
        if (location.href.match(this.href) || (mobile.matches && `/groups/member/${lastGroupName}`.match(this.href))) {
            if (!refresh) $(this)[0].scrollIntoView({block: 'center'});
            $(this).addClass('re-groups-selected');
            groupsList[index].visited = true;
            lastGroupName = `${groupsList[index].name}`;
            // console.log('1 ' + lastGroupName)
            //localStorage.setItem('RElastGroupName', lastGroupName);
        } else if (groupsList[index].visited == true) {
            $(this).addClass('re-groups-visited');
        }
    });

    //refresh content on re-click, remove indicators on first click
    $('nav.re-groups-enh a.re-add').click(function() {
        lastGroupName = this.href.replace(/(.*\/groups\/member\/)([-\w]+)$/, '$2');
        // console.log('2 ' + lastGroupName)
        if (location.href.match(this.href)) {
            $('ul.Container--ZCCAM button').click();
            return false;
        } else {
            return true;
        }
    });
    refresh = false;

    //insert separators admin/member if needed
    if (atoz && $('nav.re-groups-enh a.re-add').not(':has(.re-groups-admin)').length) {
        $('nav.re-groups-enh a.re-add').has('.re-groups-admin').first().before(
            `<h4 class="bjIHpu ml- mt- mb--">Admin</h4>`
        );
        $('nav.re-groups-enh a.re-add').has('.re-groups-admin').last().after(
            `<h4 class="bjIHpu ml- mt- mb--">Mitglied</h4>`
        );
    }
}


// ***** Handle mobile groups navigation (instead using :has() pseudo class) *****
function handleMobileGroupsNav (jNode) {
    console.log('mobile groups nav');
    // 'div[class*="withNav-"] > div[class*="ReactContainer-"]:not(:has(.re-groups-def)) {flex:0; width:0}' +
    // 'div[class*="withNav-"] > div[class*="ReactContainer-"]:not(:has(.re-groups-def)) > :is(div, nav) {display:none}' +
    // 'div[class*="withNav-"] > div[class*="ReactContainer-"]:has(.re-groups-def) + div.js-main {display:none}' +

    $(jNode).not(':has(.re-groups-def').attr('style', 'flex:0; width:0');
    $(jNode).not(':has(.re-groups-def').children('div, nav').hide();
    $(jNode).has('.re-groups-def').attr('style', '').next('div.js-main').show();
}


// ***** Toggle group manage mode *****
function groupManageMode (jNode) {
    if (resigned) $(jNode).not(':has(.re-add)').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');
		}
	}
}


// ***** Localize group post date *****
function handleGroupPostDate (jNode) {
    if ($(jNode).not('.re-done').length) {
        $(jNode).addClass('re-done');
        let date = $(jNode).text().trim();
        let localeDate = new Date(date).toLocaleDateString('de-DE');
        if (localeDate != 'Invalid Date') $(jNode).text(`${localeDate}`).attr('title', `${date}`);
    }
}


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


// ***** Sort group members by distance from travel location, save sorting permanently *****

//init
let travelName = '';
if (localStorage.getItem('PR_SETTINGS:groups:members:sorting') && localStorage.getItem('PR_SETTINGS:groups:members:sorting').match(/GROUP_JOIN_DATE_DESC|LAST_LOGIN_DESC|NEARBY_ASC/)) {
    sessionStorage.setItem('PR_SETTINGS:groups:members:sorting', localStorage.getItem('PR_SETTINGS:groups:members:sorting'));
} else {
    sessionStorage.setItem('PR_SETTINGS:groups:members:sorting', 'LAST_LOGIN_DESC');
}

function groupTravelLocation (jNode) {
	let viewMode = localStorage.getItem('REgroupTravelLocation');
    let toggleTravel = 'span.re-member-travel';
    //$('div.js-members-header div.js-dropdown button .re-add').remove();
	$('div.js-members-header div.js-dropdown button').not('.re-done').addClass('re-done').has('div:contains("Entf"), div:contains("In der"), div:contains("Nearby"), div:contains("Cercanos"), div:contains("À prox"), div:contains("Vicini"), div:contains("Por perto")').parent().append(
        `<span class="re-add re-member-travel icon icon-airplane ml- pl" title="Reiseziel" role="img" aria-label="Reiseziel"><span class="pl-"></span></span>`
	);
    handleTravelLocation(viewMode, toggleTravel);
    $('span.re-member-travel').off().click(function() {
		let viewMode = localStorage.getItem('REgroupTravelLocation');
        let toggleTravel = 'span.re-member-travel';
        let refreshClick = 'ul.Container--ZCCAM button';
		viewMode = (viewMode == 'travel') ? 'default' : 'travel';
		localStorage.setItem('REgroupTravelLocation', viewMode);
		handleTravelLocation(viewMode, toggleTravel, refreshClick);
	});

    //save sorting permanently
    setTimeout(() => {
        if (sessionStorage.getItem('PR_SETTINGS:groups:members:sorting') && sessionStorage.getItem('PR_SETTINGS:groups:members:sorting').match(/GROUP_JOIN_DATE_DESC|LAST_LOGIN_DESC|NEARBY_ASC/)) {
            localStorage.setItem('PR_SETTINGS:groups:members:sorting', sessionStorage.getItem('PR_SETTINGS:groups:members:sorting'));
        }
    }, 300);
}


function handleTravelLocation (viewMode, toggleTravel, refreshClick) {
    if (viewMode == 'travel') {
        $(toggleTravel).addClass('re-selected');
        if (xhrTravelLat && xhrTravelLong) {
            $.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;
                }
                let travelEdit = '<a href="/explore/edit" class="re-edit-travel icon icon-pen pl-" style="line-height:inherit" title="Reiseziel ändern"></a>';
                $(toggleTravel).find('span').html(travelName).attr('title', travelName).addClass('pl-');
                $(toggleTravel).next('a.re-edit-travel').remove();
                $(toggleTravel).after(travelEdit);
                travelMode = true;
                if (refreshClick) $(refreshClick).get(0).click();
            });

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

            .done(function (data) {
                let travelEdit = '';
                if (data.length) {
                    let last = data.length -1;
                    xhrTravelLat = data[last].lat;
                    xhrTravelLong = data[last].long;
                    localStorage.setItem('REtravelLat', xhrTravelLat);
                    localStorage.setItem('REtravelLong', xhrTravelLong);
                    travelName = data[last].name;
                    travelMode = true;
                    travelEdit = '<a href="/explore/edit" class="re-edit-travel icon icon-pen pl-" title="Reiseziel ändern"></a>';
                } else {
                    travelMode = false;
                    travelEdit = '<a href="/explore/new" class="re-edit-travel">Klicken, um ein Reiseziel hinzuzufügen!</a>';
                }
                $(toggleTravel).find('span').html(travelName).attr('title', travelName).addClass('pl-');
                $(toggleTravel).next('a.re-edit-travel').remove();
                $(toggleTravel).after(travelEdit);
                if (refreshClick) $(refreshClick).get(0).click();
            });

        }

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


// ***** Show picture info in slide show *****

//init
let imgNameOld = '';

function imgInfo (jNode) {

    //don't handle repeatedly within 500ms
	let imgName = $('div.swiper-slide-active img').attr('src');
    if (imgName === imgNameOld) return;

    //new picture or after 500ms
    imgNameOld = imgName;
    setTimeout(() => {
        imgNameOld = '';
    }, 500);

    //handle picture
    let jNodeSlide = $('div.swiper-slide-active img').closest('main');
    $(jNodeSlide).find('.re-add').remove();
	if (imgName?.match(/^\/img\//)) {
        let imgNameTxt = uploadDate(`${imgName.substr(imgName.lastIndexOf('/') + 1, 8)}`);
        info = `<a class="re-add" style="font-size:.8125rem; color:rgba(255,255,255,.5); cursor:default" title="Upload-Datum" href="${imgName}">${imgNameTxt}</a>`;
        $(jNodeSlide).children('section').children('div').first().children('button').after(info);
	}

    //report button
    $(jNodeSlide).children('section').children('div').first().children('p').attr('style', 'font-size:.9375rem');

    //URLs in picture caption
    setTimeout(() => {
        $('#metadata-bar div > div > p[class^="ResponsiveBodyText"]').first().each(function() {
            let replacedText = linkify($(this).text());
            $(this).html(replacedText);
        });
    }, 0);

    //localize picture date
    setTimeout(() => {
        let imgDate = $('#metadata-bar > div > div > p').last().text().trim();
        if (imgDate.match(/\d+\w{2} \w+ \d{4}/)) {
            let localeDate = new Date(imgDate.replace(/(^|: )(\d+)(\w{2}) (\w+ \d{4})/, '$2 $4')).toLocaleDateString();
            if (localeDate != 'Invalid Date') $('#metadata-bar > div > div > p').last().text(localeDate).attr('title', `${imgDate}`);
        }
    }, 0);
}


// ***** Remove refresh for slide show likes list *****
function handleSlideshowLikes (jNode) {
    $(jNode).closest('div').children('button + div, button').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,.8)' : 'rgba(255,255,255,.375)';
        $('#picture-rating button').has('p').after(
            `<a class="re-rating-date re-add" style="color:${color}" title="Upload-Datum" href="${imgName}">${imgDate}</a>`
        );
    }

    //set focus on reload button
    if (!touch.matches) {
        $('#picture-rating button[class^="SecondaryButton__Element-"]').focus();
    }
}


// ***** Relogin after timeout *****
function reLogin (jNode) {
    if ($(jNode).not('.re-add').addClass('re-add').children('span').filter(':contains("Erneut einloggen"), :contains("Log in again"), :contains("Reconnexion")').length) {
        $(jNode).closest('section').parent('div').addClass('re-relogin-frame');
        $(jNode).closest('section').find('div > button').hide();
        $(jNode).closest('section').find('h1').text('');
        $(jNode).closest('section').find('div > img').replaceWith('<div class="spinner-container"><div class="spinner"></div></div>');
        $(jNode).closest('section').children('p').text('Erneut einloggen ...');
        setTimeout(() => {
            if (location.pathname == '/visitors' || location.pathname.match(/\/messenger\/contacts/)) {
                location.replace('/');  // fix for broken profiles after reload when VISITORS or CONTACTS is selected
            } else {
                location.reload();
            }
        }, 600);
    } else {
        $(jNode).closest('div[class^="Modal__StyledModal"]').find('section p').off().on('dblclick', function() {
            $(this).closest('div[class^="Modal__StyledModal"]').remove();
            $('body').attr('class', '');
        });
    }
    //console.log('modal box');
}


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

//test
//localStorage.setItem('REpostFromDeleted', 'true')
localStorage.setItem('REtestMode', 'false')

//init
let xhrTravelLat = localStorage.getItem('REtravelLat');
let xhrTravelLong = localStorage.getItem('REtravelLong');
let travelMode = (localStorage.getItem('REradarTravelLocation') === 'travel');
let resigned = false;
let xhrFull = '';
let eyecandyActive = (localStorage.getItem('REeyecandyActive') === 'true');
let filterNoEntry = (localStorage.getItem('REfilterNoEntry') === 'true');
let postFromDeleted = (localStorage.getItem('REpostFromDeleted') === 'true');
let testMode = (localStorage.getItem('REtestMode') === 'true');


(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 (travelMode) {
            if (location.pathname.match(/^\/radar\//)) {
                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 >= 94500 && radius < 95500) {  //95
                url = url.replace(/filter%5Blocation%5D%5Bradius%5D\=[0-9\.]+/, `filter[location][radius]=${250000}`);
            }
            if (radius >= 95500 && radius < 96500) {  //96
                url = url.replace(/filter%5Blocation%5D%5Bradius%5D\=[0-9\.]+/, `filter[location][radius]=${500000}`);
            }
            if (radius >= 96500 && radius < 97500) {  //97
                url = url.replace(/filter%5Blocation%5D%5Bradius%5D\=[0-9\.]+/, `filter[location][radius]=${1000000}`);
            }
            if (radius >= 97500 && radius < 98500) {  //98
                url = url.replace(/filter%5Blocation%5D%5Bradius%5D\=[0-9\.]+/, `filter[location][radius]=${2000000}`);
            }
            if (radius >= 98500 && radius < 99500) {  //99
                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.match(/&scrollable=false/)) {
                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}`);
            }
            else if (!eyecandyActive && url.match(/&scrollable=false/)) {
                url = url.replace('sort_criteria=NEARBY_ASC', 'sort_criteria=LAST_LOGIN_DESC');
            }
            //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];
            localStorage.setItem('REtravelLat', xhrTravelLat);
            localStorage.setItem('REtravelLong', xhrTravelLong);

            //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}`);
        }

        //include "no entry" to filter
        if (filterNoEntry) {
            if (url.match(/\/profiles\?/)) {
                const categories = [
                    '&filter%5Bpersonal%5D%5Bgender_orientation%5D%5Bgender%5D%5B%5D=',
                    '&filter%5Bpersonal%5D%5Bgender_orientation%5D%5Borientation%5D%5B%5D=',
                    '&filter%5Bpersonal%5D%5Blooking_for%5D%5B%5D=',
                    '&filter%5Bpersonal%5D%5Bbody_type%5D%5B%5D=',
                    '&filter%5Bpersonal%5D%5Bbody_hair%5D%5B%5D=',
                    '&filter%5Bpersonal%5D%5Bhair_color%5D%5B%5D=',
                    '&filter%5Bpersonal%5D%5Bhair_length%5D%5B%5D=',
                    '&filter%5Bpersonal%5D%5Bethnicity%5D%5B%5D=',
                    '&filter%5Bsexual%5D%5Banal_position%5D%5B%5D=',
                    '&filter%5Bsexual%5D%5Bdick_size%5D%5B%5D=',
                    '&filter%5Bsexual%5D%5Bconcision%5D%5B%5D=',
                    '&filter%5Bsexual%5D%5Bsafer_sex%5D%5B%5D=',
                    '&filter%5Bsexual%5D%5Bfetish%5D%5B%5D=',
                    '&filter%5Bsexual%5D%5Bsm%5D%5B%5D=',
                    '&filter%5Bsexual%5D%5Bfisting%5D%5B%5D='
                ]
                for (let item of categories) {
                    url = url.replace(item, item + 'NO_ENTRY' + item)
                }
            }
        }

        //full profile
        if (url.match(/v4\/(hunqz\/)?profiles\/[-\w]+\/full\?/)) {
            this.addEventListener('load', function() {
                xhrFull = JSON.parse(this.response)
                //console.log('xhrFull');
            });
        }

        //show group posts of deleted users
        if (postFromDeleted) {
            if (url.match(/v4\/groups\/\d+\/posts\?/)) {
                this.addEventListener('load', function() {
                    try {
                        this.xhr = JSON.parse(this.response)
                        for (let item of this.xhr.items) {
                            if (item.deleted) {
                                //console.log(`deleted`);
                                delete item.deleted;
                                //item.edit_status = 'NO_EDIT';
                                item.content = `[Gelöschter Beitrag]\n${item.content}`;
                            }
                            if (item.owner.deletion_date) {
                                //console.log(`deletion_date`);
                                delete item.owner.deletion_date;
                                item.content = `[Profil gelöscht]\n\n${item.content}`;
                            }
                        }
                        Object.defineProperty(this, 'responseText', {
                            writable: true
                        });
                        this.responseText = JSON.stringify(this.xhr)
                    } catch(e) {
                    }
                });
            }
        }

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


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

//init
let 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 ('div[class*="Tile__BaseTile-"]', 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) {
            if (testMode) 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) {
            if (testMode) 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());
}

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

function makeRadio(el) {
    el.setAttribute("role", "radio");
    el.setAttribute("aria-checked", "false");
}

function makeRadioChecked(el) {
    el.setAttribute("role", "radio");
    el.setAttribute("aria-checked", "true");
}


/*** 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_ELEMENT_ADDED_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_ELEMENT_ADDED_TWEAKS);
                    //console.log('MO element node added');
                }
                /*for (let node of mutation.removedNodes) {
                    if (node.nodeType != Node.ELEMENT_NODE) {
                        continue;
                    }
                    applyTweaks(node, DYNAMIC_TWEAKS);
                }*/
/*             } else if (mutation.type === "attributes") {
                if (mutation.attributeName == "class") {
                    applyTweaks(mutation.target, DYNAMIC_ATTR_ONLY_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, subtree: true});

let observerAttr = new MutationObserver(function(mutations) {
    for (let mutation of mutations) {
        try {
            if (mutation.type === "attributes") {
                if (mutation.attributeName == "class") {
                    applyTweaks(mutation.target, DYNAMIC_ATTR_ONLY_TWEAKS);
                    //console.log('MO attr only');
                }
            }
            if (mutation.type === "childList") {
                applyTweaks(mutation.target, DYNAMIC_ANY_NODE_TWEAKS);
                //console.log('MO any node');
            }
        } catch (e) {
            // Catch exceptions for individual mutations so other mutations are still handled
            console.log(`Exception while handling mutation: ${e}`);
        }
    }
});
observerAttr.observe(document, {childList: true, subtree: true, attributes: true, attributeFilter: ["class"]});


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

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

// Tweaks to be applied whenever an element node is added
const DYNAMIC_ELEMENT_ADDED_TWEAKS = [

    //RomeoEnhancer (see also waitForKeyElements below)
    {selector: '#search input', tweak: searchInput},
    {selector: ':is(.layer-left-navigation, header) li', tweak: handleMainMenu},
    {selector: 'section.js-main-stage ul[class^="Tabbed-nav-"] li.is-selected', tweak: handleTabMenu},
    {selector: '#offcanvas-nav div[class^="Version"], div.js-filter-button button', tweak: handleVersionBookmark},
    {selector: '#messenger:not(.is-hidden) div[class^="Box"] div p[class^="SpecialText"]', tweak: previewMessage},
    {selector: '#messenger div:is(.js-correspondence, .js-header) div.reactView div > a > p', tweak: showLoginLocation},
    {selector: '.js-profile-footprints h1', tweak: handleFootprints},
    {selector: '.js-quick-filter .js-anal-position > div', tweak: handleFilterNoEntry},
    {selector: '#messenger div.js-chat .reactView a[href^="/messenger/chat"]', tweak: handleMessage},
    {selector: '#messenger div.js-contacts .reactView a[href^="/messenger/contacts"]', tweak: handleContacts},
    {selector: ':is(div.js-wrapper, div.js-admins, #group-preview) a.js-contact', tweak: handleContactStrip},
    {selector: '.js-wrapper a[href^="/eyecandy"] h2', tweak: handleDiscoverEyecandy},
    {selector: ':is(div.js-wrapper, #visits-received, #visits-made, #search, .js-profiles) :is(a div.BIG, a div.SMALL, button div.LIST) div > span[class^="sc-"]', tweak: handleTiles},
    {selector: '#profiles :is(a div.BIG, a div.SMALL, button div.LIST) div > span[class^="sc-"]', tweak: handleRadar},
    {selector: 'div.stream__content a.listitem__body .js-username p, div.stream__content div.js-list', tweak: handleStream},
    {selector: 'section.profile__stats section > p[class^="BaseText"], section.profile__stats ol', tweak: handleProfile},
    {selector: '#manage div[class^="FilterBar__Container"] > div > div', tweak: groupManageMode},
    {selector: 'div.js-members-header div.js-dropdown button', tweak: groupTravelLocation},
    {selector: '#liked-by-grid', tweak: handleSlideshowLikes},
    {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: 'section[class^="PopupWindow__Content"] button[class^="PrimaryButton__Element"]', tweak: reLogin},

    //accessibility: 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: '.ui-status--date', tweak: [makeIcon, "Date"]},
    {selector: '.ui-status--sex', tweak: [makeIcon, "Now"]},
    {selector: '.icon-airplane', tweak: [makeIcon, "Travel"]},
    {selector: '.icon-save-contact', tweak: [makeIcon, "Kontakte"]},
    {selector: '.js-nav-item .icon-group-members', tweak: [makeIcon, "Meine Gruppen"]},

    //accessibility: profile screen
    {selector: '.profile--romeo', tweak: [makeRegion, "Romeo-Profil"]},
    {selector: '.profile--hunqz', tweak: [makeRegion, "Hunqz-Profil"]},
    {selector: '.icon-add-footprint', tweak: [makeButton, "Fußtaps vergeben"]},
    {selector: '.js-remove-footprint', tweak: [setLabelFromTitle]},
    {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--"], button.js-close-button, .js-close-icon button', tweak: [setLabel, "Schließen"]},
    {selector: '.js-close-spotlight', tweak: [makeButton, "Schließen"]},
    {selector: '.js-hide.js-plus', tweak: [setLabel, "Profilbesuch verstecken"]},
    {selector: '.profile-section .reactView p[class^="BodyText"]', tweak: [makeHeading, "3"]},
    {selector: '.top-info-header p[class^="BodyText"]', tweak: [makeHeading, "1"]},
    {selector: 'button[class^="CollapsibleSection"] > p', tweak: [setLabelFromChildText]},

    //accessibility: other
    {selector: '.messages-send__select-button.messages-button-react-view', tweak: [setLabel, "Templates, Standort, Bilder senden"]},
    {selector: ':not(.js-nav-item) > .icon-group-members', tweak: [makeIcon, "Gruppe"]},
    {selector: '.js-settings-privacy div[class*="Radio-"]', tweak: [makeRadio]},
    {selector: '.js-settings-privacy div[class*="Selected-"] div[class*="Radio-"]', tweak: [makeRadioChecked]},
]

// Tweaks to be applied on any node changes
const DYNAMIC_ANY_NODE_TWEAKS = [

    //RomeoEnhancer
    {selector: 'div.js-correspondence div.js-header-region div[class*="ContextMenu__"] ul', tweak: threadOptionsMenu},
    {selector: ':is(nav.WGgGs, nav.fXsTKd):not(.re-groups-def)', tweak: recentPosts},
    {selector: ':is(.js-post-list, .js-post) .js-date span', tweak: handleGroupPostDate},
    // {selector: 'div[class*="withNav-"] > div[class*="ReactContainer-"]', tweak: handleMobileGroupsNav},
]

// Tweaks to be applied on attribute changes
const DYNAMIC_ATTR_ONLY_TWEAKS = [

    //RomeoEnhancer
    {selector: '#messenger:not(.is-hidden) div[class^="Box"] div p[class^="SpecialText"]', tweak: previewMessage},
    {selector: 'div.swiper-slide.swiper-slide-active div.swiper-zoom-container, #metadata-bar p', tweak: imgInfo},
]

initMutationObserver();



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

waitForKeyElements ('div#marionette.is-logged-in', runAtLogin, true);
waitForKeyElements ('div.js-chat .js-scrollable div[class="js-paging-spinner spinner-container l-fancy"]', fixScroll, true);