1/** 2 * LearnPress frontend course app. 3 * 4 * @version 3.2.0 5 * @author ThimPress 6 * @package LearnPress/JS/Course 7 */ 8( function( $ ) { 9 'use strict'; 10 function LP_Storage( key ) { 11 const storage = window.localStorage; 12 this.key = key; 13 this.get = function( id ) { 14 const val = storage.getItem( this.key ) || '', 15 sections = val.split( ',' ); 16 if ( id ) { 17 id = id + ''; 18 const pos = sections.indexOf( id ); 19 if ( pos >= 0 ) { 20 return sections[ pos ]; 21 } 22 } 23 return sections; 24 }; 25 this.set = function( sections ) { 26 if ( typeof sections !== 'string' ) { 27 sections = sections.join( ',' ); 28 } 29 storage.setItem( this.key, sections ); 30 return sections.split( ',' ); 31 }; 32 this.hasSection = function( id ) { 33 id = id + ''; 34 const sections = this.get(), 35 at = sections.indexOf( id ); 36 37 return at >= 0 ? at : false; 38 }; 39 this.add = function( id ) { 40 id = id + ''; 41 const sections = this.get(); 42 if ( this.hasSection( id ) ) { 43 return; 44 } 45 sections.push( id ); 46 this.set( sections ); 47 return sections; 48 }; 49 this.remove = function( id ) { 50 id = id + ''; 51 const at = this.hasSection( id ); 52 if ( at !== false ) { 53 const sections = this.get(); 54 sections.splice( at, 1 ); 55 this.set( sections ); 56 return sections; 57 } 58 return false; 59 }; 60 } 61 62 /** 63 * LP_Course 64 * 65 * @param settings 66 * @class 67 */ 68 function LP_Course( settings ) { 69 const elLPOverlay = $( '.lp-overlay' ); 70 const elFormCompleteLesson = $( 'form[name=learn-press-form-complete-lesson]' ); 71 let sectionStorage = new LP_Storage( 'sections' ), 72 $body = $( 'body' ), 73 $content = $( '.content-item-scrollable' ), 74 $curriculum = $( '#learn-press-course-curriculum' ), 75 $contentItem = $( '#learn-press-content-item' ), 76 $curriculumScrollable = $curriculum.find( '.curriculum-scrollable' ), 77 $header = $( '#course-item-content-header' ), 78 $footer = $( '#course-item-content-footer' ), 79 $courseItems = $curriculum.find( '.course-item' ), 80 isShowingHeader = true, 81 fullScreen, 82 contentTop = 0, 83 headerTimer, 84 inPopup = false; 85 86 /** 87 * Toggle answer option check/uncheck 88 * 89 * @param event 90 */ 91 function toggleAnswerOptions( event ) { 92 let $el = $( event.target ), 93 $chk; 94 if ( $el.is( 'input.option-check' ) ) { 95 return; 96 } 97 98 $chk = $el.closest( '.answer-option' ).find( 'input.option-check' ); 99 100 if ( ! $chk.length ) { 101 return; 102 } 103 104 if ( $chk.is( ':disabled' ) ) { 105 return; 106 } 107 if ( $chk.is( ':checkbox' ) ) { 108 $chk[ 0 ].checked = ! $chk[ 0 ].checked; 109 } else { 110 $chk[ 0 ].checked = true; 111 } 112 } 113 114 /** 115 * Show/Hide section content 116 */ 117 function toggleSection() { 118 const id = $( this ).closest( '.section' ).data( 'section-id' ); 119 $( this ).siblings( '.section-content' ).slideToggle( function() { 120 if ( $( this ).is( ':visible' ) ) { 121 sectionStorage.remove( id ); 122 } else { 123 sectionStorage.add( id ); 124 } 125 } ); 126 } 127 128 /** 129 * Init sections 130 */ 131 function initSections() { 132 let $activeSection = $( '.course-item.current' ).closest( '.section' ), 133 sections = $( '.curriculum-sections' ).find( '.section' ), 134 sectionId = $activeSection.data( 'section-id' ), 135 hiddenSections = []; 136 137 if ( $activeSection ) { 138 hiddenSections = sectionStorage.remove( sectionId ); 139 } else { 140 hiddenSections = sectionStorage.get(); 141 } 142 143 for ( let i = 0; i < hiddenSections.length; i++ ) { 144 sections.filter( '[data-section-id="' + hiddenSections[ i ] + '"]' ). 145 find( '.section-content' ). 146 hide(); 147 } 148 } 149 150 /** 151 * Prepare form before submitting 152 * 153 * @param form 154 */ 155 function prepareForm( form ) { 156 const $answerOptions = $( '.answer-options' ), 157 $form = $( form ), 158 data = $answerOptions.serializeJSON(), 159 $hidden = $( '<input type="hidden" name="question-data" />' ). 160 val( JSON.stringify( data ) ); 161 162 if ( ( $form.attr( 'method' ) + '' ).toLowerCase() !== 'post' ) { 163 return; 164 } 165
166 $form.find( 'input[name="question-data"]' ).remove(); 167 return $form.append( $hidden ). 168 append( $( '<div />' ).append( $answerOptions.clone() ).hide() ); 169 } 170 171 /** 172 * Tab course event 173 * 174 * @param e 175 * @param tab 176 */ 177 function onTabCourseClick( e, tab ) { 178 if ( $( document.body ).hasClass( 'course-item-popup' ) ) { 179 return; 180 } 181 182 const $tab = $( tab ), 183 $parent = $tab.closest( '.course-nav' ); 184 185 if ( $parent.siblings().length === 0 ) { 186 return; 187 } 188 LP.setUrl( $tab.attr( 'href' ) ); 189 } 190 191 /** 192 * Event on press any key into search 193 * 194 * @param e 195 * @return {boolean} 196 */ 197 function onSearchInputKeypress( e ) { 198 if ( e.type === 'keypress' && e.keyCode === 13 ) { 199 return false; 200 } 201 202 const s = this.value, 203 r = new RegExp( s, 'ig' ); 204 $courseItems.map( function() { 205 const $item = $( this ), 206 itemName = $item.find( '.item-name' ).text(); 207 if ( itemName.match( r ) || ! s.length ) { 208 $item.show(); 209 } else { 210 $item.hide(); 211 } 212 } ); 213 214 $( '.section' ).show().each( function() { 215 if ( s.length ) { 216 if ( ! $( this ). 217 find( '.section-content' ). 218 children( ':visible' ).length ) { 219 $( this ).hide(); 220 } else { 221 $( this ).show(); 222 } 223 } else { 224 $( this ).show(); 225 } 226 } ); 227 $( this ). 228 closest( '.course-item-search' ). 229 toggleClass( 'has-keyword', !! this.value.length ); 230 } 231 232 function onClearSearchInputClick( e ) { 233 const $form = $( this ).closest( '.course-item-search' );
234 $form.find( 'input' ).val( '' ).trigger( 'keyup' ); 235 } 236 237 function onClickQM() { 238 $( '#qm' ).css( { 'z-index': 999999999, position: 'relative' } ); 239 $( 'html, body' ).css( 'overflow', 'auto' ); 240 } 241 242 function getCurriculumWidth() { 243 return $curriculum.outerWidth(); 244 } 245 246 function maybeShowCurriculum( e ) { 247 return; 248 const offset = $( this ).offset(), 249 offsetX = e.pageX - offset.left, 250 curriculumWidth = getCurriculumWidth(); 251 252 if ( ! fullScreen || ( offsetX > 50 ) ) { 253 return; 254 } 255 256 timeoutToClose(); 257 258 if ( ! isShowingHeader ) { 259 $curriculum.stop().animate( { 260 left: 0, 261 } ); 262 263 $contentItem.stop().animate( { 264 left: curriculumWidth, 265 } ); 266 267 $footer.stop().animate( { 268 left: curriculumWidth, 269 }, function() { 270 $( document, window ).trigger( 'learn-press/toggle-content-item' ); 271 } ); 272 273 $header.find( '.course-item-search' ).show(); 274 toggleEventShowCurriculum( true ); 275 isShowingHeader = true; 276 } 277 } 278 279 function toggleEventShowCurriculum( b ) { 280 $( document )[ b ? 'off' : 'on' ]( 'mousemove.maybe-show-curriculum', 281 'body', maybeShowCurriculum ); 282 } 283 284 function timeoutToClose() { 285 headerTimer && clearTimeout( headerTimer ); 286 headerTimer = setTimeout( function() { 287 const curriculumWidth = getCurriculumWidth(); 288 289 if ( ! fullScreen ) { 290 return; 291 } 292 293 $curriculum.stop().animate( { 294 left: -curriculumWidth, 295 } ); 296 297 $contentItem.stop().animate( { 298 left: 0, 299 } ); 300 301 $footer.stop().animate( { 302 left: 0, 303 }, function() { 304 $( document, window ).trigger( 'learn-press/toggle-content-item' ); 305 } ); 306 307 $header.find( '.course-item-search' ).hide(); 308 309 isShowingHeader = false;
310 toggleEventShowCurriculum(); 311 }, 3000 ); 312 } 313 314 function toggleContentItem( e ) { 315 e.preventDefault(); 316 const curriculumWidth = getCurriculumWidth(); 317 let sidebarPosition = 'left'; 318 319 if ( 'rtl' === $( 'html' ).attr( 'dir' ) ) { 320 sidebarPosition = 'right'; 321 } 322 323 fullScreen = $body.toggleClass( 'full-screen-content-item' ). 324 hasClass( 'full-screen-content-item' ); 325 326 if ( 'right' === sidebarPosition ) { 327 curriculumRight(); 328 } else { 329 curriculumLeft(); 330 } 331 332 function curriculumRight() { 333 $curriculum.stop().animate( { 334 right: fullScreen ? -curriculumWidth : 0, 335 } ); 336 337 $contentItem.stop().animate( { 338 right: fullScreen ? 0 : curriculumWidth, 339 } ); 340 341 $footer.stop().animate( { 342 right: fullScreen ? 0 : curriculumWidth, 343 }, function() { 344 $( document, window ).trigger( 'learn-press/toggle-content-item' ); 345 } ); 346 } 347 348 function curriculumLeft() { 349 $curriculum.stop().animate( { 350 left: fullScreen ? -curriculumWidth : 0, 351 } ); 352 353 $contentItem.stop().animate( { 354 left: fullScreen ? 0 : curriculumWidth, 355 } ); 356 357 $footer.stop().animate( { 358 left: fullScreen ? 0 : curriculumWidth, 359 }, function() { 360 $( document, window ).trigger( 'learn-press/toggle-content-item' ); 361 } ); 362 } 363 364 isShowingHeader = ! fullScreen; 365 window.localStorage && window.localStorage.setItem( 'lp-full-screen', 366 fullScreen ? 'yes' : 'no' ); 367 368 fullScreen && toggleEventShowCurriculum(); 369 $header.find( '.course-title' ). 370 stop(). 371 animate( { marginLeft: fullScreen ? -curriculumWidth : 0 } ); 372 $header.find( '.course-item-search' ). 373 stop(). 374 animate( { opacity: fullScreen ? 0 : 1 } ). 375 css( { visibility: fullScreen ? 'hidden' : 'visible' } ); 376 } 377 378 function initEvents() { 379 // Live events 380 $( document ). 381 on( 'learn-press/nav-tabs/clicked', onTabCourseClick ). 382 on( 'keyup keypress', '.course-item-search input', 383 onSearchInputKeypress ). 384 on( 'click', '.course-item-search button', onClearSearchInputClick ). 385 on( 'click', '#wp-admin-bar-query-monitor', onClickQM ). 386 on( 'click', '.answer-options .answer-option', toggleAnswerOptions ). 387 on( 'click', '.section-header', toggleSection ). 388 on( 'submit', 'form.lp-form', function() { 389 prepareForm( this ); 390 } ). 391 on( 'click', '.toggle-content-item', toggleContentItem ); 392 393 $curriculum.hover( function() { 394 headerTimer && clearTimeout( headerTimer ); 395 }, function() { 396 if ( fullScreen ) { 397 timeoutToClose(); 398 } 399 } ); 400 } 401 402 function fitVideo() { 403 const $wrapContent = $( '.content-item-summary.content-item-video' ); 404 405 if ( ! $wrapContent.length ) { 406 return; 407 } 408 409 let $entryVideo = $wrapContent.find( '.entry-video' ), 410 $frame = $entryVideo.find( 'iframe' ), 411 width = $frame.attr( 'width' ), 412 height = $frame.attr( 'height' ), 413 ratio = 1, 414 contentHeight, 415 timer; 416 417 function resizeVideo() { 418 const frameWidth = $frame.width(); 419 contentHeight = frameWidth * ratio; 420 $frame.css( { 421 height: contentHeight, 422 marginLeft: ( $entryVideo.width() - frameWidth ) / 2, 423 } ); 424 425 $wrapContent.css( { 426 paddingTop: contentHeight, 427 } ); 428 } 429 430 if ( ! $entryVideo.length ) { 431 return false; 432 } 433 434 if ( width && height ) { 435 if ( width.indexOf( '%' ) === -1 && height.indexOf( '%' ) === -1 ) { 436 ratio = height / width; 437 } 438 } 439 440 $( window ). 441 on( 'resize.fit-content-video learn-press/toggle-content-item', 442 function() { 443 timer && clearTimeout( timer ); 444 timer = setTimeout( resizeVideo, 250 ); 445 } ). 446 trigger( 'resize.fit-content-video' ); 447 448 $( '.content-item-scrollable' ).scroll( function() { 449 $( this ).find( '.entry-video' ).css( 'padding-top', this.scrollTop ); 450 } ); 451 } 452 453 /** 454 * Init 455 */ 456 function init() { 457 inPopup = $body.hasClass( 'course-item-popup' ); 458 initSections(); 459 initEvents(); 460 461 if ( ! inPopup ) { 462 return; 463 } 464 465 // $contentItem.appendTo( $body ); 466 // $curriculum.appendTo( $body ); 467 468 if ( $( '#wpadminbar' ).length ) { 469 $body.addClass( 'wpadminbar' ); 470 contentTop = 32; 471 } 472 473 fitVideo(); 474 475 fullScreen = window.localStorage && 'yes' === 476 window.localStorage.getItem( 'lp-full-screen' ); 477 if ( $( window ).width() <= 768 ) { 478 fullScreen = true; 479 } 480 if ( fullScreen ) { 481 const curriculumWidth = getCurriculumWidth(); 482 $body.addClass( 'full-screen-content-item' ); 483 $contentItem.css( 'left', 0 ); 484 $curriculum.css( 'left', -curriculumWidth ); 485 $footer.css( 'left', 0 ); 486 isShowingHeader = ! fullScreen; 487 $header.find( '.course-title' ). 488 css( { marginLeft: fullScreen ? -curriculumWidth : 0 } ); 489 $header.find( '.course-item-search' ). 490 css( { opacity: fullScreen ? 0 : 1 } ); 491 toggleEventShowCurriculum(); 492 } 493 494 setTimeout( function() { 495 const $cs = $body.find( '.curriculum-sections' ).parent(); 496 $cs.scrollTo( $cs.find( '.course-item.current' ), 100 ); 497 498 if ( window.location.hash ) { 499 $( '.content-item-scrollable:last' ). 500 scrollTo( $( window.location.hash ) ); 501 } 502 }, 300 ); 503 504 $body.css( 'opacity', 1 ); 505 } 506 507 // new LP.Alerts(); 508 509 /** 510 * Event complete items of course 511 */ 512 this.completeItemsCourse = function() { 513 const lp_course = this; 514 $( document ).on( 'click', '.lp-btn-complete-item', function( e ) { 515 e.preventDefault(); 516 const elFormSubmit = $( this ).closest( 'form' ); 517 518 if ( 'yes' === lpGlobalSettings.show_popup_confirm_finish ) { 519 lp_course.lpModalOverlay.setElCalledModal( elFormSubmit ); 520 lp_course.lpModalOverlay.callBackYes = function() { 521 elFormSubmit.submit(); 522 }; 523 elLPOverlay.show(); 524 } else { 525 elFormSubmit.submit(); 526 } 527 } ); 528 }; 529 530 this.lpModalOverlay = { 531 elMessage: null, 532 elTitle: null, 533 callBackYes: null, 534 elCalledModal: null, 535 init() { 536 const lpModalOverlay = this; 537 this.elMessage = elLPOverlay.find( '.message' ); 538 this.elTitle = elLPOverlay.find( '.modal-title' ); 539 540 $( document ).on( 'click', '.close, .btn-no', function() { 541 elLPOverlay.hide(); 542 } ); 543
544 $( document ).on( 'click', '.btn-yes', function() { 545 lpModalOverlay.callBackYes(); 546 } ); 547 }, 548 setElCalledModal( elCalledModal ) { 549 this.elCalledModal = elCalledModal; 550 551 this.setContentModal(); 552 }, 553 setContentModal() { 554 this.elTitle.text( this.elCalledModal.data( 'title' ) ); 555 this.elMessage.text( this.elCalledModal.data( 'confirm' ) ); 556 }, 557 }; 558 559 this.finishCourse = function() { 560 const lp_course = this; 561 const elFormFinishCourse = $( '.form-button-finish-course' ); 562 563 if ( ! elFormFinishCourse ) { 564 return; 565 } 566 567 const elBtnFinishCourse = elFormFinishCourse.find( '.lp-btn-finish-course' ); 568 569 elBtnFinishCourse.on( 'click', function( e ) { 570 e.preventDefault(); 571 572 if ( 'yes' === lpGlobalSettings.show_popup_confirm_finish ) { 573 lp_course.lpModalOverlay.setElCalledModal( elFormFinishCourse ); 574 lp_course.lpModalOverlay.callBackYes = function() { 575 elFormFinishCourse.submit(); 576 }; 577 elLPOverlay.show(); 578 } else { 579 elFormFinishCourse.submit(); 580 } 581 } ); 582 }; 583 584 this.scrollToItemViewing = function() { 585 const elItemViewing = $( '.viewing-course-item' ); 586 587 if ( elItemViewing.length ) { 588 const heightCourseItemContentHeader = $( '#course-item-content-header' ).outerHeight(); 589 const heightSectionTitle = $( '.section-title' ).outerHeight(); 590 const heightSectionHeader = $( '.section-header' ).outerHeight(); 591 const regex = new RegExp( '^viewing-course-item-([0-9].*)' ); 592 const classList = elItemViewing.attr( 'class' ); 593 const classArr = classList.split( /\s+/ ); 594 let idItem = 0; 595 596 $.each( classArr, function( i, className ) { 597 const compare = regex.exec( className ); 598 599 if ( compare ) { 600 idItem = compare[ 1 ]; 601 return false; 602 } 603 } ); 604 605 if ( 0 === idItem ) { 606 return; 607 } 608 609 const elItemCurrent = $( '.course-item-' + idItem ); 610 const offSetTop = elItemCurrent.offset().top; 611 let numberOffSetTop = offSetTop - heightCourseItemContentHeader; 612 613 if ( undefined === heightSectionTitle ) { 614 numberOffSetTop = numberOffSetTop - heightSectionHeader + 20; 615 } else { 616 numberOffSetTop = numberOffSetTop - heightSectionTitle; 617 } 618 619 $( '#learn-press-course-curriculum' ).animate( { 620 scrollTop: numberOffSetTop + 7, 621 }, 800 ); 622 } 623 }; 624 625 init(); 626 } 627 628 $( function() { 629 const lp_course = new LP_Course( {} ); 630 631 lp_course.lpModalOverlay.init(); 632 // lp_course.completeLesson(); 633 lp_course.completeItemsCourse(); 634 lp_course.finishCourse(); 635 lp_course.scrollToItemViewing(); 636 637 // $( '#learn-press-course-curriculum' ).animate( { 638 // scrollTop: 300 639 // }, 800); 640 641 $( this ).on( 'submit', 'form[name="course-external-link"]', function() { 642 const redirect = $( this ).attr( 'action' ); 643 if ( redirect ) { 644 window.location.href = redirect; 645 return false; 646 } 647 } ); 648 649 // Reload course page when course duration expired 650 const course_item_html = $( '.course-item-is-blocked' ), 651 course_item_value = course_item_html.val(); 652 if ( course_item_html.length && course_item_value < 2147483647 ) { 653 setTimeout( function() { 654 window.location.reload( true ); 655 }, course_item_value ); 656 } 657 } ); 658}( jQuery ) );
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.