PageSourceSearch

https://ihn-on-line.com/wp-content/plugins/lifterlms/assets/js/llms.js?ver=1791098298

js ihn-on-line.com collected 2026-10-04 07:19:46 UTC 60,058 bytes, 2,614 lines download raw bytes

1/****************************************************************
2 *
3 * Contributor's Notice
4 * 
5 * This is a compiled file and should not be edited directly!
6 * The uncompiled script is located in the "assets/private" directory
7 * 
8 ****************************************************************/
9
10/**
11 * Main LLMS Namespace
12 *
13 * @since 1.0.0
14 * @version 5.3.3
15 */
16
17var LLMS = window.LLMS || {};
18( function( $ ){
19
20	'use strict';
21
22	/**
23	 * Load all app modules
24	 */
25	/**
26	 * Front End Achievements
27	 *
28	 * @package LifterLMS/Scripts
29	 *
30	 * @since 3.14.0
31	 * @version 6.10.2
32	 */
33	
34	LLMS.Achievements = {
35	
36		/**
37		 * Init
38		 *
39		 * @since 3.14.0
40		 * @since 4.5.1 Fix conditional loading check.
41		 *
42		 * @return {void}
43		 */
44		init: function() {
45	
46			if ( $( '.llms-achievement' ).length ) {
47	
48				var self = this;
49	
50				$( function() {
51					self.bind();
52					self.maybe_open();
53				} );
54			}
55	
56		},
57	
58		/**
59		 * Bind DOM events
60		 *
61		 * @since 3.14.0
62		 *
63		 * @return {void}
64		 */
65		bind: function() {
66	
67			var self = this;
68	
69			$( '.llms-achievement' ).each( function() {
70	
71				self.create_modal( $( this ) );
72	
73			} );
74	
75			$( '.llms-achievement' ).on( 'click', function() {
76	
77				var $this  = $( this ),
78					id     = 'achievement-' + $this.attr( 'data-id' ),
79					$modal = $( '#' + id );
80	
81				if ( ! $modal.length ) {
82					self.create_modal( $this );
83				}
84	
85				$modal.iziModal( 'open' );
86	
87			} );
88	
89		},
90	
91		/**
92		 * Creates modal a modal for an achievement
93		 *
94		 * @since 3.14.0
95		 *
96		 * @param obj $el The jQuery selector for the modal card.
97		 * @return {void}
98		 */
99		create_modal: function( $el ) {
100	
101			var id     = 'achievement-' + $el.attr( 'data-id' ),
102				$modal = $( '#' + id );
103	
104			if ( ! $modal.length ) {
105				$modal = $( '<div class="llms-achievement-modal" id="' + id + '" />' );
106				$( 'body' ).append( $modal );
107			}
108	
109			$modal.iziModal( {
110				headerColor: '#3a3a3a',
111				group: 'achievements',
112				history: true,
113				loop: true,
114				overlayColor: 'rgba( 0, 0, 0, 0.6 )',
115				transitionIn: 'fadeInDown',
116				transitionOut: 'fadeOutDown',
117				width: 340,
118				onOpening: function( modal ) {
119	
120					modal.setTitle( $el.find( '.llms-achievement-title' ).html() );
121					modal.setSubtitle( $el.find( '.llms-achievement-date' ).html() );
122					modal.setContent( '<div class="llms-achievement">' + $el.html() + '</div>' );
123	
124				},
125	
126				onClosing: function() {
127					window.history.pushState( '', document.title, window.location.pathname + window.location.search );
128				},
129	
130			} );
131	
132		},
133	
134		/**
135		 * On page load, opens a modal if the URL contains an achievement in the location hash
136		 *
137		 * @since 3.14.0
138		 * @since 6.10.2 Sanitize achievement IDs before using window.location.hash to trigger the modal open.
139		 *
140		 * @return {void}
141		 */
142		maybe_open: function() {
143	
144			let hash = window.location.hash.split( '-' );
145			if ( 2 !== hash.length ) {
146				return;
147			}
148	
149			hash[1] = parseInt( hash[1] );
150			if ( '#achievement-' !== hash[0] || ! Number.isInteger( hash[1] ) ) {
151				return;
152			}
153	
154			const a = document.querySelector( `a[href="${ hash.join( '-' ) }"]` )
155			if ( ! a ) {
156				return;
157			}
158	
159			a.click();
160	
161		}
162	
163	};
164	
165		/**
166	 * Main Ajax class
167	 * Handles Primary Ajax connection
168	 *
169	 * @package LifterLMS/Scripts
170	 *
171	 * @since Unknown
172	 * @version Unknown
173	 */
174	
175	LLMS.Ajax = {
176	
177		/**
178		 * Url
179		 *
180		 * @type {String}
181		 */
182		url: window.ajaxurl || window.llms.ajaxurl,
183	
184		/**
185		 * Type
186		 *
187		 * @type {[type]}
188		 */
189		type: 'post',
190	
191		/**
192		 * Data
193		 *
194		 * @type {[type]}
195		 */
196		data: [],
197	
198		/**
199		 * Cache
200		 *
201		 * @type {[type]}
202		 */
203		cache: false,
204	
205		/**
206		 * DataType
207		 * defaulted to json
208		 *
209		 * @type {String}
210		 */
211		dataType: 'json',
212	
213		/**
214		 * Async
215		 * default to false
216		 *
217		 * @type {Boolean}
218		 */
219		async: true,
220	
221		response:[],
222	
223		/**
224		 * Initialize Ajax methods
225		 *
226		 * @since Unknown
227		 * @since 4.4.0 Update ajax nonce source.
228		 *
229		 * @param {Object} obj Options object.
230		 * @return {Object}
231		 */
232		init: function( obj ) {
233	
234			// If obj is not of type object or null return false.
235			if ( obj === null || typeof obj !== 'object' ) {
236				return false;
237			}
238	
239			// set object defaults if values are not supplied
240			obj.url      = 'url'         in obj ? obj.url : this.url;
241			obj.type     = 'type' 		in obj ? obj.type : this.type;
242			obj.data     = 'data' 		in obj ? obj.data : this.data;
243			obj.cache    = 'cache' 		in obj ? obj.cache : this.cache;
244			obj.dataType = 'dataType'	in obj ? obj.dataType : this.dataType;
245			obj.async    = 'async'		in obj ? obj.async : this.async;
246	
247			// Add nonce to data object.
248			obj.data._ajax_nonce = window.llms.ajax_nonce || wp_ajax_data.nonce;
249	
250			// Add post id to data object.
251			var $R           = LLMS.Rest,
252			query_vars       = $R.get_query_vars();
253			obj.data.post_id = 'post' in query_vars ? query_vars.post : null;
254			if ( ! obj.data.post_id && $( 'input#post_ID' ).length ) {
255				obj.data.post_id = $( 'input#post_ID' ).val();
256			}
257	
258			return obj;
259		},
260	
261		/**
262		 * Call
263		 * Called by external classes
264		 * Sets up jQuery Ajax object
265		 *
266		 * @param  {[object]} [object of ajax settings]
267		 * @return {[mixed]} [false if not object or this]
268		 */
269		call: function(obj) {
270	
271			// get default variables if not included in call
272			var settings = this.init( obj );
273	
274			// if init return a response of false
275			if ( ! settings) {
276				return false;
277			} else {
278				this.request( settings );
279			}
280	
281			return this;
282	
283		},
284	
285		/**
286		 * Calls jQuery Ajax on settings object
287		 *
288		 * @return {[object]} [this]
289		 */
290		request: function(settings) {
291	
292			$.ajax( settings );
293	
294			return this;
295	
296		}
297	
298	};
299	
300		/**
301	 * Create a Donut Chart
302	 *
303	 * @package LifterLMS/Scripts
304	 *
305	 * @since 3.9.0
306	 * @version 4.15.0
307	 *
308	 * @link https://gist.github.com/joeyinbox/8205962
309	 *
310	 * @param {Object} $el jQuery element to draw a chart within.
311	 */
312	
313	LLMS.Donut = function( $el ) {
314	
315		/**
316		 * Constructor
317		 *
318		 * @since 3.9.0
319		 * @since 4.15.0 Flip animation in RTL.
320		 *
321		 * @param {Object} options Donut options.
322		 * @return {Void}
323		 */
324		function Donut(options) {
325	
326			this.settings = $.extend( {
327				element: options.element,
328				percent: 100
329			}, options );
330	
331			this.circle                = this.settings.element.find( 'path' );
332			this.settings.stroke_width = parseInt( this.circle.css( 'stroke-width' ) );
333			this.radius                = ( parseInt( this.settings.element.css( 'width' ) ) - this.settings.stroke_width ) / 2;
334			this.angle                 = $( 'body' ).hasClass( 'rtl' ) ? 82.5 : 97.5; // Origin of the draw at the top of the circle
335			this.i                     = Math.round( 0.75 * this.settings.percent );
336			this.first                 = true;
337			this.increment             = $( 'body' ).hasClass( 'rtl' ) ? -5 : 5;
338	
339			this.animate = function() {
340				this.timer = setInterval( this.loop.bind( this ), 10 );
341			};
342	
343			this.loop = function() {
344				this.angle += this.increment;
345				this.angle %= 360;
346				var radians = ( this.angle / 180 ) * Math.PI,
347					x       = this.radius + this.settings.stroke_width / 2 + Math.cos( radians ) * this.radius,
348					y       = this.radius + this.settings.stroke_width / 2 + Math.sin( radians ) * this.radius,
349					d;
350				if (this.first === true) {
351					d          = this.circle.attr( 'd' ) + ' M ' + x + ' ' + y;
352					this.first = false;
353				} else {
354					d = this.circle.attr( 'd' ) + ' L ' + x + ' ' + y;
355				}
356				this.circle.attr( 'd', d );
357				this.i--;
358	
359				if (this.i <= 0) {
360					clearInterval( this.timer );
361				}
362			};
363		}
364	
365		/**
366		 * Draw donut element
367		 *
368		 * @since 3.9.0
369		 *
370		 * @param {Object} $el jQuery element to draw a chart within.
371		 * @return {Void}
372		 */
373		function draw( $el ) {
374			var path = '<path d="M100,100" />';
375			$el.append( '<svg preserveAspectRatio="xMidYMid" xmlns:xlink="http://www.w3.org/1999/xlink">' + path + '</svg>' );
376			var donut = new Donut( {
377				element: $el,
378				percent: $el.attr( 'data-perc' )
379			} );
380			donut.animate();
381		}
382	
383		draw( $el );
384	
385	};
386	
387		/**
388	 * Forms
389	 *
390	 * @package LifterLMS/Scripts
391	 *
392	 * @since 5.0.0
393	 * @version 7.0.0
394	 */
395	
396	LLMS.Forms = {
397	
398		/**
399		 * Stores locale information.
400		 *
401		 * Added via PHP.
402		 *
403		 * @type {Object}
404		 */
405		address_info: {},
406	
407		/**
408		 * jQuery ref. to the city text field.
409		 *
410		 * @type {Object}
411		 */
412		$cities: null,
413	
414		/**
415		 * jQuery ref. to the countries select field.
416		 *
417		 * @type {Object}
418		 */
419		$countries: null,
420	
421		/**
422		 * jQuery ref. to the states select field.
423		 *
424		 * @type {Object}
425		 */
426		$states: null,
427	
428		/**
429		 * jQuery ref. to the hidden states holder field.
430		 *
431		 * @type {Object}
432		 */
433		$states_holder: null,
434	
435		/**
436		 * Init
437		 *
438	 	 * @since 5.0.0
439	 	 * @since 5.3.3 Move select2 dependency check into the `bind_l10_selects()` method.
440	 	 *
441	 	 * @return {void}
442		 */
443		init: function() {
444	
445			if ( $( 'body' ).hasClass( 'wp-admin' ) ) {
446				if ( ! ( $( 'body' ).hasClass( 'profile-php' ) || $( 'body' ).hasClass( 'user-edit-php' ) ) ) {
447					return;
448				}
449			}
450	
451			var self = this;
452	
453			self.bind_matching_fields();
454			self.bind_voucher_field();
455			self.bind_edit_account();
456			self.bind_l10n_selects();
457	
458		},
459	
460		/**
461		 * Bind DOM events for the edit account screen.
462		 *
463		 * @since 5.0.0
464		 *
465		 * @return {void}
466		 */
467		bind_edit_account: function() {
468	
469			// Not an edit account form.
470			if ( ! $( 'form.llms-person-form.edit-account' ).length ) {
471				return;
472			}
473	
474			$( '.llms-toggle-fields' ).on( 'click', this.handle_toggle_click );
475	
476		},
477	
478		/**
479		 * Bind DOM Events fields with dynamic localization values and language.
480		 *
481		 * @since 5.0.0
482		 * @since 5.3.3 Bind select2-related events after ensuring select2 is available.
483		 *
484		 * @return {void}
485		 */
486		bind_l10n_selects: function() {
487	
488			var self = this;
489	
490			self.$cities    = $( '#llms_billing_city' );
491			self.$countries = $( '.llms-l10n-country-select select' );
492			self.$states    = $( '.llms-l10n-state-select select' );
493			self.$zips      = $( '#llms_billing_zip' );
494	
495			if ( ! self.$countries.length ) {
496				return;
497			}
498	
499			var isSelect2Available = function() {
500				return ( undefined !== $.fn.llmsSelect2 );
501			};
502	
503			LLMS.wait_for( isSelect2Available, function() {
504	
505				if ( self.$states.length ) {
506					self.prep_state_field();
507				}
508	
509				self.$countries.add( self.$states ).llmsSelect2( { width: '100%' } );
510	
511				if ( window.llms.address_info ) {
512					self.address_info = JSON.parse( window.llms.address_info );
513				}
514	
515				self.$countries.on( 'change', function() {
516	
517					var val = $( this ).val();
518					self.update_locale_info( val );
519	
520				} ).trigger( 'change' );
521	
522			}, 'llmsSelect2' );
523	
524		},
525	
526		/**
527		 * Ensure "matching" fields match.
528		 *
529		 * @since 5.0.0
530		 *
531		 * @return {Void}
532		 */
533		bind_matching_fields: function() {
534	
535			var $fields = $( 'input[data-match]' ).not( '[type="password"]' );
536	
537			$fields.each( function() {
538	
539				var $field = $( this ),
540					$match = $( '#' + $field.attr( 'data-match' ) ),
541					$parents;
542	
543				if ( $match.length ) {
544	
545					$parents = $field.closest( '.llms-form-field' ).add( $match.closest( '.llms-form-field' ) );
546	
547					$field.on( 'input change', function() {
548	
549						var val_1 = $field.val(),
550							val_2 = $match.val();
551	
552						if ( val_1 && val_2 && val_1 !== val_2 ) {
553							$parents.addClass( 'invalid' );
554						} else {
555							$parents.removeClass( 'invalid' );
556						}
557	
558					} );
559	
560				}
561	
562			} );
563	
564		},
565	
566		/**
567		 * Bind DOM events for voucher toggles UX.
568		 *
569		 * @since 5.0.0
570		 *
571		 * @return {void}
572		 */
573		bind_voucher_field: function() {
574	
575			$( '#llms-voucher-toggle' ).on( 'click', function( e ) {
576				e.preventDefault();
577				$( '#llms_voucher' ).toggle();
578			} );
579	
580		},
581	
582		/**
583		 * Retrieve the parent element for a given field.
584		 *
585		 * The parent element is hidden when the field isn't required.
586		 *
587		 * @since 5.0.0
588		 * @since 7.0.0 Do not look for a WP column wrapper anymore, always return the field's wrapper div.
589		 *
590		 * @param {Object} $field jQuery dom object.
591		 * @return {Object}
592		 */
593		get_field_parent: function( $field ) {
594	
595			return $field.closest( '.llms-form-field' );
596	
597		},
598	
599		/**
600		 * Retrieve the text of a label
601		 *
602		 * Removes any children HTML elements (eg: required span elements) and returns only the labels text.
603		 *
604		 * @since 5.0.0
605		 *
606		 * @param {Object} $label jQuery object for a label element.
607		 * @return {String}
608		 */
609		get_label_text: function( $label ) {
610	
611			var $clone = $label.clone();
612			$clone.find( '*' ).remove();
613			return $clone.text().trim();
614	
615		},
616	
617		/**
618		 * Callback function to handle the "toggle" button links for changing email address and password on account edit forms
619		 *
620		 * @since 5.0.0
621		 *
622		 * @param {Object} event Native JS event object.
623		 * @return {void}
624		 */
625		handle_toggle_click: function( event ) {
626	
627			event.preventDefault();
628	
629			var $this       = $( this ),
630				$fields     = $( $( this ).attr( 'data-fields' ) ),
631				isShowing   = $this.attr( 'data-is-showing' ) || 'no',
632				displayFunc = 'yes' === isShowing ? 'hide' : 'show',
633				disabled    = 'yes' === isShowing ? 'disabled' : null,
634				textAttr    = 'yes' === isShowing ? 'data-change-text' : 'data-cancel-text';
635	
636			$fields.each( function() {
637	
638				$( this ).closest( '.llms-form-field' )[ displayFunc ]();
639				$( this ).attr( 'disabled', disabled );
640	
641			} );
642	
643			$this.text( $this.attr( textAttr ) );
644			$this.attr( 'data-is-showing', 'yes' === isShowing ? 'no' : 'yes' );
645	
646		},
647	
648		/**
649		 * Prepares the state select field.
650		 *
651		 * Moves All optgroup elements into a hidden & disabled select element.
652		 *
653		 * @since 5.0.0
654		 *
655		 * @return {void}
656		 */
657		prep_state_field: function() {
658	
659			var $parent = this.$states.closest( '.llms-form-field' );
660	
661			this.$holder = $( '<select disabled style="display:none !important;" />' );
662	
663			this.$holder.appendTo( $parent );
664			this.$states.find( 'optgroup' ).appendTo( this.$holder );
665	
666		},
667	
668		/**
669		 * Updates the text of a label for a given field.
670		 *
671		 * @since 5.0.0
672		 *
673		 * @param {Object} $field jQuery object of the form field.
674		 * @param {String} text Label text.
675		 * @return {void}
676		 */
677		update_label: function( $field, text ) {
678	
679			var $label = this.get_field_parent( $field ).find( 'label' ),
680				$required = $label.find( '.llms-required' ).clone();
681	
682			$label.html( text );
683			$label.append( $required );
684	
685		},
686	
687		/**
688		 * Update form fields based on selected country
689		 *
690		 * Replaces label text with locale-specific language and
691		 * hides or shows zip fields based on whether or not
692		 * they are required for the given country.
693		 *
694		 * @since 5.0.0
695		 *
696		 * @param {String} country_code Currently selected country code.
697		 * @return {void}
698		 */
699		update_locale_info: function( country_code ) {
700	
701			if ( ! this.address_info || ! this.address_info[ country_code ] ) {
702				return;
703			}
704	
705			var info = this.address_info[ country_code ];
706	
707			this.update_state_options( country_code );
708			this.update_label( this.$states, info.state );
709	
710			this.update_locale_info_for_field( this.$cities, info.city );
711			this.update_locale_info_for_field( this.$zips, info.postcode );
712	
713		},
714	
715		/**
716		 * Update locale info for a given field.
717		 *
718		 * @since 5.0.0
719		 *
720		 * @param {Object}         $field The jQuery object for the field.
721		 * @param {String|Boolean} label  The text of the label, or `false` when the field isn't supported.
722		 * @return {Void}
723		 */
724		update_locale_info_for_field: function( $field, label ) {
725	
726			if ( label ) {
727				this.update_label( $field, label );
728				this.enable_field( $field );
729			} else {
730				this.disable_field( $field );
731			}
732	
733		},
734	
735		/**
736		 * Update the available options in the state field
737		 *
738		 * Removes existing options and copies the options
739		 * for the requested country from the hidden select field.
740		 *
741		 * If there are no states for the given country the state
742		 * field will be hidden.
743		 *
744		 * @since 5.0.0
745		 *
746		 * @param {String} country_code Currently selected country code.
747		 * @return {void}
748		 */
749		update_state_options: function( country_code ) {
750	
751			if ( ! this.$states.length ) {
752				return;
753			}
754	
755			var opts = this.$holder.find( 'optgroup[data-key="' + country_code + '"] option' ).clone();
756	
757			if ( ! opts.length ) {
758				this.$states.html( '<option>&nbsp</option>' );
759				this.disable_field( this.$states );
760			} else {
761				this.enable_field( this.$states );
762				this.$states.html( opts );
763			}
764	
765		},
766	
767		/**
768		 * Disable a given field
769		 *
770		 * It also hides the parent element, and adds an empty hidden input field
771		 * with the same 'name' as teh being disabled field so to be sure to clear the field.
772		 *
773		 * @since 5.0.0
774		 *
775		 * @param {Object} $field The jQuery object for the field.
776		 */
777		disable_field: function( $field ) {
778			$(
779				'<input>',
780				{ name: $field.attr('name'), class: $field.attr( 'class' ) + ' hidden', type: 'hidden' }
781			).insertAfter( $field );
782			$field.attr( 'disabled', 'disabled' );
783			this.get_field_parent( $field ).hide();
784		},
785	
786		/**
787		 * Enable a given field
788		 *
789		 * It also shows the parent element, and removes the empty hidden input field
790		 * previously added by disable_field().
791		 *
792		 * @since 5.0.0
793		 *
794		 * @param {Object} $field The jQuery object for the field.
795		 */
796		enable_field: function( $field ) {
797			$field.removeAttr( 'disabled' );
798			$field.next( '.hidden[name='+$field.attr('name')+']' ).detach();
799			this.get_field_parent( $field ).show();
800		}
801	
802	};
803	
804		/**
805	 * Instructors List
806	 *
807	 * @package LifterLMS/Scripts
808	 *
809	 * @since  Unknown
810	 * @version  Unknown
811	 */
812	
813	LLMS.Instructors = {
814	
815		/**
816		 * Init
817		 */
818		init: function() {
819	
820			var self = this;
821	
822			if ( $( 'body' ).hasClass( 'wp-admin' ) ) {
823				return;
824			}
825	
826			if ( $( '.llms-instructors' ).length ) {
827	
828				LLMS.wait_for_matchHeight( function() {
829					self.bind();
830				} );
831	
832			}
833	
834		},
835	
836		/**
837		 * Bind Method
838		 * Handles dom binding on load
839		 *
840		 * @return {[type]} [description]
841		 */
842		bind: function() {
843	
844			$( '.llms-instructors .llms-author' ).matchHeight();
845	
846		},
847	
848	};
849	
850		/**
851	 * Localization functions for LifterLMS Javascript
852	 *
853	 * @package LifterLMS/Scripts
854	 *
855	 * @since  2.7.3
856	 * @version  2.7.3
857	 *
858	 * @todo  we need more robust translation functions to handle sprintf and pluralization
859	 *        at this moment we don't need those and haven't stubbed them out
860	 *        those will be added when they're needed
861	 */
862	
863	LLMS.l10n = LLMS.l10n || {};
864	
865	LLMS.l10n.translate = function ( string ) {
866	
867		var self = this;
868	
869		if ( self.strings[string] ) {
870	
871			return self.strings[string];
872	
873		} else {
874	
875			return string;
876	
877		}
878	
879	};
880	
881	/**
882	 * Translate and replace placeholders in a string
883	 *
884	 * @example LLMS.l10n.replace( 'This is a %2$s %1$s String', {
885	 *           	'%1$s': 'cool',
886	 *    			'%2$s': 'very'
887	 *    		} );
888	 *    		Output: "This is a very cool String"
889	 *
890	 * @param    string   string        text string
891	 * @param    object   replacements  object containing token => replacement pairs
892	 * @return   string
893	 * @since    3.16.0
894	 * @version  3.16.0
895	 */
896	LLMS.l10n.replace = function( string, replacements ) {
897	
898		var str = this.translate( string );
899	
900		$.each( replacements, function( token, value ) {
901	
902			if ( -1 !== token.indexOf( 's' ) ) {
903				value = value.toString();
904			} else if ( -1 !== token.indexOf( 'd' ) ) {
905				value = value * 1;
906			}
907	
908			str = str.replace( token, value );
909	
910		} );
911	
912		return str;
913	
914	};
915	
916		/**
917	 * Handle Lesson Preview Elements
918	 *
919	 * @package LifterLMS/Scripts
920	 *
921	 * @since    3.0.0
922	 * @version  3.16.12
923	 */
924	
925	LLMS.LessonPreview = {
926	
927		/**
928		 * A jQuery object of all outlines present on the current screen
929		 *
930		 * @type obj
931		 */
932		$els: null,
933	
934		/**
935		 * Initialize
936		 *
937		 * @return void
938		 */
939		init: function() {
940	
941			var self = this;
942	
943			this.$locked = $( 'a[href="#llms-lesson-locked"]' );
944	
945			if ( this.$locked.length ) {
946	
947				self.bind();
948	
949			}
950	
951			if ( $( '.llms-course-navigation' ).length ) {
952	
953				LLMS.wait_for_matchHeight( function() {
954	
955					self.match_height();
956	
957				} );
958	
959			}
960	
961		},
962	
963		/**
964		 * Bind DOM events
965		 *
966		 * @return void
967		 * @since    3.0.0
968		 * @version  3.16.12
969		 */
970		bind: function() {
971	
972			var self = this;
973	
974			this.$locked.on( 'click', function() {
975				return false;
976			} );
977	
978			this.$locked.on( 'mouseenter', function() {
979	
980				var $tip = $( this ).find( '.llms-tooltip' );
981				if ( ! $tip.length ) {
982					var msg = $( this ).attr( 'data-tooltip-msg' );
983					if ( ! msg ) {
984						msg = LLMS.l10n.translate( 'You do not have permission to access this content' );
985					}
986					$tip = self.get_tooltip( msg );
987					$( this ).append( $tip );
988				}
989				setTimeout( function() {
990					$tip.addClass( 'show' );
991				}, 10 );
992	
993			} );
994	
995			this.$locked.on( 'mouseleave', function() {
996	
997				var $tip = $( this ).find( '.llms-tooltip' );
998				$tip.removeClass( 'show' );
999	
1000			} );
1001	
1002		},
1003	
1004		/**
1005		 * Match the height of lesson preview items in course navigation blocks
1006		 *
1007		 * @return   void
1008		 * @since    3.0.0
1009		 * @version  3.0.0
1010		 */
1011		match_height: function() {
1012	
1013			$( '.llms-course-navigation .llms-lesson-link' ).matchHeight();
1014	
1015		},
1016	
1017		/**
1018		 * Get a tooltip element
1019		 *
1020		 * @param    string   msg   message to display inside the tooltip
1021		 * @return   obj
1022		 * @since    3.0.0
1023		 * @version  3.2.4
1024		 */
1025		get_tooltip: function( msg ) {
1026			var $el = $( '<div class="llms-tooltip" />' );
1027			$el.append(
1028				$('<div>', {
1029					'class': 'llms-tooltip-content',
1030					'aria-hidden': 'true'
1031				}).text( msg )
1032			);
1033			return $el;
1034		},
1035	
1036	};
1037	
1038		/**
1039	 * LifterLMS Loops JS
1040	 *
1041	 * @package LifterLMS/Scripts
1042	 *
1043	 * @since    3.0.0
1044	 * @version  3.14.0
1045	 */
1046	
1047	LLMS.Loops = {
1048	
1049		/**
1050		 * Initialize
1051		 *
1052		 * @return void
1053		 */
1054		init: function() {
1055	
1056			var self = this;
1057	
1058			if ( $( '.llms-loop' ).length ) {
1059	
1060				LLMS.wait_for_matchHeight( function() {
1061	
1062					self.match_height();
1063	
1064				} );
1065	
1066			}
1067	
1068		},
1069	
1070		/**
1071		 * Match the height of .llms-loop-item
1072		 *
1073		 * @return   void
1074		 * @since    3.0.0
1075		 * @version  3.14.0
1076		 */
1077		match_height: function() {
1078	
1079			$( '.llms-loop-item .llms-loop-item-content' ).matchHeight();
1080			$( '.llms-achievement-loop-item .llms-achievement' ).matchHeight();
1081			$( '.llms-certificate-loop-item .llms-certificate' ).matchHeight();
1082	
1083		},
1084	
1085	};
1086	
1087		/**
1088	 * Handle the Collapsible Syllabus Widget / Shortcode
1089	 *
1090	 * @package LifterLMS/Scripts
1091	 *
1092	 * @since Unknown
1093	 * @version Unknown
1094	 */
1095	
1096	LLMS.OutlineCollapse = {
1097	
1098		/**
1099		 * A jQuery object of all outlines present on the current screen
1100		 *
1101		 * @type obj
1102		 */
1103		$outlines: null,
1104	
1105		/**
1106		 * Initialize
1107		 *
1108		 * @return void
1109		 */
1110		init: function() {
1111	
1112			this.$outlines = $( '.llms-widget-syllabus--collapsible' );
1113	
1114			if ( this.$outlines.length ) {
1115	
1116				this.bind();
1117	
1118			}
1119	
1120		},
1121	
1122		/**
1123		 * Bind DOM events
1124		 *
1125		 * @return void
1126		 */
1127		bind: function() {
1128	
1129			var self = this;
1130	
1131			this.$outlines.each( function() {
1132	
1133				var $outline = $( this ),
1134					$headers = $outline.find( '.llms-section .section-header' );
1135	
1136				// bind header clicks
1137				$headers.on( 'click', function( e ) {
1138	
1139					e.preventDefault();
1140	
1141					var $toggle  = $( this ),
1142						$section = $toggle.closest( '.llms-section' ),
1143						state    = self.get_section_state( $section );
1144	
1145					switch ( state ) {
1146	
1147						case 'closed':
1148							self.open_section( $section );
1149						break;
1150	
1151						case 'opened':
1152							self.close_section( $section );
1153						break;
1154	
1155					}
1156	
1157				} );
1158	
1159				// bind optional toggle "buttons"
1160				$outline.find( '.llms-collapse-toggle' ).on( 'click', function( e ) {
1161	
1162					e.preventDefault();
1163	
1164					var $btn            = $( this ),
1165						action          = $btn.attr( 'data-action' ),
1166						opposite_action = ( 'close' === action ) ? 'opened' : 'closed';
1167	
1168					$headers.each( function() {
1169	
1170						var $section = $( this ).closest( '.llms-section' ),
1171							state    = self.get_section_state( $section );
1172	
1173						if ( opposite_action !== state ) {
1174							return true;
1175						}
1176	
1177						switch ( state ) {
1178	
1179							case 'closed':
1180								self.close_section( $section );
1181							break;
1182	
1183							case 'opened':
1184								self.open_section( $section );
1185							break;
1186	
1187						}
1188	
1189						$( this ).trigger( 'click' );
1190	
1191					} );
1192	
1193				} );
1194	
1195			} );
1196	
1197		},
1198	
1199		/**
1200		 * Close an outline section
1201		 *
1202		 * @param  obj    $section   jQuery selector of a '.llms-section'
1203		 * @return void
1204		 */
1205		close_section: function( $section ) {
1206	
1207			$section.removeClass( 'llms-section--opened' ).addClass( 'llms-section--closed' );
1208	
1209		},
1210	
1211		/**
1212		 * Open an outline section
1213		 *
1214		 * @param  obj    $section   jQuery selector of a '.llms-section'
1215		 * @return void
1216		 */
1217		open_section: function( $section ) {
1218	
1219			$section.removeClass( 'llms-section--closed' ).addClass( 'llms-section--opened' );
1220	
1221		},
1222	
1223		/**
1224		 * Get the current state (open or closed) of an outline section
1225		 *
1226		 * @param  obj    $section   jQuery selector of a '.llms-section'
1227		 * @return string            'opened' or 'closed'
1228		 */
1229		get_section_state: function( $section ) {
1230	
1231			return $section.hasClass( 'llms-section--opened' ) ? 'opened' : 'closed';
1232	
1233		}
1234	
1235	};
1236	
1237		/**
1238	 * Handle Password Strength Meter for registration and password update fields
1239	 *
1240	 * @package LifterLMS/Scripts
1241	 *
1242	 * @since 3.0.0
1243	 * @version 5.0.0
1244	 */
1245	
1246	$.extend( LLMS.PasswordStrength, {
1247	
1248		/**
1249		 * jQuery ref for the password strength meter object.
1250		 *
1251		 * @type {Object}
1252		 */
1253		$meter: $( '.llms-password-strength-meter' ),
1254	
1255		/**
1256		 * jQuery ref for the password field.
1257		 *
1258		 * @type {Object}
1259		 */
1260		$pass: null,
1261	
1262		/**
1263		 * jQuery ref for the password confirmation field
1264		 *
1265		 * @type {Object}
1266		 */
1267		$conf: null,
1268	
1269		/**
1270		 * jQuery ref for form element.
1271		 *
1272		 * @type {Object}
1273		 */
1274		$form: null,
1275	
1276		/**
1277		 * Init
1278		 * loads class methods
1279		 *
1280		 * @since 3.0.0
1281		 * @since 3.7.0 Unknown
1282		 * @since 5.0.0 Move reference setup to `setup_references()`.
1283		 *              Use `LLMS.wait_for()` for dependency waiting.
1284		 *
1285		 * @return {Void}
1286		 */
1287		init: function() {
1288	
1289			if ( $( 'body' ).hasClass( 'wp-admin' ) ) {
1290				return;
1291			}
1292	
1293			if ( ! this.setup_references() ) {
1294				return;
1295			}
1296	
1297			var self = this;
1298	
1299			LLMS.wait_for( function() {
1300				return ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.passwordStrength );
1301			}, function() {
1302				self.bind();
1303				self.$form.trigger( 'llms-password-strength-ready' );
1304			} );
1305	
1306		},
1307	
1308		/**
1309		 * Bind DOM Events
1310		 *
1311		 * @since 3.0.0
1312		 *
1313		 * @return void
1314		 */
1315		bind: function() {
1316	
1317			var self = this;
1318	
1319			// add submission event handlers when not on a checkout form
1320			if ( ! this.$form.hasClass( 'llms-checkout' ) ) {
1321				self.$form.on( 'submit', self, self.submit );
1322			}
1323	
1324			// check password strength on keyup
1325			self.$pass.add( self.$conf ).on( 'keyup', function() {
1326				self.check_strength();
1327			} );
1328	
1329		},
1330	
1331		/**
1332		 * Check the strength of a user entered password
1333		 * and update elements depending on the current strength
1334		 *
1335		 * @since 3.0.0
1336		 * @since 5.0.0 Allow password confirmation to be optional when checking strength.
1337		 *
1338		 * @return void
1339		 */
1340		check_strength: function() {
1341	
1342			var $pass_field = this.$pass.closest( '.llms-form-field' ),
1343				$conf_field = this.$conf && this.$conf.length ? this.$conf.closest( '.llms-form-field' ) : null,
1344				pass_length = this.$pass.val().length,
1345				conf_length = this.$conf && this.$conf.length ? this.$conf.val().length : 0;
1346	
1347			// hide the meter if both fields are empty
1348			if ( ! pass_length && ! conf_length ) {
1349				$pass_field.removeClass( 'valid invalid' );
1350				if ( $conf_field ) {
1351					$conf_field.removeClass( 'valid invalid' );
1352				}
1353				this.$meter.hide();
1354				return;
1355			}
1356	
1357			if ( this.get_current_strength_status() ) {
1358				$pass_field.removeClass( 'invalid' ).addClass( 'valid' );
1359				if ( conf_length ) {
1360					$conf_field.removeClass( 'invalid' ).addClass( 'valid' );
1361				}
1362			} else {
1363				$pass_field.removeClass( 'valid' ).addClass( 'invalid' );
1364				if ( conf_length ) {
1365					$conf_field.removeClass( 'valid' ).addClass( 'invalid' );
1366				}
1367			}
1368	
1369			this.$meter.removeClass( 'too-short very-weak weak medium strong mismatch' );
1370			this.$meter.show().addClass( this.get_current_strength( 'slug' ) );
1371			this.$meter.html( this.get_current_strength( 'text' ) );
1372	
1373		},
1374	
1375		/**
1376		 * Form submission action called during registration on checkout screen
1377		 *
1378		 * @since    3.0.0
1379		 *
1380		 * @param    obj       self      instance of this class
1381		 * @param    Function  callback  callback function, passes error message or success back to checkout handler
1382		 * @return   void
1383		 */
1384		checkout: function( self, callback ) {
1385	
1386			if ( self.get_current_strength_status() ) {
1387	
1388				callback( true );
1389	
1390			} else {
1391	
1392				callback( LLMS.l10n.translate( 'There is an issue with your chosen password.' ) );
1393	
1394			}
1395		},
1396	
1397		/**
1398		 * Get the list of blocklisted strings
1399		 *
1400		 * @since 5.0.0
1401		 *
1402		 * @return array
1403		 */
1404		get_blocklist: function() {
1405	
1406			// Default values from WP Core + any values added via settings filter..
1407			var blocklist = wp.passwordStrength.userInputDisallowedList().concat( this.get_setting( 'blocklist', [] ) );
1408	
1409			// Add values from all text fields in the form.
1410			this.$form.find( 'input[type="text"], input[type="email"], input[type="tel"], input[type="number"]' ).each( function() {
1411				var val = $( this ).val();
1412				if ( val ) {
1413					blocklist.push( val );
1414				}
1415			} );
1416	
1417			return blocklist;
1418	
1419		},
1420	
1421		/**
1422		 * Retrieve current strength as a number, a slug, or a translated text string
1423		 *
1424		 * @since 3.0.0
1425		 * @since 5.0.0 Allow password confirmation to be optional when checking strength.
1426		 *
1427		 * @param {String} format Derived return format [int|slug|text] defaults to int.
1428		 * @return mixed
1429		 */
1430		get_current_strength: function( format ) {
1431	
1432			format   = format || 'int';
1433			var pass = this.$pass.val(),
1434				conf = this.$conf && this.$conf.length ? this.$conf.val() : '',
1435				val;
1436	
1437			// enforce custom length requirement
1438			if ( pass.length < this.get_setting( 'min_length', 6 ) ) {
1439				val = -1;
1440			} else {
1441				val = wp.passwordStrength.meter( pass, this.get_blocklist(), conf );
1442				// 0 & 1 are both very-weak
1443				if ( 0 === val ) {
1444					val = 1;
1445				}
1446			}
1447	
1448			if ( 'slug' === format ) {
1449				return this.get_strength_slug( val );
1450			} else if ( 'text' === format ) {
1451				return this.get_strength_text( val );
1452			} else {
1453				return val;
1454			}
1455		},
1456	
1457		/**
1458		 * Determines if the current password strength meets the user-defined
1459		 * minimum password strength requirements
1460		 *
1461		 * @since 3.0.0
1462		 *
1463		 * @return   boolean
1464		 */
1465		get_current_strength_status: function() {
1466			var curr = this.get_current_strength(),
1467				min  = this.get_strength_value( this.get_minimum_strength() );
1468			return ( 5 === curr ) ? false : ( curr >= min );
1469		},
1470	
1471		/**
1472		 * Retrieve the minimum password strength for the current form.
1473		 *
1474		 * @since 3.0.0
1475		 * @since 5.0.0 Replaces the version output via an inline PHP script in favor of utilizing values configured in the settings object.
1476		 *
1477		 * @return {string}
1478		 */
1479		get_minimum_strength: function() {
1480			return this.get_setting( 'min_strength', 'strong' );
1481		},
1482	
1483		/**
1484		 * Get a setting and fallback to a default value.
1485		 *
1486		 * @since 5.0.0
1487		 *
1488		 * @param {String} key Setting key.
1489		 * @param {mixed} default_val Default value when the requested setting cannot be located.
1490		 * @return {mixed}
1491		 */
1492		get_setting: function( key, default_val ) {
1493			var settings = this.get_settings();
1494			return settings[ key ] ? settings[ key ] : default_val;
1495		},
1496	
1497		/**
1498		 * Get the slug associated with a strength value
1499		 *
1500		 * @since  3.0.0
1501		 *
1502		 * @param int strength_val Strength value number.
1503		 * @return string
1504		 */
1505		get_strength_slug: function( strength_val ) {
1506	
1507			var slugs = {
1508				'-1': 'too-short',
1509				1: 'very-weak',
1510				2: 'weak',
1511				3: 'medium',
1512				4: 'strong',
1513				5: 'mismatch',
1514			};
1515	
1516			return ( slugs[ strength_val ] ) ? slugs[ strength_val ] : slugs[5];
1517	
1518		},
1519	
1520		/**
1521		 * Gets the translated text associated with a strength value
1522		 *
1523		 * @since  3.0.0
1524		 *
1525		 * @param {Integer} strength_val Strength value
1526		 * @return {String}
1527		 */
1528		get_strength_text: function( strength_val ) {
1529	
1530			var texts = {
1531				'-1': LLMS.l10n.translate( 'Too Short' ),
1532				1: LLMS.l10n.translate( 'Very Weak' ),
1533				2: LLMS.l10n.translate( 'Weak' ),
1534				3: LLMS.l10n.translate( 'Medium' ),
1535				4: LLMS.l10n.translate( 'Strong' ),
1536				5: LLMS.l10n.translate( 'Mismatch' ),
1537			};
1538	
1539			return ( texts[ strength_val ] ) ? texts[ strength_val ] : texts[5];
1540	
1541		},
1542	
1543		/**
1544		 * Get the value associated with a strength slug
1545		 *
1546		 * @since 3.0.0
1547		 *
1548		 * @param string strength_slug A strength slug.
1549		 * @return {Integer}
1550		 */
1551		get_strength_value: function( strength_slug ) {
1552	
1553			var values = {
1554				'too-short': -1,
1555				'very-weak': 1,
1556				weak: 2,
1557				medium: 3,
1558				strong: 4,
1559				mismatch: 5,
1560			};
1561	
1562			return ( values[ strength_slug ] ) ? values[ strength_slug ] : values.mismatch;
1563	
1564		},
1565	
1566		/**
1567		 * Setup jQuery references to DOM elements needed to power the password meter.
1568		 *
1569		 * @since 5.0.0
1570		 *
1571		 * @return {Boolean} Returns `true` if a meter element and password field are found, otherwise returns `false`.
1572		 */
1573		setup_references: function() {
1574	
1575			if ( ! this.$meter.length ) {
1576				return false;
1577			}
1578	
1579			this.$form = this.$meter.closest( 'form' );
1580			this.$pass = this.$form.find( 'input#password' );
1581	
1582			if ( this.$pass.length && this.$pass.attr( 'data-match' ) ) {
1583				this.$conf = this.$form.find( '#' + this.$pass.attr( 'data-match' ) );
1584			}
1585	
1586			return ( this.$pass.length > 0 );
1587	
1588		},
1589	
1590		/**
1591		 * Form submission handler for registration and update forms
1592		 *
1593		 * @since 3.0.0
1594		 * @since 5.0.0 Allow the account edit for to bypass strength checking when the password field is disabled (not being submitted).
1595		 *
1596		 * @param obj e Event data.
1597		 * @return void
1598		 */
1599		submit: function( e ) {
1600	
1601			var self = e.data;
1602			e.preventDefault();
1603			self.$pass.trigger( 'keyup' );
1604	
1605			// Meets the status requirements OR we're on the account edit form and the password field is disabled.
1606			if ( self.get_current_strength_status() || ( self.$form.hasClass( 'edit-account' ) && 'disabled' === self.$pass.attr( 'disabled' ) ) ) {
1607				self.$form.off( 'submit', self.submit );
1608				self.$form.trigger( 'submit' );
1609			} else {
1610				$( 'html, body' ).animate( {
1611					scrollTop: self.$meter.offset().top - 100,
1612				}, 200 );
1613				self.$meter.hide();
1614				setTimeout( function() {
1615					self.$meter.fadeIn( 400 );
1616				}, 220 );
1617			}
1618		},
1619	
1620		/**
1621		 * Get the list of blocklist strings
1622		 *
1623		 * @since 3.0.0
1624		 * @deprecated 5.0.0 `LLMS.PasswordStrength.get_blacklist()` is deprecated in favor of `LLMS.PasswordStrength.get_blocklist()`.
1625		 *
1626		 * @return array
1627		 */
1628		get_blacklist: function() {
1629			console.log( 'Method `get_blacklist()` is deprecated in favor of `get_blocklist()`.' );
1630			return this.get_blacklist();
1631		},
1632	
1633	} );
1634	
1635		/**
1636	 * Pricing Table UI
1637	 *
1638	 * @package LifterLMS/Scripts
1639	 *
1640	 * @since  Unknown.
1641	 * @version  Unknown.
1642	 */
1643	
1644	LLMS.Pricing_Tables = {
1645	
1646		/**
1647		 * Init
1648		 */
1649		init: function() {
1650	
1651			var self = this;
1652	
1653			if ( $( 'body' ).hasClass( 'wp-admin' ) ) {
1654				return;
1655			}
1656	
1657			if ( $( '.llms-access-plans' ).length ) {
1658	
1659				LLMS.wait_for_matchHeight( function() {
1660					self.bind();
1661				} );
1662	
1663				this.$locked = $( 'a[href="#llms-plan-locked"]' );
1664	
1665				if ( this.$locked.length ) {
1666	
1667					LLMS.wait_for_popover( function() {
1668						self.bind_locked();
1669					} );
1670	
1671				}
1672	
1673			}
1674	
1675		},
1676	
1677		/**
1678		 * Bind Method
1679		 * Handles dom binding on load
1680		 *
1681		 * @return {[type]} [description]
1682		 */
1683		bind: function() {
1684	
1685			$( '.llms-access-plan-content' ).matchHeight();
1686			$( '.llms-access-plan-pricing.trial' ).matchHeight();
1687	
1688		},
1689	
1690		/**
1691		 * Setup a popover for members-only restricted plans
1692		 *
1693		 * @return void
1694		 * @since    3.0.0
1695		 * @version  3.9.1
1696		 */
1697		bind_locked: function() {
1698	
1699			this.$locked.each( function() {
1700	
1701				$( this ).webuiPopover( {
1702					animation: 'pop',
1703					closeable: true,
1704					content: function( e ) {
1705						var $content = $( '<div class="llms-members-only-restrictions" />' );
1706						$content.append( e.$element.closest( '.llms-access-plan' ).find( '.llms-access-plan-restrictions ul' ).clone() );
1707						return $content;
1708					},
1709					placement: 'top',
1710					style: 'inverse',
1711					title: LLMS.l10n.translate( 'Members Only Pricing' ),
1712					width: '280px',
1713				} );
1714	
1715			} );
1716	
1717		},
1718	
1719	};
1720	
1721		/**
1722	 * Quiz Attempt
1723	 *
1724	 * @package LifterLMS/Scripts
1725	 *
1726	 * @since 7.3.0
1727	 * @version 7.3.0
1728	 */
1729	
1730	LLMS.Quiz_Attempt = {
1731		/**
1732		 * Initialize
1733		 *
1734		 * @return void
1735		 */
1736		init: function() {
1737	
1738			$( '.llms-quiz-attempt-question-header a.toggle-answer' ).on( 'click', function( e ) {
1739	
1740				e.preventDefault();
1741	
1742				var $curr = $( this ).closest( 'header' ).next( '.llms-quiz-attempt-question-main' );
1743	
1744				$( this ).closest( 'li' ).siblings().find( '.llms-quiz-attempt-question-main' ).slideUp( 200 );
1745	
1746				if ( $curr.is( ':visible' ) ) {
1747					$curr.slideUp( 200 );
1748				}  else {
1749					$curr.slideDown( 200 );
1750				}
1751	
1752			} );
1753		}
1754	
1755	}
1756	
1757		/**
1758	 * LifterLMS Reviews JS
1759	 *
1760	 * @package LifterLMS/Scripts
1761	 *
1762	 * @since Unknown
1763	 * @version Unknown
1764	 */
1765	
1766	LLMS.Review = {
1767		/**
1768		 * Init
1769		 * loads class methods
1770		 */
1771		init: function() {
1772			// console.log('Initializing Review ');
1773			this.bind();
1774		},
1775	
1776		/**
1777		 * This function binds actions to the appropriate hooks
1778		 */
1779		bind: function() {
1780			$( '#llms_review_submit_button' ).click(function()
1781				{
1782				if ($( '#review_title' ).val() !== '' && $( '#review_text' ).val() !== '') {
1783					jQuery.ajax({
1784						type : 'post',
1785						dataType : 'json',
1786						url : window.llms.ajaxurl,
1787						data : {
1788							action : 'LLMSSubmitReview',
1789							review_title: $( '#review_title' ).val(),
1790							review_text: $( '#review_text' ).val(),
1791							pageID : $( '#post_ID' ).val(),
1792							llms_review_nonce: $( '#llms_review_nonce' ).val()
1793						},
1794						success: function()
1795						{
1796							console.log( 'Review success' );
1797							$( '#review_box' ).hide( 'swing' );
1798							$( '#thank_you_box' ).show( 'swing' );
1799						},
1800						error: function(jqXHR, textStatus, errorThrown )
1801						{
1802							console.log( jqXHR );
1803							console.log( textStatus );
1804							console.log( errorThrown );
1805						},
1806					});
1807				} else {
1808					if ($( '#review_title' ).val() === '') {
1809						$( '#review_title_error' ).show( 'swing' );
1810					} else {
1811						$( '#review_title_error' ).hide( 'swing' );
1812					}
1813					if ($( '#review_text' ).val() === '') {
1814						$( '#review_text_error' ).show( 'swing' );
1815					} else {
1816						$( '#review_text_error' ).hide( 'swing' );
1817					}
1818				}
1819			});
1820			if ( $( '#_llms_display_reviews' ).attr( 'checked' ) ) {
1821				$( '.llms-num-reviews-top' ).addClass( 'top' );
1822				$( '.llms-num-reviews-bottom' ).show();
1823	
1824			} else {
1825				$( '.llms-num-reviews-bottom' ).hide();
1826			}
1827			$( '#_llms_display_reviews' ).change(function() {
1828				if ( $( '#_llms_display_reviews' ).attr( 'checked' ) ) {
1829					$( '.llms-num-reviews-top' ).addClass( 'top' );
1830					$( '.llms-num-reviews-bottom' ).show();
1831				} else {
1832					$( '.llms-num-reviews-top' ).removeClass( 'top' );
1833					$( '.llms-num-reviews-bottom' ).hide();
1834				}
1835			});
1836	
1837		},
1838	};
1839	
1840		/* global LLMS, $ */
1841	
1842	/*!
1843	 * JavaScript Cookie v2.2.1
1844	 * https://github.com/js-cookie/js-cookie
1845	 *
1846	 * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
1847	 * Released under the MIT license
1848	 */
1849	;(function (factory) {
1850		var registeredInModuleLoader;
1851		if (typeof define === 'function' && define.amd) {
1852			define(factory);
1853			registeredInModuleLoader = true;
1854		}
1855		if (typeof exports === 'object') {
1856			module.exports = factory();
1857			registeredInModuleLoader = true;
1858		}
1859		if (!registeredInModuleLoader) {
1860			var OldCookies = window.Cookies;
1861			var api = window.Cookies = factory();
1862			api.noConflict = function () {
1863				window.Cookies = OldCookies;
1864				return api;
1865			};
1866		}
1867	}(function () {
1868		function extend () {
1869			var i = 0;
1870			var result = {};
1871			for (; i < arguments.length; i++) {
1872				var attributes = arguments[ i ];
1873				for (var key in attributes) {
1874					result[key] = attributes[key];
1875				}
1876			}
1877			return result;
1878		}
1879	
1880		function decode (s) {
1881			return s.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent);
1882		}
1883	
1884		function init (converter) {
1885			function api() {}
1886	
1887			function set (key, value, attributes) {
1888				if (typeof document === 'undefined') {
1889					return;
1890				}
1891	
1892				attributes = extend({
1893					path: '/'
1894				}, api.defaults, attributes);
1895	
1896				if (typeof attributes.expires === 'number') {
1897					attributes.expires = new Date(new Date() * 1 + attributes.expires * 864e+5);
1898				}
1899	
1900				// We're using "expires" because "max-age" is not supported by IE
1901				attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
1902	
1903				try {
1904					var result = JSON.stringify(value);
1905					if (/^[\{\[]/.test(result)) {
1906						value = result;
1907					}
1908				} catch (e) {}
1909	
1910				value = converter.write ?
1911					converter.write(value, key) :
1912					encodeURIComponent(String(value))
1913						.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
1914	
1915				key = encodeURIComponent(String(key))
1916					.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent)
1917					.replace(/[\(\)]/g, escape);
1918	
1919				var stringifiedAttributes = '';
1920				for (var attributeName in attributes) {
1921					if (!attributes[attributeName]) {
1922						continue;
1923					}
1924					stringifiedAttributes += '; ' + attributeName;
1925					if (attributes[attributeName] === true) {
1926						continue;
1927					}
1928	
1929					// Considers RFC 6265 section 5.2:
1930					// ...
1931					// 3.  If the remaining unparsed-attributes contains a %x3B (";")
1932					//     character:
1933					// Consume the characters of the unparsed-attributes up to,
1934					// not including, the first %x3B (";") character.
1935					// ...
1936					stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];
1937				}
1938	
1939				return (document.cookie = key + '=' + value + stringifiedAttributes);
1940			}
1941	
1942			function get (key, json) {
1943				if (typeof document === 'undefined') {
1944					return;
1945				}
1946	
1947				var jar = {};
1948				// To prevent the for loop in the first place assign an empty array
1949				// in case there are no cookies at all.
1950				var cookies = document.cookie ? document.cookie.split('; ') : [];
1951				var i = 0;
1952	
1953				for (; i < cookies.length; i++) {
1954					var parts = cookies[i].split('=');
1955					var cookie = parts.slice(1).join('=');
1956	
1957					if (!json && cookie.charAt(0) === '"') {
1958						cookie = cookie.slice(1, -1);
1959					}
1960	
1961					try {
1962						var name = decode(parts[0]);
1963						cookie = (converter.read || converter)(cookie, name) ||
1964							decode(cookie);
1965	
1966						if (json) {
1967							try {
1968								cookie = JSON.parse(cookie);
1969							} catch (e) {}
1970						}
1971	
1972						jar[name] = cookie;
1973	
1974						if (key === name) {
1975							break;
1976						}
1977					} catch (e) {}
1978				}
1979	
1980				return key ? jar[key] : jar;
1981			}
1982	
1983			api.set = set;
1984			api.get = function (key) {
1985				return get(key, false /* read as raw */);
1986			};
1987			api.getJSON = function (key) {
1988				return get(key, true /* read as json */);
1989			};
1990			api.remove = function (key, attributes) {
1991				set(key, '', extend(attributes, {
1992					expires: -1
1993				}));
1994			};
1995	
1996			api.defaults = {};
1997	
1998			api.withConverter = init;
1999	
2000			return api;
2001		}
2002	
2003		return init(function () {});
2004	}));
2005	
2006	/**
2007	 * Create a no conflict reference to JS Cookies.
2008	 *
2009	 * @type {Object}
2010	 */
2011	LLMS.CookieStore = Cookies.noConflict();
2012	
2013	/**
2014	 * Store information in Local Storage by group.
2015	 *
2016	 * @since 3.36.0
2017	 * @since 3.37.14 Use persistent reference to JS Cookies.
2018	 * @since 4.2.0 Set sameSite to `strict` for cookies.
2019	 *
2020	 * @param string group Storage group id/name.
2021	 */
2022	LLMS.Storage = function( group ) {
2023	
2024		var self = this,
2025			store = LLMS.CookieStore;
2026	
2027		/**
2028		 * Clear all data for the group.
2029		 *
2030		 * @since 3.36.0
2031		 *
2032		 * @return void
2033		 */
2034		this.clearAll = function() {
2035			store.remove( group );
2036		};
2037	
2038		/**
2039		 * Clear a single item from the group by key.
2040		 *
2041		 * @since 3.36.0
2042		 *
2043		 * @return obj
2044		 */
2045		this.clear = function( key ) {
2046			var data = self.getAll();
2047			delete data[ key ];
2048			return store.set( group, data );
2049		};
2050	
2051		/**
2052		 * Retrieve (and parse) all data stored for the group.
2053		 *
2054		 * @since 3.36.0
2055		 *
2056		 * @return obj
2057		 */
2058		this.getAll = function() {
2059			return store.getJSON( group ) || {};
2060		}
2061	
2062		/**
2063		 * Retrieve an item from the group by key.
2064		 *
2065		 * @since 3.36.0
2066		 *
2067		 * @param string key Item key/name.
2068		 * @param mixed default_val Item default value to be returned when item not found in the group.
2069		 * @return mixed
2070		 */
2071		this.get = function( key, default_val ) {
2072			var data = self.getAll();
2073			return data[ key ] ? data[ key ] : default_val;
2074		}
2075	
2076		/**
2077		 * Store an item in the group by key.
2078		 *
2079		 * @since 3.36.0
2080		 * @since 4.2.0 Set sameSite to `strict` for cookies.
2081		 *
2082		 * @param string key Item key name.
2083		 * @param mixed val Item value
2084		 * @return obj
2085		 */
2086		this.set = function( key, val ) {
2087			var data = self.getAll();
2088			data[ key ] = val;
2089			return store.set( group, data, { sameSite: 'strict' } );
2090		};
2091	
2092	}
2093	
2094		/**
2095	 * Student Dashboard related JS
2096	 *
2097	 * @package LifterLMS/Scripts
2098	 *
2099	 * @since 3.7.0
2100	 * @since 3.10.0 Bind events on the orders screen.
2101	 * @since 5.0.0 Removed redundant password toggle logic for edit account screen.
2102	 * @version 5.0.0
2103	 */
2104	LLMS.StudentDashboard = {
2105	
2106		/**
2107		 * Slug for the current screen/endpoint
2108		 *
2109		 * @type  {String}
2110		 */
2111		screen: '',
2112	
2113		/**
2114		 * Init
2115		 *
2116		 * @since 3.7.0
2117		 * @since 3.10.0 Unknown
2118		 * @since 5.0.0 Removed password toggle logic.
2119		 *
2120		 * @return void
2121		 */
2122		init: function() {
2123	
2124			if ( $( '.llms-student-dashboard' ).length ) {
2125				this.bind();
2126				if ( 'orders' === this.get_screen() ) {
2127					this.bind_orders();
2128				}
2129			}
2130	
2131		},
2132	
2133		/**
2134		 * Bind DOM events
2135		 *
2136		 * @since 3.7.0
2137		 * @since 3.7.4 Unknown.
2138		 * @since 5.0.0 Removed password toggle logic.
2139		 *
2140		 * @return   void
2141		 */
2142		bind: function() {
2143	
2144			$( '.llms-donut' ).each( function() {
2145				LLMS.Donut( $( this ) );
2146			} );
2147	
2148		},
2149	
2150		/**
2151		 * Bind events related to the orders screen on the dashboard
2152		 *
2153		 * @since 3.10.0
2154		 *
2155		 * @return void
2156		 */
2157		bind_orders: function() {
2158	
2159			$( '#llms-cancel-subscription-form' ).on( 'submit', this.order_cancel_warning );
2160			$( '#llms_update_payment_method' ).on( 'click', function() {
2161				$( 'input[name="llms_payment_gateway"]:checked' ).trigger( 'change' );
2162				$( this ).closest( 'form' ).find( '.llms-switch-payment-source-main' ).slideToggle( '200' );
2163			} );
2164	
2165		},
2166	
2167		/**
2168		 * Get the current dashboard endpoint/tab slug
2169		 *
2170		 * @since 3.10.0
2171		 *
2172		 * @return void
2173		 */
2174		get_screen: function() {
2175			if ( ! this.screen ) {
2176				this.screen = $( '.llms-student-dashboard' ).attr( 'data-current' );
2177			}
2178			return this.screen;
2179		},
2180	
2181		/**
2182		 * Show a confirmation warning when Cancel Subscription form is submitted
2183		 *
2184		 * @since 3.10.0
2185		 *
2186		 * @param obj e JS event data.
2187		 * @return void
2188		 */
2189		order_cancel_warning: function( e ) {
2190			e.preventDefault();
2191			var msg = LLMS.l10n.translate( 'Are you sure you want to cancel your subscription?' );
2192			if ( window.confirm( LLMS.l10n.translate( msg ) ) ) {
2193				$( this ).off( 'submit', this.order_cancel_warning );
2194				$( this ).submit();
2195			}
2196		},
2197	
2198	};
2199	
2200		/* global LLMS, $ */
2201	
2202	/**
2203	 * User event/interaction tracking.
2204	 *
2205	 * @since 3.36.0
2206	 * @since 3.36.2 Fix JS error when settings aren't loaded.
2207	 * @since 3.37.2 When adding an event to the storae also make sure the nonce is set for server-side verification.
2208	 * @since 3.37.9 Fix IE compatibility issue related to usage of `Object.assign()`.
2209	 * @since 3.37.14 Persist the tracking events via ajax when reaching the cookie size limit.
2210	 * @since 5.0.0 Set `settings` as an empty object when no settings supplied.
2211	 * @since 7.1.0 Only attempt to add a nonce to the datastore when a nonce exists in the settings object.
2212	 */
2213	LLMS.Tracking = function( settings ) {
2214	
2215		settings = settings || {};
2216	
2217		var self = this,
2218			store = new LLMS.Storage( 'llms-tracking' );
2219	
2220		settings = 'string' === typeof settings ? JSON.parse( settings ) : settings;
2221	
2222		/**
2223		 * Initialize / Bind all tracking event listeners.
2224		 *
2225		 * @since 3.36.0
2226		 * @since 5.0.0 Only attempt to add a nonce to the datastore when a nonce exists in the settings object.
2227		 * @since 7.1.0 Do not add a nonce to the datastore by default, will be added/updated
2228		 *              when storing an event to track.
2229		 *
2230		 * @return {void}
2231		 */
2232		function init() {
2233	
2234			self.addEvent( 'page.load' );
2235	
2236			window.addEventListener( 'beforeunload', onBeforeUnload );
2237			window.addEventListener( 'pagehide', onUnload );
2238	
2239			document.addEventListener( 'visibilitychange', onVisibilityChange );
2240	
2241		};
2242	
2243		/**
2244		 * Add an event.
2245		 *
2246		 * @since 3.36.0
2247		 * @since 3.36.2 Fix error when settings aren't loaded.
2248		 * @since 3.37.2 Always make sure the nonce is set for server-side verification.
2249		 * @since 3.37.14 Persist the tracking events via ajax when reaching the cookie size limit.
2250		 * @since 7.1.0 Only attempt to add a nonce to the datastore when a nonce exists in the settings object.
2251		 *
2252		 * @param string|obj event Event Id (type.event) or a full event object from `this.makeEventObj()`.
2253		 * @param int args Optional additional arguments to pass to `this.makeEventObj()`.
2254		 * @return {void}
2255		 */
2256		this.addEvent = function( event, args ) {
2257	
2258			args  = args || {};
2259			if ( 'string' === typeof event ) {
2260				args.event = event;
2261			}
2262	
2263			// If the event isn't registered in the settings don't proceed.
2264			if ( !settings.events || -1 === settings.events.indexOf( args.event ) ) {
2265				return;
2266			}
2267	
2268			// Make sure the nonce is set for server-side verification.
2269			if ( settings.nonce ) {
2270				store.set( 'nonce', settings.nonce );
2271			}
2272	
2273			event = self.makeEventObj( args );
2274	
2275			var all = store.get( 'events', [] );
2276			all.push( event );
2277			store.set( 'events', all );
2278	
2279			// If couldn't store the latest event because of size limits.
2280			if ( all.length > store.get( 'events', [] ).length ) {
2281	
2282				// Copy the cookie in a temporary variable.
2283				var _temp = store.getAll();
2284				// Clear the events from the cookie.
2285				store.clear('events');
2286	
2287				// Add the latest event to the temporary variable.
2288				_temp['events'].push( event );
2289	
2290				// Send the temporary variable as string via ajax.
2291				LLMS.Ajax.call( {
2292					data: {
2293						action: 'persist_tracking_events',
2294						'llms-tracking': JSON.stringify(_temp)
2295					},
2296	
2297					error: function( xhr, status, error ) {
2298	
2299						console.log( xhr, status, error );
2300	
2301					},
2302					success: function( r ) {
2303	
2304						if ( 'error' === r.code ) {
2305							console.log(r.code, r.message);
2306						}
2307	
2308					}
2309	
2310				} );
2311	
2312			}
2313	
2314		}
2315	
2316		/**
2317		 * Retrieve initialization settings.
2318		 *
2319		 * @since 3.36.0
2320		 *
2321		 * @return obj
2322		 */
2323		this.getSettings = function() {
2324			return settings;
2325		}
2326	
2327		/**
2328		 * Create an event object suitable to save as an event.
2329		 *
2330		 * @since 3.36.0
2331		 * @since 3.37.9 Use `$.extend()` in favor of `Object.assign()`.
2332		 *
2333		 * @param obj event {
2334		 *     Event hash
2335		 *
2336		 *     @param {string} event (Required) Event ID, eg: "page.load".
2337		 *     @param {url} url Event URL. (Optional, added automatically) Stored as metadata and used to infer an object_id for post events.
2338		 *     @param {time} float (Optional, added automatically) Timestamp (in milliseconds). Used for the event creation date.
2339		 *     @param {int} obj_id (Optional). The object ID. Inferred automatically via `url` if not provided.
2340		 *     @param {obj} meta (Optional) Hash of metadata to store with the event.
2341		 * }
2342		 * @return obj
2343		 */
2344		this.makeEventObj = function( event ) {
2345			return $.extend( event, {
2346				url: window.location.href,
2347				time: Math.round( new Date().getTime() / 1000 ),
2348			} );
2349		}
2350	
2351	
2352		/**
2353		 * Remove the visibility change event listener on window.beforeunload
2354		 *
2355		 * Prevents actual unloading from recording a blur event from the visibility change listener
2356		 *
2357		 * @param obj e JS event object.
2358		 * @return void
2359		 */
2360		function onBeforeUnload( e ) {
2361			document.removeEventListener( 'visibilitychange', onVisibilityChange );
2362		}
2363	
2364		/**
2365		 * Record a `page.exit` event on window.unload.
2366		 *
2367		 * @since 3.36.0
2368		 *
2369		 * @param obj e JS event object.
2370		 * @return void
2371		 */
2372		function onUnload( e ) {
2373			self.addEvent( 'page.exit' );
2374		}
2375	
2376		/**
2377		 * Record `page.blur` and `page.focus` events via document.visilibitychange events.
2378		 *
2379		 * @since 3.36.0
2380		 *
2381		 * @param obj e JS event object.
2382		 * @return void
2383		 */
2384		function onVisibilityChange( e ) {
2385	
2386			var event = document.hidden ? 'page.blur' : 'page.focus';
2387			self.addEvent( event );
2388	
2389		}
2390	
2391		// Initialize on the frontend only.
2392		if ( ! $( 'body' ).hasClass( 'wp-admin' ) ) {
2393			init();
2394		}
2395	
2396	};
2397	
2398	llms.tracking = new LLMS.Tracking( llms.tracking );
2399	
2400		/**
2401	 * Rest Methods
2402	 * Manages URL and Rest object parsing
2403	 *
2404	 * @package LifterLMS/Scripts
2405	 *
2406	 * @since Unknown
2407	 * @version  Unknown
2408	 */
2409	
2410	LLMS.Rest = {
2411	
2412		/**
2413		 * Init
2414		 * loads class methods
2415		 */
2416		init: function() {
2417			this.bind();
2418		},
2419	
2420		/**
2421		 * Bind Method
2422		 * Handles dom binding on load
2423		 *
2424		 * @return {[type]} [description]
2425		 */
2426		bind: function() {
2427		},
2428	
2429		/**
2430		 * Searches for string matches in url path
2431		 *
2432		 * @param  {Array}
2432  strings [Array of strings to search for matches]
2433		 * @return {Boolean}         [Was a match found?]
2434		 */
2435		is_path: function( strings ) {
2436	
2437			var path_exists = false,
2438				url         = window.location.href;
2439	
2440			for ( var i = 0; i < strings.length; i++ ) {
2441	
2442				if ( url.search( strings[i] ) > 0 && ! path_exists ) {
2443	
2444					path_exists = true;
2445				}
2446			}
2447	
2448			return path_exists;
2449		},
2450	
2451		/**
2452		 * Retrieves query variables
2453		 *
2454		 * @return {[Array]} [array object of query variable key=>value pairs]
2455		 */
2456		get_query_vars: function() {
2457	
2458			var vars   = [], hash,
2459				hashes = window.location.href.slice( window.location.href.indexOf( '?' ) + 1 ).split( '&' );
2460	
2461			for (var i = 0; i < hashes.length; i++) {
2462				hash = hashes[i].split( '=' );
2463				vars.push( hash[0] );
2464				vars[hash[0]] = hash[1];
2465			}
2466	
2467			return vars;
2468		}
2469	
2470	};
2471	
2472
2473	(()=>{"use strict";var t={d:(n,e)=>{for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},o:(t,n)=>Object.prototype.hasOwnProperty.call(t,n),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},n={};t.r(n),t.d(n,{get:()=>d,start:()=>p,stop:()=>c});const e="llms-spinning",r="default",o=window.wp.i18n;function i(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:r;const i=document.createElement("div"),l=(0,o.__)("Loading…","lifterlms");return i.innerHTML=`<i class="llms-spinner ${n}" role="alert" aria-live="assertive"><span class="screen-reader-text">${l}</span></i>`,i.classList.add(e),t.appendChild(i),i}function l(t){if((t="string"==typeof t?document.querySelectorAll(t):t)instanceof NodeList)return Array.from(t);const n=[];return t instanceof Element?n.push(t):"undefined"!=typeof jQuery&&t instanceof jQuery&&t.toArray().forEach((t=>n.push(t))),n}function s(t){const n=t.querySelectorAll(".llms-spinning");return n.length?Array.from(n).find((n=>t===n.parentNode)):null}function a(){const t="llms-spinner-styles";if(!document.getElementById(t)){const n=document.createElement("style");n.textContent="\n\t.llms-spinning {\n\t\tbackground: rgba( 250, 250, 250, 0.7 );\n\t\tbottom: 0;\n\t\tdisplay: none;\n\t\tleft: 0;\n\t\tposition: absolute;\n\t\tright: 0;\n\t\ttop: 0;\n\t\tz-index: 2;\n\t}\n\n\t.llms-spinner {\n\t\tanimation: llms-spinning 1.5s linear infinite;\n\t\tbox-sizing: border-box;\n\t\tborder: 4px solid #313131;\n\t\tborder-radius: 50%;\n\t\theight: 40px;\n\t\tleft: 50%;\n\t\tmargin-left: -20px;\n\t\tmargin-top: -20px;\n\t\tposition: absolute;\n\t\ttop: 50%;\n\t\twidth: 40px;\n\n\t}\n\n\t.llms-spinner.small {\n\t\tborder-width: 2px;\n\t\theight: 20px;\n\t\tmargin-left: -10px;\n\t\tmargin-top: -10px;\n\t\twidth: 20px;\n\t}\n\n\t@keyframes llms-spinning {\n\t\t0% {\n\t\t\ttransform: rotate( 0deg )\n\t\t}\n\t\t50% {\n\t\t\tborder-radius: 5%;\n\t\t}\n\t\t100% {\n\t\t\ttransform: rotate( 220deg) \n\t\t}\n\t}\n".replace(/\n/g,"").replace(/\t/g," ").replace(/\s\s+/g," "),n.id=t,document.head.appendChild(n)}}function d(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:r,e=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];a();const o=l(t);if(!o.length)return null;const d=o[0],p=s(d)||i(d,n);return e&&"undefined"!=typeof jQuery?jQuery(p):p}function p(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:r;l(t).forEach((t=>{const e=d(t,n,!1);e&&(e.style.display="block")}))}function c(t){l(t).forEach((t=>{const n=d(t,r,!1);n&&(n.style.display="none")}))}window.LLMS=window.LLMS||{},window.LLMS.Spinner=n})();
2474
2475	/**
2476	 * Initializes all classes within the LLMS Namespace
2477	 *
2478	 * @since Unknown
2479	 *
2480	 * @return {void}
2481	 */
2482	LLMS.init = function() {
2483
2484		for (var func in LLMS) {
2485
2486			if ( typeof LLMS[func] === 'object' && LLMS[func] !== null ) {
2487
2488				if ( LLMS[func].init !== undefined ) {
2489
2490					if ( typeof LLMS[func].init === 'function') {
2491						LLMS[func].init();
2492					}
2493
2494				}
2495
2496			}
2497
2498		}
2499
2500	};
2501
2502	/**
2503	 * Determine if the current device is touch-enabled
2504	 *
2505	 * @since 3.24.3
2506	 *
2507	 * @see {@link https://stackoverflow.com/a/4819886/400568}
2508	 *
2509	 * @return {Boolean} Whether or not the device is touch-enabled.
2510	 */
2511	LLMS.is_touch_device = function() {
2512
2513		var prefixes = ' -webkit- -moz- -o- -ms- '.split( ' ' );
2514		var mq       = function( query ) {
2515			return window.matchMedia( query ).matches;
2516		}
2517
2518		if ( ( 'ontouchstart' in window ) || window.DocumentTouch && document instanceof DocumentTouch ) {
2519			return true;
2520		}
2521
2522		/**
2523		 * Include the 'heartz' as a way to have a non matching MQ to help terminate the join.
2524		 *
2525		 * @see {@link https://git.io/vznFH}
2526		 */
2527		var query = ['(', prefixes.join( 'touch-enabled),(' ), 'heartz', ')'].join( '' );
2528		return mq( query );
2529
2530	};
2531
2532	/**
2533	 * Wait for matchHeight to load
2534	 *
2535	 * @since 3.0.0
2536	 * @since 3.16.6 Unknown.
2537	 * @since 5.3.3 Pass a dependency name to `wait_for()`.
2538	 *
2539	 * @param {Function} cb Callback function to run when matchheight is ready.
2540	 * @return {void}
2541	 */
2542	LLMS.wait_for_matchHeight = function( cb ) {
2543		this.wait_for( function() {
2544			return ( undefined !== $.fn.matchHeight );
2545		}, cb, 'matchHeight' );
2546	}
2547
2548	/**
2549	 * Wait for webuiPopover to load
2550	 *
2551	 * @since 3.9.1
2552	 * @since 3.16.6 Unknown.
2553	 *
2554	 * @param {Function} cb Callback function to run when matchheight is ready.
2555	 * @return {void}
2556	 */
2557	LLMS.wait_for_popover = function( cb ) {
2558		this.wait_for( function() {
2559			return ( undefined !== $.fn.webuiPopover );
2560		}, cb, 'webuiPopover' );
2561	}
2562
2563	/**
2564	 * Wait for a dependency to load and then run a callback once it has
2565	 *
2566	 * Temporary fix for a less-than-optimal assets loading function on the PHP side of things.
2567	 *
2568	 * @since 3.9.1
2569	 * @since 5.3.3 Added optional `name` parameter.
2570	 *
2571	 * @param {Function} test A function that returns a truthy if the dependency is loaded.
2572	 * @param {Function} cb   A callback function executed once the dependency is loaded.
2573	 * @param {string}   name The dependency name.
2574	 * @return {void}
2575	 */
2576	LLMS.wait_for = function( test, cb, name ) {
2577
2578		var counter = 0,
2579			interval;
2580
2581		name = name ? name : 'unnamed';
2582
2583		interval = setInterval( function() {
2584
2585			// If we get to 30 seconds log an error message.
2586			if ( counter >= 300 ) {
2587
2588				console.log( 'Unable to load dependency: ' + name );
2589
2590				// If we can't access yet, increment and wait...
2591			} else {
2592
2593				// Bind the events, we're good!
2594				if ( test() ) {
2595					cb();
2596				} else {
2597					// console.log( 'Waiting for dependency: ' + name );
2598					counter++;
2599					return;
2600				}
2601
2602			}
2603
2604			clearInterval( interval );
2605
2606		}, 100 );
2607
2608	};
2609
2610	LLMS.init( $ );
2611
2612} )( jQuery );
2613
2614//# sourceMappingURL=../maps/js/llms.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.