PageSourceSearch

https://bonneuil-en-france.com/wp-content/plugins/jet-reviews/assets/js/jet-reviews-frontend.js?ver=2.3.5

js bonneuil-en-france.com collected 2026-10-09 17:13:30 UTC 47,942 bytes, 1,911 lines download raw bytes

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>,&nbsp;';
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.