PageSourceSearch

https://www.sbs-inc.com/wp-content/plugins/sbs-cart-drawer/assets/js/cart-drawer.js?ver=1.0.16.1790104799

js sbs-inc.com collected 2026-10-10 00:08:43 UTC 30,236 bytes, 901 lines download raw bytes

1(function ($) {
2  'use strict';
3
4  var selectors = {
5    root: '[data-sbs-cart-drawer]',
6    drawer: '.sbs-cart-drawer',
7    toggle: '.sbs-cart-toggle',
8    overlay: '.sbs-cart-drawer__overlay',
9    close: '[data-sbs-cart-close]',
10    standardCartForm: 'form.cart:not(.vtajaxform)',
11    variationTableCartForm: 'form.vtajaxform',
12    checkoutProcessing: '[data-sbs-checkout-processing]',
13    checkoutProcessingMessage: '[data-sbs-checkout-processing-message]',
14    deliverySchedule: '[data-sbs-delivery-schedule]',
15    legalModal: '[data-sbs-legal-modal]',
16    legalContent: '[data-sbs-legal-content]',
17    legalTitle: '[data-sbs-legal-title]',
18    legalOriginal: '[data-sbs-legal-original]',
19    legalClose: '[data-sbs-legal-close]'
20  };
21
22  var lastTrigger = null;
23  var cartPageRefreshRequest = null;
24  var cartPageRefreshPending = false;
25  var checkoutCartSyncTimer = null;
26  var checkoutProcessingTimer = null;
27  var placeOrderMarkup = null;
28  var lastLegalTrigger = null;
29  var legalContentCache = {};
30  var deliveryScheduleState = {
31    date: '',
32    window: ''
33  };
34
35  function getRoot() {
36    return $(selectors.root);
37  }
38
39  function openDrawer(trigger) {
40    var $root = getRoot();
41
42    if (!$root.length) {
43      return;
44    }
45
46    lastTrigger = trigger || document.activeElement;
47    $root.addClass('is-open');
48    $root.find(selectors.drawer).attr('aria-hidden', 'false');
49    $root.find(selectors.toggle).attr('aria-expanded', 'true');
50    $root.find(selectors.overlay).prop('hidden', false);
51    $('body').addClass('sbs-cart-drawer-open');
52
53    window.setTimeout(function () {
54      $root.find('.sbs-cart-drawer__close').trigger('focus');
55    }, 220);
56  }
57
58  function closeDrawer(restoreFocus) {
59    var $root = getRoot();
60
61    $root.removeClass('is-open');
62    $root.find(selectors.drawer).attr('aria-hidden', 'true');
63    $root.find(selectors.toggle).attr('aria-expanded', 'false');
64    $root.find(selectors.overlay).prop('hidden', true);
65    $('body').removeClass('sbs-cart-drawer-open');
66
67    if (restoreFocus !== false && lastTrigger && typeof lastTrigger.focus === 'function') {
68      lastTrigger.focus();
69    }
70  }
71
72  function sanitizeLegalHtml(html) {
73    var parser = new window.DOMParser();
74    var documentFragment = parser.parseFromString('<div id="sbs-legal-sanitize-root">' + html + '</div>', 'text/html');
75    var root = documentFragment.getElementById('sbs-legal-sanitize-root');
76
77    if (!root) {
78      return '';
79    }
80
81    root.querySelectorAll('script, style, iframe, object, embed, form, link, meta').forEach(function (node) {
82      node.remove();
83    });
84
85    root.querySelectorAll('[id]').forEach(function (node) {
86      node.removeAttribute('id');
87    });
88
89    root.querySelectorAll('a[href]').forEach(function (link) {
90      link.setAttribute('target', '_blank');
91      link.setAttribute('rel', 'noopener');
92    });
93
94    return root.innerHTML.trim();
95  }
96
97  function extractLegalPageContent(html) {
98    var parser = new window.DOMParser();
99    var responseDocument = parser.parseFromString(html, 'text/html');
100    var content = responseDocument.querySelector('.post-content, .entry-content, article .fusion-post-content, main article, main #content, #main .fusion-row');
101
102    return content ? sanitizeLegalHtml(content.innerHTML) : '';
103  }
104
105  function getLegalContent(type, url) {
106    var cacheKey = type + '|' + url;
107    var inlineTerms;
108
109    if (legalContentCache[cacheKey]) {
110      return $.Deferred().resolve(legalContentCache[cacheKey]).promise();
111    }
112
113    if (type === 'terms') {
114      inlineTerms = $('.woocommerce-terms-and-conditions').first().html();
115
116      if (inlineTerms) {
117        legalContentCache[cacheKey] = sanitizeLegalHtml(inlineTerms);
118        return $.Deferred().resolve(legalContentCache[cacheKey]).promise();
119      }
120    }
121
122    return $.ajax({
123      url: url,
124      type: 'GET',
125      cache: true,
126      dataType: 'html'
127    }).then(function (html) {
128      var content = extractLegalPageContent(html);
129
130      if (!content) {
131        return $.Deferred().reject().promise();
132      }
133
134      legalContentCache[cacheKey] = content;
135      return content;
136    });
137  }
138
139  function closeLegalModal(restoreFocus) {
140    var $modal = $(selectors.legalModal);
141
142    if (!$modal.length || $modal.prop('hidden')) {
143      return;
144    }
145
146    $modal.prop('hidden', true).attr('aria-hidden', 'true').removeClass('is-open');
147    $('body').removeClass('sbs-legal-modal-open');
148
149    if (restoreFocus !== false && lastLegalTrigger && typeof lastLegalTrigger.focus === 'function') {
150      lastLegalTrigger.focus();
151    }
152  }
153
154  function openLegalModal(link, type) {
155    var $modal = $(selectors.legalModal);
156    var $content = $modal.find(selectors.legalContent);
157    var url = link.href;
158    var title = type === 'terms' ? SBSCartDrawer.i18n.termsTitle : SBSCartDrawer.i18n.privacyTitle;
159    var $loading;
160
161    if (!$modal.length || !url) {
162      return;
163    }
164
165    lastLegalTrigger = link;
166    $modal.find(selectors.legalTitle).text(title);
167    $modal.find(selectors.legalOriginal).attr('href', url).text(SBSCartDrawer.i18n.legalOpenFull);
168    $loading = $('<div class="sbs-checkout-legal-modal__loading" role="status"><span aria-hidden="true"></span>
168<p></p></div>');
169    $loading.find('p').text(SBSCartDrawer.i18n.legalLoading);
170    $content.empty().append($loading);
171    $modal.prop('hidden', false).attr('aria-hidden', 'false').addClass('is-open');
172    $('body').addClass('sbs-legal-modal-open');
173
174    window.setTimeout(function () {
175      $modal.find('.sbs-checkout-legal-modal__close').trigger('focus');
176    }, 20);
177
178    getLegalContent(type, url).done(function (html) {
179      if (!$modal.hasClass('is-open')) {
180        return;
181      }
182
183      $content.html(html).attr('aria-busy', 'false');
184      $content.scrollTop(0);
185    }).fail(function () {
186      var $error = $('<div class="sbs-checkout-legal-modal__error" role="alert"></div>');
187      $('<p></p>').text(SBSCartDrawer.i18n.legalError).appendTo($error);
188      $('<a target="_blank" rel="noopener"></a>').attr('href', url).text(SBSCartDrawer.i18n.legalOpenFull).appendTo($error);
189      $content.empty().append($error).attr('aria-busy', 'false');
190    });
191  }
192
193  function prepareCheckoutLegalLinks() {
194    if (!$('body').hasClass('woocommerce-checkout') || $('body').hasClass('woocommerce-order-received')) {
195      return;
196    }
197
198    $('.woocommerce-terms-and-conditions-link')
199      .removeClass('woocommerce-terms-and-conditions-link--open')
200      .addClass('woocommerce-terms-and-conditions-link--closed')
201      .attr('aria-haspopup', 'dialog');
202    $('.woocommerce-privacy-policy-link').attr('aria-haspopup', 'dialog');
203  }
204
205  document.addEventListener('click', function (event) {
206    var link = event.target && event.target.closest ? event.target.closest('a.woocommerce-privacy-policy-link, a.woocommerce-terms-and-conditions-link') : null;
207    var type;
208
209    if (!link || !document.body.classList.contains('woocommerce-checkout') || document.body.classList.contains('woocommerce-order-received')) {
210      return;
211    }
212
213    event.preventDefault();
214    event.stopPropagation();
215    event.stopImmediatePropagation();
216    type = link.classList.contains('woocommerce-terms-and-conditions-link') ? 'terms' : 'privacy';
217    openLegalModal(link, type);
218  }, true);
219
220  function showError($form, message) {
221    var $notice = $('<p class="sbs-cart-drawer__form-error" role="alert"></p>').text(message);
222    $form.find('.sbs-cart-drawer__form-error').remove();
223    $form.prepend($notice);
224  }
225
226  function normalizeCartFormData($form, $button) {
227    var data = $form.serializeArray();
228    var hasProductId = data.some(function (field) {
229      return field.name === 'product_id';
230    });
231
232    if (!hasProductId) {
233      data.push({
234        name: 'product_id',
235        value: $button.val() || $form.find('[name="add-to-cart"]').val()
236      });
237    }
238
239    return $.param(data);
240  }
241
242  function normalizeVariationTableData($form) {
243    var serialized = $form.serializeArray();
244    var variationId = serialized.find(function (field) {
245      return field.name === 'variation_id';
246    });
247    var data = serialized.filter(function (field) {
248      return !['quantity', 'product_id', 'variation_id', 'add-to-cart'].includes(field.name);
249    });
250    var quantity = $form.closest('tr').find('input[name="var_quantity"]').first().val();
251
252    data.push({
253      name: 'product_id',
254      value: variationId ? variationId.value : $form.attr('data-variation_id')
255    });
256    data.push({
257      name: 'quantity',
258      value: quantity || $form.find('input[name="quantity"]').val() || 1
259    });
260
261    return $.param(data);
262  }
263
264  function submitAjaxCart($form, $button, data) {
265    if ($button.hasClass('loading') || $button.is(':disabled')) {
266      return;
267    }
268
269    var originalMarkup = $button.html();
270    $button.prop('disabled', true).addClass('loading').text(SBSCartDrawer.i18n.adding);
271    $form.find('.sbs-cart-drawer__form-error').remove();
272
273    $.ajax({
274      type: 'POST',
275      url: SBSCartDrawer.wcAjaxUrl.replace('%%endpoint%%', 'add_to_cart'),
276      data: data,
277      dataType: 'json'
278    }).done(function (response) {
279      if (!response || response.error) {
280        showError($form, SBSCartDrawer.i18n.error);
281        return;
282      }
283
284      if (response.fragments) {
285        $.each(response.fragments, function (selector, html) {
286          $(selector).replaceWith(html);
287        });
288      }
289
290      $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $button]);
291    }).fail(function () {
292      showError($form, SBSCartDrawer.i18n.error);
293    }).always(function () {
294      $button.prop('disabled', false).removeClass('loading').html(originalMarkup);
295    });
296  }
297
298  function submitVariationTableCart(form) {
299    var $form = $(form);
300    var $button = $form.find('.single_add_to_cart_button').first();
301
302    if (!$button.length || $button.is(':disabled') || $('body').is('.woocommerce-cart, .woocommerce-checkout')) {
303      return;
304    }
305
306    submitAjaxCart($form, $button, normalizeVariationTableData($form));
307  }
308
309  // Standard WooCommerce loop buttons and the installed variations-table plugin
310  // both emit this event after a successful AJAX request.
311  $(document.body).on('added_to_cart', function (event, fragments, cartHash, $button) {
312    var trigger = $button && $button.length ? $button.get(0) : document.activeElement;
313    openDrawer(trigger);
314  });
315
316  // WooCommerce does not AJAX-submit single-product forms by default. Handle
317  // those here, while leaving .vtajaxform to the existing variations plugin.
318  $(document).on('submit', selectors.standardCartForm, function (event) {
319    var $form = $(this);
320    var $button = $form.find('.single_add_to_cart_button').first();
321
322    if (!$button.length || $button.is(':disabled') || $('body').is('.woocommerce-cart, .woocommerce-checkout')) {
323      return;
324    }
325
326    event.preventDefault();
327
328    submitAjaxCart($form, $button, normalizeCartFormData($form, $button));
329  });
330
331  // Avada intercepts these button clicks before the variations-table form can
332  // submit. Capture the click first and route it to WooCommerce directly.
333  document.addEventListener('click', function (event) {
334    var button = event.target && event.target.closest ? event.target.closest('form.vtajaxform .single_add_to_cart_button') : null;
335
336    if (!button) {
337      return;
338    }
339
340    event.preventDefault();
341    event.stopImmediatePropagation();
342    submitVariationTableCart(button.closest('form.vtajaxform'));
343  }, true);
344
345  // Also capture keyboard and programmatic form submissions before the legacy
346  // variations-table handler.
347  document.addEventListener('submit', function (event) {
348    var form = event.target;
349
350    if (!form || !form.matches || !form.matches(selectors.variationTableCartForm)) {
351      return;
352    }
353
354    event.preventDefault();
355    event.stopImmediatePropagation();
356    submitVariationTableCart(form);
357  }, true);
358
359  function enhanceCheckoutShipping() {
360    var isCheckout = $('body').hasClass('woocommerce-checkout');
361
362    $('#shipping_method').each(function (index) {
363      var $methods = $(this);
364      var introId = 'sbs-shipping-choice-intro-' + index;
365
366      $methods.closest('tr.woocommerce-shipping-totals').addClass('sbs-shipping-choice');
367
368      if (isCheckout) {
369        var $intro = $methods.prev('.sbs-shipping-choice__intro');
370
371        if (!$intro.length) {
372          $intro = $('<p class="sbs-shipping-choice__intro"></p>').insertBefore($methods);
373        }
374
375        $intro.attr('id', introId).text(SBSCartDrawer.i18n.shippingInstruction);
376        $methods.find('input.shipping_method').attr('aria-describedby', introId);
377      } else {
378        $methods.prev('.sbs-shipping-choice__intro').remove();
379        $methods.find('input.shipping_method').removeAttr('aria-describedby');
380      }
381    });
382
383    if (isCheckout) {
384      organizeCheckoutReview();
385    }
386  }
387
388  function organizeCheckoutReview() {
389    var $review = $('#order_review');
390    var $table = $review.find('table.woocommerce-checkout-review-order-table').first();
391    var $shippingRow = $table.find('tr.woocommerce-shipping-totals').first();
392    var $methods = $shippingRow.find('#shipping_method').first();
393    var $intro = $methods.prev('.sbs-shipping-choice__intro');
394    var $scheduleRow = $table.find('tr.sbs-delivery-schedule-row').first();
395    var $schedule = $scheduleRow.find(selectors.deliverySchedule).first();
396    var $payment = $review.find('#payment').first();
397
398    if (!$review.length || !$table.length || !$shippingRow.length || !$methods.length || !$payment.length) {
399      return;
400    }
401
402    var $grid = $review.children('.sbs-checkout-review-grid').first();
403
404    if (!$grid.length) {
405      $grid = $('<div class="sbs-checkout-review-grid"></div>').prependTo($review);
406    }
407
408    if (!$table.parent().is($grid)) {
409      $grid.append($table);
410    }
411
412    if (!$payment.parent().is($grid)) {
413      $grid.append($payment);
414    }
415
416    var $priorSchedule = $grid.children('.sbs-checkout-fulfillment-panel').find(selectors.deliverySchedule).first();
417
418    if ($priorSchedule.length) {
419      deliveryScheduleState.date = $priorSchedule.find('#sbs_requested_delivery_date').val() || deliveryScheduleState.date;
420      deliveryScheduleState.window = $priorSchedule.find('#sbs_requested_delivery_window').val() || deliveryScheduleState.window;
421    }
422
423    // WooCommerce replaces the review table after every totals update. Remove
424    // the prior panel, then relocate the fresh, authoritative shipping inputs.
425    $grid.children('.sbs-checkout-fulfillment-panel').remove();
426
427    var title = $.trim($shippingRow.children('th').first().text()) || SBSCartDrawer.i18n.shippingTitle;
428    var $panel = $('<aside class="sbs-checkout-fulfillment-panel" aria-labelledby="sbs-checkout-fulfillment-title"></aside>');
429
430    $('<h3 id="sbs-checkout-fulfillment-title"></h3>').text(title).appendTo($panel);
431    $panel.append($intro, $methods);
432
433    if ($schedule.length) {
434      $panel.append($schedule);
435      $scheduleRow.addClass('sbs-delivery-schedule-row--relocated');
436    }
437
438    $shippingRow.addClass('sbs-shipping-choice--relocated');
439    $panel.insertAfter($table);
440    syncDeliverySchedule($schedule);
441  }
442
443  function deliveryScheduleConfig() {
444    return SBSCartDrawer.deliverySchedule && SBSCartDrawer.deliverySchedule.dates
445      ? SBSCartDrawer.deliverySchedule
446      : { dates: {} };
447  }
448
449  function selectedShippingMethod() {
450    var $checked = $('#shipping_method input.shipping_method:checked').first();
451
452    if ($checked.length) {
453      return String($checked.val() || '');
454    }
455
456    return String($('#shipping_method input.shipping_method, #shipping_method select.shipping_method').first().val() || '');
457  }
458
459  function deliveryIsSelected() {
460    return /^flat_rate(?::|$)/.test(selectedShippingMethod());
461  }
462
463  function deliveryWindowsForDate(date) {
464    var dates = deliveryScheduleConfig().dates || {};
465    return dates[date] && Array.isArray(dates[date].windows) ? dates[date].windows : [];
466  }
467
468  function populateDeliveryWindows($schedule, preferredWindow) {
469    var $date = $schedule.find('#sbs_requested_delivery_date');
470    var $window = $schedule.find('#sbs_requested_delivery_window');
471    var windows = deliveryWindowsForDate(String($date.val() || ''));
472    var selected = preferredWindow || String($window.val() || '');
473
474    $window.empty().append(
475      $('<option></option>').attr('value', '').text(SBSCartDrawer.i18n.windowPlaceholder)
476    );
477
478    $.each(windows, function (index, label) {
479      $('<option></option>').attr('value', label).text(label).appendTo($window);
480    });
481
482    if (selected && windows.indexOf(selected) !== -1) {
483      $window.val(selected);
484    } else {
485      $window.val('');
486      deliveryScheduleState.window = '';
487    }
488  }
489
490  function syncDeliverySchedule($schedule) {
491    $schedule = $schedule && $schedule.length ? $schedule : $(selectors.deliverySchedule).first();
492
493    if (!$schedule.length) {
494      return;
495    }
496
497    var active = deliveryIsSelected();
498    var $date = $schedule.find('#sbs_requested_delivery_date');
499    var $window = $schedule.find('#sbs_requested_delivery_window');
500
501    if (deliveryScheduleState.date && $date.find('option[value="' + deliveryScheduleState.date.replace(/"/g, '\\"') + '"]').length) {
502      $date.val(deliveryScheduleState.date);
503    } else if ($date.val()) {
504      deliveryScheduleState.date = String($date.val());
505    }
506
507    populateDeliveryWindows($schedule, deliveryScheduleState.window);
508
509    $schedule
510      .prop('hidden', !active)
511      .toggleClass('is-visible', active)
512      .attr('aria-hidden', active ? 'false' : 'true');
513
514    $date.add($window)
515      .prop('disabled', !active)
516      .prop('required', active)
517      .attr('aria-required', active ? 'true' : 'false');
518  }
519
520  function isLandscapeContractor() {
521    return $('input[name="billing_who_is_making_this_purchase"]:checked').val() === 'Landscape Contractor';
522  }
523
524  function syncCompanyRequirement() {
525    var $field = $('#billing_company_name_field');
526    var $input = $('#billing_company_name');
527    var $label = $field.find('label[for="billing_company_name"]').first();
528    var isRequired = isLandscapeContractor();
529
530    if (!$field.length || !$input.length || !$label.length) {
531      return;
532    }
533
534    $field.toggleClass('validate-required', isRequired);
535    $input.prop('required', isRequired).attr('aria-required', isRequired ? 'true' : 'false');
536    $label.find('.required, .optional').remove();
537
538    if (isRequired) {
539      $('<abbr class="required" title="Required Field">*</abbr>').appendTo($label);
540    } else {
541      $('<span class="optional"></span>').text('(' + SBSCartDrawer.i18n.companyOptional + ')').appendTo($label);
542    }
543  }
544
545  function addressPartText(html) {
546    return $('<div></div>').html(html).text().replace(/\s+/g, ' ').trim();
547  }
548
549  function cleanAddressTail(html) {
550    var $content = $('<div></div>').html(html);
551    var $phone = $content.find('[class*="phone"]').first();
552    var phone = $.trim($phone.text());
553    var firstTextNode = $content.contents().filter(function () {
554      return this.nodeType === 3 && $.trim(this.nodeValue);
555    }).first();
556
557    // Flexible Checkout Fields can output the shipping phone once as plain
558    // text and a second time in WooCommerce's phone element. Keep one copy.
559    if (phone && firstTextNode.length && $.trim(firstTextNode.get(0).nodeValue) === phone) {
560      firstTextNode.remove();
561    }
562
563    return $.trim($content.html());
564  }
565
566  function normalizeOrderConfirmationAddresses() {
567    if (!$('body').hasClass('woocommerce-order-received')) {
568      return;
569    }
570
571    $('.sbs-confirmation-customer-details address, .sbs-order-confirmation-details .woocommerce-customer-details address').each(function () {
572      var $address = $(this);
573
574      if ($address.hasClass('sbs-address-normalized')) {
575        return;
576      }
577
578      var parts = String($address.html() || '').split(/<br\s*\/?\s*>/i).map(function (part) {
579        return $.trim(part);
580      }).filter(function (part) {
581        return addressPartText(part) !== '';
582      });
583
584      if (parts.length < 4) {
585        return;
586      }
587
588      var firstText = addressPartText(parts[0]);
589      var nameStart = firstText.indexOf(':') !== -1 ? 1 : 0;
590      var postcodeIndex = -1;
591
592      $.each(parts, function (index, part) {
593        if (/^\d{5}(?:-\d{4})?$/.test(addressPartText(part))) {
594          postcodeIndex = index;
595        }
596      });
597
598      var normalized = [];
599
600      $.each(parts.slice(0, nameStart), function (index, part) {
601        normalized.push({ className: 'sbs-address-line--context', html: part });
602      });
603
604      if (parts[nameStart] && parts[nameStart + 1]) {
605        normalized.push({
606          className: 'sbs-address-line--name',
607          html: $('<div></div>').text(addressPartText(parts[nameStart]) + ' ' + addressPartText(parts[nameStart + 1])).html()
608        });
609      }
610
611      var localityStart = postcodeIndex >= nameStart + 4 ? postcodeIndex - 2 : -1;
612      var middleEnd = localityStart === -1 ? parts.length : localityStart;
613
614      $.each(parts.slice(nameStart + 2, middleEnd), function (index, part) {
615        normalized.push({ className: 'sbs-address-line--street', html: part });
616      });
617
618      if (localityStart !== -1) {
619        var locality = addressPartText(parts[localityStart]) + ', ' + addressPartText(parts[localityStart + 1]) + ' ' + addressPartText(parts[postcodeIndex]);
620        normalized.push({ className: 'sbs-address-line--locality', html: $('<div></div>').text(locality).html() });
621
622        $.each(parts.slice(postcodeIndex + 1), function (index, part) {
623          normalized.push({ className: 'sbs-address-line--contact', html: cleanAddressTail(part) });
624        });
625      }
626
627      $address.empty().addClass('sbs-address-normalized');
628
629      $.each(normalized, function (index, part) {
630        $('<div class="sbs-address-line"></div>').addClass(part.className).html(part.html).appendTo($address);
631      });
632    });
633  }
634
635  function showCheckoutProcessing() {
636    var $indicator = $(selectors.checkoutProcessing);
637    var $button = $('#place_order');
638
639    if (!$indicator.length || $('body').hasClass('woocommerce-order-received')) {
640      return;
641    }
642
643    if ($button.length && placeOrderMarkup === null) {
644      placeOrderMarkup = $button.html();
645    }
646
647    $button
648      .prop('disabled', true)
649      .attr('aria-disabled', 'true')
650      .addClass('sbs-place-order-processing');
651
652    $indicator
653      .prop('hidden', false)
654      .attr('aria-hidden', 'false');
655
656    $indicator.find(selectors.checkoutProcessingMessage).text(SBSCartDrawer.i18n.processingMessage);
657    $('body').addClass('sbs-checkout-order-processing');
658
659    window.clearTimeout(checkoutProcessingTimer);
660    checkoutProcessingTimer = window.setTimeout(function () {
661      if ($('body').hasClass('sbs-checkout-order-processing')) {
662        $indicator.find(selectors.checkoutProcessingMessage).text(SBSCartDrawer.i18n.processingLonger);
663      }
664    }, 12000);
665  }
666
667  function hideCheckoutProcessing() {
668    var $button = $('#place_order');
669
670    window.clearTimeout(checkoutProcessingTimer);
671    checkoutProcessingTimer = null;
672
673    $(selectors.checkoutProcessing)
674      .prop('hidden', true)
675      .attr('aria-hidden', 'true');
676
677    $('body').removeClass('sbs-checkout-order-processing');
678
679    $button
680      .prop('disabled', false)
681      .removeAttr('aria-disabled')
682      .removeClass('sbs-place-order-processing');
683
684    if ($button.length && placeOrderMarkup !== null) {
685      $button.html(placeOrderMarkup);
686    }
687
688    placeOrderMarkup = null;
689  }
690
691  function bindCheckoutProcessing() {
692    $('form.checkout')
693      .off('checkout_place_order.sbsCheckoutProcessing')
694      .on('checkout_place_order.sbsCheckoutProcessing', function () {
695        showCheckoutProcessing();
696      });
697  }
698
699  function refreshMainCartFromSession() {
700    var $currentForm;
701    var $currentTotals;
702    var $currentTableWrapper;
703    var $currentTotalsWrapper;
704
705    if (!$('body').hasClass('woocommerce-cart')) {
706      return;
707    }
708
709    if (cartPageRefreshRequest) {
710      cartPageRefreshPending = true;
711      return;
712    }
713
714    cartPageRefreshPending = false;
715
716    $currentForm = $('form.woocommerce-cart-form').first();
717    $currentTotals = $('.cart_totals').first();
718    $currentTableWrapper = $('.fusion-woo-cart_table-wrapper').first();
719    $currentTotalsWrapper = $('.fusion-woo-cart-totals-wrapper').first();
720
721    if ((!$currentTableWrapper.length || !$currentTotalsWrapper.length) && (!$currentForm.length || !$currentTotals.length)) {
722      window.location.reload();
723      return;
724    }
725
726    $('body').addClass('sbs-cart-page-refreshing');
727
728    cartPageRefreshRequest = $.ajax({
729      url: window.location.href,
730      type: 'GET',
731      cache: false,
732      data: {
733        sbs_cart_refresh: Date.now()
734      },
735      dataType: 'html'
736    }).done(function (html) {
737      var $response = $('<div></div>').append($.parseHTML(html, document, true));
738      var $freshForm = $response.find('form.woocommerce-cart-form').first();
739      var $freshTotals = $response.find('.cart_totals').first();
740      var $freshTableWrapper = $response.find('.fusion-woo-cart_table-wrapper').first();
741      var $freshTotalsWrapper = $response.find('.fusion-woo-cart-totals-wrapper').first();
742
743      if ($currentTableWrapper.length && $currentTotalsWrapper.length && $freshTableWrapper.length && $freshTotalsWrapper.length) {
744        $currentTableWrapper.replaceWith($freshTableWrapper);
745        $currentTotalsWrapper.replaceWith($freshTotalsWrapper);
746      } else if ($currentForm.length && $currentTotals.length && $freshForm.length && $freshTotals.length) {
747        $currentForm.replaceWith($freshForm);
748        $currentTotals.replaceWith($freshTotals);
749      } else {
750        window.location.reload();
751        return;
752      }
753
754      enhanceCheckoutShipping();
755      $(document.body).trigger('updated_wc_div');
756    }).fail(function () {
757      window.location.reload();
758    }).always(function () {
759      var shouldRefreshAgain = cartPageRefreshPending;
760
761      cartPageRefreshRequest = null;
762      cartPageRefreshPending = false;
763      $('body').removeClass('sbs-cart-page-refreshing');
764
765      if (shouldRefreshAgain) {
766        refreshMainCartFromSession();
767      }
768    });
769  }
770
771  function refreshCheckoutFromSession() {
772    if (!$('body').hasClass('woocommerce-checkout') || $('body').hasClass('woocommerce-order-received')) {
773      return;
774    }
775
776    window.clearTimeout(checkoutCartSyncTimer);
777    checkoutCartSyncTimer = window.setTimeout(function () {
778      checkoutCartSyncTimer = null;
779      $(document.body).trigger('update_checkout');
780    }, 80);
781  }
782
783  function syncPageAfterDrawerRemoval() {
784    refreshMainCartFromSession();
785    refreshCheckoutFromSession();
786  }
787
788  enhanceCheckoutShipping();
789  syncCompanyRequirement();
790  bindCheckoutProcessing();
791  normalizeOrderConfirmationAddresses();
792  $(prepareCheckoutLegalLinks);
793  $(document.body).on('updated_checkout updated_shipping_method', function () {
794    enhanceCheckoutShipping();
795    syncCompanyRequirement();
796    bindCheckoutProcessing();
797  });
798
799  $(document).on('change', '#sbs_requested_delivery_date', function () {
800    var $schedule = $(this).closest(selectors.deliverySchedule);
801    deliveryScheduleState.date = String($(this).val() || '');
802    deliveryScheduleState.window = '';
803    populateDeliveryWindows($schedule, '');
804  });
805
806  $(document).on('change', '#sbs_requested_delivery_window', function () {
807    deliveryScheduleState.window = String($(this).val() || '');
808  });
809
810  $(document).on('change', '#shipping_method input.shipping_method, #shipping_method select.shipping_method', function () {
811    syncDeliverySchedule();
812  });
813
814  $(document.body).on('checkout_error', hideCheckoutProcessing);
815  $(document.body).on('removed_from_cart.sbsCartPageSync', syncPageAfterDrawerRemoval);
816  $(document).on('change', 'input[name="billing_who_is_making_this_purchase"]', syncCompanyRequirement);
817
818  $(document).ajaxError(function (event, request, settings) {
819    if (settings && /(?:wc-ajax=|wc_ajax=)checkout/.test(settings.url || '')) {
820      hideCheckoutProcessing();
821    }
822  });
823
824  $(window).on('pageshow', hideCheckoutProcessing);
825
826  $(document).on('click', selectors.toggle, function () {
827    openDrawer(this);
828  });
829
830  $(document).on('click', selectors.close, function (event) {
831    event.preventDefault();
832    closeDrawer();
833  });
834
835  $(document).on('keydown', function (event) {
836    if (event.key === 'Escape' && $(selectors.legalModal).hasClass('is-open')) {
837      event.preventDefault();
838      closeLegalModal();
839      return;
840    }
841
842    if (event.key === 'Escape' && getRoot().hasClass('is-open')) {
843      closeDrawer();
844    }
845  });
846
847  $(document).on('click', selectors.legalClose, function (event) {
848    event.preventDefault();
849    closeLegalModal();
850  });
851
852  $(document).on('keydown', selectors.legalModal, function (event) {
853    var $focusable;
854    var first;
855    var last;
856
857    if (event.key !== 'Tab') {
858      return;
859    }
860
861    $focusable = $(this).find('a:visible, button:visible, [tabindex]:visible').filter(':not([tabindex="-1"])');
862    first = $focusable.get(0);
863    last = $focusable.get($focusable.length - 1);
864
865    if (!first || !last) {
866      return;
867    }
868
869    if (event.shiftKey && document.activeElement === first) {
870      event.preventDefault();
871      last.focus();
872    } else if (!event.shiftKey && document.activeElement === last) {
873      event.preventDefault();
874      first.focus();
875    }
876  });
877
878  // Keep keyboard focus inside the open drawer.
879  $(document).on('keydown', selectors.drawer, function (event) {
880    if (event.key !== 'Tab') {
881      return;
882    }
883
884    var $focusable = $(this).find('a:visible, button:visible, [tabindex]:visible').filter(':not([tabindex="-1"])');
885    var first = $focusable.get(0);
886    var last = $focusable.get($focusable.length - 1);
887
888    if (event.shiftKey && document.activeElement === first) {
889      event.preventDefault();
890      last.focus();
891    } else if (!event.shiftKey && document.activeElement === last) {
892      event.preventDefault();
893      first.focus();
894    }
895  });
896
897  // Public adapter hook for future SBS-specific cart integrations.
898  $(document).on('sbs_cart_drawer_open', function (event, trigger) {
899    openDrawer(trigger);
900  });
901})(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.