1( function( $, publicConfig ) { 2 'use strict'; 3 4 let JetReviews = { 5 6 eventBus: new Vue(), 7 8 initedInstance: [], 9 10 captchaToken: false, 11 12 init: function() { 13 JetReviews.defineVueComponents(); 14 15 this.initInstances(); 16 17 window.addEventListener( 'jetReviews/editor/templateRenderer/renderSuccess', ( event ) => { 18 this.initBlockInstances(); 19 }, false ); 20 }, 21 22 initInstances: function() { 23 let instancesList = $( '.jet-reviews-advanced' ); 24 25 if ( ! instancesList[0] ) { 26 return false; 27 } 28 29 instancesList.each( function() { 30 let $target = $( this ), 31 instanceId = $target.attr( 'id' ), 32 uniqid = $target.data( 'uniqid' ) || false, 33 options = window['jetReviewsWidget' + uniqid] || false; 34 35 if ( ! options ) { 36 let optionsScript = document.getElementById( 'jetReviewsWidgetOptions' + uniqid ); 37 38 if ( optionsScript ) { 39 eval( optionsScript.innerHTML ); 40 options = window['jetReviewsWidget' + uniqid] || false; 41 } 42 } 43 44 if ( ! $target[0] || ! uniqid || ! options ) { 45 return; 46 } 47 48 window.JetReviews.createJetReviewAdvancedInstance( instanceId, options ); 49 } ); 50 }, 51 52 initBlockInstances: function() { 53 const targetNode = document.querySelector( '.jet-reviews-block-holder' ); 54 55 if ( ! targetNode ) return; 56 57 const observer = new MutationObserver( function ( mutationsList, observer ) { 58 59 for ( const mutation of mutationsList ) { 60 61 if ( mutation.type === 'childList' ) { 62 const event = new Event( 'jetReviews/editor/block/renderSuccess' ); 63 64 document.dispatchEvent( event ); 65 observer.disconnect(); 66 } 67 } 68 } ); 69 70 observer.observe( targetNode, { childList: true, subtree: true } ); 71 72 document.addEventListener( 'jetReviews/editor/block/renderSuccess', ( event ) => { 73 this.initInstances(); 74 } ); 75 }, 76 77 defineVueComponents: function() { 78 79 Vue.component( 'jet-reviews-widget-pagination', { 80 template: '#jet-reviews-widget-pagination-template', 81 props: { 82 current: { 83 type: Number, 84 default: 1 85 }, 86 total: { 87 type: Number, 88 default: 0 89 }, 90 pageSize: { 91 type: Number, 92 default: 10 93 }, 94 prevIcon: { 95 type: String, 96 default: '<svg width="7" height="12" viewBox="0 0 7 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.67089 0L-5.96046e-08 6L5.67089 12L7 10.5938L2.65823 6L7 1.40625L5.67089 0Z"/></svg>' 97 }, 98 nextIcon: { 99 type: String, 100 default: '<svg width="7" height="12" viewBox="0 0 7 12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.32911 0L7 6L1.32911 12L0 10.5938L4.34177 6L0 1.40625L1.32911 0Z"/></svg>' 101 }, 102 customCss: { 103 type: String, 104 default: '' 105 }, 106 }, 107 data() { 108 return { 109 baseClass: 'jet-reviews-widget-pagination', 110 currentPage: this.current, 111 currentPageSize: this.pageSize 112 }; 113 }, 114 115 watch: { 116 total ( val ) { 117 let maxPage = Math.ceil( val / this.currentPageSize ); 118 119 if ( maxPage < this.currentPage ) { 120 this.currentPage = ( maxPage === 0 ? 1 : maxPage ); 121 } 122 }, 123 current ( val ) { 124 this.currentPage = val; 125 }, 126 pageSize ( val ) { 127 this.currentPageSize = val; 128 } 129 }, 130 131 computed: { 132 classesList() { 133 134 let classesList = [ 135 this.baseClass, 136 ]; 137 138 if ( this.customCss ) { 139 classesList.push( this.customCss ); 140 } 141 142 return classesList; 143 }, 144 145 prevClasses () { 146 return [ 147 `${this.baseClass}__item`, 148 `${this.baseClass}__item--prev`, 149 { 150 [`${this.baseClass}__item--disabled`]: this.currentPage === 1 || false 151 } 152 ]; 153 }, 154 155 nextClasses () { 156 return [ 157 `${this.baseClass}__item`, 158 `${this.baseClass}__item--next`, 159 { 160 [`${this.baseClass}__item--disabled`]: this.currentPage === this.allPages || false 161 } 162 ]; 163 }, 164 165 firstPageClasses () { 166 return [ 167 `${this.baseClass}__item`, 168 { 169 [`${this.baseClass}__item--active`]: this.currentPage === 1 170 } 171 ]; 172 }, 173 174 lastPageClasses () { 175 return [ 176 `${this.baseClass}__item`, 177 { 178 [`${this.baseClass}__item--active`]: this.currentPage === this.allPages 179 } 180 ]; 181 }, 182 183 allPages () {
184 const allPage = Math.ceil( this.total / this.currentPageSize ); 185 186 return ( allPage === 0 ) ? 1 : allPage; 187 }, 188 }, 189 methods: { 190 changePage ( page ) { 191 192 if ( this.currentPage !== page ) { 193 194 this.currentPage = page; 195 this.$emit( 'update:current', page ); 196 this.$emit( 'on-change', page ); 197 } 198 }, 199 200 prev () { 201 const current = this.currentPage; 202 203 if ( current <= 1 ) { 204 return false; 205 } 206 207 this.changePage( current - 1 ); 208 }, 209 210 next () { 211 const current = this.currentPage; 212 213 if ( current >= this.allPages ) { 214 return false; 215 } 216 217 this.changePage(current + 1); 218 }, 219 220 fastPrev () { 221 const page = this.currentPage - 5; 222 223 if ( page > 0 ) { 224 this.changePage( page ); 225 } else { 226 this.changePage( 1 ); 227 } 228 }, 229 230 fastNext () { 231 const page = this.currentPage + 5; 232 233 if ( page > this.allPages ) { 234 this.changePage( this.allPages ); 235 } else { 236 this.changePage( page ); 237 } 238 }, 239 }, 240 241 } ); 242 243 Vue.component( 'jet-advanced-reviews-form', { 244 template: '#jet-advanced-reviews-form-template', 245 246 props: { 247 reviewFields: Array 248 }, 249 250 data: function() { 251 return ( { 252 reviewSubmiting: false, 253 reviewTitle: '', 254 reviewContent: '', 255 reviewAuthorName: '', 256 reviewAuthorMail: '', 257 messageText: '', 258 fields: this.reviewFields 259 } ); 260 }, 261 262 mounted: function() { 263 let self = this; 264 265 Vue.nextTick().then( function () { 266 let reviewContent = self.$refs.reviewContent, 267 textarea = reviewContent.$refs.textarea; 268 269 textarea.focus(); 270 } ); 271 272 if ( this.$root.isUserGuest ) { 273 this.reviewAuthorName = JetReviews.getLocalStorageData( 'guestName', '' ); 274 this.reviewAuthorMail = JetReviews.getLocalStorageData( 'guestMail', '' ); 275 } 276 }, 277 278 computed: { 279 formControlsVisible: function() { 280 281 if ( this.$root.isUserGuest ) { 282 return this.isValidReviewContent && 283 this.isValidReviewTitle && 284 this.isValidAuthorName && 285 this.isValidAuthorEmail; 286 } 287 288 return this.isValidReviewContent && this.isValidReviewTitle; 289 }, 290 291 formMessageVisible: function() { 292 return '' !== this.messageText; 293 }, 294 295 reviewTitleVisible: function() { 296 return this.$root.options.reviewTitleInputVisible; 297 }, 298 299 reviewContentVisible: function() { 300 return this.$root.options.reviewContentInputVisible; 301 }, 302 303 isValidReviewTitle: function() { 304 return '' !== this.reviewTitle || ! this.$root.options.reviewTitleInputVisible; 305 }, 306 307 isValidReviewContent: function() { 308 return '' !== this.reviewContent || ! this.$root.options.reviewContentInputVisible; 309 }, 310 311 isValidAuthorName: function() { 312 return '' !== this.reviewAuthorName; 313 }, 314 315 isValidAuthorEmail: function() { 316 return '' !== this.reviewAuthorMail && JetReviews.checkValidEmail( this.reviewAuthorMail ); 317 }, 318 319 }, 320 321 methods: { 322 cancelSubmit: function() { 323 JetReviews.eventBus.$emit( 'closeNewReviewForm', { uniqId: this.$root.options.uniqId } ); 324 }, 325 326 submitReview: function() { 327 328 let self = this, 329 forSendingData = { 330 source: this.$root.sourceData.source, 331 source_id: this.$root.sourceData.sourceId, 332 title: this.reviewTitle, 333 content: this.reviewContent, 334 author_id: this.$root.userData.id, 335 author_name: this.reviewAuthorName, 336 author_mail: this.reviewAuthorMail, 337 rating_data: this.fields 338 }, 339 recaptchaConfig = window.jetReviewPublicConfig.recaptchaConfig; 340 341 // Recaptcha enable check 342 if ( recaptchaConfig.enable ) { 343 344 window.grecaptcha.ready( function() { 345 grecaptcha.execute( 346 recaptchaConfig.site_key, 347 { 348 action: 'submit_review' 349 } 350 ).then( function( token ) { 351 352 JetReviews.captchaToken = token; 353 354 let modifyData = Object.assign( {}, forSendingData, { 355 captcha_token: token 356 } ); 357 358 self.submitReviewHandle( modifyData ); 359 } ); 360 } ); 361 362 return false;
363 } 364 365 this.submitReviewHandle( forSendingData ); 366 }, 367 368 submitReviewHandle: function( sendData = false ) { 369 let self = this; 370 371 if ( ! sendData ) { 372 console.warn( 'Empty new review data for sending' ); 373 374 return false; 375 } 376 377 this.reviewSubmiting = true; 378 this.messageText = ''; 379 380 wp.apiFetch( { 381 method: 'post', 382 path: window.jetReviewPublicConfig.submitReviewRoute, 383 data: sendData, 384 } ).then( function( response ) { 385 response = self.maybeModifyResponce( response ); 386 387 let responseSuccess = response.success, 388 responseData = response.data, 389 responseMessage = response.message; 390 391 self.reviewSubmiting = false; 392 self.messageText = responseMessage; 393 394 if ( self.$root.isUserGuest ) { 395 let guestReviewedItems = JetReviews.getLocalStorageData( self.$root.guestReviewedStorageName, [] ); 396 397 if ( responseSuccess && ! guestReviewedItems.includes( self.$root.sourceData.sourceId ) ) { 398 self.$root.userData.name = self.reviewAuthorName; 399 self.$root.userData.mail = self.reviewAuthorMail; 400 JetReviews.setLocalStorageData( 'guestName', self.reviewAuthorName ); 401 JetReviews.setLocalStorageData( 'guestMail', self.reviewAuthorMail ); 402 403 guestReviewedItems.push( self.$root.sourceData.sourceId ); 404 JetReviews.setLocalStorageData( self.$root.guestReviewedStorageName, guestReviewedItems ); 405 } 406 } 407 408 if ( responseSuccess ) { 409 if ( 'review-created' === response.code ) { 410 JetReviews.eventBus.$emit( 'addReview', { 411 uniqId: self.$root.options.uniqId, 412 reviewData: responseData.item, 413 } ); 414 415 self.$root.reviewsAverageRating = +responseData.rating; 416 } 417 418 self.$root.formVisible = false; 419 self.$root.userData.canReview.allowed = false; 420 self.$root.userData.canReview.message = responseMessage; 421 } 422 423 } ); 424 }, 425 426 maybeModifyResponce: function( responce = false ) { 427 let code = responce.code; 428 429 if ( 'need-approve' === code && '' !== this.$root.options.labels.moderatorCheckMessage ) { 430 responce.message = this.$root.options.labels.moderatorCheckMessage; 431 } 432 433 if ( 'already-created' === code && '' !== this.$root.options.labels.alreadyReviewedMessage ) { 434 responce.message = this.$root.options.labels.alreadyReviewedMessage; 435 } 436 437 return responce; 438 } 439 440 } 441 }); 442 443 Vue.component( 'slider-input', { 444 template: '#jet-advanced-reviews-slider-input-template', 445 446 props: { 447 value: { 448 type: [ String, Number ], 449 default: '' 450 }, 451 max: { 452 type: [ Number ], 453 default: 5 454 }, 455 step: { 456 type: [ Number ], 457 default: 1 458 }, 459 label: { 460 type: [ String, Boolean ], 461 default: false 462 }, 463 }, 464 465 data: function() { 466 return ( { 467 //currentValue: this.value, 468 } ); 469 }, 470 471 computed: { 472 valueLabel: function() { 473 return this.value; 474 } 475 }, 476 477 methods: { 478 handleInput ( event ) { 479 let value = event.target.value; 480 481 this.$emit( 'input', value ); 482 this.$emit( 'on-change', event ); 483 }, 484 handleChange ( event ) { 485 this.$emit( 'on-input-change', event ); 486 } 487 }, 488 }); 489 490 Vue.component( 'stars-input', { 491 template: '#jet-advanced-reviews-stars-input-template', 492 493 props: { 494 value: { 495 type: [ String, Number ], 496 default: '' 497 }, 498 max: { 499 type: [ Number ], 500 default: 5 501 }, 502 step: { 503 type: [ Number ], 504 default: 1 505 }, 506 label: { 507 type: [ String, Boolean ], 508 default: false 509 }, 510 }, 511 512 data: function() { 513 return ( { 514 currentRating: this.value, 515 } ); 516 }, 517 518 computed: { 519 valueLabel: function() { 520 return `${ this.value }/${ this.max }`; 521 }, 522 523 rating: function() { 524 return ( this.currentRating / this.max ) * 100; 525 }, 526 527 preparedRating: function() { 528 529 if ( 10 > this.rating ) { 530 return 10; 531 } 532 533 return this.rating; 534 }, 535 536 emptyIcon: function() { 537 return this.$root.refsHtml.emptyStarIcon || '<i class="far fa-star"></i>'; 538 }, 539 540 emptyIcons: function() { 541 let icon = `<div class="jet-reviews-star">${ this.$root.refsHtml.emptyStarIcon }</div>` || '<div class="jet-reviews-star"><i class="far fa-star"></i></div>'; 542 543 return icon.repeat( this.max ); 544 }, 545 546 filledIcons: function() { 547 let icon = `<div class="jet-reviews-star">${ this.$root.refsHtml.filledStarIcon }</div>` || `<div class="jet-reviews-star"><i class="fas fa-star"></i></div>`; 548 console.log(icon); 549 return icon.repeat( this.max ); 550 }, 551 552 ratingClass: function() { 553 let ratingClass = 'very-high-rating'; 554 555 if ( this.rating >= 80 && this.rating <= 100 ) { 556 ratingClass = 'very-high-rating'; 557 } 558 559 if ( this.rating >= 60 && this.rating <= 79 ) { 560 ratingClass = 'high-rating'; 561 } 562 563 if ( this.rating >= 40 && this.rating <= 59 ) { 564 ratingClass = 'medium-rating'; 565 } 566 567 if ( this.rating >= 22 && this.rating <= 39 ) { 568 ratingClass = 'low-rating'; 569 } 570 571 if ( this.rating >= 0 && this.rating <= 21 ) { 572 ratingClass = 'very-low-rating'; 573 } 574 575 return ratingClass; 576 }, 577 }, 578 579 methods: { 580 ratingClick( rating ) { 581 this.currentRating = rating; 582 this.$emit( 'input', rating ); 583 }, 584 585 ratingMouseOver( rating ) { 586 this.currentRating = rating; 587 }, 588 589 ratingMouseOut() { 590 this.currentRating = this.value; 591 }, 592 }, 593 }); 594 595 Vue.component( 'jet-advanced-reviews-item', { 596 template: '#jet-advanced-reviews-item-template', 597 598 props: { 599 itemData: Object 600 }, 601 602 data: function() { 603 return ( { 604 commentFormVisible: false, 605 commentText: '', 606 commentAuthorName: '', 607 commentAuthorMail: '', 608 commentSubmiting: false, 609 approvalSubmiting: false, 610 parentComment: 0, 611 commentsVisible: false, 612 responseMessage: '', 613 detailsVisibleState: false 614 } ); 615 }, 616 617 mounted: function() { 618 if ( this.$root.isUserGuest ) {
619 this.commentAuthorName = JetReviews.getLocalStorageData( 'guestName', '' ); 620 this.commentAuthorMail = JetReviews.getLocalStorageData( 'guestMail', '' ); 621 622 let guestReviewRatedData = JetReviews.getLocalStorageData( this.$root.guestRatedStorageName, {} ); 623 624 if ( guestReviewRatedData.hasOwnProperty( this.itemData.id ) ) { 625 this.$set( this.itemData, 'approval', guestReviewRatedData[ this.itemData.id ] ); 626 } 627 628 } 629 }, 630 631 computed: { 632 633 isDetailsFieldsAvaliable: function() { 634 return 1 < this.itemData.rating_data.length; 635 }, 636 637 detailsVisible: function() { 638 return this.isDetailsFieldsAvaliable && this.detailsVisibleState; 639 }, 640 641 averageRatingVisible: function() { 642 return 'average' === this.$root.options.reviewRatingType; 643 }, 644 645 detailsRatingVisible: function() { 646 return 'details' === this.$root.options.reviewRatingType; 647 }, 648 649 authorVerificationData: function() { 650 return 0 !== this.itemData.verifications.length ? this.itemData.verifications : false; 651 }, 652 653 isCommentsEmpty: function() { 654 return 0 === this.itemData.comments.length; 655 }, 656 657 isCommentsVisible: function() { 658 return !this.isCommentsEmpty && this.commentsVisible; 659 }, 660 661 itemCommentsCount: function() { 662 let itemCommentsCount = false; 663 664 itemCommentsCount = this.getCommentsCount( this.itemData.comments ); 665 666 return itemCommentsCount; 667 }, 668 669 pinnedVisible: function() { 670 return this.itemData.pinned; 671 }, 672 673 commentControlsVisible: function() { 674 675 if ( this.$root.isUserGuest ) { 676 return this.isCommentValid && 677 this.isValidAuthorName && 678 this.isValidAuthorEmail; 679 } 680 681 return this.isCommentValid; 682 }, 683 684 averageRatingData: function() { 685 let ratingDatalength = this.itemData.rating_data.length, 686 summaryValue = 0, 687 avarageValue = 0, 688 summaryMax = 0, 689 avarageMax = 0; 690 691 summaryValue = this.itemData.rating_data.reduce( function( accumulator, currentValue ) { 692 return accumulator + +currentValue.field_value; 693 }, 0 ); 694 695 summaryMax = this.itemData.rating_data.reduce( function( accumulator, currentValue ) { 696 return accumulator + +currentValue.field_max; 697 }, 0 ); 698 699 avarageValue = Math.round( summaryValue / ratingDatalength ); 700 avarageMax = Math.round( summaryMax / ratingDatalength ); 701 702 return { 703 rating: Math.round( avarageValue * 100 / avarageMax, 1 ), 704 max: Math.round( avarageMax, 1 ), 705 value: Math.round( avarageValue, 1 ) 706 }; 707 }, 708 709 addCommentIcon: function() { 710 return this.$root.refsHtml.newCommentButtonIcon || false; 711 }, 712 713 showCommentsIcon: function() { 714 return this.$root.refsHtml.showCommentsButtonIcon || false; 715 }, 716 717 pinnedIcon: function() { 718 return this.$root.refsHtml.pinnedIcon || '<i class="fas fa-thumbtack"></i>'; 719 }, 720 721 likeIcon: function() { 722 let emptyLike = this.$root.refsHtml.reviewEmptyLikeIcon || '<i class="far fa-thumbs-up"></i>', 723 filledLike = this.$root.refsHtml.reviewFilledLikeIcon || '<i class="fas fa-thumbs-up"></i>'; 724 725 return ! this.itemData.approval.like ? emptyLike : filledLike; 726 }, 727 728 dislikeIcon: function() { 729 let emptyDislike = this.$root.refsHtml.reviewEmptyDislikeIcon || '<i class="far fa-thumbs-down"></i>', 730 filledDislike = this.$root.refsHtml.reviewFilledDislikeIcon || '<i class="fas fa-thumbs-down"></i>'; 731 732 return ! this.itemData.approval.dislike ? emptyDislike : filledDislike; 733 }, 734 735 userCanComment: function() { 736 return this.$root.sourceData.commentsAllowed && this.$root.userData.canComment.allowed; 737 }, 738 739 userCanRate: function() { 740 return this.$root.sourceData.approvalAllowed && this.$root.userData.canRate; 741 }, 742 743 isValidAuthorName: function() { 744 return '' !== this.commentAuthorName; 745 }, 746 747 isValidAuthorEmail: function() { 748 return JetReviews.checkValidEmail( this.commentAuthorMail ); 749 }, 750 751 isCommentValid: function() { 752 return '' !== this.commentText; 753 }, 754 755 }, 756 757 methods: { 758 showCommentForm: function() { 759 let self = this; 760 761 this.commentFormVisible = !this.commentFormVisible; 762 763 if ( this.commentFormVisible ) { 764 Vue.nextTick().then( function () {
765 let commentContent = self.$refs.commentContent, 766 textarea = commentContent.$refs.textarea; 767 768 textarea.focus(); 769 } ); 770 } 771 }, 772 773 cancelNewComment: function() { 774 this.commentFormVisible = false; 775 this.responseMessage = ''; 776 }, 777 778 submitNewComment: function() { 779 780 let self = this, 781 forSendingData = { 782 source: this.$root.sourceData.source, 783 source_id: this.$root.sourceData.sourceId, 784 parent_id: this.parentComment, 785 review_id: this.itemData.id, 786 author_id: this.$root.userData.id, 787 author_name: this.commentAuthorName, 788 author_mail: this.commentAuthorMail, 789 content: this.commentText, 790 }, 791 recaptchaConfig = window.jetReviewPublicConfig.recaptchaConfig; 792 793 // Recaptcha enable check 794 if ( recaptchaConfig.enable ) { 795 796 window.grecaptcha.ready( function() { 797 grecaptcha.execute( 798 recaptchaConfig.site_key, 799 { 800 action: 'submit_review_comment' 801 } 802 ).then( function( token ) { 803 804 JetReviews.captchaToken = token; 805 806 let modifyData = Object.assign( {}, forSendingData, { 807 captcha_token: token 808 } ); 809 810 self.submitCommentHandler( modifyData ); 811 } ); 812 } ); 813 814 return false; 815 } 816 817 this.submitCommentHandler( forSendingData ); 818 }, 819 820 submitCommentHandler: function( sendData = false ) { 821 let self = this; 822 823 if ( ! sendData ) { 824 console.warn( 'Empty new comment data for sending' ); 825 826 return false; 827 } 828 829 this.commentSubmiting = true; 830 831 wp.apiFetch( { 832 method: 'post', 833 path: window.jetReviewPublicConfig.submitReviewCommentRoute, 834 data: sendData, 835 } ).then( function( response ) { 836 837 self.commentSubmiting = false; 838 839 self.$root.userData.name = self.commentAuthorName; 840 self.$root.userData.mail = self.commentAuthorMail; 841 JetReviews.setLocalStorageData( 'guestName', self.commentAuthorName ); 842 JetReviews.setLocalStorageData( 'guestMail', self.commentAuthorMail ); 843 844 if ( response.success ) { 845 self.commentFormVisible = false; 846 self.commentText = ''; 847 self.itemData.comments.unshift( response.data ); 848 self.commentsVisible = true; 849 } else { 850 self.responseMessage = response.message; 851 console.log( response.message ); 852 } 853 } ); 854 }, 855 856 updateApprovalHandler: function( type ) { 857 let self = this, 858 altType = 'like' === type ? 'dislike' : 'like'; 859 860 this.approvalSubmiting = true; 861 862 wp.apiFetch( { 863 method: 'post', 864 path: window.jetReviewPublicConfig.likeReviewRoute, 865 data: { 866 review_id: self.itemData.id, 867 type: type, 868 inc: ! self.itemData.approval[ type ], 869 current_state: self.itemData.approval, 870 }, 871 } ).then( function( response ) { 872 self.approvalSubmiting = false; 873 874 if ( response.success ) { 875 self.$set( self.itemData, 'approval', response.data.approval ); 876 self.$set( self.itemData, 'like', response.data.like ); 877 self.$set( self.itemData, 'dislike', response.data.dislike ); 878 879 if ( self.$root.isUserGuest ) { 880 self.updateGuestApprovalData( self.itemData.id, self.itemData.approval ); 881 } 882 883 } else { 884 console.log( response.message ); 885 } 886 } ); 887 }, 888 889 updateGuestApprovalData: function( $reviewId = false, rateData = false ) { 890 let guestReviewRateData = JetReviews.getLocalStorageData( this.$root.guestRatedStorageName, {} ); 891 892 guestReviewRateData[ $reviewId ] = rateData; 893 894 JetReviews.setLocalStorageData( this.$root.guestRatedStorageName, guestReviewRateData ); 895 }, 896 897 toggleCommentsVisible: function() { 898 this.commentsVisible = !this.commentsVisible; 899 }, 900 901 getCommentsCount: function( comments, initialValue = 0 ) { 902 903 let total = comments.reduce( ( accumulator, commentData ) => { 904 if ( commentData.hasOwnProperty( 'children' ) && 0 !== commentData.children.length ) { 905 let initialValue = accumulator + 1; 906 return this.getCommentsCount( commentData.children, initialValue ); 907 } else { 908 return accumulator + 1; 909 } 910 }, initialValue ); 911 912 return total; 913 } 914 } 915 }); 916 917 Vue.component( 'jet-advanced-reviews-comment', { 918 template: '#jet-advanced-reviews-comment-template', 919 920 props: { 921 commentData: Object, 922 parentId: Number, 923 parentComments: Array, 924 depth: Number, 925 }, 926 927 data: function() { 928 return ( { 929 commentsList: this.commentData.children || [], 930 replySubmiting: false, 931 replyFormVisible: false, 932 replyText: '', 933 replyAuthorName: '', 934 replyAuthorMail: '', 935 responseMessage: '' 936 } ); 937 }, 938 939 mounted: function() { 940 if ( this.$root.isUserGuest ) {
941 this.replyAuthorName = JetReviews.getLocalStorageData( 'guestName', '' ); 942 this.replyAuthorMail = JetReviews.getLocalStorageData( 'guestMail', '' ); 943 } 944 }, 945 946 computed: { 947 commentClass: function() { 948 return ''; 949 }, 950 951 formControlsVisible: function() { 952 return this.$root.sourceData.commentsAllowed; 953 }, 954 955 submitVisible: function() { 956 957 if ( this.$root.isUserGuest ) { 958 return this.isReplyTextValid && 959 this.isValidAuthorName && 960 this.isValidAuthorEmail; 961 } 962 963 return this.isReplyTextValid; 964 }, 965 966 replyIcon: function() { 967 return this.$root.refsHtml.replyButtonIcon || false; 968 }, 969 970 isValidAuthorName: function() { 971 return '' !== this.replyAuthorName; 972 }, 973 974 isValidAuthorEmail: function() { 975 return JetReviews.checkValidEmail( this.replyAuthorMail ); 976 }, 977 978 isReplyTextValid: function() { 979 return '' !== this.replyText; 980 }, 981 982 authorVerificationData: function() { 983 984 if ( ! this.commentData.verifications ) { 985 return false; 986 } 987 988 return 0 !== this.commentData.verifications.length ? this.commentData.verifications : false; 989 }, 990 }, 991 992 methods: { 993 showReplyForm: function() { 994 let self = this; 995 996 this.replyFormVisible = !this.replyFormVisible; 997 998 if ( this.replyFormVisible ) { 999 this.replyText = '<b>' + this.commentData.author.name + '</b>, '; 1000 1001 Vue.nextTick().then( function () { 1002 let commentText = self.$refs.commentText, 1003 textarea = commentText.$refs.textarea; 1004 1005 JetReviews.placeCaretAtEnd( textarea ); 1006 } ); 1007 } 1008 }, 1009 1010 cancelNewReply: function() { 1011 this.replyFormVisible = false; 1012 this.responseMessage = ''; 1013 }, 1014 1015 submitNewReply: function() { 1016 1017 let self = this, 1018 forSendingData = { 1019 source: this.$root.sourceData.source, 1020 source_id: this.$root.sourceData.sourceId, 1021 parent_id: 0 === this.depth ? this.commentData.id : this.parentId, 1022 review_id: this.commentData.review_id, 1023 author_id: this.$root.userData.id, 1024 author_name: this.replyAuthorName, 1025 author_mail: this.replyAuthorMail, 1026 content: this.replyText, 1027 }, 1028 recaptchaConfig = window.jetReviewPublicConfig.recaptchaConfig; 1029 1030 // Recaptcha enable check 1031 if ( recaptchaConfig.enable ) { 1032 1033 window.grecaptcha.ready( function() { 1034 grecaptcha.execute( 1035 recaptchaConfig.site_key, 1036 { 1037 action: 'submit_comment_reply' 1038 } 1039 ).then( function( token ) { 1040 1041 JetReviews.captchaToken = token; 1042 1043 let modifyData = Object.assign( {}, forSendingData, { 1044 captcha_token: token 1045 } ); 1046 1047 self.submitReplyHandler( modifyData ); 1048 } ); 1049 } ); 1050 1051 return false; 1052 } 1053 1054 this.submitReplyHandler( forSendingData ); 1055 }, 1056 1057 submitReplyHandler: function( sendData = false ) { 1058 let self = this; 1059 1060 if ( ! sendData ) { 1061 console.warn( 'Empty review comment data for sending' ); 1062 1063 return false; 1064 } 1065 1066 this.replySubmiting = true; 1067 1068 wp.apiFetch( { 1069 method: 'post', 1070 path: window.jetReviewPublicConfig.submitReviewCommentRoute, 1071 data: sendData, 1072 } ).then( function( response ) { 1073 1074 self.replySubmiting = false; 1075 1076 self.$root.userData.name = self.replyAuthorName; 1077 self.$root.userData.mail = self.replyAuthorMail; 1078 JetReviews.setLocalStorageData( 'guestName', self.replyAuthorName ); 1079 JetReviews.setLocalStorageData( 'guestMail', self.replyAuthorMail ); 1080 1081 if ( response.success ) { 1082 self.replyFormVisible = false; 1083 self.replyText = ''; 1084 1085 if ( 0 === self.depth ) { 1086 self.commentData.children.unshift( response.data ); 1087 } else { 1088 self.parentComments.push( response.data ); 1089 } 1090 } else { 1091 self.responseMessage = response.message; 1092 } 1093 } ); 1094 } 1095 } 1096 }); 1097 1098 Vue.component( 'points-field', { 1099 template: '#jet-advanced-reviews-point-field-template', 1100 1101 props: { 1102 before: { 1103 type: [ Number, String, Boolean ], 1104 default: false 1105 }, 1106 rating: Number, 1107 after: { 1108 type: [ Number, String, Boolean ], 1109 default: false 1110 }, 1111 }, 1112 1113 data: function() { 1114 return ( {} ); 1115 }, 1116 1117 computed: { 1118 isBeforeEmpty: function() { 1119 return false === this.before || '' === this.before; 1120 }, 1121 1122 isAfterEmpty: function() { 1123 return false === this.after || '' === this.after; 1124 }, 1125 1126 preparedRating: function() { 1127 1128 if ( 10 > this.rating ) { 1129 return 10; 1130 } 1131 1132 return this.rating; 1133 }, 1134 1135 ratingClass: function() { 1136 let ratingClass = 'very-high-rating'; 1137 1138 if ( this.rating >= 80 && this.rating <= 100 ) { 1139 ratingClass = 'very-high-rating'; 1140 } 1141 1142 if ( this.rating >= 60 && this.rating <= 79 ) { 1143 ratingClass = 'high-rating'; 1144 } 1145 1146 if ( this.rating >= 40 && this.rating <= 59 ) { 1147 ratingClass = 'medium-rating'; 1148 } 1149 1150 if ( this.rating >= 22 && this.rating <= 39 ) { 1151 ratingClass = 'low-rating'; 1152 } 1153 1154 if ( this.rating >= 0 && this.rating <= 21 ) { 1155 ratingClass = 'very-low-rating'; 1156 } 1157 1158 return ratingClass; 1159 } 1160 } 1161 }); 1162 1163 Vue.component( 'stars-field', { 1164 template: '#jet-advanced-reviews-star-field-template', 1165 1166 props: { 1167 before: { 1168 type: [ Number, String, Boolean ], 1169 default: false 1170 }, 1171 rating: Number, 1172 after: { 1173 type: [ Number, String, Boolean ], 1174 default: false 1175 }, 1176 }, 1177 1178 data: function() { 1179 return ( {} ); 1180 }, 1181 1182 computed: { 1183 isBeforeEmpty: function() { 1184 return ! this.before || '' === this.before; 1185 }, 1186 1187 isAfterEmpty: function() { 1188 return ! this.after || '' === this.after; 1189 }, 1190 1191 preparedRating: function() { 1192 1193 if ( 10 > this.rating ) { 1194 return 10; 1195 } 1196 1197 return this.rating; 1198 }, 1199 1200 emptyIcons: function() { 1201 let icon = `<div class="jet-reviews-star">${ this.$root.refsHtml.emptyStarIcon }</div>` || '<div class="jet-reviews-star"><i class="far fa-star"></i></div>'; 1202 1203 return icon.repeat( 5 ); 1204 }, 1205 1206 filledIcons: function() { 1207 let icon = `<div class="jet-reviews-star">${ this.$root.refsHtml.filledStarIcon }</div>` || '<div class="jet-reviews-star"><i class="fas fa-star"></i></div>'; 1208 1209 return icon.repeat( 5 ); 1210 }, 1211 1212 ratingClass: function() { 1213 let ratingClass = 'very-high-rating'; 1214 1215 if ( this.rating >= 80 && this.rating <= 100 ) { 1216 ratingClass = 'very-high-rating'; 1217 } 1218 1219 if ( this.rating >= 60 && this.rating <= 79 ) { 1220 ratingClass = 'high-rating'; 1221 } 1222 1223 if ( this.rating >= 40 && this.rating <= 59 ) { 1224 ratingClass = 'medium-rating'; 1225 } 1226 1227 if ( this.rating >= 22 && this.rating <= 39 ) { 1228 ratingClass = 'low-rating'; 1229 } 1230 1231 if ( this.rating >= 0 && this.rating <= 21 ) { 1232 ratingClass = 'very-low-rating'; 1233 } 1234 1235 return ratingClass; 1236 } 1237 }, 1238 }); 1239 1240 Vue.component( 'html-textarea', { 1241 template:'<div :class="classes" ref="textarea" contenteditable="true" tabindex="0" :data-placeholder="placeholder" :data-not-valid-label="notValidLabel" @input="updateHTML" @focus="focusHandler" @blur="blurHandler"></div>', 1242 1243 props: { 1244 value: String, 1245 isValid: { 1246 type: Boolean, 1247 default: true, 1248 }, 1249 placeholder: { 1250 type: String, 1251 default: 'Input', 1252 }, 1253 notValidLabel: { 1254 type: String, 1255 default: 'This field is required or not valid', 1256 }, 1257 }, 1258 1259 data: function() { 1260 return ( { 1261 isFocus: false, 1262 isEmpty: true, 1263 } ); 1264 }, 1265 1266 computed: { 1267 classes: function() {
1268 let classes = [ 1269 'jet-reviews-content-editable', 1270 !this.isValid ? 'jet-reviews-content-editable--not-valid' : false, 1271 this.isFocus ? 'jet-reviews-content-editable--focus' : false, 1272 this.isPlaceholder ? 'jet-reviews-content-editable--placeholder' : false, 1273 ]; 1274 1275 return classes; 1276 }, 1277 1278 isPlaceholder: function() { 1279 1280 if ( this.isFocus || ! this.isEmpty ) { 1281 return false; 1282 } 1283 1284 return true; 1285 } 1286 }, 1287 1288 mounted: function () { 1289 this.$el.innerHTML = this.value; 1290 }, 1291 1292 methods: { 1293 updateHTML: function( e ) { 1294 this.$emit( 'input', e.target.innerHTML ); 1295 1296 if ( 0 === $( this.$refs.textarea ).text().trim().length ) { 1297 this.isEmpty = true; 1298 } else { 1299 this.isEmpty = false; 1300 } 1301 }, 1302 1303 focusHandler: function( e ) { 1304 this.$emit( 'focus', e.target ); 1305 this.isFocus = true; 1306 }, 1307 1308 blurHandler: function( e ) { 1309 this.$emit( 'blur', e.target ); 1310 this.isFocus = false; 1311 } 1312 } 1313 }); 1314 }, 1315 1316 createJetReviewAdvancedInstance: function( instanceId, options ) { 1317 1318 if ( JetReviews.initedInstance.includes( instanceId ) ) { 1319 return; 1320 } 1321 1322 JetReviews.initedInstance.push( instanceId ); 1323 1324 let JetReviewAdvancedInstance = new Vue( { 1325 el: '#' + instanceId, 1326 1327 data: { 1328 uniqId: instanceId, 1329 options: options, 1330 reviewsLoaded: false, 1331 getReviewsProcessing: false, 1332 reviewsList: [], 1333 reviewsPage: 1, 1334 reviewsTotal: 0, 1335 reviewsAverageRating: 0, 1336 userData: options.userData, 1337 sourceData: options.sourceData, 1338 reviewTypeData: window.jetReviewPublicConfig.reviewTypeData, 1339 formVisible: false, 1340 isMounted: false, 1341 refsHtml: {}, 1342 }, 1343 1344 mounted: function() { 1345 let self = this, 1346 refsHtml = {}; 1347 1348 this.isMounted = true; 1349 1350 for ( var ref in this.$refs ) { 1351 Object.assign( refsHtml, { [ ref ]: this.$refs[ ref ].innerHTML } ); 1352 } 1353 1354 this.refsHtml = refsHtml; 1355 1356 if ( this.isUserGuest ) { 1357 this.$set( this.userData, 'id', this.guestId ); 1358 this.$set( this.userData, 'name', this.guestName ); 1359 this.$set( this.userData, 'mail', this.guestMail ); 1360 1361 let guestReviewedItems = JetReviews.getLocalStorageData( this.guestReviewedStorageName, [] ); 1362 1363 if ( guestReviewedItems.includes( this.sourceData.sourceId ) ) { 1364 this.$set( this.userData.canReview, 'allowed', false ); 1365 this.$set( this.userData.canReview, 'message', this.options.labels.alreadyReviewedMessage ); 1366 } 1367 } 1368 1369 if ( this.options.reviewsListData ) { 1370 this.reviewsList = this.options.reviewsListData.list; 1371 this.reviewsTotal = +this.options.reviewsListData.total; 1372 this.reviewsAverageRating = +this.options.reviewsListData.rating; 1373 this.reviewsLoaded = true; 1374 } else { 1375 wp.apiFetch( { 1376 method: 'post', 1377 path: window.jetReviewPublicConfig.getPublicReviewsRoute, 1378 data: { 1379 source: self.sourceData.source, 1380 source_id: self.sourceData.sourceId, 1381 page: self.reviewsPage - 1, 1382 page_size: self.options.pageSize 1383 }, 1384 } ).then( function( response ) { 1385 1386 self.reviewsLoaded = true; 1387 1388 if ( response.success && response.data ) { 1389 self.reviewsList = response.data.list; 1390 self.reviewsTotal = +response.data.total; 1391 self.reviewsAverageRating = +response.data.rating; 1392 } else { 1393 console.log( 'Error' ); 1394 } 1395 } ); 1396 } 1397 1398 JetReviews.eventBus.$on( 'addReview', function ( payLoad ) { 1399 1400 if ( self.options.uniqId !== payLoad.uniqId ) { 1401 return; 1402 } 1403 1404 self.formVisible = false; 1405 1406 let reviewData = payLoad.reviewData; 1407 1408 self.reviewsList.unshift( reviewData ); 1409 1410 self.reviewsTotal += 1; 1411 } ); 1412 1413 JetReviews.eventBus.$on( 'closeNewReviewForm', function ( payLoad ) { 1414 1415 if ( self.options.uniqId !== payLoad.uniqId ) { 1416 return; 1417 } 1418 1419 self.formVisible = false; 1420 } ); 1421 1422 }, 1423 1424 computed: { 1425 instanceClass: function() { 1426 let classes = [ 1427 'jet-reviews-advanced__instance', 1428 ]; 1429 1430 if ( this.isMounted ) {
1431 classes.push( 'is-mounted' ); 1432 } 1433 1434 return classes; 1435 }, 1436 1437 reviewsLength: function() { 1438 return this.reviewsList.length; 1439 }, 1440 1441 reviewsListEmpty: function() { 1442 return 0 === this.reviewsList.length ? true : false; 1443 }, 1444 1445 preparedFields: function() { 1446 1447 let rawFields = this.reviewTypeData.fields, 1448 preparedFields = []; 1449 1450 for ( let fieldData of rawFields ) { 1451 preparedFields.push( { 1452 field_label: fieldData.label, 1453 field_value: +fieldData.max, 1454 field_step: +fieldData.step, 1455 field_max: +fieldData.max, 1456 } ); 1457 } 1458 1459 return preparedFields; 1460 }, 1461 1462 averageRating: function() { 1463 let totalRating = 0; 1464 1465 if ( this.reviewsListEmpty ) { 1466 return 0; 1467 } 1468 1469 totalRating = this.reviewsList.reduce( function( sum, reviewItem ) { 1470 return +reviewItem.rating + sum; 1471 }, 0 ); 1472 1473 return Math.round( totalRating / this.reviewsList.length, 1 ); 1474 }, 1475 1476 averageValue: function() { 1477 let summaryValue = 0; 1478 1479 if ( this.reviewsListEmpty ) { 1480 return 0; 1481 } 1482 1483 for ( let reviewItem of this.reviewsList ) { 1484 let ratingData = reviewItem.rating_data, 1485 itemSummary = 0; 1486 1487 for ( let ratingItem of ratingData ) { 1488 itemSummary += +ratingItem.field_value 1489 } 1490 1491 summaryValue += Math.round( itemSummary / ratingData.length, 1 ) 1492 } 1493 1494 return Math.round( summaryValue / this.reviewsList.length, 1 ); 1495 }, 1496 1497 averageMax: function() { 1498 let totalMax = 0, 1499 fields = this.reviewTypeData.fields; 1500 1501 totalMax = fields.reduce( function( sum, field ) { 1502 return +field.max + sum; 1503 }, 0 ); 1504 1505 return Math.round( totalMax / fields.length, 1 ); 1506 }, 1507 1508 addReviewIcon: function() { 1509 return this.refsHtml.newReviewButtonIcon || false; 1510 }, 1511 1512 isUserGuest: function() { 1513 return this.userData.roles.includes( 'guest' ); 1514 }, 1515 1516 guestId: function() { 1517 let guestId = JetReviews.getLocalStorageData( 'guestId', false ); 1518 1519 if ( ! guestId ) { 1520 let guestId = `guest_${ JetReviews.getUniqId() }`; 1521 1522 JetReviews.setLocalStorageData( 'guestId', guestId ); 1523 1524 return guestId; 1525 } 1526 1527 return guestId; 1528 }, 1529 1530 guestName: function() { 1531 let guestName = JetReviews.getLocalStorageData( 'guestName', false ); 1532 1533 if ( ! guestName ) { 1534 JetReviews.setLocalStorageData( 'guestName', '' ); 1535 1536 return ''; 1537 } 1538 1539 return guestName; 1540 }, 1541 1542 guestMail: function() { 1543 let guestMail = JetReviews.getLocalStorageData( 'guestMail', false ); 1544 1545 if ( ! guestMail ) { 1546 JetReviews.setLocalStorageData( 'guestMail', '' ); 1547 1548 return ''; 1549 } 1550 1551 return guestMail; 1552 }, 1553 1554 guestReviewedStorageName: function() { 1555 return `reviewed${ JetReviews.capitalizeString( this.sourceData.source ) }Items`; 1556 }, 1557 1558 guestRatedStorageName: function() { 1559 return `rated${ JetReviews.capitalizeString( this.sourceData.source ) }Items`; 1560 }, 1561 1562 guestNameFieldVisible: function() { 1563 return this.isUserGuest && '' === this.userData.name; 1564 }, 1565 1566 guestMailFieldVisible: function() { 1567 return this.isUserGuest && '' === this.userData.mail; 1568 }, 1569 1570 paginationVisible: function() { 1571 return this.reviewsTotal > this.options.pageSize; 1572 }, 1573 1574 }, 1575 1576 methods: { 1577 formVisibleToggle: function() { 1578 this.formVisible = !this.formVisible; 1579 }, 1580 1581 getLabelBySlug: function( slug = false ) { 1582 1583 if ( ! slug ) { 1584 return false; 1585 } 1586 1587 if ( ! window.jetReviewPublicConfig.labels.hasOwnProperty( slug ) ) { 1588 return false; 1589 } 1590 1591 return window.jetReviewPublicConfig.labels[ slug ]; 1592 }, 1593 1594 changePageHandle: function( page ) { 1595 this.reviewsPage = page; 1596 this.getReviews(); 1597 }, 1598 1599 getReviews: function() { 1600 let self = this; 1601 1602 self.getReviewsProcessing = true; 1603 1604 wp.apiFetch( { 1605 method: 'post', 1606 path: window.jetReviewPublicConfig.getPublicReviewsRoute, 1607 data: { 1608 source: self.sourceData.source, 1609 source_id: self.sourceData.sourceId, 1610 page: self.reviewsPage - 1, 1611 page_size: self.options.pageSize, 1612 }, 1613 } ).then( function( response ) { 1614 self.getReviewsProcessing = false;
1615 1616 self.scrollToInstance(); 1617 1618 if ( response.success && response.data ) { 1619 self.reviewsList = response.data.list; 1620 self.reviewsTotal = +response.data.total; 1621 self.reviewsAverageRating = +response.data.rating; 1622 } else { 1623 console.log( 'Error' ); 1624 } 1625 } ); 1626 }, 1627 1628 scrollToInstance: function() { 1629 let $instance = $( `#${ this.uniqId }` ), 1630 offsetTop = $instance.offset().top - 50; 1631 1632 window.scrollTo( { 1633 top: offsetTop, 1634 behavior: 'smooth', 1635 } ); 1636 } 1637 } 1638 1639 } ); 1640 }, 1641 1642 getLocalStorageData: function( _key = false, _default = false ) { 1643 try { 1644 let jetReviewsData = JSON.parse( window.localStorage.getItem( 'jetReviewsData' ) ); 1645 1646 if ( _key ) { 1647 1648 if ( jetReviewsData.hasOwnProperty( _key ) ) { 1649 return jetReviewsData[ _key ]; 1650 } else { 1651 return _default; 1652 } 1653 } 1654 1655 return jetReviewsData; 1656 } catch ( e ) { 1657 return _default; 1658 } 1659 }, 1660 1661 setLocalStorageData: function( key, data ) { 1662 let jetReviewsData = this.getLocalStorageData() || {}; 1663 1664 jetReviewsData[ key ] = data; 1665 1666 window.localStorage.setItem( 'jetReviewsData', JSON.stringify( jetReviewsData ) ); 1667 }, 1668 1669 getUniqId: function() { 1670 return Math.random().toString( 36 ).substr( 2, 9 ); 1671 }, 1672 1673 capitalizeString: function( string = '' ) { 1674 1675 if ( typeof string !== 'string' ) { 1676 return '' 1677 } 1678 1679 return string.charAt( 0 ).toUpperCase() + string.slice( 1 ); 1680 }, 1681 1682 placeCaretAtEnd: function( el ) { 1683 el.focus(); 1684 1685 if ( 'undefined' !== typeof window.getSelection && 'undefined' !== typeof document.createRange ) { 1686 let range = document.createRange(); 1687 1688 range.selectNodeContents( el ); 1689 range.collapse( false ); 1690 1691 let selection = window.getSelection(); 1692 1693 selection.removeAllRanges(); 1694 selection.addRange( range ); 1695 } else if ( 'undefined' !== typeof document.body.createTextRange ) { 1696 let textRange = document.body.createTextRange(); 1697 1698 textRange.moveToElementText( el ); 1699 textRange.collapse( false ); 1700 textRange.select(); 1701 } 1702 }, 1703 1704 checkValidEmail: function ( email ) { 1705 var reg = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 1706 1707 return reg.test( email ); 1708 }, 1709 }; 1710 1711 window.JetReviews = JetReviews; 1712 1713 JetReviews.init(); 1714 1715}( jQuery, window.jetReviewPublicConfig ) ); 1716 1717// Elementor Public Scripts 1718( function( $, elementorFrontend, publicConfig ) { 1719 'use strict'; 1720 1721 let JetReviewsElementor = { 1722 init: function() { 1723 1724 var widgets = { 1725 'jet-reviews.default' : JetReviewsElementor.widgetJetReviewsSimple, 1726 'jet-reviews-advanced.default' : JetReviewsElementor.widgetJetReviewsAdvanced, 1727 }; 1728 1729 $.each( widgets, function( widget, callback ) { 1730 elementorFrontend.hooks.addAction( 'frontend/element_ready/' + widget, callback ); 1731 } ); 1732 }, 1733 1734 widgetJetReviewsSimple: function( $scope ) { 1735 var $target = $scope.find( '.jet-review' ), 1736 settings = $target.data( 'settings' ), 1737 $form = $( '.jet-review__form', $target ), 1738 $submitButton = $( '.jet-review__form-submit', $target ), 1739 $removeButton = $( '.jet-review__item-remove', $target ), 1740 $message = $( '.jet-review__form-message', $target ), 1741 $rangeControl = $( '.jet-review__form-field.type-range input', $target ), 1742 ajaxRequest = null; 1743 1744 $rangeControl.on( 'input', function( event ) { 1745 var $this = $( this ), 1746 $parent = $this.closest( '.jet-review__form-field' ), 1747 $currentValue = $( '.current-value', $parent ), 1748 value = $this.val(); 1749 1750 $currentValue.html( value ); 1751 } ); 1752 1753 $submitButton.on( 'click.widgetJetReviews', function() { 1754 addReviewHandle(); 1755 1756 return false; 1757 } ); 1758 1759 $removeButton.on( 'click.widgetJetReviews', function() { 1760 var $this = $( this ); 1761 1762 removeReviewHandle( $this ); 1763 1764 return false; 1765 } ); 1766 1767 function addReviewHandle() { 1768 var now = new Date(), 1769 reviewTime = now.getTime(),
1770 reviewDate = new Date( reviewTime ).toLocaleString(), 1771 sendData = { 1772 'post_id': settings['post_id'], 1773 'review_time': reviewTime, 1774 'review_date': reviewDate 1775 }, 1776 serializeArray = $form.serializeObject(); 1777 1778 sendData = jQuery.extend( sendData, serializeArray ); 1779 1780 ajaxRequest = jQuery.ajax( { 1781 type: 'POST', 1782 url: window.jetReviewPublicConfig.ajax_url, 1783 data: { 1784 'action': 'jet_reviews_add_meta_review', 1785 'data': sendData 1786 }, 1787 beforeSend: function( jqXHR, ajaxSettings ) { 1788 if ( null !== ajaxRequest ) { 1789 ajaxRequest.abort(); 1790 } 1791 1792 $submitButton.addClass( 'load-state' ); 1793 }, 1794 error: function( jqXHR, ajaxSettings ) { 1795 1796 }, 1797 success: function( data, textStatus, jqXHR ) { 1798 1799 var responseType = data['type'], 1800 message = data.message || ''; 1801 1802 if ( 'error' === responseType ) { 1803 $submitButton.removeClass( 'load-state' ); 1804 $message.addClass( 'visible-state' ); 1805 1806 $( 'span', $message ).html( message ); 1807 } 1808 1809 if ( 'success' === responseType ) { 1810 location.reload(); 1811 } 1812 } 1813 } ); 1814 }; 1815 1816 function removeReviewHandle( $removeButton ) { 1817 var $reviewItem = $removeButton.closest( '.jet-review__item' ), 1818 reviewUserId = $reviewItem.data( 'user-id' ), 1819 sendData = { 1820 'post_id': settings['post_id'], 1821 'user_id': reviewUserId 1822 }; 1823 1824 ajaxRequest = jQuery.ajax( { 1825 type: 'POST', 1826 url: window.jetReviewPublicConfig.ajax_url, 1827 data: { 1828 'action': 'jet_reviews_remove_review', 1829 'data': sendData 1830 }, 1831 beforeSend: function( jqXHR, ajaxSettings ) { 1832 if ( null !== ajaxRequest ) { 1833 ajaxRequest.abort(); 1834 } 1835 1836 $removeButton.addClass( 'load-state' ); 1837 }, 1838 error: function( jqXHR, ajaxSettings ) { 1839 1840 }, 1841 success: function( data, textStatus, jqXHR ) { 1842 var successType = data.type, 1843 message = data.message || ''; 1844 1845 if ( 'error' == successType ) { 1846 1847 } 1848 1849 if ( 'success' == successType ) { 1850 location.reload(); 1851 } 1852 } 1853 } ); 1854 }; 1855 }, 1856 1857 widgetJetReviewsAdvanced: function( $scope ) { 1858 let $target = $scope.find( '.jet-reviews-advanced' ), 1859 instanceId = $target.attr( 'id' ), 1860 uniqid = $target.data( 'uniqid' ) || false, 1861 options = window['jetReviewsWidget' + uniqid] || {}; 1862 1863 if ( ! $target[0] || ! uniqid ) { 1864 return; 1865 } 1866 1867 window.JetReviews.createJetReviewAdvancedInstance( instanceId, options ); 1868 }, 1869 } 1870 1871 $( window ).on( 'elementor/frontend/init', JetReviewsElementor.init ); 1872 1873 $.fn.serializeObject = function(){ 1874 1875 var self = this, 1876 json = {}, 1877 push_counters = {}, 1878 patterns = { 1879 "validate": /^[a-zA-Z][a-zA-Z0-9_-]*(?:\[(?:\d*|[a-zA-Z0-9_-]+)\])*$/, 1880 "key": /[a-zA-Z0-9_-]+|(?=\[\])/g, 1881 "push": /^$/, 1882 "fixed": /^\d+$/, 1883 "named": /^[a-zA-Z0-9_-]+$/ 1884 }; 1885 1886 this.build = function(base, key, value){ 1887 base[key] = value; 1888 return base; 1889 }; 1890 1891 this.push_counter = function(key){ 1892 if(push_counters[key] === undefined){ 1893 push_counters[key] = 0; 1894 } 1895 return push_counters[key]++; 1896 }; 1897 1898 $.each($(this).serializeArray(), function(){ 1899 // skip invalid keys 1900 if(!patterns.validate.test(this.name)){ 1901 return; 1902 } 1903 1904 var k, 1905 keys = this.name.match(patterns.key), 1906 merge = this.value, 1907 reverse_key = this.name; 1908 1909 while((k = keys.pop()) !== undefined){ 1910 1911 // adjust reverse_key 1912 reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), ''); 1913 1914 // push 1915 if(k.match(patterns.push)){ 1916 merge = self.build([], self.push_counter(reverse_key), merge); 1917 } 1918 1919 // fixed 1920 else if(k.match(patterns.fixed)){ 1921 merge = self.build([], k, merge); 1922 } 1923 1924 // named 1925 else if(k.match(patterns.named)){ 1926 merge = self.build({}, k, merge); 1927 } 1928 } 1929 1930 json = $.extend(true, json, merge); 1931 }); 1932 1933 return json; 1934 }; 1935 1936}( jQuery, window.elementorFrontend, window.jetReviewPublicConfig ) );
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.