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> </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.