\";\n\tresHtml += \"\" + storeObj.storeName + \" #\" + String(storeObj.storeNum) + \"
\";\n\tlet dirHtml = \"Directions \";\t//Directions link\n\tresHtml += \"\" + storeObj.hoursHtml + dirHtml + \"
\";\t//Store hours & directions link\n\tlet addrText = storeObj.address.replace(\" \",\" \") + \", \" + storeObj.city.replace(\" \",\" \");\n\taddrText += \", \" + storeObj.state.replace(\" \",\" \") + \", \" + storeObj.zip;\n\tresHtml += \"\" + addrText + \"
\";\t//Address\n\tresHtml += \"\" + String(storeObj.phone) + \" \";\t//Phone link\n\tlet storeLink = \"/locations/\" + (storeObj.openSoon? \"newstore/\" : \"store/\") + storeObj.city.toLowerCase().replace(/[^0-9a-z]/g,\"-\");\n\tstoreLink += \"-\" + String(storeObj.storeNum) + \"/\";\n\tresHtml += \"\");\n\t//Start shopping button\n\tlet startShoppingButton = jQuery(\"\");\n\tif(isLoggedIn){\n\t\tstartShoppingButton.addClass(\"is-logged-in\");\n\t}\n\telse{\n\t\tstartShoppingButton.append(\"Great Food. Wholesale Prices.
\")\n\t}\n\tstartShoppingButton.attr(\"href\", \"/search/fullsearch/\");\n\tbuttonsRow.append(startShoppingButton);\n\tif(!isLoggedIn){\n\t\t//Sign in button\n\t\tlet signInButton = jQuery(\"\");\n\t\tsignInButton.click(function(){\t//Open the sign in/sign up modal\n\t\t\tlet signInModal = jQuery(\"div.signinModal\");\n\t\t\topenModal(signInModal, signInModal.find(\"input[name='un']\"));\n\t\t});\n\t\tsignInButton.append(\"Savings & specials!
\");\n\t\tbuttonsRow.append(signInButton);\n\t}\n\telse{\n\t\t//Specials button\n\t\tlet specialsButton = jQuery(\"\");\n\t\tspecialsButton.attr(\"href\", \"/specials/\");\n\t\tbuttonsRow.append(specialsButton);\n\t\t//Featured brands button\n\t\tlet featuredBrandsButton = jQuery(\"Shop Our Brands \");\n\t\tfeaturedBrandsButton.attr(\"href\", \"/search/exclusivebrands/\");\n\t\tbuttonsRow.append(featuredBrandsButton);\n\t\t//Temporary price reductions button\n\t\tlet salesButton = jQuery(\"\");\n\t\tsalesButton.attr(\"href\", \"/search/temporarypricereductions/\");\n\t\tbuttonsRow.append(salesButton);\n\t}\n\treturn buttonsRow;\n}\n\nfunction hideLoadingSpinners(){\n\t/* Removes the loading spinners on the map and the results */\n\tjQuery(\".map .map-sidebar\").removeClass(\"is-loading\");\n\tjQuery(\"#map_canvas\").removeClass(\"is-loading\");\n\tjQuery(\".map .map-sidebar-search-button\").removeAttr(\"disabled\");\n}\n\nfunction parseSearchTerm(searchTerm, suggestCities=false, extendDistance=false){\n\t/* Parses a search term and returns an object containing the search parameters */\n\tlet dataObject = {\n\t\tmethod: \"getStoresBySearchAJAX\",\n\t\tdistance: 0,\n\t\tcoordinates: \"\",\n\t\tstate: \"\",\n\t\tcity: \"\",\n\t\tzip: \"\",\n\t\tstoreNum: \"\",\n\t\tsetSession: false,\n\t\thotSheet: true\n\t};\n\t//Determine what type of search to perform\n\tlet stateAbbr = getStateAbbr(searchTerm);\n\tif(searchTerm.trim() === \"\"){\t//Return the default data object\n\t\treturn dataObject;\n\t}\n\telse if(searchTerm === \"showAllStores\"){\t//Search for all stores\n\t\tdataObject.distance = -2;\n\t\tdataObject.all = true;\n\t\t//Clear the search form\n\t\tjQuery(\".map-sidebar-search-input\").val(\"\");\n\t}\n\telse if(stateAbbr !== \"\"){\t//Statewide search\n\t\tdataObject.distance = -1;\n\t\tdataObject.state = stateAbbr;\n\t}\n\telse if(/^#?\\s*\\d{2,4}$/.test(searchTerm)){\t//Store number search\n\t\tdataObject.distance = 50;\n\t\tdataObject.storeNum = searchTerm.replace(/#\\s*/g,\"\");\n\t}\n\telse if(/-?\\d{1,3}\\.\\d+\\s?(n|s|N|S)?,\\s?-?\\d{1,3}\\.\\d+\\s?(e|w|E|W)?/.test(searchTerm)){\t//Coordinate search\n\t\tdataObject.coordinates = searchTerm;\n\t\tdataObject.distance = 75;\n\t}\n\telse if(/^\\d{5}(?:[-\\s]\\d{4})?$/.test(searchTerm)){\t//Zip code search\n\t\tdataObject.distance = 50;\n\t\tdataObject.zip = searchTerm;\n\t}\n\telse{\t//The search term is either a city or a city and a state\n\t\tsearchTerm = searchTerm.replace(/,/g,\" \");\t//Remove commas\n\t\tlet words = searchTerm.split(\" \");\n\t\tstateAbbr = getStateAbbr(words[words.length - 1]);\n\t\tif(stateAbbr === \"\"){\n\t\t\tstateAbbr = getStateAbbr(words[words.length - 2] + \" \" + words[words.length - 1]);\n\t\t\tif(stateAbbr !== \"\") words.pop();\t//Remove part of the state name from the list\n\t\t}\n\t\telse words.pop();\t//Remove the state name from the list\n\t\tif(stateAbbr !== \"\"){\t//State portion of search\n\t\t\tdataObject.state = stateAbbr;\n\t\t\tdataObject.city = words.join(\" \");\n\t\t}\n\t\telse{\t//Search term is only a city\n\t\t\tif(suggestCities){\t//Show suggestions for cities\n\t\t\t\tgetCitySuggestions(searchTerm);\n\t\t\t\thideLoadingSpinners();\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\telse{\t//Search for just a city\n\t\t\t\tdataObject.city = searchTerm;\n\t\t\t}\n\t\t}\n\t\tdataObject.distance = (dataObject.city.length === 0)? -1 : 50;\n\t}\n\t//Extend the search distance if requested\n\tif(extendDistance === true && dataObject.distance > 0){\n\t\tdataObject.distance = 300;\n\t}\n\treturn dataObject;\n}\n\nfunction searchForStores(searchTerm, suggestCities=true, extendDistance=false){\n\t/* Uses the passed search term to send an AJAX request for matching stores */\n\t//Set the loading spinners\n\tif(jQuery(window).width() <= 768)\n\t\tjQuery(\".map .map-sidebar\").addClass(\"is-loading\");\n\tjQuery(\"#map_canvas\").addClass(\"is-loading\");\n\tjQuery(\".map .map-sidebar-search-button\").attr(\"disabled\",\"disabled\");\n\tjQuery(\"#map-sidebar-search-results\").attr(\"data-num-shown\",\"8\");\n\t//Determine what kind of search to perform\n\tlet dataObject = parseSearchTerm(searchTerm, suggestCities, extendDistance);\n\tif(dataObject.method === undefined) return;\n\telse lastSearchTerm = searchTerm;\t//Save the search term\n\t//Store search request\n\tjQuery.ajax({\n\t\ttype: \"GET\",\n\t\turl: \"/cfcs/storeGW.cfc\",\n\t\tdataType: \"json\",\n\t\tdata: dataObject,\n\t\tsuccess: function(res){\n\t\t\tclearResults();\n\t\t\tif(res.length > 0 && \"error\" in res[0]){\t//Check if the request returned an error\n\t\t\t\tlet errHtml = \"\" + res[0].error + \"
\";\n\t\t\t\tjQuery(\"#map-sidebar-search-results\").html(errHtml);\n\t\t\t\thideLoadingSpinners();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if(dataObject.distance === -1 && res.length > 0 && res[0].state !== dataObject.state){\n\t\t\t\t//A statewide search didn't have any results so all the stores were returned\n\t\t\t\t//Get the full state name\n\t\t\t\tlet stateName = \"\";\n\t\t\t\tfor(let i=0; i 0 && curRun.HOURS === storeObj.hours[0].HOURS){\n\t\t\t\t\t\t\t//Wrap the first entry to include the ending days if they have the same hours\n\t\t\t\t\t\t\tstoreObj.hours[0].ENDDAY = curRun.STARTDAY;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse{\n\t\t\t\t\t\t\tstoreObj.hours.push(curRun);\t//Append the last run to the store hours\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tfailure: function(res){}\n\t\t\t\t});\n\t\t\t\t//End of store hours\n\t\t\t\tstoreObj.address = (storeObj.addr1 + \" \" + storeObj.addr2).trim();\n\t\t\t\tstoreObj.bigBox = (storeObj.bigBox === 1);\n\t\t\t\tstoreObj.openSoon = (storeObj.openSoon === 1);\n\t\t\t\tstoreObj.hasInstacart = (storeObj.instacart === 1);\n\t\t\t\tstoreObj.hasClickCarry = (storeObj.clickCollect === 1);\n\t\t\t\tshowSearchResult(storeObj, resetCount=(i === 0));\n\t\t\t}\n\t\t\tsetMapSize();\n\t\t\tif(res.length === 0){\n\t\t\t\tif(extendDistance === false){\n\t\t\t\t\t//Perform an expanded search around the same location\n\t\t\t\t\tsearchForStores(searchTerm,false,true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse{\n\t\t\t\t\t//The function call already was an expanded search\n\t\t\t\t\tshowNoResults();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse showHideClickCarry();\n\t\t\thideLoadingSpinners();\n\t\t\tupdateDynamicContent(dataObject);\n\t\t},\n\t\tfailure: function(res){\n\t\t\t/* Show a generic error message */\n\t\t\tjQuery(\"#map-sidebar-search-results\").html(\"Failed to perform search
\");\n\t\t\thideLoadingSpinners();\n\t\t}\n\t});\n}\n\nfunction setMapSize() {\n\t/*Resizes the map bounds to fit the screen*/\n\tif(typeof bounds !== 'undefined' && jQuery(\"#map_canvas\").attr(\"data-initialized\") === \"1\") {\n\t\t//Check if the bounds are empty\n\t\tif(bounds.isEmpty()) return;\n\t\t//Set padding around the bounds to avoid markers being placed under the sidebar\n\t\tlet leftPadding = jQuery(\".map .map-sidebar\").width() + 40;\n\t\tmap.fitBounds(bounds, {left: leftPadding, top: 30, bottom: 30, right: 20});\n\t\t//Check if the map is too zoomed in\n\t\tif(map.getZoom() > 15){\n\t\t\tmap.setZoom(15);\n\t\t\tif(Object.keys(googleMarkers).length === 1){\n\t\t\t\t//Recenter the map if there is only one marker\n\t\t\t\tlet usableWidth = jQuery(window).width() - leftPadding;\n\t\t\t\tmap.panToBounds(bounds, {left: usableWidth / 2 + leftPadding, top: 30, bottom: 30, right: 20});\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction setWarehouse(storeNumber){\n\t/* Sets the user's currently active warehouse via an AJAX request and then reloads the page */\n\tlet dataObj = {\n\t\tmethod: \"setStoreSessionAjax\",\n\t\tstoreNum: storeNumber,\n\t\t_: Math.floor(Date.now() / 1000)\n\t};\n\t//Set the button to be loading\n\tlet warehouseButton = jQuery(\"#map-sidebar-search-results .map-sidebar-search-result-warehouse-button[data-store-num='\" + String(storeNumber) + \"']\");\n\twarehouseButton.addClass(\"is-loading\");\n\tjQuery.ajax({\n\t\ttype: \"GET\",\n\t\turl: \"/cfcs/accountDAO.cfc\",\n\t\tdataType: \"json\",\n\t\tdata: dataObj,\n\t\tsuccess: function(res){\n\t\t\tlet warehouseForm = warehouseButton.parent();\n\t\t\t//Assemble the search parameters for the page reload\n\t\t\tlet toSearch = parseSearchTerm(lastSearchTerm);\n\t\t\tdelete toSearch[\"method\"];\t//Delete the backend function name from the form\n\t\t\tif(lastSearchTerm !== \"\") toSearch[\"submitCheck\"] = 1;\n\t\t\t//Add the needed inputs to the hidden form\n\t\t\tfor(let k in toSearch){\n\t\t\t\tlet newInput = jQuery(\" \").attr(\"type\",\"hidden\");\n\t\t\t\tnewInput.attr(\"name\",k).attr(\"value\",toSearch[k]);\n\t\t\t\twarehouseForm.append(newInput);\n\t\t\t}\n\t\t\t//Submit the form to reload the page while maintaining the most recent search results\n\t\t\twarehouseForm.submit();\n\t\t},\n\t\tfailure: function(){}\n\t});\n}\n\nfunction showCityOption(cityObj){\n\t/* Shows a city search option in the results list */\n\tlet wrapper = jQuery(\"\").addClass(\"map-sidebar-city-search-option\");\n\twrapper.text(cityObj.CITY + \", \" + cityObj.STATE).attr(\"type\",\"button\");\n\twrapper.on(\"click\",function(e){\n\t\tsearchForStores(jQuery(this).text());\n\t\tjQuery(\".map-sidebar-search-input\").val(jQuery(this).text());\n\t});\n\tjQuery(\"#map-sidebar-search-results\").append(wrapper);\n}\n\nfunction showHideClickCarry(){\n\t/* Shows or hides store results without Click and Carry support */\n\tlet hasMap = (jQuery(\"#map_canvas\").attr(\"data-initialized\") === \"1\");\n\tlet isChecked = jQuery(\".map-sidebar-search-filters input[type='checkbox']\").prop(\"checked\");\n\tvar adjustedNum = 1;\n\t//Loop through results and hide those without Click and Carry support if isChecked is true\n\tjQuery(\".map-sidebar-search-result-card\").each(function(){\n\t\tlet elem = jQuery(this);\n\t\telem.find(\".map-sidebar-search-result-number\").text(String(adjustedNum) + \" -\");\t//Update the current result number\n\t\tlet hasClickCarry = elem.find(\".map-sidebar-search-result-click-carry\").text().length > 0;\n\t\tif(isChecked && !hasClickCarry){\t//Hide results without click and carry\n\t\t\telem.addClass(\"hidden\");\n\t\t\tif(hasMap){\n\t\t\t\t//Remove the marker if the result should be hidden\n\t\t\t\tlet num = parseInt(elem.attr(\"data-num\"));\n\t\t\t\tgoogleMarkers[num].setMap(null);\n\t\t\t}\n\t\t}\n\t\telse{\t//Show results with click and carry or show all results\n\t\t\telem.removeClass(\"hidden\");\n\t\t\tif(hasMap){\n\t\t\t\tlet num = parseInt(elem.attr(\"data-num\"));\n\t\t\t\tif(googleMarkers[num].getMap() === null){\n\t\t\t\t\t//Re-add the marker if the result should be shown\n\t\t\t\t\taddMarker(searchResults[num],num,adjustedNum);\n\t\t\t\t}\n\t\t\t}\n\t\t\tadjustedNum++;\n\t\t}\n\t});\n\tadjustedNum--;\n\t//Update the total count\n\tjQuery(\".map-sidebar-search-results-count\").html(\"Found \" + String(adjustedNum) + \" \");\n\tupdateLazyLoading();\n}\n\nfunction showNoResults(){\n\t/* Show the message when no results are found */\n\tjQuery(\"#map-sidebar-search-results-count\").html(\"Found 0 \");\n\tjQuery(\"#map-sidebar-search-results\").html(\"Found 0 nearby locations
\");\n}\n\nfunction showSearchResult(storeObj, resetCount=false){\n\tif(typeof showSearchResult.curStoreNum === \"undefined\"){\n\t\tshowSearchResult.curStoreNum = 1;\n\t}\n\tif(resetCount){\n\t\tshowSearchResult.curStoreNum = 1;\n\t}\n\t//Save the result to the global variable\n\tsearchResults[showSearchResult.curStoreNum] = storeObj;\n\t//Create the store hours html\n\tlet hourHtml = \"\";\n\tfor(let i=0; i\";\n\t\thourHtml += storeObj.hours[i].STARTDAY;\n\t\tif(storeObj.hours[i].STARTDAY !== storeObj.hours[i].ENDDAY){\n\t\t\thourHtml += \"‑\" + storeObj.hours[i].ENDDAY;\n\t\t}\n\t\thourHtml += \" \" + storeObj.hours[i].HOURS;\n\t\thourHtml += \"