1function FFSuggest() { 2 var pDebug = false; 3 var pInstanceName = ''; 4 var pSearchURL = ''; 5 var pQueryParamName = ''; 6 var pFormname = ''; 7 var pLayerName = ''; 8 var pQueryInput; 9 var pChannelParamName; 10 var pChannel; 11 var pSuggest = new Array(); 12 var pLastQuery; 13 var submitted = false; 14 var pSid = ''; // session id of user 15 var pCurrentSelection = -1; 16 // Callback which will be invoked when the suggest result is shown.; 17 var onDisplay; 18 19 var pSuggestRowClass = 'suggestRow'; 20 var pSuggestHighlightClass = 'suggestHighlight'; 21 var pSuggestLayerBlockText = 'suggestLayerBlockText'; 22 var pSuggestLayerBlockImage = 'suggestLayerBlockImage'; 23 24 var isUTF; 25 var renderSuggestCallback; 26 var currentQuery = ""; 27 28 this.init = function(searchURL, formname, queryParamName, divLayername, instanceName, debugMode, channelParamName, channel, sid, displayCallback, utfShop, suggestRenderer) { 29 pSearchURL = searchURL; 30 pFormname = formname; 31 pQueryParamName = queryParamName; 32 pChannelParamName = channelParamName; 33 pChannel = channel; 34 pLayerName = divLayername; 35 pInstanceName = instanceName; 36 pDebug = debugMode; 37 pSid = sid; 38 onDisplay = displayCallback; 39 40 if (pSearchURL == '') { 41 if (pDebug) 42 alert('no searchurl defined'); 43 return null; 44 } 45 else if (pInstanceName == '') { 46 if (pDebug) 47 alert('no instancename defined'); 48 return null; 49 } 50 else if (pFormname == '') { 51 if (pDebug) 52 alert('no formname defined'); 53 return null; 54 } 55 else if (pQueryParamName == '') { 56 if (pDebug) 57 alert('no queryparamname defined'); 58 return null; 59 } 60 else if (pLayerName == '') { 61 if (pDebug) 62 alert('need a layer for output'); 63 } 64 if (document[pFormname]) { 65 pQueryInput = document[pFormname][pQueryParamName]; 66 if (pQueryInput) { 67 $(pQueryInput).keyup(function(ev) { 68 handleKeyPress(ev); 69 }); 70 $(pQueryInput).keydown(function(ev) { 71 return checkTab(ev); 72 }); //tab doesn't work with keyup 73 $(pQueryInput).focus(function() { 74 if ($(this).val().trim()) { 75 showLayer(); 76 } 77 }); 78 $(pQueryInput).blur(function() { 79 hideLayer(); 80 }); 81 } 82 $(document[pFormname]).submit(function() { 83 return handleSubmit(); 84 }); 85 } 86 isUTF = utfShop; 87 renderSuggestCallback = suggestRenderer; 88 }; 89 90 function handleSubmit() { 91 if (pSuggest[pCurrentSelection] != undefined) { 92 var url = fireSubmitSuggest(pSuggest[pCurrentSelection]); 93 if (url == null || url == '') { 94 url = reEncodeURLForISO(cutParamsUrl(pSuggest[pCurrentSelection].searchParams)); 95 url = removeParam(url, 'format'); 96 url = removeParam(url, '_'); 97 url = removeParam(url, 'omitContextName'); 98 url = changeParamName(url, 'query', 'searchparam'); 99 var hiddenInputs = $('input[type=hidden]', document[pFormname]); 100 for (var i = 0; i < hiddenInputs.length; i++) { 101 url = addParam(url, hiddenInputs[i].name, hiddenInputs[i].value); 102 } 103 url = cutParamsUrl(url); 104 if (document[pFormname].action.indexOf('?') >= 0 && url.charAt(0) === '?') 105 url = url.substring(1); 106 url = document[pFormname].action + url; 107 } 108 109 url = addTrackingInformation(url); 110 window.location = url; 111 return false; 112 } 113 return true; 114 } 115 116 function addTrackingInformation(url) { 117 if (pSid) { 118 url = addParam(url, 'sid', pSid); 119 } 120 if (pLastQuery) { 121 url = addParam(url, 'userInput', pLastQuery); 122 //we need to be able to send multiple values for ignoreForCache 123 url += '&ignoreForCache[]=userInput'; 124 } 125 url = addParam(url, 'queryFromSuggest', 'true'); 126 //we need to be able to send multiple values for ignoreForCache 127 url += '&ignoreForCache[]=queryFromSuggest'; 128 return url; 129 } 130 131 this.handleClick = function() { 132 if ($('li.' + pSuggestHighlightClass).length > 0) { 133 pCurrentSelection = $('li.' + pSuggestHighlightClass)[0].id.replace(pLayerName + '_', ''); 134 } 135 $(document[pFormname]).submit(); 136 }; 137 138 this.handleMouseMove = function(id) { 139 unmarkAll(); 140 $('#' + pLayerName + ' li[id="' + id + '"]').addClass(pSuggestHighlightClass); 141 }; 142 143 this.handleMouseOut = function(id) { 144 $('#' + pLayerName + ' li[id="' + id + '"]').removeClass(pSuggestHighlightClass); 145 pCurrentSelection = -1; 146 }; 147 148 function handleKeyPress(evt) { 149 evt = (evt) ? evt : ((event) ? event : null); 150 var keyCode = evt.keyCode; 151 if (keyCode == 38) { 152 moveUp(); 153 } 154 else if (keyCode == 27) { 155 hideLayer(); 156 } 157 else if (keyCode == 40) { 158 moveDown(); 159 } 160 else if (keyCode == 37) { 161 moveLeft(); 162 } 163 else if (keyCode == 39) { 164 moveRight(); 165 } 166 else { 167 if (pQueryInput.value == '') { 168 hideLayer(); 169 pLastQuery = ''; 170 return null; 171 } 172 if (pLastQuery != pQueryInput.value) { 173 getSuggestions(); 174 } 175 pLastQuery = pQueryInput.value; 176 } 177 } 178 179 function checkTab(evt) { 180 evt = (evt) ? evt : ((event) ? event : null); 181 var keyCode = evt.keyCode; 182 if (keyCode == 9) { 183 var ovlEl = $('input#' + pQueryParamName + 'Underlay'); 184 if (ovlEl.length > 0) { 185 $('input[name=' + pQueryParamName + ']', document[pFormname]).val(ovlEl.attr('value')); 186 return false;
187 } 188 } 189 return true; 190 } 191 192 function moveUp() { 193 if ($('#' + pLayerName).is(':visible')) { 194 if ($('li.' + pSuggestHighlightClass).length == 0) { 195 $('li.' + pSuggestRowClass + ':last').addClass(pSuggestHighlightClass); 196 } 197 else { 198 var listWithSelection = $('li.' + pSuggestHighlightClass).parent('ul'); 199 var prevEl = $('li.' + pSuggestHighlightClass).prevAll('li.' + pSuggestRowClass); 200 if (prevEl.length == 0) { 201 $('li.' + pSuggestHighlightClass).removeClass(pSuggestHighlightClass); 202 listWithSelection.find('li.' + pSuggestRowClass + ':last') 203 .addClass(pSuggestHighlightClass); 204 } 205 else { 206 $('li.' + pSuggestHighlightClass).removeClass(pSuggestHighlightClass); 207 $(prevEl[0]).addClass(pSuggestHighlightClass); 208 } 209 } 210 pCurrentSelection = getCurrentSelection(); 211 } 212 } 213 214 function moveDown() { 215 if ($('#' + pLayerName).is(':visible')) { 216 if ($('li.' + pSuggestHighlightClass).length == 0) { 217 $('li.' + pSuggestRowClass + ':first').addClass(pSuggestHighlightClass); 218 } 219 else { 220 var listWithSelection = $('li.' + pSuggestHighlightClass).parent('ul'); 221 var nxtEl = $('li.' + pSuggestHighlightClass).nextAll('li.' + pSuggestRowClass); 222 if (nxtEl.length == 0) { 223 $('li.' + pSuggestHighlightClass).removeClass(pSuggestHighlightClass); 224 listWithSelection.find('li.' + pSuggestRowClass + ':first') 225 .addClass(pSuggestHighlightClass); 226 } 227 else { 228 $('li.' + pSuggestHighlightClass).removeClass(pSuggestHighlightClass); 229 $(nxtEl[0]).addClass(pSuggestHighlightClass); 230 } 231 } 232 pCurrentSelection = getCurrentSelection(); 233 } 234 } 235 236// calls the callback for "outside" listeners if the callback is implemented 237 function moveLeft() { 238 if (typeof (moveHorizontalInSuggestBox) == 'function') { 239 moveHorizontalInSuggestBox(); 240 } 241 } 242 243// calls the callback for "outside" listeners if the callback is implemented 244 function moveRight() { 245 if (typeof (moveHorizontalInSuggestBox) == 'function') { 246 moveHorizontalInSuggestBox(); 247 } 248 } 249 250 function getCurrentSelection() { 251 var selection = -1; 252 var highlightId = $('li.' + pSuggestHighlightClass).attr('id'); 253 if (highlightId) { 254 selection = highlightId.slice(highlightId.lastIndexOf('_') + 1, highlightId.length); 255 } 256 return selection; 257 } 258 259 function cutParamsUrl(param) { 260 var qPos = param.indexOf('?'); 261 if (qPos >= 0) { 262 return param.substring(qPos); 263 } 264 else { 265 return param; 266 } 267 } 268 269 function removeParam(param, name) { 270 var pPos = param.indexOf('?' + name); 271 if (pPos >= 0) { 272 var f = param.substring(0, pPos); 273 pPos = param.indexOf('&', pPos + 1); 274 if (pPos >= 0) { 275 param = f + param.substring(pPos); 276 } 277 } 278 else { 279 pPos = param.indexOf('&' + name); 280 if (pPos >= 0) { 281 var f = param.substring(0, pPos); 282 pPos = param.indexOf('&', pPos + 1); 283 if (pPos >= 0) { 284 param = f + param.substring(pPos); 285 } 286 } 287 } 288 return param; 289 } 290 291 function changeParamName(param, name, newname) { 292 var pPos = param.indexOf('?' + name); 293 if (pPos >= 0) { 294 param = param.replace('\?' + name, '\?' + newname); 295 } 296 else { 297 pPos = param.indexOf('&' + name); 298 if (pPos >= 0) { 299 param = param.replace('&' + name, '&' + newname); 300 } 301 } 302 return param; 303 } 304 305 function hideLayer() { 306 unmarkAll(); 307 $('div#' + pLayerName).hide(); 308 fireSuggestLayerHidden(); 309 310 var ovlEl = $('input#' + pQueryParamName + 'Underlay'); 311 if (ovlEl.length > 0) { 312 $('input[name=' + pQueryParamName + ']', document[pFormname]) 313 .css('background-color', ovlEl.css('background-color')); 314 } 315 } 316 317 this.hideLayerOutsideCall = function() { 318 hideLayer(); 319 }; 320 321 function showLayer() { 322 $('div#' + pLayerName).show(); 323 } 324 325// calls the callback for "outside" listeners if the callback is implemented 326 function fireSuggestCompleted(suggestLayerIsVisible) { 327 if (typeof (onSuggestCompleted) == 'function') { 328 onSuggestCompleted(suggestLayerIsVisible); 329 } 330 } 331 332// calls the callback for "outside" listeners if the callback is implemented 333 function fireSuggestLayerHidden() { 334 if (typeof (onSuggestLayerHidden) == 'function') { 335 onSuggestLayerHidden(); 336 } 337 } 338 339 function fireSubmitSuggest(chosenJson) { 340 if (typeof (onSubmitSuggest) == 'function') { 341 return onSubmitSuggest(chosenJson); 342 } 343 } 344 345 function unmarkAll() { 346 $('li.' + pSuggestHighlightClass).each(function(i) { 347 $(this).removeClass(pSuggestHighlightClass).addClass(pSuggestRowClass); 348 }); 349 } 350 351 function addParam(url, paramName, paramValue) { 352 if (paramName && paramValue) { 353 var newParam = encodeISOSave(paramName) + '=' + encodeISOSave(paramValue); 354 if (url.indexOf('?') !== -1) { 355 url += '&' + newParam; 356 } 357 else { 358 url += '?' + newParam; 359 } 360 } 361 return url; 362 } 363
364 function invokeDisplayCallback() { 365 if (onDisplay && typeof (onDisplay) == 'function') { 366 // schedule async execution 367 window.setTimeout(function() { 368 onDisplay(); 369 }, 10); 370 } 371 } 372 373 function getSuggestions() { 374 var searchInput = $('input[name=' + pQueryParamName + ']', document[pFormname]); 375 var query = $('input[name=' + pQueryParamName + ']', document[pFormname]).val(); 376 //check if the same query was asked before 377 if (pLastQuery == query) { 378 return; 379 } 380 else { 381 pLastQuery = query; 382 } 383 384 currentQuery = query; 385 386 searchInput[0].dataset.query = ''; 387 388 var requestURL = pSearchURL + (pSearchURL.indexOf('?') >= 0 ? '&' : '?') + pQueryParamName + 389 '=' + encodeISOSave(query) + '&' + pChannelParamName + '=' + pChannel + 390 '&format=json&omitContextName=true'; 391 392 $.getJSON( 393 requestURL, 394 function(jsonObj, textStatus, xhr) { 395 if(currentQuery !== query){ 396 return; 397 } 398 pCurrentSelection = -1; 399 //normalize object for old ff versions 400 if (Array.isArray(jsonObj)) { 401 jsonObj = {'suggestions': jsonObj}; 402 } 403 var tabWord = ''; 404 if (jsonObj != null && Array.isArray(jsonObj.suggestions) && jsonObj.suggestions.length) { 405 jsonObj = jsonObj.suggestions; 406 pSuggest = new Array(); 407 var orderedJsonObj = { 408 'suggestions': { 409 'BlockText': {}, 410 'BlockImage': {}, 411 }, 412 'query': query, 413 'multipleBlocks': false, 414 }; 415 var hasTextBlock = false; 416 var hasImageBlock = false; 417 for (var i = 0; i < jsonObj.length; i++) { 418 var list; 419 jsonObj[i].id = pLayerName + '_' + i; 420 if (jsonObj[i].image) { 421 list = orderedJsonObj.suggestions.BlockImage; 422 hasImageBlock = true; 423 } 424 else { 425 list = orderedJsonObj.suggestions.BlockText; 426 hasTextBlock = true; 427 } 428 429 if (jsonObj[i].attributes.parentCategory) { 430 jsonObj[i].parentCategory = decodeURIComponent( 431 jsonObj[i].attributes.parentCategory.replace(/\//g, ' > ')); 432 } 433 434 if (list[jsonObj[i].type] === undefined) { 435 list[jsonObj[i].type] = []; 436 } 437 list[jsonObj[i].type].push(jsonObj[i]); 438 pSuggest[i] = jsonObj[i]; 439 440 if (tabWord === '') { 441 if (jsonObj[i].name.toLowerCase().indexOf(query.toLowerCase()) == 0) { 442 tabWord = query + jsonObj[i].name.substring(query.length); 443 } 444 } 445 } 446 //do not print empty lists 447 if (!hasImageBlock) 448 delete orderedJsonObj.suggestions.BlockImage; 449 if (!hasTextBlock) 450 delete orderedJsonObj.suggestions.BlockText; 451 orderedJsonObj.multipleBlocks = hasImageBlock && hasTextBlock; 452 453 //show layer 454 var outputText = renderSuggestCallback(orderedJsonObj); 455 $('div#' + pLayerName).html(outputText).show(); 456 457 invokeDisplayCallback(); 458 459 var ovlEl = $('input#' + pQueryParamName + 'Underlay'); 460 if (ovlEl.length > 0) { 461 ovlEl.attr('value', tabWord); 462 if (tabWord) { 463 $('input[name=' + pQueryParamName + ']').css('background-color', 'transparent'); 464 } 465 else { 466 $('input[name=' + pQueryParamName + ']') 467 .css('background-color', ovlEl.css('background-color')); 468 } 469 } 470 searchInput[0].dataset.query = orderedJsonObj.query; 471 } 472 else { 473 hideLayer(); 474 } 475 }, 476 ).fail(function(e, xhr, settings, exception) { 477 if (pDebug) { 478 alert('Error:\nHTTP result code: ' + e.status + '\nrequested URL: ' + requestURL); 479 } 480 }); 481 } 482 483 function onSubmitSuggest(chosenJson) { 484 var type = pSuggest[pCurrentSelection].type; 485 if (type == 'productName') { 486 var dplnk = pSuggest[pCurrentSelection].attributes['deeplink']; 487 return dplnk; 488 } 489 } 490 491 function moveHorizontalInSuggestBox() { 492 var numBlockTextElements = $('ul.' + pSuggestLayerBlockText + ' li.' + pSuggestRowClass).length; 493 var numBlockImageElements = $( 494 'ul.' + pSuggestLayerBlockImage + ' li.' + pSuggestRowClass).length; 495 496 if (numBlockTextElements > 0 && numBlockImageElements > 0) { 497 var isHighlighted = $('li.' + pSuggestHighlightClass).length > 0; 498 if (!isHighlighted) { 499 $('ul.' + pSuggestLayerBlockImage + ' li.' + pSuggestRowClass + ':first') 500 .addClass(pSuggestHighlightClass); 501 } 502 else if (isHighlighted) { 503 var isInBlockImage = $( 504 'ul.' + pSuggestLayerBlockImage + ' li.' + pSuggestHighlightClass).length > 0; 505 $('li.' + pSuggestHighlightClass).removeClass(pSuggestHighlightClass); 506 if (isInBlockImage) { 507 $('ul.' + pSuggestLayerBlockText + ' li.' + pSuggestRowClass + ':first') 508 .addClass(pSuggestHighlightClass); 509 } 510 else { 511 $('ul.' + pSuggestLayerBlockImage + ' li.' + pSuggestRowClass + ':first') 512 .addClass(pSuggestHighlightClass); 513 } 514 } 515 pCurrentSelection = getCurrentSelection(); 516 } 517 } 518 519 function encodeISOSave(str) { 520 if (isUTF) 521 return encodeURIComponent(str); 522 return unescape(encodeURIComponent(escape(str))).replace(/\+/g, '%2B'); 523 } 524 525 function reEncodeURLForISO(str) { 526 if (isUTF) 527 return str.replace(/\+/g, '%20'); 528 if (!str || str.length < 1) 529 return str; 530 if (str.charAt(0) == '?') 531 str = str.substring(1); 532 str = str.replace(/\+/g, '%20'); 533 var url = ''; 534 var vars = str.split('&'); 535 for (var i = 0; i < vars.length; i++) { 536 var pair = vars[i].split('='); 537 if (pair[0] !== undefined && pair[0] !== '' && pair[1] !== undefined && pair[1] !== '') { 538 if (url === '') { 539 url += '?'; 540 } 541 else { 542 url += '&'; 543 }
544 url += encodeISOSave(decodeURIComponent(pair[0])); 545 url += '=' + encodeISOSave(decodeURIComponent(pair[1])); 546 } 547 } 548 return url; 549 } 550}
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.