1// Miva Merchant 2// 3// This file and the source codes contained herein are the property of 4// Miva, Inc. Use of this file is restricted to the specific terms and 5// conditions in the License Agreement associated with this file. Distribution 6// of this file or portions of this file for uses not covered by the License 7// Agreement is not allowed without a written agreement signed by an officer of 8// Miva, Inc. 9// 10// Copyright 1998-2026 Miva, Inc. All rights reserved. 11// http://www.miva.com 12// 13 14/* 15 Required elements: 16 $unique_id = some_unique_to_DOM_value; 17 18 <input type="[any text supported input]" class="any" data-mm_searchfield="Yes" data-mm_searchfield_id="$unique_id" value="&mvte:global:Search;" /> 19 <[HTMLElement] data-mm_searchfield_menu="Yes" data-mm_searchfield_id="$unique_id"></[HTMLElement]> 20*/ 21 22(function( obj, eventType, fn ) 23{ 24 if ( obj.addEventListener ) 25 { 26 obj.addEventListener( eventType, fn, false ); 27 } 28 else if ( obj.attachEvent ) 29 { 30 obj.attachEvent( 'on' + eventType, fn ); 31 } 32})( window, 'load', function() { MMSearchField_Initialize(); } ); 33 34var mm_searchfields = new Object(); 35 36function MMSearchField_Initialize() 37{ 38 var i, id, i_len, lookup, elements; 39 40 FireEvent( window, 'mmsearchfield_override' ); 41 42 lookup = new Object(); 43 elements = document.getElementsByTagName( '*' ); 44 45 for ( i = 0, i_len = elements.length; i < i_len; i++ ) 46 { 47 if ( id = elements[ i ].getAttribute( 'data-mm_searchfield_id' ) ) 48 { 49 if ( !lookup[ id ] ) 50 { 51 lookup[ id ] = new Object(); 52 } 53 54 if ( elements[ i ].getAttribute( 'data-mm_searchfield' ) ) lookup[ id ].element_search = elements[ i ]; 55 else if ( elements[ i ].getAttribute( 'data-mm_searchfield_menu' ) ) lookup[ id ].element_menu = elements[ i ]; 56 } 57 } 58 59 for ( id in lookup ) 60 { 61 if ( !lookup.hasOwnProperty( id ) || 62 !lookup[ id ].element_search || 63 !lookup[ id ].element_menu || 64 ( lookup[ id ].element_search.nodeType !== 1 ) || 65 ( lookup[ id ].element_menu.nodeType !== 1 ) || 66 ( lookup[ id ].element_search.nodeName.toLowerCase() !== 'input' ) ) 67 { 68 continue; 69 } 70 71 mm_searchfields[ id ] = new MMSearchField( lookup[ id ].element_search, lookup[ id ].element_menu ); 72 } 73 74 FireEvent( window, 'mmsearchfield_initialized' ); 75} 76 77function MMSearchField( element_search, element_menu ) 78{ 79 var self = this; 80 81 // Elements 82 this.element_search = element_search; 83 this.element_menu = element_menu; 84 85 // Values 86 this.current_search = ''; 87 this.search_timeout = null; 88 this.search_delay = 400; 89 this.menu_visible = false; 90 this.menu_items = new Array(); 91 this.selected_item = null; 92 this.product_count = 5; 93 this.has_focus = false; 94 this.delegator = new AJAX_ThreadPool( 1 ); 95 this.last_displayed_search_value = null; 96 97 // Events 98 this.event_search_focus = function( event ) { return self.Event_Focus( event ? event : window.event ); }; 99 this.event_search_keydown = function( event ) { return self.Event_Keydown( event ? event : window.event ); }; 100 this.event_search_keyup = function( event ) { return self.Event_Keyup( event ? event : window.event ); }; 101 this.event_search_blur = function( event ) { return self.Event_Blur( event ? event : window.event ); }; 102 this.event_search_paste = function( event ) { setTimeout( function() { self.Search( self.element_search.value ); }, 0 ); }; 103 this.event_search_click = function( event ) { eventStopPropagation( event ? event : window.event ); }; 104 this.event_document_mousedown = function( event ) { return self.Event_Document_MouseDown( event ? event : window.event ); }; 105 this.event_document_mousemove = function( event ) { return self.Event_Document_MouseMove( event ? event : window.event ); }; 106 107 AddEvent( this.element_search, 'focus', this.event_search_focus ); 108 AddEvent( this.element_search, 'keydown', this.event_search_keydown ); 109 AddEvent( this.element_search, 'keyup', this.event_search_keyup ); 110 AddEvent( this.element_search, 'blur', this.event_search_blur ); 111 AddEvent( this.element_search, 'paste', this.event_search_paste ); 112 AddEvent( this.element_search, 'click', this.event_search_click ); 113 114 // Initial Setup 115 116 this.Menu_Empty(); 117 this.Menu_Hide(); 118} 119 120MMSearchField.prototype.GetSearchURL = function() 121{ 122 return MMSearchField_Search_URL_sep; 123} 124 125MMSearchField.prototype.SetLoadCount = function( count ) 126{ 127 this.product_count = count; 128} 129 130MMSearchField.prototype.SetSearchDelay = function( delay ) 131{ 132 this.search_delay = delay; 133} 134 135MMSearchField.prototype.Menu_Empty = function() 136{ 137 this.menu_items = new Array(); 138 this.element_menu.innerHTML = ''; 139 this.last_displayed_search_value = null; 140 141 this.Menu_Item_Select( null ); 142} 143 144MMSearchField.prototype.Menu_Show = function() 145{ 146 if ( this.menu_visible ) 147 { 148 return; 149 } 150 151 this.menu_visible = true; 152 this.element_menu.style.display = 'block'; 153} 154 155MMSearchField.prototype.Menu_Hide = function() 156{ 157 if ( !this.menu_visible ) 158 { 159 return; 160 } 161 162 this.menu_visible = false;
163 this.element_menu.style.display = 'none'; 164} 165 166MMSearchField.prototype.Menu_Append_Header = function() 167{ 168 var item; 169 170 if ( item = this.onMenuAppendHeader() ) 171 { 172 this.element_menu.appendChild( item ); 173 } 174} 175 176MMSearchField.prototype.Menu_Append_Item = function( data ) 177{ 178 var item; 179 180 if ( item = this.onMenuAppendItem( data.menuitem ) ) 181 { 182 item.mm_data = data; 183 item.action = data.product_link; 184 item.onclick = ( e ) => { this.Menu_Item_OnClick( e, item.action ); }; 185 item.onauxclick = ( e ) => { this.Menu_Item_OnClick( e, item.action ); }; 186 item.onmousemove = ( e ) => { this.Event_MenuItem_MouseMove( e, item ); }; 187 this.element_menu.appendChild( item ); 188 this.menu_items.push( item ); 189 } 190} 191 192MMSearchField.prototype.Menu_Append_StoreSearch = function() 193{ 194 var item; 195 196 if ( item = this.onMenuAppendStoreSearch( this.element_search.value ) ) 197 { 198 item.action = this.GetSearchURL() + encodeURIComponent( this.element_search.value ); 199 item.onclick = ( e ) => { this.Menu_Item_OnClick( e, item.action ); }; 200 item.onauxclick = ( e ) => { this.Menu_Item_OnClick( e, item.action ); }; 201 item.onmousemove = ( e ) => { this.Event_MenuItem_MouseMove( e, item ); }; 202 203 this.element_menu.appendChild( item ); 204 205 this.menu_item_storesearch = item; 206 this.menu_items.push( item ); 207 } 208} 209 210MMSearchField.prototype.Menu_Replace_StoreSearch = function() 211{ 212 var item; 213 214 if ( item = this.onMenuAppendStoreSearch( this.element_search.value ) ) 215 { 216 item.action = this.GetSearchURL() + encodeURIComponent( this.element_search.value ); 217 item.onclick = ( e ) => { this.Menu_Item_OnClick( e, item.action ); }; 218 item.onauxclick = ( e ) => { this.Menu_Item_OnClick( e, item.action ); }; 219 item.onmousemove = ( e ) => { this.Event_MenuItem_MouseMove( e, item ); }; 220 221 this.element_menu.appendChild( item ); 222 223 if ( this.menu_item_storesearch && ( ( index = this.menu_items.indexOf( this.menu_item_storesearch ) ) != -1 ) ) 224 { 225 this.menu_item_storesearch.parentNode.removeChild( this.menu_item_storesearch ); 226 this.menu_items.splice( index, 1 ); 227 } 228 229 if ( this.selected_item === this.menu_item_storesearch ) 230 { 231 this.Menu_Item_Select( item ); 232 } 233 234 this.menu_item_storesearch = item; 235 this.menu_items.push( item ); 236 } 237} 238 239MMSearchField.prototype.Menu_Item_Select = function( item ) 240{ 241 this.selected_item = item; 242 243 if ( item !== null ) 244 { 245 this.selected_item.className = classNameAdd( this.selected_item, 'mm_searchfield_menuitem_selected' ); 246 } 247} 248 249MMSearchField.prototype.Menu_Item_OnClick = function( e, url ) 250{ 251 window.open( url, ( e.ctrlKey || e.metaKey || e.button === 1 ) ? '_blank' : '_self', 'noreferrer' ); 252} 253 254MMSearchField.prototype.SetFocus = function() 255{ 256 this.element_search.focus(); 257} 258 259MMSearchField.prototype.Search = function( value ) 260{ 261 var self = this; 262 263 if ( ( typeof value !== 'string' ) || ( value.length == 0 ) ) 264 { 265 this.Menu_Empty(); 266 this.Menu_Hide(); 267 268 return; 269 } 270 else if ( value.length < 3 ) 271 { 272 this.Menu_Empty(); 273 this.Menu_Show(); 274 this.Menu_Replace_StoreSearch(); 275 276 return; 277 } 278 279 AJAX_Call_Module( function( response ) { self.Search_Callback( response, value ); }, 'runtime', 280 'cmp-cssui-searchfield', 281 'Search', 282 'Search=' + encodeURIComponent( value ) + 283 '&Count=' + this.product_count, 284 this.delegator ); 285 286 this.delegator.LimitQueueToCount( 1 ); 287 this.delegator.Run(); 288} 289 290MMSearchField.prototype.Search_Callback = function( response, original_search_value ) 291{ 292 var i, i_len, last_selected; 293 294 last_selected = this.selected_item; 295 296 this.Menu_Empty(); 297 298 if ( !this.has_focus || this.element_search.value.length == 0 ) 299 { 300 this.Menu_Empty(); 301 this.Menu_Hide(); 302 303 return; 304 } 305 306 this.Menu_Show(); 307 308 if ( !response.success || ( response.data.length == 0 ) || ( this.element_search.value != original_search_value ) ) 309 { 310 return this.Menu_Append_StoreSearch(); 311 } 312 313 this.last_displayed_search_value = this.element_search.value; 314 315 this.Menu_Append_Header(); 316 317 for ( i = 0, i_len = response.data.length; i < i_len; i++ ) 318 { 319 this.Menu_Append_Item( response.data[ i ] ); 320 } 321 322 this.Menu_Append_StoreSearch(); 323 324 if ( last_selected ) 325 { 326 for ( i = 0, i_len = this.menu_items.length; i < i_len; i++ ) 327 { 328 if ( this.menu_items[ i ].action == last_selected.action ) 329 { 330 this.Menu_Item_Select( this.menu_items[ i ] ); 331 break; 332 } 333 } 334 } 335} 336 337MMSearchField.prototype.Event_Keydown = function( e ) 338{ 339 var keycode, modifier; 340 341 keycode = e.keyCode || e.which; 342 modifier = e.metaKey || e.ctrlKey || e.shiftKey || e.altKey; 343 this.current_search = this.element_search.value; 344 345 if ( keycode == 9 && !modifier ) return this.Menu_Hide(); 346 else if ( keycode == 13 && !modifier ) return this.Event_Enter( e ); 347 else if ( keycode == 27 && !modifier ) return this.Event_ESC( e ); 348 else if ( keycode == 38 && !modifier ) return this.Event_ArrowUp( e ); 349 else if ( keycode == 40 && !modifier ) return this.Event_ArrowDown( e ); 350 351 if ( keycode == 85 && keySupportsMultiSelect( e ) ) 352 { 353 this.element_search.value = ''; 354 this.current_search = ''; 355 356 cursorToEnd( this.element_search ); 357 this.Menu_Empty(); 358 this.Menu_Hide(); 359 360 return eventPreventDefault( e ); 361 } 362 363 if ( this.element_search.value.length == 0 ) 364 { 365 this.Menu_Empty(); 366 this.Menu_Hide(); 367 } 368 else 369 { 370 this.Menu_Show(); 371 this.Menu_Replace_StoreSearch(); 372 } 373 374 return true; 375} 376 377MMSearchField.prototype.Event_Keyup = function( e ) 378{ 379 var self = this; 380 var keycode = e.keyCode || e.which; 381 382 if ( keycode == 13 ) return; /* Enter Key */ 383 else if ( keycode == 27 ) return; /* Esc Key */ 384 else if ( keycode == 38 ) return; /* Up Arrow */ 385 else if ( keycode == 40 ) return; /* Down Arrow */ 386 387 if ( this.element_search.value.length == 0 ) 388 { 389 this.Menu_Empty(); 390 this.Menu_Hide(); 391 } 392 else 393 { 394 this.Menu_Show(); 395 this.Menu_Replace_StoreSearch(); 396 } 397 398 if ( this.current_search == this.element_search.value ) 399 { 400 return; 401 } 402 403 if ( this.search_timeout ) 404 { 405 clearTimeout( this.search_timeout ); 406 } 407 408 this.search_timeout = setTimeout( function() 409 { 410 self.Search( self.element_search.value ); 411 self.search_timeout = null; 412 }, this.search_delay ); 413} 414 415MMSearchField.prototype.Event_Focus = function( e ) 416{ 417 if ( this.has_focus ) 418 { 419 return; 420 } 421 422 this.has_focus = true; 423 this.element_search.className = classNameAdd( this.element_search, 'focu
423s' ); 424 425 this.onFocus( e ); 426 427 if ( this.element_search.value === this.last_displayed_search_value ) 428 { 429 this.Menu_Show(); 430 } 431 else if ( this.element_search.value.length ) 432 { 433 this.Menu_Empty(); 434 this.Menu_Show(); 435 this.Menu_Replace_StoreSearch(); 436 this.Search( this.element_search.value ); 437 } 438 439 AddEvent( document, 'mousedown', this.event_document_mousedown ); 440} 441 442MMSearchField.prototype.Event_Blur = function( e ) 443{ 444 if ( !this.has_focus ) 445 { 446 return; 447 } 448 449 this.has_focus = false; 450 this.element_search.className = classNameRemove( this.element_search, 'focus' ); 451 452 this.Menu_Hide(); 453 this.onBlur( e ); 454 455 RemoveEvent( document, 'mousedown', this.event_document_mousedown ); 456} 457 458MMSearchField.prototype.Event_Enter = function( e ) 459{ 460 if ( this.selected_item && this.selected_item.action ) this.Menu_Item_OnClick( e, this.selected_item.action ); 461 else this.Menu_Item_OnClick( e, this.GetSearchURL() + encodeURIComponent( this.element_search.value ) ); 462 463 eventStopPropagation( e ); 464 return eventPreventDefault( e ); 465} 466 467MMSearchField.prototype.Event_ESC = function( e ) 468{ 469 if ( this.menu_visible ) 470 { 471 this.Menu_Hide(); 472 } 473 else 474 { 475 this.element_search.value = ''; 476 this.current_search = ''; 477 478 this.Menu_Empty(); 479 this.Menu_Hide(); 480 } 481 482 eventStopPropagation( e ); 483 return eventPreventDefault( e ); 484} 485 486MMSearchField.prototype.Event_ArrowUp = function( e ) 487{ 488 var i, i_len, index, next_index; 489 490 if ( !this.menu_visible ) 491 { 492 this.Search( this.element_search.value ); 493 494 return eventPreventDefault( e ); 495 } 496 497 for ( i = 0, i_len = this.menu_items.length; i < i_len; i++ ) 498 { 499 this.menu_items[ i ].className = classNameRemove( this.menu_items[ i ], 'mm_searchfield_menuitem_selected' ); 500 } 501 502 if ( this.menu_items.length == 0 ) 503 { 504 this.Menu_Item_Select( null ); 505 } 506 507 if ( ( index = this.menu_items.indexOf( this.selected_item ) ) != -1 ) 508 { 509 next_index = ( index - 1 < 0 ) ? ( this.menu_items.length - 1 ) : ( index - 1 ); 510 } 511 else 512 { 513 next_index = ( this.menu_items.length - 1 ); 514 } 515 516 this.Menu_Item_Select( this.menu_items[ next_index ] ); 517 518 return eventPreventDefault( e ); 519} 520 521MMSearchField.prototype.Event_ArrowDown = function( e ) 522{ 523 var i, i_len, index, next_index; 524 525 if ( !this.menu_visible ) 526 { 527 this.Search( this.element_search.value ); 528 529 return eventPreventDefault( e ); 530 } 531 532 for ( i = 0, i_len = this.menu_items.length; i < i_len; i++ ) 533 { 534 this.menu_items[ i ].className = classNameRemove( this.menu_items[ i ], 'mm_searchfield_menuitem_selected' ); 535 } 536 537 if ( this.menu_items.length == 0 ) 538 { 539 this.Menu_Item_Select( null ); 540 } 541 542 if ( ( index = this.menu_items.indexOf( this.selected_item ) ) != -1 ) 543 { 544 next_index = ( index + 1 > this.menu_items.length - 1 ) ? 0 : ( index + 1 ); 545 } 546 else 547 { 548 next_index = 0; 549 } 550 551 this.Menu_Item_Select( this.menu_items[ next_index ] ); 552 553 return eventPreventDefault( e ); 554} 555 556MMSearchField.prototype.Event_Document_MouseDown = function( e ) 557{ 558 var target = e.target || e.srcElement; 559 560 if ( target === this.element_menu || containsChild( this.element_menu, target ) ) 561 { 562 eventStopPropagation( e ); 563 return eventPreventDefault( e ); 564 } 565} 566 567MMSearchField.prototype.Event_MenuItem_MouseMove = function( e, item ) 568{ 569 var i, i_len; 570 571 for ( i = 0, i_len = this.menu_items.length; i < i_len; i++ ) 572 { 573 this.menu_items[ i ].className = classNameRemove( this.menu_items[ i ], 'mm_searchfield_menuitem_selected' ); 574 } 575 576 this.Menu_Item_Select( item ); 577}
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.