Si è verificato un errore nell'elaborarazione del modello.
The following has evaluated to null or missing: ==> thisCardData [in template "20098#20124#1784553" at line 572, column 10] ---- 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: #if thisCardData?contains("journal-te... [in template "20098#20124#1784553" in macro "printCard" at line 572, column 5] - Reached through: @printCard curEntry, curEntry?index [in template "20098#20124#1784553" in function "generateHtmlData" at line 406, column 37] ----
1<!-- Tours listing UNI FTL -->
2<#assign counter = 0>
3<#assign journalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService") />
4<#assign assetCategoryService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetCategoryService") />
5<#assign thisFilterScope = "${randomNamespace}filter">
6
7<#-- instanceId with id parent layout to allow emebed differrent widget on different pages -->
8<#assign cur_time = .now>
9<#assign urlHelper = vtaLibrary.getUrlHelper()>
10<#assign cacheSufix = urlHelper + "_" + locale>
11<#assign cur_instanceId = "html_data_" + themeDisplay.getLayout().uuid + cacheSufix>
12
13<#-- tours common filters array: -->
14<#assign filter_tourType = []>
15<#assign filter_month = []>
16<#assign filter_destination = []>
17<#assign filter_price_from = []>
18<#assign filter_destinationCountry = []>
19<#assign filter_interests= []>
20<#assign filter_duration= []>
21<#assign filter_recommended= []>
22<#-- excursions specific filters array: -->
23<#assign filter_estartingpoint= []>
24<#assign filter_eduration= []>
25<#assign departureDates= []><#-- currently used only to count dates...-->
26
27<#assign data_set=[]>
28<#assign curYearYY = .now?string('yy')?number>
29<#assign curMonthMM = .now?string('MM')?number>
30
31<#assign envSettings = vtaLibrary.getEnvSettings()>
32<#assign vocDestTypenId = envSettings.vocDestTypenId?number>
33<#assign vocPopularityId = envSettings.vocPopularityId?number>
34<#assign embedTourCardUniId = envSettings.embedTourCardUniId>
35<#assign structureGuidedTourId = 975893>
36<#assign currency = "err">
37<#if vtaUtil??>
38 <#assign currency = vtaUtil.getCurrencySymbol(request)>
39</#if>
40
41<#-- clear all cache by URL param (use ...url..."?cacheUpdate" ) -->
42<#assign curUrl = themeDisplay.getURLCurrent()?string>
43<#if curUrl?contains("?cacheUpdate")>
44 <#assign temp = freemarkerFilterCache.clearFreemarkerCache()>
45 <script>
46 console.log("Force cache update.");
47 </script>
48</#if>
49
50<#assign showFilerPanel = entries?size gte 5>
51
52<#-- check if are only Guided tours in list -->
53<#assign isGuidedListingOnly = false>
54<#if entries?has_content>
55 <#assign cur_guidesTypeCounter = 0>
56 <#list entries as curEntry>
57 <#assign cur_renderer=curEntry.getAssetRenderer() />
58 <#assign cur_journalArticle=cur_renderer.getArticle() />
59 <#assign cur_structureId = cur_journalArticle.getDDMStructureId()?string>
60 <#if vtaLibrary.getTemplateForStructure(cur_structureId?string,"name") = "guided">
61 <#assign cur_guidesTypeCounter = cur_guidesTypeCounter + 1>
62 </#if>
63 </#list>
64 <#assign isGuidedListingOnly = (cur_guidesTypeCounter == entries?size)>
65</#if>
66<!-- GUIDED ONLY: ${isGuidedListingOnly?string} -->
67<#if isGuidedListingOnly>
68 <#assign sortString = "gu_rec">
69<#else>
70 <#assign sortString = "rec">
71</#if>
72
73<#-- ################## -->
74<script src="${themeDisplay.getPathThemeJavaScript()}/vta-filter.js?t=${themeDisplay.getTheme().getTimestamp()}" type="text/javascript" data-senna-track="permanent"></script>
75
76
77<@generateData/>
78<#assign htmlData = generateHtmlData()>
79
80<!-- print html -->
81${htmlData}
82
83<#-- ******** generate data ********* -->
84<#macro generateData>
85 <#if entries?has_content>
86 <#list entries as curEntry>
87 <@generateCardData curEntry curEntry?index/>
88 </#list>
89 </#if>
90</#macro>
91
92
93<#function generateHtmlData>
94 <#-- HTML cachable data -->
95 <#local htmlData>
96
97 <#compress>
98 <style>
99 .d-tour.hidden,
100 .results-line .result,
101 #zeroBanner.hidden,
102 .results-line[data-results="1"] .results {
103 display:none;
104 }
105
106 .results-line[data-results="1"] .result {
107 display:inline;
108 }
109
110 /* default thumbs css */
111 .-Tour-swiperThumbs .swiper-slide {
112 width: 110px;
113 margin-right: 2px;
114 }
115 </style>
116
117 <div id="${thisFilterScope}" class="-sidebarLayout -Section -Section--verticalIndentLarge" data-sidewidth="3" data-space="2" <#--data-direction="row-reverse" data-sideright="true"--> data-switchxsmd="true" data-overflowvisible="true">
118 <div>
119
120 <#-- filter col -->
121 <div id="${thisFilterScope}-filter" class="-formElements js-f-col-filter" style="${showFilerPanel?then('','display:none;')}">
122 <div class="-Collapse -Collapse--xsmdTypeDropdown -Collapse--xsmdExpandFullScreen -formElements">
123 <div class="-Collapse-contents">
124 <div class="-Collapse-content">
125 <div id="collapseFilters" class="-Collapse-collapse collapse" aria-labelledby="collapseFiltersToggle" data-expanded-md="true">
126 <div class="-Panel -Panel--xsmdTypeModalFlyout -Collapse-panel -Collapse-panel--typeModalFlyout">
127 <div class="-Panel-inner">
128 <div class="-Panel-head">
129 <div class="-clusterLayout" data-space="05" data-align="center" data-justify="space-between">
130 <div>
131 <div>
132 <strong class="-clusterLayout" data-space="02" data-align="center" data-nowrap="true">
133 <span>
134 <i class="-Icon -Icon--filter"></i><span>${translationsUtils.getMessage(locale,'vta.webcontent.filterBy')}</span>
135 </span>
136 </strong>
137 </div>
138 <div>
139 <button id="clearAllButton" type="button" class="-Button -Button--typeLinkSecondary">${translationsUtils.getMessage(locale,'vta.webcontent.clearAll')}</button>
140 </div>
141 </div>
142 </div>
143 </div>
144 <div class="-Panel-body">
145 <div class="-stackLayout" data-space="03">
146 <div></div>
147 <div class="-stackLayout" data-space="3" data-space-xsmd="0">
148
149
150 <#-- tour type -->
151 <#if getAllUniqueArray(filter_tourType)?size gt 1>
152 <div class="-stackLayout" data-space="01">
153 <div class="-stackLayout" data-space="04">
154 <strong class="-Heading -Heading--typeH5Immutable -Heading--skinSharp">${translationsUtils.getMessage(locale,'vta.common.labels.tourType')}</strong>
155 <hr class="-Hr">
156 </div>
157 <@printFilterCheckBoxes filter_tourType "ttype"/>
158 </div>
159 </#if>
160
161 <#-- months -->
162 <#if filter_month?size gt 0>
163 <div class="-stackLayout" data-space="01">
164 <div class="-stackLayout" data-space="04">
165 <strong class="-Heading -Heading--typeH5Immutable -Heading--skinSharp">${translationsUtils.getMessage(locale,'vta.webcontent.departureMonth')}</strong>
166 <hr class="-Hr">
167 </div>
168
169 <div class="-stackLayout" data-space="01">
170
171 <div class="-stackLayout" data-space="03">
172 <strong>20${curYearYY}</strong>
173 <div>
174 <@printFilterDates filter_month "${curYearYY}"/>
175 </div>
176 </div>
177
178 <div class="-stackLayout" data-space="03">
179 <strong>20${curYearYY+1}</strong>
180 <div>
181 <@printFilterDates filter_month "${curYearYY+1}"/>
182 </div>
183 </div>
184
185 </div>
186 </div>
187 </#if>
188
189 <#-- ex starting points -->
190 <#if filter_estartingpoint?size gt 0>
191 <div class="-stackLayout" data-space="01">
192 <div class="-stackLayout" data-space="04">
193 <strong class="-Heading -Heading--typeH5Immutable -Heading--skinSharp">${translationsUtils.getMessage(locale,'vta.webcontent.starting.point')}</strong>
194 <hr class="-Hr">
195 </div>
196 <@printFilterCheckBoxes filter_estartingpoint "estartingp"/>
197 </div>
198 </#if>
199
200 <#-- ex duration -->
201 <#if filter_eduration?size gt 0>
202 <div class="-stackLayout" data-space="01">
203 <div class="-stackLayout" data-space="04">
204 <strong class="-Heading -Heading--typeH5Immutable -Heading--skinSharp">${translationsUtils.getMessage(locale,'vta.webcontent.duration')}</strong>
205 <hr class="-Hr">
206 </div>
207 <@printFilterCheckBoxes filter_eduration "eduration"/>
208 </div>
209 </#if>
210
211
212 <#-- prices -->
213 <#if filter_price_from?size gt 0>
214 <div class="-stackLayout" data-space="01" style="${(isRangePrintable(filter_price_from))?then('','display:none;')}">
215 <div class="-stackLayout" data-space="04">
216 <strong class="-Heading -Heading--typeH5Immutable -Heading--skinSharp">
217 ${translationsUtils.getMessage(locale,'vta.webcontent.pricePerPerson')}
218 </strong>
219 <hr class="-Hr">
220 </div>
221 <div id="rangePrices" class="-Range -stackLayout js-frange" data-space="01">
222 <@printRangeCombo filter_price_from currency "Prices"/>
223 </div>
224 </div>
225 </#if>
226
227 <#-- destinations -->
228 <#if filter_destination?size gt 0>
229 <div class="-stackLayout" data-space="01">
230 <div class="-stackLayout" data-space="04">
231 <strong class="-Heading -Heading--typeH5Immutable -Heading--skinSharp">${translationsUtils.getMessage(locale,'vta.webcontent.destinations')}</strong>
232 <hr class="-Hr">
233 </div>
234
235 <div class="-CollapseGroup -stackLayout" data-space="03">
236 <#-- country -->
237 <div>
238 <div class="-Collapse -Collapse--typeChevron">
239 <div class="-Collapse-contents">
240 <div class="-Collapse-content">
241 <div class="-Collapse-head" id="destCollapseHead1">
242 <a class="-Collapse-toggle" data-toggle="collapse" href="#destCollapseItem1" role="button" aria-expanded="true" aria-controls="destCollapseItem1">
243 <strong class="-Collapse-toggleContents">
244 <span class="-Collapse-toggleContent">${translationsUtils.getMessage(locale,'vta.common.labels.country')}</span>
245 <span class="-Collapse-toggleIndicator"></span>
246 </strong>
247 </a>
248 </div>
249 <div id="destCollapseItem1" class="-Collapse-collapse collapse show" aria-labelledby="destCollapseHead1">
250 <div class="-Collapse-body">
251 <@printFilterCheckBoxes filter_destinationCountry "tcountry"/>
252 </div>
253 </div>
254 </div>
255 </div>
256 </div>
257 </div>
258
259 <#-- tem destinations -->
260 <@printDestinationsFilter filter_destination "tdest"/>
261
262 </div>
263 </div>
264 </#if>
265
266 <#-- interest -->
267 <#if filter_interests?size gt 0>
268 <div class="-stackLayout" data-space="01">
269 <div class="-stackLayout" data-space="04">
270 <strong class="-Heading -Heading--typeH5Immutable -Heading--skinSharp">${translationsUtils.getMessage(locale,'vta.webcontent.interest')}</strong>
271 <hr class="-Hr">
272 </div>
273 <@printFilterCheckBoxes filter_interests "tinterests"/>
274 </div>
275 </#if>
276
277
278 <#-- days -->
279 <#if filter_duration?size gt 0>
280 <div class="-stackLayout" data-space="01" style="${(isRangePrintable(filter_duration))?then('','display:none;')}">
281 <div class="-stackLayout" data-space="04">
282 <strong class="-Heading -Heading--typeH5Immutable -Heading--skinSharp">${translationsUtils.getMessage(locale,'vta.webcontent.tourLength')}</strong>
283 <hr class="-Hr">
284 </div>
285 <div id="rangeDurations" class="-Range -stackLayout js-frange" data-space="01">
286 <@printRangeCombo filter_duration "${translationsUtils.getMessage(locale,'vta.webcontent.days')}" "Durations"/>
287 </div>
288 </div>
289 </#if>
290
291
292 </div>
293 </div>
294 </div>
295 <div class="-Panel-foot -mdHidden">
296 <div class="-clusterLayout" data-align="center" data-justify="space-between" data-nowrap="true">
297 <div>
298 <div>
299 <button type="button" data-toggle="collapse" data-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters" class="-Button -Button--sizeMediumLarge -Button--typePrimary">
300 ${translationsUtils.getMessage(locale,'vta.common.labels.apply')}</button>
301 </div>
302 <div>
303 <button type="button" data-toggle="collapse" data-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters" class="-Button -Button--sizeMediumLarge -Button--typeLinkSecondary">
304 ${translationsUtils.getMessage(locale,'vta.common.labels.cancel')}</button>
305 </div>
306 </div>
307 </div>
308 </div>
309 </div>
310 </div>
311 </div>
312 </div>
313 </div>
314 </div>
315 </div>
316
317 <#-- list col -->
318 <div>
319 <div class="-stackLayout" data-space="05">
320 <div class="-clusterLayout" data-space="05" data-align="center" data-justify="space-between">
321 <div>
322 <div>
323 <strong class="results-line" data-results="${entries?size}">
324 <span class="js-f-results">${entries?size}</span>
325 <span class="results">${translationsUtils.getMessage(locale,'vta.webcontent.results')}</span>
326 <span class="result">${translationsUtils.getMessage(locale,'vta.webcontent.result')}</span>
327 </strong>
328 </div>
329 <div>
330 <div class="-clusterLayout" data-space="1" data-align="center" data-justify="space-between" data-nowrap="true">
331 <div>
332
333 <#if showFilerPanel>
334 <div>
335 <button type="button" class="-Button -Button--typeBare -mdHidden" id="collapseFiltersToggle" data-toggle="collapse" data-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters">
336 <span class="-clusterLayout" data-space="02" data-align="center" data-nowrap="true">
337 <span>
338 <i class="-Icon -Icon--filter"></i><span> ${translationsUtils.getMessage(locale,'vta.webcontent.filters')} </span>
339 </span>
340 </span>
341 </button>
342 </div>
343 </#if>
344
345 <div>
346 <div class="-Collapse -Collapse--typeDropdown -Collapse--dropdownJustifyEnd">
347 <div class="-Collapse-contents">
348 <div class="-Collapse-content">
349 <div class="-Collapse-head">
350 <button id="collapseSortToggle" type="button" class="-Button -Button--typeBare collapsed" data-toggle="collapse" data-target="#collapseSort" aria-expanded="false" aria-controls="collapseSort" data-collapse-dropdown="true">
351 <span class="-clusterLayout" data-space="02" data-align="center" data-nowrap="true">
352 <span>
353 <i class="-Icon -Icon--sort"></i><span> ${translationsUtils.getMessage(locale,'vta.webcontent.sortBy')} </span>
354 </span>
355 </span>
356 </button>
357 </div>
358 <div id="collapseSort" class="-Collapse-collapse collapse" aria-labelledby="collapseSortToggle" data-dropdown-justify="end" style="">
359 <div class="-Panel -Panel--typeFlyout -Collapse-panel -Collapse-panel--typeFlyout">
360 <div class="-Panel-inner">
361 <div class="-Panel-body -formElements">
362 <div class="-stackLayout js-sset" data-space="02">
363
364 <#assign sortListArray = [
365 {"id":"Recommended", "datasort":"${sortString}", "value":"Recommended", "checked":true, "label":"${translationsUtils.getMessage(locale,'vta.webcontent.recommended')}"},
366 {"id":"PriceLowToHigh", "datasort":"pr_lo", "value":"Price (low to high)", "checked":false, "label":"${translationsUtils.getMessage(locale,'vta.webcontent.priceLowToHigh')}"},
367 {"id":"PriceHighToLow", "datasort":"pr_hi", "value":"Price (high to low)", "checked":false, "label":"${translationsUtils.getMessage(locale,'vta.webcontent.priceHighToLow')}"},
368 {"id":"DepartureEarliestToLatest", "datasort":"de_er", "value":"Departure (earliest to latest)", "checked":false, "label":"${translationsUtils.getMessage(locale,'vta.webcontent.departureEarliestToLatest')}"},
369 {"id":"DepartureLatestToEarliest", "datasort":"de_lt", "value":"Departure (latest to earliest)", "checked":false, "label":"${translationsUtils.getMessage(locale,'vta.webcontent.departureLatestToEarliest')}"},
370 {"id":"DurationShortest", "datasort":"du_sh", "value":"Duration (shortest)", "checked":false, "label":"${translationsUtils.getMessage(locale,'vta.webcontent.durationShortest')}"},
371 {"id":"DurationLongest", "datasort":"du_ln", "value":"Duration (longest)", "checked":false, "label":"${translationsUtils.getMessage(locale,'vta.webcontent.durationLongest')}"}
372 ]>
373 <#list sortListArray as item>
374 <div class="-CustomControl -CustomControl--typeCheckMark js-sitem --focus-w">
375 <div class="custom-control custom-radio">
376 <label>
377 <input name="sortBy" data-sort="${item.datasort}" id="${item.id}" type="radio" class="-CustomControlInput custom-control-input js-sitem-input"
378 role="radio" value="${item.value}" ${item.checked?then('checked=""','')}>
379 <span class="custom-control-label">
380 <span class="custom-control-label-text">${item.label}</span>
381 </span>
382 </label>
383 </div>
384 </div>
385 </#list>
386
387 </div>
388 </div>
389 </div>
390 </div>
391 </div>
392 </div>
393 </div>
394 </div>
395 </div>
396
397 </div>
398 </div>
399 </div>
400 </div>
401 </div>
402
403 <div id="${thisFilterScope}-list" class="-stackLayout js-f-col-list" data-space="1">
404 <#if entries?has_content>
405 <#list entries as curEntry>
406 <@printCard curEntry curEntry?index/>
407 </#list>
408 </#if>
409
410 <#-- ********* don't remove - using for service prints -->
411 <#-- tourtype: ${filter_tourType?size} -->
412 <#-- month: ${filter_month?size} -->
413 <#-- destinations: ${filter_destination?size} -->
414 <#-- price: ${filter_price_from?size} -->
415 <#-- country: ${filter_destinationCountry?size} -->
416 <#-- interest: ${filter_interests?size} -->
417 <#-- duration: ${filter_duration?size} -->
418 <#-- recomended: ${filter_recommended?size} -->
419 <#-- ex starting point: ${filter_estartingpoint?size} -->
420 <#-- ex duration: ${filter_eduration?size} -->
421
422 <#-- loader -->
423 <#-- <div class="-LoaderContainer -LoaderContainer--variantSpinner -LoaderContainer--sizeMedium" data-state="active"></div> -->
424
425 <div id="banner" style="display:none;">
426 <#-- marketingBanner_${themeDisplay.getSiteGroupId()} -->
427 <#assign thisPrefs = freeMarkerPortletPreferences.getPreferences("portletSetupPortletDecoratorId", "barebone") />
428 <@liferay_portlet["runtime"]
429 defaultPreferences="${thisPrefs}"
430 portletProviderAction=portletProviderAction.VIEW
431 instanceId="marketingBanner_${themeDisplay.getSiteGroupId()}"
432 portletName="com_liferay_journal_content_web_portlet_JournalContentPortlet"
433 />
434 </div>
435
436 <div id="zeroBanner" class="hidden">
437 <#-- zeroBanner_${themeDisplay.getSiteGroupId()} -->
438 <#assign thisPrefs = freeMarkerPortletPreferences.getPreferences("portletSetupPortletDecoratorId", "barebone") />
439 <@liferay_portlet["runtime"]
440 defaultPreferences="${thisPrefs}"
441 portletProviderAction=portletProviderAction.VIEW
442 instanceId="zeroBanner_${themeDisplay.getSiteGroupId()}"
443 portletName="com_liferay_journal_content_web_portlet_JournalContentPortlet"
444 />
445 </div>
446
447 </div>
448
449 </div>
450 </div>
451
452
453 </div>
454 </div>
455
456 </#compress>
457 <@scripts/>
458 </#local>
459 <#-- /HTML cachable data -->
460
461<#return htmlData>
462</#function>
463
464
465<#-- ************************************** -->
466
467<#-- get last modification date by structure type -->
468<#function getModDateForArticle article>
469 <#local ret = "00000">
470 <#if article.DDMStructureId?string = structureGuidedTourId?string>
471 <#-- get GuidedTour last modification datetime from BE -->
472 <#if !guidedToursModDatesMap??>
473 <#local guidedToursModDatesMap = guidedUtil.getLastModifiedMap(request)>
474 </#if>
475 <#if guidedToursModDatesMap[article.articleId]??>
476 <#local ret = guidedToursModDatesMap[article.articleId]>
477 <#else>
478 <#-- no data on BE -->
479 <#local ret = article.modifiedDate?datetime?string>
480 </#if>
481 <#else>
482 <#-- all tours except GuidedTours -->
483 <#local ret = article.modifiedDate?datetime?string>
484 </#if>
485 <#return ret>
486</#function>
487
488
489<#-- generate data and update/create cache -->
490<#macro generateCardData curEntry index>
491 <#local renderer=curEntry.getAssetRenderer() />
492 <#local cur_journalArticle=renderer.getArticle() />
493 <#local cur_journalArticle_Uuid = cur_journalArticle.uuid?string>
494 <#local cur_journalArticle_ModDate = getModDateForArticle(cur_journalArticle)>
495 <!-- articleID: ${cur_journalArticle.getArticleId()} -->
496 <#local card_id = "${randomNamespace}tc_${index}">
497 <#-- cache logic -->
498 <#if freemarkerFilterCache??>
499 <#local thisCacheItem = freemarkerFilterCache.getArticleItem(cur_journalArticle_Uuid + cacheSufix)!"null"><#-- read cache date modification date -->
500
501 <#if thisCacheItem = "null">
502 <#local cur_cacheMessage = "No cache record ...created">
503 <#local thisCardData = renderCardData(cur_journalArticle)>
504 <#elseif cur_journalArticle_ModDate == thisCacheItem.changeDate>
505 <#local thisCardData = thisCacheItem.content!"null"><#-- read cache data -->
506 <#if thisCardData != "null">
507 <#-- <#local cur_cacheMessage = "HIT actual"> -->
508 <#else>
509 <#local cur_cacheMessage = "HIT - record empty ...created">
510 <#local thisCardData = renderCardData(cur_journalArticle)>
511 </#if>
512 <#else>
513 <#local cur_cacheMessage = "HIT modified: ${thisCacheItem.changeDate} : ${cur_journalArticle_ModDate}<br>...updated">
514 <#local thisCardData = renderCardData(cur_journalArticle)>
515 </#if>
516 <#else>
517 <#local thisCardData = renderCardData(cur_journalArticle)>
518 <#local cur_cacheMessage = "Cache not installed">
519 </#if>
520 <#-- end cache logic -->
521
522 <#local thisCode = thisCardData>
523 <#assign data_map = thisCode?split("<-DATA-SPLIT->")>
524
525 <#if !thisCode?contains("journal-template-error") && data_map?size gt 1>
526 <#local currnetDataArray = data_map[1]?eval>
527 <#local dataString>
528 <@collectData currnetDataArray card_id/>
529 </#local>
530 <#list currnetDataArray as cur_dataArry>
531 <#local thisJsStringRecord = '{ "id":"${card_id}","inRanges":true,"inScope":true,"sort-date":"",${collectJsString(cur_dataArry)}}'>
532 <#assign data_set = data_set + [thisJsStringRecord]>
533 </#list>
534 </#if>
535 <#if cur_cacheMessage??>
536 <#-- cache: ${cur_cacheMessage} -->
537 <script>console.log("cache: ${card_id} ${cur_journalArticle.getTitle(locale)}, (${cur_journalArticle_ModDate}) : ${cur_cacheMessage}");</script>
538 </#if>
539</#macro>
540
541
542
543<#-- print a tour card from cache ONLY-->
544<#macro printCard curEntry index>
545 <#local renderer=curEntry.getAssetRenderer() />
546 <#local cur_journalArticle=renderer.getArticle() />
547 <#local cur_journalArticle_Uuid = cur_journalArticle.uuid?string>
548 <#local cur_journalArticle_ModDate = getModDateForArticle(cur_journalArticle)>
549 <#local card_id = "${randomNamespace}tc_${index}">
550
551 <#-- cache logic -->
552 <#if freemarkerFilterCache??>
553 <#local thisCacheItem = freemarkerFilterCache.getArticleItem(cur_journalArticle_Uuid + cacheSufix)!"null"><#-- read cache date modification date -->
554
555 <#if thisCacheItem = "null">
556 <#local cur_cacheMessage = "No cache record ...created">
557 <#elseif cur_journalArticle_ModDate == thisCacheItem.changeDate>
558 <#local thisCardData = thisCacheItem.content!"null"><#-- read cache data -->
559 <#if thisCardData != "null">
560 <#-- <#local cur_cacheMessage = "HIT actual"> -->
561 <#else>
562 <#local cur_cacheMessage = "HIT - record empty ERRRRRRRR!">
563 </#if>
564 <#else>
565 <#local cur_cacheMessage = "HIT modified: ${thisCacheItem.changeDate} : ${cur_journalArticle_ModDate}<br>...ERRRRRRRRRR!">
566 </#if>
567 <#else>
568 <#local cur_cacheMessage = "Cache not installed">
569 </#if>
570 <#-- end cache logic -->
571
572 <#if thisCardData?contains("journal-template-error")>
573 <div id="${card_id}" class="d-tour" >
574 <div style="background-color:black; color:white; padding:1em;">
575 <h2>*** card error ***</h2>
576 <h4>${cur_journalArticle.getTitle()}</h4>
577 <a href="${urlHelper}/w/${cur_journalArticle.getUrlTitle()}" target="_blank">OPEN TOUR</a>
578 <!-- ${cur_journalArticle} -->
579 </div>
580 <!-- ${thisCardData} -->
581 </div>
582 <#elseif thisCardData?contains("PAST TOUR")>
583 <!-- PAST TOUR: ${cur_journalArticle.getTitle()} -->
584 <#-- <div id="${card_id}" class="d-tour" >
585 <div style="background-color:lightskyblue; color:white; padding:1em;">
586 <h2>*** PAST TOUR ***</h2>
587 <h4>${cur_journalArticle.getTitle()}</h4>
588 <a href="${urlHelper}/w/${cur_journalArticle.getUrlTitle()}" target="_blank">OPEN TOUR</a>
589 </div>
590 </div> -->
591 <#elseif !thisCardData?contains("<-DATA-SPLIT->")>
592 <div id="${card_id}" class="d-tour" >
593 <div style="background-color:black; color:white; padding:1em;">
594 <h2>*** wrong template or webcontent ***</h2>
595 <h4>${cur_journalArticle.getTitle()}</h4>
596 <!-- ${cur_journalArticle} -->
597 </div>
598 </div>
599 <#else>
600 <#local data_map = thisCardData?split("<-DATA-SPLIT->")>
601 <#-- <p style="font-size: 0.6em;">${thisJsStringRecord?replace(":",": ")?replace(",",", ")} </p> -->
602 <div id="${card_id}" class="d-tour js-f-tour-card hidden"><#--${(index gte 2)?then('hidden','')}-->
603 <#if cur_cacheMessage??>
604 <script>console.log("*cache: ${card_id} ${cur_journalArticle.getTitle(locale)} : ${cur_cacheMessage}");</script>
605 </#if>
606 <#-- style="font-size: 0.6em;">${thisJsStringRecord?replace(":",": ")?replace(",",", ")} -->
607 <#-- ${thisCardData} -->
608 ${data_map[0]}${data_map[3]}
609 </div>
610
611
612
613 </#if>
614</#macro>
615
616<#-- render CARD data and write to cache -->
617<#function renderCardData cur_journalArticle>
618 <#local thisEntryData>
619 <@liferay_journal["journal-article"]
620 articleId=cur_journalArticle.getArticleId()
621 ddmTemplateKey="${embedTourCardUniId}"
622 groupId=cur_journalArticle.getGroupId()
623 />
624 </#local>
625 <#local thisEntryId = cur_journalArticle.uuid?string + cacheSufix>
626 <#local thisEntryDate = getModDateForArticle(cur_journalArticle)>
627 <#if freemarkerFilterCache??>
628 <#local writeMe = freemarkerFilterCache.putArticleItem(thisEntryId, thisEntryDate, thisEntryData)><#-- update cache -->
629 </#if>
630 <#return thisEntryData>
631</#function>
632
633<#function collectJsString hash>
634 <#local retString = "">
635 <#list hash as key,val>
636 <#local retString = retString + '"${key}": ' + (val?is_number)?then('${val}','"${val}"') + (key?is_last)?then('',', ')>
637 </#list>
638 <#return retString>
639</#function>
640
641
642<#-- data collect -->
643<#macro collectData hashArry id>
644 <#list hashArry as cur_hash>
645 <#list cur_hash as key,val>
646 <#if val?string="">
647 <#continue><#-- skip empty val -->
648 </#if>
649 ${key}="${val}"
650 <#if val?contains(",")>
651 <#assign value = val?split(",")>
652 <#else>
653 <#assign value = [val]>
654 </#if>
655 <#if key = "data-ttype">
656 <#assign filter_tourType = filter_tourType + value/>
657 <#elseif key = "data-tmonth">
658 <#assign filter_month = filter_month + value/>
659 <#elseif key = "data-tdest">
660 <#assign filter_destination = filter_destination + value/>
661 <#elseif key = "data-tprice">
662 <#assign filter_price_from = filter_price_from + value/>
663 <#elseif key = "data-tcountry">
664 <#assign filter_destinationCountry = filter_destinationCountry + value/>
665 <#elseif key = "data-tinterests">
666 <#assign filter_interests = filter_interests + value/>
667 <#elseif key = "data-tlength">
668 <#assign filter_duration = filter_duration + value/>
669 <#elseif key = "data-trec" && val?string != "">
670 <#assign filter_recommended = filter_recommended + value/>
671 <#elseif key = "data-estartingp" && val != "">
672 <#assign filter_estartingpoint = filter_estartingpoint + value/>
673 <#elseif key = "data-eduration">
674 <#assign filter_eduration = filter_eduration + value/>
675 </#if>
676 </#list>
677 </#list>
678</#macro>
679
680<#-- print a range inside combo -->
681<#macro printRangeCombo array units name>
682 <#local cur_array = []>
683 <#if array?size != 0>
684 <#list array as item>
685 <#attempt>
686 <#-- try -->
687 <#local cur_array = cur_array + [item?number]>
688 <#recover>
689 <#-- catch -->
690 <!-- item is not a number! -->
691 <!-- array: ${array?join(", ")} -->
692 <!-- item: ${item} -->
693 <#local cur_array = [0,1]>
694 <#break>
695 </#attempt>
696 </#list>
697
698 <#local cur_min = cur_array?min>
699 <#local cur_max = cur_array?max>
700 <#local cur_step = "1">
701 <div class="-Range-Value">
702 <span class="-Range-fromValue" data-from-value="">${cur_min}</span>
703 <span class="-Range-fromValueSuffix">${units}</span>
704 <span class="-Range-valueSeparator">-</span>
705 <span class="-Range-toValue" data-to-value="">${cur_max}</span>
706 <span class="-Range-toValueSuffix">${units}</span>
707 </div>
708 <div class="-Range-control">
709 <input type="range" id="from${name}" name="from${name}" data-from=""
710 value="${cur_min}" min="${cur_min}" max="${cur_max}" step="${cur_step}" aria-label="from${name}">
711 <input type="range" id="to${name}" name="to${name}" data-to=""
712 value="${cur_max}" min="${cur_min}" max="${cur_max}" step="${cur_step}" aria-label="to${name}"
713 style="background: linear-gradient(to right,
714 var(--_range-control-secondary-color) 0%,
715 var(--_range-control-secondary-color) 20%,
716 var(--_range-control-primary-color) 20%,
717 var(--_range-control-primary-color) 66.66666666666666%,
718 var(--_range-control-secondary-color) 66.66666666666666%,
719 var(--_range-control-secondary-color) 100%); z-index: 0;">
720 </div>
721 </#if>
722
723</#macro>
724
725<#function isRangePrintable array>
726 <#local cur_array = []>
727 <#if array?size != 0>
728 <#list array as item>
729 <#attempt>
730 <#-- try -->
731 <#local cur_array = cur_array + [item?number]>
732 <#recover>
733 <#local cur_array = [0,1]>
734 <#break>
735 </#attempt>
736 </#list>
737 <#if cur_array?min != cur_array?max>
738 <#return true>
739 </#if>
740 </#if>
741 <#return false>
742</#function>
743
744
745<#macro printDestinationsFilter array type>
746 <#-- get array of destinations with types -->
747 <#local otherTypeName = translationsUtils.getMessage(locale,'vta.webcontent.other')>
748 <#local popularTypeName = "popular"><#-- default value if no popular tour... -->
749 <#local existPopulars = false>
750 <#local existOthers = false>
751 <#local destArray = []>
752 <!-- DEST : ${array?size} -->
753 <!-- DEST : ${array?join(", ")} -->
754
755 <#local allUniqueDestIds = getAllUniqueArray(array)>
756 <!-- UNIQ : ${allUniqueDestIds?size} -->
757 <!-- UNIQ : ${allUniqueDestIds?join(", ")} -->
758 <#list allUniqueDestIds as item>
759 <#local popular = readByClassPkAndVocIdWcCatName(item vocPopularityId "None")><#-- return name or "error" for missing classPk -->
760 <#if popular != "None" && popular != "error" >
761 <#local typeName = popular>
762 <#local popularTypeName = popular>
763 <#local existPopulars = true>
764 <#else>
765 <#local typeName = readByClassPkAndVocIdWcCatName(item vocDestTypenId "${otherTypeName}")>
766 <#local existOthers = true>
767 </#if>
768
769 <#local destArray = destArray + [{ "typeName":typeName, "destId":item }]>
770 </#list>
771
772 <#-- get array of unique types (labels of sections)-->
773 <#local allUniqueTypes = []>
774 <#if existPopulars><#-- add populars at start -->
775 <#local allUniqueTypes = allUniqueTypes + [popularTypeName]>
776 </#if>
777 <#list destArray?sort_by("typeName") as item>
778 <#if !(allUniqueTypes?seq_contains(item.typeName)) && item.typeName != popularTypeName && item.typeName != otherTypeName>
779 <#local allUniqueTypes = allUniqueTypes + [item.typeName]>
780 </#if>
781 </#list>
782 <#if existOthers><#-- add populars at end -->
783 <#local allUniqueTypes = allUniqueTypes + [otherTypeName]>
784 </#if>
785
786 <#-- iterate each type sets -->
787 <#list allUniqueTypes as cur_item>
788 <#-- types: ${cur_item} - ${popularTypeName} -->
789 <#local filteredDestNames = destArray?filter(item -> item.typeName == cur_item)?map(item -> item.destId)>
790 <#local collapseName = cur_item?replace("[^0-9a-zA-Z_]", "_", "r")>
791 <#local openDropDown = (cur_item == popularTypeName)>
792 <div>
793 <div class="-Collapse -Collapse--typeChevron">
794 <div class="-Collapse-contents">
795 <div class="-Collapse-content">
796 <div class="-Collapse-head" id="${collapseName}Head">
797 <a class="-Collapse-toggle" data-toggle="collapse" role="button" aria-expanded="${openDropDown?c}"
798 href="#${collapseName}Item" aria-controls="${collapseName}Item">
799 <strong class="-Collapse-toggleContents">
800 <span class="-Collapse-toggleContent">${cur_item}</span>
801 <span class="-Collapse-toggleIndicator"></span>
802 </strong>
803 </a>
804 </div>
805 <div id="${collapseName}Item" class="-Collapse-collapse collapse ${openDropDown?then('show','')}" aria-labelledby="${collapseName}Head">
806 <div class="-Collapse-body">
807
808 <@printFilterCheckBoxes filteredDestNames type/>
809
810 </div>
811 </div>
812 </div>
813 </div>
814 </div>
815 </div>
816
817 </#list>
818
819
820</#macro>
821
822
823<#function getAllUniqueArray array>
824 <#-- create array of unique items from array -->
825 <#local allUniqueItems = []>
826 <#list array as item>
827 <#if !(allUniqueItems?seq_contains(item))>
828 <#local allUniqueItems = allUniqueItems + [item]>
829 </#if>
830 </#list>
831
832 <#return allUniqueItems>
833</#function>
834
835
836
837<#-- filtr checkboxes -->
838<#macro printFilterCheckBoxes array type>
839
840 <#local allUniqueItems = getAllUniqueArray(array)>
841 <#local printArray = []>
842 <#list allUniqueItems?sort as item>
843 <#local screen_name = item>
844 <#if item?string = "">
845 <#continue>
846 <#elseif type = "tdest">
847 <#local screen_name = readByClassPkWebContentField(item,"ScreenName")>
848 <#elseif type = "ttype">
849 <#local langKey = vtaLibrary.getTemplateForStructure(screen_name,"lang")>
850 <#local screen_name = translationsUtils.getMessage(locale,langKey)>
851 <#elseif type = "tinterests" || type = "estartingp">
852 <#local screen_name = assetCategoryService.fetchCategory(screen_name?number).getTitle(locale)>
853 <#elseif type = "tcountry" && screen_name != "No country">
854 <#local screen_name = assetCategoryService.fetchCategory(screen_name?number).getTitle(locale)>
855 <#elseif type = "eduration">
856 <#switch item?string>
857 <#case "0">
858 <#local screen_name = translationsUtils.getMessage(locale,'vta.webcontent.short.up.2.hrs')>
859 <#break>
860 <#case "1">
861 <#local screen_name = translationsUtils.getMessage(locale,'vta.webcontent.medium')>
862 <#break>
863 <#case "2">
864 <#local screen_name = translationsUtils.getMessage(locale,'vta.webcontent.long.8plus.hrs')>
865 <#break>
866 <#default>
867 <#local screen_name = "Unknown">
868 </#switch>
869 </#if>
870 <#local count = array?filter(it -> it == item)?size >
871 <#local printArray = printArray + [{"screenName":screen_name, "item":item, "count":count}]>
872 <#-- ${item}, ${screen_name}, ${count} -->
873 </#list>
874
875 <div class="-stackLayout js-fset" data-ftype="${type}" data-space="04">
876 <#list printArray?sort_by("screenName") as item>
877 <@printFilterCheckboxItem item.item item.screenName item.count type/>
878 </#list>
879 </div>
880</#macro>
881
882 <#-- print one checkbox -->
883 <#macro printFilterCheckboxItem item screen_name count type>
884 <div class="custom-control custom-checkbox js-fitem">
885 <label>
886 <input type="checkbox"
887 class="-CustomControlInput custom-control-input js-fitem-input"
888 data-fname="${item}"
889 role="checkbox" value="${screen_name}">
890 <span class="custom-control-label">
891 <span class="custom-control-label-text filter-item">${screen_name} (<span class="-colorSecondaryPale js-fitem-count">${count}</span>)</span> <#-- to prevent orphan a number in the responsive design -->
892 </span>
893 </label>
894 </div>
895 </#macro>
896
897
898<#-- month list -->
899<#macro printFilterDates array year>
900 <div class="-gridLayout js-fset" data-fragment="1of4" data-space="02" data-overflowvisible="true" data-ftype="tmonth">
901 <div>
902 <#-- ${array?sort?join(", ")} -->
903 <#list 1..12 as cur_month>
904 <#local cur_fname = year + "-" + cur_month?left_pad(2, "00")>
905 <#local cur_is_enabled = array?seq_contains(cur_fname)>
906 <#if year?number == curYearYY?number && cur_month?number lt curMonthMM?number><#--disable past months-->
907 <#local cur_is_enabled = false>
908 </#if>
909 <#-- *${cur_fname}* -->
910 <#local cur_temp_date = dateUtil.parseDate("yyyy-MM-dd", "1999-" + cur_month?left_pad(2, "00") + "-01", locale)><#-- fake date 1999-MM-01 -->
911 <#local cur_month_string = dateUtil.getDate(cur_temp_date, "MMM", locale)>
912 <div>
913 <div class="-CustomControl -CustomControl--typeButton">
914 <div class="custom-control custom-checkbox ${cur_is_enabled?then('js-fitem','')}">
915 <label>
916 <input name="departureMonth"
917 type="checkbox"
918 class="-CustomControlInput custom-control-input ${cur_is_enabled?then('js-fitem-input','')}"
919 role="checkbox" value="${cur_month_string}"
920 ${cur_is_enabled?then('data-fname="${cur_fname}"','')}
921 ${cur_is_enabled?then("","disabled")}>
922 <span class="custom-control-label">
923 <span class="custom-control-label-text">${cur_month_string}</span>
924 </span>
925 </label>
926 </div>
927 </div>
928 </div>
929 </#list>
930 </div>
931 </div>
932</#macro>
933
934<#-- read a filed form webcontent -->
935<#function readByClassPkWebContentField classPK fieldReference>
936 <#local ret = "*****">
937 <#if classPK !="">
938 <#local journalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService")>
939 <#local journalArticle = journalArticleLocalService.fetchLatestArticle(classPK?number)>
940 <#local docXML = saxReaderUtil.read(journalArticle.getDocument().asXML())>
941 <#local ret = docXML.selectSingleNode("/root/dynamic-element[@field-reference='${fieldReference}']/dynamic-content").getData()>
942 <#assign counter = counter + 1>
943 </#if>
944 <#return ret>
945</#function>
946
947<#-- read a category name form webcontent by classPK a vocabularyId-->
948<#function readByClassPkAndVocIdWcCatName classPK vocabularyId noCategorySetReturnString>
949 <#local ret = "error">
950 <#if classPK !="">
951 <#local assetCategoryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetCategoryLocalService") />
952 <#local categoryList=assetCategoryLocalService.getCategories("com.liferay.journal.model.JournalArticle",classPK?number) >
953
954 <#-- this category is NOT multioptional (if yes only first item will be returned -->
955 <#local name = noCategorySetReturnString>
956 <#list categoryList as category>
957 <#if category.vocabularyId = vocabularyId>
958 <#local name = category.getTitle(locale)>
959 </#if>
960 </#list>
961
962 <#local ret = name >
963 </#if>
964 <#return ret>
965</#function>
966
967<#macro scripts>
968 <#-- min/max price and length -->
969 <#assign priceArray = []>
970 <#list filter_price_from as item>
971 <#if item?string != "">
972 <#assign priceArray = priceArray + [item?number]>
973 </#if>
974 </#list>
975 <#if priceArray?size = 0>
976 <#assign priceArray = [0,1]>
977 </#if>
978
979
980 <#assign durationArray = []>
981 <#list filter_duration as item>
982 <#if item?string != "">
983 <#assign durationArray = durationArray + [item?number]>
984 </#if>
985 </#list>
986 <#if durationArray?size = 0>
987 <#assign durationArray = [0,1]>
988 </#if>
989 <script>
990
991 ;AUI().ready(function() {
992 // All data source
993 var filterDataArray = [
994 <#list data_set as item>
995 ${item}<#sep>,</#sep>
996 </#list>
997 ];
998
999 var dataObject = {
1000 thisOrFilters: ["ttype","tmonth"],
1001 filterDataArray: filterDataArray,
1002 thisFilterCol: document.getElementById("${thisFilterScope}-filter"),
1003 thisResultsCol: document.getElementById("${thisFilterScope}-list"),
1004 rangePricesId: "rangePrices",
1005 rangeDurationsId: "rangeDurations",
1006 defaultRangePriceFrom: ${priceArray?min},
1007 defaultRangePriceTo: ${priceArray?max},
1008 defaultRangeLengthFrom: ${durationArray?min},
1009 defaultRangeLengthTo: ${durationArray?max},
1010 rangePriceFrom: ${priceArray?min},
1011 rangePriceTo: ${priceArray?max},
1012 rangeLengthFrom: ${durationArray?min},
1013 rangeLengthTo: ${durationArray?max},
1014 defaultSort: "${sortString}"
1015 };
1016
1017 handleRangeComponent(".-Range");
1018 startFilter(dataObject);
1019 });
1020
1021 </script>
1022
1023 <#-- <script> // DEBUG SCRIPT
1024
1025 var filterDataArray = [
1026 <#list data_set as item>
1027 ${item}<#sep>,</#sep>
1028 </#list>
1029 ];
1030
1031 var dataObject = {
1032 thisOrFilters: ["ttype","tmonth"],
1033 filterDataArray: filterDataArray,
1034 thisFilterCol: document.getElementById("${thisFilterScope}-filter"),
1035 thisResultsCol: document.getElementById("${thisFilterScope}-list"),
1036 rangePricesId: "rangePrices",
1037 rangeDurationsId: "rangeDurations",
1038 defaultRangePriceFrom: ${priceArray?min},
1039 defaultRangePriceTo: ${priceArray?max},
1040 defaultRangeLengthFrom: ${durationArray?min},
1041 defaultRangeLengthTo: ${durationArray?max},
1042 rangePriceFrom: ${priceArray?min},
1043 rangePriceTo: ${priceArray?max},
1044 rangeLengthFrom: ${durationArray?min},
1045 rangeLengthTo: ${durationArray?max},
1046 defaultSort: "${sortString}"
1047 };
1048
1049 //handleRangeComponent('.-Range');
1050 //startFilter(dataObject);
1051
1052
1053 </script> -->
1054
1055
1056
1057 <script>
1058
1059 ;function startSwipers(swiperContainers) {
1060 // + init swipers
1061 function setSwiper(container, index) {
1062 let thumbContainer = container.querySelector('.js-swiper-thumb');
1063 let mainContainer = container.querySelector('.js-swiper-main');
1064
1065 if (!thumbContainer.swiper) {
1066 var swiperThumb = new Swiper(thumbContainer, {
1067 spaceBetween: 2,
1068 speed: 500,
1069 grabCursor: true,
1070 keyboard: {
1071 enabled: true,
1072 },
1073 slidesPerView: 3,
1074 freeMode: {
1075 enabled: true,
1076 sticky: true,
1077 },
1078 watchSlidesProgress: false,
1079 });
1080
1081 var swiperMain = new Swiper(mainContainer, {
1082 spaceBetween: 2,
1083 speed: 500,
1084 grabCursor: true,
1085 keyboard: {
1086 enabled: true,
1087 },
1088 navigation: {
1089 nextEl: thumbContainer.querySelector('.swiper-button-next'),
1090 prevEl: thumbContainer.querySelector('.swiper-button-prev'),
1091 },
1092 thumbs: {
1093 swiper: swiperThumb,
1094 },
1095 });
1096
1097 // Uložit instance Swiperu, pokud je třeba s nimi dále pracovat
1098 thumbContainer.swiperInstance = swiperThumb;
1099 mainContainer.swiperInstance = swiperMain;
1100 }
1101 else {
1102 console.log(index, "uz existuje");
1103 }
1104 };
1105
1106 var observerOptions = {
1107 root: null, // Viewport jako kořen
1108 rootMargin: '0px',
1109 threshold: 0.1 // Spustí se, když je alespoň 10% prvku viditelné
1110 };
1111
1112 var observer = new IntersectionObserver((entries, observer) => {
1113 entries.forEach(entry => {
1114 if (entry.isIntersecting) {
1115 setSwiper(entry.target);
1116 observer.unobserve(entry.target); // Přestat sledovat prvek po inicializaci Swiperu
1117 }
1118 });
1119 }, observerOptions);
1120
1121 swiperContainers.forEach(container => {
1122 observer.observe(container); // Začít sledovat každý kontejner
1123 });
1124 };
1125
1126 ;AUI().ready(function() {
1127 var swContainer = document.querySelectorAll('.js-swipers-container');
1128 startSwipers(swContainer);
1129 });
1130
1131 </script>
1132</#macro>