HEX
Server: Apache
System: Linux WWW 6.1.0-40-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.153-1 (2025-09-20) x86_64
User: web11 (1011)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/epamokos.kaunokolegija.lt/wp-content/plugins/lifterlms/assets/js/builder/Models/Lesson.js
/**
 * Lesson Model
 *
 * @since 3.13.0
 * @version 4.20.0
 */
define( [ 'Models/Quiz', 'Models/_Relationships', 'Models/_Utilities', 'Schemas/Lesson' ], function( Quiz, Relationships, Utilities, LessonSchema ) {

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

		/**
		 * Model relationships
		 *
		 * @type {Object}
		 */
		relationships: {
			parents: {
				model: 'section',
				type: 'model',
			},
			children: {
				quiz: {
					class: 'Quiz',
					conditional: function( model ) {
						// if quiz is enabled OR not enabled but we have some quiz data as an obj
						return ( 'yes' === model.get( 'quiz_enabled' ) || ! _.isEmpty( model.get( 'quiz' ) ) );
					},
					model: 'llms_quiz',
					type: 'model',
				},
			},
		},

		/**
		 * Lesson Settings Schema
		 *
		 * @type {Object}
		 */
		schema: LessonSchema,

		/**
		 * New lesson defaults
		 *
		 * @since 3.13.0
		 * @since 3.24.0 Unknown.
		 *
		 * @return {Object} Default options associative array (js object).
		 */
		defaults: function() {
			return {
				id: _.uniqueId( 'temp_' ),
				title: LLMS.l10n.translate( 'New Lesson' ),
				type: 'lesson',
				order: this.collection ? this.collection.length + 1 : 1,
				parent_course: window.llms_builder.course.id,
				parent_section: '',

				// Urls.
				edit_url: '',
				view_url: '',

				// Editable fields.
				content: '',
				audio_embed: '',
				has_prerequisite: 'no',
				require_passing_grade: 'yes',
				require_assignment_passing_grade: 'yes',
				video_embed: '',
				free_lesson: '',
				points: 1,

				// Other fields.
				assignment: {}, // Assignment model/data.
				assignment_enabled: 'no',

				quiz: {}, // Quiz model/data.
				quiz_enabled: 'no',

				_forceSync: false,

			};
		},

		/**
		 * Initializer
		 *
		 * @since 3.16.0
		 * @since 3.17.0 Unknown
		 *
		 * @return {void}
		 */
		initialize: function() {

			this.init_custom_schema();
			this.startTracking();
			this.maybe_init_assignments();
			this.init_relationships();

			// If the lesson ID isn't set on a quiz, set it.
			var quiz = this.get( 'quiz' );
			if ( ! _.isEmpty( quiz ) && ! quiz.get( 'lesson_id' ) ) {
				quiz.set( 'lesson_id', this.get( 'id' ) );
			}

			window.llms.hooks.doAction( 'llms_lesson_model_init', this );

		},

		/**
		 * Retrieve a reference to the parent course of the lesson
		 *
		 * @since 3.16.0
		 * @since 4.14.0 Use Section.get_course() in favor of Section.get_parent().
		 *
		 * @return {Object} The parent course model of the lesson.
		 */
		get_course: function() {
			return this.get_parent().get_course();
		},

		/**
		 * Retrieve the translated post type name for the model's type
		 *
		 * @since  3.16.12
		 *
		 * @param bool plural If true, returns the plural, otherwise returns singular.
		 * @return string The translated post type name.
		 */
		get_l10n_type: function( plural ) {

			if ( plural ) {
				return LLMS.l10n.translate( 'lessons' );
			}

			return LLMS.l10n.translate( 'lesson' );
		},

		/**
		 * Override default get_parent to grab from collection if models parent isn't set
		 *
		 * @since 3.17.0
		 *
		 * @return {Object}|false The parent model or false if not available.
		 */
		get_parent: function() {

			var rels = this.get_relationships();
			if ( rels.parent && rels.parent.reference ) {
				return rels.parent.reference;
			} else if ( this.collection && this.collection.parent ) {
				return this.collection.parent;
			}
			return false;

		},

		/**
		 * Retrieve the questions percentage value within the quiz
		 *
		 * @since 3.24.0
		 *
		 * @return {String} Questions percentage value within the quiz.
		 */
		get_points_percentage: function() {

			var total  = this.get_course().get_total_points(),
				points = this.get( 'points' ) * 1;

			if ( ! _.isNumber( points ) ) {
				points = 0;
			}

			if ( 0 === total ) {
				return '0%';
			}

			return ( ( points / total ) * 100 ).toFixed( 2 ) + '%';

		},

		/**
		 * Retrieve an array of prerequisite options available for the current lesson
		 *
		 * @since 3.17.0
		 *
		 * @return {Object} Prerequisite options.
		 */
		get_available_prereq_options: function() {

			var parent_section_index    = this.get_parent().collection.indexOf( this.get_parent() ),
				lesson_index_in_section = this.collection.indexOf( this ),
				options                 = [];

			this.get_course().get( 'sections' ).each( function( section, curr_sec_index ) {
				if ( curr_sec_index <= parent_section_index ) {
					var group = {
							// Translators: %1$d = section order number, %2$s = section title.
						label: LLMS.l10n.replace( 'Section %1$d: %2$s', {
							'%1$d': section.get( 'order' ),
							'%2$s': section.get( 'title' )
						} ),
					options: [],
					};

					section.get( 'lessons' ).each( function( lesson, curr_les_index ) {
						if ( curr_sec_index !== parent_section_index || curr_les_index < lesson_index_in_section ) {
							// Translators: %1$d = lesson order number, %2$s = lesson title.
							group.options.push( {
								key: lesson.get( 'id' ),
								val: LLMS.l10n.replace( 'Lesson %1$d: %2$s', {
									'%1$d': lesson.get( 'order' ),
									'%2$s': lesson.get( 'title' )
								} ),
							} );
						}
					}, this );

					options.push( group );
				}
			}, this );

			return options;

		},

		/**
		 * Add a new quiz to the lesson
		 *
		 * @since 3.16.0
		 * @since 3.27.0 Unknown.
		 *
		 * @param {Object} data Object of quiz data used to construct a new quiz model.
		 * @return {Object} Model for the created quiz.
		 */
		add_quiz: function( data ) {

			data = data || {};

			data.lesson_id         = this.id;
			data._questions_loaded = true;

			if ( ! data.title ) {

				data.title = LLMS.l10n.replace( '%1$s Quiz', {
					'%1$s': this.get( 'title' ),
				} );

			}

			this.set( 'quiz', data );
			this.init_relationships();

			var quiz = this.get( 'quiz' );
			this.set( 'quiz_enabled', 'yes' );

			window.llms.hooks.doAction( 'llms_lesson_add_quiz', quiz, this );

			return quiz;

		},

		/**
		 * Determine if this is the first lesson
		 *
		 * @since 3.17.0
		 * @since 4.20.0 Use is_first_in_section() new method.
		 *
		 * @return {Boolean} Whether this is the first lesson of its course.
		 */
		is_first_in_course: function() {

			// If it's not the first item in the section it cant be the first lesson.
			if ( ! this.is_first_in_section() ) {
				return false;
			}

			// If it's not the first section it cant' be first lesson.
			var section = this.get_parent();
			if ( section.collection.indexOf( section ) ) {
				return false;
			}

			// It's first lesson in first section.
			return true;

		},

		/**
		 * Determine if this is the last lesson of the course
		 *
		 * @since 4.20.0
		 *
		 * @return {Boolean} Whether this is the last lesson of its course.
		 */
		 is_last_in_course: function() {

			// If it's not last item in the section it cant be the last lesson.
			if ( ! this.is_last_in_section() ) {
				return false;
			}

			// If it's not the last section it cant' be last lesson.
			var section = this.get_parent();
			if ( section.collection.indexOf( section ) < ( section.collection.size() - 1 ) ) {
				return false;
			}

			// It's last lesson in last section.
			return true;

		},

		/**
		 * Determine if this is the first lesson within its section
		 *
		 * @since 4.20.0
		 *
		 * @return {Boolean} Whether this is the first lesson of its section.
		 */
		is_first_in_section: function() {
			return 0 === this.collection.indexOf( this );
		},

		/**
		 * Determine if this is the last lesson within its section
		 *
		 * @since 4.20.0
		 *
		 * @return {Boolean} Whether this is the last lesson of its section.
		 */
		is_last_in_section: function() {
			return this.collection.indexOf( this ) === ( this.collection.size() - 1 );
		},

		/**
		 * Get prev lesson in a course
		 *
		 * @since 4.20.0
		 *
		 * @param {String} status Prev lesson post status. If not specified any status will be taken into account.
		 * @return {Object}|false Previous lesson model or `false` if no previous lesson could be found.
		 */
		get_prev: function( status ) {
			return this.get_sibling( 'prev', status );
		},

		/**
		 * Get next lesson in a course
		 *
		 * @since 4.20.0
		 *
		 * @param {String} status Next lesson post status. If not specified any status will be taken into account.
		 * @return {Object}|false Next lesson model or `false` if no next lesson could be found.
		 */
		get_next: function( status ) {
			return this.get_sibling( 'next', status );
		},

		/**
		 * Get a sibling lesson
		 *
		 * @param {String} direction Siblings direction [next|prev]. If not specified will fall back on 'prev'.
		 * @param {String} status    Sibling lesson post status. If not specified any status will be taken into account.
		 * @return {Object}|false Sibling lesson model, in the specified direction, or `false` if no sibling lesson could be found.
		 */
		get_sibling: function( direction, status ) {

			direction = 'next' === direction ? direction : 'prev';

			// Functions and vars to use when direction is 'prev' (default).
			var is_course_limit_reached_f               = 'is_first_in_course',
				is_section_limit_reached_f              = 'is_first_in_section',
				sibling_index_increment                 = -1,
				get_sibling_lesson_in_sibling_section_f = 'last';

			if ( 'next' === direction ) {
				is_course_limit_reached_f               = 'is_last_in_course';
				is_section_limit_reached_f              = 'is_last_in_section';
				sibling_index_increment                 = 1,
				get_sibling_lesson_in_sibling_section_f = 'first';
			}

			if ( this[ is_course_limit_reached_f ]() ) {
				return false;
			}

			var sibling_index  = this.collection.indexOf( this ) + sibling_index_increment,
				sibling_lesson = this.collection.at( sibling_index );

			if ( this[ 'next' === direction ? 'is_last_in_section' : 'is_first_in_section' ]() ) {
				var cur_section     = this.get_parent(),
					sibling_section = cur_section[ 'get_' + direction ]( false );

				// Skip sibling empty sections.
				while ( sibling_section && ! sibling_section.get( 'lessons' ).size() ) {
					sibling_section = sibling_section[ 'get_' + direction ]( false );
				}

				// Couldn't find any suitable lesson.
				if ( ! sibling_section || ! sibling_section.get( 'lessons' ).size() ) {
					return false;
				}

				sibling_lesson = sibling_section.get( 'lessons' )[ get_sibling_lesson_in_sibling_section_f ]();

			}

			// If we need a specific lesson status.
			if ( status && status !== sibling_lesson.get( 'status' ) ) {
				return sibling_lesson.get_sibling( direction, status );
			}

			return sibling_lesson;

		},

		/**
		 * Initialize lesson assignments *if* the assignments addon is available and enabled
		 *
		 * @since 3.17.0
		 *
		 * @return {Void}
		 */
		maybe_init_assignments: function() {

			if ( ! window.llms_builder.assignments ) {
				return;
			}

			this.relationships.children.assignment = {
				class: 'Assignment',
				conditional: function( model ) {
					// If assignment is enabled OR not enabled but we have some assignment data as an obj.
					return ( 'yes' === model.get( 'assignment_enabled' ) || ! _.isEmpty( model.get( 'assignment' ) ) );
				},
				model: 'llms_assignment',
				type: 'model',
			};

		},

	}, Relationships, Utilities ) );

} );