PageSourceSearch

https://sahasenin.com.tr/match-publish-flight.js?v=huexmXY3h10leCRohkZtuvCCPh7juVPRW0i8UkIoxyw

js sahasenin.com.tr collected 2026-10-02 01:09:34 UTC 17,348 bytes, 325 lines download raw bytes

1/* A visual publication interlude. This module never sends or observes notifications. */
2(function (window, document) {
3    'use strict';
4
5    if (window.MatchPublishFlight) return;
6
7    const MINIMUM_DURATION_MS = 10000;
8    const SVG_NS = 'http://www.w3.org/2000/svg';
9    const roles = [
10        { key: 'rival', label: 'Takımlar', x: 14, y: 22, icon: 'team' },
11        { key: 'player', label: 'Oyuncular', x: 86, y: 22, icon: 'player' },
12        { key: 'referee', label: 'Hakemler', x: 50, y: 88, icon: 'whistle' },
13        { key: 'coach', label: 'Antrenörler', x: 14, y: 74, icon: 'coach' },
14        { key: 'audience', label: 'İzleyiciler', x: 86, y: 74, icon: 'audience' },
15        { key: 'private', label: 'Sohbetler', x: 14, y: 22, icon: 'chat', channel: true },
16        { key: 'community', label: 'Topluluklar', x: 86, y: 22, icon: 'community', channel: true }
17    ];
18    const iconPaths = {
19        ball: '<circle cx="12" cy="12" r="9"/><path d="m12 7 4.8 3.5-1.8 5.6H9l-1.8-5.6L12 7Z M12 3v4 M3.5 9.2l3.7 1.3 M6.7 19.3 9 16.1 M17.3 19.3 15 16.1 M20.5 9.2l-3.7 1.3"/>',
20        team: '<circle cx="9" cy="8" r="3"/><path d="M3.5 20v-2a5.5 5.5 0 0 1 11 0v2 M16 5.3a3 3 0 0 1 0 5.5 M17 13a5 5 0 0 1 3.5 4.8V20"/>',
21        player: '<circle cx="12" cy="7" r="3"/><path d="M5 21v-2a7 7 0 0 1 14 0v2 M9 15l3 3 3-3"/>',
22        whistle: '<path d="M15 9h6v4h-5a6.5 6.5 0 1 1-6-7h2v5h3V9Z"/><circle cx="9.5" cy="12.5" r="2.5"/><path d="m15 4 1-2 M19 6l2-1"/>',
23        coach: '<path d="M8 5H5v16h14V5h-3 M9 3h6v4H9z M8 12h3 M8 16h3 M15 11v6 M13 14h4"/>',
24        audience: '<path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z"/><circle cx="12" cy="12" r="3"/>',
25        chat: '<path d="M21 11.5a8.5 8.5 0 0 1-8.5 8.5H4l1.5-4.5A8.5 8.5 0 1 1 21 11.5Z M8 10h8 M8 14h5"/>',
26        community: '<circle cx="12" cy="7" r="3"/><path d="M6 21v-2a6 6 0 0 1 12 0v2 M4 6a3 3 0 0 0 0 6 M2 19v-2a4 4 0 0 1 2.5-3.7 M20 6a3 3 0 0 1 0 6 M22 19v-2a4 4 0 0 0-2.5-3.7"/>',
27        signal: '<path d="M12 19h.01 M8.5 15.5a5 5 0 0 1 7 0 M5 12a10 10 0 0 1 14 0 M2 8.5a14 14 0 0 1 20 0"/>',
28        lock: '<rect x="5" y="10" width="14" height="11" rx="3"/><path d="M8 10V7a4 4 0 0 1 8 0v3 M12 14v3"/>',
29        check: '<path d="m5 12 4.5 4.5L19 7"/>',
30        warning: '<path d="m10.3 4.2-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.7-2.8l-8-14a2 2 0 0 0-3.4 0Z M12 9v4 M12 17h.01"/>'
31    };
32    let activeController = null;
33    let sequence = 0;
34
35    function createElement(tag, className, content) {
36        const element = document.createElement(tag);
37        if (className) element.className = className;
38        if (content !== undefined) element.textContent = content;
39        return element;
40    }
41
42    function icon(name) {
43        const svg = document.createElementNS(SVG_NS, 'svg');
44        svg.setAttribute('viewBox', '0 0 24 24');
45        svg.setAttribute('fill', 'none');
46        svg.setAttribute('stroke', 'currentColor');
47        svg.setAttribute('stroke-width', '1.6');
48        svg.setAttribute('stroke-linecap', 'round');
49        svg.setAttribute('stroke-linejoin', 'round');
50        svg.setAttribute('aria-hidden', 'true');
51        svg.setAttribute('focusable', 'false');
52        // These paths are constant, local artwork; user values only use textContent.
53        svg.innerHTML = iconPaths[name] || iconPaths.signal;
54        return svg;
55    }
56
57    function normalizeCategories(categories) {
58        if (categories === undefined || categories === null) return new Set(['rival', 'player', 'referee']);
59        const aliases = { team: 'rival', teams: 'rival', rivals: 'rival', players: 'player', referees: 'referee', coaches: 'coach', spectators: 'audience', chats: 'private', communities: 'community' };
60        const supplied = Array.isArray(categories)
61            ? categories
62            : Object.keys(categories).filter(key => categories[key] === true);
63        return new Set(supplied.map(key => aliases[key] || key).filter(key => roles.some(role => role.key === key)));
64    }
65
66    function addConnection(svg, role, selected) {
67        const x = role.x * 6;
68        const y = role.y * 5;
69        const route = role.x === 50
70            ? `M300 225 C300 305 300 350 ${x} ${y}`
71            : `M300 225 C${x} 225 300 ${y} ${x} ${y}`;
72        const group = document.createElementNS(SVG_NS, 'g');
73        group.setAttribute('class', `mpf-route mpf-role-${role.key}${selected ? '' : ' is-muted'}`);
74        ['mpf-route-track', 'mpf-route-light', 'mpf-route-packet'].forEach(className => {
75            const path = document.createElementNS(SVG_NS, 'path');
76            path.setAttribute('d', route);
77            path.setAttribute('pathLength', '100');
78            path.setAttribute('class', className);
79            path.setAttribute('vector-effect', 'non-scaling-stroke');
80            group.appendChild(path);
81        });
82        svg.appendChild(group);
83    }
84
85    function createNetwork(categories, facility) {
86        const network = createElement('div', 'mpf-network');
87        // Decorative network: announcements come from the real request status below.
88        network.setAttribute('aria-hidden', 'true');
89        network.appendChild(createElement('div', 'mpf-pitch'));
90        network.appendChild(createElement('div', 'mpf-orbit mpf-orbit-outer'));
91        network.appendChild(createElement('div', 'mpf-orbit mpf-orbit-inner'));
92
93        const paths = document.createElementNS(SVG_NS, 'svg');
94        paths.setAttribute('class', 'mpf-paths');
95        paths.setAttribute('viewBox', '0 0 600 500');
96        paths.setAttribute('preserveAspectRatio', 'none');
97        paths.setAttribute('focusable', 'false');
98        network.appendChild(paths);
99
100        const hasNearbyRole = roles.some(role => !role.channel && categories.has(role.key));
101        const sharingOnly = !hasNearbyRole && (categories.has('private') || categories.has('community'));
102        // Without nearby recipients, show only the selected sharing channels, never unrelated roles.
103        // Nearby mode keeps the familiar three roles and adds at most two optional recipient roles.
104        const visibleRoles = sharingOnly
105            ? roles.filter(role => role.channel && categories.has(role.key))
106            : roles.filter((role, index) => !role.channel && (index < 3 || categories.has(role.key)));
107        visibleRoles.forEach(definition => {
108            const role = sharingOnly && visibleRoles.length === 1 ? { ...definition, x: 50, y: 88 } : definition;
109            const selected = categories.has(role.key);
110            addConnection(paths, role, selected);
111            const node = createElement('div', `mpf-role mpf-role-${role.key}${selected ? '' : ' is-muted'}`);
112            node.style.setProperty('--mpf-x', `${role.x}%`);
113            node.style.setProperty('--mpf-y', `${role.y}%`);
114            const nodeIcon = createElement('span', 'mpf-role-icon');
115            nodeIcon.appendChild(icon(role.icon));
116            node.appendChild(nodeIcon);
117            node.appendChild(createElement('span', 'mpf-role-label', role.label));
118            node.appendChild(createElement('span', 'mpf-role-receipt', selected ? (role.channel ? 'Seçili kanal' : 'Seçili grup') : 'Seçili değil'));
119            network.appendChild(node);
120        });
121
122        const source = createElement('div', 'mpf-source');
123        const sourceIcon = createElement('span', 'mpf-source-icon');
124        sourceIcon.appendChild(icon('ball'));
125        source.appendChild(sourceIcon);
126        source.appendChild(createElement('span', 'mpf-source-kicker', 'OYUN BURADA BAŞLAR'));
127        source.appendChild(createElement('strong', 'mpf-source-title', 'Maç ilanın'));
128        const venue = createElement('span', 'mpf-source-venue', facility || 'Sahada buluşalım');
129        venue.title = facility || 'Sahada buluşalım';
130        source.appendChild(venue);
131        const sourceSignal = createElement('span', 'mpf-source-signal');
132        sourceSignal.appendChild(icon('signal'));
133        source.appendChild(sourceSignal);
134        network.appendChild(source);
135
136        if (!categories.size) network.classList.add('is-notifications-off');
137        return network;
138    }
139
140    function start(options) {
141        if (activeController) return activeController;
142        const settings = options || {};
143        const categories = normalizeCategories(settings.categories);
144        const facility = typeof settings.facility === 'string' ? settings.facility.trim().slice(0, 180) : '';
145        const id = `match-publish-flight-${++sequence}`;
146        const previouslyFocused = document.activeElement;
147        const overlay = createElement('dialog', 'match-publish-flight');
148        overlay.id = id;
149        overlay.setAttribute('role', 'dialog');
150        overlay.setAttribute('aria-modal', 'true');
151        overlay.setAttribute('aria-labelledby', `${id}-title`);
152        overlay.setAttribute('aria-describedby', `${id}-description ${id}-status ${id}-note`);
153        overlay.setAttribute('aria-busy', 'true');
154        overlay.tabIndex = -1;
155
156        const panel = createElement('div', 'mpf-panel');
157        const header = createElement('header', 'mpf-header');
158        const eyebrow = createElement('span', 'mpf-eyebrow');
159        eyebrow.appendChild(createElement('span', 'mpf-brand-mark'));
160        eyebrow.appendChild(createElement('span', '', 'SAHASENİN'));
161        eyebrow.appendChild(createElement('span', 'mpf-eyebrow-separator', '/'));
162        eyebrow.appendChild(createElement('span', '', 'BİLDİRİM AKIŞI'));
163        header.appendChild(eyebrow);
164        const title = createElement('h2', 'mpf-title', categories.size ? 'Bildirimler hedefine ulaşıyor.' : 'Maç ilanın sahaya çıkıyor.');
165        title.id = `${id}-title`;
166        header.appendChild(title);
167        const description = createElement('p', 'mpf-description', categories.size
168            ? 'Bir maç ilanı. Aynı anda kurulan yeni bağlantılar.'
169            : 'Bildirimler kapalı. İlanın yayın ayarlarıyla hazırlanıyor.');
170        description.id = `${id}-description`;
171        header.appendChild(description);
172        panel.appendChild(header);
173
174        panel.appendChild(createNetwork(categories, facility));
175
176        const footer = createElement('footer', 'mpf-footer');
177        const status = createElement('div', 'mpf-status');
178        status.id = `${id}-status`;
179        status.setAttribute('role', 'status');
180        status.setAttribute('aria-live', 'polite');
181        status.setAttribute('aria-atomic', 'true');
182        const statusIcon = createElement('span', 'mpf-status-icon');
183        statusIcon.setAttribute('aria-hidden', 'true');
184        statusIcon.appendChild(createElement('span', 'mpf-status-spinner'));
185        const statusCopy = createElement('div', 'mpf-status-copy');
186        const statusTitle = createElement('strong', 'mpf-status-title', 'İlanın y
186ayınlanıyor');
187        const statusDescription = createElement('span', 'mpf-status-description', 'İşlem tamamlandığında otomatik devam edeceğiz.');
188        statusCopy.appendChild(statusTitle);
189        statusCopy.appendChild(statusDescription);
190        status.appendChild(statusIcon);
191        status.appendChild(statusCopy);
192        footer.appendChild(status);
193
194        const note = createElement('p', 'mpf-note');
195        note.id = `${id}-note`;
196        note.appendChild(icon('lock'));
197        note.appendChild(createElement('span', '', 'Bu görsel bildirim akışını temsil eder; teslimat teyidi değildir.'));
198        footer.appendChild(note);
199        panel.appendChild(footer);
200        overlay.appendChild(panel);
201
202        let outcome = null;
203        let ready = false;
204        let closed = false;
205        let completionTimer = null;
206        let waitingTimer = null;
207        let resolveFinished;
208        const finished = new Promise(resolve => { resolveFinished = resolve; });
209        const clock = () => window.performance && typeof window.performance.now === 'function' ? window.performance.now() : Date.now();
210        let startedAt;
211        const fallbackInert = [];
212
213        function focusOverlay() {
214            if (closed || document.activeElement === overlay) return;
215            try { overlay.focus({ preventScroll: true }); }
216            catch (_) { overlay.focus(); }
217        }
218
219        function handleKeydown(event) {
220            if (closed) return;
221            if (event.key === 'Escape' || event.key === 'Tab') {
222                event.preventDefault();
223                event.stopImmediatePropagation();
224                focusOverlay();
225            }
226        }
227
228        function handleFocus(event) {
229            if (!closed && !overlay.contains(event.target)) focusOverlay();
230        }
231
232        function handleCancel(event) {
233            event.preventDefault();
234            event.stopPropagation();
235        }
236
237        function setOutcomeState(result) {
238            overlay.dataset.outcome = result;
239            overlay.setAttribute('aria-busy', 'false');
240            statusIcon.replaceChildren(icon(result === 'success' ? 'check' : 'warning'));
241            statusTitle.textContent = result === 'success' ? 'Maç ilanın yayınlandı' : 'İlan henüz yayınlanamadı';
242            statusDescription.textContent = result === 'success'
243                ? 'Yayın tamamlandı. Akış görseli tamamlanıyor.'
244                : 'İlan bilgilerine geri döndüğünde yeniden deneyebilirsin.';
245            if (result === 'error') {
246                title.textContent = 'İlan henüz yayınlanamadı.';
247                description.textContent = 'Bir sorun oluştu. Birazdan ilan bilgilerine geri döneceksin.';
248            }
249        }
250
251        function finish(result) {
252            if (result !== 'success' && result !== 'error') throw new TypeError('MatchPublishFlight.finish expects success or error.');
253            if (outcome || closed) return finished;
254            // Only the caller's actual publication result may settle the visual interlude.
255            outcome = result;
256            window.clearTimeout(waitingTimer);
257            setOutcomeState(result);
258            function completeWhenReady() {
259                if (closed) return;
260                // Recheck the monotonic clock: browser timer rounding must not shorten 10s.
261                const remaining = MINIMUM_DURATION_MS - (clock() - startedAt);
262                if (remaining > 0) {
263                    completionTimer = window.setTimeout(completeWhenReady, Math.ceil(remaining));
264                    return;
265                }
266                ready = true;
267                overlay.dataset.ready = 'true';
268                resolveFinished(result);
269            }
270            const remaining = Math.max(0, Math.ceil(MINIMUM_DURATION_MS - (clock() - startedAt)));
271            completionTimer = window.setTimeout(completeWhenReady, remaining);
272            return finished;
273        }
274
275        function close() {
276            // There is intentionally no forced-close option, cancel button, or timeout settlement.
277            if (!ready || closed) return false;
278            closed = true;
279            window.clearTimeout(completionTimer);
280            window.clearTimeout(waitingTimer);
281            document.removeEventListener('keydown', handleKeydown, true);
282            document.removeEventListener('focusin', handleFocus, true);
283            overlay.removeEventListener('cancel', handleCancel);
284            if (typeof overlay.close === 'function' && overlay.open) overlay.close();
285            overlay.remove();
286            fallbackInert.forEach(item => {
287                if (!item.wasInert && item.element.isConnected) item.element.removeAttribute('inert');
288            });
289            document.body.classList.remove('match-publish-flight-open');
290            activeController = null;
291            if (previouslyFocused && previouslyFocused.isConnected && typeof previouslyFocused.focus === 'function') {
292                try { previouslyFocused.focus({ preventScroll: true }); }
293                catch (_) { previouslyFocused.focus(); }
294            }
295            return true;
296        }
297
298        const controller = Object.freeze({ finish, close, finished });
299        activeController = controller;
300        document.body.appendChild(overlay);
301        document.body.classList.add('match-publish-flight-open');
302        overlay.addEventListener('cancel', handleCancel);
303        document.addEventListener('keydown', handleKeydown, true);
304        document.addEventListener('focusin', handleFocus, true);
305        try {
306            if (typeof overlay.showModal !== 'function') throw new Error('Native dialog unavailable');
307            // Top-layer isolation is limited to this non-interactive publication interlude.
308            overlay.showModal();
309        } catch (_) {
310            overlay.setAttribute('open', '');
311            Array.from(document.body.children).filter(child => child !== overlay).forEach(element => {
312                fallbackInert.push({ element, wasInert: element.hasAttribute('inert') });
313                element.setAttribute('inert', '');
314            });
315        }
316        focusOverlay();
317        startedAt = clock();
318        waitingTimer = window.setTimeout(function () {
319            if (!outcome && !closed) statusDescription.textContent = 'İşlem devam ediyor. Lütfen bu ekranı açık tut.';
320        }, 16000);
321        return controller;
322    }
323
324    window.MatchPublishFlight = Object.freeze({ start, minimumDurationMs: MINIMUM_DURATION_MS });
325})(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.