1(function ($) { 2 'use strict'; 3 4 function isMobile() { 5 return window.matchMedia && window.matchMedia('(max-width: 767px)').matches; 6 } 7 8 window.shopCustomerAccountOpenBookings = function () { 9 var $account = $('.shop-account-v2').first(); 10 var $accordionTrigger = $account.find('.shop-account-v2__accordion-trigger[aria-controls="bookings"]'); 11 var $bookingsTab = $account.find('#my_bookings'); 12 13 if (isMobile() && $accordionTrigger.length) { 14 if (!$accordionTrigger.hasClass('is-open')) { 15 $accordionTrigger.trigger('click'); 16 } 17 $('html, body').stop(true).animate({ scrollTop: Math.max(0, $accordionTrigger.offset().top - 12) }, 220); 18 return false; 19 } 20 21 if ($bookingsTab.length) { 22 $bookingsTab.trigger('click'); 23 $('html, body').stop(true).animate({ scrollTop: Math.max(0, $bookingsTab.offset().top - 12) }, 220); 24 } 25 26 return false; 27 }; 28 29 function closePanel($trigger, $panel) { 30 $trigger.removeClass('is-open').attr('aria-expanded', 'false'); 31 $panel.stop(true, true).slideUp(180).removeClass('is-open'); 32 } 33 34 function openPanel($trigger, $panel) { 35 $trigger.addClass('is-open').attr('aria-expanded', 'true'); 36 $panel.stop(true, true).slideDown(220).addClass('is-open'); 37 } 38 39 function setupMobileAccordion() { 40 $('.shop-account-v2').each(function () { 41 var $account = $(this); 42 var $nav = $account.find('.nav.nav-pills').first(); 43 var $content = $account.find('.tab-content').first(); 44 45 if (!$nav.length || !$content.length || $account.hasClass('shop-account-v2--accordion')) { 46 return; 47 } 48 49 $nav.find('a[href^="#"]').each(function (index) { 50 var $tab = $(this); 51 var selector = $tab.attr('href'); 52 var $panel = $account.find(selector).first(); 53 54 if (!$panel.length) { 55 return; 56 } 57 58 var panelId = $panel.attr('id'); 59 var triggerId = 'shop-account-v2-accordion-trigger-' + panelId + '-' + index; 60 var $trigger = $('<button>', { 61 type: 'button', 62 id: triggerId, 63 class: 'shop-account-v2__accordion-trigger', 64 'aria-controls': panelId, 65 'aria-expanded': 'false' 66 }); 67 68 $trigger.append($('<span>', { class: 'shop-account-v2__accordion-label' }).html($tab.html())); 69 $trigger.append($('<span>', { class: 'shop-account-v2__accordion-icon', 'aria-hidden': 'true' }).text('+')); 70 71 $panel.before($trigger); 72 $panel.attr('data-shop-account-original-style', $panel.attr('style') || ''); 73 $panel.attr('aria-labelledby', triggerId).addClass('shop-account-v2__accordion-panel').removeClass('active').hide(); 74 75 $trigger.on('click', function () { 76 var isOpen = $trigger.hasClass('is-open'); 77 78 $account.find('.shop-account-v2__accordion-trigger.is-open').each(function () { 79 var $otherTrigger = $(this); 80 closePanel($otherTrigger, $account.find('#' + $otherTrigger.attr('aria-controls'))); 81 }); 82 83 if (!isOpen) { 84 if (panelId === 'logbook' && typeof window.shopGetCustomerDZJumpLog === 'function') { 85 window.shopGetCustomerDZJumpLog(); 86 } 87 openPanel($trigger, $panel); 88 } 89 }); 90 }); 91 92 var $overviewTrigger = $account.find('.shop-account-v2__accordion-trigger').first(); 93 var $overviewPanel = $account.find('#overview'); 94 openPanel($overviewTrigger, $overviewPanel); 95 $account.addClass('shop-account-v2--accordion'); 96 }); 97 } 98 99 function restoreDesktopTabs() { 100 $('.shop-account-v2.shop-account-v2--accordion').each(function () { 101 var $account = $(this); 102 $account.find('.shop-account-v2__accordion-trigger').remove(); 103 $account.find('.shop-account-v2__accordion-panel').each(function () { 104 var $panel = $(this); 105 var originalStyle = $panel.attr('data-shop-account-original-style') || ''; 106 $panel.removeAttr('aria-labelledby data-shop-account-original-style') 107 .removeClass('shop-account-v2__accordion-panel is-open'); 108 if (originalStyle) { 109 $panel.attr('style', originalStyle); 110 } else { 111 $panel.removeAttr('style'); 112 } 113 }); 114 $account.removeClass('shop-account-v2--accordion'); 115 $account.find('.nav.nav-pills > li').removeClass('active').first().addClass('active'); 116 $account.find('.tab-pane').removeClass('active').first().addClass('active'); 117 }); 118 } 119 120 function syncLayout() { 121 if (isMobile()) { 122 setupMobileAccordion(); 123 } else { 124 restoreDesktopTabs(); 125 } 126 127 setupMobileVoucherActions($('.shop-account-v2').first()); 128 setupMobileListingTables($('.shop-account-v2').first()); 129 } 130 131 function setupVoucherSections($account) { 132 $account.find('.bloc_customers_activity_vouchers_v2 > table').each(function () { 133 var $table = $(this); 134 var $header = $table.find('tbody > tr.voucher-activity').first(); 135 136 if (!$header.length || $table.data('shopVoucherSectionReady')) { 137 return; 138 } 139 140 var $headerContent = $header.find('.voucher-activity-header').first(); 141 var $toggle = $('<span>', { 142 class: 'shop-account-v2__voucher-section-toggle', 143 'aria-hidden': 'true', 144 text: 'â' 145 }); 146 147 ($headerContent.length ? $headerContent : $header.find('td').first()).append($toggle); 148 $header.attr({ role: 'button', tabindex: '0', 'aria-expanded': 'true' }); 149 150 function toggleSection() { 151 var isCollapsed = $table.toggleClass('is-collapsed').hasClass('is-collapsed'); 152 $header.attr('aria-expanded', isCollapsed ? 'false' : 'true'); 153 $toggle.text(isCollapsed ? '+' : 'â'); 154 } 155 156 $header.on('click', function (event) { 157 if (!$(event.target).closest('a, button').length) { 158 toggleSection(); 159 } 160 }); 161 $header.on('keydown', function (event) { 162 if (event.key === 'Enter' || event.key === ' ') { 163 event.preventDefault(); 164 toggleSection(); 165 } 166 }); 167 168 $table.data('shopVoucherSectionReady', true); 169 }); 170 } 171 172 function setupVoucherTabSections($account) { 173 $account.find('#vouchers > .row').each(function () { 174 var $section = $(this); 175 var $content = $section.find( 176 '.bloc_customer_vouchers_bookable_vouchers, ' + 177 '.bloc_customer_dropzone_vouchers_bookable_vouchers, ' + 178 '[class*="_pass_bookable_vouchers"]' 179 ).first(); 180 var $title = $section.find('h3.panel-title').first(); 181 182 if (!$content.length || !$title.length || $title.data('shopVoucherToggleReady')) { 183 return; 184 } 185 186 var $button = $('<button>', { 187 type: 'button', 188 class: 'shop-account-v2__voucher-section-button', 189 'aria-expanded': 'true', 190 text: 'â' 191 }); 192 193 $title.append($button); 194 $title.data('shopVoucherToggleReady', true); 195 $button.on('click', function (event) { 196 event.preventDefault(); 197 event.stopPropagation(); 198 var isCollapsed = $section.toggleClass('is-collapsed').hasClass('is-collapsed'); 199 $button.attr('aria-expanded', isCollapsed ? 'false' : 'true').text(isCollapsed ? '+' : 'â'); 200 }); 201 }); 202 } 203 204 function setupBookingTabSections($account) { 205 $account.find('#bookings > .row').each(function () { 206 var $section = $(this); 207 var $content = $section.find( 208 '.bloc_customer_bookings, ' + 209 '.bloc_customer_dz_bookings, ' + 210 '.customer-account-booking-list, ' + 211 '.bloc_customer_hotel_rooms_bookings' 212 ).first(); 213 var $title = $section.find('h3.panel-title').first(); 214 215 if (!$content.length || !$title.length || $title.data('shopBookingToggleReady')) { 216 return; 217 } 218 219 var $button = $('<button>', { 220 type: 'button', 221 class: 'shop-account-v2__voucher-section-button', 222 'aria-expanded': 'true', 223 text: 'â' 224 }); 225 226 $title.append($button); 227 $title.data('shopBookingToggleReady', true); 228 $button.on('click', function (event) { 229 event.preventDefault(); 230 event.stopPropagation(); 231 var isCollapsed = $section.toggleClass('is-collapsed').hasClass('is-collapsed'); 232 $button.attr('aria-expanded', isCollapsed ? 'false' : 'true').text(isCollapsed ? '+' : 'â'); 233 }); 234 }); 235 } 236 237 function setupMobileActionMenus($account, cellSelector, linkSelector) { 238 $account.find(cellSelector).each(function () { 239 var $cell = $(this); 240 241 if (isMobile()) { 242 if ($cell.data('shopMobileActionsReady')) { 243 return; 244 } 245 246 var $links = $cell.find(linkSelector); 247 if (!$links.length) { 248 return; 249 } 250
251 var $menu = $('<div>', { class: 'dropdown shop-account-v2__mobile-actions' }); 252 var $toggle = $('<button>', { 253 type: 'button', 254 class: 'dropdown-toggle shop-account-v2__mobile-actions-trigger', 255 'data-toggle': 'dropdown', 256 'aria-haspopup': 'true', 257 'aria-expanded': 'false', 258 text: '...' 259 }); 260 var $list = $('<ul>', { class: 'dropdown-menu dropdown-menu-right' }); 261 262 $links.each(function () { 263 var $link = $(this); 264 var $placeholder = $('<span>', { class: 'shop-account-v2__mobile-action-placeholder' }); 265 266 if (!$.trim($link.text()) && $link.attr('title')) { 267 $link.append(document.createTextNode($link.attr('title'))); 268 } 269 270 $link.before($placeholder); 271 $('<li>').append($link).appendTo($list); 272 $link.data('shopMobileActionPlaceholder', $placeholder); 273 }); 274 275 $menu.append($toggle, $list).appendTo($cell); 276 $cell.data('shopMobileActionsReady', $menu); 277 } else { 278 var $menu = $cell.data('shopMobileActionsReady'); 279 if (!$menu) { 280 return; 281 } 282 283 $menu.find('a').each(function () { 284 var $link = $(this); 285 var $placeholder = $link.data('shopMobileActionPlaceholder'); 286 if ($placeholder && $placeholder.length) { 287 $placeholder.replaceWith($link); 288 } 289 $link.removeData('shopMobileActionPlaceholder'); 290 }); 291 $menu.remove(); 292 $cell.removeData('shopMobileActionsReady'); 293 } 294 }); 295 } 296 297 function setupMobileVoucherActions($account) { 298 setupMobileActionMenus( 299 $account, 300 '#vouchers td.text-right', 301 'a.shop-account-v2__action-text, a:not(.btn)' 302 ); 303 304 setupMobileActionMenus( 305 $account, 306 '#invoices .bloc_customers_unpaid_invoices table > tbody > tr > td:last-child, ' + 307 '#invoices .bloc_customer_third_parties_payments table > tbody > tr > td:last-child', 308 'a' 309 ); 310 } 311 312 /* Listings are still supplied by several legacy AJAX endpoints. Decorate 313 * their tables in the V2 layer so mobile CSS can stack the data without 314 * changing payloads, links, or existing action handlers. */ 315 function setupMobileListingTables($account) { 316 if (!isMobile()) { 317 return; 318 } 319 320 $account.find('#bookings .shop-account-v2__listing-section table, #vouchers .shop-account-v2__listing-section table, #invoices .shop-account-v2__listing-section table').each(function () { 321 var $table = $(this); 322 var $headerCells = $table.children('thead').find('tr:first > th'); 323 324 if (!$headerCells.length) { 325 $headerCells = $table.children('tbody').children('tr').first().children('th'); 326 } 327 328 var labels = []; 329 $headerCells.each(function () { 330 labels.push($.trim($(this).text())); 331 }); 332 333 if (labels.length) { 334 $table.children('tbody').children('tr').not(':has(th)').each(function () { 335 $(this).children('td').each(function (index) { 336 var label = labels[index] || ''; 337 if (label && !$(this).attr('data-label')) { 338 $(this).attr('data-label', label); 339 } 340 }); 341 }); 342 } 343 344 $table.addClass('shop-account-v2__mobile-stack-list'); 345 }); 346 } 347 348 function updateQuickBookingState($account, showEmptyState) { 349 $account.find('.shop-account-v2__quick-booking-skeleton').each(function () { 350 var $skeleton = $(this); 351 var $table = $skeleton.closest('table'); 352 var $emptyState = $table.find('.shop-account-v2__quick-booking-empty'); 353 var hasRows = $table.find('tbody').not('.shop-account-v2__quick-booking-skeleton, .shop-account-v2__quick-booking-empty').has('tr').length > 0; 354 355 if (hasRows) { 356 $skeleton.remove(); 357 $emptyState.hide(); 358 } else if (showEmptyState) { 359 $skeleton.remove(); 360 $emptyState.show(); 361 } 362 }); 363 } 364 365 $(function () { 366 syncLayout(); 367 setupVoucherSections($('.shop-account-v2').first()); 368 setupVoucherTabSections($('.shop-account-v2').first());
369 setupBookingTabSections($('.shop-account-v2').first()); 370 setupMobileVoucherActions($('.shop-account-v2').first()); 371 setupMobileListingTables($('.shop-account-v2').first()); 372 updateQuickBookingState($('.shop-account-v2').first(), false); 373 $(document).on('ajaxStop.shopCustomerAccountQuickBookings', function () { 374 updateQuickBookingState($('.shop-account-v2').first(), true); 375 }); 376 window.setTimeout(function () { 377 updateQuickBookingState($('.shop-account-v2').first(), true); 378 }, 2000); 379 if (window.MutationObserver) { 380 $('.shop-account-v2').each(function () { 381 var account = this; 382 var observer = new MutationObserver(function () { 383 setupVoucherSections($(account)); 384 setupVoucherTabSections($(account)); 385 setupBookingTabSections($(account)); 386 setupMobileVoucherActions($(account)); 387 setupMobileListingTables($(account)); 388 updateQuickBookingState($(account), false); 389 }); 390 observer.observe(account, { childList: true, subtree: true }); 391 }); 392 } 393 $(window).on('resize.shopCustomerAccountV2', syncLayout); 394 }); 395}(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.