1 2(function ($) { 3 'use strict'; 4 5 /* ========================================= 6 KS LIGHTBOX â SHARED CORE 7 Gemeinsamer Stand für Rhein-Kreis Kultur und 8 Staatstheater Hannover. Projektspezifische DOM- 9 Strukturen werden ausschlieÃlich über Selektoren 10 angebunden; die Lightbox-Logik bleibt gemeinsam. 11 ========================================= */ 12 if (!$) { 13 return; 14 } 15 16 /* ========================================= 17 CONFIG 18 ========================================= */ 19 20 var SELECTOR_CONTAINER = 21 '.view_dynamic_media_container, ' + 22 '.media-container-container, ' + 23 '.ensemble-detail-portrait'; 24 25 var SELECTOR_ITEM = 26 '.image-container[data-media-type="picture"], ' + 27 '.owl-media-container-item[data-media-type="picture"], ' + 28 '.ensemble-detail-portraits[data-media-type="picture"]'; 29 30 var SELECTOR_IMAGE = '.media_wrapper img, .image-wrapper img'; 31 var SELECTOR_EDITOR_CONTROL = 32 '.cb-flex-editor-button, ' + 33 '.pix-edit-button'; 34 var SELECTOR_TINYMCE_IMAGE_IN_ARTICLE = 35 '.cb-tex-image-container img, ' + 36 '.cb-media-tinymce img'; 37 var SELECTOR_TINYMCE_IMAGE = 38 '.dynamic-page-article ' + SELECTOR_TINYMCE_IMAGE_IN_ARTICLE.split(', ').join(', .dynamic-page-article '); 39 var DRAG_THRESHOLD = 10; 40 var LIGHTBOX_SWIPE_THRESHOLD = 40; 41 var LIGHTBOX_CLOSE_SWIPE_THRESHOLD = 60; 42 /* ========================================= 43 HELPERS 44 ========================================= */ 45 46 function escapeHtml(str) { 47 return String(str || '') 48 .replace(/&/g, '&') 49 .replace(/</g, '<') 50 .replace(/>/g, '>') 51 .replace(/"/g, '"') 52 .replace(/'/g, '''); 53 } 54 55 function normalizeText(text) { 56 return String(text || '').replace(/\s+/g, ' ').trim(); 57 } 58 59 /* ========================================= 60 IMAGE SOURCE 61 ========================================= */ 62 63 function getBestImageSrc($img) { 64 if (!$img || !$img.length) { 65 return ''; 66 } 67 68 return $img.prop('currentSrc') || 69 $img.attr('src') || 70 $img.attr('data-src') || 71 ''; 72 } 73 74 /* 75 * LIGHTBOX IMAGE SOURCE 76 * 77 * Die normale Seite darf fuer ihren jeweiligen Bildplatz eigene 78 * Crop-/Rescale-Parameter und 1x/2x-Groessen verwenden. Die Lightbox 79 * ist davon bewusst entkoppelt und baut aus der Originaldatei eine 80 * eigene, unbeschnittene High-Quality-Variante. 81 * 82 * min_length=1000 verhindert, dass kleine Ausgangsbilder unnoetig 83 * auf die Lightbox-Zielgroesse hochgerechnet werden. 84 */ 85 function getLightboxImageSrc($img) { 86 var source = getBestImageSrc($img); 87 var sourceUrl; 88 var originalFile; 89 90 if (!source) { 91 return ''; 92 } 93 94 try { 95 sourceUrl = new URL(source, window.location.href); 96 } catch (e) { 97 return source; 98 } 99 100 /* 101 * Nur vorhandene Image-Toolkit-URLs neu aufbauen. Andere Quellen 102 * bleiben unveraendert und funktionieren damit wie bisher. 103 */ 104 if (sourceUrl.hostname !== 'imgtoolkit.culturebase.org') { 105 return source; 106 } 107 108 originalFile = sourceUrl.searchParams.get('file'); 109 if (!originalFile) { 110 return source; 111 } 112 113 return 'https://imgtoolkit.culturebase.org/' + 114 '?quality=9' + 115 '&file=' + encodeURIComponent(originalFile) + 116 '&do=rescaleIn' + 117 '&width=0' + 118 '&height=2000' + 119 '&min_length=1000'; 120 } 121 122 /* ========================================= 123 IMAGE LOOKUP 124 Unterstützt unterschiedliche Media-Markups, 125 ohne seitenspezifische Lightbox-Logik. 126 ========================================= */ 127 128 function getItemImage($item) { 129 var $img; 130 if (!$item || !$item.length) { 131 return $(); 132 } 133
134 /* Bestehende CMS-Strukturen */ 135 $img = $item.find(SELECTOR_IMAGE).first(); 136 if ($img.length) { 137 return $img; 138 } 139 140 /* Event-Detail: Bild direkt im Picture-Item */ 141 $img = $item.children('img').first(); 142 if ($img.length) { 143 return $img; 144 } 145 146 /* Letzter generischer Fallback */ 147 return $item.find('img').first(); 148 } 149 150 function buildCaptionData($item) { 151 var $img = getItemImage($item); 152 153 /* 154 * Oeffentliche Bildinformationen bewusst eng lesen. 155 * Der title-Tag an image-wrapper / img enthaelt fuer eingeloggte 156 * Redakteure interne PixMeta-Kontrolldaten und ist keine Quelle 157 * fuer die oeffentliche Lightbox. 158 */ 159 var title = normalizeText( 160 $item.find('.media-container__title .title').first().text() 161 ); 162 var copyright = normalizeText( 163 $item.find('.copyright').first().text() 164 ); 165 166 var subtitle = normalizeText($item.find('.media-container__subtitle, [data-subtitle], .subtitle').first().text()) || 167 normalizeText($item.attr('data-subtitle')) || 168 normalizeText($img.attr('data-subtitle')); 169 var description = normalizeText($item.find('.media-container__description, [data-description], .description').first().text()) || 170 normalizeText($item.attr('data-description')) || 171 normalizeText($img.attr('data-description')); 172 173 return { 174 title: title, 175 subtitle: subtitle, 176 description: description, 177 copyright: copyright 178 }; 179 } 180 181 function buildCaptionHtml(data) { 182 var parts = []; 183 if (data.title) { 184 parts.push('<div class="ks-lightbox__caption-title">' + escapeHtml(data.title) + '</div>'); 185 } 186 187 if (data.subtitle) { 188 parts.push('<div class="ks-lightbox__caption-subtitle">' + escapeHtml(data.subtitle) + '</div>'); 189 } 190 191 if (data.description) { 192 parts.push('<div class="ks-lightbox__caption-description">' + escapeHtml(data.description) + '</div>'); 193 } 194 195 return parts.join(''); 196 } 197 198 function getPictureItems($container) { 199 return $container.find(SELECTOR_ITEM).filter(function () { 200 return !$(this).closest('.owl-item.cloned').length; 201 }); 202 } 203 204 function collectItems($container) { 205 var items = []; 206 getPictureItems($container).each(function () { 207 var $item = $(this); 208 var $img = getItemImage($item); 209 var src = getLightboxImageSrc($img); 210 var captionData; 211 if (!src) { 212 return; 213 } 214 215 captionData = buildCaptionData($item); 216 items.push({ 217 src: src, 218 alt: $img.attr('alt') || '', 219 captionHtml: buildCaptionHtml(captionData), 220 copyright: captionData.copyright || '' 221 }); 222 }); 223 return items; 224 } 225 226 227 function buildTinyMceItem(imgEl) { 228 var $img = $(imgEl); 229 var src = getLightboxImageSrc($img); 230 var title = normalizeText($img.attr('title')); 231 var alt = normalizeText($img.attr('alt')); 232 if (!src) { 233 return null; 234 } 235 236 return { 237 src: src, 238 alt: alt, 239 captionHtml: title 240 ? buildCaptionHtml({ 241 title: title, 242 subtitle: '', 243 description: '', 244 copyright: '' 245 }) 246 : '', 247 copyright: '' 248 }; 249 } 250 251 function collectTinyMceItems(clickedImgEl) { 252 var articleEl; 253 var imageEls; 254 var items = []; 255 var index = 0; 256 if (!clickedImgEl) { 257 return { 258 items: [], 259 index: 0 260 }; 261 } 262 263 /* 264 * Nur Bilder desselben CMS-Artikels sammeln. 265 * Mediencontainer / Header / Logos usw. bleiben ausgeschlossen. 266 */
267 articleEl = clickedImgEl.closest('.dynamic-page-article'); 268 if (!articleEl) { 269 return { 270 items: [], 271 index: 0 272 }; 273 } 274 275 imageEls = Array.from( 276 articleEl.querySelectorAll(SELECTOR_TINYMCE_IMAGE_IN_ARTICLE) 277 ); 278 imageEls.forEach(function (imgEl) { 279 var item = buildTinyMceItem(imgEl); 280 if (!item) { 281 return; 282 } 283 284 if (imgEl === clickedImgEl) { 285 index = items.length; 286 } 287 288 items.push(item); 289 }); 290 return { 291 items: items, 292 index: index 293 }; 294 } 295 296 297 function closestTinyMceImageFromTarget(target) { 298 if (!target || !target.closest) { 299 return null; 300 } 301 302 var imgEl = target.closest('img'); 303 if (!imgEl) { 304 return null; 305 } 306 307 if (!imgEl.matches(SELECTOR_TINYMCE_IMAGE)) { 308 return null; 309 } 310 311 return imgEl; 312 } 313 314 315 function closestItemFromTarget(target) { 316 if (!target || !target.closest) { 317 return null; 318 } 319 320 return target.closest(SELECTOR_ITEM); 321 } 322 323 function isMobileLandscape() { 324 return window.matchMedia( 325 '(hover: none) and (pointer: coarse) and (orientation: landscape)' 326 ).matches; 327 } 328 329 /* ========================================= 330 MARKUP 331 ========================================= */ 332 333 function ensureLightbox() { 334 var $existing = $('#ks-lightbox'); 335 if ($existing.length) { 336 return $existing; 337 } 338 339 var html = ` 340 <div class="ks-lightbox" id="ks-lightbox" aria-hidden="true"> 341 <div class="ks-lightbox__backdrop" data-lightbox-close="true"></div> 342 343 <div class="ks-lightbox__dialog" role="dialog" aria-modal="true" aria-label="Bildansicht"> 344 <button type="button" class="ks-lightbox__close" aria-label="SchlieÃen" data-lightbox-close="true">Ã</button> 345 346 <div class="ks-lightbox__counter"></div> 347 348 <button type="button" class="ks-lightbox__nav ks-lightbox__nav--prev" aria-label="Vorheriges Bild">â¹</button> 349 <button type="button" class="ks-lightbox__nav ks-lightbox__nav--next" aria-label="Nächstes Bild">âº</button> 350 351 <div class="ks-lightbox__stage"> 352 <div class="ks-lightbox__frame"> 353 <div class="ks-lightbox__image-wrap"> 354 <img class="ks-lightbox__image" src="" alt=""> 355 <div class="ks-lightbox__copyright"></div> 356 </div> 357 </div> 358 </div> 359 360 <div class="ks-lightbox__caption"></div> 361 </div> 362 </div> 363 `; 364 $('body').append(html); 365 return $('#ks-lightbox'); 366 } 367 368 /* ========================================= 369 API 370 ========================================= */ 371 372 var api = { 373 $lightbox: null, 374 items: [], 375 index: 0, 376 lastTrigger: null, 377 touchStartX: 0, 378 touchEndX: 0, 379 touchStartY: 0, 380 touchEndY: 0, 381 lightboxMouseDown: false, 382 lightboxMouseStartX: 0, 383 lightboxMouseStartY: 0, 384 _originalThemeColor: undefined, 385 _frozenScrollY: 0, 386 pointerState: { 387 isDown: false, 388 moved: false, 389 startX: 0, 390 startY: 0, 391 itemEl: null 392 }, 393 /* ========================================= 394 SCROLL / VIEWPORT 395 ========================================= */ 396 397 getMinimumOpenScrollY: function () { 398 var isLandscape = window.innerWidth > window.innerHeight; 399 if (isLandscape) { 400 return Math.max(180, Math.round(window.innerHeight * 0.55)); 401 } 402 403 return Math.max(110, Math.round(window.innerHeight * 0.28)); 404 }, 405 getScrollY: function () { 406 return window.pageYOffset || 407 document.documentElement.scrollTop || 408 document.body.scrollTop || 409 0; 410 }, 411 ensureMinimumScrollForLightbox: function (callback) { 412 var self = this; 413 var minY = this.getMinimumOpenScrollY(); 414 var attempts = 0; 415 var maxAttempts = 8; 416 var tolerance = 8; 417 function ensure() { 418 var currentY = self.getScrollY(); 419 if (currentY >= (minY - tolerance) || attempts >= maxAttempts) { 420 if (typeof callback === 'function') { 421 callback(); 422 } 423 return; 424 } 425 426 attempts += 1; 427 window.scrollTo(0, minY); 428 setTimeout(ensure, 80); 429 } 430 431 ensure(); 432 },
433 freezeBackgroundScroll: function () { 434 this._frozenScrollY = this.getScrollY(); 435 }, 436 unfreezeBackgroundScroll: function () { 437 var scrollY = typeof this._frozenScrollY === 'number' ? this._frozenScrollY : 0; 438 window.scrollTo(0, scrollY); 439 }, 440 /* ========================================= 441 INIT 442 ========================================= */ 443 444 init: function () { 445 this.$lightbox = ensureLightbox(); 446 document.querySelectorAll(SELECTOR_TINYMCE_IMAGE).forEach(function (img) { 447 if (!img.hasAttribute('tabindex')) { 448 img.setAttribute('tabindex', '0'); 449 } 450 451 if (!img.hasAttribute('role')) { 452 img.setAttribute('role', 'button'); 453 } 454 455 if (!img.hasAttribute('aria-label')) { 456 img.setAttribute('aria-label', 'Bild vergröÃern'); 457 } 458 }); 459 this.bindGlobalEvents(); 460 }, 461 /* ========================================= 462 OPEN / CLOSE 463 ========================================= */ 464 465 466 openFromTinyMceImage: function (imgEl) { 467 var collection = collectTinyMceItems(imgEl); 468 if (!collection.items.length) { 469 return; 470 } 471 472 this.lastTrigger = imgEl; 473 this.items = collection.items; 474 this.index = collection.index; 475 this.openPreparedItems(); 476 }, 477 openPreparedItems: function () { 478 if (!this.items.length) { 479 return; 480 } 481 482 document.body.classList.add('ks-lightbox-active'); 483 document.documentElement.classList.add('ks-lightbox-active'); 484 document.documentElement.style.backgroundColor = '#000000'; 485 document.body.style.backgroundColor = '#000000'; 486 $('body').addClass('ks-lightbox-open'); 487 this.freezeBackgroundScroll(); 488 this.$lightbox 489 .attr('aria-hidden', 'false') 490 .addClass('is-open'); 491 this.render(); 492 this.$lightbox.find('.ks-lightbox__close').trigger('focus'); 493 this.setThemeColor('#000000'); 494 }, 495 openFromItem: function (itemEl) { 496 var $item = $(itemEl); 497 var $container = $item.closest(SELECTOR_CONTAINER); 498 var $items = getPictureItems($container); 499 var index = $items.index($item); 500 this.lastTrigger = itemEl; 501 if (index < 0) { 502 index = 0; 503 } 504 505 this.openFromContainer($container, index); 506 }, 507 openFromContainer: function ($container, index) { 508 this.items = collectItems($container); 509 this.index = index || 0; 510 this.openPreparedItems(); 511 }, 512 close: function () { 513 if (document.activeElement) { 514 document.activeElement.blur(); 515 } 516 517 $('body').removeClass('ks-lightbox-open'); 518 document.body.classList.remove('ks-lightbox-active'); 519 document.documentElement.classList.remove('ks-lightbox-active'); 520 document.documentElement.style.backgroundColor = ''; 521 document.body.style.backgroundColor = ''; 522 this.$lightbox 523 .attr('aria-hidden', 'true') 524 .removeClass('is-open'); 525 this.unfreezeBackgroundScroll(); 526 this.restoreThemeColor(); 527 if (this.lastTrigger && typeof this.lastTrigger.focus === 'function') { 528 this.lastTrigger.focus(); 529 } 530 }, 531 setThemeColor: function (color) { 532 var themeMeta = document.querySelector('meta[name="theme-color"]'); 533 if (!themeMeta) 534 return; 535 // Originalwert für das spätere Wiederherstellen merken. 536 if (typeof this._originalThemeColor === 'undefined') { 537 this._originalThemeColor = themeMeta.getAttribute('content'); 538 } 539 540 // Meta-Element ersetzen; iOS/Safari reagiert darauf zuverlässiger als auf reine Attributänderung. 541 var newMeta = themeMeta.cloneNode(); 542 newMeta.setAttribute('content', color); 543 themeMeta.parentNode.replaceChild(newMeta, themeMeta); 544 }, 545 restoreThemeColor: function () { 546 var themeMeta = document.querySelector('meta[name="theme-color"]'); 547 if (!themeMeta || typeof this._originalThemeColor === 'undefined') { 548 return; 549 } 550 551 var newMeta = themeMeta.cloneNode(); 552 newMeta.setAttribute('content', this._originalThemeColor); 553 themeMeta.parentNode.replaceChild(newMeta, themeMeta); 554 }, 555 /* ========================================= 556 NAVIGATION 557 ========================================= */ 558 559 prev: function () { 560 if (!this.items.length) { 561 return; 562 } 563 564 this.index = (this.index - 1 + this.items.length) % this.items.length; 565 this.render(); 566 }, 567 next: function () { 568 if (!this.items.length) { 569 return; 570 } 571 572 this.index = (this.index + 1) % this.items.length; 573 this.render(); 574 }, 575 /* ========================================= 576 IMAGE WRAP SIZE 577 ========================================= */ 578 579 updateImageWrapSize: function () { 580 var $frame = this.$lightbox.find('.ks-lightbox__frame'); 581 var $wrap = this.$lightbox.find('.ks-lightbox__image-wrap'); 582 var img = this.$lightbox.find('.ks-lightbox__image').get(0); 583 var frameWidth; 584 var frameHeight; 585 var imageRatio; 586 var frameRatio; 587 var width; 588 var height; 589 if (!$frame.length || !$wrap.length || !img || !img.naturalWi
589dth || !img.naturalHeight) { 590 return; 591 } 592 593 frameWidth = $frame.width(); 594 frameHeight = $frame.height(); 595 if (!frameWidth || !frameHeight) { 596 return; 597 } 598 599 imageRatio = img.naturalWidth / img.naturalHeight; 600 frameRatio = frameWidth / frameHeight; 601 if (imageRatio > frameRatio) { 602 width = frameWidth; 603 height = Math.round(frameWidth / imageRatio); 604 } else { 605 height = frameHeight; 606 width = Math.round(frameHeight * imageRatio); 607 } 608 609 /* 610 * Kleine Bildressourcen niemals über ihre natürliche PixelgröÃe 611 * hinaus vergröÃern. Das Image Toolkit verhindert mit min_length 612 * bereits das serverseitige Hochskalieren; diese Begrenzung schützt 613 * zusätzlich vor clientseitiger VergröÃerung durch die Lightbox. 614 */ 615 if (width > img.naturalWidth || height > img.naturalHeight) { 616 width = img.naturalWidth; 617 height = img.naturalHeight; 618 } 619 620 $wrap.css({ 621 width: width + 'px', 622 height: height + 'px' 623 }); 624 }, 625 render: function () { 626 var self = this; 627 var item = this.items[this.index]; 628 var $image = this.$lightbox.find('.ks-lightbox__image'); 629 var $wrap = this.$lightbox.find('.ks-lightbox__image-wrap'); 630 $wrap.addClass('is-loading'); 631 if (!item) { 632 return; 633 } 634 635 this.$lightbox.find('.ks-lightbox__image-wrap').css({ 636 width: '', 637 height: '' 638 }); 639 $image 640 .off('load.ksLightbox') 641 .on('load.ksLightbox', function () { 642 self.updateImageWrapSize(); 643 window.requestAnimationFrame(function () { 644 $wrap.removeClass('is-loading'); 645 }); 646 }) 647 648 .attr('src', item.src) 649 .attr('alt', item.alt || ''); 650 if ($image.get(0) && $image.get(0).complete) { 651 self.updateImageWrapSize(); 652 window.requestAnimationFrame(function () { 653 $wrap.removeClass('is-loading'); 654 }); 655 } 656 657 this.$lightbox.find('.ks-lightbox__counter') 658 .text((this.index + 1) + '/' + this.items.length); 659 this.$lightbox.find('.ks-lightbox__caption') 660 .html(item.captionHtml || ''); 661 this.$lightbox.find('.ks-lightbox__copyright') 662 .text(item.copyright || ''); 663 this.$lightbox.toggleClass('is-single', this.items.length < 2); 664 }, 665 /* ========================================= 666 POINTER STATE: THUMBNAIL STRIP 667 ========================================= */ 668 669 resetPointerState: function () { 670 if (this.pointerState.itemEl) { 671 $(this.pointerState.itemEl).removeClass('is-dragging'); 672 } 673 674 this.pointerState.isDown = false; 675 this.pointerState.moved = false; 676 this.pointerState.startX = 0; 677 this.pointerState.startY = 0; 678 this.pointerState.itemEl = null; 679 }, 680 /* ========================================= 681 EVENTS 682 ========================================= */ 683 684 bindGlobalEvents: function () { 685 var self = this; 686 687 /* ========================================= 688 TINYMCE ARTICLE IMAGES 689 ========================================= */ 690 691 document.addEventListener('click', function (event) { 692 var imgEl = closestTinyMceImageFromTarget(event.target); 693 694 if (!imgEl || $(event.target).closest('#ks-lightbox').length) { 695 return; 696 } 697 698 event.preventDefault(); 699 event.stopPropagation(); 700 701 self.openFromTinyMceImage(imgEl); 702 }, true); 703 704 /* ========================================= 705 THUMBNAIL STRIP: CLICK VS DRAG 706 ========================================= */ 707 708 document.addEventListener('mousedown', function (event) { 709 var itemEl = closestItemFromTarget(event.target); 710 711 /* 712 * Nur primaeren Mausklick fuer die Lightbox auswerten. 713 * Rechtsklick, Mittelklick und Ctrl+Klick (macOS-Kontextmenue)
714 * muessen fuer Browser-/Inspector-Kontextmenues frei bleiben. 715 */ 716 if (event.button !== 0 || event.ctrlKey) { 717 self.resetPointerState(); 718 return; 719 } 720 721 if ( 722 !itemEl || 723 $(event.target).closest('#ks-lightbox').length || 724 $(event.target).closest(SELECTOR_EDITOR_CONTROL).length 725 ) { 726 return; 727 } 728 729 self.pointerState.isDown = true; 730 self.pointerState.moved = false; 731 self.pointerState.startX = event.clientX; 732 self.pointerState.startY = event.clientY; 733 self.pointerState.itemEl = itemEl; 734 }, true); 735 document.addEventListener('mousemove', function (event) { 736 var state = self.pointerState; 737 var dx; 738 var dy; 739 if (!state.isDown || !state.itemEl) { 740 return; 741 } 742 743 dx = Math.abs(event.clientX - state.startX); 744 dy = Math.abs(event.clientY - state.startY); 745 if (dx > DRAG_THRESHOLD || dy > DRAG_THRESHOLD) { 746 state.moved = true; 747 $(state.itemEl).addClass('is-dragging'); 748 } 749 }, true); 750 document.addEventListener('mouseup', function (event) { 751 var state = self.pointerState; 752 var itemEl = closestItemFromTarget(event.target); 753 754 if (event.button !== 0 || event.ctrlKey) { 755 self.resetPointerState(); 756 return; 757 } 758 759 if ($(event.target).closest(SELECTOR_EDITOR_CONTROL).length) { 760 self.resetPointerState(); 761 return; 762 } 763 764 if (!state.isDown || !state.itemEl) { 765 return; 766 } 767 768 if (!itemEl || itemEl !== state.itemEl || state.moved) { 769 self.resetPointerState(); 770 return; 771 } 772 773 event.preventDefault(); 774 event.stopPropagation(); 775 self.openFromItem(state.itemEl); 776 self.resetPointerState(); 777 }, true); 778 document.addEventListener('dragstart', function (event) { 779 if ($(event.target).closest('#ks-lightbox').length) { 780 event.preventDefault(); 781 } 782 783 self.resetPointerState(); 784 }, true); 785 document.addEventListener('mouseleave', function () { 786 self.resetPointerState(); 787 }, true); 788 /* ========================================= 789 KEYBOARD 790 ========================================= */ 791 792 document.addEventListener('keydown', function (event) { 793 var active = document.activeElement; 794 var tinyMceImg = active && active.matches 795 ? active.matches(SELECTOR_TINYMCE_IMAGE) 796 : false; 797 if (tinyMceImg && (event.key === 'Enter' || event.key === ' ')) { 798 event.preventDefault(); 799 self.openFromTinyMceImage(active); 800 return; 801 } 802 if (active && $(active).closest(SELECTOR_EDITOR_CONTROL).length) { 803 return; 804 } 805 806 var itemEl = closestItemFromTarget(active); 807 if (itemEl && (event.key === 'Enter' || event.key === ' ')) { 808 event.preventDefault(); 809 self.openFromItem(itemEl); 810 return; 811 } 812 813 if (!self.$lightbox || !self.$lightbox.hasClass('is-open')) { 814 return; 815 } 816 817 if (event.key === 'Escape') { 818 self.close(); 819 } 820 821 if (event.key === 'ArrowLeft') { 822 self.prev(); 823 } 824 825 if (event.key === 'ArrowRight') { 826 self.next(); 827 } 828 }, true); 829 /* ========================================= 830 ORIENTATION CHANGE 831 ========================================= */ 832 833 window.addEventListener('orientationchange', function () { 834 if (!self.$lightbox || !self.$lightbox.hasClass('is-open')) { 835 return; 836 } 837 838 setTimeout(function () { 839 self.ensureMinimumScrollForLightbox(function () { 840 self._frozenScrollY = self.getScrollY(); 841 self.updateImageWrapSize(); 842 setTimeout(function () { 843 self.ensureMinimumScrollForLightbox(function () { 844 self._frozenScrollY = self.getScrollY(); 845 self.updateImageWrapSize(); 846 }); 847 }, 220); 848 }); 849 }, 160); 850 }, true); 851 window.addEventListener('resize', function () { 852 if (!self.$lightbox || !self.$lightbox.hasClass('is-open')) { 853 return; 854 } 855 856 self.updateImageWrapSize(); 857 }, true); 858 /* ========================================= 859 LIGHTBOX BUTTONS 860 ========================================= */ 861 862 self.$lightbox.on('click', '[data-lightbox-close="true"]', function (event) { 863 event.preventDefault(); 864 event.stopPropagation(); 865 self.close(); 866 }); 867 self.$lightbox.on('click', '.ks-lightbox__nav--prev', function (event) { 868 event.preventDefault(); 869 event.stopPropagation(); 870 self.prev(); 871 }); 872 self.$lightbox.on('click', '.ks-lightbox__nav--next', function (event) { 873 event.preventDefault(); 874 event.stopPropagation(); 875 self.next(); 876 }); 877 /* ========================================= 878 TOUCH LIGHTBOX SWIPE 879 ========================================= */ 880 881 self.$lightbox.on('touchstart', '.ks-lightbox__stage', function (event) { 882 var touch = event.originalEvent.touches[0]; 883 self.touchStartX = touch.clientX; 884 self.touchStartY = touch.clientY; 885 }); 886 self.$lightbox.on('touchend', '.ks-lightbox__stage', function (event) { 887 var touch = event.originalEvent.changedTouches[0]; 888 var deltaX; 889 var deltaY; 890 self.touchEndX = touch.clientX; 891 self.touchEndY = touch.clientY; 892 deltaX = self.touchEndX - self.touchStartX; 893 deltaY = self.touchEndY - self.touchStartY; 894 /* ========================================= 895 VERTICAL SWIPE / CLOSE 896 ========================================= */ 897 898 if ( 899 Math.abs(deltaY) >= LIGHTBOX_CLOSE_SWIPE_THRESHOLD && 900 Math.abs(deltaY) > Math.abs(deltaX) 901 ) { 902 /* 903 * MOBILE LANDSCAPE: 904 * Nur Swipe nach unten schlieÃt. 905 * Swipe nach oben bleibt frei, damit Safari seine 906 * Browser-UI durch normalen Dokument-Scroll ausblenden kann. 907 * 908 * deltaY > 0 = Finger bewegt sich nach unten 909 * deltaY < 0 = Finger bewegt sich nach oben 910 */ 911 if (isMobileLandscape()) { 912 if (deltaY > 0) { 913 self.close(); 914 } 915 916 return; 917 } 918 919 /* 920 * MOBILE PORTRAIT / sonstige Touch-Zustände: 921 * Swipe nach oben ODER unten schlieÃt weiterhin. 922 */ 923 self.close(); 924 return; 925 } 926 927 928 if (Math.abs(deltaX) >= LIGHTBOX_SWIPE_THRESHOLD && Math.abs(deltaX) > Math.abs(deltaY)) { 929 if (deltaX > 0) { 930 self.prev(); 931 } else { 932 self.next(); 933 } 934 } 935 }); 936 /* ========================================= 937 DESKTOP LIGHTBOX SWIPE / DRAG 938 ========================================= */ 939 940 self.$lightbox.on('mousedown', '.ks-lightbox__stage', function (event) { 941 if (event.button !== 0) { 942 return; 943 } 944 945 if ($(event.target).closest('.ks-lightbox__close, .ks-lightbox__nav').length) { 946 return; 947 } 948 949 event.preventDefault(); 950 self.lightboxMouseDown = true; 951 self.lightboxMouseStartX = event.clientX; 952 self.lightboxMouseStartY = event.clientY; 953 }); 954 document.addEventListener('mousemove', function (event) { 955 if (!self.lightboxMouseDown) { 956 return; 957 } 958 959 event.preventDefault(); 960 }, true); 961 document.addEventListener('mouseup', function (event) { 962 var deltaX; 963 var deltaY; 964 if (!self.lightboxMouseDown) { 965 return; 966 } 967 968 self.lightboxMouseDown = false;
969 deltaX = event.clientX - self.lightboxMouseStartX; 970 deltaY = event.clientY - self.lightboxMouseStartY; 971 if (Math.abs(deltaX) >= LIGHTBOX_SWIPE_THRESHOLD && Math.abs(deltaX) > Math.abs(deltaY)) { 972 event.preventDefault(); 973 if (deltaX > 0) { 974 self.prev(); 975 } else { 976 self.next(); 977 } 978 } 979 }, true); 980 } 981 }; 982 983 /* ========================================= 984 START 985 ========================================= */ 986 987 $(document).ready(function () { 988 api.init(); 989 }); 990} 991)(window.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.