1/* 2 * jQuery Search Light Plugin 3 * version: 1.0.1 (2009/07/29) 4 * @requires jQuery v1.3.0 or later 5 * 6 * Copyright 2009 Ryan Williams 7 * http://ryanwilliams.org 8 * [email protected] 9 * 10 * Dual licensed under the MIT and GPL licenses: 11 * http://www.opensource.org/licenses/mit-license.php 12 * http://www.gnu.org/licenses/gpl.html 13 * 14 */ 15 16//For jsonp version of searchlight, surround the .jso template content in the FusionBot account 17//in a static function named jsonp_callback() 18 19//make global var, so can reference outside of dynamic function 20var searchlight; 21 22(function($) { 23 var SearchLight = function(input, url, options) { 24 settings = $.extend({ 25 minimumCharacters: 3, 26 searchDelay: 100, 27 limitPerCategory: 5, 28 actionFunction: null, 29 align: 'left', 30 width: '400', 31 showIcons: true, 32 ratings: false, 33 saleImg: '/assets/templates/time-core/fusionbot/on-sale-tag.png', 34 showEffect: 'scroll', // TODO 35 hideEffect: 'fade' // TODO 36 }, options); 37 this._settings = settings; 38 39 //short circuit for small screens 40 if( $( document ).width() <= 766) 41 return true; 42 43 var input = $(input); 44 var container = $(document.createElement('div')); 45 container.attr('class', 'searchlight-balloon'); 46 47 //myTop defined before insertion of sl.js in client side page, to hard code position of preview box, rather than calculate dynamically 48 if(typeof myTop == "undefined") 49 { 50 container.css({ 51 position: 'absolute', 52 top: input.offset().top + input.outerHeight() + 3, 53 display: 'none' 54 }); 55 } 56 else 57 { 58 container.css({ 59 position: 'absolute', 60 top: myTop, 61 display: 'none' 62 }); 63 64 } 65 66 67 if (settings.width == 'auto') { 68 container.css('width', input.outerWidth()); 69 } 70 71 if (settings.align == 'left') { 72 //myLeft defined before insertion of sl.js in client side page, to hard code position of preview box, rather than calculate dynamically 73 if(typeof myLeft == "undefined") 74 container.css('left', input.offset().left - 1.5); 75 else 76 container.css('left', myLeft); 77 78 79 } else if (settings.align == 'right') { 80 if(typeof myRight == "undefined") 81 container.css('right', $(document.body).innerWidth() - (input.offset().left + input.outerWidth())); 82 else 83 container.css('right', myRight); 84 } 85 86 87 var results = $(document.createElement('div')); 88 results.attr('class', 'searchlight-results-wrapper'); 89 //results.css({ 90 // height: '100%' 91 //}); 92 93 //if ($.browser.msie && parseFloat($.browser.version) <= 7) { 94 // results.css({ 95 // width: '100%' 96 // }); 97 //} 98 99 container.append(results); 100 $(document.body).append(container); 101 102 103 this._input = input; 104 this._container = container; 105 this._searchURL = url; 106 this._resultsContainer = results; 107 108 input.bind('focus.searchlight', {searchlight: this}, function(evt) { 109 searchlight = evt.data.searchlight; 110 if (this.value.length >= settings.minimumCharacters) { 111 searchlight.search(this.value); 112 } 113 }); 114 $(document.body).bind('mousedown.searchlight', {searchlight: this}, function(evt) { 115 searchlight = evt.data.searchlight; 116 117 // Make sure we didn't click the searchlight 118 var node = evt.target; 119 var c = 0 120 while (node) { 121 // Clicked searchlight, so return 122 if (node == searchlight._input[0] || node == searchlight._container[0]) { 123 return; 124 } 125 node = node.parentNode; 126 } 127 128 searchlight.hide(); 129 }); 130 131 input.bind('keydown.searchlight', {searchlight: this}, function(evt) { 132 searchlight = evt.data.searchlight; 133 if (evt.which == 38 && searchlight._selectedRow > 0) { 134 // Up arrow 135 searchlight.selectRow(searchlight._selectedRow-1); 136 } else if (evt.which == 40 && searchlight._selectedRow < searchlight._rowCount -1) { 137 // Down arrow 138 searchlight.selectRow(searchlight._selectedRow+1); 139 } else if (evt.which == 13 && searchlight._selectedRow > -1) { 140 searchlight.activateRow(searchlight._selectedRow); 141 } 142 //if (evt.which == 13 || evt.which == 38 || evt.which == 40) { 143 //remove 13 so can submit search with enter key 144 if (evt.which == 38 || evt.which == 40) { 145 evt.preventDefault(); 146 } 147 }); 148 input.bind('keyup.searchlight', {searchlight: this}, function(evt) { 149 searchlight = evt.data.searchlight; 150 if (searchlight._searchDelayTimer) { 151 clearTimeout(searchlight._searchDelayTimer); 152 } 153 searchlight._searchDelayTimer = setTimeout(function() { 154 var input = searchlight._input[0]; 155 if (input.value == searchlight._previousQuery) { 156 } else if (input.value.length >= settings.minimumCharacters) { 157 searchlight.search(input.value); 158 } else { 159 searchlight.hide(); 160 } 161 }, settings.searchDelay); 162 }); 163 input.bind('keypress.searchlight', {searchlight: this}, function(evt) { 164 165 searchlight = evt.data.searchlight; 166 });
167 this._container.bind('mouseleave.searchlight', {searchlight: this}, function(evt) { 168 searchlight = evt.data.searchlight; 169 searchlight.selectRow(-1); 170 }); 171 172 173 this.resultAction = settings.actionFunction ? settings.actionFunction : this.defaultResultAction; 174 }; 175 SearchLight.prototype.show = function() { 176 if (!this._container.is(':visible')) { 177 this._container.fadeIn('fast'); 178 } 179 }; 180 SearchLight.prototype.hide = function() { 181 if (this._disableHide) { 182 return; 183 } 184 this._container.fadeOut('fast'); 185 }; 186 187 SearchLight.prototype.search = function(query) { 188 searchlight = this; 189 this._previousXHR = $.ajax({ 190 dataType: 'jsonp', 191 data: 'k=' + query, 192 url: this._searchURL, 193 success: function jsonp_callback(results){} 194 }); 195 searchlight.show(); 196 }; 197 198 SearchLight.prototype.clearResults = function() { 199 this._categoryCount = 0; 200 this._rowCount = 0; 201 this._selectedRow = -1; 202 this._resultsContainer.html('<table class="searchlight-results"></table>'); 203 }; 204 205 SearchLight.prototype.addFooter = function(results) { 206 207 if(results.length == 5) { 208 209 theForm = ""; 210 if(document.fbsearch !== undefined) 211 theForm = "fbsearch"; 212 else if(document.searchForm !== undefined) 213 theForm = "searchForm"; 214 215 if(theForm.length > 0) { 216 217 var tr = document.createElement('tr'); 218 $(tr).attr('id', 'searchlight-footer'); 219 var td = document.createElement('td'); 220 $(td).attr('align', 'center'); 221 $(td).attr('width', '100%'); 222 $(td).attr('colspan', '2'); 223 //$(td).html('<div id="searchlight-footer" onclick="document.getElementsByName(theForm)[0].submit();">More...</div>'); 224 $(td).html('<div id="searchlight-footer" onclick="document.getElementsByName(theForm)[0].submit();">View All Results<div id="qsMoleculeDiv"><a onclick="event.stopPropagation();" href="https://www.fusionbot.com/default.asp?source=FB-164720028" target="_blank"><img id="qsMoleculeImg" title="Powered by FusionBot" src="/assets/templates/ledlightexpert-core/fusionbot/molecule.png" border=0></a></div></div>'); 225 $(tr).append(td); 226 this._resultsContainer.children('table').append(tr); 227 228 } 229 230 } 231 232 }; 233 234 235SearchLight.prototype.addResultCategory = function(name, results) { 236 var first = true; 237 for (var i = 0; i < results.length; i++) { 238 var r = results[i]; 239 240 var tr = document.createElement('tr'); 241 $(tr).attr('class', 'searchlight-not-selected'); 242 243 //var th = document.createElement('th'); 244 var td = document.createElement('td'); 245 //$(th).html('<span class="searchlight-header-text"></span>'); 246 $(td).html('<span class="searchlight-result-text"></span>'); 247 //var th_d = th.firstChild; 248 var td_d = td.firstChild; 249 250 //if (first) { 251 // $(th_d).text(name); 252 // first = false; 253 //} 254 255 if (this._settings.showIcons) { 256 var img = document.createElement('img'); 257 img.className = 'searchlight-result-icon'; 258 //img.style.width = '50px'; 259 //if(typeof noHeight == "undefined") 260 // img.style.height = '50px'; 261 // If icon, then use that otherwise use blank 262 img.src = r[2] ? r[2] : 'assets/templates/common/images/spacer.gif'; 263 $(td_d).append(img); 264 //$(".searchlight-result-icon").attr("onerror","this.onerror=null;this.src='/assets/images/default.jpg'"); 265 $(img).error(function() { 266 $(this).attr( "src", "/assets/images/default.jpg" ); 267 }) 268 } 269 270 var ttl = r[1]; 271 $(td_d).append("<span class=sl_title>" + ttl + "</span>"); 272 //if(!r[4] || 0 === r[4].length || r[4] == "-1") { 273 // $(td_d).append("<br><span align=left class=search-price>" + r[3] + "</span>"); 274 //} else { 275 276 //r[5] = $RES_ONS (is on sale?) 277 var priceline = ""; 278 //var clvl = getPriceLvl(); 279 var clvl = 0; 280 if(clvl > 0) { 281 if(typeof(r[7]) !== "undefined" && r[7].length > 0) //r[7] = price level 2 when present 282 $(td_d).append("<br><span align=left class='search-price'>" + r[7] + "</span>"); 283 else 284 $(td_d).append("<br><span align=left class='search-price'>" + r[3] + "</span>"); 285 } else { 286 if(typeof(r[5]) !== "undefined" && r[5] == "1") { 287 if( typeof(r[6]) !== "undefined" && r[6].length > 0) 288 priceline = "<span align=left class='search-price' style='margin-right:5px;'><s>" + r[6] + "</s></span><img class='sale-price-img' src='" + this._settings.saleImg + "'>"; 289 priceline += "<span align=left class='sale-price'>" + r[3] + "</span>"; 290 $(td_d).append("<br>" + priceline); 291 } else { 292 $(td_d).append("<br><span align=left class='search-price'>" + r[3] + "</span>"); 293 } 294 } 295 296 if (this._settings.ratings) { 297 rating = 0; 298 if(typeof r[4] != "undefined" && r[4].length > 0) 299 rating = r[4]; 300 $(td_d).append("<br><span class='star"+rating+"'><i class='fa fa-star-o'></i> <i class='fa fa-star-o'></i> <i class='fa fa-star-o'></i> <i class='fa fa-star-o'></i> <i class='fa fa-star-o'></i></span>"); 301 } 302 303 //$(tr).append(th); 304 $(tr).append(td); 305 306 $(tr).bind('mousemove', {searchlight: this}, function(evt) { 307 searchlight = evt.data.searchlight; 308 searchlight.selectRow(this._rowId); 309 }); 310 $(tr).bind('click', {searchlight: this}, function(evt) { 311 searchlight = evt.data.searchlight; 312 searchlight.activateRow(this._rowId); 313 }); 314 315 tr._rowId = this._rowCount; 316 actURL = r[0]; 317 actURL = actURL.replace(/&/g, "&"); 318 tr._actionValue = actURL; 319 this._resultsContainer.children('table').append(tr); 320 this._rowCount++; 321 } 322 323 // Add spacer if this isn't the first category 324 var tr = document.createElement('tr'); 325 //var th = document.createElement('th'); 326 var td = document.createElement('td'); 327 tr.className = 'searchlight-spacer-row'; 328 329 //$(tr).append(th); 330 $(tr).append(td); 331 this._resultsContainer.children('table').append(tr); 332 333 this._categoryCount++; 334 }; 335 336 337 SearchLight.prototype.selectRow = function(id) { 338 this._selectedRow = id; 339 340 this._resultsContainer.find('tr:not(.searchlight-spacer-row)').each(function(i) { 341 if (this._rowId == id) { 342 if (!$(this).hasClass('searchlight-selected')) { 343 $(this).removeClass('searchlight-not-selected'); 344 $(this).addClass('searchlight-selected'); 345 } 346 } else { 347 if (!$(this).hasClass('searchlight-not-selected')) { 348 $(this).removeClass('searchlight-selected'); 349 $(this).addClass('searchlight-not-selected'); 350 } 351 } 352 }); 353 }; 354 355 SearchLight.prototype.activateRow = function(id) { 356 this.resultAction(this._resultsContainer.find('tr:not(.searchlight-spacer-row):eq('+ id +')')[0]._actionValue); 357 } 358 359 SearchLight.prototype.defaultResultAction = function(val) { 360 window.location.href = val; 361 }; 362 363 364 $.fn.searchlight = function(url, options) { 365 this.each(function() { 366 new SearchLight(this, url, options); 367 }); 368 }; 369})(jQuery); 370 371//custom callback function for ajax based searchlight / quicksearch (cross domain requires jsonp callbacks) 372function jsonp_callback(results) { 373 searchlight.clearResults(); 374 for (var i = 0; i < results.length; i++) { 375 var r = results[i];
376 if (r.results.length > 0 && typeof r.results[0][0] != 'undefined') { //hide of no results returned (empty array values) in json callback 377 //console.log(r.results[0][0]); 378 searchlight.addResultCategory(r.title, r.results); 379 } 380 } 381 if(typeof r.results[0][0] != 'undefined') { 382 searchlight.addFooter(r.results); 383 //setScrollbar(); 384 385 } else { 386 jQuery('.searchlight-balloon').hide(); 387 } 388 //run custom stuff directly in search results template (file that instansiates search light), if desired. Just include function in main template 389 if (typeof slPostProcessing == 'function') { 390 slPostProcessing(); 391 } 392 393} 394 395//Adjust position of quicksearch once search box is opened, since position changes after page load to to slow load of logo and/or pop down coupon bar 396function calibrateQS() { 397 398 var input = $("#searchlight"); 399 $(".searchlight-balloon").css({ 400 position: 'absolute', 401 top: input.offset().top + input.outerHeight(), 402 //width: input.outerWidth(), 403 //left: input.offset().left 404 right: $(document.body).innerWidth() - (input.offset().left + input.outerWidth()) 405 }); 406} 407 408function setScrollbar() { 409 410 //QuickSearch ScrollBar 411
412 if(typeof mCustomScrollbar !== "undefined") { 413 var $searchScroll = $('.searchlight-balloon'); 414 $searchScroll.mCustomScrollbar('destroy'); 415 if (jQuery.browser.mobile) { 416 $searchScroll.mCustomScrollbar('destroy'); 417 $searchScroll.css({ 418 'overflow': 'auto', 419 'max-height': '98px' 420 }); 421 } else { 422 $searchScroll.mCustomScrollbar({ 423 scrollInertia: 400 424 }); 425 } 426 } 427} 428 429function getPriceLvlQS() { 430 431 if(typeof pLvl != "undefined" && pLvl.length > 0) { 432 433 var num = parseInt(pLvl) || 0; 434 return num; 435 436 } else { 437 438 return 0; 439 440 } 441 442} 443 444
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.