PageSourceSearch

https://almerecity.nl/wp-content/themes/Impreza/common/js/elements/grid-filter.js?ver=8.46.1

js almerecity.nl collected 2026-10-02 11:43:23 UTC 22,375 bytes, 722 lines download raw bytes

1/**
2 * Grid Filter (deprecated)
3 */
4! function( $, _undefined ) {
5	"use strict";
6
7	const _document = document;
8
9	// Math API
10	const abs = Math.abs;
11
12	/**
13	 * @class usGridFilter
14	 * @param {String} container The container
15	 * @param {{}} options The options
16	 */
17	function usGridFilter( container, options ) {
18		const self = this;
19
20		// Related params for get data, number of records for taxonomy, price range for WooCommerce, etc.
21		self.filtersArgs = {};
22
23		// The default options
24		self.options = {
25			filterPrefix: 'filter', // default prefix
26			gridNotFoundMessage: false,
27			gridPaginationSelector: '.w-grid-pagination',
28			gridSelector: '.w-grid[data-filterable="true"]:first',
29			layout: 'hor',
30			mobileWidth: 600,
31			use_grid: 'first' // default
32		};
33
34		// Elements
35		self.$container = $( container );
36		self.$filtersItem = $( '.w-filter-item', container );
37
38		// Load json data
39		if ( self.$container.is( '[onclick]' ) ) {
40			$.extend( self.options, self.$container[0].onclick() || {} );
41			// Delete data everywhere except for the preview of the Live Builder, the data may be needed again to restore the elements
42			if ( ! $us.usbPreview() ) {
43				self.$container.removeAttr( 'onclick' );
44			}
45		}
46
47		// Connect use grid if it is set in options
48		if ( self.options.use_grid !== 'first' ) {
49			const $use_grid = $( self.options.use_grid );
50			if ( $use_grid.length && $use_grid.hasClass( 'w-grid' ) ) {
51				self.$grid = $use_grid;
52			}
53		}
54
55		// Finds the filter Grid
56		if ( $ush.isUndefined( self.$grid ) ) {
57			self.$grid = $( '.l-main ' + self.options.gridSelector, $us.$canvas );
58		}
59
60		// Load filters args
61		var $filtersArgs = $( '.w-filter-json-filters-args:first', self.$container );
62		if ( $filtersArgs.length ) {
63			self.filtersArgs = $filtersArgs[0].onclick() || {};
64			$filtersArgs.remove();
65		}
66
67		// Show the message when suitable Grid is not found
68		if ( ! self.$grid.length && self.options.gridNotFoundMessage ) {
69			self.$container.prepend( '<div class="w-filter-message">' + self.options.gridNotFoundMessage + '</div>' );
70		}
71
72		/**
73		 * @var {{}} Bindable events.
74		 */
75		self._events = {
76			changeFilter: self.changeFilter.bind( self ),
77			closeMobileFilters: self._closeMobileFilters.bind( self ),
78			openMobileFilters: self._openMobileFilters.bind( self ),
79			hideItemDropdown: self._hideItemDropdown.bind( self ),
80			loadPageNumber: self._loadPageNumber.bind( self ),
81			resetItemValues: self._resetItemValues.bind( self ),
82			resize: self._resize.bind( self ),
83			toggleItemSection: self._toggleItemSection.bind( self ),
84			showItemDropdown: self._showItemDropdown.bind( self ),
85			changeItemAtts: self._changeItemAtts.bind( self ),
86			updateItemsAmount: self._updateItemsAmount.bind( self ),
87			woocommerceOrdering: self._woocommerceOrdering.bind( self ),
88			navUsingKeyPress: self.navUsingKeyPress.bind( self ),
89		};
90
91		// Set class to define the grid is used by Grid Filter
92		self.$grid.addClass( 'used_by_grid_filter' );
93
94		// Events
95		self.$container
96			.on( 'click', '.w-filter-opener', self._events.openMobileFilters )
97			.on( 'click', '.w-filter-list-closer, .w-filter-list-panel > .w-btn', self._events.closeMobileFilters );
98
99		// Item events
100		self.$filtersItem
101			.on( 'change', 'input, select', self._events.changeFilter ) // exclude [type="number"] these types for range
102			.on( 'click', '.w-filter-item-reset', self._events.resetItemValues );
103
104		// Pagination
105		$( self.options.gridPaginationSelector, self.$grid )
106			.on( 'click', '.page-numbers', self._events.loadPageNumber );
107		$us.$window.on( 'resize load', $ush.debounce( self._events.resize, 10 ) );
108
109		// Built-in private event system
110		self.on( 'changeItemAtts', self._events.changeItemAtts );
111
112		// Show or Hide filter item
113		if ( self.$container.hasClass( 'drop_on_click' ) ) {
114			self.$filtersItem.on( 'click', '.w-filter-item-title', self._events.showItemDropdown );
115			$( _document ).on( 'mouseup', self._events.hideItemDropdown );
116		}
117
118		$us.$document.on( 'keydown', self._events.navUsingKeyPress );
119
120		// Add filter options to Woocommerce ordering
121		$( 'form.woocommerce-ordering', $us.$canvas )
122			.off( 'change', 'select.orderby' )
123			.on( 'change', 'select.orderby', self._events.woocommerceOrdering );
124
125		// Range Slider
126		$( '.ui-slider', self.$container ).each( function( _, node ) {
127			var $node = $( node ),
128				$parent = $node.parent(),
129				valueFormat = function( value ) {
130					value = $ush.toString( value );
131					if ( options.priceFormat ) {
132						var priceFormat = $ush.toPlainObject( options.priceFormat ),
133							decimals = $ush.parseInt( abs( priceFormat.decimals ) );
134						if ( decimals ) {
135							value = $ush.toString( $ush.parseFloat( value ).toFixed( decimals ) )
136								.replace( /^(\d+)(\.)(\d+)$/, '$1' + priceFormat.decimal_separator + '$3' );
137						}
138						value = value.replace( /\B(?=(\d{3})+(?!\d))/g, priceFormat.thousand_separator );
139					}
140					return $ush.toString( options.unitFormat ).replace( '%d', value );
141				},
142				showRangeResult = function( e, ui ) {
143					$( '.for_min_value', $parent ).html( valueFormat( ui.values[ 0 ] ) );
144					$( '.for_max_value', $parent ).html( valueFormat( ui.values[ 1 ] ) );
145				};
146			var options = $.extend( /* deep */true, {
147				// https://api.jqueryui.com/slider/#options
148				slider: {
149					animate: true,
150					max: 100,
151					min: 0,
152					range: true,
153					step: 1,
154					values: [ 0, 100 ],
155				},
156				unitFormat: '%d', // example: $0 000.00
157				priceFormat: null, // example: 0 000.00
158			}, node.onclick() || {} );
159			$node
160				.data( 'defautlValues', [ options.slider.min, options.slider.max ].join( '-' ) )
161				.removeAttr( 'onclick' )
162				.slider( $.extend( options.slider, {
163					slide: showRangeResult,
164					change: $ush.debounce( function( e, ui ) {
165						showRangeResult( e, ui );
166						$( 'input[type=hidden]', $parent )
167							.val( ui.values.join( '-' ) )
168							.trigger( 'change' );
169					} ),
170				} ) );
171		} );
172
173		// Change item values
174		self.checkItemValues();
175
176		// If there are selected params then add the class `active` to main container
177		self.$container.toggleClass( 'active', self.$filtersItem.hasClass( 'has_value' ) );
178
179		// Subscription to receive data on recounts of amounts
180		self.on( 'us_grid_filter.update-items-amount', self._events.updateItemsAmount );
181
182		// Set state to fix mobile Safari issue
183		self._events.resize();
184
185		if ( self.$container.hasClass( 'togglable' ) ) {
186			self.$filtersItem.on( 'click', '.w-filter-item-title', self._events.toggleItemSection );
187		}
188
189		self.setupTabindex();
190	};
191
192	// Export API
193	$.extend( usGridFilter.prototype, $us.mixins.Events, {
194
195		/**
196		 * Determines if mobile.
197		 *
198		 * @return {Boolean} True if mobile, False otherwise.
199		 */
200		isMobile: function() {
201			return $ush.parseInt( $us.$window.width() ) <= $ush.parseInt( this.options.mobileWidth );
202		},
203
204		/**
205		 * Change values.
206		 *
207		 * @event handler
208		 * @param {Event} e The Event interface represents an event which takes place in the DOM.
209		 */
210		changeFilter: function( e ) {
211			const self = this;
212			const $target = $( e.target );
213			const $item = $target.closest( '.w-filter-item' );
214			const type = $ush.toString( $item.usMod( 'type' ) );
215
216			// Locked filters
217			$item.removeClass( 'loading' );
218			self.$filtersItem
219				.not( $item )
220				.addClass( 'loading' );
221
222			if ( [ 'radio', 'checkbox' ].includes( type ) ) {
223				// Reset All
224				if ( type === 'radio' ) {
225					$( '.w-filter-item-value', $item )
226						.removeClass( 'selected' );
227				}
228				$target
229					.closest( '.w-filter-item-value' )
230					.toggleClass( 'selected', $target.is( ':checked ') );
231
232			} else if ( type === 'range' ) {
233				var $inputs = $( 'input[type!=hidden]', $item ),
234					rangeValues = [];
235				$inputs.each( ( i, input ) => {
236					let $input = $( input ),
237						value = $ush.parseInt( input.value ),
238						name = $ush.toString( input.dataset['name'] );
239					// If no value, check placeholders
240					if ( ! value && name == [ 'min_value', 'max_value' ][ i ] && rangeValues.length == i ) {
241						value = $input.attr( 'placeholder' );
242					}
243					rangeValues.push( $ush.parseInt( value ) );
244				} );
245
246				rangeValues = rangeValues.join( '-' );
247				$( 'input[type="hidden"]', $item ).val( rangeValues !== '0-0' ? rangeValues : '' );
248
249			} else if ( type === 'range_slider' ) {
250				var $input = $( 'input[type="hidden"]', $item );
251				if ( $input.val() == $ush.toString( $( '.ui-slider', $item ).data( 'defautlValues' ) ) ) {
252					$input.val( '' );
253				}
254			}
255
256			var value = self.getValue();
257			$ush.debounce_fn_1ms( self.URLSearchParams.bind( self, value ) );
258
259			self.triggerGrid( 'us_grid.updateState', [ value, /*page*/1, self ] );
260
261			// Change item values
262			self.trigger( 'changeItemAtts', $item );
263
264			// If there are selected params then add the class `active` to the main container
265			self.$container.toggleClass( 'active', self.$filtersItem.hasClass( 'has_value' ) );
266		},
267
268		/**
269		 * Load a grid page via AJAX.
270		 *
271		 * @event handler
272		 * @param {Event} e The Event interface represents an event which takes place in the DOM.
273		 */
274		_loadPageNumber: function ( e ) {
275			e.preventDefault();
276			e.stopPropagation();
277			var self = this,
278				$target = $( e.currentTarget ),
279				href = $ush.toString( $target.attr( 'href' ) ),
280				page = $ush.parseInt( ( href.match( /page(=|\/)(\d+)(\/?)/ ) || [] )[2] || /* default first page */1 );
281
282			history.replaceState( _document.title, _document.title, href );
283			self.triggerGrid( 'us_grid.updateState', [ self.getValue(), page, self ] );
284		},
285
286		/**
287		 * Reset item selected.
288		 *
289		 * @event handler
290		 * @param {Event} e The Event interface represents an event which takes place in the DOM.
291		 */
292		_resetItemValues: function( e ) {
293			e.preventDefault();
294			e.stopPropagation();
295
296			var self = this,
297				$item = $( e.currentTarget ).closest( '.w-filter-item' ),
298				type = $ush.toString( $item.usMod( 'type' ) );
299
300			if ( ! type ) {
301				return;
302			}
303
304			// Reset checkboxes and radio buttons.
305			if ( [ 'checkbox', 'radio' ].indexOf( type ) > -1 ) {
306				$( 'input:checked', $item ).prop( 'checked', false );
307				$( 'input[value="*"]:first', $item ).each( function( _, input ) {
308					$( input )
309						.prop( 'checked', true )
310						.closest( '.w-filter-item' )
311						.addClass( 'selected' );
312				} );
313			}
314			// Reset range values
315			if ( type === 'range' ) {
316				$( 'input', $item ).val( '' );
317			}
318			// Reset select option
319			if ( type === 'dropdown' ) {
320				$( 'option', $item ).prop( 'selected', false );
321			}
322			// Reset slider
323			if ( type === 'range_slider' ) {
324				var $uiSlider = $( '.ui-slider', $item );
325				$uiSlider.slider( 'values', $ush.toString( $uiSlider.data( 'defautlValues' ) ).split( '-' ) );
326			}
327
328			$( '.w-filter-item-value', $item ).removeClass( 'selected' );
329
330			// Change item values
331			self.trigger( 'changeItemAtts', $item );
332
333			// If there are selected params then add the class `active` to the main container
334			self.$container.toggleClass( 'active', self.$filtersItem.hasClass( 'has_value' ) );
335
336			// Update URL
337			var value = self.getValue();
338			$ush.debounce_fn_1ms( self.URLSearchParams.bind( self, value ) );
339			self.URLSearchParams( value );
340			self.triggerGrid( 'us_grid.updateState', [ value, /*page*/1, self ] );
341		},
342
343		/**
344		 * Change item class and title for visual indication
345		 *
346		 * @event handler
347		 * @param {{}} _ self
348		 * @param {*} item
349		 */
350		_changeItemAtts: function( _, item ) {
351			var self = this,
352				$item = $( item ),
353				title = '',
354				hasValue = false,
355				type = $ush.toString( $item.usMod( 'type' ) ),
356				$selected = $( 'input:not([value="*"]):checked', $item );
357
358			if ( ! type ) {
359				return;
360			}
361			// Get title from radio buttons and checkboxes
362			if ( [ 'checkbox', 'radio' ].indexOf( type ) > -1 ) {
363				hasValue = $selected.length;
364
365				// For a horizontal filter, if there are selected params, display either the selected parameter or quantity
366				if ( self.options.layout == 'hor' ) {
367					var title = '';
368					if ( $selected.length === 1 ) {
369						title += $selected.nextAll( '.w-filter-item-value-label:first' ).text();
370					} else if( $selected.length > 1 ) {
371						title += $selected.length;
372					}
373				}
374			}
375
376			if ( type === 'dropdown' ) {
377				var value = $( 'select:first', $item ).val();
378				hasValue = ( value !== '*' )
379					? value
380					: '';
381			}
382			// Get title from range inputs
383			if ( type === 'range' ) {
384				var value = $( 'input[type="hidden"]:first', $item ).val();
385				hasValue = value;
386				if ( self.options.layout == 'hor' && value ) {
387					title += value;
388				}
389			}
390			if ( type === 'range_slider' ) {
391				var value = $( 'input[type="hidden"]:first', $item ).val();
392				hasValue = value && value != $ush.toString( $( '.ui-slider', $item ).data( 'defautlValues' ) );
393			}
394
395			// Add of `has_value` class when selecting options
396			$item.toggleClass( 'has_value', !! hasValue );
397
398			// Add open class when selecting options
399			if (
400				self.$container.hasClass( 'togglable' )
401				&& hasValue
402			) {
403				$item.addClass( 'expand' );
404			}
405
406			// Set title
407			$( '> .w-filter-item-title > span:not(.w-filter-item-reset)', item ).html( title );
408		},
409
410		/**
411		 * Changes when resizing the screen.
412		 *
413		 * @event handler
414		 */
415		_resize: function() {
416			var self = this;
417			self.$container.usMod( 'state', self.isMobile() ? 'mobile' : 'desktop' );
418			if ( ! self.isMobile() ) {
419				$us.$body.removeClass( 'us_filter_open' );
420				self.$container.removeClass( /*filter opener*/'open_for_mobile' );
421			}
422		},
423
424		/**
425		 * Open Mobile Filter.
426		 *
427		 * @event handler
428		 */
429		_openMobileFilters: function() {
430			$us.$body.addClass( 'us_filter_open' );
431			this.$container.addClass( 'open_for_mobile' );
432		},
433
434		/**
435		 * Close Mobile Filter.
436		 *
437		 * @event handler
438		 */
439		_closeMobileFilters: function() {
440			$us.$body.removeClass( 'us_filter_open' );
441			this.$container.removeClass( 'open_for_mobile' );
442		},
443
444		/**
445		 * Show vertical items.
446		 *
447		 * @event handler
448		 * @param {Event} e The Event interface represents an event which takes place in the DOM.
449		 */
450		_showItemDropdown: function( e ) {
451			// Close all other items (extra check for accessibility browsing)
452			this._hideItemDropdown( e );
453			// Show current item
454			$( e.currentTarget )
455				.closest( '.w-filter-item' )
456				.addClass( 'expand' );
457		},
458
459		/**
460		 * Hide vertical items when click outside the item.
461		 *
462		 * @event handler
463		 * @param {Event} e The Event interface represents an event which takes place in the DOM.
464		 */
465		_hideItemDropdown: function( e ) {
466			var self = this;
467			if ( self.$filtersItem.hasClass( 'expand' ) ) {
468				self.$filtersItem.filter( '.expand' ).each( function( _, node ) {
469					var $node = $( node );
470					if ( ! $node.is( e.target ) && $node.has( e.target ).length === 0 ) {
471						$node.removeClass( 'expand' );
472					}
473				} );
474			}
475		},
476
477		/**
478		 * Show or hide accordion item on click .w-filter-item-title.
479		 *
480		 * @event handler
481		 * @param {Event} e The Event interface represents an event which takes place in the DOM.
482		 */
483		_toggleItemSection: function( e ) {
484			$( e.currentTarget )
485				.closest( '.w-filter-item' )
486				.toggleClass( 'expand' );
487		},
488
489		/**
490		 * Add grid filter options to sort request.
491		 *
492		 * @event handler
493		 * @param {Event} e The Event interface represents an event which takes place in the DOM.
494		 */
495		_woocommerceOrdering: function( e ) {
496			e.stopPropagation();
497
498			var self = this,
499				$form = $( e.currentTarget ).closest( 'form' );
500			$( 'input[name^="'+ self.options.filterPrefix +'"]', $form )
501				.remove();
502			$.each( self.getValue().split( '&' ), function( _, value ) {
503				value = value.split( '=' );
504				if ( value.length === 2 ) {
505					$form.append( '<input type="hidden" name="'+ value[0] +'" value="'+ value[1] +'"/>' );
506				}
507			} );
508			$form.trigger( 'submit' );
509		},
510
511		/**
512		 * Update amount items.
513		 *
514		 * @event handler
515		 * @param {usGridFilter} _
516		 * @param {{}} data
517		 */
518		_updateItemsAmount: function( _, data ) {
519			const self = this;
520
521			// Taxonomy updates
522			$.each( ( data.taxonomies_query_args || {} ), function( key, items ) {
523				var $item = self.$filtersItem.filter( '[data-source="'+ key +'"]' ),
524					type = $ush.toString( $item.usMod( 'type' ) ),
525					numActiveValues = 0;
526				$.each( items, function( value, amount ) {
527					var disabled = ! amount;
528					if ( ! disabled ) {
529						numActiveValues++;
530					}
531					// For dropdowns
532					if ( type === 'dropdown' ) {
533						var $option = $( 'select:first option[value="'+ value +'"]', $item ),
534							template = $option.data( 'template' ) || '';
535						// Apply option template
536						if ( template ) {
537							template = template
538								.replace( '%s', ( amount ? '(' + amount + ')' : '' ) );
539							$option.text( template );
540						}
541						$option
542							.prop( 'disabled', disabled )
543							.toggleClass( 'disabled', disabled );
544
545						// For inputs
546					} else {
547						var $input = $( 'input[value="'+ value +'"]', $item );
548
549						$input
550							.prop( 'disabled', disabled )
551							.nextAll( '.w-filter-item-value-amount' )
552							.text( amount );
553						$input
554							.closest( '.w-filter-item-value' )
555							.toggleClass( 'disabled', disabled );
556
557						// Disable option if there are no entries for it
558						if ( disabled && $input.is( ':checked' ) ) {
559							$input.prop( 'checked', false );
560						}
561					}
562				} );
563
564				$item.removeClass( 'loading' );
565				$item.toggleClass( 'disabled', numActiveValues < 1 );
566			} );
567
568			// Prices range update
569			if (
570				data.hasOwnProperty( 'wc_min_max_price' )
571				&& data.wc_min_max_price instanceof Object
572			) {
573				const $price = self.$filtersItem.filter( '[data-source$="|_price"]' );
574				$.each( ( data.wc_min_max_price || {} ), function( name, value ) {
575					var $input = $( 'input.type_' + name, $price );
576					$input.attr( 'placeholder', value ? value : $input.attr( 'aria-label' ) );
577				} );
578				$price.removeClass( 'loading' );
579			}
580
581			// Update URL
582			if ( ! $.isEmptyObject( data ) ) {
583				if ( self.handle ) {
584					$ush.clearTimeout( self.handle );
585				}
586				self.handle = $ush.timeout( function() {
587					$ush.debounce_fn_1ms( self.URLSearchParams.bind( self, self.getValue() ) );
588					self.checkItemValues();
589				}, 100 );
590			}
591
592			self.setupTabindex();
593		},
594
595		/**
596		 * Raises a private event in the grid.
597		 *
598		 * @param {String} eventType
599		 * @param {*} extraParams
600		 */
601		triggerGrid: function ( eventType, extraParams ) {
602			$ush.debounce_fn_10ms( function() { $us.$body.trigger( eventType, extraParams ); } );
603		},
604
605		/**
606		 * Check item values.
607		 */
608		checkItemValues: function() {
609			var self = this;
610			self.$filtersItem.each( function( _, node ) {
611				self.trigger( 'changeItemAtts', node );
612			} );
613		},
614
615		/**
616		 * Get the value.
617		 *
618		 * @return {String}
619		 */
620		getValue: function() {
621			var value = '',
622				filters = {};
623			$.each( this.$container.serializeArray(), function( _, filter ) {
624				if ( filter.value === /* all */'*' || ! filter.value ) {
625					return;
626				}
627				if ( ! filters.hasOwnProperty( filter.name ) ) {
628					filters[ filter.name ] = [];
629				}
630				filters[ filter.name ].push( filter.value );
631			} );
632			// Convert params in a string
633			for ( var k in filters ) {
634				if ( value ) {
635					value += '&';
636				}
637				if ( Array.isArray( filters[ k ] ) ) {
638					value += k + '=' + filters[ k ].join( ',' );
639				}
640			}
641
642			return encodeURI( value );
643		},
644
645		/**
646		 * Set search queries in the url.
647		 *
648		 * @param {String} params The url params.
649		 */
650		URLSearchParams: function( params ) {
651			var url = location.origin + location.pathname + ( location.pathname.slice( -1 ) != '/' ? '/' : '' ),
652				// Get current search and remove filter params
653				search = location.search.replace( new RegExp( this.options.filterPrefix + "(.+?)(&|$)", "g" ), '' );
654			if ( ! search || search.substr( 0, 1 ) !== '?' ) {
655				search += '?';
656			}
656 else if ( '?&'.indexOf( search.slice( -1 ) ) === -1 ) {
657				search += '&';
658			}
659			// Remove last ?&
660			if ( ! params && '?&'.indexOf( search.slice( -1 ) ) !== -1 ) {
661				search = search.slice( 0, -1 );
662			}
663			history.replaceState( _document.title, _document.title, url + search + params );
664		},
665
666		/**
667		 * Navigation using key press.
668		 *
669		 * @event handler
670		 * @param {Event} e The Event interface represents an event which takes place in the DOM.
671		 */
672		navUsingKeyPress: function( e ) {
673			const self = this;
674			const keyCode = e.keyCode;
675
676			if ( keyCode == $ush.ESC_KEYCODE ) {
677				self._hideItemDropdown( {} );
678			}
679
680			if (
681				keyCode === $ush.ENTER_KEYCODE
682				&& $.contains( self.$container[0], e.target )
683			) {
684				const $target = $( e.target );
685				if ( $target.hasClass( 'w-filter-item-value' ) ) {
686					$( 'input[type=radio], input[type=checkbox]', $target ).trigger( 'click' );
687				}
688			}
689		},
690
691		/**
692		 * Setup tabindex for horizontal layout and switch style
693		 */
694		setupTabindex: function() {
695			const self = this;
696			if ( self.options.layout === 'hor' && /\sstyle_switch_/.test( self.$container[0].className ) ) {
697				self.$filtersItem.each( ( _, filter ) => {
698					if ( filter.classList.contains( 'type_radio' ) || filter.classList.contains( 'type_checkbox' ) ) {
699						$( '.w-filter-item-value', filter ).each( ( _,  itemValue ) => {
700							const $itemValue = $( itemValue );
701							if ( $itemValue.hasClass( 'disabled' ) ) {
702								$itemValue.removeAttr( 'tabindex' );
703							} else {
704								$itemValue.attr( 'tabindex', 0 );
705							}
706						} );
707					}
708				} );
709			}
710		}
711
712	} );
713
714	$.fn.usGridFilter = function ( options ) {
715		return this.each( function () {
716			$( this ).data( 'usGridFilter', new usGridFilter( this, options ) );
717		} );
718	};
719
720	$( () => $( '.w-filter.for_grid', $us.$canvas ).usGridFilter() );
721
722}( jQuery );

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