PageSourceSearch

https://www.befrugal.com/Style/NEWjavascriptFunctions.js?v=ZdStaW986CZ4bMytW0TKepXxF87V6F13fWzwrjb8fzw

js befrugal.com collected 2026-09-24 09:19:12 UTC 39,052 bytes, 1,018 lines download raw bytes

1var Sys =
2{
3    Application: {
4        add_load: function (arg) {
5            $(document).ready(arg);
6            console.log("add_load: Just load once and hope it's fine", { fx: arg });
7        },
8        add_init: function (arg) {
9            $(document).ready(arg);
10            console.log("add_init: Just load once and hope it's fine", { fx: arg });
11        }
12    }
13};
14
15
16var friendArray = new Array();
17function createNewEmailRow(dvId, textboxClassName) {
18  if (!textboxClassName) {
19      textboxClassName = 'add-friend-textbox';
20  }
21  if(friendArray[dvId] == null)
22  {
23     friendArray[dvId] = 0;
24  }
25    // only allow 4 additional friends for emailing coupon
26    if (friendArray[dvId] < 4) {
27        friendArray[dvId]++;
28        var d = document.getElementById(dvId);
29        var _nwTxt = document.createElement('input');
30        var _nwBr = document.createElement('br');
31        var _nwMsg = document.createElement('span');
32        _nwTxt.type = 'text';
33        _nwTxt.className = textboxClassName;
34        _nwTxt.id = _nwTxt.name = 'friendEmail' + friendArray[dvId];
35        _nwMsg.id = _nwMsg.name = 'friendMsg' + friendArray[dvId];
36        _nwMsg.innerHTML = '&nbsp;*'
37        _nwMsg.className = 'coupon-email-frnd';
38        d.appendChild(_nwBr);
39        d.appendChild(_nwTxt);
40        d.appendChild(_nwMsg);
41        _nwTxt.addEventListener("keyup", function () {
42            _nwTxt.style.border = "1px solid #e5e5e8";
43            _nwMsg.className = 'coupon-email-frnd';
44        });
45        if (friendArray[dvId] == 4) {
46            var lnkId = dvId.replace('dvFriendEmail', 'lnkNewFriend');
47            document.getElementById(lnkId).innerHTML = ''; // disable
48        }
49    } else {
50        var lnkId = dvId.replace('dvFriendEmail', 'lnkNewFriend');
51        document.getElementById(lnkId).innerHTML = ''; // disable
52    }
53  
54}
55
56function createNewEmailRowValidate(dvId, textboxClassName, friendemail) {
57    if (friendArray[dvId] == null) {
58        friendArray[dvId] = 0;
59    }
60    if (friendArray[dvId] == 0) {
61        if ($(friendemail).val() != null && validEmail($(friendemail).val())) {
62            $(friendemail).attr("style", "border:1px solid #e5e5e8;margin-bottom: 5px;");
63            createNewEmailRow(dvId, textboxClassName);
64        } else {
65            $(friendemail).attr("style", "border:1px solid #F50000;");
66        }
67    }
68    else {
69        var d = document.getElementById('friendEmail' + friendArray[dvId]);
70        if (d.value && validEmail(d.value)) {
71            $('#friendEmail' + friendArray[dvId]).attr("style", "border:1px solid #e5e5e8;margin-bottom: 5px;");
72            createNewEmailRow(dvId, textboxClassName);
73        } else {
74            $('#friendEmail' + friendArray[dvId]).attr("style", "border:1px solid #F50000;");
75        }
76    }
77}
78
79function validEmail(email){      
80	var emailReg = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
81	return emailReg.test(email); 
82}
83
84function InvalidAddFriend(lstControl){
85   var cntrArr = lstControl.split(';');
86   var elm;
87   for(i=1;i<=4;i++)
88   {
89     elm = document.getElementById('friendMsg'+i);
90     if(elm !=null)
91     {
92       if (cntrArr.indexOf('friendEmail' + i) == -1)
93       {
94         elm.className = 'coupon-email-frnd';
95       }else{
96         elm.className = 'coupon-email-frnd-invalid';
97       }
98     }
99   }
100}
101
102function InvalidAddFriendBorder(lstControl) {
103    var cntrArr = lstControl.split(';');
104    var elm;
105    for (i = 1; i <= 4; i++) {
106        elm = document.getElementById('friendEmail' + i);
107        if (elm != null) {
108            if (cntrArr.indexOf('friendEmail' + i) == -1) {
109                elm.style["border"] = "1px solid #e5e5e8";
110            } else {
111                elm.style["border"] = "1px solid #F50000";
112            }
113        }
114    }
115    InvalidAddFriend(lstControl);
116}
117function hideCouponDetail() { }
118function EmailSuccessfullySent(Event, Action, Label) {
119    if (Event != null && Action != null && Label != null) {
120        ga('send', Event, Action, Label);
121    }
122    try {
123        recaptchaV2_reset();
124        hideCouponDetail();
125    } catch (e) { }
126}
127function EmailFailSent(msg) {
128    alert(msg);
129    try {
130        recaptchaV2_reset();
131        hideCouponDetail();
132    } catch (e) { }
133}
134
135var couponTextId = null;
136var bf_coupon_title_link = null;
137var lastPopupShown = null;
138
139function coupon_click() {
140    // flash assume yes
141    var bf_lnk = $get(bf_coupon_title_link);
142    googleTrackOut(couponTextId, 'yes');
143    eval(bf_lnk.getAttribute('onclick').replace('return ', '').replace('this', 'bf_lnk'));
144}
145
146function pageloadingtime(){
147    // loads all flash objects
148
149    //calculate the current time in afterload 
150    afterload = (new Date()).getTime();
151    // now use the beforeload and afterload to calculate the seconds
152    secondes = (afterload-beforeload)/1000;
153    // If necessary update in window.status
154    window.status='Page loaded in ' + secondes + ' seconds.';
155    // Place the seconds in the innerHTML to show the results
156    document.getElementById("loadingtime").innerHTML = "<font color='white'>Page loaded in  " + secondes + " seconds.</font>";
157       
158    //popDD();
159    flashload = (new Date()).getTime();
160    flSeconds = (flashload-afterload)/1000;
161    document.getElementById("loadingtime").innerHTML += "<br/><font color='white'>Flash and cb  " + flSeconds + " seconds.</font>";
162}
163
164function offerClick(lnk,id, isP, dest){
165    googleTrackOut(id, isP);
166    document.location = lnk.href;
167
168    var scrollToText = '&scrollTo=' + $(document).scrollTop();
169    var hashIndex = dest.indexOf("#");
170    var finalDest = dest.substring(0, hashIndex) + scrollToText + dest.substring(hashIndex);
171
172    var nWinow = window.open(finalDest, '_blank');
173    try { nWinow.focus(); } catch (er) { }
174    return false;
175}
176
177function cbOfferClick(lnk, id, isP, dest) {
178    var rnd = randomString();
179    googleTrackOut(id, isP);
180    document.location = addQueryStringValue(lnk.href, 'rndLookup=' + rnd);
181    var nWinow = window.open(addQueryStringValue(dest, 'rndLookup=' + rnd), '_blank');
182    try { nWinow.focus(); } catch (er) { }
183    return false;
184}
185var recaptchaV2_invisible = function (token, host) {
186    const tokenname = 'g-recaptcha-response-invis';
187    var parentDiv = $(host).closest('div');
188    var response = parentDiv.find(`input[name="${tokenname}"]`);
189    if (response.length == 0) {
190        response = $(`<input type="hidden" name="${tokenname}" />`).appendTo(parentDiv);
191    }
192    response.val(token);
193    $(host).closest('form').find('.recaptcha-click').click();
194}
195
196var recaptchaV2_validation = function (response) {
197    if ('function' === typeof recaptchaV2_validation_sendtofriend_callback) {
198        recaptchaV2_validation_sendtofriend_callback(response);
199    }
200    if ('function' === typeof recaptchaV2_validation_forgotpassword_callback) {
201        recaptchaV2_validation_forgotpassword_callback(response);
202    }
203}
204//Do not call this function directly.  Call recaptchaV2_render(container)
205var recaptchaV2_loader = function () {
206    recaptchaV2_loader.loadState = 2;
207    while (recaptchaV2_loader.backlog.length > 0) {
208        let nextEntry = recaptchaV2_loader.backlog.pop();
209        $(nextEntry).attr('captchaNumber', recaptchaV2_loader.widgetCount);
210        var isInvisible = $(nextEntry).hasClass('c-invisible');
211
212        var widgetid;
213        if (isInvisible) {
214            var hideAnimatedBadge = !$(nextEntry).hasClass('grecaptcha-nobadge') &&
215                $(nextEntry).hasClass('popup-captcha-span') &&
216                $(nextEntry).closest('.modal-content,.modal-content-clickout').length > 0;
217
218            if (hideAnimatedBadge) {
219                $(nextEntry).addClass('grecaptcha-nobadge');
220                /* Hack.  Don't show recaptcha badge until css "blowUpModal" animation completes.
221                 * While blowUpModal animation is running, badge will be mispositioned
222                 * In theory, we could wait 501ms, since the animation runs for 0.5s.  In practice, that isn't usually a long enough wait.
223                 * Only needed for modals (popup-captcha-span) and only if badge isn't hidden.
224                 */
225                $(document).one('animationend', '.modal-content,.modal-content-clickout', function () {
226                    $(nextEntry).removeClass('grecaptcha-nobadge');
227                });
228                setTimeout(function () {
229                    $(nextEntry).removeClass('grecaptcha-nobadge');
230                }, 750);
231            }
232            widgetid = grecaptcha.render(nextEntry, {
233                'sitekey': '6Lf2MscpAAAAANHlrwxJMY7manlrIITazmr9BnLF', 'size': 'invisible', 'badge':'bottomright', 'callback': function (token) { recaptchaV2_invisible(token, nextEntry); }
234            });
235
236
237        } else {
238            widgetid = grecaptcha.render(nextEntry, {
239                'sitekey': '6LcpK6spAAAAADSFvKOGpPE0zDqqMEET_diXHehQ', 'callback': recaptchaV2_validation
240            });
241        }
242        recaptchaV2_loader.recaptchawidgets[recaptchaV2_loader.widgetCount] = widgetid;
243        recaptchaV2_loader.widgetCount++;
244    }
245};
246recaptchaV2_loader.widgetCount = 0;
247recaptchaV2_loader.loadState = 0;
248recaptchaV2_loader.backlog = [];
249recaptchaV2_loader.recaptchawidgets = {};
250
251//Usage:
252//This clears the recaptchawidgets backlog/widget count/load state/etc. so a shell load will have a fresh start.
253var reset_recaptchav2_Loader = function () {
254    recaptchaV2_loader.widgetCount = 0;
255    recaptchaV2_loader.loadState = 0;
256    recaptchaV2_loader.backlog = [];
257    recaptchaV2_loader.recaptchawidgets = {};
258}
259
260//Usage: 
261//recaptchaV2_render(container): Load recaptcha in container.  If already loaded, resets it.
262//recaptchaV2_render(): Preload Recaptcha script.  Resets all captchas.
263var recaptchaV2_render = function (container) {
264    if (container && document.querySelectorAll(container).length<=0) {
265        // Tried to call render on a captcha that's been hidden or does not exist; update all existing captchas
266        console.log("Error adding Recaptcha to hidden container: " + container);
267        recaptchaV2_render();
268        return;
269    }
270    document.querySelectorAll(container).forEach(node => {
271        var currentWidget
272        if (!!$(node).attr('captchaNumber'))
273            currentWidget = recaptchaV2_loader.recaptchawidgets[$(node).attr('captchaNumber')];
274        //containerIsEmpty is used to see if the div is empty.  It will be empty if there is curently a widget when the captcha is in a updatepanel.
275        var containerIsEmpty = $(node).attr('captchaNumber')==undefined
276        //There's already a recaptcha here.  Reset it.
277        if (typeof (node) !== "undefined" && !containerIsEmpty) {
278            grecaptcha.reset(node);
279            return;
280        }
281        if ($(node)) {
282            recaptchaV2_loader.backlog.push(node);
283        }
284    });
285
286    switch (recaptchaV2_loader.loadState) {
287        case 0: //Recaptcha is not loaded
288            recaptchaV2_loader.loadState = 1;
289            $.getScript('//www.google.com/recaptcha/api.js?onload=recaptchaV2_loader&render=explicit');
290            break;
291        case 1: //Recaptcha is busy loading.
292            break;
293        case 2: //Recaptcha is already loaded.
294            if (!container) {
295                //Reset all captchas
296                for (var key in recaptchaV2_loader.recaptchawidgets) {
297                    if (!recaptchaV2_loader.hasOwnProperty(key) && document.getElementById(key)) {
298                        recaptchaV2_render(key);
299                    }
300                }
301            }
302            recaptchaV2_loader();
303            break;
304    }
305};
306var recaptchaV2_last_widgetid = function () {
307    return recaptchaV2_loader.recaptchawidgets[recaptchaV2_loader.widgetCount - 1];
308}
309var recaptchaV2_response = function (widget_id) {
310    return grecaptcha.getResponse(widget_id);
311}
312var recaptchaV2_reset = function (widget_id) {
313    if ('undefined' !== typeof (widget_id)) {
314        grecaptcha.reset(widget_id);
315    }
316    else {
317        for (var id = recaptchaV2_loader.widgetCount; id > 0; --id) {
318            grecaptcha.reset(recaptchaV2_loader.recaptchawidgets[id - 1]);
319        }
320    }
321};
322
323function bf_lgnGetUrl(elm) {
324    $get(elm).value = document.location.pathname + document.location.search;
325    return true;
326}
327function cb_CloseReload() {
328    document.location = document.location.href.replace('https://', 'http://');
329}
330function bf_blReload() {
331    window.location.reload(true);
332}
333function GoToBeFrugal(lnk) {
334    try {
335        if (window.opener != null && !window.opener.closed) {
336            try {
337                window.opener.open(lnk.href, '_blank');
338                window.close();
339            } catch (e2) {
340                window.open(lnk.href, '_blank');
341                window.close();
342            }
343        } else {
344            window.open(lnk.href, '_blank');
345            window.close();
346        }
347    } catch (e) { }
348    return false;
349}
350 //toadd ":", "<", ">","/", "\"
351function SearchUrlCleanup(link) {
352    return link.replace(/&/g, "[and]").replace(/%26/g, "[and]").replace(/\*/g, "[ast]").replace(/\+/g, "[plus]").replace(/\?/g, "[ques]").replace(/@/g, "[em]")
353        .replace(/%/g, "[percent]").replace(/:/g, "[colon]").replace(/</g, "[openang]").replace(/>/g, "[closeang]").replace(/\\/g, "[backslash]")
354        .replace(/[\u2018\u2019]/g, "'")/*this is called out seperately, since this is a keyboard dependant apostraphe*/.replace(/[\u{0080}-\u{FFFFF}]/gu, "")
355        .substring(0,150); /*this is to prevent the url from breaking if it's too large.  Nobody should want an actual response from this anyway*/
356}
357
358function SerchURLToDest(link) {
359    return link.replace(/\[and]/g, "&").replace(/\[ast]/g, "*").replace(/\[plus]/g, "+").replace(/\[ques]/g, "?").replace(/\[em]/g, "@").replace(/\[percent]/g, "%")
360    .replace(/\[colon\]/g, ":").replace(/\[openang\]/g, "<").replace(/\[closeang\]/g, ">").replace(/\[backslash\]/g, "\\");
361}
362
363function bf_man_click(elm) {
364    // DOM 2 Events 
365    var dispatchMouseEvent = function (target, var_args) {
366        var e = document.createEvent("MouseEvents");
367        // If you need clientX, clientY, etc., you can call 
368        // initMouseEvent instead of initEvent 
369        e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
370        target.dispatchEvent(e);
371    };
372    dispatchMouseEvent(elem, 'mouseover', true, true);
373    dispatchMouseEvent(elem, 'mousedown', true, true);
374    dispatchMouseEvent(elem, 'click', true, true);
375    dispatchMouseEvent(elem, 'mouseup', true, true);
376}
377
378function bf_mediaplayer_show() {
379    try {
380        if (player) { $get('player').className = 'bf-movie-on-screen'; }
381    } catch (e) { }
382}
383function bf_mediaplayer_hide() {
384    try {
385        if (player) {
386            //If video refuses to pause, try to hide the video anyways.
387            try {player.pauseVideo();} catch (e) { }
388            $get('player').className = 'bf-movie-off-screen';
389        }
390    } catch (e) { }
391}
392
393//Warning: Do not apply this function to new pages without testing that stopText and startText are assigned successfully.
394//Some pages (e.g., the contact us page) do not assign the startText successfully.  The latter can be tested by placing a breakpoint
395//in the button's codebehind and waiting 60 seconds for it to timeout.
396//Usage: OnClientClick="if(!bf_DisableButton(this, 'submitting', 'submit')) return false;"
397function bf_DisableButton(sender, stopText, startText) {
398    try {
399        if (sender.stop) return false;
400        sender.stop = true;
401        if (sender.nodeName === 'BUTTON') {
402            startText = startText || sender.innerHTML;
403            sender.innerHTML = stopText;
404        }
405        else {
406            startText = startText || sender.value;
407            sender.value = stopText;
408        }
409        setTimeout(function () {
410            bf_enable_button(sender, startText);
411        }, 60000);
412        return true;
413    } catch (e) {
414        return true;
415    }
416}
417
418function bf_enable_button(element, startText) {
419    var tmp = $(element);
420    var sender;
421    if (tmp.length > 0)
422        sender = tmp[0];
423    sender.stop = false;
424    if (sender.nodeName === 'BUTTON') {
425        sender.innerHTML = startText;
426    }
427    else {
428        sender.value = startText;
429    }
430}
431
432async function ShowTermsForRetailer(resultdiv, retailerid) {
433    const resp = await fetch('/Service/CashBackTermsControl/' + retailerid + '/');
434    if(resp.ok) {
435        document.getElementById(resultdiv).innerHTML = await resp.text();
436
437        //If The terms window is inside a modal with scrollhack turned on, make the popup scrollable if it hangs of the bottom.
438        setTimeout(function () {
439            var scrollhack = $('#' + resultdiv).closest('.modal-scrollhack');
440            if (scrollhack.length) {
441                var bottom = $('#' + resultdiv).position().top + $('#' + resultdiv).outerHeight(true);
442                var height = $(window).height();
443                if (height < bottom) {
444                    scrollhack.css('overflow-y', 'scroll');
445                }
446            }
447        }, 1);
448    };
449
450    hideCategories();
451    return false;
452}
453
454async function ShowTermsForClickout(resultdiv, id, cDate) {
455    const resp = await fetch(`/Service/CashBackTermsControl/0/?ClickID=${id}&ClickDate=${cDate}`);
456    if (resp.ok) {
457        document.getElementById(resultdiv).innerHTML = await resp.text();
458
459        //If The terms window is inside a modal with scrollhack turned on, make the popup scrollable if it hangs of the bottom.
460        setTimeout(function () {
461            var scrollhack = $('#' + resultdiv).closest('.modal-scrollhack');
462            if (scrollhack.length) {
463                var bottom = $('#' + resultdiv).position().top + $('#' + resultdiv).outerHeight(true);
464                var height = $(window).height();
465                if (height < bottom) {
466                    scrollhack.css('overflow-y', 'scroll');
467                }
468            }
469        }, 1);
470    };
471
472    hideCategories();
473    return false;
474}
475
476async function CallTermsAndPolicy(SourceID, type, isPopupID, TPPopupID, ContainerID, PopupButton) {
477    setTimeout(function () {
478        $('#' + PopupButton)[0].click();
479    }, 1);
480    HidePopupTemporarily(ContainerID);
481    if (isPopupID.trim()) {
482        PopupContainerOverflow(isPopupID, TPPopupID);
483    }
484    const resp = await fetch(`/ServicePages/BefrugalSiteWs/?handler=ShowTermsAndPolicy&type=${type}`);
485    if(resp.ok) {
486        document.getElementById(TPPopupID).innerHTML = await rslt.text();
487        CallTermsAndPolicyFollowUp(SourceID, isPopupID, TPPopupID, ContainerID);
488    };
489    return false;
490}
491
492function CallTermsAndPolicyFollowUp(SourceID, isPopupID, TPPopupID, ContainerID) {
493    centerScrollPopupSmallWindow(TPPopupID, ContainerID, SourceID, isPopupID);
494    toggleCenterScrollPopupListenerSmallWindow(TPPopupID, ContainerID, SourceID, isPopupID);
495    setTimeout(function () {
496        $('#' + TPPopupID + ' .privacy-policy a').bind('mouseup', function () {
497            var fixedpositiony = $("#" + TPPopupID).offset().top - $(window).scrollTop();
498            $('#' + TPPopupID).css("top", fixedpositiony);
499            var fixedpositionx = $("#" + TPPopupID).offset().left;
500            $('#' + TPPopupID).css("left", fixedpositionx);
501            $('#' + TPPopupID).css("position", "fixed");
502        });
503    }, 1000);
504
505}
506
507function PopupContainerOverflow(isPopupID, TPPopupID) {
508    if ($('#' + TPPopupID).css("display") != "none") {
509        $('#' + isPopupID).css("overflow", "visible");
510        window.addEventListener('click', function eventfunction(event) {
511            if (event.target.id != "isPopupID" && !($(event.target).parents('#' + isPopupID).length)) {
512                $('#' + isPopupID).css("overflow", "auto");
513                window.removeEventListener('click', eventfunction);
514            }
515        });
516    }
517    return false;
518}
519
520//Requires jquery.js, includeoffer.js
521//txtSearch: Textbox containing search terms
522//btnSearch: "Search" button.  Can be null.
523//ulAutoComplete: Autocomplete Node.
524//contextKey: ContextKey passed to search webservice.
525function InitSearch() {
526    let searchConfigs = document.querySelectorAll("[name=\"autoCompleteConfig\"]");
527    for (let i = 0; i < searchConfigs.length; ++i) {
528        try {
529            let args = JSON.parse(searchConfigs[i].value);
530            args.TemplateControl = searchConfigs[i];
531            MakeSearchListener(args);
532        } catch (e) { }
533    }
534}
535InitSearch();
536$(document).on('bf:shellload', InitSearch);
537
538function MakeSearchListener(args) {
539    var txtSearch = document.getElementById(args.Parameters.TxtSearchId);
540    var btnSearch = document.getElementById(args.Parameters.BtnSearchId);
541    var ulAutoComplete = document.getElementById(args.Parameters.UlAutoCompleteId);
542    var contextKey = args.Parameters.ContextKey;
543    var bypassSearch = args.Parameters.BypassSearch;
544    var favoriteSelected = args.Parameters.FavoriteToggle;
545    var templateControl = args.TemplateControl;
546
547    var HASSEARCH = 'data-hassearch';
548    if ($(txtSearch).attr(HASSEARCH)) return;
549    $(txtSearch).attr(HASSEARCH, true);
550
551    var VK_ENTER = 13;
552    var VK_UP = 38;
553    var VK_DOWN = 40;
554
555    var hideAutocomplete = function (targetClicked) {
556        delete ulAutoComplete.selectedNode;
557        ulAutoComplete.style.display = 'none';
558        hideCategories();
559        if (!targetClicked || !targetClicked.is('.search-box-mobile-and-desktop-view'))
560            FullMobileSearch(false);
561        ClearAutoCompleteMobile($(ulAutoComplete));
562    }
563
564    //Hide autocomplete dropdown when user clicks elsewhere.
565    $(document).click(function (e) {
566        var ignored = $([txtSearch, btnSearch, ulAutoComplete]);
567        if (!ignored.is(e.target) &&
568            ignored.has(e.target).length == 0 &&
569            !$(e.target).is('.icon-cross')) {
570            hideAutocomplete($(e.target));
571        }
572    });
573
574    var getAutoCompleteUrl = function (st) {
575        var fixquery = function (str) { return encodeURIComponent(str); };
576        return String.format("/SearchOffers/search.asmx/GetCompletionListMultiJSON?contextKey={0}&restrict=0&count={2}&prefixText={1}",
577            contextKey,
578            fixquery(st),
579            checkIfMobileBasedOnWidth() ? "10" : "10");
580    };
581
582    var doSearch = function () {
583        if (!ulAutoComplete.selectedNode) {
584            if (!bypassSearch) {
585                LgSrc(txtSearch.value);
586                return false;
587            }
588            else {
589                return true;
590            }
591        }
592        var ganText = $(ulAutoComplete.selectedNode).attr('data-gan');
593        var dest = $(ulAutoComplete.selectedNode).attr('data-href');
594
595        if (bypassSearch) {
596            txtSearch.value = $(ulAutoComplete.selectedNode).attr('data-friend');
597            delete ulAutoComplete.selectedNode;
598            ulAutoComplete.style.display = 'none';
599            if (favoriteSelected) {
600                doFavoriteAdd(ganText);
601            }
602        } else {
603            var searchType = $(ulAutoComplete.selectedNode).find('span:first');
604            if (searchType != null && searchType.length == 1 && searchType.attr('TACSearchType') == 'tool') {
605                dest = SerchURLToDest(dest)
606            }
607            var useShell = true;
608            var firstShell = $(ulAutoComplete).closest('[data-bfshell]');
609            if (firstShell.length == 0) {
610                useShell = false;
611            } else {
612                var shellchoice = firstShell.closest('[data-bfshell]').attr('data-bfshell');
613                if (0 === 'False'.localeCompare(shellchoice, 'en', { sensitivity: 'accent' })) {
614                    useShell = false;
615                }
616            }
617            if (new URL(document.baseURI).origin !== new URL(dest, document.baseURI).origin) {
618                window.open(dest, '_blank');
619            }
620            else if (useShell) {
621                hideAutocomplete();
622                txtSearch.value = '';
623                $(document).trigger("bf:ShellRequest", dest);
624            } else {
625                window.location = dest;
626            }
627        }
628        return false;
629    };
630
631    var currentSearchStatus = {
632        currentSearch: txtSearch.value,
633        currentHit: 0,
634        lastHit: 0,
635        SetSearch: function(search) {
636            currentSearchStatus.currentSearch = search;
637            currentSearchStatus.currentHit++;
638        }
639    }
640
641    var currentTimer = null;
642
643    var processAutocomplete = function () {
644        var myhit = currentSearchStatus.currentHit;
645        var autocompleteHandler = function (data) {
646            if (currentSearchStatus.lastHit > myhit) {
647                //Due to timing issues, the responses from the autocomplete task arrived out of order.  This result is stale and should be ignored.
648                return;
649            }
650
651            currentSearchStatus.lastHit = myhit;
652            var searches = $.parseJSON(data);
653            delete ulAutoComplete.selectedNode;
654            while (ulAutoComplete.firstChild) ulAutoComplete.removeChild(ulAutoComplete.firstChild);
655            ulAutoComplete.style.display = searches.length !== undefined && searches.length !== null ? '' : 'none';
656            var totalcount = 0;
657            var isplacement = false;
658            var useBasicSeeAll = true;
659            var showDealSeperator = null;
660            $.each(searches, function (i, currentResult) {
661                var currentTemplateParameters = currentResult.TemplateParameters;
662                var html = templateControl.getAttribute('template_' + currentResult.TemplateName);
663                for (var c = 0; c < currentTemplateParameters.length; c++) {
664                    if (c == 0) {
665                        currentTemplateParameters[c] = currentTemplateParameters[c].replace(/\"/g, "&quot;");
666                    }
667                    if (!!html) {
668                        html = html.replace(new RegExp('##' + c + '##', 'g'), currentTemplateParameters[c].replace(/\$/g, "$$$$"));
669                    }
670                    else {
671                        html = new String();
672                    }
673                }
674                totalcount++;
675                if (!isplacement && currentResult.IsRecommended) {
676                    isplacement = true;
677                    totalcount++;
678                    $("<div class='search-placement-divider'><span>Recommended Stores</span></div>").appendTo(ulAutoComplete);
679                }
680                if (showDealSeperator == false && SearchUrlCleanup(currentResult.URL).startsWith("/deals/")) {
681                    showDealSeperator = true;
682                    totalcount++;
683                    $("<div class='search-placement-divider'><span>Deals</span></div>").appendTo(ulAutoComplete);
684                }
685
686                if (useBasicSeeAll && currentResult.TemplateName == 'SearchAllSearchTerm') {
687                    useBasicSeeAll = false;
688                }
689
690                $('<div/>')
691                    .mouseenter(updateHighlightedNode.bind(this, i + AmountToAddToIndex(isplacement, showDealSeperator), false))
692                    .click(doSearch)
693                    .attr('data-href', SearchUrlCleanup(currentResult.URL))
694                    .attr('data-gan', currentResult.SearchInternalName)
695                    .attr('data-friend', currentResult.SearchFriendlyName)
696                    .attr('class', isplacement ? 'search-placement' : '')
697                    .html(html)
698                    .appendTo(ulAutoComplete);
699
700
701                if (showDealSeperator == null && SearchUrlCleanup(currentResult.URL).startsWith("/store/")) {
702                    showDealSeperator = false;
703                }
704
705            });
706
707            function AmountToAddToIndex(hasPlacement, dealSeperator)
708            {
709                var x = 0;
710                if (hasPlacement == true)
711                    x++;
712                if (dealSeperator == true)
713                    x++;
714                return x;
715            }
716
717            var searchBoxTerm = $('.search-box').val();
718            if (searchBoxTerm.length > 1 && contextKey != "favorites-search" && useBasicSeeAll) {
719                $('<div/>')
720                    .mouseenter(updateHighlightedNode.bind(this, totalcount, false))
721                    .click(doSearch)
722                    .html('<span><div class="searchbarhlb"><p class="search-text"><span class="searchBarTitle"></span><span class="searchbarAllResults">See all results for <strong>' + searchBoxTerm + '</strong> <span class="ico-moon icon-chevron-right" style="font-size:13px;vertical-align:middle;"></span></span><span class="searchBarTitle"></span><span class="searchbarhcbrate"></span><span class="searchbarhright"></span></p></div></span>')
723                    .attr('data-href', "/coupons/search/" + SearchUrlCleanup(searchBoxTerm) + "/")
724                    .appendTo(ulAutoComplete);
725            }
726            $(ulAutoComplete).mouseleave(function () {
727                if (ulAutoComplete.selectedNode) {
728                    ulAutoComplete.selectedNode.className = ulAutoComplete.selectedNode.className.replace(' selected', '');
729                    delete ulAutoComplete.selectedNode;
730                }
731            });
732        };
733
734        $.ajax({
735    		type: 'Get',
736    		url: getAutoCompleteUrl(currentSearchStatus.currentSearch),
737    		success: autocompleteHandler,
738    		error: function (jqXHR, textStatus, errorThrown) {
739    			//console.log("Failed search", textStatus, errorThrown);
740    		}
741    	});
742    };
743
744    var updateHighlightedNode = function (newIndex, isdelta) {
745        var nodes = ulAutoComplete.childNodes;
746        if (nodes.length == 0) return;
747        $.each(nodes, function (i, currentResult) {
748            if (currentResult.className.includes("search-placement-divider")) {
749                //nodes = ulAutoComplete.removeChild(ulAutoComplete.childNodes[i]);
750                //return false;
751            }
752        });
753        if (isdelta) {
754            if (ulAutoComplete.selectedNode) {
755                newIndex = (newIndex + (ulAutoComplete.selectedIndex || 0) + nodes.length) % nodes.length;
756            } else {
757                newIndex = newIndex == 1 ? 0 : nodes.length - 1;
758            }
759        }
760        if (ulAutoComplete.selectedNode) {
761            ulAutoComplete.selectedNode.className = ulAutoComplete.selectedNode.className.replace(' selected', '');
762        }
763        ulAutoComplete.selectedIndex = newIndex;
764        ulAutoComplete.selectedNode = nodes[newIndex];
765        ulAutoComplete.selectedNode.className = ulAutoComplete.selectedNode.className + ' selected';
766    };
767
768    $(txtSearch).bind({
769        keydown: function (e) {
770            //Prevent caret from moving while user navigates auto-complete dropdown. 
771            return e.keyCode != VK_UP && e.keyCode != VK_DOWN;
772        },
773        keypress: function (e) {
774            if (e.keyCode === VK_ENTER) {
775                e.preventDefault();
776                e.stopPropagation();
777                doSearch();
778            }
779        },
780        keyup: function (e) {
781            if (e.keyCode == VK_ENTER) return;
782            if (e.keyCode == VK_UP || e.keyCode == VK_DOWN) {
783                updateHighlightedNode(e.keyCode == VK_DOWN ? 1 : -1, true);
784                e.preventDefault();
785                return;
786            }
787
788            if (currentSearchStatus.currentSearch == txtSearch.value) return;
789            currentSearchStatus.SetSearch(txtSearch.value);
790
791            if (currentTimer) {
792                //Debounce
793                clearTimeout(currentTimer);
794            }
795            currentTimer = setTimeout(processAutocomplete, 50);
796        },
797        paste: function (e) {
798            currentTimer = setTimeout(function () {
799                currentSearchStatus.SetSearch(txtSearch.value);
800                processAutocomplete();
801            }, 50);
802        },
803        click: function (e) {
804            console.log('Click X');
805            currentTimer = setTimeout(function () {
806                currentSearchStatus.SetSearch(txtSearch.value);
807                processAutocomplete();
808            }, 50);
809        }
810    });
811    $(btnSearch).click(doSearch);
812};
813
814function ClearAutoCompleteMobile(autoCompleteMobile) {
815    $('.store-search-box').val('');
816    $('.icon-cross').hide();
817    autoCompleteMobile.empty();
818}
819
820// Add toAdd to the link as a query string value.  If there is a tag, keep the tag at the end of the url
821function addQueryStringValue(link, toAdd) {
822    var start = link;
823    var end = '';
824
825    if (link.indexOf("#") > -1) {
826        start = link.split('#')[0];
827        end = '#' + link.split('#')[1];
828    }
829
830    var seperator = '?';
831    if (link.indexOf('?') > -1)
832        seperator = '&';
833
834    return start + seperator + toAdd + end;
835}
836
837function showCategories() {
838    var dropdownUL = document.querySelector("#dropdown-nav");
839    if (dropdownUL) dropdownUL.className = "dropdown-nav open";
840}
841
842function hideCategories() {
843    var dropdownUL = document.querySelector("#dropdown-nav");
844    if(dropdownUL) dropdownUL.className = "dropdown-nav bf-is-hidden";
845}
846
847function toggleCategories() {
848    var dropdownUL = document.querySelector("#dropdown-nav");
849    if (dropdownUL.className == "dropdown-nav open")
850        dropdownUL.className = "dropdown-nav bf-is-hidden";
851    else
852        dropdownUL.className = "dropdown-nav open";
853}
854
855function doFavoriteAdd(retailerid) {
856    const token = $('input[name="__RequestVerificationToken"]').val();
857    $.ajax({
858        type: "POST",
859        url: '/ServicePages/BefrugalSiteWs/?handler=AddStoreFavorite',
860        data: { retailerInternalName: retailerid },
861        headers: { RequestVerificationToken: token },
862        success: async function (msg) {
863            var inputDiag = $(".favoriteSearchBoxInputClass");
864            if (inputDiag.length > 0)
865                inputDiag[0]._haschanged = false;
866
867            if (msg.alreadyFull) {
868                //alreadyFull
869                console.log("favorites full");
870                $(".searchboxSuccess").hide();
871                $(".searchboxFull").css('display', 'inline-block');
872            }
873            else {
874                if (msg.storeIsFavorite) {
875                    let originaltext = $(".favorite-search-box").val();
876                    await RefreshPage();
877                    //success
878                    $(".favorite-search-box").val(originaltext);
879                    $(".searchboxSuccess").show();
880                }
881                else {
882                    //alreadyadded
883                    $(".searchboxSuccess").hide();
884                    $(".searchboxFull").hide();
885                }
886            }
887        },
888        error: function (msg) {
889            console.log("failure to add favorite");
890        }
891    });
892}
893
894function toggleJoinLogin (memberLogin, joinNow) {
895    var joinDiv = $("#join_dialog_wrapper");
896    var signDiv = $("#sign_dialog_wrapper");
897    var style = joinDiv.css("display");
898    joinDiv.css("display", signDiv.css("display"));
899    signDiv.css("display", style);
900    if (style == "none")
901    {
902        $("#loginLnk").text(memberLogin);
903        $("#joinScroll").text(joinNow);
904    }
905    else
906    {
907        $("#loginLnk").text(joinNow);
908                
909
910        $("#joinScroll").text(memberLogin);
911
912    }
913}
914
915function checkIfMobileBasedOnWidth() {
916    return $(window).width() < 750;
917}
918
919function ScrollToTop() {
920    jQuery('html,body').animate({ scrollTop: jQuery('#TopOfPageControl').offset().top }, 250);
921}
922
923var shouldShowOffer = false;
924function ShowOffersFull() {
925    if ($find('pceFavoriteOffersFullBehaviorID') != null) {
926        shouldShowOffer = true;
927        ShowOffersFullPopup();
928    }
929    else {
930        shouldShowOffer = true;
931        Sys.Application.add_load(function(event) { 
932            ShowOffersFullPopup();
933        });
934    }
935}
936
937function ShowOffersFullPopup() {
938    if (shouldShowOffer) {
939        $find('pceFavoriteOffersFullBehaviorID').show();
940        shouldShowOffer = false;
941    }
942}
943
944var shouldShowDeal = false;
945function ShowDealsFull() {
946    if ($find('pceFavoriteDealsFullBehaviorID')!=null) {
947        shouldShowDeal = true;
948        ShowDealsFullPopup();
949    }
950    else {
951        shouldShowDeal = true;
952        Sys.Application.add_load(function (event) {
953            ShowDealsFullPopup();
954        });
955    }
956}
957
958function ShowDealsFullPopup() {
959    if (shouldShowDeal) {
960        $find('pceFavoriteDealsFullBehaviorID').show();
961        shouldShowDeal = false;
962    }
963}
964
965//#region Sidebar
966function disableIfNeeded(button) {
967    if (button.classList.contains("alreadyClicked")) {
968        return false;
969    }
970
971    button.classList.add("alreadyClicked");
972
973    return true;
974}
975//#Region RetailerDescriptionSideBar Show button
976$(document).on('click', '.seeMoreSpn', function (e) {
977    var parent = $(e.target).parent();
978
979    //this is so that if the "more" button is clicked directly, it goes up two levels, while if the "..." is clicked, it goes up one level.
980    if (parent.hasClass("seeMoreSpn")) {
981        parent = parent.parent();
982    }
983    showRetailerDescription(parent);
984});
985function showRetailerDescription(container) {
986    $(container).find(".continuation").show();
987    $(container).find(".seeMoreSpn").hide();
988}
989//#region CashBackCategories hide/expand
990$(document).on('click', '.cashback-sidebar-up-arrow', function (e) {
991    var parent = $(e.target).parent();
992    if (parent.hasClass('cashback-sidebar-up-arrow')) {
993        parent = parent.parent();
994    }
995    cashback_sidebar_Hide(parent);
996});
997
998$(document).on('click', '.cashback-sidebar-down-arrow', function (e) {
999    var parent = $(e.target).parent();
1000    if (parent.hasClass('cashback-sidebar-down-arrow')) {
1001        parent = parent.parent();
1002    }
1003    cashback_sidebar_Show(parent);
1004});
1005
1006function cashback_sidebar_Hide(container) {
1007    container.find('.Cashback-Department-Contents').hide();
1008    container.find('.cashback-sidebar-down-arrow').show();
1009    container.find('.cashback-sidebar-up-arrow').hide();
1010}
1011function cashback_sidebar_Show(container) {
1012    container.find('.Cashback-Department-Contents').show();
1013    container.find('.cashback-sidebar-down-arrow').hide();
1014    container.find('.cashback-sidebar-up-arrow').show();
1015}
1016
1017//#endregion
1018//#endregion

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.