1 2<!doctype html> 3<html class="diks-no-js diks-site--sipoonsanomat 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 | Sipoon Sanomat</title> 8 <link id="diks-stylesheet" rel="stylesheet" href="https://a.sipoonsanomat.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.sipoonsanomat.fi/styles/2efe7af9/styles/diks-news-darkmode.css"; 16 const defaultStylesheet = "https://a.sipoonsanomat.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://g335.sipoonsanomat.fi/script.js" as="script"> 76
76<script> 77 window.JankkoSSOConfig = { 78 accountOrigin: "SIPS" 79 }; 80 </script>
80 81 82 83
83<script id="jankko-sso-script" src="https://sso.sipoonsanomat.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.sipoonsanomat.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="Sipoon Sanomat" href="https://www.sipoonsanomat.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.sipoonsanomat.fi\/asenna\/' 128 } 129 pageData.publication = { 130 'name': 'sipoonsanomat', 131 'url': 'https\u003A\/\/www.sipoonsanomat.fi\/', 132 'engage': 'SIPS', 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="Sipoon Sanomat"> 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 | Sipoon Sanomat'> 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.sipoonsanomat.fi/asenna/'> 158<meta property='og:type' content='webpage'><meta property='og:image' content='https://a.sipoonsanomat.fi/styles/2efe7af9/images/default/og-image-sipoonsanomat.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.sipoonsanomat.fi/styles/2efe7af9/js/blueconic/dialog.min.js"></script>
168 169
169<script> 170 BCinst = 'blueconic'; 171 BCshortcode = 'SIPS'; 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.sipoonsanomat.fi/styles/2efe7af9/js/blueconic/events.min.js"></script>
178 179
179<script> 180 const BCPlatform = "web"; 181 const BCChannel = 'SIPS'; 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://g335.sipoonsanomat.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.sipoonsanomat.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/sipoon-sanomat/"><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/sipoon-sanomat/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="/aihe/mina-sipoolainen">Minä sipoolainen</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/mysteerimaistaja">Mysteerimaistaja</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/paikalliset-yrittajat">Paikalliset yrittäjät</a></li><li class="diks-navigation__item " >
466<a class="diks-navigation__link " href="/aihe/Hyv%C3%A4t_uutiset">Hyvät uutiset</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/Liikunta_ja_urheilu">Liikunta ja urheilu</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/maakunta-uusimaa">Uusimaa 360</a></li></ul> 467 </nav><!-- ESI start element "recommendedTopics" generated at 02-10-2026 15:12:47.915220 --> <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/mysteerimaistaja">Mysteerimaistaja</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/miten-on">Miten on?</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/vuoden-sipoolainen">Vuoden sipoolainen</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/Hyv%C3%A4t_uutiset">Hyvät uutiset</a></li></ul> 468 </nav><!-- ESI end element "recommendedTopics" --><!-- ESI start element "promotedAdvertisers" generated at 02-10-2026 15:13:00.474942 --><!-- 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://meks.fi/mainonta/alueet/etela-suomen-media/">Mediamyynti</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="https://oma.media.fi/sipoon-sanomat/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.7547 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.sipoonsanomat.fi/styles/2efe7af9/images/logos/sipoonsanomat.svg" class="diks-logo diks-logo--default diks-header__logo diks-header__main-logo" alt="Sipoon Sanomat"><img src="https://a.sipoonsanomat.fi/styles/2efe7af9/images/logos/sipoonsanomat-white.svg" class="diks-logo diks-logo--darkmode diks-header__logo diks-header__main-logo" alt="Sipoon Sanomat"></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="/aihe/mina-sipoolainen"draggable="false">Minä sipoolainen</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/mysteerimaistaja"draggable="false">Mysteerimaistaja</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/paikalliset-yrittajat"draggable="false">Paikalliset yrittäjät</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/Hyv%C3%A4t_uutiset"draggable="false">Hyvät uutiset</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/Liikunta_ja_urheilu"draggable="false">Liikunta ja urheilu</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/maakunta-uusimaa"draggable="false">Uusimaa 360</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/sipoon-sanomat/id6740268452"><img class="diks-apps__app-image" src="https://a.sipoonsanomat.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.sipoonsanomat"><img class="diks-apps__app-image" src="https://a.sipoonsanomat.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="/aihe/mina-sipoolainen"draggable="false">Minä sipoolainen</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/mysteerimaistaja"draggable="false">Mysteerimaistaja</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/paikalliset-yrittajat"draggable="false">Paikalliset yrittäjät</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/Hyv%C3%A4t_uutiset"draggable="false">Hyvät uutiset</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/Liikunta_ja_urheilu"draggable="false">Liikunta ja urheilu</a></li><li class="diks-navigation__item " ><a class="diks-navigation__link " href="/aihe/maakunta-uusimaa"draggable="false">Uusimaa 360</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.sipoonsanomat.fi/styles/2efe7af9/images/logos/sipoonsanomat.svg" class="diks-logo diks-logo--default diks-footer__main-logo--narrow" alt="Sipoon Sanomat"><img src="https://a.sipoonsanomat.fi/styles/2efe7af9/images/logos/sipoonsanomat-white.svg" class="diks-logo diks-logo--darkmode diks-footer__main-logo--narrow" alt="Sipoon Sanomat"></p> 1010 <p><strong>Postiosoite:</strong> Lundinkatu 8, 06100 Porvoo<br><strong>Käyntiosoite:</strong> Lundinkatu 8, 06100 Porvoo (käynti Runeberginkatu 31)<br><strong>Vastaava päätoimittaja:</strong> Ulla Yliherne</p><div class="diks-footer__main-secondary-logos-container"><div class="diks-footer__main-secondary-logo"><a href="https://vastuullistajournalismia.fi/"><img src="https://a.sipoonsanomat.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"> 1011 <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/">
1011Tekoäly journalismissamme</a></div> 1012 <div class="diks-column diks-column--span-2"><a class="diks-footer__main-link" href="https://oma.media.fi/sipoon-sanomat/">Asiakaspalvelu</a><a class="diks-footer__main-link" href="https://meks.fi/mainonta/alueet/etela-suomen-media/">Mediamyynti</a><a class="diks-footer__main-link" href="https://oma.media.fi/sipoon-sanomat/yhteystiedot/">Yhteystiedot</a></div> 1013 </div></div> 1014 1015 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-4 diks-column--xl-span-5"><h2 class="diks-footer__main-title">Mistä tänään puhutaan?</h2><form id="lianamailer-footer" class="diks-newsletter-form js-diks-recaptcha-newsletter-form" action="https://sisaltokirjeet.media.fi/account" method="post"> 1016 <input type="hidden" name="join" value="759513"><input type="hidden" name="consent[]" value="2952-4-fi"><div class="diks-form__field-wrapper"> 1017 <label class="diks-form__label" for="email">Sähköpostiosoite</label> 1018 <input class="diks-form__field" type="email" name="email" id="lianamailer-footer-email" size="30" value=""> 1019 </div> 1020 <div class="diks-form__field-wrapper"> 1021 <input class="diks-form__submit g-recaptcha" type="submit" data-sitekey="6Lfmg9UZAAAAAORf4dzYH7iNb-xFbjh-D45PEgqq" data-callback="onSubmitFooter" value="Tilaa uutiskirje"> 1022 </div> 1023 </form></div> 1024 1025 <div class="diks-column diks-column--span-4 diks-column--m-span-8 diks-column--l-span-4 diks-column--xl-span-5"> 1026 <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"> 1027 <a class="diks-apps__app-link diks-hide-on-android" href="https://apps.apple.com/fi/app/sipoon-sanomat/id6740268452"><img class="diks-apps__app-image" src="https://a.sipoonsanomat.fi/styles/2efe7af9/images/default/appstore-badge.svg" loading="lazy" alt="Lataa sovellus App Storesta"></a> 1028 <a class="diks-apps__app-link diks-hide-on-ios" href="https://play.google.com/store/apps/details?id=fi.media.sipoonsanomat"><img class="diks-apps__app-image" src="https://a.sipoonsanomat.fi/styles/2efe7af9/images/default/google-play-badge.svg" loading="lazy" alt="Lataa sovellus Google Play -kaupasta"></a> 1029 </div> 1030 1031 1032 </div> 1033 </div> 1034 </div><div class="diks-footer__bottom"> 1035 <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.sipoonsanomat.fi/styles/2efe7af9/images/logos/keskisuomalainen-sign.svg" loading="lazy" alt="Keskisuomalainen Oyj"></a></div><span>Kuulumme Mediakonserni Keskisuomalaiseen</span></div> 1036 <div class="diks-footer__bottom-link-farm-wrapper"> 1037 <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/'>
1037Viispiikkinen</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> 1038 </div> 1039 </div></div> 1040</footer> 1041 1042 1043
1043<script> 1044 if (!DOMTokenList.prototype.replace) { 1045 // IE Polyfill 1046 DOMTokenList.prototype.replace = function (a, b) { 1047 if (this.contains(a)) { 1048 this.add(b); 1049 this.remove(a); 1050 return true; 1051 } 1052 return false; 1053 } 1054 } 1055 1056 document.querySelector('.diks-no-js').classList.replace('diks-no-js', 'diks-js'); 1057 1058 // User agent sniffing is not reliable and it's going away. 1059 // Use only to sprinkle a little bit of progressive enhancement on top. 1060 // Dear Apple, stop lying, iPad on iOS 13 is not a Mac. You're turning into 90s Microsoft. 1061 var userAgent = navigator.userAgent || navigator.vendor || window.opera; 1062 if (/android/i.test(userAgent)) { 1063 document.querySelector('html').classList.add('diks-user-agent--android'); 1064 } else if (/iPad|iPhone|iPod/i.test(userAgent) && !window.MSStream) { 1065 document.querySelector('html').classList.add('diks-user-agent--ios'); 1066 } else if (/macintosh/i.test(userAgent) && navigator.maxTouchPoints > 1) { 1067 document.querySelector('html').classList.add('diks-user-agent--ios'); 1068 } 1069</script>
1069 1070
1071<script> 1072 // Fix forEach for IE 1073 if (window.NodeList && !NodeList.prototype.forEach) { 1074 NodeList.prototype.forEach = Array.prototype.forEach; 1075 } 1076 1077 // Element.closest() needs a polyfill for older browsers. 1078 // While we only need the closest() functionality for mobile browsers, 1079 // we don't want the code to fail even on old desktop browsers. 1080 // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill 1081 // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Browser_compatibility 1082 if (!Element.prototype.matches) { 1083 Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; 1084 } 1085 1086 if (!Element.prototype.closest) { 1087 Element.prototype.closest = function(s) { 1088 var el = this; 1089 1090 do { 1091 if (el.matches(s)) return el; 1092 el = el.parentElement || el.parentNode; 1093 } while (el !== null && el.nodeType === 1); 1094 return null; 1095 }; 1096 } 1097 1098 function setAriaExpanded(toggler, ariaExpandedValue) { 1099 // Set aria-expanded attribute of dropdown toggler 1100 // If the toggler contains a button, set the aria-expanded attribute for the button instead 1101 if ( toggler.querySelector('button') != null ) { 1102 toggler.querySelector('button').setAttribute('aria-expanded', ariaExpandedValue); 1103 } 1104 else { 1105 toggler.setAttribute('aria-expanded', ariaExpandedValue); 1106 } 1107 } 1108 1109 function toggleElement(target, toggleClass, toggler) { 1110 if (toggleClass) { 1111 var isOpenClass = toggleClass + '--is-open'; 1112 var isClosedClass = toggleClass + '--is-closed'; 1113 1114 if (target.classList.contains(isOpenClass)) { 1115 // Close this toggle 1116 target.classList.remove(isOpenClass); 1117 target.classList.add(isClosedClass); 1118 1119 // Set aria-expanded to false for the toggler 1120 setAriaExpanded(toggler, 'false'); 1121 1122 } else if (target.classList.contains(isClosedClass)) { 1123 // Open this toggle 1124 target.classList.remove(isClosedClass); 1125 target.classList.add(isOpenClass); 1126 1127 // Set aria-expanded to true for the toggler 1128 setAriaExpanded(toggler, 'true'); 1129 } else { 1130 // Open this toggle as a fallback 1131 target.classList.add(isOpenClass); 1132 1133 // Set aria-expanded to true for the toggler 1134 setAriaExpanded(toggler, 'true'); 1135 } 1136 } else { 1137 if (window.getComputedStyle(target).display !== 'none') { 1138 target.style.display = 'none'; 1139 } else { 1140 target.style.display = 'block'; 1141 } 1142 } 1143 }; 1144 1145 document.querySelectorAll('.js-diks-toggler').forEach(function(toggler) { 1146 var toggleId = toggler.getAttribute('data-diks-toggle'); 1147 var target = document.getElementById(toggleId); 1148 var eventListenerAttached = toggler.getAttribute('data-diks-event-click'); 1149 1150 if (target && !eventListenerAttached) { 1151 1152 // Set aria-controls of toggler 1153 if ( toggler.querySelector('button') != null ) { 1154 toggler.querySelector('button').setAttribute('aria-controls', toggleId); 1155 } 1156 else { 1157 toggler.setAttribute('aria-controls', toggleId); 1158 } 1159 1160 toggler.addEventListener('click', function(e) { 1161 e.preventDefault(); 1162 1163 var toggleClass = toggler.getAttribute('data-diks-toggle-class'); 1164 1165 if (webShareAPIEnabled(toggleId)) { 1166 share(); 1167 } else { 1168 toggleElement(target, toggleClass, toggler); 1169 } 1170 }); 1171 1172 toggler.setAttribute('data-diks-event-click', true); 1173 } 1174 }); 1175 1176 // Web Share and Clipboard API capabilities 1177 1178 function webShareAPIEnabled(toggleId) { 1179 const webShareAPIEnabled = true; 1180 const webShareAPIDropdownShareIds = ["jaa","jakopainikkeet"]; 1181 const isMobileDevice = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); 1182 1183 return webShareAPIEnabled && webShareAPIDropdownShareIds.includes(toggleId) && isMobileDevice && navigator.share ? true : false; 1184 } 1185 1186 function share() { 1187 const metaTitleElement = document.querySelector('meta[property="og:title"]'); 1188 const metaDescriptionElement = document.querySelector('meta[name="description"]'); 1189 const metaUrlElement = document.querySelector('meta[property="og:url"]'); 1190 let title = ''; 1191 let description = ''; 1192 let url = document.location.href; 1193 1194 if (metaTitleElement && metaTitleElement.content) { 1195 title = metaTitleElement.content; 1196 } 1197 1198 if (metaDescriptionElement && metaDescriptionElement.content) { 1199 description = metaDescriptionElement.content; 1200 } 1201 1202 if (metaUrlElement && metaUrlElement.content) { 1203 url = metaUrlElement.content; 1204 } 1205 1206 navigator.share({ 1207 title: title, 1208 text: description, 1209 url: url 1210 }); 1211 } 1212 1213 document.querySelectorAll('.js-diks-share-clipboard').forEach(function(element) { 1214 element.addEventListener('click', function(e) { 1215 e.preventDefault(); 1216 1217 let url = document.location.href; 1218 const metaUrlElement = document.querySelector('meta[property="og:url"]'); 1219 1220 if (metaUrlElement && metaUrlElement.content) { 1221 url = metaUrlElement.content; 1222 } 1223 1224 // Copy URL to clipboard 1225 navigator.clipboard.writeText(url).then(function () { 1226 // Copying URL to clipboard succeeded 1227 const dropdownId = element.getAttribute('data-diks-dropdown-id'); 1228 // Find closest dropdown toggler 1229 let dropdownToggler = element.closest('.diks-dropdown').querySelector('.js-diks-toggler'); 1230 1231 if (dropdownId) { 1232 const dropdownElement = document.getElementById(dropdownId); 1233 1234 if (dropdownElement) { 1235 // Close currently open dropdown 1236 toggleElement(dropdownElement, 'diks-dropdown__content', dropdownToggler); 1237 } 1238 1239 // Show link copied notification text 1240 const notificationTextWrapperDiv = document.createElement('div'); 1241 const notificationTextDiv = document.createElement('div'); 1242 const notificationText = document.createTextNode('Linkki kopioitu leikepöydälle'); 1243 1244 notificationTextDiv.classList.add('diks-dropdown__notification-text'); 1245 notificationTextDiv.appendChild(notificationText); 1246 notificationTextWrapperDiv.classList.add('diks-dropdown__notification-text-wrapper'); 1247 notificationTextWrapperDiv.appendChild(notificationTextDiv); 1248 dropdownElement.parentNode.insertBefore(notificationTextWrapperDiv, null); 1249 1250 setTimeout(function () { 1251 notificationTextWrapperDiv.remove(); 1252 }, 5000); 1253 } 1254 }); 1255 }); 1256 }); 1257
1258 const initContentBlockToggleButtons = () => { 1259 const toggleContainers = document.querySelectorAll('[data-diks-init="toggle"]'); 1260 toggleContainers.forEach(container => { 1261 const toggleButton = container.querySelector('.diks-toggle-button'); 1262 const toggleContent = container.querySelector('.diks-toggle-content-container'); 1263 if (toggleButton && toggleContent) { 1264 toggleButton.addEventListener('click', () => { 1265 toggleElement(toggleContent, 'diks-toggle', toggleButton); 1266 }); 1267 } 1268 }); 1269 }; 1270 document.addEventListener('DOMContentLoaded', () => { 1271 initContentBlockToggleButtons(); 1272 }); 1273 1274</script>
1274 1275
1276<script> 1277 const useDragThreshold = true; 1278 1279 // Horizontally scrollable & draggable navigation 1280 class HorizontalScrollDragger { 1281 constructor(element) { 1282 this.slider = typeof element === 'string' ? document.querySelector(element) : element instanceof Element ? element : null; 1283 if (this.slider === null) return console.error('HorizontalScrollDragger: Element not found'); 1284 1285 this.isDown = false; 1286 this.preventClick = false; 1287 this.startX; 1288 this.scrollLeft; 1289 1290 this.dragThreshold = 5; 1291 this.dragSensitivity = 1; 1292 this.dragged = false; 1293 1294 this.init(); 1295 } 1296 1297 init() { 1298 this.slider.addEventListener('mousedown', (e) => this.handleMouseDown(e)); 1299 this.slider.addEventListener('mouseleave', () => this.handleMouseLeave()); 1300 this.slider.addEventListener('mouseup', () => this.handleMouseUp()); 1301 this.slider.addEventListener('mousemove', (e) => this.handleMouseMove(e)); 1302 this.slider.addEventListener('click', (e) => this.handleClick(e)); 1303 } 1304 1305 handleMouseDown(e) { 1306 this.isDown = true; 1307 this.slider.classList.add('diks-navigation--draggable-active'); 1308 this.startX = e.pageX - this.slider.offsetLeft; 1309 this.scrollLeft = this.slider.scrollLeft; 1310 this.preventClick = false; 1311 } 1312 1313 handleMouseLeave() { 1314 this.isDown = false; 1315 this.slider.classList.remove('diks-navigation--draggable-active'); 1316 } 1317 1318 handleMouseUp() { 1319 this.isDown = false; 1320 this.slider.classList.remove('diks-navigation--draggable-active'); 1321 } 1322 1323 handleMouseMove(e) { 1324 if (!this.isDown) return; 1325 1326 if ( useDragThreshold == true ) { 1327 const xWithThreshold = e.pageX - this.slider.offsetLeft; 1328 const delta = xWithThreshold - this.startX; 1329 1330 // If not yet dragging, check threshold 1331 if (!this.dragged && Math.abs(delta) > this.dragThreshold) { 1332 this.dragged = true; 1333 this.preventClick = true; 1334 1335 // Reset baseline so scrolling starts smoothly from here 1336 this.startX = xWithThreshold; 1337 this.scrollLeft = this.slider.scrollLeft; 1338 } 1339 1340 if (this.dragged) { 1341 e.preventDefault(); 1342 const walkWithThreshold = (xWithThreshold - this.startX) * this.dragSensitivity; // adjust sensitivity 1343 this.slider.scrollLeft = this.scrollLeft - walkWithThreshold; 1344 } 1345 } 1346 else { 1347 e.preventDefault(); 1348 const x = e.pageX - this.slider.offsetLeft; 1349 const walk = (x - this.startX) * this.dragSensitivity; // adjust sensitivity 1350 this.slider.scrollLeft = this.scrollLeft - walk; 1351 this.preventClick = true; 1352 } 1353 1354 } 1355 1356 handleClick(e) { 1357 if ( useDragThreshold == true && this.preventClick && this.dragged ) { 1358 e.preventDefault(); 1359 } 1360 else if ( useDragThreshold == false && this.preventClick) { 1361 e.preventDefault(); 1362 } 1363 } 1364 } 1365 1366 // Set the draggable navs (multiple) to handle their scrollbars 1367 let scrollableNavs = document.querySelectorAll('.diks-navigation--draggable'); 1368 1369 // Make draggable navigation draggable 1370 if ( scrollableNavs ) { 1371 scrollableNavs.forEach(function(element) { 1372 new HorizontalScrollDragger(element); 1373 }); 1374 } 1375 1376 1377 // Handle has-scrollbars class adding and removing 1378 function handleScrollableNavScrollbars(scrollableNavElement) { 1379 // If scrollable area is wider (scrollWidth) than what's visible (clientWidth) the element has scrollbars 1380 if ( scrollableNavElement.scrollWidth > scrollableNavElement.clientWidth ) { 1381 scrollableNavElement.classList.add('diks-navigation--draggable-has-scrollbars'); 1382 scrollableNavElement.parentElement.classList.add('diks-navigation-wrapper--has-scrollbars'); 1383 } 1384 // Remove the has-scrollbars class if it exists but shouldn't (element does not have scrollbars) 1385 else if ( scrollableNavElement.classList.contains('diks-navigation--draggable-has-scrollbars') ) { 1386 scrollableNavElement.classList.remove('diks-navigation--draggable-has-scrollbars'); 1387 scrollableNavElement.parentElement.classList.remove('diks-navigation-wrapper--has-scrollbars'); 1388 } 1389 } 1390 1391 // If scrollable nav exists add (or remove) necessary classes 1392 // "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 1393 window.addEventListener('load', (event) => { 1394 if ( scrollableNavs ) {
1395 scrollableNavs.forEach(function(element) { 1396 handleScrollableNavScrollbars(element); 1397 }); 1398 } 1399 }); 1400 1401 // Check if scrollable nav has scrollbar 1402 window.addEventListener('resize', function(event){ 1403 if ( scrollableNavs ) { 1404 scrollableNavs.forEach(function(element) { 1405 handleScrollableNavScrollbars(element); 1406 }); 1407 } 1408 }); 1409</script>
1409 1410
1411<script> 1412 // Back button handling 1413 document.querySelectorAll('a.js-diks-back-button').forEach(function(item) { 1414 var referrerIsSameDomain = false; 1415 // Check if referrer is found as referrer isn't always available (browser, tab, window, bookmark, privacy). 1416 if (typeof(document.referrer) === 'string' && document.referrer.length > 0) { 1417 // 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) 1418 if ( document.referrer.includes(document.location.hostname) && document.referrer != window.location.href ) { 1419 referrerIsSameDomain = true; 1420 } 1421 } 1422 1423 // Overwrite link with history.go, as it also remembers scroll position but only if referrer is the same domain as current location hostname 1424 if (history.length > 1 && referrerIsSameDomain == true) { 1425 // Set referrer as back button href to enable visual cue & opening to a new window 1426 item.setAttribute('href', document.referrer); 1427 1428 // Set back button text 1429 item.querySelector('.diks-header__cta-text').innerHTML = "Takaisin"; 1430 1431 var stepsToGoBack = -1; 1432 1433 item.addEventListener('click', function(e) { 1434 e.preventDefault(); 1435 history.go(stepsToGoBack); 1436 }); 1437 } 1438 }); 1439</script>
1439 1440 1441 <link rel="stylesheet" href="/theme/css/includes/lightgallery/css/lightgallery-bundle.min.css"> 1442 <style>.lg-sub-html { background-color: rgba(0,0,0, .6); }</style> 1443
1443<script src="/theme/css/includes/lightgallery/lightgallery.min.js"></script>
1443 1444
1444<script src="/theme/css/includes/lightgallery/plugins/autoplay/lg-autoplay.min.js"></script>
1444 1445
1445<script src="/theme/css/includes/lightgallery/plugins/fullscreen/lg-fullscreen.min.js"></script>
1445 1446
1446<script src="/theme/css/includes/lightgallery/plugins/thumbnail/lg-thumbnail.min.js"></script>
1446 1447
1447<script src="/theme/css/includes/lightgallery/plugins/zoom/lg-zoom.min.js"></script>
1447 1448
1448<script> 1449 // lightGallery 1450 // Separate gallery for every .js-lightgallery 1451 // Gallery items come from .js-lightgallery .js-lightgallery__link 1452 // https://www.lightgalleryjs.com/docs/getting-started/ 1453 document.querySelectorAll('.js-diks-lightgallery').forEach(function(gallery) { 1454 var galleryImageLinks = gallery.getElementsByClassName('diks-gallery__link'); 1455 1456 // Thumbnails come from img inside the links, unless links specify data-thumbnail 1457 var thumbnailSource = (galleryImageLinks[0].hasAttribute('data-thumbnail')) ? ('data-thumbnail') : (false); 1458 1459 // Adjust certain settings depending on whether the gallery has one or more images 1460 var galleryHasMultipleImages = (galleryImageLinks.length > 1 ? true : false); 1461 1462 // Initialize 1463 lightGallery(gallery, { 1464 autoplay: galleryHasMultipleImages, 1465 counter: galleryHasMultipleImages, 1466 download: false, 1467 exThumbImage: thumbnailSource, 1468 hideControlOnEnd: true, 1469 licenseKey: "96BAAD7E-3BF04397-BBB55AE3-6D5A2C0B", 1470 loop: false, 1471 plugins: [lgAutoplay, lgFullscreen, lgThumbnail, lgZoom], 1472 selector: '.diks-gallery__link', 1473 mobileSettings: { 1474 controls: true, 1475 showCloseIcon: true 1476 }, 1477 strings: { 1478 closeGallery: 'Sulje galleria', 1479 toggleMaximize: 'Maksimointi', 1480 previousSlide: 'Edellinen', 1481 nextSlide: 'Seuraava', 1482 download: 'Lataa', 1483 playVideo: 'Katso video', 1484 mediaLoadingFailed: 'Sisällön lataus epäonnistui', 1485 }, 1486 autoplayPluginStrings: { 1487 toggleAutoplay: 'Automaattinen toisto' 1488 }, 1489 fullscreenPluginStrings: { 1490 toggleFullscreen: 'Kokoruututila' 1491 }, 1492 thumbnailPluginStrings: { 1493 toggleThumbnails: 'Pikkukuvat' 1494 }, 1495 zoomPluginStrings: { 1496 zoomIn: 'Suurenna', 1497 zoomOut: 'Pienennä', 1498 viewActualSize: 'Näytä täysikokoisena' 1499 } 1500 }); 1501 1502 // After closing LightGallery return focus to the image link that opened the gallery 1503 gallery.addEventListener('lgAfterClose', () => { 1504 galleryImageLinks[0].focus(); 1505 }); 1506 1507 }); 1508 1509 </script>
1509 1510
1511<script> 1512 function loadScript(url, callback) { 1513 var script = document.createElement("script"); 1514 script.type = "text/javascript"; 1515 if (script.readyState) { // IE 1516 script.onreadystatechange = function() { 1517 if (script.readyState === "loaded" || script.readyState === "complete") { 1518 script.onreadystatechange = null; 1519 callback(); 1520 } 1521 }; 1522 } else { // Others 1523 script.onload = function() { 1524 callback(); 1525 }; 1526 } 1527 script.src = url; 1528 document.getElementsByTagName("head")[0].appendChild(script); 1529 } 1530 function checkAudioElements() { 1531 // load audioplayer script only when audio elements are found 1532 if (document.querySelector(".init-audio-player")) { 1533 loadScript("https://a.sipoonsanomat.fi/styles/2efe7af9/js/audioplayer/diks-audioplayer.min.js", function() { 1534 DiksAudioPlayer.init({ 1535 selector: '.init-audio-player', 1536 stopOthersOnPlay: true, 1537 enableKeystrokes: true 1538 }); 1539 }); 1540 } 1541 } 1542 window.onload = (event) => { 1543 checkAudioElements(); 1544 }; 1545</script>
1545<script> 1546 // ================================================================ 1547 // Unified Content Block Component 1548 // ================================================================ 1549 1550 const DiksContentBlock = (() => { 1551 // Private utility functions 1552 const utils = { 1553 setCookie: (name, value, days = 30) => { 1554 const expires = new Date(); 1555 expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000)); 1556 document.cookie = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/`; 1557 }, 1558 1559 getCookie: (name) => { 1560 const value = `; ${document.cookie}`; 1561 const parts = value.split(`; ${name}=`); 1562 if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift()); 1563 return null; 1564 }, 1565 1566 renderArticleCard: (article) => { 1567 let mainImage = article.mainImage; 1568 1569 // For ContentPackage: if mainImage is empty, get from first story in teaserStories 1570 if (article.type === 'ContentPackage' && (!mainImage || mainImage.length === 0)) { 1571 if (article.teaserStories && article.teaserStories.length > 0) { 1572 // Find first teaser story with a mainImage 1573 for (const teaserStory of article.teaserStories) { 1574 if (teaserStory.content && 1575 teaserStory.content.mainImage && 1576 teaserStory.content.mainImage.length > 0) { 1577 mainImage = teaserStory.content.mainImage; 1578 break; 1579 } 1580 } 1581 } 1582 } 1583 1584 let imageHtml = ''; 1585 if (mainImage && mainImage.length > 0) { 1586 const imageFields = mainImage[0].content.fields; 1587 const src = imageFields.landscape_720.href_full; 1588 const srcset = ` 1589 ${imageFields.landscape_240.href_full} 240w, 1590 ${imageFields.landscape_480.href_full} 480w, 1591 ${imageFields.landscape_720.href_full} 720w, 1592 ${imageFields.landscape_960.href_full} 960w 1593 `; 1594 imageHtml = ` 1595 <div class="diks-card__image-wrapper"> 1596 <img class="diks-card__image" 1597 src="${src}" 1598 loading="lazy" 1599 sizes="(max-width: 504px) 480px, (max-width: 1279px) 720px, 960px" 1600 srcset="${srcset}" 1601 decoding="async" 1602 alt="${article.headline}" 1603 pinger-seen="true"> 1604 </div> 1605 `; 1606 } 1607 1608 const fullHeadline = article.headlinePrefix 1609 ? `<span class="diks-ui-accent">${article.headlinePrefix} | </span>${article.headline}` 1610 : article.headline; 1611 1612 const publishedDate = new Date(article.published).toLocaleString('fi-FI', { 1613 day: 'numeric', 1614 month: 'numeric', 1615 year: 'numeric', 1616 hour: 'numeric', 1617 minute: 'numeric' 1618 }); 1619 1620 return ` 1621 <article class="diks-card diks-card--size-s"> 1622 <div class="diks-card__main-wrapper"> 1623 ${imageHtml} 1624 <div class="diks-card__text-wrapper"> 1625 <div class="diks-card__text-inner-wrapper"> 1626 <h2 class="diks-card__title"> 1627 <a class="diks-card__link" href="${article.href}">${fullHeadline}</a> 1628 </h2> 1629 <div class="diks-card__meta-wrapper"> 1630 <div class="diks-meta"> 1631 <div class="diks-date"> 1632 <time class="diks-date__published" datetime="${article.published}">${publishedDate}</time> 1633 </div> 1634 </div> 1635 </div> 1636 </div> 1637 </div> 1638 </div> 1639 </article> 1640 `; 1641 }, 1642 1643 getComponentType: (containerId) => { 1644 if (containerId.startsWith('diks-content-block-search-')) return 'search'; 1645 if (containerId.startsWith('diks-content-block-aggregator-')) return 'aggregator'; 1646 return null; 1647 }, 1648 1649 getBlockId: (containerId, type) => { 1650 if (type === 'search') return containerId.replace('diks-content-block-search-', ''); 1651 if (type === 'aggregator') return containerId.replace('diks-content-block-aggregator-', ''); 1652 return containerId; 1653 }, 1654 1655 getResultsId: (blockId, type) => { 1656 if (type === 'search') return `diks-search-results-${blockId}`; 1657 if (type === 'aggregator') return `diks-results-${blockId}`; 1658 return null; 1659 } 1660 }; 1661 1662 // Build query parameters based on component type and filters 1663 const buildQueryParams = (containerId) => { 1664 const container = document.getElementById(containerId); 1665 const type = utils.getComponentType(containerId); 1666 const blockId = utils.getBlockId(containerId, type); 1667 1668 const params = new URLSearchParams({ 1669 type: container.dataset.queryType || 'story', 1670 size: container.dataset.maxResults || 20 1671 }); 1672 1673 if (type === 'search') { 1674 // Search-specific parameters 1675 const defaultSort = container.dataset.sort; 1676 const defaultPaywall = container.dataset.paywall; 1677 const defaultStoryTypes = container.dataset.storyTypes; 1678 const defaultTopics = container.dataset.topics; 1679 const defaultAdvertisers = container.dataset.advertisers; 1680 const dateRangeType = container.dataset.dateRangeType; 1681 1682 // Sort 1683 if (defaultSort && defaultSort !== '*') { 1684 params.append('sort', defaultSort); 1685 } 1686 1687 // Paywall 1688 const paywallSelect = document.getElementById(`diks-search-paywall-${blockId}`); 1689 if (paywallSelect && paywallSelect.value) { 1690 params.append('paywall', paywallSelect.value); 1691 } else if (defaultPaywall && defaultPaywall !== '*') { 1692 params.append('paywall', defaultPaywall); 1693 } 1694 1695 // Story types 1696 let storyTypesValue = defaultStoryTypes; 1697 const storyTypesSelect = document.getElementById(`diks-search-story-types-${blockId}`); 1698 if (storyTypesSelect) { 1699 const selectedOptions = Array.from(storyTypesSelect.selectedOptions).map(opt => opt.value); 1700 if (selectedOptions.length > 0) { 1701 storyTypesValue = selectedOptions.join(','); 1702 } 1703 } 1704 if (storyTypesValue) { 1705 params.append('storyTypes', storyTypesValue); 1706 } 1707 1708 // Topics 1709 if (defaultTopics) { 1710 params.append('topics', defaultTopics); 1711 const topicsOperator = container.dataset.topicsOperator || 'OR'; 1712 params.append('topicsOperator', topicsOperator); 1713 } 1714 1715 // Advertisers 1716 if (defaultAdvertisers) { 1717 params.append('advertisers', defaultAdvertisers); 1718 const advertisersOperator = container.dataset.advertisersOperator || 'OR'; 1719 params.append('advertisersOperator', advertisersOperator); 1720 } 1721 1722 // Date ranges 1723 const fromDateInput = document.getElementById(`diks-search-from-date-${blockId}`); 1724 const toDateInput = document.getElementById(`diks-search-to-date-${blockId}`); 1725 const monthInput = document.getElementById(`diks-search-month-${blockId}`); 1726 1727 if (dateRangeType === 'byMonth' && monthInput && monthInput.value) { 1728 const [year, month] = monthInput.value.split('-').map(Number); 1729 const fromDate = new Date(year, month - 1, 1, 0, 0, 0, 0); 1730 const toDate = new Date(year, month, 0, 23, 59, 59, 999); 1731 params.append('fromDate', fromDate.toISOString().split('.')[0] + 'Z'); 1732 params.append('toDate', toDate.toISOString().split('.')[0] + 'Z'); 1733 } else { 1734 if (fromDateInput && fromDateInput.value) { 1735 const fromDate = new Date(fromDateInput.value); 1736 fromDate.setHours(0, 0, 0, 0); 1737 params.append('fromDate', fromDate.toISOString().split('.')[0] + 'Z'); 1738 } 1739 if (toDateInput && toDateInput.value) { 1740 const toDate = new Date(toDateInput.value); 1741 toDate.setHours(23, 59, 59, 999); 1742 params.append('toDate', toDate.toISOString().split('.')[0] + 'Z'); 1743 } 1744 } 1745 } else if (type === 'aggregator') { 1746 // Aggregator-specific parameters 1747 const selectedTopic = container.dataset.selectedTopic; 1748 if (selectedTopic) { 1749 params.append('topics', selectedTopic); 1750 } 1751 } 1752 1753 return params; 1754 }; 1755 1756 // Main fetch content function 1757 const fetchContent = async (containerId) => { 1758 const container = document.getElementById(containerId); 1759 const type = utils.getComponentType(containerId); 1760 const blockId = utils.getBlockId(containerId, type); 1761 const resultsId = utils.getResultsId(blockId, type); 1762 const resultsDiv = document.getElementById(resultsId); 1763 1764 if (!container || !resultsDiv) { 1765 console.error(`Content block container or results div not found: ${containerId}`); 1766 return; 1767 } 1768 1769 try { 1770 // Show loading state 1771 const loadingMessage = type === 'search' 1772 ? '<p class="diks-content-block-search--loading">Ladataan...</p>' 1773 : '<p class="diks-content-block-aggregator--loading">Ladataan...</p>'; 1774 resultsDiv.innerHTML = loadingMessage; 1775 1776 // Build query parameters 1777 const params = buildQueryParams(containerId); 1778 const apiUrl = container.dataset.apiUrl || '/.content/items'; 1779 1780 // Fetch data 1781 const response = await fetch(`${apiUrl}?${params.toString()}`, { 1782 method: 'GET' 1783 }); 1784 1785 if (!response.ok) { 1786 throw new Error('Verkkovirhe.'); 1787 } 1788 1789 const data = await response.json(); 1790 1791 // Render results 1792 let html = ''; 1793 if (data.items && data.items.length > 0) {
1794 data.items.forEach(article => { 1795 html += utils.renderArticleCard(article); 1796 }); 1797 } else { 1798 const noResultsMessage = type === 'search' 1799 ? '<p class="diks-content-block-search--no-results">Ei hakutuloksia.</p>' 1800 : '<p>Ei sisältöä.</p>'; 1801 html = noResultsMessage; 1802 } 1803 1804 resultsDiv.innerHTML = html; 1805 } catch (error) { 1806 const errorMessage = type === 'search' 1807 ? `<p class="diks-content-block-search--error">Virhe: ${error.message}</p>` 1808 : `<p class="error">Error: ${error.message}</p>`; 1809 resultsDiv.innerHTML = errorMessage; 1810 } 1811 }; 1812 1813 // Initialize search component 1814 const initializeSearch = (containerId) => { 1815 const container = document.getElementById(containerId); 1816 const blockId = utils.getBlockId(containerId, 'search'); 1817 const dateRangeType = container.dataset.dateRangeType; 1818 1819 // Get UI elements 1820 const searchButton = document.getElementById(`diks-search-button-${blockId}`); 1821 const fromDateInput = document.getElementById(`diks-search-from-date-${blockId}`); 1822 const toDateInput = document.getElementById(`diks-search-to-date-${blockId}`); 1823 const monthInput = document.getElementById(`diks-search-month-${blockId}`); 1824 1825 // Set current month as default for byMonth type 1826 if (dateRangeType === 'byMonth' && monthInput) { 1827 const now = new Date(); 1828 const year = now.getFullYear();
1829 const month = String(now.getMonth() + 1).padStart(2, '0'); 1830 monthInput.value = `${year}-${month}`; 1831 1832 // Auto-trigger search on month change 1833 monthInput.addEventListener('change', () => fetchContent(containerId)); 1834 } 1835 1836 // Search button click handler 1837 if (searchButton) { 1838 searchButton.addEventListener('click', () => fetchContent(containerId)); 1839 } 1840 1841 // Enter key support 1842 [fromDateInput, toDateInput, monthInput].forEach(input => { 1843 if (input) { 1844 input.addEventListener('keypress', (e) => { 1845 if (e.key === 'Enter') fetchContent(containerId); 1846 }); 1847 } 1848 }); 1849 1850 // Initial search 1851 fetchContent(containerId); 1852 }; 1853 1854 // Initialize aggregator component 1855 const initializeAggregator = (containerId) => { 1856 const container = document.getElementById(containerId); 1857 const blockId = utils.getBlockId(containerId, 'aggregator'); 1858 const topicLabels = JSON.parse(container.dataset.topicLabels || '{}'); 1859 const cookieName = `selectedTopic_${blockId}`; 1860 1861 const listId = `diks-topic-list-${blockId}`; 1862 const labelId = `diks-selected-topic-label-${blockId}`; 1863 const toggleId = `diks-toggle-button-${blockId}`; 1864 const listContainerId = `diks-toggle-area-${blockId}`; 1865 1866 const topicItems = document.querySelectorAll(`#${listId} .diks-topic-list__item`); 1867 const labelElement = document.getElementById(labelId); 1868 1869 // Update topic label display 1870 const updateTopicLabel = (topic) => { 1871 if (labelElement && topicLabels[topic]) { 1872 labelElement.textContent = topicLabels[topic]; 1873 } else if (labelElement) { 1874 labelElement.textContent = ''; 1875 } 1876 1877 // Update active state 1878 topicItems.forEach(item => { 1879 item.classList.toggle('diks-is-active', item.dataset.topic === topic); 1880 }); 1881 }; 1882 1883 // Handle single topic case 1884 if (Object.keys(topicLabels).length === 1) { 1885 const initialTopic = Object.keys(topicLabels)[0]; 1886 container.dataset.selectedTopic = initialTopic; 1887 updateTopicLabel(initialTopic); 1888 fetchContent(containerId); 1889 return; 1890 } 1891 1892 // Handle multiple topics case 1893 if (topicItems.length > 0) { 1894 // Determine initial topic (cookie or first) 1895 const cookieTopic = utils.getCookie(cookieName); 1896 let initialTopic = topicItems[0].dataset.topic; 1897 1898 if (cookieTopic) { 1899 const validTopic = Array.from(topicItems).some(item => item.dataset.topic === cookieTopic); 1900 if (validTopic) { 1901 initialTopic = cookieTopic; 1902 } 1903 } 1904 1905 // Set up topic click handlers 1906 topicItems.forEach(item => { 1907 item.addEventListener('click', () => { 1908 const selectedTopic = item.dataset.topic; 1909 container.dataset.selectedTopic = selectedTopic; 1910 utils.setCookie(cookieName, selectedTopic); 1911 updateTopicLabel(selectedTopic); 1912 fetchContent(containerId); 1913 1914 // Close toggle if open 1915 const toggleButton = document.getElementById(toggleId); 1916 const listContainer = document.getElementById(listContainerId); 1917 if (toggleButton && listContainer) { 1918 toggleButton.setAttribute('aria-expanded', 'false'); 1919 listContainer.classList.remove('diks-toggle--is-open'); 1920 listContainer.classList.add('diks-toggle--is-closed'); 1921 container.dataset.diksToggleState = 'closed'; 1922 } 1923 }); 1924 }); 1925 1926 // Initial fetch 1927 container.dataset.selectedTopic = initialTopic; 1928 updateTopicLabel(initialTopic); 1929 fetchContent(containerId); 1930 } 1931 }; 1932 1933 // Public API 1934 return { 1935 fetchContent, 1936 initialize: (containerId) => { 1937 const type = utils.getComponentType(containerId); 1938 if (type === 'search') { 1939 initializeSearch(containerId); 1940 } else if (type === 'aggregator') { 1941 initializeAggregator(containerId); 1942 } 1943 } 1944 }; 1945 })(); 1946 1947 1948 // ================================================================ 1949 // Auto-initialize all content block components 1950 // ================================================================ 1951 1952 document.addEventListener('DOMContentLoaded', () => { 1953 // Initialize all content block components (search + aggregator) 1954 const allContainers = document.querySelectorAll('[id^="diks-content-block-search-"], [id^="diks-content-block-aggregator-"]');
1955 allContainers.forEach(container => { 1956 DiksContentBlock.initialize(container.id); 1957 }); 1958 }); 1959</script>
vendor: 1 bytes, line 1959
1959
1960<script> 1961 // Service Worker 1962 if ('serviceWorker' in navigator) { 1963 window.addEventListener('load', function() { 1964 navigator.serviceWorker.register('/sw.js').then( 1965 function(registration) { 1966 console.log('PWA: Service worker registered to scope: ', registration.scope); 1967 }, 1968 function(err) { 1969 console.error('PWA: Service worker failed to register with error: ', err); 1970 } 1971 ); 1972 }); 1973 } 1974 1975 // Show installation option, hide generic fallback instructions 1976 function pwaShowInstallationOption() { 1977 document.querySelectorAll('.js-diks-apps-pwa-installation').forEach(function(element) { 1978 element.style.display = 'block'; 1979 }); 1980 document.querySelectorAll('.js-diks-apps-pwa-instructions').forEach(function(element) { 1981 element.style.display = 'none'; 1982 }); 1983 } 1984 1985 let deferredPrompt; 1986 var dataLayer = window.dataLayer || []; 1987 1988 // Basic installation process 1989 window.addEventListener('beforeinstallprompt', (e) => { 1990 // Stash the event so it can be triggered later 1991 deferredPrompt = e; 1992 1993 const enablePrompt = 0; 1994 1995 if(!enablePrompt) { 1996 e.preventDefault(); 1997 } 1998 1999 // Update the UI to notify the user they can install PWA 2000 pwaShowInstallationOption(); 2001 2002 // Handle installation 2003 document.querySelectorAll('.js-diks-apps-pwa-installation-button').forEach(function(button) { 2004 button.addEventListener('click', (e) => { 2005 // Show the prompt 2006 deferredPrompt.prompt(); 2007 2008 // Wait for user to respond 2009 deferredPrompt.userChoice.then((choiceResult) => { 2010 if (choiceResult.outcome === 'accepted') { 2011 console.log('PWA: Installed (prompt)'); 2012 dataLayer.push({ 2013 'event': 'PWA.installation', 2014 'eventData': 'success' 2015 }); 2016 } else { 2017 console.log('PWA: User dismissed installation prompt'); 2018 dataLayer.push({ 2019 'event': 'PWA.installation', 2020 'eventData': 'canceled' 2021 }); 2022 } 2023 deferredPrompt = null; 2024 }); 2025 }); 2026 }); 2027 }); 2028 2029 // iOS installation process (show instructions modal) 2030 if (!navigator.standalone) { 2031 var userAgent = navigator.userAgent.toLowerCase(); 2032 var iPhoneUser = (/iphone/.test(userAgent)); 2033 var iPadUser = (/ipad/.test(userAgent) || (/macintosh/.test(userAgent) && navigator.maxTouchPoints > 1)); 2034 var safariUser = (/^(?!.*(brave|chrome|crios|duckduckgo|edgeios|firefox|fxios|opios|ucbrowser|yabrowser)).*safari.*$/.test(userAgent)); 2035 2036 if (safariUser && (iPhoneUser || iPadUser)) { 2037 // Update the UI to notify the user they can install PWA 2038 pwaShowInstallationOption(); 2039 2040 // Instructions 2041 var modals = document.querySelectorAll('.js-diks-apps-pwa-ios-instructions').forEach(function(element) { 2042 // Position on screen based on device 2043 if (iPhoneUser) { 2044 element.classList.add('diks-apps__pwa-ios-modal--bottom'); 2045 } else if (iPadUser) { 2046 element.classList.add('diks-apps__pwa-ios-modal--top'); 2047 } 2048 }); 2049 2050 // Handle modal instructions from installation button 2051 document.querySelectorAll('.js-diks-apps-pwa-installation-button').forEach(function(button) { 2052 button.addEventListener('click', (e) => { 2053 // Show first modal 2054 document.querySelector('.js-diks-apps-pwa-ios-instructions').classList.toggle('diks-apps__pwa-ios-modal--is-visible'); 2055 dataLayer.push({ 2056 'event': 'PWA.instructions.shown', 2057 'eventData': 'iOS' 2058 }); 2059 }); 2060 }); 2061 } 2062 } 2063</script>
2063 2064
2064<script class="coral-script" src="//coral.prod.diks.fi/assets/js/count.js?v=9.3.1.3" defer></script>
vendor: 78 bytes, lines 2064-2065
2064<script src="https://www.google.com/recaptcha/api.js" async defer></script> 2065
2065<script> 2066 function onSubmitSidebar(token) { 2067 document.getElementById("lianamailer-sidebar").submit(); 2068 } 2069 function onSubmitFooter(token) { 2070 document.getElementById("lianamailer-footer").submit(); 2071 } 2072 </script>
2072 2073 <style>.grecaptcha-badge { visibility: hidden; }</style> 2074<div id="diks-bc-order-button" style="display: none;"></div> 2075 </body> 2076</html> 2077
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.