1/** 2 * ============================================================================ 3 * ÐÐÐÐÐТÐÐÐ: ÐзбÑанное и ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ 4 * ============================================================================ 5 * 6 * ÐодÑÐ»Ñ Ð´Ð»Ñ ÑабоÑÑ Ñ work-level ÑпиÑками маÑеÑиалов. 7 * ÐаннÑе Ñ ÑанÑÑÑÑ Ð² localStorage бÑаÑзеÑа в клÑÑÐ°Ñ : 8 * - `media_favorites` 9 * - `media_watch_later` 10 * 11 * Ðажно не пÑÑаÑÑ Ñ file-level ÑпиÑками из `media-player-core.js`: 12 * - `predanie_favorites` 13 * - `predanie_watch_later` 14 * 15 * ÐдеÑÑ Ð¶Ð¸Ð²ÑÑ Ð¸Ð¼ÐµÐ½Ð½Ð¾ пÑÐ¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ñеликом (works), а не оÑделÑнÑе ÑÑеки/ÑайлÑ. 16 * 17 * API (window.MediaFavorites): 18 * ---------------------------- 19 * getFavorites() / getWatchLater() â Array 20 * addToFavorites(item) / addToWatchLater() â Array 21 * removeFromFavorites(id) / removeFromWatchLater() â Array 22 * isInFavorites(id) / isInWatchLater() â Boolean 23 * toggleFavorite(item) / toggleWatchLater() â Boolean 24 * getFavoritesCount() / getWatchLaterCount() â Number 25 */ 26 27(function () { 28 'use strict'; 29 30 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 31 // ÐÐÐСТÐÐТЫ 32 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 33 34 const STORAGE_KEYS = { 35 favorites: 'media_favorites', 36 watchLater: 'media_watch_later', 37 }; 38 39 // ÐлÑÑи плееÑа Ð´Ð»Ñ ÑÑеков/Ñайлов 40 const PLAYER_STORAGE_KEYS = { 41 favorites: 'predanie_favorites', 42 watchLater: 'predanie_watch_later', 43 }; 44 45 /** 46 * ÐоÑмализÑÐµÑ Ð²Ñ Ð¾Ð´Ð½Ð¾Ð¹ обÑÐµÐºÑ Ð¿ÑÐ¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ðº компакÑÐ½Ð¾Ð¼Ñ storage-ÑоÑмаÑÑ. 47 * 48 * ÐÐ¾Ð´Ñ Ð¾Ð´Ð¸Ñ Ð¸ длÑ: 49 * - новÑÑ Ð¾Ð±ÑекÑов из UI 50 * - ÑÑаÑÑÑ Ð¶Ð¸ÑнÑÑ Ð¾Ð±ÑекÑов, оÑÑавÑÐ¸Ñ ÑÑ Ð² localStorage 51 */ 52 function toCompactWorkItem(item) { 53 if (!item || typeof item !== 'object') return null; 54 const itemId = item.id || item.workId; 55 if (!itemId) return null; 56 57 return { 58 id: itemId, 59 title: item.title || item.workTitle || '', 60 href: item.href || '', 61 thumbUrl: item.thumbUrl || item.coverUrl || '', 62 addedAt: item.addedAt || null, 63 }; 64 } 65 66 /** ÐезопаÑно ÑÑиÑÐ°ÐµÑ Ð´Ð»Ð¸Ð½Ñ file-level ÑпиÑка из localStorage. */ 67 function getPlayerListCount(key) { 68 try { 69 const parsed = JSON.parse(localStorage.getItem(key)) || []; 70 return Array.isArray(parsed) ? parsed.length : 0; 71 } catch (e) { 72 return 0; 73 } 74 } 75 76 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 77 // Ð ÐÐÐТРС LOCALSTORAGE 78 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 79 80 /** ÐолÑÑиÑÑ ÑпиÑок из localStorage */ 81 function getItems(key) { 82 try { 83 const raw = JSON.parse(localStorage.getItem(key)) || []; 84 if (!Array.isArray(raw)) return []; 85 return raw 86 .map(normalizeStoredItem) 87 .filter(Boolean); 88 } catch (e) { 89 return []; 90 } 91 } 92 93 /** 94 * ÐоÑмализÑÐµÑ Ð¸ ÑÐ¶Ð¸Ð¼Ð°ÐµÑ ÑÐ¾Ñ ÑанÑннÑй обÑÐµÐºÑ Ð¿ÑоизведениÑ. 95 * Ð¥Ñаним ÑолÑко полÑ, ÑеалÑно нÑжнÑе Ð´Ð»Ñ UI и ÑÐ¸Ð½Ñ ÑонизаÑии. 96 */ 97 function normalizeStoredItem(item) { 98 return toCompactWorkItem(item); 99 } 100 101 /** Ð¡Ð¾Ñ ÑаниÑÑ ÑпиÑок в localStorage */ 102 function saveItems(key, items) { 103 try { 104 const compactItems = (Array.isArray(items) ? items : []) 105 .map(normalizeStoredItem) 106 .filter(Boolean); 107 localStorage.setItem(key, JSON.stringify(compactItems)); 108 } catch (e) { 109 console.error('[MediaFavorites] Storage error:', e); 110 } 111 } 112 113 /** ÐÑовеÑиÑÑ Ð½Ð°Ð»Ð¸Ñие ÑлеменÑа */ 114 function isInList(key, itemId) { 115 return getItems(key).some(item => item.id === itemId); 116 } 117 118 /** ÐобавиÑÑ ÑÐ»ÐµÐ¼ÐµÐ½Ñ (в наÑало) */ 119 function addToList(key, itemData) { 120 const items = getItems(key); 121 if (!items.some(item => item.id === itemData.id)) { 122 items.unshift({ 123 ...toCompactWorkItem(itemData), 124 addedAt: new Date().toISOString(), 125 }); 126 saveItems(key, items); 127 updateSidebarCounters(); 128 } 129 return items; 130 } 131 132 /** УдалиÑÑ ÑÐ»ÐµÐ¼ÐµÐ½Ñ */ 133 function removeFromList(key, itemId) { 134 const items = getItems(key).filter(item => item.id !== itemId); 135 saveItems(key, items); 136 updateSidebarCounters(); 137 return items; 138 } 139 140 /** ÐеÑеклÑÑиÑÑ (добавиÑÑ/ÑдалиÑÑ) */ 141 function toggleInList(key, itemData) { 142 if (isInList(key, itemData.id)) { 143 removeFromList(key, itemData.id); 144 return false;
145 } 146 addToList(key, itemData); 147 return true; 148 } 149 150 /** ÐбновиÑÑ ÑÑÑÑÑики в ÑайдбаÑе (пÑÐ¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ + ÑайлÑ/ÑÑеки) */ 151 function updateSidebarCounters() { 152 // ÐÑÐ¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ (MediaFavorites) 153 const worksInFav = getItems(STORAGE_KEYS.favorites).length; 154 const worksInWL = getItems(STORAGE_KEYS.watchLater).length; 155 156 // ФайлÑ/ÑÑеки (плееÑ) â оÑделÑнÑе file-level ÑпиÑки из media-player-core.js. 157 const tracksInFav = getPlayerListCount(PLAYER_STORAGE_KEYS.favorites); 158 const tracksInWL = getPlayerListCount(PLAYER_STORAGE_KEYS.watchLater); 159 160 // СÑммиÑÑем 161 const favCount = worksInFav + tracksInFav; 162 const wlCount = worksInWL + tracksInWL; 163 164 const favEl = document.getElementById('sidebarFavoritesCount'); 165 const wlEl = document.getElementById('sidebarWatchLaterCount'); 166 167 if (favEl) { 168 favEl.textContent = favCount > 0 ? favCount : ''; 169 favEl.classList.toggle('has-items', favCount > 0); 170 } 171 172 if (wlEl) { 173 wlEl.textContent = wlCount > 0 ? wlCount : ''; 174 wlEl.classList.toggle('has-items', wlCount > 0); 175 } 176 } 177 178 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 179 // ÐУÐÐÐЧÐÐÐ API 180 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 181 182 window.MediaFavorites = { 183 // ÐзбÑанное 184 getFavorites: () => getItems(STORAGE_KEYS.favorites), 185 addToFavorites: item => addToList(STORAGE_KEYS.favorites, item), 186 removeFromFavorites: id => removeFromList(STORAGE_KEYS.favorites, id), 187 isInFavorites: id => isInList(STORAGE_KEYS.favorites, id), 188 toggleFavorite: item => toggleInList(STORAGE_KEYS.favorites, item), 189 getFavoritesCount: () => getItems(STORAGE_KEYS.favorites).length, 190 191 // ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ 192 getWatchLater: () => getItems(STORAGE_KEYS.watchLater), 193 addToWatchLater: item => addToList(STORAGE_KEYS.watchLater, item), 194 removeFromWatchLater: id => removeFromList(STORAGE_KEYS.watchLater, id), 195 isInWatchLater: id => isInList(STORAGE_KEYS.watchLater, id), 196 toggleWatchLater: item => toggleInList(STORAGE_KEYS.watchLater, item), 197 getWatchLaterCount: () => getItems(STORAGE_KEYS.watchLater).length, 198 199 // ÐбновиÑÑ ÑÑÑÑÑики в ÑайдбаÑе 200 updateSidebarCounters: updateSidebarCounters, 201 }; 202 203 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 204 // Ð ÐÐÐТРС DOM 205 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 206 207 /** ÐзвлеÑÑ Ð´Ð°Ð½Ð½Ñе из data-аÑÑибÑÑов каÑÑоÑки */ 208 function getItemDataFromCard(card) { 209 return { 210 id: card.dataset.itemId, 211 title: card.dataset.itemTitle || 'Ðез названиÑ', 212 href: card.dataset.itemHref || '#', 213 thumbUrl: card.dataset.itemThumb || '', 214 author: card.dataset.itemAuthor || '', 215 type: card.dataset.itemType || 'lectures', 216 description: card.dataset.itemDescription || '', 217 }; 218 } 219 220 /** ÐбновиÑÑ ÑоÑÑоÑние кнопок на каÑÑоÑке */ 221 function updateCardState(card) { 222 const itemId = card.dataset.itemId; 223 if (!itemId) return; 224 225 const inFav = window.MediaFavorites.isInFavorites(itemId); 226 const inWatch = window.MediaFavorites.isInWatchLater(itemId); 227 228 // Ðнопка избÑанного 229 const favBtn = card.querySelector('.stroki-card-icon-btn--favorite'); 230 if (favBtn) { 231 favBtn.classList.toggle('active', inFav); 232 const icon = favBtn.querySelector('i'); 233 if (icon) 234 icon.className = inFav ? 'fa-solid fa-heart' : 'fa-regular fa-heart'; 235 favBtn.title = inFav ? 'УдалиÑÑ Ð¸Ð· избÑанного' : 'РизбÑанное'; 236 } 237 238 // Ðнопка "ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ" (иконка) 239 const watchBtn = card.querySelector('.stroki-card-icon-btn--watch-later'); 240 if (watchBtn) { 241 watchBtn.classList.toggle('active', inWatch); 242 const icon = watchBtn.querySelector('i'); 243 if (icon) 244 icon.className = inWatch 245 ? 'fa-solid fa-bookmark' 246 : 'fa-regular fa-bookmark'; 247 watchBtn.title = inWatch 248 ? 'УдалиÑÑ Ð¸Ð· "ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ"' 249 : 'ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ'; 250 } 251 252 // Ðнопка "Ðозже" в овеÑлее 253 const watchBtnOverlay = card.querySelector('.stroki-card-btn--watch-later'); 254 if (watchBtnOverlay) { 255 watchBtnOverlay.classList.toggle('active', inWatch); 256 const icon = watchBtnOverlay.querySelector('i'); 257 if (icon) 258 icon.className = inWatch 259 ? 'fa-solid fa-bookmark' 260 : 'fa-regular fa-bookmark'; 261 const text = watchBtnOverlay.lastChild; 262 if (text?.nodeType === Node.TEXT_NODE) { 263 text.textContent = inWatch ? ' ÐÑложено' : ' Ðозже'; 264 } 265 } 266 } 267 268 /** ÐбновиÑÑ Ð²Ñе каÑÑоÑки */ 269 function updateAllCards() { 270 document 271 .querySelectorAll('.stroki-card[data-item-id]') 272 .forEach(updateCardState); 273 } 274 275 /** ÐбновиÑÑ ÑÑÑÑÑики в навигаÑии */ 276 function updateNavCounters() { 277 const fav = window.MediaFavorites.getFavoritesCount(); 278 const watch = window.MediaFavorites.getWatchLaterCount(); 279 280 document.querySelectorAll('.media-favorites-count').forEach(el => { 281 el.textContent = fav; 282 el.style.display = fav > 0 ? '' : 'none'; 283 }); 284 285 document.querySelectorAll('.media-watch-later-count').forEach(el => { 286 el.textContent = watch; 287 el.style.display = watch > 0 ? '' : 'none'; 288 }); 289 } 290 291 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 292 // TOAST-УÐÐÐÐÐÐÐÐÐЯ 293 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 294 295 function showToast(message) { 296 const existing = document.querySelector('.media-toast'); 297 if (existing) existing.remove(); 298 299 const toast = document.createElement('div'); 300 toast.className = 'media-toast'; 301 toast.textContent = message; 302 document.body.appendChild(toast); 303 304 requestAnimationFrame(() => toast.classList.add('show')); 305 setTimeout(() => { 306 toast.classList.remove('show'); 307 setTimeout(() => toast.remove(), 300); 308 }, 2000); 309 } 310 311 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 312 // ÐÐÐ ÐÐÐТЧÐÐРСÐÐЫТÐÐ 313 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 314 315 function handleFavoriteClick(e) { 316 e.preventDefault(); 317 e.stopPropagation(); 318 319 const card = e.currentTarget.closest('.stroki-card'); 320 if (!card) return; 321 322 const itemData = getItemDataFromCard(card); 323 const added = window.MediaFavorites.toggleFavorite(itemData); 324 325 updateCardState(card); 326 updateNavCounters(); 327 showToast(added ? 'Ðобавлено в избÑанное' : 'Удалено из избÑанного'); 328 } 329 330 function handleWatchLaterClick(e) { 331 e.preventDefault(); 332 e.stopPropagation(); 333 334 const card = e.currentTarget.closest('.stroki-card'); 335 if (!card) return; 336 337 const itemData = getItemDataFromCard(card); 338 const added = window.MediaFavorites.toggleWatchLater(itemData); 339 340 updateCardState(card); 341 updateNavCounters(); 342 showToast( 343 added ? 'Ðобавлено в "ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ"' : 'Удалено из "ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ"', 344 ); 345 } 346 347 function handleRemoveFromList(e) { 348 e.preventDefault(); 349 e.stopPropagation(); 350 351 const btn = e.currentTarget; 352 const card = btn.closest('.stroki-card'); 353 const listType = btn.dataset.listType; 354 const itemId = card?.dataset.itemId; 355 356 if (!card || !itemId) return; 357 358 // УдалÑем из ÑпиÑка 359 if (listType === 'favorites') { 360 window.MediaFavorites.removeFromFavorites(itemId); 361 showToast('Удалено из избÑанного'); 362 } else if (listType === 'watchLater') { 363 window.MediaFavorites.removeFromWatchLater(itemId); 364 showToast('Удалено из "ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ"'); 365 } 366 367 // ÐнимаÑÐ¸Ñ ÑÐ´Ð°Ð»ÐµÐ½Ð¸Ñ 368 card.style.transition = 'opacity 0.3s, transform 0.3s'; 369 card.style.opacity = '0'; 370 card.style.transform = 'scale(0.9)'; 371 372 setTimeout(() => { 373 card.remove(); 374 updateNavCounters(); 375 376 // ÐÑовеÑÑем пÑÑÑой ÑпиÑок 377 const grid = document.querySelector('.stroki-grid'); 378 const empty = document.querySelector('.media-list-empty'); 379 if (grid && empty && grid.querySelectorAll('.stroki-card').length === 0) { 380 grid.style.display = 'none'; 381 empty.style.display = ''; 382 } 383 }, 300); 384 } 385 386 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 387 // ÐÐÐÐÐЬÐЫРÐÐÐÐ ÐÐ ÐÐРТÐЧÐÐ 388 // Ðа мобилÑнÑÑ ÑÑÑÑойÑÑÐ²Ð°Ñ Ð¸ планÑеÑÐ°Ñ ÐºÐ»Ð¸Ðº по каÑÑоÑке ÑÑÐ°Ð·Ñ Ð¿ÐµÑÐµÑ Ð¾Ð´Ð¸Ñ Ð½Ð° ÑÑÑаниÑÑ 389 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 390 391 /** 392 * ÐпÑеделÑеÑ, нÑжно ли обÑабаÑÑваÑÑ ÐºÐ»Ð¸Ðº по каÑÑоÑке ÑеÑез JS. 393 * true Ð´Ð»Ñ ÑÑÑÑойÑÑв без hover (мобилÑнÑе, планÑеÑÑ) â на Ð½Ð¸Ñ 394 * CSS-овеÑлей невидим и некликабелен, поÑÑÐ¾Ð¼Ñ Ð½Ñжен JS-пеÑÐµÑ Ð¾Ð´. 395 */ 396 function shouldNavigateOnCardClick() { 397 // УÑÑÑойÑÑво без hover-поддеÑжки (touch-планÑеÑÑ, ÑелеÑонÑ) 398 if (window.matchMedia && !window.matchMedia('(hover: hover)').matches) return true; 399 // ФолбÑк: маленÑкий ÑкÑан 400 return window.innerWidth <= 767; 401 } 402 403 function handleMobileCardClick(e) { 404 if (!shouldNavigateOnCardClick()) return; 405 406 // Ðе пеÑÐµÑ Ð²Ð°ÑÑваем клики по кнопкам 407 if (e.target.closest('.stroki-card-icon-btn, .stroki-card-btn, button')) return; 408 409 const card = e.currentTarget; 410 const href = card.dataset.itemHref; 411 if (href) { 412 window.location.href = href; 413 } 414 } 415 416 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 417 // СТРÐÐÐЦРРÐÐÐТЫ: ÐÐÐÐÐÐ ÐÐÐСТÐÐÐ 418 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 419 420 /** ÐолÑÑиÑÑ Ð´Ð°Ð½Ð½Ñе ÑабоÑÑ Ñо ÑÑÑаниÑÑ */ 421 function getWorkDataFromPage() { 422 const container = document.querySelector('.container[data-work-id]'); 423 if (!container) return null; 424 425 // СобиÑаем даннÑе из ÑÑÑаниÑÑ 426 const workId = container.dataset.workId; 427 const workTitle = container.dataset.workTitle || document.querySelector('.work-title')?.textContent || 'Ðез названиÑ'; 428 const author = document.querySelector('.work-author a')?.textContent || ''; 429 const thumbUrl = document.querySelector('.work-cover-image img')?.src || ''; 430 431 return { 432 id: workId, 433 title: workTitle, 434 href: window.location.pathname, 435 thumbUrl: thumbUrl, 436 author: author, 437 type: 'work', 438 description: '', 439 }; 440 } 441 442 /** ÐбновиÑÑ ÑоÑÑоÑние иконок на ÑÑÑаниÑе ÑабоÑÑ */ 443 function updateWorkPageIcons() { 444 const container = document.querySelector('.container[data-work-id]'); 445 if (!container) return; 446 447 const workId = container.dataset.workId; 448 if (!workId) return; 449 450 const inFav = window.MediaFavorites.isInFavorites(workId); 451 const inWatch = window.MediaFavorites.isInWatchLater(workId); 452 453 // Ðнопка избÑанного (поддеÑживаем оба клаÑÑа: ÑÑаÑÑй и новÑй) 454 const favBtn = document.querySelector('.work-action-btn--favorite, .work-action-icon--favorite'); 455 if (favBtn) { 456 favBtn.classList.toggle('active', inFav); 457 const icon = favBtn.querySelector('i'); 458 if (icon) icon.className = inFav ? 'fa-solid fa-heart' : 'fa-regular fa-heart'; 459 favBtn.title = inFav ? 'УдалиÑÑ Ð¸Ð· избÑанного' : 'РизбÑанное'; 460 // ÐбновлÑем ÑекÑÑ ÐµÑли еÑÑÑ span 461 const textSpan = favBtn.querySelector('span'); 462 if (textSpan) textSpan.textContent = inFav ? 'РизбÑанном' : 'РизбÑанное'; 463 } 464 465 // Ðнопка "Ðозже" (поддеÑживаем оба клаÑÑа) 466 const watchBtn = document.querySelector('.work-action-btn--later, .work-action-icon--later'); 467 if (watchBtn) { 468 watchBtn.classList.toggle('active', inWatch); 469 const icon = watchBtn.querySelector('i'); 470 if (icon) icon.className = inWatch ? 'fa-solid fa-clock' : 'fa-regular fa-clock'; 471 watchBtn.title = inWatch ? 'УдалиÑÑ Ð¸Ð· "Ðозже"' : 'Ðозже'; 472 // ÐбновлÑем ÑекÑÑ ÐµÑли еÑÑÑ span 473 const textSpan = watchBtn.querySelector('span'); 474 if (textSpan) textSpan.textContent = inWatch ? 'Ðобавлено' : 'Ðозже'; 475 } 476 } 477 478 function handleWorkFavoriteClick(e) { 479 e.preventDefault(); 480 e.stopPropagation(); 481 482 const itemData = getWorkDataFromPage(); 483 if (!itemData) return; 484 485 const added = window.MediaFavorites.toggleFavorite(itemData); 486 updateWorkPageIcons(); 487 updateNavCounters(); 488 showToast(added ? 'Ðобавлено в избÑанное' : 'Удалено из избÑанного'); 489 } 490 491 function handleWorkWatchLaterClick(e) { 492 e.preventDefault(); 493 e.stopPropagation(); 494 495 const itemData = getWorkDataFromPage(); 496 if (!itemData) return; 497 498 const added = window.MediaFavorites.toggleWatchLater(itemData); 499 updateWorkPageIcons(); 500 updateNavCounters(); 501 showToast(added ? 'Ðобавлено в "Ðозже"' : 'Удалено из "Ðозже"'); 502 } 503 504 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 505 // ÐÐÐÐÐÐЯ СТРÐÐÐЦÐ: ÐÐРТÐЧÐÐ ÐÐÐÐÐТÐÐÐ 506 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 507 508 /** ÐолÑÑиÑÑ Ð´Ð°Ð½Ð½Ñе из каÑÑоÑки медиаÑеки на главной */ 509 function getItemDataFromFeedCard(container) { 510 return { 511 id: container.da
511taset.workId, 512 title: container.dataset.workTitle || 'Ðез названиÑ', 513 href: container.dataset.workHref || '#', 514 thumbUrl: container.dataset.workThumb || '', 515 author: container.dataset.workAuthor || '', 516 type: 'work', 517 description: '', 518 }; 519 } 520 521 /** ÐбновиÑÑ ÑоÑÑоÑние кнопок на каÑÑоÑке главной ÑÑÑаниÑÑ */ 522 function updateFeedCardState(container) { 523 const workId = container.dataset.workId; 524 if (!workId) return; 525 526 const inFav = window.MediaFavorites.isInFavorites(workId); 527 const inWatch = window.MediaFavorites.isInWatchLater(workId); 528 529 const favBtn = container.querySelector('.feed-card__media-btn--favorite'); 530 if (favBtn) { 531 favBtn.classList.toggle('active', inFav); 532 const icon = favBtn.querySelector('i'); 533 if (icon) icon.className = inFav ? 'fa-solid fa-heart' : 'fa-regular fa-heart'; 534 favBtn.title = inFav ? 'УдалиÑÑ Ð¸Ð· избÑанного' : 'РизбÑанное'; 535 } 536 537 const watchBtn = container.querySelector('.feed-card__media-btn--later'); 538 if (watchBtn) { 539 watchBtn.classList.toggle('active', inWatch); 540 const icon = watchBtn.querySelector('i'); 541 if (icon) icon.className = inWatch ? 'fa-solid fa-clock' : 'fa-regular fa-clock'; 542 watchBtn.title = inWatch ? 'УдалиÑÑ Ð¸Ð· "Ðозже"' : 'Ðозже'; 543 } 544 } 545 546 /** ÐбновиÑÑ Ð²Ñе каÑÑоÑки на главной */ 547 function updateAllFeedCards() { 548 document.querySelectorAll('.feed-card__media-actions[data-work-id]').forEach(updateFeedCardState); 549 } 550 551 function handleFeedFavoriteClick(e) { 552 e.preventDefault(); 553 e.stopPropagation(); 554 555 const container = e.currentTarget.closest('.feed-card__media-actions'); 556 if (!container) return; 557 558 const itemData = getItemDataFromFeedCard(container); 559 const added = window.MediaFavorites.toggleFavorite(itemData); 560 561 updateFeedCardState(container); 562 updateNavCounters(); 563 showToast(added ? 'Ðобавлено в избÑанное' : 'Удалено из избÑанного'); 564 } 565 566 function handleFeedWatchLaterClick(e) { 567 e.preventDefault(); 568 e.stopPropagation(); 569 570 const container = e.currentTarget.closest('.feed-card__media-actions'); 571 if (!container) return; 572 573 const itemData = getItemDataFromFeedCard(container); 574 const added = window.MediaFavorites.toggleWatchLater(itemData); 575 576 updateFeedCardState(container); 577 updateNavCounters(); 578 showToast(added ? 'Ðобавлено в "Ðозже"' : 'Удалено из "Ðозже"'); 579 } 580 581 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 582 // ÐÐÐЦÐÐÐÐÐÐЦÐЯ 583 // âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 584 585 function init() { 586 updateAllCards(); 587 updateAllFeedCards(); 588 updateWorkPageIcons(); 589 updateNavCounters(); 590 updateSidebarCounters(); 591 592 // Ðнопки избÑанного 593 document 594 .querySelectorAll('.stroki-card-icon-btn--favorite') 595 .forEach(btn => { 596 btn.addEventListener('click', handleFavoriteClick); 597 }); 598 599 // Ðнопки "ÐоÑмоÑÑÑ Ð¿Ð¾Ð·Ð¶Ðµ" 600 document 601 .querySelectorAll('.stroki-card-icon-btn--watch-later') 602 .forEach(btn => { 603 btn.addEventListener('click', handleWatchLaterClick); 604 }); 605 606 document.querySelectorAll('.stroki-card-btn--watch-later').forEach(btn => { 607 btn.addEventListener('click', handleWatchLaterClick); 608 }); 609 610 // Ðнопки ÑÐ´Ð°Ð»ÐµÐ½Ð¸Ñ 611 document.querySelectorAll('.stroki-card-remove-btn').forEach(btn => { 612 btn.addEventListener('click', handleRemoveFromList); 613 }); 614 615 // ÐобилÑнÑй / планÑеÑнÑй клик по каÑÑоÑке â ÑÑÐ°Ð·Ñ Ð¿ÐµÑÐµÑ Ð¾Ð´Ð¸Ð¼ на ÑÑÑаниÑÑ Ð¼Ð°ÑеÑиала. 616 // ÐÑполÑзÑем event delegation ÑÑÐ¾Ð±Ñ Ð»Ð¾Ð²Ð¸ÑÑ ÐºÐ»Ð¸ÐºÐ¸ и по динамиÑеÑки подгÑÑженнÑм каÑÑоÑкам. 617 document.addEventListener('click', function(e) { 618 const card = e.target.closest('.stroki-card'); 619 if (!card) return; 620 handleMobileCardClick({ target: e.target, currentTarget: card, preventDefault: e.preventDefault.bind(e) }); 621 }); 622 623 // Ðнопки на ÑÑÑаниÑе ÑабоÑÑ (поддеÑживаем оба клаÑÑа) 624 const workFavBtn = document.querySelector('.work-action-btn--favorite, .work-action-icon--favorite'); 625 if (workFavBtn) { 626 workFavBtn.addEventListener('click', handleWorkFavoriteClick); 627 } 628 629 const workWatchBtn = document.querySelector('.work-action-btn--later, .work-action-icon--later'); 630 if (workWatchBtn) { 631 workWatchBtn.addEventListener('click', handleWorkWatchLaterClick); 632 } 633 634 // Ðнопки на главной ÑÑÑаниÑе (каÑÑоÑки медиаÑеки) 635 document.querySelectorAll('.feed-card__media-btn--favorite').forEach(btn => { 636 btn.addEventListener('click', handleFeedFavoriteClick); 637 }); 638 639 document.querySelectorAll('.feed-card__media-btn--later').forEach(btn => { 640 btn.addEventListener('click', handleFeedWatchLaterClick); 641 }); 642 } 643 644 // ÐапÑÑк 645 if (document.readyState === 'loading') { 646 document.addEventListener('DOMContentLoaded', init); 647 } else { 648 init(); 649 } 650})();
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.