1(function ($, Drupal) { 2 'use strict'; 3 4 function findScenesSubwrapper(wrapper) { 5 // Try direct siblings first. 6 var el = wrapper.nextElementSibling; 7 while (el && !el.classList?.contains('scenes-media-caption-subwrapper')) { 8 el = el.nextElementSibling; 9 } 10 if (el) return el; 11 12 // Fallback: search within the same parent column. 13 var parentCol = wrapper.closest('.scenes-media-col'); 14 if (!parentCol) return null; 15 16 var allSubs = parentCol.querySelectorAll('.scenes-media-caption-subwrapper'); 17 if (allSubs.length === 1) return allSubs[0]; 18 19 // Last fallback: find first subwrapper after wrapper within parent. 20 var foundWrapper = false; 21 for (var i = 0; i < parentCol.children.length; i++) { 22 var child = parentCol.children[i]; 23 if (child === wrapper) { 24 foundWrapper = true; 25 continue; 26 } 27 if (foundWrapper && child.classList && child.classList.contains('scenes-media-caption-subwrapper')) { 28 return child; 29 } 30 } 31 return null; 32 } 33 34 function initializeScenesCaptionWrappers(context) { 35 var wrappers = (context || document).querySelectorAll('.scenes-media-caption-wrapper'); 36 37 wrappers.forEach(function (wrapper) { 38 if (wrapper.dataset.scenesInit === 'true') return; 39 wrapper.dataset.scenesInit = 'true'; 40 41 var subwrapper = findScenesSubwrapper(wrapper); 42 if (!subwrapper) return; 43 44 wrapper.setAttribute('tabindex', '0'); 45 wrapper.setAttribute('role', 'button'); 46 subwrapper.setAttribute('aria-hidden', 'true'); 47 48 var heading = subwrapper.querySelector('.scenes-media-caption-heading'); 49 var headingText = heading ? heading.textContent.trim() : ''; 50 51 // Manage links inside subwrapper 52 var links = subwrapper.querySelectorAll('a'); 53 function setLinksFocusable(enable) { 54 links.forEach(function (link) { 55 if (enable) { 56 link.removeAttribute('tabindex'); 57 } else { 58 link.setAttribute('tabindex', '-1'); 59 } 60 }); 61 } 62 setLinksFocusable(false); 63 64 function updateAria() { 65 var isOpen = wrapper.classList.contains('clicked'); 66 var state = isOpen ? 'Close' : 'Open'; 67 wrapper.setAttribute('aria-label', `${headingText} ${state}`); 68 wrapper.setAttribute('aria-pressed', isOpen ? 'true' : 'false'); 69 subwrapper.setAttribute('aria-hidden', isOpen ? 'false' : 'true'); 70 } 71 72 function openWrapper() { 73 wrapper.classList.add('clicked'); 74 subwrapper.classList.add('visible'); 75 setLinksFocusable(true); 76 updateAria(); 77 } 78 79 function closeWrapper() { 80 wrapper.classList.remove('clicked'); 81 subwrapper.classList.remove('visible'); 82 setLinksFocusable(false); 83 updateAria(); 84 } 85 86 function toggleWrapper() { 87 if (wrapper.classList.contains('clicked')) { 88 closeWrapper(); 89 } else { 90 openWrapper(); 91 } 92 } 93 94 updateAria(); 95 96 // Click behavior 97 wrapper.addEventListener('click', function (e) { 98 if (e.target.closest('.scenes-media-caption-subwrapper a')) return; 99 toggleWrapper(); 100 }); 101 102 // Keyboard behavior 103 wrapper.addEventListener('keydown', function (event) { 104 if (document.activeElement !== wrapper) return; 105 var key = event.key; 106 if (key === 'Enter' || key === ' ') { 107 event.preventDefault(); 108 toggleWrapper(); 109 } else if (key === 'Escape') { 110 if (wrapper.classList.contains('clicked')) { 111 event.preventDefault(); 112 closeWrapper(); 113 wrapper.focus(); 114 } 115 } 116 }); 117 118 // Hover behavior (wrapper + subwrapper) 119 wrapper.addEventListener('mouseenter', openWrapper); 120 wrapper.addEventListener('mouseleave', closeWrapper); 121 subwrapper.addEventListener('mouseenter', openWrapper); 122 subwrapper.addEventListener('mouseleave', closeWrapper); 123 124 if (heading) heading.removeAttribute('tabindex'); 125 126 var text = subwrapper.querySelector('.scenes-media-caption-text'); 127 if (text) { 128 text.removeAttribute('tabindex'); 129 links.forEach(function (link) { 130 link.addEventListener('click', function (e) { e.stopPropagation(); }); 131 link.addEventListener('keydown', function (e) { 132 if (e.key === 'Enter' || e.key === ' ') e.stopPropagation(); 133 }); 134 }); 135 } 136 }); 137 } 138 139 $(document).ready(function () { 140 initializeScenesCaptionWrappers(); 141 }); 142 143 var originalInsertCommand = Drupal.AjaxCommands.prototype.insert; 144 Drupal.AjaxCommands.prototype.insert = function (ajax, response, status) { 145 originalInsertCommand.apply(this, arguments); 146 var scope = ajax.wrapper ? document.querySelector(ajax.wrapper) : document; 147 initializeScenesCaptionWrappers(scope); 148 }; 149})(jQuery, Drupal);
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.