( function ( $ ) {
$( document ).ready( function () {
/**
* Initialise one search widget instance.
*
* All DOM queries are scoped to the specific form element so that
* multiple widgets on the same page never interfere with each other.
*
* @param {jQuery} $form - The .bbpc_search_form_wrapper element for this instance.
*/
function initSearchWidget( $form ) {
var inputId = $form.data( 'input-id' );
var resultId = $form.data( 'result-id' );
var $input = $( '#' + inputId );
var $result = $( '#' + resultId );
var $overlay = $( '.bbpc-search-overlay[data-widget-id="' + $form.data( 'widget-id' ) + '"]' );
if ( ! $input.length ) {
return;
}
/**
* Highlight the matching keyword inside result links.
*
* @param {string} keyword - Current search term.
*/
function highlightKeyword( keyword ) {
var isHighlight = $input.data( 'highlight' );
if ( ! isHighlight || ! keyword ) {
return;
}
var escaped = keyword.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
var regex = new RegExp( '(' + escaped + ')', 'gi' );
$result.find( '.search-result-item .bbpc-result-title' ).each( function () {
var text = $( this ).text();
var highlighted = text.replace( regex, '$1' );
$( this ).html( highlighted );
} );
}
/**
* Run an AJAX search request and populate the result container.
*
* @param {string} keyword - The search term.
* @param {string} postType - The post type to search.
*/
function runAjaxSearch( keyword, postType ) {
var ajaxUrl = forumax_localize_script.ajaxurl; /* eslint-disable-line camelcase */
var nonce = forumax_localize_script.nonce; /* eslint-disable-line camelcase */
$.ajax( {
url : ajaxUrl,
method : 'POST',
data : {
action : 'forumax_search_data_fetch',
keyword : keyword,
post_type : postType,
nonce : nonce
},
beforeSend: function () {
$form.find( '.spinner' ).css( 'display', 'inline-flex' );
},
success: function ( data ) {
$result.html( data ).addClass( 'ajax-search' );
$form.find( '.spinner' ).hide();
// Check for "no results" marker returned by the server.
var noResult = $result.find( '.tab-item.active.all-active' ).attr( 'data-noresult' );
if ( noResult ) {
var msg = noResult.replace( /-/g, ' ' );
$result.html( '
' + msg + '
' );
} else {
highlightKeyword( keyword );
}
}
} );
}
// --------------- Overlay ------------------
$input.add( $result ).add( $form.find( '.bbpc-search-keyword ul li a' ) ).on( 'click', function () {
$overlay.css( 'display', 'block' ).addClass( 'active' );
} );
$overlay.on( 'click', function () {
$overlay.css( 'display', 'none' ).removeClass( 'active' );
// Close the results box so the input returns to its pill shape.
$result.removeClass( 'ajax-search' ).empty();
} );
// --------------- Focus states ------------------
$form.on( 'focusin', function () {
if ( $( 'body' ).hasClass( 'body_dark' ) ) {
$input.addClass( 'input_focused' );
}
} );
$form.on( 'focusout', function () {
if ( $( 'body' ).hasClass( 'body_dark' ) ) {
if ( ! $result.hasClass( 'ajax-search' ) ) {
$input.removeClass( 'input_focused' );
}
}
} );
$input.on( 'focus', function () {
$( 'body' ).addClass( 'bbpc-search-open' );
$form.css( { position: 'relative', 'z-index': '999' } );
} );
$overlay.on( 'click', function () {
$( 'body' ).removeClass( 'bbpc-search-open' );
$form.css( { 'z-index': '', position: '' } );
} );
// --------------- Clear input ------------------
$input.on( 'input', function () {
if ( this.value === '' ) {
$result.removeClass( 'ajax-search' ).empty();
}
} );
// --------------- Result type tabs ------------------
// Delegated so it works on the markup injected by each AJAX response.
$result.on( 'click', '.bbpc-tab', function () {
var tab = $( this ).data( 'tab' );
$result.find( '.bbpc-tab' ).removeClass( 'active' );
$( this ).addClass( 'active' );
if ( tab === 'all' ) {
$result.find( '.bbpc-result-group' ).show();
} else {
$result.find( '.bbpc-result-group' ).hide();
$result.find( '.bbpc-result-group[data-type="' + tab + '"]' ).show();
}
} );
// --------------- AJAX live search ------------------
var isAjaxSearch = $input.data( 'ajax-search' );
if ( isAjaxSearch ) {
var debounceTimer;
$input.on( 'keyup', function () {
$form.find( '.bbpc-not-found-text' ).hide();
clearTimeout( debounceTimer );
var keyword = $( this ).val();
var postType = $( this ).data( 'post-type' ) || '';
debounceTimer = setTimeout( function () {
if ( keyword !== '' ) {
runAjaxSearch( keyword, postType );
}
}, 1000 );
} );
}
// --------------- Keyword pill click ------------------
$form.find( '.bbpc-search-keyword ul li a' ).on( 'click', function ( e ) {
e.preventDefault();
var keyword = $( this ).text().trim();
var postType = $input.data( 'post-type' ) || '';
$input.val( keyword ).focus();
if ( ! isAjaxSearch || keyword === '' ) {
return;
}
runAjaxSearch( keyword, postType );
} );
}
// --------------- Bootstrap: find every widget instance ------------------
$( '.bbpc_search_form_wrapper[data-widget-id]' ).each( function () {
initSearchWidget( $( this ) );
} );
// --------------- TinyMCE dark mode theming ------------------
/**
* Style a single TinyMCE editor's iframe body to match the current
* light/dark mode. Toolbar, statusbar and popup dialogs are themed
* via CSS (body.body_dark selectors) since they live in the parent
* document, but the iframe body is a separate document that CSS
* cascades can't reach.
*
* @param {Object} editor - A tinymce.Editor instance.
*/
function frmxThemeTinyMCEEditor( editor ) {
if ( ! editor || ! editor.getBody ) {
return;
}
var isDark = document.body.classList.contains( 'body_dark' );
var body = editor.getBody();
if ( body ) {
body.style.backgroundColor = isDark ? '#1e1e2a' : '#fff';
body.style.fontFamily = "'Inter', 'Roboto', -apple-system, sans-serif";
body.style.fontSize = '14px';
body.style.lineHeight = '1.6';
body.style.color = isDark ? '#e4e6eb' : '#1a1a2e';
body.style.padding = '14px 18px';
}
var container = editor.getContainer();
if ( container ) {
container.style.border = 'none';
container.style.boxShadow = 'none';
}
}
function frmxThemeAllTinyMCEEditors() {
if ( typeof tinymce === 'undefined' ) {
return;
}
tinymce.editors.forEach( frmxThemeTinyMCEEditor );
}
if ( typeof tinymce !== 'undefined' ) {
tinymce.on( 'AddEditor', function ( e ) {
e.editor.on( 'init', function () {
frmxThemeTinyMCEEditor( this );
} );
} );
}
// Re-theme open editors whenever dark mode is toggled, regardless
// of which switcher/script flips the `body_dark` class.
if ( window.MutationObserver ) {
new MutationObserver( frmxThemeAllTinyMCEEditors )
.observe( document.body, { attributes: true, attributeFilter: [ 'class' ] } );
}
} );
} )( jQuery ); /* eslint-disable-line no-undef */