1 2<!doctype html> 3<html class="diks-no-js diks-site--ylakarjala diks-user--is-not-logged-in diks-user--is-not-subscriber diks-user--is-not-company-subscriber diks-site--layout-master diks-site--ads-disabled-from-page-level" lang="fi" prefix="og: http://ogp.me/ns#"> 4 <head> 5 <meta charset="utf-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Sovellus | Ylä-Karjala</title> 8 <link id="diks-stylesheet" rel="stylesheet" href="https://a.ylakarjala.fi/styles/2efe7af9/styles/diks-news.css"> 9 <link rel="dns-prefetch" href="https://cdn.gravito.net"> 10 <link rel="dns-prefetch" href="https://cdn.gravito.network"> 11 <link rel="dns-prefetch" href="https://handshake.gravito.network"> 12
12<script> 13 14 const checkUserPreferenceEnabled = 1; 15 const darkmodeStylesheet = "https://a.ylakarjala.fi/styles/2efe7af9/styles/diks-news-darkmode.css"; 16 const defaultStylesheet = "https://a.ylakarjala.fi/styles/2efe7af9/styles/diks-news.css"; 17 const savedTheme = localStorage.getItem('theme'); 18 const stylesheet = document.getElementById("diks-stylesheet"); 19 20 // Check local storage for saved theme 21 if ( savedTheme == 'darkmode' ) { 22 stylesheet.href = darkmodeStylesheet; 23 document.documentElement.classList.add('diks-theme--darkmode'); 24 } 25 // Check user's theme preference through an OS setting or a user agent setting 26 else if ( checkUserPreferenceEnabled && savedTheme != 'default' && ( window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ) ) { 27 stylesheet.href = darkmodeStylesheet; 28 document.documentElement.classList.add('diks-theme--darkmode'); 29 } 30 else { 31 document.documentElement.classList.add('diks-theme--default'); 32 } 33 34 35 // Darkmode toggle switch 36 document.addEventListener('DOMContentLoaded', function () { 37 const darkmodeButton = document.getElementById("diks-darkmode-toggler"); 38 39 if (darkmodeButton) { 40 // Set darkmode button to checked if darkmode is already on 41 // Check local storage for saved theme 42 if (savedTheme == 'darkmode' ) { 43 darkmodeButton.checked = true; 44 } 45 // Check user's theme preference through an OS setting or a user agent setting 46 else if ( checkUserPreferenceEnabled && savedTheme != 'default' && ( window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ) ) { 47 darkmodeButton.checked = true; 48 } 49 50 // Change theme based on darkmode button checked/unchecked 51 darkmodeButton.addEventListener("click", function() { 52 // Swap out the URL for the different stylesheets 53 if ( stylesheet.getAttribute("href") == defaultStylesheet ) { 54 stylesheet.href = darkmodeStylesheet; 55 document.documentElement.classList.remove('diks-theme--default'); 56 document.documentElement.classList.add('diks-theme--darkmode'); 57 // Save theme to local storage 58 window.localStorage.setItem('theme', 'darkmode'); 59 } else { 60 stylesheet.href = defaultStylesheet; 61 document.documentElement.classList.remove('diks-theme--darkmode'); 62 document.documentElement.classList.add('diks-theme--default'); 63 // Save theme to local storage 64 window.localStorage.setItem('theme', 'default'); 65 } 66 // Dispatch event ThemeChanged when theme inside Local Storage changes 67 window.dispatchEvent(new Event("ThemeChanged")); 68 }); 69 } 70 }, false); 71 72 </script>
72 73 74 <link rel="preload" href="/gravito-cmp.js" as="script"> 75 <link rel="preload" href="https://cdn.gravito.net/sdk/v6/latest/sdk.js" as="script"><link rel="preload" href="https://r209.ylakarjala.fi/script.js" as="script"> 76
76<script> 77 window.JankkoSSOConfig = { 78 accountOrigin: "YLKA" 79 }; 80 </script>
80 81 82 83
83<script id="jankko-sso-script" src="https://sso.ylakarjala.fi/assets/jankko-sso-client.modern.min.js"></script>
83 84 85 86 87 88 89
89<script> 90 // Hyphenopoly library initialization. The library will add soft-hyphen characters into the content if the 91 // browser doesn't support CSS hyphenation. For debugging on macOS Chrome: require: { 'fi': 'FORCEHYPHENOPOLY' } 92 const Hyphenopoly = { 93 require: { 'fi': 'yhdyssanahirviö' }, 94 setup: { 95 defaultLanguage: "fi", 96 selectors: { '.diks-card__title': { minWordLength: 11, leftmin: 4, rightmin: 4 }, '.diks-article__headline': { minWordLength: 11, leftmin: 4, rightmin: 4 }, '.diks-parallax-content .diks-article-box .diks-sub-headline': { minWordLength: 11, leftmin: 4, rightmin: 4 } } 97 } 98 } 99 console.log('Hyphenopoly settings:', Hyphenopoly.setup) 100 101 // Helper function for hyphenating content that was added after pageload. We must query a nodelist and iterate 102 // each node because the library can't handle a nodelist. 103 // https://mnater.github.io/Hyphenopoly/Hyphenators.html 104 // https://github.com/mnater/Hyphenopoly/issues/92#issuecomment-597728300 105 const diksHyphenateContent = (containerSelector) => { 106 if (!Hyphenopoly.hyphenators) { 107 return 108 } 109 Hyphenopoly.hyphenators["HTML"].then((hyphenator) => { 110 for (const itemSelector in Hyphenopoly.setup.selectors) { 111 const containerNode = document.querySelector(containerSelector) 112 const itemNodeList = containerNode.querySelectorAll(itemSelector) 113 itemNodeList.forEach((itemNode) => { hyphenator(itemNode, itemSelector) }) 114 } 115 }) 116 } 117 </script>
117 118
118<script src="https://a.ylakarjala.fi/styles/2efe7af9/includes/hyphenopoly/Hyphenopoly_Loader.js"></script>
118 119
119<script src="/theme/css/includes/waypoints/lib/noframework.waypoints.min.js"></script>
119 120 <link rel="icon" type="image/png" href="/favicon.png" sizes="96x96" /><link rel="icon" href="/favicon.ico" sizes="48x48"><link rel="icon" href="/favicon.svg" sizes="any" type="image/svg+xml" /><link rel="apple-touch-icon" href="/icon-180.png"/><meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)" /> 121 <meta name="theme-color" content="#121416" media="(prefers-color-scheme: dark)" /><link rel="alternate" type="application/rss+xml" title="Ylä-Karjala" href="https://www.ylakarjala.fi/feed/rss"><meta name="robots" content="max-image-preview:large">
121<script> 122 var pageData = {}; 123 124 pageData.section = { 125 'name': 'Sovellus', 126 'uname': 'asenna', 127 'url': 'https\u003A\/\/www.ylakarjala.fi\/asenna\/' 128 } 129 pageData.publication = { 130 'name': 'ylakarjala', 131 'url': 'https\u003A\/\/www.ylakarjala.fi\/', 132 'engage': 'YLKA', 133 'paywallEnabled': true, 134 'registrationWallEnabled': true, 135 'env':'prod' 136 } 137 pageData.user = {} 138 pageData.companyUser = {} 139 140 function isPwa() { 141 // window.navigator.standalone = iOS 142 // window.matchMedia('(display-mode: standalone)').matches = Android/universal 143 return (window.navigator.standalone) || 144 (["fullscreen", "standalone", "minimal-ui"].some( 145 function(displayMode) { return window.matchMedia('(display-mode: ' + displayMode + ')') 146 .matches })); 147 } 148 149 pageData.general = { 150 'browseMode': (isPwa() ? 'pwa' : 'browser') 151 } 152</script>
152 153 <meta property="og:site_name" content="Ylä-Karjala"> 154 <meta property="og:locale" content="fi_FI"><meta name='description' content='Sovellus sisältää tuoreet juttumme, joita ilmestyy ympäri vuorokauden vuoden jokaisena päivänä sekä näköislehtemme liitteineen sekä näköislehtien arkiston.'> 155<meta property='og:title' content='Sovellus | Ylä-Karjala'> 156<meta property='og:description' content='Sovellus sisältää tuoreet juttumme, joita ilmestyy ympäri vuorokauden vuoden jokaisena päivänä sekä näköislehtemme liitteineen sekä näköislehtien arkiston.'> 157<meta property='og:url' content='https://www.ylakarjala.fi/asenna/'> 158<meta property='og:type' content='webpage'><meta property='og:image' content='https://a.ylakarjala.fi/styles/2efe7af9/images/default/og-image-ylakarjala.jpg'> 159<meta property='og:image:width' content='1200'> 160<meta property='og:image:height' content='630'> 161 <link rel="manifest" href="/manifest.webmanifest"> 162 <style>@media (max-width: 600px) { .gravitoCMP-footer { margin-top: 1.5em; } }</style> 163
163<script type="text/javascript">window.cX = window.cX || {};// Async load of cx.js 164 (function(d,s,e,t){e=d.createElement(s);e.type='text/java'+s;e.async='async'; 165 e.src='http'+('https:'===location.protocol?'s://s':'://')+'cdn.cxense.com/cx.js'; 166 t=d.getElementsByTagName(s)[0];t.parentNode.insertBefore(e,t);})(document,'script'); 167 </script>
167 168
168<script src="https://a.ylakarjala.fi/styles/2efe7af9/js/blueconic/dialog.min.js"></script>
168 169
169<script> 170 BCinst = 'blueconic'; 171 BCshortcode = 'YLKA'; 172 BCHighImpactAdHidingEnabled = true; 173 BCHighImpactAdSizes = [[1600,1150],[970,970],[970,540],[970,170],[320,320],[300,240],[300,220],[300,210],[1,2]]; 174 BCHighImpactPrerequisiteSlots = ["gam-ad-topscroll_skin","gam-ad-topscroll_mob"]; 175 BCHighImpactPrerequisiteTimeout = 5000; 176 BCGAMAdsEnabled = false; 177 initHighImpactAdHiding(); 178 </script>
178<script src="https://a.ylakarjala.fi/styles/2efe7af9/js/blueconic/events.min.js"></script>
178 179
179<script> 180 const BCPlatform = "web"; 181 const BCChannel = 'YLKA'; 182 183 const BCArticleViewProperties = {}; 184 const BCSessionStartProperties = {}; 185 186 BCArticleViewProperties["platform"] = BCPlatform; 187 BCArticleViewProperties["channel"] = BCChannel; 188 BCSessionStartProperties["platform"] = BCPlatform; 189 BCSessionStartProperties["channel"] = BCChannel; 190 191 const BCArticleID = pageData?.article?.id || ''; 192 const BCArticleMarketingCategories = pageData?.article?.marketingCategories || []; 193 const BCArticleTopics = pageData?.article?.topics || []; 194 195 if (BCArticleID) { 196 BCArticleViewProperties["articleID"] = BCArticleID; 197 } 198 199 if (Array.isArray(BCArticleMarketingCategories) && BCArticleMarketingCategories.length > 0) { 200 BCArticleViewProperties["marketing_categories"] = BCArticleMarketingCategories.join(', '); 201 } 202 203 if (Array.isArray(BCArticleTopics) && BCArticleTopics.length > 0) { 204 BCArticleViewProperties["tags"] = BCArticleTopics.join(', '); 205 } 206 207 const BCEventParams = { 208 currentPage: '/asenna/' || null, 209 pageType: 'section' || null, 210 dependenciesTimeout: 5000, 211 inHouseAdEnabled: true, 212 articleViewEventEnabled: true, 213 articleViewProperties: BCArticleViewProperties, 214 sessionStartEventEnabled: true, 215 sessionStartProperties: BCSessionStartProperties, 216 recommendationsEnabled: false, 217 recommendationsMaxItems: 3, 218 recommendationsTitle: 'Luitko\u0020jo\u0020n\u00E4m\u00E4\u003F' 219 }; 220 InitializeEventScript(BCEventParams); 221 </script>
221<script> 222 window.BCDatalayer = []; 223 </script>
223<script> 224 (function () { 225 window.BCscriptInjected = false; 226 var scriptSrc = ''; 227 228 scriptSrc = 'https://r209.ylakarjala.fi/script.js'; 229 230 window.addEventListener( 231 "gravito:tcfv2:client", 232 function (event) { 233 let eventType = event.detail.eventType; 234 let consentUpdateEventsList = [ 235 "layer1:opt-out:all", 236 "layer2:opt-out:all", 237 "layer2:opt-in:selected", 238 "layer1:opt-in:all", 239 "layer2:opt-in:all", 240 "opt-in:previously", 241 ]; 242 if (consentUpdateEventsList.includes(eventType) && window.BCscriptInjected === false) { 243 window.BCscriptInjected = true; 244 245 var script = document.createElement("script"); 246 script.src = scriptSrc; 247 document.head.appendChild(script); 248 } 249 }, 250 true 251 ); 252 }()); 253 </script>
253 254 255
255<script type='text/javascript'> 256 (function() { 257 /** CONFIGURATION START **/ 258 var _sf_async_config = window._sf_async_config = (window._sf_async_config || {}); 259 _sf_async_config.uid = 60754; 260 _sf_async_config.domain = location.host.replace("www.",""); 261 _sf_async_config.flickerControl = false; 262 _sf_async_config.useCanonical = true; 263 _sf_async_config.useCanonicalDomain = true; 264 /** CONFIGURATION END **/ 265 })(); 266 </script>
266<script> 267 function configureChartbeat(userType) { 268 /** CONFIGURATION START **/ 269 var _sf_async_config = window._sf_async_config = (window._sf_async_config || {}); 270 _sf_async_config.sections = pageData && pageData.section ? pageData.section.name : ''; 271 _sf_async_config.authors = pageData && pageData.article && Array.isArray(pageData.article.authors) ? pageData.article.authors.toString() : ''; 272 273 if (userType) { 274 var _cbq = window._cbq = (window._cbq || []); 275 _cbq.push(['_acct', userType]); 276 } 277 /** CONFIGURATION END **/ 278 function loadChartbeat() { 279 var e = document.createElement('script'); 280 var n = document.getElementsByTagName('script')[0]; 281 e.type = 'text/javascript'; 282 e.async = true; 283 e.src = 'https://static.chartbeat.com/js/chartbeat.js'; 284 n.parentNode.insertBefore(e, n); 285 } 286 loadChartbeat(); 287 }; 288 289 if (typeof JankkoSSOClient !== 'undefined') { 290 JankkoSSOClient.getSession().then(function(data) { 291 if (data && data.user && data.user.id) { 292 var userType = Array.isArray(data.permissions) && data.permissions.includes("paywall") ? 'paid' : 'lgdin'; 293 294 configureChartbeat(userType); 295 } else { 296 configureChartbeat('anon'); 297 } 298 }).catch(function(error) { 299 configureChartbeat('anon'); 300 }); 301 } else { 302 configureChartbeat('anon'); 303 } 304 </script>
304 305
305<script> 306 const gtmContainerId = 'GTM-KTJNB7R'; 307 const sendDatalayerPush = Boolean(1); 308 const context = 'sec'; 309 310 (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 311 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 312 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 313 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 314 })(window,document,'script','dataLayer',gtmContainerId); 315 316 if (sendDatalayerPush) { 317 window.dataLayer.push({'event':'pageDataPush','pageData': window.pageData}); 318 } 319 320 (async function () { 321 const paywallEnabled = window?.pageData?.publication?.paywallEnabled ?? true; 322 const registrationWallEnabled = window?.pageData?.publication?.registrationWallEnabled ?? true; 323 324 const user = {}; 325 const companyUser = {}; 326 327 let articleState; 328 let articlePaywall = 'free'; // free is always the safer fallback 329 330 if (context === 'art') { 331 if (window?.pageData?.article?.effectivePaywall) { 332 articlePaywall = window.pageData.article.effectivePaywall; 333 } else if (window?.pageData?.article?.paywall){ 334 articlePaywall = window.pageData.article.paywall; 335 } 336 337 if ((articlePaywall === 'free' || !paywallEnabled) || (articlePaywall === "registered" && !registrationWallEnabled)) { 338 articleState = "full"; 339 } 340 else { 341 articleState = 'preview'; 342 } 343 } 344 345 function getCookie(name) { 346 name = name + '='; 347 const decodedCookie = decodeURIComponent(document.cookie); 348 const cookies = decodedCookie.split(';'); 349 for (let i = 0; i < cookies.length; i++) { 350 const cookie = cookies[i].trim(); 351 if (cookie.indexOf(name) === 0) { 352 return cookie.substring(name.length, cookie.length); 353 } 354 } 355 } 356 357 function pushDataLayerEvent() { 358 let segmentsCookie = getCookie('jankkosegments'); 359 360 if (segmentsCookie) { 361 let segmentsData = JSON.parse(segmentsCookie); 362 let segments = segmentsData.segments; 363 364 if (segments && typeof segments === 'object') { 365 user.segments = segments; 366 } 367 } 368 const pageData = { 369 'user': user,
370 'companyUser': companyUser 371 }; 372 if (context === 'art') { 373 pageData.article = { 'state': articleState }; 374 } 375 if (sendDatalayerPush) { 376 window.dataLayer.push({'event': 'userDataPush', 'pageData': pageData }); 377 } 378 } 379 380 async function modifyDatalayerData() { 381 const sessionData = await JankkoSSOClient.getSession(); 382 if (!sessionData) { 383 return; 384 } 385 386 user.id = sessionData?.personal?.personal_user_id; 387 user.products = sessionData?.personal?.personal_active_products; 388 user.permissions = sessionData?.personal?.personal_permissions; 389 390 companyUser.id = sessionData?.company?.company_user_id ?? null; 391 companyUser.products = sessionData?.company?.company_active_products ?? null; 392 companyUser.permissions = sessionData?.company?.company_permissions ?? null; 393 394 const permissions = sessionData?.permissions ?? []; 395 if (context === 'art') { 396 var userIsLoggedIn = false;if ((articlePaywall != "free" && permissions.includes("paywall") && articlePaywall != "registered") || (userIsLoggedIn === true && articlePaywall === "registered")) { 397 articleState = "full"; 398 } 399 } 400 } 401 402 try { 403 await modifyDatalayerData(); 404 } 405 catch (error) { 406 console.warn(error); 407 } 408 409 pushDataLayerEvent(); 410 }()); 411 412 </script>
412 413 414 </head> 415 <body class="diks-body diks-body--page-type-apps"> 416 <!-- Google Tag Manager (noscript) -->
vendor: 72 bytes, lines 416-417
416 417 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
417GTM-KTJNB7R
vendor: 95 bytes, lines 417-419
417" 418 height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 419
419<!-- End Google Tag Manager (noscript) --> 420
420<script src='/gravito-cmp.js'></script>
420 421 422 423 424 425 <header id="main-header" class="diks-header diks-header diks-header--is-fixed"> 426 <div class="diks-skip-link-navigation diks-skip-link-navigation--top"> 427 <ul class="diks-navigation diks-navigation--skip-links"> 428 <li class="diks-navigation__item"> 429 <a class="diks-button diks-button--primary diks-a11y__screen-reader diks-a11y__screen-reader--focusable" href="#sisalto" >Siirry suoraan sisältöön</a> 430 </li> 431 </ul> 432 </div> 433 434 <div class="diks-header-wrapper"> 435 <div class="diks-container diks-container--header"> 436 <div class="diks-header-container"><div class="diks-header__navigation-container diks-header__navigation-container--left"> 437 <button class="diks-button diks-button--icon-only diks-button--ghost diks-navigation__link js-diks-toggler diks-dropdown-toggler diks-dropdown-toggler--main" id="diks-dropdown-toggle" data-toggle="diks-dropdown-navigation" aria-expanded="false" aria-controls="diks-dropdown-navigation" aria-label="Valikko"><span class="diks-icon diks-icon--size-m" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 438 <path fill-rule="evenodd" d="M22 6H2V4H22V6ZM22 13H2V11H22V13ZM22 20H2V18H22V20Z" clip-rule="evenodd"/> 439</svg> 440</span><span class="diks-icon diks-icon--size-m diks-icon--hidden" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 441 <path fill-rule="evenodd" d="M10.5858 12L4.2218 5.63603L5.63602 4.22182L12 10.5858L18.3639 4.22183L19.7782 5.63605L13.4142 12L19.7782 18.364L18.3639 19.7782L12 13.4142L5.63602 19.7782L4.2218 18.364L10.5858 12Z" clip-rule="evenodd"/> 442</svg> 443</span></button> 444 445 <div class="diks-dropdown-navigation" id="diks-dropdown-navigation"> 446 <button id="diks-close-dropdown-main-nav" class="diks-button diks-button--outline diks-dropdown-navigation__close-button diks-a11y__screen-reader diks-a11y__screen-reader--focusable">Sulje valikko</button><div class="diks-dropdown-navigation__sign-container"> 447 <img src="https://a.ylakarjala.fi/styles/2efe7af9/images/logos/keskisuomalainen-sign.svg" class="diks-logo diks-dropdown-navigation__sign" alt="Keskisuomalainen Oyj"> 448 </div><form id="dropdown-navigation-search" name="dropdownnavigationsearch" class="diks-form diks-form--fluid" action="/haku/" method="get"> 449 <div class="diks-form__field-wrapper diks-form__field-wrapper--padding-right diks-form__field-wrapper--required"><input class="diks-form__field" type="search" name="q" id="dropdown-navigation-searchSearch1" aria-label="Haku" placeholder="Hae sivustolta" required value=""></div> 450 451 <div class="diks-form__field-wrapper diks-form__field-wrapper--submit-button-icon-only"> 452 <button class="diks-form__submit" type="submit" id="dropdown-navigation-searchSubmitbutton2"><span class="diks-icon diks-icon--color-primary" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 453 <path fill-rule="evenodd" d="M9.5 4C6.46243 4 4 6.46243 4 9.5C4 12.5376 6.46243 15 9.5 15C12.5376 15 15 12.5376 15 9.5C15 6.46243 12.5376 4 9.5 4ZM2 9.5C2 5.35786 5.35786 2 9.5 2C13.6421 2 17 5.35786 17 9.5C17 11.2106 16.4273 12.7874 15.4632 14.0493L19.7782 18.3642L18.3639 19.7784L14.049 15.4634C12.7872 16.4274 11.2105 17 9.5 17C5.35786 17 2 13.6421 2 9.5Z" clip-rule="evenodd"/> 454</svg> 455</span>Hae 456 </button> 457 </div> 458 </form> <nav class="diks-navigation-wrapper diks-navigation-wrapper--border-bottom diks-navigation-wrapper--border-top" ><ul class="diks-navigation diks-navigation--row diks-navigation--mobile-row diks-navigation--no-active-links diks-navigation--size-s" id="dropdown-highlights-nav"><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/asenna/">Sovellus</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/nakoislehti/">Lehdet</a></li><li class="diks-navigation__item diks-navigation__item--button diks-navigation__item--align-right diks-hide-from-subscribers" ><a class="diks-navigation__link diks-bc-order-button" href="/jatka/">Tilaa</a></li></ul> 459 </nav> <nav class="diks-navigation-wrapper diks-navigation-wrapper--divider-bottom" ><ul class="diks-navigation diks-navigation--no-active-links diks-navigation--size-s" id="dropdown-customer-upper-nav"><li class="diks-navigation__item " ><a class="diks-navigation__link " href="https://oma.media.fi/yla-karjala/"><span class="diks-icon diks-icon--size-m" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 460 <path d="M17 8H22V17H19.1875V20.8125L14.1045 17H9V13.4307L4.4375 17.0811V13H2V3H17V8ZM17 13H11V15H14.7705L17.1875 16.8125V15H20V10H17V13ZM4 11H6.4375V12.918L8.5625 11.2188L8.83691 11H15V5H4V11Z"/> 461</svg> 462</span>Asiakaspalvelu</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="https://oma.media.fi/yla-karjala/yhteystiedot/juttuvinkki/"><span class="diks-icon diks-icon--size-m" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 463 <path fill-rule="evenodd" d="M7.38197 4H16.618L17.618 6H22V20H2V6H6.38197L7.38197 4ZM8.61803 6L7.61803 8H4V18H20V8H16.382L15.382 6H8.61803ZM12 11C10.8954 11 10 11.8954 10 13C10 14.1046 10.8954 15 12 15C13.1046 15 14 14.1046 14 13C14 11.8954 13.1046 11 12 11ZM8 13C8 10.7909 9.79086 9 12 9C14.2091 9 16 10.7909 16 13C16 15.2091 14.2091 17 12 17C9.79086 17 8 15.2091 8 13Z" clip-rule="evenodd"/> 464</svg> 465</span>Lähetä juttuvinkki</a></li></ul> 466 </nav> <nav class="diks-navigation-wrapper " ><ul class="diks-navigation diks-navigation--light" id="dropdown-section-nav"><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/">Etusivu</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/uusimmat/">Uusimmat</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/luetuimmat/">Luetuimmat</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/paikalliset">Uutiset</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/paakirjoitus-mielipide/">Mielipiteet</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/blogit">Blogit</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/menneilta-vuosilta">Menneiltä vuosilta</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="https://pohjois-karjala.menoinfo.fi/fi-FI?sort=startDate&area=area-143">Menoinfo</a></li></ul> 467 </nav><!-- ESI start element "recommendedTopics" generated at 02-10-2026 16:55:56.622804 --> <nav class="diks-navigation-wrapper diks-navigation-wrapper--divider-top" ><ul class="diks-navigation diks-navigation--light diks-navigation--size-s" ><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/yla-karjalan-visat">Ylä-Karjalan visat</a></li></ul> 468 </nav><!-- ESI end element "recommendedTopics" --><!-- ESI start element "promotedAdvertisers" generated at 02-10-2026 16:55:26.115561 --><!-- ESI end element "promotedAdvertisers" --> <nav class="diks-navigation-wrapper diks-navigation-wrapper--divider-top" ><h2 class="diks-navigation__title">Yhteystiedot</h2><ul class="diks-navigation diks-navigation--light diks-navigation--size-s" id="dropdown-customer-nav"><li class="diks-navigation__item " ><a class="diks-navigation__link " href="https://www.meks.fi/mainonta/alueet/savon-media/">Mediamyynti</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="https://oma.media.fi/yla-karjala/yhteystiedot/">Kaikki yhteystiedot</a></li></ul> 469 </nav><div class="diks-navigation-wrapper diks-navigation-wrapper--divider-top"><h2 class="diks-navigation__title">Ulkoasu</h2><label for="diks-darkmode-toggler" class="diks-form__switch"> 470 <span class="diks-form__switch-label"><span class="diks-icon diks-icon--size-m" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 471 <path fill-rule="evenodd" d="M13.7547 1.76065L11.7581 3.75666C10.6708 4.8436 10 6.34252 10 8C10 11.3137 12.6863 14 16 14C17.6575 14 19.1564 13.3292 20.2433 12.2419L22.2394 10.2453L21.9451 13.0531C21.4182 18.0815 17.1674 22 12 22C6.47715 22 2 17.5229 2 12C2 6.83263 5.91855 2.58178 10.9469 2.05488L13.7
471547 1.76065ZM8.70749 4.70657C5.93176 5.9617 4 8.75566 4 12C4 16.4183 7.58172 20 12 20C15.2443 20 18.0383 18.0682 19.2934 15.2925C18.289 15.7468 17.1738 16 16 16C11.5817 16 8 12.4183 8 8C8 6.82623 8.25316 5.71105 8.70749 4.70657Z" clip-rule="evenodd"/> 472</svg> 473</span>Käytä tummaa tilaa</span> 474 <div class="diks-form__switch-wrapper"> 475 <input id="diks-darkmode-toggler" class="diks-form__switch-input" type="checkbox"> 476 <span class="diks-form__switch-slider"></span> 477 </div> 478 </label> 479 </div></div> <nav class="diks-navigation-wrapper " ><ul class="diks-navigation diks-navigation--main diks-navigation--size-s diks-navigation--row diks-navigation--mobile-row diks-navigation--no-active-links diks-hide-below-large" ><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/uusimmat/"><span class="diks-icon diks-hide-above-large" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 480 <path fill-rule="evenodd" d="M12 4a8 8 0 100 16 8 8 0 000-16zM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12zm9-6h2v5.586l2.707 2.707-1.414 1.414L11 12.414V6z" clip-rule="evenodd"/> 481</svg> 482</span>Uusimmat</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/luetuimmat/"><span class="diks-icon diks-hide-above-large" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 483 <path fill-rule="evenodd" d="M11.03 3.455c1.168-1.438 3.495-.652 3.55 1.2l.117 3.845h6a1.25 1.25 0 0 1 1.229 1.484l-1.905 10A1.25 1.25 0 0 1 18.793 21H9.06a1.25 1.25 0 0 1-.559-.132L6.764 20H3.25C2.56 20 2 19.44 2 18.75v-8.5C2 9.56 2.56 9 3.25 9h3.274l4.506-5.545ZM8 10.355v8.027L9.236 19h8.936l1.62-8.5h-5.58a1.5 1.5 0 0 1-1.499-1.455l-.131-4.33L8 10.356Zm10.056 9.255ZM4 11v7h2v-7H4Zm14.793 8.75.145.027Z" clip-rule="evenodd"/> 484</svg> 485</span>Luetuimmat</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/nakoislehti/"><span class="diks-icon diks-hide-above-large" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 486 <path fill-rule="evenodd" d="M4 2H20V22H4V2ZM6 4V20H18V4H6ZM8 6H16V12H8V6ZM10 8V10H14V8H10ZM16 15H8V13H16V15ZM16 18H8V16H16V18Z" clip-rule="evenodd"/> 487</svg> 488</span>Lehdet</a></li><li class="diks-navigation__item diks-navigation__item--button diks-navigation__item--align-right diks-hide-above-medium diks-hide-from-subscribers" ><a class="diks-navigation__link diks-bc-order-button" href="/jatka/">Tilaa</a></li></ul> 489 </nav></div><div class="diks-header__logo-container"><a class="diks-logo__link diks-header__logo-link diks-header__main-logo-link" href="/"><img src="https://a.ylakarjala.fi/styles/2efe7af9/images/logos/ylakarjala.svg" class="diks-logo diks-logo--default diks-header__logo diks-header__main-logo" alt="Ylä-Karjala"><img src="https://a.ylakarjala.fi/styles/2efe7af9/images/logos/ylakarjala-white.svg" class="diks-logo diks-logo--darkmode diks-header__logo diks-header__main-logo" alt="Ylä-Karjala"></a> 490 </div><div class="diks-header__navigation-container diks-header__navigation-container--right"><a class="diks-bc-order-button diks-button diks-button--primary diks-button--size-m diks-hide-from-subscribers diks-hide-below-medium " href="/jatka/">Tilaa</a><div class="diks-user-navigation-wrapper"> 491 <button class="diks-button diks-button--icon-only diks-button--ghost diks-navigation__link diks-dropdown-toggler diks-dropdown-toggler--user" aria-expanded="false" id="diks-sso-user" data-diks-toggle="diks-user-navigation" data-diks-toggle-class="diks-user-navigation" aria-controls="diks-user-navigation"><span class="diks-icon diks-icon--size-m" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 492 <path fill-rule="evenodd" d="M4 12a8 8 0 1 1 13.496 5.814c-1.216-1.593-3.262-2.602-5.491-2.602-2.231 0-4.28 1.011-5.495 2.607A7.978 7.978 0 0 1 4 12Zm4.04 6.953A7.963 7.963 0 0 0 12 20a7.964 7.964 0 0 0 3.966-1.05c-.83-1.087-2.254-1.84-3.961-1.84-1.71 0-3.135.754-3.965 1.843ZM12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm.005 5.898c-1.247 0-2.305 1.049-2.305 2.4 0 1.353 1.058 2.402 2.305 2.402s2.305-1.05 2.305-2.401c0-1.352-1.058-2.4-2.305-2.4Zm-4.203 2.4C7.802 7.952 9.658 6 12.005 6c2.347 0 4.203 1.95 4.203 4.299 0 2.348-1.856 4.299-4.203 4.299-2.347 0-4.203-1.951-4.203-4.3Z" clip-rule="evenodd"/> 493</svg> 494</span><span class="diks-icon diks-icon--size-m diks-icon--hidden" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 495 <path fill-rule="evenodd" d="M10.5858 12L4.2218 5.63603L5.63602 4.22182L12 10.5858L18.3639 4.22183L19.7782 5.63605L13.4142 12L19.7782 18.364L18.3639 19.7782L12 13.4142L5.63602 19.7782L4.2218
49518.364L10.5858 12Z" clip-rule="evenodd"/> 496</svg> 497</span></button> 498 499 <div class="diks-navigation diks-sub-navigation diks-user-navigation" id="diks-user-navigation"> 500 <button id="diks-close-dropdown-user-nav" class="diks-button diks-button--outline diks-dropdown-navigation__close-button diks-a11y__screen-reader diks-a11y__screen-reader--focusable">Sulje valikko</button> 501 <p id="diks-sso-username" class="diks-user-navigation__username diks-hide-from-company-subscribers"></p><p class="diks-user-navigation__username diks-show-for-company-subscribers">Yritystilaus tunnistettu</p><div class="diks-show-for-company-subscribers"><p>Voit käyttää palvelun kaikkia sisältöjä vapaasti. Jos haluat kommentoida, kirjaudu sisään henkilökohtaisella Mediatunnuksella.</p></div> <ul class="diks-navigation diks-navigation--size-m" id="diks-sso-links"><li class="diks-navigation__item " ><a class="diks-navigation__link " href="javascript:window.gravito.cmp.openPreferences();">Evästevalinnat</a></li></ul> 502 </div> 503</div> 504 </div> 505 </div> 506 </div> 507 </div><div class="diks-section-navigation-wrapper"> 508 <div class="diks-container diks-container--narrow-mobile diks-container--full-width-before-l"> 509 <nav class="diks-navigation-wrapper diks-navigation-wrapper--border-bottom" aria-label="Osastovalikko"><ul class="diks-navigation diks-navigation--row diks-navigation--light diks-navigation--centered diks-navigation--mobile-row diks-navigation--draggable diks-navigation--size-xs diks-navigation--active-subtle" id="osastovalikko"><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/paikalliset"draggable="false">Uutiset</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/paakirjoitus-mielipide/"draggable="false">Mielipiteet</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/blogit"draggable="false">Blogit</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/menneilta-vuosilta"draggable="false">Menneiltä vuosilta</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="https://pohjois-karjala.menoinfo.fi/fi-FI?sort=startDate&area=area-143"draggable="false">Menoinfo</a></li></ul> 510 </nav> 511 </div> 512 </div><div class="diks-quicklinks-wrapper"> 513 <div class="diks-container diks-container--narrow-mobile"> 514 <nav class="diks-navigation-wrapper " ><ul class="diks-navigation diks-navigation--main diks-navigation--size-s diks-navigation--row diks-navigation--mobile-row diks-navigation--no-active-links diks-navigation--centered diks-hide-above-large" ><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/uusimmat/"><span class="diks-icon diks-hide-above-large" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 515 <path fill-rule="evenodd" d="M12 4a8 8 0 100 16 8 8 0 000-16zM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12zm9-6h2v5.586l2.707 2.707-1.414 1.414L11 12.414V6z" clip-rule="evenodd"/> 516</svg> 517</span>Uusimmat</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/luetuimmat/"><span class="diks-icon diks-hide-above-large" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 518 <path fill-rule="evenodd" d="M11.03 3.455c1.168-1.438 3.495-.652 3.55 1.2l.117 3.845h6a1.25 1.25 0 0 1 1.229 1.484l-1.905 10A1.25 1.25 0 0 1 18.793 21H9.06a1.25 1.25 0 0 1-.559-.132L6.764 20H3.25C2.56 20 2 19.44 2 18.75v-8.5C2 9.56 2.56 9 3.25 9h3.274l4.506-5.545ZM8 10.355v8.027L9.236 19h8.936l1.62-8.5h-5.58a1.5 1.5 0 0 1-1.499-1.455l-.131-4.33L8 10.356Zm10.056 9.255ZM4 11v7h2v-7H4Zm14.793 8.75.145.027Z" clip-rule="evenodd"/> 519</svg> 520</span>Luetuimmat</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/nakoislehti/"><span class="diks-icon diks-hide-above-large" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 521 <path fill-rule="evenodd" d="M4 2H20V22H4V2ZM6 4V20H18V4H6ZM8 6H16V12H8V6ZM10 8V10H14V8H10ZM16 15H8V13H16V15ZM16 18H8V16H16V18Z" clip-rule="evenodd"/> 522</svg> 523</span>Lehdet</a></li><li class="diks-navigation__item diks-navigation__item--button diks-navigation__item--align-right diks-hide-above-medium diks-hide-from-subscribers" ><a class="diks-navigation__link diks-bc-order-button" href="/jatka/">Tilaa</a></li></ul> 524 </nav> 525 </div> 526 </div><div id="diks-dropdown-navigation-shade" class="diks-dropdown-navigation-shade"></div></header>
526<script src="/theme/css/includes/waypoints/lib/noframework.waypoints.min.js"></script>
526 527
527<script> 528 // Fix forEach for IE 529 if (window.NodeList && !NodeList.prototype.forEach) { 530 NodeList.prototype.forEach = Array.prototype.forEach; 531 } 532 533 // Element.closest() needs a polyfill for older browsers. 534 // While we only need the closest() functionality for mobile browsers, 535 // we don't want the code to fail even on old desktop browsers. 536 // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill 537 // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Browser_compatibility 538 if (!Element.prototype.matches) { 539 Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; 540 } 541 542 if (!Element.prototype.closest) { 543 Element.prototype.closest = function(s) { 544 var el = this; 545 546 do { 547 if (el.matches(s)) return el; 548 el = el.parentElement || el.parentNode; 549 } while (el !== null && el.nodeType === 1); 550 return null; 551 }; 552 } 553 554 (function () { 555 if (typeof JankkoSSOClient !== 'undefined') { 556 var stringToHTML = function (htmlString) { 557 var div = document.createElement('div'); 558 div.innerHTML = htmlString.trim(); 559 return div.firstChild; 560 }; 561 562 var showUserRelatedItems = function(personalData) { 563 var usernameElement = document.querySelector('#diks-sso-username'); 564 if (usernameElement) { 565 usernameElement.innerHTML = personalData.full_name; 566 } 567 568 var linksElement = document.querySelector('#diks-sso-links'); 569 if (linksElement) { 570 var myAccountLink = stringToHTML('<li class="diks-navigation__item"><a class="diks-navigation__link" href="'+JankkoSSOClient.serviceUrls.myAccount+'">Omat tiedot</a></li>'); 571 var logoutForm = stringToHTML('<li class="diks-navigation__item diks-navigation__item--logout"><form id="logout-form" action="'+JankkoSSOClient.serviceUrls.logout+'" method="post"><input class="diks-navigation__link" type="submit" value="Kirjaudu ulos"></form></li>'); 572 linksElement.insertBefore(myAccountLink, linksElement.childNodes[0]); 573 linksElement.appendChild(logoutForm); 574 } 575 576 var htmlElement = document.querySelector('.diks-user--is-not-logged-in'); // Class is set to main html element 577 if (htmlElement) { 578 htmlElement.classList.replace('diks-user--is-not-logged-in', 'diks-user--is-logged-in'); 579 } 580 } 581 582 var showMenuLinksForLoggedOutUser = function() { 583 const linksElement = document.querySelector('#diks-sso-links'); 584 if (!linksElement) { 585 return; 586 } 587 588 // Pidetään vain tietyt query-parametrit. Alunperin tässä poistettiin kaikki query-parametrit. 589 // @todo Selvitetään voisiko tässä sallia kaikki query-parametrit. 590 const currentUrlObject = new URL(window.location.href); 591 const returnUrlObject = new URL(`${currentUrlObject.origin}${currentUrlObject.pathname}`); 592 const allowedParams = ['limit-active-products']; 593 allowedParams.forEach(function(name) { 594 if (currentUrlObject.searchParams.get(name)) { 595 returnUrlObject.searchParams.set(name, currentUrlObject.searchParams.get(name)); 596 } 597 }); 598 599 const loginPageUrlObject = new URL(`${window.location.origin}/kirjaudu/`); 600 loginPageUrlObject.searchParams.set('returnUrl', returnUrlObject.toString()); 601 const loginLinkString = `<li class="diks-navigation__item"><a class="diks-navigation__link" href="${loginPageUrlObject.toString()}">Kirjaudu</a></li>`; 602 603 linksElement.insertBefore(stringToHTML(loginLinkString), linksElement.childNodes[0]); 604 } 605 606 var showCompanyUserRelatedItems = function(companyData) { 607 var htmlElement = document.querySelector('.diks-user--is-not-company-subscriber'); // Class is set to main html element 608 var permissions = companyData?.company_permissions || []; 609 if (htmlElement && permissions.length) { 610 htmlElement.classList.replace('diks-user--is-not-company-subscriber', 'diks-user--is-company-subscriber'); 611 } 612 } 613 614 document.addEventListener("DOMContentLoaded", async function() { 615 try { 616 const data = await JankkoSSOClient.getSession(); 617 if (data.company?.company_user_id) { 618 showCompanyUserRelatedItems(data.company); 619 }
620 if (data.personal?.personal_user_id) { 621 showUserRelatedItems(data.personal); 622 } else { 623 showMenuLinksForLoggedOutUser(); 624 } 625 } catch (error) { 626 showMenuLinksForLoggedOutUser(); 627 } 628 }); 629 } 630 }()); 631 632 633 ////////////////////////////////////////////////////////////////////////////////////////// 634 // NAVIGATIONS 635 ////////////////////////////////////////////////////////////////////////////////////////// 636 637 // Toggle icon inside a toggler 638 function toggleTogglerIcon(toggler) { 639 togglerIcons = toggler.querySelectorAll('.diks-icon'); 640 if ( togglerIcons.length > 1 ) { 641 for (var j= 0; j < togglerIcons.length; j++) { 642 togglerIcons[j].classList.toggle('diks-icon--hidden'); 643 } 644 } 645 } 646 647 // Function for closing all dropdown navigations 648 function closeAllDropdownNavigations() { 649 closeMainDropdownNavigation(); 650 closeUserDropdownNavigation(); 651 } 652 653 // Close main nav or user nav if hidden screen reader only close navigation button is clicked 654 let closeDropdownMainNav = document.getElementById('diks-close-dropdown-main-nav'); 655 let closeDropdownUserNav = document.getElementById('diks-close-dropdown-user-nav'); 656 if ( closeDropdownMainNav ) { 657 closeDropdownMainNav.addEventListener('click', function() { 658 closeMainDropdownNavigation(); 659 }); 660 } 661 if ( closeDropdownUserNav ) { 662 closeDropdownUserNav.addEventListener('click', function(e) { 663 e.stopPropagation(); 664 closeUserDropdownNavigation(); 665 }); 666 } 667 668 // Trap focus inside opened dropdown navigation 669 document.addEventListener('keydown', function(event) { 670 // Initialize variables 671 var ssoLinks = document.getElementById('diks-sso-links'); 672 var linksUserNav = ""; 673 var firstElUserNav = ""; 674 var lastElUserNav = ""; 675 var mainDropdownLastNavLinks = document.getElementById('dropdown-customer-nav'); // Last navigation element inside of main dropdown navigation 676 var linksMainDropdownNav = ""; 677 var firstElMainDropdownNav = "" 678 var lastElMainDropdownNav = ""; 679 680 // Set user nav variable values if SSO links exist 681 if (ssoLinks) { 682 linksUserNav = ssoLinks.querySelectorAll('.diks-navigation__link'); 683 firstElUserNav = document.getElementById('diks-close-dropdown-user-nav'); 684 lastElUserNav = linksUserNav[linksUserNav.length - 1]; 685 } 686 687 // Set main dropdown nav variable values if main dropdown last nav links exist 688 if (mainDropdownLastNavLinks) { 689 linksMainDropdownNav = mainDropdownLastNavLinks.querySelectorAll('.diks-navigation__link'); 690 firstElMainDropdownNav = document.getElementById('diks-close-dropdown-main-nav'); 691 lastElMainDropdownNav = linksMainDropdownNav[linksMainDropdownNav.length - 1]; 692 } 693 694 // If darkmode-toggler is found set it as the last element of main dropdown nav 695 if ( document.getElementById('diks-darkmode-toggler') ) { 696 lastElMainDropdownNav = document.getElementById('diks-darkmode-toggler'); 697 } 698 699 // Close all dropdown navigations & reposition fixed header when ESC key is pressed 700 if (event.key === "Escape" || event.key === "Esc") { 701 closeAllDropdownNavigations(); 702 } 703 // Trap focus inside user navigation and/or main dropdown navigation when they are open 704 if (event.keyCode === 9) { 705 if (event.shiftKey && (document.activeElement === firstElUserNav || document.activeElement === firstElMainDropdownNav)) { 706 event.preventDefault(); 707 if (document.activeElement === firstElUserNav) { 708 lastElUserNav.focus(); 709 } 710 else if ( document.activeElement === firstElMainDropdownNav ) { 711 lastElMainDropdownNav.focus(); 712 } 713 } 714 else if (!event.shiftKey && (document.activeElement === lastElUserNav || document.activeElement === lastElMainDropdownNav)) { 715 event.preventDefault(); 716 if (document.activeElement === lastElUserNav) { 717 firstElUserNav.focus(); 718 } 719 else if ( document.activeElement === lastElMainDropdownNav ) { 720 firstElMainDropdownNav.focus(); 721 } 722 } 723 } 724 }); 725 726 727 ////////////////////////////////////////////////////////////////////////////////////////// 728 // User navigation 729 ////////////////////////////////////////////////////////////////////////////////////////// 730 731 let userDropdownNavigation = document.getElementById('diks-user-navigation'); // user dropdown navigation 732 let userDropdownNavigationToggler = document.getElementById('diks-sso-user'); // user dropdown navigation toggler button (opens and closes user dropdown navigation) 733 let userDropdownNavigationCloseButton = document.getElementById('diks-close-dropdown-main-nav'); // Hidden screen-reader-only close button inside user dropdown navigation 734 735 // Close user dropdown navigation if hidden screen-reader-only close button is clicked 736 if ( userDropdownNavigationCloseButton ) { 737 userDropdownNavigationCloseButton.addEventListener('click', function() { 738 closeUserDropdownNavigation(); 739 }); 740 } 741 742 function closeUserDropdownNavigation() { 743 if (userDropdownNavigation) { 744 if (userDropdownNavigation.classList.contains('diks-user-navigation--is-open')) { 745 // Close user dropdown nav by removing --is-open class 746 userDropdownNavigation.classList.remove('diks-user-navigation--is-open'); 747 // Remove --is-open class from user dropdown navigation toggler 748 userDropdownNavigationToggler.classList.remove('diks-dropdown-toggle--is-open'); 749 // Toggle icon 750 toggleTogglerIcon(userDropdownNavigationToggler); 751
752 // Set aria-expanded to false for user dropdown toggler 753 userDropdownNavigationToggler.setAttribute('aria-expanded', 'false'); 754 } 755 } 756 } 757 758 // User Navigation Dropdown 759 if (userDropdownNavigation) { 760 let userDropdownNavigationAriaControls = userDropdownNavigationToggler.getAttribute('data-diks-toggle'); // aria-controls value for user dropown navigation toggler 761 userDropdownNavigationToggler.setAttribute('aria-controls', userDropdownNavigationAriaControls); 762 userDropdownNavigationToggler.addEventListener('click', function(e) { 763 e.preventDefault(); 764 // If the main dropdown navigation is already open (contains --is-open class) close it 765 if ( (userDropdownNavigation).classList.contains('diks-user-navigation--is-open') ) { 766 closeUserDropdownNavigation(); 767 } 768 // If the main dropdown navigation isn't open (doesn't contain --is-open class) open it 769 else { 770 // If other toggles are open, close them 771 document.querySelectorAll('.diks-user-navigation--is-open').forEach(function(item) { 772 item.classList.remove('diks-user-navigation--is-open'); 773 userDropdownNavigationToggler.classList.remove('diks-dropdown-toggler--is-open'); 774 }); 775 776 // Toggle icon 777 toggleTogglerIcon(userDropdownNavigationToggler); 778 779 // Set aria-expanded 780 userDropdownNavigationToggler.setAttribute('aria-expanded', 'true'); 781 782 // Open this toggle 783 userDropdownNavigation.classList.add('diks-user-navigation--is-open'); 784 785 // Close main dropdown navigation if it's open 786 closeMainDropdownNavigation(); 787 788 // Add event listener to window when user nav toggler is clicked and user nav is open 789 window.addEventListener('click', function(e){ 790 if (document.querySelector('.diks-user-navigation-wrapper').contains(e.target)){ 791 // Clicked inside user nav, do nothing 792 } 793 else { 794 // Clicked outside user nav, close user nav 795 closeUserDropdownNavigation(); 796 } 797 }); 798 799 } 800 }); 801 } 802 803 ////////////////////////////////////////////////////////////////////////////////////////// 804 // Main navigation 805 ////////////////////////////////////////////////////////////////////////////////////////// 806 807 let mainDropdownNavigation = document.getElementById('diks-dropdown-navigation'); // main dropdown navigation 808 let mainDropdownNavigationToggler = document.getElementById('diks-dropdown-toggle'); // main dropdown navigation toggler button (opens and closes main dropdown navigation) 809 let mainDropdownNavigationShade = document.querySelector('.diks-dropdown-navigation-shade'); // shade effect that is visible when main dropdown navigation is open 810 let mainDropdownNavigationCloseButton = document.getElementById('diks-close-dropdown-main-nav'); // Hidden screen-reader-only close button inside main dropdown navigation 811 812 // Close main dropdown navigation if hidden screen-reader-only close button is clicked 813 if ( mainDropdownNavigationCloseButton ) { 814 mainDropdownNavigationCloseButton.addEventListener('click', function() { 815 closeMainDropdownNavigation(); 816 }); 817 } 818 819 // Function for closing main dropdown navigation 820 function closeMainDropdownNavigation() { 821 // Main dropdown navigation 822 if (mainDropdownNavigation) { 823 if (mainDropdownNavigation.classList.contains('diks-dropdown-navigation--is-open')) { 824 // Close main dropdown nav by removing --is-open class 825 mainDropdownNavigation.classList.remove('diks-dropdown-navigation--is-open'); 826 // Remove --is-open class from main dropdown navigation toggler 827 mainDropdownNavigationToggler.classList.remove('diks-dropdown-toggle--is-open'); 828 // Toggle icon 829 toggleTogglerIcon(mainDropdownNavigationToggler); 830 831 // Set aria-expanded to false for main dropdown toggler 832 mainDropdownNavigationToggler.setAttribute('aria-expanded', 'false'); 833 834 // Remove --is-open class from body 835 document.body.classList.remove('diks-body--dropdown-navigation-is-open'); 836 837 // Turn off shade effect 838 mainDropdownNavigationShade.classList.remove('diks-dropdown-navigation-shade--is-visible'); 839 } 840 } 841 } 842 843 844 // Main Navigation Dropdown 845 if (mainDropdownNavigation) {
846 let mainDropdownNavigationAriaControls = mainDropdownNavigationToggler.getAttribute('data-toggle'); // aria-controls value for main dropown navigation toggler 847 mainDropdownNavigationToggler.setAttribute('aria-controls', mainDropdownNavigationAriaControls); 848 mainDropdownNavigationToggler.addEventListener('click', function(e) { 849 e.preventDefault(); 850 // If the main dropdown navigation is already open (contains --is-open class) close it 851 if ( (mainDropdownNavigation).classList.contains('diks-dropdown-navigation--is-open') ) { 852 closeMainDropdownNavigation(); 853 } 854 // If the main dropdown navigation isn't open (doesn't contain --is-open class) open it 855 else { 856 // If other toggles are open, close them 857 document.querySelectorAll('.diks-dropdown-navigation--is-open').forEach(function(item) { 858 item.classList.remove('diks-dropdown-navigation--is-open'); 859 mainDropdownNavigationToggler.classList.remove('diks-dropdown-toggler--is-open'); 860 }); 861 862 // Toggle icon 863 toggleTogglerIcon(mainDropdownNavigationToggler); 864 865 // Set aria-expanded 866 mainDropdownNavigationToggler.setAttribute('aria-expanded', 'true'); 867 868 // Open this toggle 869 mainDropdownNavigation.classList.add('diks-dropdown-navigation--is-open'); 870 // Add is-open class to body 871 document.body.classList.add('diks-body--dropdown-navigation-is-open'); 872 // Turn on shade effect 873 mainDropdownNavigationShade.classList.add('diks-dropdown-navigation-shade--is-visible'); 874 875 // Close main Dropdown Navigation when visible shade area is clicked 876 document.querySelector('.diks-dropdown-navigation-shade--is-visible').addEventListener('click', function() { 877 closeMainDropdownNavigation(); 878 }); 879 880 // Close user dropdown navigation if it's open 881 closeUserDropdownNavigation(); 882 883 } 884 }); 885 } 886 887 888</script>
888 889 890 891 892 893 894 895 896 <div class="diks-note-wrapper "><div class="diks-container diks-container--narrower-mobile"> 897 <div id="diks-segment-notes"></div><div id="diks-bc-notes"></div></div></div>
897<script> 898 let segmentTeasersContainer = document.getElementById("diks-segment-notes"); 899 900 (function() { 901 function getCookie(name) { 902 name = name + '='; 903 const decodedCookie = decodeURIComponent(document.cookie); 904 const cookies = decodedCookie.split(';'); 905 906 for (let i = 0; i < cookies.length; i++) { 907 const cookie = cookies[i].trim(); 908 if (cookie.indexOf(name) == 0) { 909 return cookie.substring(name.length, cookie.length); 910 } 911 } 912 } 913 914 function injectSegmentTeasers(segmentsQueryString) { 915 var xhttp = new XMLHttpRequest(); 916 917 xhttp.onreadystatechange = function() { 918 if (this.readyState == 4 && this.status == 200) { 919 var segmentTeasers = this.responseText.trim(); 920 921 if (segmentTeasers) { 922 if (segmentTeasersContainer) { 923 segmentTeasersContainer.innerHTML = segmentTeasers; 924 } 925 } 926 else { 927 // Remove segment teaser container if there are no segment teasers 928 if (segmentTeasersContainer) { 929 segmentTeasersContainer.remove(); 930 } 931 } 932 } 933 }; 934 935 xhttp.open("GET", "/.ajax/segmentTeaser?" + segmentsQueryString, true); 936 xhttp.send(); 937 } 938 939 var segmentsCookie = getCookie('jankkosegments'); 940 941 if (segmentsCookie) { 942 var segmentsData = JSON.parse(segmentsCookie); 943 var segments = segmentsData.segments; 944 945 if (segments && typeof segments === 'object') { 946 var segmentsQueryString = ""; 947 948 Object.keys(segments).forEach(function(key, index) { 949 segmentsQueryString = index === 0 ? "" : segmentsQueryString + "&"; 950 var value = segments[key]; 951 952 if (Array.isArray(value)) { 953 value = value.join(','); 954 } 955 956 segmentsQueryString = segmentsQueryString + key + "=" + value; 957 }); 958 959 injectSegmentTeasers(segmentsQueryString); 960 } 961 } 962 else { 963 // Remove segment teaser container if there are no segment teasers 964 if (segmentTeasersContainer) { 965 segmentTeasersContainer.remove(); 966 } 967 } 968 })(); 969</script>
969 970 971 <div class="diks-custom-page diks-apps"> 972 <div class="diks-container"> 973 <div class="diks-grid"> 974 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-12 diks-column--xl-span-16"><nav aria-label="Murupolku"> 975 <ul class="diks-breadcrumb"><li class="diks-breadcrumb__item"><a class="diks-breadcrumb__link" href="/">Etusivu</a></li><li class="diks-breadcrumb__item"><span class="diks-breadcrumb__sub-item">Sovellus</span></li></ul> 976 </nav></div> 977 </div> 978 <main id="sisalto"> 979 <div class="diks-apps__content-wrapper"> 980 <div class="diks-grid"> 981 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-12 diks-column--xl-span-2"></div> 982 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-12 diks-column--xl-span-12"> 983 <h1 class="diks-title diks-title--size-3xl" >Sovellus</h1> 984 </div> 985 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-12 diks-column--xl-span-2"></div> 986 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-12 diks-column--xl-span-2"></div><div class="diks-apps-installation-wrapper diks-column diks-column--span-4 diks-column--m-span-4 diks-column--l-span-6 diks-column--xl-span-6"> 987 <div class="diks-apps__icon-wrapper"> 988 <span class="diks-icon diks-icon--size-l diks-icon--color-primary" aria-hidden="true"><svg role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"> 989 <path fill-rule="evenodd" d="M5 2H19V22H5V2ZM7 4V20H17V4H7ZM15 19H9V17H15V19Z" clip-rule="evenodd"/> 990</svg> 991</span> 992 </div> <h2 class="diks-title diks-title--size-xl" >Sovellus</h2><p>Sovellus sisältää tuoreet juttumme, joita ilmestyy ympäri vuorokauden vuoden jokaisena päivänä sekä näköislehtemme liitteineen sekä näköislehtien arkiston. Asenna sovellus laitteesi sovelluskaupasta.</p><div class="diks-apps-image-wrapper"> 993 <a class="diks-apps__app-link diks-hide-on-android" href="https://apps.apple.com/fi/app/yl%C3%A4-karjala/id6695740400"><img class="diks-apps__app-image" src="https://a.ylakarjala.fi/styles/2efe7af9/images/default/appstore-badge.svg" alt="Lataa sovellus App Storesta"></a> 994 <a class="diks-apps__app-link diks-hide-on-ios" href="https://play.google.com/store/apps/details?id=fi.media.ylakarjala"><img class="diks-apps__app-image" src="https://a.ylakarjala.fi/styles/2efe7af9/images/default/google-play-badge.svg" alt="Lataa sovellus Google Play -kaupasta"></a> 995 </div></div> 996 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-12 diks-column--xl-span-2"></div> 997 </div> 998 </div> 999 </main> 1000 </div> 1001 </div> 1002 1003 <footer class="diks-footer"><div class="diks-container diks-container--narrow-mobile diks-container--full-width-before-l"> 1004 <div class="diks-footer__top"> <nav class="diks-navigation-wrapper " aria-label="Osastovalikko"><ul class="diks-navigation diks-navigation--row diks-navigation--light diks-navigation--centered diks-navigation--mobile-row diks-navigation--draggable diks-navigation--size-xs diks-navigation--active-subtle" ><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/paikalliset"draggable="false">Uutiset</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/paakirjoitus-mielipide/"draggable="false">Mielipiteet</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/blogit"draggable="false">Blogit</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/menneilta-vuosilta"draggable="false">Menneiltä vuosilta</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="https://pohjois-karjala.menoinfo.fi/fi-FI?sort=startDate&area=area-143"draggable="false">Menoinfo</a></li></ul> 1005 </nav></div> 1006 </div><div class="diks-container"><div class="diks-footer__main"> 1007 <div class="diks-grid"> 1008 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-4 diks-column--xl-span-4"> 1009 <p><img src="https://a.ylakarjala.fi/styles/2efe7af9/images/logos/ylakarjala.svg" class="diks-logo diks-logo--default diks-footer__main-logo--wide" alt="Ylä-Karjala"><img src="https://a.ylakarjala.fi/styles/2efe7af9/images/logos/ylakarjala-w
1009hite.svg" class="diks-logo diks-logo--darkmode diks-footer__main-logo--wide" alt="Ylä-Karjala"></p> 1010 <p><strong>Päätoimittaja:</strong> Anu Saarelainen</p></div> 1011 1012 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-4 diks-column--xl-span-5"><div class="diks-footer__main-secondary-logos-container"><div class="diks-footer__main-secondary-logo"><a href="https://vastuullistajournalismia.fi/"><img src="https://a.ylakarjala.fi/styles/2efe7af9/images/logos/vastuullista-journalismia.png" loading="lazy" alt="Vastuullisen journalismin merkki kertoo, että sitä käyttävä media on sitoutunut noudattamaan Julkisen sanan neuvoston laatimia Journalistin ohjeita."></a></div></div><div class="diks-footer__main-links-wrapper"> 1013 <div class="diks-column diks-column--span-2"><a class="diks-footer__main-link" href="https://www.keskisuomalainen.com/tietosuojalauseke/">Tietosuojalauseke</a><a class="diks-footer__main-link" href="javascript:window.gravito.cmp.openPreferences();">Evästevalinnat</a><a class="diks-footer__main-link" href="https://www.keskisuomalainen.com/kayttoehdot/">Käyttöehdot</a><a class="diks-footer__main-link" href="https://keskisuomalainen.com/medianvapausasetus/">Medianvapausasetus</a><a class="diks-footer__main-link" href="https://oma.media.fi/tekoaly-journalismissamme/">Tekoäly journalismissamme</a></div> 1014 <div class="diks-column diks-column--span-2"><a class="diks-footer__main-link" href="https://oma.media.fi/yla-karjala/">Asiakaspalvelu</a><a class="diks-footer__main-link" href="https://www.meks.fi/mainonta/alueet/savon-media/">Mediamyynti</a><a class="diks-footer__main-link" href="https://oma.media.fi/yla-karjala/yhteystiedot/">Yhteystiedot</a></div> 1015 </div></div> 1016 1017 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-4 diks-column--xl-span-5"> 1018 <h2 class="diks-footer__main-title">Sovellus</h2><p>Sovellus sisältää tuoreet juttumme, joita ilmestyy ympäri vuorokauden vuoden jokaisena päivänä sekä näköislehtemme liitteineen sekä näköislehtien arkiston. Asenna sovellus laitteesi sovelluskaupasta.</p><div class="diks-footer__main-app-image-wrapper"> 1019 <a class="diks-apps__app-link diks-hide-on-android" href="https://apps.apple.com/fi/app/yl%C3%A4-karjala/id6695740400"><img class="diks-apps__app-image" src="https://a.ylakarjala.fi/styles/2efe7af9/images/default/appstore-badge.svg" loading="lazy" alt="Lataa sovellus App Storesta"></a> 1020 <a class="diks-apps__app-link diks-hide-on-ios" href="https://play.google.com/store/apps/details?id=fi.media.ylakarjala"><img class="diks-apps__app-image" src="https://a.ylakarjala.fi/styles/2efe7af9/images/default/google-play-badge.svg" loading="lazy" alt="Lataa sovellus Google Play -kaupasta"></a> 1021 </div> 1022 1023 1024 </div> 1025 </div> 1026 </div><div class="diks-footer__bottom"> 1027 <div class="diks-footer__bottom-row"><div class="diks-footer__bottom-logo-wrapper"><a href="https://www.keskisuomalainen.com/"><img class="diks-footer__bottom-logo" src="https://a.ylakarjala.fi/styles/2efe7af9/images/logos/keskisuomalainen-sign.svg" loading="lazy" alt="Keskisuomalainen Oyj"></a></div><span>Kuulumme Mediakonserni Keskisuomalaiseen</span></div> 1028 <div class="diks-footer__bottom-link-farm-wrapper"> 1029 <p><a class='diks-footer__bottom-link-farm-item' href='https://www.aamuposti.fi/'>Aamuposti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.avecmedia.fi/'>Avec</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.esaimaa.fi/'>Etelä-Saimaa</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.ess.fi/'>Etelä-Suomen Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.forssanlehti.fi/'>Forssan Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.hankasalmensanomat.fi/'>Hankasalmen Sanomat </a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.heinavedenlehti.fi/'>Heinäveden Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.helsinginuutiset.fi/'>Helsingin Uutiset</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.hameensanomat.fi/'>Hämeen Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.hameenlinnankaupunkiuutiset.fi/'>Hämeenlinnan Kaupunkiuutiset</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.iisalmensanomat.fi/'>Iisalmen Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.itahame.fi/'>Itä-Häme</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.ita-savo.fi/'>Itä-Savo</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.joroistenlehti.fi/'>Joroisten Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.juvanlehti.fi/'>Juvan Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.kaakonkulma.fi/'>Kaakonkulma</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.kangasniemenlehti.fi/'>Kangasniemen Kunnallislehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.karjalainen.fi/'>Karjalainen</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.heili.fi/'>Karjalan Heili</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.karkkilalainen.fi/'>Karkkilalainen</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.keski-hame.fi/'>Keski-Häme</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.keski-uusimaa.fi/'>Keski-Uusimaa</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.keskilaakso.fi/'>Keskilaakso</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.ksml.fi/'>Keskisuomalainen</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.koillis-savo.fi/'>Koillis-Savo</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.kouvolansanomat.fi/'>Kouvolan Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.kymensanomat.fi/'>Kymen Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.lappeenrannanuutiset.fi/'>Lappeenrannan Uutiset</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.laukaa-konnevesi.fi/'>Laukaa-Konnevesi</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.lieksanlehti.fi/'>Lieksan Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.lopenlehti.fi/'>Lopen Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.loviisansanomat.fi/'>Loviisan Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.luumaenlehti.fi/'>Luumäen Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.lansisaimaa.fi/'>Länsi-Saimaan Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.lansi-savo.fi/'>Länsi-Savo</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.lansi-uusimaa.fi/'>Länsi-Uusimaa</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.lansivayla.fi/'>Länsiväylä</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.mattijaliisa.fi/'>Matti ja Liisa</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.meksi.fi/'>Meksi Urheilumedia</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.miilu.fi/'>Miilu</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.mikkelinkaupunkilehti.fi/'>Mikkelin Kaupunkilehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.mantsalanuutiset.fi/'>Mäntsälän Uutiset</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.aamuposti.fi/aihe/Nurmij%C3%A4rvi'>Nurmijärven Uutiset</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.outokummunseutu.fi/'>Outokummun Seutu</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.joutsenolehti.fi/'>Paikallislehti Joutseno</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.prsanomat.fi/'>Parikkalan-Rautjärven Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.pernionseudunlehti.fi/'>Perniönseudun Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.pieksamaenlehti.fi/'>Pieksämäen Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.pielavesi-keitele.fi/'>Pielavesi-Keitele</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.pielisjokiseutu.fi/'>Pielisjokiseutu</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.pitajalainen.fi/'>Pitäjäläinen</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.pitajanuutiset.fi/'>Pitäjänuutiset</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.pogostansanomat.fi/'>Pogostan Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.puruvesi.net/'>Puruvesi</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.pyhtaanlehti.fi/'>Pyhtäänlehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.salonjokilaakso.fi/'>Salonjokilaakso</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.sampolehti.fi/'>Sampo</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.savonsanomat.fi/'>Savon Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.sinunsavo.fi/'>Sinun Savo</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.sisa-savolehti.fi/'>Sisä-Savo</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.sisasuomenlehti.fi/'>Sisä-Suomen Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.soisalonseutu.fi/'>Soisalon Seutu</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.sjl.fi/'>Suur-Jyväskylän Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.tamperelainen.fi/'>Tamperelainen</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.tollotin.fi/'>Töllötin</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.uusimaa.fi/'>Uusimaa</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.uutis-jousi.fi/'>Uutis-Jousi</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.uutisvuoksi.fi/'>Uutisvuoksi</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.vantaansanomat.fi/'>Vantaan Sanomat</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.vihdinuutiset.fi/'>Vihdin Uutiset</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.viikkosavo.fi/'>Viikkosavo</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.viispiikkinen.fi/'>
1029Viispiikkinen</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.viitasaarenseutu.fi/'>Viitasaaren Seutu</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.warkaudenlehti.fi/'>Warkauden Lehti</a> • <a class='diks-footer__bottom-link-farm-item' href='https://www.ylakarjala.fi/'>Ylä-Karjala</a></p><p>Kaikki sisällöt voidaan julkaista kaikissa Mediakonserni Keskisuomalaisen medioissa.</p> 1030 </div> 1031 </div></div> 1032</footer> 1033 1034 1035
1035<script> 1036 if (!DOMTokenList.prototype.replace) { 1037 // IE Polyfill 1038 DOMTokenList.prototype.replace = function (a, b) { 1039 if (this.contains(a)) { 1040 this.add(b); 1041 this.remove(a); 1042 return true; 1043 } 1044 return false; 1045 } 1046 } 1047 1048 document.querySelector('.diks-no-js').classList.replace('diks-no-js', 'diks-js'); 1049 1050 // User agent sniffing is not reliable and it's going away. 1051 // Use only to sprinkle a little bit of progressive enhancement on top. 1052 // Dear Apple, stop lying, iPad on iOS 13 is not a Mac. You're turning into 90s Microsoft. 1053 var userAgent = navigator.userAgent || navigator.vendor || window.opera; 1054 if (/android/i.test(userAgent)) { 1055 document.querySelector('html').classList.add('diks-user-agent--android'); 1056 } else if (/iPad|iPhone|iPod/i.test(userAgent) && !window.MSStream) { 1057 document.querySelector('html').classList.add('diks-user-agent--ios'); 1058 } else if (/macintosh/i.test(userAgent) && navigator.maxTouchPoints > 1) { 1059 document.querySelector('html').classList.add('diks-user-agent--ios'); 1060 } 1061</script>
1061 1062
1063<script> 1064 // Fix forEach for IE 1065 if (window.NodeList && !NodeList.prototype.forEach) { 1066 NodeList.prototype.forEach = Array.prototype.forEach; 1067 } 1068 1069 // Element.closest() needs a polyfill for older browsers. 1070 // While we only need the closest() functionality for mobile browsers, 1071 // we don't want the code to fail even on old desktop browsers. 1072 // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill 1073 // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Browser_compatibility 1074 if (!Element.prototype.matches) { 1075 Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; 1076 } 1077 1078 if (!Element.prototype.closest) { 1079 Element.prototype.closest = function(s) { 1080 var el = this; 1081 1082 do { 1083 if (el.matches(s)) return el; 1084 el = el.parentElement || el.parentNode; 1085 } while (el !== null && el.nodeType === 1); 1086 return null; 1087 }; 1088 } 1089 1090 function setAriaExpanded(toggler, ariaExpandedValue) { 1091 // Set aria-expanded attribute of dropdown toggler 1092 // If the toggler contains a button, set the aria-expanded attribute for the button instead 1093 if ( toggler.querySelector('button') != null ) { 1094 toggler.querySelector('button').setAttribute('aria-expanded', ariaExpandedValue); 1095 } 1096 else { 1097 toggler.setAttribute('aria-expanded', ariaExpandedValue); 1098 } 1099 } 1100 1101 function toggleElement(target, toggleClass, toggler) { 1102 if (toggleClass) { 1103 var isOpenClass = toggleClass + '--is-open'; 1104 var isClosedClass = toggleClass + '--is-closed'; 1105 1106 if (target.classList.contains(isOpenClass)) { 1107 // Close this toggle 1108 target.classList.remove(isOpenClass); 1109 target.classList.add(isClosedClass); 1110 1111 // Set aria-expanded to false for the toggler 1112 setAriaExpanded(toggler, 'false'); 1113 1114 } else if (target.classList.contains(isClosedClass)) { 1115 // Open this toggle 1116 target.classList.remove(isClosedClass); 1117 target.classList.add(isOpenClass); 1118 1119 // Set aria-expanded to true for the toggler 1120 setAriaExpanded(toggler, 'true'); 1121 } else { 1122 // Open this toggle as a fallback 1123 target.classList.add(isOpenClass); 1124 1125 // Set aria-expanded to true for the toggler 1126 setAriaExpanded(toggler, 'true'); 1127 } 1128 } else { 1129 if (window.getComputedStyle(target).display !== 'none') { 1130 target.style.display = 'none'; 1131 } else { 1132 target.style.display = 'block'; 1133 } 1134 } 1135 }; 1136 1137 document.querySelectorAll('.js-diks-toggler').forEach(function(toggler) { 1138 var toggleId = toggler.getAttribute('data-diks-toggle'); 1139 var target = document.getElementById(toggleId); 1140 var eventListenerAttached = toggler.getAttribute('data-diks-event-click'); 1141 1142 if (target && !eventListenerAttached) { 1143 1144 // Set aria-controls of toggler 1145 if ( toggler.querySelector('button') != null ) { 1146 toggler.querySelector('button').setAttribute('aria-controls', toggleId); 1147 } 1148 else { 1149 toggler.setAttribute('aria-controls', toggleId); 1150 } 1151 1152 toggler.addEventListener('click', function(e) { 1153 e.preventDefault(); 1154 1155 var toggleClass = toggler.getAttribute('data-diks-toggle-class'); 1156 1157 if (webShareAPIEnabled(toggleId)) { 1158 share(); 1159 } else { 1160 toggleElement(target, toggleClass, toggler); 1161 } 1162 }); 1163 1164 toggler.setAttribute('data-diks-event-click', true); 1165 } 1166 }); 1167 1168 // Web Share and Clipboard API capabilities 1169 1170 function webShareAPIEnabled(toggleId) { 1171 const webShareAPIEnabled = true; 1172 const webShareAPIDropdownShareIds = ["jaa","jakopainikkeet"]; 1173 const isMobileDevice = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); 1174 1175 return webShareAPIEnabled && webShareAPIDropdownShareIds.includes(toggleId) && isMobileDevice && navigator.share ? true : false; 1176 } 1177 1178 function share() { 1179 const metaTitleElement = document.querySelector('meta[property="og:title"]'); 1180 const metaDescriptionElement = document.querySelector('meta[name="description"]'); 1181 const metaUrlElement = document.querySelector('meta[property="og:url"]'); 1182 let title = ''; 1183 let description = ''; 1184 let url = document.location.href; 1185 1186 if (metaTitleElement && metaTitleElement.content) { 1187 title = metaTitleElement.content; 1188 } 1189 1190 if (metaDescriptionElement && metaDescriptionElement.content) { 1191 description = metaDescriptionElement.content; 1192 } 1193 1194 if (metaUrlElement && metaUrlElement.content) { 1195 url = metaUrlElement.content; 1196 } 1197 1198 navigator.share({ 1199 title: title, 1200 text: description, 1201 url: url 1202 }); 1203 } 1204 1205 document.querySelectorAll('.js-diks-share-clipboard').forEach(function(element) { 1206 element.addEventListener('click', function(e) { 1207 e.preventDefault(); 1208 1209 let url = document.location.href; 1210 const metaUrlElement = document.querySelector('meta[property="og:url"]'); 1211 1212 if (metaUrlElement && metaUrlElement.content) { 1213 url = metaUrlElement.content; 1214 } 1215 1216 // Copy URL to clipboard 1217 navigator.clipboard.writeText(url).then(function () { 1218 // Copying URL to clipboard succeeded 1219 const dropdownId = element.getAttribute('data-diks-dropdown-id'); 1220 // Find closest dropdown toggler 1221 let dropdownToggler = element.closest('.diks-dropdown').querySelector('.js-diks-toggler'); 1222 1223 if (dropdownId) { 1224 const dropdownElement = document.getElementById(dropdownId); 1225 1226 if (dropdownElement) { 1227 // Close currently open dropdown 1228 toggleElement(dropdownElement, 'diks-dropdown__content', dropdownToggler); 1229 } 1230 1231 // Show link copied notification text 1232 const notificationTextWrapperDiv = document.createElement('div'); 1233 const notificationTextDiv = document.createElement('div'); 1234 const notificationText = document.createTextNode('Linkki kopioitu leikepöydälle'); 1235 1236 notificationTextDiv.classList.add('diks-dropdown__notification-text'); 1237 notificationTextDiv.appendChild(notificationText); 1238 notificationTextWrapperDiv.classList.add('diks-dropdown__notification-text-wrapper'); 1239 notificationTextWrapperDiv.appendChild(notificationTextDiv); 1240 dropdownElement.parentNode.insertBefore(notificationTextWrapperDiv, null); 1241 1242 setTimeout(function () { 1243 notificationTextWrapperDiv.remove(); 1244 }, 5000); 1245 } 1246 }); 1247 }); 1248 }); 1249
1250 const initContentBlockToggleButtons = () => { 1251 const toggleContainers = document.querySelectorAll('[data-diks-init="toggle"]'); 1252 toggleContainers.forEach(container => { 1253 const toggleButton = container.querySelector('.diks-toggle-button'); 1254 const toggleContent = container.querySelector('.diks-toggle-content-container'); 1255 if (toggleButton && toggleContent) { 1256 toggleButton.addEventListener('click', () => { 1257 toggleElement(toggleContent, 'diks-toggle', toggleButton); 1258 }); 1259 } 1260 }); 1261 }; 1262 document.addEventListener('DOMContentLoaded', () => { 1263 initContentBlockToggleButtons(); 1264 }); 1265 1266</script>
1266 1267
1268<script> 1269 const useDragThreshold = true; 1270 1271 // Horizontally scrollable & draggable navigation 1272 class HorizontalScrollDragger { 1273 constructor(element) { 1274 this.slider = typeof element === 'string' ? document.querySelector(element) : element instanceof Element ? element : null; 1275 if (this.slider === null) return console.error('HorizontalScrollDragger: Element not found'); 1276 1277 this.isDown = false; 1278 this.preventClick = false; 1279 this.startX; 1280 this.scrollLeft; 1281 1282 this.dragThreshold = 5; 1283 this.dragSensitivity = 1; 1284 this.dragged = false; 1285 1286 this.init(); 1287 } 1288 1289 init() { 1290 this.slider.addEventListener('mousedown', (e) => this.handleMouseDown(e)); 1291 this.slider.addEventListener('mouseleave', () => this.handleMouseLeave()); 1292 this.slider.addEventListener('mouseup', () => this.handleMouseUp()); 1293 this.slider.addEventListener('mousemove', (e) => this.handleMouseMove(e)); 1294 this.slider.addEventListener('click', (e) => this.handleClick(e)); 1295 } 1296 1297 handleMouseDown(e) { 1298 this.isDown = true; 1299 this.slider.classList.add('diks-navigation--draggable-active'); 1300 this.startX = e.pageX - this.slider.offsetLeft; 1301 this.scrollLeft = this.slider.scrollLeft; 1302 this.preventClick = false; 1303 } 1304 1305 handleMouseLeave() { 1306 this.isDown = false; 1307 this.slider.classList.remove('diks-navigation--draggable-active'); 1308 } 1309 1310 handleMouseUp() { 1311 this.isDown = false; 1312 this.slider.classList.remove('diks-navigation--draggable-active'); 1313 } 1314 1315 handleMouseMove(e) { 1316 if (!this.isDown) return; 1317 1318 if ( useDragThreshold == true ) { 1319 const xWithThreshold = e.pageX - this.slider.offsetLeft; 1320 const delta = xWithThreshold - this.startX; 1321 1322 // If not yet dragging, check threshold 1323 if (!this.dragged && Math.abs(delta) > this.dragThreshold) { 1324 this.dragged = true; 1325 this.preventClick = true; 1326 1327 // Reset baseline so scrolling starts smoothly from here 1328 this.startX = xWithThreshold; 1329 this.scrollLeft = this.slider.scrollLeft; 1330 } 1331 1332 if (this.dragged) { 1333 e.preventDefault(); 1334 const walkWithThreshold = (xWithThreshold - this.startX) * this.dragSensitivity; // adjust sensitivity 1335 this.slider.scrollLeft = this.scrollLeft - walkWithThreshold; 1336 } 1337 } 1338 else { 1339 e.preventDefault(); 1340 const x = e.pageX - this.slider.offsetLeft; 1341 const walk = (x - this.startX) * this.dragSensitivity; // adjust sensitivity 1342 this.slider.scrollLeft = this.scrollLeft - walk; 1343 this.preventClick = true; 1344 } 1345 1346 } 1347 1348 handleClick(e) { 1349 if ( useDragThreshold == true && this.preventClick && this.dragged ) { 1350 e.preventDefault(); 1351 } 1352 else if ( useDragThreshold == false && this.preventClick) { 1353 e.preventDefault(); 1354 } 1355 } 1356 } 1357 1358 // Set the draggable navs (multiple) to handle their scrollbars 1359 let scrollableNavs = document.querySelectorAll('.diks-navigation--draggable'); 1360 1361 // Make draggable navigation draggable 1362 if ( scrollableNavs ) { 1363 scrollableNavs.forEach(function(element) { 1364 new HorizontalScrollDragger(element); 1365 }); 1366 } 1367 1368 1369 // Handle has-scrollbars class adding and removing 1370 function handleScrollableNavScrollbars(scrollableNavElement) { 1371 // If scrollable area is wider (scrollWidth) than what's visible (clientWidth) the element has scrollbars 1372 if ( scrollableNavElement.scrollWidth > scrollableNavElement.clientWidth ) { 1373 scrollableNavElement.classList.add('diks-navigation--draggable-has-scrollbars'); 1374 scrollableNavElement.parentElement.classList.add('diks-navigation-wrapper--has-scrollbars'); 1375 } 1376 // Remove the has-scrollbars class if it exists but shouldn't (element does not have scrollbars) 1377 else if ( scrollableNavElement.classList.contains('diks-navigation--draggable-has-scrollbars') ) { 1378 scrollableNavElement.classList.remove('diks-navigation--draggable-has-scrollbars'); 1379 scrollableNavElement.parentElement.classList.remove('diks-navigation-wrapper--has-scrollbars'); 1380 } 1381 } 1382 1383 // If scrollable nav exists add (or remove) necessary classes 1384 // "DOMContentLoaded" does not wait for styles to load and styles affect the size of the scrollable nav directly so we have to use "load" instead 1385 window.addEventListener('load', (event) => { 1386 if ( scrollableNavs ) {
1387 scrollableNavs.forEach(function(element) { 1388 handleScrollableNavScrollbars(element); 1389 }); 1390 } 1391 }); 1392 1393 // Check if scrollable nav has scrollbar 1394 window.addEventListener('resize', function(event){ 1395 if ( scrollableNavs ) { 1396 scrollableNavs.forEach(function(element) { 1397 handleScrollableNavScrollbars(element); 1398 }); 1399 } 1400 }); 1401</script>
1401 1402
1403<script> 1404 // Back button handling 1405 document.querySelectorAll('a.js-diks-back-button').forEach(function(item) { 1406 var referrerIsSameDomain = false; 1407 // Check if referrer is found as referrer isn't always available (browser, tab, window, bookmark, privacy). 1408 if (typeof(document.referrer) === 'string' && document.referrer.length > 0) { 1409 // Set referrerIsSameDomain to true if referrer is in the same domain as current location hostname and referrer is not the same page as current page (window.location.href) 1410 if ( document.referrer.includes(document.location.hostname) && document.referrer != window.location.href ) { 1411 referrerIsSameDomain = true; 1412 } 1413 } 1414 1415 // Overwrite link with history.go, as it also remembers scroll position but only if referrer is the same domain as current location hostname 1416 if (history.length > 1 && referrerIsSameDomain == true) { 1417 // Set referrer as back button href to enable visual cue & opening to a new window 1418 item.setAttribute('href', document.referrer); 1419 1420 // Set back button text 1421 item.querySelector('.diks-header__cta-text').innerHTML = "Takaisin"; 1422 1423 var stepsToGoBack = -1; 1424 1425 item.addEventListener('click', function(e) { 1426 e.preventDefault(); 1427 history.go(stepsToGoBack); 1428 }); 1429 } 1430 }); 1431</script>
1431 1432 1433 <link rel="stylesheet" href="/theme/css/includes/lightgallery/css/lightgallery-bundle.min.css"> 1434 <style>.lg-sub-html { background-color: rgba(0,0,0, .6); }</style> 1435
1435<script src="/theme/css/includes/lightgallery/lightgallery.min.js"></script>
1435 1436
1436<script src="/theme/css/includes/lightgallery/plugins/autoplay/lg-autoplay.min.js"></script>
1436 1437
1437<script src="/theme/css/includes/lightgallery/plugins/fullscreen/lg-fullscreen.min.js"></script>
1437 1438
1438<script src="/theme/css/includes/lightgallery/plugins/thumbnail/lg-thumbnail.min.js"></script>
1438 1439
1439<script src="/theme/css/includes/lightgallery/plugins/zoom/lg-zoom.min.js"></script>
1439 1440
1440<script> 1441 // lightGallery 1442 // Separate gallery for every .js-lightgallery 1443 // Gallery items come from .js-lightgallery .js-lightgallery__link 1444 // https://www.lightgalleryjs.com/docs/getting-started/ 1445 document.querySelectorAll('.js-diks-lightgallery').forEach(function(gallery) { 1446 var galleryImageLinks = gallery.getElementsByClassName('diks-gallery__link'); 1447 1448 // Thumbnails come from img inside the links, unless links specify data-thumbnail 1449 var thumbnailSource = (galleryImageLinks[0].hasAttribute('data-thumbnail')) ? ('data-thumbnail') : (false); 1450 1451 // Adjust certain settings depending on whether the gallery has one or more images 1452 var galleryHasMultipleImages = (galleryImageLinks.length > 1 ? true : false); 1453 1454 // Initialize 1455 lightGallery(gallery, { 1456 autoplay: galleryHasMultipleImages, 1457 counter: galleryHasMultipleImages, 1458 download: false, 1459 exThumbImage: thumbnailSource, 1460 hideControlOnEnd: true, 1461 licenseKey: "96BAAD7E-3BF04397-BBB55AE3-6D5A2C0B", 1462 loop: false, 1463 plugins: [lgAutoplay, lgFullscreen, lgThumbnail, lgZoom], 1464 selector: '.diks-gallery__link', 1465 mobileSettings: { 1466 controls: true, 1467 showCloseIcon: true 1468 }, 1469 strings: { 1470 closeGallery: 'Sulje galleria', 1471 toggleMaximize: 'Maksimointi', 1472 previousSlide: 'Edellinen', 1473 nextSlide: 'Seuraava', 1474 download: 'Lataa', 1475 playVideo: 'Katso video', 1476 mediaLoadingFailed: 'Sisällön lataus epäonnistui', 1477 }, 1478 autoplayPluginStrings: { 1479 toggleAutoplay: 'Automaattinen toisto' 1480 }, 1481 fullscreenPluginStrings: { 1482 toggleFullscreen: 'Kokoruututila' 1483 }, 1484 thumbnailPluginStrings: { 1485 toggleThumbnails: 'Pikkukuvat' 1486 }, 1487 zoomPluginStrings: { 1488 zoomIn: 'Suurenna', 1489 zoomOut: 'Pienennä', 1490 viewActualSize: 'Näytä täysikokoisena' 1491 } 1492 }); 1493 1494 // After closing LightGallery return focus to the image link that opened the gallery 1495 gallery.addEventListener('lgAfterClose', () => { 1496 galleryImageLinks[0].focus(); 1497 }); 1498 1499 }); 1500 1501 </script>
1501 1502
1503<script> 1504 function loadScript(url, callback) { 1505 var script = document.createElement("script"); 1506 script.type = "text/javascript"; 1507 if (script.readyState) { // IE 1508 script.onreadystatechange = function() { 1509 if (script.readyState === "loaded" || script.readyState === "complete") { 1510 script.onreadystatechange = null; 1511 callback(); 1512 } 1513 }; 1514 } else { // Others 1515 script.onload = function() { 1516 callback(); 1517 }; 1518 } 1519 script.src = url; 1520 document.getElementsByTagName("head")[0].appendChild(script); 1521 } 1522 function checkAudioElements() { 1523 // load audioplayer script only when audio elements are found 1524 if (document.querySelector(".init-audio-player")) { 1525 loadScript("https://a.ylakarjala.fi/styles/2efe7af9/js/audioplayer/diks-audioplayer.min.js", function() { 1526 DiksAudioPlayer.init({ 1527 selector: '.init-audio-player', 1528 stopOthersOnPlay: true, 1529 enableKeystrokes: true 1530 }); 1531 }); 1532 } 1533 } 1534 window.onload = (event) => { 1535 checkAudioElements(); 1536 }; 1537</script>
1537<script> 1538 // ================================================================ 1539 // Unified Content Block Component 1540 // ================================================================ 1541 1542 const DiksContentBlock = (() => { 1543 // Private utility functions 1544 const utils = { 1545 setCookie: (name, value, days = 30) => { 1546 const expires = new Date(); 1547 expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000)); 1548 document.cookie = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/`; 1549 }, 1550 1551 getCookie: (name) => { 1552 const value = `; ${document.cookie}`; 1553 const parts = value.split(`; ${name}=`); 1554 if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift()); 1555 return null; 1556 }, 1557 1558 renderArticleCard: (article) => { 1559 let mainImage = article.mainImage; 1560 1561 // For ContentPackage: if mainImage is empty, get from first story in teaserStories 1562 if (article.type === 'ContentPackage' && (!mainImage || mainImage.length === 0)) { 1563 if (article.teaserStories && article.teaserStories.length > 0) { 1564 // Find first teaser story with a mainImage 1565 for (const teaserStory of article.teaserStories) { 1566 if (teaserStory.content && 1567 teaserStory.content.mainImage && 1568 teaserStory.content.mainImage.length > 0) { 1569 mainImage = teaserStory.content.mainImage; 1570 break; 1571 } 1572 } 1573 } 1574 } 1575 1576 let imageHtml = ''; 1577 if (mainImage && mainImage.length > 0) { 1578 const imageFields = mainImage[0].content.fields; 1579 const src = imageFields.landscape_720.href_full; 1580 const srcset = ` 1581 ${imageFields.landscape_240.href_full} 240w, 1582 ${imageFields.landscape_480.href_full} 480w, 1583 ${imageFields.landscape_720.href_full} 720w, 1584 ${imageFields.landscape_960.href_full} 960w 1585 `; 1586 imageHtml = ` 1587 <div class="diks-card__image-wrapper"> 1588 <img class="diks-card__image" 1589 src="${src}" 1590 loading="lazy" 1591 sizes="(max-width: 504px) 480px, (max-width: 1279px) 720px, 960px" 1592 srcset="${srcset}" 1593 decoding="async" 1594 alt="${article.headline}" 1595 pinger-seen="true"> 1596 </div> 1597 `; 1598 } 1599 1600 const fullHeadline = article.headlinePrefix 1601 ? `<span class="diks-ui-accent">${article.headlinePrefix} | </span>${article.headline}` 1602 : article.headline; 1603 1604 const publishedDate = new Date(article.published).toLocaleString('fi-FI', { 1605 day: 'numeric', 1606 month: 'numeric', 1607 year: 'numeric', 1608 hour: 'numeric', 1609 minute: 'numeric' 1610 }); 1611 1612 return ` 1613 <article class="diks-card diks-card--size-s"> 1614 <div class="diks-card__main-wrapper"> 1615 ${imageHtml} 1616 <div class="diks-card__text-wrapper"> 1617 <div class="diks-card__text-inner-wrapper"> 1618 <h2 class="diks-card__title"> 1619 <a class="diks-card__link" href="${article.href}">${fullHeadline}</a> 1620 </h2> 1621 <div class="diks-card__meta-wrapper"> 1622 <div class="diks-meta"> 1623 <div class="diks-date"> 1624 <time class="diks-date__published" datetime="${article.published}">${publishedDate}</time> 1625 </div> 1626 </div> 1627 </div> 1628 </div> 1629 </div> 1630 </div> 1631 </article> 1632 `; 1633 }, 1634 1635 getComponentType: (containerId) => { 1636 if (containerId.startsWith('diks-content-block-search-')) return 'search'; 1637 if (containerId.startsWith('diks-content-block-aggregator-')) return 'aggregator'; 1638 return null; 1639 }, 1640 1641 getBlockId: (containerId, type) => { 1642 if (type === 'search') return containerId.replace('diks-content-block-search-', ''); 1643 if (type === 'aggregator') return containerId.replace('diks-content-block-aggregator-', ''); 1644 return containerId; 1645 }, 1646 1647 getResultsId: (blockId, type) => { 1648 if (type === 'search') return `diks-search-results-${blockId}`; 1649 if (type === 'aggregator') return `diks-results-${blockId}`; 1650 return null; 1651 } 1652 }; 1653 1654 // Build query parameters based on component type and filters 1655 const buildQueryParams = (containerId) => { 1656 const container = document.getElementById(containerId); 1657 const type = utils.getComponentType(containerId); 1658 const blockId = utils.getBlockId(containerId, type); 1659 1660 const params = new URLSearchParams({ 1661 type: container.dataset.queryType || 'story', 1662 size: container.dataset.maxResults || 20 1663 }); 1664 1665 if (type === 'search') { 1666 // Search-specific parameters 1667 const defaultSort = container.dataset.sort; 1668 const defaultPaywall = container.dataset.paywall; 1669 const defaultStoryTypes = container.dataset.storyTypes; 1670 const defaultTopics = container.dataset.topics; 1671 const defaultAdvertisers = container.dataset.advertisers; 1672 const dateRangeType = container.dataset.dateRangeType; 1673 1674 // Sort 1675 if (defaultSort && defaultSort !== '*') { 1676 params.append('sort', defaultSort); 1677 } 1678 1679 // Paywall 1680 const paywallSelect = document.getElementById(`diks-search-paywall-${blockId}`); 1681 if (paywallSelect && paywallSelect.value) { 1682 params.append('paywall', paywallSelect.value); 1683 } else if (defaultPaywall && defaultPaywall !== '*') { 1684 params.append('paywall', defaultPaywall); 1685 } 1686 1687 // Story types 1688 let storyTypesValue = defaultStoryTypes; 1689 const storyTypesSelect = document.getElementById(`diks-search-story-types-${blockId}`); 1690 if (storyTypesSelect) { 1691 const selectedOptions = Array.from(storyTypesSelect.selectedOptions).map(opt => opt.value); 1692 if (selectedOptions.length > 0) { 1693 storyTypesValue = selectedOptions.join(','); 1694 } 1695 } 1696 if (storyTypesValue) { 1697 params.append('storyTypes', storyTypesValue); 1698 } 1699 1700 // Topics 1701 if (defaultTopics) { 1702 params.append('topics', defaultTopics); 1703 const topicsOperator = container.dataset.topicsOperator || 'OR'; 1704 params.append('topicsOperator', topicsOperator); 1705 } 1706 1707 // Advertisers 1708 if (defaultAdvertisers) { 1709 params.append('advertisers', defaultAdvertisers); 1710 const advertisersOperator = container.dataset.advertisersOperator || 'OR'; 1711 params.append('advertisersOperator', advertisersOperator); 1712 } 1713 1714 // Date ranges 1715 const fromDateInput = document.getElementById(`diks-search-from-date-${blockId}`); 1716 const toDateInput = document.getElementById(`diks-search-to-date-${blockId}`); 1717 const monthInput = document.getElementById(`diks-search-month-${blockId}`); 1718 1719 if (dateRangeType === 'byMonth' && monthInput && monthInput.value) { 1720 const [year, month] = monthInput.value.split('-').map(Number); 1721 const fromDate = new Date(year, month - 1, 1, 0, 0, 0, 0); 1722 const toDate = new Date(year, month, 0, 23, 59, 59, 999); 1723 params.append('fromDate', fromDate.toISOString().split('.')[0] + 'Z'); 1724 params.append('toDate', toDate.toISOString().split('.')[0] + 'Z'); 1725 } else { 1726 if (fromDateInput && fromDateInput.value) { 1727 const fromDate = new Date(fromDateInput.value); 1728 fromDate.setHours(0, 0, 0, 0); 1729 params.append('fromDate', fromDate.toISOString().split('.')[0] + 'Z'); 1730 } 1731 if (toDateInput && toDateInput.value) { 1732 const toDate = new Date(toDateInput.value); 1733 toDate.setHours(23, 59, 59, 999); 1734 params.append('toDate', toDate.toISOString().split('.')[0] + 'Z'); 1735 } 1736 } 1737 } else if (type === 'aggregator') { 1738 // Aggregator-specific parameters 1739 const selectedTopic = container.dataset.selectedTopic; 1740 if (selectedTopic) { 1741 params.append('topics', selectedTopic); 1742 } 1743 } 1744 1745 return params; 1746 }; 1747 1748 // Main fetch content function 1749 const fetchContent = async (containerId) => { 1750 const container = document.getElementById(containerId); 1751 const type = utils.getComponentType(containerId); 1752 const blockId = utils.getBlockId(containerId, type); 1753 const resultsId = utils.getResultsId(blockId, type); 1754 const resultsDiv = document.getElementById(resultsId); 1755 1756 if (!container || !resultsDiv) { 1757 console.error(`Content block container or results div not found: ${containerId}`); 1758 return; 1759 } 1760 1761 try { 1762 // Show loading state 1763 const loadingMessage = type === 'search' 1764 ? '<p class="diks-content-block-search--loading">Ladataan...</p>' 1765 : '<p class="diks-content-block-aggregator--loading">Ladataan...</p>'; 1766 resultsDiv.innerHTML = loadingMessage; 1767 1768 // Build query parameters 1769 const params = buildQueryParams(containerId); 1770 const apiUrl = container.dataset.apiUrl || '/.content/items'; 1771 1772 // Fetch data 1773 const response = await fetch(`${apiUrl}?${params.toString()}`, { 1774 method: 'GET' 1775 }); 1776 1777 if (!response.ok) { 1778 throw new Error('Verkkovirhe.'); 1779 } 1780 1781 const data = await response.json(); 1782 1783 // Render results 1784 let html = ''; 1785 if (data.items && data.items.length > 0) {
1786 data.items.forEach(article => { 1787 html += utils.renderArticleCard(article); 1788 }); 1789 } else { 1790 const noResultsMessage = type === 'search' 1791 ? '<p class="diks-content-block-search--no-results">Ei hakutuloksia.</p>' 1792 : '<p>Ei sisältöä.</p>'; 1793 html = noResultsMessage; 1794 } 1795 1796 resultsDiv.innerHTML = html; 1797 } catch (error) { 1798 const errorMessage = type === 'search' 1799 ? `<p class="diks-content-block-search--error">Virhe: ${error.message}</p>` 1800 : `<p class="error">Error: ${error.message}</p>`; 1801 resultsDiv.innerHTML = errorMessage; 1802 } 1803 }; 1804 1805 // Initialize search component 1806 const initializeSearch = (containerId) => { 1807 const container = document.getElementById(containerId); 1808 const blockId = utils.getBlockId(containerId, 'search'); 1809 const dateRangeType = container.dataset.dateRangeType; 1810 1811 // Get UI elements 1812 const searchButton = document.getElementById(`diks-search-button-${blockId}`); 1813 const fromDateInput = document.getElementById(`diks-search-from-date-${blockId}`); 1814 const toDateInput = document.getElementById(`diks-search-to-date-${blockId}`); 1815 const monthInput = document.getElementById(`diks-search-month-${blockId}`); 1816 1817 // Set current month as default for byMonth type 1818 if (dateRangeType === 'byMonth' && monthInput) { 1819 const now = new Date(); 1820 const year = now.getFullYear();
1821 const month = String(now.getMonth() + 1).padStart(2, '0'); 1822 monthInput.value = `${year}-${month}`; 1823 1824 // Auto-trigger search on month change 1825 monthInput.addEventListener('change', () => fetchContent(containerId)); 1826 } 1827 1828 // Search button click handler 1829 if (searchButton) { 1830 searchButton.addEventListener('click', () => fetchContent(containerId)); 1831 } 1832 1833 // Enter key support 1834 [fromDateInput, toDateInput, monthInput].forEach(input => { 1835 if (input) { 1836 input.addEventListener('keypress', (e) => { 1837 if (e.key === 'Enter') fetchContent(containerId); 1838 }); 1839 } 1840 }); 1841 1842 // Initial search 1843 fetchContent(containerId); 1844 }; 1845 1846 // Initialize aggregator component 1847 const initializeAggregator = (containerId) => { 1848 const container = document.getElementById(containerId); 1849 const blockId = utils.getBlockId(containerId, 'aggregator'); 1850 const topicLabels = JSON.parse(container.dataset.topicLabels || '{}'); 1851 const cookieName = `selectedTopic_${blockId}`; 1852 1853 const listId = `diks-topic-list-${blockId}`; 1854 const labelId = `diks-selected-topic-label-${blockId}`; 1855 const toggleId = `diks-toggle-button-${blockId}`; 1856 const listContainerId = `diks-toggle-area-${blockId}`; 1857 1858 const topicItems = document.querySelectorAll(`#${listId} .diks-topic-list__item`); 1859 const labelElement = document.getElementById(labelId); 1860 1861 // Update topic label display 1862 const updateTopicLabel = (topic) => { 1863 if (labelElement && topicLabels[topic]) { 1864 labelElement.textContent = topicLabels[topic]; 1865 } else if (labelElement) { 1866 labelElement.textContent = ''; 1867 } 1868 1869 // Update active state 1870 topicItems.forEach(item => { 1871 item.classList.toggle('diks-is-active', item.dataset.topic === topic); 1872 }); 1873 }; 1874 1875 // Handle single topic case 1876 if (Object.keys(topicLabels).length === 1) { 1877 const initialTopic = Object.keys(topicLabels)[0]; 1878 container.dataset.selectedTopic = initialTopic; 1879 updateTopicLabel(initialTopic); 1880 fetchContent(containerId); 1881 return; 1882 } 1883 1884 // Handle multiple topics case 1885 if (topicItems.length > 0) { 1886 // Determine initial topic (cookie or first) 1887 const cookieTopic = utils.getCookie(cookieName); 1888 let initialTopic = topicItems[0].dataset.topic; 1889 1890 if (cookieTopic) { 1891 const validTopic = Array.from(topicItems).some(item => item.dataset.topic === cookieTopic); 1892 if (validTopic) { 1893 initialTopic = cookieTopic; 1894 } 1895 } 1896 1897 // Set up topic click handlers 1898 topicItems.forEach(item => { 1899 item.addEventListener('click', () => { 1900 const selectedTopic = item.dataset.topic; 1901 container.dataset.selectedTopic = selectedTopic; 1902 utils.setCookie(cookieName, selectedTopic); 1903 updateTopicLabel(selectedTopic); 1904 fetchContent(containerId); 1905 1906 // Close toggle if open 1907 const toggleButton = document.getElementById(toggleId); 1908 const listContainer = document.getElementById(listContainerId); 1909 if (toggleButton && listContainer) { 1910 toggleButton.setAttribute('aria-expanded', 'false'); 1911 listContainer.classList.remove('diks-toggle--is-open'); 1912 listContainer.classList.add('diks-toggle--is-closed'); 1913 container.dataset.diksToggleState = 'closed'; 1914 } 1915 }); 1916 }); 1917 1918 // Initial fetch 1919 container.dataset.selectedTopic = initialTopic; 1920 updateTopicLabel(initialTopic); 1921 fetchContent(containerId); 1922 } 1923 }; 1924 1925 // Public API 1926 return { 1927 fetchContent, 1928 initialize: (containerId) => { 1929 const type = utils.getComponentType(containerId); 1930 if (type === 'search') { 1931 initializeSearch(containerId); 1932 } else if (type === 'aggregator') { 1933 initializeAggregator(containerId); 1934 } 1935 } 1936 }; 1937 })(); 1938 1939 1940 // ================================================================ 1941 // Auto-initialize all content block components 1942 // ================================================================ 1943 1944 document.addEventListener('DOMContentLoaded', () => { 1945 // Initialize all content block components (search + aggregator) 1946 const allContainers = document.querySelectorAll('[id^="diks-content-block-search-"], [id^="diks-content-block-aggregator-"]');
1947 allContainers.forEach(container => { 1948 DiksContentBlock.initialize(container.id); 1949 }); 1950 }); 1951</script>
vendor: 1 bytes, line 1951
1951
1952<script> 1953 // Service Worker 1954 if ('serviceWorker' in navigator) { 1955 window.addEventListener('load', function() { 1956 navigator.serviceWorker.register('/sw.js').then( 1957 function(registration) { 1958 console.log('PWA: Service worker registered to scope: ', registration.scope); 1959 }, 1960 function(err) { 1961 console.error('PWA: Service worker failed to register with error: ', err); 1962 } 1963 ); 1964 }); 1965 } 1966 1967 // Show installation option, hide generic fallback instructions 1968 function pwaShowInstallationOption() { 1969 document.querySelectorAll('.js-diks-apps-pwa-installation').forEach(function(element) { 1970 element.style.display = 'block'; 1971 }); 1972 document.querySelectorAll('.js-diks-apps-pwa-instructions').forEach(function(element) { 1973 element.style.display = 'none'; 1974 }); 1975 } 1976 1977 let deferredPrompt; 1978 var dataLayer = window.dataLayer || []; 1979 1980 // Basic installation process 1981 window.addEventListener('beforeinstallprompt', (e) => { 1982 // Stash the event so it can be triggered later 1983 deferredPrompt = e; 1984 1985 const enablePrompt = 0; 1986 1987 if(!enablePrompt) { 1988 e.preventDefault(); 1989 } 1990 1991 // Update the UI to notify the user they can install PWA 1992 pwaShowInstallationOption(); 1993 1994 // Handle installation 1995 document.querySelectorAll('.js-diks-apps-pwa-installation-button').forEach(function(button) { 1996 button.addEventListener('click', (e) => { 1997 // Show the prompt 1998 deferredPrompt.prompt(); 1999 2000 // Wait for user to respond 2001 deferredPrompt.userChoice.then((choiceResult) => { 2002 if (choiceResult.outcome === 'accepted') { 2003 console.log('PWA: Installed (prompt)'); 2004 dataLayer.push({ 2005 'event': 'PWA.installation', 2006 'eventData': 'success' 2007 }); 2008 } else { 2009 console.log('PWA: User dismissed installation prompt'); 2010 dataLayer.push({ 2011 'event': 'PWA.installation', 2012 'eventData': 'canceled' 2013 }); 2014 } 2015 deferredPrompt = null; 2016 }); 2017 }); 2018 }); 2019 }); 2020 2021 // iOS installation process (show instructions modal) 2022 if (!navigator.standalone) { 2023 var userAgent = navigator.userAgent.toLowerCase(); 2024 var iPhoneUser = (/iphone/.test(userAgent)); 2025 var iPadUser = (/ipad/.test(userAgent) || (/macintosh/.test(userAgent) && navigator.maxTouchPoints > 1)); 2026 var safariUser = (/^(?!.*(brave|chrome|crios|duckduckgo|edgeios|firefox|fxios|opios|ucbrowser|yabrowser)).*safari.*$/.test(userAgent)); 2027 2028 if (safariUser && (iPhoneUser || iPadUser)) { 2029 // Update the UI to notify the user they can install PWA 2030 pwaShowInstallationOption(); 2031 2032 // Instructions 2033 var modals = document.querySelectorAll('.js-diks-apps-pwa-ios-instructions').forEach(function(element) { 2034 // Position on screen based on device 2035 if (iPhoneUser) { 2036 element.classList.add('diks-apps__pwa-ios-modal--bottom'); 2037 } else if (iPadUser) { 2038 element.classList.add('diks-apps__pwa-ios-modal--top'); 2039 } 2040 }); 2041 2042 // Handle modal instructions from installation button 2043 document.querySelectorAll('.js-diks-apps-pwa-installation-button').forEach(function(button) { 2044 button.addEventListener('click', (e) => { 2045 // Show first modal 2046 document.querySelector('.js-diks-apps-pwa-ios-instructions').classList.toggle('diks-apps__pwa-ios-modal--is-visible'); 2047 dataLayer.push({ 2048 'event': 'PWA.instructions.shown', 2049 'eventData': 'iOS' 2050 }); 2051 }); 2052 }); 2053 } 2054 } 2055</script>
2055 2056
2056<script class="coral-script" src="//coral.prod.diks.fi/assets/js/count.js?v=9.3.1.3" defer></script>
2056 2057<div id="diks-bc-order-button" style="display: none;"></div> 2058 </body> 2059</html> 2060
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.