Files
WP-Business-Forum/assets/js/forum-script.js
2026-03-21 00:54:23 +01:00

1570 lines
74 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.success) cb(res.data);
else if (errCb) errCb(res.data);
});
}
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()
}, 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);
$('#wbfNewThreadModal').addClass('active');
};
$(document).on('click', '#wbfSubmitThread', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
wbfPost('wbf_new_thread', {
category_id: $('#wbfThreadCat').val(),
title: $('#wbfThreadTitle').val(),
content: $('#wbfThreadContent').val(),
tags: $('#wbfThreadTags').val()
}, 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');
});
});
/* ══════════════════════════════════════════════════════════
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', function () {
var $btn = $(this).prop('disabled', true);
wbfPost('wbf_update_profile', {
display_name: $('#wbfEditName').val(),
bio: $('#wbfEditBio').val(),
signature: $('#wbfEditSignature').val(),
new_password: $('#wbfNewPassword').val()
}, function (d) {
showMsg($('#wbfProfileMsg'), d.message, true);
$btn.prop('disabled', false);
}, function (d) {
showMsg($('#wbfProfileMsg'), 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>');
wbfPost('wbf_login', {
username: $(this).closest('.wbf-auth-box').find('.wbf-field-username').val(),
password: $(this).closest('.wbf-auth-box').find('.wbf-field-password').val(),
remember_me: $(this).closest('.wbf-auth-box').find('.wbf-field-remember').is(':checked') ? '1' : ''
}, function () {
location.reload();
}, function (d) {
showMsg($btn.closest('.wbf-auth-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 = ['👍','❤️','😂','😮','😢','😡'];
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');
});
});
}(jQuery));