null Helsingissä kokeillaan maksukorttikolehtia

Virhe tapahtui prosessoidessa esitysmallia.
The following has evaluated to null or missing:
==> slot.videoFile  [in template "20116#20160#45930" at line 698, column 42]

----
Tip: It's the step after the last dot that caused this error, not those before it.
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
	- Failed at: #assign videoFile = slot.videoFile.ge...  [in template "20116#20160#45930" in macro "printRemainingVideoContents" at line 698, column 21]
	- Reached through: @printRemainingVideoContents articleV...  [in template "20116#20160#45930" at line 336, column 9]
----
1<#----------------------------------------------------------------------------- 
2    INIT 
3------------------------------------------------------------------------------> 
4<#setting time_zone="Europe/Helsinki"> 
5<#assign serviceContext = staticUtil["com.liferay.portal.kernel.service.ServiceContextThreadLocal"].getServiceContext()> 
6<#assign themeDisplay = serviceContext.getThemeDisplay() /> 
7<#assign javascript_folder = themeDisplay.getPathThemeJavaScript() > 
8 
9 
10<#assign JournalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService")> 
11<#assign articleContentService = serviceLocator.findService("com.ch5finland.helsinginseurakuntayhtyma.kirkkojakaupunki.article.content.service.ArticleContentService")> 
12 
13<#assign currentArticle = JournalArticleLocalService.getArticle(groupId, .vars['reserved-article-id'].data) /> 
14<#setting url_escaping_charset="UTF-8"> 
15 
16<#-- Helper variables --> 
17<#assign includeOwlScript = false> 
18 
19<#assign previewMode = false > 
20<#-- Check if article is viewed in preview mode --> 
21 
22<#if themeDisplay.getURLCurrent()?contains("preview_article_content.jsp") > 
23    <#-- this aplies if preview is selected via folder/list view. Site css is included  --> 
24    <#assign previewMode = true > 
25</#if> 
26<#if themeDisplay.getURLCurrent()?contains("preview_article_content_template.jsp") > 
27    <#-- this aplies if preview is selected via article edit. Site css is not included for some reason so here is little hack --> 
28    <#assign previewMode = true > 
29    <link rel="stylesheet" type="text/css"  href="/o/kirkkojakaupunki-site-theme/css/main.css" /> 
30    <script>$.getScript("/o/kirkkojakaupunki-site-theme/js/main.js");</script> 
31</#if> 
32 
33<#-- 
34    Counters for keeping track of now many elements 
35    have been placed with a tag. 
36--> 
37<#global tagReplaceCounter = 0 /> 
38<#assign kainaloTagCounter = 0 /> 
39<#assign infoTagCounter = 0 /> 
40<#assign faktaTagCounter = 0 /> 
41<#assign quoteTagCounter = 0 /> 
42<#assign videoTagCounter = 0 /> 
43<#assign audioTagCounter = 0 /> 
44<#assign carousel1TagCounter = 0 /> 
45<#assign carousel2TagCounter = 0 /> 
46<#assign imageComparisonTagCounter = 0 /> 
47 
48<#-- Article ID --> 
49<#assign articleId = .vars['reserved-article-id'].data> 
50 
51<#-- Article --> 
52<#assign article = JournalArticleLocalService.getArticle(groupId, articleId) /> 
53 
54<#-- Article complete URL --> 
55<#assign urlTitle = article.urlTitle?trim /> 
56<#assign articleCompleteUrl = themeDisplay.getPortalURL() + "/-/" + urlTitle /> 
57 
58<#-- available author names : Those authors which have author card --> 
59<#assign authorCardFolderIds = [140767, 140764] /> <#-- Toimittjat, Kolumnistit --> 
60<#assign availableAuthorNames = getAvailableAuthorNames(authorCardFolderIds) /> 
61<#assign authorCardBaseUrl = "/toimittajat?author=" /> 
62 
63<#-- Article title --> 
64<#assign title = .vars['reserved-article-title'].data> 
65 
66<#-- Article lead --> 
67<#assign lead = .vars['reserved-article-description'].data> 
68 
69<#-- Get and format article publish date --> 
70<#assign displayDate = .vars['reserved-article-display-date'].data> 
71 
72    <#-- Save the original page locale for later --> 
73    <#assign originalLocale = locale> 
74 
75    <#-- Set the page locale to the portals default locale --> 
76    <#setting locale = localeUtil.getDefault()> 
77 
78    <#-- Parse the date to a date object --> 
79    <#assign displayDate = displayDate?datetime("EEE, d MMM yyyy HH:mm:ss Z")> 
80 
81    <#-- Set the page locale back to the original page locale --> 
82    <#assign locale = originalLocale> 
83 
84 
85<#-- Article body tags --> 
86<#assign tagKainalo = "[[kainalo]]"> 
87<#assign tagInfo = "[[info]]"> 
88<#assign tagFakta = "[[fakta]]"> 
89<#assign tagQuote = "[[sitaatti]]"> 
90<#assign tagImage = "[[kuva]]"> 
91<#assign tagVideo = "[[video]]"> 
92<#assign tagAudio = "[[audio]]"> 
93<#assign tagCarousel1 = "[[kuvakaruselli1]]"> 
94<#assign tagCarousel2 = "[[kuvakaruselli2]]"> 
95<#assign tagImageComparison = "[[vaihtokuva]]"> 
96 
97<#-- lisäksi käytössä  
98  [[mainos]] 
99  [[eimainosta]] 
100--> 
101 
102<#-- wrapper css classes --> 
103<#assign wrapperCSSKainalo = "article-section article-tail"> 
104<#assign wrapperCSSInfo = "article-box article-info"> 
105<#assign wrapperCSSFakta = "article-box article-fact"> 
106<#assign wrapperCSSQuote = "template-quote"> 
107 
108<#-- Article tags --> 
109<#assign articleTags = .vars['reserved-article-asset-tag-names'].data> 
110 
111<#-- Article raw body --> 
112<#assign articleBody = articleBody.getData()> 
113 
114<#-- Ad :  
115 https://tiketti.ch5.fi/issues/24425 
116  
117    Lähtökohta: Artikkelin sisäinen mainos näkyy aina neljän leipätekstin <p>-tagin jälkeen. 
118    Tarvittaessa artikkelin muokkaaja voi muuttaa mainoksen näyttöpaikkaa [[mainos]]-tagilla 
119    Tarvittaessa artikkelin muokkaaja voi estää mainoksen näkemisen yksittäisessä artikkelissa [[eimainosta]]-tagilla. 
120    Mainos ei näy ”erikoisartikkeleissa” eikä ”kuva-artikkeleissa”, ainoastaan ”Koko artikkeleissa”. 
121    Artikkelin sisäinen mainos ei näy koskaan artikkeleissa, joissa on tägi ”pääkirjoitus” tai ”hyvää pyhää” tai joiden leipäteksti on hyvin lyhyt (pituus alle 8 <p>) 
122    Mainos näytetään kaikissa palvelun vanhoissa artikkeleissa edellä mainituin rajoituksin. 
123    Artikkelin sisäisiä mainospaikkoja voidaan tehdä montakin, mutta ehkä lähdetään yhdellä liikkeelle… 
124    Pyritään testaamaan artikkelin sisäisen mainospaikan toimivuus kehitysympäristössä. 
125 
126     
127    --> 
128<#assign insertAdAfterNthParagraph = 4 /> 
129<#assign insertAdIfContainsMoreThanParagraphs = 8 /> 
130<#assign tagPreventsShowingAd = articleTags?contains("pääkirjoitus") || articleTags?contains("hyvää pyhää") /> 
131 
132<#if !articleBody?contains("[[eimainosta]]") && !tagPreventsShowingAd> 
133 
134    <#if !articleBody?contains("[[mainos]]")> 
135        <#-- no ad tag so inject --> 
136        <#assign articleBody = articleContentService.injectAdMarker(articleBody, insertAdAfterNthParagraph, insertAdIfContainsMoreThanParagraphs) /> 
137    </#if> 
138    <#assign articleBody = replaceAdTag(articleBody) /> 
139<#else> 
140 
141    <#-- Clean [[eimainosta]] and possible [[mainos]] tags -->  
142     
143    <#assign articleBody = articleBody?replace("<p>[[eimainosta]]</p>", "") /> 
144    <#assign articleBody = articleBody?replace("[[eimainosta]]", "") /> 
145    <#assign articleBody = articleBody?replace("<p>[[mainos]]</p>", "") /> 
146    <#assign articleBody = articleBody?replace("[[mainos]]", "") />     
147</#if> 
148 
149<#-- // END OF AD stuff --> 
150 
151<#if articleTail?has_content> 
152    <#assign articleBody = replaceTags(articleBody, tagKainalo, articleTail, wrapperCSSKainalo)> 
153    <#assign kainaloTagCounter = tagReplaceCounter /> 
154</#if> 
155<#if articleInfo?has_content> 
156    <#assign articleBody = replaceTags(articleBody, tagInfo, articleInfo, wrapperCSSInfo)> 
157    <#assign infoTagCounter = tagReplaceCounter /> 
158</#if> 
159<#if articleFacts?has_content> 
160    <#assign articleBody = replaceTags(articleBody, tagFakta, articleFacts, wrapperCSSFakta)> 
161    <#assign faktaTagCounter = tagReplaceCounter /> 
162</#if> 
163<#if articleQuotes?has_content> 
164    <#assign articleBody = replaceTags(articleBody, tagQuote, articleQuotes, wrapperCSSQuote)> 
165    <#assign quoteTagCounter = tagReplaceCounter /> 
166</#if> 
167<#if articleImages?has_content> 
168    <#assign articleBody = replaceImageTags(articleBody, tagImage, articleImages)> 
169    <#assign articleBody = replaceCarouselTags(articleBody, tagCarousel1, tagCarousel2, articleImages)> 
170</#if> 
171<#if articleVideos?has_content> 
172    <#assign articleBody = replaceVideoTags(articleBody, tagVideo, articleVideos)> 
173    <#assign videoTagCounter = tagReplaceCounter /> 
174</#if> 
175<#if articleAudio?has_content> 
176    <#assign articleBody = replaceAudioTags(articleBody, tagAudio, articleAudio)> 
177    <#assign audioTagCounter = tagReplaceCounter /> 
178</#if> 
179<#if imageComparisons?has_content> 
180    <#assign articleBody = replaceImageComparisonTags(articleBody, tagImageComparison, imageComparisons)> 
181</#if> 
182 
183 
184<#----------------------------------------------------------------------------- 
185    OUTPUT ARTICLE 
186------------------------------------------------------------------------------> 
187<#if previewMode> 
188 
189        <script> 
190                var iframeContainer = jQuery('.modal-body-iframe', window.parent.document).parent().parent(); 
191                jQuery(iframeContainer).addClass("custom-preview-dialog"); 
192                jQuery(iframeContainer).css("width", "940px"); 
193                jQuery(iframeContainer).css("margin", "auto"); 
194                jQuery(iframeContainer).find("iframe").css("width", "900px"); 
195                jQuery('body', window.parent.document).addClass("custom-in-preview-mode"); 
196        </script> 
197</#if> 
198<article> 
199 
200    <#-- Show article url in preview mode --> 
201    <#if previewMode?? && previewMode> 
202	    <div><span>${articleCompleteUrl}</span></div> 
203    </#if> 
204 
205    <#-- Article image(s) --> 
206 
207    <#if articleImages?has_content> 
208        <#assign headerImages = getHeaderImages(articleImages) > 
209        <#assign contentCarouselImages = getContentCarouselImages(articleImages) > 
210    </#if> 
211 
212    <#if contentCarouselImages?has_content> 
213        <#if (contentCarouselImages?size > 1) > 
214            <#assign includeOwlScript = true> 
215        </#if> 
216    </#if> 
217     
218    <#if headerImages?has_content> 
219 
220        <#-- Container with bottom margin --> 
221        <div class="article-header-img"> 
222 
223            <#-- More than one image > create owl-carousel --> 
224            <#if (headerImages?size > 1) > 
225 
226                    <#list headerImages as image> 
227 
228            		    <#if image.getData()?? && image.getData()?has_content> 
229 
230                            <#-- Open carousel container on first image --> 
231                            <#if image?is_first> 
232                                <div class="owl-carousel owl-theme"> 
233 
234                                <#-- Load carousel script at the end of display template --> 
235                                <#assign includeOwlScript = true> 
236                            </#if> 
237 
238                            <#-- Output carousel slides --> 
239                            <div class="item"> 
240                                <#if image.getAttribute("alt")?has_content> 
241                                    <div class="aspect-ratio aspect-ratio-middle aspect-ratio-xs"> 
242                                        <img  data-fileentryid="${image.getAttribute("fileEntryId")}" class="aspect-ratio-item-fluid" src="${getImageURLByTemplateModel(image, '1200')}" alt="${image.getAttribute("alt")}" /> 
243                                    </div> 
244                                    <span class="caption">${image.getAttribute("alt")}</span> 
245                                <#else> 
246                                    <div class="aspect-ratio aspect-ratio-middle aspect-ratio-xs"> 
247                                        <img data-fileentryid="${image.getAttribute("fileEntryId")}"  class="aspect-ratio-item-fluid" src="${getImageURLByTemplateModel(image, '1200')}" alt="" /> 
248                                    </div> 
249                                </#if> 
250                            </div> 
251 
252                            <#-- Close carousel contaner after last item --> 
253                            <#if image?is_last> 
254                                </div> <#-- div.owl-carousel --> 
255                            </#if> 
256 
257            		    </#if> 
258            	    </#list> 
259 
260            <#else> 
261 
262            <#-- Display a single image without carousel --> 
263            <#list headerImages as image> 
264 
265    		    <#if image.getData()?? && image.getData()?has_content> 
266                     
267                    <#-- <@adaptive_media_image["img"] fileVersion=dlAppServiceUtil.getFileEntry(.getFileVersion())/>  --> 
268         
269                    <#if image.getAttribute("alt")?has_content> 
270            	        <img data-fileentryid="${image.getAttribute("fileEntryId")}" src="${getImageURLByTemplateModel(image, '1200')}" alt="${image.getAttribute("alt")}" /> 
271                        <p class="caption">${image.getAttribute("alt")}</p> 
272                    <#else> 
273            	        <img data-fileentryid="${image.getAttribute("fileEntryId")}" src="${getImageURLByTemplateModel(image, '1200')}" alt="" /> 
274                   </#if> 
275    		    </#if> 
276    	    </#list> 
277 
278            </#if> 
279 
280        </div> <#-- div.article-header-img --> 
281 
282    </#if> 
283 
284    <#-- R&S sharing buttons --> 
285    <div class="article-sharing"> 
286        <div class="rns-share-plugin"></div> 
287    </div> 
288 
289    <h1>${title}</h1> 
290 
291    <#if lead?? && lead?has_content> 
292        <#-- Remove possible HTML-tags --> 
293        <#assign leadCleaned = htmlUtil.stripHtml(lead) /> 
294        <p class="lead">${leadCleaned}</p> 
295    </#if> 
296 
297    <ul class="list-inline article-meta"> 
298    	<li class="list-inline-item"><span class="date">${displayDate?string["dd.MM.yyyy HH:mm"]}</span></li> 
299 
300    	<#if articleAuthors.authors.getData()?? && articleAuthors.authors.getData()?has_content> 
301            <#assign articleAuthor = articleAuthors.authors.getData()> 
302            <li class="list-inline-item"> 
303	      <span class="author authortext-no-margin author-text"></span> 
304	      <span class="author author-name">${makeAuthorLinks(articleAuthor,availableAuthorNames, authorCardBaseUrl)}</span> 
305            </li> 
306        </#if> 
307 
308    	<#if articleAuthors.photographers.getData()?? && articleAuthors.photographers.getData()?has_content> 
309        	<li class="list-inline-item"> 
310        	    <span class="author author-name author-photo"> 
311        	        ${makeAuthorLinks(articleAuthors.photographers.getData(),availableAuthorNames, authorCardBaseUrl)} 
312    	        </span> 
313        	</li> 
314    	</#if> 
315    </ul> 
316 
317    ${articleBody} 
318 
319    <#-- Output article quotes --> 
320    <#if articleQuotes?has_content && articleQuotes.getSiblings()?has_content> 
321     <@printRemainingTagContents articleQuotes quoteTagCounter wrapperCSSQuote /> 
322    </#if> 
323 
324    <#-- Print article factbox(es) --> 
325    <#if articleFacts?has_content && articleFacts.getSiblings()?has_content> 
326        <@printRemainingTagContents articleFacts faktaTagCounter wrapperCSSFakta /> 
327    </#if> 
328 
329    <#-- Print article infobox(es) --> 
330    <#if articleInfo?has_content && articleInfo.getSiblings()?has_content> 
331        <@printRemainingTagContents articleInfo infoTagCounter wrapperCSSInfo/> 
332    </#if> 
333 
334    <#-- Print article video(s) --> 
335    <#if articleVideos?has_content && articleVideos.getSiblings()?has_content> 
336        <@printRemainingVideoContents articleVideos videoTagCounter  /> 
337    </#if> 
338 
339    <#-- Print remaining audio embeds --> 
340    <#if articleAudio?has_content && articleAudio.getSiblings()?has_content> 
341        <@printRemainingAudioContents articleAudio audioTagCounter  /> 
342    </#if> 
343     
344    <#-- Print article tail(s) --> 
345    <#if articleTail?has_content && articleTail.getSiblings()?has_content> 
346        <@printRemainingTagContents articleTail kainaloTagCounter wrapperCSSKainalo /> 
347    </#if> 
348 
349 
350    <#-- 
351        Output author card for the main author. 
352    --> 
353 
354    <#-- <#if articleAuthors.mainAuthor.getData()?? && articleAuthors.mainAuthor.getData()?has_content> 
355        <#assign authorCard = articleAuthors.mainAuthor.getData()?eval /> 
356        <@liferay_ui["asset-display"] 
357			className=authorCard.className 
358			classPK=getterUtil.getLong(authorCard.classPK, 0) 
359			template="full_content" 
360	    /> 
361    </#if> --> 
362 
363    <#-- react&share buttons --> 
364    <div class="rns"></div> 
365    <script type="text/javascript"> 
366      (function() { 
367    'use strict'; 
368    var a=document.querySelector(".article-meta .date"), 
369        b=b?b.innerHTML:"", 
370        d=document.querySelector("article h1"), 
371        e=document.querySelector("a.author-name"); 
372    var rnsRecommend = function() { window.rnsRecommend() }; 
373    window.rnsData={ 
374        recommenderToggle: '.article-assets', 
375        apiKey:"oyrv5xd6dmwj3lkm", 
376        reactionCallback: rnsRecommend, 
377        maxRecommendations: 3, 
378        date:function(f){ 
379            try{ 
380                var a=f.match(/(\d{1,2})\.(\d{1,2})\.(\d{4})(\s+(\d{1,2}):(\d{2}))*/), 
381        c=new Date(Date.UTC.apply(this,a[4]?[a[3],a[2]-1,a[1],a[5],a[6]]:[a[3],a[2]-1,a[1]]));return c.setHours(c.getHours()-2),c.toISOString()}catch(g){return""}}(b),title:d?d.innerHTML:"",author:e?e.innerHTML:"", 
382    canonicalUrl:window.location.protocol+"//"+window.location.hostname+window.location.pathname}; 
383    b=document.createElement("script"); 
384    b.src="https://cdn.reactandshare.com/plugin/rns.js";document.body.appendChild(b); 
385    b=document.createElement("script"); 
386    b.src="https://cdn.reactandshare.com/recommender/rnsrw.js"; 
387    document.body.appendChild(b); 
388 
389})(); 
390    </script> 
391     
392    <#if articleTags?has_content> 
393        <div class="article-section article-tags"> 
394            <p class="sans text-uppercase">Lis&auml;&auml; n&auml;ist&auml; aiheista:</p> 
395            <ul class="list-inline"> 
396                <#list articleTags?split(",") as tag> 
397                    <#if !tag?starts_with("_")> 
398                        <li class="list-inline-item"><a href="/artikkelit/-/tag/${htmlUtil.escapeURL(tag)}?article=${article.getResourcePrimKey()}" class="tag tag-default">${tag}</a></li> 
399                    </#if> 
400                </#list> 
401            </ul> 
402        </div> 
403    </#if> 
404 
405    <#-- R&S sharing buttons --> 
406    <div class="article-sharing"> 
407        <p class="sans text-uppercase">Jaa t&auml;m&auml; artikkeli:</p> 
408        <div class="rns-share-plugin"></div> 
409    </div> 
410 
411    <#-- MORE FROM AUTHOR --> 
412 
413    <#assign hasAuthor = articleAuthors.authors.getData()?? && articleAuthors.authors.getData()?has_content /> 
414    <#assign hasPhotographer = articleAuthors.photographers.getData()?? && articleAuthors.photographers.getData()?has_content /> 
415     
416    <#if hasAuthor || hasPhotographer> 
417        <#assign articleAuthor = articleAuthors.authors.getData()> 
418        <#assign articlePhotographer = articleAuthors.photographers.getData() /> 
419         
420        <#assign authorObjs = [] /> 
421 
422        <#-- Find which authors (author card titles) appears in author field and make list of those... 
423        We need no maintain order of appearance 
424        --> 
425        <#list availableAuthorNames as authorName> 
426            <#assign authorPos = articleAuthor?index_of(authorName) /> 
427            <#assign photographerPos = articlePhotographer?index_of(authorName) /> 
428             
429            <#-- Keep the order... first authors then photographers --> 
430            <#if authorPos != -1 || photographerPos != -1> 
431                <#assign pos = authorPos /> 
432                <#if authorPos == -1 && photographerPos != -1> 
433                    <#assign pos = 1000 + photographerPos /> 
434                </#if> 
435                <#assign authorObj = {"pos" : pos, "name": authorName} /> 
436                <#assign authorObjs = authorObjs + [authorObj] /> 
437            </#if> 
438        </#list> 
439 
440        <#if authorObjs?size gt 0> 
441            <#assign authorObjs = authorObjs?sort_by("pos") /> 
442 
443            <div class="article-tags"> 
444                <p class="sans text-uppercase">Lis&auml;&auml; tekij&auml;lt&auml;:</p> 
445                <ul class="list-inline"> 
446                    <#list authorObjs as authorObj> 
447                        <li class="list-inline-item"> 
448                            <#assign authorSearchURL = "/toimittajat?author=" /> 
449                            <a class="tag tag-default" href="${authorCardBaseUrl}${authorObj.name?url}">${authorObj.name}</a> 
450                        </li>		   
451                    </#list> 
452                </ul> 
453            </div> 
454        </#if> 
455 
456    </#if> 
457 
458 
459    <#-- RELATED CONTENT -->  
460    <#assign assetLinkLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetLinkLocalService") /> 
461    <#assign assetEntryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetEntryLocalService") /> 
462    <#assign currentArticleResourcePrimKey = currentArticle.getResourcePrimKey() /> 
463    <#assign currentArticleAssetEntry = assetEntryLocalService.getEntry("com.liferay.journal.model.JournalArticle", currentArticleResourcePrimKey) /> 
464    <#assign currentArticleAssetEntryId = currentArticleAssetEntry.getEntryId() /> 
465    <#assign currentArticleRelatedLinks = assetLinkLocalService.getDirectLinks(currentArticleAssetEntryId) /> 
466    <#if currentArticleRelatedLinks?has_content> 
467        <#assign relatedLinks = []> 
468        <#list currentArticleRelatedLinks as related_entry> 
469            <#assign relatedAssetEntryId = related_entry.getEntryId2() /> 
470            <#assign relatedAssetEntry = assetEntryLocalService.getEntry(relatedAssetEntryId) /> 
471            <#assign relatedAssetEntryPrimKey = relatedAssetEntry.getClassPK() /> 
472            <#assign relatedArticle = JournalArticleLocalService.getLatestArticle(relatedAssetEntryPrimKey) /> 
473            <#assign relatedArticleId = relatedArticle.getArticleId() /> 
474            <#assign docXml = saxReaderUtil.read(relatedArticle.getContent()) />	 
475            <#assign image = docXml.valueOf("//dynamic-element[@name='articleImages']/dynamic-content/text()") /> 
476     
477            <#assign linkObject = {"title": relatedArticle.getTitleCurrentValue(),"urlTitle": relatedArticle.getUrlTitle(), "date": relatedArticle.getDisplayDate()?date, "urlImage": image } /> 
478             
479            <#if image?? && image?has_content> 
480 
481            <#else> 
482                <#assign nostokuva = docXml.valueOf("//dynamic-element[@name='nostokuva']/dynamic-content/text()") /> 
483                <#if nostokuva?? && nostokuva?has_content> 
484                    <#assign image = nostokuva /> 
485                    <#assign linkObject = {"title": relatedArticle.getTitleCurrentValue(),"urlTitle": relatedArticle.getUrlTitle(), "date": relatedArticle.getDisplayDate()?date, "urlImage": image } /> 
486                </#if> 
487            </#if> 
488             
489            <#assign relatedLinks += [linkObject] /> 
490        </#list> 
491        <#if relatedLinks?has_content> 
492 
493            <div class="article-assets"> 
494                <h2 class="heading-decor-section">Lue lis&auml;&auml;:</h2> 
495                 
496                <#assign relatedLinksSorted = relatedLinks?sort_by("date") /> 
497                <#list relatedLinksSorted?reverse as link> 
498 
499                    <div class="media"> 
500                        <div class="media-left"> 
501                            <a href="/-/${link.urlTitle}" class="sans"> 
502 
503                                <#if link.urlImage?? && link.urlImage?has_content > 
504                                    <#assign imageJSON = jsonFactoryUtil.createJSONObject(link.urlImage)> 
505                                    <#assign fileEntryId = imageJSON.getString("fileEntryId")> 
506                                    <#assign fileName = imageJSON.getString("name")> 
507                                    <img src="${getImageURL(imageJSON, '600')}" class="media-object" /> 
508                                     
509                                <#else> 
510                                    <div class="aspect-ratio aspect-ratio-top aspect-ratio-3-to-2"> 
511                                        <div class="placeholder placeholder-colored"></div>  
512                                    </div> 
513                                </#if> 
514 
515                            </a> 
516                        </div> 
517                        <div class="media-body"> 
518                            <a href="/-/${link.urlTitle}" class="sans">${link.title}</a> 
519                            <span class="date">${link.date}</span> 
520                        </div> 
521                    </div> 
522                </#list> 
523 
524            </div> 
525 
526        </#if> 
527    </#if> 
528 
529    <script src="${javascript_folder}/audioplayer.js"></script> 
530 
531    <#--  NEWSLETTER SUBSCRIPTION BANNER  --> 
532    <div class="article-subscription"> 
533        <div class="visible-md visible-lg"> 
534            <div class="banner-category-d"><h2 class="no-top-margin" style="margin-bottom: 30px;">Tilaa Kirkko ja kaupungin viikoittainen juttukooste</h2> 
535                <form class="form-inline" action="https://kirkkojakaupunki.creamailer.fi/tilaa/59d75acf78807" id="subForm" method="post"> 
536                    <input id="redirect" name="redirect" type="hidden" value="https://kirkkojakaupunki-dev.ch5finland.com/"> 
537                    <input class="form-control" id="S&auml;hk&ouml;postiosoite" name="userEmail" placeholder="S&auml;hk&ouml;postiosoite"> 
538                    <input id="newsletter-submit-article" class="btn btn-inverse" type="submit" value="Tilaa uutiskirje"> 
539                </form>  
540            </div> 
541        </div> 
542    </div> 
543 
544    <#-- FACEBOOK COMMENTS --> 
545    <#if articleComments?? && getterUtil.getBoolean(articleComments.getData()) == true> 
546        <div id="fb-root"></div> 
547        <script> 
548            (function(d, s, id) { 
549                var js, fjs = d.getElementsByTagName(s)[0]; 
550                if (d.getElementById(id)) return; 
551                js = d.createElement(s); js.id = id; 
552                js.src = "//connect.facebook.net/fi_FI/sdk.js#xfbml=1&version=v2.8&appId=206864833121117"; 
553                fjs.parentNode.insertBefore(js, fjs); 
554            }(document, 'script', 'facebook-jssdk')); 
555        </script> 
556 
557        <h2 class="heading-decor-section">Kommentoi</h2> 
558        <div class="fb-comments" data-href="${articleCompleteUrl}" data-width="725" data-numposts="5"></div> 
559    </#if> 
560 
561</article> 
562 
563 
564<#-- Include script only if owl carousel among content has been built --> 
565 
566<script src="${javascript_folder}/owl.carousel.min.js"></script> 
567 
568<#if includeOwlScript == true> 
569    <script> 
570    	$('.owl-carousel').owlCarousel({ 
571            items: 1, 
572    		loop: true, 
573    		rewind: false, 
574    		margin: 0, 
575    		nav: true, 
576            navText : ['<span class="owl-carousel-nav glyphicon glyphicon-chevron-left"></span>','<span class="owl-carousel-nav glyphicon glyphicon-chevron-right"></span>'], 
577            autoplay:true, 
578            autoplayTimeout:3000, 
579            smartSpeed:1400, 
580            autoplayHoverPause:true 
581    	}); 
582 
583    </script> 
584</#if> 
585 
586<#-- 
587    Convert Youtube links to embedded videos 
588--> 
589<script type="text/javascript"> 
590 
591    $(document).ready(function () { 
592        $(".js-video-embed" ).each(function() { 
593            // Creates an iframe if it's youtube video. Otherwise just shows the original link. 
594            if (getYoutubeVideoId($(this).attr("href"))) { 
595                var div = $("<div>", {"class": "responsive-video"}); 
596                var wrapper = $("<div>", {"class": "responsive-wrapper"}); 
597                wrapper.append('<iframe src="https://www.youtube.com/embed/'+getYoutubeVideoId($(this).attr("href"))+'?rel=0" frameborder="0" allowfullscreen=""></iframe>'); 
598                div.append(wrapper); 
599                $(this).parent().append(div); 
600                $(this).hide(); //iframe is shown, hide the original link 
601
602        }); 
603    }); 
604    function getYoutubeVideoId(url) { 
605        var videoid = url.match(/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/); 
606        if(videoid != null) { 
607           return videoid[1]; 
608        } else { 
609            return ""; 
610
611
612</script> 
613 
614<#--   
615    Create image comparison slider   
616--> 
617<script src="/o/kirkkojakaupunki-site-theme/js/cocoen.js"></script> 
618 
619<script type="text/javascript"> 
620    $(document).ready(function () { 
621        document.querySelectorAll('.cocoen').forEach(function(element){ 
622            var imageComparison = new Cocoen(element); 
623                var sliderOffset = $(element).attr("slider-offset"); 
624                if ($(element).attr("slider-offset") >= 100 || !($(element).attr("slider-offset"))){ 
625                    sliderOffset = 98; 
626                } else if ($(element).attr("slider-offset") == 0) { 
627                    sliderOffset = 2; 
628
629                imageComparison.element.children[0].style.width = sliderOffset+"%"; 
630                imageComparison.dragElement.style.left = sliderOffset+"%"; 
631        }); 
632 
633        // depending on slider offset show either the caption of the first or the second image 
634        $('.cocoen').mousemove(function(event){ 
635            addCaption(this); 
636        }); 
637 
638        // same functionality for touch screens 
639        document.querySelectorAll('.cocoen').forEach(function(element){ 
640            element.addEventListener("touchmove", function() { 
641                addCaption(this); 
642            }); 
643        }); 
644 
645        function addCaption(element) { 
646            var index = $('.cocoen').index(element); 
647            var sliderOffset = parseInt(element.firstElementChild.style.width); 
648            if(sliderOffset < 50){ 
649                $('.caption1').eq(index).hide(); 
650                $('.caption2').eq(index).show(); 
651            } else { 
652                $('.caption2').eq(index).hide(); 
653                $('.caption1').eq(index).show(); 
654
655
656    });     
657     
658</script> 
659 
660 
661<#----------------------------------------------------------------------------- 
662    Helper functions 
663------------------------------------------------------------------------------> 
664 
665<#macro printRemainingTagContents container startAt wrapperCSSClass> 
666    <#local contentSlots = container.getSiblings() /> 
667    <#if contentSlots?has_content && contentSlots?size gt 0> 
668        <#list contentSlots as slot> 
669            <#if slot?index gte startAt> 
670            <#if slot.getData()?? && slot.getData()?has_content> 
671        	    <#switch wrapperCSSClass> 
672        	        <#case 'template-quote'> 
673        	            <div class="article-box article-quote"> 
674        	                <p class="quote-body">${slot.getData()}</p> 
675        	            </div> 
676                	    <#break> 
677            	    <#default> 
678            	        <div class="${wrapperCSSClass}">${slot.getData()}</div> 
679        	    </#switch> 
680            </#if> 
681            </#if> 
682        </#list> 
683    </#if> 
684</#macro> 
685 
686 
687<#-- 
688    Print remainging videos, if any 
689--> 
690<#macro printRemainingVideoContents container startAt> 
691    <#local contentSlots = container.getSiblings() /> 
692    <#if contentSlots?has_content && contentSlots?size gt 0> 
693        <#list contentSlots as slot> 
694            <#if slot?index gte startAt> 
695                <#if slot.URL.getData()?? && slot.URL.getData()?has_content> 
696 
697                    <#assign URL = slot.URL.getData()!"" /> 
698                    <#assign videoFile = slot.videoFile.getData()!"" /> 
699                    <#assign caption = slot.caption.getData()!"" /> 
700                    <#assign embed = videoEmbed(URL, caption, videoFile)!"" /> 
701                    ${embed} 
702 
703                </#if> 
704            </#if> 
705        </#list> 
706    </#if> 
707</#macro> 
708 
709<#-- 
710    Print remainging audio embeds, if any 
711--> 
712<#macro printRemainingAudioContents container startAt> 
713    <#local contentSlots = container.getSiblings() /> 
714    <#if contentSlots?has_content && contentSlots?size gt 0> 
715        <#list contentSlots as slot> 
716            <#if slot?index gte startAt> 
717                <#if slot.audioFile.getData()?? && slot.audioFile.getData()?has_content> 
718                    <#assign description = slot.audioDescription.getData() /> 
719                    <#assign files = slot.audioFile /> 
720                    <#assign embed = audioEmbed(description, files) /> 
721                    ${embed} 
722                </#if> 
723            </#if> 
724        </#list> 
725    </#if> 
726</#macro> 
727 
728<#function replaceTags content tag replacementElement wrapperCSSClass> 
729    <#global tagReplaceCounter = 0 /> 
730    <#if replacementElement?has_content> 
731        <#assign replacementList = replacementElement.getSiblings() > 
732        <#if replacementList?has_content> 
733 
734        	<#list replacementList as cur_replacement> 
735 
736        	    <#switch wrapperCSSClass> 
737        	        <#case 'template-quote'> 
738                	    <#assign replacement> 
739                	        <div class="article-box article-quote"> 
740                	            <p class="quote-body">${cur_replacement.getData()}</p> 
741            	            </div> 
742                	    </#assign> 
743                	    <#break> 
744            	    <#default> 
745                	    <#assign replacement> 
746                	        <div class="${wrapperCSSClass}">${cur_replacement.getData()}</div> 
747                	    </#assign> 
748        	    </#switch> 
749 
750        	    <#if content?index_of(tag) gt -1> 
751            		<#local content = content?replace(tag, replacement, 'f') > 
752            		<#global tagReplaceCounter++ /> 
753        		</#if> 
754        	</#list> 
755        </#if> 
756    </#if> 
757 
758  <#-- Remove "empty" tags--> 
759  <#local content = content?replace(tag, '') > 
760 
761  <#return content > 
762</#function> 
763 
764<#function replaceCarouselTags content tag1 tag2 replacementElement> 
765    <#if replacementElement?has_content> 
766        <#assign replacementList = replacementElement.getSiblings()> 
767        <#assign carousel1Array = []> 
768        <#assign carousel2Array = []> 
769        <#if replacementList?has_content> 
770        	<#list replacementList as cur_image> 
771                <#if cur_image.getData?? && cur_image.getData()?has_content> 
772                    <#if cur_image.position.getData() == "article-image-carousel-1"> 
773                        <#assign carousel1Array = carousel1Array + [createCarouselItem(cur_image)] /> 
774                    </#if> 
775                    <#if cur_image.position.getData() == "article-image-carousel-2"> 
776                        <#assign carousel2Array = carousel2Array + [createCarouselItem(cur_image)] /> 
777                    </#if> 
778                </#if> 
779            </#list> 
780 
781            <#assign replacement1> 
782                <div class="owl-carousel owl-theme"> 
783                    <#list carousel1Array as carousel1> 
784                    <div class="item"> 
785                        ${carousel1} 
786                    </div> 
787                    </#list> 
788                </div> 
789            </#assign> 
790            <#local content = content?replace(tag1, replacement1, 'f') > 
791 
792            <#assign replacement2> 
793                <div class="owl-carousel owl-theme"> 
794                    <#list carousel2Array as carousel2> 
795                    <div class="item"> 
796                        ${carousel2} 
797                    </div> 
798                    </#list> 
799                </div> 
800            </#assign> 
801            <#local content = content?replace(tag2, replacement2, 'f') > 
802 
803        </#if> 
804    </#if> 
805    <#return content /> 
806</#function> 
807 
808<#function createCarouselItem cur_image> 
809    <#assign result = "" /> 
810    <#if cur_image.getAttribute("alt")?has_content> 
811        <#assign result> 
812            <div class="aspect-ratio aspect-ratio-middle aspect-ratio-xs"> 
813                <img class="aspect-ratio-item-fluid" src="${getImageURLByTemplateModel(cur_image, '1200')}" alt="${cur_image.getAttribute("alt")}" /> 
814            </div> 
815            <p class="caption">${cur_image.getAttribute("alt")}</p> 
816        </#assign> 
817    <#else> 
818        <#assign result> 
819            <div class="aspect-ratio aspect-ratio-middle aspect-ratio-xs"> 
820                <img class="aspect-ratio-item-fluid" src="${getImageURLByTemplateModel(cur_image, '1200')}" alt="" /> 
821            </div> 
822        </#assign> 
823    </#if> 
824    <#return result /> 
825</#function> 
826 
827<#function replaceImageTags content tag replacementElement> 
828    <#if replacementElement?has_content> 
829        <#assign replacementList = replacementElement.getSiblings() > 
830        <#if replacementList?has_content> 
831        	<#list replacementList as cur_image> 
832 
833                <#if cur_image.getData?? && cur_image.getData()?has_content> 
834 
835                    <#-- Skip header images --> 
836                    <#if cur_image.position.getData() != "article-image-header" && cur_image.position.getData() != "article-image-carousel-1" && cur_image.position.getData() != "article-image-carousel-2"> 
837                        <#assign thumbnailSize = 2 > 
838                        <#if cur_image.position.getData() == "article-image-full"> 
839                            <#assign thumbnailSize = 3 > 
840                        </#if> 
841                        <#assign replacement> 
842 
843                            <div class="article-box ${cur_image.position.getData()}"> 
844                                <#if cur_image.getAttribute("alt")?has_content> 
845                                    <img data-fileentryid="${cur_image.getAttribute("fileEntryId")}"  src="${getImageURLByTemplateModel(cur_image, '1200')}" alt="${cur_image.getAttribute("alt")}" /> 
846                                    <p class="caption">${cur_image.getAttribute("alt")}</p> 
847                                <#else> 
848                                    <img data-fileentryid="${cur_image.getAttribute("fileEntryId")}"  src="${getImageURLByTemplateModel(cur_image, '1200')}" alt="" /> 
849                                </#if> 
850                            </div> 
851                        </#assign> 
852                        <#local content = content?replace(tag, replacement, 'f') > 
853                    </#if> 
854 
855                </#if> 
856 
857        	</#list> 
858        </#if> 
859    </#if> 
860 
861  <#-- Remove "empty" tags--> 
862  <#local content = content?replace(tag, '') > 
863 
864  <#return content > 
865</#function> 
866 
867 
868<#function replaceImageComparisonTags content tag replacementElement> 
869    <#if replacementElement?has_content> 
870        <#assign replacementList = replacementElement.getSiblings() > 
871        <#if replacementList?has_content> 
872            <#list replacementList as cur_imageComparison> 
873                <#if cur_imageComparison.getData??> 
874                    <#assign image1 = cur_imageComparison.image1 /> 
875                    <#assign image2 = cur_imageComparison.image2 /> 
876                    <#if cur_imageComparison.sliderOffset.getData() == "" > 
877                        <#assign offset = 100 /> 
878                    <#else> 
879                        <#assign offset = cur_imageComparison.sliderOffset.getData() /> 
880                    </#if> 
881                    <#assign replacement> 
882                        <#if image1.getData?? && image1.getData()?has_content && image2.getData?? && image2.getData()?has_content> 
883                            <div class="cocoen-theme"> 
884                                <div class="cocoen" slider-offset="${offset}"> 
885                                    <#if image1.getAttribute("alt")?has_content> 
886                                        <img src="${getThumbnailUrl(image1.getData(), 3) }" alt="${image1.getAttribute("alt")}" /> 
887                                    <#else> 
888                                        <img src="${getThumbnailUrl(image1.getData(), 3) }" alt="" /> 
889                                    </#if> 
890                                    <#if image2.getAttribute("alt")?has_content> 
891                                        <img src="${getThumbnailUrl(image2.getData(), 3) }" alt="${image2.getAttribute("alt")}" /> 
892                                    <#else> 
893                                        <img src="${getThumbnailUrl(image2.getData(), 3) }" alt="" /> 
894                                    </#if> 
895                                </div> 
896                                <#if image1.getAttribute("alt")?has_content && image2.getAttribute("alt")?has_content> 
897                                    <#if offset?number lt 50> <!-- pienempi kuin 50, n&auml;yt&auml; kuvateksti 2 --> 
898                                        <div class="caption2"> 
899                                            <p class="caption"> 
900                                                ${image2.getAttribute("alt")} 
901                                            </p> 
902                                        </div> 
903                                        <div class="caption1" style="display:none"> 
904                                            <p class="caption"> 
905                                                ${image1.getAttribute("alt")} 
906                                            </p> 
907                                        </div> 
908                                    <#else> 
909                                        <div class="caption2" style="display:none"> 
910                                            <p class="caption"> 
911                                                ${image2.getAttribute("alt")} 
912                                            </p> 
913                                        </div> 
914                                        <div class="caption1"> 
915                                            <p class="caption"> 
916                                                ${image1.getAttribute("alt")} 
917                                            </p> 
918                                        </div> 
919                                    </#if> 
920                                </#if> 
921                            </div> 
922                        </#if> 
923                    </#assign> 
924                    <#local content = content?replace(tag, replacement, 'f') > 
925                </#if> 
926            </#list> 
927        </#if> 
928    </#if> 
929    <#-- Remove "empty" tags--> 
930    <#local content = content?replace(tag, '') > 
931 
932    <#return content > 
933</#function> 
934 
935 
936<#function replaceVideoTags content tag replacementElement> 
937    <#global tagReplaceCounter = 0 /> 
938    <#if replacementElement?has_content> 
939        <#assign replacementList = replacementElement.getSiblings() > 
940        <#if replacementList?has_content> 
941 
942            <#global videoListSize = replacementList?size /> 
943 
944        	<#list replacementList as cur_video> 
945                <#assign videoFile = "" /> 
946                <#assign URL = cur_video.URL.getData()!"" /> 
947                <#if (cur_video.videoFile)??> 
948                    <#assign videoFile = cur_video.videoFile.getData()!"" /> 
949                </#if> 
950                <#assign caption = cur_video.caption.getData()!"" /> 
951                 
952                <#if (cur_video.videoFile)?? || URL?has_content> 
953                    <#assign replacement = videoEmbed(URL, caption, videoFile)!"" /> 
954                </#if> 
955                
956                <#if content?index_of(tag) gt -1> 
957                    <#local content = content?replace(tag, replacement, 'f') > 
958                    <#global tagReplaceCounter++ /> 
959                </#if> 
960 
961                <#-- <#local content = content?replace(tag, replacement, 'f') > --> 
962        	</#list> 
963        </#if> 
964    </#if> 
965 
966  <#-- Remove "empty" tags--> 
967  <#local content = content?replace(tag, '') > 
968 
969  <#return content > 
970</#function> 
971 
972 
973<#function replaceAudioTags content tag replacementElement> 
974    <#global tagReplaceCounter = 0 /> 
975    <#if replacementElement?has_content> 
976        <#assign replacementList = replacementElement.getSiblings() > 
977        <#if replacementList?has_content> 
978 
979            <#global audioListSize = replacementList?size /> 
980 
981        	<#list replacementList as cur_audioEmbed> 
982                <#assign description = cur_audioEmbed.audioDescription.getData() /> 
983                <#assign files = cur_audioEmbed.audioFile /> 
984                <#assign replacement = audioEmbed(description, files) /> 
985 
986                <#if content?index_of(tag) gt -1> 
987                    <#local content = content?replace(tag, replacement, 'f') > 
988                    <#global tagReplaceCounter++ /> 
989                </#if> 
990 
991        	</#list> 
992        </#if> 
993    </#if> 
994    <#local content = content?replace(tag, '') > 
995    <#return content > 
996</#function> 
997 
998<#function replaceAdTag content> 
999    <#local adScript> 
1000    	<div class="ad-container"> 
1001            <div> 
1002                <script type="text/javascript"> 
1003 
1004                if((window.innerWidth > 1019)) 
1005
1006                // NM 468x400 
1007                document.write('<scri'+'pt data-adfscript="adx.adform.net/adx/?mid=937873&mkv=" id="ar"></'+'script>'); 
1008                document.write('<scri'+'pt src="//s1.adform.net/banners/scripts/adx.js" async defer ></'+'script>'); 
1009
1010                else 
1011
1012                // NM 300x300 3 
1013                document.write('<scri'+'pt data-adfscript="adx.adform.net/adx/?mid=937882&mkv="></'+'script>'); 
1014                document.write('<scri'+'pt src="//s1.adform.net/banners/scripts/adx.js" async defer></'+'script>'); 
1015
1016                </script> 
1017            </div> 
1018        </div> 
1019    </#local> 
1020     
1021    <#return content?replace("[[mainos]]", adScript) /> 
1022</#function> 
1023 
1024<#function videoEmbed URL, caption, videoFile> 
1025    <#local embed = "" /> 
1026 
1027    <#if URL?contains("vimeo")> 
1028        <#local iframeSrc = "https://player.vimeo.com/video/" /> 
1029        <#local videoID = URL?split("/")?last /> 
1030		<#local embed> 
1031		    <div class="responsive-video"> 
1032    			<div class="responsive-wrapper vimeo" id="vimeo-${videoID}" data-embed="${videoID}"> 
1033    				<img class="decor-video" alt="Toista video" src="/o/kirkkojakaupunki-site-theme/images/icons/video-decor.svg"> 
1034    			</div> 
1035				<noscript><a href="${URL}" target="_blank" rel="noopener"Videota</a> ei voida n&auml;ytt&auml;&auml; koska javascript on kytketty pois p&auml;&auml;lt&auml; selaimesta.</noscript> 
1036    		</div> 
1037		</#local> 
1038    <#elseif URL?contains("youtube")> 
1039        <#local iframeSrc = "https://www.youtube.com/embed/" /> 
1040        <#local videoID = URL?replace("^.*\\?v=([\\w-_]*).*", "$1", "r") /> 
1041        <#--<#local videoID = videoID + "?rel=0" />--> 
1042		<#local embed> 
1043		    <div class="responsive-video"> 
1044    			<div class="responsive-wrapper youtube" data-embed="${videoID}"> 
1045    				<img class="decor-video" alt="Toista video" src="/o/kirkkojakaupunki-site-theme/images/icons/video-decor.svg"> 
1046    			</div> 
1047    			<noscript><a href="${URL}" target="_blank" rel="noopener">Videota</a> ei voida n&auml;ytt&auml;&auml; koska javascript on kytketty pois p&auml;&auml;lt&auml; selaimesta.</noscript> 
1048			</div> 
1049		</#local> 
1050    <#elseif videoFile?has_content> 
1051        <#local embed> 
1052            <div class="responsive-video"> 
1053                <video controls controlsList="nodownload" style="max-width:100%"> 
1054                <source src="${videoFile}" type="video/mp4"> 
1055                    Your browser does not support the video tag. 
1056                </video> 
1057            </div>  
1058        </#local> 
1059    <#else> 
1060        <#return embed> 
1061    </#if> 
1062 
1063    <#return embed> 
1064</#function> 
1065 
1066 
1067<#function audioEmbed description, files> 
1068    <#local embed = ""> 
1069    <#local audioFiles = [] /> 
1070     
1071    <#list files.getSiblings() as file>        
1072        <#local fileURL = file.getData()!""> 
1073        <#if fileURL?has_content> 
1074            <#local fileFormat = file.audioFormat.getData()> 
1075            <#local fileObject = {"format": fileFormat, "URL": fileURL}> 
1076            <#local audioFiles += [fileObject]> 
1077        </#if> 
1078    </#list> 
1079 
1080    <#if audioFiles?has_content> 
1081        <#local embed> 
1082            <div class="audio-player sans"> 
1083                <button class="btn btn-link flex-item-center"> 
1084                    <i class="icon-play" aria-hidden="true"></i> 
1085                </button> 
1086                <div class="audio-player-details flex-item-center"> 
1087                    <#if description?has_content> 
1088                        <p>${description}</p> 
1089                    </#if> 
1090                    <div class="audio-player-progress"> 
1091                        <span class="audio-player-time-display">-:- / -:-</span> 
1092                        <div class="audio-player-seekbar"> 
1093                            <div class="audio-player-bufferbar"></div> 
1094                            <div class="audio-player-progressbar"></div> 
1095                        </div> 
1096                    </div>                 
1097                </div> 
1098                <audio preload="metadata" loop> 
1099                    <#list audioFiles as file> 
1100                        <source src="${file.URL}" type="${file.format}" /> 
1101                    </#list> 
1102                </audio> 
1103            </div> 
1104        </#local> 
1105    </#if> 
1106 
1107    <#return embed> 
1108</#function> 
1109 
1110 
1111<#function getHeaderImages articleImages> 
1112    <#local headerImages = [] /> 
1113 
1114    <#if articleImages?has_content && articleImages.getSiblings()?has_content> 
1115        <#list articleImages.getSiblings() as cur_image> 
1116            <#if cur_image.position.getData() == 'article-image-header'> 
1117                <#local headerImages = headerImages + [cur_image] /> 
1118            </#if> 
1119        </#list> 
1120    </#if> 
1121 
1122    <#if !headerImages?has_content && articleImages?has_content && articleImages.getSiblings()?has_content > 
1123        <#local headerImages = headerImages + [articleImages.getSiblings()?first] /> 
1124    </#if> 
1125 
1126    <#return headerImages> 
1127</#function> 
1128 
1129<#function getContentCarouselImages articleImages> 
1130 
1131    <#local contentCarouselImages = [] /> 
1132 
1133    <#if articleImages?has_content && articleImages.getSiblings()?has_content> 
1134        <#list articleImages.getSiblings() as cur_image> 
1135            <#if cur_image.position.getData() == 'article-image-carousel-1'> 
1136                <#local contentCarouselImages = contentCarouselImages + [cur_image] /> 
1137            </#if> 
1138            <#if cur_image.position.getData() == 'article-image-carousel-2'> 
1139                <#local contentCarouselImages = contentCarouselImages + [cur_image] /> 
1140            </#if> 
1141        </#list> 
1142    </#if> 
1143 
1144    <#if !contentCarouselImages?has_content && articleImages?has_content && articleImages.getSiblings()?has_content > 
1145        <#local contentCarouselImages = contentCarouselImages + [articleImages.getSiblings()?first] /> 
1146    </#if> 
1147 
1148    <#return contentCarouselImages> 
1149</#function> 
1150 
1151<#-- 
1152    Attempts to get mime type for a file in document library. 
1153 
1154    Parameters: 
1155        fileUrl: A string containig a url to the file. 
1156 
1157    Returns mime type as a string or an empty string. 
1158--> 
1159<#function getMimeType fileUrl> 
1160    <#if fileUrl?? && fileUrl?has_content> 
1161 
1162        <#-- Attempt to parse uuid & groupId from provided Url --> 
1163        <#assign splitUrl = fileUrl?split("/", "r") /> 
1164 
1165        <#if splitUrl?seq_contains("documents") && splitUrl?size gte 6> 
1166            <#attempt> 
1167 
1168                <#assign groupId = splitUrl[2] /> 
1169                <#assign uuid = splitUrl[5] /> 
1170                <#assign uuid = uuid?split("?")[0] /> 
1171 
1172                <#-- Get DLFileEntry --> 
1173                <#assign DLFileEntryLocalService = serviceLocator.findService("com.liferay.document.library.kernel.service.DLFileEntryLocalService") /> 
1174                <#assign fileEntry = DLFileEntryLocalService.fetchDLFileEntryByUuidAndGroupId(uuid, getterUtil.getLong(groupId)) /> 
1175                <#return fileEntry.getMimeType()> 
1176            <#recover> 
1177                <#-- Parsing mime type failed, return empty --> 
1178                <#return ""> 
1179            </#attempt> 
1180 
1181        <#elseif splitUrl?seq_contains("journal") && splitUrl?size gte 4> 
1182            <#attempt> 
1183                <#assign imageId = splitUrl[3]?split("?")[1]?split("=")[1]?split("&")[0] /> 
1184                <#assign ImageLocalService = serviceLocator.findService("com.liferay.portal.kernel.service.ImageLocalService") /> 
1185                <#assign imageType = ImageLocalService.getImage(getterUtil.getLong(imageId)).getType() /> 
1186                <#switch imageType> 
1187                    <#case "gif"> 
1188                        <#return "image/gif"> 
1189                    <#case "svg"> 
1190                        <#return "image/svg"> 
1191                    <#default> 
1192                        <#return imageType> 
1193                </#switch> 
1194            <#recover> 
1195                <#return ""> 
1196            </#attempt> 
1197 
1198        <#else> 
1199            <#-- Parsing image url failed, return empty --> 
1200            <#return ""> 
1201        </#if> 
1202 
1203    <#else> 
1204        <#-- parameter empty or missing, return empty --> 
1205        <#return ""> 
1206    </#if> 
1207</#function> 
1208 
1209<#-- 
1210    Returns thumbnail image url for given image. 
1211    Handles SVG & bitmap formats. 
1212 
1213    Parameters: 
1214        imageUrl: string containing relative url to an image in document library 
1215        thumbnailSize: value for Liferay's imageThumbnail parameter (0-3) 
1216 
1217    Returns image url with appended thumbnail info or an empty string. 
1218 --> 
1219<#function getThumbnailUrl imageUrl thumbnailSize> 
1220    <#if imageUrl?? && imageUrl?has_content> 
1221 
1222        <#-- Attempt to get mime type --> 
1223        <#assign mimeType = getMimeType(imageUrl) /> 
1224 
1225        <#if mimeType?has_content> 
1226 
1227            <#-- Check image mime type --> 
1228            <#switch mimeType> 
1229                <#case "image/svg+xml"> 
1230                    <#-- SVG image -> do not append thumbnail parameter --> 
1231                    <#return imageUrl> 
1232                <#case "image/gif"> 
1233                    <#return imageUrl> 
1234                <#default> 
1235                    <#-- Predume bitmap image -> append thumbnail parameter --> 
1236                    <#if imageUrl?contains("?")> 
1237                        <#assign paramChar = "&" /> 
1238                    <#else> 
1239                        <#assign paramChar = "?" /> 
1240                    </#if> 
1241<#-- 
1242    Liferay 7.3 thumbSize 3 on pienempi kuin aiemmin. Woodwingist tulevat kuvat nykyisin 1200 joten voidaan palauttaa suoraan kuvan url. Vaihtoehto hyodyntaa adaptive mediaa 
1243--> 
1244                    <#if thumbnailSize == 3> 
1245                        <#return imageUrl /> 
1246                    <#else> 
1247                        <#return imageUrl + paramChar + "imageThumbnail=" + thumbnailSize /> 
1248                    </#if> 
1249            </#switch> 
1250        <#else> 
1251            <#-- Embbedded journal article image (type: journal) mime recognizion fails for ie  
1252            /-/sini-mikkola-tutkii-lutherin-kasityksia 
1253            /-/kristinuskon-alkeita-ja-syventavia-keskusteluja 
1254            /-/mika-keisarin-on 
1255            --> 
1256            <#return imageUrl > 
1257        </#if> 
1258 
1259    </#if> 
1260</#function> 
1261 
1262<#-- 
1263    Return list of web content titles from given folder 
1264    parameter: 
1265        - authorCardFolderId 
1266--> 
1267<#function getAvailableAuthorNames authorCardFolderIds > 
1268 
1269  <#local authorNames = []>     
1270  <#local seenArticles = [] > 
1271  <#-- Multiple version exists so keep track which articles we have already checked --> 
1272 
1273  <#list authorCardFolderIds as authorCardFolderId> 
1274    <#assign authorCards = JournalArticleLocalService.getArticles(groupId, authorCardFolderId) /> 
1275 
1276    <#if authorCards?has_content> 
1277        <#list authorCards as authorCard> 
1278	    <#if !seenArticles?seq_contains(authorCard.articleId)> 
1279	      <#assign latest = JournalArticleLocalService.getLatestArticle(authorCard.resourcePrimKey) /> 
1280	      <#if latest.getStatus() == 0> 
1281		  <#local authorName = latest.getTitle(locale) /> 
1282		  <#if !authorNames?seq_contains(authorName) > 
1283		      <#local authorNames = authorNames + [authorName] /> 
1284		  </#if> 
1285	      </#if> 
1286	      <#local seenArticles = seenArticles + [authorCard.articleId] /> 
1287	    </#if> 
1288        </#list> 
1289         
1290    </#if> 
1291  </#list> 
1292  <#return authorNames /> 
1293</#function> 
1294 
1295<#-- 
1296    Replaces from given string author names with links to author page 
1297    parameters:  
1298    - authorNameStr : String where replacements is targeted 
1299    - authorNames : seq : list of author names 
1300    - baseUrl : url where author name is appended (as url escaped string) 
1301--> 
1302 
1303<#function makeAuthorLinks authorNameStr authorNames baseUrl > 
1304    <#list authorNames as authorName> 
1305        <#local authorNameInUrl = authorName?url("utf-8") /> 
1306        <#local authorNameStr = authorNameStr?replace(authorName, "<a class=\"author-name\" href=\""+baseUrl +authorNameInUrl + "\">"+authorName+"</a>") /> 
1307    </#list> 
1308    <#return authorNameStr /> 
1309</#function> 
1310 
1311<#function getImageURL imageJSON thumbId> 
1312    <#if imageJSON?? && imageJSON?has_content> 
1313        <#assign fileEntryId = imageJSON.getLong("fileEntryId")/> 
1314        <#assign fileName = imageJSON.getString("name")/> 
1315         
1316        <#if fileName?ends_with(".svg") || fileName?ends_with(".gif")> 
1317            <#assign imageGroupId = imageJSON.getLong("groupId")/> 
1318            <#assign imageUuid = imageJSON.getString("uuid")/> 
1319         
1320            <#return getFullImageUrL(imageGroupId, imageUuid) > 
1321        <#else> 
1322            <#return getAdaptiveImageURLByFileEntryId(fileEntryId, fileName, thumbId) > 
1323        </#if> 
1324    </#if> 
1325</#function> 
1326 
1327<#function getImageURLByTemplateModel imageTemplateModel thumbId> 
1328		<#local imageFileEntryId = imageTemplateModel.getAttribute("fileEntryId")/> 
1329		<#local fileName = imageTemplateModel.getAttribute("title")/> 
1330        <#local uuid = imageTemplateModel.getAttribute("uuid")/> 
1331		 <#if fileName?ends_with(".svg") || fileName?ends_with(".gif")> 
1332            <#return getFullImageUrL(groupId, uuid)> 
1333         <#else> 
1334            <#return getAdaptiveImageURLByFileEntryId(imageFileEntryId, fileName, thumbId)> 
1335		 </#if> 
1336</#function> 
1337 
1338<#function getAdaptiveImageURLByFileEntryId fileEntryId fileName thumbId> 
1339 
1340		<#local adaptiveMediaUrl = "/o/adaptive-media/image/${fileEntryId}/${thumbId}/${fileName}" > 
1341		 
1342        <#return adaptiveMediaUrl> 
1343</#function> 
1344 
1345<#function getFullImageUrL(groupId, uuid)> 
1346            <#assign imageURL = "/documents/" /> 
1347            <#assign imageURL += groupId /> 
1348            <#assign imageURL += "/" + uuid /> 
1349         
1350            <#return imageURL> 
1351</#function>