PageSourceSearch

https://techwhirl.com/wp-content/plugins/wp-views/embedded/res/js/wpv-pagination-embedded.js?ver=2.7.4

js techwhirl.com collected 2026-09-24 08:57:05 UTC 150,300 bytes, 4,159 lines download raw bytes

1var WPViews = WPViews || {};
2
3var wpv_stop_rollover = {};
4window.wpvPaginationAjaxLoaded = {};
5window.wpvPaginationAnimationFinished = {};
6window.wpvPaginationQueue = {};
7
8// ------------------------------------
9// Clone
10// ------------------------------------
11
12// Textarea and select clone() bug workaround | Spencer Tipping
13// Licensed under the terms of the MIT source code license
14// Motivation.
15// jQuery's clone() method works in most cases, but it fails to copy the value of textareas and select elements. This patch replaces jQuery's clone() method with a wrapper that fills in the
16// values after the fact.
17// An interesting error case submitted by Piotr Przybyl: If two <select> options had the same value, the clone() method would select the wrong one in the cloned box. The fix, suggested by Piotr
18// and implemented here, is to use the selectedIndex property on the <select> box itself rather than relying on jQuery's value-based val().
19;( function() {
20    jQuery.fn.wpv_clone = function() {
21        var result = jQuery.fn.clone.apply( this, arguments ),
22		my_textareas = this.find( 'textarea' ).add( this.filter( 'textarea' ) ),
23		result_textareas = result.find( 'textarea' ).add( result.filter( 'textarea' ) ),
24		my_selects = this.find( 'select' ).add( this.filter( 'select' ) ),
25		result_selects = result.find( 'select' ).add( result.filter( 'select' ) );
26		for ( var i = 0, l = my_textareas.length; i < l; ++i ) {
27			jQuery( result_textareas[i] ).val( jQuery( my_textareas[i] ).val() );
28		}
29		for ( var i = 0, l = my_selects.length; i < l; ++i ) {
30			for ( var j = 0, m = my_selects[i].options.length; j < m; ++j ) {
31				if ( my_selects[i].options[j].selected === true ) {
32					result_selects[i].options[j].selected = true;
33				} else {
34					result_selects[i].options[j].selected = false;
35				}
36			}
37		}
38        return result;
39    };
40})();
41
42WPViews.ViewFrontendUtils = function( $ ) {
43
44	// ------------------------------------
45	// Constants and variables
46	// ------------------------------------
47
48	var self = this;
49
50	self.datepicker_style_id		= 'js-toolset-datepicker-style';
51	self.is_datepicker_style_loaded	= false;
52
53	// ------------------------------------
54	// Methods
55	// ------------------------------------
56
57	self.just_return = function() {
58		return;
59	};
60
61	/**
62	* extract_url_query_parameters
63	*
64	* Extracts parameters from a query string, managing arrays, and returns an array of pairs key => value
65	*
66	* @param string query_string
67	*
68	* @return array
69	*
70	* @note ##URLARRAYVALHACK## is a hacky constant
71	*
72	* @uses decodeURIComponent
73	*
74	* @since 1.9
75	*/
76
77	self.extract_url_query_parameters = function( query_string ) {
78		var query_string_pairs = {};
79		if ( query_string == "" ) {
80			return query_string_pairs;
81		}
82		var query_string_split = query_string.split( '&' ),
83		query_string_split_length = query_string_split.length;
84		for ( var i = 0; i < query_string_split_length; ++i ) {
85			var qs_part = query_string_split[i].split( '=' );
86			if ( qs_part.length != 2 ) {
87				continue;
88			};
89			var thiz_key = qs_part[0],
90			thiz_val = decodeURIComponent( qs_part[1].replace( /\+/g, " " ) );
91			// Adjust thiz_key to work with POSTed arrays
92			thiz_key = thiz_key.replace( /(\[)\d?(\])/, "" );
93			thiz_key = thiz_key.replace( "[]", "" );// Just in case
94			thiz_key = thiz_key.replace( /(%5B)\d?(%5D)/, "" );
95			thiz_key = thiz_key.replace( "%5B%5D", "" );// Just in case
96			thiz_key = thiz_key.replace( /(%255B)\d?(%255D)/, "" );
97			thiz_key = thiz_key.replace( "%255B%255D", "" );// Just in case
98			if ( query_string_pairs.hasOwnProperty( thiz_key ) ) {
99				if ( query_string_pairs[thiz_key] != thiz_val ) {
100					// @hack alert!! We can not avoid using this :-(
101					query_string_pairs[thiz_key] += '##URLARRAYVALHACK##' + thiz_val;
102				} else {
103					query_string_pairs[thiz_key] = thiz_val;
104				}
105			} else {
106				query_string_pairs[thiz_key] = thiz_val;
107			}
108		}
109		return query_string_pairs;
110	};
111
112	/**
113	* get_extra_url_query_parameters
114	*
115	* Gets the current URL query parameters, but only those that do not belong to the current form already.
116	*
117	* @note Arrays are returned on a single key with values on a single str
117ing, separated by the ##URLARRAYVALHACK## placeholder. We might review that later
118	* @note Since 2.2, force include URL parameters that belong to form controls when the form does not use dependency nor AJAX results.
119	* 		As the form should be posted with page reload in this case, and we are not performing a dependency call, URL parameters win over optios in the form
120	* 		and have been not recorded yet in the AJAX method manager.
121	*
122	* @return array
123	*
124	* @uses self.extract_url_query_parameters
125	*
126	* @since 2.1
127	*/
128
129	self.get_extra_url_query_parameters_by_form = function( form ) {
130		var query_string = self.extract_url_query_parameters( window.location.search.substr( 1 ) ),
131		data = {},
132		force_from_form = ! ( form.hasClass( 'js-wpv-dps-enabled' )
133			|| form.hasClass( 'js-wpv-ajax-results-enabled' )
134			|| form.hasClass( 'js-wpv-ajax-results-submit-enabled' )
135		);
136		for ( var prop in query_string ) {
137			var $matchExisting = form.find( '[name="' + prop + '"], [name="' + prop + '\\[\\]"]' );
138			if (
139				query_string.hasOwnProperty( prop )
140				&& ! data.hasOwnProperty( prop )
141				&& (
142					force_from_form
143					|| $matchExisting.length === 0
144					|| $matchExisting
145						.filter( '.js-wpv-extra-url-param' )
146						.length > 0
147				)
148			) {
149				data[ prop ] = query_string[ prop ];
150			}
151		}
152		return data;
153	};
154
155	/**
156	* set_extra_url_query_parameters
157	*
158	* Forces the current URL query parameters in a form, but only those that do not belong to the form already.
159	*
160	* @uses self.extract_url_query_parameters
161	*
162	* @since 2.1
163	*/
164
165	self.set_extra_url_query_parameters_by_form = function( form ) {
166		var extra = self.get_extra_url_query_parameters_by_form( form );
167		$.each( extra, function( key, value ) {
168			if ( form.find( '[name="' + key + '"], [name="' + key + '\\[\\]"]' ).length === 0 ) {
169				// @hack alert!! WE can not avoid this :-(
170				var pieces = value.split( '##URLARRAYVALHACK##' ),
171				pieces_length = pieces.length;
172				if ( pieces_length < 2 ) {
173					$( '<input>' ).attr({
174						type: 'hidden',
175						name: key,
176						value: value,
177						class: 'js-wpv-extra-url-param'
178					})
179					.appendTo( form );
180				} else {
181					for ( var iter = 0; iter < pieces_length; iter++ ) {
182						$( '<input>' ).attr({
183							type: 'hidden',
184							name: key + "[]",
185							value: pieces[iter],
186							class: 'js-wpv-extra-url-param'
187						})
188						.appendTo( form );
189					}
190				}
191			}
192		});
193	};
194
195	/**
196	 * Dynamically load the Toolset datepicker style, only when needed.
197	 *
198	 * @note we should do the same with mediaelement and wp-mediaelement
199	 * @note the handle for this used to be wptoolset-field-datepicker
200	 *
201	 * @since 2.3.0
202	 */
203
204	self.maybe_load_datepicker_style = function() {
205
206		if ( ! self.is_datepicker_style_loaded ) {
207
208			if ( document.getElementById( self.datepicker_style_id ) ) {
209
210				self.is_datepicker_style_loaded = true;
211
212			} else {
213
214				var head	= document.getElementsByTagName( 'head' )[0],
215					link	= document.createElement( 'link' );
216
217				link.id 	= self.datepicker_style_id;
218				link.rel	= 'stylesheet';
219				link.type	= 'text/css';
220				link.href	= wpv_pagination_local.datepicker_style_url;
221				link.media	= 'all';
222				head.appendChild( link );
223
224				self.is_datepicker_style_loaded = true;
225
226			}
227
228		}
229
230	};
231
232	/**
233	 * Destroy all initialized datepickers in the page, before doing changes and initializing them again.
234	 *
235	 * @since 2.3.0
236	 */
237
238	self.destroy_frontend_datepicker = function() {
239
240		$( ".js-wpv-frontend-datepicker.js-wpv-frontend-datepicker-inited" )
241			.removeClass( 'js-wpv-frontend-datepicker-inited' )
242			.datepicker( "destroy" );
243
244	};
245
246	/**
247	* render_frontend_datepicker
248	*
249	* Adds a datepicker to a selector but only if it has not been added before.
250	*
251	* Fired on document.ready, after AJAX pagination and after AJAX parametric search events.
252	*
253	* @since 1.9
254	*/
255
256	self.render_frontend_datepicker = function() {
257		$( '.js-wpv-frontend-datepicker:not(.js-wpv-frontend-datepicker-inited)' ).each( function() {
258			self.maybe_load_datepicker_style();
259			var thiz = $( this );
260			thiz
261				.addClass( 'js-wpv-frontend-datepicker-inited' )
262				.datepicker({
263					onSelect: function( dateText, inst ) {
264						var url_param = thiz.data( 'param' ),
265						data = 'date=' + dateText,
266						form = thiz.closest( 'form' );
267						data += '&date-format=' + $( '.js-wpv-date-param-' + url_param + '-format' ).val();
268						data += '&action=wpv_format_date';
269						$.post( wpv_pagination_local.front_ajaxurl, data, function( response ) {
270							response = $.parseJSON( response );
271							form.find('.js-wpv-date-param-' + url_param ).html( response['display'] );
272							form.find('.js-wpv-date-front-end-clear-' + url_param ).show();
273							form.find('.js-wpv-date-param-' + url_param + '-value' ).val( response['timestamp'] ).trigger( 'change' );
274						});
275					},
276					dateFormat: 'ddmmyy',
277					minDate: wpv_pagination_local.datepicker_min_date,
278					maxDate: wpv_pagination_local.datepicker_max_date,
279					showOn: "button",
280					buttonImage: wpv_pagination_local.calendar_image,
281					buttonText: wpv_pagination_local.calendar_text,
282					buttonImageOnly: true,
283					changeMonth: true,
284					changeYear: true
285				});
286		});
287	};
288
289	/**
290	* clone_form
291	*
292	* Clones a form using the fixed clone() method that covers select and textarea elements
293	*
294	* @param object fil
295	* @param array targets
296	*
297	* @since 1.9
298	*/
299
300	self.clone_form = function( fil, targets ) {
301		var cloned = fil.wpv_clone();
302		targets.each( function() {
303			$( this ).replaceWith( cloned );
304		});
305	};
306
307	/**
308	* render_frontend_media_shortcodes
309	*
310	* Render the WordPress media players for items inside a container.
311	*
312	* @param object container
313	*
314	* @since 1.9
315	*/
316
317	self.render_frontend_media_shortcodes = function( container ) {
318		container.find( '.wp-audio-shortcode, .wp-video-shortcode' ).each( function() {
319			var thiz = $( this );
320			thiz.mediaelementplayer();
321		});
322		container.find( '.wp-playlist' ).each( function() {
323			var thiz = $( this );
324			return new WPPlaylistView({ el: this });
325		});
326	};
327
328	/**
329	* get_form_element_type
330	*
331	* Get a form element type, be it text, radio, checkbox, textarea or select
332	*
333	* @param selector	A jQuery selector object
334	*
335	* @return string	'text'|'radio'|'checkbox'|'textarea'|'select'|empty if selector is empty
336	*
337	* @since 2.2
338	*/
339
340	self.get_form_element_type = function( selector ) {
341		if ( selector.length > 0 ) {
342			return selector[0].tagName == "INPUT" ? selector[0].type.toLowerCase() : selector[0].tagName.toLowerCase();
343		} else {
344			return '';
345		}
346	}
347
348	self.get_hidden_item_width = function( item ) {
349		var item_clone = item
350			.clone()
351				.css({
352					'display': 'block',
353					'visibility': 'visible',
354					'position': 'absolute',
355					'z-index': '-99999',
356					'left': '99999999px',
357					'top': '0px',
358					'white-space': 'nowrap'
359				})
360				.appendTo( 'body' ),
361			width = item_clone.outerWidth();
362		item_clone.remove();
363		return width;
364	};
365
366	// ------------------------------------
367	// Get updated results
368	// ------------------------------------
369
370	/**
371	* get_updated_query_results
372	*
373	* Shared method for paginating Views and WordPress Archives, and also for parametric search on both.
374	* Returns a promise so you can operate on the response at will.
375	*
376	* Note that it applis the current form regardless it being submitted or not.
377	* @todo we might want to avoid adding that data when the form contains unsubmitted changes.
378	*
379	* @param view_number	The object hash
380	* @param page			The page that we want to get
381	* @param form			The form to track changes against
382	* @param expect			'form'|'full'|'both' Whether to return the full View, just the form, or both
383	*
384	* @since 2.1
385	*/
386
387	self.get_updated_query_results = function( view_number, page, form, expect ) {
388		var data				= {},
389		sort					= {},
390		environment				= {},
391		search					= {},
392		extra					= {}.
393		attributes				= {},
394		lang					= wpv_pagination_local.wpmlLang,
395		parametric_data			= form.data( 'parametric' );
396
397		sort = WPViews.view_sorting.get_sort_data( view_number, form );
398
399		if ( parametric_data['environment'].current_post_id > 0 ) {
400			environment['wpv_aux_current_post_id'] = parametric_data['environment'].current_post_id;
401		}
402		if ( parametric_data['environment'].parent_post_id > 0 ) {
403			environment['wpv_aux_parent_post_id'] = parametric_data['environment'].parent_post_id;
404		}
405		if ( parametric_data['environment'].parent_term_id > 0 ) {
406			environment['wpv_aux_parent_term_id'] = parametric_data['environment'].parent_term_id;
407		}
408		if ( parametric_data['environment'].parent_user_id > 0 ) {
409			environment['wpv_aux_parent_user_id'] = parametric_data['environment'].parent_user_id;
410		}
411
412		environment['archive'] = parametric_data['environment'].archive;
413
414		if ( form.find( '.js-wpv-post-relationship-update' ).length ) {
415			search['dps_pr'] = form.find( '.js-wpv-post-relationship-update' ).serializeArray();
416		}
417		if (
418			form.hasClass( 'js-wpv-dps-enabled' )
419			|| form.hasClass( 'js-wpv-ajax-results-enabled' )
420			|| form.hasClass( 'js-wpv-ajax-results-submit-enabled' )
421		) {
422			search['dps_general'] = form.find( '.js-wpv-filter-trigger, .js-wpv-filter-trigger-delayed' ).serializeArray();
423		}
424
425		attributes = parametric_data['attributes'];
426
427		extra = self.get_extra_url_query_parameters_by_form( form, true );
428
429		data = {
430			'view_number':	view_number,
431			page:			page,
432			sort:			sort,
433			attributes:		attributes,
434			environment:	environment,
435			search:			search,
436			extra:			extra,
437			expect:			expect
438		};
439
440		if ( lang ) {
441			data['lang'] = lang;
442		}
443
444		switch ( parametric_data.query ) {
445			case 'archive':
446				data['action']	= 'wpv_get_archive_query_results';
447				data['loop']	= parametric_data.loop;
448				break;
449			default:
450				data['action']	= 'wpv_get_view_query_results';
451				data['id']		= parametric_data.id;
452				if ( form.attr( 'data-targetid' ) ) {
453					data['target_id'] = form.data( 'targetid' );
454				} else if ( $( '.js-wpv-form-only.js-wpv-filter-form-' + view_number ).length > 0 ) {
455					data['target_id'] = $( '.js-wpv-form-only.js-wpv-filter-form-' + view_number ).data( 'targetid' );
456				}
457				data['wpv_view_widget_id']	= parametric_data['widget_id'];
458				break;
459		}
460		return $.ajax({
461			type:		"POST",
462			dataType:	"json",
463			url:		wpv_pagination_local.front_ajaxurl,
464			data:		data
465		});
466	};
467
468    /**
469     * Add, update or remove query string argument.
470     *
471     * @param 	key		{string} 	Argument name
472     * @param 	value	{string} 	Argument value. Not supplying a value will remove the parameter, supplying one will
473     *     							add/update the argument.
474     * @param 	url		{string} 	The URL. If no URL is supplied, it will be grabbed from window.location
475     * @return	string				The updated URL.
476	 * @link http://stackoverflow.com/a/11654596/3191395
477     *
478     * @since 2.3
479     */
480    self.updateUrlQuery = function(key, value, url) {
481        if(!url) {
482            url = window.location.href;
483        }
484        var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi");
485        var hash;
486        if (re.test(url)) {
487            if (typeof value !== 'undefined' && value !== null) {
488                return url.replace(re, '$1' + key + "=" + value + '$2$3');
489            } else {
490                hash = url.split('#');
491                url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
492                if(typeof hash[1] !== 'undefined' && hash[1] !== null) {
493                    url += '#' + hash[1];
494                }
495                return url;
496            }
497        } else {
498            if (typeof value !== 'undefined' && value !== null) {
499                var separator = url.indexOf('?') !== -1 ? '&' : '?';
500                hash = url.split('#');
501                url = hash[0] + separator + key + '=' + value;
502                if (typeof hash[1] !== 'undefined' && hash[1] !== null) {
503                    url += '#' + hash[1];
504                }
505                return url;
506            } else {
507                return url;
508            }
509        }
510    };
511
512	// ------------------------------------
513	// Events
514	// ------------------------------------
515
516	/**
517	* Window resize event
518	*
519	* Make Views layouts responsive
520	*
521	* @since 1.9
522	* @since 1.11 added debounce
523	*/
524
525	$( window ).on( 'resize', _.debounce(
526		function() {
527			$( '.js-wpv-layout-responsive' ).each( function() {
528				$( this ).css( 'width', '' );
529			})
530			.promise()
531			.done( function() {
532				$( document ).trigger( 'js_event_wpv_layout_responsive_resize_completed' );
533			});
534		},
535		wpv_pagination_local.resize_debounce_tolerance
536	));
537
538	// ------------------------------------
539	// Init
540	// ------------------------------------
541
542	self.init = function() {
543		self.render_frontend_datepicker();
544	};
545
546	self.init();
547
548};
549
550WPViews.ViewSorting = function( $ ) {
551
552	var self = this;
553
554	/**
555	 * Get the sort data for a given View.
556	 *
557	 * @note If the current orderby option does not have an orderby_as option and does not match the stored option,
558	 * we remove (we empty) that value so it gets sorted as a native, string value.
559	 *
560	 * @since 2.3.0
561	 */
562	self.get_sort_data = function( view_number, form ) {
563		var sort			= {},
564		parametric_data		= form.data( 'parametric' );
565
566		sort['wpv_sort_orderby']	= parametric_data['sort']['orderby'];
567		sort['wpv_sort_order']		= parametric_data['sort']['order'];
568		sort['wpv_sort_orderby_as']	= parametric_data['sort']['orderby_as'];
569		sort['wpv_sort_orderby_second']	= parametric_data['sort']['orderby_second'];
570		sort['wpv_sort_order_second']	= parametric_data['sort']['order_second'];
571
572		if ( form.find( '.js-wpv-sort-control-orderby' ).length > 0 ) {
573			var orderby_type = WPViews.view_frontend_utils.get_form_element_type( form.find( '.js-wpv-sort-control-orderby' ) );
574			switch ( orderby_type ) {
575				case 'select':
576					sort['wpv_sort_orderby'] = form.find( '.js-wpv-sort-control-orderby' ).val();
577					if ( form.find( '.js-wpv-sort-control-orderby option:selected' ).data( 'orderbyas' ) ) {
578						sort['wpv_sort_orderby_as'] = form.find( '.js-wpv-sort-control-orderby option:selected' ).data( 'orderbyas' );
579					} else if ( sort['wpv_sort_orderby'] != parametric_data['sort']['orderby'] ) {
580						sort['wpv_sort_orderby_as'] = '';
581					}
582					break;
583				case 'radio':
584					sort['wpv_sort_orderby'] = form.find( '.js-wpv-sort-control-orderby:checked' ).val();
585					if ( form.find( '.js-wpv-sort-control-orderby:checked' ).data( 'orderbyas' ) ) {
586						sort['wpv_sort_orderby_as'] = form.find( '.js-wpv-sort-control-orderby:checked' ).data( 'orderbyas' );
587					} else if ( sort['wpv_sort_orderby'] != parametric_data['sort']['orderby'] ) {
588						sort['wpv_sort_orderby_as'] = '';
589					}
590					break;
591				case 'text':
592				case 'hidden':
593					sort['wpv_sort_orderby'] = form.find( '.js-wpv-sort-control-orderby' ).val();
594					if ( form.find( '.js-wpv-sort-control-orderby' ).data( 'orderbyas' ) ) {
595						sort['wpv_sort_orderby_as'] = form.find( '.js-wpv-sort-control-orderby' ).data( 'orderbyas' );
596					} else if ( sort['wpv_sort_orderby'] != parametric_data['sort']['orderby'] ) {
597						sort['wpv_sort_orderby_as'] = '';
598					}
599					break;
600			}
601		}
602
603		if ( form.find( '.js-wpv-sort-control-order' ).length > 0 ) {
604			var order_type = WPViews.view_frontend_utils.get_form_element_type( form.find( '.js-wpv-sort-control-order' ) );
605			switch ( order_type ) {
606				case 'select':
607					sort['wpv_sort_order'] = form.find( '.js-wpv-sort-control-order' ).val();
608					break;
609				case 'radio':
610					sort['wpv_sort_order'] = form.find( '.js-wpv-sort-control-order:checked' ).val();
611					break;
612				case 'text':
613				case 'hidden':
614					sort['wpv_sort_order'] =  form.find( '.js-wpv-sort-control-order' ).val();
615					break;
616			}
617		}
618
619        if (
620        	'' === sort['wpv_sort_order']
621			&& form.find( '.js-wpv-sort-control-orderby' ).length > 0
622		) {
623            var order_type = WPViews.view_frontend_utils.get_form_element_type( form.find( '.js-wpv-sort-control-orderby' ) );
624            switch ( order_type ) {
625                case 'select':
626                    sort['wpv_sort_order'] = form.find( '.js-wpv-sort-control-orderby option:selected' ).data( 'forceorder' );
627                    break;
628                case 'radio':
629                    sort['wpv_sort_order'] = form.find( '.js-wpv-sort-control-orderby:checked' ).data( 'forceorder' );
630                    break;
631                case 'text':
632                case 'hidden':
633                    sort['wpv_sort_order'] =  form.find( '.js-wpv-sort-control-orderby' ).data( 'forceorder' );
634                    break;
635            }
636        }
637
638		return sort;
639	}
640
641	/**
642	 * Sync the sorting controls used inside and outside of the View form.
643	 *
644	 * @since 2.3.0
645	 * @since 2.3.1 Split into set_sort_data_inside_form and set_sort_data_outside_form.
646	 */
647	self.set_sort_data = function( form, sort ) {
648		self.set_sort_data_inside_form( form, sort )
649			.set_sort_data_outside_form( form, sort );
650		return self;
651	};
652
653    /**
654     * Sync the sorting controls used inside and outside of the View form for the case of paginating while the View is
655	 * already sorted.
656     *
657     * @since 2.5.2
658     */
659	self.set_sort_data_for_pagination = function ( form ) {
660        var sort = {},
661        parametric_data	= form.data( 'parametric' );;
662
663        if ( form.find( '.js-wpv-sort-control-orderby' ).length > 0 ) {
664            var orderby_type = WPViews.view_frontend_utils.get_form_element_type( form.find( '.js-wpv-sort-control-orderby' ) );
665            switch ( orderby_type ) {
666                case 'select':
667                    sort['orderby'] = form.find( '.js-wpv-sort-control-orderby' ).val();
668                    if ( form.find( '.js-wpv-sort-control-orderby option:selected' ).data( 'orderbyas' ) ) {
669                        sort['orderby_as'] = form.find( '.js-wpv-sort-control-orderby option:selected' ).data( 'orderbyas' );
670                    } else if ( sort['orderby'] != parametric_data['sort']['orderby'] ) {
671                        sort['orderby_as'] = '';
672                    }
673
674                    if ( form.find( '.js-wpv-sort-control-orderby option:selected' ).data( 'forceorder' ) ) {
675                        sort['order'] = form.find( '.js-wpv-sort-control-orderby option:selected' ).data( 'forceorder' );
676                    }
677                    break;
678                case 'radio':
679                    sort['orderby'] = form.find( '.js-wpv-sort-control-orderby:checked' ).val();
680                    if ( form.find( '.js-wpv-sort-control-orderby:checked' ).data( 'orderbyas' ) ) {
681                        sort['orderby_as'] = form.find( '.js-wpv-sort-control-orderby:checked' ).data( 'orderbyas' );
682                    } else if ( sort['orderby'] != parametric_data['sort']['orderby'] ) {
683                        sort['orderby_as'] = '';
684                    }
685
686                    if ( form.find( '.js-wpv-sort-control-orderby:checked' ).data( 'forceorder' ) ) {
687                        sort['order'] = form.find( '.js-wpv-sort-control-orderby:checked' ).data( 'forceorder' );
688                    }
689                    break;
690                case 'text':
691                case 'hidden':
692                    sort['orderby'] = form.find( '.js-wpv-sort-control-orderby' ).val();
693                    if ( form.find( '.js-wpv-sort-control-orderby' ).data( 'orderbyas' ) ) {
694                        sort['orderby_as'] = form.find( '.js-wpv-sort-control-orderby' ).data( 'orderbyas' );
695                    } else if ( sort['orderby'] != parametric_data['sort']['orderby'] ) {
696                        sort['orderby_as'] = '';
697                    }
698
699                    if ( form.find( '.js-wpv-sort-control-orderby' ).data( 'forceorder' ) ) {
700                        sort['order'] = form.find( '.js-wpv-sort-control-orderby' ).data( 'forceorder' );
701                    }
702                    break;
703            }
704        }
705        self.set_sort_data( form, sort );
706    };
707
708	/**
709	 * Set the sort data for orderby controls as select dropdowns.
710	 *
711	 * @since 2.3.1
712	 */
713	self.set_sort_data_orderby_select = function( orderby_control, sort ) {
714		if ( orderby_control.find( 'option[value="' + sort['orderby'] + '"]' ).length == 0 ) {
715			orderby_control.append( $('<option>', {
716				value:	sort['orderby'],
717				text:	sort['orderby']
718			}));
719		}
720		orderby_control.val( sort['orderby'] );
721		if ( _.has( sort, 'orderby_as' ) ) {
722			orderby_control.find( 'option:selected' )
723				.data( 'orderbyas', sort['orderby_as'] )
724				.attr( 'data-orderbyas', sort['orderby_as'] );
725		}
726		return self;
727	};
728
729	/**
730	 * Set the sort data for orderby controls as text or hidden input
730s.
731	 *
732	 * @since 2.3.1
733	 */
734	self.set_sort_data_orderby_text = function( orderby_control, sort ) {
735		orderby_control.val( sort['orderby'] );
736		if ( _.has( sort, 'orderby_as' ) ) {
737			orderby_control
738				.data( 'orderbyas', sort['orderby_as'] )
739				.attr( 'data-orderbyas', sort['orderby_as'] );
740		}
741		return self;
742	};
743
744	/**
745	 * Set the sort data for orderby controls as radio inputs.
746	 *
747	 * @note Create a radio input if it does not exist one for the right orderby value.
748	 *
749	 * @since 2.3.1
750	 */
751	self.set_sort_data_orderby_radio = function( orderby_control, sort, form ) {
752		if ( orderby_control.filter( '[value="' + sort['orderby'] + '"]' ).length == 0 ) {
753			form.append( $('<input>', {
754				type:		'radio',
755				"class":	'js-wpv-sort-control-orderby',
756				name:		'wpv_sort_orderby',
757				value:		sort['orderby']
758			}));
759		}
760		form.find( '.js-wpv-sort-control-orderby[value="' +  sort['orderby'] + '"]' ).prop( 'checked', true );
761		if ( _.has( sort, 'orderby_as' ) ) {
762			form.find( '.js-wpv-sort-control-orderby:checked' )
763				.data( 'orderbyas', sort['orderby_as'] )
764				.attr( 'data-orderbyas', sort['orderby_as'] );
765		}
766		return self;
767	};
768
769	/**
770	 * Set the sort data for orderby controls as radio inputs.
771	 *
772	 * @note Uncheck all radio inputs if it does not exist one for the right orderby value.
773	 *
774	 * @since 2.3.1
775	 */
776	self.maybe_set_sort_data_orderby_radio = function( orderby_control, sort ) {
777		if ( orderby_control.filter( '[value="' + sort['orderby'] + '"]' ).length == 0 ) {
778			orderby_control.prop( 'checked', false );
779		} else {
780			orderby_control.filter( '[value="' +  sort['orderby'] + '"]' ).prop( 'checked', true );
781			if ( _.has( sort, 'orderby_as' ) ) {
782				orderby_control.filter( ':checked' )
783					.data( 'orderbyas', sort['orderby_as'] )
784					.attr( 'data-orderbyas', sort['orderby_as'] );
785			}
786		}
787		return self;
788	};
789
790	/**
791	 * Set the sort data for orderby controls as lists.
792	 *
793	 * @since 2.3.1
794	 */
795	self.set_sort_data_orderby_list = function( orderby_control, sort ) {
796		var orderby_control_to_set = orderby_control.find( '.js-wpv-sort-list-orderby[data-orderby="' + sort['orderby'] + '"]' );
797		if ( orderby_control_to_set.length == 0 ) {
798
799		} else {
800			if ( ! orderby_control_to_set.closest( '.js-wpv-sort-list-item' ).hasClass( 'wpv-sort-list-current' ) ) {
801				orderby_control
802					.find( 'span.wpv-sort-list-current' )
803						.removeClass( 'wpv-sort-list-current' );
804				orderby_control
805					.find( '.js-wpv-sort-list-item' )
806						.css( {'display':'none'} );
807				orderby_control_to_set
808					.closest( '.js-wpv-sort-list-item' )
809						.addClass( 'wpv-sort-list-current' )
810						.css( {'display': 'block'} );
811				orderby_control
812					.find( '.js-wpv-sort-list' )
813						.prepend( orderby_control_to_set.closest( '.js-wpv-sort-list-item' ) );
814			}
815		}
816		return self;
817	};
818
819	/**
820	 * Set the sort data for order controls as select dropdowns.
821	 *
822	 * @since 2.3.1
823	 */
824	self.set_sort_data_order_select = function( order_control, sort ) {
825		if ( order_control.find( 'option[value="' + sort['order'] + '"]' ).length == 0 ) {
826			order_control.append( $('<option>', {
827				value:	sort['order'],
828				text:	sort['order']
829			}));
830		}
831		order_control.val( sort['order'] );
832		if (
833			_.has( sort, 'orderby' )
834			&& typeof order_control.data('labels') !== 'undefined'
835		) {
836			// We are setting the order in a command that also sets the orderby, so we need to modify the order labels
837			var order_labels = order_control.data( 'labels' ),
838				order_labels_to_push = {},
839				orderby_to_check = sort.orderby.toLowerCase();
840			if ( _.has( order_labels, orderby_to_check ) ) {
841				order_labels_to_push = order_labels[ orderby_to_check ];
842			} else if ( _.has( order_labels, 'default' ) ) {
843				order_labels_to_push = order_labels[ 'default' ];
844			}
845
846			if ( _.has( order_labels_to_push, 'asc' ) ) {
847				order_control.find( 'option[value="asc"]' ).html( order_labels_to_push.asc );
848			}
849			if ( _.has( order_labels_to_push, 'desc' ) ) {
850				order_control.find( 'option[value="desc"]' ).html( order_labels_to_push.desc );
851			}
852		}
853		return self;
854	};
855
856	/**
857	 * Set the sort data for order controls as text or hidden input
857s.
858	 *
859	 * @since 2.3.1
860	 */
861	self.set_sort_data_order_text = function( order_control, sort ) {
862		order_control.val( sort['order'] );
863		return self;
864	};
865
866	/**
867	 * Set the sort data for order controls as radio inputs.
868	 *
869	 * @note Create a radio input if it does not exist one for the right order value.
870	 *
871	 * @since 2.3.1
872	 */
873	self.set_sort_data_order_radio = function( order_control, sort, form ) {
874		if ( order_control.filter( '[value="' + sort['order'] + '"]' ).length == 0 ) {
875			form.append( $('<input>', {
876				type:		'radio',
877				"class":	'js-wpv-sort-control-order',
878				name:		'wpv_sort_order',
879				value:		sort['order']
880			}));
881		}
882		form.find( '.js-wpv-sort-control-order[value="' +  sort['order'] + '"]' ).prop( 'checked', true );
883
884		if (
885			_.has( sort, 'orderby' )
886			&& typeof order_control.data('labels') !== 'undefined'
887		) {
888			// We are setting the order in a command that also sets the orderby, so we need to modify the order labels
889			var order_labels = order_control.data( 'labels' ),
890				order_labels_to_push = {},
891				orderby_to_check = sort.orderby.toLowerCase(),
892				label_to_update = null,
893				input_to_keep = null;
894			if ( _.has( order_labels, orderby_to_check ) ) {
895				order_labels_to_push = order_labels[ orderby_to_check ];
896			} else if ( _.has( order_labels, 'default' ) ) {
897				order_labels_to_push = order_labels[ 'default' ];
898			}
899
900			if ( _.has( order_labels_to_push, 'asc' ) ) {
901				label_to_update = order_control.filter( '[value="asc"]' ).closest( 'label' );
902				input_to_keep = order_control.filter( '[value="asc"]' ).detach();
903
904				label_to_update
905					.html( order_labels_to_push.asc )
906					.prepend( input_to_keep );
907			}
908			if ( _.has( order_labels_to_push, 'desc' ) ) {
909				label_to_update = order_control.filter( '[value="desc"]' ).closest( 'label' );
910				input_to_keep = order_control.filter( '[value="desc"]' ).detach();
911
912				label_to_update
913					.html( order_labels_to_push.desc )
914					.prepend( input_to_keep );
915			}
916		}
917		return self;
918	};
919
920	/**
921	 * Set the sort data for order controls as radio inputs.
922	 *
923	 * @note Uncheck all radio inputs if it does not exist one for the right order value.
924	 *
925	 * @since 2.3.1
926	 */
927	self.maybe_set_sort_data_order_radio = function( order_control, sort ) {
928		if ( order_control.filter( '[value="' + sort['order'] + '"]' ).length == 0 ) {
929			order_control.prop( 'checked', true );
930		} else {
931			order_control.filter( '[value="' +  sort['order'] + '"]' ).prop( 'checked', true );
932		}
933		return self;
934	};
935
936	/**
937	 * Set the sort data for order controls as lists.
938	 *
939	 * @since 2.3.1
940	 */
941	self.set_sort_data_order_list = function( order_control, sort ) {
942		var order_control_to_set = order_control.find( '.js-wpv-sort-list-order[data-order="' + sort['order'] + '"]' );
943		if ( order_control_to_set.length == 0 ) {
944
945		} else {
946			if ( ! order_control_to_set.closest( '.js-wpv-sort-list-item' ).hasClass( 'wpv-sort-list-current' ) ) {
947				order_control
948					.find( 'span.wpv-sort-list-current' )
949						.removeClass( 'wpv-sort-list-current' );
950				order_control
951					.find( '.js-wpv-sort-list-item' )
952						.css( {'display':'none'} );
953				order_control_to_set
954					.closest( '.js-wpv-sort-list-item' )
955						.addClass( 'wpv-sort-list-current' )
956						.css( {'display': 'block'} );
957				order_control
958					.find( '.js-wpv-sort-list' )
959						.prepend( order_control_to_set.closest( '.js-wpv-sort-list-item' ) );
960			}
961		}
962		if (
963			_.has( sort, 'orderby' )
964			&& typeof order_control.data('labels') !== 'undefined'
965		) {
966			// We are setting the order in a command that also sets the orderby, so we need to modify the order labels
967			var order_labels = order_control.data( 'labels' ),
968				order_labels_to_push = {},
969				orderby_to_check = sort.orderby.toLowerCase(),
970				width_update_needed = false;
971			if ( _.has( order_labels, orderby_to_check ) ) {
972				order_labels_to_push = order_labels[ orderby_to_check ];
973			} else if ( _.has( order_labels, 'default' ) ) {
974				order_labels_to_push = order_labels[ 'default' ];
975			}
976
977			if ( _.has( order_labels_to_push, 'asc' ) ) {
978				order_control.find( '.js-wpv-sort-list-order[data-order="asc"] span' ).html( order_labels_to_push.asc );
979				width_update_needed = true;
980			}
981			if ( _.has( order_labels_to_push, 'desc' ) ) {
982				order_control.find( '.js-wpv-sort-list-order[data-order="desc"] span' ).html( order_labels_to_push.desc );
983				width_update_needed = true;
984			}
985			if ( width_update_needed ) {
986				self.adjust_sort_list_width( order_control );
987			}
988		}
989		return self;
990	};
991
992	/**
993	 * Sync the sorting controls used inside of the View form.
994	 *
995	 * @since 2.3.1
996	 *
997	 * @note Setting "orderby_as" demands that "orderby" is also set.
998	 */
999	self.set_sort_data_inside_form = function( form, sort ) {
1000		if ( _.has( sort, 'orderby' ) ) {
1001			if ( form.find( '.js-wpv-sort-control-orderby' ).length > 0 ) {
1002				var orderby_control = form.find( '.js-wpv-sort-control-orderby' ),
1003					orderby_type = WPViews.view_frontend_utils.get_form_element_type( orderby_control );
1004				switch ( orderby_type ) {
1005					case 'select':
1006						self.set_sort_data_orderby_select( orderby_control, sort );
1007						break;
1008					case 'text':
1009					case 'hidden':
1010						self.set_sort_data_orderby_text( orderby_control, sort );
1011						break;
1012					case 'radio':
1013						self.set_sort_data_orderby_radio( orderby_control, sort, form );
1014						break;
1015				}
1016			} else {
1017				var orderby_attributes = {
1018					type:		'hidden',
1019					"class":	'js-wpv-sort-control-orderby',
1020					name:		'wpv_sort_orderby',
1021					value:		sort['orderby']
1022				};
1023				if ( _.has( sort, 'orderby_as' ) ) {
1024					orderby_attributes['data-orderbyas'] = sort['orderby_as'];
1025				}
1026                if ( _.has( sort, 'forceorder' ) ) {
1027                    orderby_attributes['data-forceorder'] = sort['forceorder'];
1028                }
1029                $( '<input>' )
1030					.attr( orderby_attributes )
1031					.appendTo( form );
1032			}
1033			// We set this as a hidden input for forms that submit with page refresh.
1034			if ( _.has( sort, 'orderby_as' ) ) {
1035				if ( form.find( '.js-wpv-sort-control-orderby-as' ).length > 0 ) {
1036					form.find( '.js-wpv-sort-control-orderby-as' ).remove();
1037				}
1038				$( '<input>' )
1039					.attr({
1040						type:		'hidden',
1041						"class":	'js-wpv-sort-control-orderby-as',
1042						name:		'wpv_sort_orderby_as',
1043						value:		sort['orderby_as']
1044					})
1045					.appendTo( form );
1046			}
1047			if ( form.find( '.js-wpv-sort-list-orderby-dropdown' ).length > 0 ) {
1048				var orderby_list_control = form.find( '.js-wpv-sort-list-orderby-dropdown' );
1049				self.set_sort_data_orderby_list( orderby_list_control, sort );
1050			}
1051		}
1052		if ( _.has( sort, 'order' ) ) {
1053			if ( form.find( '.js-wpv-sort-control-order' ).length > 0 ) {
1054				var order_control = form.find( '.js-wpv-sort-control-order' ),
1055					order_type = WPViews.view_frontend_utils.get_form_element_type( order_control );
1056				switch ( order_type ) {
1057					case 'select':
1058						self.set_sort_data_order_select( order_control, sort );
1059						break;
1060					case 'text':
1061					case 'hidden':
1062						self.set_sort_data_order_text( order_control, sort );
1063						break;
1064					case 'radio':
1065						self.set_sort_data_order_radio( order_control, sort, form );
1066						break;
1067				}
1068			} else {
1069				$( '<input>' )
1070					.attr({
1071						type:		'hidden',
1072						"class":	'js-wpv-sort-control-order',
1073						name:		'wpv_sort_order',
1074						value:		sort['order']
1075					})
1076					.appendTo( form );
1077			}
1078			if ( form.find( '.js-wpv-sort-list-order-dropdown' ).length > 0 ) {
1079				var order_list_control = form.find( '.js-wpv-sort-list-order-dropdown' );
1080				self.set_sort_data_order_list( order_list_control, sort );
1081			}
1082		}
1083		return self;
1084	}
1085
1086	/**
1087	 * Sync the sorting controls used outside of the View form.
1088	 *
1089	 * @since 2.3.1
1090	 *
1091	 * @note Setting "orderby_as" demands that "orderby" is also set.
1092	 */
1093	self.set_sort_data_outside_form = function( form, sort ) {
1094
1095		var view_number = form.data( 'viewnumber' );
1096
1097		if ( _.has( sort, 'orderby' ) ) {
1098			var extra_orderby = $( '.js-wpv-sort-control-orderby[data-viewnumber="' + view_number + '"]:not(.js-wpv-filter-form .js-wpv-sort-control-orderby)' ),
1099				extra_orderby_list = $( '.js-wpv-sort-list-orderby-dropdown[data-viewnumber="' + view_number + '"]:not(.js-wpv-filter-form .js-wpv-sort-list-orderby-dropdown)' );
1100			if ( extra_orderby.length > 0 ) {
1101				var orderby_type = WPViews.view_frontend_utils.get_form_element_type( extra_orderby );
1102				switch ( orderby_type ) {
1103					case 'select':
1104						self.set_sort_data_orderby_select( extra_orderby, sort );
1105						break;
1106					case 'text':
1107					case 'hidden':
1108						self.set_sort_data_orderby_text( extra_orderby, sort );
1109						break;
1110					case 'radio':
1111						self.maybe_set_sort_data_orderby_radio( extra_orderby, sort );
1112						break;
1113				}
1114			}
1115			if ( extra_orderby_list.length > 0 ) {
1116				self.set_sort_data_orderby_list( extra_orderby_list, sort );
1117			}
1118		}
1119
1120		if ( _.has( sort, 'order' ) ) {
1121			var extra_order = $( '.js-wpv-sort-control-order[data-viewnumber="' + view_number + '"]:not(.js-wpv-filter-form .js-wpv-sort-control-order)' ),
1122				extra_order_list = $( '.js-wpv-sort-list-order-dropdown[data-viewnumber="' + view_number + '"]:not(.js-wpv-filter-form .js-wpv-sort-list-order-dropdown)' );
1123			if ( extra_order.length > 0 ) {
1124				var order_type = WPViews.view_frontend_utils.get_form_element_type( extra_order );
1125				switch ( order_type ) {
1126					case 'select':
1127						self.set_sort_data_order_select( extra_order, sort );
1128						break;
1129					case 'text':
1130					case 'hidden':
1131						self.set_sort_data_order_text( extra_order, sort );
1132						break;
1133					case 'radio':
1134						self.maybe_set_sort_data_order_radio( extra_order, sort );
1135						break;
1136				}
1137			}
1138			if ( extra_order_list.length > 0 ) {
1139				self.set_sort_data_order_list( extra_order_list, sort );
1140			}
1141		}
1142
1143		return self;
1144
1145	};
1146
1147	// ------------------------------------
1148	// Sorting links - not implemented yet
1149	// ------------------------------------
1150
1151	$( document ).on( 'click', '.js-wpv-sort-trigger', function( e ) {
1152		e.preventDefault();
1153		var thiz		= $( this ),
1154		view_number		= thiz.data( 'viewnumber' ),
1155		form			= $( '.js-wpv-filter-form-' + view_number ),
1156		sort			= {
1157							'orderby':		( typeof thiz.data( 'orderby' ) != 'undefined' ) ? thiz.data( 'orderby' ) : '',
1158							'order':		( typeof thiz.data( 'order' ) != 'undefined' ) ? thiz.data( 'order' ) : '',
1159							'orderby_as':	( typeof thiz.data( 'orderbyas' ) != 'undefined' ) ? thiz.data( 'orderbyas' ) : '',
1160		},
1161		form_each;
1162		form.each( function() {
1163			form_each = $( this );
1164			self.set_sort_data( form_each, sort );
1165			WPViews.view_frontend_utils.set_extra_url_query_parameters_by_form( form_each );
1166		});
1167		if (
1168			thiz.hasClass( 'js-wpv-column-header-click' )
1169			|| (
1170				! form.hasClass( 'js-wpv-ajax-results-enabled' )
1171				&& ! form.hasClass( 'js-wpv-ajax-results-submit-enabled' )
1172			)
1173		) {
1174			// Backwards compatibility: table sorting does reload the page for sorting
1175			// Also, submit the form for a page reload when the form is not set to load results using AJAX
1176			form.submit();
1177		} else {
1178			// Elsewhere, trigger a parametric search change, and make sure AJAX is used where due
1179			var data_for_events = {
1180				view_unique_id:			view_number,
1181				form:					form,
1182				force_results_update:	form.hasClass( 'js-wpv-ajax-results-submit-enabled' )
1183			};
1184
1185			data_for_events = WPViews.view_parametric_search.extend_wpv_parametric_search_triggered_data( data_for_events );
1186
1187			$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
1188		}
1189	});
1190
1191	// ------------------------------------
1192	// Sorting controls
1193	// ------------------------------------
1194
1195	$( document ).on( 'change', 'select.js-wpv-sort-control-orderby', function() {
1196
1197		var thiz		= $( this ),
1198		thiz_selected	= thiz.find( ':selected' ),
1199		sort			= {},
1200		view_number		= thiz.data( 'viewnumber' ),
1201		form			= $( '.js-wpv-filter-form-' + view_number ),
1202		data_for_events = {
1203			view_unique_id:	view_number,
1204			form:			form
1205		};
1206
1207		sort[ 'orderby' ] = thiz.val();
1208		if ( thiz_selected.data( 'orderbyas' ) !== undefined ) {
1209			sort[ 'orderby_as' ] = thiz_selected.data( 'orderbyas' );
1210		}
1211		if (
1212			thiz_selected.data( 'forceorder' ) !== undefined
1213			&& thiz_selected.data( 'forceorder' ) !== ''
1214		) {
1215			sort[ 'order' ] = thiz_selected.data( 'forceorder' );
1216		}
1217
1218		self.set_sort_data( form, sort );
1219
1220		wpv_stop_rollover[ view_number ] = true;
1221
1222		data_for_events = WPViews.view_parametric_search.extend_wpv_parametric_search_triggered_data( data_for_events );
1223
1224		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
1225
1226	});
1227
1228	$( document ).on( 'change', 'input.js-wpv-sort-control-orderby', function() {
1229
1230		var thiz		= $( this ),
1231		sort			= {},
1232		view_number		= thiz.data( 'viewnumber' ),
1233		sort_control	= $( '.js-wpv-sort-control-orderby[data-viewnumber="' + view_number + '"]:checked' ),
1234		form			= $( '.js-wpv-filter-form-' + view_number ),
1235		data_for_events = {
1236			view_unique_id:	view_number,
1237			form:			form
1238		};
1239
1240		sort[ 'orderby' ] = sort_control.val();
1241		if ( sort_control.data( 'orderbyas' ) !== undefined ) {
1242			sort[ 'orderby_as' ] = sort_control.data( 'orderbyas' );
1243		}
1244		if (
1245			sort_control.data( 'forceorder' ) !== undefined
1246			&& sort_control.data( 'forceorder' ) !== ''
1247		) {
1248			sort[ 'order' ] = sort_control.data( 'forceorder' );
1249		}
1250
1251		self.set_sort_data( form, sort );
1252
1253		wpv_stop_rollover[ view_number ] = true;
1254
1255		data_for_events = WPViews.view_parametric_search.extend_wpv_parametric_search_triggered_data( data_for_events );
1256
1257		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
1258
1259	});
1260
1261	$( document ).on( 'click', '.js-wpv-sort-list-orderby', function( e ) {
1262		e.preventDefault();
1263		var orderby_link = $( this ),
1264			orderby = orderby_link.data( 'orderby' ),
1265			orderby_as = orderby_link.data( 'orderbyas' ),
1266			orderby_order = orderby_link.data( 'forceorder' ),
1267			orderby_list = orderby_link.closest( '.wpv-sort-list' ),
1268			orderby_list_item = orderby_link.closest( 'span.js-wpv-sort-list-item' ),
1269			view_number = orderby_link.data( 'viewnumber' ),
1270			form = $( '.js-wpv-filter-form-' + view_number ),
1271			sort = {
1272				'orderby':		orderby,
1273				'orderby_as':	orderby_as
1274			},
1275			data_for_events = {
1276				view_unique_id:	view_number,
1277				form:			form
1278			};
1279
1280
1281		if ( orderby_list_item.hasClass( 'wpv-sort-list-current' ) ) {
1282			return;
1283		}
1284
1285		orderby_list
1286			.find( 'span.wpv-sort-list-current' )
1287				.removeClass( 'wpv-sort-list-current' );
1288		orderby_list
1289			.find( '.js-wpv-sort-list-item' )
1290				.css( {'display':'none'} );
1291		orderby_list_item
1292			.addClass( 'wpv-sort-list-current' )
1293			.css( {'display': 'block'} );
1294		orderby_list.prepend( orderby_list_item );
1295
1296		if ( orderby_order !== '' ) {
1297			sort[ 'order' ] = orderby_order;
1298		}
1299
1300		self.set_sort_data( form, sort );
1301
1302		wpv_stop_rollover[ view_number ] = true;
1303
1304		data_for_events = WPViews.view_parametric_search.extend_wpv_parametric_search_triggered_data( data_for_events );
1305
1306		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
1307
1308	});
1309
1310	$( document ).on( 'change', 'select.js-wpv-sort-control-order', function() {
1311
1312		var thiz		= $( this ),
1313		sort			= {},
1314		view_number		= thiz.data( 'viewnumber' ),
1315		form			= $( '.js-wpv-filter-form-' + view_number ),
1316		data_for_events = {
1317			view_unique_id:	view_number,
1318			form:			form
1319		};
1320
1321		sort[ 'order' ] = thiz.val();
1322
1323		self.set_sort_data( form, sort );
1324
1325		wpv_stop_rollover[ view_number ] = true;
1326
1327		data_for_events = WPViews.view_parametric_search.extend_wpv_parametric_search_triggered_data( data_for_events );
1328
1329		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
1330
1331	});
1332
1333	$( document ).on( 'change', 'input.js-wpv-sort-control-order', function() {
1334
1335		var thiz		= $( this ),
1336		sort			= {},
1337		view_number		= thiz.data( 'viewnumber' ),
1338		sort_control	= $( '.js-wpv-sort-control-order[data-viewnumber="' + view_number + '"]:checked' ),
1339		form			= $( '.js-wpv-filter-form-' + view_number ),
1340		data_for_events = {
1341			view_unique_id:	view_number,
1342			form:			form
1343		};
1344
1345		sort[ 'order' ] = sort_control.val();
1346
1347		self.set_sort_data( form, sort );
1348
1349		wpv_stop_rollover[ view_number ] = true;
1350
1351		data_for_events = WPViews.view_parametric_search.extend_wpv_parametric_search_triggered_data( data_for_events );
1352
1353		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
1354
1355	});
1356
1357	$( document ).on( 'click', '.js-wpv-sort-list-order', function( e ) {
1358		e.preventDefault();
1359		var order_link = $( this ),
1360			order = order_link.data( 'order' ),
1361			order_list = order_link.closest( '.wpv-sort-list' ),
1362			order_list_item = order_link.closest( 'span.js-wpv-sort-list-item' ),
1363			view_number = order_link.data( 'viewnumber' ),
1364			form = $( '.js-wpv-filter-form-' + view_number ),
1365			sort = {
1366				'order': order
1367			},
1368			data_for_events = {
1369				view_unique_id:	view_number,
1370				form:			form
1371			};
1372
1373
1374		if ( order_list_item.hasClass( 'wpv-sort-list-current' ) ) {
1375			return;
1376		}
1377
1378		order_list
1379			.find( 'span.wpv-sort-list-current' )
1380				.removeClass( 'wpv-sort-list-current' );
1381		order_list
1382			.find( '.js-wpv-sort-list-item' )
1383				.css( {'display':'none'} );
1384		order_list_item
1385			.addClass( 'wpv-sort-list-current' )
1386			.css( {'display': 'block'} );
1387		order_list.prepend( order_list_item );
1388
1389		self.set_sort_data( form, sort );
1390
1391		wpv_stop_rollover[ view_number ] = true;
1392
1393		data_for_events = WPViews.view_parametric_search.extend_wpv_parametric_search_triggered_data( data_for_events );
1394
1395		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
1396
1397	});
1398
1399	$( document ).on( 'mouseenter', '.js-wpv-sort-list-dropdown', function() {
1400		var orderby_list = $( this );
1401		orderby_list
1402			.find( '.js-wpv-sort-list-item' )
1403				.css( {'display': 'block'} );
1404	});
1405
1406	$( document ).on( 'mouseleave', '.js-wpv-sort-list-dropdown', function() {
1407		var orderby_list = $( this );
1408		orderby_list
1409			.find( '.js-wpv-sort-list-item:not(.wpv-sort-list-current)' )
1410				.css( {'display': 'none'} );
1411	});
1412
1413	// @todo calculate inital width and height of the container and also maybe re-calculate height on selection
1414
1415	self.init_sort_list_width = function( selector ) {
1416		/*
1417		selector
1418			.find( '.js-wpv-sort-list-dropdown' )
1419				.each( function() {
1420					var sort_list = $( this ),
1421						sort_list_width = 0,
1422						sort_list_items = sort_list.find( '.js-wpv-sort-list-item' ),
1423						sort_list_anchors = sort_list.find( '.wpv-sort-list-anchor' );
1424						sort_list_anchors.each( function() {
1425							var item_width = WPViews.view_frontend_utils.get_hidden_item_width( $( this ) );
1426							if ( item_width > sort_list_width ) {
1427								sort_list_width = item_width;
1428							}
1429						});
1430						sort_list.css( { 'width': sort_list_width + 'em' } );
1431						sort_list_items.css( { 'width': sort_list_width + 'em' } );
1432				});
1433		*/
1434	};
1435
1436	/**
1437	 * Adjust the sort list width to span at least its length in "em" units.
1438	 *
1439	 * @note When the length is less than 10 units, add an extra one for the toggle icon.
1440	 *
1441	 * @since 2.3.1
1442	 */
1443	self.adjust_sort_list_width = function( selector ) {
1444		var sort_list = selector,
1445			sort_list_width = 0,
1446			sort_list_items = sort_list.find( '.js-wpv-sort-list-item' ),
1447			sort_list_anchors = sort_list.find( '.wpv-sort-list-anchor span' );
1448
1449		sort_list_anchors.each( function() {
1450			var item_width = $( this ).text().length;
1451			if ( item_width > sort_list_width ) {
1452				sort_list_width = item_width;
1453			}
1454		});
1455		sort_list_width = ( sort_list_width < 10 ) ? sort_list_width + 1 : sort_list_width;
1456		sort_list.css( { 'width': sort_list_width + 'em' } );
1457		sort_list_items.css( { 'width': sort_list_width + 'em' } );
1458	};
1459
1460	/**
1461	 * Initialize sorting data for special cass:
1462	 * - When the sorting controls are of type 'list'.
1463	 * - When the sorting controls are of type 'dropdown' or 'radio', but are located outside of their related form.
1464	 * In those cases, there are no real sorting control elements inside the form, so we need to manually add them.
1465	 *
1466	 * @param selector The DOM object where this should be run, helps to narrow down on AJAX related operations.
1467	 *
1468	 * @since 2.3.0
1469	 *
1470	 * @todo Check whether we need to initialize the order coming from "forceorder" attributes in orderby controlos.
1471	 */
1472	self.maybe_init_sort_data = function( selector ) {
1473
1474		// Init sort data for sort list controls, as they do not add proper form input elements
1475		selector.find( '.js-wpv-sort-list-dropdown' ).each( function() {
1476			var sorting_list = $( this ),
1477				sorting_list_type = sorting_list.hasClass( 'wpv-sort-list-orderby-dropdown' ) ? 'orderby' : 'order';
1478
1479			switch ( sorting_list_type ) {
1480				case 'orderby':
1481					var sorting_current = sorting_list.find( '.wpv-sort-list-current .js-wpv-sort-list-orderby' ),
1482						sorting_viewnumber = sorting_current.data( 'viewnumber' ),
1483						sorting_form = $( '.js-wpv-filter-form-' + sorting_viewnumber ),
1484						sorting_data = {
1485							orderby: sorting_current.data( 'orderby' ),
1486                            orderby_as: sorting_current.data( 'orderbyas' ),
1487                            forceorder: sorting_current.data( 'forceorder' )
1488                        };
1489
1490						self.set_sort_data( sorting_form, sorting_data );
1491					break;
1492				case 'order':
1493					var sorting_current = sorting_list.find( '.wpv-sort-list-current .js-wpv-sort-list-order' ),
1494						sorting_viewnumber = sorting_current.data( 'viewnumber' ),
1495						sorting_form = $( '.js-wpv-filter-form-' + sorting_viewnumber ),
1496						sorting_data = {
1497							order: sorting_current.data( 'order' ),
1498						};
1499
1500						self.set_sort_data( sorting_form, sorting_data );
1501					break;
1502			}
1503		});
1504
1505		// Init sort data for dropdowns or radios that are rendered outside their proper form
1506		selector.find( 'select.js-wpv-sort-control-orderby' ).each( function() {
1507			var sorting_control = $( this );
1508			if ( sorting_control.closest( '.js-wpv-filter-form' ).length == 0 ) {
1509				var sorting_viewnumber = sorting_control.data( 'viewnumber' ),
1510					sorting_form = $( '.js-wpv-filter-form-' + sorting_viewnumber ),
1511					sorting_data = {
1512						orderby: sorting_control.val()
1513					};
1514
1515				if ( sorting_control.find( ':selected' ).data( 'orderbyas' ) !== undefined ) {
1516					sorting_data[ 'orderby_as' ] = sorting_control.find( ':selected' ).data( 'orderbyas' );
1517				}
1518
1519				self.set_sort_data( sorting_form, sorting_data );
1520			}
1521		});
1522		selector.find( 'select.js-wpv-sort-control-order' ).each( function() {
1523			var sorting_control = $( this );
1524			if ( sorting_control.closest( '.js-wpv-filter-form' ).length == 0 ) {
1525				var sorting_viewnumber = sorting_control.data( 'viewnumber' ),
1526					sorting_form = $( '.js-wpv-filter-form-' + sorting_viewnumber ),
1527					sorting_data = {
1528						order: sorting_control.val()
1529					};
1530
1531				self.set_sort_data( sorting_form, sorting_data );
1532			}
1533		});
1534		selector.find( 'input.js-wpv-sort-control-orderby:checked' ).each( function() {
1535			var sorting_control = $( this );
1536			if ( sorting_control.closest( '.js-wpv-filter-form' ).length == 0 ) {
1537				var sorting_viewnumber = sorting_control.data( 'viewnumber' ),
1538					sorting_form = $( '.js-wpv-filter-form-' + sorting_viewnumber ),
1539					sorting_data = {
1540						orderby: sorting_control.val()
1541					};
1542
1543				if ( sorting_control.data( 'orderbyas' ) !== undefined ) {
1544					sorting_data[ 'orderby_as' ] = sorting_control.data( 'orderbyas' );
1545				}
1546
1547				self.set_sort_data( sorting_form, sorting_data );
1548			}
1549		});
1550		selector.find( 'input.js-wpv-sort-control-order:checked' ).each( function() {
1551			var sorting_control = $( this );
1552			if ( sorting_control.closest( '.js-wpv-filter-form' ).length == 0 ) {
1553				var sorting_viewnumber = sorting_control.data( 'viewnumber' ),
1554					sorting_form = $( '.js-wpv-filter-form-' + sorting_viewnumber ),
1555					sorting_data = {
1556						order: sorting_control.val()
1557					};
1558
1559				self.set_sort_data( sorting_form, sorting_data );
1560			}
1561		});
1562
1563	};
1564
1565	$( document ).on( 'js_event_wpv_pagination_completed', function( event, data ) {
1566		//self.init_sort_list_width( data.layout );
1567		self.maybe_init_sort_data( data.layout );
1568		if ( data.filter ) {
1569            self.maybe_init_sort_data( data.filter );
1570		}
1571	});
1572
1573	$( document ).on( 'js_event_wpv_parametric_search_form_updated', function( event, data ) {
1574		//self.init_sort_list_width( data.view_changed_form );
1575		self.maybe_init_sort_data( data.view_changed_form );
1576	});
1577
1578	$( document ).on( 'js_event_wpv_parametric_search_results_updated', function( event, data ) {
1579		//self.init_sort_list_width( data.layout );
1580		self.maybe_init_sort_data( data.layout );
1581	});
1582
1583	// ------------------------------------
1584	// Init
1585	// ------------------------------------
1586
1587	self.init = function() {
1588		//self.init_sort_list_width( $( 'body' ) );
1589		self.maybe_init_sort_data( $( 'body' ) );
1590	};
1591
1592	self.init();
1593
1594};
1595
1596WPViews.ViewPagination = function( $ ) {
1597
1598	// ------------------------------------
1599	// Constants and variables
1600	// ------------------------------------
1601
1602	var self = this;
1603
1604	self.rollover_running = [];
1605	self.rollover_stopped = [];
1606    self.rollover_paused = [];
1607
1608	self.init_rollover_timing_fired = false;
1609
1610	self.pagination_queue = {};
1611
1612	self.pagination_effects				= {};
1613	self.pagination_effects_conditions	= {};
1614	self.pagination_effects_spinner		= {};
1615	self.paged_views					= {};
1616	self.paged_views_initial_page		= {};
1617
1618	self.last_paginated_view				= [];
1619	self.paginated_history_reach			= 0;
1620	self.add_paginated_history				= true;
1621	self.pagination_effect_state_push		= [ 'fade', 'slidev', 'slideh' ];
1622	self.pagination_effect_state_replace	= [];
1623	self.pagination_effect_state_keep		= [ 'infinite' ];
1624
1625	self.init_scrolling_event_fired = false;
1626
1627	self.slide_data_defaults = {
1628		view_number:		'',
1629		page:				0,
1630		max_pages:			0,
1631		speed:				500,
1632		next:				true,
1633		effect:				'fade',
1634		response:			null,
1635		wpvPaginatorFilter: null,
1636		wpvPaginatorLayout: null,
1637		responseFilter:		null,
1638		responseView:		null,
1639		callback_next_func:	WPViews.view_frontend_utils.just_return
1640	};
1641
1642	self.pagination_doing_ajax = [];
1643
1644	// ------------------------------------
1645	// Methods
1646	// ------------------------------------
1647
1648	/**
1649	* add_view_parameters
1650	*
1651	* Add several information to the data used to get pagination pages.
1652	* For example, add column sorting data, parametric search data and parent View data.
1653	*
1654	* @since 1.9
1655	* @since 2.1	Deprecated
1656	*/
1657
1658	self.add_view_parameters = function( data, page, view_number ) {
1659		var this_form			= $( 'form.js-wpv-filter-form-' + view_number ),
1660		this_prelements			= this_form.find( '.js-wpv-post-relationship-update' ),
1661		this_form_environment	= this_form.data( 'environment' );
1662
1663		data['action']				= 'wpv_get_page';
1664		data['page']				= page;
1665		data['view_number']			= view_number;
1666		data['wpv_sort_orderby']	= ( typeof this_form.data( 'orderby' ) != 'undefined' ) ? this_form.data( 'orderby' ) : '';
1667		data['wpv_sort_order']		= ( typeof this_form.data( 'order' ) != 'undefined' ) ? this_form.data( 'order' ) : '';
1668		data['wpv_sort_orderby_as']	= ( typeof this_form.data( 'orderbyas' ) != 'undefined' ) ? this_form.data( 'orderbyas' ) : '';
1669		data['wpv_sort_orderby_second']	= ( typeof this_form.data( 'orderbysecond' ) != 'undefined' ) ? this_form.data( 'orderbysecond' ) : '';
1670		data['wpv_sort_order_second']	= ( typeof this_form.data( 'ordersecond' ) != 'undefined' ) ? this_form.data( 'ordersecond' ) : '';
1671		data['wpv_view_widget_id']	= this_form.data( 'viewwidgetid' );
1672		data['view_hash']			= this_form.data( 'viewhash' );
1673		data['dps_pr']				= {};
1674		data['dps_general']			= {};
1675
1676		if ( this_form_environment.current_post_id > 0 ) {
1677			data['wpv_aux_current_post_id'] = this_form_environment.current_post_id;
1678		}
1679		if ( this_form_environment.parent_post_id > 0 ) {
1680			data['wpv_aux_parent_post_id'] = this_form_environment.parent_post_id;
1681		}
1682		if ( this_form_environment.parent_term_id > 0 ) {
1683			data['wpv_aux_parent_term_id'] = this_form_environment.parent_term_id;
1684		}
1685		if ( this_form_environment.parent_user_id > 0 ) {
1686			data['wpv_aux_parent_user_id'] = this_form_environment.parent_user_id;
1687		}
1688
1689		if ( this_prelements.length ) {
1690			data['dps_pr'] = this_prelements.serializeArray();
1691		}
1692		if ( this_form.hasClass( 'js-wpv-dps-enabled' ) || this_form.hasClass( 'js-wpv-ajax-results-enabled' ) ) {
1693			data['dps_general'] = this_form.find( '.js-wpv-filter-trigger, .js-wpv-filter-trigger-delayed' ).serializeArray();
1694		}
1695
1696		return data;
1697	};
1698
1699	/**
1700	* pagination_preload_pages
1701	*
1702	* Preload pages to a reach.
1703	*
1704	* @param object preload_data
1705	*
1706	* @since 1.9
1707	*/
1708
1709	self.pagination_preload_pages = function( preload_data ) {
1710		var page = parseInt( preload_data.page, 10 ),
1711		max_pages = parseInt( preload_data.max_pages, 10 ),
1712		max_reach = parseInt( preload_data.max_reach, 10 );
1713
1714		if ( max_reach > max_pages ) {
1715			max_reach = max_pages;
1716		}
1717
1718		if ( preload_data.preload_pages == 'enabled' ) {
1719			var reach = 1;
1720			while ( reach < max_reach ) {
1721				self.pagination_preload_next_page( preload_data.view_number, page, max_pages, reach );
1722				self.pagination_preload_previous_page( preload_data.view_number, page, max_pages, reach );
1723				reach++;
1724			}
1725		}
1726		if ( preload_data.cache_pages == 'enabled' ) {
1727			self.pagination_cache_current_page( preload_data.view_number, page );
1728		}
1729	};
1730
1731	/**
1732	* pagination_cache_current_page
1733	*
1734	* Cache current page.
1735	*
1736	* @param string	view_number
1737	* @param int	page
1738	*
1739	* @since 1.9
1740	*/
1741
1742	self.pagination_cache_current_page = function( view_number, page ) {
1743		window.wpvCachedPages[ view_number ] = window.wpvCachedPages[ view_number ] || [];
1744		var current_page_permalink,
1745		content;
1746		if ( ! window.wpvCachedPages[view_number].hasOwnProperty( page ) ) {
1747			WPViews.view_frontend_utils.get_updated_query_results( view_number, page, $( 'form.js-wpv-filter-form-' + view_number ), 'full' )
1748				.done( function( response ) {
1749					if ( response.success ) {
1750						window.wpvCachedPages[view_number][page] = response.data.full;
1751						content = $( response.data.full ).find( 'img' );
1752						content.each( function() {
1753							window.wpvCachedImages.push( this.src );
1754						});
1755					}
1756				});
1757		}
1758	};
1759
1760	/**
1761	* pagination_preload_next_page
1762	*
1763	* Load the next page, or the next one counting "reach" pages.
1764	*
1765	* @param string	view_number
1766	* @param int	page
1767	* @param int	max_pages
1768	* @param int	reach
1769	*
1770	* @since 1.9
1771	*/
1772
1773	self.pagination_preload_next_page = function( view_number, page, max_pages, reach ) {
1774		window.wpvCachedPages[ view_number ] = window.wpvCachedPages[ view_number ] || [];
1775		var next_page = page + reach,
1776		content,
1777		current_page_permalink;
1778		if ( ! window.wpvCachedPages[view_number].hasOwnProperty( next_page ) ) {
1779			if ( ( next_page - 1 ) < max_pages ) {
1780				WPViews.view_frontend_utils.get_updated_query_results( view_number, next_page, $( 'form.js-wpv-filter-form-' + view_number ), 'full' )
1781					.done( function( response ) {
1782						if ( response.success ) {
1783							window.wpvCachedPages[view_number][next_page] = response.data.full;
1784							content = $( response.data.full ).find( 'img' );
1785							content.each( function() {
1786								window.wpvCachedImages.push( this.src );
1787							});
1788						}
1789					});
1790			}
1791		}
1792	};
1793
1794	/**
1795	* pagination_preload_previous_page
1796	*
1797	* Load the previous page, or the previous one counting "reach" pages.
1798	*
1799	* @param string	view_number
1800	* @param int	page
1801	* @param int	max_pages
1802	* @param int	reach
1803	*
1804	* @since 1.9
1805	*/
1806
1807	self.pagination_preload_previous_page = function(view_number, page, max_pages, reach) {
1808		window.wpvCachedPages[ view_number ] = window.wpvCachedPages[ view_number ] || [];
1809		var previous_page = page - reach,
1810		current_page_permalink,
1811		content;
1812		if ( ! window.wpvCachedPages[view_number].hasOwnProperty( previous_page ) ) {
1813			// LOAD PREVIOUS
1814			if ( ( previous_page + 1 ) > 1 ) {
1815				WPViews.view_frontend_utils.get_updated_query_results( view_number, previous_page, $( 'form.js-wpv-filter-form-' + view_number ), 'full' )
1816					.done( function( response ) {
1817						if ( response.success ) {
1818							window.wpvCachedPages[view_number][previous_page] = response.data.full;
1819							content = $( response.data.full ).find( 'img' );
1820							content.each( function() {
1821								window.wpvCachedImages.push( this.src );
1822							});
1823						}
1824					});
1825			} else if ( (previous_page + 1 ) === 1 ) { // LOAD LAST PAGE IF ON FIRST PAGE
1826				WPViews.view_frontend_utils.get_updated_query_results( view_number, max_pages, $( 'form.js-wpv-filter-form-' + view_number ), 'full' )
1827					.done( function( response ) {
1828						if ( response.success ) {
1829							window.wpvCachedPages[view_number][max_pages] = response.data.full;
1830							window.wpvCachedPages[view_number][0] = response.data.full;
1831							content = $( response.data.full ).find( 'img' );
1832							content.each( function() {
1833								window.wpvCachedImages.push( this.src );
1834							});
1835						}
1836					});
1837			}
1838		}
1839	};
1840
1841	/**
1842	* trigger_pagination
1843	*
1844	* Manage the View pagination after a control has been inited
1845	*
1846	* @param string	view_number
1847	* @param int	page
1848	*
1849	* @since 1.9
1850	*/
1851
1852	self.trigger_pagination = function( view_number, page ) {
1853		if ( ! window.wpvPaginationAnimationFinished.hasOwnProperty( view_number ) ) {
1854			window.wpvPaginationAnimationFinished[ view_number ] = false;
1855		} else if ( window.wpvPaginationAnimationFinished[ view_number ] !== true ) {
1856			if ( ! window.wpvPaginationQueue.hasOwnProperty( view_number ) ) {
1857				window.wpvPaginationQueue[view_number] = [];
1858			}
1859			window.wpvPaginationQueue[ view_number ].push( arguments );
1860			return;
1861		}
1862
1863		window.wpvPaginationAnimationFinished[ view_number ] = false;
1864
1865		var data = {},
1866		wpvPaginatorLayout = $( '#wpv-view-layout-' + view_number ),
1867		wpvPaginatorFilter = $( 'form[name="wpv-filter-' + view_number + '"]' ),
1868		speed = 500,
1869		next = true,
1870		paged_view_data = self.get_paged_view( view_number ),
1871		max_reach = parseInt( paged_view_data.preload_reach ) + 1,
1872		callback_next_func = WPViews.view_frontend_utils.just_return,
1873		data_for_get_page,
1874		img;
1875
1876		// Not using AJAX pagination
1877		// For WPAs we just need to adjust the form target and we are done :-)
1878		if (
1879			paged_view_data.type == 'disabled'
1880			|| paged_view_data.type == 'paged'
1881		) {
1882
1883			switch ( paged_view_data.query ) {
1884				case 'archive':
1885					current_page_permalink = paged_view_data[ 'base_permalink' ].replace( 'WPV_PAGE_NUM', page );
1886					window.location.replace( current_page_permalink );
1887					break;
1888				default:
1889					WPViews.view_frontend_utils.set_extra_url_query_parameters_by_form( wpvPaginatorFilter );
1890					// Adjust the wpv_paged hidden input to the page that we want to show
1891					if ( $( 'input[name="wpv_paged"]' ).length > 0 ) {
1892						$( 'input[name="wpv_paged"]' ).attr( 'value', page );
1893					} else {
1894						$( '<input>')
1895							.attr({
1896								type: 'hidden',
1897								name: 'wpv_paged',
1898								value: page
1899							})
1900							.appendTo( wpvPaginatorFilter );
1901					}
1902					wpvPaginatorFilter[0].submit();
1903					break;
1904			}
1905			return;
1906		}
1907
1908		// Using AJAX pagination
1909
1910		if ( paged_view_data.effect in self.pagination_effects_conditions ) {
1911			if ( ! self.pagination_effects_conditions[ paged_view_data.effect ]( paged_view_data, page ) ) {
1912				window.wpvPaginationAnimationFinished[ view_number ] = true;
1913				return;
1914			}
1915		}
1916
1917		window.wpvPaginationAjaxLoaded[view_number] = false;
1918		window.wpvCachedPages[ view_number ] = window.wpvCachedPages[view_number] || [];
1919
1920		if ( this.historyP.hasOwnProperty( view_number ) ) {
1921			next = ( this.historyP[ view_number ] < page ) ? true : false;
1922		}
1923
1924		if ( paged_view_data.callback_next !== '' ) {
1925			callback_next_func = window[ paged_view_data.callback_next ];
1926			if ( typeof callback_next_func !== "function" ) {
1927				callback_next_func = WPViews.view_frontend_utils.just_return;
1928			}
1929		}
1930
1931		data_for_get_page = {
1932			view_number:		view_number,
1933			page:				page,
1934			max_pages:			parseInt( paged_view_data.max_pages, 10 ),
1935			speed:				parseFloat( paged_view_data.duration ),
1936			next:				next,
1937			effect:				paged_view_data.effect,
1938			wpvPaginatorFilter: wpvPaginatorFilter,
1939			wpvPaginatorLayout: wpvPaginatorLayout,
1940			responseView:		null,
1941			callback_next_func:	callback_next_func
1942		};
1943
1944		if (
1945			window.wpvCachedPages[ view_number ].hasOwnProperty( page )
1946		) {
1947			data_for_get_page.response = window.wpvCachedPages[ view_number ][ page ];
1948			self.prepare_slide( data_for_get_page );
1949		} else {
1950			// Set loading class
1951			if ( paged_view_data.spinner !== 'disabled' ) {
1952				if ( paged_view_data.effect in self.pagination_effects_spinner ) {
1953					self.pagination_effects_spinner[ paged_view_data.effect ]( view_number, wpvPaginatorLayout );
1954				} else {
1955					self.pagination_effects_spinner[ 'fade' ]( view_number, wpvPaginatorLayout );
1956				}
1957			}
1958			WPViews.view_frontend_utils.get_updated_query_results( view_number, page, $( 'form.js-wpv-filter-form-' + view_number ), 'full' )
1959				.done( function( response ) {
1960					if ( response.success ) {
1961						data_for_get_page.response = response.data.full;
1962						self.prepare_slide( data_for_get_page );
1963					}
1964				});
1965		}
1966		self.pagination_preload_pages({
1967			view_number:	view_number,
1968			cache_pages:	paged_view_data.cache_pages,
1969			preload_pages:	paged_view_data.preload_pages,
1970			page:			page,
1971			max_reach:		max_reach,
1972			max_pages:		paged_view_data.max_pages
1973		});
1974		this.historyP[ view_number ] = page;
1975		return;
1976	};
1977
1978	/**
1979	* prepare_slide
1980	*
1981	* Wrap the old layout into a div.wpv_slide_remove and change its ID to ~-response
1982	* Preload images on the new page if needed
1983	* Fire self.pagination_slide
1984	*
1985	* @param object data
1986	*
1987	* @since 1.9
1988	*/
1989
1990	self.prepare_slide = function( data ) {
1991		var slide_data = $.extend( {}, self.slide_data_defaults, data ),
1992		width = slide_data.wpvPaginatorLayout.width(),
1993		outer_width = slide_data.wpvPaginatorLayout.outerWidth(),
1994		height = slide_data.wpvPaginatorLayout.height(),
1995		outer_height = slide_data.wpvPaginatorLayout.outerHeight(),
1996		responseObj = $( '<div></div>' ).append( slide_data.response ),
1997		preloadedImages,
1998		images;
1999
2000		slide_data.responseView = responseObj.find( '.js-wpv-view-layout-' + slide_data.view_number );
2001        slide_data.responseFilterSelector = responseObj.find( 'form.js-wpv-filter-form-' + slide_data.view_number );
2002		slide_data.responseFilter = responseObj.find( 'form.js-wpv-filter-form-' + slide_data.view_number ).html();
2003		slide_data.pagination_page_permalink = slide_data.responseView.data( 'permalink' );
2004
2005		// Wrap old layout in a div.wpv_slide_remove nd change its ID to ~-response
2006		slide_data.wpvPaginatorLayout
2007			.attr( 'id', 'wpv-view-layout-' + slide_data.view_number + '-response' )
2008			.wrap( '<div class="wpv_slide_remove" style="width:' + outer_width + 'px;height:' + outer_height + 'px;overflow:hidden;" />' )
2009			.css( 'width', width );
2010		// Add an ID attribute to the upcoming new layout, and hide it
2011		slide_data.responseView
2012			.attr( 'id', 'wpv-view-layout-' + slide_data.view_number )
2013			.css( {
2014				'visibility': 'hidden',
2015				'width': width
2016			} );
2017
2018		// Preload images if needed
2019		if ( slide_data.wpvPaginatorLayout.hasClass( 'js-wpv-layout-preload-images' ) ) {
2020			preloadedImages = [];
2021			images = slide_data.responseView.find( 'img' );
2022			if ( images.length < 1 ) {
2023				self.pagination_slide( slide_data );
2024			} else {
2025				images.one( 'load', function() {
2026					preloadedImages.push( $( this ).attr( 'src' ) );
2027					if ( preloadedImages.length === images.length ) {
2028						self.pagination_slide( slide_data );
2029					}
2030				}).each( function() {
2031					$( this ).load();
2032				});
2033			}
2034			// Fix inner nested Views with AJAX pagination: the inner View, when preloading images, was rendered with visibility:hidden by default
2035			slide_data.responseView
2036				.find( '.js-wpv-layout-preload-images' )
2037					.css( 'visibility', 'visible' );
2038		} else {
2039			self.pagination_slide( slide_data );
2040		}
2041	};
2042
2043	/**
2044	* pagination_slide
2045	*
2046	* Routes to the right pagination effect callback
2047	*
2048	* @param object data
2049	*
2050	* @since 1.9
2051	*/
2052
2053	self.pagination_slide = function( data ) {
2054		var slide_data = $.extend( {}, self.slide_data_defaults, data );
2055
2056		switch ( slide_data.effect ) {
2057			case 'slideleft':
2058				slide_data.next = true;
2059				slide_data.effect = 'slideh';
2060				break;
2061			case 'slideright':
2062			case 'sliderightforward':
2063				slide_data.next = false;
2064				slide_data.effect = 'slideh';
2065				break;
2066			case 'slidedown':
2067			case 'slidedownforward':
2068				slide_data.next = false;
2069				slide_data.effect = 'slidev';
2070				break;
2071			case 'slideup':
2072				slide_data.next = true;
2073				slide_data.effect = 'slidev';
2074				break;
2075		}
2076
2077		if ( ! slide_data.effect in self.pagination_effects ) {
2078			slide_data.effect = 'fade';
2079		}
2080
2081		self.pagination_effects[ slide_data.effect ]( slide_data );
2082	};
2083
2084	/**
2085	* pagination_queue_trigger
2086	*
2087	* Manage multiple and fast pagination requests.
2088	*
2089	* @param int		view_number
2090	* @param boolean	next
2091	* @param object		wpvPaginatorFilter
2092	*
2093	* @todo why do we pass next and wpvPaginatorFilter here?
2094	*
2095	* @since 1.9
2096	*/
2097
2098	self.pagination_queue_trigger = function( view_number, next, wpvPaginatorFilter ) {
2099		var args,
2100		page,
2101		max_pages;
2102		if ( window.wpvPaginationQueue.hasOwnProperty( view_number ) && window.wpvPaginationQueue[view_number].length > 0 ) {
2103		// when double clicking,we have set window.wpvPaginationQueue[view_number][1] and maybe we could tweak it to change the page number. Maybe checkin historyP
2104			window.wpvPaginationQueue[view_number].sort();
2105			args = window.wpvPaginationQueue[view_number][0];
2106			window.wpvPaginationQueue[view_number].splice(0, 1);
2107			page = args[1];
2108			max_pages = args[4];
2109			if ( page > max_pages ) {
2110				page = 1;
2111			} else if ( page < 1 ) {
2112				page = max_pages;
2113			}
2114			self.trigger_pagination( view_number, page );
2115		}
2116	};
2117
2118	// ------------------------------------
2119	// Events for Views pagination
2120	// ------------------------------------
2121
2122	/**
2123	* Manage pagination triggered from prev/next or first/last links
2124	*
2125	* @since 1.9
2126	* @since 2.4.1 Added support for first/last pagination links
2127	*/
2128
2129	$( document ).on( 'click', '.js-wpv-pagination-next-link, .js-wpv-pagination-previous-link, .js-wpv-pagination-first-link, .js-wpv-pagination-last-link', function( e ) {
2130		e.preventDefault();
2131		var thiz = $( this ),
2132		view_number = thiz.data( 'viewnumber' ),
2133        page = thiz.data( 'page' ),
2134        form = $( '.js-wpv-filter-form-' + view_number );
2135
2136        WPViews.view_sorting.set_sort_data_for_pagination( form );
2137
2138        wpv_stop_rollover[ view_number ] = true;
2139		self.trigger_pagination( view_number, page );
2140	});
2141
2142	/**
2143	* Manage pagination triggered by a change in the page selector dropdown
2144	*
2145	* @since 1.9
2146	*/
2147
2148	$( document ).on( 'change', '.js-wpv-page-selector', function( e ) {
2149		e.preventDefault();
2150		var thiz = $( this ),
2151		view_number = thiz.data( 'viewnumber' ),
2152		page = thiz.val();
2153		wpv_stop_rollover[ view_number ] = true
2154		self.trigger_pagination( view_number, page );
2155	});
2156
2157	/**
2158	* Manage pagination triggered by a click on a pagination link.
2159	*
2160	* @since 1.9
2161	*
2162	* @note Safari on iOS might need to also listen to the touchstart event. Investigate this!
2163	*/
2164
2165	$( document ).on( 'click', '.js-wpv-pagination-link', function( e ) {
2166		e.preventDefault();
2167		var thiz = $( this ),
2168		data_collected = {};
2169		data_collected.view_number = thiz.data( 'viewnumber');
2170		data_collected.page = thiz.data( 'page' );
2171		var i;
2172		// TODO this can be improved: we should not need a loop here at all
2173		// @todo review this, data_collected.max_pages is UNDEFINED
2174		for ( i = 1; i <= data_collected.max_pages; i++ ) {
2175			if ( i === data_collected.page ) {
2176				$( '.js-wpv-page-link-' + data_collected.view_number + '-' + i ).addClass( 'wpv_page_current' );
2177			} else {
2178				$( '.js-wpv-page-link-' + data_collected.view_number + '-' + i ).removeClass( 'wpv_page_current' );
2179			}
2180
2181		}
2182		wpv_stop_rollover[ data_collected.view_number ] = true
2183		self.trigger_pagination( data_collected.view_number, data_collected.page );
2184	});
2185
2186	/*
2187	$( document ).on( 'click', '.js-wpv-pagination-pause-rollover', function( e ) {
2188		e.preventDefault();
2189		var view_num = $( this ).data( 'viewnumber' );
2190		wpv_stop_rollover[view_num] = true;
2191	});
2192
2193	$( document ).on( 'click', '.js-wpv-pagination-resume-rollover', function( e ) {
2194		e.preventDefault();
2195		var view_num = $( this ).data( 'viewnumber' );
2196		delete wpv_stop_rollover[view_num];
2197	});
2198	*/
2199
2200	// ------------------------------------
2201	// Events for WordPress Archives pagination
2202	// ------------------------------------
2203
2204	/**
2205	* Manage pagination triggered from prev/next and first/last links, and numeric links, including their own prev/next ones
2206	*
2207	* @since 2.1
2208	* @since 2.4.1 Added support for first/last links.
2209	*/
2210
2211	$( document ).on( 'click', '.js-wpv-archive-pagination-link, .js-wpv-archive-pagination-next-link, .js-wpv-archive-pagination-prev-link, .js-wpv-archive-pagination-links-next-link, .js-wpv-archive-pagination-links-prev-link, .js-wpv-archive-pagination-links-first-link, .js-wpv-archive-pagination-links-last-link', function( e ) {
2212		e.preventDefault();
2213		var thiz = $( this ),
2214		view_number = thiz.data( 'viewnumber' ),
2215		page = thiz.data( 'page' );
2216		wpv_stop_rollover[ view_number ] = true;
2217		self.trigger_pagination( view_number, page );
2218	});
2219
2220	// ------------------------------------
2221	// Custom events
2222	// ------------------------------------
2223
2224	/**
2225	* js_event_wpv_pagination_completed
2226	*
2227	* Event fired after a pagination transition has been completed
2228	*
2229	* @param data
2230	* 	- view_unique_id
2231	* 	- effect
2232	* 	- speed
2233	* 	- layout
2234	*
2235	* @since 1.9
2236	*/
2237
2238	$( document ).on( 'js_event_wpv_pagination_completed', function( event, data ) {
2239		WPViews.view_frontend_utils.render_frontend_media_shortcodes( data.layout );
2240		WPViews.view_frontend_utils.render_frontend_datepicker();
2241		// Init pagination for any inner View that might be included inside the new page
2242		self.init_paged_views( data.layout );
2243		self.init_preload_images( data.layout );
2244		self.init_preload_pages( data.layout );
2245        self.init_pagination_button_state();
2246	});
2247
2248	$( document ).on( 'js_event_wpv_parametric_search_results_updated', function( event, data ) {
2249		// Init the pagination settings for this View results being updated
2250		self.init_paged_view( data.view_unique_id );
2251		// Init pagination for any inner View that might be included inside the new page
2252		self.init_paged_views( data.layout );
2253		self.init_preload_images( data.layout );
2254		self.init_preload_pages( data.layout );
2255	});
2256
2257	// ------------------------------------
2258	// Init
2259	// ------------------------------------
2260
2261	/**
2262	* init_effects
2263	*
2264	* Define the default pagination effects, can be extended by third parties.
2265	* Each callback gets the following parameters:
2266	* @param object slide_data
2267	*
2268	* @since 1.11
2269	*/
2270
2271	self.init_effects = function() {
2272		self.pagination_effects = {
2273			infinite: function( slide_data ) {
2274
2275				if ( slide_data.page != ( self.get_paged_view( slide_data.view_number ).page + 1 ) ) {
2276					// This should never happen! See self.pagination_effects_conditions
2277					$( '.js-wpv_slide_loading_img_' + slide_data.view_number ).fadeOut( function() {
2278						$( this ).remove();
2279					});
2280					slide_data.wpvPaginatorLayout
2281						.animate( { opacity: 1 }, 300 )
2282						.unwrap()// If it got here, it has a -response suffix in the ID and is wrapped in an auxiliar div
2283						.attr( 'id', 'wpv-view-layout-' + slide_data.view_number  );
2284					window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2285					window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2286
2287				} else {
2288
2289					// content.match(/<!-- HOOK OPEN -->(W?)\<!-- HOOK CLOSE -->/);
2290					var data_for_events = {},
2291					data_for_history = {};
2292
2293					data_for_events.view_unique_id	= slide_data.view_number;
2294					data_for_events.effect			= 'infinite';
2295					data_for_events.speed			= slide_data.speed;
2296
2297					data_for_history.view_number				= slide_data.view_number;
2298					data_for_history.page						= slide_data.page;
2299					data_for_history.effect						= 'infinite';
2300					data_for_history.pagination_page_permalink	= slide_data.pagination_page_permalink;
2301
2302					if (
2303						slide_data.wpvPaginatorLayout.find( '.js-wpv-loop' ).length > 0
2304						&& slide_data.responseView.find( '.js-wpv-loop' ).length > 0
2305					) {
2306						slide_data.responseView
2307							.find( '.js-wpv-loop' )
2308							.children()
2309								.addClass( 'wpv-loop-item-blink' )
2310								.css( { 'opacity': '0.3' } );
2311						slide_data.responseView
2312							.find( '.js-wpv-loop' )
2313							.prepend(
2314								slide_data.wpvPaginatorLayout
2315									.find( '.js-wpv-loop' )
2316									.html()
2317							);
2318						slide_data.wpvPaginatorLayout.html( slide_data.responseView.html() );
2319						slide_data.wpvPaginatorLayout
2320							.find( '.wpv-loop-item-blink' )
2321								.removeClass( 'wpv-loop-item-blink' )
2322								.animate( { opacity: 1 }, slide_data.speed );
2323
2324					} else {
2325						var oldHTML = slide_data.wpvPaginatorLayout.html(),
2326						oldArray = oldHTML.split( '<!-- WPV_Infinite_Scroll -->', 3 ),
2327						oldReplace = ( oldArray.length > 2 ) ? oldArray[1] : '';
2328						slide_data.wpvPaginatorLayout.html(
2329							slide_data.responseView.html().replace(
2330								'<!-- WPV_Infinite_Scroll_Insert -->',
2331								oldReplace
2332							)
2333						);
2334					}
2335					data_for_events.layout = slide_data.wpvPaginatorLayout;
2336					$( '.js-wpv_slide_loading_img_' + slide_data.view_number ).fadeOut( function() {
2337						$( this ).remove();
2338					});
2339					slide_data.wpvPaginatorLayout
2340						.unwrap()
2341						.attr( 'id', 'wpv-view-layout-' + slide_data.view_number  )
2342						.animate( { opacity: 1 }, 300, function() {
2343							slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2344
2345							self.get_paged_view( slide_data.view_number ).page = slide_data.page;
2346
2347							window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2348							window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2349							slide_data.callback_next_func();
2350							self.manage_browser_history( data_for_history );
2351							$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2352							self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2353						});
2354				}
2355			},
2356			slideh: function( slide_data ) {
2357				var height = slide_data.wpvPaginatorLayout.height(),
2358				old_height = slide_data.wpvPaginatorLayout.outerHeight(),
2359				new_height,
2360				data_for_events = {},
2361				data_for_history = {};
2362
2363				data_for_events.view_unique_id	= slide_data.view_number;
2364				data_for_events.effect			= 'slideh';
2365				data_for_events.speed			= slide_data.speed;
2366				data_for_events.layout			= slide_data.responseView;
2367                data_for_events.filter			= slide_data.responseFilterSelector;
2368
2369				data_for_history.view_number				= slide_data.view_number;
2370				data_for_history.page						= slide_data.page;
2371				data_for_history.effect						= 'slideh';
2372				data_for_history.pagination_page_permalink	= slide_data.pagination_page_permalink;
2373
2374				if ( slide_data.next === true ) {
2375					slide_data.wpvPaginatorLayout.css( 'float', 'left' );
2376					slide_data.responseView.css( {"float": "left", "visibility": "visible"} );
2377					slide_data.wpvPaginatorLayout
2378						.after( slide_data.responseView )
2379						.parent()
2380							.children()
2381								.wrapAll( '<div style="width:5000px;" />' );
2382					$( '.js-wpv_slide_loading_img_' + slide_data.view_number ).fadeOut(function() {
2383						$( this ).remove();
2384					});
2385					new_height = slide_data.responseView.outerHeight();
2386					if ( old_height === new_height ) {
2387						slide_data.wpvPaginatorLayout
2388							.parent()
2389								.animate( {marginLeft: '-' + slide_data.wpvPaginatorLayout.outerWidth()+'px'}, slide_data.speed+500, function() {
2390									slide_data.responseView.css( {'position': 'static', 'float': 'none'} );
2391									slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2392
2393									slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2394
2395									window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2396									window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2397									slide_data.callback_next_func();
2398									self.manage_browser_history( data_for_history );
2399									$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2400									self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2401								});
2402					} else if ( old_height > new_height ) {
2403						slide_data.wpvPaginatorLayout
2404							.parent()
2405								.animate( {marginLeft: '-' + slide_data.wpvPaginatorLayout.outerWidth()+'px'}, slide_data.speed+500, function() {
2406									slide_data.wpvPaginatorLayout
2407										.parent().parent()
2408											.animate( {height: slide_data.responseView.outerHeight()+'px'}, slide_data.speed/2, function() {
2409												slide_data.responseView.css( {'position': 'static', 'float': 'none'} );
2410												slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2411
2412												slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2413
2414												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2415												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2416												slide_data.callback_next_func();
2417												self.manage_browser_history( data_for_history );
2418												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2419												self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2420											});
2421								});
2422					} else {
2423						slide_data.wpvPaginatorLayout
2424							.parent().parent()
2425								.animate( {height: slide_data.responseView.outerHeight()+'px'}, slide_data.speed/2, function() {
2426									slide_data.wpvPaginatorLayout
2427										.parent()
2428											.animate( {marginLeft: '-' + slide_data.wpvPaginatorLayout.outerWidth()+'px'}, slide_data.speed+500, function() {
2429												slide_data.responseView.css( {'position': 'static', 'float': 'none'} );
2430												slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2431
2432												slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2433
2434												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2435												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2436												slide_data.callback_next_func();
2437												self.manage_browser_history( data_for_history );
2438												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2439												self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2440											});
2441								});
2442					}
2443				} else {
2444					slide_data.wpvPaginatorLayout.css( 'float', 'right' );
2445					slide_data.responseView.css( {'float': 'right', 'visibility': 'visible'} );
2446					slide_data.wpvPaginatorLayout
2447						.after( slide_data.responseView )
2448						.parent()
2449							.children()
2450								.wrapAll( '<div style="height:' + height +  ';width:' + ( slide_data.responseView.outerWidth() + slide_data.wpvPaginatorLayout.outerWidth() ) + 'px; margin-left:-' + ( slide_data.wpvPaginatorLayout.outerWidth() ) + 'px;" />' );
2451					$( '.js-wpv_slide_loading_img_' + slide_data.view_number ).fadeOut( function() {
2452						$( this ).remove();
2453					});
2454					new_height = slide_data.responseView.outerHeight();
2455					if ( old_height === new_height ) {
2456						slide_data.wpvPaginatorLayout
2457							.parent()
2458								.animate( {marginLeft: '0px'}, slide_data.speed+500, function() {
2459									slide_data.responseView.css( {'position': 'static', 'margin': '0px', 'float': 'none'} );
2460									slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2461
2462									slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2463
2464									window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2465									window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2466									slide_data.callback_next_func();
2467									self.manage_browser_history( data_for_history );
2468									$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2469									self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2470								});
2471					} else if ( old_height > new_height ) {
2472						slide_data.wpvPaginatorLayout
2473							.parent()
2474								.animate( {marginLeft: '0px'}, slide_data.speed+500, function() {
2475									slide_data.wpvPaginatorLayout
2476										.parent().parent()
2477											.animate( {height: slide_data.responseView.outerHeight()+'px'}, slide_data.speed/2, function() {
2478												slide_data.responseView.css( {'position': 'static', 'margin': '0px', 'float': 'none'} );
2479												slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2480
2481												slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2482
2483												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2484												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2485												slide_data.callback_next_func();
2486												self.manage_browser_history( data_for_history );
2487												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2488												self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2489											});
2490								});
2491					} else {
2492						slide_data.wpvPaginatorLayout
2493							.parent().parent()
2494								.animate( {height: slide_data.responseView.outerHeight()+'px'}, slide_data.speed/2, function() {
2495									slide_data.wpvPaginatorLayout
2496										.parent()
2497											.animate( {marginLeft: '0px'}, slide_data.speed+500, function() {
2498												slide_data.responseView.css( {'position': 'static', 'margin': '0px', 'float': 'none'} );
2499												slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2500
2501												slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2502
2503												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2504												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2505												slide_data.callback_next_func();
2506												self.manage_browser_history( data_for_history );
2507												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2508												self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2509											});
2510								});
2511					}
2512				}
2513			},
2514			slidev: function( slide_data ) {
2515				var old_height = slide_data.wpvPaginatorLayout.outerHeight(),
2516				new_height,
2517				data_for_events = {},
2518				data_for_history = {};
2519
2520				data_for_events.view_unique_id	= slide_data.view_number;
2521				data_for_events.effect			= 'slidev';
2522				data_for_events.speed			= slide_data.speed;
2523				data_for_events.layout			= slide_data.responseView;
2524                data_for_events.filter			= slide_data.responseFilterSelector;
2525
2526				data_for_history.view_number				= slide_data.view_number;
2527				data_for_history.page						= slide_data.page;
2528				data_for_history.effect						= 'slidev';
2529				data_for_history.pagination_page_permalink	= slide_data.pagination_page_permalink;
2530
2531				if ( slide_data.next === true ) {
2532					slide_data.responseView.css( 'visibility', 'visible' );
2533					slide_data.wpvPaginatorLayout
2534						.after( slide_data.responseView )
2535						.parent()
2536							.children()
2537								.wrapAll( '<div />' );
2538					$( '.js-wpv_slide_loading_img_' + slide_data.view_number ).fadeOut( function(){
2539						$( this ).remove();
2540					});
2541					new_height = slide_data.responseView.outerHeight();
2542					if ( old_height === new_height ) {
2543						slide_data.wpvPaginatorLayout
2544							.parent()
2545								.animate( {marginTop: '-' + slide_data.responseView.outerHeight()+'px'}, slide_data.speed+500, function() {
2546									slide_data.responseView.css( {'position': 'static', 'margin': '0px'} );
2547									slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2548
2549									slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2550
2551									window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2552									window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2553									slide_data.callback_next_func();
2554									self.manage_browser_history( data_for_history );
2555									$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2556									self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2557								});
2558					} else if ( old_height > new_height ) {
2559						slide_data.wpvPaginatorLayout
2560							.parent()
2561								.animate( {marginTop: '-'+old_height+'px'}, slide_data.speed+500, function() {
2562									slide_data.wpvPaginatorLayout
2563										.parent().parent()
2564											.animate( {height: slide_data.responseView.outerHeight()+'px'}, slide_data.speed/2, function() {
2565												slide_data.responseView.css( {'position': 'static', 'margin': '0px'} );
2566												slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2567
2568												slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2569
2570												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2571												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2572												slide_data.callback_next_func();
2573												self.manage_browser_history( data_for_history );
2574												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2575												self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2576											});
2577								});
2578					} else {
2579						slide_data.wpvPaginatorLayout
2580							.parent().parent()
2581								.animate( {height: slide_data.responseView.outerHeight()+'px'}, slide_data.speed/2, function() {
2582									slide_data.wpvPaginatorLayout
2583										.parent()
2584											.animate( {marginTop: '-'+old_height+'px'}, slide_data.speed+500, function() {
2585												slide_data.responseView.css( {'position': 'static', 'margin': '0px'} );
2586												slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2587
2588												slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2589
2590												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2591												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2592												slide_data.callback_next_func();
2593												self.manage_browser_history( data_for_history );
2594												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2595												self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2596											});
2597								});
2598					}
2599				} else {
2600					slide_data.responseView.css( 'visibility', 'visible' );
2601					slide_data.wpvPaginatorLayout
2602						.before( slide_data.responseView )
2603						.parent()
2604							.children()
2605								.wrapAll( '<div />' );
2606					$( '.js-wpv_slide_loading_img_' + slide_data.view_number ).fadeOut( function() {
2607						$( this ).remove();
2608					});
2609					new_height = slide_data.responseView.outerHeight();
2610					slide_data.wpvPaginatorLayout.parent().css( {'position': 'relative', 'margin-top': '-' + slide_data.responseView.outerHeight() + 'px'} );
2611					if ( old_height === new_height ) {
2612						slide_data.wpvPaginatorLayout
2613							.parent()
2614								.animate( {marginTop: '0px'}, slide_data.speed+500, function() {
2615									slide_data.responseView.css( {'position': 'static', 'margin': '0px'} );
2616									slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2617
2618									slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2619
2620									window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2621									window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2622									slide_data.callback_next_func();
2623									self.manage_browser_history( data_for_history );
2624									$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2625									self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2626								});
2627					} else if ( old_height > new_height ) {
2628						slide_data.wpvPaginatorLayout
2629							.parent()
2630								.animate( {marginTop: '0px'}, slide_data.speed+500, function() {
2631									slide_data.wpvPaginatorLayout
2632										.parent().parent()
2633											.animate( {height: slide_data.responseView.outerHeight()+'px'}, slide_data.speed/2, function() {
2634												slide_data.responseView.css( {'position': 'static', 'margin': '0px'} );
2635												slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2636
2637												slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2638
2639												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2640												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2641												slide_data.callback_next_func();
2642												self.manage_browser_history( data_for_history );
2643												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2644												self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2645											});
2646						});
2647					} else {
2648						slide_data.wpvPaginatorLayout
2649							.parent().parent()
2650								.animate( {height: slide_data.responseView.outerHeight()+'px'}, slide_data.speed/2, function() {
2651									slide_data.wpvPaginatorLayout
2652										.parent()
2653											.animate( {marginTop: '0px'}, slide_data.speed+500, function() {
2654												slide_data.responseView.css( {'position': 'static', 'margin': '0px'} );
2655												slide_data.wpvPaginatorLayout.unwrap().unwrap().remove();
2656
2657												slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2658
2659												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2660												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2661												slide_data.callback_next_func();
2662												self.manage_browser_history( data_for_history );
2663												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2664												self.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2665											});
2666								});
2667					}
2668				}
2669			},
2670			fade: function( slide_data ) {
2671				var old_height = slide_data.wpvPaginatorLayout.outerHeight(),
2672				new_height,
2673				data_for_events = {},
2674				data_for_history = {};
2675
2676				data_for_events.view_unique_id	= slide_data.view_number;
2677				data_for_events.effect			= 'fade';
2678				data_for_events.speed			= slide_data.speed;
2679				data_for_events.layout			= slide_data.responseView;
2680                data_for_events.filter			= slide_data.responseFilterSelector;
2681
2682				data_for_history.view_number				= slide_data.view_number;
2683				data_for_history.page						= slide_data.page;
2684				data_for_history.effect						= 'fade';
2685				data_for_history.pagination_page_permalink	= slide_data.pagination_page_permalink;
2686
2687				$( '.js-wpv_slide_loading_img_' + slide_data.view_number ).fadeOut( function() {
2688					$( this ).remove();
2689				});
2690
2691				slide_data.wpvPaginatorLayout
2692					.css( {'position': 'absolute', 'z-index': '5'} )
2693					.after( slide_data.responseView )
2694						.next()
2695						.css( 'position', 'static' );
2696				// We need to set a zero timeout here since the above modifications introduce a race condition that produces a wrong new_height
2697				setTimeout( function() {
2698					new_height = slide_data.responseView.outerHeight();
2699					if ( old_height === new_height ) {
2700						slide_data.wpvPaginatorLayout.fadeOut( slide_data.speed, function() {
2701							slide_data.wpvPaginatorLayout.unwrap().remove();
2702
2703							slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2704						});
2705						// In the case when the old height is equal to the new height, we need to perform the fade-out of the old container and the
2706						// fade in of the new container in parallel, at the same time. Even though, UX-wise, this is not 100% correct, this choice
2707						// was made because of some clients complaints. This is due to backwards compatibility
2708						slide_data.responseView
2709							.hide()
2710							.css( 'visibility', 'visible' )
2711							.fadeIn( slide_data.speed, function() {
2712								window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2713								window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2714								slide_data.callback_next_func();
2715								self.manage_browser_history( data_for_history );
2716								$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2717								WPViews.view_pagination.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2718							});
2719					} else {
2720						slide_data.wpvPaginatorLayout.fadeOut( slide_data.speed, function() {
2721							slide_data.wpvPaginatorLayout
2722								.parent()
2723									.animate( {height: new_height+'px'}, slide_data.speed, function() {
2724										slide_data.wpvPaginatorLayout.unwrap().remove();
2725
2726										slide_data.wpvPaginatorFilter.html( slide_data.responseFilter );
2727
2728										slide_data.responseView
2729											.hide()
2730											.css( 'visibility', 'visible' )
2731											.fadeIn( slide_data.speed, function() {
2732												window.wpvPaginationAjaxLoaded[slide_data.view_number] = true;
2733												window.wpvPaginationAnimationFinished[slide_data.view_number] = true;
2734												slide_data.callback_next_func();
2735												self.manage_browser_history( data_for_history );
2736												$( document ).trigger( 'js_event_wpv_pagination_completed', [ data_for_events ] );
2737												WPViews.view_pagination.pagination_queue_trigger( slide_data.view_number, slide_data.next, slide_data.wpvPaginatorFilter );
2738											});
2739									});
2740						});
2741					}
2742				}, 0 );
2743			}
2744		};
2745
2746	};
2747
2748	/**
2749	* manage_browser_history
2750	*
2751	* Makes the history in the browser work with AJAX pagination, except infinite scrolling and sliders
2752	*
2753	* @param object data
2754	*
2755	* @since 1.11
2756	*/
2757
2758	self.manage_browser_history = function( data ) {
2759		if (
2760			self.get_paged_view( data.view_number ).type != 'rollover'
2761		) {
2762			if ( self.get_paged_view( data.view_number ).manage_history == 'enabled' ) {
2763				if ( self.add_paginated_history == true ) {
2764					if ( ! _.contains( self.pagination_effect_state_keep, data.effect ) ) {
2765						if ( _.contains( self.pagination_effect_state_replace, data.effect ) ) {
2766							history.replaceState( null, '', data.pagination_page_permalink );
2767						} else {
2768							self.last_paginated_view.push( data.view_number );
2769							state_obj = {
2770								view_number: data.view_number,
2771								page: data.page
2772							};
2773							history.pushState( state_obj, '', data.pagination_page_permalink );
2774							// http://scrollsample.appspot.com/items
2775							// http://html5.gingerhost.com/
2776							self.paginated_history_reach = self.paginated_history_reach + 1;
2777						}
2778					}
2779				} else {
2780					self.add_paginated_history = true;
2781				}
2782			}
2783		}
2784	};
2785
2786	/**
2787	* window.onpopstate
2788	*
2789	* Manages the browser back button click based on daya added by Views pagination
2790	*
2791	* @since 1.11
2792	*/
2793
2794	window.onpopstate = function( event ) {
2795		if ( event.state == null ) {
2796			var last_paginated_view_number = self.last_paginated_view.pop();
2797			if ( last_paginated_view_number != undefined ) {
2798				self.add_paginated_history = false;
2799				self.paged_views_initial_page[ last_paginated_view_number ] = self.paged_views_initial_page[ last_paginated_view_number ] || 1;
2800				self.trigger_pagination( last_paginated_view_number, self.paged_views_initial_page[ last_paginated_view_number ] );
2801			}
2802		} else {
2803			if (
2804				_.has( event.state, 'view_number' )
2805				&& _.has( event.state, 'page' )
2806			) {
2807				self.add_paginated_history = false;
2808				self.trigger_pagination( event.state.view_number, event.state.page );
2809			}
2810		}
2811	};
2812
2813	/**
2814	* When the parametric search with automatic results has been completed, reset the pagination history and add a state with the current URL
2815	*
2816	* @since 1.11
2817	*/
2818
2819	$( document ).on( 'js_event_wpv_parametric_search_results_updated', function( event, data ) {
2820		window.wpvCachedPages[ data.view_unique_id ] = [];
2821		self.last_paginated_view = [];
2822		if ( self.paginated_history_reach > 0 ) {
2823			window.history.go( -Math.abs( self.paginated_history_reach ) );
2824			self.paginated_history_reach = 0;
2825		}
2826		// HACK! HACK! HACK!
2827		// Chrome and Safari execute history.replaceState before history.go so we end up with a mess
2828		// That is why we need to set a timeout here
2829		// See https://code.google.com/p/chromium/issues/detail?id=529810
2830		setTimeout( function() {
2831			history.replaceState( null, '', data.permalink );
2832		}, 100 );
2833
2834		self.updateLanguageSwitcherURLs( data.permalink );
2835	});
2836
2837	/**
2838	 * Updates the href attribute of the language switcher anchor for the cases where a custom search add URL parameters
2839	 * on the page's URL.
2840	 *
2841	 * @param string permalink
2842	 *
2843	 * @since 2.7.0
2844	 */
2845	self.updateLanguageSwitcherURLs = function( permalink ) {
2846		$( '.wpml-ls-item' ).each(
2847			function( index, langSwitcher ) {
2848				var parsedPermalink = self.parseURL( permalink ),
2849					langSwitcherHref = $( langSwitcher ).find( 'a' ).first().attr( 'href' ),
2850					parsedLangSwitcherHref = self.parseURL( langSwitcherHref );
2851
2852				// For language switchers displayed as a dropdown list, the link for the default language is a hashtag,
2853				// so nothing to do in this case.
2854				if ( '#' === langSwitcherHref ) {
2855					return;
2856				}
2857
2858				delete parsedPermalink.params.lang;
2859
2860				var finalParams = $.extend( {}, parsedLangSwitcherHref.params, parsedPermalink.params ),
2861					strippedLanguageSwitcherHref = langSwitcherHref.replace( parsedLangSwitcherHref.query, '' );
2862
2863				$( langSwitcher ).find( 'a' ).first().attr( 'href', strippedLanguageSwitcherHref + '?' + $.param( finalParams ) );
2864			}
2865		);
2866	};
2867
2868	/**
2869	 * Parses a URL string via the DOM API.
2870	 *
2871	 * It creates a new anchor element and uses location properties (inherent) to get the desired URL data. Some String
2872	 * operations are used (to normalize results across browsers).
2873	 *
2874	 * @note Taken from https://j11y.io/javascript/parsing-urls-with-the-dom/ and adjusted accordingly.
2875	 *
2876	 * @param url
2877	 * @returns {{source: *, protocol: *, host: *, port: *, query: *, params, file: *, hash: *, path: *, relative: *, segments: *}}
2878	 */
2879	self.parseURL = function( url ) {
2880		var a =  document.createElement( 'a' );
2881		a.href = url;
2882		return {
2883			source: url,
2884			protocol: a.protocol.replace( ':', '' ),
2885			host: a.hostname,
2886			port: a.port,
2887			query: a.search,
2888			params: ( function() {
2889				var ret = {},
2890					seg = a.search.replace( /^\?/, '' ).split( '&' ),
2891					len = seg.length, i = 0, s;
2892				for ( ; i < len; i++ ) {
2893					if ( ! seg[ i ] ) { continue; }
2894					s = seg[ i ].split( '=' );
2895					var key = decodeURI( s[0] ),
2896						value = s[1];
2897					switch ( typeof ret[ key ] ) {
2898						case 'string':
2899							value = [ ret[ key ], s[1] ];
2900							break;
2901						case 'object':
2902							value = ret[ key ];
2903							value.push( s[1] );
2904							break;
2905					}
2906					ret[ key ] = value;
2907				}
2908				return ret;
2909			} )(),
2910			file: ( a.pathname.match( /\/([^/?#]+)$/i) || [,''] )[ 1 ],
2911			hash: a.hash.replace( '#', '' ),
2912			path: a.pathname.replace( /^([^/])/, '/$1' ),
2913			relative: ( a.href.match( /tps?:\/\/[^/]+(.+)/ ) || [,''] )[ 1 ],
2914			segments: a.pathname.replace( /^\//, '' ).split( '/' )
2915		};
2916	};
2917
2918	/**
2919	* init_effects_conditions
2920	*
2921	* Lets you define a condition that an effect must meet to be applied, doing nothing otherwise.
2922	* Each callback gets the following parameters:
2923	* @param object	view_pagination_data
2924	* @param int	page
2925	*
2926	* @since 1.11
2927	*/
2928
2929	self.init_effects_conditions = function() {
2930		self.pagination_effects_conditions = {
2931			infinite: function( view_pagination_data, page ) {
2932				if ( page != ( view_pagination_data.page + 1 ) ) {
2933					return false;
2934				}
2935				return true;
2936			}
2937		};
2938	};
2939
2940	/**
2941	* init_effects_conditions
2942	*
2943	* Lets you define a condition that an effect must meet to be applied, doing nothing otherwise.
2944	* Each callback gets the following parameters:
2945	* @param string	view_number
2946	* @param object	wpvPaginatorLayout
2947	*
2948	* @since 1.11
2949	*/
2950
2951	self.init_effects_spinner = function() {
2952		self.pagination_effects_spinner['fade']			=
2953		self.pagination_effects_spinner['slideleft']	=
2954		self.pagination_effects_spinner['slideright']	=
2955		self.pagination_effects_spinner['sliderightforward']	=
2956		self.pagination_effects_spinner['slideh']		=
2957		self.pagination_effects_spinner['slideup']		=
2958		self.pagination_effects_spinner['slidedown']	=
2959		self.pagination_effects_spinner['slidedownforward']	=
2960		self.pagination_effects_spinner['slidev']		= function( view_number, wpvPaginatorLayout ) {
2961			var img = new Image();
2962			img.src = self.get_paged_view( view_number ).spinner_image;
2963			img.onload = function() {
2964				var wpvPaginatorLayoutOffset = wpvPaginatorLayout.position(),
2965				wpvPaginatorSpinner = '<div style="'
2966					+ 'width:' + img.width + 'px;'
2967					+ 'height:' + img.height + 'px;'
2968					+ 'border:none;'
2969					+ 'background:transparent 50% 50% no-repeat url(' + self.get_paged_view( view_number ).spinner_image + ');'
2970					+ 'position:absolute;'
2971					+ 'z-index:99;'
2972					+ 'top:' + ( Math.round( wpvPaginatorLayoutOffset.top ) + ( Math.round( wpvPaginatorLayout.height()/2 ) ) - ( Math.round( img.height/2 ) ) ) + 'px;'
2973					+ 'left:' + ( Math.round( wpvPaginatorLayoutOffset.left ) + ( Math.round( wpvPaginatorLayout.width()/2 ) ) - ( Math.round( img.width/2 ) ) ) + 'px;'
2974					+ '" '
2975					+ 'class="wpv_slide_loading_img js-wpv_slide_loading_img_' + view_number + '"'
2976					+ '>'
2977					+ '</div>';
2978				wpvPaginatorLayout
2979					.before( wpvPaginatorSpinner )
2980						.animate( { opacity: 0.5 }, 300 );
2981			};
2982		};
2983		self.pagination_effects_spinner['infinite'] = function( view_number, wpvPaginatorLayout ) {
2984			var img = new Image();
2985			img.src = self.get_paged_view( view_number ).spinner_image;
2986			img.onload = function() {
2987				var wpvPaginatorLayoutOffset = wpvPaginatorLayout.position(),
2988				wpvPaginatorSpinner = '<div style="'
2989					+ 'width:' + img.width + 'px;'
2990					+ 'height:' + img.height + 'px;'
2991					+ 'border:none;'
2992					+ 'background:transparent 50% 50% no-repeat url(' + self.get_paged_view( view_number ).spinner_image + ');'
2993					+ 'position:absolute;'
2994					+ 'z-index:99;'
2995					+ 'top:' + ( Math.round( wpvPaginatorLayoutOffset.top ) + ( wpvPaginatorLayout.height() ) - ( Math.round( img.height/2 ) ) ) + 'px;'
2996					+ 'left:' + ( Math.round( wpvPaginatorLayoutOffset.left ) + ( Math.round( wpvPaginatorLayout.width()/2 ) ) - ( Math.round( img.width/2 ) ) ) + 'px;'
2997					+ '" '
2998					+ 'class="wpv_slide_loading_img js-wpv_slide_loading_img_' + view_number + '"'
2999					+ '>'
3000					+ '</div>';
3001				wpvPaginatorLayout
3002					.before( wpvPaginatorSpinner )
3003						.animate( { opacity: 0.5 }, 300 );
3004			};
3005		};
3006	};
3007
3008	/**
3009	* init_paged_views
3010	*
3011	* Gather the data for paginating each of the Views rendered in a page
3012	*
3013	* @since 1.11
3014	*/
3015
3016	self.init_paged_views = function( container ) {
3017		var init_scrolling_event = false,
3018		init_rollover_timing = false;
3019		this.historyP = this.historyP || [];
3020		window.wpvCachedPages = window.wpvCachedPages || [];
3021		window.wpvCachedImages = window.wpvCachedImages || [];
3022		$( '.js-wpv-view-layout', container ).each( function() {
3023			var thiz = $( this ),
3024			view_number = thiz.data( 'viewnumber' );
3025			self.init_paged_view( view_number );
3026			if (
3027				self.paged_views[ view_number ].effect == 'infinite'
3028				&& self.paged_views[ view_number ].page == 1
3029			) {
3030				init_scrolling_event = true;
3031			}
3032			if (
3033				self.paged_views[ view_number ].type == 'rollover'
3034				&& ! wpv_stop_rollover.hasOwnProperty( view_number )
3035			) {
3036				init_rollover_timing	= true;
3037				rollover_has_index		= _.findIndex( self.rollover_running, function( item ) {
3038					return item.view_number == view_number;
3039				});
3040				if ( rollover_has_index < 0 ) {
3041					// This rollover was not there, so trigger it
3042					var thiz_rollover_data = {
3043						view_number:	view_number,
3044						page:			1,
3045						force_reset:	false
3046					};
3047					self.rollover_running.push( thiz_rollover_data );
3048					if ( self.init_rollover_timing_fired ) {
3049						self.trigger_rollover( thiz_rollover_data );
3050					}
3051				} else {
3052					self.rollover_running[ rollover_has_index ] = {
3053						view_number:	view_number,
3054						page:			1,
3055						force_reset:	true
3056					};
3057				}
3058			}
3059		});
3060		if (
3061			! self.init_scrolling_event_fired
3062			&& init_scrolling_event
3063		) {
3064			self.init_scrolling_event_callback();
3065		}
3066
3067		if (
3068			! self.init_rollover_timing_fired
3069			&& init_rollover_timing
3070		) {
3071			self.init_rollover_timing_callback();
3072		}
3073		return self;
3074	};
3075
3076	/**
3077	* init_paged_view
3078	*
3079	* Gather pagination info for a specific View rendered in a page.
3080	* Note that this is also used to init the View pagination data after a parametric search change.
3081	*
3082	* @todo check the existence of $( '#wpv-view-layout-' + view_number ) and set defauls otherwise.
3083	*
3084	* @since 1.11
3085	*/
3086
3087	self.init_paged_view = function( view_number ) {
3088		this.historyP = this.historyP || [];
3089		self.paged_views[ view_number ] = $( '#wpv-view-layout-' + view_number ).data( 'pagination' );
3090		self.paged_views_initial_page[ view_number ] = self.paged_views[ view_number ].page;
3091		this.historyP[ view_number ] = self.paged_views[ view_number ].page;
3092		window.wpvCachedPages[ view_number ] = [];
3093		if (
3094			self.paged_views[ view_number ].type != 'disabled'
3095			&& self.paged_views[ view_number ].type != 'paged'
3096			&& self.paged_views[ view_number ].page > 1
3097		) {
3098			// Infinite scrolling only can be triggered from the first page - individual URLs can not have that effect
3099			$( '#wpv-view-layout-' + view_number ).removeClass( 'js-wpv-layout-infinite-scrolling' );
3100		}
3101		if ( $( '#wpv-view-layout-' + view_number ).parents( '.js-wpv-view-layout' ).length > 0 ) {
3102			// Disable history management in inner Views in nested structures
3103			self.paged_views[ view_number ].manage_history = 'disabled';
3104		}
3105	};
3106
3107	/**
3108	 * Generate the View pagintion data, on demand.
3109	 *
3110	 * This method will ensure that we can generate the pagination data for a View JIT
3111	 * when it is needed for a View which was dynamically added to the page, by a
3112	 * third party provider.
3113	 *
3114	 * @param view_number string The View unique hash.
3115	 *
3116	 * @since 2.3.1
3117	 */
3118
3119	self.get_paged_view = function( view_number ) {
3120		if ( ! _.has( self.paged_views, view_number ) ) {
3121			self.init_paged_view( view_number );
3122		}
3123		return self.paged_views[ view_number ];
3124	}
3125
3126	/**
3127	* is_infinite_triggable
3128	*
3129	* Auxiliar method to check whether the scroll got to a point where a pagination event should be triggered
3130	*
3131	* @param object	view_layout
3132	*
3133	* @since 1.11
3134	* @since 2.0	Add a tolerance setting
3135	* @since 2.0	Trigger the pagination when scrolling to the bottom of the page
3136	*/
3137
3138	self.is_infinite_triggable = function( view_layout ) {
3139		var flag_element = view_layout,
3140		view_number = view_layout.data( 'viewnumber' );
3141		infinite_tolerance = self.get_paged_view( view_number ).infinite_tolerance;
3142		infinite_tolerance = ( isNaN( infinite_tolerance ) ) ? 0 : + infinite_tolerance;
3143		if ( view_layout.find( '.js-wpv-loop' ).length > 0 ) {
3144			flag_element = view_layout.find( '.js-wpv-loop' );
3145		}
3146		return (
3147			( flag_element.offset().top + flag_element.outerHeight() ) <= ( $( window ).scrollTop() + $( window ).height() + infinite_tolerance )
3148			|| ( $( window ).scrollTop() + $(window).height() ) == $( document ).height()
3149		);
3150	};
3151
3152	/**
3153	* init_scrolling_event_callback
3154	*
3155	* Init the scrolling event callback, only when there is a View with infinite scrolling in a page
3156	*
3157	* @since 1.11
3158	*/
3159
3160	self.init_scrolling_event_callback = function() {
3161		$( window ).on( 'scroll', _.debounce(
3162			_.throttle(
3163				function() {
3164					$( '.js-wpv-layout-infinite-scrolling' ).each( function() {
3165						var thiz = $( this ),
3166						thiz_view_number = thiz.data( 'viewnumber' ),
3167						thiz_paged_view_data = self.get_paged_view( thiz_view_number );
3168						if (
3169							thiz_paged_view_data.page < thiz_paged_view_data.max_pages
3170							&& self.is_infinite_triggable( thiz )
3171						) {
3172							self.trigger_pagination( thiz_view_number, thiz_paged_view_data.page + 1 );
3173						}
3174					});
3175				},
3176				1000
3177			),
3178			1000
3179		));
3180		self.init_scrolling_event_fired = true;
3181	};
3182
3183	/**
3184	* trigger_rollover
3185	*
3186	* Trigger the rollover on a given View, using timeout
3187	*
3188	* @since 2.2
3189	*/
3190
3191	self.trigger_rollover = function( view_data ) {
3192		var rollover_data	= $.extend( {}, { view_number: '', page: 1, force_reset: false }, view_data ),
3193		view_number			= rollover_data.view_number,
3194		paged_view_data		= self.get_paged_view( view_number );
3195
3196		if (
3197			paged_view_data.max_pages > 1
3198			&& $("#wpv-view-layout-" + view_number ).length > 0
3199		) {
3200			setTimeout( function() {
3201				var rollover_index = _.findIndex( self.rollover_paused, function ( item ) {
3202                    return item.view_number == view_number;
3203                } );
3204                if ( self.rollover_paused[ rollover_index ]
3205                	&& self.rollover_paused[ rollover_index ].view_number
3206					&& self.rollover_paused[ rollover_index ].view_number == view_number
3207				) {
3208                    setTimeout( function() { self.trigger_rollover( view_data ) }, 100 );
3209                } else {
3210                    if (
3211                        ! wpv_stop_rollover.hasOwnProperty( view_number ) &&
3212						$( "#wpv-view-layout-" + view_number ).length > 0
3213                    ) {
3214                        // Get native current page and index on the rollovers array
3215                        var view_page = rollover_data.page,
3216                            rollover_index = _.findIndex( self.rollover_running, function ( item ) {
3217                                return item.view_number == view_number;
3218                            } );
3219                        // If reset is force, set the current page to 1 and stop the force
3220                        if ( self.rollover_running[ rollover_index ].force_reset ) {
3221                            self.rollover_running[ rollover_index ].force_reset = false;
3222                            view_page = 1;
3223                        }
3224                        // Calculate the page to rollover to
3225                        if (
3226                            paged_view_data.effect === 'slideright'
3227                            || paged_view_data.effect === 'slidedown'
3228                        ) {
3229                            // Note that this was deprecated in 2.2, keep for backwards compatibility
3230                            if ( view_page <= 1 ) {
3231                                view_page = paged_view_data.max_pages;
3232                            } else {
3233                                view_page = view_page - 1;
3234                            }
3235                        } else {
3236                            if ( view_page === paged_view_data.max_pages ) {
3237                                view_page = 1;
3238                            } else {
3239                                view_page = view_page + 1;
3240                            }
3241                        }
3242                        // Set the new current page
3243                        self.rollover_running[ rollover_index ].page = view_page;
3244                        WPViews.view_pagination.trigger_pagination( view_number, view_page );
3245                    } else {
3246                        var rollover_index = _.findIndex( self.rollover_running, function ( item ) {
3247                            return item.view_number == view_number;
3248                        });
3249                        self.rollover_running.splice( rollover_index, 1 );
3250                    }
3251                }
3252			}, paged_view_data.speed * 1000 );
3253		} else {
3254			var rollover_index	= _.findIndex( self.rollover_running, function( item ) {
3255				return item.view_number == view_number;
3256			});
3257			self.rollover_running.splice( rollover_index, 1 );
3258		}
3259	};
3260
3261	/**
3262	* init_rollover_timing_callback
3263	*
3264	* Init the rollover event callback, only when there is a View with rollover in a page
3265	*
3266	* @since 2.2
3267	* @since 2.3 Switched from iterating self.rollover_running to iterating a clone of it
3268	*			in order to avoid modification of the iterated array.
3269	*/
3270
3271	self.init_rollover_timing_callback = function() {
3272		// We are cloning the self.rollover_running array and iterating the clone as we want to avoid modifying
3273		// the array that is been iterated. Remember that self.trigger_rollover removes views from the self.rollover_running
3274		// array, that don't need to be initiated as a rollover, for example when the view has only one page of results.
3275		var rollover_running = self.rollover_running.slice(0);
3276		_.each( rollover_running, function( view_data, index, list ) {
3277			self.trigger_rollover( view_data );
3278		});
3279		self.init_rollover_timing_fired = true;
3280	};
3281
3282	$( document ).on( 'js_event_wpv_pagination_completed', function( event, data ) {
3283		if (
3284			self.get_paged_view( data.view_unique_id ).type == 'rollover'
3285			&& ! wpv_stop_rollover.hasOwnProperty( data.view_unique_id )
3286		) {
3287			var rollover_index = _.findIndex( self.rollover_running, function( item ) {
3288				return item.view_number == data.view_unique_id;
3289			});
3290			self.trigger_rollover( self.rollover_running[ rollover_index ] );
3291		}
3292	});
3293
3294	$( document ).on( 'js_event_wpv_parametric_search_results_updated', function( event, data ) {
3295		if (
3296			self.get_paged_view( data.view_unique_id ).type == 'rollover'
3297			&& ! wpv_stop_rollover.hasOwnProperty( data.view_unique_id )
3298		) {
3299			var rollover_index = _.findIndex( self.rollover_running, function( item ) {
3300				return item.view_number == data.view_unique_id;
3301			});
3302			self.trigger_rollover( self.rollover_running[ rollover_index ] );
3303		}
3304	});
3305
3306    $( document ).on( 'mouseenter', '.js-wpv-view-layout', function() {
3307        var view_number = $( this ).data( 'viewnumber' );
3308        if ( self.get_paged_view( view_number ).pause_on_hover == 'enabled' ) {
3309            var thiz_rollover_data = {
3310                view_number:	view_number,
3311            };
3312            self.rollover_paused.push( thiz_rollover_data );
3313        }
3314
3315    });
3316
3317    $( document ).on( 'mouseleave', '.js-wpv-view-layout', function() {
3318        var view_number = $( this ).data( 'viewnumber' );
3319        var rollover_index = _.findIndex( self.rollover_paused, function ( item ) {
3320            return item.view_number == view_number;
3321        });
3322        self.rollover_paused.splice( rollover_index, 1 );
3323    });
3324
3325	/**
3326	* pagination_init_preload_images
3327	*
3328	* Init-preload images.
3329	*
3330	* @since 1.9
3331	*/
3332
3333	self.init_preload_images = function( container ) {
3334		$( '.js-wpv-layout-preload-images', container ).css( 'visibility', 'hidden' ); // TODO move it to the CSS file and test
3335		$( '.js-wpv-layout-preload-images', container ).each( function() {
3336			var preloadedImages = [],
3337			element = $( this ),
3338			images = element.find( 'img' );
3339			if ( images.length < 1 ) {
3340				element.css( 'visibility', 'visible' );
3341			} else {
3342				images.one( 'load', function() {
3343					preloadedImages.push( $( this ).attr( 'src' ) );
3344					if ( preloadedImages.length === images.length ) {
3345						element.css( 'visibility', 'visible' );
3346					}
3347				}).each( function() {
3348					if( this.complete ) {
3349						$( this ).load();
3350					}
3351				});
3352				setTimeout( function() {
3353					element.css( 'visibility', 'visible' );
3354				}, 3000 );
3355			}
3356		});
3357	};
3358
3359	self.init_preload_pages = function( container ) {
3360		$( '.js-wpv-layout-preload-pages', container ).each( function() {
3361			var thiz = $( this ),
3362			view_number = thiz.data( 'viewnumber' ),
3363			paged_view_data = self.get_paged_view( view_number ),
3364			max_pages = parseInt( paged_view_data.max_pages, 10 ),
3365			max_reach = parseInt( paged_view_data.preload_reach, 10 ) + 1;
3366
3367			self.pagination_preload_pages({
3368				view_number:	view_number,
3369				cache_pages:	'disabled',
3370				preload_pages:	'enabled',
3371				page:			1,
3372				max_reach:		max_reach,
3373				max_pages:		max_pages
3374			});
3375		});
3376	};
3377
3378    /**
3379     * init_pagination_button_state
3380	 *
3381	 * Initiate the pagination's button state in the case of a Bootstrap pager (only "Previous" and "Next" links)
3382     *
3383     * @since 2.4.0
3384     */
3385    self.init_pagination_button_state = function() {
3386        $( '.pagination li:not(.disabled,.active)' ).has('span').addClass('disabled');
3387        $( '.pagination li.page-item:empty' ).remove();
3388	};
3389
3390	self.init = function() {
3391		self.init_effects();
3392		self.init_effects_conditions();
3393		self.init_effects_spinner();
3394		// Init Views inside the html
3395		var init_html = $( 'html' );
3396		self.init_paged_views( init_html );
3397		self.init_preload_images( init_html );
3398		self.init_preload_pages( init_html );
3399		self.init_pagination_button_state();
3400	}
3401
3402	self.init();
3403
3404};
3405
3406WPViews.ViewParametricSearch = function( $ ) {
3407
3408	// ------------------------------------
3409	// Constants and variables
3410	// ------------------------------------
3411
3412	var self = this;
3413
3414	// ------------------------------------
3415	// Methods
3416	// ------------------------------------
3417
3418	self.manage_update_results = function( data ) {
3419		if ( data.ajax_before !== '' ) {
3420			var ajax_before_func = window[data.ajax_before];
3421			if ( typeof ajax_before_func === "function" ) {
3422				ajax_before_func( data.view_num );
3423			}
3424		}
3425		var data_for_events = {};
3426		data_for_events.view_unique_id	= data.view_num;
3427		data_for_events.permalink	= data.permalink;
3428		data.layout.fadeOut( 200, function() {
3429			data.layout.html( data.new_layout.html() )
3430				.attr( 'data-pagination', data.new_layout.attr( 'data-pagination' ) )
3431				.data( 'pagination', data.new_layout.data( 'pagination' ) )
3432				.fadeIn( 'fast', function() {
3433					var ajax_after_func = window[data.ajax_after];
3434					if ( typeof ajax_after_func === "function" ) {
3435						ajax_after_func( data.view_num );
3436					}
3437					data_for_events.layout = data.layout;
3438					$( document ).trigger( 'js_event_wpv_parametric_search_results_updated', [ data_for_events ] );
3439				});
3440		});
3441	};
3442
3443	/**
3444	* manage_changed_form
3445	*
3446	* @param settings	object
3447	* 	{
3448	*		view_unique_id:			string			View unique ID
3449	*		form:					collection		Filter jQuery object
3450	* 		force_form_update:		boolean
3451	* 		update_form				boolean			Whether the form will be updated, either because forced or native
3452	* 		force_results_update:	boolean
3453	* 		update_results			boolean			Whether the results wil be updated, either because forced or native
3454	*	};
3455	*
3456	* @todo Switch .js-wpv-filter-data-for-this-form into a form data attribute that we can init on document.ready and refresh when neeeded
3457	*
3458	* @since 1.9
3459	* @since 2.1	Merge a group of parameters into an object
3460	*/
3461
3462	self.manage_changed_form = function( settings ) {
3463
3464		var fil				= settings.form,
3465		view_num			= settings.view_unique_id,
3466		lay					= $( '#wpv-view-layout-' + view_num ),
3467		full_data			= fil.find( '.js-wpv-filter-data-for-this-form' ),
3468		ajax_pre_before		= full_data.data( 'ajaxprebefore' ),
3469		view_type			= 'full',
3470		additional_forms	= $( '.js-wpv-filter-form-' + view_num ).not( fil ),
3471		additional_forms_only,
3472		additional_forms_full,
3473		ajax_get			= 'both',
3474		new_content_form,
3475		new_content_form_filter,
3476		new_content_full,
3477		new_content_full_filter,
3478		new_content_full_layout,
3479		spinnerContainer	= fil.find( '.js-wpv-dps-spinner' ).add( additional_forms.find( '.js-wpv-dps-spinner' ) ),
3480		spinnerItems		= spinnerContainer.length
3481		data_for_events		= {},
3482		data_for_manage_updated_results	= {};
3483
3484		data_for_events.view_unique_id	= view_num;
3485
3486		data_for_manage_updated_results.view_num		= view_num;
3487		data_for_manage_updated_results.ajax_before		= full_data.data( 'ajaxbefore' );
3488		data_for_manage_updated_results.ajax_after		= full_data.data( 'ajaxafter' );
3489
3490		if ( fil.hasClass( 'js-wpv-form-only' ) ) {
3491			view_type = 'form';
3492		}
3493		if ( settings.update_form ) {
3494			if ( additional_forms.length > 0 ) {
3495				additional_forms_only = additional_forms.not( '.js-wpv-form-full' );
3496				additional_forms_full = additional_forms.not( '.js-wpv-form-only' );
3497				if ( view_type == 'form' ) {
3498					if ( additional_forms_full.length > 0 || settings.update_results ) {
3499						ajax_get = 'both';
3500					} else {
3501						ajax_get = 'form';
3502					}
3503					if ( settings.update_results ) {
3504						if ( ajax_pre_before !== '' ) {
3505							var ajax_pre_before_func = window[ajax_pre_before];
3506							if ( typeof ajax_pre_before_func === "function" ) {
3507								ajax_pre_before_func( view_num );
3508							}
3509						}
3510						$( document ).trigger( 'js_event_wpv_parametric_search_started', [ data_for_events ] );
3511					}
3512					if ( spinnerItems ) {// TODO maybe only when updating results
3513						$( spinnerContainer ).fadeIn( 'fast' );
3514					}
3515
3516					wpv_stop_rollover[ view_num ] = true;
3517
3518					WPViews.view_frontend_utils.get_updated_query_results( view_num, 1, fil, ajax_get )
3519						.done( function( response ) {
3520							if ( response.success ) {
3521								new_content_form = $( '<div></div>' ).append( response.data.form );
3522								new_content_full = $( '<div></div>' ).append( response.data.full );
3523								new_content_form_filter = new_content_form.find( '.js-wpv-filter-form-' + view_num );
3524								new_content_full_filter = new_content_full.find( '.js-wpv-filter-form-' + view_num );
3525								new_content_full_layout = new_content_full.find( '.js-wpv-view-layout-' + view_num );
3526
3527								fil.html( new_content_form_filter.html() );
3528								WPViews.view_frontend_utils.destroy_frontend_datepicker();
3529								WPViews.view_frontend_utils.clone_form( fil, additional_forms_only );
3530								additional_forms_full.each( function() {
3531									$( this ).html( new_content_full_filter.html() );
3532								});
3533								data_for_events.view_changed_form						= fil;
3534								data_for_events.view_changed_form_additional_forms_only	= additional_forms_only;
3535								data_for_events.view_changed_form_additional_forms_full	= additional_forms_full;
3536								$( document ).trigger( 'js_event_wpv_parametric_search_form_updated', [ data_for_events ] );
3537								if ( settings.update_results ) {
3538									data_for_manage_updated_results.layout			= lay;
3539									data_for_manage_updated_results.new_layout		= new_content_full_layout;
3540									data_for_manage_updated_results.permalink		= response.data['parametric_permalink'];
3541									self.manage_update_results( data_for_manage_updated_results );
3542								}
3543								spinnerContainer.hide();
3544							}
3545						});
3546				} else {
3547					if ( additional_forms_only.length > 0 ) {
3548						ajax_get = 'both';
3549					} else {
3550						ajax_get = 'full';
3551					}
3552					if ( settings.update_results ) {
3553						if ( ajax_pre_before !== '' ) {
3554							var ajax_pre_before_func = window[ajax_pre_before];
3555							if ( typeof ajax_pre_before_func === "function" ) {
3556								ajax_pre_before_func( view_num );
3557							}
3558						}
3559						$( document ).trigger( 'js_event_wpv_parametric_search_started', [ data_for_events ] );
3560					}
3561					if ( spinnerItems ) {// TODO maybe only when updating results
3562						$( spinnerContainer ).fadeIn( 'fast' );
3563					}
3564
3565					wpv_stop_rollover[ view_num ] = true;
3566
3567					WPViews.view_frontend_utils.get_updated_query_results( view_num, 1, fil, ajax_get )
3568						.done( function( response ) {
3569							if ( response.success ) {
3570								new_content_form = $( '<div></div>' ).append( response.data.form );
3571								new_content_full = $( '<div></div>' ).append( response.data.full );
3572								new_content_form_filter = new_content_form.find( '.js-wpv-filter-form-' + view_num );
3573								new_content_full_filter = new_content_full.find( '.js-wpv-filter-form-' + view_num );
3574								new_content_full_layout = new_content_full.find( '.js-wpv-view-layout-' + view_num );
3575
3576								fil.html( new_content_full_filter.html() );
3577								WPViews.view_frontend_utils.destroy_frontend_datepicker();
3578								WPViews.view_frontend_utils.clone_form( fil, additional_forms_full );
3579								additional_forms_only.each( function() {
3580									$( this ).html( new_content_form_filter.html() );
3581								});
3582								data_for_events.view_changed_form						= fil;
3583								data_for_events.view_changed_form_additional_forms_only	= additional_forms_only;
3584								data_for_events.view_changed_form_additional_forms_full	= additional_forms_full;
3585								$( document ).trigger( 'js_event_wpv_parametric_search_form_updated', [ data_for_events ] );
3586								if ( settings.update_results ) {
3587									data_for_manage_updated_results.layout			= lay;
3588									data_for_manage_updated_results.new_layout		= new_content_full_layout;
3589									data_for_manage_updated_results.permalink		= response.data['parametric_permalink'];
3590									self.manage_update_results( data_for_manage_updated_results );
3591								}
3592								spinnerContainer.hide();
3593							}
3594						});
3595				}
3596			} else {
3597				if ( view_type == 'form' ) {
3598					if ( settings.update_results ) {
3599						ajax_get = 'both';
3600						// NOTE this should never happen:
3601						// If change is done on an only-form and there is no extra form, there is no full form thus there is no layout
3602						// WARNING this can be executed on an only-form form from a View with automatic results
3603						// I might want to avoid this branch completely
3604						// NOTE-2 might be a good idea to keep-on-clear// As we might be displaying the layout in non-standard ways
3605						// So keeping the check for lay.length should suffice
3606
3607					} else {
3608						ajax_get = 'form';
3609					}
3610
3611					if ( settings.update_results ) {
3612						if ( ajax_pre_before !== '' ) {
3613							var ajax_pre_before_func = window[ajax_pre_before];
3614							if ( typeof ajax_pre_before_func === "function" ) {
3615								ajax_pre_before_func( view_num );
3616							}
3617						}
3618						$( document ).trigger( 'js_event_wpv_parametric_search_started', [ data_for_events ] );
3619					}
3620					if ( spinnerItems ) {// TODO maybe only when updating results
3621						$( spinnerContainer ).fadeIn( 'fast' );
3622					}
3623
3624					wpv_stop_rollover[ view_num ] = true;
3625
3626					WPViews.view_frontend_utils.get_updated_query_results( view_num, 1, fil, ajax_get )
3627						.done( function( response ) {
3628							if ( response.success ) {
3629								new_content_form = $( '<div></div>' ).append( response.data.form );
3630								new_content_full = $( '<div></div>' ).append( response.data.full );
3631								new_content_form_filter = new_content_form.find( '.js-wpv-filter-form-' + view_num );
3632								//new_content_full_filter = new_content_full.find( '.js-wpv-filter-form' ).html();
3633								new_content_full_layout = new_content_full.find( '.js-wpv-view-layout-' + view_num );
3634								fil.html( new_content_form_filter.html() );
3635								data_for_events.view_changed_form						= fil;
3636								data_for_events.view_changed_form_additional_forms_only	= additional_forms_only;
3637								data_for_events.view_changed_form_additional_forms_full	= additional_forms_full;
3638								$( document ).trigger( 'js_event_wpv_parametric_search_form_updated', [ data_for_events ] );
3639								if ( settings.update_results ) {
3640									data_for_manage_updated_results.layout			= lay;
3641									data_for_manage_updated_results.new_layout		= new_content_full_layout;
3642									data_for_manage_updated_results.permalink		= response.data['parametric_permalink'];
3643									self.manage_update_results( data_for_manage_updated_results );
3644								}
3645								spinnerContainer.hide();
3646							}
3647						});
3648				} else {
3649					if ( settings.update_results ) {
3650						if ( ajax_pre_before !== '' ) {
3651							var ajax_pre_before_func = window[ajax_pre_before];
3652							if ( typeof ajax_pre_before_func === "function" ) {
3653								ajax_pre_before_func( view_num );
3654							}
3655						}
3656						$( document ).trigger( 'js_event_wpv_parametric_search_started', [ data_for_events ] );
3657					}
3658					if ( spinnerItems ) {// TODO maybe only when updating results
3659						$( spinnerContainer ).fadeIn( 'fast' );
3660					}
3661
3662					wpv_stop_rollover[ view_num ] = true;
3663
3664					WPViews.view_frontend_utils.get_updated_query_results( view_num, 1, fil, 'full' )
3665						.done( function( response ) {
3666							if ( response.success ) {
3667								//new_content_form = $( '<div></div>' ).append( ajax_result.form );
3668								new_content_full = $( '<div></div>' ).append( response.data.full );
3669								//new_content_form_filter = new_content_form.find( '.js-wpv-filter-form' ).html();
3670								new_content_full_filter = new_content_full.find( '.js-wpv-filter-form-' + view_num );
3671								new_content_full_layout = new_content_full.find( '.js-wpv-view-layout-' + view_num );
3672								fil.html( new_content_full_filter.html() );
3673								data_for_events.view_changed_form						= fil;
3674								data_for_events.view_changed_form_additional_forms_only	= additional_forms_only;
3675								data_for_events.view_changed_form_additional_forms_full	= additional_forms_full;
3676								$( document ).trigger( 'js_event_wpv_parametric_search_form_updated', [ data_for_events ] );
3677								if ( settings.update_results ) {
3678									data_for_manage_updated_results.layout			= lay;
3679									data_for_manage_updated_results.new_layout		= new_content_full_layout;
3680									data_for_manage_updated_results.permalink		= response.data['parametric_permalink'];
3681									self.manage_update_results( data_for_manage_updated_results );
3682								}
3683								spinnerContainer.hide();
3684							}
3685						});
3686				}
3687			}
3688		} else {
3689			if ( additional_forms.length > 0 ) {
3690				additional_forms_only = additional_forms.not( '.js-wpv-form-full' );
3691				additional_forms_full = additional_forms.not( '.js-wpv-form-only' );
3692				if ( view_type == 'form' ) {
3693					WPViews.view_frontend_utils.destroy_frontend_datepicker();
3694					WPViews.view_frontend_utils.clone_form( fil, additional_forms_only );
3695					if ( additional_forms_full.length > 0 || settings.update_results ) {
3696						if ( settings.update_results ) {
3697							if ( ajax_pre_before !== '' ) {
3698								var ajax_pre_before_func = window[ajax_pre_before];
3699								if ( typeof ajax_pre_before_func === "function" ) {
3700									ajax_pre_before_func( view_num );
3701								}
3702							}
3703							$( document ).trigger( 'js_event_wpv_parametric_search_started', [ data_for_events ] );
3704						}
3705						if ( spinnerItems ) {// TODO maybe only when updating results
3706							$( spinnerContainer ).fadeIn( 'fast' );
3707						}
3708
3709						wpv_stop_rollover[ view_num ] = true;
3710
3711						WPViews.view_frontend_utils.get_updated_query_results( view_num, 1, fil, 'full' )
3712							.done( function( response ) {
3713								if ( response.success ) {
3714									//new_content_form = $( '<div></div>' ).append( response.data.form );
3715									new_content_full = $( '<div></div>' ).append( response.data.full );
3716									//new_content_form_filter = new_content_form.find( '.js-wpv-filter-form' ).html();
3717									new_content_full_filter = new_content_full.find( '.js-wpv-filter-form-' + view_num );
3718									new_content_full_layout = new_content_full.find( '.js-wpv-view-layout-' + view_num );
3719
3720									additional_forms_full.each( function() {
3721										$( this ).html( new_content_full_filter.html() );
3722									});
3723									data_for_events.view_changed_form						= fil;
3724									data_for_events.view_changed_form_additional_forms_only	= additional_forms_only;
3725									data_for_events.view_changed_form_additional_forms_full	= additional_forms_full;
3726									$( document ).trigger( 'js_event_wpv_parametric_search_form_updated', [ data_for_events ] );
3727									if ( settings.update_results ) {
3728										data_for_manage_updated_results.layout			= lay;
3729										data_for_manage_updated_results.new_layout		= new_content_full_layout;
3730										data
3730_for_manage_updated_results.permalink		= response.data['parametric_permalink'];
3731										self.manage_update_results( data_for_manage_updated_results );
3732									}
3733									spinnerContainer.hide();
3734								}
3735							});
3736					} else {
3737						data_for_events.view_changed_form						= fil;
3738						data_for_events.view_changed_form_additional_forms_only	= additional_forms_only;
3739						data_for_events.view_changed_form_additional_forms_full	= additional_forms_full;
3740						$( document ).trigger( 'js_event_wpv_parametric_search_form_updated', [ data_for_events ] );
3741					}
3742				} else {
3743					WPViews.view_frontend_utils.destroy_frontend_datepicker();
3744					WPViews.view_frontend_utils.clone_form( fil, additional_forms_full );
3745					WPViews.view_frontend_utils.render_frontend_datepicker();
3746					if ( additional_forms_only.length > 0 || settings.update_results ) {
3747						if ( additional_forms_only.length > 0 ) {
3748							ajax_get = 'both';
3749						} else {
3750							ajax_get = 'full';
3751						}
3752
3753						if ( settings.update_results ) {
3754							if ( ajax_pre_before !== '' ) {
3755								var ajax_pre_before_func = window[ajax_pre_before];
3756								if ( typeof ajax_pre_before_func === "function" ) {
3757									ajax_pre_before_func( view_num );
3758								}
3759							}
3760							$( document ).trigger( 'js_event_wpv_parametric_search_started', [ data_for_events ] );
3761						}
3762						if ( spinnerItems ) {// TODO maybe only when updating results
3763							$( spinnerContainer ).fadeIn( 'fast' );
3764						}
3765
3766						wpv_stop_rollover[ view_num ] = true;
3767
3768						WPViews.view_frontend_utils.get_updated_query_results( view_num, 1, fil, ajax_get )
3769							.done( function( response ) {
3770								if ( response.success ) {
3771									new_content_form = $( '<div></div>' ).append( response.data.form );
3772									new_content_full = $( '<div></div>' ).append( response.data.full );
3773									new_content_form_filter = new_content_form.find( '.js-wpv-filter-form-' + view_num );
3774									//new_content_full_filter = new_content_full.find( '.js-wpv-filter-form' ).html();
3775									new_content_full_layout = new_content_full.find( '.js-wpv-view-layout-' + view_num );
3776									additional_forms_only.each( function() {
3777										$( this ).html( new_content_form_filter.html() );
3778									});
3779									data_for_events.view_changed_form						= fil;
3780									data_for_events.view_changed_form_additional_forms_only	= additional_forms_only;
3781									data_for_events.view_changed_form_additional_forms_full	= additional_forms_full;
3782									$( document ).trigger( 'js_event_wpv_parametric_search_form_updated', [ data_for_events ] );
3783									if ( settings.update_results ) {
3784										data_for_manage_updated_results.layout			= lay;
3785										data_for_manage_updated_results.new_layout		= new_content_full_layout;
3786										data_for_manage_updated_results.permalink		= response.data['parametric_permalink'];
3787										self.manage_update_results( data_for_manage_updated_results );
3788									}
3789									spinnerContainer.hide();
3790								}
3791							});
3792					}
3793				}
3794			} else {
3795				if ( settings.update_results ) {
3796					if ( ajax_pre_before !== '' ) {
3797						var ajax_pre_before_func = window[ajax_pre_before];
3798						if ( typeof ajax_pre_before_func === "function" ) {
3799							ajax_pre_before_func( view_num );
3800						}
3801					}
3802					$( document ).trigger( 'js_event_wpv_parametric_search_started', [ data_for_events ] );
3803					if ( spinnerItems ) {// TODO maybe only when updating results
3804						$( spinnerContainer ).fadeIn( 'fast' );
3805					}
3806
3807					wpv_stop_rollover[ view_num ] = true;
3808
3809					WPViews.view_frontend_utils.get_updated_query_results( view_num, 1, fil, 'full' )
3810						.done( function( response ) {
3811							if ( response.success ) {
3812								//new_content_form = $( '<div></div>' ).append( response.data.form );
3813								new_content_full = $( '<div></div>' ).append( response.data.full );
3814								//new_content_form_filter = new_content_form.find( '.js-wpv-filter-form' ).html();
3815								//new_content_full_filter = new_content_full.find( '.js-wpv-filter-form' ).html();
3816								new_content_full_layout = new_content_full.find( '.js-wpv-view-layout-' + view_num );
3817								data_for_manage_updated_results.layout			= lay;
3818								data_for_manage_updated_results.new_layout		= new_content_full_layout;
3819								data_for_manage_updated_results.permalink		= response.data['parametric_permalink'];
3820								self.manage_update_results( data_for_manage_updated_results );
3821								spinnerContainer.hide();
3822							}
3823						});
3824				}
3825			}
3826		}
3827	};
3828
3829	/**
3830	* Normalize the data passed to the js_event_wpv_parametric_search_triggered event.
3831	*
3832	* @para data	Object
3833	* 	{
3834	*		view_unique_id:			string			View unique ID
3835	*		form:					collection		Filter jQuery object
3836	* 		force_form_update:		boolean			Optional
3837	* 		force_results_update:	boolean			Optional
3838	*	};
3839	*
3840	* @return settings	Object
3841	* 	{
3842	*		view_unique_id:			string			View unique ID
3843	*		form:					collection		Filter jQuery object
3844	* 		force_form_update:		boolean
3845	* 		update_form				boolean			Whether the form will be updated, either because forced or native
3846	* 		force_results_update:	boolean
3847	* 		update_results			boolean			Whether the results wil be updated, either because forced or native
3848	*	};
3849	*
3850	* @since 2.1
3851	*/
3852
3853	self.extend_wpv_parametric_search_triggered_data = function( data ) {
3854		var defaults	= {
3855			force_form_update:		false,
3856			force_results_update:	false
3857		},
3858		settings		= $.extend( {}, defaults, data ),
3859		view_loop		= $( '#wpv-view-layout-' + settings.view_unique_id );
3860
3861		if (
3862			settings.force_form_update == false
3863			&& (
3864				// The form contains pagination settings and the results are set to be updated
3865				(
3866					settings.force_results_update == true
3867					|| settings.form.hasClass( 'js-wpv-ajax-results-enabled' )
3868				)
3869				&& _.has( WPViews.view_pagination.paged_views, settings.view_unique_id )
3870				&& _.has( WPViews.view_pagination.paged_views[ settings.view_unique_id ], 'has_controls_in_form' )
3871				&& WPViews.view_pagination.paged_views[ settings.view_unique_id ].has_controls_in_form == 'enabled'
3872			)
3873		) {
3874			settings.force_form_update = true;
3875		}
3876
3877		settings.update_form	= ( settings.force_form_update || settings.form.hasClass( 'js-wpv-dps-enabled' ) );
3878		settings.update_results	= ( view_loop.length > 0 && ( settings.force_results_update || settings.form.hasClass( 'js-wpv-ajax-results-enabled' ) ) );
3879
3880		return settings;
3881	};
3882
3883	// ------------------------------------
3884	// Events
3885	// ------------------------------------
3886
3887	// Show datepicker on date string click
3888	$( document ).on( 'click', '.js-wpv-date-display', function() {
3889		var url_param = $( this ).data( 'param' );
3890		$( '.js-wpv-date-front-end-' + url_param ).datepicker( 'show' );
3891	});
3892
3893	// Remove current selected date
3894	$( document ).on( 'click', '.js-wpv-date-front-end-clear', function(e) {
3895		e.preventDefault();
3896		var thiz = $( this ),
3897		url_param = thiz.data( 'param' ),
3898		form = thiz.closest( 'form' );
3899		form.find( '.js-wpv-date-param-' + url_param ).html( '' );
3900		form.find( '.js-wpv-date-front-end-' + url_param ).val( '' );
3901		thiz.hide();
3902		form.find('.js-wpv-date-param-' + url_param + '-value' )
3903			.val( '' )
3904			.trigger( 'change' );
3905	});
3906
3907	$( document ).on( 'change', '.js-wpv-post-relationship-update', function() {
3908		var thiz = $( this ),
3909		fil = thiz.closest( 'form' ),
3910		view_number = fil.data( 'viewnumber' ),
3911		additional_forms = $( '.js-wpv-filter-form-' + view_number ).not( fil ),
3912		currentposttype = thiz.data( 'currentposttype' ),
3913		watchers = fil.find( '.js-wpv-' + currentposttype + '-watch' ).add( additional_forms.find( '.js-wpv-' + currentposttype + '-watch' ) ),
3914		watcherslength = watchers.length,
3915		i;
3916		if ( watcherslength ) {
3917			for( i = 0; i < watcherslength; i++ ) {
3918				$( watchers[i] )
3919					.attr( 'disabled', true )
3920					.removeAttr( 'checked' )
3921					.removeAttr( 'selected' )
3922					.not( ':button, :submit, :reset, :hidden, :radio, :checkbox' )
3923					.val( '0' );
3924			}
3925		}
3926
3927		var data_for_events = {
3928			view_unique_id:		view_number,
3929			form:				fil,
3930			force_form_update:	true
3931		};
3932
3933		data_for_events = self.extend_wpv_parametric_search_triggered_data( data_for_events );
3934
3935		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
3936	});
3937
3938	$( document ).on( 'change', '.js-wpv-filter-trigger', function() {
3939		var thiz = $( this ),
3940		fil = thiz.closest( 'form' ),
3941		view_number = fil.data( 'viewnumber' );
3942
3943		var data_for_events = {
3944			view_unique_id:	view_number,
3945			form:			fil
3946		};
3947
3948		data_for_events = self.extend_wpv_parametric_search_triggered_data( data_for_events );
3949
3950		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
3951	});
3952
3953	$( document ).on( 'click', '.js-wpv-ajax-results-enabled .js-wpv-submit-trigger, .js-wpv-ajax-results-submit-enabled .js-wpv-submit-trigger', function( e ) {
3954		e.preventDefault();
3955		var thiz = $( this ),
3956		fil = thiz.closest( 'form' ),
3957		view_number = fil.data( 'viewnumber' );
3958
3959		var data_for_events = {
3960			view_unique_id:			view_number,
3961			form:					fil,
3962			force_form_update:		false,
3963			force_results_update:	true
3964		};
3965
3966		data_for_events = self.extend_wpv_parametric_search_triggered_data( data_for_events );
3967
3968		$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
3969	});
3970
3971	$( document).on( 'keypress', '.js-wpv-ajax-results-enabled .js-wpv-filter-trigger-delayed, .js-wpv-ajax-results-submit-enabled .js-wpv-filter-trigger-delayed', function( e ) {
3972		// Enter pressed?
3973		if ( e.which == 13 ) {
3974			e.preventDefault();
3975			var thiz = $( this ),
3976			fil = thiz.closest( 'form' ),
3977			view_number = fil.data( 'viewnumber' );
3978
3979			var data_for_events = {
3980				view_unique_id:			view_number,
3981				form:					fil,
3982				force_results_update:	true
3983			};
3984
3985			data_for_events = self.extend_wpv_parametric_search_triggered_data( data_for_events );
3986
3987			$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
3988		}
3989	});
3990
3991	$( document ).on( 'click', '.js-wpv-reset-trigger', function( e ) {
3992		e.preventDefault();
3993		var thiz = $( this ),
3994		fil = thiz.closest( 'form' ),
3995		view_number = fil.data( 'viewnumber' ),
3996		additional_forms = $( '.js-wpv-filter-form-' + view_number ).not( fil ),
3997		watchers,
3998		watcherslength,
3999		i,
4000		target = fil.attr( 'action' ),
4001		form_only_force_update = false,
4002		form_only_results_force_update = false;
4003
4004		if ( fil.hasClass( 'js-wpv-form-only' ) ) {
4005			watchers = fil.find( 'input, select' ).add( additional_forms.find( 'input, select' ) );
4006			watcherslength = watchers.length;
4007			form_only_force_update = ( fil.hasClass( 'js-wpv-dps-enabled' ) || ( fil.find( '.js-wpv-post-relationship-update' ).length > 0 ) );
4008			if (
4009				fil.hasClass( 'js-wpv-ajax-results-enabled' )
4010				|| fil.hasClass( 'js-wpv-ajax-results-submit-enabled' )
4011			) {
4012				form_only_results_force_update = true;
4013			}
4014
4015			if ( watcherslength ) {
4016				for ( i = 0; i < watcherslength; i++ ) {
4017					if ( ! $( watchers[i] ).hasClass( 'js-wpv-keep-on-clear' ) ) {
4018						$( watchers[i] )
4019							.attr( 'disabled', form_only_force_update )
4020							.removeAttr( 'checked' )
4021							.removeAttr( 'selected' )
4022							.not( ':button, :submit, :reset, :hidden, :radio, :checkbox' )
4023							.val( '' );
4024					}
4025				}
4026			}
4027
4028			var data_for_events = {
4029				view_unique_id:			view_number,
4030				form:					fil,
4031				force_form_update:		form_only_force_update,
4032				force_results_update:	form_only_results_force_update
4033			};
4034
4035			data_for_events = self.extend_wpv_parametric_search_triggered_data( data_for_events );
4036
4037			if( ! form_only_results_force_update && $( '#wpv-view-layout-' + fil.data( 'viewnumber' ) ).length > 0 ) {
4038                window.location.href = target;
4039			}
4040			else {
4041                $( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
4042			}
4043
4044		} else if ( fil.hasClass( 'js-wpv-ajax-results-enabled' ) || fil.hasClass( 'js-wpv-ajax-results-submit-enabled' ) ) {
4045			watchers = fil.find( 'input, select' ).add( additional_forms.find( 'input, select' ) );
4046			watcherslength = watchers.length;
4047			if ( watcherslength ) {
4048				for ( i = 0; i < watcherslength; i++ ) {
4049					if ( ! $( watchers[i] ).hasClass( 'js-wpv-keep-on-clear' ) ) {
4050						$( watchers[i] )
4051							.attr( 'disabled', true )
4052							.removeAttr( 'checked' )
4053							.removeAttr( 'selected' )
4054							.not( ':button, :submit, :reset, :hidden, :radio, :checkbox' )
4055							.val( '' );
4056					}
4057				}
4058			}
4059
4060			var data_for_events = {
4061				view_unique_id:			view_number,
4062				form:					fil,
4063				force_form_update:		true,
4064				force_results_update:	true
4065			};
4066
4067			data_for_events = self.extend_wpv_parametric_search_triggered_data( data_for_events );
4068
4069			$( document ).trigger( 'js_event_wpv_parametric_search_triggered', [ data_for_events ] );
4070		} else {
4071            window.location.href = WPViews.view_frontend_utils.updateUrlQuery( 'wpv_view_count', null, target );
4072		}
4073	});
4074
4075	$( document ).on( 'js_event_wpv_parametric_search_triggered', function( event, settings ) {
4076		self.manage_changed_form( settings );
4077	});
4078
4079	// Also, stop the rollover if we do any modification on the parametric search form
4080
4081	$( document ).on( 'change', '.js-wpv-filter-trigger, .js-wpv-filter-trigger-delayed', function() {
4082		var thiz = $( this ),
4083		fil = thiz.closest( 'form' ),
4084		view_num = fil.data( 'viewnumber' );
4085		wpv_stop_rollover[view_num] = true;
4086	});
4087
4088	// ------------------------------------
4089	// Custom events
4090	// ------------------------------------
4091
4092	/**
4093	* js_event_wpv_parametric_search_started
4094	*
4095	* Event fired before updating the parametric search forms and results.
4096	*
4097	* @param data
4098	* 	- view_unique_id
4099	*
4100	* @since 1.9
4101	*/
4102
4103	$( document ).on( 'js_event_wpv_parametric_search_started', function( event, data ) {
4104
4105	});
4106
4107
4108	/**
4109	* js_event_wpv_parametric_search_form_updated
4110	*
4111	* Event fired after updating the parametric search forms.
4112	*
4113	* @param data
4114	* 	- view_unique_id
4115	* 	- view_changed_form
4116	* 	- view_changed_form_additional_forms_only
4117	* 	- view_changed_form_additional_forms_full
4118	*
4119	* @since 1.9
4120	*/
4121
4122	$( document ).on( 'js_event_wpv_parametric_search_form_updated', function( event, data ) {
4123		WPViews.view_frontend_utils.render_frontend_datepicker();
4124	});
4125
4126	/**
4127	* js_event_wpv_parametric_search_results_updated
4128	*
4129	* Event fired after updating the parametric search results.
4130	*
4131	* @param data
4132	* 	- view_unique_id
4133	* 	- layout
4134	*
4135	* @since 1.9
4136	*/
4137
4138	$( document ).on( 'js_event_wpv_parametric_search_results_updated', function( event, data ) {
4139		WPViews.view_frontend_utils.render_frontend_media_shortcodes( data.layout );
4140	});
4141
4142	// ------------------------------------
4143	// Init
4144	// ------------------------------------
4145
4146	self.init = function() {
4147
4148	}
4149
4150	self.init();
4151
4152};
4153
4154jQuery( document ).ready( function( $ ) {
4155	WPViews.view_frontend_utils = new WPViews.ViewFrontendUtils( $ );
4156	WPViews.view_sorting = new WPViews.ViewSorting( $ );
4157	WPViews.view_pagination = new WPViews.ViewPagination( $ );
4158    WPViews.view_parametric_search = new WPViews.ViewParametricSearch( $ );
4159});

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.