{{ERROR}}
\"),this.setContent(t,i,{suffix:\"error\"})}showLoading(t){t.state=\"loading\",t.$el.classList.add(\"is-loading\");let e=t.$el.querySelector(\".fancybox__spinner\");e||(e=document.createElement(\"div\"),e.classList.add(\"fancybox__spinner\"),e.innerHTML=this.option(\"template.spinner\"),e.addEventListener(\"click\",(()=>{this.Carousel.Panzoom.velocity||this.close()})),t.$el.prepend(e))}hideLoading(t){const e=t.$el&&t.$el.querySelector(\".fancybox__spinner\");e&&(e.remove(),t.$el.classList.remove(\"is-loading\")),\"loading\"===t.state&&(this.trigger(\"load\",t),t.state=\"ready\")}next(){const t=this.Carousel;t&&t.pages.length>1&&t.slideNext()}prev(){const t=this.Carousel;t&&t.pages.length>1&&t.slidePrev()}jumpTo(...t){this.Carousel&&this.Carousel.slideTo(...t)}isClosing(){return[\"closing\",\"customClosing\",\"destroy\"].includes(this.state)}isTopmost(){return R.getInstance().id==this.id}close(t){if(t&&t.preventDefault(),this.isClosing())return;if(!1===this.trigger(\"shouldClose\",t))return;if(this.state=\"closing\",this.Carousel.Panzoom.destroy(),this.detachEvents(),this.trigger(\"closing\",t),\"destroy\"===this.state)return;this.$container.setAttribute(\"aria-hidden\",\"true\"),this.$container.classList.add(\"is-closing\");const e=this.getSlide();if(this.Carousel.slides.forEach((t=>{t.$content&&t.index!==e.index&&this.Carousel.trigger(\"removeSlide\",t)})),\"closing\"===this.state){const t=void 0===e.hideClass?this.option(\"hideClass\"):e.hideClass;this.animateCSS(e.$content,t,(()=>{this.destroy()}),!0)}}destroy(){if(\"destroy\"===this.state)return;this.state=\"destroy\",this.trigger(\"destroy\");const t=this.option(\"placeFocusBack\")?this.option(\"triggerTarget\",this.getSlide().$trigger):null;this.Carousel.destroy(),this.detachPlugins(),this.Carousel=null,this.options={},this.events={},this.$container.remove(),this.$container=this.$backdrop=this.$carousel=null,t&&w(t),I.delete(this.id);const e=R.getInstance();e?e.focus():(document.documentElement.classList.remove(\"with-fancybox\"),document.body.classList.remove(\"is-using-mouse\"),this.revealScrollbar())}static show(t,e={}){return new R(t,e)}static fromEvent(t,e={}){if(t.defaultPrevented)return;if(t.button&&0!==t.button)return;if(t.ctrlKey||t.metaKey||t.shiftKey)return;const i=t.composedPath()[0];let s,o,n,a=i;if((a.matches(\"[data-fancybox-trigger]\")||(a=a.closest(\"[data-fancybox-trigger]\")))&&(e.triggerTarget=a,s=a&&a.dataset&&a.dataset.fancyboxTrigger),s){const t=document.querySelectorAll(`[data-fancybox=\"${s}\"]`),e=parseInt(a.dataset.fancyboxIndex,10)||0;a=t.length?t[e]:a}Array.from(R.openers.keys()).reverse().some((e=>{n=a||i;let s=!1;try{n instanceof Element&&(\"string\"==typeof e||e instanceof String)&&(s=n.matches(e)||(n=n.closest(e)))}catch(t){}return!!s&&(t.preventDefault(),o=e,!0)}));let r=!1;if(o){e.event=t,e.target=n,n.origTarget=i,r=R.fromOpener(o,e);const s=R.getInstance();s&&\"ready\"===s.state&&t.detail&&document.body.classList.add(\"is-using-mouse\")}return r}static fromOpener(t,i={}){let s=[],o=i.startIndex||0,n=i.target||null;const a=void 0!==(i=e({},i,R.openers.get(t))).groupAll&&i.groupAll,r=void 0===i.groupAttr?\"data-fancybox\":i.groupAttr,h=r&&n?n.getAttribute(`${r}`):\"\";if(!n||h||a){const e=i.root||(n?n.getRootNode():document.body);s=[].slice.call(e.querySelectorAll(t))}if(n&&!a&&(s=h?s.filter((t=>t.getAttribute(`${r}`)===h)):[n]),!s.length)return!1;const l=R.getInstance();return!(l&&s.indexOf(l.options.$trigger)>-1)&&(o=n?s.indexOf(n):o,s=s.map((function(t){const e=[\"false\",\"0\",\"no\",\"null\",\"undefined\"],i=[\"true\",\"1\",\"yes\"],s=Object.assign({},t.dataset),o={};for(let[t,n]of Object.entries(s))if(\"fancybox\"!==t)if(\"width\"===t||\"height\"===t)o[`_${t}`]=n;else if(\"string\"==typeof n||n instanceof String)if(e.indexOf(n)>-1)o[t]=!1;else if(i.indexOf(o[t])>-1)o[t]=!0;else try{o[t]=JSON.parse(n)}catch(e){o[t]=n}else o[t]=n;return t instanceof Element&&(o.$trigger=t),o})),new R(s,e({},i,{startIndex:o,$trigger:n})))}static bind(t,e={}){function i(){document.body.addEventListener(\"click\",R.fromEvent,!1)}v&&(R.openers.size||(/complete|interactive|loaded/.test(document.readyState)?i():document.addEventListener(\"DOMContentLoaded\",i)),R.openers.set(t,e))}static unbind(t){R.openers.delete(t),R.openers.size||R.destroy()}static destroy(){let t;for(;t=R.getInstance();)t.destroy();R.openers=new Map,document.body.removeEventListener(\"click\",R.fromEvent,!1)}static getInstance(t){if(t)return I.get(t);return Array.from(I.values()).reverse().find((t=>!t.isClosing()&&t))||null}static close(t=!0,e){if(t)for(const t of I.values())t.close(e);else{const t=R.getInstance();t&&t.close(e)}}static next(){const t=R.getInstance();t&&t.next()}static prev(){const t=R.getInstance();t&&t.prev()}}R.version=\"4.0.31\",R.defaults=M,R.openers=new Map,R.Plugins=O,R.bind(\"[data-fancybox]\");for(const[t,e]of Object.entries(R.Plugins||{}))\"function\"==typeof e.create&&e.create(R);export{y as Carousel,R as Fancybox,d as Panzoom};\n","import React, { useEffect } from 'react';\r\nimport { Fancybox as NativeFancybox } from '@fancyapps/ui/dist/fancybox.esm.js';\r\nimport '@fancyapps/ui/dist/fancybox.css';\r\n\r\nfunction ProductFullScreenHandler(props) {\r\n const delegate = props.delegate || '[data-fancybox]';\r\n\r\n useEffect(() => {\r\n const opts = props.options || {};\r\n\r\n NativeFancybox.bind(delegate, opts);\r\n NativeFancybox.show(props.items, opts);\r\n\r\n return () => {\r\n NativeFancybox.destroy();\r\n };\r\n }, []);\r\n\r\n return <>>;\r\n}\r\n\r\nexport default ProductFullScreenHandler;\r\n","require('intersection-observer');\r\nimport { each, some } from 'lodash';\r\n\r\n// https://developers.google.com/web/updates/2016/04/intersectionobserver\r\nconst _observeItem = function ({ element, threshold = 0.5, callback }) {\r\n // \"10% visible for more than a continuous second\"\r\n\r\n const isVisible = function (boundingClientRect, intersectionRect) {\r\n return (\r\n (intersectionRect.width * intersectionRect.height) /\r\n (boundingClientRect.width * boundingClientRect.height) >=\r\n threshold\r\n );\r\n };\r\n\r\n const visibleTimerCallback = function (element, observer) {\r\n delete element.visibleTimeout;\r\n // Process any pending observations\r\n processChanges(observer.takeRecords());\r\n if ('isVisible' in element) {\r\n delete element.isVisible;\r\n observer.unobserve(element);\r\n }\r\n };\r\n\r\n function processChanges(changes) {\r\n each(changes, function (changeRecord) {\r\n var element = changeRecord.target;\r\n element.isVisible = isVisible(\r\n changeRecord.boundingClientRect,\r\n changeRecord.intersectionRect\r\n );\r\n if (element.isVisible) {\r\n // Transitioned from hidden to visible\r\n // console.log(\"Became visibile\");\r\n callback(element);\r\n element.visibleTimeout = setTimeout(\r\n visibleTimerCallback,\r\n 1000,\r\n element,\r\n observer\r\n );\r\n } else {\r\n // Transitioned from visible to hidden\r\n // console.log(\"invisible\");\r\n if ('visibleTimeout' in element) {\r\n clearTimeout(element.visibleTimeout);\r\n delete element.visibleTimeout;\r\n }\r\n }\r\n });\r\n }\r\n\r\n var observer = new IntersectionObserver(processChanges, {\r\n threshold: [threshold],\r\n });\r\n\r\n observer.observe(element);\r\n};\r\n\r\nconst _observeItems = ({ items, imageIdentifier, sourceAttribute, cb }) => {\r\n each(items, (item) => {\r\n const itm = item;\r\n const imagesToLazyLoad = itm.getElementsByClassName(imageIdentifier);\r\n\r\n each(imagesToLazyLoad, (imageToLazyLoad) => {\r\n if (imageToLazyLoad !== undefined) {\r\n const url =\r\n imageToLazyLoad.getAttribute(sourceAttribute) ||\r\n imageToLazyLoad.getAttribute('data-src');\r\n const shouldLazyLoad =\r\n (url !== '' &&\r\n url !== null &&\r\n url !== undefined &&\r\n imageToLazyLoad.isSrcAlreadySet === undefined) ||\r\n imageToLazyLoad.getAttribute('data-src') !==\r\n imageToLazyLoad.getAttribute('src');\r\n\r\n _observeItem({\r\n element: itm,\r\n threshold: 0.1,\r\n callback: function () {\r\n imageToLazyLoad.isSrcAlreadySet =\r\n imageToLazyLoad.getAttribute('data-src') ===\r\n imageToLazyLoad.getAttribute('src');\r\n if (shouldLazyLoad) {\r\n if (!imageToLazyLoad.isSrcAlreadySet) {\r\n imageToLazyLoad.setAttribute('src', url);\r\n\r\n if (cb) {\r\n cb(imageToLazyLoad);\r\n }\r\n imageToLazyLoad.isSrcAlreadySet =\r\n imageToLazyLoad.getAttribute('data-src') ===\r\n imageToLazyLoad.getAttribute('src');\r\n imageToLazyLoad.classList.add(\r\n 'image-lazyloaded'\r\n );\r\n }\r\n }\r\n },\r\n });\r\n }\r\n });\r\n });\r\n};\r\n\r\nconst setIntervalX = function (callback, delay, repetitions) {\r\n var x = 0;\r\n var intervalID = window.setInterval(function () {\r\n callback();\r\n\r\n if (++x === repetitions) {\r\n window.clearInterval(intervalID);\r\n }\r\n }, delay);\r\n return intervalID;\r\n};\r\n\r\nconst observeArticleImages = () => {\r\n // Per ogni .article__image visibile per almeno 10% della superficie,\r\n // richiedo al server la immagine.\r\n // Se l'utente ha in viewport l'immagine per + di 1 secondo, non la richiedo più\r\n // e sgancio l'observer\r\n var intervalTryId = setIntervalX(\r\n function () {\r\n var items = document.getElementsByClassName('article');\r\n if (items && items.length > 0) {\r\n _observeItems({\r\n items: items,\r\n imageIdentifier: 'article__image',\r\n });\r\n window.clearInterval(intervalTryId);\r\n }\r\n },\r\n //1000,\r\n 300,\r\n 8\r\n );\r\n};\r\n\r\nconst observeBannerImages = () => {\r\n var items = document.getElementsByClassName('banner__link');\r\n\r\n each(items, (item) => {\r\n const itm = item;\r\n const sourcesToLazyLoad = itm.getElementsByTagName('source');\r\n const imageToLazyLoad = itm.getElementsByClassName('banner__img')[0];\r\n\r\n if (imageToLazyLoad !== undefined) {\r\n const url = imageToLazyLoad.getAttribute('data-src') || '';\r\n const shouldLazyLoad =\r\n url !== '' &&\r\n some(\r\n sourcesToLazyLoad,\r\n (s) => (s.getAttribute('data-srcset') || '') !== ''\r\n );\r\n\r\n _observeItem({\r\n element: itm,\r\n threshold: 0.1,\r\n callback: function () {\r\n if (shouldLazyLoad) {\r\n if (!imageToLazyLoad.isSrcAlreadySet) {\r\n each(sourcesToLazyLoad, (s) => {\r\n const srcset =\r\n s.getAttribute('data-srcset') || '';\r\n s.setAttribute('srcset', srcset);\r\n });\r\n\r\n imageToLazyLoad.setAttribute('src', url);\r\n imageToLazyLoad.isSrcAlreadySet = true;\r\n imageToLazyLoad.classList.add('image-lazyloaded');\r\n }\r\n }\r\n },\r\n });\r\n }\r\n });\r\n};\r\n\r\nexport default {\r\n observeArticleImages,\r\n observeBannerImages,\r\n observeReviewGallery: function ({ element, cb }) {\r\n if (element) {\r\n _observeItem({\r\n element,\r\n callback: (imageToLazyLoad) => {\r\n if (imageToLazyLoad) {\r\n return cb(imageToLazyLoad);\r\n }\r\n return undefined;\r\n },\r\n });\r\n }\r\n return undefined;\r\n },\r\n observeQuestionGallery: function ({ element, cb }) {\r\n if (element) {\r\n _observeItem({\r\n element,\r\n callback: (imageToLazyLoad) => {\r\n if (imageToLazyLoad) {\r\n return cb(imageToLazyLoad);\r\n }\r\n return undefined;\r\n },\r\n });\r\n }\r\n return undefined;\r\n },\r\n};\r\n","require('flickity-fullscreen');\r\nimport React, { useState, useRef, useEffect } from 'react';\r\nimport Flickity from 'react-flickity-component';\r\nimport { isEmpty, isNil, map } from 'lodash';\r\nimport Globalizer from '../../../globalizer';\r\nimport common from '../../../common/common';\r\nimport translateService from '../../../common/translateService';\r\nimport {\r\n userVoteYes,\r\n userVoteNo,\r\n reportAbuse,\r\n} from '../../../services/reviews/reviewsService';\r\nimport {\r\n toggleNextElementVisibility,\r\n handleAbuseTypeChange,\r\n} from '../../../services/product/productInteraction';\r\nimport ReviewStars from './ReviewStars.jsx';\r\nimport ProductFullScreenHandler from '../products/ProductFullScreenHandler.jsx';\r\nimport deferredImagesLoader from '../../../deferredImagesLoader';\r\n\r\nconst ProductReview = ({ review, isAuthenticated, isMobile, slug }) => {\r\n Globalizer.init();\r\n const reviewRef = useRef(null);\r\n const flktyHtmlRef = useRef(null);\r\n const reportAbuseFormRef = useRef(null);\r\n const [reviewState, setReviewState] = useState(review);\r\n const [flktyRef, setFlktyRef] = useState();\r\n const [abuseType, setAbuseType] = useState('');\r\n const [fullScreenActive, setFullScreenActive] = useState(false);\r\n const [indexZoom, setIndexZoom] = useState(0);\r\n\r\n useEffect(() => {\r\n setReviewState(review);\r\n\r\n deferredImagesLoader.observeReviewGallery({\r\n element: reviewRef.current,\r\n cb: (imageToLazyLoad) => {\r\n if (imageToLazyLoad) {\r\n setReviewState({ ...review, renderImages: true });\r\n }\r\n },\r\n });\r\n }, [review]);\r\n\r\n const handleRadioButtonClick = (e) => {\r\n handleAbuseTypeChange(e, reportAbuseFormRef);\r\n const type = e.currentTarget.value;\r\n setAbuseType(type);\r\n };\r\n\r\n async function handleReviewIsUseful() {\r\n if (!isAuthenticated) {\r\n window.location = common.getSiteUrl(\r\n `${\r\n window.$pathBase\r\n }Account/LogOn?returnUrl=${encodeURIComponent(\r\n window.$pathBase + slug + window.location.search\r\n )}`\r\n );\r\n return;\r\n }\r\n if (isAuthenticated && !reviewState.userAlreadyVotedForUseful) {\r\n const result = await userVoteYes(reviewState.id, slug);\r\n if (result.isSuccess) {\r\n setReviewState((prevState) => {\r\n const uselessCount = prevState.userAlreadyVotedForUseless\r\n ? prevState.uselessCount - 1\r\n : prevState.uselessCount;\r\n const usefulCount = prevState.usefulCount + 1;\r\n\r\n return {\r\n ...prevState,\r\n userAlreadyVotedForUseful: true,\r\n userAlreadyVotedForUseless: false,\r\n usefulCount: usefulCount,\r\n uselessCount: uselessCount,\r\n };\r\n });\r\n }\r\n }\r\n }\r\n\r\n async function handleReviewIsUseless() {\r\n if (!isAuthenticated) {\r\n window.location = common.getSiteUrl(\r\n `${\r\n window.$pathBase\r\n }Account/LogOn?returnUrl=${encodeURIComponent(\r\n window.$pathBase + slug + window.location.search\r\n )}`\r\n );\r\n return;\r\n }\r\n if (isAuthenticated && !reviewState.userAlreadyVotedForUseless) {\r\n const result = await userVoteNo(reviewState.id, slug);\r\n if (result.isSuccess) {\r\n setReviewState((prevState) => {\r\n const usefulCount = prevState.userAlreadyVotedForUseful\r\n ? prevState.usefulCount - 1\r\n : prevState.usefulCount;\r\n const uselessCount = prevState.uselessCount + 1;\r\n\r\n return {\r\n ...prevState,\r\n userAlreadyVotedForUseful: false,\r\n userAlreadyVotedForUseless: true,\r\n usefulCount: usefulCount,\r\n uselessCount: uselessCount,\r\n };\r\n });\r\n }\r\n }\r\n }\r\n\r\n async function handleSendReportAbuse() {\r\n const result = await reportAbuse(reviewState.id, slug, abuseType);\r\n\r\n if (result.isSuccess) {\r\n reportAbuseFormRef.current.classList.remove('open');\r\n }\r\n }\r\n\r\n const handleImageClick = (e, index) => {\r\n setFullScreenActive(!fullScreenActive);\r\n setIndexZoom(index);\r\n };\r\n\r\n const images = map(reviewState.images, (image, idx) => (\r\n\r\n \r\n {translateService.get(`reviews.ratingLabel`)}\r\n {' '}\r\n
\r\n \r\n {translateService.get(\r\n `reviews.qualityPriceRatio`\r\n )}\r\n {' '}\r\n
\r\n {`${\r\n reviewState.user.nickname\r\n } | ${Globalizer.formatDateLong(\r\n reviewState.creationDate\r\n )}`}\r\n
\r\n\r\n {translateService.get(`productDetail.isUseful`)}{' '}\r\n \r\n {' '}\r\n {reviewState.usefulCount}\r\n {' '}\r\n \r\n
{' '}\r\n {reviewState.uselessCount}\r\n \r\n
\r\n {translateService.get(`reviews.reportAbuse`)}\r\n
\r\n\r\n {translateService.get(`reviews.abuseKind`)}\r\n
\r\n \r\n \r\n \r\n\r\n \r\n {translateService.get(\r\n `productDetail.send`\r\n )}\r\n \r\n
\r\n{translateService.get(`reviews.mustBeLoggedIn`)}
\r\n{\r\n window.location = common.getSiteUrl(\r\n `${\r\n window.$pathBase\r\n }Account/LogOn?returnUrl=${encodeURIComponent(\r\n reviews.allReviewsLink +\r\n window.location.search\r\n )}`\r\n );\r\n }}>\r\n {translateService.get(`reviews.clickToLogIn`)}\r\n
\r\n >\r\n ) : !isEmpty(reviews.reviews) ? (\r\n{`${reviews.percAverageRating5}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.excellent`\r\n )}\r\n
\r\n{`${reviews.percAverageRating4}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.good`\r\n )}\r\n
\r\n{`${reviews.percAverageRating3}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.average`\r\n )}\r\n
\r\n{`${reviews.percAverageRating2}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.poor`\r\n )}\r\n
\r\n{`${reviews.percAverageRating1}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.bad`\r\n )}\r\n
\r\n{`${reviews.percAverageQuality5}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.excellent`\r\n )}\r\n
\r\n{`${reviews.percAverageQuality4}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.good`\r\n )}\r\n
\r\n{`${reviews.percAverageQuality3}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.average`\r\n )}\r\n
\r\n{`${reviews.percAverageQuality2}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.poor`\r\n )}\r\n
\r\n{`${reviews.percAverageQuality1}%`}
\r\n\r\n
\r\n {translateService.get(\r\n `form.ratings.bad`\r\n )}\r\n
\r\n{translateService.get(`reviews.noReviews`)}
\r\n )}\r\n