( 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 */