gocodebox/lifterlms

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

Summary

Maintainability
A
3 hrs
Test Coverage
/**
 * Single Question View
 * @since    3.16.0
 * @version  3.27.0
 */
define( [
        'Views/_Detachable',
        'Views/_Editable',
        'Views/QuestionChoiceList'
    ], function(
        Detachable,
        Editable,
        ChoiceListView
    ) {

    return Backbone.View.extend( _.defaults( {

        /**
         * Generate CSS classes for the question
         * @return   string
         * @since    3.16.0
         * @version  3.16.0
         */
        className: function() {
            return 'llms-question qtype--' + this.model.get( 'question_type' ).get( 'id' );
        },

        events: _.defaults( {
            'click .clone--question': 'clone',
            'click .delete--question': 'delete',
            'click .expand--question': 'expand',
            'click .collapse--question': 'collapse',
            'change input[name="question_points"]': 'update_points',
        }, Detachable.events, Editable.events ),

        /**
         * HTML element wrapper ID attribute
         * @return   string
         * @since    3.16.0
         * @version  3.16.0
         */
        id: function() {
            return 'llms-question-' + this.model.id;
        },

        /**
         * Wrapper Tag name
         * @type  {String}
         */
        tagName: 'li',

        /**
         * Get the underscore template
         * @type  {[type]}
         */
        template: wp.template( 'llms-question-template' ),

        /**
         * Initialization callback func (renders the element on screen)
         * @return   void
         * @since    3.16.0
         * @version  3.16.0
         */
        initialize: function() {

            var change_events = [
                'change:_expanded',
                'change:menu_order',
            ];
            _.each( change_events, function( event ) {
                this.listenTo( this.model, event, this.render );
            }, this );

            this.listenTo( this.model.get( 'image' ), 'change', this.render );

            this.listenTo( this.model.get_parent(), 'change:_points', this.render_points_percentage );

            this.on( 'multi_choices_toggle', this.multi_choices_toggle, this );

            Backbone.pubSub.on( 'del-question-choice', this.del_choice, this );

        },

        /**
         * Compiles the template and renders the view
         * @return   self (for chaining)
         * @since    3.16.0
         * @version  3.16.0
         */
        render: function() {

            this.$el.html( this.template( this.model ) );

            if ( this.model.get( 'question_type').get( 'choices' ) ) {

                this.choiceListView = new ChoiceListView( {
                    el: this.$el.find( '.llms-question-choices' ),
                    collection: this.model.get( 'choices' ),
                } );
                this.choiceListView.render();
                this.choiceListView.on( 'sortStart', this.choiceListView.sortable_start );
                this.choiceListView.on( 'sortStop', this.choiceListView.sortable_stop );

            }

            if ( 'group' === this.model.get( 'question_type' ).get( 'id' ) ) {

                var self = this;
                setTimeout( function() {
                    self.questionListView = self.collectionListView.quiz.get_question_list( {
                        el: self.$el.find( '.llms-quiz-questions' ),
                        collection: self.model.get( 'questions' ),
                    } );
                    self.questionListView.render();
                    self.questionListView.on( 'sortStart', self.questionListView.sortable_start );
                    self.questionListView.on( 'sortStop', self.questionListView.sortable_stop );
                }, 1 );

            }

            if ( this.model.get( 'description_enabled' ) ) {
                this.init_editor( 'question-desc--' + this.model.get( 'id' ) );
            }

            if ( this.model.get( 'clarifications_enabled' ) ) {
                this.init_editor( 'question-clarifications--' + this.model.get( 'id' ), {
                    mediaButtons: false,
                    tinymce: {
                        toolbar1: 'bold,italic,strikethrough,bullist,numlist,alignleft,aligncenter,alignright',
                        toolbar2: '',
                        setup: _.bind( this.on_editor_ready, this ),
                    }
                } );
            }

            this.init_formatting_els();
            this.init_selects();

            return this;
        },

        /**
         * rerender points percentage when question points are updated
         * @return   void
         * @since    3.16.0
         * @version  3.16.0
         */
        render_points_percentage: function() {

            this.$el.find( '.llms-question-points' ).attr( 'data-tip', this.model.get_points_percentage() );

        },

        /**
         * Click event to duplicate a question within a quiz
         * @param    obj   event  js event object
         * @return   void
         * @since    3.16.0
         * @version  3.16.0
         */
        clone: function( event ) {

            event.stopPropagation();
            event.preventDefault();
            this.model.collection.add( this._get_question_clone( this.model ) );

        },

        /**
         * Recursive clone function which will correctly clone children of a question
         * @param    obj   question  question model
         * @return   obj
         * @since    3.16.0
         * @version  3.16.0
         */
        _get_question_clone: function( question ) {

            // create a duplicate
            var clone = _.clone( question.attributes );

            // remove id (we want the duplicate to have a temp id)
            delete clone.id;

            clone.parent_id = question.get( 'id' );

            // set the question type ID
            clone.question_type = question.get( 'question_type' ).get( 'id' );

            // clone the image attributes separately
            clone.image = _.clone( question.get( 'image' ).attributes );

            // if it has choices clone all the choices
            if ( question.get( 'choices' ) ) {

                clone.choices = [];

                question.get( 'choices' ).each( function ( choice ) {

                    var choice_clone = _.clone( choice.attributes );
                    delete choice_clone.id;
                    delete choice_clone.question_id;

                    clone.choices.push( choice_clone );

                } );

            }

            if ( 'group' === question.get( 'question_type' ).get( 'id' ) ) {

                clone.questions = [];
                question.get( 'questions' ).each( function( child ) {
                    clone.questions.push( this._get_question_clone( child ) );
                }, this );

            }

            return clone;

        },

        /**
         * Collapse a question and hide it's settings
         * @param obj event js event obj.
         * @return   void
         * @since    3.16.0
         * @version  3.27.0
         */
        collapse: function( event ) {

            if ( event ) {
                event.preventDefault();
            }

            this.model.set( '_expanded', false );

        },

        /**
         * Delete the question from a quiz / question group
         * @param    obj   event  js event object
         * @return   void
         * @since    3.16.0
         * @version  3.16.0
         */
        delete: function( event ) {

            event.preventDefault();

            if ( window.confirm( LLMS.l10n.translate( 'Are you sure you want to delete this question?' ) ) ) {

                this.model.collection.remove( this.model );
                Backbone.pubSub.trigger( 'model-trashed', this.model );

            }

        },

        /**
         * Click event to reveal a question's settings & choices
         * @param obj event js event obj.
         * @return   void
         * @since    3.16.0
         * @version  3.27.0
         */
        expand: function( event ) {

            if ( event ) {
                event.preventDefault();
            }

            this.model.set( '_expanded', true );

        },

        /**
         * When toggling multiple correct answers *off* remove all correct choices except the first correct choice in the list
         * @param    string   val  value of the question's `multi_choice` attr [yes|no]
         * @return   void
         * @since    3.16.0
         * @version  3.16.0
         */
        multi_choices_toggle: function( val ) {

            if ( 'yes' === val ) {
                return;
            }

            this.model.get( 'choices' ).update_correct( _.first( this.model.get( 'choices' ).get_correct() ) );

        },

        /**
         * Update the model's points when the value of the points input is updated
         * @return   void
         * @since    3.16.0
         * @version  3.16.0
         */
        update_points: function() {

            this.model.set( 'points', this.$el.find( 'input[name="question_points"]' ).val() * 1 );

        }

    }, Detachable, Editable ) );

} );