/* global wp, BP_Nouveau, JSON */ /* jshint devel: true */ /* jshint browser: true */ /* @since 3.0.0 */ /* @version 10.0.0 */ window.wp = window.wp || {}; window.bp = window.bp || {}; ( function( bp, $ ) { // Bail if not set. if ( typeof BP_Nouveau === 'undefined' ) { return; } /** * [Nouveau description] * @type {Object} */ bp.Nouveau = { /** * [start description] * @return {[type]} [description] */ start: function() { // Setup globals. this.setupGlobals(); // Adjust Document/Forms properties. this.prepareDocument(); // Init the BuddyPress objects. this.initObjects(); // Set BuddyPress HeartBeat. this.setHeartBeat(); // Listen to events ("Add hooks!"). this.addListeners(); }, /** * [setupGlobals description] * @return {[type]} [description] */ setupGlobals: function() { this.ajax_request = null; // Object Globals. this.objects = $.map( BP_Nouveau.objects, function( value ) { return value; } ); this.objectNavParent = BP_Nouveau.object_nav_parent; // HeartBeat Global. this.heartbeat = wp.heartbeat || {}; // An object containing each query var. this.querystring = this.getLinkParams(); }, /** * [prepareDocument description] * @return {[type]} [description] */ prepareDocument: function() { // Remove the no-js class and add the js one. if ( $( 'body' ).hasClass( 'no-js' ) ) { $('body').removeClass( 'no-js' ).addClass( 'js' ); } // Log Warnings into the console instead of the screen. if ( BP_Nouveau.warnings && 'undefined' !== typeof console && console.warn ) { $.each( BP_Nouveau.warnings, function( w, warning ) { console.warn( warning ); } ); } // Remove the directory title if there's a widget containing it. if ( $( '.buddypress_object_nav .widget-title' ).length ) { var text = $( '.buddypress_object_nav .widget-title' ).html(); $( 'body' ).find( '*:contains("' + text + '")' ).each( function( e, element ) { if ( ! $( element ).hasClass( 'widget-title' ) && text === $( element ).html() && ! $( element ).is( 'a' ) ) { $( element ).remove(); } } ); } }, /** Helpers *******************************************************************/ /** * [getStorage description] * @param {[type]} type [description] * @param {[type]} property [description] * @return {[type]} [description] */ getStorage: function( type, property ) { var store = sessionStorage.getItem( type ); if ( store ) { store = JSON.parse( store ); } else { store = {}; } if ( undefined !== property ) { return store[property] || false; } return store; }, /** * [setStorage description] * @param {[type]} type [description] * @param {[type]} property [description] * @param {[type]} value [description] */ setStorage: function( type, property, value ) { var store = this.getStorage( type ); if ( undefined === value && undefined !== store[ property ] ) { delete store[ property ]; } else { // Set property. store[ property ] = value; } sessionStorage.setItem( type, JSON.stringify( store ) ); return sessionStorage.getItem( type ) !== null; }, /** * [getLinkParams description] * @param {[type]} url [description] * @param {[type]} param [description] * @return {[type]} [description] */ getLinkParams: function( url, param ) { var qs; if ( url ) { qs = ( -1 !== url.indexOf( '?' ) ) ? '?' + url.split( '?' )[1] : ''; } else { qs = document.location.search; } if ( ! qs ) { return null; } var params = qs.replace( /(^\?)/, '' ).split( '&' ).map( function( n ) { return n = n.split( '=' ), this[n[0]] = n[1], this; }.bind( {} ) )[0]; if ( param ) { return params[param]; } return params; }, /** * URL Decode a query variable. * * @param {string} qv The query variable to decode. * @param {object} chars The specific characters to use. Optional. * @return {string} The URL decoded variable. */ urlDecode: function( qv, chars ) { var specialChars = chars || { amp: '&', lt: '<', gt: '>', quot: '"', '#039': '\'' }; return decodeURIComponent( qv.replace( /\+/g, ' ' ) ).replace( /&([^;]+);/g, function( v, q ) { return specialChars[q] || ''; } ); }, /** * [ajax description] * @param {[type]} post_data [description] * @param {[type]} object [description] * @return {[type]} [description] */ ajax: function( post_data, object ) { if ( this.ajax_request ) { this.ajax_request.abort(); } // Extend posted data with stored data and object nonce. var postData = $.extend( {}, bp.Nouveau.getStorage( 'bp-' + object ), { nonce: BP_Nouveau.nonces[object] }, post_data ); if ( undefined !== BP_Nouveau.customizer_settings ) { postData.customized = BP_Nouveau.customizer_settings; } this.ajax_request = $.post( BP_Nouveau.ajaxurl, postData, 'json' ); return this.ajax_request; }, inject: function( selector, content, method ) { if ( ! $( selector ).length || ! content ) { return; } /** * How the content should be injected in the selector * * possible methods are * - reset: the selector will be reset with the content * - append: the content will be added after selector's content * - prepend: the content will be added before selector's content */ method = method || 'reset'; if ( 'append' === method ) { $( selector ).append( content ); } else if ( 'prepend' === method ) { $( selector ).prepend( content ); } else { $( selector ).html( content ); } if ( 'undefined' !== typeof bp_mentions || 'undefined' !== typeof bp.mentions ) { $( '.bp-suggestions' ).bp_mentions( bp.mentions.users ); } }, /** * [objectRequest description] * @param {[type]} data [description] * @return {[type]} [description] */ objectRequest: function( data ) { var postdata = {}, self = this; data = $.extend( { object : '', scope : null, filter : null, target : '#buddypress [data-bp-list]', search_terms : '', page : 1, extras : null, caller : null, template : null, method : 'reset' }, data ); // Do not request if we don't have the object or the target to inject results into. if ( ! data.object || ! data.target ) { return; } // Prepare the search terms for the request. if ( data.search_terms ) { data.search_terms = data.search_terms.replace( //g, '>' ); } // Set session's data. if ( null !== data.scope ) { this.setStorage( 'bp-' + data.object, 'scope', data.scope ); } if ( null !== data.filter ) { this.setStorage( 'bp-' + data.object, 'filter', data.filter ); } if ( null !== data.extras ) { this.setStorage( 'bp-' + data.object, 'extras', data.extras ); } /* Set the correct selected nav and filter */ $( this.objectNavParent + ' [data-bp-object]' ).each( function() { $( this ).removeClass( 'selected loading' ); } ); $( this.objectNavParent + ' [data-bp-scope="' + data.scope + '"], #object-nav li.current' ).addClass( 'selected loading' ); $( '#buddypress [data-bp-filter="' + data.object + '"] option[value="' + data.filter + '"]' ).prop( 'selected', true ); if ( 'friends' === data.object || 'friend_requests' === data.object || 'group_members' === data.object ) { data.template = data.object; data.object = 'members'; } else if ( 'group_requests' === data.object ) { data.object = 'groups'; data.template = 'group_requests'; } else if ( 'notifications' === data.object ) { data.object = 'members'; data.template = 'member_notifications'; } postdata = $.extend( { action: data.object + '_filter' }, data ); return this.ajax( postdata, data.object ).done( function( response ) { if ( false === response.success ) { return; } var selectedObjectNavParent = $( self.objectNavParent + ' [data-bp-scope="' + data.scope + '"]' ); selectedObjectNavParent.removeClass( 'loading' ); if ( response.data && response.data.totalItems && response.data.navLabel ) { selectedObjectNavParent.find( 'a' ).first().html( response.data.navLabel + ' ' ).append( $( '' ).addClass( 'count' ).html( response.data.totalItems ) ); } if ( 'reset' !== data.method ) { self.inject( data.target, response.data.contents, data.method ); $( data.target ).trigger( 'bp_ajax_' + data.method, $.extend( data, { response: response.data } ) ); } else { /* animate to top if called from bottom pagination */ if ( data.caller === 'pag-bottom' && $( '#subnav' ).length ) { var top = $('#subnav').parent(); $( 'html,body' ).animate( { scrollTop: top.offset().top }, 'slow', function() { $( data.target ).fadeOut( 100, function() { self.inject( this, response.data.contents, data.method ); $( this ).fadeIn( 100, 'swing', function(){ // Inform other scripts the list of objects has been refreshed. $( data.target ).trigger( 'bp_ajax_request', $.extend( data, { response: response.data } ) ); } ); } ); } ); } else { $( data.target ).fadeOut( 100, function() { self.inject( this, response.data.contents, data.method ); $( this ).fadeIn( 100, 'swing', function(){ // Inform other scripts the list of objects has been refreshed. $( data.target ).trigger( 'bp_ajax_request', $.extend( data, { response: response.data } ) ); } ); } ); } } } ); }, /** * [initObjects description] * @return {[type]} [description] */ initObjects: function() { var self = this, objectData = {}, queryData = {}, scope = 'all', search_terms = '', extras = null, filter = null; $.each( this.objects, function( o, object ) { objectData = self.getStorage( 'bp-' + object ); if ( undefined !== objectData.scope ) { scope = objectData.scope; } // Notifications always need to start with Newest ones. if ( undefined !== objectData.extras && 'notifications' !== object ) { extras = objectData.extras; } if ( $( '#buddypress [data-bp-filter="' + object + '"]' ).length ) { if ( '-1' !== $( '#buddypress [data-bp-filter="' + object + '"]' ).val() && '0' !== $( '#buddypress [data-bp-filter="' + object + '"]' ).val() ) { filter = $( '#buddypress [data-bp-filter="' + object + '"]' ).val(); } else if ( undefined !== objectData.filter ) { filter = objectData.filter, $( '#buddypress [data-bp-filter="' + object + '"] option[value="' + filter + '"]' ).prop( 'selected', true ); } } if ( $( this.objectNavParent + ' [data-bp-object="' + object + '"]' ).length ) { $( this.objectNavParent + ' [data-bp-object="' + object + '"]' ).each( function() { $( this ).removeClass( 'selected' ); } ); $( this.objectNavParent + ' [data-bp-scope="' + object + '"], #object-nav li.current' ).addClass( 'selected' ); } // Check the querystring to eventually include the search terms. if ( null !== self.querystring ) { if ( undefined !== self.querystring[ object + '_search'] ) { search_terms = self.querystring[ object + '_search']; } else if ( undefined !== self.querystring.s ) { search_terms = self.querystring.s; } if ( search_terms ) { search_terms = self.urlDecode( search_terms ); $( '#buddypress [data-bp-search="' + object + '"] input[type=search]' ).val( search_terms ); } } if ( $( '#buddypress [data-bp-list="' + object + '"]' ).length ) { queryData = { object : object, scope : scope, filter : filter, search_terms : search_terms, extras : extras }; // Populate the object list. self.objectRequest( queryData ); } } ); }, /** * [setHeartBeat description] */ setHeartBeat: function() { if ( typeof BP_Nouveau.pulse === 'undefined' || ! this.heartbeat ) { return; } this.heartbeat.interval( Number( BP_Nouveau.pulse ) ); // Extend "send" with BuddyPress namespace. $.fn.extend( { 'heartbeat-send': function() { return this.bind( 'heartbeat-send.buddypress' ); } } ); // Extend "tick" with BuddyPress namespace. $.fn.extend( { 'heartbeat-tick': function() { return this.bind( 'heartbeat-tick.buddypress' ); } } ); }, /** Event Listeners ***********************************************************/ /** * [addListeners description] */ addListeners: function() { // Disabled inputs. $( '[data-bp-disable-input]' ).on( 'change', this.toggleDisabledInput ); // HeartBeat Send and Receive. $( document ).on( 'heartbeat-send.buddypress', this.heartbeatSend ); $( document ).on( 'heartbeat-tick.buddypress', this.heartbeatTick ); // Refreshing. $( this.objectNavParent + ' .bp-navs' ).on( 'click', 'a', this, this.scopeQuery ); // Filtering. $( '#buddypress [data-bp-filter]' ).on( 'change', this, this.filterQuery ); // Searching. $( '#buddypress [data-bp-search]' ).on( 'submit', 'form', this, this.searchQuery ); $( '#buddypress [data-bp-search] form' ).on( 'search', 'input[type=search]', this.resetSearch ); // Buttons. $( '#buddypress [data-bp-list], #buddypress #item-header' ).on( 'click', '[data-bp-btn-action]', this, this.buttonAction ); // Close notice. $( '#buddypress [data-bp-close]' ).on( 'click', this, this.closeNotice ); // Pagination. $( '#buddypress [data-bp-list]' ).on( 'click', '[data-bp-pagination] a', this, this.paginateAction ); // Password updates. if ( BP_Nouveau.bpPasswordVerify && BP_Nouveau.bpPasswordVerify.requiredPassStrength ) { $( '#pass1' ).on( 'input pwupdate', this.checkPassStrength ); } }, /** Event Callbacks ***********************************************************/ /** * Toggle the availability of Delete My account button. * * @param {Object} event The change event. */ toggleDisabledInput: function( event ) { var target = $( event.currentTarget ), disabledControl = '#' + target.data( 'bp-disable-input' ), isChecked = target.prop( 'checked' ); target.removeClass( 'enabled disabled' ); if ( isChecked ) { target.addClass( 'enabled' ); $( disabledControl ).prop( 'disabled', false ); } else { $( disabledControl ).prop( 'disabled', true ); target.addClass( 'disabled' ); } }, /** * [heartbeatSend description] * @param {[type]} event [description] * @param {[type]} data [description] * @return {[type]} [description] */ heartbeatSend: function( event, data ) { // Add an heartbeat send event to possibly any BuddyPress pages. $( '#buddypress' ).trigger( 'bp_heartbeat_send', data ); }, /** * [heartbeatTick description] * @param {[type]} event [description] * @param {[type]} data [description] * @return {[type]} [description] */ heartbeatTick: function( event, data ) { // Add an heartbeat send event to possibly any BuddyPress pages. $( '#buddypress' ).trigger( 'bp_heartbeat_tick', data ); }, /** * [queryScope description] * @param {[type]} event [description] * @return {[type]} [description] */ scopeQuery: function( event ) { var self = event.data, target = $( event.currentTarget ).parent(), scope = 'all', object, filter = null, search_terms = ''; if ( target.hasClass( 'no-ajax' ) || $( event.currentTarget ).hasClass( 'no-ajax' ) || ! target.attr( 'data-bp-scope' ) ) { return event; } scope = target.data( 'bp-scope' ); object = target.data( 'bp-object' ); if ( ! scope || ! object ) { return event; } // Stop event propagation. event.preventDefault(); filter = $( '#buddypress' ).find( '[data-bp-filter="' + object + '"]' ).first().val(); if ( $( '#buddypress [data-bp-search="' + object + '"] input[type=search]' ).length ) { search_terms = $( '#buddypress [data-bp-search="' + object + '"] input[type=search]' ).val(); } // Remove the New count on dynamic tabs. if ( target.hasClass( 'dynamic' ) ) { target.find( 'a span' ).html(''); } self.objectRequest( { object : object, scope : scope, filter : filter, search_terms : search_terms, page : 1 } ); }, /** * [filterQuery description] * @param {[type]} event [description] * @return {[type]} [description] */ filterQuery: function( event ) { var self = event.data, object = $( event.target ).data( 'bp-filter' ), scope = 'all', filter = $( event.target ).val(), search_terms = '', template = null; if ( ! object ) { return event; } if ( $( self.objectNavParent + ' [data-bp-object].selected' ).length ) { scope = $( self.objectNavParent + ' [data-bp-object].selected' ).data( 'bp-scope' ); } if ( $( '#buddypress [data-bp-search="' + object + '"] input[type=search]' ).length ) { search_terms = $( '#buddypress [data-bp-search="' + object + '"] input[type=search]' ).val(); } self.objectRequest( { object : object, scope : scope, filter : filter, search_terms : search_terms, page : 1, template : template } ); }, /** * [searchQuery description] * @param {[type]} event [description] * @return {[type]} [description] */ searchQuery: function( event ) { var self = event.data, object, scope = 'all', filter = null, template = null, search_terms = ''; if ( $( event.delegateTarget ).hasClass( 'no-ajax' ) || undefined === $( event.delegateTarget ).data( 'bp-search' ) ) { return event; } // Stop event propagation. event.preventDefault(); object = $( event.delegateTarget ).data( 'bp-search' ); filter = $( '#buddypress' ).find( '[data-bp-filter="' + object + '"]' ).first().val(); search_terms = $( event.delegateTarget ).find( 'input[type=search]' ).first().val(); if ( $( self.objectNavParent + ' [data-bp-object]' ).length ) { scope = $( self.objectNavParent + ' [data-bp-object="' + object + '"].selected' ).data( 'bp-scope' ); } self.objectRequest( { object : object, scope : scope, filter : filter, search_terms : search_terms, page : 1, template : template } ); }, /** * [showSearchSubmit description] * @param {[type]} event [description] * @return {[type]} [description] */ showSearchSubmit: function( event ) { $( event.delegateTarget ).find( '[type=submit]' ).addClass( 'bp-show' ); if( $('[type=submit]').hasClass( 'bp-hide' ) ) { $( '[type=submit]' ).removeClass( 'bp-hide' ); } }, /** * [resetSearch description] * @param {[type]} event [description] * @return {[type]} [description] */ resetSearch: function( event ) { if ( ! $( event.target ).val() ) { $( event.delegateTarget ).submit(); } else { $( event.delegateTarget ).find( '[type=submit]' ).show(); } }, /** * [buttonAction description] * @param {[type]} event [description] * @return {[type]} [description] */ buttonAction: function( event ) { var self = event.data, target = $( event.currentTarget ), action = target.data( 'bp-btn-action' ), nonceUrl = target.data( 'bp-nonce' ), item = target.closest( '[data-bp-item-id]' ), item_id = item.data( 'bp-item-id' ), item_inner = target.closest('.list-wrap'), object = item.data( 'bp-item-component' ), nonce = ''; // Simply let the event fire if we don't have needed values. if ( ! action || ! item_id || ! object ) { return event; } // Stop event propagation. event.preventDefault(); if ( ( undefined !== BP_Nouveau[ action + '_confirm'] && false === window.confirm( BP_Nouveau[ action + '_confirm'] ) ) || target.hasClass( 'pending' ) ) { return false; } // If the nonceUrl is not set, use the `href` attribute. if ( ! nonceUrl ) { nonceUrl = target.prop( 'href' ); } // Set the nonce. nonce = self.getLinkParams( nonceUrl, '_wpnonce' ); // Unfortunately unlike groups // Friends actions does not match the wpnonce var friends_actions_map = { is_friend : 'remove_friend', not_friends : 'add_friend', pending : 'withdraw_friendship', accept_friendship : 'accept_friendship', reject_friendship : 'reject_friendship' }; if ( 'members' === object && undefined !== friends_actions_map[ action ] ) { action = friends_actions_map[ action ]; object = 'friends'; } // Add a pending class to prevent queries while we're processing the action. target.addClass( 'pending loading' ); self.ajax( { action : object + '_' + action, item_id : item_id, _wpnonce : nonce }, object ).done( function( response ) { if ( false === response.success ) { item_inner.prepend( response.data.feedback ); target.removeClass( 'pending loading' ); item.find( '.bp-feedback' ).fadeOut( 6000 ); } else { // Specific cases for groups. if ( 'groups' === object ) { // Group's header button. if ( undefined !== response.data.is_group && response.data.is_group ) { return window.location.reload(); } } // User's groups invitations screen & User's friend screens. if ( undefined !== response.data.is_user && response.data.is_user ) { target.parent().html( response.data.feedback ); item.fadeOut( 1500 ); return; } // Update count. if ( $( self.objectNavParent + ' [data-bp-scope="personal"]' ).length ) { var personal_count = Number( $( self.objectNavParent + ' [data-bp-scope="personal"] span' ).html() ) || 0; if ( -1 !== $.inArray( action, ['leave_group', 'remove_friend'] ) ) { personal_count -= 1; } else if ( -1 !== $.inArray( action, ['join_group'] ) ) { personal_count += 1; } if ( personal_count < 0 ) { personal_count = 0; } $( self.objectNavParent + ' [data-bp-scope="personal"] span' ).html( personal_count ); } target.parent().replaceWith( response.data.contents ); } } ); }, /** * [closeNotice description] * @param {[type]} event [description] * @return {[type]} [description] */ closeNotice: function( event ) { var closeBtn = $( event.currentTarget ); event.preventDefault(); // Make sure cookies are removed. if ( 'clear' === closeBtn.data( 'bp-close' ) ) { if ( undefined !== $.cookie( 'bp-message' ) ) { $.removeCookie( 'bp-message' ); } if ( undefined !== $.cookie( 'bp-message-type' ) ) { $.removeCookie( 'bp-message-type' ); } } // @todo other cases... // Dismissing site-wide notices. if ( closeBtn.closest( '.bp-feedback' ).hasClass( 'bp-sitewide-notice' ) ) { bp.Nouveau.ajax( { action : 'messages_dismiss_sitewide_notice' }, 'messages' ); } // Remove the notice. closeBtn.closest( '.bp-feedback' ).remove(); }, paginateAction: function( event ) { var self = event.data, navLink = $( event.currentTarget ), pagArg, scope = null, object, objectData, filter = null, search_terms = null, extras = null; pagArg = navLink.closest( '[data-bp-pagination]' ).data( 'bp-pagination' ) || null; if ( null === pagArg ) { return event; } event.preventDefault(); object = $( event.delegateTarget ).data( 'bp-list' ) || null; // Set the scope & filter. if ( null !== object ) { objectData = self.getStorage( 'bp-' + object ); if ( undefined !== objectData.scope ) { scope = objectData.scope; } if ( undefined !== objectData.filter ) { filter = objectData.filter; } if ( undefined !== objectData.extras ) { extras = objectData.extras; } } // Set the search terms. if ( $( '#buddypress [data-bp-search="' + object + '"] input[type=search]' ).length ) { search_terms = $( '#buddypress [data-bp-search="' + object + '"] input[type=search]' ).val(); } var queryData = { object : object, scope : scope, filter : filter, search_terms : search_terms, extras : extras, page : self.getLinkParams( navLink.prop( 'href' ), pagArg ) || 1 }; // Request the page. self.objectRequest( queryData ); }, checkPassStrength: function( event ) { var bpPasswordVerify = BP_Nouveau.bpPasswordVerify, strength, requiredStrength = parseInt( bpPasswordVerify.requiredPassStrength, 10 ), pass1 = $( event.currentTarget ).val(), pass2 = $( '#pass2' ).val(), currentForm = $( event.currentTarget ).closest( 'form' ); // wp.passwordStrength.userInputBlacklist() has been deprecated in WP 5.5.0. if ( 'function' === typeof wp.passwordStrength.userInputDisallowedList ) { strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputDisallowedList(), pass2 ); } else { strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputBlacklist(), pass2 ); } if ( requiredStrength && 4 >= requiredStrength ) { var passwordWarningContainer = $( currentForm ).find( '#password-warning' ); if ( strength < requiredStrength ) { if ( ! $( passwordWarningContainer ).length ) { $( event.currentTarget ).before( $( '

' ).prop( 'id', 'password-warning' ) .addClass( 'description' ) ); } $( passwordWarningContainer ).html( bpPasswordVerify.tooWeakPasswordWarning ); } else if ( $( passwordWarningContainer ).length ) { $( passwordWarningContainer ).remove(); } if ( ! $( currentForm ).find( '#password-strength-score' ).length ) { $( currentForm ).prepend( $('').prop( { id: 'password-strength-score', type: 'hidden', 'name': '_password_strength_score' } ) ); } $( '#password-strength-score' ).val( strength ); if ( requiredStrength > strength ) { $( '.pw-weak' ).remove(); } } } }; // Launch BP Nouveau. bp.Nouveau.start(); } )( window.bp, jQuery );