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