PageSourceSearch

https://pythonmagazine.com/theme/netcity_postype/js/rara-htmlopt-20260929-tail-sub-1-97b34fe4473b.js

js pythonmagazine.com collected 2026-10-01 14:41:29 UTC 7,024 bytes, 231 lines download raw bytes

1
2(function() {
3    var memoLinks = Array.prototype.slice.call(document.querySelectorAll('.win_memo[data-nc-memo-unread]'));
4
5    if (!memoLinks.length) {
6        return;
7    }
8
9    var firstLink = memoLinks[0];
10    var memberKey = firstLink.getAttribute('data-nc-memo-key') || 'member';
11    var checkUrl = firstLink.getAttribute('data-nc-memo-check-url') || '';
12    var storeKey = 'ncPostypeMemoUnread:' + memberKey;
13    var soundPending = false;
14    var queuedSoundCount = 0;
15    var pollTimer = null;
16
17    function toCount(value) {
18        var count = parseInt(value, 10);
19        return isNaN(count) || count < 0 ? 0 : count;
20    }
21
22    function readStoredCount() {
23        try {
24            return toCount(window.localStorage.getItem(storeKey));
25        } catch (e) {
26            return 0;
27        }
28    }
29
30    function writeStoredCount(count) {
31        try {
32            window.localStorage.setItem(storeKey, String(toCount(count)));
33        } catch (e) {
34        }
35    }
36
37    function formatCount(count) {
38        return String(count).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
39    }
40
41    function updateMemoBadges(count) {
42        count = toCount(count);
43        memoLinks.forEach(function(link) {
44            var badge = link.querySelector('.nc-top-auth__badge, .nc-mobile-memo__badge');
45            var badgeClass = link.classList.contains('nc-mobile-memo') ? 'nc-mobile-memo__badge' : 'nc-top-auth__badge';
46
47            link.setAttribute('data-nc-memo-unread', String(count));
48            link.classList.toggle('is-unread', count > 0);
49            link.setAttribute('aria-label', count > 0 ? '쪽지함 안 읽은 쪽지 ' + count + '개' : '쪽지함');
50
51            if (count > 0) {
52                if (!badge) {
53                    badge = document.createElement('strong');
54                    badge.className = badgeClass;
55                    link.appendChild(badge);
56                }
57                badge.textContent = formatCount(count);
58            } else if (badge) {
59                badge.parentNode.removeChild(badge);
60            }
61        });
62    }
63
64    function playMemoTone() {
65        return new Promise(function(resolve, reject) {
66            var AudioContext = window.AudioContext || window.webkitAudioContext;
67
68            if (!AudioContext) {
69                reject(new Error('AudioContext unavailable'));
70                return;
71            }
72
73            var context = new AudioContext();
74
75            function startTone() {
76                var now = context.currentTime;
77                var gain = context.createGain();
78                var first = context.createOscillator();
79                var second = context.createOscillator();
80
81                gain.gain.setValueAtTime(0.0001, now);
82                gain.gain.exponentialRampToValueAtTime(0.16, now + 0.025);
83                gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.38);
84
85                first.type = 'sine';
86                first.frequency.setValueAtTime(880, now);
87                first.frequency.exponentialRampToValueAtTime(990, now + 0.16);
88
89                second.type = 'sine';
90                second.frequency.setValueAtTime(1175, now + 0.12);
91
92                first.connect(gain);
93                second.connect(gain);
94                gain.connect(context.destination);
95
96                first.start(now);
97                first.stop(now + 0.22);
98                second.start(now + 0.12);
99                second.stop(now + 0.38);
100
101                window.setTimeout(function() {
102                    if (context.close) {
103                        context.close();
104                    }
105                    resolve();
106                }, 460);
107            }
108
109            if (context.state === 'suspended' && context.resume) {
110                context.resume().then(startTone).catch(function(error) {
111                    if (context.close) {
112                        context.close();
113                    }
114                    reject(error);
115                });
116                return;
117            }
118
119            try {
120                startTone();
121            } catch (error) {
122                if (context.close) {
123                    context.close();
124                }
125                reject(error);
126            }
127        });
128    }
129
130    function queueGesturePlayback(count) {
131        if (queuedSoundCount >= count) {
132            return;
133        }
134
135        queuedSoundCount = count;
136        var played = false;
137
138        function cleanup() {
139            document.removeEventListener('pointerdown', onGesture, true);
140            document.removeEventListener('keydown', onGesture, true);
141            document.removeEventListener('touchstart', onGesture, true);
142        }
143
144        function onGesture() {
145            if (played) {
146                return;
147            }
148            played = true;
149            playMemoTone().then(function() {
150                writeStoredCount(count);
151                queuedSoundCount = 0;
152                cleanup();
153            }).catch(function() {
154                cleanup();
155            });
156        }
157
158        document.addEventListener('pointerdown', onGesture, true);
159        document.addEventListener('keydown', onGesture, true);
160        document.addEventListener('touchstart', onGesture, true);
161    }
162
163    function notifyIfNeeded(count) {
164        count = toCount(count);
165        updateMemoBadges(count);
166
167        var lastCount = readStoredCount();
168
169        if (count <= 0) {
170            queuedSoundCount = 0;
171            writeStoredCount(0);
172            return;
173        }
174
175        if (count < lastCount) {
176            queuedSoundCount = 0;
177            writeStoredCount(count);
178            return;
179        }
180
181        if (count <= lastCount || soundPending) {
182            return;
183        }
184
185        soundPending = true;
186        playMemoTone().then(function() {
187            writeStoredCount(count);
188            soundPending = false;
189        }).catch(function() {
190            queueGesturePlayback(count);
191            soundPending = false;
192        });
193    }
194
195    function pollMemoCount() {
196        if (!checkUrl || !window.fetch || document.hidden) {
197            return;
198        }
199
200        fetch(checkUrl, {
201            credentials: 'same-origin',
202            cache: 'no-store',
203            headers: {
204                'Accept': 'application/json'
205            }
206        }).then(function(response) {
207            return response.json();
208        }).then(function(data) {
209            if (!data || data.ok === false) {
210                return;
211            }
212            notifyIfNeeded(data.count);
213        }).catch(function() {
214        });
215    }
216
217    var initialCount = memoLinks.reduce(function(max, link) {
218        return Math.max(max, toCount(link.getAttribute('data-nc-memo-unread')));
219    }, 0);
220
221    notifyIfNeeded(initialCount);
222
223    if (checkUrl && window.fetch) {
224        pollTimer = window.setInterval(pollMemoCount, 45000);
225        document.addEventListener('visibilitychange', function() {
226            if (!document.hidden) {
227                pollMemoCount();
228            }
229        });
230    }
231})();

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.