PageSourceSearch

https://static.sobaka.ru/assets/74010523/infinity-post-scroll.js

js sobaka.ru collected 2026-10-02 02:53:21 UTC 12,345 bytes, 344 lines download raw bytes

1(function ($, utils, BannersManager, CountersManager) {
2    $(function () {
3        var loadMorePostIds = Array.isArray(window.loadMorePostIds) ? window.loadMorePostIds : [];
4
5        // If there are no posts to load, then it's pointless to continue
6        if (!loadMorePostIds.length) {
7            return;
8        }
9
10        var nextPostOffset = 0;
11
12        var nextPostDataRequest = null;
13
14        var hasMorePosts = true;
15
16        var $currentPost = null;
17
18        var prevPostData = null;
19
20        var currentPostData = null;
21
22        var $window = $(window);
23
24        var lastScrollTop = $window.scrollTop();
25
26        var isScrollingUp = false;
27
28        var bannersReloadScrollStates = {};
29
30        var ymPreloadArticleId = null;
31
32        // Prevent automatic browser scroll on refresh
33        $window.on('unload', function () {
34            $window.scrollTop(0);
35        });
36
37        $window.on('scroll', utils.debounce(onScroll, 100));
38
39        function onScroll() {
40            var currentScrollTop = window.scrollY;
41
42            var scrollDelta = currentScrollTop - lastScrollTop;
43
44            isScrollingUp = currentScrollTop <= lastScrollTop;
45
46            lastScrollTop = currentScrollTop;
47
48            var viewportHeight = window.innerHeight,
49                viewportTop = window.scrollY,
50                viewportBottom = viewportTop + viewportHeight;
51
52            var $posts = $('.b-post-view');
53
54            $posts.each(function (index) {
55                var $post = $(this);
56
57                var postData = $post.data('postData');
58
59                if (!postData) {
60                    return;
61                }
62
63                var postContainer = $post.get(0).closest('.b-site-body');
64
65                var postContainerRect = postContainer.getBoundingClientRect(),
66                    postContainerHeight = postContainerRect.height,
67                    postContainerTop = postContainerRect.top + window.scrollY,
68                    postContainerMiddle = postContainerTop + postContainerHeight / 2,
69                    postContainerBottom = postContainerTop + postContainerHeight;
70
71                var intersectionLine = viewportTop + 300;
72
73                var isIntersecting = intersectionLine >= postContainerTop && intersectionLine <= postContainerBottom;
74
75                if (index === 0) {
76                    isIntersecting = isIntersecting || viewportTop <= postContainerTop;
77                } else if (index === $posts.length - 1) {
78                    isIntersecting = isIntersecting || viewportBottom >= postContainerBottom;
79                }
80
81                if (isIntersecting) {
82                    var shouldChangeCurrentPost = currentPostData?.id !== postData.id && viewportTop <= postContainerBottom - Math.min(postContainerHeight * 0.5, viewportHeight * 0.5);
83
84                    var bannersReloadScrollState = bannersReloadScrollStates[postData.id];
85
86                    if (!bannersReloadScrollState) {
87                        bannersReloadScrollState = bannersReloadScrollStates[postData.id] = {
88                            heightAcquired: 0,
89                            heightRequired: 2400,
90                        };
91                    }
92
93                    if (shouldChangeCurrentPost) {
94                        changeCurrentPost($post);
95
96                        bannersReloadScrollState.heightAcquired = 0;
97                    } else {
98                        if (bannersReloadScrollState.heightAcquired >= bannersReloadScrollState.heightRequired) {
99                            bannersReloadScrollState.heightAcquired = 0;
100
101                            // В рамках задачи SOB-77: так как у нас нет баннеров в лонгридах, то мы перезагружаем счетчики баннеров отдельно
102                            if (postData.isLongread) {
103                                updateBannerCounters();
104                            } else {
105                                updateBanners($post);
106                            }
107                        } else {
108                            bannersReloadScrollState.heightAcquired += Math.abs(scrollDelta);
109                        }
110                    }
111                }
112
113                var shouldLoadNextPost = !hasNextPostLoaded(index) && !isScrollingUp && viewportBottom >= postContainerMiddle;
114
115                if (shouldLoadNextPost) {
116                    loadNextPost($post);
117                }
118            });
119        }
120
121        function changeCurrentPost($post) {
122            $currentPost = $post;
123
124            prevPostData = currentPostData;
125
126            currentPostData = $currentPost.data('postData');
127
128            if (prevPostData && currentPostData && prevPostData.id !== currentPostData.id) {
129                updatePageUrl();
130                updatePageMetaData();
131                updateAdriverDefaults();
132                updateSiteZone();
133                updateCity();
134                updateIsCommercialPage();
135                updateRubricTags();
136                updatePageViewCounters();
137                // В рамках задачи SOB-77: так как у нас нет баннеров в лонгридах, то мы перезагружаем счетчики баннеров отдельно
138                if (currentPostData.isLongread) {
139                    updateBannerCounters();
140                } else {
141                    updateBanners();
142                }
143                updateInstagramWidgets();
144                sendYmOnloadArticleEvent(currentPostData.id);
145            }
146        }
147
148        function hasNextPostLoaded(postIndex) {
149            return nextPostOffset > postIndex;
150        }
151
152        function loadNextPost($post) {
153            if (!hasMorePosts || nextPostDataRequest) {
154                return;
155            }
156
157            var nextPostId = loadMorePostIds[nextPostOffset] ? loadMorePostIds[nextPostOffset] : null;
158
159            if (!nextPostId) {
160                hasMorePosts = false;
161
162                return;
163            }
164
165            nextPostDataRequest = $.ajax({
166                url: $post.data('nextPostFetchUrl'),
167                method: 'GET',
168                data: {
169                    id: nextPostId,
170                },
171                success: function (data) {
172                    if (!data) {
173                        hasMorePosts = false;
174
175                        return;
176                    }
177
178                    var siteBody = document.querySelector('.b-site__body');
179
180                    utils.insertHtml(data.template, siteBody);
181
182                    nextPostOffset++;
183
184                    if (!ymPreloadArticleId) {
185                        ymReachGoal('preload_article', {
186                            article_id: String(nextPostId),
187                        });
188                        ymPreloadArticleId = nextPostId;
189                    }
190                },
191                error: function () {
192                    hasMorePosts = false;
193                },
194                complete: function () {
195                    nextPostDataRequest = null;
196                },
197            });
198        }
199
200        function updatePageUrl() {
201            if (!(currentPostData && currentPostData.url)) {
202                return;
203            }
204
205            window.history.replaceState({}, '', currentPostData.url);
206        }
207
208        function updatePageMetaData() {
209            if (!(currentPostData && currentPostData.metaData)) {
210                return;
211            }
212
213            var metaData = currentPostData.metaData;
214
215            if (metaData.title) {
216                document.title = metaData.title;
217            }
218
219            if (metaData.ogTitle) {
220                $('head').find('meta[property="og:title"]').attr('content', metaData.ogTitle);
221            }
222
223            if (metaData.url) {
224                $('head').find('meta[property="og:url"]').attr('content', metaData.url);
225
226                // A workaround for IOS 15.2+: IOS browsers use canonical URL for sharing via the native share buttons
227                $('head').find('link[rel="canonical"]').attr('href', metaData.url);
228            }
229
230            if (metaData.image) {
231                $('head').find('meta[property="og:image"]').attr('content', metaData.image);
232            }
233
234            if (metaData.ogDescription) {
235                $('head').find('meta[property="og:description"]').attr('content', metaData.ogDescription);
236            }
237
238            if (metaData.description) {
239                $('head').find('meta[name="description"]').attr('content', metaData.description);
240            }
241
242            if (metaData.keywords) {
243                $('head').find('meta[name="keywords"]').attr('content', metaData.keywords);
244            }
245
246            if (metaData.canonical) {
247                $('head').find('link[rel="canonical"]').attr('href', metaData.canonical);
248            }
249        }
250
251        function updateInstagramWidgets() {
252            if ($currentPost.find('.instagram-media').length === 0) {
253                return;
254            }
255
256            if (window.instgrm && window.instgrm.Embeds && typeof window.instgrm.Embeds.process === 'function') {
257                window.instgrm.Embeds.process();
258            }
259        }
260
261        function ymReachGoal(targetName, goalParams) {
262            if (typeof window.ym === 'function') {
263                var cityAlias = utils.getCityFromCookie() || utils.getCityFromUrl() || 'spb';
264                var targetNamePostfix = '_' + cityAlias;
265                if (cityAlias === 'spb') {
266                    targetNamePostfix = '';
267                }
268                window.ym(19015538, 'reachGoal', targetName + targetNamePostfix, goalParams || {});
269                //$.notify('REACH GOAL: ' + targetName + targetNamePostfix);
270            }
271        }
272
273        function sendYmOnloadArticleEvent(postId) {
274            if (ymPreloadArticleId === postId) {
275                // article_id mirrors preload_article: the goal reaches Metrika before
276                // the async hit of the new URL, so URL-based goal attribution points to
277                // the source post — the param carries the actually loaded post id.
278                ymReachGoal('onload_article', {
279                    article_id: String(postId),
280                });
281            }
282        }
283
284        function updatePageViewCounters() {
285            if (!(currentPostData && currentPostData.url)) {
286                return;
287            }
288
289            var url = new URL(currentPostData.url, window.location.origin);
290            url.searchParams.set('scroll', 'true');
291
292            CountersManager.hitCounters({
293                url: url.href,
294                trackTags: ['pageViews'],
295            });
296        }
297
298        function updateBannerCounters() {
299            CountersManager.hitCounters({
300                trackTags: ['bannerViews'],
301            });
302        }
303
304        function updateBanners($post) {
305            var container = ($post || $currentPost).closest('.b-site-body').get(0);
306
307            BannersManager.reloadBanners({ rootContainer: container });
308        }
309
310        function updateAdriverDefaults() {
311            if (!currentPostData || !currentPostData.adriverParams) {
312                return;
313            }
314
315            utils.setAdriverDefaults(currentPostData.adriverParams);
316        }
317
318        function updateSiteZone() {
319            if (!currentPostData || !currentPostData.siteZone) {
320                return;
321            }
322
323            utils.setSiteZone(currentPostData.siteZone);
324        }
325
326        function updateCity() {
327            var city = currentPostData && currentPostData.city ? currentPostData.city : null;
328            window.sobaka = window.sobaka || {};
329            window.sobaka.city = city;
330        }
331
332        function updateIsCommercialPage() {
333            var value = currentPostData && currentPostData.isCommercialPage ? currentPostData.isCommercialPage : false;
334            window.sobaka = window.sobaka || {};
335            window.sobaka.isCommercialPage = value;
336        }
337
338        function updateRubricTags() {
339            var tags = currentPostData && Array.isArray(currentPostData.rubricTags) ? currentPostData.rubricTags : [];
340            window.sobaka = window.sobaka || {};
341            window.sobaka.rubricTags = tags;
342        }
343    });
344})(window.jQuery, window.siteUtils, window.BannersManager, window.CountersManager);

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.