Si è verificato un errore nell'elaborarazione del modello.
The following has evaluated to null or missing: ==> response.data.docs.getJSONObject(0) [in template "3572636#3572671#4320625" at line 21, column 24] ---- Tip: If the failing expression is known to be 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 doc = response.data.docs.getJ... [in template "3572636#3572671#4320625" at line 21, column 9] ----
1<#import "_TEMPLATE_CONTEXT_/3572636/3574130/23609/3793037" as d40>
2
3<#setting url_escaping_charset="UTF-8">
4
5<#assign
6 PUTIL = objectUtil("com.liferay.portal.kernel.util.PortalUtil")
7 uuid = themeDisplay.getURLCurrent()?keep_after("-/c/n/")?keep_after_last("_")
8 langId = locale?keep_before("_")
9 contentType = "poi"
10 vueActions = ""
11 doc = ""
12 lat = "0"
13 lng = "0"
14 hasCoords = false
15/>
16
17<#if uuid?has_content>
18 <#assign response = d40.fetchData("https://guestapp.d40.it/v1/search/apt-trento/${contentType}/?uuid=${uuid}")>
19
20 <#if !response.error && response.data.docs?has_content>
21 <#assign doc = response.data.docs.getJSONObject(0)>
22
23 ${request.session.removeAttribute("circularCategories")}
24 ${request.session.setAttribute("circularCategories", doc.details.assetCategoryIds)}
25 </#if>
26</#if>
27
28<#if doc?has_content>
29 <#if doc.details?? && doc.details.name[langId]?has_content>
30 ${PUTIL.setPageTitle(doc.details.name[langId], request)}
31 </#if>
32
33 <#assign vueActions = "getFavorites();">
34
35 <#if doc.details.location.geoCoords.lat?has_content && doc.details.location.geoCoords.lon?has_content>
36 <#assign
37 hasCoords = true
38 lat = doc.details.location.geoCoords.lat
39 lng = doc.details.location.geoCoords.lon
40 vueActions = "getFavorites();buildMap(${lat}, ${lng})"
41 />
42 </#if>
43</#if>
44
45<style>
46 .swiper-button-next:after{
47 content: "";
48 }
49 .swiper-button-prev:after{
50 content: "";
51 }
52</style>
53
54<div v-scope id="${d40.portletNamespace}" @vue:mounted="${vueActions}" v-cloak>
55 <#if doc?has_content>
56 <#if themeDisplay.getLayout().getAncestors()?has_content>
57 <section id="breadcrumb" class="z-15 mt-25">
58 <div class="container">
59 <ol class="breadcrumb flex lg:justify-center">
60 <li class="pl-1 text-sm mr-2 relative">${d40.getLabel("sei_in")}:</li>
61
62 <li class="breadcrumb-item">
63 <a href="/" class="breadcrumb-link" title="Home">
64 <span class="breadcrumb-text-truncate text-dark underline">Home</span>
65 </a>
66 </li>
67
68 <#list themeDisplay.getLayout().getAncestors()?reverse as ancestor>
69 <li class="breadcrumb-item">
70 <a href="${ancestor.getFriendlyURL()}" class="breadcrumb-link" title="${d40.escape(ancestor.getHTMLTitle(locale))}">
71 <span class="breadcrumb-text-truncate text-dark underline">
72 ${d40.escape(ancestor.getHTMLTitle(locale))}
73 </span>
74 </a>
75 </li>
76 </#list>
77
78 <li class="breadcrumb-item active">
79 <span class="breadcrumb-text-truncate text-dark">
80 ${doc.details.name[langId]}
81 </span>
82 </li>
83 </ol>
84 </div>
85 </section>
86 </#if>
87
88 <section class="relative mt-20">
89 <div class="container">
90 <div class="card-row flex-col lg:flex-row mb-10">
91 <div class="autofit-col autofit-col-expand order-2 lg:order-1">
92 <div class="autofit-section">
93 <p class="text-dark font-semibold font-heading text-3xl lg:text-4xl uppercase">
94 ${doc.details.name[langId]}
95 </p>
96 </div>
97 </div>
98 <div class="autofit-col justify-start order-1 lg:order-2 mb-3 lg:mb-0">
99 <div class="autofit-section">
100 <div class="flex space-x-4">
101 <button type="button" v-if="isFavorite('${doc.uuid}')" @click="removeFavorite($event, '${doc.uuid}')" class="btn btn-link px-2">
102 <i class="fas fa-heart text-dark fa-2x"></i>
103 </button>
104 <button type="button" v-else @click="setFavorite($event, {id: '${doc.uuid}', contentJSON: {title: '${doc.details.name[langId]?js_string}', previewDescription: '', description: '', geoRef: '', previewPicture: '${d40.getDTNPreview(doc)}', viewUrl: '${d40.getDTNUrl(doc.details.name[langId], doc.uuid, contentType)}'}})" class="btn btn-link px-2">
105 <i class="fal fa-heart text-dark fa-2x"></i>
106 </button>
107
108 <button type="button" @click="share('${doc.details.name[langId]}', '')" class="btn btn-link px-2">
109 <i class="fas fa-share-alt text-dark fa-2x"></i>
110 </button>
111 </div>
112 </div>
113 </div>
114 </div>
115
116 <#if doc.details.description[langId]?has_content>
117 <div class="underline-grow text-dark font-light lg:text-lg leading-loose">
118 ${doc.details.description[langId]}
119 </div>
120 </#if>
121 </div>
122 </section>
123
124 <#if doc.details.images?has_content>
125 <#assign usePreview = false>
126
127 <section id="gallery" class="relative overflow-x-hidden mt-20 lg:mt-40">
128 <div class="container">
129 <p class="text-dark uppercase text-lg lg:text-3xl mb-7">
130 ${d40.getLabel("galleria_immagini")}
131 </p>
132
133 <div class="swiper-container">
134 <div class="swiper-wrapper">
135 <#if doc.details.cover != "[]">
136 <#assign usePreview = true>
137
138 <#list doc.details.cover.iterator() as pic>
139 <div class="swiper-slide">
140 <a href="javascript:void(0);" class="card shadow-none border-0 bg-transparent no-underline" data-toggle="modal" data-target="#lightbox-modal" data-slider="${pic?index}" data-senna-off="true">
141 <div class="relative">
142 <img data-src="${d40.getKitPrefix(pic[langId], 'n-preview_h')}" loading="lazy" class="lazyload rounded-custom size-full fit-cover" alt="" />
143 </div>
144 </a>
145 </div>
146
147 <#break>
148 </#list>
149 </#if>
150
151 <#list doc.details.images.iterator() as pic>
152 <#if usePreview>
153 <#assign counter = pic?counter>
154 <#else>
155 <#assign counter = pic?index>
156 </#if>
157
158 <div class="swiper-slide">
159 <a href="javascript:void(0);" class="card shadow-none border-0 bg-transparent no-underline" data-toggle="modal" data-target="#lightbox-modal" data-slider="${counter}" data-senna-off="true">
160 <div class="relative">
161 <img data-src="${d40.getKitPrefix(pic[langId], 'n-preview_h')}" loading="lazy" class="lazyload rounded-custom size-full fit-cover" alt="" />
162 </div>
163 </a>
164 </div>
165 </#list>
166 </div>
167 </div>
168 </div>
169 <div class="btn-square absolute btn-prev bg-white left-0">
170 <span class="sr-only">${d40.getLabel("precedente")}</span>
171 <i class="fas fa-chevron-left text-dark w-6 h-6"></i>
172 </div>
173 <div class="btn-square absolute btn-next bg-white right-0">
174 <span class="sr-only">${d40.getLabel("successivo")}</span>
175 <i class="fas fa-chevron-right text-dark w-6 h-6"></i>
176 </div>
177 </section>
178
179 <div id="lightbox-modal" tabindex="-1" role="dialog" class="modal fade">
180 <div class="modal-dialog modal-full-screen inset-0 border-0 shadow-none">
181 <div class="modal-content bg-transparent">
182 <div class="modal-body overflow-hidden">
183 <div class="absolute top-0 right-0 z-5">
184 <button type="button" data-dismiss="modal" class="btn btn-monospaced btn-secondary m-3">
185 <span class="sr-only">${d40.getLabel("chiudi")}</span>
186 <i class="fal fa-times fa-lg"></i>
187 </button>
188 </div>
189
190 <#-- lightbox -->
191 <div id="lightbox-swiper" class="swiper-container h-75 my-auto">
192 <div class="swiper-button-prev w-12 h-12">
193 <span class="sticker sticker-circle bg-green size-full">
194 <span class="sticker-overlay">
195 <i class="fas fa-chevron-left fa-2x text-white"></i>
196 </span>
197 </span>
198 </div>
199
200 <div class="swiper-button-next w-12 h-12">
201 <span class="sticker sticker-circle bg-green size-full">
202 <span class="sticker-overlay">
203 <i class="fas fa-chevron-right fa-2x text-white"></i>
204 </span>
205 </span>
206 </div>
207
208 <div class="swiper-wrapper h-full">
209 <#if doc.details.cover != "[]">
210 <#list doc.details.cover.iterator() as pic>
211 <div class="swiper-slide h-full">
212 <img data-src="${d40.getKitPrefix(pic[langId], 'n-full_hd')}" loading="lazy" class="lazyload flex h-full object-contain mx-auto" alt="">
213 </div>
214 <#break>
215 </#list>
216 </#if>
217
218 <#list doc.details.images.iterator() as pic>
219 <div class="swiper-slide h-full">
220 <img data-src="${d40.getKitPrefix(pic[langId], 'n-full_hd')}" loading="lazy" class="lazyload flex h-full object-contain mx-auto" alt="">
221 </div>
222 </#list>
223 </div>
224 </div>
225
226 <#-- thumbnails -->
227 <div id="thumbnail-swiper" class="swiper-container" thumbsSlider="">
228 <div class="swiper-wrapper">
229 <#if doc.details.cover != "[]">
230 <#list doc.details.cover.iterator() as pic>
231 <div class="swiper-slide">
232 <img data-src="${d40.getKitPrefix(pic[langId], 'n-preview_h')}" loading="lazy" class="lazyload object-cover cursor-pointer size-full" alt="">
233 </div>
234 <#break>
235 </#list>
236 </#if>
237
238 <#list doc.details.images.iterator() as pic>
239 <div class="swiper-slide">
240 <img data-src="${d40.getKitPrefix(pic[langId], 'n-preview_h')}" loading="lazy" class="lazyload object-cover cursor-pointer size-full" alt="">
241 </div>
242 </#list>
243 </div>
244 </div>
245 </div>
246 </div>
247 </div>
248 </div>
249 </#if>
250
251 <#if doc.details.categories?has_content>
252 <section class="mt-20">
253 <div class="container">
254 <div class="border-t-2 border-b-2 border-light py-10">
255 <div class="row">
256 <#list doc.details.categories.iterator() as cat>
257 <div class="col-12 col-md-6 col-lg-4 mb-7">
258 <div class="card-row space-x-4">
259 <div class="autofit-col">
260 <div class="autofit-section">
261 <div class="flex items-center justify-center w-18 h-18 rounded-full border-1 border-dark">
262 <i class="fas fa-user fa-lg"></i>
263 </div>
264 </div>
265 </div>
266 <div class="autofit-col autofit-col-expand">
267 <div class="autofit-section">
268 <p class="text-dark font-bold uppercase mb-1">
269 ${cat}
270 </p>
271
272 <#--
273 <p class="text-dark mb-0">
274 Details
275 </p>
276 -->
277 </div>
278 </div>
279 </div>
280 </div>
281 </#list>
282 </div>
283 </div>
284 </div>
285 </section>
286 </#if>
287
288 <#if hasCoords>
289 <section class="mt-20">
290 <div class="bg-light py-10">
291 <div class="container">
292 <div class="flex justify-between items-center">
293 <div>
294 <p class="text-dark uppercase text-lg font-bold mb-0">
295 ${d40.getLabel("posizione")}
296 </p>
297 </div>
298
299 <div>
300 <a href="https://www.google.com/maps/@${lat},${lng},14z" class="d-block no-underline space-x-5" target="_blank">
301 <span class="font-bold text-dark uppercase">
302 ${d40.getLabel("indicazioni_stradali")}
303 </span>
304 <img src="${d40.icons_folder}/arrow_circle-dark.svg" class="w-20" />
305 </a>
306 </div>
307 </div>
308
309 <div class="mt-5">
310 <div id="suggesto-map" class="monochrome-map h-500px"></div>
311 </div>
312 </div>
313 </div>
314 </section>
315 </#if>
316
317 <section class="mt-20">
318 <div class="container">
319 <div class="bg-light">
320 <div class="space-y-10 p-5 lg:p-10">
321 <p class="text-dark uppercase text-lg lg:text-3xl mb-0">
322 ${d40.getLabel("contatti")}
323 </p>
324 <div class="space-y-2">
325 <#if doc.contacts.getJSONObject(0).address.fullText?has_content>
326 <p class="text-dark mb-0">
327 ${doc.contacts.getJSONObject(0).address.fullText?has_content}
328 </p>
329 </#if>
330
331 <#if doc.contacts.getJSONObject(0).mail?? && doc.contacts.getJSONObject(0).mail?has_content>
332 <div>
333 <span class="text-dark font-bold">${d40.getLabel("email")}: </span>
334 <a href="mailto:${doc.contacts.getJSONObject(0).mail}" class="text-dark underline">
335 ${doc.contacts.getJSONObject(0).mail}
336 </a>
337 </div>
338 </#if>
339
340 <#if doc.contacts.getJSONObject(0).telephone?? && doc.contacts.getJSONObject(0).telephone?has_content>
341 <#list doc.contacts.getJSONObject(0).telephone.iterator() as phone>
342 <p class="text-dark mb-0">
343 <span class="font-bold">${d40.getLabel("tel")}:</span> ${phone}
344 </p>
345 </#list>
346 </#if>
347
348 <#if doc.contacts.getJSONObject(0).websiteUrl[langId]?? && doc.contacts.getJSONObject(0).websiteUrl[langId]?has_content>
349 <a href="${doc.contacts.getJSONObject(0).websiteUrl}" class="d-block text-dark no-underline">
350 <span class="font-bold">${d40.getLabel("sito_web")}: </span>
351 <u>${doc.contacts.getJSONObject(0).websiteUrl[langId]}</u>
352 </a>
353 </#if>
354
355 <#--
356 <#if doc.contacts.getJSONObject(0).socialRefs != "{}">
357 <a href="#" class="d-block text-dark underline">
358 <span class="font-bold">Social: </span>
359 Social test
360 </a>
361 </#if>
362 -->
363 </div>
364 </div>
365 </div>
366 </div>
367 </section>
368 <#else>
369 <div class="text-center mt-35">
370 <p class="text-dark">Errore durante il caricamento del contenuto.</p>
371 </div>
372 </#if>
373</div>
374
375<script type="module">
376 import { createApp } from "https://cdnjs.cloudflare.com/ajax/libs/petite-vue/0.4.1/petite-vue.es.min.js";
377
378 createApp({
379 isMobile: window.innerWidth < 768,
380 favorites: [],
381 getFavorites() {
382 if(localStorage.getItem("favorites") !== null) {
383 this.favorites = JSON.parse(localStorage.getItem("favorites"));
384 }
385 },
386 setFavorite(e, item) {
387 e.preventDefault();
388 this.getFavorites();
389
390 var newFavorite = {
391 id: item.id,
392 contentJSON: {
393 title: item.contentJSON.title,
394 previewDescription: item.contentJSON.previewDescription,
395 description: item.contentJSON.description,
396 geoRef: item.contentJSON.geoRef,
397 previewPicture: item.contentJSON.previewPicture,
398 viewUrl: item.contentJSON.viewUrl,
399 type: "lfr",
400 }
401 };
402
403 this.favorites.push(newFavorite);
404 this.saveFavorites();
405 },
406 removeFavorite(e, itemId) {
407 e.preventDefault();
408 this.favorites = this.favorites.filter((obj) => obj.id !== itemId);
409 this.saveFavorites();
410 },
411 saveFavorites() {
412 localStorage.setItem("favorites", JSON.stringify(this.favorites));
413 },
414 isFavorite(itemId) {
415 if (this.favorites.some((e) => e.id == itemId)) {
416 return true;
417 }
418
419 return false;
420 },
421 buildMap(lat, lng) {
422 var map = new SuggestoMap("suggesto-map"),
423 location = [parseFloat(lat), parseFloat(lng)];
424
425 var svgIcon = L.divIcon({
426 html: `
427 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44.537 64.343">
428 <path d="M32.952,68c-4.491-5.935-9.221-11.692-12.837-18.251-3.4-6.175-5.892-12.453-4.094-19.725,2.422-9.787,12.253-17.363,22.112-16.878,11.322.558,20.189,8.638,21.649,19.385.8,5.867-1.25,10.892-3.84,15.837A105.38,105.38,0,0,1,43.958,65.889c-.687.844-1.309,1.739-1.96,2.611-.953,1.06-1.971,2.068-2.841,3.191-.965,1.245-1.729,1.629-2.844.1A50.506,50.506,0,0,0,32.952,68m4.722-46.328A14.156,14.156,0,0,0,23.76,35.59c-.168,7.053,6.542,13.748,13.813,13.783A14.234,14.234,0,0,0,51.46,35.693,14.138,14.138,0,0,0,37.674,21.676" transform="translate(-15.419 -13.129)" fill="#5098c6"/>
429 <path d="M31.941,63.206A50.288,50.288,0,0,1,35.3,67c1.116,1.526,1.878,1.142,2.844-.1.87-1.123,1.889-2.132,2.841-3.191a8.579,8.579,0,0,1,5.414,2.22c1.659,1.642,1.586,3.631-.455,4.627a21.209,21.209,0,0,1-16.871.723c-1.611-.565-3.293-1.51-3.092-3.645.19-2.018,1.855-2.718,3.511-3.342.833-.315,1.791-.363,2.447-1.079" transform="translate(-14.408 -8.331)" fill="#222"/>
430 <path d="M36.946,20.929A14.137,14.137,0,0,1,50.732,34.944,14.236,14.236,0,0,1,36.844,48.626c-7.27-.036-13.98-6.732-13.813-13.783A14.157,14.157,0,0,1,36.946,20.929m-.023,5.445a8.4,8.4,0,1,0,8.394,8.22,8.5,8.5,0,0,0-8.394-8.22" transform="translate(-14.69 -12.381)" fill="#fdfcf9"/>
431 <path d="M36.45,25.9a8.4,8.4,0,0,1,.077,16.809A8.405,8.405,0,1,1,36.45,25.9" transform="translate(-14.218 -11.905)" fill="#222"/>
432 </svg>`,
433 className: "",
434 iconSize: [44.537, 64.343],
435 iconAnchor: [22.287, 64.343],
436 });
437
438 var mapData = {
439 tilelayer: "osm",
440 gestureHandling: true,
441 fitBounds: false,
442 mapcenter: location,
443 zoom: 15,
444 markersFilter: "*",
445 markers: [],
446 layers: []
447 };
448
449 map.sm.createMap(mapData);
450
451 L.circleMarker(location, {
452 radius: 0,
453 color: "#000",
454 fillOpacity: 0,
455 weight: 0
456 }).addTo(map.sm.lmap);
457
458 L.marker(location, {
459 icon: svgIcon
460 }).addTo(map.sm.lmap);
461 },
462 share(title, desc, mode = "legacy") {
463 if (this.isMobile) {
464 var shareData = {
465 title: title,
466 text: desc,
467 url: window.location.href.replaceAll("{", "%7B").replaceAll("}", "%7D")
468 };
469
470 try {
471 navigator.share(shareData);
472 } catch (err) {
473 console.error("Error sharing: " + err);
474
475 if (mode == "legacy") {
476 this.legacyShare();
477 } else if (mode == "social") {
478 this.socialShare(title, desc);
479 }
480 }
481 } else {
482 if (mode == "legacy") {
483 this.legacyShare();
484 } else if (mode == "social") {
485 this.socialShare(title, desc);
486 }
487 }
488 },
489 legacyShare() {
490 var clip = document.createElement("input"),
491 toCopy = window.location.href.replaceAll("{", "%7B").replaceAll("}", "%7D");
492
493 document.body.appendChild(clip);
494 clip.value = toCopy;
495 clip.select();
496 clip.setSelectionRange(0, 999999);
497
498 navigator.clipboard.writeText(clip.value);
499 document.body.removeChild(clip);
500
501 alert("Link copiato");
502 },
503 socialShare(title, desc, image = "") {
504 var link = "#",
505 winHeight = 450,
506 winWidth = 600,
507 winTop = window.screen.height / 2 - winHeight / 2,
508 winLeft = window.screen.width / 2 - winWidth / 2,
509 params = "scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,width=" + winWidth + ",height=" + winHeight + ",left=" + winLeft + ",top=" + winTop;
510
511 link = "http://www.facebook.com/sharer.php?s=100&p[title]=" + encodeURIComponent(title) + "&p[summary]=" + encodeURIComponent(desc) + "&p[url]=" + window.location.href + "&p[images][0]=" + image;
512
513 window.open(link, "Facebook", params);
514 }
515 }).mount("#${d40.portletNamespace}");
516</script>
517
518<#if doc?has_content>
519 <script>
520 document.addEventListener("DOMContentLoaded", function(){
521 if(${(doc.details.images?has_content)?c}){
522 new Swiper("#gallery .swiper-container", {
523 slidesPerView: 1,
524 spaceBetween: 50,
525 centerSlides: true,
526 centerInsufficientSlides: true,
527 breakpoints: {
528 768: {
529 slidesPerView: 2,
530 spaceBetween: 25
531 },
532 992: {
533 slidesPerView: 3,
534 spaceBetween: 25
535 }
536 },
537 navigation: {
538 prevEl: "#gallery .btn-prev",
539 nextEl: "#gallery .btn-next"
540 }
541 });
542
543 var thumbnails = new Swiper("#thumbnail-swiper", {
544 spaceBetween: 10,
545 slidesPerView: 2,
546 centerInsufficientSlides: true,
547 freeMode: true,
548 watchSlidesVisibility: true,
549 watchSlidesProgress: true,
550 breakpoints: {
551 768: {
552 slidesPerView: 3
553 },
554 992: {
555 slidesPerView: 4
556 },
557 1200: {
558 slidesPerView: 5
559 }
560 },
561 });
562
563 var lightbox = new Swiper("#lightbox-swiper", {
564 spaceBetween: 10,
565 autoHeight: true,
566 observer: true,
567 observeSlideChildren: true,
568 keyboard: {
569 enabled: true
570 },
571 navigation: {
572 nextEl: ".swiper-button-next",
573 prevEl: ".swiper-button-prev"
574 },
575 thumbs: {
576 swiper: thumbnails
577 }
578 });
579
580 $("#lightbox-modal").on("shown.bs.modal", function(e){
581 lightbox.update();
582 thumbnails.update();
583 lightbox.slideTo($(e.relatedTarget).data("slider"));
584 lightbox.update();
585 });
586
587 $("#lightbox-modal .swiper-wrapper").on("click", function(e){
588 if($(e.target).attr("class").includes("swiper-slide")){
589 $("#lightbox-modal").modal("hide");
590 }
591 });
592 }
593 });
594 </script>
595</#if>