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.