PageSourceSearch

https://www.annefrank.ch/extras/js/2019-04-16/af.js

js annefrank.ch collected 2026-10-01 12:16:42 UTC 12,739 bytes, 295 lines download raw bytes

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.