You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
8.0 KiB
282 lines
8.0 KiB
9 months ago
|
( function ( $, rwmb ) {
|
||
|
'use strict';
|
||
|
|
||
|
// Object holds all methods related to fields' index when clone
|
||
|
var cloneIndex = {
|
||
|
/**
|
||
|
* Set index for fields in a .rwmb-clone
|
||
|
* @param $inputs .rwmb-clone element
|
||
|
* @param index Index value
|
||
|
*/
|
||
|
set: function ( $inputs, index ) {
|
||
|
$inputs.each( function () {
|
||
|
var $field = $( this );
|
||
|
|
||
|
// Name attribute
|
||
|
var name = this.name;
|
||
|
if ( name && ! $field.closest( '.rwmb-group-clone' ).length ) {
|
||
|
$field.attr( 'name', cloneIndex.replace( index, name, '[', ']', false ) );
|
||
|
}
|
||
|
|
||
|
// ID attribute
|
||
|
var id = this.id;
|
||
|
if ( id ) {
|
||
|
$field.attr( 'id', cloneIndex.replace( index, id, '_', '', true, true ) );
|
||
|
}
|
||
|
|
||
|
$field.trigger( 'update_index', index );
|
||
|
} );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Replace an attribute of a field with updated index
|
||
|
* @param index New index value
|
||
|
* @param value Attribute value
|
||
|
* @param before String before returned value
|
||
|
* @param after String after returned value
|
||
|
* @param alternative Check if attribute does not contain any integer, will reset the attribute?
|
||
|
* @param isEnd Check if we find string at the end?
|
||
|
* @return string
|
||
|
*/
|
||
|
replace: function ( index, value, before, after, alternative, isEnd ) {
|
||
|
before = before || '';
|
||
|
after = after || '';
|
||
|
|
||
|
if ( typeof alternative === 'undefined' ) {
|
||
|
alternative = true;
|
||
|
}
|
||
|
|
||
|
var end = isEnd ? '$' : '';
|
||
|
|
||
|
var regex = new RegExp( cloneIndex.escapeRegex( before ) + '(\\d+)' + cloneIndex.escapeRegex( after ) + end ),
|
||
|
newValue = before + index + after;
|
||
|
|
||
|
return regex.test( value ) ? value.replace( regex, newValue ) : (alternative ? value + newValue : value );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Helper function to escape string in regular expression
|
||
|
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
|
||
|
* @param string
|
||
|
* @return string
|
||
|
*/
|
||
|
escapeRegex: function ( string ) {
|
||
|
return string.replace( /[.*+?^${}()|[\]\\]/g, "\\$&" );
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Helper function to create next index for clones
|
||
|
* @param $container .rwmb-input container
|
||
|
* @return integer
|
||
|
*/
|
||
|
nextIndex: function ( $container ) {
|
||
|
var nextIndex = $container.data( 'next-index' );
|
||
|
|
||
|
// If we render cloneable fields via AJAX, the mb_ready event is not fired.
|
||
|
// so nextIndex is undefined. In this case, we get the next index from the number of existing clones.
|
||
|
if ( nextIndex === undefined ) {
|
||
|
nextIndex = $container.children( '.rwmb-clone' ).length;
|
||
|
}
|
||
|
|
||
|
$container.data( 'next-index', nextIndex + 1 );
|
||
|
return nextIndex;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Object holds all method related to fields' value when clone.
|
||
|
var cloneValue = {
|
||
|
setDefault: function() {
|
||
|
var $field = $( this );
|
||
|
|
||
|
if ( true !== $field.data( 'clone-default' ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var type = $field.attr( 'type' ),
|
||
|
defaultValue = $field.data( 'default' );
|
||
|
|
||
|
if ( 'radio' === type ) {
|
||
|
$field.prop( 'checked', $field.val() === defaultValue );
|
||
|
} else if ( $field.hasClass( 'rwmb-checkbox' ) || $field.hasClass( 'rwmb-switch' ) ) {
|
||
|
$field.prop( 'checked', !! defaultValue );
|
||
|
} else if ( $field.hasClass( 'rwmb-checkbox_list' ) ) {
|
||
|
var value = $field.val();
|
||
|
$field.prop( 'checked', Array.isArray( defaultValue ) ? -1 !== defaultValue.indexOf( value ) : value == defaultValue );
|
||
|
} else if ( $field.is( 'select' ) ) {
|
||
|
$field.find( 'option[value="' + defaultValue + '"]' ).prop( 'selected', true );
|
||
|
} else if ( ! $field.hasClass( 'rwmb-hidden' ) ) {
|
||
|
$field.val( defaultValue );
|
||
|
}
|
||
|
},
|
||
|
clear: function() {
|
||
|
const $field = $( this ),
|
||
|
type = $field.attr( 'type' );
|
||
|
|
||
|
if ( 'radio' === type || 'checkbox' === type ) {
|
||
|
$field.prop( 'checked', false );
|
||
|
} else if ( $field.is( 'select' ) ) {
|
||
|
$field.prop( 'selectedIndex', 0 );
|
||
|
} else if ( ! $field.hasClass( 'rwmb-hidden' ) ) {
|
||
|
$field.val( '' );
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Clone fields
|
||
|
* @param $container A div container which has all fields
|
||
|
*/
|
||
|
function clone( $container ) {
|
||
|
var $last = $container.children( '.rwmb-clone' ).last(),
|
||
|
$clone = $last.clone(),
|
||
|
nextIndex = cloneIndex.nextIndex( $container );
|
||
|
|
||
|
// Clear fields' values.
|
||
|
var $inputs = $clone.find( rwmb.inputSelectors );
|
||
|
$inputs.each( cloneValue.clear );
|
||
|
|
||
|
// Remove validation errors.
|
||
|
$clone.find( 'p.rwmb-error' ).remove();
|
||
|
|
||
|
// Insert clone.
|
||
|
$clone.insertAfter( $last );
|
||
|
|
||
|
// Trigger custom event for the clone instance. Required for Group extension to update sub fields.
|
||
|
$clone.trigger( 'clone_instance', nextIndex );
|
||
|
|
||
|
// Set fields index. Must run before trigger clone event.
|
||
|
cloneIndex.set( $inputs, nextIndex );
|
||
|
|
||
|
// Set fields' default values: do after index is set to prevent previous radio fields from unchecking.
|
||
|
$inputs.each( cloneValue.setDefault );
|
||
|
|
||
|
// Trigger custom clone event.
|
||
|
$inputs.trigger( 'clone', nextIndex );
|
||
|
|
||
|
// After cloning fields.
|
||
|
$inputs.trigger( 'after_clone', nextIndex );
|
||
|
|
||
|
// Trigger custom change event for MB Blocks to update block attributes.
|
||
|
$inputs.first().trigger( 'mb_change' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Hide remove buttons when there's only 1 of them
|
||
|
*
|
||
|
* @param $container .rwmb-input container
|
||
|
*/
|
||
|
function toggleRemoveButtons( $container ) {
|
||
|
|
||
|
var $clones = $container.children( '.rwmb-clone' ),
|
||
|
minClone = 1;
|
||
|
|
||
|
if ( $container.data( 'min-clone' ) ) {
|
||
|
minClone = parseInt( $container.data( 'min-clone' ) );
|
||
|
}
|
||
|
$clones.children( '.remove-clone' ).toggle( $clones.length > minClone );
|
||
|
|
||
|
// Recursive for nested groups.
|
||
|
$container.find( '.rwmb-input' ).each( function () {
|
||
|
toggleRemoveButtons( $( this ) );
|
||
|
} );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Toggle add button
|
||
|
* Used with [data-max-clone] attribute. When max clone is reached, the add button is hid and vice versa
|
||
|
*
|
||
|
* @param $container .rwmb-input container
|
||
|
*/
|
||
|
function toggleAddButton( $container ) {
|
||
|
var $button = $container.children( '.add-clone' ),
|
||
|
maxClone = parseInt( $container.data( 'max-clone' ) ),
|
||
|
numClone = $container.children( '.rwmb-clone' ).length;
|
||
|
|
||
|
$button.toggle( isNaN( maxClone ) || ( maxClone && numClone < maxClone ) );
|
||
|
}
|
||
|
|
||
|
function addClone( e ) {
|
||
|
e.preventDefault();
|
||
|
|
||
|
var $container = $( this ).closest( '.rwmb-input' );
|
||
|
clone( $container );
|
||
|
|
||
|
toggleRemoveButtons( $container );
|
||
|
toggleAddButton( $container );
|
||
|
sortClones.apply( $container[0] );
|
||
|
}
|
||
|
|
||
|
function removeClone( e ) {
|
||
|
e.preventDefault();
|
||
|
|
||
|
var $this = $( this ),
|
||
|
$container = $this.closest( '.rwmb-input' );
|
||
|
|
||
|
// Remove clone only if there are 2 or more of them
|
||
|
if ( $container.children( '.rwmb-clone' ).length < 2 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$this.parent().trigger( 'remove' ).remove();
|
||
|
toggleRemoveButtons( $container );
|
||
|
toggleAddButton( $container );
|
||
|
|
||
|
// Trigger custom change event for MB Blocks to update block attributes.
|
||
|
$container.find( rwmb.inputSelectors ).first().trigger( 'mb_change' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sort clones.
|
||
|
* Expect this = .rwmb-input element.
|
||
|
*/
|
||
|
function sortClones() {
|
||
|
var $container = $( this );
|
||
|
|
||
|
if ( undefined !== $container.sortable( 'instance' ) ) {
|
||
|
return;
|
||
|
}
|
||
|
if ( 0 === $container.children( '.rwmb-clone' ).length ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$container.sortable( {
|
||
|
handle: '.rwmb-clone-icon',
|
||
|
placeholder: ' rwmb-clone rwmb-sortable-placeholder',
|
||
|
items: '> .rwmb-clone',
|
||
|
start: function ( event, ui ) {
|
||
|
// Make the placeholder has the same height as dragged item
|
||
|
ui.placeholder.height( ui.item.outerHeight() );
|
||
|
},
|
||
|
stop: function( event, ui ) {
|
||
|
ui.item.trigger( 'mb_init_editors' );
|
||
|
ui.item.find( rwmb.inputSelectors ).first().trigger( 'mb_change' );
|
||
|
}
|
||
|
} );
|
||
|
}
|
||
|
|
||
|
function start() {
|
||
|
var $container = $( this );
|
||
|
toggleRemoveButtons( $container );
|
||
|
toggleAddButton( $container );
|
||
|
|
||
|
$container.data( 'next-index', $container.children( '.rwmb-clone' ).length );
|
||
|
sortClones.apply( this );
|
||
|
}
|
||
|
|
||
|
function init( e ) {
|
||
|
$( e.target ).find( '.rwmb-input' ).each( start );
|
||
|
}
|
||
|
|
||
|
rwmb.$document
|
||
|
.on( 'mb_ready', init )
|
||
|
.on( 'click', '.add-clone', addClone )
|
||
|
.on( 'click', '.remove-clone', removeClone );
|
||
|
|
||
|
// Export for use outside.
|
||
|
rwmb.cloneIndex = cloneIndex;
|
||
|
rwmb.cloneValue = cloneValue;
|
||
|
rwmb.sortClones = sortClones;
|
||
|
rwmb.toggleRemoveButtons = toggleRemoveButtons;
|
||
|
rwmb.toggleAddButton = toggleAddButton;
|
||
|
} )( jQuery, rwmb );
|