Files
WP-Business-Forum/assets/js/forum-script.js
2026-03-22 00:40:17 +01:00

2117 lines
102 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function ($) {
'use strict';
/* ── Utilities ──────────────────────────────────────────────── */
function wbfPost(action, data, cb, errCb) {
data.action = action;
data.nonce = WBF.nonce;
$.post(WBF.ajax_url, data, function (res) {
if (res && res.success) cb(res.data);
else if (errCb) errCb(res ? res.data : {message: 'Server-Fehler'});
}, 'json').fail(function(xhr) {
if (errCb) errCb({message: 'Verbindungsfehler (' + xhr.status + ')'});
});
}
function showMsg($el, msg, ok) {
$el.text(msg).css('color', ok ? '#56cf7e' : '#f05252').show();
setTimeout(function () { $el.fadeOut(); }, 4000);
}
/* ── Auth Tabs ──────────────────────────────────────────────── */
$(document).on('click', '.wbf-auth-tab', function () {
var tab = $(this).data('tab');
$('.wbf-auth-tab').removeClass('active');
$(this).addClass('active');
$('.wbf-auth-panel').removeClass('active');
$('[data-panel="' + tab + '"]').addClass('active');
});
/* ── Auth Modal öffnen ──────────────────────────────────────── */
$(document).on('click', '#wbfOpenLogin, .wbf-login-link', function (e) {
e.preventDefault();
$('[data-tab="login"]').trigger('click');
$('#wbfAuthModal').addClass('active');
});
$(document).on('click', '#wbfOpenRegister, .wbf-register-link', function (e) {
e.preventDefault();
$('[data-tab="register"]').trigger('click');
$('#wbfAuthModal').addClass('active');
});
/* ── Modal schließen bei Klick außerhalb ────────────────────── */
$(document).on('click', '.wbf-modal', function (e) {
if ($(e.target).hasClass('wbf-modal')) $(this).removeClass('active');
});
/* ── Login ──────────────────────────────────────────────────── */
$(document).on('keydown', '.wbf-field-password', function (e) {
if (e.key === 'Enter') $(this).closest('.wbf-auth-box').find('.wbf-login-submit-btn').trigger('click');
});
/* ── Registrieren ───────────────────────────────────────────── */
$(document).on('click', '.wbf-reg-submit-btn', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
wbfPost('wbf_register', {
username: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-user').val(),
display_name: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-name').val(),
email: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-email').val(),
password: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-pass').val(),
invite_code: $(this).closest('.wbf-auth-box').find('.wbf-field-invite-code').val().toUpperCase().trim(),
rules_accepted: $(this).closest('.wbf-auth-box').find('.wbf-field-rules-accept').is(':checked') ? '1' : ''
}, function () {
location.reload();
}, function (d) {
showMsg($btn.closest('.wbf-auth-box').find('.wbf-reg-msg'), d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-user-plus"></i> Konto erstellen');
});
});
/* ── Logout — direkt via Link (?wbf_do_logout=1) ───────────── */
// Kein JS nötig — Logout-Button ist jetzt ein <a href> Link
/* ── Neuer Thread Modal ─────────────────────────────────────── */
window.wbfShowNewThread = function (catId) {
if (WBF.logged_in !== 'yes') { $('#wbfAuthModal').addClass('active'); return; }
if (catId) $('#wbfThreadCat').val(catId);
// Thread mode: alle Felder sichtbar
$('#wbfModalTitle').html('<i class="fas fa-plus-circle"></i> Neuen Thread erstellen');
$('#wbfThreadTitle').attr('placeholder', 'Titel deines Threads');
$('#wbfContentRow, #wbfTagsRow').show();
$('#wbfPollSection').hide();
$('#wbfThreadSubmitRow').show();
$('#wbfNewThreadModal').addClass('active');
};
window.wbfShowNewPoll = function (catId) {
if (WBF.logged_in !== 'yes') { $('#wbfAuthModal').addClass('active'); return; }
if (catId) $('#wbfThreadCat').val(catId);
// Poll mode: Inhalt + Tags ausblenden
$('#wbfModalTitle').html('<i class="fas fa-chart-bar" style="color:#fbbf24"></i> Neue Umfrage erstellen');
$('#wbfThreadTitle').attr('placeholder', 'Titel der Umfrage');
$('#wbfContentRow, #wbfTagsRow').hide();
$('#wbfThreadSubmitRow').hide();
$('#wbfPollSection').show();
$('#wbfNewThreadModal').addClass('active');
setTimeout(function() {
$('#wbfPollSection')[0].scrollIntoView({behavior:'smooth', block:'nearest'});
}, 150);
};
// Show poll section → Inhalt+Tags ausblenden
$(document).on('click', '#wbfShowPollSection', function () {
$('#wbfModalTitle').html('<i class="fas fa-chart-bar" style="color:#fbbf24"></i> Neue Umfrage erstellen');
$('#wbfThreadTitle').attr('placeholder', 'Titel der Umfrage');
$('#wbfContentRow, #wbfTagsRow').hide();
$('#wbfThreadSubmitRow').hide();
$('#wbfPollSection').show();
});
// Entfernen → zurück zu Thread-Modus
$(document).on('click', '#wbfRemovePollSection', function () {
$('#wbfModalTitle').html('<i class="fas fa-plus-circle"></i> Neuen Thread erstellen');
$('#wbfThreadTitle').attr('placeholder', 'Titel deines Threads');
$('#wbfContentRow, #wbfTagsRow').show();
$('#wbfPollSection').hide();
$('#wbfThreadSubmitRow').show();
// Poll-Felder zurücksetzen
$('#wbfNewThreadPollQuestion').val('');
$('#wbfNTPollEndsAt').val('');
$('#wbfNTPollMulti').prop('checked', false);
$('#wbfNewThreadPollOptions .wbf-poll-opt-row').slice(2).remove();
$('#wbfNewThreadPollOptions .wbf-nt-poll-opt').val('');
});
// Poll option add/remove for new-thread poll
window.wbfRemoveNTPollOpt = function(btn) {
var $rows = $('#wbfNewThreadPollOptions .wbf-poll-opt-row');
if ($rows.length <= 2) return;
$(btn).closest('.wbf-poll-opt-row').remove();
};
$(document).on('click', '#wbfNTPollAddOpt', function () {
var count = $('#wbfNewThreadPollOptions .wbf-poll-opt-row').length;
if (count >= 10) return;
var n = count + 1;
$('#wbfNewThreadPollOptions').append(
'<div class="wbf-poll-opt-row">' +
'<input type="text" class="wbf-nt-poll-opt" placeholder="Option ' + n + '" maxlength="100">' +
'<button type="button" class="wbf-btn wbf-btn--sm" ' +
'style="background:rgba(240,82,82,.1);color:var(--c-danger);border-color:rgba(240,82,82,.3);min-width:32px;flex-shrink:0" ' +
'onclick="wbfRemoveNTPollOpt(this)">✕</button></div>'
);
});
$(document).on('click', '#wbfSubmitThread', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var data = {
category_id: $('#wbfThreadCat').val(),
prefix_id: $('#wbfThreadPrefix').val() || '',
title: $('#wbfThreadTitle').val(),
content: $('#wbfThreadContent').val(),
tags: $('#wbfThreadTags').val()
};
wbfPost('wbf_new_thread', data, function (d) {
var base = WBF.forum_url || window.location.href.split('?')[0];
var sep = base.indexOf('?') !== -1 ? '&' : '?';
window.location.href = base + sep + 'forum_thread=' + d.thread_id;
}, function (d) {
showMsg($('#wbfThreadMsg'), d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Thread erstellen');
});
});
/* ── Umfrage erstellen (eigener Submit-Button) ──────────────────────── */
$(document).on('click', '#wbfSubmitPollThread', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $msg = $('#wbfPollThreadMsg');
var data = {
category_id: $('#wbfThreadCat').val(),
prefix_id: $('#wbfThreadPrefix').val() || '',
title: $('#wbfThreadTitle').val(),
content: $('#wbfThreadContent').val(),
tags: $('#wbfThreadTags').val(),
poll_question: $('#wbfNewThreadPollQuestion').val().trim(),
poll_multi: $('#wbfNTPollMulti').is(':checked') ? '1' : '',
poll_ends_at: $('#wbfNTPollEndsAt').val()
};
var optIdx = 0;
$('#wbfNewThreadPollOptions .wbf-nt-poll-opt').each(function () {
var v = $(this).val().trim();
if (v) { data['poll_options[' + optIdx + ']'] = v; optIdx++; }
});
if (!data.poll_question) {
showMsg($msg, 'Bitte gib eine Umfrage-Frage ein.', false);
$btn.prop('disabled', false).html('<i class="fas fa-chart-bar"></i> Umfrage erstellen');
return;
}
if (optIdx < 2) {
showMsg($msg, 'Mindestens 2 Antwortmöglichkeiten erforderlich.', false);
$btn.prop('disabled', false).html('<i class="fas fa-chart-bar"></i> Umfrage erstellen');
return;
}
wbfPost('wbf_new_thread', data, function (d) {
var base = WBF.forum_url || window.location.href.split('?')[0];
var sep = base.indexOf('?') !== -1 ? '&' : '?';
window.location.href = base + sep + 'forum_thread=' + d.thread_id;
}, function (d) {
showMsg($msg, d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-chart-bar"></i> Umfrage erstellen');
});
});
/* ── Poll Option hinzufügen / entfernen ─────────────────────────── */
window.wbfRemovePollOpt = function(btn) {
var $rows = $('#wbfPollOptions .wbf-poll-opt-row');
if ($rows.length <= 2) return; // min. 2 bleiben
$(btn).closest('.wbf-poll-opt-row').remove();
};
$(document).on('click', '#wbfPollAddOpt', function () {
var count = $('#wbfPollOptions .wbf-poll-opt-row').length;
if (count >= 10) return;
var n = count + 1;
$('#wbfPollOptions').append(
'<div class="wbf-poll-opt-row">' +
'<input type="text" class="wbf-poll-opt" placeholder="Option ' + n + '" maxlength="100">' +
'<button type="button" class="wbf-btn wbf-btn--sm" ' +
'style="background:rgba(240,82,82,.1);color:var(--c-danger);border-color:rgba(240,82,82,.3);min-width:32px;flex-shrink:0" ' +
'onclick="wbfRemovePollOpt(this)">✕</button></div>'
);
});
/* ── Poll erstellen (separates Modal) ───────────────────────────── */
$(document).on('click', '#wbfSubmitPoll', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $msg = $('#wbfPollMsg');
var opts = [];
$('.wbf-poll-opt').each(function () {
var v = $(this).val().trim();
if (v) opts.push(v);
});
var data = {
thread_id: $('#wbfPollThreadId').val(),
poll_question: $('#wbfPollQuestion').val(),
poll_multi: $('#wbfPollMulti').is(':checked') ? '1' : '',
poll_ends_at: $('#wbfPollEndsAt').val()
};
$.each(opts, function (i, v) { data['poll_options[' + i + ']'] = v; });
wbfPost('wbf_create_poll', data, function (d) {
showMsg($msg, d.message, true);
setTimeout(function () { location.reload(); }, 1200);
}, function (d) {
showMsg($msg, d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-chart-bar"></i> Umfrage erstellen');
});
});
/* ── Poll Abstimmen ─────────────────────────────────────────────── */
$(document).on('submit', '.wbf-poll__form', function (e) {
e.preventDefault();
var $form = $(this);
var pollId = $form.data('poll-id');
var multi = $form.closest('.wbf-poll').data('multi');
var $btn = $form.find('button[type="submit"]').prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $msg = $form.find('.wbf-poll__msg');
var selected = [];
if (multi) {
$form.find('input[type="checkbox"]:checked').each(function () { selected.push($(this).val()); });
} else {
var val = $form.find('input[type="radio"]:checked').val();
if (val !== undefined) selected.push(val);
}
if (!selected.length) {
showMsg($msg, 'Bitte eine Option wählen.', false);
$btn.prop('disabled', false).html('<i class="fas fa-vote-yea"></i> Abstimmen');
return;
}
var postData = { poll_id: pollId };
$.each(selected, function (i, v) { postData['options[' + i + ']'] = v; });
wbfPost('wbf_vote_poll', postData, function (d) {
// Formular durch Ergebnisse ersetzen
var html = wbfRenderPollResults(d.results, d.my_votes, d.total, $form.closest('.wbf-poll').find('.wbf-poll__header'));
$form.replaceWith(html);
}, function (d) {
showMsg($msg, d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-vote-yea"></i> Abstimmen');
});
});
function wbfRenderPollResults(results, myVotes, total, $header) {
var options = [];
$header.closest('.wbf-poll').find('.wbf-poll__form label').each(function () {
options.push($(this).text().trim());
});
var html = '';
$.each(options, function (i, label) {
var votes = results[i] || 0;
var pct = total > 0 ? Math.round(votes / total * 100) : 0;
var mine = myVotes.indexOf(i) !== -1 || myVotes.indexOf('' + i) !== -1;
html += '<div class="wbf-poll__result' + (mine ? ' wbf-poll__result--mine' : '') + '">' +
'<div class="wbf-poll__result-bar" style="width:' + pct + '%"></div>' +
'<div class="wbf-poll__result-content">' +
'<span class="wbf-poll__result-label">' + (mine ? '<i class="fas fa-check-circle" style="color:var(--c-primary)"></i> ' : '') + label + '</span>' +
'<span class="wbf-poll__result-pct">' + pct + '% <span style="color:var(--c-muted);font-size:.75em">(' + votes + ')</span></span>' +
'</div></div>';
});
html += '<div class="wbf-poll__footer"><i class="fas fa-users"></i> ' + total + ' Stimme' + (total !== 1 ? 'n' : '') + ' · <i class="fas fa-flag-checkered"></i> Abgestimmt</div>';
return html;
}
/* ══════════════════════════════════════════════════════════
FEATURE: Tag-Input Widget
══════════════════════════════════════════════════════════ */
var wbfTagList = []; // aktuell ausgewählte Tags
var wbfTagTimer = null;
function wbfUpdateTagHidden() {
$('#wbfThreadTags').val(wbfTagList.join(', '));
}
function wbfAddTag(name) {
name = name.replace(/^#+/, '').trim().toLowerCase();
if (!name || name.length < 2 || name.length > 30) return;
if (wbfTagList.length >= 10) { return; }
if (wbfTagList.indexOf(name) !== -1) return;
wbfTagList.push(name);
wbfUpdateTagHidden();
wbfRenderTagPills();
$('#wbfTagInput').val('');
$('#wbfTagSuggest').hide().empty();
}
function wbfRemoveTag(name) {
wbfTagList = wbfTagList.filter(function(t){ return t !== name; });
wbfUpdateTagHidden();
wbfRenderTagPills();
}
function wbfRenderTagPills() {
var $pills = $('#wbfTagPills');
$pills.empty();
wbfTagList.forEach(function(tag) {
$pills.append(
$('<span class="wbf-tag-pill"></span>')
.text('#' + tag)
.append($('<button type="button">×</button>').on('click', function(){ wbfRemoveTag(tag); }))
);
});
// Show/hide count hint
var remaining = 10 - wbfTagList.length;
if (remaining <= 3 && remaining > 0) {
$pills.append('<span class="wbf-tag-pill-hint">' + remaining + ' übrig</span>');
}
}
// Keydown on tag input
$(document).on('keydown', '#wbfTagInput', function(e) {
var val = $(this).val().trim();
if (e.key === 'Enter' || e.key === ',' || e.key === ' ') {
e.preventDefault();
if (val) wbfAddTag(val);
} else if (e.key === 'Backspace' && !val && wbfTagList.length) {
wbfRemoveTag(wbfTagList[wbfTagList.length - 1]);
}
});
// Autocomplete on input
$(document).on('input', '#wbfTagInput', function() {
var q = $(this).val().replace(/^#+/, '').trim();
clearTimeout(wbfTagTimer);
if (q.length < 1) { $('#wbfTagSuggest').hide().empty(); return; }
wbfTagTimer = setTimeout(function() {
wbfPost('wbf_tag_suggest', { q: q }, function(d) {
var $s = $('#wbfTagSuggest');
if (!d.tags || !d.tags.length) { $s.hide().empty(); return; }
$s.empty();
d.tags.forEach(function(tag) {
if (wbfTagList.indexOf(tag.slug) !== -1) return;
$s.append(
$('<div class="wbf-tag-suggest-item"></div>')
.html('<i class="fas fa-hashtag"></i>' + $('<span>').text(tag.name).html() +
'<span class="wbf-tag-suggest-count">' + tag.use_count + '</span>')
.on('click', function(){ wbfAddTag(tag.name); })
);
});
$s.show();
});
}, 200);
});
// Click on tag input wrap focuses input
$(document).on('click', '#wbfTagInputWrap', function(e) {
if (!$(e.target).is('button')) $('#wbfTagInput').focus();
});
// Reset tags when modal closes
$(document).on('click', '#wbfNewThreadModal .wbf-modal__close, #wbfNewThreadModal', function(e) {
if ($(e.target).hasClass('wbf-modal') || $(e.target).hasClass('wbf-modal__close')) {
wbfTagList = [];
wbfRenderTagPills();
$('#wbfTagInput').val('');
$('#wbfTagSuggest').hide();
}
});
// Hide suggest on outside click
$(document).on('click', function(e) {
if (!$(e.target).closest('#wbfTagInputWrap').length) {
$('#wbfTagSuggest').hide();
}
});
/* ── Antwort senden ─────────────────────────────────────────── */
$(document).on('input', '#wbfReplyContent', function () {
$('#wbfReplyCounter').text($(this).val().length + ' Zeichen');
});
$(document).on('click', '#wbfSubmitReply', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var threadId = $(this).data('thread');
wbfPost('wbf_new_post', {
thread_id: threadId,
content: $('#wbfReplyContent').val()
}, function (d) {
$('#wbfPosts').append(d.html);
$('#wbfReplyContent').val('');
$('#wbfReplyCounter').text('0 Zeichen');
$btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Antwort senden');
$('html, body').animate({ scrollTop: $('#post-' + d.post_id).offset().top - 80 }, 400);
$(document).trigger('wbf:post_added', [d.post_id]);
}, function (d) {
alert(d.message || 'Fehler beim Senden.');
$btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Antwort senden');
});
});
/* ── Like ───────────────────────────────────────────────────── */
$(document).on('click', '.wbf-like-btn', function () {
if (WBF.logged_in !== 'yes') {
$('#wbfAuthModal').addClass('active');
return;
}
var $btn = $(this);
wbfPost('wbf_toggle_like', {
object_id: $btn.data('id'),
object_type: $btn.data('type')
}, function (d) {
$btn.find('.wbf-like-count').text(d.count);
$btn.toggleClass('wbf-liked', d.action === 'liked');
});
});
/* ── Mod-Aktionen ───────────────────────────────────────────── */
$(document).on('click', '.wbf-mod-btn', function () {
var action = $(this).data('action');
var id = $(this).data('id');
var $btn = $(this);
// Move modal — open inline, no AJAX yet
if (action === 'open_move') {
$('#wbfMoveThreadId').val(id);
$('#wbfMoveMsg').hide();
$('#wbfMoveModal').addClass('active');
return;
}
var confirmMsgs = {
'delete_thread': 'Thread wirklich löschen? Alle Antworten werden entfernt.',
'delete_post': 'Post wirklich löschen?',
'archive_thread': 'Thread archivieren? Er wird aus der Kategorie ausgeblendet.'
};
if (confirmMsgs[action] && !confirm(confirmMsgs[action])) return;
$btn.prop('disabled', true).css('opacity', '.5');
wbfPost('wbf_mod_action', {
mod_action: action,
object_id: id
}, function (d) {
if (d.action === 'deleted') {
window.location.href = window.location.pathname;
} else if (d.action === 'archived') {
// Redirect back — page.reload keeps the archived thread visible until refresh
history.back();
} else if (d.action === 'post_deleted') {
$('#post-' + id).fadeOut(300, function () { $(this).remove(); });
} else {
location.reload();
}
}, function (d) {
alert(d.message || 'Aktion fehlgeschlagen.');
$btn.prop('disabled', false).css('opacity', '1');
});
});
/* ── Thread verschieben ─────────────────────────────────────── */
$(document).on('click', '#wbfSubmitMove', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var threadId = $('#wbfMoveThreadId').val();
var catId = $('#wbfMoveCatSelect').val();
wbfPost('wbf_move_thread', {
thread_id: threadId,
category_id: catId
}, function (d) {
showMsg($('#wbfMoveMsg'), '✔ ' + (d.message || 'Verschoben!'), true);
$btn.prop('disabled', false).html('<i class="fas fa-right-left"></i> Verschieben');
setTimeout(function () {
$('#wbfMoveModal').removeClass('active');
location.reload();
}, 1200);
}, function (d) {
showMsg($('#wbfMoveMsg'), d.message || 'Fehler.', false);
$btn.prop('disabled', false).html('<i class="fas fa-right-left"></i> Verschieben');
});
});
/* ── Profil speichern ───────────────────────────────────────── */
$(document).on('click', '#wbfSaveProfile, #wbfSaveProfileCf', function () {
var $btn = $(this).prop('disabled', true);
var $msg = $(this).siblings('.wbf-msg').length ? $(this).siblings('.wbf-msg') : $('#wbfProfileMsg');
var data = {
display_name: $('#wbfEditName').val(),
bio: $('#wbfEditBio').val(),
signature: $('#wbfEditSignature').val(),
new_password: $('#wbfNewPassword').val()
};
// Benutzerdefinierte Profilfelder einsammeln
$('.wbf-cf-input').each(function () {
data[$(this).data('field')] = $(this).val();
});
wbfPost('wbf_update_profile', data, function (d) {
showMsg($msg, d.message, true);
$btn.prop('disabled', false);
}, function (d) {
showMsg($msg, d.message || 'Fehler', false);
$btn.prop('disabled', false);
});
});
/* ── Signatur Zeichenzähler ─────────────────────────────────── */
$(document).on('input', '#wbfEditSignature', function () {
$('#wbfSigCount').text($(this).val().length);
});
/* ── Avatar Upload ──────────────────────────────────────────── */
$(document).on('change', '#wbfAvatarFile', function () {
var file = this.files[0];
if (!file) return;
var fd = new FormData();
fd.append('action', 'wbf_upload_avatar');
fd.append('nonce', WBF.nonce);
fd.append('avatar', file);
$.ajax({
url: WBF.ajax_url,
type: 'POST',
data: fd,
processData: false,
contentType: false,
success: function (res) {
if (res.success) {
$('.wbf-profile-page__avatar').attr('src', res.data.avatar_url);
}
}
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Ungelesene Beiträge
══════════════════════════════════════════════════════════ */
var WBF_STORAGE_KEY = 'wbf_last_seen';
function wbfGetLastSeen() {
try { return JSON.parse(localStorage.getItem(WBF_STORAGE_KEY) || '{}'); } catch (e) { return {}; }
}
function wbfSetLastSeen(data) {
try { localStorage.setItem(WBF_STORAGE_KEY, JSON.stringify(data)); } catch (e) {}
}
function wbfMarkNewThreads() {
var lastSeen = wbfGetLastSeen();
$('.wbf-thread-row[data-thread-id]').each(function () {
var $row = $(this);
var threadId = $row.data('thread-id').toString();
var lastReply = $row.data('last-reply');
if (!lastReply) return;
var seenAt = lastSeen[threadId];
var isNew = !seenAt || new Date(lastReply) > new Date(seenAt);
if (isNew) {
$row.addClass('wbf-thread-row--has-new');
$row.find('.wbf-new-badge').show();
}
});
}
function wbfMarkNewPosts() {
var $container = $('[data-thread-view]');
if (!$container.length) return;
var threadId = $container.data('thread-view').toString();
var lastSeen = wbfGetLastSeen();
var seenAt = lastSeen[threadId];
$('.wbf-post[data-created]').each(function () {
var created = $(this).data('created');
if (!created) return;
if (seenAt && new Date(created) > new Date(seenAt)) {
$(this).addClass('wbf-post--new');
}
});
lastSeen[threadId] = new Date().toISOString();
wbfSetLastSeen(lastSeen);
}
$(function () {
wbfMarkNewThreads();
wbfMarkNewPosts();
});
$(document).on('wbf:post_added', function (e, postId) {
$('#post-' + postId).addClass('wbf-post--new');
var $container = $('[data-thread-view]');
if (!$container.length) return;
var threadId = $container.data('thread-view').toString();
var lastSeen = wbfGetLastSeen();
lastSeen[threadId] = new Date().toISOString();
wbfSetLastSeen(lastSeen);
});
/* ══════════════════════════════════════════════════════════
FEATURE: Editor-Toolbar — Bold, Italic, Bild, Smilies
══════════════════════════════════════════════════════════ */
function wbfInsertAtCursor($ta, text) {
var el = $ta[0];
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + text + val.slice(end);
el.selectionStart = el.selectionEnd = start + text.length;
$ta.trigger('input');
el.focus();
}
function wbfWrapSelection($ta, before, after) {
var el = $ta[0];
var sel = el.value.slice(el.selectionStart, el.selectionEnd) || 'Text';
wbfInsertAtCursor($ta, before + sel + after);
}
function wbfGetTargetTextarea($toolbar) {
return $('#' + $toolbar.data('target'));
}
// ── BBCode-Wrap-Helfer ────────────────────────────────────────
function wbfBBWrap($ta, tag) {
var el = $ta[0];
var sel = el.value.slice(el.selectionStart, el.selectionEnd) || 'Text';
wbfInsertAtCursor($ta, '[' + tag + ']' + sel + '[/' + tag + ']');
}
$(document).on('click', '.wbf-tb-btn', function (e) {
e.preventDefault();
e.stopPropagation();
var $btn = $(this);
var action = $btn.data('action');
var $toolbar = $btn.closest('.wbf-editor-toolbar');
var $ta = wbfGetTargetTextarea($toolbar);
// ── Standard BBCode wrap [tag]sel[/tag] ───────────────────
if (action === 'bb') {
var bb = $btn.data('bb');
if (bb === 'hr') {
wbfInsertAtCursor($ta, '\n[hr]\n');
} else {
wbfBBWrap($ta, bb);
}
// ── Dropdown-Toggle ───────────────────────────────────────
} else if (action === 'dropdown') {
var panelId = 'wbf-' + $btn.data('target') + '-panel-' + $toolbar.data('target');
var $panel = $('#' + panelId);
var wasOpen = $panel.is(':visible');
$('.wbf-tb-dropdown__panel').hide();
if (!wasOpen) $panel.show();
// ── Farbe ─────────────────────────────────────────────────
} else if (action === 'bb-color') {
var color = $btn.data('color');
var el = $ta[0];
var sel = el.value.slice(el.selectionStart, el.selectionEnd) || 'Text';
wbfInsertAtCursor($ta, '[color=' + color + ']' + sel + '[/color]');
$('.wbf-tb-dropdown__panel').hide();
// ── Größe ─────────────────────────────────────────────────
} else if (action === 'bb-size') {
var size = $btn.data('size');
var el2 = $ta[0];
var sel2 = el2.value.slice(el2.selectionStart, el2.selectionEnd) || 'Text';
wbfInsertAtCursor($ta, '[size=' + size + ']' + sel2 + '[/size]');
$('.wbf-tb-dropdown__panel').hide();
// ── Liste ─────────────────────────────────────────────────
} else if (action === 'bb-list') {
var ordered = $btn.data('ordered') ? '=1' : '';
wbfInsertAtCursor($ta, '[list' + ordered + ']\n[*] Punkt 1\n[*] Punkt 2\n[/list]');
// ── Spoiler ───────────────────────────────────────────────
} else if (action === 'bb-spoiler') {
var title = prompt('Spoiler-Titel (optional):', '') || '';
var el3 = $ta[0];
var sel3 = el3.value.slice(el3.selectionStart, el3.selectionEnd) || 'Inhalt hier';
var tag3 = title ? '[spoiler=' + title + ']' : '[spoiler]';
wbfInsertAtCursor($ta, tag3 + sel3 + '[/spoiler]');
// ── Link ──────────────────────────────────────────────────
} else if (action === 'bb-url') {
var href = prompt('URL eingeben:', 'https://');
if (href && href.length > 7) {
var el4 = $ta[0];
var sel4 = el4.value.slice(el4.selectionStart, el4.selectionEnd) || href;
wbfInsertAtCursor($ta, '[url=' + href + ']' + sel4 + '[/url]');
}
// ── Bild-URL ──────────────────────────────────────────────
} else if (action === 'bb-img') {
var imgUrl = prompt('Bild-URL eingeben:', 'https://');
if (imgUrl && imgUrl.length > 8) {
wbfInsertAtCursor($ta, '[img]' + imgUrl + '[/img]');
}
// ── Bild hochladen ────────────────────────────────────────
} else if (action === 'img-upload') {
$btn.find('.wbf-img-file-input').trigger('click');
// ── Emoji-Picker ──────────────────────────────────────────
} else if (action === 'emoji') {
var $picker = $toolbar.find('.wbf-emoji-picker');
var wasOpen2 = $picker.is(':visible');
$('.wbf-emoji-picker, .wbf-tb-dropdown__panel').hide();
if (!wasOpen2) $picker.show();
}
});
// Dropdown-Panel schließen bei Klick außerhalb
$(document).on('click', function (e) {
if (!$(e.target).closest('.wbf-tb-dropdown, .wbf-tb-dropdown__panel').length) {
$('.wbf-tb-dropdown__panel').hide();
}
});
/* ── Bild hochladen ─────────────────────────────────────────── */
$(document).on('change', '.wbf-img-file-input', function () {
var file = this.files[0];
if (!file) return;
var $input = $(this);
var $btn = $input.closest('.wbf-tb-btn');
var $toolbar = $input.closest('.wbf-editor-toolbar');
var $ta = wbfGetTargetTextarea($toolbar);
$btn.addClass('wbf-tb-btn--loading');
var $prog = $toolbar.find('.wbf-tb-upload-progress');
if (!$prog.length) {
$prog = $('<span class="wbf-tb-upload-progress"><i class="fas fa-spinner fa-spin"></i> Wird hochgeladen…</span>');
$toolbar.append($prog);
}
$prog.addClass('active');
var fd = new FormData();
fd.append('action', 'wbf_upload_post_image');
fd.append('nonce', WBF.nonce);
fd.append('image', file);
$.ajax({
url: WBF.ajax_url,
type: 'POST',
data: fd,
processData: false,
contentType: false,
success: function (res) {
if (res.success) {
wbfInsertAtCursor($ta, '[img]' + res.data.url + '[/img]');
} else {
alert(res.data.message || 'Fehler beim Hochladen.');
}
},
error: function () { alert('Verbindungsfehler beim Hochladen.'); },
complete: function () {
$btn.removeClass('wbf-tb-btn--loading');
$prog.removeClass('active');
$input.val('');
}
});
});
/* ── Emoji einfügen ─────────────────────────────────────────── */
$(document).on('click', '.wbf-emoji-item', function (e) {
e.stopPropagation();
var $toolbar = $(this).closest('.wbf-editor-toolbar');
wbfInsertAtCursor(wbfGetTargetTextarea($toolbar), $(this).data('emoji'));
$toolbar.find('.wbf-emoji-picker').hide();
});
$(document).on('click', function (e) {
if (!$(e.target).closest('.wbf-editor-toolbar').length) {
$('.wbf-emoji-picker').hide();
}
});
/* ══════════════════════════════════════════════════════════
FEATURE: Beitrag melden (auch eigene)
══════════════════════════════════════════════════════════ */
$(document).on('click', '.wbf-report-btn', function (e) {
e.preventDefault();
if (WBF.logged_in !== 'yes') {
$('#wbfAuthModal').addClass('active');
return;
}
$('#wbfReportId').val($(this).data('id'));
$('#wbfReportType').val($(this).data('type') || 'post');
$('#wbfReportReason').val('');
$('#wbfReportNote').val('');
$('#wbfReportMsg').hide();
$('#wbfReportModal').addClass('active');
});
$(document).on('click', '#wbfSubmitReport', function () {
var reason = $('#wbfReportReason').val();
if (!reason) {
showMsg($('#wbfReportMsg'), 'Bitte einen Grund auswählen.', false);
return;
}
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
wbfPost('wbf_report_post', {
object_id: $('#wbfReportId').val(),
object_type: $('#wbfReportType').val(),
reason: reason,
note: $('#wbfReportNote').val()
}, function (d) {
showMsg($('#wbfReportMsg'), d.message, true);
var postId = $('#wbfReportId').val();
$('.wbf-report-btn[data-id="' + postId + '"]')
.replaceWith('<span class="wbf-reported-label" title="Bereits gemeldet"><i class="fas fa-flag"></i></span>');
setTimeout(function () { $('#wbfReportModal').removeClass('active'); }, 1800);
$btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Melden');
}, function (d) {
showMsg($('#wbfReportMsg'), d.message || 'Fehler beim Melden.', false);
$btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Melden');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Beitrag inline bearbeiten
══════════════════════════════════════════════════════════ */
$(document).on('click', '.wbf-edit-post-btn', function (e) {
e.preventDefault();
var postId = $(this).data('id');
$('#wbf-post-content-' + postId).hide();
$('#wbf-post-edit-' + postId).show().find('textarea').focus();
});
$(document).on('click', '.wbf-cancel-edit-btn', function () {
var postId = $(this).data('id');
$('#wbf-post-content-' + postId).show();
$('#wbf-post-edit-' + postId).hide();
});
$(document).on('click', '.wbf-save-edit-btn', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var postId = $btn.data('id');
var $wrap = $('#wbf-post-edit-' + postId);
var $msg = $wrap.find('.wbf-edit-msg');
wbfPost('wbf_edit_post', {
post_id: postId,
content: $wrap.find('textarea').val()
}, function (d) {
$('#wbf-post-content-' + postId).html(d.content).show();
$wrap.hide();
$btn.prop('disabled', false).html('<i class="fas fa-save"></i> Speichern');
$msg.css('color', '#56cf7e').text(d.message || 'Gespeichert!').show();
setTimeout(function () { $msg.fadeOut(); }, 2500);
}, function (d) {
$msg.css('color', '#f05252').text(d.message || 'Fehler').show();
$btn.prop('disabled', false).html('<i class="fas fa-save"></i> Speichern');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Zitieren (BBCode)
══════════════════════════════════════════════════════════ */
$(document).on('click', '.wbf-quote-btn', function () {
if (WBF.logged_in !== 'yes') {
$('#wbfAuthModal').addClass('active');
return;
}
var sourceId = $(this).data('source');
var author = $(this).data('author');
var $content = $('#' + sourceId);
if (!$content.length) return;
// Strip HTML tags to get plain text, skip nested blockquotes
var rawText = $content.clone().find('blockquote, .wbf-bb-quote').remove().end().text().trim();
if (rawText.length > 600) rawText = rawText.substring(0, 600) + '…';
var quote = '[quote=' + author + ']\n' + rawText + '\n[/quote]\n\n';
var $ta = $('#wbfReplyContent');
var $replyBox = $('#wbfReplyBox');
if (!$ta.length) return;
var el = $ta[0];
var start = el.selectionStart;
var end = el.selectionEnd;
var val = el.value;
el.value = val.slice(0, start) + quote + val.slice(end);
el.selectionStart = el.selectionEnd = start + quote.length;
$ta.trigger('input').focus();
$('html, body').animate({ scrollTop: $replyBox.offset().top - 80 }, 350);
});
/* ══════════════════════════════════════════════════════════
FEATURE: Bild-Lightbox
══════════════════════════════════════════════════════════ */
$(document).on('click', '.wbf-post__content img', function () {
var $lb = $('<div class="wbf-lightbox">' +
'<button class="wbf-lightbox__close" title="Schließen">✕</button>' +
'<img src="' + $(this).attr('src') + '" alt="' + ($(this).attr('alt') || '') + '">' +
'</div>');
$('body').append($lb).css('overflow', 'hidden');
});
$(document).on('click', '.wbf-lightbox', function (e) {
if ($(e.target).hasClass('wbf-lightbox') || $(e.target).hasClass('wbf-lightbox__close')) {
$(this).remove();
$('body').css('overflow', '');
}
});
$(document).on('keydown', function (e) {
if (e.key === 'Escape') {
$('.wbf-lightbox').remove();
$('body').css('overflow', '');
$('.wbf-emoji-picker').hide();
$('.wbf-tb-dropdown__panel').hide();
}
});
/* ══════════════════════════════════════════════════════════
FEATURE: Thread-Inhalt (OP) inline bearbeiten
══════════════════════════════════════════════════════════ */
$(document).on('click', '.wbf-edit-thread-btn', function (e) {
e.preventDefault();
var threadId = $(this).data('id');
$('#wbf-thread-content-' + threadId).hide();
$('#wbf-thread-edit-' + threadId).show().find('textarea').focus();
});
$(document).on('click', '.wbf-cancel-thread-btn', function () {
var threadId = $(this).data('id');
$('#wbf-thread-content-' + threadId).show();
$('#wbf-thread-edit-' + threadId).hide();
});
$(document).on('click', '.wbf-save-thread-btn', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var threadId = $btn.data('id');
var $wrap = $('#wbf-thread-edit-' + threadId);
var $msg = $wrap.find('.wbf-edit-msg');
wbfPost('wbf_edit_thread', {
thread_id: threadId,
title: $wrap.find('.wbf-edit-title-input').val(),
content: $wrap.find('textarea').val()
}, function (d) {
$('#wbf-thread-content-' + threadId).html(d.content).show();
// Titel im DOM aktualisieren
if (d.title) {
$('.wbf-thread-title').first().text(d.title);
}
$wrap.hide();
$btn.prop('disabled', false).html('<i class="fas fa-save"></i> Speichern');
$msg.css('color', '#56cf7e').text(d.message || 'Gespeichert!').show();
setTimeout(function () { $msg.fadeOut(); }, 2500);
}, function (d) {
$msg.css('color', '#f05252').text(d.message || 'Fehler').show();
$btn.prop('disabled', false).html('<i class="fas fa-save"></i> Speichern');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Live-Suche
══════════════════════════════════════════════════════════ */
var wbfSearchTimer = null;
function wbfTimeAgo(dateStr) {
var diff = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000);
if (diff < 60) return 'Gerade eben';
if (diff < 3600) return Math.floor(diff/60) + ' Min. ago';
if (diff < 86400) return Math.floor(diff/3600) + ' Std. ago';
return Math.floor(diff/86400) + ' Tage ago';
}
$(document).on('input', '#wbfSearchInput', function () {
var q = $(this).val().trim();
var $dd = $('#wbfSearchDropdown');
clearTimeout(wbfSearchTimer);
if (q.length < 2) { $dd.hide().empty(); return; }
wbfSearchTimer = setTimeout(function () {
$dd.html('<div class="wbf-search-dd-empty"><i class="fas fa-spinner fa-spin"></i> Suche…</div>').show();
wbfPost('wbf_search', { query: q }, function (d) {
if (!d.results || !d.results.length) {
$dd.html('<div class="wbf-search-dd-empty">Keine Ergebnisse für &bdquo;' + $('<span>').text(q).html() + '&ldquo;</div>');
return;
}
var html = '';
var shown = d.results.slice(0, 6);
shown.forEach(function (r) {
var isThread = r.result_type === 'thread';
var base3 = WBF.forum_url || window.location.href.split('?')[0];
var sep3 = base3.indexOf('?') !== -1 ? '&' : '?';
var url = base3 + sep3 + 'forum_thread=' + r.id;
var preview = r.content ? r.content.replace(/<[^>]+>/g,'').substring(0,80) : '';
var typeLabel = isThread
? '<span style="color:var(--c-primary);font-size:.68rem;font-weight:700"><i class="fas fa-layer-group"></i> Thread</span>'
: '<span style="color:var(--c-muted);font-size:.68rem"><i class="fas fa-comment"></i> Antwort</span>';
html += '<a class="wbf-search-dd-item" href="' + url + '">' +
'<div class="wbf-search-dd-title">' + $('<span>').text(r.title.substring(0,55)).html() + '</div>' +
'<div class="wbf-search-dd-meta">' + typeLabel +
'<span><i class="fas fa-folder" style="font-size:.65rem"></i> ' + $('<span>').text(r.cat_name).html() + '</span>' +
'<span>' + $('<span>').text(r.display_name).html() + '</span>' +
'</div>' +
(preview ? '<div class="wbf-search-dd-preview">' + $('<span>').text(preview).html() + '…</div>' : '') +
'</a>';
});
if (d.results.length > 6) {
html += '<div class="wbf-search-dd-footer" data-query="' + $('<span>').text(q).html() + '">' +
'Alle ' + d.results.length + ' Ergebnisse anzeigen →</div>';
}
$dd.html(html).show();
}, function () {
$dd.html('<div class="wbf-search-dd-empty">Fehler bei der Suche.</div>');
});
}, 320);
});
// Alle Ergebnisse anzeigen → zur Suchseite
$(document).on('click', '.wbf-search-dd-footer', function () {
var q = $(this).data('query');
window.location.href = window.location.pathname + '?forum_search=1&q=' + encodeURIComponent(q);
});
// Enter-Taste → Suchseite
$(document).on('keydown', '#wbfSearchInput', function (e) {
if (e.key === 'Enter') {
var q = $(this).val().trim();
if (q.length >= 2) {
window.location.href = window.location.pathname + '?forum_search=1&q=' + encodeURIComponent(q);
}
}
if (e.key === 'Escape') { $('#wbfSearchDropdown').hide(); }
});
// Dropdown schließen bei Klick außerhalb
$(document).on('click', function (e) {
if (!$(e.target).closest('#wbfSearchForm').length) {
$('#wbfSearchDropdown').hide();
}
});
/* ══════════════════════════════════════════════════════════
FEATURE: Benachrichtigungen
══════════════════════════════════════════════════════════ */
var wbfNotifLoaded = false;
function wbfLoadNotifications() {
wbfPost('wbf_get_notifications', {}, function (d) {
var $list = $('#wbfNotifList');
if (!d.notifications || !d.notifications.length) {
$list.html('<div class="wbf-notif-empty"><i class="fas fa-bell-slash"></i><br>Keine Benachrichtigungen</div>');
return;
}
var html = '';
d.notifications.forEach(function (n) {
var isUnread = n.is_read == 0;
var avatar = n.actor_avatar || '';
var base = WBF.forum_url || window.location.href.split('?')[0];
var sep = base.indexOf('?') !== -1 ? '&' : '?';
var actor = '<strong>' + $('<span>').text(n.actor_name).html() + '</strong>';
// URL + Text je nach Typ
var url, text, sub;
if (n.type === 'message') {
url = base + sep + 'forum_dm=1';
text = actor + ' hat dir eine Nachricht gesendet';
sub = '';
} else if (n.type === 'mention') {
url = base + sep + 'forum_thread=' + n.thread_id;
text = actor + ' hat dich erwähnt';
sub = n.thread_title || '';
} else if (n.type === 'like') {
url = base + sep + 'forum_thread=' + n.thread_id;
text = actor + ' hat deinen Beitrag geliket';
sub = n.thread_title || '';
} else {
// reply (default)
url = base + sep + 'forum_thread=' + n.thread_id;
text = actor + ' hat in deinem Thread geantwortet';
sub = n.thread_title || '';
}
html += '<a class="wbf-notif-item' + (isUnread ? ' wbf-notif-item--unread' : '') + '" href="' + url + '">' +
'<div class="wbf-notif-item__avatar"><img src="' + avatar + '" alt=""></div>' +
'<div class="wbf-notif-item__body">' +
'<div class="wbf-notif-item__text">' + text +
(sub ? '<br><span style="color:var(--c-muted);font-size:.78rem">' + $('<span>').text(sub).html() + '</span>' : '') +
'</div>' +
'<div class="wbf-notif-item__time">' + wbfTimeAgo(n.created_at) + '</div>' +
'</div>' +
(isUnread ? '<div class="wbf-notif-dot"></div>' : '') +
'</a>';
});
$list.html(html);
// Badge aktualisieren
var unread = d.unread || 0;
var $badge = $('#wbfNotifBadge');
if (unread > 0) { $badge.text(Math.min(unread,99)).show(); }
else { $badge.hide(); }
});
wbfNotifLoaded = true;
}
// Glocke klicken → Dropdown öffnen + Nachrichten laden
$(document).on('click', '#wbfNotifBtn', function (e) {
e.stopPropagation();
var $dd = $('#wbfNotifDropdown');
if ($dd.is(':visible')) {
$dd.hide();
} else {
$dd.show();
if (!wbfNotifLoaded) wbfLoadNotifications();
}
});
// Alle als gelesen markieren
$(document).on('click', '#wbfMarkAllRead', function (e) {
e.stopPropagation();
wbfPost('wbf_mark_notifications_read', {}, function () {
$('#wbfNotifBadge').hide();
$('#wbfNotifList .wbf-notif-item').removeClass('wbf-notif-item--unread');
$('#wbfNotifList .wbf-notif-dot').remove();
wbfNotifLoaded = false; // neu laden beim nächsten Öffnen
});
});
// Benachrichtigung klicken → als gelesen markieren + navigieren
$(document).on('click', '.wbf-notif-item', function (e) {
e.preventDefault();
var href = $(this).attr('href');
wbfPost('wbf_mark_notifications_read', {}, function () {
window.location.href = href;
});
});
// Dropdown schließen bei Klick außerhalb
$(document).on('click', function (e) {
if (!$(e.target).closest('#wbfNotifWrap').length) {
$('#wbfNotifDropdown').hide();
}
});
// ── Unified Smart Polling ─────────────────────────────────────────────────
// Benachrichtigungen + DM-Badge + Live-Konversation in einem einzigen Interval
if (WBF.logged_in === 'yes') {
var wbfLastMsgId = 0;
function wbfPoll() {
// 1. Benachrichtigungs-Badge
wbfPost('wbf_get_notifications', {}, function(d) {
var $badge = $('#wbfNotifBadge');
var unread = d.unread || 0;
if (unread > 0) { $badge.text(Math.min(unread,99)).show(); }
else { $badge.hide(); }
});
// 2. DM-Badge + Live-Nachrichten
wbfPost('wbf_get_inbox', {}, function(d) {
var unread = parseInt(d.unread) || 0;
var $badge = $('.wbf-dm-btn .wbf-notif-badge');
if (unread > 0) { $badge.text(Math.min(unread,99)).css('display',''); }
else { $badge.css('display','none'); }
// 3. Wenn Konversation offen: neue Nachrichten live nachladen
var $conv = $('#wbfDmConversation');
if (!$conv.length || !wbfLastMsgId) return;
var partnerId = parseInt($conv.data('partner'));
if (!partnerId) return;
wbfPost('wbf_get_new_messages', { partner_id: partnerId, since_id: wbfLastMsgId }, function(r) {
if (!r.messages || !r.messages.length) return;
var $msgs = $('#wbfDmMessages');
var wasAtBottom = $msgs[0].scrollHeight - $msgs[0].scrollTop - $msgs[0].clientHeight < 60;
r.messages.forEach(function(m) {
if ($('[data-msg-id="' + m.id + '"]').length) return; // kein Duplikat
var isMine = parseInt(m.from_id) === parseInt(WBF.my_id);
var cls = isMine ? 'wbf-dm-msg wbf-dm-msg--out' : 'wbf-dm-msg wbf-dm-msg--in';
var time = wbfTimeAgo(m.created_at);
var delBtn = '<button class="wbf-dm-msg__del" data-id="' + m.id + '" title="Loeschen"><i class="fas fa-trash-can"></i></button>';
var html = '<div class="' + cls + '" data-msg-id="' + m.id + '">';
if (!isMine) {
html += '<img src="' + (m.sender_avatar||'') + '" class="wbf-dm-inbox-item__avatar">';
}
html += '<div class="wbf-dm-msg__bubble"><div class="wbf-dm-msg__text">'
+ $('<span>').text(m.content).html().replace(/\n/g,'<br>')
+ '</div><div class="wbf-dm-msg__time">' + time + delBtn + '</div></div></div>';
$msgs.append(html);
wbfLastMsgId = Math.max(wbfLastMsgId, parseInt(m.id));
});
if (wasAtBottom) $msgs[0].scrollTop = $msgs[0].scrollHeight;
wbfPost('wbf_mark_messages_read', { partner_id: partnerId });
});
});
}
// Nach dem Laden der Konversation: hoechste ID merken
var origConvLoad = window.wbfConvLoaded;
$(document).on('wbf:messages_loaded', function(e, msgs) {
if (msgs && msgs.length) {
wbfLastMsgId = Math.max.apply(null, msgs.map(function(m){ return parseInt(m.id)||0; }));
}
});
setTimeout(wbfPoll, 3000);
setInterval(wbfPoll, 15000);
}
/* ══════════════════════════════════════════════════════════
FEATURE: Auto-Logout bei Inaktivität
══════════════════════════════════════════════════════════ */
if ( WBF.logged_in === 'yes' && WBF.auto_logout_minutes > 0 ) {
var wbfIdleMs = WBF.auto_logout_minutes * 60 * 1000;
var wbfWarnMs = 30 * 1000; // 30 Sek. Vorwarnung
var wbfIdleTimer = null;
var wbfWarnTimer = null;
var wbfCountTimer = null;
var wbfWarning = false;
// ── Toast-Element ────────────────────────────────────────
var $wbfToast = $([
'<div id="wbfIdleToast" style="',
'display:none;position:fixed;bottom:24px;right:24px;z-index:99999;',
'background:#1e293b;border:1.5px solid rgba(251,191,36,.45);',
'border-radius:10px;padding:14px 18px;max-width:320px;',
'box-shadow:0 8px 32px rgba(0,0,0,.45);',
'font-family:inherit;font-size:.875rem;color:#f1f5f9;',
'animation:wbfFadeIn .2s ease;">',
'<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">',
'<i class="fas fa-clock" style="color:#fbbf24;font-size:1rem"></i>',
'<strong style="color:#fbbf24">Sitzung läuft ab</strong>',
'</div>',
'<p style="margin:0 0 10px;line-height:1.45;color:rgba(241,245,249,.75)">',
'Du wirst in <strong id="wbfIdleCountdown" style="color:#fbbf24">30</strong> Sekunden',
' automatisch abgemeldet.',
'</p>',
'<div style="display:flex;gap:8px">',
'<button id="wbfIdleStay" style="',
'flex:1;padding:6px 12px;border-radius:6px;',
'background:rgba(0,180,216,.2);border:1px solid rgba(0,180,216,.4);',
'color:#00b4d8;font-family:inherit;font-size:.82rem;',
'font-weight:600;cursor:pointer">',
'<i class="fas fa-check"></i> Eingeloggt bleiben',
'</button>',
'<button id="wbfIdleLogout" style="',
'padding:6px 12px;border-radius:6px;',
'background:rgba(240,82,82,.1);border:1px solid rgba(240,82,82,.3);',
'color:#f05252;font-family:inherit;font-size:.82rem;',
'font-weight:600;cursor:pointer">',
'Abmelden',
'</button>',
'</div>',
'</div>'
].join('')).appendTo('body');
function wbfDoLogout() {
clearTimeout(wbfIdleTimer);
clearTimeout(wbfWarnTimer);
clearInterval(wbfCountTimer);
$wbfToast.hide();
wbfPost('wbf_logout', {}, function () {
location.reload();
});
}
function wbfShowWarning() {
wbfWarning = true;
var secs = 30;
$('#wbfIdleCountdown').text(secs);
$wbfToast.fadeIn(200);
wbfCountTimer = setInterval(function () {
secs--;
$('#wbfIdleCountdown').text(secs);
if (secs <= 0) {
clearInterval(wbfCountTimer);
wbfDoLogout();
}
}, 1000);
// Auto-logout after warning period
wbfIdleTimer = setTimeout(wbfDoLogout, wbfWarnMs);
}
function wbfResetIdleTimer() {
if (wbfWarning) return; // Nutzer hat aktiv Warnung bestätigt — nicht resetten
clearTimeout(wbfIdleTimer);
clearTimeout(wbfWarnTimer);
// Warn 30 sec before timeout
wbfWarnTimer = setTimeout(wbfShowWarning, wbfIdleMs - wbfWarnMs);
}
// "Bleiben" Button
$(document).on('click', '#wbfIdleStay', function () {
clearTimeout(wbfIdleTimer);
clearInterval(wbfCountTimer);
$wbfToast.fadeOut(200);
wbfWarning = false;
wbfResetIdleTimer();
});
// "Abmelden" Button
$(document).on('click', '#wbfIdleLogout', function () {
wbfDoLogout();
});
// Aktivitäts-Events überwachen
var wbfActivityEvents = 'mousemove keydown mousedown touchstart scroll click';
$(document).on(wbfActivityEvents, function () {
if (!wbfWarning) wbfResetIdleTimer();
});
// Start
wbfResetIdleTimer();
}
/* ── Login: Remember-Me ─────────────────────────────────── */
$(document).on('click', '.wbf-login-submit-btn', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $box = $(this).closest('.wbf-auth-box');
wbfPost('wbf_login', {
username: $box.find('.wbf-field-username').val(),
password: $box.find('.wbf-field-password').val(),
remember_me: $box.find('.wbf-field-remember').is(':checked') ? '1' : ''
}, function () {
location.reload();
}, function (d) {
showMsg($box.find('.wbf-login-msg'), d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-sign-in-alt"></i> Einloggen');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Reaktionen
══════════════════════════════════════════════════════════ */
// Picker öffnen/schließen
$(document).on('click', '.wbf-reaction-trigger', function (e) {
e.stopPropagation();
var $picker = $(this).next('.wbf-reaction-picker');
var wasOpen = $picker.is(':visible');
$('.wbf-reaction-picker').hide();
if (!wasOpen) $picker.show();
});
// Reaktion wählen (aus Picker oder Summary)
$(document).on('click', '.wbf-reaction-btn, .wbf-reaction-pill', function (e) {
e.stopPropagation();
if (WBF.logged_in !== 'yes') { $('#wbfAuthModal').addClass('active'); return; }
var $bar = $(this).closest('.wbf-reaction-bar');
var reaction = $(this).data('reaction');
wbfPost('wbf_set_reaction', {
object_id: $bar.data('id'),
object_type: $bar.data('type'),
reaction: reaction
}, function (d) {
var $summary = $bar.find('.wbf-reaction-summary');
var $picker = $bar.find('.wbf-reaction-picker');
// Rebuild summary
var order = (WBF.reactions && WBF.reactions.length) ? WBF.reactions : ['👍','❤️','😂','😮','😢','😡'];
var html = '';
order.forEach(function(e) {
if (d.counts[e]) {
var active = (d.mine === e) ? ' wbf-reaction-active' : '';
html += '<button class="wbf-reaction-pill' + active + '" data-reaction="' + e + '">'
+ e + '<span>' + d.counts[e] + '</span></button>';
}
});
if (!$summary.length) {
$bar.prepend('<div class="wbf-reaction-summary">' + html + '</div>');
} else {
$summary.html(html || '').toggle(!!html);
}
// Update picker active states
$picker.find('.wbf-reaction-btn').each(function() {
$(this).toggleClass('wbf-reaction-active', $(this).data('reaction') === d.mine);
});
$picker.hide();
});
});
// Close pickers on outside click
$(document).on('click', function() { $('.wbf-reaction-picker').hide(); });
/* ══════════════════════════════════════════════════════════
FEATURE: @Erwähnungen Autocomplete im Reply-Textarea
══════════════════════════════════════════════════════════ */
var wbfMentionTimer = null;
var wbfMentionActive = false;
var $wbfMentionDrop = null;
function wbfCloseMention() {
if ($wbfMentionDrop) $wbfMentionDrop.remove();
$wbfMentionDrop = null;
wbfMentionActive = false;
}
$(document).on('input', '#wbfReplyContent, #wbfThreadContent', function () {
var $ta = $(this);
var val = $ta.val();
var pos = $ta[0].selectionStart;
var before = val.slice(0, pos);
var match = before.match(/@([a-zA-Z0-9_]{0,30})$/);
if (!match) { wbfCloseMention(); return; }
var q = match[1];
wbfMentionActive = true;
clearTimeout(wbfMentionTimer);
wbfMentionTimer = setTimeout(function() {
wbfPost('wbf_user_suggest', { q: q }, function(d) {
if (!d.users || !d.users.length) { wbfCloseMention(); return; }
if ($wbfMentionDrop) $wbfMentionDrop.remove();
var html = '';
d.users.forEach(function(u) {
html += '<div class="wbf-mention-item" data-username="' + $('<span>').text(u.username).html() + '">'
+ '<img src="' + (u.avatar_url || '') + '" width="22" height="22" style="border-radius:50%;flex-shrink:0">'
+ '<span>' + $('<span>').text(u.display_name).html() + '</span>'
+ '<small>@' + $('<span>').text(u.username).html() + '</small>'
+ '</div>';
});
$wbfMentionDrop = $('<div class="wbf-mention-dropdown">' + html + '</div>');
$ta.closest('.wbf-reply-form__input, .wbf-form-row').append($wbfMentionDrop);
$wbfMentionDrop.show();
});
}, 200);
});
$(document).on('click', '.wbf-mention-item', function(e) {
e.stopPropagation();
var username = $(this).data('username');
var $ta = $('#wbfReplyContent:visible, #wbfThreadContent:visible').first();
if (!$ta.length) return;
var val = $ta.val(), pos = $ta[0].selectionStart;
var before = val.slice(0, pos);
var after = val.slice(pos);
var newBefore = before.replace(/@([a-zA-Z0-9_]*)$/, '@' + username + ' ');
$ta.val(newBefore + after);
$ta[0].selectionStart = $ta[0].selectionEnd = newBefore.length;
wbfCloseMention();
$ta.focus();
});
$(document).on('click', function(e) {
if (!$(e.target).closest('.wbf-mention-dropdown').length) wbfCloseMention();
});
/* ══════════════════════════════════════════════════════════
FEATURE: Private Nachrichten (DM)
══════════════════════════════════════════════════════════ */
// Helper: format messages in conversation
function wbfRenderMessages(msgs, myId) {
if (!msgs || !msgs.length) {
return '<div class="wbf-dm-no-msgs"><i class="fas fa-comments"></i><p>Noch keine Nachrichten.</p></div>';
}
var html = '';
msgs.forEach(function(m) {
var isMine = parseInt(m.from_id) === parseInt(myId);
var cls = isMine ? 'wbf-dm-msg wbf-dm-msg--out' : 'wbf-dm-msg wbf-dm-msg--in';
var time = wbfTimeAgo(m.created_at);
var delBtn = '<button class="wbf-dm-msg__del" data-id="' + m.id + '" title="Löschen"><i class="fas fa-trash-can"></i></button>';
if (!isMine) {
html += '<div class="' + cls + '" data-msg-id="' + m.id + '">'
+ '<img src="' + (m.sender_avatar || '') + '" class="wbf-dm-msg__avatar">'
+ '<div class="wbf-dm-msg__bubble"><div class="wbf-dm-msg__text">'
+ $('<span>').text(m.content).html().replace(/\n/g,'<br>')
+ '</div><div class="wbf-dm-msg__time">' + time + delBtn + '</div></div></div>';
} else {
html += '<div class="' + cls + '" data-msg-id="' + m.id + '">'
+ '<div class="wbf-dm-msg__bubble"><div class="wbf-dm-msg__text">'
+ $('<span>').text(m.content).html().replace(/\n/g,'<br>')
+ '</div><div class="wbf-dm-msg__time">' + time + delBtn + '</div></div></div>';
}
});
return html;
}
// Render inbox list
function wbfRenderInbox(inbox, myId) {
if (!inbox || !inbox.length) {
return '<div style="padding:1.5rem;text-align:center;color:var(--c-muted);font-size:.85rem">Keine Nachrichten.</div>';
}
var html = '';
inbox.forEach(function(conv) {
var href = window.location.pathname + '?forum_dm=inbox&with=' + conv.partner_id;
var unread = parseInt(conv.unread_cnt) > 0;
html += '<a class="wbf-dm-inbox-item' + (unread ? ' wbf-dm-inbox-item--unread' : '') + '" href="' + href + '">'
+ '<img src="' + (conv.partner_avatar || '') + '" class="wbf-dm-inbox-item__avatar">'
+ '<div class="wbf-dm-inbox-item__body">'
+ '<span class="wbf-dm-inbox-item__name">' + $('<span>').text(conv.partner_name).html() + '</span>'
+ (unread ? '<span class="wbf-dm-inbox-item__badge">' + conv.unread_cnt + '</span>' : '')
+ '</div></a>';
});
return html;
}
// Auto-load inbox if on DM page
if ($('#wbfDmInbox').length && WBF.logged_in === 'yes') {
wbfPost('wbf_get_inbox', {}, function(d) {
$('#wbfDmInbox').html(wbfRenderInbox(d.inbox, WBF.my_id));
});
// Load conversation if partner given
var $conv = $('#wbfDmConversation');
if ($conv.length) {
var partnerId = $conv.data('partner');
wbfPost('wbf_get_conversation', { partner_id: partnerId }, function(d) {
// Header
var p = d.partner;
if (p) {
var backUrl = window.location.pathname + '?forum_dm=inbox';
$('#wbfDmHeader').html(
'<a href="' + backUrl + '" class="wbf-dm-back-btn" title="Zurück zur Inbox"><i class="fas fa-arrow-left"></i></a>'
+ '<img src="' + (p.avatar_url||'') + '" class="wbf-dm-inbox-item__avatar">'
+ '<strong>' + $('<span>').text(p.display_name).html() + '</strong>'
+ '<a href="?forum_profile=' + p.id + '" style="font-size:.78rem;color:var(--c-muted);text-decoration:none">@' + $('<span>').text(p.username).html() + '</a>'
);
}
var html = wbfRenderMessages(d.messages, d.my_id);
var $msgs = $('#wbfDmMessages').html(html);
$msgs[0].scrollTop = $msgs[0].scrollHeight;
$(document).trigger('wbf:messages_loaded', [d.messages]);
// "Ältere laden" initialisieren — AJAX gibt total zurück
if (d.total !== undefined) {
$('#wbfDmLoadMore').data('offset', 0).data('partner', d.partner ? d.partner.id : 0);
wbfUpdateLoadMoreBtn(d.total, d.messages.length);
}
});
}
}
// Send reply in open conversation
$(document).on('click', '#wbfDmSendReply', function() {
var toId = $(this).data('to');
var text = $('#wbfDmReplyText').val().trim();
if (!text) return;
var $btn = $(this).prop('disabled', true);
wbfPost('wbf_send_message', { to_id: toId, content: text }, function(d) {
var delBtn = '<button class="wbf-dm-msg__del" data-id="' + d.message_id + '" title="Loeschen"><i class="fas fa-trash-can"></i></button>';
var html = '<div class="wbf-dm-msg wbf-dm-msg--out" data-msg-id="' + d.message_id + '">'
+ '<div class="wbf-dm-msg__bubble"><div class="wbf-dm-msg__text">'
+ $('<span>').text(d.content).html().replace(/\n/g,'<br>')
+ '</div><div class="wbf-dm-msg__time">Gerade eben' + delBtn + '</div></div></div>';
var $msgs = $('#wbfDmMessages');
$msgs.append(html);
$msgs[0].scrollTop = $msgs[0].scrollHeight;
$('#wbfDmReplyText').val('');
$btn.prop('disabled', false);
if (d.message_id) wbfLastMsgId = Math.max(wbfLastMsgId, parseInt(d.message_id));
}, function(e) {
alert(e.message || 'Fehler');
$btn.prop('disabled', false);
});
});
// Delete message
$(document).on('click', '.wbf-dm-msg__del', function() {
var msgId = $(this).data('id');
var $row = $(this).closest('[data-msg-id]');
if (!confirm('Nachricht löschen?')) return;
wbfPost('wbf_delete_message', { message_id: msgId }, function() {
$row.fadeOut(200, function() { $(this).remove(); });
}, function(e) {
alert(e.message || 'Fehler beim Löschen.');
});
});
// Enter to send (Shift+Enter = newline)
$(document).on('keydown', '#wbfDmReplyText', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
$('#wbfDmSendReply').trigger('click');
}
});
// New DM button → open compose modal
$(document).on('click', '#wbfNewDmBtn, #wbfNewDmBtn2', function() {
$('#wbfDmRecipientInput').val('');
$('#wbfDmToId').val('');
$('#wbfDmComposeText').val('');
$('#wbfDmComposeMsg').hide();
$('#wbfDmComposeModal').addClass('active');
});
// Recipient autocomplete in compose modal
var wbfDmSugTimer = null;
$(document).on('input', '#wbfDmRecipientInput', function() {
var q = $(this).val().trim();
clearTimeout(wbfDmSugTimer);
if (!q) { $('#wbfDmSuggest').hide().empty(); return; }
wbfDmSugTimer = setTimeout(function() {
wbfPost('wbf_user_suggest', { q: q }, function(d) {
var $s = $('#wbfDmSuggest');
if (!d.users || !d.users.length) { $s.hide().empty(); return; }
var html = '';
d.users.forEach(function(u) {
html += '<div class="wbf-tag-suggest-item" data-id="' + u.id + '" data-name="' + $('<span>').text(u.display_name).html() + '">'
+ '<img src="' + (u.avatar_url||'') + '" width="22" height="22" style="border-radius:50%;flex-shrink:0">'
+ $('<span>').text(u.display_name).html()
+ '<span style="color:var(--c-muted);font-size:.75rem">@' + $('<span>').text(u.username).html() + '</span>'
+ '</div>';
});
$s.html(html).show();
});
}, 200);
});
$(document).on('click', '#wbfDmSuggest .wbf-tag-suggest-item', function() {
var id = $(this).data('id');
var name = $(this).data('name');
$('#wbfDmToId').val(id);
$('#wbfDmRecipientInput').val(name);
$('#wbfDmSuggest').hide().empty();
});
// Send from compose modal
$(document).on('click', '#wbfDmComposeSend', function() {
var toId = $('#wbfDmToId').val();
var text = $('#wbfDmComposeText').val().trim();
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
if (!toId) { showMsg($('#wbfDmComposeMsg'), 'Bitte Empfänger wählen.', false); $btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Senden'); return; }
if (!text) { showMsg($('#wbfDmComposeMsg'), 'Nachricht leer.', false); $btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Senden'); return; }
wbfPost('wbf_send_message', { to_id: toId, content: text }, function() {
showMsg($('#wbfDmComposeMsg'), '✔ Gesendet!', true);
setTimeout(function() {
$('#wbfDmComposeModal').removeClass('active');
window.location.href = window.location.pathname + '?forum_dm=inbox&with=' + toId;
}, 800);
}, function(e) {
showMsg($('#wbfDmComposeMsg'), e.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Senden');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Online-Nutzer (Notification polling erweitern)
══════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════
FEATURE: Passwort vergessen
══════════════════════════════════════════════════════════ */
// "Passwort vergessen?" → Panel wechseln
$(document).on('click', '.wbf-forgot-link', function(e) {
e.preventDefault();
$('.wbf-auth-tab').removeClass('active');
$('.wbf-auth-panel').removeClass('active');
$('[data-panel="forgot"]').addClass('active');
});
$(document).on('click', '.wbf-show-login', function(e) {
e.preventDefault();
$('.wbf-auth-panel').removeClass('active');
$('[data-tab="login"]').addClass('active');
$('[data-panel="login"]').addClass('active');
});
// Reset-Link anfordern
$(document).on('click', '.wbf-forgot-submit-btn', function() {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
wbfPost('wbf_forgot_password', {
email: $(this).closest('.wbf-auth-box').find('.wbf-field-forgot-email').val()
}, function(d) {
showMsg($btn.closest('.wbf-auth-box').find('.wbf-forgot-msg'), d.message, true);
$btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Reset-Link senden');
}, function(d) {
showMsg($btn.closest('.wbf-auth-box').find('.wbf-forgot-msg'), d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-paper-plane"></i> Reset-Link senden');
});
});
// Passwort-Reset Formular (separate Seite via ?wbf_reset_token=...)
$(document).on('click', '#wbfResetSubmit', function() {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
wbfPost('wbf_reset_password', {
token: $('#wbfResetToken').val(),
password: $('#wbfResetPass1').val(),
password2: $('#wbfResetPass2').val()
}, function(d) {
showMsg($('#wbfResetMsg'), d.message, true);
$btn.html('<i class="fas fa-check"></i> Passwort geändert!');
setTimeout(function() {
window.location.href = WBF.forum_url || window.location.pathname.split('?')[0];
}, 2000);
}, function(d) {
showMsg($('#wbfResetMsg'), d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-check"></i> Passwort ändern');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Ältere Nachrichten laden (DM)
══════════════════════════════════════════════════════════ */
// Wird nach dem ersten Laden der Konversation aufgerufen
function wbfUpdateLoadMoreBtn(total, currentCount) {
var $wrap = $('#wbfDmLoadMoreWrap');
var $btn = $('#wbfDmLoadMore');
if (total > currentCount) {
var remaining = total - currentCount;
$btn.html('<i class="fas fa-clock-rotate-left"></i> ' + remaining + ' ältere Nachrichten laden');
$wrap.show();
} else {
$wrap.hide();
}
}
$(document).on('click', '#wbfDmLoadMore', function() {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var partnerId = $(this).data('partner');
var offset = parseInt($(this).data('offset')) || 0;
var loadCount = 30;
var newOffset = offset + loadCount;
wbfPost('wbf_load_more_messages', {
partner_id: partnerId,
offset: newOffset
}, function(d) {
if (!d.messages || !d.messages.length) {
$('#wbfDmLoadMoreWrap').hide();
return;
}
// Ältere Nachrichten oben einfügen
var html = wbfRenderMessages(d.messages, d.my_id);
var $msgs = $('#wbfDmMessages');
var oldHeight = $msgs[0].scrollHeight;
$msgs.prepend(html);
// Scroll-Position halten (nicht nach oben springen)
$msgs.scrollTop($msgs[0].scrollHeight - oldHeight);
// Offset aktualisieren
$('#wbfDmLoadMore').data('offset', newOffset).prop('disabled', false);
// Button update
var loaded = newOffset + d.messages.length;
wbfUpdateLoadMoreBtn(d.total, loaded);
}, function() {
$btn.prop('disabled', false).html('<i class="fas fa-clock-rotate-left"></i> Ältere laden');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Thread-Vorschau beim Hover
══════════════════════════════════════════════════════════ */
var $wbfPreviewTip = $('<div class="wbf-thread-preview-tip"></div>').appendTo('body');
var wbfPreviewTimer = null;
$(document).on('mouseenter', '.wbf-thread-row__title', function(e) {
var $row = $(this).closest('.wbf-thread-row');
var preview = $row.data('preview');
if (!preview || preview.length < 10) return;
clearTimeout(wbfPreviewTimer);
wbfPreviewTimer = setTimeout(function() {
$wbfPreviewTip.text(preview + (preview.length >= 160 ? '…' : '')).addClass('visible');
positionTip(e);
}, 280);
});
$(document).on('mousemove', '.wbf-thread-row__title', function(e) {
if ($wbfPreviewTip.hasClass('visible')) positionTip(e);
});
$(document).on('mouseleave', '.wbf-thread-row__title', function() {
clearTimeout(wbfPreviewTimer);
$wbfPreviewTip.removeClass('visible');
});
function positionTip(e) {
var tipW = $wbfPreviewTip.outerWidth();
var left = Math.min(e.pageX + 12, $(window).width() - tipW - 16);
var top = e.pageY + 18;
$wbfPreviewTip.css({ left: left, top: top });
}
/* ══════════════════════════════════════════════════════════
FEATURE: Ungelesene Threads markieren
══════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════
FEATURE: Thread-Abonnement
══════════════════════════════════════════════════════════ */
$(document).on('click', '.wbf-subscribe-btn', function(e) {
e.preventDefault();
var $btn = $(this).prop('disabled', true);
var threadId = $btn.data('thread');
wbfPost('wbf_toggle_subscribe', { thread_id: threadId }, function(d) {
$btn.prop('disabled', false);
if (d.subscribed) {
$btn.addClass('wbf-btn--primary')
.html('<i class="fas fa-bell"></i> Abonniert');
$btn.attr('title','Abonnement entfernen');
} else {
$btn.removeClass('wbf-btn--primary')
.html('<i class="fas fa-bell-slash"></i> Abonnieren');
$btn.attr('title','Thread abonnieren');
}
if (d.msg) { var $n = $('<div class="wbf-toast">'+d.msg+'</div>').appendTo('body'); setTimeout(function(){$n.remove();},3000); }
}, function() {
$btn.prop('disabled', false);
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Profil-Sichtbarkeit
══════════════════════════════════════════════════════════ */
$(document).on('click', '#wbfToggleProfileVis', function() {
var $btn = $(this).prop('disabled', true);
wbfPost('wbf_toggle_profile_visibility', {}, function(d) {
$btn.prop('disabled', false).data('state', d.public);
if (d.public) {
$btn.addClass('wbf-btn--primary').html('<i class="fas fa-eye"></i> Öffentlich');
} else {
$btn.removeClass('wbf-btn--primary').html('<i class="fas fa-eye-slash"></i> Privat');
}
if (d.msg) { var $n = $('<div class="wbf-toast">'+d.msg+'</div>').appendTo('body'); setTimeout(function(){$n.remove();},3000); }
}, function() { $btn.prop('disabled', false); });
});
/* ══════════════════════════════════════════════════════════
FEATURE: DSGVO Konto-Löschung
══════════════════════════════════════════════════════════ */
$(document).on('click', '#wbfGdprSubmit', function () {
var $btn = $(this);
var password = $('#wbfGdprPassword').val();
var confirmed = $('#wbfGdprConfirm').is(':checked');
var $msg = $('#wbfGdprMsg');
if (!password) {
showMsg($msg, 'Bitte Passwort eingeben.', false);
return;
}
if (!confirmed) {
showMsg($msg, 'Bitte Bestätigungs-Checkbox anhaken.', false);
return;
}
if (!confirm('Letzter Schritt: Konto wirklich unwiderruflich löschen?')) return;
$btn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Wird gelöscht…');
wbfPost('wbf_delete_account', {
password: password,
confirm: '1'
}, function (d) {
// Erfolgreich — kurze Meldung, dann Weiterleitung
$('#wbfGdprBox').html(
'<p style="color:var(--c-green);font-size:.88rem"><i class="fas fa-circle-check"></i> ' +
(d.message || 'Konto gelöscht.') + '</p>'
);
setTimeout(function () {
window.location.href = d.redirect || WBF.forum_url || '/';
}, 2500);
}, function (d) {
showMsg($msg, d.message || 'Fehler.', false);
$btn.prop('disabled', false).html('<i class="fas fa-trash-can"></i> Konto endgültig löschen');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Spam-Schutz — Formzeit + Honeypot
══════════════════════════════════════════════════════════ */
// Sende Honeypot + Zeitstempel mit Registrierung
$(document).on('click', '.wbf-reg-submit-btn', function() {
var $box = $(this).closest('.wbf-auth-box');
$box.find('[name="wbf_website"]').val(''); // Honeypot immer leer lassen
});
/* ══════════════════════════════════════════════════════════
FIX: Modal Teleport — verschiebe alle Modals zu <body>
damit kein Stacking-Context (sticky/transform) das
position:fixed Overlay blockieren kann.
══════════════════════════════════════════════════════════ */
$(function() {
// CSS-Variablen aus .wbf-wrap auf die teleportierten Modals übertragen
var wrapStyle = document.querySelector('.wbf-wrap');
$('.wbf-modal').each(function() {
var $modal = $(this);
// Klone den computed style für CSS-Variablen
if (wrapStyle) {
var vars = [
'--c-surface','--c-primary','--c-primary-l','--c-accent',
'--c-text','--c-text-dim','--c-muted','--c-border',
'--c-danger','--c-green','--c-warning','--radius','--radius-sm',
'--c-bg','--c-bg2','--c-surface2'
];
vars.forEach(function(v) {
var val = getComputedStyle(wrapStyle).getPropertyValue(v);
if (val) $modal[0].style.setProperty(v, val.trim());
});
}
$modal.appendTo('body');
});
});
/* ── E-Mail-Adresse ändern ──────────────────────────────────────────── */
$(document).on('click', '#wbfSaveEmail', function () {
var $btn = $(this);
var email = $('#wbfNewEmail').val().trim();
var password = $('#wbfEmailPassword').val();
var $msg = $('#wbfEmailMsg');
if (!email) { $msg.removeClass('wbf-ok').addClass('wbf-err').text('Bitte E-Mail eingeben.'); return; }
if (!password) { $msg.removeClass('wbf-ok').addClass('wbf-err').text('Bitte Passwort eingeben.'); return; }
$btn.prop('disabled', true);
wbfPost('wbf_change_email', { new_email: email, password: password }, function (d) {
$msg.removeClass('wbf-err').addClass('wbf-ok').text(d.message || 'E-Mail geaendert.');
$('#wbfNewEmail').val('');
$('#wbfEmailPassword').val('');
$btn.prop('disabled', false);
}, function (d) {
$msg.removeClass('wbf-ok').addClass('wbf-err').text(d.message || 'Fehler.');
$btn.prop('disabled', false);
});
});
/* ── Toggle-Switch (Notification Prefs) ─────────────────────────────── */
$(document).on('click', '.wbf-toggle', function () {
var $t = $(this);
var on = String($t.data('state')) === '1';
var val = on ? '0' : '1';
$t.data('state', val).attr('data-state', val);
if (val === '1') { $t.addClass('wbf-toggle--on'); }
else { $t.removeClass('wbf-toggle--on'); }
});
/* ── Benachrichtigungs-Einstellungen speichern ───────────────────────── */
$(document).on('click', '#wbfSaveNotifPrefs', function () {
var $btn = $(this);
var $msg = $('#wbfNotifPrefsMsg');
$btn.prop('disabled', true);
wbfPost('wbf_save_notification_prefs', {
notify_reply: String($('#wbfNotifReply').data('state')) === '1' ? '1' : '0',
notify_mention: String($('#wbfNotifMention').data('state')) === '1' ? '1' : '0',
notify_message: String($('#wbfNotifMessage').data('state')) === '1' ? '1' : '0'
}, function (d) {
$msg.removeClass('wbf-err').addClass('wbf-ok').text(d.message || 'Gespeichert!');
$btn.prop('disabled', false);
}, function (d) {
$msg.removeClass('wbf-ok').addClass('wbf-err').text(d.message || 'Fehler.');
$btn.prop('disabled', false);
});
});
/* ── Lesezeichen ────────────────────────────────────────────────────── */
$(document).on('click', '.wbf-bookmark-btn', function () {
var $btn = $(this);
var threadId = $btn.data('thread');
wbfPost('wbf_toggle_bookmark', { thread_id: threadId }, function (d) {
if (d.bookmarked) {
$btn.addClass('wbf-bookmarked').attr('title', 'Lesezeichen entfernen');
$btn.find('i').removeClass('far').addClass('fas');
} else {
$btn.removeClass('wbf-bookmarked').attr('title', 'Lesezeichen hinzufügen');
$btn.find('i').removeClass('fas').addClass('far');
}
});
});
/* ── Nutzer ignorieren / Ignorierung aufheben ────────────────────────── */
$(document).on('click', '.wbf-ignore-btn', function () {
var $btn = $(this);
var ignoredId = parseInt($btn.data('id'), 10);
var name = $btn.data('name') || 'diesen Nutzer';
var isIgnored = String($btn.data('ignored')) === '1';
// Bestätigung nur beim Ignorieren, nicht beim Entblocken
if (!isIgnored) {
if (!confirm(name + ' ignorieren?\n\nDessen Beiträge werden in Threads ausgeblendet und DMs werden blockiert.')) {
return;
}
}
$btn.prop('disabled', true);
wbfPost('wbf_toggle_ignore', { ignored_id: ignoredId }, function (d) {
var nowIgnored = d.ignored;
// Alle Buttons mit dieser User-ID auf der Seite aktualisieren
$('.wbf-ignore-btn[data-id="' + ignoredId + '"]').each(function () {
var $b = $(this);
$b.data('ignored', nowIgnored ? '1' : '0');
$b.attr('data-ignored', nowIgnored ? '1' : '0');
// Icon + Label aktualisieren
$b.find('i').attr('class', 'fas fa-' + (nowIgnored ? 'eye' : 'eye-slash'));
// Button-Variante (Post-Footer, klein ohne wbf-btn)
if (!$b.hasClass('wbf-btn')) {
$b.text('');
$b.append('<i class="fas fa-' + (nowIgnored ? 'eye' : 'eye-slash') + '"></i> ' + (nowIgnored ? 'Entblocken' : 'Ignorieren'));
} else {
// Profil-Variante mit wbf-btn
$b.html('<i class="fas fa-' + (nowIgnored ? 'eye' : 'eye-slash') + '"></i> ' + (nowIgnored ? 'Ignorierung aufheben' : 'Nutzer ignorieren'));
}
$b.prop('disabled', false);
});
// Posts des Users auf der aktuellen Seite ein-/ausblenden
$('.wbf-post, .wbf-post--op').each(function () {
var $post = $(this);
// Buttons innerhalb dieses Posts mit der User-ID suchen
var $ib = $post.find('.wbf-ignore-btn[data-id="' + ignoredId + '"]');
if (!$ib.length) return;
if (nowIgnored) {
// Ignoriert → Overlay zeigen wenn noch nicht vorhanden
if (!$post.find('.wbf-ignored-bar').length) {
var barHtml = '<div class="wbf-ignored-bar">' +
'<span><i class="fas fa-eye-slash"></i> Beitrag von ignoriertem Nutzer: <strong>' +
$('<span>').text(name).html() + '</strong></span>' +
'<button class="wbf-show-ignored-btn" type="button">Trotzdem anzeigen</button>' +
'</div>' +
'<div class="wbf-ignored-content" style="display:none">';
$post.addClass('wbf-post--ignored');
$post.prepend(barHtml);
// Restlichen Inhalt in ignored-content verschieben
$post.children(':not(.wbf-ignored-bar):not(.wbf-ignored-content)').wrapAll('<div class="wbf-ignored-content-inner">');
$post.find('.wbf-ignored-content').append($post.find('.wbf-ignored-content-inner').children());
$post.find('.wbf-ignored-content-inner').remove();
}
} else {
// Entblockt → Overlay entfernen
var $bar = $post.find('.wbf-ignored-bar');
var $content = $post.find('.wbf-ignored-content');
if ($bar.length) {
// Inhalt wieder nach oben holen
$content.children().unwrap();
$bar.remove();
$post.removeClass('wbf-post--ignored');
}
}
});
// Ignore-Liste im Profil aktualisieren (falls Nutzer auf eigener Profil-Seite)
if (!nowIgnored) {
// Eintrag aus der Liste entfernen
$('#wbf-ignore-item-' + ignoredId).fadeOut(300, function () {
$(this).remove();
var remaining = $('#wbfIgnoreList .wbf-ignore-item').length;
$('#wbfIgnoreCount').text(remaining);
if (remaining === 0) {
$('#wbfIgnoreList').replaceWith('<p class="wbf-profile-empty" id="wbfIgnoreEmpty">Du ignorierst niemanden.</p>');
}
});
}
// Toast-Meldung
var $t = $('<div class="wbf-toast">' + (d.message || (nowIgnored ? name + ' ignoriert.' : 'Ignorierung aufgehoben.')) + '</div>').appendTo('body');
setTimeout(function () { $t.remove(); }, 3000);
}, function () {
// Fehler-Callback
$('.wbf-ignore-btn[data-id="' + ignoredId + '"]').prop('disabled', false);
});
});
/* "Trotzdem anzeigen" — eingeklappten ignorierten Post aufdecken */
$(document).on('click', '.wbf-show-ignored-btn', function () {
var $bar = $(this).closest('.wbf-ignored-bar');
var $content = $bar.next('.wbf-ignored-content');
$content.slideDown(200);
$bar.hide();
});
}(jQuery));