assets/js/builder/Views/Question.js
/**
* 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 ) );
} );