1!(function ($) { 2 "use strict"; 3 4 var $dateInput = $("#date"); 5 6 var dayKeysByIndex = ["sun", "mon", "tues", "wed", "thurs", "fri", "sat"]; 7 8 // If dropdown for branch id doesnt exist it will use defaultbranch. 9 var getBranchId = function () { 10 var $branch = $('select[name="branch"]'); 11 return $branch.length ? $branch.val() : defaultBranchId; 12 }; 13 14 // Check if the selected date is closed 15 var isDateClosed = function (dateValue) { 16 if (typeof openingTimesByBranch === "undefined" || !dateValue) { 17 return false; 18 } 19 20 var branchId = getBranchId(); 21 22 // No Branch selected, all dates disabled. 23 if (!branchId) { 24 return true; 25 } 26 27 var openingTimes = openingTimesByBranch[branchId] || {}; 28 var selectedDate = new Date(dateValue + "T00:00:00"); 29 var dayKey = dayKeysByIndex[selectedDate.getDay()]; 30 var sessions = openingTimes[dayKey]; 31 return !sessions || !sessions.length; 32 }; 33 34 var $occasionSelect = $('select[name="occasion"]'); 35 if ($occasionSelect.length) { 36 $occasionSelect.on("change", function() { 37 $occasionSelect.toggleClass("has-value", !!$occasionSelect.val()); 38 }); 39 } 40 41 $(".book-a-table .select select").each(function() { 42 var $select = $(this); 43 $select.on("mousedown", function() { 44 $select.addClass("is-open"); 45 }); 46 $select.on("blur change", function() { 47 $select.removeClass("is-open"); 48 }); 49 }); 50 51 var fp = null; 52 var $branchSelect = $('select[name="branch"]'); 53 if ($dateInput.length && typeof flatpickr !== "undefined") { 54 var $dateValidate = $dateInput.closest(".form-group").find(".validate"); 55 var $dateFieldWrap = $dateInput.closest(".date-field-wrap"); 56 57 // Flatpickr configuration properties and functions. 58 fp = flatpickr($dateInput[0], { 59 dateFormat: "Y-m-d", 60 altInput: true, 61 altFormat: "d/m/Y", 62 minDate: "today", 63 static: true, 64 disable: [ 65 function (date) { 66 var value = 67 date.getFullYear() + 68 "-" + 69 String(date.getMonth() + 1).padStart(2, "0") + 70 "-" + 71 String(date.getDate()).padStart(2, "0"); 72 return isDateClosed(value); 73 } 74 ], 75 onOpen: function (selectedDates, dateStr, instance) { 76 const inputToFocus = instance.altInput || instance.element; 77 78 // Flat picker is setting internally dateInput.focus(), Safari sees this as that this input has a focus and it should scroll 79 // to it, so scrollY = 0, during debugging, date clicked, flatpickr focus, input gets focu
79s, and safari scrolls to top. 80 // so we need to force a prevent scroll (so that safari focuses but doesn't scroll.) 81 82 const originalFocus = inputToFocus.focus; 83 inputToFocus.focus = function (options) { 84 options = options || {}; 85 options.preventScroll = true; 86 originalFocus.call(this, options); 87 }; 88 89 if (!getBranchId()) { 90 fp.close(); 91 if ($branchSelect.length) { 92 $branchSelect[0].focus({ preventScroll: true }); 93 } 94 return; 95 } 96 $dateFieldWrap.addClass("is-open"); 97 }, 98 onClose: function(){ 99 $dateFieldWrap.removeClass("is-open"); 100 }, 101 onChange: function (selectedDates, dateStr) { 102 var closed = isDateClosed(dateStr); 103 $dateValidate.html( 104 closed ? "We are closed on this day, please pick another date" : "" 105 ); 106 } 107 }); 108 109 // If a branch dropdown exists, clear out any date that was picked before the branch changed. 110 if ($branchSelect.length) { 111 $branchSelect.on("change", function () { 112 fp.clear(); 113 $dateValidate.html(""); 114 fp.redraw(); 115 }); 116 } 117 // capture the pickers state before the click that opens it 118 // isn't mistaken for a re-click and immediately closes 119 var wasOpenOnPointerDown = false; 120 121 var IfOpenOnPointerDown = function () { 122 wasOpenOnPointerDown = fp.isOpen; 123 }; 124 var closeIfOpenOnReclick = function (e) { 125 if (!wasOpenOnPointerDown) { 126 return; 127 } 128 wasOpenOnPointerDown = false; 129 e.stopPropagation(); 130 e.preventDefault(); 131 fp.close(); 132 }; 133 [$dateInput[0], fp.altInput].forEach(function (el) { 134 if(el) { 135 el.addEventListener("mousedown", IfOpenOnPointerDown, true); 136 el.addEventListener("click", closeIfOpenOnReclick, true); 137 } 138 }); 139 140 141 // Flatpickr's altInput sits between #date and .validate in the DOM, so validate.js's i.next('.validate') never finds it for this field. 142 $dateInput.closest("form").on("submit", function () { 143 if (!$dateInput.val()) { 144 $dateValidate 145 .html($dateInput.attr("data-msg") || "Please select a date") 146 .show("blind"); 147 } 148 }); 149 } 150 151 /* 152 If the user clicks the time field before picking a date, the select is disabled and 153 the date entry box is highlighted, if the date is also not selected the branch entry box is highlighted. 154 */ 155 var $timeSelect = $("#time"); 156 if ($dateInput.length && $timeSelect.length) { 157 $timeSelect.closest(".select").on("click", function () { 158 if (!$timeSelect.prop("disabled") || $dateInput.val()) { 159 return; 160 } 161 if ($branchSelect.length && !getBranchId()) { 162 $branchSelect[0].focus({ preventScroll: true}); 163 return; 164 } 165 (fp.altInput || $dateInput[0]).focus({ preventScroll: true}); 166 fp.close(); 167 }); 168 } 169 170 // Dropdowns will be populated with times relevant to each branch and date. 171 if ( 172 $dateInput.length && 173 $timeSelect.length && 174 typeof openingTimesByBranch !== "undefined" 175 ) { 176 var buildTimeSlots = function () { 177 $timeSelect.empty(); 178 $timeSelect.prop("disabled", true); 179 $timeSelect.removeClass("has-value"); 180 181 var dateValue = $dateInput.val(); 182 if (!dateValue) { 183 $timeSelect.append('<option value="" selected>Choose Time</option>'); 184 return; 185 } 186 187 var branchId = getBranchId(); 188 if (!branchId) { 189 $timeSelect.append( 190 '<option value="" selected>Select a branch first</option>', 191 ); 192 return; 193 } 194 var openingTimes = openingTimesByBranch[branchId] || {}; 195 196 var selectedDate = new Date(dateValue + "T00:00:00"); 197 var dayKey = dayKeysByIndex[selectedDate.getDay()]; 198 var sessions = openingTimes[dayKey]; 199 200 // If there arent any sessions, then they're closed. 201 if (!sessions || !sessions.length) { 202 $timeSelect.append('<option value="" selected>Closed</option>'); 203 return; 204 } 205 206 var now = new Date(); 207 var isToday = 208 selectedDate.getFullYear() === now.getFullYear() && 209 selectedDate.getMonth() === now.getMonth() && 210 selectedDate.getDate() === now.getDate(); 211 212 var slots = []; 213 sessions.forEach(function (session) { 214 var start = parseTimeToMinutes(session.opening_time_start); 215 var end = parseTimeToMinutes(session.opening_time_end); 216 if (start === null || end === null) { 217 return; 218 } 219 // Closing time at/before opening time means the session runs past midnight. 220 if (end <= start) { 221 end += 24 * 60; 222 }
223 var firstSlot = Math.ceil(start/15) * 15; 224 for (var minutes = firstSlot; minutes < end; minutes += 15) { 225 if (isToday && minutes <= now.getHours() * 60 + now.getMinutes()) { 226 continue; 227 } 228 slots.push(minutes); 229 } 230 }); 231 232 if (!slots.length) { 233 $timeSelect.append( 234 '<option value="" selected>No times available</option>', 235 ); 236 return; 237 } 238 239 // Time select is only available to click once Select date has been chosen. 240 $timeSelect.prop("disabled", false); 241 $timeSelect.append('<option value="" selected>Choose Time</option>'); 242 slots.forEach(function (minutes) { 243 var value = formatMinutesAsTime(minutes % (24 * 60)); // Getting Time value 244 var label = formatMinutesAsDisplayTime(minutes % (24 * 60)); // Getting PM or AM 245 $timeSelect.append( 246 '<option value="' + value + '">' + label + "</option>", 247 ); 248 }); 249 }; 250 251 // A function to get the total amount of time for comparision. 252 var parseTimeToMinutes = function (timeString) { 253 if (!timeString) { 254 return null; 255 } 256 var parts = timeString.split(":"); 257 if (parts.length < 2) { 258 return null; 259 } 260 var hours = parseInt(parts[0], 10); 261 var mins = parseInt(parts[1], 10); 262 if (isNaN(hours) || isNaN(mins)) { 263 return null; 264 } 265 return hours * 60 + mins; 266 }; 267 268 // Calculate total hours and remaining minutes. 269 var formatMinutesAsTime = function (minutes) { 270 var hours = Math.floor(minutes / 60); 271 var mins = minutes % 60; 272 return ( 273 (hours < 10 ? "0" + hours : hours) + 274 ":" + 275 (mins < 10 ? "0" + mins : mins) 276 ); 277 }; 278 279 // Gets the Time in AM and PM to display to the user. 280 var formatMinutesAsDisplayTime = function (minutes) { 281 var hours = Math.floor(minutes / 60); 282 var mins = minutes % 60; 283 var period = hours < 12 ? "AM" : "PM"; 284 var displayHours = hours % 12; 285 if (displayHours === 0) { 286 displayHours = 12; 287 } 288 return ( 289 displayHours + ":" + (mins < 10 ? "0" + mins : mins) + " " + period 290 ); 291 }; 292 293 // Toggle the class for time, so when a branch or date is changed the time resets. 294 $timeSelect.on("change", function () { 295 $timeSelect.toggleClass("has-value", !!$timeSelect.val()); 296 }); 297 298 $dateInput.on("change", buildTimeSlots); 299 $('select[name="branch"]').on("change", buildTimeSlots); 300 buildTimeSlots(); 301 } 302 303 var $guest_count = $("#stepper"); 304 305 $guest_count.val(0); 306 307 function updateGuestCount(value) { 308 $guest_count.val(value || ""); 309 } 310 311 function updateRemoveButton(value) { 312 const $button = $(".stepper-button.remove"); 313 const $icon = $button.find("img"); 314 315 if (value !== "" && parseInt(value, 10) > 0) { 316 $icon.attr("src", "/assets/icons/stepper/remove.svg"); 317 $button.prop("disabled", false); 318 } 319 else { 320 $icon.attr("src", "/assets/icons/stepper/remove_disabled.svg"); 321 $button.prop("disabled", true); 322 } 323 } 324 325 326 function updateAddButton(value) { 327 const $button = $(".stepper-button.add"); 328 const $icon = $button.find("img"); 329 330 if (value === "" || parseInt(value, 10) < 30) { 331 $icon.attr("src", "/assets/icons/stepper/add.svg"); 332 $button.prop("disabled", false); 333 } else { 334 $icon.attr("src", "/assets/icons/stepper/add_disabled.svg"); 335 $button.prop("disabled", true); 336 } 337 } 338 339 $(function () { 340 updateGuestCount(parseInt($guest_count.val() || 0)); 341 342 updateRemoveButton($guest_count.val()); 343 updateAddButton($guest_count.val()); 344 345 $guest_count.on("input", function () { 346 const value = $(this).val(); 347 updateRemoveButton(value); 348 updateAddButton(value); 349 }); 350 351 $(".stepper-button").on("touchstart", function () { 352 $(this).addClass("is-active"); 353 }); 354 355 $(".stepper-button").on("touchend touchcancel", function() { 356 $(this).removeClass("is-active"); 357 }); 358 359 $(".stepper-button").click(function () { 360 let value = parseInt($guest_count.val() || 0, 10); 361 $('#stepper').focus() 362 363 if ($(this).hasClass("add")) { 364 value = Math.min(30, value + 1); 365 } 366 else if ($(this).hasClass("remove")) { 367 value = Math.max(0, value - 1); 368 } 369 370 updateGuestCount(value); 371 372 $guest_count.trigger("input"); 373 374 updateRemoveButton(value); 375 updateAddButton(value); 376 }) 377 }) 378 379 380 $dateInput.closest("form").on("form-reset-success", function () { 381 if (fp) { 382 fp.clear(); 383 } 384 385 $(this).find(".form-group select").each(function () { 386 this.selectedIndex = 0; 387 }).removeClass("has-value"); 388 389 if ($timeSelect.length) { 390 $timeSelect.prop("disabled", true); 391 } 392 393 updateGuestCount(0); 394 updateAddButton(0); 395 updateRemoveButton(0); 396 }); 397})(jQuery);
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.