PageSourceSearch

https://aicomics.ru/static/modules/vote/neurocomics2025/js/voter.js?rnd=1791387567

js aicomics.ru collected 2026-10-07 15:39:35 UTC 27,381 bytes, 720 lines download raw bytes

1(function(w, d){
2
3    function telegramLoginBegin()
4    {
5        w.voteShowModal('Получение данных...');
6
7        const FD = new FormData();
8        FD.append(d.querySelector('meta[name=csrf-param]').getAttribute('content'), d.querySelector('meta[name=csrf-token]').getAttribute('content'));
9
10        fetch('/telegram-webhook/login-button', {
11            method: 'post',
12            body: FD,
13        }).then(function(res) {
14            return res.json();
15        }).then(function(json) {
16            if (!json) {
17                return;
18            }
19
20            var btn = document.createElement('a');
21            btn.target = '_blank';
22            btn.innerText = 'Войти через Telegram';
23            btn.classList.add('app-btn');
24            btn.addEventListener('click', onTelegramLogin);
25            btn.href= json.buttonLink;
26
27            w.voteShowModal('После нажатия кнопки вы перейдете в диалог с нашим ботом @aicomics_bot.<br><br>В диалоге с ботом нажмите кнопку Start<br><br>', btn);
28
29        });
30
31
32    }
33
34
35    function onTelegramLogin(event)
36    {
37        var currentIteration = 0;
38        var telegramLoginInterval;
39
40        telegramLoginInterval = setInterval(function(){
41
42            fetch('/vote/socialuser/telegram-auth-check')
43                .then(function(data){
44                    return data.json()
45                })
46                .then(function(json){
47
48                    if (json.ok) {
49
50                        clearInterval(telegramLoginInterval);
51
52                        if (w.localStorage) {
53                            w.localStorage.setItem('snloginsuccess', '1');
54                        }
55                        location.reload();
56
57                    }
58
59                    if (currentIteration > 50) {
60
61                        clearInterval(telegramLoginInterval)
62
63
64
65                    }
66
67                    console.log(currentIteration);
68
69                })
70
71
72        }, 3000);
73    }
74
75    function onSnLogin(event)
76    {
77        event.preventDefault();
78        event.stopPropagation();
79
80        var promise = new Promise(function(onSnLoginResolve, onSnLoginReject) {
81
82            var sn = event.target.dataset.sn;
83
84            if (sn === 'telegram') {
85
86                telegramLoginBegin();
87
88                return;
89            }
90
91            /*
92                По клику кнопка открывает окно авторизации
93                Запускается скрипт который ждет от окна данные:
94                - Долго ждет - валится в ошибку
95                - Окно закрыто - валится в ошибку
96                - Если удалось получить из окна данные - получает
97                    - Проверяет данные - если все ок, то считаем что пользователь верифицирован
98                                       - если не ок - валится в ошибку
99             */
100
101            var snModalInterval; // setInterval handler
102            var snModalIntarvalIterations = 0; // текущее количество опросов окна
103            var snModalIntervalMaxIterations = 60; // максимальное количество опросов окна
104            var snModalIterationMSec = 1000; // время задержки между опросами
105            var snWindow; // окно
106
107            snWindow =  window.open('https://www.sostav.ru/socialapi/authorize?social_id='+sn+'&redirect_uri=' + window.location.origin + '/vote/socialuser/auth', 'vote', 'width=800,height=600,resizable=no,scrollbars=no,status=no');
108            w.voteShowModal('Ждем вашего разрешения...');
109
110            snModalIntarvalIterations = 0;
111            snModalInterval = setInterval(function(){
112
113                snModalIntarvalIterations++;
114
115                if (snModalIntarvalIterations > snModalIntervalMaxIterations) {
116
117                    // истекло время ожидания
118
119                    clearInterval(snModalInterval);
120                    w.voteShowModal('Не дождались вашего разрешения. Время ожидания истекло ('+(snModalIntervalMaxIterations*snModalIterationMSec/1000)+'сек)');
121                    snWindow.close();
122                    snWindow = null;
123                    onSnLoginReject();
124
125                } else if (snWindow.closed) {
126
127                    // окно было закрыто
128
129                    clearInterval(snModalInterval);
130                    w.voteShowModal('Не дождались вашего разрешения. Окно авторизации было закрыто.');
131                    snWindow = null;
132                    onSnLoginReject();
133
134                } else {
135
136                    // попытка получить ответ
137
138                    var answerExists;
139
140                    try {
141                        answerExists = typeof snWindow.window.socialUserAuthResult;
142                    } catch (e) {
143
144                        //
145                        // отсутствует необходимая функция в окне, которая вернет результат верификации
146                        // это нормально, так как пользователь может ползать по открытому окну с соц сетью
147                        // к которой нет доступа
148                        // поэтому попытка найти нужную функцию в cross-origin окне может вызывать ошибку
149                    }
150
151                    if (answerExists === 'function') {
152
153                        var authResult = snWindow.window.socialUserAuthResult();
154
155                        clearInterval(snModalInterval);
156                        snWindow.close();
157                        snWindow = null;
158
159                        if (authResult.ok) {
160                            onSnLoginResolve(authResult);
161                        } else {
162                            w.voteShowModal(authResult.message);
163                            onSnLoginReject();
164                        }
165                    }
166                }
167
168            }, snModalIterationMSec);
169
170        });
171
172        promise.then(function (res) {
173            if (w.localStorage) {
174                w.localStorage.setItem('snloginsuccess', '1');
175            }
176            location.reload();
177        });
178
179    }
180
181    function createOneSnBtn(sn)
182    {
183        var btn = document.createElement('button');
184        btn.type = 'button';
185        btn.innerText = 'Пройти верификацию';
186        btn.classList.add('app-btn', 'app-btn--icon');
187        btn.setAttribute('data-sn', sn);
188        btn.addEventListener('click', onSnLogin);
189
190        return btn;
191    }
192
193    function createInitSnBtns(targetBtn)
194    {
195        var div = document.createElement('div');
196        div.classList.add('vote-sns-list-selector');
197
198        var sns = targetBtn.closest('.js-vote-buttons').dataset.sns.split(';');
199        sns.forEach(function(sn) {
200            div.appendChild(createOneSnBtn(sn))
201        })
202
203        return div;
204    }
205
206    function initSn(targetBtn)
207    {
208        return new Promise(function(resolve, reject) {
209
210            var btns = createInitSnBtns(targetBtn);
211            w.voteShowModal('Для голосования требуется верификация через соц. сеть. Кликните на выбранный значок ' +
212                'аккаунта чтобы продолжить. Это необходимо, чтобы защитить участников от возможной накрутки. ' +
213                'Мы используем ваш аккаунт только для голосования и сохраним только ваш идентификатор. Вы сможете ' +
214                'отменить верификацию в любой момент.<br><br>', btns)
215
216            // вернем результат - что проголосовать не получилось
217            // silent - без какого-либо уведомления для пользователя
218            resolve({
219                ok: false,
220                need: 'silent',
221            })
222        })
223    }
224
225    function needSnLogin(sn)
226    {
227        return new Promise(function(needSnLoginResolve, needSnLoginReject){
228
229            // кнопка перехода в окно авторизации через соц сеть
230            var btn = document.createElement('button');
231            btn.type = 'button';
232            btn.innerText = 'Пройти верификацию';
233            btn.classList.add('app-btn');
234            btn.addEventListener('click', function() {
235
236                /*
237                По клику кнопка открывает окно авторизации
238                Запускается скрипт который ждет от окна данные:
239                - Долго ждет - валится в ошибку
240                - Окно закрыто - валится в ошибку
241                - Если удалось получить из окна данные - получает
242                    - Проверяет данные - если все ок, то считаем что пользователь верифицирован
243                                       - если не ок - валится в ошибку
244                 */
245
246                var snModalInterval; // setInterval handler
247                var snModalIntarvalIterations = 0; // текущее количество опросов окна
248                var snModalIntervalMaxIterations = 60; // максимальное количество опросов окна
249                var snModalIterationMSec = 1000; // время задержки между опросами
250                var snWindow; // окно
251
252                snWindow =  window.open('https://www.sostav.ru/socialapi/authorize?social_id='+sn+'&redirect_uri=' 
252+ window.location.origin + '/vote/socialuser/auth', 'vote', 'width=800,height=600,resizable=no,scrollbars=no,status=no');
253                w.voteShowModal('Ждем вашего разрешения...');
254
255                snModalIntarvalIterations = 0;
256                snModalInterval = setInterval(function(){
257
258                    snModalIntarvalIterations++;
259
260                    if (snModalIntarvalIterations > snModalIntervalMaxIterations) {
261
262                        // истекло время ожидания
263
264                        clearInterval(snModalInterval);
265                        w.voteShowModal('Не дождались вашего разрешения. Время ожидания истекло ('+(snModalIntervalMaxIterations*snModalIterationMSec/1000)+'сек)');
266                        snWindow.close();
267                        snWindow = null;
268                        needSnLoginReject();
269
270                    } else if (snWindow.closed) {
271
272                        // окно было закрыто
273
274                        clearInterval(snModalInterval);
275                        w.voteShowModal('Не дождались вашего разрешения. Окно авторизации было закрыто.');
276                        snWindow = null;
277                        needSnLoginReject();
278
279                    } else {
280
281                        // попытка получить ответ
282
283                        var answerExists;
284
285                        try {
286                            answerExists = typeof snWindow.window.socialUserAuthResult;
287                        } catch (e) {
288
289                            //
290                            // отсутствует необходимая функция в окне, которая вернет результат верификации
291                            // это нормально, так как пользователь может ползать по открытому окну с соц сетью
292                            // к которой нет доступа
293                            // поэтому попытка найти нужную функцию в cross-origin окне может вызывать ошибку
294                        }
295
296                        if (answerExists === 'function') {
297
298                            var authResult = snWindow.window.socialUserAuthResult();
299
300                            clearInterval(snModalInterval);
301                            snWindow.close();
302                            snWindow = null;
303
304                            if (authResult.ok) {
305                                //w.voteShowModal(authResult.message);
306                                needSnLoginResolve();
307                            } else {
308                                w.voteShowModal(authResult.message);
309                                needSnLoginReject();
310                            }
311                        }
312                    }
313
314                }, snModalIterationMSec);
315            });
316
317            w.voteShowModal('Чтобы проголосовать через этот аккаунт, нам необходимо ваше разрешение. Мы не собираем ваши данные и не храним информацию о вас.<br><br>', btn)
318
319        });
320    }
321
322    function voteError(message)
323    {
324        w.voteShowModal(message || 'Не удалось проголосовать. Возникла ошибка.', null, 'error')
325    }
326
327
328    function voteBy_sn(catItemId, targetBtn)
329    {
330        var sn = targetBtn.dataset.sn;
331
332        if (sn === 'init') {
333            return initSn(targetBtn);
334        }
335
336        return new Promise(function(resolve, reject) {
337
338            var FD = new FormData();
339            FD.append('catItemId', catItemId);
340            FD.append('sn', targetBtn.dataset.sn);
341            FD.append('voice', targetBtn.dataset.voice);
342            FD.append(
343                document.querySelector('meta[name=csrf-param]').getAttribute('content'),
344                document.querySelector('meta[name=csrf-token]').getAttribute('content'),
345            );
346
347            var xhr = fetch('/vote/voice/set', {
348                method: 'post',
349                body: FD
350            }).then(function(res) {
351                return res.json();
352            }).then(function(json) {
353                if (json.ok === false) {
354                    if (json.need === 'sn-login') {
355
356                        return initSn(targetBtn)
357                    }
358                }
359
360                resolve(json);
361
362            }).catch(function(err) {
363                reject(err);
364            })
365
366        });
367    }
368
369    function createInitExpertBtns(targetBtn, containerId)
370    {
371        var catItemId = targetBtn.closest('.js-vote-buttons').getAttribute('data-cat_item_id');
372        var FD = new FormData();
373        FD.append('catItemId', catItemId);
374        FD.append(
375            document.querySelector('meta[name=csrf-param]').getAttribute('content'),
376            document.querySelector('meta[name=csrf-token]').getAttribute('content'),
377        );
378
379        var xhr = fetch('/vote/voice/expert-available', {
380            method: 'post',
381            body: FD
382        }).then(function(res) {
383            return res.json();
384        }).then(function(json) {
385
386            if (json.ok === false) {
387
388                if (json.need === 'remove-voice') {
389
390                    var div = document.createElement('div');
391
392                    var container = document.getElementById(containerId);
393                    if (container) {
394                        container.innerHTML = 'Вы уже проголосовали за эту работу.';
395                        container.appendChild(div);
396                    }
397                }
398
399                return;
400            }
401
402            var availableVoices = json.availableVoices;
403
404            var div = document.createElement('div');
405            div.classList.add('vote-expert-list-selector');
406
407            var voices = ["3", "4", "5"];
408            if (availableVoices.length) {
409                for (var i = 0; i < voices.length; i++) {
410
411                    var btn = document.createElement('button');
412                    btn.classList.add('app-btn', 'app-btn--outline', 'js-voice-btn', 'vote-star-btn', 'vote-star-btn--' + voices[i]);
413
414                    if (availableVoices.indexOf(voices[i]) === -1) {
415                        btn.classList.add('vote-star-btn--disabled');
416                    } else {
417                        btn.setAttribute('data-voice', voices[i]);
418                        btn.addEventListener('click', function (event) {
419                            event.preventDefault()
420                            event.stopPropagation()
421                            vote('experts', catItemId, this);
422                        });
423                    }
424
425                    btn.innerHTML = '<span class="vote-star-btn__icon vote-star-btn__icon--' + voices[i] + '"></span>';
426
427                    div.appendChild(btn)
428                }
429            } else {
430                div.innerHTML = 'Вы потратили все свои голоса. Если хотите переголосовать, сначала удалите голос у другой работы.';
431            }
432
433            var container = document.getElementById(containerId);
434            if (container) {
435                container.innerHTML = '';
436                container.appendChild(div);
437            }
438
439        }).catch(function(err) {
440            reject(err);
441        })
442    }
443
444    function initExpert(targetBtn)
445    {
446        return new Promise(function(resolve, reject) {
447
448            var containerId = 'expert-voice-buttons-container';
449            var div = document.createElement('div')
450            div.id = containerId;
451            div.innerHTML = 'Загружаем доступные оценки';
452
453            w.voteShowModal('Какую оценку вы хотите поставить этому комиксу?<br><br>', div)
454
455            createInitExpertBtns(targetBtn, containerId);
456
457            // вернем результат - что проголосовать не получилось
458            // silent - без какого-либо уведомления для пользователя
459            resolve({
460                ok: false,
461                need: 'silent',
462            })
463        })
464
465    }
466
467    function voteBy_experts(catItemId, targetBtn)
468    {
469        var voice = targetBtn.dataset.voice;
470
471        if (!voice) {
472            return initExpert(targetBtn)
473        }
474
475        return new Promise(function(resolve, reject) {
476
477            var FD = new FormData();
478            FD.append('catItemId', catItemId);
479            FD.append('voice', targetBtn.dataset.voice);
480            FD.append(
481                document.querySelector('meta[name=csrf-param]').getAttribute('content'),
482                document.querySelector('meta[name=csrf-token]').getAttribute('content'),
483            );
484
485            var xhr = fetch('/vote/voice/set', {
486                method: 'post',
487                body: FD
488            }).then(function(res) {
489                return res.json();
490            }).then(function(json) {
491                if (json.ok === false) {
492                    reject({
493                        ok: false,
494                        message: json.message
495                    })
496                }
497
498                flipExpertVoicePlace(catItemId, 'vote', targetBtn.dataset.voice);
499
500                resolve(json);
501
502            }).catch(function(err) {
503                reject(err);
504            })
505
506        });
507    }
508
509    function vote(voiceType, catItemId, targetBtn)
510    {
511        targetBtn.classList.add('process-voice');
512
513        var votePromise;
514
515        switch (voiceType) {
516            case 'sn':
517                votePromise = voteBy_sn(catItemId, targetBtn);
518                break;
519            case 'experts':
520                votePromise = voteBy_experts(catItemId, targetBtn);
521                break;
522            default:
523                voteError('Неизвестный метод голосования');
524                break;
525        }
526
527        if (!votePromise) {
528            return;
529        }
530
531        votePromise.then(function(res){
532
533            if (res.ok === false) {
534
535                if (res.need === 'silent') {
536                    return;
537                }
538
539                w.voteShowModal(res.message, false, 'error');
540
541            } else if (res.ok) {
542
543                var counterEls = document.querySelectorAll('.js-voice-counter-' + catItemId);
544                if (counterEls.length) {
545                    for (var i = 0; i < counterEls.length; i++) {
546                        counterEls[i].innerHTML = res.data.counts.voicesCount;
547                    }
548                }
549                var counterWordEls = document.querySelectorAll('.js-voice-counter-word-' + catItemId);
550                if(counterWordEls.length) {
551                    for (var i = 0; i < counterWordEls.length; i++) {
552                        counterWordEls[i].innerHTML = res.data.counts.voicesCountWord;
553                    }
554                }
555                var sumEls = document.querySelectorAll('.js-voice-sum-' + catItemId);
556                if (sumEls.length) {
557                    for (var i = 0; i < sumEls.length; i++) {
558                        sumEls[i].innerHTML = res.data.counts.voicesSum;
559                    }
560                }
561                var sumWordEls = document.querySelectorAll('.js-voice-sum-word-' + catItemId)
562                if (sumWordEls.length) {
563                    for (var i = 0; i < sumWordEls.length; i++) {
564                        sumWordEls[i].innerHTML = res.data.counts.voicesSumWord;
565                    }
566                }
567                var voicePlaces = document.querySelectorAll('.js-voice-place-' + catItemId);
568                if (voicePlaces) {
569                    for (var i = 0; i < voicePlaces.length; i++) {
570                        voicePlaces[i].classList.add('voted');
571                    }
572                }
573
574                w.voteShowModal(res.message, false, 'success');
575            } else {
576
577                if (res.message) {
578                    w.voteShowModal(res.message, false, 'error');
579                } else {
580                    w.voteShowModal('Не удалось проголосовать из-за неизвестной ошибки', false, 'error');
581                }
582
583            }
584        });
585
586        votePromise.catch(function(e){
587            voteError('не удалось проголосовать из за ошибки: ' + e.message)
588        });
589
590        votePromise.finally(() => {
591            setTimeout(function(){
592                targetBtn.classList.remove('process-voice');
593            }, 1000)
594        });
595    }
596
597    function flipExpertVoicePlace(catItemId, type, voice)
598    {
599        var placeEls = document.querySelectorAll('.js-expert-voice-place-' + catItemId);
600
601        for (var i = 0; i < placeEls.length; i++) {
602
603            var placeUnvote = placeEls[i].querySelector('.js-expert-voice-place-voices');
604            if (placeUnvote) {
605
606                if (voice) {
607                    var unvoteIconsEl = placeUnvote.querySelector('.vote-star-btn__icon');
608                    if (unvoteIconsEl) {
609                        unvoteIconsEl.classList.remove('vote-star-btn__icon--3', 'vote-star-btn__icon--4', 'vote-star-btn__icon--5');
610                        unvoteIconsEl.classList.add('vote-star-btn__icon--' + voice);
611                    }
612                }
613
614                if (type === 'vote') {
615                    placeUnvote.classList.remove('hidden')
616                } else {
617                    placeUnvote.classList.add('hidden')
618                }
619            }
620
621            var placeVote = placeEls[i].querySelector('.js-expert-voice-place-buttons');
622            if (placeVote) {
623                if (type === 'vote') {
624                    placeVote.classList.add('hidden')
625                } else {
626                    placeVote.classList.remove('hidden')
627                }
628            }
629        }
630    }
631
632    function removeExpertVoice(catItemId, targetBtn)
633    {
634        return new Promise(function(resolve, reject) {
635
636            var FD = new FormData();
637            FD.append('catItemId', catItemId);
638            FD.append(
639                document.querySelector('meta[name=csrf-param]').getAttribute('content'),
640                document.querySelector('meta[name=csrf-token]').getAttribute('content'),
641            );
642
643            fetch('/vote/voice/remove', {
644                method: 'post',
645                body: FD
646            }).then(function(res) {
647                return res.json();
648            }).then(function(json) {
649
650                if (json.ok) {
651
652                    flipExpertVoicePlace(catItemId, 'unvote');
653                    resolve(json);
654                } else {
655                    reject({
656                        ok: false,
657                        message: json.message
658                    })
659                }
660
661            }).catch(function(err) {
662                reject(err);
663            })
664
665        })
666
667    }
668
669    function onVote(event)
670    {
671        if (event.target.classList.contains('js-unvoice-btn')) {
672            event.preventDefault();
673            removeExpertVoice(event.target.closest('.js-vote-buttons').dataset.cat_item_id, event.target)
674            return;
675        } else if (event.target.classList.contains('js-items-results-button')) {
676            event.preventDefault();
677            var link = event.target.closest('.vote-items-page-card');
678            if (link) {
679                location.href = link.href + '#results'
680            }
681            return;
682        } else if (!event.target.classList.contains('js-voice-btn')) {
683            return;
684        }
685
686        event.preventDefault();
687
688        if (event.target.classList.contains('process-voice')) {
689            w.voteShowModal('Пожалуйста дождитесь завершения процесса голосования...');
690            return;
691        }
692
693        var buttonsContainer = event.target.closest('.js-vote-buttons');
694        var catItemId = buttonsContainer.dataset.cat_item_id || '';
695        var voiceType = buttonsContainer.dataset.voice_type || '';
696
697        vote(voiceType, catItemId, event.target);
698    }
699
700    function init(buttonsContainers)
701    {
702        for (var i = 0; i < buttonsContainers.length; i++) {
703            buttonsContainers[i].addEventListener('click', onVote, false);
704        }
705
706        if (buttonsContainers[0].dataset.voice_type === 'sn' && w.localStorage) {
707            var snLoginSuccess = w.localStorage.getItem('snloginsuccess');
708            w.localStorage.removeItem('snloginsuccess');
709            if (snLoginSuccess === '1') {
710                w.voteShowModal('Теперь вы можете проголосовать с помощью выбранного аккаунта');
711            }
712        }
713    }
714
715    w.aicomics_voter = {
716        init: init,
717        vote: vote,
718    }
719
720})(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.