1AnneFrank = function() { 2 this.setup(); 3}; 4 5AnneFrankPrototype = { 6 scrollPositionBar: null, 7 elements: null, 8 lastScrollPosition: null, 9 lastViewPortWidth: null, 10 scrollDetectionTimeout: null, 11 setup: function() { 12 this.scrollPositionBar = document.querySelector('.scroll-position-bar'); 13 this.lastScrollPosition = window.pageYOffset; 14 this.lastViewPortWidth = window.innerWidth; 15 16 this.setUpAudioElements(); 17 this.scrollToInitialPosition(); 18 this.installEventHandlers(); 19 this.colorMenus(); 20 }, 21 _setup: function() { 22 this.updateScrollPositionBar(); 23 this.setUpOverlayHidingLogic(); 24 this.installAfterLoadEventHandlers(); 25 this.setUpAnimations(); 26 // the position changes about 40px after load and I don't know why... 27 window.setTimeout(this.setUpAnimations.bind(this), 1000) 28 }, 29 setUpAudioElements: function() { 30 document.getElementsByTagName('audio').forEach(function(audio) { 31 audio.removeAttribute('controls') 32 var audioParent = audio.parentNode; 33 var container = document.createElement('div'); 34 var controlsContainer = document.createElement('div'); 35 var playButton = document.createElement('button'); 36 var replayButton = document.createElement('button'); 37 var progressBarContainer = document.createElement('div') 38 var progressBar = document.createElement('div'); 39 var progressTrigger = document.createElement('div'); 40 container.classList.add('af-audio'); 41 controlsContainer.classList.add('af-audio__controls'); 42 playButton.classList.add('af-audio__playbutton'); 43 replayButton.classList.add('af-audio__replaybutton'); 44 progressBarContainer.classList.add('af-audio__progress'); 45 progressBar.classList.add('progress-bar'); 46 progressTrigger.classList.add('progress-trigger'); 47 progressBarContainer.appendChild(progressBar); 48 progressBarContainer.appendChild(progressTrigger); 49 controlsContainer.appendChild(playButton); 50 controlsContainer.appendChild(replayButton); 51 controlsContainer.appendChild(progressBarContainer); 52 container.appendChild(audio); 53 container.appendChild(controlsContainer); 54 55 audioParent.appendChild(container); 56 }); 57 }, 58 scrollToInitialPosition: function() { 59 if(document.body.classList.contains('cb-layout3')) { 60 window.history.scrollRestoration = 'manual'; 61 document.querySelector('.section--main').scrollTo(); 62 } 63 }, 64 installEventHandlers: function() { 65 window.addEventListener('load', this._setup.bind(this)); 66 window.addEventListener('scroll', this.handleScroll.bind(this)); 67 document.querySelectorAll('.af-audio').forEach(function(node) { 68 node.querySelector('audio').addEventListener('timeupdate', this.handleTimeUpdate.bind(this)) 69 node.querySelector('.af-audio__playbutton').addEventListener('click', this.handlePlayButtonClicked.bind(this)) 70 node.querySelector('.af-audio__replaybutton').addEventListener('click', this.handleResetButtonClicked.bind(this)) 71 }, this); 72 }, 73 installAfterLoadEventHandlers: function() { 74 document.addEventListener('scroll', function(event) { 75 wasScrolledDirectionDown = this.lastScrollPosition < window.pageYOffset; 76 this.lastScrollPosition = window.pageYOffset; 77 78 this.updateScrollPositionBar(); 79 this.runAnimations(wasScrolledDirectionDown); 80 }.bind(this)); 81 window.addEventListener('resize', function() { 82 if(window.innerWidth == this.lastViewPortWidth) { 83 // only height changed (Safari on iOS chrome fade-in / out) 84 return; 85 } 86 this.lastViewPortWidth = window.innerWidth; 87 this.setUpAnimations(); 88 }.bind(this)); 89 }, 90 setUpAnimations: function() { 91 if (this.elements) { 92 for(var i = 0; i < this.elements.length; i += 1) { 93 this.resetAnimations(this.elements[i]) 94 } 95 } 96 this.elements = [] 97 for(var i = 0; i < this.animationDefinitions.length; i += 1) { 98 var definition = this.animationDefinitions[i] 99 Array.prototype.slice.call(document.querySelectorAll(definition.selector)) 100 .filter(definition.filter) 101 .forEach(function(element) { 102 this.elements.push(element) 103 element.scenario = definition.scenario; 104 element.animationDefinition = definition; 105 var rect = element.getBoundingClientRect(); 106 element.absoluteTop = window.pageYOffset + rect.top; 107 element.absoluteBottom = window.pageYOffset + rect.bottom; 108 var bottom = window.pageYOffset + window.innerHeight; 109 if(element.absoluteTop > bottom + element.animationDefinition.offsetBottomIn) { 110 element.state = 'low'; 111 } else if(element.absoluteBottom < window.pageYOffset - element.animationDefinition.offsetTopIn) { 112 element.state = 'high'; 113 } else { 114 element.state = 'exact'; 115 } 116 }, this); 117 } 118 }, 119 runAnimations: function(wasScrolledDirectionDown) { 120 var bottom = window.pageYOffset + window.innerHeight; 121 for(var i = 0; i < this.elements.length; i += 1) { 122 var element = this.elements[i] 123 if(wasScrolledDirectionDown) { 124 if(element.state == 'low' && element.absoluteTop <= bottom + element.animationDefinition.offsetBottomIn) { 125 this.resetAnimations(element) 126 element.classList.add(element.scenario.bottomIn); 127 element.state = 'exact'; 128 } else if(element.state == 'exact' && element.absoluteBottom <= window.pageYOffset + element.animationDefinition.offsetTopOut) { 129 this.resetAnimations(element) 130 element.classList.add(element.scenario.topOut); 131 element.state = 'high' 132 } 133 } else { 134 if(element.state == 'high' && element.absoluteBottom >= window.pageYOffset - element.animationDefinition.offsetTopIn) { 135 this.resetAnimations(element) 136 element.classList.add(element.scenario.topIn); 137 element.state = 'exact'; 138 } else if(element.state == 'exact' && element.absoluteTop >= bottom - element.animationDefinition.offsetBottomOut) { 139 this.resetAnimations(element) 140 element.classList.add(element.scenario.bottomOut); 141 element.state = 'low' 142 } 143 } 144 } 145 }, 146 resetAnimations(element) { 147 element.classList.remove(element.scenario.bottomIn, element.scenario.bottomOut, element.scenario.bottomOut, element.scenario.topOut); 148 }, 149 updateScrollPositionBar: function() { 150 this.scrollPositionBar.style.width = (100 / (document.documentElement.scrollHeight - window.innerHeight) * window.pageYOffset) + '%'; 151 }, 152 handleScroll: function() { 153 document.body.classList.add('is-scrolling'); 154 if(this.scrollDetectionTimeout) { 155 window.clearTimeout(this.scrollDetectionTimeout); 156 } 157 this.scrollDetectionTimeout = window.setTimeout( 158 function() { 159 document.body.classList.remove('is-scrolling'); 160 }, 161 200); 162 }, 163 setUpOverlayHidingLogic() { 164 var delta = window.pageYOffset; 165 var baseHandler = function() { 166 document.body.classList.add('hide-root-links'); 167 window.removeEventListener('scroll', baseHandler); 168 window.removeEventListener('click', baseHandler); 169 } 170 window.addEventListener('scroll', baseHandler); 171 window.addEventListener('click', baseHandler); 172 }, 173 colorMenus() { 174 document.querySelectorAll('.cb-layout5 .news-overview a.menu').forEach(function(menu) { 175 var text = menu.textContent; 176 var textArray = text.split(' '); 177 var date = textArray[0]; 178 var wrapper = document.createElement('span'); 179 wrapper.classList.add('af-date'); 180 wrapper.innerText = date; 181 menu.replaceChild(wrapper, menu.firstChild);
182 wrapper.insertAdjacentText('afterEnd', textArray.slice(1).join(' ')); 183 }); 184 }, 185 handleTimeUpdate(event) { 186 var video = event.target; 187 if(!('progressTrigger' in video)) { 188 // The progress bar includes vertical start and end points which must be aligned to the left 189 // and to the right of the trigger, but at 100% width the trigger would overlap the endpoint. 190 // We correct for this by setting the maximum percentage to less than 100%, depending on the 191 // trigger width. 192 video.progressTrigger = video.parentNode.querySelector('.progress-trigger'); 193 video.maxPercentage = 100 - 100 / video.progressTrigger.previousElementSibling.getBoundingClientRect().width * video.progressTrigger.getBoundingClientRect().width; 194 } 195 video.progressTrigger.style.left = Math.floor((video.currentTime / video.duration) * video.maxPercentage) + '%'; 196 }, 197 handlePlayButtonClicked(event) { 198 var button = event.target; 199 button.classList.toggle('af-audio__playbutton--playing'); 200 var audio = button.parentNode.parentNode.firstElementChild; 201 if (audio.paused || audio.ended) { 202 audio.play(); 203 } 204 else { 205 audio.pause() 206 } 207 }, 208 handleResetButtonClicked(event) { 209 var container = event.target.parentNode.parentNode; 210 var audio = container.firstElementChild; 211 var progressTrigger = container.querySelector('.progress-trigger'); 212 audio.pause(); 213 audio.currentTime = 0; 214 progressTrigger.style.left = '0%'; 215 container.querySelector('.af-audio__playbutton').classList.remove('af-audio__playbutton--playing'); 216 } 217}; 218AnneFrankPrototype.scenarios = { 219 slideBlurredTopBottom: { 220 bottomIn: 'slide-in-blurred-bottom', 221 bottomOut: 'slide-out-blurred-bottom', 222 topIn: 'slide-in-blurred-top', 223 topOut: 'slide-out-blurred-top' 224 }, 225 slideBlurredNoFadeOut: { 226 bottomIn: 'slide-in-blurred-bottom', 227 bottomOut: 'slide-in-blurred-bottom', 228 topIn: 'slide-in-blurred-bottom', 229 topOut: 'slide-in-blurred-bottom' 230 }, 231 slideBlurredRightAndBlurOut: { 232 bottomIn: 'slide-in-blurred-right', 233 bottomOut: 'text-blur-out', 234 topIn: 'slide-in-blurred-right', 235 topOut: 'text-blur-out' 236 }, 237 fadeInAndBlurOut: { 238 bottomIn: 'fade-in-fwd', 239 bottomOut: 'text-blur-out', 240 topIn: 'fade-in-fwd', 241 topOut: 'text-blur-out' 242 } 243}; 244 245AnneFrankPrototype.animationDefinitions = [ 246 { 247 selector: '.part.text, .part.line, .part.load, .part.cb-widget', 248 filter: function(element) { 249 return element.querySelector('.skew') == null && !element.closest(".unit").classList.contains('fold'); 250 }, 251 scenario: AnneFrankPrototype.scenarios.slideBlurredTopBottom, 252 offsetBottomIn: 50, 253 offsetBottomOut: 100, 254 offsetTopIn: 50, 255 offsetTopOut: 100 256 }, 257 { 258 selector: '.unit.fold', 259 scenario: AnneFrankPrototype.scenarios.slideBlurredNoFadeOut, 260 filter: function(element) { 261 return true; 262 }, 263 offsetBottomIn: 50, 264 offsetBottomOut: 0, 265 offsetTopIn: 50, 266 offsetTopOut: 0 267 }, 268 { 269 selector: '.unit', 270 filter: function(element) { 271 return element.querySelector('.skew') != null && !element.classList.contains('fold'); 272 }, 273 scenario: AnneFrankPrototype.scenarios.slideBlurredRightAndBlurOut, 274 offsetBottomIn: 50, 275 offsetBottomOut: 100, 276 offsetTopIn: 50, 277 offsetTopOut: 100 278 }, 279 { 280 selector: '.part.pict, .part.link', 281 filter: function(element) { 282 return !element.closest(".unit").classList.contains('fold'); 283 }, 284 scenario: AnneFrankPrototype.scenarios.fadeInAndBlurOut, 285 offsetBottomIn: 50, 286 offsetBottomOut: 100, 287 offsetTopIn: 50, 288 offsetTopOut: 100 289 } 290]; 291AnneFrank.prototype = AnneFrankPrototype; 292 293window.addEventListener('DOMContentLoaded', function() { 294 new AnneFrank(); 295});
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.