PageSourceSearch

https://jamesmeadowcroft.com/assets/application-296f098e3487548255…1bc1964ed81c05fcacf32231e83ed174d4680fdb.js

js jamesmeadowcroft.com collected 2026-10-07 19:30:23 UTC 9,302 bytes, 269 lines download raw bytes

1// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2import "@hotwired/turbo-rails"
3import "controllers"
4import "add_jquery"
5import "@barba/core"
6
7document.addEventListener("turbo:load", () => {
8
9    barba.init({
10        debug: false,
11        preventRunning: true,
12
13        transitions: [
14            {
15                name: 'slide',
16
17                leave(data) {
18                    // console.log('leave')
19
20                    return new Promise(resolve => {
21                        const timeline = gsap.timeline({
22                            onComplete() {
23                                resolve()
24                            }
25                        })
26
27                        timeline
28                            .fromTo($('section.dark'), {opacity: '0'}, {opacity: '1', duration: 0.5, ease: "power3", delay: 0}, 0)
29                    })
30                },
31
32                after(data) {
33                    // console.log('enter')
34                    projectPanelHeight()
35
36                    // reset page positioning
37                    window.scrollTo({
38                        top: 0,
39                        behavior: "auto"
40                    });
41
42                    // Load animation on background image
43                    listenImageLoad()
44
45                    data.current.container.remove();
46                    const timeline = gsap.timeline()
47
48                    timeline
49                        .fromTo($('section.dark'), {opacity: '1'}, {opacity: '0', duration: 2, ease: "power3", delay: 0}, 0)
50                        .to($('header'), {color: 'white', duration: 2, ease: "power3", delay: 0}, 0)
51                        .to($('header .tag'), {borderColor: 'white', duration: 2, ease: "power3", delay: 0}, 0)
52
53                }
54            },
55
56            // When leaving home, force close the menu
57            {
58                name: "toHome",
59
60                leave(data) {
61
62                    return new Promise(resolve => {
63                        const timeline = gsap.timeline({
64                            onComplete() {
65                                resolve()
66                            }
67                        })
68
69                        timeline
70                            .to($('.landing .white-bar'), {display: 'block'})
71                            .to($('header'), {mixBlendMode: 'difference'}, 0)
72                            .to($('section.landing'), {zIndex: '2', duration: 0, ease: "power3", delay: 0}, 0)
73                            .fromTo($('section.landing'), {y: '100vh'}, {y: '0vh', duration: 0.4, ease: "power3", delay: 0}, 0)
74                            // .to($('header'), {color: 'blue', duration: 0.4, ease: "power3", delay: 0}, 0)
75                            // .to($('header .tag'), {borderColor: 'blue', duration: 0.4, ease: "power3", delay: 0}, 0)
76                    })
77                },
78
79                beforeEnter(data) {
80                    // console.log('enter')
81                    projectPanelHeight()
82
83                    // reset page positioning
84                    window.scrollTo({
85                        top: 0,
86                        behavior: "auto"
87                    });
88
89                    data.current.container.remove();
90                    const timeline = gsap.timeline()
91
92                    timeline
93                        .to($('section.landing'), {zIndex: '-1', duration: 0, ease: "power3", delay: 0}, 0)
94                        .fromTo($('section.about'), {y: '4rem'}, {y: '0', duration: 0.4, ease: "back", delay: 0}, 0)
95                        .fromTo($('section.websites'), {y: '4rem'}, {y: '0', duration: 0.4, ease: "back", delay: 0.1}, 0)
96                        .fromTo($('section.projects'), {y: '4rem'}, {y: '0', duration: 0.4, ease: "back", delay: 0.2}, 0)
97                        .to($('.landing .white-bar'), {display: 'none'}, 0)
98                },
99
100                to: {
101                    namespace: [
102                        'home'
103                    ]
104                }
105
106            }
107
108        ],
109
110        views: [
111
112            {
113                namespace: "home",
114
115                afterEnter() {
116
117                    //  Scroll to sections
118                    $('#about').click(function() {
119                        $('html, body').animate({
120                            scrollTop: $('#about').offset().top
121                        }, 700);
122                    })
123
124                    $('#websites').click(function() {
125                        $('html, body').animate({
126                            scrollTop: $('#websites').offset().top + $('section.about').height()
127                        }, 700);
128                    })
129
130                    $('#projects').click(function() {
131                        if (($('#projects').offset().top - $('#about').offset().top) < 100) {
132                            $('html, body').animate({
133                                scrollTop: $('#projects').offset().top + $('section.about').height() - $('#projects').height()
134                            }, 1000);
135                        } else {
136                            $('html, body').animate({
137                                scrollTop: $('#projects').offset().top
138                            }, 1000);
139                        }
140                    })
141
142                }
143            },
144
145            {
146                namespace: "project",
147
148                afterEnter() {
149
150                    $('header').css({'color': 'white', 'mixBlendMode': 'difference'})
151                    $('header .tag').css('borderColor', 'white')
152
153                    listenImageLoad()
154
155                    document.querySelectorAll('video').forEach(video => {
156                        video.play()
157                    })
158
159                }
160            }
161
162        ]
163    })
164
165    function projectPanelHeight() {
166        // Set bottom value of project panel depending on it's height
167        let projectPanel = $('section.projects')
168        let projectHeight = projectPanel.innerHeight()
169        projectHeight = -Math.abs(projectHeight)
170
171
172        // Set bottom value of project panel depending on it's height
173        let websitesPanel = $('section.websites')
174        let websitesHeight = websitesPanel.innerHeight()
175        websitesHeight = -Math.abs(websitesHeight)
176        // websitesHeight = websitesHeight + projectHeight
177
178        websitesPanel.css('bottom', 'calc(5.5rem + ' + websitesHeight + 'px)')
179        projectPanel.css('bottom', 'calc(2.75rem + ' + projectHeight + 'px)')
180
181    }
182
183    projectPanelHeight()
184    window.addEventListener('resize', function(event) {
185        projectPanelHeight()
186    }, true);
187
188    function listenImageLoad() {
189        var background = document.querySelector('.background');
190        var image
191        var type
192        if (background.querySelector('img')) {
193            image = background.querySelector('img');
194            type = 'image'
195        } else if (background.querySelector('video')) {
196            image = background.querySelector('video');
197            type = 'video'
198        }
199
200        $(image).css('opacity', '0')
201
202        function handleImageLoad() {
203            // console.log('Image loaded!');
204            $(image).css('opacity', '1')
205        }
206
207        if (type === 'image') {
208            if (image.complete) {
209                // Image has already loaded
210                handleImageLoad();
211            } else {
212                // Add event listener for the "load" event
213                image.addEventListener('load', handleImageLoad);
214            }
215        } else if (type === 'video') {
216            if ( image.readyState === 4 ) {
217                // it's loaded
218                handleImageLoad();
219            } else {
220                image.addEventListener('loadeddata', handleImageLoad);
221            }
222
223        }
224
225    }
226
227    // randomizeImages()
228    function randomizeImages() {
229        const landingArea = document.querySelector('.landing');
230        const images = Array.from(document.querySelectorAll('.project-image'));
231
232        let currentIndex = images.length;
233        const clonedImages = [];
234
235        const intervalId = setInterval(() => {
236            if (currentIndex <= 0) {
237                clearInterval(intervalId);
238                return;
239            }
240
241            currentIndex--;
242
243            const randomIndex = Math.floor(Math.random() * (currentIndex + 1));
244            const image = images[randomIndex];
245
246            const randomX = Math.floor(Math.random() * (landingArea.offsetWidth - (window.innerWidth * 0.2)));
247            const randomY = Math.floor(Math.random() * (landingArea.offsetHeight - 180 - (((window.innerWidth * 0.2)*9) / 16)));
248
249            const clonedImage = image.cloneNode();
250            clonedImage.style.position = 'absolute';
251            clonedImage.style.left = `${randomX}px`;
252            clonedImage.style.top = `${randomY}px`;
253
254            clonedImage.addEventListener('click', () => {
255                clonedImage.style.display = 'none';
256            });
257
258            clonedImages.push(clonedImage);
259
260            document.querySelector('.landing-images').appendChild(clonedImage);
261
262            if (randomIndex !== currentIndex) {
263                images[randomIndex] = images[currentIndex];
264                images[currentIndex] = image;
265            }
266        }, 10000);
267    }
268
269});

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.