2358 lines
113 KiB
JavaScript
2358 lines
113 KiB
JavaScript
(function ($) {
|
||
'use strict';
|
||
|
||
/* ── Utilities ──────────────────────────────────────────────── */
|
||
function wbfPost(action, data, cb, errCb) {
|
||
if (typeof WBF === 'undefined' || !WBF.ajax_url || !WBF.nonce) {
|
||
if (errCb) errCb({message: 'Forum-Fehler: AJAX-Setup fehlt. Bitte Seite neu laden.'});
|
||
return;
|
||
}
|
||
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>');
|
||
var $invite = $(this).closest('.wbf-auth-box').find('.wbf-field-invite-code');
|
||
var inviteVal = '';
|
||
if ($invite.length > 0) {
|
||
var raw = $invite.val();
|
||
if (typeof raw === 'string') inviteVal = raw.toUpperCase().trim();
|
||
}
|
||
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: inviteVal,
|
||
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 (alles auf einmal) ───────────────────── */
|
||
$(document).on('click', '#wbfSaveProfile', function () {
|
||
var $btn = $(this).prop('disabled', true);
|
||
var $msg = $('#wbfProfileMsg');
|
||
var data = {
|
||
display_name: $('#wbfEditName').val(),
|
||
bio: $('#wbfEditBio').val(),
|
||
signature: $('#wbfEditSignature').val()
|
||
};
|
||
// Alle benutzerdefinierten Felder (alle Kategorien) 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);
|
||
// Bio und Signatur sofort aktualisieren (ohne Reload)
|
||
if (typeof data.bio !== 'undefined') {
|
||
$('.wbf-profile-sidebar__bio-text').text(data.bio);
|
||
}
|
||
if (typeof data.signature !== 'undefined') {
|
||
$('.wbf-profile-sidebar__sig').text(data.signature);
|
||
}
|
||
}, function (d) {
|
||
showMsg($msg, d.message || 'Fehler', false);
|
||
$btn.prop('disabled', false);
|
||
});
|
||
});
|
||
|
||
/* ── Passwort ändern ────────────────────────────────────────── */
|
||
$(document).on('click', '#wbfSavePassword', function () {
|
||
var $btn = $(this).prop('disabled', true);
|
||
var $msg = $('#wbfPasswordMsg');
|
||
var cur = $('#wbfCurrentPassword').val();
|
||
var pw1 = $('#wbfNewPassword').val();
|
||
var pw2 = $('#wbfNewPassword2').val();
|
||
|
||
if (!cur) {
|
||
showMsg($msg, 'Bitte aktuelles Passwort eingeben.', false);
|
||
return $btn.prop('disabled', false);
|
||
}
|
||
if (pw1.length < 6) {
|
||
showMsg($msg, 'Neues Passwort mindestens 6 Zeichen.', false);
|
||
return $btn.prop('disabled', false);
|
||
}
|
||
if (pw1 !== pw2) {
|
||
showMsg($msg, 'Die Passwörter stimmen nicht überein.', false);
|
||
return $btn.prop('disabled', false);
|
||
}
|
||
|
||
wbfPost('wbf_update_profile', {
|
||
current_password: cur,
|
||
new_password: pw1
|
||
}, function (d) {
|
||
showMsg($msg, d.message, true);
|
||
$('#wbfCurrentPassword, #wbfNewPassword, #wbfNewPassword2').val('');
|
||
$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;
|
||
|
||
// Sofort-Vorschau — synchron, kein Callback, kein Warten
|
||
var objectUrl = URL.createObjectURL(file);
|
||
$('#wbfProfileAvatar').attr('src', objectUrl).css('opacity', '.6');
|
||
|
||
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) {
|
||
$('#wbfProfileAvatar').css('opacity', '1');
|
||
if (res.success) {
|
||
// Object-URL freigeben, endgültige Server-URL setzen
|
||
URL.revokeObjectURL(objectUrl);
|
||
var finalUrl = res.data.avatar_url + '?v=' + Date.now();
|
||
$('#wbfProfileAvatar').attr('src', finalUrl);
|
||
// Topbar-Avatar ebenfalls aktualisieren
|
||
$('.wbf-topbar__user img').attr('src', finalUrl);
|
||
$('.wbf-profile-widget__avatar img').attr('src', finalUrl);
|
||
}
|
||
},
|
||
error: function () {
|
||
$('#wbfProfileAvatar').css('opacity', '1');
|
||
}
|
||
});
|
||
});
|
||
|
||
// ── Banner-Upload ─────────────────────────────────────────────────────────
|
||
$(document).on('change', '#wbfBannerFile', function () {
|
||
var file = this.files[0];
|
||
if (!file) return;
|
||
|
||
// Sofort-Vorschau
|
||
var objectUrl = URL.createObjectURL(file);
|
||
var $wrap = $('#wbfProfileBannerWrap');
|
||
var $existing = $wrap.find('.wbf-profile-banner__img');
|
||
|
||
// Falls noch kein Banner-Bild existiert, eins einfügen
|
||
if ($existing.length === 0) {
|
||
$wrap.prepend('<img src="' + objectUrl + '" alt="" id="wbfProfileBanner" class="wbf-profile-banner__img" style="opacity:.4">');
|
||
} else {
|
||
$existing.attr('src', objectUrl).css('opacity', '.4');
|
||
}
|
||
|
||
var fd = new FormData();
|
||
fd.append('action', 'wbf_upload_banner');
|
||
fd.append('nonce', WBF.nonce);
|
||
fd.append('banner', file);
|
||
|
||
$.ajax({
|
||
url: WBF.ajax_url,
|
||
type: 'POST',
|
||
data: fd,
|
||
processData: false,
|
||
contentType: false,
|
||
success: function (res) {
|
||
var $img = $wrap.find('.wbf-profile-banner__img');
|
||
if (res.success) {
|
||
URL.revokeObjectURL(objectUrl);
|
||
$img.attr('src', res.data.banner_url + '?v=' + Date.now());
|
||
}
|
||
$img.css('opacity', '');
|
||
},
|
||
error: function () {
|
||
$wrap.find('.wbf-profile-banner__img').css('opacity', '');
|
||
}
|
||
});
|
||
});
|
||
|
||
/* ══════════════════════════════════════════════════════════
|
||
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 „' + $('<span>').text(q).html() + '“</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 = $('<img>').attr('src', n.actor_avatar || '').attr('alt', '').attr('class', 'wbf-dm-inbox-item__avatar')[0].outerHTML;
|
||
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">' + avatar + '</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>').attr('src', m.sender_avatar || '').attr('class', 'wbf-dm-inbox-item__avatar')[0].outerHTML;
|
||
}
|
||
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');
|
||
|
||
var wbfLogoutFired = false; // Guard gegen doppelten Logout-Call
|
||
|
||
function wbfDoLogout() {
|
||
if (wbfLogoutFired) return; // doppelten Aufruf verhindern
|
||
wbfLogoutFired = true;
|
||
clearTimeout(wbfIdleTimer);
|
||
clearTimeout(wbfWarnTimer);
|
||
clearInterval(wbfCountTimer);
|
||
$wbfToast.hide();
|
||
wbfPost('wbf_logout', { nonce: WBF.nonce }, function () {
|
||
location.reload();
|
||
});
|
||
}
|
||
|
||
function wbfShowWarning() {
|
||
wbfWarning = true;
|
||
var secs = 30;
|
||
$('#wbfIdleCountdown').text(secs);
|
||
$wbfToast.fadeIn(200);
|
||
// Countdown-Interval läuft bis 0 und ruft dann wbfDoLogout() auf —
|
||
// kein zusätzlicher setTimeout(wbfDoLogout) nötig (war die Ursache des Doppel-Logouts)
|
||
wbfCountTimer = setInterval(function () {
|
||
secs--;
|
||
$('#wbfIdleCountdown').text(Math.max(0, secs));
|
||
if (secs <= 0) {
|
||
clearInterval(wbfCountTimer);
|
||
wbfDoLogout();
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
function wbfResetIdleTimer() {
|
||
if (wbfWarning) return; // Nutzer hat aktiv Warnung bestätigt — nicht resetten
|
||
clearTimeout(wbfIdleTimer);
|
||
clearTimeout(wbfWarnTimer);
|
||
// Warnung 30 Sek. vor Ablauf zeigen
|
||
wbfWarnTimer = setTimeout(wbfShowWarning, wbfIdleMs - wbfWarnMs);
|
||
}
|
||
|
||
// "Bleiben" Button
|
||
$(document).on('click', '#wbfIdleStay', function () {
|
||
clearTimeout(wbfIdleTimer);
|
||
clearInterval(wbfCountTimer);
|
||
$wbfToast.fadeOut(200);
|
||
wbfWarning = false;
|
||
wbfLogoutFired = false; // Guard zurücksetzen
|
||
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>').attr('src', u.avatar_url || '').attr('width', '22').attr('height', '22').css({'border-radius':'50%','flex-shrink':'0'})[0].outerHTML
|
||
+ '<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>').attr('src', m.sender_avatar || '').attr('class', 'wbf-dm-msg__avatar')[0].outerHTML
|
||
+ '<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>').attr('src', conv.partner_avatar || '').attr('class', 'wbf-dm-inbox-item__avatar')[0].outerHTML
|
||
+ '<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>').attr('src', p.avatar_url || '').attr('class', 'wbf-dm-inbox-item__avatar')[0].outerHTML
|
||
+ '<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>').attr('src', u.avatar_url || '').attr('width', '22').attr('height', '22').css({'border-radius':'50%','flex-shrink':'0'})[0].outerHTML
|
||
+ $('<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();
|
||
});
|
||
|
||
|
||
|
||
// ── Discord-Integration (3-Schritt Verifikation) ─────────────────────────
|
||
|
||
var wbfDcStep = 1; // aktueller Schritt
|
||
|
||
function wbfDcMsg(text, color) {
|
||
var $m = $('#wbf-discord-msg');
|
||
$m.css('color', color || 'var(--c-muted)').html(text);
|
||
}
|
||
|
||
function wbfDcSetBadge(connected) {
|
||
var $badge = $('.wbf-connection-card--discord .wbf-connection-badge');
|
||
if (connected) {
|
||
$badge.removeClass('wbf-connection-badge--disconnected')
|
||
.addClass('wbf-connection-badge--connected')
|
||
.html('<i class="fas fa-check-circle"></i> Verbunden');
|
||
} else {
|
||
$badge.removeClass('wbf-connection-badge--connected')
|
||
.addClass('wbf-connection-badge--disconnected')
|
||
.html('<i class="fas fa-circle-xmark"></i> Nicht verbunden');
|
||
}
|
||
}
|
||
|
||
// Schritt 1 → Code senden
|
||
$(document).on('click', '#wbf-discord-send-code', function () {
|
||
var username = $('#wbf-discord-input').val().trim();
|
||
if (!username) { wbfDcMsg('<i class="fas fa-triangle-exclamation"></i> Bitte Benutzername eingeben.', '#f97316'); return; }
|
||
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Sende…');
|
||
wbfDcMsg('');
|
||
$.post(WBF.ajax_url, {
|
||
action: 'wbf_discord_send_code',
|
||
nonce: WBF.nonce,
|
||
discord_username: username,
|
||
}, function (res) {
|
||
$btn.prop('disabled', false).html('<i class="fab fa-discord"></i> Code senden');
|
||
if (res.success) {
|
||
wbfDcMsg('<i class="fas fa-check" style="color:#16a34a"></i> ' + (res.data.message || 'Code gesendet!'), '#16a34a');
|
||
$('#wbf-dc-step1').slideUp(200, function () { $('#wbf-dc-step2').slideDown(200); });
|
||
$('#wbf-discord-code-input').val('').focus();
|
||
wbfDcStep = 2;
|
||
} else {
|
||
wbfDcMsg('<i class="fas fa-circle-xmark"></i> ' + ((res.data && res.data.message) || 'Fehler.'), '#dc2626');
|
||
}
|
||
}).fail(function () {
|
||
$btn.prop('disabled', false).html('<i class="fab fa-discord"></i> Code senden');
|
||
wbfDcMsg('<i class="fas fa-circle-xmark"></i> Netzwerkfehler.', '#dc2626');
|
||
});
|
||
});
|
||
|
||
// Schritt 2 → Code bestätigen
|
||
$(document).on('click', '#wbf-discord-verify', function () {
|
||
var code = $('#wbf-discord-code-input').val().trim().toUpperCase();
|
||
if (code.length < 4) { wbfDcMsg('<i class="fas fa-triangle-exclamation"></i> Bitte Code eingeben.', '#f97316'); return; }
|
||
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Prüfe…');
|
||
$.post(WBF.ajax_url, {
|
||
action: 'wbf_discord_verify_code',
|
||
nonce: WBF.nonce,
|
||
verify_code: code,
|
||
}, function (res) {
|
||
$btn.prop('disabled', false).html('<i class="fas fa-check"></i> Bestätigen');
|
||
if (res.success) {
|
||
wbfDcMsg('<i class="fas fa-check-circle"></i> ' + (res.data.message || 'Verbunden!'), '#16a34a');
|
||
wbfDcSetBadge(true);
|
||
// UI auf "Verbunden"-Ansicht umschalten
|
||
var name = res.data.display_name || '';
|
||
$('#wbf-discord-form').slideUp(200);
|
||
// Verbunden-Info einfügen/aktualisieren
|
||
var $info = $('.wbf-discord-connected-info');
|
||
if ($info.length) {
|
||
$info.find('.wbf-discord-linked-name').html('<i class="fab fa-discord" style="color:#5865f2"></i> ' + $('<span>').text(name).html());
|
||
} else {
|
||
// Frisch laden damit die PHP-Struktur stimmt
|
||
setTimeout(function(){ location.reload(); }, 1200);
|
||
}
|
||
} else {
|
||
wbfDcMsg('<i class="fas fa-circle-xmark"></i> ' + ((res.data && res.data.message) || 'Fehler.'), '#dc2626');
|
||
}
|
||
}).fail(function () {
|
||
$btn.prop('disabled', false).html('<i class="fas fa-check"></i> Bestätigen');
|
||
wbfDcMsg('<i class="fas fa-circle-xmark"></i> Netzwerkfehler.', '#dc2626');
|
||
});
|
||
});
|
||
|
||
// Enter-Taste auf Code-Feld
|
||
$(document).on('keydown', '#wbf-discord-code-input', function (e) {
|
||
if (e.key === 'Enter') { e.preventDefault(); $('#wbf-discord-verify').trigger('click'); }
|
||
});
|
||
|
||
// Enter-Taste auf Username-Feld
|
||
$(document).on('keydown', '#wbf-discord-input', function (e) {
|
||
if (e.key === 'Enter') { e.preventDefault(); $('#wbf-discord-send-code').trigger('click'); }
|
||
});
|
||
|
||
// „Zurück" in Schritt 2
|
||
$(document).on('click', '#wbf-discord-code-back', function () {
|
||
$('#wbf-dc-step2').slideUp(200, function () { $('#wbf-dc-step1').slideDown(200); });
|
||
wbfDcMsg('');
|
||
wbfDcStep = 1;
|
||
});
|
||
|
||
// „Neu verknüpfen" bei bereits verbundenem Account
|
||
$(document).on('click', '#wbf-discord-relink', function () {
|
||
$('#wbf-discord-form').slideDown(200);
|
||
$('#wbf-discord-input').val('').focus();
|
||
});
|
||
|
||
// Verbindung trennen
|
||
$(document).on('click', '#wbf-discord-disconnect', function () {
|
||
if (!confirm('Discord-Verbindung wirklich trennen?')) return;
|
||
var $btn = $(this).prop('disabled', true);
|
||
$.post(WBF.ajax_url, {
|
||
action: 'wbf_save_discord',
|
||
nonce: WBF.nonce,
|
||
sub_action: 'disconnect',
|
||
}, function (res) {
|
||
$btn.prop('disabled', false);
|
||
if (res.success) {
|
||
wbfDcMsg('<i class="fas fa-check"></i> ' + (res.data.message || 'Getrennt.'), '#16a34a');
|
||
wbfDcSetBadge(false);
|
||
setTimeout(function () { location.reload(); }, 900);
|
||
} else {
|
||
wbfDcMsg('<i class="fas fa-circle-xmark"></i> ' + ((res.data && res.data.message) || 'Fehler.'), '#dc2626');
|
||
}
|
||
});
|
||
});
|
||
|
||
}(jQuery));
|
||
// Overwrite last line — Discord handlers appended via patch:
|