1DP.Views.Search.MainSearchView = Backbone.View.extend({ 2 3 events : { 4 'focus .main-search-input' : 'recentSearches', 5 'keyup .main-search-input' : 'autocomplete', 6 'click .js-main-search-ai-toggle' : 'toggleAi', 7 'click .js-main-search-ai-upload-btn' : 'openAiFilePicker', 8 'change .js-main-search-ai-file-input': 'onAiFileSelected', 9 'click .js-main-search-ai-analyze-btn': 'analyzeAiImage', 10 'keydown .js-main-search-ai-url-input': 'onAiUrlKeydown', 11 'click .js-main-search-ai-clarification-option': 'onAiClarificationOption', 12 'click .js-main-search-ai-new-search' : 'startNewAiSearch', 13 'click .js-tab-products' : 'productTab', 14 'click .js-tab-folders' : 'foldersTab', 15 'click .js-tab-vendors' : 'vendorsTab', 16 'click .vendor-login-small-icon' : 'openVendorHub', 17 'mouseenter .search_collapse_wrapper' : 'resetSearchCollapse', 18 'mouseenter .vendor-login-small-icon' : 'displayVendorLabel', 19 'mouseleave .vendor-login-small-icon' : 'hideVendorLabel' 20 }, 21 22 AI_PLACEHOLDER: 'Search by visual traits (e.g., chrome sled base, wood arms...)', 23 24 AI_IMAGE_MIME_TYPES: ['image/jpeg', 'image/png', 'image/webp'], 25 26 /** PixelSeek rejects decoded uploads over 5 MiB / 40 MP â downscale before sending. */ 27 AI_IMAGE_MAX_DIMENSION: 1600, 28 29 initialize: function(options) { 30 var _this = this; 31 var templateID = $('#main-search-template').html(); 32 33 _this.isGlassHover = options.isGlassHover; 34 _this.enableButtons = false; 35 _this.enableLeftRight = false; 36 _this.useAi = false; 37 _this.aiImageFile = null; 38 _this.lastPixelSeekPayload = null; 39 _this.pixelSeekRequestSeq = 0; 40 _this.autocompleteRequestSeq = 0; 41 /** Debounce delay (ms) before GET /autocompletes â avoids one request per keystroke */ 42 _this.autocompleteDebounceMs = 800; 43 _this.autocompleteDebounceTimer = null; 44 _this.template = _.template(templateID); 45 }, 46 47 render: function() { 48 $(this.el).html( this.template() ); 49 if (this.isGlassHover) { 50 $(this.el).addClass('main-search-glass-hover'); 51 } 52 53 this.initEvents(); 54 this.listen(); 55 }, 56 57 initEvents : function() { 58 var _this = this; 59 var searchInput = _this.$el.find('.main-search-input'); 60 var searchWrapper = _this.$el.find('.search_wrapper'); 61 var recentSearchWrapper = _this.$el.find('.recent_search_wrapper'); 62 var quickKeys = _this.$el.find('.quick_keys_wrapper'); 63 var fullSearchWrapper = _this.$el.find('.full_search_wrapper'); 64 var autoCompleteSection = document.getElementById("autocomplete-section"); 65 66 if( _this.isGlassHover ) { 67 _this.$el.find('.vendor-login-small-icon').addClass('short'); 68 _this.$el.find('.vendor-login-small-label').addClass('short'); 69 $('.stub-button').addClass('is-short'); 70 $('.header.header-full').find('.bottom').find('ul.browse').css('left', '25%'); 71 _this.$el.find('.search_collapse_wrapper').addClass('active'); 72 } else { 73 searchWrapper.addClass('display'); 74 } 75 76 // Adding action to show or hide search extra options when the user uses keys cmd + k 77 document.addEventListener('keydown', (event) => { 78 let isKKey = event.key === "k" || event.key === "K"; 79 let isMetaKey = event.metaKey; 80 81 if (isMetaKey && isKKey) { 82 $(autoCompleteSection).addClass('autocomplete-opacity-main-search'); 83 document.body.style.setProperty('overflow', 'hidden'); 84 if ( $(searchInput).hasClass('active') ) { 85 $(searchInput).blur(); 86 $(searchInput).removeClass('active'); 87 $(recentSearchWrapper).removeClass('active'); 88 $(quickKeys).removeClass('active'); 89 $(searchWrapper).removeClass('active'); 90 $(fullSearchWrapper).removeClass('active'); 91 $(searchInput).val(''); 92 $(searchInput).css('height', 'auto'); 93 } else { 94 $(searchInput).focus(); 95 $(searchInput).css('height', 'auto'); 96 }; 97 }; 98 }); 99 100 // Hide all serach extra options when the user clicks outside the search wrapper 101 document.addEventListener('click', (e) => { 102 // Use the DOM node (or closest) â composedPath contains Elements, not jQuery objects.
103 var searchWrapperEl = searchWrapper.get(0); 104 var isClickedInsideDiv = (searchWrapperEl && e.composedPath().indexOf(searchWrapperEl) !== -1) || 105 $(e.target).closest(searchWrapper).length > 0; 106 107 $(autoCompleteSection).addClass('autocomplete-opacity-main-search'); 108 document.body.style.setProperty('overflow', 'hidden'); 109 110 if (!$(e.target).hasClass('main-search-input') && !$(e.target).hasClass('js-search-tab-item') && !isClickedInsideDiv ) { 111 $(searchInput).val(''); 112 $(searchInput).removeClass('active'); 113 $(recentSearchWrapper).removeClass('active'); 114 $(quickKeys).removeClass('active'); 115 $(searchWrapper).removeClass('active'); 116 $(fullSearchWrapper).removeClass('active'); 117 $(searchWrapper).css('height', '10px'); 118 $(fullSearchWrapper).html(''); 119 $(autoCompleteSection).removeClass('autocomplete-opacity-main-search'); 120 document.body.style.setProperty('overflow', 'visible'); 121 _this.enableButtons = false; 122 _this.clearAutocompleteDebounce(); 123 _this.resetAiMode(); 124 125 if( _this.isGlassHover ) { 126 _this.$el.find('.vendor-login-small-icon').addClass('short'); 127 _this.$el.find('.vendor-login-small-label').addClass('short'); 128 } 129 130 if( !$(e.target).closest('.inner').length && _this.isGlassHover ) { 131 $('.stub-button').addClass('is-short'); 132 $('.header.header-full').find('.bottom').find('ul.browse').css('left', '25%'); 133 searchWrapper.removeClass('display').fadeOut(300); 134 _this.$el.find('.search_collapse_wrapper').addClass('active').fadeIn(0); 135 } else { 136 /* Clicks hit .browse-cat-button children (text, icon), not the button node â hasClass never matched. */ 137 var clickedBrowseChrome = $(e.target).closest('.browse-cat-button, .browse-cat-list, .browse-cat-wrapper').length > 0; 138 if( _this.isGlassHover && clickedBrowseChrome ) { 139 /* Keep collapsed three-icon bar; categories open from Browse without expanding main search. */ 140 } else { 141 searchWrapper.fadeIn(0).addClass('display'); 142 } 143 } 144 }; 145 }); 146 147 const osIcon = _this.$el.find('.icon-os'); 148 const currentOs = _this.operatingS(); 149 150 $(osIcon).addClass(currentOs === "Windows" ? "win" : "mac"); 151 }, 152 153 clearAutocompleteDebounce : function() { 154 if (this.autocompleteDebounceTimer) { 155 clearTimeout(this.autocompleteDebounceTimer); 156 this.autocompleteDebounceTimer = null; 157 } 158 }, 159 160 clearAiImageSelection : function() { 161 this.aiImageFile = null; 162 this.$el.find('.js-main-search-ai-upload-filename').text(''); 163 this.$el.find('.js-main-search-ai-file-input').val(''); 164 }, 165 166 // Clear Visual Search toggle, placeholder, and any pending image when the panel closes. 167 resetAiMode : function() { 168 this.useAi = false; 169 this.clearAiImageSelection(); 170 this.lastPixelSeekPayload = null; 171 this.applyAiChrome(); 172 }, 173 174 applyAiChrome : function() { 175 var searchInput = this.$el.find('.main-search-input'); 176 var toggle = this.$el.find('.js-main-search-ai-toggle'); 177 178 toggle.toggleClass('active', this.useAi); 179 toggle.attr('aria-pressed', this.useAi ? 'true' : 'false'); 180 searchInput.toggleClass('ai-mode', this.useAi); 181 182 if (this.useAi) { 183 searchInput.attr('placeholder', this.AI_PLACEHOLDER); 184 } else { 185 searchInput.removeAttr('placeholder'); 186 } 187 }, 188 189 renderAiIdlePanel : function() { 190 var fullSearchWrapper = this.$el.find('.full_search_wrapper'); 191 var searchWrapper = this.$el.find('.search_wrapper'); 192 var quickKeys = this.$el.find('.quick_keys_wrapper'); 193 var searchInput = this.$el.find('.main-search-input'); 194 var templateHtml = $('#main-search-ai-idle-template').html(); 195 if (!templateHtml) { return; } 196 197 fullSearchWrapper.html(_.template(templateHtml)()); 198 fullSearchWrapper.addClass('active'); 199 searchWrapper.addClass('active').css('height', 'auto'); 200 quickKeys.addClass('active'); 201 searchInput.addClass('active'); 202 }, 203 204 renderAiLoadingPanel : function() {
205 var fullSearchWrapper = this.$el.find('.full_search_wrapper'); 206 var templateHtml = $('#main-search-ai-loading-template').html(); 207 if (!templateHtml) { return; } 208 fullSearchWrapper.html(_.template(templateHtml)()); 209 fullSearchWrapper.addClass('active'); 210 }, 211 212 renderAiResultsPanel : function(options) { 213 var fullSearchWrapper = this.$el.find('.full_search_wrapper'); 214 var templateHtml = $('#main-search-ai-results-template').html(); 215 if (!templateHtml) { return; } 216 217 options = options || {}; 218 fullSearchWrapper.html(_.template(templateHtml)({ 219 results: options.results || [], 220 results_url: options.results_url || 'https://pixelseek.com', 221 error: options.error || null, 222 clarification: options.clarification || null, 223 status: options.status || null, 224 message: options.message || null 225 })); 226 fullSearchWrapper.addClass('active'); 227 }, 228 229 runPixelSeekSearch : function(payload) { 230 var _this = this; 231 var searchWrapper = _this.$el.find('.search_wrapper'); 232 var requestSeq = ++_this.pixelSeekRequestSeq; 233 234 // Keep the last image/query payload so a clarification pill can re-call with 235 // the chosen visual_type without re-uploading. 236 _this.lastPixelSeekPayload = $.extend({}, payload); 237 _this.renderAiLoadingPanel(); 238 239 DP.Actions.Search.PixelSeek.search(payload, function(response) { 240 if (requestSeq !== _this.pixelSeekRequestSeq) { return; } 241 if (!_this.useAi || !$(searchWrapper).hasClass('active')) { return; } 242 243 if (response && response.error) { 244 // Prefer API `message` when present (e.g. invalid image URL guidance). 245 _this.renderAiResultsPanel({ error: response.message || response.error }); 246 return; 247 } 248 249 var clarificationOptions = 250 (response && response.clarification && response.clarification.options) || []; 251 if (clarificationOptions.length > 0) { 252 _this.renderAiResultsPanel({ clarification: response.clarification }); 253 return; 254 } 255 256 _this.renderAiResultsPanel({ 257 results: (response && response.results) || [], 258 results_url: (response && response.results_url) || 'https://pixelseek.com', 259 status: response && response.status, 260 message: response && response.message 261 }); 262 }); 263 }, 264 265 onAiClarificationOption : function(e) { 266 e.preventDefault(); 267 e.stopPropagation(); 268 269 var visualType = $(e.currentTarget).data('visual-type'); 270 if (!visualType || !this.lastPixelSeekPayload) { return; } 271 272 var payload = $.extend({}, this.lastPixelSeekPayload, { visual_type: visualType }); 273 this.runPixelSeekSearch(payload); 274 }, 275 276 // Results "New search" â return to the idle upload / paste-URL panel (AI mode stays on). 277 startNewAiSearch : function(e) { 278 e.preventDefault(); 279 e.stopPropagation(); 280 281 var _this = this; 282 if (!_this.useAi) { return; } 283 284 _this.pixelSeekRequestSeq += 1; 285 _this.clearAiImageSelection(); 286 _this.lastPixelSeekPayload = null; 287 _this.$el.find('.main-search-input').val(''); 288 _this.renderAiIdlePanel(); 289 }, 290 291 toggleAi : function(e) { 292 e.preventDefault(); 293 e.stopPropagation(); 294 295 var _this = this; 296 _this.useAi = !_this.useAi; 297 _this.clearAutocompleteDebounce(); 298 _this.clearAiImageSelection(); 299 _this.lastPixelSeekPayload = null; 300 _this.applyAiChrome(); 301 302 var fullSearchWrapper = _this.$el.find('.full_search_wrapper'); 303 304 if (_this.useAi) { 305 _this.recentSearches(); 306 _this.renderAiIdlePanel(); 307 } else { 308 fullSearchWrapper.html('').removeClass('active'); 309 } 310 }, 311 312 openAiFilePicker : function(e) { 313 e.preventDefault(); 314 e.stopPropagation(); 315 this.$el.find('.js-main-search-ai-file-input').trigger('click'); 316 }, 317 318 onAiFileSelected : function(e) { 319 var file = e.target.files && e.target.files[0]; 320 var _this = this; 321 322 if (!file) { 323 _this.clearAiImageSelection(); 324 return; 325 } 326 327 if (_this.AI_IMAGE_MIME_TYPES.indexOf(file.type) === -1) { 328 _this.clearAiImageSelection(); 329 _this.showAiError('Unsupported image type. Use JPEG, PNG, or WebP.'); 330 return; 331 } 332 333 _this.aiImageFile = file; 334 _this.$el.find('.js-main-search-ai-upload-filename').text(file.name); 335 }, 336 337 onAiUrlKeydown : function(e) { 338 if (e.key !== 'Enter' && e.keyCode !== 13) { return; } 339 340 e.preventDefault(); 341 e.stopPropagation(); 342 this.analyzeAiImage(e); 343 }, 344 345 analyzeAiImage : function(e) { 346 if (e && e.preventDefault) { e.preventDefault(); } 347 if (e && e.stopPropagation) { e.stopPropagation(); } 348 349 var _this = this; 350 if (!_this.useAi) { return; } 351 352 var imageUrl = $.trim(_this.$el.find('.js-main-search-ai-url-input').val() || ''); 353 354 // PixelSeek accepts exactly one of image_url / image_base64. 355 if (_this.aiImageFile && imageUrl) { 356 _this.showAiError('Use either an image URL or an uploaded image, not both.'); 357 return; 358 } 359 360 if (_this.aiImageFile) { 361 _this.encodeAiImageFile(_this.aiImageFile, function(err, payload) { 362 if (err) { 363 _this.showAiError('Unable to read the selected image. Please try another file.'); 364 return; 365 } 366 _this.runPixelSeekSearch(payload); 367 }); 368 return; 369 } 370 371 if (imageUrl) { 372 _this.runPixelSeekSearch({ image_url: imageUrl }); 373 return; 374 } 375 376 _this.showAiError('Upload an image or paste an image URL to analyze.'); 377 }, 378 379 /** 380 * Downscales the file to AI_IMAGE_MAX_DIMENSION and encodes it as raw base64 381 * (no "data:" prefix) â PixelSeek rejects decoded images over 5 MiB / 40 MP. 382 * callback(err, { image_base64, image_mime_type }) 383 */ 384 encodeAiImageFile : function(file, callback) { 385 var _this = this; 386 var objectUrl = URL.createObjectURL(file); 387 var img = new Image(); 388 389 img.onload = function() { 390 URL.revokeObjectURL(objectUrl); 391
392 try { 393 var scale = Math.min(1, _this.AI_IMAGE_MAX_DIMENSION / Math.max(img.width, img.height)); 394 var canvas = document.createElement('canvas'); 395 canvas.width = Math.max(1, Math.round(img.width * scale)); 396 canvas.height = Math.max(1, Math.round(img.height * scale)); 397 canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height); 398 399 // The browser falls back to image/png when it cannot encode the 400 // requested type (e.g. WebP on older Safari), so read the actual mime 401 // type back from the data URL instead of trusting file.type. 402 var dataUrl = canvas.toDataURL(file.type, 0.9); 403 var match = dataUrl.match(/^data:(image\/[a-z+.-]+);base64,(.+)$/); 404 if (!match) { 405 callback(new Error('encode failed')); 406 return; 407 } 408 409 callback(null, { image_base64: match[2], image_mime_type: match[1] }); 410 } catch (err) { 411 callback(err); 412 } 413 }; 414 415 img.onerror = function() { 416 URL.revokeObjectURL(objectUrl); 417 callback(new Error('image load failed')); 418 }; 419 420 img.src = objectUrl; 421 }, 422 423 showAiError : function(title) { 424 if (window.dp && dp.utilities && dp.utilities.setFlashNotice) { 425 dp.utilities.setFlashNotice({ 426 type: 'error', 427 title: title, 428 content: '', 429 timeout: 4000 430 }); 431 } 432 }, 433 434 openVendorHub : function(e) { 435 e.stopPropagation(); 436 e.preventDefault(); 437 438 var _this = this; 439 440 if( !$(e.target).hasClass('public-disabled') ) { 441 if( $(e.target).hasClass('public-enabled') || (window.dp && window.dp.user && window.dp.user.type == "Supplier") ) { 442 window.location.href = "/hub/vendors/"; 443 } else { 444 window.location.href = "/vendors/"; 445 } 446 } 447 }, 448 449 displayVendorLabel : function() { 450 var _this = this; 451 452 _this.$el.find('.vendor-login-small-label').removeClass('hidden').show(); 453 }, 454 455 hideVendorLabel : function() { 456 var _this = this; 457 458 _this.$el.find('.vendor-login-small-label').addClass('hidden'); 459 }, 460 461 resetSearchExpand : function () { 462 // There is call from main.js with mouseleave to execute this code 463 var _this = this; 464 var searchInput = _this.$el.find('.main-search-input'); 465 var searchWrapper = _this.$el.find('.search_wrapper'); 466 var recentSearchWrapper = _this.$el.find('.recent_search_wrapper'); 467 var quickKeys = _this.$el.find('.quick_keys_wrapper'); 468 var fullSearchWrapper = _this.$el.find('.full_search_wrapper'); 469 var autoCompleteSection = document.getElementById("autocomplete-section"); 470 471 $('body').click(); 472 $(searchInput).val(''); 473 $(searchInput).removeClass('active'); 474 $(recentSearchWrapper).removeClass('active'); 475 $(quickKeys).removeClass('active'); 476 $(searchWrapper).removeClass('active'); 477 $(fullSearchWrapper).removeClass('active'); 478 $(searchWrapper).css('height', '10px'); 479 $(fullSearchWrapper).html(''); 480 $(autoCompleteSection).removeClass('autocomplete-opacity-main-search'); 481 document.body.style.setProperty('overflow', 'visible'); 482 _this.enableButtons = false; 483 _this.clearAutocompleteDebounce(); 484 _this.resetAiMode(); 485 486 $('.stub-button').addClass('is-short'); 487 $('.header.header-full').find('.bottom').find('ul.browse').css('left', '25%'); 488 searchWrapper.removeClass('display').fadeOut(300); 489 _this.$el.find('.search_collapse_wrapper').addClass('active').fadeIn(0); 490 }, 491 492 resetSearchCollapse : function() { 493 var _this = this; 494 var searchWrapper = _this.$el.find('.search_wrapper'); 495 496 _this.$el.find('.vendor-login-small-icon').removeClass('short'); 497 _this.$el.find('.vendor-login-small-label').removeClass('short'); 498 499 $('.stub-button').removeClass('is-short'); 500 $('.header.header-full').find('.bottom').find('ul.browse').css('left', ''); 501 _this.$el.find('.search_collapse_wrapper').removeClass('active'); 502 searchWrapper.fadeIn(500).addClass('display'); 503 }, 504 505 operatingS : function() { 506 let os = undefined 507 if (navigator.appVersion.indexOf('Win') != -1) os = 'Windows'; 508 if (navigator.appVersion.indexOf('Mac') != -1) os = 'MacOS'; 509 return os; 510 }, 511 512 autocomplete : function(e) { 513 var _this = this; 514 var searchInput = _this.$el.find('.main-search-input');
515 var searchWrapper = _this.$el.find('.search_wrapper'); 516 var recentSearchWrapper = _this.$el.find('.recent_search_wrapper'); 517 var quickKeys = _this.$el.find('.quick_keys_wrapper'); 518 var fullSearchWrapper = _this.$el.find('.full_search_wrapper'); 519 520 var term = $.trim($(searchInput).val()); 521 522 if (_this.useAi) { 523 _this.clearAutocompleteDebounce(); 524 _this.recentSearches(); 525 526 if (e.key === 'Enter' || e.keyCode === 13) { 527 e.preventDefault(); 528 if (term.length > 0) { 529 _this.clearAiImageSelection(); 530 _this.runPixelSeekSearch({ query: term }); 531 } 532 return; 533 } 534 535 // Keep idle panel visible while typing visual traits (unless already showing results/loading). 536 if (!fullSearchWrapper.find('.js-main-search-ai-panel').length) { 537 _this.renderAiIdlePanel(); 538 } 539 return; 540 } 541 542 if (term.length <= 1) { 543 _this.clearAutocompleteDebounce(); 544 return; 545 } 546 547 _this.recentSearches(); 548 $(fullSearchWrapper).addClass('active'); 549 $(quickKeys).addClass('active'); 550 $(searchInput).addClass('active'); 551 552 if (e.key === 'Enter' || e.keyCode === 13) { 553 _this.clearAutocompleteDebounce(); 554 e.preventDefault(); 555 if ($('.full_search_item.hover', $('.full_search_tab.active')).length > 0) { 556 var urlEnter = $('.full_search_item.hover', $('.full_search_tab.active')).attr('href'); 557 window.location.href = urlEnter; 558 } else if ($('.recent_search_item.hover', $('.full_search_tab.active')).length) { 559 var urlRecent = $('.recent_search_item.hover', $('.full_search_tab.active')).attr('href'); 560 window.location.href = urlRecent; 561 } else { 562 var urlFooter = $('.active_search_footer', $('.full_search_tab.active')).attr('href'); 563 if (urlFooter == undefined) { 564 window.location.href = '/search/products?query=' + encodeURIComponent(term); 565 } else { 566 window.location.href = urlFooter; 567 } 568 } 569 return; 570 } 571 572 if (e.keyCode === 40 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 37) { 573 return; 574 } 575 576 e.preventDefault(); 577 578 _this.clearAutocompleteDebounce(); 579 _this.autocompleteDebounceTimer = setTimeout(function() { 580 _this.autocompleteDebounceTimer = null; 581 var t = $.trim($(searchInput).val()); 582 if (t.length <= 1) { 583 return; 584 } 585 if (!$(searchWrapper).hasClass('active')) { 586 return; 587 } 588 if (_this.useAi) { 589 return; 590 } 591 var requestSeq = ++_this.autocompleteRequestSeq; 592 _this.enableButtons = false; 593 // Show immediate feedback while the server request is in flight. 594 $(fullSearchWrapper).html("<div class='main-search-loading'>Searching results...</div>"); 595 DP.Actions.Autocompletes.index(t, function(response) { 596 // Ignore stale responses if a newer request was started. 597 if (requestSeq !== _this.autocompleteRequestSeq) { return; } 598 if ($(searchWrapper).hasClass('active') && !_this.useAi) { 599 var resultsTemplate = _.template($('#main-search-results-template').html(), { results: response, term: $(searchInput).val() }); 600 $(fullSearchWrapper).html(resultsTemplate); 601 $(fullSearchWrapper).addClass('active'); 602 $(quickKeys).addClass('active'); 603 $(searchInput).addClass('active'); 604 _this.enableButtons = true; 605 $('.js-tooltip-trigger').dpTooltipTrigger(); 606 } 607 }); 608 }, _this.autocompleteDebounceMs); 609 }, 610 611 removeKeyboardState : function(e) { 612 $(".full_search_item").removeClass('hover'); 613 $('.active_search_footer').removeClass('hover'); 614 $(".recent_search_category").removeClass('hover'); 615 $(".recent_search_brand").removeClass('hover'); 616 }, 617 618 resetSearch : function(e) { 619 var _this = this; 620 var searchInput = _this.$el.find('.main-search-input'); 621 var searchWrapper = _this.$el.find('.search_wrapper'); 622 var recentSearchWrapper = _this.$el.find('.recent_search_wrapper'); 623 var quickKeys = _this.$el.find('.quick_keys_wrapper');
624 var fullSearchWrapper = _this.$el.find('.full_search_wrapper'); 625 var autoCompleteSection = document.getElementById("autocomplete-section"); 626 627 $(searchInput).val(''); 628 $(searchInput).removeClass('active'); 629 $(recentSearchWrapper).removeClass('active'); 630 $(quickKeys).removeClass('active'); 631 $(searchWrapper).removeClass('active'); 632 $(fullSearchWrapper).removeClass('active'); 633 $(searchWrapper).css('height', '10px'); 634 $(fullSearchWrapper).html(''); 635 $(autoCompleteSection).removeClass('autocomplete-opacity-main-search'); 636 document.body.style.setProperty('overflow', 'visible'); 637 _this.enableButtons = false; 638 _this.clearAutocompleteDebounce(); 639 _this.resetAiMode(); 640 }, 641 642 recentSearches : function(e) { 643 var _this = this; 644 var searchInput = _this.$el.find('.main-search-input'); 645 var searchWrapper = _this.$el.find('.search_wrapper'); 646 var recentSearchWrapper = _this.$el.find('.recent_search_wrapper'); 647 var quickKeys = _this.$el.find('.quick_keys_wrapper'); 648 var fullSearchWrapper = _this.$el.find('.full_search_wrapper'); 649 650 $(searchInput).addClass('active'); 651 $(searchWrapper).css("height", "auto"); 652 653 if( !$(fullSearchWrapper).hasClass("active") ) { 654 //$(recentSearchWrapper).addClass('active'); 655 } 656 657 $(quickKeys).addClass('active'); 658 $(searchWrapper).addClass('active'); 659 660 if (_this.useAi && !$.trim(fullSearchWrapper.html())) { 661 _this.renderAiIdlePanel(); 662 } 663 664 if( e != undefined ) { 665 e.preventDefault(); 666 e.stopPropagation(); 667 } 668 }, 669 670 productTab : function(e) { 671 e.preventDefault(); 672 673 var _this = this; 674 675 _this.$el.find('.js-tab-folders').removeClass('active'); 676 _this.$el.find('.js-tab-vendors').removeClass('active'); 677 678 _this.$el.find('.js-tab-products').addClass('active'); 679 680 _this.$el.find('.full_search_tab').removeClass('active'); 681 682 _this.$el.find('.product_tab').addClass('active'); 683 684 _this.$el.find('.js-full-search-extra').addClass('active'); 685 }, 686 687 foldersTab : function(e) { 688 e.preventDefault(); 689 690 var _this = this; 691 692 _this.$el.find('.js-tab-products').removeClass('active'); 693 _this.$el.find('.js-tab-vendors').removeClass('active'); 694 695 _this.$el.find('.js-tab-folders').addClass('active'); 696 697 _this.$el.find('.full_search_tab').removeClass('active'); 698 699 _this.$el.find('.folders_tab').addClass('active'); 700 701 _this.$el.find('.js-full-search-extra').removeClass('active'); 702 }, 703 704 vendorsTab : function(e) { 705 e.preventDefault(); 706 707 var _this = this; 708 709 _this.$el.find('.js-tab-folders').removeClass('active'); 710 _this.$el.find('.js-tab-products').removeClass('active'); 711 712 _this.$el.find('.js-tab-vendors').addClass('active'); 713 714 _this.$el.find('.full_search_tab').removeClass('active'); 715 716 _this.$el.find('.vendors_tab').addClass('active'); 717 718 _this.$el.find('.js-full-search-extra').removeClass('active'); 719 }, 720 721 "listen" : function() { 722 var autocomplete = this; 723 724 // Add event listeners 725 this.$el.find('.main-search-input').on("keydown", function(ev) { 726 if (autocomplete.useAi) { 727 if (ev.keyCode === 27) { 728 autocomplete.enableLeftRight = false; 729 autocomplete.enableButtons = false; 730 autocomplete.resetSearch(); 731 ev.preventDefault(); 732 } 733 if (ev.keyCode === 13 || ev.keyCode === 9) { 734 ev.preventDefault(); 735 } 736 ev.stopPropagation(); 737 return; 738 } 739 740 if( autocomplete.enableButtons ) { 741 switch(ev.keyCode) { 742 case 8: // Delete arrow 743 autocomplete.enableLeftRight = false; 744 autocomplete.enableButtons = false; 745 break; 746 case 46: // Delete arrow 747 autocomplete.enableLeftRight = false; 748 autocomplete.enableButtons = false; 749 ev.preventDefault(); 750 break; 751 case 40: // Down arrow 752 autocomplete.next(); 753 autocomplete.enableLeftRight = true; 754 ev.preventDefault(); 755 break; 756 case 38: // Up arrow 757 if( autocomplete.enableLeftRight ) { 758 autocomplete.prev(); 759 ev.preventDefault(); 760 } 761 break; 762 case 39: // Right arrow 763 if( autocomplete.enableLeftRight ) { 764 autocomplete.right(); 765 ev.preventDefault(); 766 } 767 break; 768 case 37: // Left arrow 769 if( autocomplete.enableLeftRight ) { 770 autocomplete.left(); 771 ev.preventDefault(); 772 } 773 break; 774 case 27: // Escape 775 autocomplete.enableLeftRight = false;
776 autocomplete.enableButtons = false; 777 autocomplete.resetSearch(); 778 ev.preventDefault(); 779 break; 780 case 13: // Return 781 case 9: // Tab 782 ev.preventDefault(); // Allow these keys to be ignored until the keyup 783 break; 784 } // End switch 785 } 786 ev.stopPropagation(); 787 }); 788 789 return this; 790 }, 791 792 "next" : function(ev) { // Get the next element relative to the current hovered element (hovered via keyboard or mouse) 793 var $next = $(".full_search_item.hover", $('.full_search_tab.active')); 794 795 if($next.length) { 796 var temporalNext = $next.removeClass("hover").next(); 797 if(!temporalNext.length && $next.hasClass('full_search_item')) { 798 $next = $(".active_search_footer", $('.full_search_tab.active')).first().addClass("hover"); 799 } else { 800 $next = temporalNext.addClass("hover"); 801 } 802 } else if( $(".active_search_footer", $('.full_search_tab.active')).first().hasClass('hover')) { 803 $(".active_search_footer", $('.full_search_tab.active')).first().removeClass('hover'); 804 $next = $(".recent_search_brand", $('.full_search_tab.active')).first().addClass("hover"); 805 if(!$next.length) { 806 $next = $(".recent_search_category", $('.full_search_tab.active')).first().addClass("hover"); 807 if(!$next.length) { 808 $next = $(".full_search_item", $('.full_search_tab.active')).first().addClass("hover"); 809 } 810 } 811 } else if( $(".recent_search_brand.hover", $('.full_search_tab.active')).length ) { 812 $next = $(".recent_search_brand.hover", $('.full_search_tab.active')).removeClass("hover").next(); 813 if(!$next.length) { 814 $next = $(".recent_search_category", $('.full_search_tab.active')).first().addClass("hover"); 815 if(!$next.length) { 816 $next = $(".full_search_item", $('.full_search_tab.active')).first().addClass("hover"); 817 } 818 } else { 819 $next.addClass("hover"); 820 } 821 } else if( $(".recent_search_category.hover", $('.full_search_tab.active')).length ) { 822 $next = $(".recent_search_category.hover", $('.full_search_tab.active')).removeClass("hover").next(); 823 if(!$next.length) { 824 $next = $(".full_search_item", $('.full_search_tab.active')).first().addClass("hover"); 825 } else { 826 $next.addClass("hover"); 827 } 828 }Â else if (!$next.length) { 829 $next = $(".full_search_item", $('.full_search_tab.active')).first().addClass("hover"); 830 } else { 831 $next.addClass("hover"); 832 } 833 }, 834 835 "prev" : function(ev) { 836 var $prev = $(".full_search_item.hover", $('.full_search_tab.active')); 837 838 if($prev.length) { 839 var temporalNext = $prev.removeClass("hover").prev(); 840 if(!temporalNext.length && $prev.hasClass('full_search_item')) { 841 $prev = $(".recent_search_item", $('.full_search_tab.active')).last().addClass("hover"); 842 } else { 843 $prev = temporalNext.addClass("hover"); 844 } 845 } else if( $(".recent_search_item.hover", $('.full_search_tab.active')).length ) { 846 $prev = $(".recent_search_item.hover", $('.full_search_tab.active')).removeClass("hover").prev(); 847 if($prev.hasClass('brands_search_title') ) { 848 $prev = ""; 849 } 850 if(!$prev.length) { 851 $prev = $(".active_search_footer", $('.full_search_tab.active')).first().addClass('hover'); 852 } else { 853 $prev.addClass("hover"); 854 } 855 } else if( $(".active_search_footer", $('.full_search_tab.active')).first().hasClass('hover')) { 856 $(".active_search_footer", $('.full_search_tab.active')).first().removeClass('hover'); 857 $prev = $(".full_search_item", $('.full_search_tab.active')).last().addClass("hover"); 858 }Â else if (!$prev.length) { 859 $prev = $(".recent_search_item", $('.full_search_tab.active')).last().addClass("hover"); 860 } else { 861 $prev.addClass("hover"); 862 } 863 }, 864 865 "left" : function(ev) { // Get the next element relative to the current hovered element (hovered via keyboard or mouse) 866 var index = 0; 867 var currentContainer = $(".full_search_tab.active", $('.full_search_wrapper')).removeClass('active'); 868 869 if( currentContainer.data("main-search-index") == 0 ) { 870 index = 2; 871 } else { 872 index = parseInt(currentContainer.data("main-search-index")) - 1; 873 } 874 875 $('.tab_item').removeClass('active'); 876 877 var currentTab = $('.tab_item[data-main-search-tab-index="' + index + '"]').addClass('active'); 878 var $next = $('.full_search_tab[data-main-search-index="' + index + '"]').addClass('active'); 879 }, 880 881 "right" : function(ev) { 882 var index = 0; 883 var currentContainer = $(".full_search_tab.active", $('.full_search_wrapper')).removeClass('active'); 884 885 if( currentContainer.data("main-search-index") == 2 ) { 886 index = 0; 887 } else { 888 index = parseInt(currentContainer.data("main-search-index")) + 1; 889 } 890 891 $('.tab_item').removeClass('active'); 892 893 var currentTab = $('.tab_item[data-main-search-tab-index="' + index + '"]').addClass('active'); 894 var $next = $('.full_search_tab[data-main-search-index="' + index + '"]').addClass('active'); 895 } 896 897});
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.