1(function (window, document) { 2 var modules = window.EditorumPublicationDetailModules = window.EditorumPublicationDetailModules || {}; 3 var shared = modules.shared || {}; 4 var journal = modules.journal = modules.journal || {}; 5 6 var getStickyOffset = shared.getStickyOffset; 7 var parseQuery = shared.parseQuery; 8 var getQueryParam = shared.getQueryParam; 9 var getHashId = shared.getHashId; 10 var getHashIdFromHref = shared.getHashIdFromHref; 11 var updateUrlState = shared.updateUrlState; 12 var scrollToAnchor = shared.scrollToAnchor; 13 14 function initStickySectionNav(options) { 15 var settings = options || {}; 16 var activeClass = settings.activeClass || 'active'; 17 var anchorLinks = Array.prototype.slice.call(document.querySelectorAll(settings.linkSelector || '.journal-tabs-bar__link')); 18 var queryParam = settings.queryParam || 'section'; 19 var sections = []; 20 var sectionIds = {}; 21 var isAutoScrolling = false; 22 var autoScrollTargetId = null; 23 var autoScrollTimer = null; 24 var scrollMarginExtra = settings.scrollMarginExtra; 25 var thresholdExtra = settings.thresholdExtra; 26 27 function getSectionStateIdFromHref(href) { 28 var value = href || ''; 29 var hashIndex = value.indexOf('#'); 30 var queryIndex = value.indexOf('?'); 31 var queryValue = null; 32 var query; 33 34 if (queryIndex !== -1) { 35 query = value.slice(queryIndex, hashIndex === -1 ? value.length : hashIndex); 36 queryValue = parseQuery(query)[queryParam] || null; 37 } 38 39 return queryValue || getHashIdFromHref(value); 40 } 41 42 function setActiveAnchor(id) { 43 anchorLinks.forEach(function (link) { 44 var linkId = getSectionStateIdFromHref(link.getAttribute('href') || ''); 45 46 if (!linkId) { 47 return; 48 } 49 50 link.classList.toggle(activeClass, linkId === id); 51 }); 52 } 53 54 function updateSectionScrollMargins() { 55 var marginTop = getStickyOffset() + (typeof scrollMarginExtra === 'number' ? scrollMarginExtra : 8); 56 57 sections.forEach(function (item) { 58 item.section.style.scrollMarginTop = marginTop + 'px'; 59 }); 60 } 61 62 function syncAnchorsToScroll() { 63 var offset; 64 var current; 65 66 if (isAutoScrolling && autoScrollTargetId) {
67 setActiveAnchor(autoScrollTargetId); 68 return; 69 } 70 71 if (!sections.length) { 72 return; 73 } 74 75 offset = getStickyOffset() + (typeof thresholdExtra === 'number' ? thresholdExtra : 12); 76 current = ''; 77 78 sections.forEach(function (item) { 79 if (item.section.getBoundingClientRect().top - offset <= 0) { 80 current = item.id; 81 } 82 }); 83 84 setActiveAnchor(current); 85 } 86 87 function scrollToSection(id, behavior) { 88 var target = null; 89 90 sections.some(function (item) { 91 if (item.id === id) { 92 target = item; 93 return true; 94 } 95 96 return false; 97 }); 98 99 if (!target) { 100 return false; 101 } 102 103 if (behavior === 'smooth') { 104 isAutoScrolling = true; 105 autoScrollTargetId = id; 106 107 if (autoScrollTimer) { 108 window.clearTimeout(autoScrollTimer); 109 } 110 111 autoScrollTimer = window.setTimeout(function () { 112 isAutoScrolling = false; 113 autoScrollTargetId = null; 114 syncAnchorsToScroll(); 115 }, 700); 116 } else { 117 isAutoScrolling = false; 118 autoScrollTargetId = null; 119 120 if (autoScrollTimer) { 121 window.clearTimeout(autoScrollTimer); 122 autoScrollTimer = null; 123 } 124 } 125 126 return scrollToAnchor('#' + id, behavior || 'auto'); 127 } 128 129 function findKnownSectionId(sectionId) { 130 var knownSectionId = ''; 131 132 sections.some(function (item) { 133 if (item.id === sectionId) { 134 knownSectionId = item.id; 135 return true; 136 } 137 138 return false; 139 }); 140 141 return knownSectionId; 142 } 143 144 function getRequestedSectionId() { 145 var requestedSection = getQueryParam(queryParam); 146 var hashSection = getHashId(window.location.hash); 147 148 if (requestedSection) { 149 requestedSection = findKnownSectionId(requestedSection); 150 if (requestedSection) { 151 if (window.location.hash !== '#' + requestedSection) { 152 updateUrlState((function () { 153 var state = {}; 154 155 state[queryParam] = requestedSection; 156 157 return state; 158 })(), '#' + requestedSection, true); 159 } 160 161 return requestedSection; 162 } 163 } 164 165 if (hashSection) { 166 hashSection = findKnownSectionId(hashSection); 167 if (hashSection) { 168 updateUrlState((function () { 169 var state = {}; 170 171 state[queryParam] = hashSection; 172 173 return state; 174 })(), '#' + hashSection, true); 175 176 return hashSection; 177 } 178 } 179 180 return ''; 181 } 182 183 if (!anchorLinks.length) { 184 return false; 185 } 186 187 anchorLinks.forEach(function (link) { 188 var id; 189 var stateId; 190 var section; 191 192 id = getHashIdFromHref(link.getAttribute('href') || ''); 193 stateId = getSectionStateIdFromHref(link.getAttribute('href') || '') || id; 194 section = document.getElementById(id); 195 if (!section) { 196 return; 197 } 198 199 if (!sectionIds[id]) { 200 sectionIds[id] = true; 201 sections.push({ id: id, section: section }); 202 } 203 204 link.addEventListener('click', function (event) { 205 event.preventDefault();
206 setActiveAnchor(stateId); 207 updateUrlState((function () { 208 var state = {}; 209 210 state[queryParam] = stateId; 211 212 return state; 213 })(), '#' + id, false); 214 215 scrollToSection(id, 'smooth'); 216 }); 217 }); 218 219 updateSectionScrollMargins(); 220 window.addEventListener('resize', updateSectionScrollMargins, { passive: true }); 221 window.addEventListener('scroll', syncAnchorsToScroll, { passive: true }); 222 window.addEventListener('hashchange', function () { 223 var hashId = findKnownSectionId(getHashId(window.location.hash)); 224 225 if (!hashId) { 226 return; 227 } 228 229 updateUrlState((function () { 230 var state = {}; 231 232 state[queryParam] = hashId; 233 234 return state; 235 })(), '#' + hashId, true); 236 setActiveAnchor(hashId); 237 scrollToSection(hashId, 'auto'); 238 }); 239 240 window.addEventListener('popstate', function () { 241 var requestedSectionId = getRequestedSectionId(); 242 243 if (requestedSectionId) {
244 setActiveAnchor(requestedSectionId); 245 scrollToSection(requestedSectionId, 'auto'); 246 return; 247 } 248 249 syncAnchorsToScroll(); 250 }); 251 252 var initialSectionId = getRequestedSectionId(); 253 254 if (initialSectionId) { 255 setActiveAnchor(initialSectionId); 256 scrollToSection(initialSectionId, 'auto'); 257 } else { 258 syncAnchorsToScroll(); 259 } 260 261 return true; 262 } 263 264 journal.initStickySectionNav = initStickySectionNav; 265})(window, document);
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.