import $ from 'jquery';
import 'jqueryui';
import { hideUp, showUp, hideLeft, showLeft } from '../../admin-blocks/twrpb-hidden/twrpb-hidden';
declare const wpApiSettings: any;
// #region -- Show/Hide Posts List.
const postTypeSelect = $( '#twrpb-posts-settings__js-filter-type' );
const postVisualList = $( '#twrpb-posts-settings__js-posts-list' );
const searchingWrapper = $( '#twrpb-posts-settings__js-posts-search-wrap' );
const includeNote = $( '#twrpb-setting-note__post_settings_note' );
$( hideOrShowVisualList );
$( document ).on( 'change', '#twrpb-posts-settings__js-filter-type', hideOrShowVisualList );
/**
* Hide or show the visual list and the form input to search for users.
*/
function hideOrShowVisualList() {
const postsTypeVal = postTypeSelect.val();
if ( 'NA' === postsTypeVal ) {
hideUp( searchingWrapper );
hideUp( postVisualList );
} else {
showUp( searchingWrapper );
showUp( postVisualList );
}
if ( 'IP' === postsTypeVal ) {
showUp( includeNote );
} else {
hideUp( includeNote );
}
}
// #endregion -- Show/Hide Posts List.
// #region -- Manage Posts Search
/**
* The search input where administrators will search for posts.
*/
const postsSearchInput = $( '#twrpb-posts-settings__js-posts-search' );
$( initializeAutoComplete );
/**
* Initialize the search input, when a user enter some characters, it will
* automatically search and display the options.
*/
function initializeAutoComplete() {
if ( postsSearchInput.length === 0 ) {
return;
}
postsSearchInput.autocomplete( {
source: showSearchedPosts,
minLength: 2,
} ).autocomplete( 'widget' ).addClass( 'twrpb-jqueryui-autocomplete-menu' );
}
/**
* Search for the posts and append the results into the autocomplete selector.
*/
async function showSearchedPosts( request: any, sendToControl: CallableFunction ) {
const wpApiURL = wpApiSettings.root + wpApiSettings.versionString;
const fetchUrl = wpApiURL + `search?_fields=id,title&search=${ request.term }&page=1&per_page=10`;
const fetchedResult = await fetch( fetchUrl );
const posts = await fetchedResult.json();
if ( ! ( posts instanceof Array ) ) {
console.error( 'TWRP error when fetching posts.' ); // eslint-disable-line
}
const postsToSend = [];
for ( let i = 0; i < posts.length; i++ ) {
postsToSend.push( {
value: decodeHtml( posts[ i ].title ),
label: decodeHtml( posts[ i ].title ),
id: posts[ i ].id,
} );
}
sendToControl( postsToSend );
}
// #endregion -- Manage Posts Search
// #region -- Add Post.
const postsIdsInput = $( '#twrpb-posts-settings__js-posts-ids' );
/**
* The post attribute name that hold the Id of a visual item.
*/
const postIdAttrName = 'data-post-id';
/**
* The template for a post item, to be appended to the visual list. Note that
* we have a similar template in the PHP file, so a change here will require
* also a change there, and vice-versa.
*/
const postVisualItem = $(
'
' +
'' +
'' +
'
'
);
$( document ).on( 'click', '#twrpb-posts-settings__js-posts-add-btn', handleAddPostClick );
$( document ).on( 'keypress', '#twrpb-posts-settings__js-posts-search', handleSearchInputKeypress );
/**
* When a user click "enter", add the current selected author to the list.
*/
function handleSearchInputKeypress( event: JQueryEventObject ): void {
const keycode = ( event.keyCode ? event.keyCode : event.which );
if ( keycode !== 13 ) {
return;
}
event.preventDefault();
const autocompleteInstance: any = postsSearchInput.autocomplete( 'instance' );
let id: string, name: string;
try {
const selectedItem = autocompleteInstance.selectedItem;
id = selectedItem.id;
name = selectedItem.label;
} catch ( error ) {
return;
}
addPost( id, name );
}
/**
* Handles the click on "Add post" button.
*/
function handleAddPostClick(): void {
const autocompleteInstance: any = postsSearchInput.autocomplete( 'instance' );
let id: string, name: string;
try {
const selectedItem = autocompleteInstance.selectedItem;
id = selectedItem.id;
name = selectedItem.label;
} catch ( error ) {
return;
}
addPost( id, name );
}
/**
* Add a post to the list of selected posts(both visual, and in the hidden
* input).
*/
function addPost( id:number|string, name: string ): void {
_addPostToVisualList( id, name );
_addPostToHiddenInput( id );
removeOrAddNoPostsText();
}
/**
* Add a post to the visual list.
*/
function _addPostToVisualList( id: number|string, name: string ): void {
if ( postExistInVisualList( id ) ) {
return;
}
const newAuthorItem = postVisualItem.clone();
const removeButtonLabel = getRemoveButtonAriaLabel().replace( '%s', sanitizePostName( name ) );
newAuthorItem.find( '.twrpb-posts-settings__post-item-title' ).text( sanitizePostName( name ) );
newAuthorItem.find( '.twrpb-display-list__item-remove-btn' ).attr( 'aria-label', removeButtonLabel );
newAuthorItem.attr( postIdAttrName, id );
postVisualList.append( newAuthorItem );
showLeft( newAuthorItem, 'hide_first' );
}
/**
* Add a post to the list of hidden input.
*/
function _addPostToHiddenInput( id: number|string ): void {
if ( postExistInHiddenInput( id ) ) {
return;
}
id = String( id );
const previousVal = postsIdsInput.val();
let newVal = '';
if ( previousVal ) {
newVal = previousVal + ';' + id;
} else {
newVal = id;
}
postsIdsInput.val( newVal );
}
// #endregion -- Add Post.
// #region -- Remove post
$( document ).on( 'click', '.twrpb-posts-settings__js-post-remove-btn', handleRemovePost );
/**
* Handle the removing of the posts from the selected list.
*/
function handleRemovePost(): void {
let id: string|number = $( this ).closest( '[' + postIdAttrName + ']' ).attr( postIdAttrName );
id = Number( id );
if ( ! ( id > 0 ) ) {
return;
}
removePost( id );
}
/**
* Removes a post to the list of selected posts(both visual, and in the
* hidden input).
*/
function removePost( id:number|string ): void {
_removePostFromVisualList( id );
_removePostFromHiddenInput( id );
removeOrAddNoPostsText();
}
/**
* Removes a post from the visual list, based on id.
*/
function _removePostFromVisualList( id: string|number ): void {
const itemToRemove = postVisualList.find( '[' + postIdAttrName + '="' + id + '"]' );
itemToRemove.removeAttr( postIdAttrName );
hideLeft( itemToRemove, 'remove' );
}
/**
* Removes a post from the hidden input list.
*/
function _removePostFromHiddenInput( id: number|string ): void {
const currentValue = String( postsIdsInput.val() );
id = String( id );
const postsIds = currentValue.split( ';' );
const indexOfId = postsIds.indexOf( id );
if ( indexOfId !== -1 ) {
postsIds.splice( indexOfId, 1 );
postsIdsInput.val( postsIds.join( ';' ) );
}
}
// #endregion -- Remove post
// #region -- Manage the "No Authors Added" Text.
/**
* Contains the HTML Element that will be inserted/removed as necessary.
*/
const noPostsText = $( '#twrpb-posts-settings__js-no-posts-selected' );
$( removeOrAddNoPostsText );
/**
* Remove or add "No posts selected" text if necessary.
*/
function removeOrAddNoPostsText() {
_removeNoPostsTextIfNecessary();
_addNoPostsTextIfNecessary();
}
/**
* If Necessary, removes the "No Posts selected" text.
*/
function _removeNoPostsTextIfNecessary() {
const haveItems = ( postVisualList.find( `[${ postIdAttrName }]` ).length > 0 );
if ( haveItems ) {
hideLeft( noPostsText );
}
}
/**
* If Necessary, adds the "No Posts selected" text.
*/
function _addNoPostsTextIfNecessary() {
const haveItems = ( postVisualList.find( `[${ postIdAttrName }]` ).length > 0 );
if ( ! haveItems ) {
showLeft( noPostsText );
}
}
// #endregion -- Manage the "No Authors Added" Text.
// #region -- Sorting function
$( initializeSorting );
/**
* Make the visual items sortable, and update the hidden input accordingly.
*/
function initializeSorting() {
postVisualList.sortable( {
placeholder: 'twrpb-display-list__placeholder',
stop: updatePostsIdsFromVisualList,
} );
}
// #endregion -- Sorting function
// #region -- Helper Functions
/**
* Check to see if a given post item is displayed in visual list.
*/
function postExistInVisualList( id: number|string ): boolean {
const postItem = postVisualList.find( '[' + postIdAttrName + '="' + id + '"]' );
if ( postItem.length ) {
return true;
}
return false;
}
/**
* Check to see if a given post item exist in the hidden list.
*/
function postExistInHiddenInput( id: number|string ): boolean {
const inputVal = String( postsIdsInput.val() );
id = Number( id );
if ( ( ! inputVal ) || ! ( id > 0 ) ) {
return false;
}
const postsIds = inputVal.split( ';' ).map( mapToInt );
if ( postsIds.indexOf( id ) !== -1 ) {
return true;
}
return false;
function mapToInt( val: any ): number {
return parseInt( val );
}
}
/**
* Updates the Ids in the input in the same order as the ones
* from the visual list.
*/
function updatePostsIdsFromVisualList(): void {
const postItems = postVisualList.find( '.twrpb-posts-settings__post-item' ).filter( '[' + postIdAttrName + ']' );
const authorsIds = [];
postItems.each( function() {
let itemId: string|number = $( this ).attr( postIdAttrName );
itemId = +itemId;
if ( itemId > 0 ) {
authorsIds.push( itemId );
}
} );
postsIdsInput.val( authorsIds.join( ';' ) );
}
/**
* Sanitize the post name. It does not do something yet.
*/
function sanitizePostName( name: string ): string {
return name;
}
/**
* Get the aria label for the remove button. In the aria label will be present
* "%s", which will need to be replaced with an author.
*/
function getRemoveButtonAriaLabel(): string {
const ariaLabel = postVisualList.attr( 'data-twrpb-aria-remove-label' );
if ( ! ariaLabel ) {
return '';
}
return ariaLabel;
}
/**
* Decodes the HTML entities.
*/
function decodeHtml( html: string ): string {
return $( '
' ).html( html ).text();
}
// #endregion -- Helper Functions