PageSourceSearch

https://wsform.com/wp-content/plugins/simple-feature-requests-pro/assets/frontend/js/main.js?ver=2.4.9.6

js wsform.com collected 2026-10-02 14:31:55 UTC 14,734 bytes, 605 lines download raw bytes

1(function( $, document ) {
2	var sfr = {
3
4		/**
5		 * Set up cache with common elements and vars.
6		 */
7		cache: function() {
8			sfr.vars = {};
9
10			sfr.vars.vote_button_selector = '[data-sfr-vote]';
11			sfr.vars.button_status_classes = {
12				'voting': 'sfr-vote-button--voting',
13				'voted': 'sfr-vote-button--voted'
14			};
15			sfr.vars.toggle_button_selector = '[data-sfr-toggle]';
16			sfr.vars.toggle_user_type_selector = '[data-sfr-toggle-submission-user-type]';
17
18			sfr.els = {};
19
20			sfr.els.container = $( '.sfr-container' );
21			sfr.els.filters = $( '.sfr-filters' );
22			sfr.els.submission_form = {
23				'form': $( '.sfr-form--submission' ),
24				'title': $( '.sfr-form--submission .sfr-form__title' ),
25				'reveal': $( '.sfr-form--submission .sfr-form__reveal' ),
26				'loader': $( '.sfr-search-field__icon--loader' ),
27				'clear': $( '.sfr-search-field__icon--clear' ),
28				'choices': {
29					'container': $( '.sfr-form--submission .sfr-form__choices' ),
30					'count': $( '.sfr-form--submission .sfr-form__choices-count' ),
31					'vote': $( '.sfr-form--submission .sfr-form__choices-vote' ),
32					'or': $( '.sfr-form--submission .sfr-form__choices-or' ),
33					'post': $( '.sfr-form--submission .sfr-form__choices-post' )
34				}
35			};
36			sfr.els.loop = {
37				'container': $( '.sfr-content' ),
38			};
39		},
40
41		/**
42		 * Run on doc ready.
43		 */
44		on_ready: function() {
45			sfr.cache();
46			sfr.setup_vote_buttons();
47			sfr.setup_toggle_buttons();
48			sfr.setup_toggle_user_type();
49			sfr.setup_filter_selects();
50			sfr.setup_submission_form();
51			sfr.setup_image_uploader();
52			sfr.setup_image_gallery();
53		},
54
55		/**
56		 * Setup vote buttons.
57		 */
58		setup_vote_buttons: function() {
59			$( document ).on( 'click', sfr.vars.vote_button_selector, function() {
60				var $button = $( this );
61
62				if ( sfr.is_voting( $button ) ) {
63					return;
64				}
65
66				if ( sfr.has_user_voted( $button ) ) {
67					sfr.update_vote_count( $button, 'remove' );
68					return;
69				}
70
71				sfr.update_vote_count( $button, 'add' );
72			} );
73		},
74
75		/**
76		 * Update vote count.
77		 *
78		 * @key $button
79		 * @key type add|remove
80		 */
81		update_vote_count: function( $button, type ) {
82			var $button_text = $button.text(),
83				$button_class = $button.attr( 'class' );
84
85			sfr.add_button_status_class( $button, 'voting' );
86			$button.text( sfr_vars.il8n.voting + '...' );
87
88			var post_id = sfr.get_post_id( $button ),
89				$badge = $button.closest( '.sfr-vote-badge' ),
90				$votes_counter = $badge.find( '.sfr-vote-badge__count strong' ),
91				$votes_text = $badge.find( '.sfr-vote-badge__count span' ),
92				data = {
93					'action': 'sfr_update_vote_count',
94					'post_id': post_id,
95					'type': type,
96					'nonce': sfr_vars.nonce
97				};
98
99			$.post( sfr_vars.ajax_url, data, function( response ) {
100				if ( ! response.success ) {
101					sfr.display_notice( response.message, 'error' );
102					$button.attr( 'class', $button_class ).text( $button_text );
103					return;
104				}
105
106			if ( type === 'add' ) {
107				$button.text( sfr_vars.il8n.voted );
108				sfr.add_button_status_class( $button, 'voted' );
109				$button.attr( 'aria-pressed', 'true' );
110				$votes_counter.text( response.votes );
111				$votes_text.text( response.votes_wording );
112				return;
113			}
114
115			sfr.remove_button_status_classes( $button );
116			$button.text( sfr_vars.il8n.vote );
117			$button.attr( 'aria-pressed', 'false' );
118			$votes_counter.text( response.votes );
119			$votes_text.text( response.votes_wording );
120			} );
121		},
122
123		/**
124		 * Display notice.
125		 *
126		 * @key message
127		 * @key type
128		 */
129		display_notice: function( message, type ) {
130			alert( message );
131		},
132
133		/**
134		 * Get post ID from button.
135		 *
136		 * @key $button
137		 * @return {Number}
138		 */
139		get_post_id: function( $button ) {
140			return parseInt( $button.data( 'sfr-vote' ) );
141		},
142
143		/**
144		 * Has user voted?
145		 *
146		 * @key $button
147		 */
148		has_user_voted: function( $button ) {
149			return $button.hasClass( sfr.vars.button_status_classes.voted );
150		},
151
152		/**
153		 * Is voting in progress?
154		 *
155		 * @key $button
156		 */
157		is_voting: function( $button ) {
158			return $button.hasClass( sfr.vars.button_status_classes.voting );
159		},
160
161		/**
162		 * Add button status classes.
163		 *
164		 * @key $button
165		 * @key type
166		 */
167		add_button_status_class: function( $button, type ) {
168			sfr.remove_button_status_classes( $button );
169
170			$button.addClass( sfr.vars.button_status_classes[ type ] );
171		},
172
173		/**
174		 * Remove button status classes.
175		 *
176		 * @key $button
177		 */
178		remove_button_status_classes: function( $button ) {
179			$.each( sfr.vars.button_status_classes, function( index, status_class ) {
180				$button.removeClass( status_class );
181			} );
182		},
183
184		/**
185		 * Setup toggle buttons.
186		 */
187		setup_toggle_buttons: function() {
188			$( document ).on( 'click', sfr.vars.toggle_button_selector, function() {
189				var $button = $( this ),
190					$toggle = $( '.sfr-js-toggle-' + $button.data( 'sfr-toggle' ) );
191
192				$toggle.toggle();
193			} );
194		},
195
196		/**
197		 * Setup toggle user type.
198		 */
199		setup_toggle_user_type: function() {
200			$( document ).on( 'click', sfr.vars.toggle_user_type_selector, function() {
201				var $button = $( this ),
202					type = $button.data( 'sfr-toggle-submission-user-type' );
203
204				$( '[name="sfr-login-user-type"]' ).val( type );
205			} );
206		},
207
208		/**
209		 * Setup filter select redirects.
210		 */
211		setup_filter_selects: function() {
212			$( document ).on( 'change', '.sfr-filter-select[data-sfr-filter-redirect]', function() {
213				location.href = this.value;
214			} );
215		},
216
217		/**
218		 * Setup submission form.
219		 */
220		setup_submission_form: function() {
221			if ( sfr.els.submission_form.form.length <= 0 ) {
222				return;
223			}
224
225			sfr.els.submission_form.title.keypress( function( e ) {
226				if ( e.which === 13 ) {
227					e.preventDefault();
228				}
229			} );
230
231			var timeout_id = null;
232
233			sfr.els.submission_form.title.keyup( function( e ) {
234				clearTimeout( timeout_id );
235
236				timeout_id = setTimeout( function() {
237					sfr.search_feature_requests( e.target.value );
238				}, 500 );
239			} );
240
241			sfr.els.submission_form.choices.post.on( 'click', function() {
242				sfr.reveal_submission_form();
243				return false;
244			} );
245
246			$( '.sfr-js-clear-search-field' ).on( 'click', function() {
247				sfr.els.submission_form.title.val('').keyup();
248				$( this ).hide();
249			} );
250		},
251
252		/**
253		 * Search feature requests based on string.
254		 *
255		 * @key search
256		 */
257		search_feature_requests: function( search ) {
258			sfr.update_query_args( 'search', search );
259			sfr.toggle_loader( 'show' );
260
261			var data = {
262				'action': 'sfr_search_feature_requests',
263				'nonce': sfr_vars.nonce,
264				'paged': sfr_vars.paged,
265			};
266
267			$.extend( data, sfr.get_url_parameters( window.location.href ) );
268
269			// Add here so the characters aren't encoded when using $.extend()
270			data.search = search;
271
272			$.post( sfr_vars.ajax_url, data, function( response ) {
273				if ( ! response.success && ! response.html ) {
274					sfr.display_notice( response.message, 'error' );
275					sfr.toggle_loader( 'hide' );
276					return;
277				}
278
279				if ( ! response.success && search.length > 0 ) {
280					sfr.reveal_submission_form();
281				} else {
282					sfr.hide_submission_form();
283				}
284
285			sfr.els.loop.container.html( response.html );
286			sfr.replace_pagination( response.pagination );
287			sfr.toggle_filters( response );
288			sfr.toggle_choices( response );
289			sfr.toggle_loader( 'hide' );
290
291			// Announce search results to screen readers
292			var announcement = response.count > 0
293				? sfr_vars.il8n.search_results_found.replace( '%d', response.count )
294				: sfr_vars.il8n.no_search_results;
295			sfr.announce_to_screen_readers( announcement );
296		} );
297		},
298
299		/**
300		 * Update a URL query arg.
301		 *
302		 * @param key
303		 * @param value
304		 */
305		update_query_args: function( key, value ) {
306			key = encodeURIComponent( key );
307
308			var url = window.location.href,
309				params = sfr.get_url_parameters( url );
310
311			if ( value.length > 0 ) {
312				params[ key ] = value;
313			} else {
314				delete params[ key ];
315			}
316
317			url = url.split( '?' )[ 0 ];
318
319			if ( $.param( params ).length > 0 ) {
320				url += "?" + $.param( params );
321			}
322
323			window.history.pushState( { key: key, value: value }, document.title, url );
324		},
325
326		/**
327		 * Get URL parameters.
328		 *
329		 * @param url
330		 *
331		 * @return object
332		 */
333		get_url_parameters: function( url ) {
334			var result = {},
335				searchIndex = url.indexOf( "?" );
336
337			if ( searchIndex === - 1 ) {
338				return result;
339			}
340
341			var sPageURL = url.substring( searchIndex + 1 ),
342				sURLVariables = sPageURL.split( '&' );
343
344			for ( var i = 0; i < sURLVariables.length; i ++ ) {
345				var sParameterName = sURLVariables[ i ].split( '=' );
346				result[ sParameterName[ 0 ] ] = sParameterName[ 1 ];
347			}
348
349			return result;
350		},
351
352		/**
353		 * Replace pagination.
354		 *
355		 * @param pagination
356		 */
357		replace_pagination: function( pagination ) {
358			if ( $( '.sfr-pagination' ).length <= 0 ) {
359				return;
360			}
361
362			$( '.sfr-pagination' ).replaceWith( pagination );
363		},
364
365		/**
366		 * Toggle filters.
367		 *
368		 * @param search
369		 */
370		toggle_filters: function( response ) {
371			sfr.els.filters.hide();
372
373			if ( response.search.length <= 0 ) {
374				sfr.els.filters.show();
375			}
376		},
377
378		/**
379		 * Toggle choices.
380		 *
381		 * @param response
382		 */
383		toggle_choices: function( response ) {
384			sfr.els.submission_form.choices.vote.show();
385			sfr.els.submission_form.choices.or.show();
386			sfr.els.submission_form.choices.post.show();
387			sfr.els.submission_form.choices.count.text( response.count );
388			sfr.els.submission_form.choices.container.show();
389
390			if ( response.count <= 0 || response.search.length <= 0 ) {
391				sfr.els.submission_form.choices.container.hide();
392			}
393		},
394
395		/**
396		 * Reveal submission form.
397		 */
398		reveal_submission_form: function() {
399			sfr.els.submission_form.choices.or.hide();
400			sfr.els.submission_form.choices.post.hide();
401			sfr.els.submission_form.reveal.show();
402		},
403
404		/**
405		 * Hide submission form.
406		 */
407		hide_submission_form: function() {
408			sfr.els.submission_form.reveal.hide();
409		},
410
411		/**
412		 * Toggle loader.
413		 *
414		 * @param visiblity
415		 */
416		toggle_loader: function( visiblity ) {
417			if ( typeof visiblity === 'undefined' ) {
418				return;
419			}
420
421			if ( visiblity === 'show' ) {
422				sfr.els.submission_form.clear.hide();
423				sfr.els.submission_form.loader.show();
424			} else {
425				sfr.els.submission_form.loader.hide();
426
427				if ( sfr.els.submission_form.title.val().length > 0 ) {
428					sfr.els.submission_form.clear.show();
429				}
430			}
431		},
432
433		/**
434		 * Image upload handler
435		 */
436		setup_image_uploader: function(){
437			if ( typeof Dropzone !== 'undefined' ) {
438				var uploader = $('#sfr-image-uploader');
439
440				if( !uploader.length ){
441					return;
442				}
443
444				var submit_btn = sfr.els.submission_form.form.find('.sfr-form__button');
445
446				uploader.dropzone({
447					url: sfr_vars.ajax_url,
448					params: {
449						action: 'sfr_set_feature_request_attachments',
450						nonce: sfr_vars.attachment_nonce
451					},
452					init: function(){
453
454						sfr.check_for_already_submitted_attachments( uploader, this );
455
456						this.on('successmultiple', function(data, response){
457							if( response.success && response.data.attachment_ids ){
458
459								uploader.find('[name="attachment_ids[]"]').remove();
460
461								$.each( response.data.attachment_ids, function( i, id ){
462									uploader.append( '<input type="hidden" name="attachment_ids[]" value="'+id+'" />' );
463								});
464							}
465						});
466
467						this.on('processingmultiple', function(){
468							submit_btn.prop('disabled', true);
469						});
470
471						this.on('successmultiple', function(){
472							submit_btn.removeAttr('disabled');
473							submit_btn.trigger('click');
474						});
475
476						this.on('removedfile', function(file){
477							if( ! file.attachment_id ){
478								return;
479							}
480
481							$('[name="attachment_ids[]"][value="'+file.attachment_id+'"]').remove();
482						});
483					},
484					acceptedFiles: 'image/*',
485					uploadMultiple: true,
486					paramName: 'attachments',
487					autoProcessQueue: false,
488					addRemoveLinks: true,
489					hiddenInputContainer: uploader[0],
490					maxFiles: 5,
491					maxFilesize: sfr_vars['attachments_max_filesize'],
492					parallelUploads: 5
493				});
494
495				var dropzone = Dropzone.forElement( uploader.get(0) );
496
497				sfr.els.submission_form.form.on('submit', function(e){
498
499					if( !dropzone.getQueuedFiles().length ){
500						return;
501					}
502
503					e.preventDefault();
504					dropzone.processQueue();
505				});
506			}
507		},
508
509		/**
510		 * Initiate already added attachments
511		 *
512		 * @param {jQuery} el jquery element of attachments container
513		 * @param {Dropzone} dropzone Dropzone instance
514		 */
515		check_for_already_submitted_attachments: function( el, dropzone ){
516			if ( typeof Dropzone !== 'undefined' ) {
517				var attachment_ids = el.find( '[name="attachment_ids[]"]' );
518
519				if( ! attachment_ids.length ){
520					return;
521				}
522
523				var values = attachment_ids.toArray().map( function( input ){ return $( input ).val(); });
524
525				$.get({
526					url: sfr_vars.ajax_url,
527					data: {
528						action: 'sfr_get_feature_request_attachments',
529						nonce: sfr_vars.attachment_nonce,
530						attachment_ids: values
531					},
532					success: function(response){
533						if( response.success && response.data.attachments ){
534							$.each( response.data.attachments, function( i, attachment ){
535
536								var mockFile = { name: attachment.name, size: attachment.size, attachment_id: i };
537
538								dropzone.emit("addedfile", mockFile);
539								dropzone.options.thumbnail.call(dropzone, mockFile, attachment.url);
540								dropzone.emit("complete", mockFile);
541							});
542						}
543					}
544				});
545			}
546		},
547
548		/**
549		 * Image gallery on frontend
550		 */
551		setup_image_gallery: function(){
552
553			var wrapper 	= $('.sfr-attachments');
554
555			if( ! wrapper.length ){
556				return;
557			}
558
559			wrapper.on('click', '.sfr-attachment', function(){
560
561				var attachments = $(this).parent().find('.sfr-attachment');
562				var images 		= [];
563
564				attachments.each(function(){
565
566					var img = $(this).find('img');
567					if( img ){
568						images.push({
569							src: img.data('src'),
570							w: img.data('width'),
571							h: img.data('height'),
572						});
573					}
574				});
575
576			var gallery = new PhotoSwipe( $('.pswp').get(0), PhotoSwipeUI_Default, images, {index: 0});
577			gallery.init();
578		});
579	},
580
581	/**
582	 * Announce message to screen readers
583	 *
584	 * @param message
585	 */
586	announce_to_screen_readers: function( message ) {
587		var $announcer = $( '.sfr-sr-announcer' );
588
589		if ( $announcer.length === 0 ) {
590			$announcer = $( '<div class="sfr-sr-announcer" aria-live="assertive" aria-atomic="true"></div>' );
591			$( 'body' ).append( $announcer );
592		}
593
594		$announcer.text( message );
595		setTimeout( function() {
596			$announcer.text( '' );
597		}, 1000 );
598	}
599};
600
601	if( ( typeof Dropzone !== 'undefined' && Dropzone !== null ) && sfr_vars.allow_attachments ) {
602    	Dropzone.autoDiscover = false;
603	}
604	$( document ).ready( sfr.on_ready );
605}( jQuery, document ));

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.