Spaces:
Running
Running
Delete static/wc_shorts_slide.js, static/wc_shorts_inject.js
Browse files- static/wc_shorts_inject.js +0 -76
- static/wc_shorts_slide.js +0 -597
static/wc_shorts_inject.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* WC Shorts Inject — Chạy SAU app_v2.js
|
| 3 |
-
* Tự tạo anchor DIV rồi render WC Shorts slide TRÊN World Cup section
|
| 4 |
-
*/
|
| 5 |
-
(function(){
|
| 6 |
-
'use strict';
|
| 7 |
-
|
| 8 |
-
function inject(){
|
| 9 |
-
// Tìm WC section
|
| 10 |
-
const wcSection = document.getElementById('wc2026-live-section');
|
| 11 |
-
const homeEl = document.getElementById('view-home');
|
| 12 |
-
if(!homeEl) return;
|
| 13 |
-
|
| 14 |
-
// Đã inject?
|
| 15 |
-
if(document.getElementById('wc-shorts-slide-section')) return;
|
| 16 |
-
|
| 17 |
-
// Đảm bảo renderWCSSlideSection đã load
|
| 18 |
-
if(typeof renderWCSSlideSection !== 'function'){
|
| 19 |
-
// Chờ rồi thử lại
|
| 20 |
-
setTimeout(inject, 500);
|
| 21 |
-
return;
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
// Tạo container rồi render vào
|
| 25 |
-
const div = document.createElement('div');
|
| 26 |
-
div.id = 'wc-shorts-slide-container-tmp';
|
| 27 |
-
div.style.display = 'none';
|
| 28 |
-
|
| 29 |
-
// Insert TRƯỚC WC section, hoặc vào đầu home
|
| 30 |
-
if(wcSection && wcSection.parentNode){
|
| 31 |
-
wcSection.parentNode.insertBefore(div, wcSection);
|
| 32 |
-
} else {
|
| 33 |
-
homeEl.insertBefore(div, homeEl.firstChild);
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
// Render slide section vào div
|
| 37 |
-
renderWCSSlideSection(div);
|
| 38 |
-
|
| 39 |
-
// Bỏ wrapper thừa, di chuyển section ra đúng vị trí
|
| 40 |
-
const section = div.querySelector('.wc-shorts-slide-section');
|
| 41 |
-
if(section){
|
| 42 |
-
section.id = 'wc-shorts-slide-section';
|
| 43 |
-
if(wcSection && wcSection.parentNode){
|
| 44 |
-
wcSection.parentNode.insertBefore(section, wcSection);
|
| 45 |
-
}
|
| 46 |
-
div.remove();
|
| 47 |
-
}
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
// Chạy khi DOM sẵn sàng
|
| 51 |
-
if(document.readyState === 'loading'){
|
| 52 |
-
document.addEventListener('DOMContentLoaded', ()=> setTimeout(inject, 800));
|
| 53 |
-
} else {
|
| 54 |
-
setTimeout(inject, 800);
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
// Cũng chạy sau khi loadHome xong
|
| 58 |
-
const origLoadHome = window.loadHome;
|
| 59 |
-
if(origLoadHome){
|
| 60 |
-
window.loadHome = function(){
|
| 61 |
-
const result = origLoadHome.apply(this, arguments);
|
| 62 |
-
setTimeout(inject, 200);
|
| 63 |
-
setTimeout(inject, 800);
|
| 64 |
-
setTimeout(inject, 2000);
|
| 65 |
-
return result;
|
| 66 |
-
};
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
-
// Fallback: retry nhiều lần
|
| 70 |
-
let retries = 0;
|
| 71 |
-
const retryIv = setInterval(()=>{
|
| 72 |
-
retries++;
|
| 73 |
-
inject();
|
| 74 |
-
if(retries > 20) clearInterval(retryIv);
|
| 75 |
-
}, 1000);
|
| 76 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static/wc_shorts_slide.js
DELETED
|
@@ -1,597 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* WC Shorts Slide — A standalone slide section ABOVE the World Cup section
|
| 3 |
-
* TikTok-style vertical feed with autoplay, titles, interaction buttons
|
| 4 |
-
* Each short = 1 fullscreen slide with scroll-snap
|
| 5 |
-
*/
|
| 6 |
-
(function(){
|
| 7 |
-
'use strict';
|
| 8 |
-
|
| 9 |
-
// ===== Inject CSS =====
|
| 10 |
-
if(document.getElementById('wc-shorts-slide-css')) return;
|
| 11 |
-
const style = document.createElement('style');
|
| 12 |
-
style.id = 'wc-shorts-slide-css';
|
| 13 |
-
style.textContent = `
|
| 14 |
-
/* === WC SHORTS SLIDE SECTION (above WC section) === */
|
| 15 |
-
.wc-shorts-slide-section {
|
| 16 |
-
margin: 6px 4px;
|
| 17 |
-
background: linear-gradient(135deg, #000 0%, #0a0a2e 50%, #0d1117 100%);
|
| 18 |
-
border: 1px solid #1a3a5a;
|
| 19 |
-
border-radius: 10px;
|
| 20 |
-
overflow: hidden;
|
| 21 |
-
}
|
| 22 |
-
.wc-shorts-slide-header {
|
| 23 |
-
display: flex;
|
| 24 |
-
align-items: center;
|
| 25 |
-
justify-content: space-between;
|
| 26 |
-
padding: 10px 12px 6px;
|
| 27 |
-
}
|
| 28 |
-
.wc-shorts-slide-header h3 {
|
| 29 |
-
font-size: 15px;
|
| 30 |
-
color: #fff;
|
| 31 |
-
font-weight: 800;
|
| 32 |
-
margin: 0;
|
| 33 |
-
display: flex;
|
| 34 |
-
align-items: center;
|
| 35 |
-
gap: 6px;
|
| 36 |
-
}
|
| 37 |
-
.wc-shorts-slide-header h3 .emoji { font-size: 18px; }
|
| 38 |
-
.wc-shorts-slide-header .badge-live {
|
| 39 |
-
font-size: 9px;
|
| 40 |
-
color: #e74c3c;
|
| 41 |
-
font-weight: 700;
|
| 42 |
-
animation: wc-slide-pulse 1.5s infinite;
|
| 43 |
-
}
|
| 44 |
-
@keyframes wc-slide-pulse { 0%,100%{opacity:1} 50%{opacity:.4} }
|
| 45 |
-
.wc-shorts-slide-header .view-all-btn {
|
| 46 |
-
background: #0b6bcb;
|
| 47 |
-
border: none;
|
| 48 |
-
color: #fff;
|
| 49 |
-
padding: 5px 12px;
|
| 50 |
-
border-radius: 12px;
|
| 51 |
-
font-size: 10px;
|
| 52 |
-
font-weight: 700;
|
| 53 |
-
cursor: pointer;
|
| 54 |
-
white-space: nowrap;
|
| 55 |
-
}
|
| 56 |
-
.wc-shorts-slide-header .view-all-btn:active { opacity: .8; }
|
| 57 |
-
|
| 58 |
-
/* Horizontal thumbnail slider */
|
| 59 |
-
.wc-shorts-h-slider {
|
| 60 |
-
display: flex;
|
| 61 |
-
overflow-x: auto;
|
| 62 |
-
gap: 8px;
|
| 63 |
-
padding: 4px 10px 12px;
|
| 64 |
-
scroll-snap-type: x mandatory;
|
| 65 |
-
scrollbar-width: none;
|
| 66 |
-
}
|
| 67 |
-
.wc-shorts-h-slider::-webkit-scrollbar { display: none; }
|
| 68 |
-
.wc-shorts-h-item {
|
| 69 |
-
flex: 0 0 130px;
|
| 70 |
-
scroll-snap-align: start;
|
| 71 |
-
cursor: pointer;
|
| 72 |
-
border-radius: 8px;
|
| 73 |
-
overflow: hidden;
|
| 74 |
-
background: #0a1520;
|
| 75 |
-
border: 1px solid #1a3a5a;
|
| 76 |
-
transition: border-color .2s, transform .2s;
|
| 77 |
-
position: relative;
|
| 78 |
-
}
|
| 79 |
-
.wc-shorts-h-item:hover { border-color: #0b6bcb; transform: scale(1.03); }
|
| 80 |
-
.wc-shorts-h-item:active { transform: scale(.97); }
|
| 81 |
-
.wc-shorts-h-thumb {
|
| 82 |
-
position: relative;
|
| 83 |
-
width: 100%;
|
| 84 |
-
aspect-ratio: 9/16;
|
| 85 |
-
background: #1a2a3a;
|
| 86 |
-
overflow: hidden;
|
| 87 |
-
}
|
| 88 |
-
.wc-shorts-h-thumb img {
|
| 89 |
-
width: 100%; height: 100%; object-fit: cover;
|
| 90 |
-
}
|
| 91 |
-
.wc-shorts-h-thumb .h-play-icon {
|
| 92 |
-
position: absolute; left: 50%; top: 50%;
|
| 93 |
-
transform: translate(-50%, -50%);
|
| 94 |
-
width: 36px; height: 36px;
|
| 95 |
-
border-radius: 50%;
|
| 96 |
-
background: rgba(0,0,0,.6);
|
| 97 |
-
display: flex; align-items: center; justify-content: center;
|
| 98 |
-
color: #fff; font-size: 14px;
|
| 99 |
-
}
|
| 100 |
-
.wc-shorts-h-thumb .h-duration {
|
| 101 |
-
position: absolute; bottom: 4px; right: 4px;
|
| 102 |
-
background: rgba(0,0,0,.7);
|
| 103 |
-
font-size: 8px; color: #fff;
|
| 104 |
-
padding: 1px 4px; border-radius: 3px;
|
| 105 |
-
}
|
| 106 |
-
.wc-shorts-h-thumb .h-vtv-badge {
|
| 107 |
-
position: absolute; top: 4px; left: 4px;
|
| 108 |
-
background: rgba(11,107,203,.85);
|
| 109 |
-
font-size: 7px; color: #fff;
|
| 110 |
-
padding: 1px 5px; border-radius: 3px;
|
| 111 |
-
font-weight: 700;
|
| 112 |
-
}
|
| 113 |
-
.wc-shorts-h-info { padding: 5px 6px; }
|
| 114 |
-
.wc-shorts-h-info .h-title {
|
| 115 |
-
font-size: 9px; color: #ccc; line-height: 1.25;
|
| 116 |
-
display: -webkit-box; -webkit-line-clamp: 2;
|
| 117 |
-
-webkit-box-orient: vertical; overflow: hidden;
|
| 118 |
-
}
|
| 119 |
-
.wc-shorts-empty-slider {
|
| 120 |
-
text-align: center; padding: 20px 10px; color: #666; font-size: 12px;
|
| 121 |
-
width: 100%;
|
| 122 |
-
}
|
| 123 |
-
.wc-shorts-empty-slider .reload-btn {
|
| 124 |
-
background: #0b6bcb; border: none; color: #fff;
|
| 125 |
-
padding: 6px 16px; border-radius: 14px; font-size: 11px;
|
| 126 |
-
font-weight: 700; cursor: pointer; margin-top: 10px;
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
/* === FULL-SCREEN TIKTOK OVERLAY (opened from slide) === */
|
| 130 |
-
.wc-st-overlay {
|
| 131 |
-
position: fixed; inset: 0; background: #000;
|
| 132 |
-
z-index: 99999; display: none; flex-direction: column;
|
| 133 |
-
}
|
| 134 |
-
.wc-st-overlay.active { display: flex; }
|
| 135 |
-
.wc-st-top-bar {
|
| 136 |
-
position: absolute; top: 0; left: 0; right: 0;
|
| 137 |
-
z-index: 10; padding: 10px 12px;
|
| 138 |
-
display: flex; align-items: center; justify-content: space-between;
|
| 139 |
-
background: linear-gradient(rgba(0,0,0,.7), transparent);
|
| 140 |
-
}
|
| 141 |
-
.wc-st-top-bar .back-btn {
|
| 142 |
-
background: rgba(255,255,255,.15);
|
| 143 |
-
border: none; color: #fff;
|
| 144 |
-
width: 36px; height: 36px; border-radius: 50%;
|
| 145 |
-
font-size: 18px; cursor: pointer;
|
| 146 |
-
display: flex; align-items: center; justify-content: center;
|
| 147 |
-
}
|
| 148 |
-
.wc-st-top-bar .st-counter {
|
| 149 |
-
color: #fff; font-size: 12px; font-weight: 600;
|
| 150 |
-
background: rgba(0,0,0,.4); padding: 3px 10px;
|
| 151 |
-
border-radius: 10px; backdrop-filter: blur(4px);
|
| 152 |
-
}
|
| 153 |
-
|
| 154 |
-
.wc-st-feed {
|
| 155 |
-
flex: 1; overflow-y: scroll;
|
| 156 |
-
scroll-snap-type: y mandatory;
|
| 157 |
-
scrollbar-width: none;
|
| 158 |
-
}
|
| 159 |
-
.wc-st-feed::-webkit-scrollbar { display: none; }
|
| 160 |
-
|
| 161 |
-
.wc-st-slide {
|
| 162 |
-
height: 100vh; width: 100%;
|
| 163 |
-
scroll-snap-align: start;
|
| 164 |
-
position: relative;
|
| 165 |
-
background: #000;
|
| 166 |
-
display: flex; align-items: center; justify-content: center;
|
| 167 |
-
overflow: hidden;
|
| 168 |
-
}
|
| 169 |
-
.wc-st-slide iframe {
|
| 170 |
-
width: 100%; height: 100%; border: none; background: #000;
|
| 171 |
-
}
|
| 172 |
-
|
| 173 |
-
/* Title & info overlay */
|
| 174 |
-
.wc-st-info {
|
| 175 |
-
position: absolute; bottom: 0; left: 0; right: 65px;
|
| 176 |
-
padding: 12px 14px 18px;
|
| 177 |
-
background: linear-gradient(transparent 0%, rgba(0,0,0,.65) 35%, rgba(0,0,0,.9) 100%);
|
| 178 |
-
z-index: 3; pointer-events: none;
|
| 179 |
-
}
|
| 180 |
-
.wc-st-info .st-channel {
|
| 181 |
-
display: inline-block; font-size: 9px; font-weight: 700;
|
| 182 |
-
padding: 2px 8px; border-radius: 10px; color: #fff;
|
| 183 |
-
background: #0b6bcb; margin-bottom: 8px;
|
| 184 |
-
}
|
| 185 |
-
.wc-st-info .st-title {
|
| 186 |
-
font-size: 14px; color: #fff; font-weight: 700;
|
| 187 |
-
line-height: 1.35; text-shadow: 0 1px 4px rgba(0,0,0,.7);
|
| 188 |
-
display: -webkit-box; -webkit-line-clamp: 3;
|
| 189 |
-
-webkit-box-orient: vertical; overflow: hidden;
|
| 190 |
-
}
|
| 191 |
-
.wc-st-info .st-link {
|
| 192 |
-
display: inline-block; margin-top: 8px;
|
| 193 |
-
font-size: 11px; color: #8ab4d8; text-decoration: underline;
|
| 194 |
-
pointer-events: auto; cursor: pointer;
|
| 195 |
-
}
|
| 196 |
-
|
| 197 |
-
/* Right-side action buttons */
|
| 198 |
-
.wc-st-actions {
|
| 199 |
-
position: absolute; right: 8px; bottom: 90px;
|
| 200 |
-
display: flex; flex-direction: column; align-items: center;
|
| 201 |
-
gap: 16px; z-index: 5;
|
| 202 |
-
}
|
| 203 |
-
.wc-st-action {
|
| 204 |
-
display: flex; flex-direction: column; align-items: center; gap: 2px;
|
| 205 |
-
background: none; border: none; color: #fff; cursor: pointer;
|
| 206 |
-
font-size: 10px; text-shadow: 0 1px 3px rgba(0,0,0,.5);
|
| 207 |
-
}
|
| 208 |
-
.wc-st-action .a-icon {
|
| 209 |
-
width: 46px; height: 46px; border-radius: 50%;
|
| 210 |
-
background: rgba(255,255,255,.15);
|
| 211 |
-
display: flex; align-items: center; justify-content: center;
|
| 212 |
-
font-size: 20px; backdrop-filter: blur(4px);
|
| 213 |
-
transition: background .2s, transform .15s;
|
| 214 |
-
}
|
| 215 |
-
.wc-st-action:active .a-icon { transform: scale(.88); background: rgba(255,255,255,.3); }
|
| 216 |
-
.wc-st-action .a-count { font-size: 10px; color: #eee; font-weight: 600; }
|
| 217 |
-
|
| 218 |
-
/* Loading state */
|
| 219 |
-
.wc-st-loading {
|
| 220 |
-
position: absolute; inset: 0; display: flex;
|
| 221 |
-
flex-direction: column; align-items: center; justify-content: center;
|
| 222 |
-
gap: 12px; color: #888; font-size: 13px;
|
| 223 |
-
}
|
| 224 |
-
.wc-st-loading .spinner {
|
| 225 |
-
width: 28px; height: 28px;
|
| 226 |
-
border: 3px solid #333; border-top-color: #0b6bcb;
|
| 227 |
-
border-radius: 50%; animation: wcst-spin .8s linear infinite;
|
| 228 |
-
}
|
| 229 |
-
@keyframes wcst-spin { to { transform: rotate(360deg); } }
|
| 230 |
-
|
| 231 |
-
/* No shorts message */
|
| 232 |
-
.wc-st-nosho {
|
| 233 |
-
position: absolute; inset: 0; display: flex;
|
| 234 |
-
flex-direction: column; align-items: center; justify-content: center;
|
| 235 |
-
gap: 10px; color: #666; font-size: 14px; text-align: center; padding: 20px;
|
| 236 |
-
}
|
| 237 |
-
.wc-st-nosho .icon { font-size: 48px; }
|
| 238 |
-
.wc-st-nosho .retry-btn {
|
| 239 |
-
background: #0b6bcb; border: none; color: #fff;
|
| 240 |
-
padding: 10px 28px; border-radius: 20px;
|
| 241 |
-
font-size: 13px; font-weight: 700; cursor: pointer;
|
| 242 |
-
}
|
| 243 |
-
`;
|
| 244 |
-
document.head.appendChild(style);
|
| 245 |
-
|
| 246 |
-
// ===== State =====
|
| 247 |
-
let _wcSCached = null; // cached shorts data
|
| 248 |
-
let _wcSOverlayTimer = null; // auto-advance timer
|
| 249 |
-
let _wcSManualScroll = false;
|
| 250 |
-
|
| 251 |
-
// ===== Fetch shorts =====
|
| 252 |
-
async function fetchShorts(){
|
| 253 |
-
const endpoints = [
|
| 254 |
-
'/api/shorts/wc?count=50',
|
| 255 |
-
'/api/shorts/vtvnamo?count=50',
|
| 256 |
-
'/api/shorts?channel=vtvnambo',
|
| 257 |
-
];
|
| 258 |
-
for(const ep of endpoints){
|
| 259 |
-
try{
|
| 260 |
-
const r = await fetch(ep, {signal: AbortSignal.timeout(12000)});
|
| 261 |
-
const data = await r.json();
|
| 262 |
-
if(Array.isArray(data) && data.length > 0){
|
| 263 |
-
// Deduplicate
|
| 264 |
-
const seen = new Set(), deduped = [];
|
| 265 |
-
for(const s of data){
|
| 266 |
-
if(!seen.has(s.id)){ seen.add(s.id); deduped.push(s); }
|
| 267 |
-
}
|
| 268 |
-
if(deduped.length > 0) return deduped;
|
| 269 |
-
}
|
| 270 |
-
}catch(e){ console.warn('WC Shorts fetch failed for', ep, e); }
|
| 271 |
-
}
|
| 272 |
-
return [];
|
| 273 |
-
}
|
| 274 |
-
|
| 275 |
-
// ===== Render the HORIZONTAL SLIDE (placed above WC section) =====
|
| 276 |
-
window.renderWCSSlideSection = async function(container){
|
| 277 |
-
const section = document.createElement('div');
|
| 278 |
-
section.className = 'wc-shorts-slide-section';
|
| 279 |
-
section.id = 'wc-shorts-slide-section';
|
| 280 |
-
|
| 281 |
-
// Header
|
| 282 |
-
section.innerHTML = `
|
| 283 |
-
<div class="wc-shorts-slide-header">
|
| 284 |
-
<h3><span class="emoji">📱</span> Shorts World Cup <span class="badge-live">● LIVE</span></h3>
|
| 285 |
-
<button class="view-all-btn" id="wc-st-viewall">Xem tất cả →</button>
|
| 286 |
-
</div>
|
| 287 |
-
<div class="wc-shorts-h-slider" id="wc-st-hslider">
|
| 288 |
-
<div style="width:100%;text-align:center;padding:20px;color:#666;font-size:12px">
|
| 289 |
-
<div class="spinner" style="width:20px;height:20px;border:2px solid #333;border-top-color:#0b6bcb;border-radius:50%;animation:wcst-spin .8s linear infinite;margin:0 auto 8px"></div>
|
| 290 |
-
Đang tải shorts...
|
| 291 |
-
</div>
|
| 292 |
-
</div>
|
| 293 |
-
`;
|
| 294 |
-
container.appendChild(section);
|
| 295 |
-
|
| 296 |
-
const slider = section.querySelector('#wc-st-hslider');
|
| 297 |
-
const viewAllBtn = section.querySelector('#wc-st-viewall');
|
| 298 |
-
|
| 299 |
-
// Fetch data
|
| 300 |
-
let shorts = await fetchShorts();
|
| 301 |
-
_wcSCached = shorts;
|
| 302 |
-
|
| 303 |
-
if(!shorts || !shorts.length){
|
| 304 |
-
slider.innerHTML = `<div class="wc-shorts-empty-slider">
|
| 305 |
-
<div>📱</div><div style="margin-top:6px;color:#888">Đang cập nhật shorts...</div>
|
| 306 |
-
<button class="reload-btn" onclick="renderWCSSlideSection(document.getElementById('wc-shorts-slide-section').parentNode)">Thử lại</button>
|
| 307 |
-
</div>`;
|
| 308 |
-
viewAllBtn.onclick = () => openWCSF(shorts || [], 0);
|
| 309 |
-
return;
|
| 310 |
-
}
|
| 311 |
-
|
| 312 |
-
// Render horizontal slider items
|
| 313 |
-
let h = '';
|
| 314 |
-
shorts.forEach((s, i)=>{
|
| 315 |
-
const thumbImg = s.img || `https://i.ytimg.com/vi/${s.id}/hqdefault.jpg`;
|
| 316 |
-
h += `<div class="wc-shorts-h-item" data-idx="${i}" onclick="openWCSF(_wcSCached, ${i})">`;
|
| 317 |
-
h += `<div class="wc-shorts-h-thumb">`;
|
| 318 |
-
h += `<img src="${escHtml(thumbImg)}" loading="lazy" onerror="this.style.display='none'">`;
|
| 319 |
-
h += `<span class="h-vtv-badge">VTV Nam Bộ</span>`;
|
| 320 |
-
h += `<div class="h-play-icon">▶</div>`;
|
| 321 |
-
h += `</div>`;
|
| 322 |
-
h += `<div class="wc-shorts-h-info"><div class="h-title">${escHtml(s.title)}</div></div>`;
|
| 323 |
-
h += `</div>`;
|
| 324 |
-
});
|
| 325 |
-
slider.innerHTML = h;
|
| 326 |
-
|
| 327 |
-
viewAllBtn.onclick = () => openWCSF(shorts, 0);
|
| 328 |
-
};
|
| 329 |
-
|
| 330 |
-
// ===== FULL-SCREEN TIKTOK OVERLAY =====
|
| 331 |
-
window.openWCSF = function(shorts, startIdx){
|
| 332 |
-
if(!shorts || !shorts.length){
|
| 333 |
-
// Try to fetch fresh
|
| 334 |
-
openWCSFLoading(startIdx);
|
| 335 |
-
return;
|
| 336 |
-
}
|
| 337 |
-
buildWCSF(shorts, startIdx);
|
| 338 |
-
};
|
| 339 |
-
|
| 340 |
-
function openWCSFLoading(startIdx){
|
| 341 |
-
const overlay = getOrCreateWCSFOverlay();
|
| 342 |
-
overlay.classList.add('active');
|
| 343 |
-
overlay.innerHTML = `<div class="wc-st-loading" style="position:absolute;inset:0;background:#000;z-index:100">
|
| 344 |
-
<div class="spinner"></div>Đang tải shorts...
|
| 345 |
-
</div>`;
|
| 346 |
-
fetchShorts().then(shorts => {
|
| 347 |
-
_wcSCached = shorts;
|
| 348 |
-
if(shorts && shorts.length){
|
| 349 |
-
buildWCSF(shorts, startIdx || 0);
|
| 350 |
-
} else {
|
| 351 |
-
overlay.innerHTML = `<div class="wc-st-nosho">
|
| 352 |
-
<div class="icon">📱</div><div>Không tải được shorts</div>
|
| 353 |
-
<button class="retry-btn" onclick="openWCSF(_wcSCached, ${startIdx||0})">Thử lại</button>
|
| 354 |
-
<button class="retry-btn" style="background:#333;margin-top:4px" onclick="closeWCSF()">Đóng</button>
|
| 355 |
-
</div>`;
|
| 356 |
-
}
|
| 357 |
-
}).catch(() => {
|
| 358 |
-
overlay.innerHTML = `<div class="wc-st-nosho">
|
| 359 |
-
<div class="icon">📱</div><div>Lỗi kết nối</div>
|
| 360 |
-
<button class="retry-btn" onclick="openWCSF(_wcSCached, ${startIdx||0})">Thử lại</button>
|
| 361 |
-
</div>`;
|
| 362 |
-
});
|
| 363 |
-
}
|
| 364 |
-
|
| 365 |
-
function getOrCreateWCSFOverlay(){
|
| 366 |
-
let overlay = document.getElementById('wc-st-overlay');
|
| 367 |
-
if(!overlay){
|
| 368 |
-
overlay = document.createElement('div');
|
| 369 |
-
overlay.id = 'wc-st-overlay';
|
| 370 |
-
overlay.className = 'wc-st-overlay';
|
| 371 |
-
document.body.appendChild(overlay);
|
| 372 |
-
}
|
| 373 |
-
return overlay;
|
| 374 |
-
}
|
| 375 |
-
|
| 376 |
-
window.closeWCSF = function(){
|
| 377 |
-
const overlay = document.getElementById('wc-st-overlay');
|
| 378 |
-
if(!overlay) return;
|
| 379 |
-
overlay.classList.remove('active');
|
| 380 |
-
overlay.innerHTML = '';
|
| 381 |
-
if(_wcSOverlayTimer){ clearInterval(_wcSOverlayTimer); _wcSOverlayTimer = null; }
|
| 382 |
-
// Stop any playing iframes
|
| 383 |
-
overlay.querySelectorAll('iframe').forEach(f => { f.src = ''; });
|
| 384 |
-
};
|
| 385 |
-
|
| 386 |
-
function buildWCSF(shorts, startIdx){
|
| 387 |
-
const overlay = getOrCreateWCSFOverlay();
|
| 388 |
-
overlay.classList.add('active');
|
| 389 |
-
|
| 390 |
-
// Rotate so startIdx is first
|
| 391 |
-
const ordered = startIdx > 0
|
| 392 |
-
? [...shorts.slice(startIdx), ...shorts.slice(0, startIdx)]
|
| 393 |
-
: [...shorts];
|
| 394 |
-
const total = ordered.length;
|
| 395 |
-
|
| 396 |
-
// Build HTML
|
| 397 |
-
let h = '';
|
| 398 |
-
// Top bar
|
| 399 |
-
h += `<div class="wc-st-top-bar">
|
| 400 |
-
<button class="back-btn" onclick="closeWCSF()">←</button>
|
| 401 |
-
<span class="st-counter" id="wc-st-counter">1/${total}</span>
|
| 402 |
-
</div>`;
|
| 403 |
-
|
| 404 |
-
// Feed
|
| 405 |
-
h += `<div class="wc-st-feed" id="wc-st-feed">`;
|
| 406 |
-
|
| 407 |
-
ordered.forEach((s, i)=>{
|
| 408 |
-
const id = s.id || '';
|
| 409 |
-
const embedSrc = `https://www.youtube.com/embed/${id}?autoplay=0&rel=0&playsinline=1&mute=1&loop=1&playlist=${id}&enablejsapi=1&origin=${encodeURIComponent(location.origin)}`;
|
| 410 |
-
|
| 411 |
-
h += `<div class="wc-st-slide" data-vid="${escHtml('wcst-'+id)}" data-idx="${i}" data-ytid="${escHtml(id)}">`;
|
| 412 |
-
// iframe — lazy loaded, autoplay handled by scroll observer
|
| 413 |
-
h += `<iframe data-yt-src="${embedSrc}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture;fullscreen"></iframe>`;
|
| 414 |
-
|
| 415 |
-
// Info overlay (title)
|
| 416 |
-
h += `<div class="wc-st-info">`;
|
| 417 |
-
h += `<span class="st-channel">VTV Nam Bộ</span>`;
|
| 418 |
-
h += `<div class="st-title">${escHtml(s.title)}</div>`;
|
| 419 |
-
h += `<span class="st-link" onclick="window.open('https://youtube.com/shorts/${escHtml(id)}','_blank')">Mở trên YouTube →</span>`;
|
| 420 |
-
h += `</div>`;
|
| 421 |
-
|
| 422 |
-
// Right action buttons
|
| 423 |
-
h += `<div class="wc-st-actions">`;
|
| 424 |
-
h += `<button class="wc-st-action" title="Xem" onclick="event.stopPropagation();wcSTTrack('${escHtml(id)}','view',${i})"><div class="a-icon">👁</div><span class="a-count" id="wcst-vc-${i}">0</span></button>`;
|
| 425 |
-
h += `<button class="wc-st-action" title="Thích" onclick="event.stopPropagation();wcSTTrack('${escHtml(id)}','like',${i})"><div class="a-icon">❤️</div><span class="a-count" id="wcst-lc-${i}">0</span></button>`;
|
| 426 |
-
h += `<button class="wc-st-action" title="Chia sẻ" onclick="event.stopPropagation();wcSTShare('${escHtml(id)}')"><div class="a-icon">📤</div></button>`;
|
| 427 |
-
h += `<button class="wc-st-action" title="YouTube" onclick="event.stopPropagation();window.open('https://youtube.com/shorts/${escHtml(id)}','_blank')"><div class="a-icon">🔗</div></button>`;
|
| 428 |
-
h += `</div>`;
|
| 429 |
-
|
| 430 |
-
h += `</div>`;
|
| 431 |
-
});
|
| 432 |
-
|
| 433 |
-
h += `</div>`; // end feed
|
| 434 |
-
overlay.innerHTML = h;
|
| 435 |
-
|
| 436 |
-
// Init autoplay on scroll
|
| 437 |
-
initWCSFAutoplay(ordered);
|
| 438 |
-
|
| 439 |
-
// Load stats
|
| 440 |
-
loadWCSFStats(ordered);
|
| 441 |
-
}
|
| 442 |
-
|
| 443 |
-
// ===== Autoplay on scroll (intersection observer) =====
|
| 444 |
-
function initWCSFAutoplay(shorts){
|
| 445 |
-
const feed = document.getElementById('wc-st-feed');
|
| 446 |
-
if(!feed) return;
|
| 447 |
-
const slides = feed.querySelectorAll('.wc-st-slide');
|
| 448 |
-
let curIdx = -1;
|
| 449 |
-
_wcSManualScroll = false;
|
| 450 |
-
let manualTimer = null;
|
| 451 |
-
|
| 452 |
-
function activate(idx){
|
| 453 |
-
if(idx === curIdx || idx < 0 || idx >= slides.length) return;
|
| 454 |
-
slides.forEach((sl, j)=>{
|
| 455 |
-
const fr = sl.querySelector('iframe');
|
| 456 |
-
if(j === idx){
|
| 457 |
-
// Load iframe if needed
|
| 458 |
-
if(fr && !fr.src && fr.dataset.ytSrc){ fr.src = fr.dataset.ytSrc; }
|
| 459 |
-
// Play (with mute to satisfy autoplay policy)
|
| 460 |
-
if(fr && fr.src){
|
| 461 |
-
setTimeout(()=>{
|
| 462 |
-
try{
|
| 463 |
-
fr.contentWindow.postMessage(JSON.stringify({event:'command',func:'mute',args:''}),'*');
|
| 464 |
-
fr.contentWindow.postMessage(JSON.stringify({event:'command',func:'playVideo',args:''}),'*');
|
| 465 |
-
}catch(e){}
|
| 466 |
-
}, 400);
|
| 467 |
-
}
|
| 468 |
-
} else {
|
| 469 |
-
// Pause others
|
| 470 |
-
if(fr && fr.src){
|
| 471 |
-
try{ fr.contentWindow.postMessage(JSON.stringify({event:'command',func:'pauseVideo',args:''}),'*'); }catch(e){}
|
| 472 |
-
}
|
| 473 |
-
}
|
| 474 |
-
});
|
| 475 |
-
curIdx = idx;
|
| 476 |
-
// Update counter
|
| 477 |
-
const counter = document.getElementById('wc-st-counter');
|
| 478 |
-
if(counter) counter.textContent = `${idx+1}/${shorts.length}`;
|
| 479 |
-
}
|
| 480 |
-
|
| 481 |
-
// Use IntersectionObserver for precise snap detection
|
| 482 |
-
const io = new IntersectionObserver((entries)=>{
|
| 483 |
-
if(_wcSManualScroll) return;
|
| 484 |
-
entries.forEach(entry=>{
|
| 485 |
-
if(entry.isIntersecting && entry.intersectionRatio > 0.6){
|
| 486 |
-
const idx = parseInt(entry.target.dataset.idx);
|
| 487 |
-
if(!isNaN(idx)) activate(idx);
|
| 488 |
-
}
|
| 489 |
-
});
|
| 490 |
-
}, {threshold: [0.6], root: feed});
|
| 491 |
-
|
| 492 |
-
slides.forEach(sl => io.observe(sl));
|
| 493 |
-
|
| 494 |
-
// Scroll event for manual detection (fallback)
|
| 495 |
-
let scrollTimer;
|
| 496 |
-
feed.addEventListener('scroll', ()=>{
|
| 497 |
-
_wcSManualScroll = true;
|
| 498 |
-
clearTimeout(manualTimer);
|
| 499 |
-
manualTimer = setTimeout(()=>{ _wcSManualScroll = false; }, 3000);
|
| 500 |
-
clearTimeout(scrollTimer);
|
| 501 |
-
scrollTimer = setTimeout(()=>{
|
| 502 |
-
const rect = feed.getBoundingClientRect();
|
| 503 |
-
const ctr = rect.top + rect.height / 2;
|
| 504 |
-
let best = -1, bestD = 1e9;
|
| 505 |
-
slides.forEach((sl,i)=>{
|
| 506 |
-
const r = sl.getBoundingClientRect();
|
| 507 |
-
const d = Math.abs(r.top + r.height/2 - ctr);
|
| 508 |
-
if(d < bestD){ bestD = d; best = i; }
|
| 509 |
-
});
|
| 510 |
-
if(best >= 0) activate(best);
|
| 511 |
-
}, 100);
|
| 512 |
-
}, {passive: true});
|
| 513 |
-
|
| 514 |
-
// Auto-advance every 12 seconds
|
| 515 |
-
if(_wcSOverlayTimer) clearInterval(_wcSOverlayTimer);
|
| 516 |
-
_wcSOverlayTimer = setInterval(()=>{
|
| 517 |
-
if(_wcSManualScroll) return;
|
| 518 |
-
if(!document.getElementById('wc-st-feed')){ clearInterval(_wcSOverlayTimer); _wcSOverlayTimer = null; return; }
|
| 519 |
-
const next = curIdx + 1;
|
| 520 |
-
if(next < slides.length){
|
| 521 |
-
slides[next].scrollIntoView({behavior:'smooth', block:'start'});
|
| 522 |
-
} else {
|
| 523 |
-
slides[0].scrollIntoView({behavior:'smooth', block:'start'});
|
| 524 |
-
}
|
| 525 |
-
}, 12000);
|
| 526 |
-
|
| 527 |
-
// Activate first after short delay
|
| 528 |
-
setTimeout(()=>{
|
| 529 |
-
if(slides.length > 0){ slides[0].scrollIntoView({behavior:'start'}); activate(0); }
|
| 530 |
-
}, 300);
|
| 531 |
-
}
|
| 532 |
-
|
| 533 |
-
// ===== Interaction tracking =====
|
| 534 |
-
window.wcSTTrack = async function(videoId, type, idx){
|
| 535 |
-
try{
|
| 536 |
-
const r = await fetch('/api/short-action', {
|
| 537 |
-
method:'POST', headers:{'Content-Type':'application/json'},
|
| 538 |
-
body: JSON.stringify({id:videoId, action:type})
|
| 539 |
-
});
|
| 540 |
-
const j = await r.json();
|
| 541 |
-
if(j && j.stats){
|
| 542 |
-
if(idx !== undefined){
|
| 543 |
-
const vc = document.getElementById(`wcst-vc-${idx}`);
|
| 544 |
-
const lc = document.getElementById(`wcst-lc-${idx}`);
|
| 545 |
-
if(type==='view' && vc) vc.textContent = fmtNum(j.stats.views);
|
| 546 |
-
if(type==='like' && lc) lc.textContent = fmtNum(j.stats.likes);
|
| 547 |
-
}
|
| 548 |
-
}
|
| 549 |
-
}catch(e){ console.warn('Track error:', e); }
|
| 550 |
-
};
|
| 551 |
-
|
| 552 |
-
window.wcSTShare = function(videoId){
|
| 553 |
-
const url = 'https://youtube.com/shorts/' + videoId;
|
| 554 |
-
if(navigator.share){
|
| 555 |
-
navigator.share({title:'Short World Cup', url}).catch(()=>{});
|
| 556 |
-
} else {
|
| 557 |
-
navigator.clipboard.writeText(url).then(()=>alert('Đã sao chép link!')).catch(()=>{});
|
| 558 |
-
}
|
| 559 |
-
};
|
| 560 |
-
|
| 561 |
-
async function loadWCSFStats(shorts){
|
| 562 |
-
const ids = shorts.map(s=>s.id).filter(Boolean);
|
| 563 |
-
if(!ids.length) return;
|
| 564 |
-
// Batch in groups of 20
|
| 565 |
-
for(let i=0;i<ids.length;i+=20){
|
| 566 |
-
const batch = ids.slice(i,i+20).join(',');
|
| 567 |
-
try{
|
| 568 |
-
const r = await fetch('/api/short-stats?ids='+batch);
|
| 569 |
-
const j = await r.json();
|
| 570 |
-
if(j.stats){
|
| 571 |
-
shorts.forEach((s,idx)=>{
|
| 572 |
-
const st = j.stats[s.id];
|
| 573 |
-
if(st){
|
| 574 |
-
const vc = document.getElementById(`wcst-vc-${idx}`);
|
| 575 |
-
const lc = document.getElementById(`wcst-lc-${idx}`);
|
| 576 |
-
if(vc) vc.textContent = fmtNum(st.views);
|
| 577 |
-
if(lc) lc.textContent = fmtNum(st.likes);
|
| 578 |
-
}
|
| 579 |
-
});
|
| 580 |
-
}
|
| 581 |
-
}catch(e){}
|
| 582 |
-
}
|
| 583 |
-
}
|
| 584 |
-
|
| 585 |
-
function fmtNum(n){
|
| 586 |
-
if(!n) return '0';
|
| 587 |
-
if(n>=1e6) return (n/1e6).toFixed(1)+'M';
|
| 588 |
-
if(n>=1e3) return (n/1e3).toFixed(1)+'K';
|
| 589 |
-
return String(n);
|
| 590 |
-
}
|
| 591 |
-
|
| 592 |
-
// ===== Helpers =====
|
| 593 |
-
function escHtml(s){
|
| 594 |
-
return String(s||'').replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));
|
| 595 |
-
}
|
| 596 |
-
|
| 597 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|