1var closeTimer = false; 2var lastFocusedTrigger = null; 3 4/** 5 * Gibt alle sichtbaren, fokussierbaren Elemente innerhalb eines Containers zurück 6 * 7 * @param {Element|jQuery} container 8 * @returns {jQuery} 9 */ 10function knmGetFocusableElements(container) { 11 return $(container).find( 12 'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), ' + 13 'select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])' 14 ).filter(':visible'); 15} 16 17/** 18 * Ãffnet ein Offcanvas-Panel und verschiebt den Fokus hinein 19 * 20 * @param {Element} trigger Auslösendes Element 21 * @param {boolean} autoClose Automatisch schlieÃen nach Verzögerung 22 * @returns {boolean} 23 */ 24function knmOpenCanvas(trigger, autoClose = false) { 25 lastFocusedTrigger = document.activeElement; 26 27 let bsOverlay = $('.bs-canvas-overlay'); 28 let ctrl = $(trigger); 29 let elm = ctrl.is('button') ? ctrl.data('target') : ctrl.attr('href'); 30 let $elm = $(elm); 31 32 $('body').addClass('knm-canvas-openend'); 33 $elm.addClass('show'); 34 $(elm + ' .bs-canvas-close').attr('aria-expanded', 'true'); 35 $('[data-target="' + elm + '"], a[href="' + elm + '"]').attr('aria-expanded', 'true'); 36 if (bsOverlay.length) { 37 bsOverlay.addClass('show'); 38 } 39 40 // Fokus auf ersten fokussierbaren Inhalt verschieben (nach Transition-Start) 41 setTimeout(function () { 42 let $focusable = knmGetFocusableElements($elm); 43 if ($focusable.length) { 44 $focusable.first().focus(); 45 } 46 }, 50); 47 48 if (autoClose) { 49 $elm.on('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function (event) { 50 if (event.target.id === 'cart-offcanvas' && closeTimer === false) { 51 if (isTouchDevice() && closeDelayMobile > 0) { 52 closeTimer = setTimeout(function () { 53 knmCloseCanvas(null); 54 }, closeDelayMobile * 1000); 55 } else if (closeDelayDesktop > 0) { 56 closeTimer = setTimeout(function () { 57 knmCloseCanvas(null); 58 }, closeDelayDesktop * 1000); 59 } 60 } 61 }); 62 } 63 64 return false; 65} 66 67/** 68 * SchlieÃt ein Offcanvas-Panel und gibt den Fokus zurück 69 * 70 * @param {Element|null} targetElement Auslösendes Element (Close-Button) oder null für alle schlieÃen 71 * @returns {boolean} 72 */ 73function knmCloseCanvas(targetElement) { 74 clearTimeout(closeTimer); 75 closeTimer = false; 76 77 let elm; 78 let bsOverlay = $('.bs-canvas-overlay'); 79 $('body').removeClass('knm-canvas-openend'); 80 81 if (targetElement && $(targetElement).hasClass('bs-canvas-close')) { 82 // Gezieltes SchlieÃen über den Close-Button 83 elm = $(targetElement).closest('.bs-canvas'); 84 let elmId = elm.attr('id'); 85 $('[data-target="#' + elmId + '"], a[href="#' + elmId + '"]').attr('aria-expanded', 'false'); 86 } else { 87 // Alle Offcanvas schlieÃen (Overlay-Klick, Timer oder Escape) 88 elm = $('.bs-canvas'); 89 $('[data-toggle="canvas"]').attr('aria-expanded', 'false'); 90 } 91 92 elm.removeClass('show'); 93 $('.bs-canvas-close', elm).attr('aria-expanded', 'false'); 94 if (bsOverlay.length) { 95 bsOverlay.removeClass('show'); 96 } 97 98 // Fokus zurück auf auslösendes Element 99 if (lastFocusedTrigger && typeof lastFocusedTrigger.focus === 'function') { 100 lastFocusedTrigger.focus(); 101 lastFocusedTrigger = null; 102 } 103 104 return false; 105} 106 107/** 108 * Gibt true zurück wenn das Gerät Touch-Eingabe unterstützt 109 * 110 * @returns {boolean} 111 */ 112function isTouchDevice() { 113 return window.ontouchstart !== undefined; 114} 115 116$(function () { 117 118 let $body = $('body'); 119 let $cartOffcanvas = $('#cart-offcanvas'); 120 let accountDropdown = $('body.offcanvas-account .account-icon-dropdown'); 121 let wishlistDropdown = $('body.offcanvas-wishlist #shop-nav-wish'); 122 let compareDropdown = $('body.offcanvas-compare #shop-nav-compare'); 123 124 // Offcanvas öffnen per Klick 125 $('[data-toggle="canvas"]').on('click', function (e) { 126 e.preventDefault(); 127 knmOpenCanvas(this); 128 }); 129 130 // Offcanvas schlieÃen per Klick auf Close-Button oder Overlay 131 $(document).on('click', '.bs-canvas-close, .bs-canvas-overlay', function (e) { 132 e.preventDefault(); 133 knmCloseCanvas(this); 134 }); 135 136 // Escape-Taste schlieÃt das aktive Offcanvas 137 $(document).on('keydown', function (e) { 138 if (e.key === 'Escape' && $body.hasClass('knm-canvas-openend')) { 139 e.preventDefault(); 140 let $closeBtn = $('.bs-canvas.show .bs-canvas-close').first(); 141 knmCloseCanvas($closeBtn[0] || null); 142 } 143 }); 144 145 // Fokus-Trap: Tab-Navigation bleibt innerhalb des geöffneten Offcanvas 146 $(document).on('keydown', '.bs-canvas.show', function (e) { 147 if (e.key !== 'Tab') { 148 return; 149 } 150 let $focusable = knmGetFocusableElements(this); 151 if (!$focusable.length) { 152 return; 153 } 154 let first = $focusable.first()[0]; 155 let last = $focusable.last()[0]; 156 if (e.shiftKey) { 157 if (document.activeElement === first) { 158 e.preventDefault(); 159 last.focus(); 160 } 161 } else { 162 if (document.activeElement === last) { 163 e.preventDefault(); 164 first.focus(); 165 } 166 } 167 }); 168 169 // Tastatur-Handler für klickbare Tabellenzeilen (Wunschliste) 170 $(document).on('keydown', '#offcanvas-wrapper .clickable-row', function (e) { 171 if (e.key === 'Enter' || e.key === ' ') { 172 e.preventDefault(); 173 let href = $(this).data('href'); 174 if (href) { 175 window.location.href = href; 176 } 177 } 178 }); 179 180 // override jtl js class 181 let basketClassOverride = $.evo.basket(); 182 basketClassOverride.updateCart = function (type, cartNotices, cartIndex, couponNotices, autoClose = true) { 183 let that = this; 184 let t = parseInt(type); 185 186 if (type === undefined) { 187 t = 0; 188 } 189 190 let paymenBtnsWrapper = $('#ppc-paypal-button-custom-miniCart-wrapper'); 191 let paymenBtnsContainer = $('#paypal-button-miniCart-container'); 192 let paymentBanner = $('#ppc-miniCart-instalment-banner'); 193 let paymentBannerAmazon = $('#cart-offcanvas .lpa-button'); 194 195 $.evo.io().call('knmGetBasketItemsOffcanvas', [t], this, function (error, data) { 196 197 if (error) { 198 return; 199 } 200 201 let tpl = data.response.cTemplate; 202 let triggerSelector = 'a[href="#cart-offcanvas"]';
203 let $trigger = $(triggerSelector); 204 let $content = $(tpl); 205 let highlightDuration = 7000; 206 207 // Warenkorbinhalt ersetzen und Mengenfelder initialisieren 208 $cartOffcanvas.html($content.find('#cart-offcanvas').html()); 209 $trigger.html($content.find(triggerSelector).html()); 210 211 $cartOffcanvas.html($content.find('#cart-offcanvas').html()); 212 $cartOffcanvas.find('.cart-dropdown-buttons').append(paymenBtnsWrapper); 213 $cartOffcanvas.find('.cart-dropdown-buttons').append(paymenBtnsContainer); 214 $cartOffcanvas.find('.cart-dropdown-buttons').after(paymentBanner); 215 $cartOffcanvas.find('.cart-dropdown-buttons').after(paymentBannerAmazon); 216 217 if (typeof knm_unregister_qty_input_events == 'function') { 218 knm_unregister_qty_input_events(); 219 } 220 221 window.initNumberInput(); 222 223 // Warenkorbhinweise anzeigen 224 if (cartNotices) { 225 for (let i = 0; i < cartNotices.length; i++) { 226 let Warenkorbhinweis = cartNotices[i]; 227 let listChildItem = $('.cart-dropdown .item:nth-child(' + (1 + parseInt(cartIndex)) + ')'); 228 let alert = $('<div/>').addClass('alert alert-warning'); 229 alert.text(Warenkorbhinweis); 230 listChildItem.find('.alert-wrapper').html(alert); 231 } 232 233 setTimeout(function () { 234 $('.alert-wrapper').fadeOut(); 235 }, highlightDuration); 236 } 237 238 if (couponNotices) { 239 let $drodownbody = $('.cart-dropdown .dropdown-body'); 240 $cartOffcanvas.removeClass('loading'); 241 $drodownbody.find('.alert').remove(); 242 $drodownbody.prepend($alerts); 243 } 244 245 // Screenreader über Warenkorb-Aktualisierung informieren 246 let $statusLive = $('#knm-cart-status'); 247 let statusMsg = $statusLive.data('msg-updated') || ''; 248 $statusLive.text(''); 249 setTimeout(function () { 250 $statusLive.text(statusMsg); 251 }, 100); 252 253 // Offcanvas öffnen wenn entsprechende Einstellung aktiv ist 254 if (isTouchDevice() && $body.hasClass('offcanvas-cart-hidepushedsuccess-mobile') || !isTouchDevice() && $body.hasClass('offcanvas-cart-hidepushedsuccess-desktop')) { 255 knmOpenCanvas($trigger, autoClose); 256 } 257 258 // Aktualisierte Position ins Sichtfeld scrollen 259 let $firstItem = $('.cart-dropdown .item.updated').first(); 260 if ($firstItem.length) { 261 var targetElm = $firstItem[0]; 262 targetElm.scrollIntoView(); 263 setTimeout(function () { 264 $('.cart-dropdown .item.updated').removeClass('updated'); 265 }, highlightDuration); 266 } 267 268 $.evo.trigger('loaded.evo.content', {}); 269 $cartOffcanvas.removeClass('loading'); 270 271 if ($body.attr('data-page') === '3') { 272 window.location = window.location.href; 273 } 274 275 }); 276 }; 277 278 basketClassOverride.pushToBasket = function ($main, productId, quantity, data) { 279 let that = this; 280 281 that.toggleState($main, true); 282 283 $.evo.io().call('pushToBasket', [productId, quantity, data], that, function (error, data) { 284 285 that.toggleState($main, false); 286 287 if (error) { 288 return; 289 } 290 291 let response = data.response; 292 293 if (response) { 294 switch (response.nType) { 295 case 0: 296 that.error(response); 297 break; 298 case 1: 299 that.redirectTo(response); 300 break; 301 case 2: 302 that.updateCart(); 303 if ((isTouchDevice() === true && !$body.hasClass('offcanvas-cart-hidepushedsuccess-mobile')) || (isTouchDevice() === false && !$body.hasClass('offcanvas-cart-hidepushedsuccess-desktop'))) { 304 that.pushedToBasket(response); 305 } else { 306 $('.product-wrapper.loading').removeClass('loading'); 307 } 308 break; 309 } 310 } 311 }); 312 }; 313 314 $.evo.basket = function () { 315 return basketClassOverride; 316 }; 317 318 // Artikel aus dem Warenkorb entfernen 319 function knmRemoveFromCart(index) { 320 let context = {}; 321 $cartOffcanvas.addClass('loading'); 322 323 $.evo.io().call('knmRemoveFromCart', {'index': index}, context, function (error, data) { 324 if (data.state === 'success') { 325 $.evo.basket().updateCart(null, null, null, null, false); 326 } 327 }); 328 } 329 330 // Klick-Events im Canvas nicht an übergeordnete Dropdowns weitergeben 331 $(document).on('click', '.bs-canvas .dropdown-body *:not(.btn)', function (event) { 332 event.stopPropagation(); 333 }); 334 335 // Artikel-Entfernen-Button 336 $(document).on('click', '#cart-offcanvas .btn-cart-remove', function (e) { 337 e.preventDefault(); 338 let $item = $(this).closest('.item'); 339 let index = $item.find('input[name="index"]').val(); 340 $item.fadeOut(); 341 knmRemoveFromCart(index); 342 }); 343 344 // Mengenänderung im Warenkorb 345 $(document).on('change', '#cart-offcanvas .quantity', function (event) { 346 $cartOffcanvas.addClass('loading'); 347 let index = $(this).closest('.item').find('input[name="index"]').val(); 348 let qty = $(this).val(); 349 let kArtikel = $(this).closest('.item').find('input[name="kArtikel"]').val(); 350 let context = {}; 351 $.evo.io().call('knmChangeCartQuantity', { 352 'index': index, 353 'qty': qty, 354 'kArtikel': kArtikel 355 }, context, function (error, data) { 356 $.evo.basket().updateCart(2, data.Warenkorbhinweise, data.index, null, false); 357 }); 358 }); 359 360 // Kupon-Formular 361 $(document).on('submit', '#offcanvas-wrapper #basket-coupon-form', function (e) { 362 e.preventDefault(); 363 let $form = $(this); 364 let $cartOffcanvasLocal = $('#offcanvas-wrapper .cart-dropdown'); 365 $cartOffcanvasLocal.addClass('loading'); 366 367 $.ajax({ 368 type: 'POST', 369 url: $form.attr('action'), 370 data: $form.serialize(), 371 async: true, 372 success: function (data) { 373 $dataContent = $(data); 374 $alerts = $dataContent.find('.alert[data-key="couponError"],.alert[data-key="couponSuccess"]'); 375 $.evo.basket().updateCart(null, null, null, $alerts, false); 376 } 377 }); 378 }); 379 380 $('#coupon-form-offcanvas').on('shown.bs.collapse', function (event) { 381 $('input#couponCode').focus(); 382 }); 383 384 // Pushed-Success auf Detailseite entfernen 385 if (isTouchDevice() && !$body.hasClass('offcanvas-cart-hidepushedsuccess-mobile') || !isTouchDevice() && !$body.hasClass('offcanvas-cart-hidepushedsuccess-desktop')) { 386 if ($body.attr('data-page') == '1') { 387 let $pushSuccessAlert = $('#pushed-success'); 388 389 if (isTouchDevice() && closeDelayMobile > 0) { 390 closeTimer = setTimeout(function () { 391 $pushSuccessAlert.remove(); 392 }, closeDelayMobile * 1000); 393 394 $pushSuccessAlert.on('touchstart', function () { 395 clearTimeout(closeTimer); 396 }); 397 } else if (closeDelayDesktop > 0) { 398 closeTimer = setTimeout(function () { 399 $pushSuccessAlert.remove(); 400 }, closeDelayDesktop * 1000); 401 402 $pushSuccessAlert.on('mouseover', function () { 403 clearTimeout(closeTimer); 404 }); 405 } 406 } 407 } 408 409 // Pushed-Success-Modal auf Listenseite entfernen 410 let evoClassOverride = $.evo; 411 $.evo.showNotify = function (options) { 412 eModal.alert({ 413 size: 'xl',
414 buttons: false, 415 title: options.title, 416 message: options.text, 417 keyboard: true, 418 tabindex: -1 419 }) 420 .then( 421 function () { 422 $.evo.generateSlickSlider(); 423 424 let $pushSuccessModal = $('#pushed-success').closest('.modal'); 425 if (isTouchDevice() && closeDelayMobile > 0) { 426 closeTimer = setTimeout(function () { 427 $pushSuccessModal.modal('hide'); 428 }, closeDelayMobile * 1000); 429 430 $pushSuccessModal.on('touchstart', function () { 431 clearTimeout(closeTimer); 432 }); 433 } else if (closeDelayDesktop > 0) { 434 closeTimer = setTimeout(function () { 435 $pushSuccessModal.modal('hide'); 436 }, closeDelayDesktop * 1000); 437 438 $pushSuccessModal.on('mouseover', function () { 439 clearTimeout(closeTimer); 440 }); 441 } 442 } 443 ); 444 }; 445 446 // Konto-Offcanvas: Fokus-Tracking und Fokus-Verschiebung 447 accountDropdown.on('show.bs.dropdown', function (event) { 448 lastFocusedTrigger = document.activeElement; 449 $body.addClass('show-offcanvas-dropdown'); 450 $('#offcanvas-wrapper .account-dropdown').addClass('show'); 451 $('#quick-login input').on('keyup mouseup', function (e) { 452 if (e.which == 9) { 453 return false; 454 } 455 }); 456 }); 457 458 accountDropdown.on('shown.bs.dropdown', function (event) { 459 $('#quick-login input[name="email"]').focus(); 460 }); 461 462 accountDropdown.on('hide.bs.dropdown', function (event) { 463 $body.removeClass('show-offcanvas-dropdown'); 464 $('#offcanvas-wrapper .account-dropdown').removeClass('show'); 465 // Fokus zurück auf auslösendes Element 466 if (lastFocusedTrigger && typeof lastFocusedTrigger.focus === 'function') { 467 lastFocusedTrigger.focus(); 468 lastFocusedTrigger = null; 469 } 470 }); 471 472 // Wunschliste-Offcanvas: Fokus-Tracking und Fokus-Verschiebung 473 wishlistDropdown.on('show.bs.dropdown', function (event) { 474 lastFocusedTrigger = document.activeElement; 475 $body.addClass('show-offcanvas-dropdown'); 476 $('#offcanvas-wrapper .wishlist-dropdown').addClass('show'); 477 }); 478 479 wishlistDropdown.on('shown.bs.dropdown', function (event) { 480 let $focusable = knmGetFocusableElements($('#offcanvas-wrapper .wishlist-dropdown')); 481 if ($focusable.length) { 482 $focusable.first().focus(); 483 } 484 }); 485 486 wishlistDropdown.on('hide.bs.dropdown', function (event) { 487 $body.removeClass('show-offcanvas-dropdown'); 488 $('#offcanvas-wrapper .wishlist-dropdown').removeClass('show'); 489 if (lastFocusedTrigger && typeof lastFocusedTrigger.focus === 'function') { 490 lastFocusedTrigger.focus(); 491 lastFocusedTrigger = null; 492 } 493 }); 494 495 // Vergleichsliste-Offcanvas: Fokus-Tracking und Fokus-Verschiebung 496 compareDropdown.on('show.bs.dropdown', function (event) { 497 lastFocusedTrigger = document.activeElement; 498 $body.addClass('show-offcanvas-dropdown'); 499 $('#offcanvas-wrapper .compare-dropdown ').addClass('show'); 500 }); 501 502 compareDropdown.on('shown.bs.dropdown', function (event) { 503 let $focusable = knmGetFocusableElements($('#offcanvas-wrapper .compare-dropdown')); 504 if ($focusable.length) { 505 $focusable.first().focus(); 506 } 507 }); 508 509 compareDropdown.on('hide.bs.dropdown', function (event) { 510 $body.removeClass('show-offcanvas-dropdown'); 511 $('#offcanvas-wrapper .compare-dropdown').removeClass('show'); 512 if (lastFocusedTrigger && typeof lastFocusedTrigger.focus === 'function') { 513 lastFocusedTrigger.focus(); 514 lastFocusedTrigger = null; 515 } 516 }); 517 518 $cartOffcanvas.on('touchstart mouseover mousemove', function () { 519 clearTimeout(closeTimer); 520 }); 521 522});
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.