PageSourceSearch

https://tushetiland.ge/wp-content/plugins/trip-reviews/assets/public/js/frontend.js?ver=1.0.4

js tushetiland.ge collected 2026-10-02 14:04:02 UTC 9,939 bytes, 357 lines download raw bytes

1/**
2 * Trip Review for WP Travel Engine - Frontend JavaScript
3 *
4 * @package TripReviewForWPTravelEngine
5 */
6
7(function ($) {
8	'use strict';
9
10	// Initialize when document is ready.
11	$( document ).ready(
12		function () {
13			TRPRE_Frontend.init();
14		}
15	);
16
17	// Main frontend object.
18	window.TRPRE_Frontend = {
19
20		// Flag to prevent duplicate initialization.
21		initialized: false,
22
23		/**
24		 * Initialize frontend functionality
25		 * Free version: Simplified initialization
26		 */
27		init: function () {
28			// Prevent duplicate initialization.
29			if (this.initialized) {
30				return;
31			}
32
33			this.initStarRating();
34			this.initFormSubmission();
35			this.initLoadMore();
36
37			this.initialized = true;
38		},
39
40		/**
41		 * Initialize star rating interaction
42		 */
43		initStarRating: function () {
44			var self = this;
45
46			// Unbind first to avoid duplicate handlers.
47			$( '.trpre-star-input input[type="radio"]' ).off( 'change.trpre-rating' );
48			$( '.trpre-star-input .star-label' ).off( 'mouseenter.trpre-rating mouseleave.trpre-rating' );
49
50			// Bind change event.
51			$( '.trpre-star-input input[type="radio"]' ).on(
52				'change.trpre-rating',
53				function () {
54					var $radio = $( this );
55					var rating = $radio.val();
56					var text   = '';
57
58					switch (rating) {
59						case '1': text = trpre_frontend.strings.poor || 'Poor'; break;
60						case '2': text = trpre_frontend.strings.fair || 'Fair'; break;
61						case '3': text = trpre_frontend.strings.good || 'Good'; break;
62						case '4': text = trpre_frontend.strings.very_good || 'Very Good'; break;
63						case '5': text = trpre_frontend.strings.excellent || 'Excellent'; break;
64					}
65
66					// Update the rating text for this specific rating field.
67					$radio.closest( '.trpre-form-field' ).find( '.trpre-rating-text' ).text( text );
68
69					// Update selected state for all stars in this container.
70					self.updateSelectedState( $radio );
71				}
72			);
73
74			// Handle hover effects.
75			$( '.trpre-star-input .star-label' ).on(
76				'mouseenter.trpre-rating',
77				function () {
78					var $starContainer = $( this ).closest( '.trpre-star-input' );
79					var $stars         = $starContainer.find( '.star-label' );
80					var currentIndex   = $stars.index( this );
81
82					// Add hover class to current and all previous stars.
83					$stars.each(
84						function (index) {
85							if (index <= currentIndex) {
86								$( this ).addClass( 'trpre-star-hover' );
87							} else {
88								$( this ).removeClass( 'trpre-star-hover' );
89							}
90						}
91					);
92				}
93			);
94
95			$( '.trpre-star-input' ).on(
96				'mouseleave.trpre-rating',
97				function () {
98					// Remove hover class from all stars in this container.
99					$( this ).find( '.star-label' ).removeClass( 'trpre-star-hover' );
100				}
101			);
102
103			// Initialize selected state for any pre-selected ratings.
104			$( '.trpre-star-input input[type="radio"]:checked' ).each(
105				function () {
106					self.updateSelectedState( $( this ) );
107				}
108			);
109
110			// Initialize SVG states for all star inputs.
111			self.initializeStarStates();
112		},
113
114		/**
115		 * Update selected state for star ratings
116		 */
117		updateSelectedState: function ($radio) {
118			var $starContainer = $radio.closest( '.trpre-star-input' );
119			var $stars         = $starContainer.find( '.star-label' );
120			var selectedIndex  = parseInt( $radio.val() ) - 1; // Convert to 0-based index.
121
122			// Remove selected class from all stars first.
123			$stars.removeClass( 'trpre-star-selected' );
124
125			// Add selected class to stars up to the selected rating.
126			$stars.each(
127				function (index) {
128					if (index <= selectedIndex) {
129						$( this ).addClass( 'trpre-star-selected' );
130					}
131				}
132			);
133		},
134
135		/**
136		 * Initialize SVG star states
137		 */
138		initializeStarStates: function () {
139			$( '.trpre-star-input .star-label' ).each(
140				function () {
141					var $label = $( this );
142					// Remove any existing state classes.
143					$label.removeClass( 'trpre-star-hover trpre-star-selected' );
144					// Ensure default state shows line SVG and hides fill SVG.
145					$label.find( '.star-line' ).show();
146					$label.find( '.star-fill' ).hide();
147				}
148			);
149		},
150
151		// Image upload methods removed in free version.
152
153		/**
154		 * Initialize form submission
155		 */
156		initFormSubmission: function () {
157			var self = this;
158
159			// Use object property for global isSubmitting flag.
160			if ( ! this.formSubmitting) {
161				this.formSubmitting = false;
162			}
163
164			// Remove any existing handlers first to prevent duplicates.
165			$( document ).off( 'submit.trpre-form', '.trpre-review-form-content' );
166
167			$( document ).on(
168				'submit.trpre-form',
169				'.trpre-review-form-content',
170				function (e) {
171					e.preventDefault();
172
173					var form = $( this );
174
175					if (self.formSubmitting) {
176						console.log( 'Form already submitting, preventing duplicate' );
177						return false;
178					}
179
180					self.formSubmitting = true;
181
182					var formData   = new FormData( this );
183					var submitBtn  = form.find( '.trpre-submit-btn' );
184					var btnText    = submitBtn.find( '.trpre-btn-text' );
185					var btnLoading = submitBtn.find( '.trpre-btn-loading' );
186
187					submitBtn.prop( 'disabled', true );
188					btnText.hide();
189					btnLoading.show();
190					form.find( '.trpre-form-messages' ).empty();
191
192					$.ajax(
193						{
194							url: trpre_frontend.ajax_url,
195							type: 'POST',
196							data: formData,
197							processData: false,
198							contentType: false,
199							success: function (response) {
200								if (response.success) {
201									// SECURITY FIX: Use .text() to prevent DOM XSS.
202									var successMsg = $( '<div class="trpre-message trpre-success"></div>' ).text( response.data.message );
203									form.find( '.trpre-form-messages' ).empty().append( successMsg );
204									form[0].reset();
205									form.find( '.trpre-rating-text' ).text( 'Select a rating' );
206
207									setTimeout(
208										function () {
209											location.reload();
210										},
211										2000
212									);
213								} else {
214									// SECURITY FIX: Use .text() to prevent DOM XSS.
215									var errorMsg = $( '<div class="trpre-message trpre-error"></div>' ).text( response.data.message );
216									form.find( '.trpre-form-messages' ).empty().append( errorMsg );
217								}
218							},
219							error: function () {
220								form.find( '.trpre-form-messages' ).html( '<div class="trpre-message trpre-error">An error occurred. Please try again.</div>' );
221							},
222							complete: function () {
223								submitBtn.prop( 'disabled', false );
224								btnText.show();
225								btnLoading.hide();
226								self.formSubmitting = false;
227							}
228						}
229					);
230				}
231			);
232		},
233
234		/**
235		 * Initialize Load More functionality
236		 * Free version: Simple chronological loading (date DESC)
237		 */
238		initLoadMore: function () {
239			$( document ).on(
240				'click',
241				'.trpre-load-more-btn',
242				function (e) {
243					e.preventDefault();
244
245					var button         = $( this );
246					var tripId         = button.data( 'trip-id' );
247					var currentPage    = parseInt( button.data( 'current-page' ) );
248					var totalPages     = parseInt( button.data( 'total-pages' ) );
249					var reviewsPerPage = parseInt( button.data( 'reviews-per-page' ) );
250
251					// Calculate next page.
252					var nextPage = currentPage + 1;
253
254					if (nextPage > totalPages) {
255						return;
256					}
257
258					// Show loading state.
259					button.prop( 'disabled', true );
260					button.find( '.trpre-load-more-text' ).hide();
261					button.find( '.trpre-load-more-spinner' ).show();
262
263					// Make AJAX request.
264					$.ajax(
265						{
266							url: trpre_frontend.ajax_url,
267							type: 'POST',
268							data: {
269								action: 'trpre_load_more_reviews',
270								nonce: trpre_frontend.nonce,
271								trip_id: tripId,
272								page: nextPage,
273								reviews_per_page: reviewsPerPage
274							},
275							success: function (response) {
276								if (response.success) {
277									var newReviews = $( response.data.html );
278
279									// Add fade-in animation.
280									newReviews.css(
281										{
282											opacity: 0,
283											transform: 'translateY(20px)'
284										}
285									);
286
287									// Append new reviews.
288									var reviewsList = button.closest( '.trpre-reviews-container' ).find( '.trpre-reviews-list' );
289									reviewsList.append( newReviews );
290
291									// Animate in.
292									newReviews.animate(
293										{
294											opacity: 1,
295											transform: 'translateY(0)'
296										},
297										400
298									);
299
300									// Smooth scroll to first new review.
301									if (newReviews.length > 0) {
302										var firstNewReview = newReviews.first();
303										$( 'html, body' ).animate(
304											{
305												scrollTop: firstNewReview.offset().top - 100
306											},
307											400
308										);
309									}
310
311									// Update button state.
312									button.data( 'current-page', response.data.current_page );
313
314									// Update button text.
315									button.find( '.trpre-load-more-text' ).text(
316										trpre_frontend.strings.load_more_count
317										.replace( '%1$d', response.data.showing )
318										.replace( '%2$d', response.data.total_reviews )
319									);
320
321									// Hide button if no more reviews.
322									if ( ! response.data.has_more) {
323										button.fadeOut(
324											400,
325											function () {
326												// Show the info div with "All reviews loaded" message.
327												button.siblings( '.trpre-load-more-info' )
328												.text( trpre_frontend.strings.all_reviews_loaded || 'All reviews loaded' )
329												.fadeIn( 400 );
330											}
331										);
332									}
333
334								} else {
335									TRPRE_Frontend.showMessage( response.data.message, 'error' );
336								}
337							},
338							error: function () {
339								TRPRE_Frontend.showMessage(
340									trpre_frontend.strings.error || 'An error occurred. Please try again.',
341									'error'
342								);
343							},
344							complete: function () {
345								// Hide loading state.
346								button.prop( 'disabled', false );
347								button.find( '.trpre-load-more-text' ).show();
348								button.find( '.trpre-load-more-spinner' ).hide();
349							}
350						}
351					);
352				}
353			);
354		}
355	};
356
357})( jQuery );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.