/*
* field-group.js
*
* All javascript needed to create a field group
*
* @type JS
* @date 1/08/13
*/
var acf = {
// vars
ajaxurl : '',
admin_url : '',
post_id : 0,
nonce : '',
l10n : {},
text : {},
// helper functions
helpers : {
uniqid : null,
sortable : null,
create_field : null
},
// modules
conditional_logic : null,
location : null
};
(function($){
/*
* Exists
*
* @since 3.1.6
* @description returns true or false on a element's existance
*/
$.fn.exists = function()
{
return $(this).length>0;
};
/*
* Sortable Helper
*
* @description: keeps widths of td's inside a tr
* @since 3.5.1
* @created: 10/11/12
*/
acf.helpers.sortable = function(e, ui)
{
ui.children().each(function(){
$(this).width($(this).width());
});
return ui;
};
/*
* acf.helpers.uniqid
*
* @description: JS equivelant of PHP uniqid
* @since: 3.6
* @created: 7/03/13
*/
acf.helpers.uniqid = function(prefix, more_entropy)
{
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + revised by: Kankrelune (http://www.webfaktory.info/)
// % note 1: Uses an internal counter (in php_js global) to avoid collision
// * example 1: uniqid();
// * returns 1: 'a30285b160c14'
// * example 2: uniqid('foo');
// * returns 2: 'fooa30285b1cd361'
// * example 3: uniqid('bar', true);
// * returns 3: 'bara20285b23dfd1.31879087'
if (typeof prefix == 'undefined') {
prefix = "";
}
var retId;
var formatSeed = function (seed, reqWidth) {
seed = parseInt(seed, 10).toString(16); // to hex str
if (reqWidth < seed.length) { // so long we split
return seed.slice(seed.length - reqWidth);
}
if (reqWidth > seed.length) { // so short we pad
return Array(1 + (reqWidth - seed.length)).join('0') + seed;
}
return seed;
};
// BEGIN REDUNDANT
if (!this.php_js) {
this.php_js = {};
}
// END REDUNDANT
if (!this.php_js.uniqidSeed) { // init seed with big random int
this.php_js.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
}
this.php_js.uniqidSeed++;
retId = prefix; // start with prefix, add current milliseconds hex string
retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
retId += formatSeed(this.php_js.uniqidSeed, 5); // add seed hex string
if (more_entropy) {
// for more entropy we add a float lower to 10
retId += (Math.random() * 10).toFixed(8).toString();
}
return retId;
};
/*
* Submit Post
*
* Run validation and return true|false accordingly
*
* @type function
* @date 1/03/2011
*
* @param N/A
* @return N/A
*/
var acf_submit = {
init: function(){
// events
$(document).on('submit', '#post', this.submit);
// return
return this;
},
submit: function( e ){
// validate post title
var $title = $('#titlewrap #title'),
$spinner = $('#submitdiv .spinner').last(),
$submit = $('#submitdiv input[type="submit"]').last();
// title empty
if( !$title.val() ) {
// prevent default
e.preventDefault();
// hide spinner
acf_submit.hide_spinner( $spinner );
acf_submit.enable_submit( $submit );
// alert
alert( acf.l10n.title );
// focus
$title.focus();
}
},
hide_spinner: function( $spinner ){
// bail early if no spinner
if( !$spinner.exists() ) return;
// vars
var wp_version = acf.wp_version;
// hide
if( parseFloat(wp_version) >= 4.2 ) {
$spinner.removeClass('is-active');
} else {
$spinner.css('display', 'none');
}
},
enable_submit: function( $submit ){
// bail early if no submit
if( !$submit.exists() ) {
return;
}
// remove class
$submit.removeClass('disabled button-disabled button-primary-disabled');
}
}.init();
/*
* Place Confirm message on Publish trash button
*
* @since 3.1.6
* @description
*/
$(document).on('click', '#submit-delete', function(){
var response = confirm( acf.l10n.move_to_trash );
if( !response )
{
return false;
}
});
/*
* acf/update_field_options
*
* @since 3.1.6
* @description Load in the opions html
*/
$(document).on('change', '#acf_fields tr.field_type select', function(){
// vars
var select = $(this),
tbody = select.closest('tbody'),
field = tbody.closest('.field'),
field_type = field.attr('data-type'),
field_key = field.attr('data-id'),
val = select.val();
// update data atts
field.removeClass('field_type-' + field_type).addClass('field_type-' + val);
field.attr('data-type', val);
// tab - override field_name
if( val == 'tab' || val == 'message' )
{
tbody.find('tr.field_name input[type="text"]').val('').trigger('keyup');
}
// show field options if they already exist
if( tbody.children( 'tr.field_option_' + val ).exists() )
{
// hide + disable options
tbody.children('tr.field_option').hide().find('[name]').attr('disabled', 'true');
// show and enable options
tbody.children( 'tr.field_option_' + val ).show().find('[name]').removeAttr('disabled');
}
else
{
// add loading gif
var tr = $('
| |
');
// hide current options
tbody.children('tr.field_option').hide().find('[name]').attr('disabled', 'true');
// append tr
if( tbody.children('tr.conditional-logic').exists() )
{
tbody.children('tr.conditional-logic').before(tr);
}
else
{
tbody.children('tr.field_save').before(tr);
}
var ajax_data = {
'action' : 'acf/field_group/render_options',
'post_id' : acf.post_id,
'field_key' : select.attr('name'),
'field_type' : val,
'nonce' : acf.nonce
};
$.ajax({
url: ajaxurl,
data: ajax_data,
type: 'post',
dataType: 'html',
success: function(html){
if( ! html )
{
tr.remove();
return;
}
tr.replaceWith(html);
}
});
}
});
/*
* Update Names
*
* @description:
* @since 3.5.1
* @created: 15/10/12
*/
$.fn.update_names = function()
{
var field = $(this),
old_id = field.attr('data-id'),
new_id = 'field_' + acf.helpers.uniqid();
// give field a new id
field.attr('data-id', new_id);
// update class
field.attr('class', field.attr('class').replace(old_id, new_id) );
// update field key column
field.find('.field_meta td.field_key').text( new_id );
// update attributes
field.find('[id*="' + old_id + '"]').each(function()
{
$(this).attr('id', $(this).attr('id').replace(old_id, new_id) );
});
field.find('[name*="' + old_id + '"]').each(function()
{
$(this).attr('name', $(this).attr('name').replace(old_id, new_id) );
});
};
/*
* update_order_numbers
*
* @description:
* @since 3.5.1
* @created: 15/10/12
*/
function update_order_numbers(){
$('#acf_fields .fields').each(function(){
$(this).children('.field').each(function(i){
$(this).find('td.field_order .circle').first().html(i+1);
});
});
}
/*
* Edit Field
*
* @description:
* @since 3.5.1
* @created: 13/10/12
*/
$(document).on('click', '#acf_fields a.acf_edit_field', function(){
var $field = $(this).closest('.field');
if( $field.hasClass('form_open') )
{
$field.removeClass('form_open');
$(document).trigger('acf/field_form-close', [ $field ]);
}
else
{
$field.addClass('form_open');
$(document).trigger('acf/field_form-open', [ $field ]);
}
$field.children('.field_form_mask').animate({'height':'toggle'}, 250);
});
/*
* Delete Field
*
* @description:
* @since 3.5.1
* @created: 13/10/12
*/
$(document).on('click', '#acf_fields a.acf_delete_field', function(){
// vars
var a = $(this),
field = a.closest('.field'),
fields = field.closest('.fields'),
temp = $('');
// fade away
field.animate({'left' : '50px', 'opacity' : 0}, 250, function(){
field.before(temp);
field.remove();
// no more fields, show the message
if( fields.children('.field').length <= 1 )
{
temp.remove();
fields.children('.no_fields_message').show();
}
else
{
temp.animate({'height' : 0 }, 250, function(){
temp.remove();
});
}
update_order_numbers();
});
});
/*
* Duplicate Field
*
* @description:
* @since 3.5.1
* @created: 13/10/12
*/
$(document).on('click', '#acf_fields a.acf_duplicate_field', function(){
// vars
var a = $(this),
field = a.closest('.field'),
new_field = null;
// save select values
field.find('select').each(function(){
$(this).attr( 'data-val', $(this).val() );
});
// clone field
new_field = field.clone();
// update names
new_field.update_names();
new_field.find('.field:not(.field_key-field_clone)').each(function(){
$(this).update_names();
});
// add new field
field.after( new_field );
// set select values
new_field.find('select').each(function(){
$(this).val( $(this).attr('data-val') ).trigger('change');
});
// open up form
if( field.hasClass('form_open') )
{
field.find('.acf_edit_field').first().trigger('click');
}
else
{
new_field.find('.acf_edit_field').first().trigger('click');
}
// update new_field label / name
var label = new_field.find('tr.field_label:first input[type="text"]'),
name = new_field.find('tr.field_name:first input[type="text"]');
name.val('');
label.val( label.val() + ' (' + acf.l10n.copy + ')' );
label.trigger('blur').trigger('keyup');
// update order numbers
update_order_numbers();
});
/*
* Add Field
*
* @description:
* @since 3.5.1
* @created: 13/10/12
*/
$(document).on('click', '#acf_fields #add_field', function(){
var fields = $(this).closest('.table_footer').siblings('.fields');
// clone last tr
var new_field = fields.children('.field_key-field_clone').clone();
// update names
new_field.update_names();
// show
new_field.show();
// append to table
fields.children('.field_key-field_clone').before(new_field);
// remove no fields message
if(fields.children('.no_fields_message').exists())
{
fields.children('.no_fields_message').hide();
}
// clear name
new_field.find('tr.field_type select').trigger('change');
new_field.find('.field_form input[type="text"]').val('');
// focus after form has dropped down
// - this prevents a strange rendering bug in Firefox
setTimeout(function(){
new_field.find('.field_form input[type="text"]').first().focus();
}, 500);
// open up form
new_field.find('a.acf_edit_field').first().trigger('click');
// update order numbers
update_order_numbers();
return false;
});
/*
* Auto Complete Field Name
*
* @description:
* @since 3.5.1
* @created: 15/10/12
*/
$(document).on('blur', '#acf_fields tr.field_label input.label', function(){
// vars
var $label = $(this),
$field = $label.closest('.field'),
$name = $field.find('tr.field_name:first input[type="text"]'),
type = $field.attr('data-type');
// leave blank for tab or message field
if( type == 'tab' || type == 'message' )
{
$name.val('').trigger('keyup');
return;
}
if( $name.val() == '' )
{
// thanks to https://gist.github.com/richardsweeney/5317392 for this code!
var val = $label.val(),
replace = {
'ä': 'a',
'æ': 'a',
'å': 'a',
'ö': 'o',
'ø': 'o',
'é': 'e',
'ë': 'e',
'ü': 'u',
'ó': 'o',
'ő': 'o',
'ú': 'u',
'é': 'e',
'á': 'a',
'ű': 'u',
'í': 'i',
' ' : '_',
'\'' : '',
'\\?' : ''
};
$.each( replace, function(k, v){
var regex = new RegExp( k, 'g' );
val = val.replace( regex, v );
});
val = val.toLowerCase();
$name.val( val );
$name.trigger('keyup');
}
});
/*
* Update field meta
*
* @description:
* @since 3.5.1
* @created: 15/10/12
*/
$(document).on('keyup', '#acf_fields .field_form tr.field_label input.label', function(){
var val = $(this).val();
var name = $(this).closest('.field').find('td.field_label strong a').first().html(val);
});
$(document).on('keyup', '#acf_fields .field_form tr.field_name input.name', function(){
var val = $(this).val();
var name = $(this).closest('.field').find('td.field_name').first().html(val);
});
$(document).on('change', '#acf_fields .field_form tr.field_type select', function(){
var val = $(this).val();
var label = $(this).find('option[value="' + val + '"]').html();
$(this).closest('.field').find('td.field_type').first().html(label);
});
// sortable
$(document).on('mouseover', '#acf_fields td.field_order', function(){
// vars
var fields = $(this).closest('.fields');
if( fields.hasClass('sortable') )
{
return false;
}
fields.addClass('sortable').sortable({
update: function(event, ui){
update_order_numbers();
},
handle: 'td.field_order'
});
});
/*
* Setup Location Rules
*
* @description:
* @since 3.5.1
* @created: 15/10/12
*/
$(document).ready(function(){
acf.location.init();
acf.conditional_logic.init();
});
/*
* location
*
* {description}
*
* @since: 4.0.3
* @created: 13/04/13
*/
acf.location = {
$el : null,
init : function(){
// vars
var _this = this;
// $el
_this.$el = $('#acf_location');
// add rule
_this.$el.on('click', '.location-add-rule', function(){
_this.add_rule( $(this).closest('tr') );
return false;
});
// remove rule
_this.$el.on('click', '.location-remove-rule', function(){
_this.remove_rule( $(this).closest('tr') );
return false;
});
// add rule
_this.$el.on('click', '.location-add-group', function(){
_this.add_group();
return false;
});
// change rule
_this.$el.on('change', '.param select', function(){
// vars
var $tr = $(this).closest('tr'),
rule_id = $tr.attr('data-id'),
$group = $tr.closest('.location-group'),
group_id = $group.attr('data-id'),
ajax_data = {
'action' : "acf/field_group/render_location",
'nonce' : acf.nonce,
'rule_id' : rule_id,
'group_id' : group_id,
'value' : '',
'param' : $(this).val()
};
// add loading gif
var div = $('');
$tr.find('td.value').html( div );
// load location html
$.ajax({
url: acf.ajaxurl,
data: ajax_data,
type: 'post',
dataType: 'html',
success: function(html){
div.replaceWith(html);
}
});
});
},
add_rule : function( $tr ){
// vars
var $tr2 = $tr.clone(),
old_id = $tr2.attr('data-id'),
new_id = acf.helpers.uniqid();
// update names
$tr2.find('[name]').each(function(){
$(this).attr('name', $(this).attr('name').replace( old_id, new_id ));
$(this).attr('id', $(this).attr('id').replace( old_id, new_id ));
});
// update data-i
$tr2.attr( 'data-id', new_id );
// add tr
$tr.after( $tr2 );
return false;
},
remove_rule : function( $tr ){
// vars
var siblings = $tr.siblings('tr').length;
if( siblings == 0 )
{
// remove group
this.remove_group( $tr.closest('.location-group') );
}
else
{
// remove tr
$tr.remove();
}
},
add_group : function(){
// vars
var $group = this.$el.find('.location-group:last'),
$group2 = $group.clone(),
old_id = $group2.attr('data-id'),
new_id = acf.helpers.uniqid();
// update names
$group2.find('[name]').each(function(){
$(this).attr('name', $(this).attr('name').replace( old_id, new_id ));
$(this).attr('id', $(this).attr('id').replace( old_id, new_id ));
});
// update data-i
$group2.attr( 'data-id', new_id );
// update h4
$group2.find('h4').text( acf.l10n.or );
// remove all tr's except the first one
$group2.find('tr:not(:first)').remove();
// add tr
$group.after( $group2 );
},
remove_group : function( $group ){
$group.remove();
}
};
/*----------------------------------------------------------------------
*
* Document Ready
*
*---------------------------------------------------------------------*/
$(document).ready(function(){
// custom Publish metabox
$('#submitdiv #publish').attr('class', 'acf-button large');
$('#submitdiv a.submitdelete').attr('class', 'delete-field-group').attr('id', 'submit-delete');
// hide on screen toggle
var $ul = $('#hide-on-screen ul.acf-checkbox-list'),
$li = $('');
// start checked?
if( $ul.find('input:not(:checked)').length == 0 )
{
$li.find('input').attr('checked', 'checked');
}
// event
$li.on('change', 'input', function(){
var checked = $(this).is(':checked');
$ul.find('input').attr('checked', checked);
});
// add to ul
$ul.prepend( $li );
});
/*
* Screen Options
*
* @description:
* @created: 4/09/12
*/
$(document).on('change', '#adv-settings input[name="show-field_key"]', function(){
if( $(this).val() == "1" )
{
$('#acf_fields table.acf').addClass('show-field_key');
}
else
{
$('#acf_fields table.acf').removeClass('show-field_key');
}
});
/*
* Create Field
*
* @description:
* @since 3.5.1
* @created: 11/10/12
*/
acf.helpers.create_field = function( options ){
// dafaults
var defaults = {
'type' : 'text',
'classname' : '',
'name' : '',
'value' : ''
};
options = $.extend(true, defaults, options);
// vars
var html = "";
if( options.type == "text" )
{
html += '';
}
else if( options.type == "select" )
{
// vars
var groups = {};
// populate groups
$.each(options.choices, function(k, v){
// group may not exist
if( v.group === undefined )
{
v.group = 0;
}
// instantiate group
if( groups[ v.group ] === undefined )
{
groups[ v.group ] = [];
}
// add to group
groups[ v.group ].push( v );
});
html += '';
}
html = $(html);
return html;
};
/*
* Conditional Logic
*
* This object contains all the functionality for seting up the conditional logic rules for fields
*
* @type object
* @date 21/08/13
*
* @param N/A
* @return N/A
*/
acf.conditional_logic = {
triggers : null,
init : function(){
// reference
var _this = this;
// events
$(document).on('acf/field_form-open', function(e, $field){
// render select elements
_this.render( $field );
});
$(document).on('change', '#acf_fields tr.field_label input.label', function(){
// render all open fields
$('#acf_fields .field.form_open').each(function(){
_this.render( $(this) );
});
});
$(document).on('change', 'tr.conditional-logic input[type="radio"]', function( e ){
e.preventDefault();
_this.change_toggle( $(this) );
});
$(document).on('change', 'select.conditional-logic-field', function( e ){
e.preventDefault();
_this.change_trigger( $(this) );
});
$(document).on('click', 'tr.conditional-logic .acf-button-add', function( e ){
e.preventDefault();
_this.add( $(this).closest('tr') );
});
$(document).on('click', 'tr.conditional-logic .acf-button-remove', function( e ){
e.preventDefault();
_this.remove( $(this).closest('tr') );
});
},
render : function( $field ){
// reference
var _this = this;
// vars
var choices = [],
key = $field.attr('data-id'),
$ancestors = $field.parents('.fields'),
$tr = $field.find('> .field_form_mask > .field_form > table > tbody > tr.conditional-logic');
$.each( $ancestors, function( i ){
var group = (i == 0) ? acf.l10n.sibling_fields : acf.l10n.parent_fields;
$(this).children('.field').each(function(){
// vars
var $this_field = $(this),
this_id = $this_field.attr('data-id'),
this_type = $this_field.attr('data-type'),
this_label = $this_field.find('tr.field_label input').val();
// validate
if( this_id == 'field_clone' )
{
return;
}
if( this_id == key )
{
return;
}
// add this field to available triggers
if( this_type == 'select' || this_type == 'checkbox' || this_type == 'true_false' || this_type == 'radio' )
{
choices.push({
value : this_id,
label : this_label,
group : group
});
}
});
});
// empty?
if( choices.length == 0 )
{
choices.push({
'value' : 'null',
'label' : acf.l10n.no_fields
});
}
// create select fields
$tr.find('.conditional-logic-field').each(function(){
var val = $(this).val(),
name = $(this).attr('name');
// create select
var $select = acf.helpers.create_field({
'type' : 'select',
'classname' : 'conditional-logic-field',
'name' : name,
'value' : val,
'choices' : choices
});
// update select
$(this).replaceWith( $select );
// trigger change
$select.trigger('change');
});
},
change_toggle : function( $input ){
// vars
var val = $input.val(),
$tr = $input.closest('tr.conditional-logic');
if( val == "1" )
{
$tr.find('.contional-logic-rules-wrapper').show();
}
else
{
$tr.find('.contional-logic-rules-wrapper').hide();
}
},
change_trigger : function( $select ){
// vars
var val = $select.val(),
$trigger = $('.field_key-' + val),
type = $trigger.attr('data-type'),
$value = $select.closest('tr').find('.conditional-logic-value'),
choices = [];
// populate choices
if( type == "true_false" )
{
choices = [
{ value : 1, label : acf.l10n.checked }
];
}
else if( type == "select" || type == "checkbox" || type == "radio" )
{
var field_choices = $trigger.find('.field_option-choices').val().split("\n");
if( field_choices )
{
for( var i = 0; i < field_choices.length; i++ )
{
var choice = field_choices[i].split(':');
var label = choice[0];
if( choice[1] )
{
label = choice[1];
}
choices.push({
'value' : $.trim( choice[0] ),
'label' : $.trim( label )
});
}
}
}
// create select
var $select = acf.helpers.create_field({
'type' : 'select',
'classname' : 'conditional-logic-value',
'name' : $value.attr('name'),
'value' : $value.val(),
'choices' : choices
});
$value.replaceWith( $select );
$select.trigger('change');
},
add : function( $old_tr ){
// vars
var $new_tr = $old_tr.clone(),
old_i = parseFloat( $old_tr.attr('data-i') ),
new_i = acf.helpers.uniqid();
// update names
$new_tr.find('[name]').each(function(){
// flexible content uses [0], [1] as the layout index. To avoid conflict, make sure we search for the entire conditional logic string in the name and id
var find = '[conditional_logic][rules][' + old_i + ']',
replace = '[conditional_logic][rules][' + new_i + ']';
$(this).attr('name', $(this).attr('name').replace(find, replace) );
$(this).attr('id', $(this).attr('id').replace(find, replace) );
});
// update data-i
$new_tr.attr('data-i', new_i);
// add tr
$old_tr.after( $new_tr );
// remove disabled
$old_tr.closest('table').removeClass('remove-disabled');
},
remove : function( $tr ){
var $table = $tr.closest('table');
// validate
if( $table.hasClass('remove-disabled') )
{
return false;
}
// remove tr
$tr.remove();
// add clas to table
if( $table.find('tr').length <= 1 )
{
$table.addClass('remove-disabled');
}
},
};
/*
* Field: Radio
*
* Simple toggle for the radio 'other_choice' option
*
* @type function
* @date 1/07/13
*/
$(document).on('change', '.radio-option-other_choice input', function(){
// vars
var $el = $(this),
$td = $el.closest('td');
if( $el.is(':checked') )
{
$td.find('.radio-option-save_other_choice').show();
}
else
{
$td.find('.radio-option-save_other_choice').hide();
$td.find('.radio-option-save_other_choice input').removeAttr('checked');
}
});
})(jQuery);