gocodebox/lifterlms

View on GitHub
assets/js/builder/Views/SettingsFields.js

Summary

Maintainability
C
7 hrs
Test Coverage
/**
 * 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 );
                }

            }

        },

    } ) );

} );