assets/js/builder/Views/SettingsFields.js
/**
* Model settings fields view
*
* @since 3.17.0
* @version 4.7.0
*/
define( [], function() {
return Backbone.View.extend( _.defaults( {
/**
* DOM events
*
* @type {Object}
*/
events: {
'click .llms-settings-group-toggle': 'toggle_group',
},
/**
* Processed fields data
* Allows access by ID without traversing the schema
*
* @type {Object}
*/
fields: {},
/**
* Wrapper Tag name
*
* @type {String}
*/
tagName: 'div',
/**
* Get the underscore template
*
* @type {[type]}
*/
template: wp.template( 'llms-settings-fields-template' ),
/**
* Initialization callback func (renders the element on screen)
*
* @return void
* @since 3.17.0
* @version 3.17.0
*/
// initialize: function() {},
/**
* Retrieve an array of all editor fields in all groups
*
* @return array
* @since 3.17.1
* @version 3.17.1
*/
get_editor_fields: function() {
return _.filter( this.fields, function( field ) {
return this.is_editor_field( field.type );
}, this );
},
/**
* Get settings group data from a model
*
* @return {[type]}
* @since 3.17.0
* @version 3.17.0
*/
get_groups: function() {
return this.model.get_settings_fields();
},
/**
* Determine if a settings group is hidden in localStorage
*
* @param string group_id id of the group
* @return {Boolean}
* @since 3.17.0
* @version 3.17.0
*/
is_group_hidden: function( group_id ) {
var id = 'llms-' + this.model.get( 'type' ) + '-settings-group--' + group_id;
if ( 'undefined' !== window.localStorage ) {
return ( 'hidden' === window.localStorage.getItem( id ) );
}
return false;
},
/**
* Get the switch attribute for a field with switches
*
* @param obj field field data obj
* @return string
* @since 3.17.0
* @version 3.17.0
*/
get_switch_attribute: function( field ) {
return field.switch_attribute ? field.switch_attribute : field.attribute;
},
/**
* Determine if a field has a switch
*
* @param string type field type string
* @return {Boolean}
* @since 3.17.0
* @version 3.17.0
*/
has_switch: function( type ) {
return ( -1 !== type.indexOf( 'switch' ) );
},
/**
* Determine if a field is a default (text) field
*
* @param string type field type string
* @return {Boolean}
* @since 3.17.0
* @version 3.17.0
*/
is_default_field: function( type ) {
var types = [ 'audio_embed', 'datepicker', 'number', 'text', 'video_embed' ];
return ( -1 !== types.indexOf( type.replace( 'switch-', '' ) ) );
},
/**
* Determine if a field is a WYSIWYG editor field
*
* @param string type field type string
* @return {Boolean}
* @since 3.17.1
* @version 3.17.1
*/
is_editor_field: function( type ) {
var types = [ 'editor', 'switch-editor' ];
return ( -1 !== types.indexOf( type.replace( 'switch-', '' ) ) );
},
/**
* Determine if a switch is enabled for a field
*
* @param obj field field data object
* @return {Boolean}
* @since 3.17.0
* @version 3.17.6
*/
is_switch_condition_met: function( field ) {
return ( field.switch_on === this.model.get( field.switch_attribute ) );
},
/**
* Compiles the template and renders the view
*
* @return self (for chaining)
* @since 3.17.0
* @version 3.17.1
*/
render: function() {
this.$el.html( this.template( this ) );
// if editors exist, render them
_.each( this.get_editor_fields(), function( field ) {
this.render_editor( field );
}, this );
return this;
},
/**
* Renders an editor field
*
* @since 3.17.1
* @since 3.37.11 Replace references to `wp.editor` with `_.getEditor()` helper.
*
* @param {Object} field Field data object.
* @return {Void}
*/
render_editor: function( field ) {
var self = this,
wpEditor = _.getEditor();
// Exit early if there's no editor to work with.
if ( undefined === wpEditor ) {
console.error( 'Unable to access `wp.oldEditor` or `wp.editor`.' );
return;
}
wpEditor.remove( field.id );
field.settings.tinymce.setup = function( editor ) {
var $ed = $( '#' + editor.id ),
$parent = $ed.closest( '.llms-editable-editor' ),
$label = $parent.find( '.llms-label' ),
prop = $ed.attr( 'data-attribute' )
if ( $label.length ) {
$label.prependTo( $parent.find( '.wp-editor-tools' ) );
}
// save changes to the model via Visual ed
editor.on( 'change', function( event ) {
self.model.set( prop, wpEditor.getContent( editor.id ) );
} );
// save changes via Text ed
$ed.on( 'input', function( event ) {
self.model.set( prop, $ed.val() );
} );
// trigger an input on the Text ed when quicktags buttons are clicked
$parent.on( 'click', '.quicktags-toolbar .ed_button', function() {
setTimeout( function() {
$ed.trigger( 'input' );
}, 10 );
} );
};
wpEditor.initialize( field.id, field.settings );
},
/**
* Get the HTML for a select field
*
* @param obj options flat or multi-dimensional options object
* @param string attribute name of the select field's attribute
* @return string
* @since 3.17.0
* @version 3.17.2
*/
render_select_options: function( options, attribute ) {
var html = '',
selected = this.model.get( attribute );
function option_html( label, val ) {
return '<option value="' + val + '"' + _.selected( val, selected ) + '>' + label.substring( 0, 100 ) + ( label.length > 100 ? '...' : '' ) + '</option>';
}
_.each( options, function( option, index ) {
// this will be an key:val object
if ( 'string' === typeof option ) {
html += option_html( option, index );
// either option group or array of key,val objects
} else if ( 'object' === typeof option ) {
// option group
if ( option.label && option.options ) {
html += '<optgroup label="' + option.label + '">';
html += this.render_select_options( option.options, attribute );
} else {
html += option_html( option.val, option.key );
}
}
}, this );
return html;
},
/**
* Setup and fill fields with default data based on field type
*
* @since 3.17.0
* @since 3.24.0 Unknown.
* @since 3.37.11 Replace reference to `wp.editor` with `_.getEditor()` helper.
* @since 4.7.0 Ensure `switch-number` fields are set with the `number` type attribute.
*
* @param {Object} orig_field Original field as defined in the settings.
* @param {Integer} field_index Index of the field in the current row.
* @return {Object}
*/
setup_field: function( orig_field, field_index ) {
var defaults = {
classes: [],
id: _.uniqueId( orig_field.attribute + '_' ),
input_type: 'text',
label: '',
options: {},
placeholder: '',
tip: '',
tip_position: 'top-right',
settings: {},
};
// check the field condition if set
if ( orig_field.condition && false === _.bind( orig_field.condition, this.model )() ) {
return false;
}
switch ( orig_field.type ) {
case 'audio_embed':
defaults.classes.push( 'llms-editable-audio' );
defaults.placeholder = 'https://';
defaults.tip = LLMS.l10n.translate( 'Use SoundCloud or Spotify audio URLS.' );
defaults.input_type = 'url';
break;
case 'datepicker':
defaults.classes.push( 'llms-editable-date' );
break;
case 'editor':
case 'switch-editor':
var orig_settings = orig_field.settings || {};
defaults.settings = $.extend( true, _.getEditor().getDefaultSettings(), {
mediaButtons: true,
tinymce: {
toolbar1: 'bold,italic,strikethrough,bullist,numlist,blockquote,hr,alignleft,aligncenter,alignright,link,unlink,wp_adv',
toolbar2: 'formatselect,underline,alignjustify,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
}
}, orig_settings );
break;
case 'number':
case 'switch-number':
defaults.input_type = 'number';
break;
case 'permalink':
defaults.label = LLMS.l10n.translate( 'Permalink' );
break;
case 'video_embed':
defaults.classes.push( 'llms-editable-video' );
defaults.placeholder = 'https://';
defaults.tip = LLMS.l10n.translate( 'Use YouTube, Vimeo, or Wistia video URLS.' );
defaults.input_type = 'url';
break;
}
if ( this.has_switch( orig_field.type ) ) {
defaults.switch_on = 'yes';
defaults.switch_off = 'no';
}
var field = _.defaults( _.deepClone( orig_field ), defaults );
// if options is a function run it
if ( _.isFunction( field.options ) ) {
field.options = _.bind( field.options, this.model )();
}
// if it's a radio field options values can be submitted as images
// this will transform those images into <img> html
if ( -1 !== [ 'radio', 'switch-radio' ].indexOf( orig_field.type ) ) {
var has_images = false;
_.each( orig_field.options, function( val, key ) {
if ( -1 !== val.indexOf( '.png' ) || -1 !== val.indexOf( '.jpg' ) ) {
field.options[key] = '<span><img src="' + val + '"></span>';
has_images = true;
}
} );
if ( has_images ) {
field.classes.push( 'has-images' );
}
}
// transform classes array to a css class string
if ( field.classes.length ) {
field.classes = ' ' + field.classes.join( ' ' );
}
this.fields[ field.id ] = field;
return field;
},
/**
* Determine if toggling a switch select should rerender the view
*
* @param string field_type field type string
* @return boolean
* @since 3.17.0
* @version 3.17.0
*/
should_rerender_on_toggle: function( field_type ) {
return ( -1 !== field_type.indexOf( 'switch-' ) ) ? 'yes' : 'no';
},
/**
* Click event for toggling visibility of settings groups
* If localStorage is available, persist state
*
* @param obj event js event object
* @return void
* @since 3.17.0
* @version 3.17.0
*/
toggle_group: function( event ) {
event.preventDefault();
var $el = $( event.currentTarget ),
$group = $el.closest( '.llms-model-settings' );
$group.toggleClass( 'hidden' );
if ( 'undefined' !== window.localStorage ) {
var id = $group.attr( 'id' );
if ( $group.hasClass( 'hidden' ) ) {
window.localStorage.setItem( id, 'hidden' );
} else {
window.localStorage.removeItem( id );
}
}
},
} ) );
} );