1// ============================================================ 2// Grad Vinkovci â pristupaÄnost + cookie privola 3// Postavke se automatski spremaju u sessionStorage i cookie. 4// ============================================================ 5 6(function () { 7 "use strict"; 8 9 var COOKIE_A11Y = "vk_a11y"; 10 var COOKIE_SETTINGS = "vk_cookie_settings"; 11 var COOKIE_LEGACY = "vk_cookie_consent"; 12 var COOKIE_CAT_OPEN = "vk_cookie_cat_open"; 13 var SESSION_KEY = "vk_a11y"; 14 var SLIDER_MIN = 80; 15 var SLIDER_MAX = 150; 16 var SLIDER_STEP = 10; 17 var BASE_URL = window.VK_BASE_URL || ""; 18 var PRIVACY_URL = (BASE_URL || "") + "/privatnost"; 19 20 var MODE_CLASSES = [ 21 "a11y-invert", 22 "a11y-mono", 23 "a11y-dark-contrast", 24 "a11y-light-contrast", 25 "a11y-low-sat", 26 "a11y-high-sat" 27 ]; 28 29 var MODE_CLASS_MAP = { 30 invert: "a11y-invert", 31 mono: "a11y-mono", 32 darkContrast: "a11y-dark-contrast", 33 lightContrast: "a11y-light-contrast", 34 lowSat: "a11y-low-sat", 35 highSat: "a11y-high-sat" 36 }; 37 38 var defaults = { 39 mode: null, 40 highlightLinks: false, 41 highlightHeadings: false, 42 contentScale: 100, 43 fontSize: 100, 44 lineHeight: 100, 45 letterSpacing: 100 46 }; 47 48 var defaultCategories = { 49 system: true, 50 preferences: false, 51 analytics: false 52 }; 53 54 var consentState = { 55 consentId: null, 56 consentedAt: null, 57 categories: Object.assign({}, defaultCategories) 58 }; 59 60 var cookieCategories = [ 61 { 62 key: "system", 63 title: "Sistemski kolaÄiÄi (obavezni)", 64 desc: "Nužni za osnovno funkcioniranje web stranice (sigurnost, sesija, postavke suglasnosti).", 65 locked: true 66 }, 67 { 68 key: "preferences", 69 title: "KolaÄiÄi postavki", 70 desc: "Pamte vaÅ¡e postavke pristupaÄnosti (veliÄina fonta, kontrast i sl.).", 71 locked: false 72 }, 73 { 74 key: "analytics", 75 title: "AnalitiÄki kolaÄiÄi", 76 desc: "Pomažu nam razumjeti koriÅ¡tenje stranice putem anonimizirane statistike posjeta.", 77 locked: false 78 } 79 ]; 80 81 var state = Object.assign({}, defaults); 82 83 function clampSlider(value) { 84 var n = parseInt(value, 10); 85 if (isNaN(n)) return 100; 86 n = Math.round(n / SLIDER_STEP) * SLIDER_STEP; 87 return Math.min(SLIDER_MAX, Math.max(SLIDER_MIN, n)); 88 } 89 90 function setCookie(name, value, days) { 91 var expires = ""; 92 if (days) { 93 var d = new Date(); 94 d.setTime(d.getTime() + days * 864e5); 95 expires = "; expires=" + d.toUTCString(); 96 } 97 document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/; SameSite=Lax"; 98 } 99 100 function getCookie(name) { 101 var match = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)")); 102 return match ? decodeURIComponent(match[1]) : null; 103 } 104 105 function deleteCookie(name) { 106 document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; SameSite=Lax"; 107 } 108 109 function loadOpenCategories() { 110 try { 111 var raw = localStorage.getItem(COOKIE_CAT_OPEN); 112 if (!raw) return []; 113 var list = JSON.parse(raw); 114 if (!Array.isArray(list)) return []; 115 var validKeys = cookieCategories.map(function (cat) { return cat.key; }); 116 return list.filter(function (key) { return validKeys.indexOf(key) !== -1; }); 117 } catch (e) { 118 return []; 119 } 120 } 121 122 function saveOpenCategories(list) { 123 try { 124 localStorage.setItem(COOKIE_CAT_OPEN, JSON.stringify(list)); 125 } catch (e) { /* quota ili privatni naÄin */ } 126 } 127 128 function generateUuid() { 129 if (window.crypto && typeof window.crypto.randomUUID === "function") { 130 return window.crypto.randomUUID(); 131 } 132 return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { 133 var r = Math.random() * 16 | 0; 134 var v = c === "x" ? r : (r & 0x3 | 0x8); 135 return v.toString(16); 136 }); 137 } 138 139 function hasPreferencesConsent() { 140 return !!consentState.categories.preferences; 141 } 142 143 function migrateLegacyConsent() { 144 var legacy = getCookie(COOKIE_LEGACY); 145 if (!legacy) return null; 146 deleteCookie(COOKIE_LEGACY); 147 if (legacy === "accepted") { 148 return { 149 consentId: generateUuid(), 150 consentedAt: new Date().toISOString(), 151 categories: { system: true, preferences: true, analytics: true } 152 }; 153 } 154 if (legacy === "declined") { 155 return { 156 consentId: generateUuid(), 157 consentedAt: new Date().toISOString(), 158 categories: { system: true, preferences: false, analytics: fal
158se } 159 }; 160 } 161 return null; 162 } 163 164 function loadConsentSettings() { 165 var raw = getCookie(COOKIE_SETTINGS); 166 if (raw) { 167 try { 168 var data = JSON.parse(raw); 169 if (data && data.consentId && data.categories) { 170 consentState.consentId = data.consentId; 171 consentState.consentedAt = data.consentedAt || null; 172 consentState.categories = { 173 system: true, 174 preferences: !!data.categories.preferences, 175 analytics: !!data.categories.analytics 176 }; 177 return; 178 } 179 } catch (e) { /* neispravan zapis */ } 180 } 181 var migrated = migrateLegacyConsent(); 182 if (migrated) { 183 consentState.consentId = migrated.consentId; 184 consentState.consentedAt = migrated.consentedAt; 185 consentState.categories = migrated.categories; 186 saveConsentSettings(false); 187 } 188 } 189 190 function saveConsentSettings(logServer) { 191 if (!consentState.consentId) { 192 consentState.consentId = generateUuid(); 193 } 194 if (!consentState.consentedAt) { 195 consentState.consentedAt = new Date().toISOString(); 196 } 197 setCookie(COOKIE_SETTINGS, JSON.stringify({ 198 consentId: consentState.consentId, 199 consentedAt: consentState.consentedAt, 200 categories: consentState.categories 201 }), 365); 202 applyConsentEffects(); 203 if (logServer !== false) { 204 syncCookieMetaUI(); 205 } 206 } 207 208 function applyConsentEffects() { 209 if (!hasPreferencesConsent()) { 210 sessionStorage.removeItem(SESSION_KEY); 211 deleteCookie(COOKIE_A11Y); 212 state = Object.assign({}, defaults); 213 apply(); 214 syncUI(); 215 } 216 } 217 218 function logConsentToServer(action, categories) { 219 if (!consentState.consentId) return; 220 var url = (BASE_URL || "") + "/consent_log.php"; 221 fetch(url, { 222 method: "POST", 223 headers: { "Content-Type": "application/json" }, 224 body: JSON.stringify({ 225 consent_id: consentState.consentId, 226 action: action, 227 categories: categories 228 }), 229 keepalive: true 230 }).catch(function () { /* ne blokira UI */ }); 231 } 232 233 function formatConsentDate(iso) { 234 if (!iso) return "â"; 235 var d = new Date(iso); 236 if (isNaN(d.getTime())) return "â"; 237 var pad = function (n) { return n < 10 ? "0" + n : String(n); }; 238 return pad(d.getDate()) + ". " + pad(d.getMonth() + 1) + ". " + d.getFullYear() + ". " 239 + pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds()); 240 } 241 242 function hasConsentChoice() { 243 return !!getCookie(COOKIE_SETTINGS) || !!consentState.consentId; 244 } 245 246 function migrateLegacy(data) { 247 if (data.mode !== undefined || data.contentScale !== undefined) { 248 return { 249 mode: data.mode || null, 250 highlightLinks: !!data.highlightLinks, 251 highlightHeadings: !!data.highlightHeadings, 252 contentScale: clampSlider(data.contentScale), 253 fontSize: clampSlider(data.fontSize), 254 lineHeight: clampSlider(data.lineHeight), 255 letterSpacing: clampSlider(data.letterSpacing) 256 }; 257 } 258 259 var result = Object.assign({}, defaults); 260 if (data.contrast) result.mode = "darkContrast"; 261 if (data.underline) result.highlightLinks = true; 262 if (typeof data.fontStep === "number") { 263 result.fontSize = clampSlider(100 + data.fontStep * 10); 264 } 265 return result; 266 } 267 268 function loadA11y() { 269 if (!hasPreferencesConsent()) { 270 return Object.assign({}, defaults); 271 } 272 var raw = getCookie(COOKIE_A11Y) || sessionStorage.getItem(SESSION_KEY); 273 if (raw) { 274 try { 275 return migrateLegacy(JSON.parse(raw)); 276 } catch (e) { /* neispravan zapis */ } 277 } 278 return Object.assign({}, defaults); 279 } 280 281 function persist() { 282 if (!hasPreferencesConsent()) return; 283 sessionStorage.setItem(SESSION_KEY, JSON.stringify(state));
284 setCookie(COOKIE_A11Y, JSON.stringify(state), 365); 285 } 286 287 function resetAll() { 288 state = Object.assign({}, defaults); 289 sessionStorage.removeItem(SESSION_KEY); 290 deleteCookie(COOKIE_A11Y); 291 apply(); 292 syncUI(); 293 } 294 295 function wrapContentRoot() { 296 if (document.getElementById("a11y-root")) return; 297 298 var root = document.createElement("div"); 299 root.id = "a11y-root"; 300 var toMove = []; 301 302 Array.prototype.forEach.call(document.body.children, function (el) { 303 if (el.classList.contains("a11y-fab") || 304 el.classList.contains("cookie-fab") || 305 el.classList.contains("a11y-overlay") || 306 el.classList.contains("cookie-overlay") || 307 el.classList.contains("cookie-banner") || 308 el.classList.contains("nav-backdrop")) { 309 return; 310 } 311 toMove.push(el); 312 }); 313 314 document.body.insertBefore(root, document.body.firstChild); 315 toMove.forEach(function (el) { 316 root.appendChild(el); 317 }); 318 } 319 320 function apply() { 321 var root = document.getElementById("a11y-root"); 322 if (!root) return; 323 324 var html = document.documentElement; 325 html.style.fontSize = state.fontSize === 100 ? "" : state.fontSize + "%"; 326 327 root.style.zoom = state.contentScale === 100 ? "" : String(state.contentScale / 100); 328 root.style.lineHeight = state.lineHeight === 100 ? "" : String(1.6 * state.lineHeight / 100); 329 root.style.letterSpacing = state.letterSpacing === 100 330 ? "" 331 : ((state.letterSpacing - 100) * 0.01) + "em"; 332 333 MODE_CLASSES.forEach(function (cls) { 334 root.classList.remove(cls); 335 }); 336 337 if (state.mode && MODE_CLASS_MAP[state.mode]) { 338 root.classList.add(MODE_CLASS_MAP[state.mode]); 339 } 340 341 root.classList.toggle("a11y-highlight-links", state.highlightLinks); 342 root.classList.toggle("a11y-highlight-headings", state.highlightHeadings); 343 } 344 345 function updateState() { 346 apply(); 347 syncUI(); 348 persist(); 349 } 350 351 var icons = { 352 invert: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M12 3v18" fill="currentColor" stroke="none"/></svg>', 353 mono: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M4 20L20 4"/></svg>', 354 darkContrast: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 14A9 9 0 1114 3a7 7 0 007 11z"/></svg>', 355 lightContrast: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>', 356 lowSat: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M12 3a9 9 0 010 18" fill="currentColor" stroke="none" opacity="0.35"/></svg>', 357 highSat: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" stroke="none"><circle cx="12" cy="12" r="9"/></svg>', 358 highlightLinks: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/></svg>', 359 highlightHeadings: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 9h4M7 13h7M7 17h5"/></svg>', 360 reset: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 12a9 9 0 109-9"/><path d="M3 4v5h5"/></svg>', 361 person: '<svg width="26" height="26" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="4.5" r="2.2"/><path d="M12 8c-3 0-5.5-.6-5.5-.6a.9.9 0 00-.5 1.7s2.4.7 4.2.9c0 0 .1 1.6-.3 3.1-.4 1.6-1.7 4.6-1.7 4.6a.95.95 0 001.7.8s1.4-3 2.1-4.6c.7 1.6 2.1 4.6 2.1 4.6a.95.95 0 001.7-.8s-1.3-3-1.7-4.6c-.4-1.5-.3-3.1-.3-3.1 1.8-.2 4.2-.9 4.2-.9a.9.9 0 00-.5-1.7S15 8 12 8z"/></svg>', 362 cookie: '<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.25 2 11.5 2 16.75 6.48 21 12 21c5.52 0 10-4.25 10-9.5S17.52 2 12 2zm0 2c.55 0 1.08.05 1.58.14-.36.62-.58 1.34-.58 2.11 0 2.21 1.79 4 4 4 .47 0 .92-.08 1.34-.23.64 1.05 1.08 2.24 1.25 3.48C17.9 17.2 15.12 19 12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7z"/><circle cx="8.5" cy="11" r="1.2" fill="#fff"/><circle cx="12.5" cy="8.5" r="1" fill="#fff"/><circle cx="15" cy="12.5" r="1.1" fill="#fff"/><circle cx="10" cy="15" r=".9" fill="#fff"/></svg>' 363 }; 364 365 var tiles = [ 366 { mode: "invert", label: "Invertiraj boje", icon: icons.invert }, 367 { mode: "mono", label: "Monokromatski", icon: icons.mono },
368 { mode: "darkContrast", label: "Tamni kontrast", icon: icons.darkContrast }, 369 { mode: "lightContrast", label: "Svijetli kontrast", icon: icons.lightContrast }, 370 { mode: "lowSat", label: "Niska zasiÄenost", icon: icons.lowSat }, 371 { mode: "highSat", label: "Visoka zasiÄenost", icon: icons.highSat }, 372 { toggle: "highlightLinks", label: "Istakni veze", icon: icons.highlightLinks }, 373 { toggle: "highlightHeadings", label: "Istakni naslove", icon: icons.highlightHeadings } 374 ]; 375 376 var sliders = [ 377 { key: "contentScale", label: "Skaliranje sadržaja" }, 378 { key: "fontSize", label: "VeliÄina fonta" }, 379 { key: "lineHeight", label: "Visina retka" }, 380 { key: "letterSpacing", label: "Prored izmeÄu slova" } 381 ]; 382 383 function buildTileHtml(tile) { 384 var attrs = tile.mode 385 ? ' data-mode="' + tile.mode + '"' 386 : ' data-toggle="' + tile.toggle + '"'; 387 return '<button type="button" class="a11y-tile"' + attrs + ' aria-pressed="false">' + 388 '<span class="a11y-tile__icon">' + tile.icon + '</span>' + 389 '<span class="a11y-tile__label">' + tile.label + '</span>' + 390 '</button>'; 391 } 392 393 function buildSliderHtml(slider) { 394 return '<div class="a11y-slider-row" data-key="' + slider.key + '">' + 395 '<span class="a11y-slider-row__label">' + slider.label + '</span>' + 396 '<input type="range" class="a11y-slider" min="' + SLIDER_MIN + '" max="' + SLIDER_MAX + '" step="' + SLIDER_STEP + '" value="100" aria-label="' + slider.label + '">' + 397 '<span class="a11y-slider-row__value">100%</span>' + 398 '<div class="a11y-slider-row__steppers">' + 399 '<button type="button" class="a11y-stepper" data-dir="-1" aria-label="Smanji ' + slider.label.toLowerCase() + '">−</button>' + 400 '<button type="button" class="a11y-stepper" data-dir="1" aria-label="PoveÄaj ' + slider.label.toLowerCase() + '">+</button>' + 401 '</div>' + 402 '</div>'; 403 } 404 405 function buildCategoryHtml(cat) { 406 var checked = consentState.categories[cat.key] ? " checked" : ""; 407 var disabled = cat.locked ? " disabled" : ""; 408 var isOpen = loadOpenCategories().indexOf(cat.key) !== -1; 409 return '<div class="cookie-category' + (cat.locked ? " cookie-category--locked" : "") + '" data-key="' + cat.key + '">' + 410 '<div class="cookie-category__head">' + 411 '<button type="button" class="cookie-category__toggle" aria-expanded="' + (isOpen ? "true" : "false") + '" aria-controls="cookieCat-' + cat.key + '">' + 412 '<span class="cookie-category__title">' + cat.title + '</span>' + 413 '<span class="cookie-category__chevron" aria-hidden="true">▾</span>' + 414 '</button>' + 415 '<label class="cookie-switch">' + 416 '<input type="checkbox" class="cookie-switch__input" data-cat="' + cat.key + '"' + checked + disabled + '>' + 417 '<span class="cookie-switch__track"></span>' + 418 '</label>' + 419 '</div>' + 420 '<div class="cookie-category__body" id="cookieCat-' + cat.key + '"' + (isOpen ? "" : " hidden") + '>
420' + 421 '<p>' + cat.desc + '</p>' + 422 '</div>' + 423 '</div>'; 424 } 425 426 function buildCookieModal() { 427 var categoriesHtml = cookieCategories.map(buildCategoryHtml).join(""); 428 var wrap = document.createElement("div"); 429 wrap.innerHTML = 430 '<div class="cookie-overlay" id="cookieOverlay" role="dialog" aria-modal="true" aria-labelledby="cookieTitle" hidden>' + 431 '<div class="cookie-modal">' + 432 '<div class="cookie-modal__head">' + 433 '<h2 id="cookieTitle">Postavke kolaÄiÄa</h2>' + 434 '<button type="button" class="cookie-modal__close" id="cookieClose" aria-label="Zatvori">×</button>' + 435 '</div>' + 436 '<div class="cookie-modal__body">' + 437 '<p class="cookie-modal__intro">KoriÅ¡tenje kolaÄiÄa. Koristimo kolaÄiÄe kako bismo osigurali osnovne funkcionalnosti web stranice i poboljÅ¡ali vaÅ¡e online iskustvo. Za odreÄene kategorije možete odabrati da se ukljuÄite/iskljuÄite kad god želite. ViÅ¡e informacija u <a href="' + PRIVACY_URL + '" class="cookie-banner__link">Politici privatnosti</a>.</p>' + 438 '<div class="cookie-categories">' + categoriesHtml + '</div>' + 439 '<div class="cookie-meta" id="cookieMeta">' + 440 '<div><strong>Suglasnost dana:</strong> <span id="cookieMetaDate">â</span></div>' + 441 '<div><strong>ID suglasnosti:</strong> <span id="cookieMetaId">â</span></div>' + 442 '</div>' + 443 '</div>' + 444 '<div class="cookie-modal__foot">' + 445 '<button type="button" class="cookie-btn cookie-btn--primary" id="cookieAcceptAll">Prihvati sve</button>' + 446 '<button type="button" class="cookie-btn cookie-btn--secondary" id="cookieRejectAll">Odbij sve</button>' + 447 '<button type="button" class="cookie-btn cookie-btn--secondary" id="cookieSave">Spremi postavke</button>' + 448 '</div>' + 449 '</div>' + 450 '</div>'; 451 while (wrap.firstChild) document.body.appendChild(wrap.firstChild); 452 } 453 454 function buildCookieFab() { 455 var fab = document.createElement("button"); 456 fab.type = "button"; 457 fab.className = "cookie-fab"; 458 fab.id = "cookieFab"; 459 fab.setAttribute("aria-label", "Postavke kolaÄiÄa"); 460 fab.setAttribute("title", "Postavke kolaÄiÄa"); 461 fab.innerHTML = icons.cookie; 462 document.body.appendChild(fab); 463 } 464 465 function buildCookieBanner() { 466 if (hasConsentChoice()) return; 467 var banner = document.createElement("div"); 468 banner.className = "cookie-banner"; 469 banner.id = "cookieBanner"; 470 banner.setAttribute("role", "region"); 471 banner.setAttribute("aria-label", "Obavijest o kolaÄiÄima"); 472 banner.innerHTML = 473 '<div class="cookie-banner__inner">' + 474 '<p>Ova web stranica koristi kolaÄiÄe kako bi osigurala bolje korisniÄko iskustvo i ispravno funkcioniranje stranice. Možete odabrati koje kategorije kolaÄiÄa prihvaÄate. <a href="' + PRIVACY_URL + '" class="cookie-banner__link">Politika privatnosti</a>. <button type="button" class="cookie-banner__link" id="cookieBannerSettings">Postavke kolaÄiÄa</button></p>' + 475 '<div class="cookie-banner__actions">' + 476 '<button type="button" class="cookie-banner__btn cookie-banner__btn--accept" id="cookieBannerAccept">Prihvati sve</button>' + 477 '<button type="button" class="cookie-banner__btn cookie-banner__btn--decline" id="cookieBannerDecline">Odbij sve</button>' + 478 '</div>' + 479 '</div>'; 480 document.body.appendChild(banner); 481 document.body.classList.add("cookie-banner-visible"); 482 483 document.getElementById("cookieBannerSettings").addEventListener("click", function () { 484 openCookieModal(); 485 }); 486 document.getElementById("cookieBannerAccept").addEventListener("click", function () { 487 acceptAllCookies(); 488 document.body.classList.remove("cookie-banner-visible"); 489 banner.remove(); 490 }); 491 document.getElementById("cookieBannerDecline").addEventListener("click", function () { 492 rejectAllCookies(); 493 document.body.classList.remove("cookie-banner-visible"); 494 banner.remove(); 495 }); 496 } 497 498 function buildWidget() { 499 var gridHtml = tiles.map(buildTileHtml).join(""); 500 var slidersHtml = sliders.map(buildSliderHtml).join(""); 501 502 var wrap = document.createElement("div"); 503 wrap.innerHTML = 504 '<button type="button" class="a11y-fab" id="a11yFab" aria-label="PristupaÄnost" title="PristupaÄnost">' + icons.person + '</button>' + 505 506 '<div class="a11y-overlay" id="a11yOverlay" role="dialog" aria-modal="true" aria-labelledby="a11yTitle" hidden>' + 507 '<div class="a11y-modal">' + 508 '<div class="a11y-modal__head">' + 509 '<h2 id="a11yTitle">Alati za pristupaÄnost</h2>' + 510 '<div class="a11y-modal__actions">' + 511 '<button type="button" class="a11y-modal__reset" id="a11yReset" aria-label="Resetiraj postavke">' + icons.reset + '</button>' + 512 '<button type="button" class="a11y-modal__close" id="a11yClose" aria-label="Zatvori">
512×</button>' + 513 '</div>' + 514 '</div>' + 515 '<div class="a11y-modal__body">' + 516 '<div class="a11y-grid">' + gridHtml + '</div>' + 517 '<div class="a11y-sliders">' + slidersHtml + '</div>' + 518 '</div>' + 519 '</div>' + 520 '</div>'; 521 522 while (wrap.firstChild) document.body.appendChild(wrap.firstChild); 523 } 524 525 function syncCookieMetaUI() { 526 var dateEl = document.getElementById("cookieMetaDate"); 527 var idEl = document.getElementById("cookieMetaId"); 528 if (dateEl) dateEl.textContent = formatConsentDate(consentState.consentedAt); 529 if (idEl) idEl.textContent = consentState.consentId || "â"; 530 } 531 532 function syncCookieTogglesUI() { 533 document.querySelectorAll(".cookie-switch__input").forEach(function (input) { 534 var key = input.dataset.cat; 535 if (!key || key === "system") return; 536 input.checked = !!consentState.categories[key]; 537 }); 538 } 539 540 function openCookieModal() { 541 var overlay = document.getElementById("cookieOverlay"); 542 if (!overlay) return; 543 syncCookieTogglesUI(); 544 syncCookieMetaUI(); 545 overlay.hidden = false; 546 document.body.classList.add("cookie-modal-open"); 547 document.getElementById("cookieClose").focus(); 548 } 549 550 function closeCookieModal() { 551 var overlay = document.getElementById("cookieOverlay"); 552 if (!overlay) return; 553 overlay.hidden = true; 554 document.body.classList.remove("cookie-modal-open"); 555 var fab = document.getElementById("cookieFab"); 556 if (fab) fab.focus(); 557 } 558 559 function readTogglesFromUI() { 560 document.querySelectorAll(".cookie-switch__input").forEach(function (input) { 561 var key = input.dataset.cat; 562 if (!key || key === "system") return; 563 consentState.categories[key] = input.checked; 564 }); 565 consentState.categories.system = true; 566 } 567 568 function acceptAllCookies() { 569 consentState.categories = { system: true, preferences: true, analytics: true }; 570 consentState.consentedAt = new Date().toISOString(); 571 if (!consentState.consentId) consentState.consentId = generateUuid(); 572 saveConsentSettings(false); 573 logConsentToServer("accept_all", consentState.categories); 574 syncCookieTogglesUI(); 575 syncCookieMetaUI(); 576 closeCookieModal(); 577 var banner = document.getElementById("cookieBanner"); 578 if (banner) banner.remove(); 579 state = loadA11y(); 580 apply(); 581 syncUI(); 582 } 583 584 function rejectAllCookies() { 585 consentState.categories = { system: true, preferences: false, analytics: false }; 586 consentState.consentedAt = new Date().toISOString(); 587 if (!consentState.consentId) consentState.consentId = generateUuid(); 588 saveConsentSettings(false); 589 logConsentToServer("reject_all", consentState.categories); 590 syncCookieTogglesUI(); 591 syncCookieMetaUI(); 592 closeCookieModal(); 593 var banner = document.getElementById("cookieBanner"); 594 if (banner) banner.remove(); 595 } 596 597 function saveCookieSettings() { 598 readTogglesFromUI(); 599 consentState.consentedAt = new Date().toISOString(); 600 if (!consentState.consentId) consentState.consentId = generateUuid(); 601 saveConsentSettings(false); 602 logConsentToServer("save_settings", consentState.categories); 603 syncCookieMetaUI(); 604 closeCookieModal(); 605 var banner = document.getElementById("cookieBanner"); 606 if (banner) banner.remove(); 607 if (hasPreferencesConsent()) { 608 state = loadA11y(); 609 } else { 610 state = Object.assign({}, defaults); 611 } 612 apply(); 613 syncUI(); 614 } 615 616 function initCookieUI() { 617 buildCookieModal(); 618 buildCookieFab(); 619 buildCookieBanner(); 620 syncCookieMetaUI(); 621
622 var overlay = document.getElementById("cookieOverlay"); 623 var closeBtn = document.getElementById("cookieClose"); 624 var fab = document.getElementById("cookieFab"); 625 626 fab.addEventListener("click", openCookieModal); 627 closeBtn.addEventListener("click", closeCookieModal); 628 629 overlay.addEventListener("click", function (e) { 630 if (e.target === overlay) closeCookieModal(); 631 }); 632 633 document.getElementById("cookieAcceptAll").addEventListener("click", acceptAllCookies); 634 document.getElementById("cookieRejectAll").addEventListener("click", rejectAllCookies); 635 document.getElementById("cookieSave").addEventListener("click", saveCookieSettings); 636 637 overlay.querySelectorAll(".cookie-category__toggle").forEach(function (btn) { 638 btn.addEventListener("click", function () { 639 var category = btn.closest(".cookie-category"); 640 var key = category ? category.getAttribute("data-key") : null; 641 var body = btn.parentElement.nextElementSibling; 642 var expanded = btn.getAttribute("aria-expanded") === "true"; 643 var nextExpanded = !expanded; 644 btn.setAttribute("aria-expanded", String(nextExpanded)); 645 if (body) body.hidden = expanded; 646 if (!key) return; 647 var open = loadOpenCategories(); 648 if (nextExpanded) { 649 if (open.indexOf(key) === -1) open.push(key); 650 } else { 651 open = open.filter(function (k) { return k !== key; }); 652 } 653 saveOpenCategories(open); 654 }); 655 }); 656 657 document.addEventListener("keydown", function (e) { 658 if (e.key === "Escape" && overlay && !overlay.hidden) closeCookieModal(); 659 }); 660 } 661 662 function syncUI() { 663 document.querySelectorAll(".a11y-tile[data-mode]").forEach(function (btn) { 664 var on = state.mode === btn.dataset.mode; 665 btn.setAttribute("aria-pressed", String(on)); 666 btn.classList.toggle("is-active", on); 667 }); 668 669 document.querySelectorAll(".a11y-tile[data-toggle]").forEach(function (btn) { 670 var on = !!state[btn.dataset.toggle]; 671 btn.setAttribute("aria-pressed", String(on)); 672 btn.classList.toggle("is-active", on); 673 }); 674 675 document.querySelectorAll(".a11y-slider-row").forEach(function (row) { 676 var key = row.dataset.key; 677 var value = state[key]; 678 var input = row.querySelector(".a11y-slider"); 679 var display = row.querySelector(".a11y-slider-row__value"); 680 if (input) input.value = value; 681 if (display) display.textContent = value + "%"; 682 }); 683 } 684 685 function setSliderValue(key, value) { 686 state[key] = clampSlider(value); 687 updateState(); 688 } 689 690 function init() { 691 loadConsentSettings(); 692 state = loadA11y(); 693 buildWidget(); 694 wrapContentRoot(); 695 initCookieUI(); 696 apply(); 697 syncUI(); 698 699 var fab = document.getElementById("a11yFab"); 700 var overlay = document.getElementById("a11yOverlay"); 701 var closeBtn = document.getElementById("a11yClose"); 702 var resetBtn = document.getElementById("a11yReset"); 703 704 function open() { 705 overlay.hidden = false; 706 document.body.classList.add("a11y-modal-open"); 707 closeBtn.focus(); 708 } 709 710 function close() { 711 overlay.hidden = true; 712 document.body.classList.remove("a11y-modal-open"); 713 fab.focus(); 714 } 715 716 fab.addEventListener("click", open); 717 closeBtn.addEventListener("click", close); 718 resetBtn.addEventListener("click", resetAll); 719 720 overlay.addEventListener("click", function (e) { 721 if (e.target === overlay) close(); 722 }); 723 724 document.addEventListener("keydown", function (e) { 725 if (e.key === "Escape" && !overlay.hidden) close(); 726 }); 727 728 overlay.querySelectorAll(".a11y-tile[data-mode]").forEach(function (btn) { 729 btn.addEventListener("click", function () { 730 var mode = btn.dataset.mode; 731 state.mode = state.mode === mode ? null : mode; 732 updateState(); 733 }); 734 }); 735 736 overlay.querySelectorAll(".a11y-tile[data-toggle]").forEach(function (btn) { 737 btn.addEventListener("click", function () { 738 var key = btn.dataset.toggle; 739 state[key] = !state[key]; 740 updateState(); 741 }); 742 }); 743 744 overlay.querySelectorAll(".a11y-slider-row").forEach(function (row) { 745 var key = row.dataset.key; 746 var input = row.querySelector(".a11y-slider"); 747 748 input.addEventListener("input", function () { 749 setSliderValue(key, input.value); 750 }); 751 752 row.querySelectorAll(".a11y-stepper").forEach(function (stepper) { 753 stepper.addEventListener("click", function () { 754 var dir = parseInt(stepper.dataset.dir, 10); 755 setSliderValue(key, state[key] + dir * SLIDER_STEP); 756 }); 757 }); 758 }); 759 } 760 761 if (document.readyState === "loading") { 762 document.addEventListener("DOMContentLoaded", init); 763 } else { 764 init(); 765 } 766})();
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.