1// ÐлобалÑÐ½Ð°Ñ ÑÑнкÑÐ¸Ñ Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² календаÑÑ 2function issAddToCal(title, date, time, url) { 3 if (!date || typeof date !== 'string') return; 4 var startTime = time || '12:00'; 5 var startDate = date.replace(/-/g, ''); 6 var startHour = startTime.replace(':', ''); 7 8 // ÐÑпадаÑÑее Ð¼ÐµÐ½Ñ 9 var menu = document.createElement('div'); 10 menu.className = 'iss-cal-menu'; 11 menu.innerHTML = 12 '<div class="iss-cal-menu-title">ÐобавиÑÑ Ð² календаÑÑ</div>' + 13 '<a href="https://calendar.google.com/calendar/render?action=TEMPLATE' + 14 '&text=' + encodeURIComponent('СÑÑим: ' + title) + 15 '&dates=' + startDate + 'T' + startHour + '00/' + startDate + 'T' + startHour + '00' + 16 '&details=' + encodeURIComponent('СÑÑлка на ÑÑанÑлÑÑиÑ: ' + url) + 17 '" target="_blank" class="iss-cal-menu-item">Google Calendar</a>' + 18 '<a href="https://calendar.yandex.ru/new?title=' + encodeURIComponent('СÑÑим: ' + title) + 19 '&from=' + startDate + 'T' + startHour + '00' + 20 '&to=' + startDate + 'T' + startHour + '00' + 21 '&description=' + encodeURIComponent('СÑÑлка на ÑÑанÑлÑÑиÑ: ' + url) + 22 '" target="_blank" class="iss-cal-menu-item">Ð¯Ð½Ð´ÐµÐºÑ ÐалендаÑÑ</a>' + 23 '<a href="#" class="iss-cal-menu-item iss-cal-ics">Apple / Outlook</a>' + 24 '<button class="iss-cal-menu-close">×</button>'; 25 26 document.body.appendChild(menu); 27 28 // .ics Ñайл 29 menu.querySelector('.iss-cal-ics').addEventListener('click', function(e) { 30 e.preventDefault(); 31 var ics = 'BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\n' + 32 'DTSTART:' + startDate + 'T' + startHour + '00\n' + 33 'SUMMARY:СÑÑим: ' + title + '\n' + 34 'DESCRIPTION:' + url + '\n' + 35 'END:VEVENT\nEND:VCALENDAR'; 36 var blob = new Blob([ics], {type: 'text/calendar'}); 37 var a = document.createElement('a'); 38 a.href = URL.createObjectURL(blob); 39 a.download = 'stream.ics'; 40 a.click(); 41 menu.remove(); 42 }); 43 44 menu.querySelector('.iss-cal-menu-close').addEventListener('click', function() { 45 menu.remove(); 46 }); 47 48 setTimeout(function() { 49 document.addEventListener('click', function handler(e) { 50 if (!menu.contains(e.target)) { menu.remove(); document.removeEventListener('click', handler); } 51 }); 52 }, 100); 53} 54 55(function ($) { 56 'use strict'; 57 58 var MONTHS_RU = [ 59 'ЯнваÑÑ', 'ФевÑалÑ', 'ÐаÑÑ', 'ÐпÑелÑ', 'Ðай', 'ÐÑнÑ', 60 'ÐÑлÑ', 'ÐвгÑÑÑ', 'СенÑÑбÑÑ', 'ÐкÑÑбÑÑ', 'ÐоÑбÑÑ', 'ÐекабÑÑ' 61 ]; 62 63 // SVG-иконки, иÑполÑзÑемÑе в каÑÑоÑÐºÐ°Ñ ÑÑÑима. ÐбÑÑÐ²Ð»ÐµÐ½Ñ Ð² Ñамом наÑале, 64 // ÑÑÐ¾Ð±Ñ Ð±ÑÑÑ Ð´Ð¾ÑÑÑпнÑми во вÑÐµÑ Ð¾Ð±ÑабоÑÑÐ¸ÐºÐ°Ñ (вклÑÑÐ°Ñ submit ÑоÑмÑ). 65 var REMIND_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>'; 66 var PLAY_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="vertical-align:-2px;margin-right:4px"><polygon points="5 3 19 12 5 21 5 3"/></svg>'; 67 var TRASH_SVG = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>'; 68 69 function escapeHtml(text) { 70 var div = document.createElement('div'); 71 div.textContent = text; 72 return div.innerHTML; 73 } 74 75 // ÐозвÑаÑÐ°ÐµÑ URL ÑолÑко еÑли он http(s) â инаÑе пÑÑÑÑÑ ÑÑÑокÑ. 76 // ÐаÑиÑÐ°ÐµÑ Ð¾Ñ javascript:/data:/vbscript: пÑи вÑÑавке в href/src. 77 function safeUrl(url) { 78 if (typeof url !== 'string' || !url) return ''; 79 var s = url.trim().toLowerCase(); 80 if (s.indexOf('http://') === 0 || s.indexOf('https://') === 0) return url; 81 return ''; 82 } 83 84 // ======================================== 85 // ÐодалÑное окно «ÐÑÐ´Ñ ÑÑÑимиÑÑ» 86 // ======================================== 87 88 $(document).on('click', '.iss-btn-stream', function () { 89 var gameId = $(this).data('game-id'); 90 $('#iss-modal-' + gameId).fadeIn(200); 91 }); 92 93 $(document).on('click', '.iss-modal-close, .iss-modal-overlay', function () { 94 $(this).closest('.iss-modal').fadeOut(200); 95 }); 96 97 $(document).on('submit', '.iss-stream-form', function (e) { 98 e.preventDefault(); 99 var $form = $(this); 100 var $btn = $form.find('.iss-btn-submit'); 101 102 var dateVal = $form.find('[name="stream_date"]').val(); 103 var timeVal = $form.find('[name="stream_time"]').val();
104 if (!dateVal) { 105 alert('УкажиÑе даÑÑ ÑÑÑима'); 106 $form.find('[name="stream_date"]').focus(); 107 return; 108 } 109 if (!timeVal) { 110 alert('УкажиÑе вÑÐµÐ¼Ñ ÑÑÑима'); 111 $form.find('[name="stream_time"]').focus(); 112 return; 113 } 114 115 $btn.prop('disabled', true).text('Ð¡Ð¾Ñ Ñанение...'); 116 117 $.post(issAjax.url, { 118 action: 'stream_create', 119 nonce: issAjax.nonce, 120 game_id: $form.data('game-id'), 121 festival: $form.data('festival'), 122 stream_date: $form.find('[name="stream_date"]').val(), 123 stream_time: $form.find('[name="stream_time"]').val(), 124 stream_url: $form.find('[name="stream_url"]').val() 125 }, function (res) { 126 if (res.success) { 127 $form.closest('.iss-modal').fadeOut(200); 128 $form[0].reset(); 129 130 // РендеÑим каÑÑоÑÐºÑ ÑеÑез обÑÑÑ ÑÑнкÑÐ¸Ñ â ÑÑÑÑкÑÑÑа ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ 131 // Ñ ÑеÑвеÑнÑм ÑоÑÑкодом (Ñа же даÑа/вÑемÑ, ÑÐ¾Ñ Ð¶Ðµ DOM). 132 var d = res.data; 133 var cardData = d.card || null; 134 var $container = $('.iss-game-streams'); 135 if (!$container.length) { 136 var $modal = $form.closest('.iss-modal');
137 $container = $('<div class="iss-game-streams gray-section"><h3 class="iss-game-streams-title">ÐапланиÑованнÑе ÑÑÑимÑ</h3></div>'); 138 $modal.after($container); 139 } 140 141 if (cardData) { 142 // Ðа ÑÑÑаниÑе игÑÑ Ð¾Ð±Ð»Ð¾Ð¶ÐºÑ Ð¸Ð³ÑÑ Ð½Ðµ показÑваем (ÑÑо Ð²ÐµÐ´Ñ ÐµÑ Ð¶Ðµ ÑÑÑаниÑа). 143 var $newCard = $(renderStreamCard(cardData, false)).hide(); 144 insertCardSorted($container, $newCard, cardData); 145 $newCard.slideDown(300); 146 } 147 148 // ÐбновиÑÑ ÐºÐ°Ð»ÐµÐ½Ð´Ð°ÑÑ, еÑли на ÑÑÑаниÑе 149 if (window.issCalendar) { 150 window.issCalendar.load(); 151 } 152 } else { 153 alert(res.data || 'ÐÑибка'); 154 } 155 $btn.prop('disabled', false).text('ÐапланиÑоваÑÑ'); 156 }).fail(function () { 157 alert('ÐÑибка ÑеÑи'); 158 $btn.prop('disabled', false).text('ÐапланиÑоваÑÑ'); 159 }); 160 }); 161 162 // ======================================== 163 // Ðкладки «ÐалендаÑÑ» / «Ðои ÑÑÑимÑ» 164 // ======================================== 165 166 $(document).on('click', '.iss-tab', function () { 167 var tab = $(this).data('tab'); 168 $('.iss-tab').removeClass('active'); 169 $(this).addClass('active'); 170 $('.iss-tab-content').hide(); 171 $('#iss-tab-' + tab).show(); 172 173 if (tab === 'my-streams') { 174 loadMyStreams(); 175 } 176 }); 177 178 function loadMyStreams() { 179 var $list = $('.iss-my-streams-list'); 180 var $empty = $('.iss-my-streams-empty'); 181 $list.html('<div style="text-align:center;padding:20px;color:#999;">ÐагÑÑзка...</div>'); 182 $empty.hide(); 183 184 $.post(issAjax.url, { 185 action: 'stream_my_streams', 186 nonce: issAjax.nonce 187 }, function (res) { 188 $list.empty(); 189 var items = (res && res.success && Array.isArray(res.data)) ? res.data : []; 190 if (items.length > 0) { 191 items.forEach(function (s) { 192 $list.append(renderStreamCard(s, true)); 193 }); 194 } else { 195 $list.empty(); 196 $empty.show(); 197 } 198 }); 199 } 200 201 // ======================================== 202 // Ðнопка «ÐапомниÑÑ» â добавиÑÑ Ð² календаÑÑ 203 // ======================================== 204 205 $(document).on('click', '.iss-stream-plan-btn', function () { 206 var $btn = $(this); 207 issAddToCal( 208 $btn.data('game-title') || '', 209 $btn.data('date') || '', 210 $btn.data('time') || '', 211 $btn.data('url') || '' 212 ); 213 }); 214 215 // ======================================== 216 // ÐÑмена ÑÑÑима 217 // ======================================== 218 219 $(document).on('click', '.iss-stream-cancel', function () { 220 if (!confirm('ÐÑмениÑÑ ÑÑÑим?')) return; 221 var streamId = $(this).data('stream-id'); 222 var $card = $(this).closest('.iss-stream-card'); 223 224 $.post(issAjax.url, { 225 action: 'stream_cancel', 226 nonce: issAjax.nonce, 227 stream_id: streamId 228 }, function (res) { 229 if (!res.success) { 230 alert(res.data || 'ÐÑибка'); 231 return; 232 } 233 // СеÑÐ²ÐµÑ Ð¼Ð¾Ð³ ÑизиÑеÑки ÑдалиÑÑ ÑÑÑим (оÑмена в окне iss_delete_hours) 234 // â в ÑÑом ÑлÑÑае ÑбиÑаем каÑÑоÑÐºÑ Ñеликом. 235 if (res.data && res.data.deleted) {
236 $card.slideUp(300, function () { $(this).remove(); }); 237 if (window.issCalendar) window.issCalendar.load(); 238 return; 239 } 240 $card.addClass('iss-stream-status-cancelled'); 241 $card.find('.iss-stream-cancel').remove(); 242 $card.find('.iss-stream-plan-btn').remove(); 243 $card.find('.iss-stream-platform-link').remove(); 244 // Ðейбл «ÐÑменÑн» добавлÑем в наÑало actions â ÑооÑвеÑÑÑвÑÐµÑ PHP-ÑаблонÑ. 245 $card.find('.iss-stream-card-actions') 246 .prepend($('<span class="iss-stream-cancelled-label"></span>').text('ÐÑменÑн')); 247 }); 248 }); 249 250 // ======================================== 251 // Удаление ÑÑÑима (в ÑеÑение 1 ÑаÑа) 252 // ======================================== 253 254 $(document).on('click', '.iss-stream-delete', function () { 255 if (!confirm('УдалиÑÑ ÑÑÑим полноÑÑÑÑ? ÐÑо дейÑÑвие нелÑÐ·Ñ Ð¾ÑмениÑÑ.')) return; 256 var streamId = $(this).data('stream-id'); 257 var $card = $(this).closest('.iss-stream-card'); 258 259 $.post(issAjax.url, { 260 action: 'stream_delete', 261 nonce: issAjax.nonce, 262 stream_id: streamId 263 }, function (res) { 264 if (res.success) {
265 $card.slideUp(300, function () { $(this).remove(); }); 266 } else { 267 alert(res.data || 'ÐÑибка'); 268 } 269 }); 270 }); 271 272 // ======================================== 273 // ÐалендаÑÑ 274 // ======================================== 275 276 var $wrap = $('.iss-calendar-wrap'); 277 if (!$wrap.length) return; 278 279 var now = new Date(); 280 var currentYear = now.getFullYear(); 281 var currentMonth = now.getMonth(); // 0-based 282 var festival = $wrap.data('festival') || ''; 283 var allStreams = []; 284 var filterGame = ''; 285 var filterStreamer = ''; 286 287 window.issCalendar = { load: loadMonth }; 288 289 // ÐавигаÑÐ¸Ñ 290 $wrap.on('click', '.iss-prev-month', function () { 291 currentMonth--; 292 if (currentMonth < 0) { currentMonth = 11; currentYear--; } 293 loadMonth(); 294 }); 295 $wrap.on('click', '.iss-next-month', function () { 296 currentMonth++; 297 if (currentMonth > 11) { currentMonth = 0; currentYear++; } 298 loadMonth(); 299 }); 300 301 // ФилÑÑÑÑ â показÑваем/ÑкÑÑваем ÐºÐ½Ð¾Ð¿ÐºÑ Â«ÐайÑи» 302 var $btnSearch = $('#iss-btn-search'); 303 var $searchResults = $wrap.find('.iss-search-results'); 304 305 $wrap.on('input', '.iss-filter-input', function () { 306 var gameVal = $('#iss-filter-game').val().trim(); 307 var streamerVal = $('#iss-filter-streamer').val().trim(); 308 309 if (gameVal || streamerVal) { 310 $btnSearch.show(); 311 } else { 312 $btnSearch.hide(); 313 $searchResults.hide(); 314 // ÐоÑÑÑанавливаем ÑилÑÑÑ ÐºÐ°Ð»ÐµÐ½Ð´Ð°ÑÑ 315 filterGame = ''; 316 filterStreamer = ''; 317 renderCalendar(); 318 } 319 320 // ÐокалÑнÑй ÑилÑÑÑ ÐºÐ°Ð»ÐµÐ½Ð´Ð°ÑÑ ÑекÑÑего меÑÑÑа 321 filterGame = gameVal.toLowerCase(); 322 filterStreamer = streamerVal.toLowerCase(); 323 renderCalendar(); 324 }); 325 326 // Ðнопка «ÐайÑи» â глобалÑнÑй поиÑк по вÑем меÑÑÑам 327 $btnSearch.on('click', function () { 328 var gameVal = $('#iss-filter-game').val().trim(); 329 var streamerVal = $('#iss-filter-streamer').val().trim(); 330 if (!gameVal && !streamerVal) return; 331 332 $btnSearch.prop('disabled', true).text('ÐоиÑк...'); 333 334 $.post(issAjax.url, { 335 action: 'stream_search', 336 nonce: issAjax.nonce, 337 game: gameVal, 338 streamer: streamerVal, 339 festival: festival 340 }, function (res) { 341 $btnSearch.prop('disabled', false).text('ÐайÑи'); 342 var items = (res && res.success && Array.isArray(res.data)) ? res.data : []; 343 if (items.length > 0) { 344 var $list = $searchResults.find('.iss-search-results-list').empty(); 345 items.forEach(function (s) { 346 $list.append(renderStreamCard(s)); 347 }); 348 $searchResults.slideDown(200); 349 } else { 350 $searchResults.find('.iss-search-results-list').html( 351 '<div class="iss-no-results">СÑÑÐ¸Ð¼Ñ Ð½Ðµ найденÑ</div>' 352 ); 353 $searchResults.slideDown(200); 354 } 355 }).fail(function () { 356 $btnSearch.prop('disabled', false).text('ÐайÑи'); 357 }); 358 }); 359 360 function loadMonth() { 361 $wrap.find('.iss-current-month').text(MONTHS_RU[currentMonth] + ' ' + currentYear); 362 $wrap.find('.iss-day-details').hide(); 363 364 $.post(issAjax.url, { 365 action: 'stream_get_by_month', 366 nonce: issAjax.nonce, 367 year: currentYear, 368 month: currentMonth + 1, 369 festival: festival 370 }, function (res) { 371 if (res.success) { 372 allStreams = res.data || []; 373 renderCalendar(); 374 } else { 375 console.error('ISS: оÑибка загÑÑзки ÑÑÑимов', res.data); 376 allStreams = []; 377 renderCalendar(); 378 } 379 }).fail(function (xhr, status, err) { 380 console.error('ISS: ÑеÑÐµÐ²Ð°Ñ Ð¾Ñибка', status, err); 381 allStreams = [];
382 renderCalendar(); 383 }); 384 } 385 386 function getFilteredStreams() { 387 return allStreams.filter(function (s) { 388 if (filterGame && (s.game_title || '').toLowerCase().indexOf(filterGame) === -1) return false; 389 if (filterStreamer && (s.streamer_name || '').toLowerCase().indexOf(filterStreamer) === -1) return false; 390 return true; 391 }); 392 } 393 394 function renderCalendar() { 395 var streams = getFilteredStreams(); 396 var $days = $wrap.find('.iss-calendar-days'); 397 $days.empty(); 398 399 var firstDay = new Date(currentYear, currentMonth, 1); 400 var lastDay = new Date(currentYear, currentMonth + 1, 0); 401 var startDow = (firstDay.getDay() + 6) % 7; // Ðн = 0 402 var daysInMonth = lastDay.getDate(); 403 404 var today = new Date(); 405 var todayStr = today.getFullYear() + '-' + 406 String(today.getMonth() + 1).padStart(2, '0') + '-' + 407 String(today.getDate()).padStart(2, '0'); 408 409 // ÐÑÑппиÑÑем ÑÑÑÐ¸Ð¼Ñ Ð¿Ð¾ даÑе (ÑÑÑÐ¸Ð¼Ñ Ð±ÐµÐ· даÑÑ Ð½Ðµ показÑваем в ÑаÑпиÑании) 410 var streamsByDate = {}; 411 streams.forEach(function (s) { 412 if (!s.date) return; 413 if (!streamsByDate[s.date]) streamsByDate[s.date] = []; 414 streamsByDate[s.date].push(s); 415 }); 416 417 // ÐÑÑÑÑе ÑÑейки до 1 ÑиÑла 418 for (var i = 0; i < startDow; i++) { 419 $days.append('<div class="iss-day iss-other-month"></div>'); 420 } 421 422 // Ðни меÑÑÑа 423 for (var d = 1; d <= daysInMonth; d++) { 424 var dateStr = currentYear + '-' + 425 String(currentMonth + 1).padStart(2, '0') + '-' + 426 String(d).padStart(2, '0'); 427 428 var dayStreams = streamsByDate[dateStr] || []; 429 var isToday = dateStr === todayStr; 430 431 var html = '<div class="iss-day' + (isToday ? ' iss-today' : '') + '" data-date="' + dateStr + '">'; 432 html += '<div class="iss-day-number">' + d + '</div>';
433 if (dayStreams.length > 0) { 434 for (var j = 0; j < Math.min(dayStreams.length, 3); j++) { 435 html += '<span class="iss-day-dot"></span>'; 436 } 437 html += '<div class="iss-day-count">' + dayStreams.length + ' ÑÑÑим' + pluralize(dayStreams.length) + '</div>'; 438 } 439 html += '</div>'; 440 $days.append(html); 441 } 442 443 // ÐвÑопоказ ÑÑÑимов за ÑÐµÐ³Ð¾Ð´Ð½Ñ â ÑолÑко еÑли оÑкÑÑÑ ÑекÑÑий меÑÑÑ. 444 // Ðажно: .iss-today Ñакже навеÑиваеÑÑÑ Ð½Ð° ÑÑÐµÐ¹ÐºÑ ÑегоднÑÑнего Ð´Ð½Ñ Ð² ÑеÑке; 445 // здеÑÑ Ð½Ñжен именно авÑоблок, поÑÑÐ¾Ð¼Ñ Ð¸ÑклÑÑаем ÑÑейки календаÑÑ ÑеÑез :not(.iss-day). 446 var $today = $wrap.find('.iss-today').not('.iss-day'); 447 var isCurrentMonth = (currentMonth === today.getMonth() && currentYear === today.getFullYear()); 448 if (isCurrentMonth) { 449 var todayStreams = streams.filter(function (s) { return s.date === todayStr; }); 450 $today.find('.iss-today-title').text('СÑÑÐ¸Ð¼Ñ ÑегоднÑ'); 451 var $todayList = $today.find('.iss-today-list').empty(); 452 if (todayStreams.length > 0) { 453 todayStreams.forEach(function (s) { $todayList.append(renderStreamCard(s)); }); 454 $today.find('.iss-today-empty').hide(); 455 $todayList.show(); 456 } else { 457 $todayList.hide(); 458 $today.find('.iss-today-empty').show(); 459 } 460 $today.show(); 461 } else { 462 $today.hide(); 463 } 464 } 465 466 // Ðлик на Ð´ÐµÐ½Ñ 467 $wrap.on('click', '.iss-day:not(.iss-other-month)', function () { 468 var $clicked = $(this); 469 var date = $clicked.data('date'); 470 if (!date) return; 471 472 // ÐомеÑаем кликнÑÑÑÑ ÑÑÐµÐ¹ÐºÑ Ð°ÐºÑивной 473 $wrap.find('.iss-day.iss-day-selected').removeClass('iss-day-selected'); 474 $clicked.addClass('iss-day-selected'); 475 476 // Ðлик по ÑегоднÑÑÐ½ÐµÐ¼Ñ Ð´Ð½Ñ â ÑкÑоллим к авÑоблокÑ, а не дÑблиÑÑем деÑали. 477 // ÐÑÑÑем .iss-day-details, ÑÑÐ¾Ð±Ñ Ð½Ðµ оÑÑалоÑÑ ÑÑаÑого ÑодеÑжимого Ð¾Ñ Ð´ÑÑгого днÑ. 478 var now = new Date(); 479 var nowStr = now.getFullYear() + '-' + 480 String(now.getMonth() + 1).padStart(2, '0') + '-' + 481 String(now.getDate()).padStart(2, '0'); 482 if (date === nowStr) { 483 var $todayBlock = $wrap.find('.iss-today').not('.iss-day'); 484 $wrap.find('.iss-day-details').hide(); 485 if ($todayBlock.is(':visible')) { 486 $('html, body').animate({ 487 scrollTop: $todayBlock.offset().top - 100 488 }, 300); 489 return; 490 } 491 } 492 493 var streams = getFilteredStreams().filter(function (s) { return s.date === date; }); 494 var $details = $wrap.find('.iss-day-details'); 495 496 if (streams.length === 0) { 497 $details.hide(); 498 return; 499 } 500 501 var parts = date.split('-'); 502 $details.find('.iss-details-title').text( 503 parseInt(parts[2]) + ' ' + MONTHS_RU[parseInt(parts[1]) - 1].toLowerCase() + ' ' + parts[0] 504 ); 505 506 var $list = $details.find('.iss-details-list').empty(); 507 streams.forEach(function (s) { 508 $list.append(renderStreamCard(s)); 509 }); 510 511 $details.slideDown(200, function () { 512 $('html, body').animate({ 513 scrollTop: $details.offset().top - 100 514 }, 300); 515 }); 516 }); 517 518 // СоÑÑиÑовоÑнÑй клÑÑ ÐºÐ°ÑÑоÑки: "YYYY-MM-DD HH:MM" из data-аÑÑибÑÑов 519 // или из обÑекÑа даннÑÑ . Ðез даÑÑ/вÑемени â '9999-12-31 23:59' (в конеÑ). 520 function streamSortKey(s) { 521 var d = (s && s.date) ? String(s.date) : '9999-12-31'; 522 var t = (s && s.time) ? String(s.time) : '23:59'; 523 return d + ' ' + t; 524 } 525 526 // ÐÐ·Ð²Ð»ÐµÐºÐ°ÐµÑ ÐºÐ»ÑÑ ÑоÑÑиÑовки из ÑÑÑеÑÑвÑÑÑей каÑÑоÑки в DOM â 527 // и ÑеÑвеÑнÑй ÑоÑÑкод, и renderStreamCard ÑÑавÑÑ data-stream-key на коÑне каÑÑоÑки.
528 function streamSortKeyFromCard($card) { 529 return $card.attr('data-stream-key') || '9999-12-31 23:59'; 530 } 531 532 // ÐÑÑавлÑÐµÑ $newCard в $container в пÑавилÑное меÑÑо по возÑаÑÑÐ°Ð½Ð¸Ñ Ð´Ð°ÑÑ/вÑемени. 533 function insertCardSorted($container, $newCard, newData) { 534 var newKey = streamSortKey(newData); 535 var inserted = false; 536 $container.find('.iss-stream-card').each(function () { 537 var $existing = $(this); 538 var existingKey = streamSortKeyFromCard($existing); 539 if (newKey < existingKey) { 540 $newCard.insertBefore($existing); 541 inserted = true; 542 return false; // break 543 } 544 }); 545 if (!inserted) $container.append($newCard); 546 } 547 548 // Ð ÐµÐ½Ð´ÐµÑ ÐºÐ°ÑÑоÑки ÑÑÑима. ÐÑли showCover=false â ÑоÑÐ¼Ð°Ñ ÑÑÑаниÑÑ Ð¸Ð³ÑÑ 549 // (ÑооÑвеÑÑÑвÑÐµÑ ÑеÑвеÑÐ½Ð¾Ð¼Ñ ÑÐ°Ð±Ð»Ð¾Ð½Ñ class-stream-shortcodes.php), инаÑе â 550 // ÑоÑÐ¼Ð°Ñ ÐºÐ°Ð»ÐµÐ½Ð´Ð°ÑÑ (Ñ Ð¾Ð±Ð»Ð¾Ð¶ÐºÐ¾Ð¹ игÑÑ Ð¸ названием).
551 function renderStreamCard(s, showCover) { 552 if (showCover === undefined) showCover = true; 553 var statusClass = s.status === 'completed' ? 'iss-stream-status-completed' : (s.status === 'cancelled' ? 'iss-stream-status-cancelled' : ''); 554 555 var $card = $('<div class="iss-stream-card"></div>'); 556 if (statusClass) $card.addClass(statusClass); 557 $card.attr('data-stream-key', streamSortKey(s)); 558 559 var gameUrl = safeUrl(s.game_url); 560 var gameCover = safeUrl(s.game_cover); 561 var streamUrl = safeUrl(s.url); 562 var streamerUrl = safeUrl(s.streamer_url); 563 564 // ÐÑевÑÑ Ð¸Ð³ÑÑ â ÑолÑко в ÑоÑмаÑе календаÑÑ. 565 if (showCover && gameCover) { 566 var $cover = $('<a class="iss-stream-card-cover"></a>').attr('href', gameUrl); 567 $('<img />').attr('src', gameCover).attr('alt', s.game_title || '').appendTo($cover); 568 $cover.appendTo($card); 569 } 570 571 var $body = $('<div class="iss-stream-card-body"></div>').appendTo($card); 572 573 // Ðа ÑÑÑаниÑе игÑÑ (showCover=false) поÑÑдок: clock â info â actions. 574 // РкалендаÑе (showCover=true): info â clock â actions. 575 function renderClock() { 576 if (s.date || s.time) { 577 var $clock = $('<div class="iss-stream-card-clock"></div>'); 578 if (s.date) { 579 var parts = String(s.date).split('-'); 580 // ФоÑÐ¼Ð°Ñ "DD.MM" Ñ Ð²ÐµÐ´ÑÑим нÑлÑм â ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ Ñ PHP date('d.m', ...) 581 $('<div class="iss-clock-date"></div>').text(parts[2] + '.' + parts[1]).appendTo($clock); 582 } 583 if (s.time) { 584 $('<div class="iss-clock-time"></div>').text(s.time).appendTo($clock); 585 } 586 $clock.appendTo($body); 587 } else { 588 // ТекÑÑ "TBD" ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ Ñ PHP-Ñаблоном (class-stream-shortcodes.php). 589 var $tbd = $('<div class="iss-stream-card-clock iss-clock-tbd"></div>'); 590 $('<div class="iss-clock-date"></div>').text('TBD').appendTo($tbd); 591 $tbd.appendTo($body); 592 } 593 } 594 595 function renderInfo() { 596 var $info = $('<div class="iss-stream-card-info"></div>').appendTo($body); 597 // Ðазвание игÑÑ Ð¿Ð¾ÐºÐ°Ð·Ñваем ÑолÑко в ÑоÑмаÑе календаÑÑ â 598 // на ÑÑÑаниÑе игÑÑ ÑÑо и Ñак ÑекÑÑÐ°Ñ ÑÑÑаниÑа. 599 if (showCover) { 600 var $gameTitle = $('<div class="iss-stream-card-game"></div>'); 601 $('<a></a>').attr('href', gameUrl).text(s.game_title || '').appendTo($gameTitle); 602 $gameTitle.appendTo($info); 603 } 604 // ÐÐ¼Ñ ÑÑÑимеÑа â ÑÑÑлка на пÑоÑилÑ, еÑли url пеÑедан, инаÑе пÑоÑÑо ÑекÑÑ. 605 var $streamer = $('<div class="iss-stream-card-streamer"></div>'); 606 if (streamerUrl) { 607 $('<a></a>').attr('href', streamerUrl).text(s.streamer_name || '').appendTo($streamer); 608 } else { 609 $streamer.text(s.streamer_name || ''); 610 } 611 $streamer.appendTo($info); 612 } 613 614 if (showCover) { 615 renderInfo(); 616 renderClock(); 617 } else { 618 renderClock(); 619 renderInfo(); 620 } 621 622 // Ðа ÑÑÑаниÑе игÑÑ actions ÑенÑÑиÑÑÑÑÑÑ. 623 var $actions = $('<div class="iss-stream-card-actions"></div>'); 624 if (!showCover) $actions.addClass('iss-actions-center'); 625 $actions.appendTo($body); 626 627 if (s.status === 'cancelled') { 628 $('<span class="iss-stream-cancelled-label"></span>').text('ÐÑменÑн').appendTo($actions); 629 } 630 631 // Ðнопка «ÐапомниÑÑ» â паÑамеÑÑÑ Ð² data-* аÑÑибÑÑÐ°Ñ , обÑабоÑÑик ÑеÑез делегиÑование. 632 if (s.date && s.status === 'planned') { 633 $('<button class="iss-stream-link iss-stream-plan-btn" type="button"></button>') 634 .attr('data-game-title', s.game_title || '') 635 .attr('data-date', s.date || '') 636 .attr('data-time', s.time || '') 637 .attr('data-url', streamUrl)
638 .html(REMIND_SVG + 'ÐапомниÑÑ') 639 .appendTo($actions); 640 } 641 642 // СÑÑлка на ÑÑанÑлÑÑÐ¸Ñ 643 if (streamUrl && s.status !== 'cancelled') { 644 $('<a class="iss-stream-platform-link" target="_blank"></a>') 645 .attr('href', streamUrl) 646 .html(PLAY_SVG + 'ТÑанÑлÑÑиÑ') 647 .appendTo($actions); 648 } 649 650 if (s.is_owner && s.status === 'planned') { 651 $('<button class="iss-stream-cancel" type="button">ÐÑмениÑÑ</button>') 652 .attr('data-stream-id', s.id) 653 .appendTo($actions); 654 } 655 // Ðнопка «УдалиÑÑ» â ÑолÑко в ÑоÑмаÑе календаÑÑ (на ÑÑÑаниÑе игÑÑ ÐµÑ Ð½Ðµ бÑло в Ñаблоне). 656 if (showCover && s.can_delete) { 657 $('<button class="iss-stream-delete" type="button" title="УдалиÑÑ ÑÑÑим"></button>') 658 .attr('data-stream-id', s.id) 659 .html(TRASH_SVG) 660 .appendTo($actions); 661 } 662 663 // ÐозвÑаÑаем outerHTML â каÑÑоÑка вÑÑавлÑеÑÑÑ Ð² DOM ÑеÑез .append/.appendTo, 664 // и внÑÑÑи Ð½ÐµÑ Ð½ÐµÐ±ÐµÐ·Ð¾Ð¿Ð°ÑнÑÑ ÐºÐ¾Ð½ÐºÐ°ÑенаÑий полÑзоваÑелÑÑÐºÐ¸Ñ ÑÑÑок. 665 return $card[0].outerHTML; 666 } 667 668 function formatDate(dateStr) { 669 var parts = dateStr.split('-'); 670 return parseInt(parts[2]) + '.' + parts[1] + '.' + parts[0]; 671 } 672 673 function pluralize(n) { 674 if (n % 10 === 1 && n % 100 !== 11) return ''; 675 if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) return 'а'; 676 return 'ов'; 677 } 678 679 // ÐапÑÑк 680 loadMonth(); 681 682})(jQuery);
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.