';
var formFirstChild = loginCard.firstElementChild;
if (formFirstChild) {
loginCard.insertBefore(ssoBox, formFirstChild);
} else {
loginCard.appendChild(ssoBox);
}
var registerLinks = document.querySelectorAll('a[href*="/signup"], a[href*="/register"]');
registerLinks.forEach(function (l) {
l.href = 'https://auth.yakhub.com.tr/register';
l.target = '_blank';
l.innerText = 'YakNet Hesabı Oluştur';
});
var formInputs = loginCard.querySelector('.form-group, .inputs, form') || loginCard.querySelector('input');
var containerToHide = formInputs ? formInputs.closest('form') || formInputs.parentElement : null;
if (containerToHide && !isLocalLogin) {
var subElements = Array.from(loginCard.children).filter(function (el) {
return (
el.id !== 'yaknet-login-section' &&
!el.classList.contains('title') &&
!el.classList.contains('page-title')
);
});
subElements.forEach(function (el) {
el.style.display = 'none';
});
var showAdminBtn = document.getElementById('yaktube-show-admin-login');
if (showAdminBtn) {
showAdminBtn.addEventListener('click', function (e) {
e.preventDefault();
openLocalAdminLoginModal();
});
}
}
}
}
// 3. Intelligent Branding & A11y DOM Auto-Fixer
function autoFixDOM() {
injectAccessiblePlayerBar();
// Check if on 403 / unauthorized page and provide admin login button
var notAuthorizedEl = document.querySelector('my-not-found, .not-found, .forbidden, my-forbidden, .error-page');
if (notAuthorizedEl && !document.getElementById('yaktube-403-admin-btn')) {
var btn403 = document.createElement('div');
btn403.id = 'yaktube-403-admin-btn';
btn403.style.margin = '20px auto';
btn403.style.textAlign = 'center';
btn403.innerHTML =
'';
btn403.querySelector('button').addEventListener('click', openLocalAdminLoginModal);
notAuthorizedEl.appendChild(btn403);
}
injectYakNetSSO();
// Check auth status for login button rendering
var tokenVal = localStorage.getItem('access_token');
var userVal = localStorage.getItem('username');
var isAlreadyAuth = !!tokenVal && !!userVal;
if (!isAlreadyAuth) {
// Replace all login buttons labels and titles ONLY for guests
document
.querySelectorAll(
'my-login-link, a[href*="/login"], a[title*="Giriş sayfasına git"], [aria-label*="Giriş sayfasına git"]'
)
.forEach(function (el) {
if (el.getAttribute('title') && el.getAttribute('title').includes('Giriş sayfasına git')) {
el.setAttribute('title', 'YakNet ile Giriş Yap');
}
if (el.getAttribute('aria-label') && el.getAttribute('aria-label').includes('Giriş sayfasına git')) {
el.setAttribute('aria-label', 'YakNet ile Giriş Yap');
}
var anchor = el.tagName.toLowerCase() === 'a' ? el : el.querySelector('a');
if (anchor) {
anchor.setAttribute('title', 'YakNet ile Giriş Yap');
anchor.setAttribute('aria-label', 'YakNet ile Giriş Yap');
var spans = anchor.querySelectorAll('span');
spans.forEach(function (s) {
if (
s.textContent.includes('Giriş sayfasına git') ||
s.textContent.trim() === 'Giriş' ||
s.textContent.trim() === 'Giriş yap'
) {
s.textContent = 'YakNet ile Giriş Yap';
}
});
}
});
}
// Aggressively remove generic Framasoft Open-in-App / Play Store banners
document
.querySelectorAll(
'.mobile-msg, [class*="mobile-msg"], button[title*="Bu mesajı kapat"], button[aria-label*="Bu mesajı kapat"]'
)
.forEach(function (m) {
m.remove();
});
var genericAppBanners = document.querySelectorAll(
'my-open-app, my-client-app-banner, .open-app-container, .app-banner, [class*="open-app"], [class*="open-in-app"], .mobile-app-banner, my-open-in-app, .app-promotion, a[href*="play.google.com/store/apps/details?id=app.joinpeertube"], a[href*="f-droid.org"]'
);
genericAppBanners.forEach(function (banner) {
banner.setAttribute('aria-hidden', 'true');
banner.setAttribute('tabindex', '-1');
if (banner.parentElement) {
try {
banner.remove();
} catch (e) {
banner.style.display = 'none';
}
}
});
// Replace leftover PeerTube branding in visible UI texts
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
var node;
while ((node = walker.nextNode())) {
var val = node.nodeValue;
if (val && (val.includes('PeerTube') || val.includes('Peertube'))) {
var parentTag = node.parentElement ? node.parentElement.tagName : '';
var parentElem = node.parentElement;
if (
parentElem &&
(parentElem.closest('#yaktube-ondemand-container') ||
parentElem.closest('#yaktube-live-watch-container') ||
parentElem.tagName === 'SCRIPT' ||
parentElem.tagName === 'STYLE')
) {
continue;
}
if (true) {
node.nodeValue = val
.replace(/PeerTube hakkında/gi, 'YakTube Hakkında')
.replace(/PeerTube'a katılın/gi, "YakTube'a Katılın")
.replace(/PeerTube örneği/gi, 'YakTube Sunucusu')
.replace(/PeerTube/g, 'YakTube')
.replace(/Giriş sayfasına git/gi, 'YakNet ile Giriş Yap');
}
}
}
// Semantic Headings (H3 for video titles, H2 for sections)
document
.querySelectorAll('.video-miniature, my-video-miniature, .video-card, .yaktube-card, [class*="miniature"]')
.forEach(function (card) {
var titleElement = card.querySelector(
'.video-miniature-name, .video-name, .yaktube-video-name, a[title], .title'
);
if (titleElement) {
if (!['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(titleElement.tagName)) {
titleElement.setAttribute('role', 'heading');
titleElement.setAttribute('aria-level', '3');
}
}
});
document
.querySelectorAll('.section-title, .sub-header, .videos-header, .page-header, .yaktube-title-h3')
.forEach(function (sec) {
if (!['H1', 'H2'].includes(sec.tagName)) {
sec.setAttribute('role', 'heading');
sec.setAttribute('aria-level', '2');
}
});
// Make native PeerTube comment authors accessible as H3 headings for screen readers
document
.querySelectorAll('my-video-comment, .video-comment, my-video-comment-thread, .comment, .comment-thread')
.forEach(function (commentEl) {
var authorEl = commentEl.querySelector(
'.display-name, .actor-name, .comment-author, a.actor-link, .video-comment-header-left a, .video-comment-header-left span'
);
if (
authorEl &&
!['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(authorEl.tagName) &&
!authorEl.getAttribute('role')
) {
authorEl.setAttribute('role', 'heading');
authorEl.setAttribute('aria-level', '3');
}
});
// Auto-fix unlabeled buttons
document.querySelectorAll('button:not([data-a11y-fixed])').forEach(function (btn) {
btn.setAttribute('data-a11y-fixed', 'true');
var text = (btn.textContent || '').trim();
var hasAria = btn.getAttribute('aria-label') || btn.getAttribute('title');
if (!text && !hasAria) {
var html = btn.innerHTML.toLowerCase();
var className = (btn.className || '').toLowerCase();
var icon = btn.querySelector('my-global-icon, i, svg, [class*="icon-"]');
var iconName = icon ? icon.className || icon.getAttribute('name') || '' : '';
if (html.includes('search') || className.includes('search') || iconName.includes('search')) {
btn.setAttribute('aria-label', 'Ara');
} else if (html.includes('bell') || className.includes('notification') || iconName.includes('bell')) {
btn.setAttribute('aria-label', 'Bildirimler');
} else if (html.includes('cog') || html.includes('setting') || className.includes('setting')) {
btn.setAttribute('aria-label', 'Ayarlar');
} else if (
html.includes('menu') ||
html.includes('bars') ||
className.includes('burger') ||
className.includes('menu')
) {
btn.setAttribute('aria-label', 'Ana Gezinme Menüsü');
} else if (html.includes('play') || className.includes('play')) {
btn.setAttribute('aria-label', 'Oynat');
} else if (html.includes('pause') || className.includes('pause')) {
btn.setAttribute('aria-label', 'Duraklat');
} else if (html.includes('volume') || html.includes('mute') || className.includes('volume')) {
btn.setAttribute('aria-label', 'Ses Kontrolü');
} else if (html.includes('fullscreen') || className.includes('fullscreen')) {
btn.setAttribute('aria-label', 'Tam Ekran');
} else if (
html.includes('ellipsis') ||
html.includes('more') ||
className.includes('more') ||
className.includes('action')
) {
btn.setAttribute('aria-label', 'Diğer İşlemler ve Seçenekler');
} else if (html.includes('trash') || html.includes('delete') || className.includes('delete')) {
btn.setAttribute('aria-label', 'Sil');
} else if (html.includes('edit') || className.includes('edit')) {
btn.setAttribute('aria-label', 'Düzenle');
} else if (html.includes('share') || className.includes('share')) {
btn.setAttribute('aria-label', 'Paylaş');
} else if (html.includes('thumb-up') || html.includes('like') || className.includes('like')) {
btn.setAttribute('aria-label', 'Beğen');
} else if (html.includes('thumb-down') || html.includes('dislike') || className.includes('dislike')) {
btn.setAttribute('aria-label', 'Beğenme');
} else if (html.includes('filter') || className.includes('filter')) {
btn.setAttribute('aria-label', 'Filtreler');
} else if (html.includes('close') || html.includes('times') || className.includes('close')) {
btn.setAttribute('aria-label', 'Kapat');
} else if (btn.closest('.vjs-control-bar')) {
btn.setAttribute('aria-label', 'Oynatıcı Düğmesi');
} else {
var parent = btn.parentElement;
if (parent && parent.title) {
btn.setAttribute('aria-label', parent.title);
} else {
btn.setAttribute('aria-label', 'Düğme');
}
}
}
});
// Auto-fix unlabeled images
document.querySelectorAll('img:not([data-a11y-fixed])').forEach(function (img) {
img.setAttribute('data-a11y-fixed', 'true');
var alt = img.getAttribute('alt');
if (!alt || alt.trim() === '') {
var card = img.closest('.video-miniature, .video-card, .yaktube-card, [class*="miniature"]');
if (card) {
var titleEl = card.querySelector(
'.video-miniature-name, .video-name, .yaktube-video-name, h3, h4, a[title]'
);
var titleText = titleEl ? (titleEl.textContent || titleEl.getAttribute('title') || '').trim() : '';
if (titleText) {
img.setAttribute('alt', titleText + ' video kapağı');
return;
}
}
var avatarContainer = img.closest('.actor-avatar, .channel-avatar, .avatar, [class*="avatar"]');
if (avatarContainer) {
var nameEl = avatarContainer.parentElement
? avatarContainer.parentElement.querySelector('.name, .actor-name, .channel-name')
: null;
var nameText = nameEl ? (nameEl.textContent || '').trim() : '';
img.setAttribute('alt', (nameText ? nameText + ' ' : '') + 'Profil Resmi');
return;
}
if (img.src && (img.src.includes('logo') || img.className.includes('logo'))) {
img.setAttribute('alt', getInstanceName() + ' Logo');
return;
}
var parentLink = img.closest('a');
if (parentLink && parentLink.textContent && parentLink.textContent.trim().length > 0) {
img.setAttribute('alt', '');
} else if (parentLink && parentLink.getAttribute('title')) {
img.setAttribute('alt', parentLink.getAttribute('title'));
} else {
img.setAttribute('alt', 'Görsel');
}
}
});
// Auto-fix unlabeled links
document.querySelectorAll('a:not([data-a11y-fixed])').forEach(function (a) {
a.setAttribute('data-a11y-fixed', 'true');
var text = (a.textContent || '').trim();
var hasAria = a.getAttribute('aria-label') || a.getAttribute('title');
if (!text && !hasAria) {
var img = a.querySelector('img');
if (img && img.alt) {
a.setAttribute('aria-label', img.alt);
} else if (a.href) {
if (a.href.includes('/videos/watch/')) {
a.setAttribute('aria-label', 'Videoyu İzle');
} else if (a.href.includes('/video-channels/')) {
a.setAttribute('aria-label', 'Kanal Sayfasına Git');
} else if (a.href.includes('/accounts/')) {
a.setAttribute('aria-label', 'Hesap Profiline Git');
} else if (a.href.includes('/videos/search')) {
a.setAttribute('aria-label', 'Arama Sayfası');
} else {
a.setAttribute('aria-label', 'Bağlantı');
}
}
}
});
// Auto-fix search inputs
document.querySelectorAll('input:not([data-a11y-fixed])').forEach(function (inp) {
inp.setAttribute('data-a11y-fixed', 'true');
if (!inp.getAttribute('aria-label') && !inp.getAttribute('placeholder') && !(inp.labels && inp.labels.length)) {
var type = (inp.getAttribute('type') || 'text').toLowerCase();
if (type === 'search') {
inp.setAttribute('aria-label', getInstanceName() + ' Video Arama');
} else if (type === 'checkbox') {
inp.setAttribute('aria-label', 'Seçim Kutusu');
} else if (type === 'password') {
inp.setAttribute('aria-label', 'Åifre');
}
}
});
}
setInterval(autoFixDOM, 1000);
// 3. Screen-Reader & Turkish Q Optimized Shortcuts & Player Bar (v1.4.1)
function toggleShortcutsModal() {
var modal = document.getElementById('yaktube-shortcuts-dialog');
if (modal) {
if (modal.style.display === 'none' || !modal.style.display) {
modal.style.display = 'flex';
announce('Klavye kısayolları yardım penceresi açıldı. Kapatmak için Escape tuşuna basın.');
var closeBtn = document.getElementById('yaktube-shortcuts-close');
if (closeBtn) closeBtn.focus();
} else {
modal.style.display = 'none';
announce('Yardım penceresi kapatıldı.');
}
} else {
modal = document.createElement('div');
modal.id = 'yaktube-shortcuts-dialog';
modal.className = 'yaktube-modal-overlay';
modal.setAttribute('role', 'dialog');
modal.setAttribute('aria-modal', 'true');
modal.setAttribute('aria-labelledby', 'yaktube-shortcuts-title');
modal.innerHTML =
'
' +
'
' +
'
⌨️ Klavye Kısayolları (Ekran Okuyucu Uyumlu)
' +
'' +
'
' +
'
💡 Ekran Okuyucu Notu: Sanal imleç (Browse mode) açıkken kısayolları Alt tuşuyla birlikte kullanabilir veya video oynatıcı butonlarından faydalanabilirsiniz.
' +
'
' +
'
Kısayol (Evrensel / Tek Tuş)
İşlev
' +
'' +
'
Alt + K veya Space
Oynat / Duraklat
' +
'
Alt + J veya J
10 saniye geri sar
' +
'
Alt + L veya L
10 saniye ileri sar
' +
'
Alt + Yukarı / Aşağı
Sesi %5 artır / azalt
' +
'
Alt + M veya M
Sesi Kapat / Aç (Mute)
' +
'
Alt + T veya T
Sinema / Tiyatro Modu
' +
'
Alt + I veya I
Hızlı Video Bilgisi & İstatistikler
' +
'
Alt + C veya C
Altyazıları Aç / Kapat
' +
'
Alt + . / Alt + ,
Oynatma hızını artır / azalt
' +
'
Alt + S veya / (Shift+7)
Arama penceresini aç
' +
'
Alt + V veya Shift + V
Sesli aramayı başlat
' +
'
Alt + H veya F1 veya ?
Bu kılavuzu aç / kapat
' +
'' +
'
' +
'
';
document.body.appendChild(modal);
var closeBtn = modal.querySelector('#yaktube-shortcuts-close');
if (closeBtn) {
closeBtn.addEventListener('click', function () {
modal.style.display = 'none';
});
}
modal.addEventListener('click', function (e) {
if (e.target === modal) modal.style.display = 'none';
});
announce('Klavye kısayolları yardım penceresi açıldı. Kapatmak için Escape tuşuna basın.');
}
}
function isInputFocused() {
var el = document.activeElement;
if (!el) return false;
var tag = el.tagName ? el.tagName.toUpperCase() : '';
return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;
}
function getVideoElement() {
// 1. Direct video tag in DOM
var v = document.querySelector('video');
if (v) return v;
// 2. Video inside iframes (e.g. embed player)
var iframes = document.querySelectorAll('iframe');
for (var i = 0; i < iframes.length; i++) {
try {
var doc = iframes[i].contentDocument || (iframes[i].contentWindow && iframes[i].contentWindow.document);
if (doc) {
var iv = doc.querySelector('video');
if (iv) return iv;
}
} catch (e) {}
}
return null;
}
function formatTime(seconds) {
if (isNaN(seconds) || seconds < 0) return '0 saniye';
var mins = Math.floor(seconds / 60);
var secs = Math.floor(seconds % 60);
if (mins === 0) return secs + ' saniye';
return mins + ' dakika ' + secs + ' saniye';
}
function showQuickVideoInfo() {
var video = getVideoElement();
if (!video) {
announce('Şu anda aktif oynatılan bir video bulunmuyor.');
return;
}
var titleEl = document.querySelector('h1.video-title, .video-name, my-video-watch h1, .title');
var title = titleEl ? titleEl.textContent.trim() : 'Video';
var curTime = formatTime(video.currentTime);
var totalTime = formatTime(video.duration || 0);
var remaining = formatTime((video.duration || 0) - video.currentTime);
var speed = (video.playbackRate || 1) + 'x';
var volume = Math.round((video.volume || 1) * 100) + '%';
var quality = video.videoHeight ? video.videoHeight + 'p' : 'Otomatik';
// Visual Toast
var existingToast = document.querySelector('.yaktube-video-info-toast');
if (existingToast) existingToast.remove();
var toast = document.createElement('div');
toast.className = 'yaktube-video-info-toast';
toast.innerHTML =
'
YouTube\'daki en pop\u00fcler e\u015fle\u015fmeler taran\u0131yor...
' +
'
';
var shortcutsBtn = document.getElementById('yaktube-open-shortcuts');
if (shortcutsBtn) {
shortcutsBtn.addEventListener('click', toggleShortcutsModal);
}
announce(query + ' i\u00e7in canl\u0131 YouTube aramas\u0131 ba\u015flat\u0131ld\u0131.');
isSearching = true;
fetch('/api-custom/youtube-search?q=' + encodeURIComponent(query))
.then(function (res) {
return res.json();
})
.then(function (data) {
var rawResults = data.results || [];
var uniqueResults = [];
var seenIds = {};
rawResults.forEach(function (item) {
if (item && item.id && !seenIds[item.id] && item.title) {
seenIds[item.id] = true;
uniqueResults.push(item);
}
});
var count = uniqueResults.length;
if (count === 0) {
container.innerHTML =
'
' +
'
' +
'⚡ Canlı YouTube & Web Arama' +
'
"' +
query +
'" için YouTube\'da Sonuç Bulunamadı
' +
'
' +
'
';
announce(query + " için YouTube\'da eşleşen video bulunamadı.");
} else {
var countText = count === 1 ? '1 Video Bulundu' : count + ' Video Bulundu';
announce(query + ' araması için ' + count + ' video listelendi.');
var visibleCount = Math.min(12, count);
function render() {
var visibleResults = uniqueResults.slice(0, visibleCount);
var cardsHtml = '';
visibleResults.forEach(function (item) {
var isLocal = !!item.isDownloaded;
var isLive = !!item.isLive;
var statusBadge = '';
if (isLive) {
statusBadge =
'🔴 CANLI YAYIN';
} else if (isLocal) {
statusBadge =
'✅ ' +
getInstanceName() +
"'da Mevcut";
}
var actionButton = '';
if (isLive) {
actionButton =
'';
} else if (isLocal) {
actionButton =
'▶️ ' +
getInstanceName() +
"'dan İzle";
} else {
actionButton =
'";
}
var durationText = isLive ? 'CANLI' : item.durationFormatted;
var cardLabel =
item.title +
(isLive ? ' (Canlı Yayın)' : isLocal ? ' (' + getInstanceName() + ' kütüphanenizde mevcut)' : '') +
', ' +
item.channel +
' kanalı, Süre: ' +
durationText;
cardsHtml +=
'
' +
'
' +
'' +
'' +
durationText +
'' +
'
' +
'
' +
'
' +
statusBadge +
'
' +
item.title +
'
' +
'
' +
item.channel +
'
' +
'
' +
actionButton +
'
' +
'
';
});
var loadMoreHtml = '';
if (visibleCount < count) {
loadMoreHtml =
'
' +
'' +
'
';
}
container.innerHTML =
'
' +
'
' +
'⚡ Canlı YouTube & Web Arama' +
'
"' +
query +
'" için ' +
countText +
'
' +
'
' +
'' +
'
' +
'
' +
cardsHtml +
'
' +
loadMoreHtml;
var sBtn = document.getElementById('yaktube-open-shortcuts');
if (sBtn) {
sBtn.addEventListener('click', toggleShortcutsModal);
}
var loadMoreBtn = document.getElementById('yaktube-load-more-btn');
if (loadMoreBtn) {
loadMoreBtn.addEventListener('click', function () {
visibleCount = Math.min(visibleCount + 6, count);
render();
announce(visibleCount + ' adet video gösteriliyor.');
});
}
container.querySelectorAll('.yaktube-live-btn').forEach(function (btn) {
btn.addEventListener('click', function (e) {
var liveUrl = e.currentTarget.getAttribute('data-url');
var liveTitle = e.currentTarget.getAttribute('data-title');
handleLiveStream(liveUrl, liveTitle);
});
});
container.querySelectorAll('.yaktube-import-btn').forEach(function (btn) {
btn.addEventListener('click', function (e) {
var videoUrl = e.currentTarget.getAttribute('data-url');
var videoTitle = e.currentTarget.getAttribute('data-title');
handleImport(videoUrl, e.currentTarget, videoTitle);
});
});
}
render();
}
})
.catch(function (err) {
console.error('[YakTube] Search fetch error:', err);
container.innerHTML =
'
' +
'
' +
'\u26a1 Canl\u0131 YouTube & Web Arama' +
'
Arama s\u0131ras\u0131nda bir ba\u011flant\u0131 sorunu olu\u015ftu.
' +
'
' +
'
';
announce('Arama s\u0131ras\u0131nda bir ba\u011flant\u0131 sorunu olu\u015ftu.');
})
.finally(function () {
isSearching = false;
});
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// 8. FULL YOUTUBE SEARCH & VOICE MODAL ENGINE (v1.3.3 - Guaranteed Close Fix)
// --------------------------------------------------------------------------
var activeVoiceRec = null;
var isVoiceListening = false;
// Dynamic Search History & Trending Helpers with YakNet Cloud Sync
function isUserLoggedIn() {
try {
var token = localStorage.getItem('access_token');
var username = localStorage.getItem('username');
return Boolean(token || username);
} catch (e) {
return false;
}
}
function isAccountSyncEnabled() {
try {
var userPref = localStorage.getItem('yaktube_sync_search_history');
if (userPref === 'false') return false;
return isUserLoggedIn();
} catch (e) {
return false;
}
}
function getRecentSearches() {
try {
var data = localStorage.getItem('yaktube_recent_searches');
if (data) {
var parsed = JSON.parse(data);
if (Array.isArray(parsed) && parsed.length > 0) return parsed;
}
} catch (e) {}
// Default initial recent searches for first-time visitors
var defaults = ['Tarkan', 'Barış Manço', 'Yapay Zeka', 'Canlı Haber'];
if (!isUserLoggedIn()) {
try {
localStorage.setItem('yaktube_recent_searches', JSON.stringify(defaults));
} catch (e) {}
}
return defaults;
}
function callCloudSearchHistory(method, queryParams, body, onSuccess, onError) {
var token = localStorage.getItem('access_token');
var email = localStorage.getItem('email');
var username = localStorage.getItem('username');
var headers = {};
if (body) headers['Content-Type'] = 'application/json';
if (token) headers['Authorization'] = 'Bearer ' + token;
if (email) headers['X-User-Email'] = email;
if (username) headers['X-User-Username'] = username;
var authUrl = 'https://auth.yakhub.com.tr/api/yaktube/search-history' + (queryParams || '');
var bridgeUrl = '/api-custom/search-history' + (queryParams || '');
var options = {
method: method,
headers: headers,
credentials: 'include'
};
if (body) options.body = JSON.stringify(body);
fetch(authUrl, options)
.then(function (res) {
if (!res.ok) throw new Error('Auth server status ' + res.status);
return res.json();
})
.then(function (data) {
if (typeof onSuccess === 'function') onSuccess(data);
})
.catch(function () {
// Fallback to local PeerTube bridge proxy
fetch(bridgeUrl, options)
.then(function (res) {
return res.json();
})
.then(function (data) {
if (typeof onSuccess === 'function') onSuccess(data);
})
.catch(function (e) {
if (typeof onError === 'function') onError(e);
});
});
}
var isSyncingHistory = false;
function syncCloudSearchHistory(callback) {
if (!isAccountSyncEnabled() || isSyncingHistory) return;
isSyncingHistory = true;
callCloudSearchHistory(
'GET',
'',
null,
function (data) {
isSyncingHistory = false;
if (data && data.status === 'success' && Array.isArray(data.history)) {
if (data.history.length > 0) {
localStorage.setItem('yaktube_recent_searches', JSON.stringify(data.history));
}
if (typeof callback === 'function') callback();
}
},
function () {
isSyncingHistory = false;
}
);
}
function saveRecentSearch(query) {
if (!query || query.trim().length < 2) return;
var q = query.trim();
try {
var list = getRecentSearches();
list = list.filter(function (item) {
return item.toLowerCase() !== q.toLowerCase();
});
list.unshift(q);
if (list.length > 12) list = list.slice(0, 12);
localStorage.setItem('yaktube_recent_searches', JSON.stringify(list));
} catch (e) {}
// If user is logged in and sync is active, sync with YakNet account
if (isAccountSyncEnabled()) {
callCloudSearchHistory('POST', '', { query: q, device_name: 'YakTube Web' });
}
}
function clearRecentSearches() {
try {
localStorage.removeItem('yaktube_recent_searches');
} catch (e) {}
if (isAccountSyncEnabled()) {
callCloudSearchHistory('DELETE', '', null);
}
renderDynamicSearchChips();
announce('Arama geçmişi temizlendi.');
}
var popularTrendingQueries = [
{ title: '🎵 Türkçe Pop', q: 'Türkçe Pop' },
{ title: '🎸 Anadolu Rock', q: 'Anadolu Rock' },
{ title: '🤖 Yapay Zeka', q: 'Yapay Zeka' },
{ title: '💻 Yazılım Dersleri', q: 'Yazılım Dersleri' },
{ title: '🎧 Sesli Kitap & Radyo Tiyatrosu', q: 'Sesli Kitap' },
{ title: '📺 Canlı Haber', q: 'Canlı Haber' },
{ title: '🌍 Bilim & Belgesel', q: 'Bilim Belgeseli' },
{ title: '🎼 Akustik Canlı Performans', q: 'Akustik Canlı' },
{ title: '🎙️ Podcast & Söyleşi', q: 'Podcast Türkçe' }
];
// Auto capture search query from URL into recent searches
function captureUrlSearchQuery() {
try {
var params = new URLSearchParams(window.location.search);
var q = params.get('search') || params.get('q');
if (q && q.trim().length >= 2) {
saveRecentSearch(q.trim());
}
} catch (e) {}
}
captureUrlSearchQuery();
window.addEventListener('popstate', captureUrlSearchQuery);
function renderDynamicSearchChips() {
var modal = document.getElementById('yaktube-full-search-modal');
if (!modal) return;
var container = modal.querySelector('#yaktube-modal-dynamic-chips-section');
if (!container) return;
var recent = getRecentSearches();
var loggedIn = isUserLoggedIn();
var syncEnabled = isAccountSyncEnabled();
var html = '';
// 1. Recent searches section
html +=
'
' +
'
🕒 Son Aramalarınız
' +
'
';
if (loggedIn) {
var syncTitle = syncEnabled
? 'YakNet Bulut Eşitlemesi Aktif (Tüm cihazlarınızda senkronize edilir. Kapatmak için tıklayın.)'
: 'Eşitleme Kapalı (Aramalar sadece bu cihazda saklanır. Açmak için tıklayın.)';
var syncText = syncEnabled ? '☁️ Eşitleme: Açık' : '☁️ Eşitleme: Kapalı';
var syncBg = syncEnabled ? '#0f766e' : '#334155';
var syncColor = syncEnabled ? '#5eead4' : '#94a3b8';
html +=
'';
} else {
html +=
'🔒 Bu Cihazda';
}
if (recent.length > 0) {
html +=
'';
}
html += '
';
if (recent.length > 0) {
html += '
';
recent.forEach(function (term) {
var safeTerm = term.replace(/"/g, '"');
html +=
'';
});
html += '
';
}
// 2. Popular & Trending searches section
html +=
'
🔥 Hızlı ve Popüler Aramalar
' +
'
';
popularTrendingQueries.forEach(function (item) {
var safeQ = item.q.replace(/"/g, '"');
html +=
'';
});
html += '
';
container.innerHTML = html;
// Attach click events
container.querySelectorAll('.yaktube-modal-chip').forEach(function (chip) {
chip.addEventListener('click', function () {
var query = chip.getAttribute('data-query');
var inp = modal.querySelector('#yaktube-modal-input');
if (inp) inp.value = query;
if (typeof window.__yaktube_execute_search__ === 'function') {
window.__yaktube_execute_search__(query);
}
});
});
var clearBtn = container.querySelector('#yaktube-clear-recent-btn');
if (clearBtn) {
clearBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
clearRecentSearches();
});
}
var syncToggleBtn = container.querySelector('#yaktube-sync-toggle-btn');
if (syncToggleBtn) {
syncToggleBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
var current = localStorage.getItem('yaktube_sync_search_history') !== 'false';
var next = !current;
localStorage.setItem('yaktube_sync_search_history', next ? 'true' : 'false');
if (next) {
announce('YakNet arama geçmişi eşitlemesi açıldı.');
syncCloudSearchHistory(renderDynamicSearchChips);
} else {
announce('Arama geçmişi eşitlemesi kapatıldı. Aramalar sadece bu cihazda saklanacak.');
}
renderDynamicSearchChips();
});
}
}
function createFullSearchModal() {
var existingModal = document.getElementById('yaktube-full-search-modal');
if (existingModal) return existingModal;
var modal = document.createElement('div');
modal.id = 'yaktube-full-search-modal';
modal.className = 'yaktube-full-search-modal';
modal.setAttribute('role', 'dialog');
modal.setAttribute('aria-modal', 'true');
modal.setAttribute('aria-labelledby', 'yaktube-modal-search-title');
modal.innerHTML =
'
' +
'
' +
'' +
'
' +
'' +
'' +
'
' +
'' +
'' +
'
' +
'
' +
'
' +
'
🎙️ Dinleniyor...
' +
'
Lütfen aramak istediğiniz videoyu söyleyin...
' +
'' +
'
' +
'' +
'' +
'
' +
'
';
document.body.appendChild(modal);
var inp = modal.querySelector('#yaktube-modal-input');
var clearBtn = modal.querySelector('#yaktube-modal-clear-btn');
var backBtn = modal.querySelector('#yaktube-modal-back-btn');
var submitBtn = modal.querySelector('#yaktube-modal-submit-btn');
var voiceBtn = modal.querySelector('#yaktube-modal-voice-btn');
var micPulseBtn = modal.querySelector('#yaktube-modal-mic-pulse');
// Input typing & clear
inp.addEventListener('input', function () {
if (inp.value && inp.value.trim().length > 0) {
clearBtn.style.display = 'inline-flex';
} else {
clearBtn.style.display = 'none';
}
});
clearBtn.addEventListener('click', function () {
inp.value = '';
clearBtn.style.display = 'none';
inp.focus();
announce('Arama metni temizlendi.');
});
// Close on Back button or Escape
backBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
closeFullSearchModal();
});
modal.addEventListener('click', function (e) {
if (e.target === modal) {
closeFullSearchModal();
}
});
// Search execute
function executeSearch(query) {
var q = (query || inp.value || '').trim();
if (q) {
saveRecentSearch(q);
announce('"' + q + '" için arama yapılıyor...', true);
closeFullSearchModal();
window.location.href = '/search?search=' + encodeURIComponent(q);
}
}
window.__yaktube_execute_search__ = executeSearch;
renderDynamicSearchChips();
submitBtn.addEventListener('click', function () {
executeSearch();
});
inp.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
executeSearch();
} else if (e.key === 'Escape') {
e.preventDefault();
closeFullSearchModal();
}
});
// Chips click
modal.querySelectorAll('.yaktube-modal-chip').forEach(function (chip) {
chip.addEventListener('click', function () {
var query = chip.getAttribute('data-query');
executeSearch(query);
});
});
// Voice button click
voiceBtn.addEventListener('click', function () {
toggleVoiceInsideModal();
});
micPulseBtn.addEventListener('click', function () {
toggleVoiceInsideModal();
});
return modal;
}
function openFullSearchModal(initialQuery, autoStartVoice) {
var modal = createFullSearchModal();
modal.classList.add('yaktube-modal-open');
modal.style.display = 'flex';
renderDynamicSearchChips();
syncCloudSearchHistory(renderDynamicSearchChips);
var inp = modal.querySelector('#yaktube-modal-input');
var clearBtn = modal.querySelector('#yaktube-modal-clear-btn');
if (inp) {
inp.value = initialQuery || '';
if (inp.value) clearBtn.style.display = 'inline-flex';
setTimeout(function () {
inp.focus();
inp.select();
}, 50);
}
announce('Arama penceresi açıldı. Arayacağınız konuyu yazın veya sesle arayın.', true);
if (autoStartVoice) {
startVoiceInsideModal();
}
}
function closeFullSearchModal() {
var modal = document.getElementById('yaktube-full-search-modal');
if (modal) {
modal.classList.remove('yaktube-modal-open');
modal.style.display = 'none';
}
stopVoiceInsideModal();
announce('Arama penceresi kapatıldı.');
var trigger = document.querySelector('.yaktube-search-trigger-pill');
if (trigger) {
try {
trigger.focus();
} catch (e) {}
}
}
function toggleVoiceInsideModal() {
if (isVoiceListening) {
stopVoiceInsideModal();
} else {
startVoiceInsideModal();
}
}
function startVoiceInsideModal() {
var SpeechRec = window.SpeechRecognition || window.webkitSpeechRecognition;
var modal = document.getElementById('yaktube-full-search-modal');
if (!modal) return;
var voiceSec = modal.querySelector('#yaktube-modal-voice-section');
var statusText = modal.querySelector('#yaktube-modal-voice-status');
var transcriptEl = modal.querySelector('#yaktube-modal-voice-transcript');
var inp = modal.querySelector('#yaktube-modal-input');
if (voiceSec) voiceSec.style.display = 'block';
if (statusText) statusText.textContent = '🎙️ Dinleniyor...';
if (transcriptEl) transcriptEl.textContent = 'Lütfen aramak istediğiniz videoyu söyleyin...';
if (!SpeechRec) {
if (statusText) statusText.textContent = 'Tarayıcıda Ses Tanıma Desteklenmiyor';
if (transcriptEl) transcriptEl.textContent = 'Lütfen Google Chrome veya Microsoft Edge kullanın.';
announce('Tarayıcınız ses tanımayı desteklemiyor. Lütfen Chrome veya Edge kullanın.');
return;
}
announce('Sesli arama dinleniyor, lütfen aramak istediğiniz videoyu söyleyin.', true);
try {
activeVoiceRec = new SpeechRec();
activeVoiceRec.lang = navigator.language || 'tr-TR';
activeVoiceRec.continuous = false;
activeVoiceRec.interimResults = true;
activeVoiceRec.maxAlternatives = 1;
isVoiceListening = true;
activeVoiceRec.onresult = function (ev) {
if (ev.results && ev.results[0]) {
var transcript = ev.results[0][0].transcript.trim();
if (transcriptEl) transcriptEl.textContent = '"' + transcript + '"';
if (inp) inp.value = transcript;
if (ev.results[0].isFinal) {
announce('"' + transcript + '" için arama başlatılıyor...', true);
setTimeout(function () {
closeFullSearchModal();
window.location.href = '/search?search=' + encodeURIComponent(transcript);
}, 500);
}
}
};
activeVoiceRec.onerror = function (ev) {
if (statusText) statusText.textContent = 'Ses Algılanamadı';
if (transcriptEl) transcriptEl.textContent = 'Tekrar denemek için mikrofona dokunun.';
stopVoiceInsideModal();
};
activeVoiceRec.onend = function () {
stopVoiceInsideModal();
};
activeVoiceRec.start();
} catch (err) {
stopVoiceInsideModal();
}
}
function stopVoiceInsideModal() {
isVoiceListening = false;
if (activeVoiceRec) {
try {
activeVoiceRec.stop();
} catch (e) {}
activeVoiceRec = null;
}
var modal = document.getElementById('yaktube-full-search-modal');
if (modal) {
var voiceSec = modal.querySelector('#yaktube-modal-voice-section');
if (voiceSec) voiceSec.style.display = 'none';
}
}
function transformToYouTubeSearchBar(searchInp, searchContainer) {
if (searchContainer.hasAttribute('data-yaktube-trigger-styled')) return;
searchContainer.setAttribute('data-yaktube-trigger-styled', 'true');
// Hide original native inputs
searchInp.style.display = 'none';
searchInp.setAttribute('tabindex', '-1');
searchInp.setAttribute('aria-hidden', 'true');
var nativeInputs = searchContainer.querySelectorAll('input, button');
nativeInputs.forEach(function (el) {
if (
!el.classList.contains('yaktube-search-trigger-pill') &&
!el.classList.contains('yaktube-voice-search-btn')
) {
el.style.display = 'none';
el.setAttribute('tabindex', '-1');
el.setAttribute('aria-hidden', 'true');
}
});
// 1. Create Outer Wrapper
var outerWrap = searchContainer.querySelector('.yaktube-search-outer-wrap');
if (!outerWrap) {
outerWrap = document.createElement('div');
outerWrap.className = 'yaktube-search-outer-wrap';
searchContainer.appendChild(outerWrap);
}
// 2. Create Single Sleek Pill Trigger
var triggerPill = outerWrap.querySelector('.yaktube-search-trigger-pill');
if (!triggerPill) {
triggerPill = document.createElement('button');
triggerPill.type = 'button';
triggerPill.className = 'yaktube-search-trigger-pill';
triggerPill.setAttribute('aria-label', 'Ara');
triggerPill.setAttribute('title', 'Arama Yap (/)');
triggerPill.innerHTML =
'🔍' +
'Ara...' +
'/';
outerWrap.appendChild(triggerPill);
triggerPill.addEventListener('click', function (e) {
e.preventDefault();
openFullSearchModal('', false);
});
}
// 3. YouTube Circular Voice Button (🎙️) Beside the Pill Box
var voiceBtn = outerWrap.querySelector('.yaktube-voice-search-btn');
if (!voiceBtn) {
voiceBtn = document.createElement('button');
voiceBtn.type = 'button';
voiceBtn.className = 'yaktube-voice-search-btn';
voiceBtn.setAttribute('aria-label', 'Sesle Ara (Shift+V)');
voiceBtn.setAttribute('title', 'Sesle Ara (Shift+V)');
voiceBtn.innerHTML = '🎙️';
outerWrap.appendChild(voiceBtn);
voiceBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
openFullSearchModal('', true);
});
}
}
function injectSearchEnhancements() {
var searchContainers = document.querySelectorAll(
'my-search-typeahead, .search-container, .header-search, my-header .search'
);
searchContainers.forEach(function (container) {
var searchInp = container.querySelector('input[type="search"], input[type="text"], input');
if (searchInp) {
transformToYouTubeSearchBar(searchInp, container);
}
});
// Clean up repetitive card titles and strange relative date buttons
document.querySelectorAll('my-video-miniature, .video-miniature').forEach(function (card) {
var thumbLink = card.querySelector('a.thumbnail-link, .video-thumbnail a, a.thumbnail');
if (thumbLink && !thumbLink.hasAttribute('aria-hidden')) {
thumbLink.setAttribute('aria-hidden', 'true');
thumbLink.setAttribute('tabindex', '-1');
}
var dateBtn = card.querySelector('.video-date-line button, .video-date button, button[title*="tarih"]');
if (dateBtn && !dateBtn.hasAttribute('data-cleaned')) {
dateBtn.setAttribute('data-cleaned', 'true');
var dateText = dateBtn.textContent.trim();
dateBtn.setAttribute('aria-label', dateText);
}
});
}
// Global Keyboard Shortcuts
document.addEventListener('keydown', function (e) {
var activeTag = document.activeElement ? document.activeElement.tagName.toLowerCase() : '';
var isInputActive = activeTag === 'input' || activeTag === 'textarea';
// Shift + V: Open YouTube Voice Search Modal
if (e.shiftKey && (e.key === 'V' || e.key === 'v') && !isInputActive) {
e.preventDefault();
openFullSearchModal('', true);
}
// Escape: Close Full Search Modal
else if (e.key === 'Escape' || e.keyCode === 27) {
var modal = document.getElementById('yaktube-full-search-modal');
if (modal && modal.classList.contains('yaktube-modal-open')) {
e.preventDefault();
closeFullSearchModal();
}
}
// / or S: Open Search Modal
else if ((e.key === '/' || e.key === 's' || e.key === 'S') && !isInputActive && !e.ctrlKey && !e.metaKey) {
e.preventDefault();
openFullSearchModal('', false);
}
});
// Automatically trigger cloud search history pull on startup if logged in
try {
if (isAccountSyncEnabled()) {
syncCloudSearchHistory();
}
} catch (e) {}
// 7.5. Reactive Hybrid Related Videos Engine (Watch Page)
var currentActiveVideoKey = null;
var isFetchingRelated = false;
var relatedShowAll = false;
var cachedRelatedResults = [];
var cachedRelatedQuery = '';
function renderRelatedCards(container, results, query) {
var count = results ? results.length : 0;
if (count === 0) {
container.style.display = 'none';
return;
}
container.style.display = 'block';
var limit = relatedShowAll ? count : Math.min(count, 12);
var displayed = results.slice(0, limit);
var cardsHtml = '';
displayed.forEach(function (item) {
var isLocal = !!item.isDownloaded;
var statusBadge = isLocal
? '✅ ' +
getInstanceName() +
"'da İzle"
: '⚡ Canlı Keşif';
var actionButton = isLocal
? '▶️ Hemen İzle'
: '';
var cardLabel =
item.title +
(isLocal ? ' (' + getInstanceName() + ' kütüphanenizde mevcut)' : ' (Canlı YouTube keşfi)') +
', ' +
item.channel +
' kanalı, Süre: ' +
item.durationFormatted;
cardsHtml +=
'
YouTube ve Fediverse\'deki en alakalı videolar getiriliyor...
' +
'
';
announce(searchQuery + ' için ilgili video önerileri yükleniyor.');
fetch('/api-custom/youtube-search?q=' + encodeURIComponent(searchQuery))
.then(function (res) {
return res.json();
})
.then(function (data) {
var rawResults = data.results || [];
var normalizedCurrentTitle = rawTitle.toLowerCase().trim();
var uniqueResults = [];
var seenIds = {};
rawResults.forEach(function (item) {
if (item && item.id && !seenIds[item.id] && item.title) {
var itemTitle = item.title.toLowerCase().trim();
if (itemTitle !== normalizedCurrentTitle && !itemTitle.includes(normalizedCurrentTitle)) {
seenIds[item.id] = true;
uniqueResults.push(item);
}
}
});
cachedRelatedResults = uniqueResults;
cachedRelatedQuery = searchQuery;
currentActiveVideoKey = thisVideoKey;
relatedShowAll = false;
renderRelatedCards(container, uniqueResults, searchQuery);
var initialCount = Math.min(uniqueResults.length, 12);
if (uniqueResults.length === 0) {
announce(searchQuery + ' için ilgili öneri bulunamadı.');
} else {
announce(
searchQuery +
' için ' +
initialCount +
' öneri gösteriliyor' +
(uniqueResults.length > 12 ? ', toplam ' + uniqueResults.length + ' video bulundu' : '') +
'.'
);
}
})
.catch(function (err) {
console.error('[YakTube] Related fetch error:', err);
container.style.display = 'none';
})
.finally(function () {
isFetchingRelated = false;
});
}
var lastWatchUuid = null;
function checkAndInjectWatchPageComments() {
var pathname = window.location.pathname;
var match = pathname.match(/\/videos\/watch\/([a-zA-Z0-9_-]+)/) || pathname.match(/\/w\/([a-zA-Z0-9_-]+)/);
if (!match) {
lastWatchUuid = null;
var existingWatchComments = document.getElementById('yaktube-watch-comments-panel');
if (existingWatchComments) existingWatchComments.remove();
return;
}
var uuid = match[1];
var existingPanel = document.getElementById('yaktube-watch-comments-panel');
if (uuid === lastWatchUuid && existingPanel) {
var nativeComments = document.querySelector('my-video-comments, .video-comments, .comments-block, #comments');
if (nativeComments && existingPanel.previousElementSibling !== nativeComments) {
nativeComments.insertAdjacentElement('afterend', existingPanel);
}
return;
}
lastWatchUuid = uuid;
var mountTarget =
document.querySelector('my-video-comments, .video-comments, my-video-watch, .video-bottom') || document.body;
if (!mountTarget) return;
fetch('/api-custom/video-origin/' + encodeURIComponent(uuid))
.then(function (r) {
return r.json();
})
.then(function (originData) {
if (!originData || !originData.ok || !originData.hasYouTubeOrigin || !originData.youtubeId) {
var old = document.getElementById('yaktube-watch-comments-panel');
if (old) old.remove();
return;
}
var youtubeId = originData.youtubeId;
var existing = document.getElementById('yaktube-watch-comments-panel');
if (existing) existing.remove();
var panel = document.createElement('div');
panel.id = 'yaktube-watch-comments-panel';
panel.className = 'yaktube-comments-panel';
panel.setAttribute('role', 'region');
panel.setAttribute('aria-label', 'YouTube Orijinal Yorumlar\u0131');
panel.style.cssText =
'margin-top: 24px; margin-bottom: 24px; background: #0f172a; border-radius: 12px; padding: 20px; border: 1px solid rgba(255,255,255,0.08); box-shadow: 0 8px 24px rgba(0,0,0,0.4);';
panel.innerHTML =
'
' +
'
' +
'
' +
"\ud83d\udcac YouTube'daki Orijinal Yorumlar" +
'
' +
'
Bu video YouTube\'dan aktar\u0131lm\u0131\u015ft\u0131r. Orijinal topluluk yorumlar\u0131n\u0131 a\u015fa\u011f\u0131dan anl\u0131k olarak y\u00fckleyebilirsiniz.
' +
'
' +
'' +
'
' +
'' +
'
' +
'' +
'
';
var nativeComments = document.querySelector('my-video-comments, .video-comments, .comments-block, #comments');
if (nativeComments && nativeComments.parentElement) {
nativeComments.insertAdjacentElement('afterend', panel);
} else {
var watchCol = document.querySelector('.video-info, .video-bottom, my-video-watch, .main-col');
if (watchCol) {
watchCol.appendChild(panel);
} else {
mountTarget.appendChild(panel);
}
}
var watchCommentToken = null;
var listContainer = document.getElementById('yaktube-watch-comments-list');
var toggleBtn = document.getElementById('yaktube-watch-toggle-comments-btn');
var moreWrap = document.getElementById('yaktube-watch-more-comments-wrap');
var moreBtn = document.getElementById('yaktube-watch-more-comments-btn');
function fetchWatchComments(token) {
if (!token) {
toggleBtn.disabled = true;
toggleBtn.textContent = '⏳ Yorumlar Y\u00fckleniyor...';
} else {
moreBtn.disabled = true;
moreBtn.textContent = '⏳ Y\u00fckleniyor...';
}
var fUrl =
'/api-custom/youtube-comments?id=' +
encodeURIComponent(youtubeId) +
(token ? '&token=' + encodeURIComponent(token) : '');
fetch(fUrl)
.then(function (r) {
return r.json();
})
.then(function (res) {
if (!token) {
listContainer.innerHTML = '';
listContainer.style.display = 'flex';
toggleBtn.textContent = '🔄 Yorumlar\u0131 Yenile';
toggleBtn.disabled = false;
} else {
moreBtn.textContent = '➕ Daha Fazla Yorum G\u00f6ster (+20 Yorum)';
moreBtn.disabled = false;
}
var cList = res.comments || [];
if (cList.length === 0 && !token) {
listContainer.innerHTML =
'