1const A=(()=>{const e=[];let t=null;const o=e=>{"loading"!==document.readyState?e():document.addEventListener("DOMContentLoaded",e)},r=()=>{clearInterval(t),t=null,e.forEach((e=>e()))};return o((()=>{t||(t=setInterval((()=>{document.querySelectorAll(".a-aem-module:not(.a-header, .a-main, .a-footer, .experiencefragment, .workday-reader-pagination) > *, a-mo-oferta, a-mo-wysiwyg").length&&0===document.querySelectorAll(".a-aem-module:not(.a-header, .a-main, .a-footer, .experiencefragment, .workday-reader-pagination) > *:not(.hydrated, div), a-mo-oferta:not(.hydrated), a-mo-wysiwyg:not(.hydrated)").length&&r()}),300))})),{ready:o,loaded:t=>{e.push(t)}}})();var refFn;A.loaded((()=>{const e=e=>"A"===e.tagName||e.matches("a-button-cta")||e.matches("a-button-action")||e.matches("a-button-icon")||e.matches("a-link")||e.matches("a-highlight-title")||e.matches("a-file-download"),t=t=>e(t)&&t.getAttribute("href")?.startsWith("http")&&"_blank"===t.getAttribute("target"),o=e=>e?.trim()?.toLowerCase()?.normalize("NFD")?.replace(/[\u0300-\u036f]/g,"")?.replace(/[\s,\-]+/g,"_")?.replace(/[^a-z0-9ñ_=|/]/g,""),r=e=>{if(!e)return null;const t=e.cloneNode(!0);return t.querySelectorAll(".cmp-link__screen-reader-only").forEach((e=>e.remove())),o(t.textContent)},a=e=>{const t=window.location.href.replace(window.location.origin,"").trim().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[\s,]+/g,"_").replace(/[^a-z0-9ñ_/]/g,"");let o=e;for(;o;){const e=o.getRootNode();if(!(e instanceof ShadowRoot))break;o=e.host}for(;o&&!o.tagName?.toLowerCase().startsWith("a-");)o=o.parentElement;if(o){const e=o.querySelector('[slot="title"]'),t=e?.getAttribute("text");if(t)return t}return t},n=e=>e?.getAttribute("url")?.toLowerCase()||e?.getAttribute("href")?.toLowerCase(),l=e=>{if(e?.getAttribute("analytic")&&""!==e?.getAttribute("analytic")||e?.getAttribute("data-analytic")&&e?.getAttribute("data-analytic")){switch(e?.getAttribute("analytic")){case"go-to-offers":let t=document.querySelector("a-mo-oferta")?.querySelector('[slot="title"]')?.getAttribute("text");s(e,{event_name:"job_offers_goto_listing",title:t,location:!1,__forceTitle:!0},!0);break;case"go-to-offer-detail":let r=!1,l=e.getRootNode().querySelector("a-link")?.shadowRoot.querySelector("a")?.href.split("?id=");l&&l.length>1&&(r=l[1]),s(e,{event_name:"job_offers_view_detail",title:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),link_url:n(e),link_text:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),location:!1,option:r||void 0},!0);break;case"file-download":let c=e.getAttribute("href"),i=null,u=null;c&&(i=c.split("/").pop(),u=i.split(".").pop(),i=i.replace(`.${u}`,"")),s(e,{event_name:"file_download",file_name:o(i),file_extension:u,link_url:c,location:a(e),title:!1},!0);break;case"ruedas-action-button":let d=e.closest("a-mo-ruedas-content")?.querySelector('[slot="content"] a-text-header')?.getAttribute("text");s(e,{title:d},!0)}return!0}return!1},c=new Set,i=new WeakMap;let u=window.location.pathname.match(/\/[^\/]+$/)?.[0];u=u?.replace("/","")?.replace(/-/g,"_");const d=/\/[a-z]{2}$/.test(window.location.pathname),s=(e,t,o=!1)=>{const r="click";t&&i.set(e,t),o&&(e.removeEventListener(r,h),c.delete(e)),c.has(e)||(e.addEventListener(r,h,{capture:!0}),c.add(e))},m=(e,t)=>{let o=[],r=1===e.length,a=e[0];return t?.querySelectorAll(a).forEach((t=>{r&&o.push(t),!r&&t.shadowRoot&&(o=o.concat(m(e.slice(1),t.shadowRoot)))})),o};refFn=m;const h=o=>{let l={};new URLSearchParams(window.location.search).has("debug-tracking")&&(l._aem=!0,o.preventDefault());let c={};if(i.has(o.currentTarget)&&(c=i.get(o.currentTarget)),l.event="custom_event",l.event_name=c.event_name??(t(o.currentTarget)?"outbound_link":"internal_link"),c.link_text?l.link_text=c.link_text:e(o.currentTarget)&&r(o.currentTarget)&&(l.link_text=r(o.currentTarget)),e(o.currentTarget)){const e=n(o.currentTarget)||window.location.pathname;l.link_url=c.link_url??e.replace(window.location.origin,"")}if(e(o.currentTarget)&&c.file_name&&(l.file_name=c.file_name),e(o.currentTarget)&&c.file_extension&&(l.file_extension=c.file_extension),t(o.currentTarget)||!1===c.location||(l.location=c.location??a(o.currentTarget)),!t(o.currentTarget)&&!1!==c.title||c.__forceTitle){const e=r(o.currentTarget)||c.link_text;l.title=c.__forceTitle?c.title:c.title??e}c.option&&(l.option=c.option),dataLayer.push(l)};function f(e){const t=[];function o(r){r.shadowRoot&&(r.shadowRoot.querySelectorAll(e).forEach((e=>
1t.push(e))),Array.from(r.shadowRoot.children).forEach(o))}return document.querySelectorAll("*").forEach((e=>o(e))),t}const y=[...document.querySelectorAll("a-link"),...f("a-link")],p=[...document.querySelectorAll("a-highlight-title"),...f("a-highlight-title")],b=[...document.querySelectorAll("a-button-icon"),...f("a-button-icon")],S=[...document.querySelectorAll("a-button-cta"),...f("a-button-cta")],q=[...document.querySelectorAll(".acciona-richtext a"),...f(".acciona-richtext a")];if(document.querySelectorAll("a-mo-text a-mo-text-content a-link").forEach((e=>{l(e)||s(e,{title:o(e?.textContent),location:o(e.closest("a-mo-text")?.querySelector("a-text-header")?.textContent)||!1},!0)})),document.querySelectorAll("a-mo-text-boxes a-flex-boxes a-link").forEach((e=>{l(e)||s(e,{title:o(e.closest("a-flex-boxes")?.querySelector("div")?.querySelector("a-text-header")?.shadowRoot?.querySelector(".text-header").textContent),location:a(e)||!1},!0)})),element=document.querySelector(".jobdetail a-pa-path"),element&&element.shadowRoot){const e=element.shadowRoot.querySelector("slot[name='button-cta']");if(e){const t=e.assignedElements().find((e=>"A-BUTTON-CTA"===e.tagName));t&&!l(t)&&s(t,{event_name:"job_offers_start",title:r(t),title:o(document.querySelector("a-pa-path")?.getAttribute("header")),option:new URLSearchParams(window.location.search)?.get("id"),location:!1},!0)}}(()=>{let e;y.forEach((e=>{!l(e)&&s(e)})),p.forEach((e=>{!l(e)&&s(e)})),q.forEach((e=>{!l(e)&&s(e)})),S.forEach((e=>{!l(e)&&s(e)})),document.querySelectorAll("a-button-action").forEach((e=>{!l(e)&&s(e)})),b.forEach((e=>{!l(e)&&s(e)})),m(["a-list","a-link"],document).forEach((e=>{!l(e)&&s(e)})),m(["a-mo-visual-header","a-button-icon"],document).forEach((e=>{if("A-BUTTON-ICON"===e.tagName){let t=null,r=null,a=e.getAttribute("a-aria-label");if(e.shadowRoot){const o=e.shadowRoot.querySelector("a");o&&(t=o.getAttribute("href"))}const n=e.closest(".visual-header-item-content");if(n){const e=n.querySelector(".content-top-title");e&&(r=e?.textContent)}!l(e)&&s(e,{event_name:"click_cta",location:"hero_banner",link_text:o(a),link_url:t,title:o(r)},!0)}})),m(["a-mo-visual-header","a-slider","a-button-action"],document).forEach((e=>{!l(e)&&s(e,{event_name:"click_cta",location:"hero_banner",title:r(e.closest(".visual-header-item-content")?.querySelector(".content-top-title")?.textContent)},!0)})),m(["a-mo-horizontal-distributor","a-slider","a-button-action"],document).forEach((e=>{!l(e)&&s(e,{event_name:"click_cta",location:"hero_banner"})})),m(["a-mo-featured-distributor","a-slider","a-button-action"],document).forEach((e=>{s(e)})),document.querySelector(".a-header")&&m(["a-highlight-title"],document).forEach((e=>{s(e,{event_name:"click_menu",location:"main",title:!1},!0)})),m(["a-mo-distributor-list","a-highlight-title"],document.querySelector(".distributorlist.a-aem-module")).forEach((e=>{let t=null;if(e.shadowRoot){const o=e.shadowRoot.querySelector("a");o&&(t=o.getAttribute("href"))}s(e,{event_name:"click_cta",link_url:t,location:d?"home":a(e),title:!1},!0)})),m(["a-mo-distributor-list","a-highlight-title"],document.querySelector(".a-footer.a-aem-module")).forEach((e=>{let t=null;if(e.shadowRoot){const o=e.shadowRoot.querySelector("a");o&&(t=o.getAttribute("href"))}s(e,{event_name:"click_footer",link_url:t,location:"prefooter",title:!1},!0)})),m(["a-mo-distributor-list","a-mo-compartir-rrss"],document.querySelector(".a-footer.a-aem-module")).forEach((e=>{let t=null;if(e.shadowRoot){const o=e.shadowRoot.querySelector("a");o&&(t=o.getAttribute("href"))}s(e,{event_name:"click_footer",link_url:t,location:"prefooter"})})),document.querySelector(".a-footer.a-aem-module")&&document.querySelector("a-mo-newsletter")&&m(["a-mo-newsletter","a-input button"],document).forEach((e=>{s(e,{event_name:"click_footer",link_url:n(e),location:"prefooter"},!0)})),document.querySelector(".featuredproject")&&m(["a-mo-proyecto-destacado","a-button-icon"],document).forEach((e=>{const t=e.closest(".basicinfo-container")?.querySelector(".basicinfo-content-header")?.textContent;s(e,{location:a(e),link_url:e.shadowRoot.querySelector("a")?.href,title:o(t)},!0)})),document.addEventListener("a-aem:tracking",(e=>{dataLayer.push(e.detail)})),e=document.querySelector("a-mo-oferta")||document.querySelector("a-mo-oferta-v2"),e&&(m(["a-oferta","a-link"],document).forEach((e=>{let t=!1,r=e.getRootNode().querySelector("a-link")?.shadowRoot.querySelector("a")?.href.split("?id=");r&&r.length>1&&(t=r[1]),!l(e)&&s(e,{event_name:"job_offers_view_detail",title:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),link_url:e.getRootNode().querySelector("a.title-clickable")?.href,option:t??void 0,location:!1},!0)})),m(["a-oferta","a"],document).forEach((e=>{let t=!1,r=e.getRootNode().querySelector("a-link")?.shadowRoot.querySelector("a")?.href.split("?id=");r&&r.length>1&&(t=r[1]),!l(e)&&s(e,{event_name:"job_offers_view_detail",title:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),link_url:e.getAttribute("href"),link_text:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),option:t??void 0,location:!1},!0)})),m(["a-mo-oferta-v2","ul li a-oferta","a-link"],document).forEach((e=>{let t=!1,r=e.getRootNode().querySelector("a-link")?.shadowRoot.querySelector("a")?.href.split("?id=");r&&r.length>1&&(t=r[1]),!l(e)&&s(e,{event_name:"job_offers_view_detail",title:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),link_url:e.getRootNode().querySelector("a.title-clickable")?.href,option:t??void 0,location:!1},!0)})),m(["a-mo-oferta","ul li a-oferta","a-link"],document).forEach((e=>{let t=!1,r=e.getRootNode().querySelector("a-link")?.shadowRoot.querySelector("a")?.href.split("?id=");r&&r.length>1&&(t=r[1]),!l(e)&&s(e,{event_name:"job_offers_view_detail",title:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),link_url:e.getRootNode().querySelector("a.title-clickable")?.href,option:t??void 0,location:!1},!0)})),m(["a-mo-oferta","a-oferta","a"],document).forEach((e=>{let t=!1,r=e.getRootNode().querySelector("a-link")?.shadowRoot.querySelector("a")?.href.split("?id=");r&&r.length>1&&(t=r[1]),!l(e)&&s(e,{event_name:"job_offers_view_detail",title:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),link_url:e.getAttribute("href"),link_text:o(e.querySelector("a-text-header")?.getAttribute("text")),option:t??void 0,location:!1},!0)})),m(["a-mo-oferta-v2","a-oferta","a"],document).forEach((e=>{let t=!1,r=e.getRootNode().querySelector("a-link")?.shadowRoot.querySelector("a")?.href.split("?id=");r&&r.length>1&&(t=r[1]),!l(e)&&s(e,{event_name:"job_offers_view_detail",title:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),link_url:e.getAttribute("href"),link_text:o(e.getRootNode().querySelector("a.title-clickable")?.querySelector("a-text-header")?.shadowRoot?.querySelector("div")?.textContent),option:t??void 0,location:!1},!0)})))})()})),A.loaded((()=>{const e=e=>{let t=document.createElement("div");return t.id=`scroll-marker-${e}`,t.dataset.threshold=25*e,t.style.position="absolute",t.style.top=`calc(${25*e}% - 2px)`,t.style.left="0",t.style.width="1px",t.style.height="1px",t.style.zIndex="-1",t},t=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&e.intersectionRatio>=1&&(dataLayer.push({event:"custom_event",event_name:`scroll_${e.target.dataset.threshold}`,percent_scrolled:e.target.dataset.threshold}),t.unobserve(e.target),e.target.remove())}
1))}),{root:null,threshold:[1]});for(let o=1;o<=4;o++){let r=e(o);document.body.appendChild(r),t.observe(r)}})),A.ready((()=>{if(document.querySelector('a-video[src*="youtube"]')){let e=document.createElement("script");e.src="https://www.youtube.com/iframe_api",e.defer=!0,document.head.appendChild(e)}if(document.querySelector('a-video[src*="vimeo"]')){let e=document.createElement("script");e.src="https://player.vimeo.com/api/player.js",e.defer=!0,document.head.appendChild(e)}})),document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll('[data-action="openModal"]').forEach((function(e){e.addEventListener("aClick",(()=>{const t=e.getAttribute("data-modalid"),o=document.querySelector(`a-modal[a-id="${t}"]`),r=document.querySelector(`a-dialog[a-id="${t}"]`);if(o)o.open();else if(r){let e=document.querySelector(`a-dialog[a-id="${t}"]`).parentElement;e.style.position="relative",r.open(e)}else console.warn(`No se encontró el modal con ID "${t}".`)}))}))}));const newsletterComponent=document.querySelector("a-mo-newsletter"),checkboxComponent=newsletterComponent?.querySelector("a-checkbox"),link=checkboxComponent?.querySelector("div.acciona-richtext a");link&&link.addEventListener("click",(e=>{e.preventDefault();const t=document.querySelector("a-mo-newsletter + a-modal");t&&t.open()}));const gdprForm=document.querySelector("#gdpr-form"),checkboxGdpr=gdprForm?.querySelector("[name='gdpr-consent']"),linkGdpr=checkboxGdpr?.querySelector("p a");linkGdpr&&linkGdpr.addEventListener("click",(e=>{e.preventDefault();const t=document.querySelector("[name='gdpr-consent'] + a-modal");t&&t.open()}));const waterForm=document.querySelector("#water-form"),checkboxWater=waterForm?.querySelector("[name='water-consent']"),linkWater=checkboxWater?.querySelector("p a");linkWater&&linkWater.addEventListener("click",(e=>{e.preventDefault();const t=document.querySelector("[name='water-consent'] + a-modal");t&&t.open()}));let links=document.querySelectorAll('a[href^="#modal-"]');links.forEach((function(e){let t=e.getAttribute("href").replace("#",""),o=document.querySelector(`a-modal[a-id="${t}"]`);o&&(e.setAttribute("target","self"),e.addEventListener("click",(function(e){e.preventDefault(),o.open()})))})),A.ready((()=>{if(!document.querySelector("#gdpr-form"))return;let e=document.querySelector("#gdpr-form");async function t(){let t=new FormData(e),a=e.querySelectorAll("[required]"),n=!0;for(let e=0;e<a.length;e++)a[e].value||a[e].selectedValue||a[e].files&&a[e].files.length>0?a[e].setAttribute("has-error",!1):(a[e].setAttribute("has-error",!0),n=!1);if(e.querySelector('[name="gdpr-consent"]').checked||(e.querySelector('[name="gdpr-consent"]').setAttribute("has-error",!0),n=!1),!n)return;for(var l of t.entries());let c=[];document.querySelectorAll('a-checkbox-list[data-name="relation-type"] a-checkbox').forEach((e=>{e.checked&&c.push(e.value)})),t.append("relation-type[]",c.join(",")),e.querySelector('a-textarea[name="other-relation-type"]').value&&t.append("relation-type-other",e.querySelector('a-textarea[name="other-relation-type"]').value);let i=[];document.querySelectorAll('a-checkbox-list[data-name="areas"] a-checkbox').forEach((e=>{e.checked&&i.push(e.value)})),t.append("areas[]",i.join(",")),e.querySelector('a-textarea[name="other-areas"]').value&&t.append("areas-other",e.querySelector('a-textarea[name="other-areas"]').value);let u=[];document.querySelectorAll('a-checkbox-list[data-name="rights"] a-checkbox').forEach((e=>{e.checked&&u.push(e.value)})),t.append("rights[]",u.join(",")),e.querySelector('a-textarea[name="additional"]').value&&t.append("additional",e.querySelector('a-textarea[name="additional"]').value);let d=document.querySelector('a-file-uploader[name="files"]');if(d&&d.files.length>0)for(let e=0;e<d.files.length;e++)t.append(`files-${e}`,d.files[e]);let s=document.querySelector('a-file-uploader[name="on-behalf-of-personal-id"]');if(s&&s.files.length>0)for(let e=0;e<s.files.length;e++)t.append("on-behalf-of-personal-id",s.files[e]);try{let a=[location.protocol,"//",location.host,location.pathname].join("");a.endsWith(".html")&&(a=a.replace(".html","")),a+="/jcr:content.gdpr-send.json",fetch(a,{method:"POST",body:t,headers:{"CSRF-Token":await r()}}).then((e=>e.json())).then((t=>{t._result&&"success"===t._result&&(o(),e.dataset.redirect&&(window.location.href=e.dataset.redirect))}))}catch(e){}}function o(){document.querySelector("#gdpr-form").reset(),document.querySelectorAll("a-radio-button-list").forEach((e=>{e.selectedValue&&(e.selectedValue="")}
1)),document.querySelectorAll("a-radio-button").forEach((e=>{e.checked&&(e.checked=!1)})),document.querySelectorAll("a-file-uploader").forEach((e=>{e.files&&(e.files=[])})),document.querySelectorAll("a-input, a-textarea").forEach((e=>{e.value&&(e.value="")})),document.querySelectorAll("a-checkbox").forEach((e=>{e.checked&&(e.checked=!1)}))}async function r(){const e=await fetch("/libs/granite/csrf/token.json");return(await e.json()).token}document.getElementById("gdpr-form").addEventListener("submit",(e=>{e.preventDefault(),t()})),document.querySelector("#gdpr-form-submit-btn")?.addEventListener("click",(function(e){e.preventDefault(),t()})),document.querySelector("#gdpr-form-reset-btn")?.addEventListener("click",(function(e){e.preventDefault(),document.querySelector("#gdpr-form").reset(),o()}));const a=document.querySelector("#gdpr-form a-radio-button-list a-radio-button[value='yes']"),n=document.querySelector("#gdpr-form a-radio-button-list a-radio-button[value='no']");a.addEventListener("click",(t=>{e.querySelector('[name="on-behalf-of-name"]').removeAttribute("required"),e.querySelector('[name="on-behalf-of-name"]').removeAttribute("has-error"),e.querySelector('[name="on-behalf-of-surnames"]').removeAttribute("required"),e.querySelector('[name="on-behalf-of-surnames"]').removeAttribute("has-error"),e.querySelector('[name="on-behalf-of-personal-id"]').removeAttribute("required"),e.querySelector('[name="on-behalf-of-personal-id"]').removeAttribute("has-error")})),n.addEventListener("click",(t=>{e.querySelector('[name="on-behalf-of-name"]').setAttribute("required",!0),e.querySelector('[name="on-behalf-of-surnames"]').setAttribute("required",!0),e.querySelector('[name="on-behalf-of-personal-id"]').setAttribute("required",!0)}))})),A.ready((()=>{if(!document.querySelector("#water-form"))return;let e=document.querySelector("#water-form");async function t(){let t=new FormData(e),a=e.querySelectorAll("[required]"),n=!0;for(let e=0;e<a.length;e++)a[e].value||a[e].selectedValue?a[e].setAttribute("has-error",!1):(a[e].setAttribute("has-error",!0),n=!1);if(e.querySelector('[name="water-consent"]').checked||(e.querySelector('[name="water-consent"]').setAttribute("has-error",!0),n=!1),n){for(var l of t.entries())console.log(l[0]+", "+l[1]);e.querySelector('a-textarea[name="comments"]').value&&t.append("comments",e.querySelector('a-textarea[name="comments"]').value);try{let a=[location.protocol,"//",location.host,location.pathname].join("");a.endsWith(".html")&&(a=a.replace(".html","")),a+="/jcr:content.water-form-send.json",fetch(a,{method:"POST",body:t,headers:{"CSRF-Token":await r()}}).then((e=>e.json())).then((t=>{t._result&&"success"===t._result?(console.log("Success:",t),o(),e.dataset.redirect&&(window.location.href=e.dataset.redirect)):console.error("Error:",t)}))}catch(e){console.error("Error:",e)}}}function o(){document.querySelector("#water-form").reset(),document.querySelectorAll("a-input, a-textarea").forEach((e=>{e.value&&(e.value="")})),document.querySelectorAll("a-checkbox").forEach((e=>{e.checked&&(e.checked=!1)}))}async function r(){const e=await fetch("/libs/granite/csrf/token.json");return(await e.json()).token}document.getElementById("water-form").addEventListener("submit",(e=>{e.preventDefault(),t()})),document.querySelector("#water-form-submit-btn")?.addEventListener("click",(function(e){e.preventDefault(),console.log("submit"),t()})),document.querySelector("#water-form-reset-btn")?.addEventListener("click",(function(e){e.preventDefault(),console.log("reset"),document.querySelector("#water-form").reset(),o()}))})),A.ready((()=>{if(!document.querySelector("#turbine-form"))return;let e=document.querySelector("#turbine-form");function t(){document.querySelector("#turbine-form").reset(),document.querySelectorAll("a-input, a-textarea, a-select").forEach((e=>{e.value&&(e.value="")}))}async function o(){const e=await fetch("/libs/granite/csrf/token.json");return(await e.json()).token}document.querySelector("#turbine-form-submit-btn")?.addEventListener("click",(function(r){r.preventDefault(),async function(){let r=new FormData(e),a=e.querySelectorAll("[required]"),n=!0;for(let e=0;e<a.length;e++)a[e].value||a[e].selectedValue?a[e].setAttribute("has-error",!1):(a[e].setAttribute("has-error",!0),n=!1);if(n){e.querySelector('a-select[name="representingArea"]').value&&r.append("representation-area",e.querySelector('a-select[name="representingArea"]').value),e.querySelector('a-select[name="interestArea"]').value&&r.append("interest-area",e.querySelector('a-select[name="interestArea"]').value),e.querySelector('a-textarea[name="comments"]').value&&r.append("comments",e.querySelector('a-textarea[name="comments"]').value);try{let a=[location.protocol,"//",location.host,location.pathname].join("");a.endsWith(".html")&&(a=a.replace(".html","")),a+="/jcr:content.turbine-send.json",fetch(a,{method:"POST",body:r,headers:{"CSRF-Token":await o()}}).then((e=>e.json())).then((o=>{o._result&&"success"===o._result&&(t(),e.dataset.redirect&&(window.location.href=e.dataset.redirect))}))}catch(e){}}}()})),document.querySelector("#turbine-form-reset-btn")?.addEventListener("click",(function(e){e.preventDefault(),document.querySelector("#turbine-form").reset(),t()}))})),A.ready((()=>
1{if(!document.querySelector("#mx-form"))return;let e=document.querySelector("#mx-form");function t(){document.querySelector("#mx-form").reset(),document.querySelectorAll("a-input, a-textarea, a-select").forEach((e=>{e.value&&(e.value="")})),document.querySelectorAll("a-radio-button-list").forEach((e=>{e.selectedValue&&(e.selectedValue="")})),document.querySelectorAll("a-radio-button").forEach((e=>{e.checked&&(e.checked=!1)})),document.querySelectorAll("a-checkbox").forEach((e=>{e.checked&&(e.checked=!1)}))}async function o(){const e=await fetch("/libs/granite/csrf/token.json");return(await e.json()).token}document.querySelector("#mx-form-submit-btn")?.addEventListener("click",(function(r){r.preventDefault(),async function(){let r=new FormData(e),a=e.querySelectorAll("[required]"),n=!0;for(let e=0;e<a.length;e++)a[e].value||a[e].selectedValue?a[e].setAttribute("has-error",!1):(a[e].setAttribute("has-error",!0),n=!1);if(e.querySelector('[name="mx-consent"]').checked||(e.querySelector('[name="mx-consent"]').setAttribute("has-error",!0),n=!1),!n)return;e.querySelector('a-select[name="mensualPayment"]').value?r.append("mensualPayment",e.querySelector('a-select[name="mensualPayment"]').value):r.append("mensualPayment","no data");const l=e.querySelector('a-radio-button-list[data-name="radioButtonList"]'),c=l?l.getAttribute("selected-value"):null;l&&c?r.append("buyInterest",c):r.append("buyInterest","no data");try{let a=[location.protocol,"//",location.host,location.pathname].join("");a.endsWith(".html")&&(a=a.replace(".html","")),a+="/jcr:content.mx-send.json",fetch(a,{method:"POST",body:r,headers:{"CSRF-Token":await o()}}).then((e=>e.json())).then((o=>{o._result&&"success"===o._result&&(t(),e.dataset.redirect&&(window.location.href=e.dataset.redirect))}))}catch(e){}}()})),document.querySelector("#mx-form-reset-btn")?.addEventListener("click",(function(e){e.preventDefault(),document.querySelector("#mx-form").reset(),t()}))})),A.ready((()=>{const e=new MutationObserver((()=>{const t=document.querySelector("#custom-form");t&&t.shadowRoot&&(!function(){const e=document.querySelector("#custom-form");if(!e||!e.shadowRoot)return;const t=e.shadowRoot,a=setInterval((()=>{const n=t.querySelector("#custom-form-send"),l=t.querySelector("#custom-form-cancel");n&&l&&(clearInterval(a),n.addEventListener("click",(function(e){e.preventDefault(),o(t)})),l.addEventListener("click",(function(e){e.preventDefault(),r(t)})),e.addEventListener("submit",(e=>{e.preventDefault(),o(t)})))}),200)}(),e.disconnect())}));e.observe(document.body,{childList:!0,subtree:!0});let t=document.querySelector("#custom-form");async function o(e){let o=new FormData,n=t.querySelectorAll("[required]"),l=!0;var c=new Array;if(n.forEach((e=>{("A-CHECKBOX"!==e.tagName||e.checked)&&(e.selectedValue||e.value)?e.setAttribute("has-error",!1):(e.setAttribute("has-error",!0),l=!1)})),l){var i=t.querySelectorAll("a-selector");for(let e=0;e<i.length;e++)i[e].value&&(c.push(i[e].getAttribute("name")),o.append("selector-"+e+"-"+i[e].getAttribute("name"),i[e].value));var u=t.querySelectorAll("a-input");for(let e=0;e<u.length;e++)u[e].value&&(c.push(u[e].getAttribute("name")),o.append("input-"+e+"-"+u[e].getAttribute("name"),u[e].value));var d=t.querySelectorAll("a-textarea");for(let e=0;e<d.length;e++)d[e].value&&(c.push(d[e].getAttribute("name")),o.append("textarea-"+e+"-"+d[e].getAttribute("name"),d[e].value));var s=t.querySelectorAll("a-checkbox-list");for(let e=0;e<s.length;e++){var m=s[e].querySelectorAll("a-checkbox");c.push(s[e].getAttribute("name"));var h=!1;for(let t=0;t<m.length;t++)m[t].checked&&(c.push(m[t].getAttribute("name")),o.append("checkboxList-"+e+"-"+s[e].getAttribute("name"),m[t].value),m[t].getAttribute("class").includes("show-hidden-element")&&(h=!0));if(h){var f=t.querySelector('div[class="related-fields"]').querySelector("a-textarea");f&&(c.push(f.getAttribute("name")),o.append("checkboxList-"+e+"-textArea",f.value))}}var y=t.querySelectorAll("a-radio-button-list");for(let e=0;e<y.length;e++){var p=y[e].querySelectorAll("a-radio-button");c.push(y[e].getAttribute("name"));for(let t=0;t<p.length;t++)p[t].value===y[e].getAttribute("selected-value")&&(c.push(p[t].getAttribute("name")),o.append("radioButtonList-"+e+"-"+y[e].getAttribute("name"),y[e].getAttribute("selected-value")))}var b=t.querySelectorAll("a-file-uploader");if(b&&b.length>0)for(let e=0;e<b.length;e++){c.push(b[e].getAttribute("name"));for(let t=0;t<b[e].files.length;t++)o.append("fileUploader-"+e+"-"+b[e].getAttribute("name"),b[e].files[t])}s=t.querySelectorAll("a-checkbox");for(let e=0;e<s.length;e++)!c.includes(s[e].getAttribute("name"))&&s[e].checked&&(c.push(s[e].getAttribute("name")),o.append("checkbox-"+e+"-"+s[e].getAttribute("name"),s[e].value));y=t.querySelectorAll("a-radio-button");for(let e=0;e<y.length;e++)!c.includes(y[e].getAttribute("name"))&&y[e].checked&&(c.push(y[e].getAttribute("name")),o.append("radioButton-"+e+"-"+y[e].getAttribute("name"),y[e].value));try{let e=[location.protocol,"//",location.host,location.pathname].join("");e.endsWith(".html")&&(e=e.replace(".html","")),e+="/jcr:content.custom-form-send.json",fetch(e,{method:"POST",body:o,headers:{"CSRF-Token":await a()}}).then((e=>e.json())).then((e=>{e._result&&"success"===e._result&&(r(),t.dataset.redirect&&(window.location.href=t.dataset.redirect))}))}catch(e){console.error("Error:",e)}}}function r(e){document.querySelectorAll("a-input, a-textarea, a-select").forEach((e=>{e.value&&(e.value="")}
1)),document.querySelectorAll("a-radio-button-list").forEach((e=>{e.selectedValue&&(e.selectedValue="")})),document.querySelectorAll("a-radio-button").forEach((e=>{e.checked&&(e.checked=!1)})),document.querySelectorAll("a-checkbox").forEach((e=>{e.checked&&(e.checked=!1)})),document.querySelectorAll("a-file-uploader").forEach((e=>{e.value&&(e.value="")}))}async function a(){const e=await fetch("/libs/granite/csrf/token.json");return(await e.json()).token}})); 2/******************************************************************************* 3 * Copyright 2019 Adobe 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 ******************************************************************************/ 17 18/** 19 * Element.matches() 20 * https://developer.mozilla.org/enUS/docs/Web/API/Element/matches#Polyfill 21 */ 22if (!Element.prototype.matches) { 23 Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; 24} 25 26// eslint-disable-next-line valid-jsdoc 27/** 28 * Element.closest() 29 * https://developer.mozilla.org/enUS/docs/Web/API/Element/closest#Polyfill 30 */ 31if (!Element.prototype.closest) { 32 Element.prototype.closest = function(s) { 33 "use strict"; 34 var el = this; 35 if (!document.documentElement.contains(el)) { 36 return null; 37 } 38 do { 39 if (el.matches(s)) { 40 return el; 41 } 42 el = el.parentElement || el.parentNode; 43 } while (el !== null && el.nodeType === 1); 44 return null; 45 }; 46} 47 48/******************************************************************************* 49 * Copyright 2019 Adobe 50 * 51 * Licensed under the Apache License, Version 2.0 (the "License"); 52 * you may not use this file except in compliance with the License. 53 * You may obtain a copy of the License at 54 * 55 * http://www.apache.org/licenses/LICENSE-2.0 56 * 57 * Unless required by applicable law or agreed to in writing, software 58 * distributed under the License is distributed on an "AS IS" BASIS, 59 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 60 * See the License for the specific language governing permissions and 61 * limitations under the License. 62 ******************************************************************************/ 63(function() { 64 "use strict"; 65 66 var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined; 67 if (!containerUtils) { 68 // eslint-disable-next-line no-console 69 console.warn("Accordion: container utilities at window.CQ.CoreComponents.container.utils are not available. This can lead to missing features. Ensure the core.wcm.components.commons.site.container client library is included on the page."); 70 } 71 var dataLayerEnabled; 72 var dataLayer; 73 var dataLayerName; 74 var delay = 100; 75 76 var NS = "cmp"; 77 var IS = "accordion"; 78 79 var keyCodes = { 80 ENTER: 13, 81 SPACE: 32, 82 END: 35, 83 HOME: 36, 84 ARROW_LEFT: 37, 85 ARROW_UP: 38, 86 ARROW_RIGHT: 39, 87 ARROW_DOWN: 40 88 }; 89 90 var selectors = { 91 self: "[data-" + NS + '-is="' + IS + '"]' 92 }; 93
94 var cssClasses = { 95 button: { 96 disabled: "cmp-accordion__button--disabled", 97 expanded: "cmp-accordion__button--expanded" 98 }, 99 panel: { 100 hidden: "cmp-accordion__panel--hidden", 101 expanded: "cmp-accordion__panel--expanded" 102 } 103 }; 104 105 var dataAttributes = { 106 item: { 107 expanded: "data-cmp-expanded" 108 } 109 }; 110 111 var properties = { 112 /** 113 * Determines whether a single accordion item is forced to be expanded at a time. 114 * Expanding one item will collapse all others. 115 * 116 * @memberof Accordion 117 * @type {Boolean} 118 * @default false 119 */ 120 "singleExpansion": { 121 "default": false, 122 "transform": function(value) { 123 return !(value === null || typeof value === "undefined"); 124 } 125 } 126 }; 127 128 /** 129 * Accordion Configuration. 130 * 131 * @typedef {Object} AccordionConfig Represents an Accordion configuration 132 * @property {HTMLElement} element The HTMLElement representing the Accordion 133 * @property {Object} options The Accordion options 134 */ 135 136 /** 137 * Accordion. 138 * 139 * @class Accordion 140 * @classdesc An interactive Accordion component for toggling panels of related content 141 * @param {AccordionConfig} config The Accordion configuration 142 */ 143 function Accordion(config) { 144 var that = this; 145 146 if (config && config.element) { 147 init(config); 148 } 149 150 /** 151 * Initializes the Accordion. 152 * 153 * @private 154 * @param {AccordionConfig} config The Accordion configuration 155 */ 156 function init(config) { 157 that._config = config; 158 159 // prevents multiple initialization 160 config.element.removeAttribute("data-" + NS + "-is"); 161 162 setupProperties(config.options); 163 cacheElements(config.element); 164 165 if (that._elements["item"]) { 166 // ensures multiple element types are arrays. 167 that._elements["item"] = Array.isArray(that._elements["item"]) ? that._elements["item"] : [that._elements["item"]]; 168 that._elements["button"] = Array.isArray(that._elements["button"]) ? that._elements["button"] : [that._elements["button"]]; 169 that._elements["panel"] = Array.isArray(that._elements["panel"]) ? that._elements["panel"] : [that._elements["panel"]]; 170 171 if (that._properties.singleExpansion) { 172 var expandedItems = getExpandedItems(); 173 // multiple expanded items annotated, display the last item open. 174 if (expandedItems.length > 1) { 175 toggle(expandedItems.length - 1); 176 } 177 } 178 179 refreshItems(); 180 bindEvents(); 181 scrollToDeepLinkIdInAccordion(); 182 } 183 if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { 184 /* 185 * Editor message handling: 186 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame 187 * - check that the message data panel container type is correct and that the id (path) matches this specific Accordion component 188 * - if so, route the "navigate" operation to enact a navigation of the Accordion based on index data 189 */ 190 window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); 191 window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) { 192 if (message.data && message.data.type === "cmp-accordion" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) { 193 if (message.data.operation === "navigate") { 194 // switch to single expansion mode when navigating in edit mode. 195 var singleExpansion = that._properties.singleExpansion; 196 that._properties.singleExpansion = true; 197 toggle(message.data.index); 198 199 // revert to the configured state. 200 that._properties.singleExpansion = singleExpansion; 201 } 202 } 203 }); 204 } 205 } 206 207 /**
208 * Displays the panel containing the element that corresponds to the deep link in the URI fragment 209 * and scrolls the browser to this element. 210 */ 211 function scrollToDeepLinkIdInAccordion() { 212 if (containerUtils) { 213 var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "item", "item"); 214 if (deepLinkItemIdx > -1) { 215 var deepLinkItem = that._elements["item"][deepLinkItemIdx]; 216 if (deepLinkItem && !deepLinkItem.hasAttribute(dataAttributes.item.expanded)) { 217 // if single expansion: close all accordion items 218 if (that._properties.singleExpansion) { 219 for (var j = 0; j < that._elements["item"].length; j++) { 220 if (that._elements["item"][j].hasAttribute(dataAttributes.item.expanded)) { 221 setItemExpanded(that._elements["item"][j], false, true); 222 } 223 } 224 } 225 // expand the accordion item containing the deep link 226 setItemExpanded(deepLinkItem, true, true); 227 } 228 var hashId = window.location.hash.substring(1); 229 if (hashId) { 230 var hashItem = document.querySelector("[id='" + hashId + "']"); 231 if (hashItem) { 232 hashItem.scrollIntoView(); 233 } 234 } 235 } 236 } 237 } 238 239 /** 240 * Caches the Accordion elements as defined via the {@code data-accordion-hook="ELEMENT_NAME"} markup API. 241 * 242 * @private 243 * @param {HTMLElement} wrapper The Accordion wrapper element 244 */ 245 function cacheElements(wrapper) { 246 that._elements = {}; 247 that._elements.self = wrapper; 248 var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]"); 249 250 for (var i = 0; i < hooks.length; i++) { 251 var hook = hooks[i]; 252 if (hook.closest("." + NS + "-" + IS) === that._elements.self) { // only process own accordion elements 253 var capitalized = IS; 254 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 255 var key = hook.dataset[NS + "Hook" + capitalized]; 256 if (that._elements[key]) { 257 if (!Array.isArray(that._elements[key])) { 258 var tmp = that._elements[key]; 259 that._elements[key] = [tmp]; 260 } 261 that._elements[key].push(hook); 262 } else { 263 that._elements[key] = hook; 264 } 265 } 266 } 267 } 268 269 /** 270 * Sets up properties for the Accordion based on the passed options. 271 * 272 * @private 273 * @param {Object} options The Accordion options 274 */ 275 function setupProperties(options) { 276 that._properties = {}; 277 278 for (var key in properties) { 279 if (Object.prototype.hasOwnProperty.call(properties, key)) { 280 var property = properties[key]; 281 var value = null; 282 283 if (options && options[key] != null) { 284 value = options[key]; 285 286 // transform the provided option 287 if (property && typeof property.transform === "function") { 288 value = property.transform(value); 289 } 290 } 291 292 if (value === null) { 293 // value still null, take the property default 294 value = properties[key]["default"]; 295 } 296 297 that._properties[key] = value; 298 } 299 } 300 } 301 302 /** 303 * Binds Accordion event handling. 304 * 305 * @private 306 */ 307 function bindEvents() { 308 window.addEventListener("hashchange", scrollToDeepLinkIdInAccordion, false); 309 var buttons = that._elements["button"]; 310 if (buttons) { 311 for (var i = 0; i < buttons.length; i++) { 312 (function(index) { 313 buttons[i].addEventListener("click", function(event) { 314 toggle(index); 315 focusButton(index); 316 }); 317 buttons[i].addEventListener("keydown", function(event) { 318 onButtonKeyDown(event, index); 319 }); 320 })(i); 321 } 322 } 323 } 324 325 /** 326 * Handles button keydown events. 327 * 328 * @private 329 * @param {Object} event The keydown event 330 * @param {Number} index The index of the button triggering the event 331 */ 332 function onButtonKeyDown(event, index) { 333 var lastIndex = that._elements["button"].length - 1; 334 335 switch (event.keyCode) { 336 case keyCodes.ARROW_LEFT: 337 case keyCodes.ARROW_UP: 338 event.preventDefault(); 339 if (index > 0) { 340 focusButton(index - 1); 341 } 342 break; 343 case keyCodes.ARROW_RIGHT: 344 case keyCodes.ARROW_DOWN: 345 event.preventDefault(); 346 if (index < lastIndex) { 347 focusButton(index + 1); 348 } 349 break; 350 case keyCodes.HOME: 351 event.preventDefault(); 352 focusButton(0); 353 break; 354 case keyCodes.END: 355 event.preventDefault(); 356 focusButton(lastIndex); 357 break;
358 case keyCodes.ENTER: 359 case keyCodes.SPACE: 360 event.preventDefault(); 361 toggle(index); 362 focusButton(index); 363 break; 364 default: 365 return; 366 } 367 } 368 369 /** 370 * General handler for toggle of an item. 371 * 372 * @private 373 * @param {Number} index The index of the item to toggle 374 */ 375 function toggle(index) { 376 var item = that._elements["item"][index]; 377 if (item) { 378 if (that._properties.singleExpansion) { 379 // ensure only a single item is expanded if single expansion is enabled. 380 for (var i = 0; i < that._elements["item"].length; i++) { 381 if (that._elements["item"][i] !== item) { 382 var expanded = getItemExpanded(that._elements["item"][i]); 383 if (expanded) { 384 setItemExpanded(that._elements["item"][i], false); 385 } 386 } 387 } 388 } 389 setItemExpanded(item, !getItemExpanded(item)); 390 391 if (dataLayerEnabled) { 392 var accordionId = that._elements.self.id; 393 var expandedItems = getExpandedItems() 394 .map(function(item) { 395 return getDataLayerId(item); 396 }); 397 398 var uploadPayload = { component: {} }; 399 uploadPayload.component[accordionId] = { shownItems: expandedItems }; 400 401 var removePayload = { component: {} }; 402 removePayload.component[accordionId] = { shownItems: undefined }; 403 404 dataLayer.push(removePayload); 405 dataLayer.push(uploadPayload); 406 } 407 } 408 } 409 410 /** 411 * Sets an item's expanded state based on the provided flag and refreshes its internals. 412 * 413 * @private 414 * @param {HTMLElement} item The item to mark as expanded, or not expanded 415 * @param {Boolean} expanded true to mark the item expanded, false otherwise 416 * @param {Boolean} keepHash true to keep the hash in the URL, false to update it 417 */ 418 function setItemExpanded(item, expanded, keepHash) { 419 if (expanded) { 420 item.setAttribute(dataAttributes.item.expanded, ""); 421 var index = that._elements["item"].indexOf(item); 422 if (!keepHash && containerUtils) { 423 containerUtils.updateUrlHash(that, "item", index); 424 } 425 if (dataLayerEnabled) { 426 dataLayer.push({ 427 event: "cmp:show", 428 eventInfo: { 429 path: "component." + getDataLayerId(item) 430 } 431 }); 432 } 433 434 } else { 435 item.removeAttribute(dataAttributes.item.expanded); 436 if (!keepHash && containerUtils) { 437 containerUtils.removeUrlHash(); 438 } 439 if (dataLayerEnabled) { 440 dataLayer.push({ 441 event: "cmp:hide", 442 eventInfo: { 443 path: "component." + getDataLayerId(item) 444 } 445 }); 446 } 447 } 448 refreshItem(item); 449 } 450 451 /** 452 * Gets an item's expanded state. 453 * 454 * @private 455 * @param {HTMLElement} item The item for checking its expanded state 456 * @returns {Boolean} true if the item is expanded, false otherwise 457 */ 458 function getItemExpanded(item) { 459 return item && item.dataset && item.dataset["cmpExpanded"] !== undefined; 460 } 461 462 /** 463 * Refreshes an item based on its expanded state. 464 * 465 * @private 466 * @param {HTMLElement} item The item to refresh 467 */ 468 function refreshItem(item) { 469 var expanded = getItemExpanded(item); 470 if (expanded) { 471 expandItem(item); 472 } else { 473 collapseItem(item); 474 } 475 } 476 477 /** 478 * Refreshes all items based on their expanded state. 479 * 480 * @private 481 */ 482 function refreshItems() { 483 for (var i = 0; i < that._elements["item"].length; i++) { 484 refreshItem(that._elements["item"][i]); 485 } 486 } 487 488 /** 489 * Returns all expanded items. 490 * 491 * @private 492 * @returns {HTMLElement[]} The expanded items 493 */ 494 function getExpandedItems() { 495 var expandedItems = []; 496 497 for (var i = 0; i < that._elements["item"].length; i++) { 498 var item = that._elements["item"][i]; 499 var expanded = getItemExpanded(item); 500 if (expanded) { 501 expandedItems.push(item); 502 } 503 } 504 505 return expandedItems; 506 } 507 508 /** 509 * Annotates the item and its internals with 510 * the necessary style and accessibility attributes to indicate it is expanded. 511 * 512 * @private 513 * @param {HTMLElement} item The item to annotate as expanded 514 */ 515 function expandItem(item) { 516 var index = that._elements["item"].indexOf(item); 517 if (index > -1) { 518 var button = that._elements["button"][index]; 519 var panel = that._elements["panel"][index];
520 button.classList.add(cssClasses.button.expanded); 521 // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute 522 // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934 523 setTimeout(function() { 524 button.setAttribute("aria-expanded", true); 525 }, delay); 526 panel.classList.add(cssClasses.panel.expanded); 527 panel.classList.remove(cssClasses.panel.hidden); 528 panel.setAttribute("aria-hidden", false); 529 } 530 } 531 532 /** 533 * Annotates the item and its internals with 534 * the necessary style and accessibility attributes to indicate it is not expanded. 535 * 536 * @private 537 * @param {HTMLElement} item The item to annotate as not expanded 538 */ 539 function collapseItem(item) { 540 var index = that._elements["item"].indexOf(item); 541 if (index > -1) { 542 var button = that._elements["button"][index]; 543 var panel = that._elements["panel"][index]; 544 button.classList.remove(cssClasses.button.expanded); 545 // used to fix some known screen readers issues in reading the correct state of the 'aria-expanded' attribute 546 // e.g. https://bugs.webkit.org/show_bug.cgi?id=210934 547 setTimeout(function() { 548 button.setAttribute("aria-expanded", false); 549 }, delay); 550 panel.classList.add(cssClasses.panel.hidden); 551 panel.classList.remove(cssClasses.panel.expanded); 552 panel.setAttribute("aria-hidden", true); 553 } 554 } 555 556 /** 557 * Focuses the button at the provided index. 558 * 559 * @private 560 * @param {Number} index The index of the button to focus 561 */ 562 function focusButton(index) { 563 var button = that._elements["button"][index]; 564 button.focus(); 565 } 566 } 567 568 /** 569 * Reads options data from the Accordion wrapper element, defined via {@code data-cmp-*} data attributes. 570 * 571 * @private 572 * @param {HTMLElement} element The Accordion element to read options data from 573 * @returns {Object} The options read from the component data attributes 574 */ 575 function readData(element) { 576 var data = element.dataset; 577 var options = []; 578 var capitalized = IS; 579 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 580 var reserved = ["is", "hook" + capitalized]; 581 582 for (var key in data) { 583 if (Object.prototype.hasOwnProperty.call(data, key)) { 584 var value = data[key]; 585 586 if (key.indexOf(NS) === 0) { 587 key = key.slice(NS.length); 588 key = key.charAt(0).toLowerCase() + key.substring(1); 589 590 if (reserved.indexOf(key) === -1) { 591 options[key] = value; 592 } 593 } 594 } 595 } 596 597 return options; 598 } 599 600 /** 601 * Parses the dataLayer string and returns the ID 602 * 603 * @private 604 * @param {HTMLElement} item the accordion item 605 * @returns {String} dataLayerId or undefined 606 */ 607 function getDataLayerId(item) { 608 if (item) { 609 if (item.dataset.cmpDataLayer) { 610 return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0]; 611 } else { 612 return item.id; 613 } 614 } 615 return null; 616 } 617 618 /** 619 * Document ready handler and DOM mutation observers. Initializes Accordion components as necessary. 620 * 621 * @private 622 */ 623 function onDocumentReady() { 624 dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled"); 625 if (dataLayerEnabled) { 626 dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer"; 627 dataLayer = window[dataLayerName] = window[dataLayerName] || []; 628 } 629 630 var elements = document.querySelectorAll(selectors.self); 631 for (var i = 0; i < elements.length; i++) { 632 new Accordion({ element: elements[i], options: readData(elements[i]) }); 633 } 634 635 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; 636 var body = document.querySelector("body"); 637 var observer = new MutationObserver(function(mutations) { 638 mutations.forEach(function(mutation) { 639 // needed for IE 640 var nodesArray = [].slice.call(mutation.addedNodes); 641 if (nodesArray.length > 0) { 642 nodesArray.forEach(function(addedNode) { 643 if (addedNode.querySelectorAll) { 644 var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self)); 645 elementsArray.forEach(function(element) { 646 new Accordion({ element: element, options: readData(element) }); 647 }); 648 } 649 }); 650 } 651 }); 652 }); 653 654 observer.observe(body, { 655 subtree: true, 656 childList: true, 657 characterData: true 658 }); 659 } 660 661 if (document.readyState !== "loading") { 662 onDocumentReady(); 663 } else { 664 document.addEventListener("DOMContentLoaded", onDocumentReady); 665 } 666 667 if (containerUtils) { 668 window.addEventListener("load", containerUtils.scrollToAnchor, false); 669 } 670 671}()); 672 673 674/******************************************************************************* 675 * Copyright 2018 Adobe 676 * 677 * Licensed under the Apache License, Version 2.0 (the "License");
678 * you may not use this file except in compliance with the License. 679 * You may obtain a copy of the License at 680 * 681 * http://www.apache.org/licenses/LICENSE2.0 682 * 683 * Unless required by applicable law or agreed to in writing, software 684 * distributed under the License is distributed on an "AS IS" BASIS, 685 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 686 * See the License for the specific language governing permissions and 687 * limitations under the License. 688 ******************************************************************************/ 689 690/** 691 * Element.matches() 692 * https://developer.mozilla.org/enUS/docs/Web/API/Element/matches#Polyfill 693 */ 694if (!Element.prototype.matches) { 695 Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; 696} 697 698// eslint-disable-next-line valid-jsdoc 699/** 700 * Element.closest() 701 * https://developer.mozilla.org/enUS/docs/Web/API/Element/closest#Polyfill 702 */ 703if (!Element.prototype.closest) { 704 Element.prototype.closest = function(s) { 705 "use strict"; 706 var el = this; 707 if (!document.documentElement.contains(el)) { 708 return null; 709 } 710 do { 711 if (el.matches(s)) { 712 return el; 713 } 714 el = el.parentElement || el.parentNode; 715 } while (el !== null && el.nodeType === 1); 716 return null; 717 }; 718} 719 720/******************************************************************************* 721 * Copyright 2018 Adobe 722 * 723 * Licensed under the Apache License, Version 2.0 (the "License"); 724 * you may not use this file except in compliance with the License. 725 * You may obtain a copy of the License at 726 * 727 * http://www.apache.org/licenses/LICENSE-2.0 728 * 729 * Unless required by applicable law or agreed to in writing, software 730 * distributed under the License is distributed on an "AS IS" BASIS, 731 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 732 * See the License for the specific language governing permissions and 733 * limitations under the License. 734 ******************************************************************************/ 735/* global 736 CQ 737 */ 738(function() { 739 "use strict"; 740 741 var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined; 742 if (!containerUtils) { 743 // eslint-disable-next-line no-console 744 console.warn("Tabs: container utilities at window.CQ.CoreComponents.container.utils are not available. This can lead to missing features. Ensure the core.wcm.components.commons.site.container client library is included on the page."); 745 } 746 var dataLayerEnabled; 747 var dataLayer; 748 var dataLayerName; 749 750 var NS = "cmp"; 751 var IS = "tabs"; 752 753 var keyCodes = { 754 END: 35, 755 HOME: 36, 756 ARROW_LEFT: 37, 757 ARROW_UP: 38, 758 ARROW_RIGHT: 39, 759 ARROW_DOWN: 40 760 }; 761 762 var selectors = { 763 self: "[data-" + NS + '-is="' + IS + '"]', 764 active: { 765 tab: "cmp-tabs__tab--active", 766 tabpanel: "cmp-tabs__tabpanel--active" 767 } 768 }; 769 770 /** 771 * Tabs Configuration 772 * 773 * @typedef {Object} TabsConfig Represents a Tabs configuration 774 * @property {HTMLElement} element The HTMLElement representing the Tabs 775 * @property {Object} options The Tabs options 776 */ 777 778 /** 779 * Tabs 780 * 781 * @class Tabs 782 * @classdesc An interactive Tabs component for navigating a list of tabs 783 * @param {TabsConfig} config The Tabs configuration 784 */ 785 function Tabs(config) { 786 var that = this; 787 788 if (config && config.element) { 789 init(config); 790 } 791 792 /** 793 * Initializes the Tabs 794 * 795 * @private 796 * @param {TabsConfig} config The Tabs configuration 797 */ 798 function init(config) { 799 that._config = config; 800 801 // prevents multiple initialization 802 config.element.removeAttribute("data-" + NS + "-is"); 803 804 cacheElements(config.element); 805 that._active = getActiveIndex(that._elements["tab"]); 806 807 if (that._elements.tabpanel) { 808 refreshActive(); 809 bindEvents(); 810 scrollToDeepLinkIdInTabs(); 811 } 812 813 if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { 814 /* 815 * Editor message handling: 816 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame
817 * - check that the message data panel container type is correct and that the id (path) matches this specific Tabs component 818 * - if so, route the "navigate" operation to enact a navigation of the Tabs based on index data 819 */ 820 CQ.CoreComponents.MESSAGE_CHANNEL = CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); 821 CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) { 822 if (message.data && message.data.type === "cmp-tabs" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) { 823 if (message.data.operation === "navigate") { 824 navigate(message.data.index); 825 } 826 } 827 }); 828 } 829 } 830 831 /** 832 * Displays the panel containing the element that corresponds to the deep link in the URI fragment 833 * and scrolls the browser to this element. 834 */ 835 function scrollToDeepLinkIdInTabs() { 836 if (containerUtils) { 837 var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "tab", "tabpanel"); 838 if (deepLinkItemIdx > -1) { 839 var deepLinkItem = that._elements["tab"][deepLinkItemIdx]; 840 if (deepLinkItem && that._elements["tab"][that._active].id !== deepLinkItem.id) { 841 navigateAndFocusTab(deepLinkItemIdx, true); 842 } 843 var hashId = window.location.hash.substring(1); 844 if (hashId) { 845 var hashItem = document.querySelector("[id='" + hashId + "']"); 846 if (hashItem) { 847 hashItem.scrollIntoView(); 848 } 849 } 850 } 851 } 852 } 853 854 /** 855 * Returns the index of the active tab, if no tab is active returns 0 856 * 857 * @param {Array} tabs Tab elements 858 * @returns {Number} Index of the active tab, 0 if none is active 859 */ 860 function getActiveIndex(tabs) { 861 if (tabs) { 862 for (var i = 0; i < tabs.length; i++) { 863 if (tabs[i].classList.contains(selectors.active.tab)) { 864 return i; 865 } 866 } 867 } 868 return 0; 869 } 870 871 /** 872 * Caches the Tabs elements as defined via the {@code data-tabs-hook="ELEMENT_NAME"} markup API 873 * 874 * @private 875 * @param {HTMLElement} wrapper The Tabs wrapper element 876 */ 877 function cacheElements(wrapper) { 878 that._elements = {}; 879 that._elements.self = wrapper; 880 var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]"); 881 882 for (var i = 0; i < hooks.length; i++) { 883 var hook = hooks[i]; 884 if (hook.closest("." + NS + "-" + IS) === that._elements.self) { // only process own tab elements 885 var capitalized = IS; 886 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 887 var key = hook.dataset[NS + "Hook" + capitalized]; 888 if (that._elements[key]) { 889 if (!Array.isArray(that._elements[key])) { 890 var tmp = that._elements[key]; 891 that._elements[key] = [tmp]; 892 } 893 that._elements[key].push(hook); 894 } else { 895 that._elements[key] = hook; 896 } 897 } 898 } 899 } 900 901 /** 902 * Binds Tabs event handling 903 * 904 * @private 905 */ 906 function bindEvents() { 907 window.addEventListener("hashchange", scrollToDeepLinkIdInTabs, false); 908 var tabs = that._elements["tab"]; 909 if (tabs) { 910 for (var i = 0; i < tabs.length; i++) { 911 (function(index) { 912 tabs[i].addEventListener("click", function(event) { 913 navigateAndFocusTab(index); 914 }); 915 tabs[i].addEventListener("keydown", function(event) { 916 onKeyDown(event); 917 }); 918 })(i); 919 } 920 } 921 } 922 923 /** 924 * Handles tab keydown events 925 * 926 * @private 927 * @param {Object} event The keydown event 928 */ 929 function onKeyDown(event) { 930 var index = that._active; 931 var lastIndex = that._elements["tab"].length - 1; 932 933 switch (event.keyCode) { 934 case keyCodes.ARROW_LEFT: 935 case keyCodes.ARROW_UP: 936 event.preventDefault(); 937 if (index > 0) { 938 navigateAndFocusTab(index - 1); 939 } 940 break; 941 case keyCodes.ARROW_RIGHT: 942 case keyCodes.ARROW_DOWN: 943 event.preventDefault(); 944 if (index < lastIndex) { 945 navigateAndFocusTab(index + 1); 946 } 947 break; 948 case keyCodes.HOME: 949 event.preventDefault(); 950 navigateAndFocusTab(0); 951 break; 952 case keyCodes.END: 953 event.preventDefault(); 954 navigateAndFocusTab(lastIndex); 955 break; 956 default: 957 return; 958 } 959 } 960 961 /** 962 * Refreshes the tab markup based on the current {@code Tabs#_active} index 963 * 964 * @private 965 */ 966 function refreshActive() { 967 var tabpanels = that._elements["tabpanel"]; 968 var tabs = that._elements["tab"]; 969 970 if (tabpanels) { 971 if (Array.isArray(tabpanels)) { 972 for (var i = 0; i < tabpanels.length; i++) { 973 if (i === parseInt(that._active)) { 974 tabpanels[i].classList.add(selectors.active.tabpanel); 975 tabpanels[i].removeAttribute("aria-hidden"); 976 tabs[i].classList.add(selectors.active.tab); 977 tabs[i].setAttribute("aria-selected", true); 978 tabs[i].setAttribute("tabindex", "0"); 979 } else { 980 tabpanels[i].classList.remove(selectors.active.tabpanel); 981 tabpanels[i].setAttribute("aria-hidden", true); 982 tabs[i].classList.remove(selectors.active.tab); 983 tabs[i].setAttribute("aria-selected", false); 984 tabs[i].setAttribute("tabindex", "-1"); 985 } 986 } 987 } else { 988 // only one tab 989 tabpanels.classList.add(selectors.active.tabpanel); 990 tabs.classList.add(selectors.active.tab); 991 } 992 } 993 } 994 995 /** 996 * Focuses the element and prevents scrolling the element into view 997 * 998 * @param {HTMLElement} element Element to focus 999 */ 1000 function focusWithoutScroll(element) { 1001 var x = window.scrollX || window.pageXOffset; 1002 var y = window.scrollY || window.pageYOffset; 1003 element.focus(); 1004 window.scrollTo(x, y); 1005 } 1006 1007 /** 1008 * Navigates to the tab at the provided index 1009 * 1010 * @private 1011 * @param {Number} index The index of the tab to navigate to 1012 */ 1013 function navigate(index) { 1014 that._active = index; 1015 refreshActive(); 1016 } 1017 1018 /** 1019 * Navigates to the item at the provided index and ensures the active tab gains focus 1020 * 1021 * @private 1022 * @param {Number} index The index of the item to navigate to 1023 * @param {Boolean} keepHash true to keep the hash in the URL, false to update it 1024 */ 1025 function navigateAndFocusTab(index, keepHash) { 1026 var exActive = that._active; 1027 if (!keepHash && containerUtils) { 1028 containerUtils.updateUrlHash(that, "tab", index); 1029 } 1030 navigate(index); 1031 focusWithoutScroll(that._elements["tab"][index]); 1032 1033 if (dataLayerEnabled) { 1034 1035 var activeItem = getDataLayerId(that._elements.tabpanel[index]); 1036 var exActiveItem = getDataLayerId(that._elements.tabpanel[exActive]); 1037 1038 dataLayer.push({ 1039 event: "cmp:show", 1040 eventInfo: { 1041 path: "component." + activeItem 1042 } 1043 }); 1044 1045 dataLayer.push({ 1046 event: "cmp:hide", 1047 eventInfo: { 1048 path: "component." + exActiveItem 1049 } 1050 }); 1051 1052 var tabsId = that._elements.self.id; 1053 var uploadPayload = { component: {} }; 1054 uploadPayload.component[tabsId] = { shownItems: [activeItem] }; 1055 1056 var removePayload = { component: {} }; 1057 removePayload.component[tabsId] = { shownItems: undefined }; 1058 1059 dataLayer.push(removePayload); 1060 dataLayer.push(uploadPayload); 1061 } 1062 } 1063 } 1064 1065 /**
1066 * Reads options data from the Tabs wrapper element, defined via {@code data-cmp-*} data attributes 1067 * 1068 * @private 1069 * @param {HTMLElement} element The Tabs element to read options data from 1070 * @returns {Object} The options read from the component data attributes 1071 */ 1072 function readData(element) { 1073 var data = element.dataset; 1074 var options = []; 1075 var capitalized = IS; 1076 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 1077 var reserved = ["is", "hook" + capitalized]; 1078 1079 for (var key in data) { 1080 if (Object.prototype.hasOwnProperty.call(data, key)) { 1081 var value = data[key]; 1082 1083 if (key.indexOf(NS) === 0) { 1084 key = key.slice(NS.length); 1085 key = key.charAt(0).toLowerCase() + key.substring(1); 1086 1087 if (reserved.indexOf(key) === -1) { 1088 options[key] = value; 1089 } 1090 } 1091 } 1092 } 1093 1094 return options; 1095 } 1096 1097 /** 1098 * Parses the dataLayer string and returns the ID 1099 * 1100 * @private 1101 * @param {HTMLElement} item the accordion item 1102 * @returns {String} dataLayerId or undefined 1103 */ 1104 function getDataLayerId(item) { 1105 if (item) { 1106 if (item.dataset.cmpDataLayer) { 1107 return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0]; 1108 } else { 1109 return item.id; 1110 } 1111 } 1112 return null; 1113 } 1114 1115 /** 1116 * Document ready handler and DOM mutation observers. Initializes Tabs components as necessary. 1117 * 1118 * @private 1119 */ 1120 function onDocumentReady() { 1121 dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled"); 1122 if (dataLayerEnabled) { 1123 dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer"; 1124 dataLayer = window[dataLayerName] = window[dataLayerName] || []; 1125 } 1126 1127 var elements = document.querySelectorAll(selectors.self); 1128 for (var i = 0; i < elements.length; i++) { 1129 new Tabs({ element: elements[i], options: readData(elements[i]) }); 1130 } 1131 1132 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; 1133 var body = document.querySelector("body"); 1134 var observer = new MutationObserver(function(mutations) { 1135 mutations.forEach(function(mutation) { 1136 // needed for IE 1137 var nodesArray = [].slice.call(mutation.addedNodes); 1138 if (nodesArray.length > 0) { 1139 nodesArray.forEach(function(addedNode) { 1140 if (addedNode.querySelectorAll) { 1141 var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self)); 1142 elementsArray.forEach(function(element) { 1143 new Tabs({ element: element, options: readData(element) }); 1144 }); 1145 } 1146 }); 1147 } 1148 }); 1149 }); 1150 1151 observer.observe(body, { 1152 subtree: true, 1153 childList: true, 1154 characterData: true 1155 }); 1156 } 1157 1158 if (document.readyState !== "loading") { 1159 onDocumentReady(); 1160 } else { 1161 document.addEventListener("DOMContentLoaded", onDocumentReady); 1162 } 1163 1164 if (containerUtils) { 1165 window.addEventListener("load", containerUtils.scrollToAnchor, false); 1166 } 1167 1168}()); 1169 1170 1171/******************************************************************************* 1172 * Copyright 2022 Adobe 1173 * 1174 * Licensed under the Apache License, Version 2.0 (the "License"); 1175 * you may not use this file except in compliance with the License. 1176 * You may obtain a copy of the License at 1177 * 1178 * http://www.apache.org/licenses/LICENSE-2.0 1179 * 1180 * Unless required by applicable law or agreed to in writing, software 1181 * distributed under the License is distributed on an "AS IS" BASIS, 1182 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1183 * See the License for the specific language governing permissions and 1184 * limitations under the License. 1185 ******************************************************************************/ 1186(function(document) { 1187 "use strict"; 1188 1189 window.CMP = window.CMP || {}; 1190 window.CMP.utils = (function() { 1191 var NS = "cmp"; 1192 1193 /**
1194 * Reads options data from the Component wrapper element, defined via {@code data-cmp-*} data attributes 1195 * 1196 * @param {HTMLElement} element The component element to read options data from 1197 * @param {String} is The component identifier 1198 * @returns {String[]} The options read from the component data attributes 1199 */ 1200 var readData = function(element, is) { 1201 var data = element.dataset; 1202 var options = []; 1203 var capitalized = is; 1204 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 1205 var reserved = ["is", "hook" + capitalized]; 1206 1207 for (var key in data) { 1208 if (Object.prototype.hasOwnProperty.call(data, key)) { 1209 var value = data[key]; 1210 1211 if (key.indexOf(NS) === 0) { 1212 key = key.slice(NS.length); 1213 key = key.charAt(0).toLowerCase() + key.substring(1); 1214 1215 if (reserved.indexOf(key) === -1) { 1216 options[key] = value; 1217 } 1218 } 1219 } 1220 } 1221 return options; 1222 }; 1223 1224 /** 1225 * Set up the final properties of a component by evaluating the transform function or fall back to the default value on demand 1226 * @param {String[]} options the options to transform 1227 * @param {Object} properties object of properties of property functions 1228 * @returns {Object} transformed properties 1229 */ 1230 var setupProperties = function(options, properties) { 1231 var transformedProperties = {}; 1232 1233 for (var key in properties) { 1234 if (Object.prototype.hasOwnProperty.call(properties, key)) { 1235 var property = properties[key]; 1236 if (options && options[key] != null) { 1237 if (property && typeof property.transform === "function") { 1238 transformedProperties[key] = property.transform(options[key]); 1239 } else { 1240 transformedProperties[key] = options[key]; 1241 } 1242 } else { 1243 transformedProperties[key] = properties[key]["default"]; 1244 } 1245 } 1246 } 1247 return transformedProperties; 1248 }; 1249 1250 1251 return { 1252 readData: readData, 1253 setupProperties: setupProperties 1254 }; 1255 }()); 1256}(window.document)); 1257 1258/******************************************************************************* 1259 * Copyright 2018 Adobe 1260 * 1261 * Licensed under the Apache License, Version 2.0 (the "License"); 1262 * you may not use this file except in compliance with the License. 1263 * You may obtain a copy of the License at 1264 * 1265 * http://www.apache.org/licenses/LICENSE-2.0 1266 * 1267 * Unless required by applicable law or agreed to in writing, software 1268 * distributed under the License is distributed on an "AS IS" BASIS, 1269 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1270 * See the License for the specific language governing permissions and 1271 * limitations under the License. 1272 ******************************************************************************/ 1273(function() { 1274 "use strict"; 1275 1276 var containerUtils = window.CQ && window.CQ.CoreComponents && window.CQ.CoreComponents.container && window.CQ.CoreComponents.container.utils ? window.CQ.CoreComponents.container.utils : undefined; 1277 if (!containerUtils) { 1278 // eslint-disable-next-line no-console 1279 console.warn("Tabs: container utilities at window.CQ.CoreComponents.container.utils are not available. This can lead to missing features. Ensure the core.wcm.components.commons.site.container client library is included on the page."); 1280 } 1281 var dataLayerEnabled; 1282 var dataLayer; 1283 var dataLayerName; 1284 1285 var NS = "cmp"; 1286 var IS = "carousel"; 1287 1288 var keyCodes = { 1289 SPACE: 32, 1290 END: 35, 1291 HOME: 36, 1292 ARROW_LEFT: 37, 1293 ARROW_UP: 38, 1294 ARROW_RIGHT: 39, 1295 ARROW_DOWN: 40 1296 }; 1297 1298 var selectors = { 1299 self: "[data-" + NS + '-is="' + IS + '"]' 1300 }; 1301 1302 var properties = { 1303 /** 1304 * Determines whether the Carousel will automatically transition between slides 1305 * 1306 * @memberof Carousel
1307 * @type {Boolean} 1308 * @default false 1309 */ 1310 "autoplay": { 1311 "default": false, 1312 "transform": function(value) { 1313 return !(value === null || typeof value === "undefined"); 1314 } 1315 }, 1316 /** 1317 * Duration (in milliseconds) before automatically transitioning to the next slide 1318 * 1319 * @memberof Carousel 1320 * @type {Number} 1321 * @default 5000 1322 */ 1323 "delay": { 1324 "default": 5000, 1325 "transform": function(value) { 1326 value = parseFloat(value); 1327 return !isNaN(value) ? value : null; 1328 } 1329 }, 1330 /** 1331 * Determines whether automatic pause on hovering the carousel is disabled 1332 * 1333 * @memberof Carousel 1334 * @type {Boolean} 1335 * @default false 1336 */ 1337 "autopauseDisabled": { 1338 "default": false, 1339 "transform": function(value) { 1340 return !(value === null || typeof value === "undefined"); 1341 } 1342 } 1343 }; 1344 1345 /** 1346 * Carousel Configuration 1347 * 1348 * @typedef {Object} CarouselConfig Represents a Carousel configuration 1349 * @property {HTMLElement} element The HTMLElement representing the Carousel 1350 * @property {*[]} options The Carousel options 1351 */ 1352 1353 /** 1354 * Carousel 1355 * 1356 * @class Carousel 1357 * @classdesc An interactive Carousel component for navigating a list of generic items 1358 * @param {CarouselConfig} config The Carousel configuration 1359 */ 1360 function Carousel(config) { 1361 var that = this; 1362 1363 if (config && config.element) { 1364 init(config); 1365 } 1366 1367 /** 1368 * Initializes the Carousel 1369 * 1370 * @private 1371 * @param {CarouselConfig} config The Carousel configuration 1372 */ 1373 function init(config) { 1374 that._config = config; 1375 1376 // prevents multiple initialization 1377 config.element.removeAttribute("data-" + NS + "-is"); 1378 1379 setupProperties(config.options); 1380 cacheElements(config.element); 1381 1382 that._active = 0; 1383 that._paused = false; 1384 1385 if (that._elements.item) { 1386 initializeActive(); 1387 bindEvents(); 1388 resetAutoplayInterval(); 1389 refreshPlayPauseActions(); 1390 scrollToDeepLinkIdInCarousel(); 1391 } 1392 1393 // TODO: This section is only relevant in edit mode and should move to the editor clientLib 1394 if (window.Granite && window.Granite.author && window.Granite.author.MessageChannel) { 1395 /* 1396 * Editor message handling: 1397 * - subscribe to "cmp.panelcontainer" message requests sent by the editor frame 1398 * - check that the message data panel container type is correct and that the id (path) matches this specific Carousel component 1399 * - if so, route the "navigate" operation to enact a navigation of the Carousel based on index data 1400 */ 1401 window.CQ = window.CQ || {}; 1402 window.CQ.CoreComponents = window.CQ.CoreComponents || {}; 1403 window.CQ.CoreComponents.MESSAGE_CHANNEL = window.CQ.CoreComponents.MESSAGE_CHANNEL || new window.Granite.author.MessageChannel("cqauthor", window); 1404 window.CQ.CoreComponents.MESSAGE_CHANNEL.subscribeRequestMessage("cmp.panelcontainer", function(message) { 1405 if (message.data && message.data.type === "cmp-carousel" && message.data.id === that._elements.self.dataset["cmpPanelcontainerId"]) { 1406 if (message.data.operation === "navigate") { 1407 navigate(message.data.index); 1408 } 1409 } 1410 }); 1411 } 1412 } 1413 1414 /** 1415 * Displays the slide containing the element that corresponds to the deep link in the URI fragment 1416 * and scrolls the browser to this element. 1417 */ 1418 function scrollToDeepLinkIdInCarousel() { 1419 if (containerUtils) { 1420 var deepLinkItemIdx = containerUtils.getDeepLinkItemIdx(that, "item", "item"); 1421 if (deepLinkItemIdx > -1) { 1422 var deepLinkItem = that._elements["item"][deepLinkItemIdx]; 1423 if (deepLinkItem && that._elements["item"][that._active].id !== deepLinkItem.id) { 1424 navigateAndFocusIndicator(deepLinkItemIdx, true); 1425 // pause the carousel auto-rotation 1426 pause(); 1427 } 1428 var hashId = window.location.hash.substring(1); 1429 if (hashId) { 1430 var hashItem = document.querySelector("[id='" + hashId + "']"); 1431 if (hashItem) { 1432 hashItem.scrollIntoView(); 1433 } 1434 } 1435 } 1436 } 1437 } 1438 1439 /** 1440 * Caches the Carousel elements as defined via the {@code data-carousel-hook="ELEMENT_NAME"} markup API 1441 * 1442 * @private 1443 * @param {HTMLElement} wrapper The Carousel wrapper element 1444 */ 1445 function cacheElements(wrapper) { 1446 that._elements = {}; 1447 that._elements.self = wrapper; 1448 var hooks = that._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]"); 1449 1450 for (var i = 0; i < hooks.length; i++) { 1451 var hook = hooks[i]; 1452 var capitalized = IS; 1453 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 1454 var key = hook.dataset[NS + "Hook" + capitalized]; 1455 if (that._elements[key]) { 1456 if (!Array.isArray(that._elements[key])) { 1457 var tmp = that._elements[key]; 1458 that._elements[key] = [tmp]; 1459 } 1460 that._elements[key].push(hook); 1461 } else { 1462 that._elements[key] = hook; 1463 } 1464 } 1465 } 1466 1467 /** 1468 * Sets up properties for the Carousel based on the passed options. 1469 * 1470 * @private 1471 * @param {Object} options The Carousel options 1472 */ 1473 function setupProperties(options) { 1474 that._properties = {}; 1475 1476 for (var key in properties) { 1477 if (Object.prototype.hasOwnProperty.call(properties, key)) { 1478 var property = properties[key]; 1479 var value = null; 1480 1481 if (options && options[key] != null) { 1482 value = options[key]; 1483 1484 // transform the provided option 1485 if (property && typeof property.transform === "function") { 1486 value = property.transform(value); 1487 } 1488 } 1489 1490 if (value === null) { 1491 // value still null, take the property default 1492 value = properties[key]["default"]; 1493 } 1494 1495 that._properties[key] = value; 1496 } 1497 } 1498 } 1499 1500 /** 1501 * Binds Carousel event handling 1502 * 1503 * @private 1504 */ 1505 function bindEvents() { 1506 window.addEventListener("hashchange", scrollToDeepLinkIdInCarousel, false); 1507 if (that._elements["previous"]) { 1508 that._elements["previous"].addEventListener("click", function() { 1509 var index = getPreviousIndex(); 1510 navigate(index); 1511 if (dataLayerEnabled) { 1512 dataLayer.push({ 1513 event: "cmp:show", 1514 eventInfo: { 1515 path: "component." + getDataLayerId(that._elements.item[index]) 1516 } 1517 }); 1518 } 1519 }); 1520 } 1521 1522 if (that._elements["next"]) { 1523 that._elements["next"].addEventListener("click", function() { 1524 var index = getNextIndex(); 1525 navigate(index); 1526 if (dataLayerEnabled) { 1527 dataLayer.push({ 1528 event: "cmp:show", 1529 eventInfo: { 1530 path: "component." + getDataLayerId(that._elements.item[index]) 1531 } 1532 }); 1533 } 1534 }); 1535 } 1536 1537 var indicators = that._elements["indicator"]; 1538 if (indicators) { 1539 for (var i = 0; i < indicators.length; i++) { 1540 (function(index) { 1541 indicators[i].addEventListener("click", function(event) { 1542 navigateAndFocusIndicator(index); 1543 // pause the carousel auto-rotation 1544 pause(); 1545 }); 1546 })(i); 1547 } 1548 } 1549 1550 if (that._elements["pause"]) { 1551 if (that._properties.autoplay) { 1552 that._elements["pause"].addEventListener("click", onPauseClick); 1553 } 1554 } 1555 1556 if (that._elements["play"]) { 1557 if (that._properties.autoplay) { 1558 that._elements["play"].addEventListener("click", onPlayClick); 1559 } 1560 } 1561 1562 that._elements.self.addEventListener("keydown", onKeyDown); 1563 1564 if (!that._properties.autopauseDisabled) { 1565 that._elements.self.addEventListener("mouseenter", onMouseEnter); 1566 that._elements.self.addEventListener("mouseleave", onMouseLeave); 1567 } 1568 1569 // for accessibility we pause animation when a element get focused 1570 var items = that._elements["item"]; 1571 if (items) { 1572 for (var j = 0; j < items.length; j++) { 1573 items[j].addEventListener("focusin", onMouseEnter); 1574 items[j].addEventListener("focusout", onMouseLeave); 1575 } 1576 } 1577 } 1578 1579 /**
1580 * Handles carousel keydown events 1581 * 1582 * @private 1583 * @param {Object} event The keydown event 1584 */ 1585 function onKeyDown(event) { 1586 var index = that._active; 1587 var lastIndex = that._elements["indicator"].length - 1; 1588 1589 switch (event.keyCode) { 1590 case keyCodes.ARROW_LEFT: 1591 case keyCodes.ARROW_UP: 1592 event.preventDefault(); 1593 if (index > 0) { 1594 navigateAndFocusIndicator(index - 1); 1595 } 1596 break; 1597 case keyCodes.ARROW_RIGHT: 1598 case keyCodes.ARROW_DOWN: 1599 event.preventDefault(); 1600 if (index < lastIndex) { 1601 navigateAndFocusIndicator(index + 1); 1602 } 1603 break; 1604 case keyCodes.HOME: 1605 event.preventDefault(); 1606 navigateAndFocusIndicator(0); 1607 break; 1608 case keyCodes.END: 1609 event.preventDefault(); 1610 navigateAndFocusIndicator(lastIndex); 1611 break; 1612 case keyCodes.SPACE: 1613 if (that._properties.autoplay && (event.target !== that._elements["previous"] && event.target !== that._elements["next"])) { 1614 event.preventDefault(); 1615 if (!that._paused) { 1616 pause(); 1617 } else { 1618 play(); 1619 } 1620 } 1621 if (event.target === that._elements["pause"]) { 1622 that._elements["play"].focus(); 1623 } 1624 if (event.target === that._elements["play"]) { 1625 that._elements["pause"].focus(); 1626 } 1627 break; 1628 default: 1629 return; 1630 } 1631 } 1632 1633 /** 1634 * Handles carousel mouseenter events 1635 * 1636 * @private 1637 * @param {Object} event The mouseenter event 1638 */ 1639 function onMouseEnter(event) { 1640 clearAutoplayInterval(); 1641 } 1642 1643 /** 1644 * Handles carousel mouseleave events 1645 * 1646 * @private 1647 * @param {Object} event The mouseleave event 1648 */ 1649 function onMouseLeave(event) { 1650 resetAutoplayInterval(); 1651 } 1652 1653 /** 1654 * Handles pause element click events 1655 * 1656 * @private 1657 * @param {Object} event The click event 1658 */ 1659 function onPauseClick(event) { 1660 pause(); 1661 that._elements["play"].focus(); 1662 } 1663 1664 /** 1665 * Handles play element click events 1666 * 1667 * @private 1668 * @param {Object} event The click event 1669 */ 1670 function onPlayClick() { 1671 play(); 1672 that._elements["pause"].focus(); 1673 } 1674 1675 /** 1676 * Pauses the playing of the Carousel. Sets {@code Carousel#_paused} marker. 1677 * Only relevant when autoplay is enabled 1678 * 1679 * @private 1680 */ 1681 function pause() { 1682 that._paused = true; 1683 clearAutoplayInterval(); 1684 refreshPlayPauseActions(); 1685 } 1686 1687 /** 1688 * Enables the playing of the Carousel. Sets {@code Carousel#_paused} marker. 1689 * Only relevant when autoplay is enabled 1690 * 1691 * @private 1692 */ 1693 function play() { 1694 that._paused = false; 1695 1696 // If the Carousel is hovered, don't begin auto transitioning until the next mouse leave event 1697 var hovered = false; 1698 if (that._elements.self.parentElement) { 1699 hovered = that._elements.self.parentElement.querySelector(":hover") === that._elements.self; 1700 } 1701 if (that._properties.autopauseDisabled || !hovered) { 1702 resetAutoplayInterval(); 1703 } 1704 1705 refreshPlayPauseActions(); 1706 } 1707 1708 /** 1709 * Refreshes the play/pause action markup based on the {@code Carousel#_paused} state 1710 * 1711 * @private 1712 */ 1713 function refreshPlayPauseActions() {
1714 setActionDisabled(that._elements["pause"], that._paused); 1715 setActionDisabled(that._elements["play"], !that._paused); 1716 } 1717 1718 /** 1719 * Initialize {@code Carousel#_active} based on the active item of the carousel. 1720 */ 1721 function initializeActive() { 1722 var items = that._elements["item"]; 1723 if (items && Array.isArray(items)) { 1724 for (var i = 0; i < items.length; i++) { 1725 if (items[i].classList.contains("cmp-carousel__item--active")) { 1726 that._active = i; 1727 break; 1728 } 1729 } 1730 } 1731 } 1732 1733 /** 1734 * Refreshes the item markup based on the current {@code Carousel#_active} index 1735 * 1736 * @private 1737 */ 1738 function refreshActive() { 1739 var items = that._elements["item"]; 1740 var indicators = that._elements["indicator"]; 1741 1742 if (items) { 1743 if (Array.isArray(items)) { 1744 for (var i = 0; i < items.length; i++) { 1745 if (i === parseInt(that._active)) { 1746 items[i].classList.add("cmp-carousel__item--active"); 1747 items[i].removeAttribute("aria-hidden"); 1748 indicators[i].classList.add("cmp-carousel__indicator--active"); 1749 indicators[i].setAttribute("aria-selected", true); 1750 indicators[i].setAttribute("tabindex", "0"); 1751 } else { 1752 items[i].classList.remove("cmp-carousel__item--active"); 1753 items[i].setAttribute("aria-hidden", true); 1754 indicators[i].classList.remove("cmp-carousel__indicator--active"); 1755 indicators[i].setAttribute("aria-selected", false); 1756 indicators[i].setAttribute("tabindex", "-1"); 1757 } 1758 } 1759 } else { 1760 // only one item 1761 items.classList.add("cmp-carousel__item--active"); 1762 indicators.classList.add("cmp-carousel__indicator--active"); 1763 } 1764 } 1765 } 1766 1767 /** 1768 * Focuses the element and prevents scrolling the element into view 1769 * 1770 * @param {HTMLElement} element Element to focus 1771 */ 1772 function focusWithoutScroll(element) { 1773 var x = window.scrollX || window.pageXOffset; 1774 var y = window.scrollY || window.pageYOffset; 1775 element.focus(); 1776 window.scrollTo(x, y); 1777 } 1778 1779 /** 1780 * Retrieves the next active index, with looping 1781 * 1782 * @private 1783 * @returns {Number} Index of the next carousel item 1784 */ 1785 function getNextIndex() { 1786 return that._active === (that._elements["item"].length - 1) ? 0 : that._active + 1; 1787 } 1788 1789 /** 1790 * Retrieves the previous active index, with looping 1791 * 1792 * @private 1793 * @returns {Number} Index of the previous carousel item 1794 */ 1795 function getPreviousIndex() { 1796 return that._active === 0 ? (that._elements["item"].length - 1) : that._active - 1; 1797 } 1798 1799 /** 1800 * Navigates to the item at the provided index 1801 * 1802 * @private 1803 * @param {Number} index The index of the item to navigate to 1804 * @param {Boolean} keepHash true to keep the hash in the URL, false to update it 1805 */ 1806 function navigate(index, keepHash) { 1807 if (index < 0 || index > (that._elements["item"].length - 1)) { 1808 return; 1809 } 1810 1811 that._active = index; 1812 refreshActive(); 1813 1814 if (!keepHash && containerUtils) { 1815 containerUtils.updateUrlHash(that, "item", index); 1816 } 1817 1818 if (dataLayerEnabled) { 1819 var carouselId = that._elements.self.id; 1820 var activeItem = getDataLayerId(that._elements.item[index]); 1821 var updatePayload = { component: {} }; 1822 updatePayload.component[carouselId] = { shownItems: [activeItem] }; 1823 1824 var removePayload = { component: {} }; 1825 removePayload.component[carouselId] = { shownItems: undefined }; 1826 1827 dataLayer.push(removePayload); 1828 dataLayer.push(updatePayload); 1829 } 1830 1831 // reset the autoplay transition interval following navigation, if not already hovering the carousel
1832 if (that._elements.self.parentElement) { 1833 if (that._elements.self.parentElement.querySelector(":hover") !== that._elements.self) { 1834 resetAutoplayInterval(); 1835 } 1836 } 1837 } 1838 1839 /** 1840 * Navigates to the item at the provided index and ensures the active indicator gains focus 1841 * 1842 * @private 1843 * @param {Number} index The index of the item to navigate to 1844 * @param {Boolean} keepHash true to keep the hash in the URL, false to update it 1845 */ 1846 function navigateAndFocusIndicator(index, keepHash) { 1847 navigate(index, keepHash); 1848 focusWithoutScroll(that._elements["indicator"][index]); 1849 1850 if (dataLayerEnabled) { 1851 dataLayer.push({ 1852 event: "cmp:show", 1853 eventInfo: { 1854 path: "component." + getDataLayerId(that._elements.item[index]) 1855 } 1856 }); 1857 } 1858 } 1859 1860 /** 1861 * Starts/resets automatic slide transition interval 1862 * 1863 * @private 1864 */ 1865 function resetAutoplayInterval() { 1866 if (that._paused || !that._properties.autoplay) { 1867 return; 1868 } 1869 clearAutoplayInterval(); 1870 that._autoplayIntervalId = window.setInterval(function() { 1871 if (document.visibilityState && document.hidden) { 1872 return; 1873 } 1874 var indicators = that._elements["indicators"]; 1875 if (indicators !== document.activeElement && indicators.contains(document.activeElement)) { 1876 // if an indicator has focus, ensure we switch focus following navigation 1877 navigateAndFocusIndicator(getNextIndex(), true); 1878 } else { 1879 navigate(getNextIndex(), true); 1880 } 1881 }, that._properties.delay); 1882 } 1883 1884 /** 1885 * Clears/pauses automatic slide transition interval 1886 * 1887 * @private 1888 */ 1889 function clearAutoplayInterval() { 1890 window.clearInterval(that._autoplayIntervalId); 1891 that._autoplayIntervalId = null; 1892 } 1893 1894 /** 1895 * Sets the disabled state for an action and toggles the appropriate CSS classes 1896 * 1897 * @private 1898 * @param {HTMLElement} action Action to disable 1899 * @param {Boolean} [disable] {@code true} to disable, {@code false} to enable 1900 */ 1901 function setActionDisabled(action, disable) { 1902 if (!action) { 1903 return; 1904 } 1905 if (disable !== false) { 1906 action.disabled = true; 1907 action.classList.add("cmp-carousel__action--disabled"); 1908 } else { 1909 action.disabled = false; 1910 action.classList.remove("cmp-carousel__action--disabled"); 1911 } 1912 } 1913 } 1914 1915 /** 1916 * Parses the dataLayer string and returns the ID 1917 * 1918 * @private 1919 * @param {HTMLElement} item the accordion item 1920 * @returns {String} dataLayerId or undefined 1921 */ 1922 function getDataLayerId(item) { 1923 if (item) { 1924 if (item.dataset.cmpDataLayer) { 1925 return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0]; 1926 } else { 1927 return item.id; 1928 } 1929 } 1930 return null; 1931 } 1932 1933 /** 1934 * Document ready handler and DOM mutation observers. Initializes Carousel components as necessary. 1935 * 1936 * @private 1937 */ 1938 function onDocumentReady() { 1939 dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled"); 1940 if (dataLayerEnabled) { 1941 dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer"; 1942 dataLayer = window[dataLayerName] = window[dataLayerName] || []; 1943 } 1944 1945 var elements = document.querySelectorAll(selectors.self); 1946 for (var i = 0; i < elements.length; i++) { 1947 new Carousel({ element: elements[i], options: CMP.utils.readData(elements[i], IS) }); 1948 } 1949 1950 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; 1951 var body = document.querySelector("body"); 1952 var observer = new MutationObserver(function(mutations) { 1953 mutations.forEach(function(mutation) { 1954 // needed for IE 1955 var nodesArray = [].slice.call(mutation.addedNodes); 1956 if (nodesArray.length > 0) { 1957 nodesArray.forEach(function(addedNode) { 1958 if (addedNode.querySelectorAll) { 1959 var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self)); 1960 elementsArray.forEach(function(element) { 1961 new Carousel({ element: element, options: CMP.utils.readData(element, IS) }); 1962 }); 1963 } 1964 }); 1965 } 1966 }); 1967 }); 1968 1969 observer.observe(body, { 1970 subtree: true, 1971 childList: true, 1972 characterData: true 1973 }); 1974 } 1975 1976 var documentReady = document.readyState !== "loading" ? Promise.resolve() : new Promise(function(resolve) { 1977 document.addEventListener("DOMContentLoaded", resolve); 1978 }); 1979 Promise.all([documentReady]).then(onDocumentReady); 1980 1981 if (containerUtils) { 1982 window.addEventListener("load", containerUtils.scrollToAnchor, false); 1983 } 1984 1985}()); 1986 1987 1988/******************************************************************************* 1989 * Copyright 2022 Adobe 1990 * 1991 * Licensed under the Apache License, Version 2.0 (the "License");
1992 * you may not use this file except in compliance with the License. 1993 * You may obtain a copy of the License at 1994 * 1995 * http://www.apache.org/licenses/LICENSE-2.0 1996 * 1997 * Unless required by applicable law or agreed to in writing, software 1998 * distributed under the License is distributed on an "AS IS" BASIS, 1999 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2000 * See the License for the specific language governing permissions and 2001 * limitations under the License. 2002 ******************************************************************************/ 2003(function(document) { 2004 "use strict"; 2005 2006 window.CMP = window.CMP || {}; 2007 window.CMP.utils = (function() { 2008 var NS = "cmp"; 2009 2010 /** 2011 * Reads options data from the Component wrapper element, defined via {@code data-cmp-*} data attributes 2012 * 2013 * @param {HTMLElement} element The component element to read options data from 2014 * @param {String} is The component identifier 2015 * @returns {String[]} The options read from the component data attributes 2016 */ 2017 var readData = function(element, is) { 2018 var data = element.dataset; 2019 var options = []; 2020 var capitalized = is; 2021 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 2022 var reserved = ["is", "hook" + capitalized]; 2023 2024 for (var key in data) { 2025 if (Object.prototype.hasOwnProperty.call(data, key)) { 2026 var value = data[key]; 2027 2028 if (key.indexOf(NS) === 0) { 2029 key = key.slice(NS.length); 2030 key = key.charAt(0).toLowerCase() + key.substring(1); 2031 2032 if (reserved.indexOf(key) === -1) { 2033 options[key] = value; 2034 } 2035 } 2036 } 2037 } 2038 return options; 2039 }; 2040 2041 /** 2042 * Set up the final properties of a component by evaluating the transform function or fall back to the default value on demand 2043 * @param {String[]} options the options to transform 2044 * @param {Object} properties object of properties of property functions 2045 * @returns {Object} transformed properties 2046 */ 2047 var setupProperties = function(options, properties) { 2048 var transformedProperties = {}; 2049 2050 for (var key in properties) { 2051 if (Object.prototype.hasOwnProperty.call(properties, key)) { 2052 var property = properties[key]; 2053 if (options && options[key] != null) { 2054 if (property && typeof property.transform === "function") { 2055 transformedProperties[key] = property.transform(options[key]); 2056 } else { 2057 transformedProperties[key] = options[key]; 2058 } 2059 } else { 2060 transformedProperties[key] = properties[key]["default"]; 2061 } 2062 } 2063 } 2064 return transformedProperties; 2065 }; 2066 2067 2068 return { 2069 readData: readData, 2070 setupProperties: setupProperties 2071 }; 2072 }()); 2073}(window.document)); 2074 2075/******************************************************************************* 2076 * Copyright 2022 Adobe 2077 * 2078 * Licensed under the Apache License, Version 2.0 (the "License"); 2079 * you may not use this file except in compliance with the License. 2080 * You may obtain a copy of the License at 2081 * 2082 * http://www.apache.org/licenses/LICENSE-2.0 2083 * 2084 * Unless required by applicable law or agreed to in writing, software 2085 * distributed under the License is distributed on an "AS IS" BASIS, 2086 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2087 * See the License for the specific language governing permissions and 2088 * limitations under the License. 2089 ******************************************************************************/ 2090(function(document) { 2091 "use strict"; 2092 2093 window.CMP = window.CMP || {}; 2094 window.CMP.image = window.CMP.image || {}; 2095 window.CMP.image.dynamicMedia = (function() { 2096 var autoSmartCrops = {}; 2097 var SRC_URI_TEMPLATE_WIDTH_VAR = "{.width}"; 2098 var SRC_URI_TEMPLATE_DPR_VAR = "{dpr}";
2099 var SRC_URI_DPR_OFF = "dpr=off"; 2100 var SRC_URI_DPR_ON = "dpr=on,{dpr}"; 2101 var dpr = window.devicePixelRatio || 1; 2102 var config = { 2103 minWidth: 20 2104 }; 2105 2106 /** 2107 * get auto smart crops from dm 2108 * @param {String} src the src uri 2109 * @returns {{}} the smart crop json object 2110 */ 2111 var getAutoSmartCrops = function(src) { 2112 var request = new XMLHttpRequest(); 2113 var url = src.split(SRC_URI_TEMPLATE_WIDTH_VAR)[0] + "?req=set,json"; 2114 request.open("GET", url, false); 2115 request.onload = function() { 2116 if (request.status >= 200 && request.status < 400) { 2117 // success status 2118 var responseText = request.responseText; 2119 var rePayload = new RegExp(/^(?:\/\*jsonp\*\/)?\s*([^()]+)\(([\s\S]+),\s*"[0-9]*"\);?$/gmi); 2120 var rePayloadJSON = new RegExp(/^{[\s\S]*}$/gmi); 2121 var resPayload = rePayload.exec(responseText); 2122 var payload; 2123 if (resPayload) { 2124 var payloadStr = resPayload[2]; 2125 if (rePayloadJSON.test(payloadStr)) { 2126 payload = JSON.parse(payloadStr); 2127 } 2128 2129 } 2130 // check "relation" - only in case of smartcrop preset 2131 if (payload && payload.set.relation && payload.set.relation.length > 0) { 2132 for (var i = 0; i < payload.set.relation.length; i++) { 2133 autoSmartCrops[parseInt(payload.set.relation[i].userdata.SmartCropWidth)] = 2134 ":" + payload.set.relation[i].userdata.SmartCropDef; 2135 } 2136 } 2137 } else { 2138 // error status 2139 } 2140 }; 2141 request.send(); 2142 return autoSmartCrops; 2143 }; 2144 2145 /** 2146 * Build and return the srcset value based on the available auto smart crops 2147 * @param {String} src the src uri 2148 * @param {Object} smartCrops the smart crops object 2149 * @returns {String} the srcset 2150 */ 2151 var getSrcSet = function(src, smartCrops) { 2152 var srcset; 2153 var keys = Object.keys(smartCrops); 2154 if (keys.length > 0) { 2155 srcset = []; 2156 for (var key in autoSmartCrops) { 2157 srcset.push(src.replace(SRC_URI_TEMPLATE_WIDTH_VAR, smartCrops[key]) + " " + key + "w"); 2158 } 2159 } 2160 return srcset.join(","); 2161 }; 2162 2163 /** 2164 * Get the optimal width based on the available sizes 2165 * @param {[Number]} sizes the available sizes 2166 * @param {Number} width the element width 2167 * @returns {String} the optimal width 2168 */ 2169 function getOptimalWidth(sizes, width) { 2170 var len = sizes.length; 2171 var key = 0; 2172 2173 while ((key < len - 1) && (sizes[key] < width)) { 2174 key++; 2175 } 2176 2177 return sizes[key] !== undefined ? sizes[key].toString() : width; 2178 } 2179 2180 /** 2181 * Get the width of an element or parent element if the width is smaller than the minimum width 2182 * @param {HTMLElement} component the image component 2183 * @param {HTMLElement | Node} parent the parent element 2184 * @returns {Number} the width of the element 2185 */ 2186 var getWidth = function(component, parent) { 2187 var width = component.offsetWidth; 2188 while (width < config.minWidth && parent && !component._autoWidth) { 2189 width = parent.offsetWidth; 2190 parent = parent.parentNode; 2191 } 2192 return width; 2193 }; 2194 2195 /** 2196 * Set the src and srcset attribute for a Dynamic Media Image which auto smart crops enabled. 2197 * @param {HTMLElement} component the image component 2198 * @param {{}} properties the component properties 2199 */ 2200 var setDMAttributes = function(component, properties) { 2201 // for v3 we first have to turn the dpr on
2202 var src = properties.src.replace(SRC_URI_DPR_OFF, SRC_URI_DPR_ON); 2203 src = src.replace(SRC_URI_TEMPLATE_DPR_VAR, dpr); 2204 var smartCrops = {}; 2205 var width; 2206 if (properties["smartcroprendition"] === "SmartCrop:Auto") { 2207 smartCrops = getAutoSmartCrops(src); 2208 } 2209 var hasWidths = (properties.widths && properties.widths.length > 0) || Object.keys(smartCrops).length > 0; 2210 if (hasWidths) { 2211 var image = component.querySelector("img"); 2212 var elemWidth = getWidth(component, component.parentNode); 2213 if (properties["smartcroprendition"] === "SmartCrop:Auto") { 2214 image.setAttribute("srcset", CMP.image.dynamicMedia.getSrcSet(src, smartCrops)); 2215 width = getOptimalWidth(Object.keys(smartCrops, elemWidth)); 2216 image.setAttribute("src", CMP.image.dynamicMedia.getSrc(src, smartCrops[width])); 2217 } else { 2218 width = getOptimalWidth(properties.widths, elemWidth); 2219 image.setAttribute("src", CMP.image.dynamicMedia.getSrc(src, width)); 2220 } 2221 } 2222 }; 2223 2224 /** 2225 * Get the src attribute based on the optimal width 2226 * @param {String} src the src uri 2227 * @param {String} width the element width 2228 * @returns {String} the final src attribute 2229 */ 2230 var getSrc = function(src, width) { 2231 if (src.indexOf(SRC_URI_TEMPLATE_WIDTH_VAR) > -1) { 2232 src = src.replace(SRC_URI_TEMPLATE_WIDTH_VAR, width); 2233 } 2234 return src; 2235 }; 2236 2237 2238 return { 2239 getAutoSmartCrops: getAutoSmartCrops, 2240 getSrcSet: getSrcSet, 2241 getSrc: getSrc, 2242 setDMAttributes: setDMAttributes, 2243 getWidth: getWidth 2244 }; 2245 }()); 2246 document.dispatchEvent(new CustomEvent("core.wcm.components.commons.site.image.dynamic-media.loaded")); 2247}(window.document)); 2248 2249/******************************************************************************* 2250 * Copyright 2022 Adobe 2251 * 2252 * Licensed under the Apache License, Version 2.0 (the "License"); 2253 * you may not use this file except in compliance with the License. 2254 * You may obtain a copy of the License at 2255 * 2256 * http://www.apache.org/licenses/LICENSE-2.0 2257 * 2258 * Unless required by applicable law or agreed to in writing, software 2259 * distributed under the License is distributed on an "AS IS" BASIS, 2260 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2261 * See the License for the specific language governing permissions and 2262 * limitations under the License. 2263 ******************************************************************************/ 2264(function(document) { 2265 2266 "use strict"; 2267 2268 window.CMP = window.CMP || {}; 2269 window.CMP.image = window.CMP.image || {}; 2270 window.CMP.image.v3 = (function() { 2271 var IS = "image"; 2272 2273 var selectors = { 2274 self: "[data-cmp-hook-image='imageV3']" 2275 }; 2276 2277 var properties = { 2278 "widths": { 2279 "default": [], 2280 "transform": function(value) { 2281 var widths = []; 2282 value.split(",").forEach(function(item) { 2283 item = parseFloat(item); 2284 if (!isNaN(item)) { 2285 widths.push(item); 2286 } 2287 }); 2288 return widths; 2289 } 2290 }, 2291 "dmimage": { 2292 "default": false, 2293 "transform": function(value) { 2294 return !(value === null || typeof value === "undefined"); 2295 } 2296 }, 2297 "src": { 2298 "transform": function(value) { 2299 return decodeURIComponent(value); 2300 } 2301 }, 2302 "smartcroprendition": "" 2303 }; 2304 2305 function Image(config) { 2306 var that = this; 2307 /** 2308 * Init the image if the image is from dynamic media 2309 * @param {HTMLElement} component the image component 2310 */ 2311 that.initImage = function(component) { 2312 var options = CMP.utils.readData(component, IS); 2313 that._properties = CMP.utils.setupProperties(options, properties); 2314 if (that._properties.dmimage) { 2315 // use encoded src for dynamic media 2316 that._properties.src = options["src"]; 2317 CMP.image.dynamicMedia.setDMAttributes(component, that._properties); 2318 } 2319 }; 2320 that.initImage(config.element); 2321 } 2322 2323 return { 2324 init: function() { 2325 var elements = document.querySelectorAll(selectors.self); 2326 for (var i = 0; i < elements.length; i++) { 2327 new Image({ element: elements[i], options: CMP.utils.readData(elements[i], IS) }); 2328 } 2329 2330 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; 2331 var body = document.querySelector("body"); 2332 var observer = new MutationObserver(function(mutations) { 2333 mutations.forEach(function(mutation) { 2334 // needed for IE 2335 var nodesArray = [].slice.call(mutation.addedNodes); 2336 if (nodesArray.length > 0) { 2337 nodesArray.forEach(function(addedNode) { 2338 if (addedNode.querySelectorAll) { 2339 var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self)); 2340 elementsArray.forEach(function(element) { 2341 new Image({ element: element, options: CMP.utils.readData(element, IS) }); 2342 }); 2343 } 2344 }); 2345 } 2346 }); 2347 }); 2348 2349 observer.observe(body, { 2350 subtree: true, 2351 childList: true, 2352 characterData: true 2353 }); 2354 } 2355 }; 2356 }()); 2357 2358 var documentReady = document.readyState !== "loading" ? Promise.resolve() : new Promise(function(resolve) { 2359 document.addEventListener("DOMContentLoaded", resolve); 2360 }); 2361 Promise.all([documentReady]).then(window.CMP.image.v3.init); 2362}(window.document)); 2363 2364 2365/******************************************************************************* 2366 * Copyright 2022 Adobe 2367 * 2368 * Licensed under the Apache License, Version 2.0 (the "License");
2369 * you may not use this file except in compliance with the License. 2370 * You may obtain a copy of the License at 2371 * 2372 * http://www.apache.org/licenses/LICENSE-2.0 2373 * 2374 * Unless required by applicable law or agreed to in writing, software 2375 * distributed under the License is distributed on an "AS IS" BASIS, 2376 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2377 * See the License for the specific language governing permissions and 2378 * limitations under the License. 2379 ******************************************************************************/ 2380(function() { 2381 "use strict"; 2382 2383 var NS = "cmp"; 2384 var IS = "search"; 2385 2386 var DELAY = 300; // time before fetching new results when the user is typing a search string 2387 var LOADING_DISPLAY_DELAY = 300; // minimum time during which the loading indicator is displayed 2388 var PARAM_RESULTS_OFFSET = "resultsOffset"; 2389 2390 var keyCodes = { 2391 TAB: 9, 2392 ENTER: 13, 2393 ESCAPE: 27, 2394 ARROW_UP: 38, 2395 ARROW_DOWN: 40 2396 }; 2397 2398 var selectors = { 2399 self: "[data-" + NS + '-is="' + IS + '"]', 2400 item: { 2401 self: "[data-" + NS + "-hook-" + IS + '="item"]', 2402 title: "[data-" + NS + "-hook-" + IS + '="itemTitle"]', 2403 focused: "." + NS + "-search__item--is-focused" 2404 } 2405 }; 2406 2407 var properties = { 2408 /** 2409 * The minimum required length of the search term before results are fetched. 2410 * 2411 * @memberof Search 2412 * @type {Number} 2413 * @default 3 2414 */ 2415 minLength: { 2416 "default": 3, 2417 transform: function(value) { 2418 value = parseFloat(value); 2419 return isNaN(value) ? null : value; 2420 } 2421 }, 2422 /** 2423 * The maximal number of results fetched by a search request. 2424 * 2425 * @memberof Search 2426 * @type {Number} 2427 * @default 10 2428 */ 2429 resultsSize: { 2430 "default": 10, 2431 transform: function(value) { 2432 value = parseFloat(value); 2433 return isNaN(value) ? null : value; 2434 } 2435 } 2436 }; 2437 2438 var idCount = 0; 2439 2440 function readData(element) { 2441 var data = element.dataset; 2442 var options = []; 2443 var capitalized = IS; 2444 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 2445 var reserved = ["is", "hook" + capitalized]; 2446 2447 for (var key in data) { 2448 if (Object.prototype.hasOwnProperty.call(data, key)) { 2449 var value = data[key]; 2450 2451 if (key.indexOf(NS) === 0) { 2452 key = key.slice(NS.length); 2453 key = key.charAt(0).toLowerCase() + key.substring(1); 2454 2455 if (reserved.indexOf(key) === -1) { 2456 options[key] = value; 2457 } 2458 } 2459 } 2460 } 2461 2462 return options; 2463 } 2464 2465 function toggleShow(element, show) { 2466 if (element) { 2467 if (show !== false) { 2468 element.style.display = "block"; 2469 element.setAttribute("aria-hidden", false); 2470 } else { 2471 element.style.display = "none"; 2472 element.setAttribute("aria-hidden", true); 2473 } 2474 } 2475 } 2476 2477 function serialize(form) { 2478 var query = []; 2479 if (form && form.elements) { 2480 for (var i = 0; i < form.elements.length; i++) { 2481 var node = form.elements[i]; 2482 if (!node.disabled && node.name) { 2483 var param = [node.name, encodeURIComponent(node.value)]; 2484 query.push(param.join("=")); 2485 } 2486 } 2487 } 2488 return query.join("&"); 2489 } 2490 2491 function mark(node, regex) { 2492 if (!node || !regex) { 2493 return; 2494 } 2495 2496 // text nodes 2497 if (node.nodeType === 3) { 2498 var nodeValue = node.nodeValue; 2499 var match = regex.exec(nodeValue); 2500 2501 if (nodeValue && match) { 2502 var element = document.createElement("mark"); 2503 element.className = NS + "-search__item-mark"; 2504 element.appendChild(document.createTextNode(match[0])); 2505 2506 var after = node.splitText(match.index); 2507 after.nodeValue = after.nodeValue.substring(match[0].length); 2508 node.parentNode.insertBefore(element, after); 2509 } 2510 } else if (node.hasChildNodes()) { 2511 for (var i = 0; i < node.childNodes.length; i++) { 2512 // recurse 2513 mark(node.childNodes[i], regex); 2514 } 2515 } 2516 } 2517 2518 function localizeMessage(searchElementId, message, values = []) { 2519 const i18nMessages = JSON.parse(document.querySelector("#" + searchElementId).getAttribute("data-i18n-messages")); 2520 let translation = i18nMessages[message]; 2521 // Insert values 2522 if (values.length > 0) { 2523 translation = translation.replace(/{(\d+)}/g, function(match, index) { 2524 return typeof values[index] !== "undefined" ? values[index] : match; 2525 }); 2526 } 2527 return translation; 2528 } 2529 2530 // useful for Accessibility, helping users with low vision and users with cognitive disabilities to identify the change in results 2531 function updateSearchResultsStatusMessageElement(searchElementId, totalResults) { 2532 var searchResultsStatusMessage = document.querySelector("#" + searchElementId + "> .cmp_search__info"); 2533 searchResultsStatusMessage.style.visibility = "visible"; 2534 var searchResultsFoundMessage = localizeMessage(searchElementId, totalResults === 1 ? "{0} result" : "{0} results", [totalResults]); 2535 var searchResultsNotFoundMessage = localizeMessage(searchElementId, "No results"); 2536 searchResultsStatusMessage.innerText = totalResults > 0 ? searchResultsFoundMessage : searchResultsNotFoundMessage; 2537 } 2538 2539 function Search(config) { 2540 if (config.element) { 2541 // prevents multiple initialization 2542 config.element.removeAttribute("data-" + NS + "-is"); 2543 } 2544 2545 this._cacheElements(config.element); 2546 this._setupProperties(config.options); 2547 2548 this._action = this._elements.form.getAttribute("action"); 2549 this._resultsOffset = 0; 2550 this._hasMoreResults = true; 2551 2552 this._elements.input.addEventListener("input", this._onInput.bind(this)); 2553 this._elements.input.addEventListener("focus", this._onInput.bind(this)); 2554 this._elements.input.addEventListener("keydown", this._onKeydown.bind(this)); 2555 this._elements.clear.addEventListener("click", this._onClearClick.bind(this)); 2556 document.addEventListener("click", this._onDocumentClick.bind(this)); 2557 this._elements.results.addEventListener("scroll", this._onScroll.bind(this)); 2558 2559 this._makeAccessible(); 2560 } 2561 2562 Search.prototype._displayResults = function() { 2563 if (this._elements.input.value.length === 0) { 2564 toggleShow(this._elements.clear, false); 2565 this._cancelResults(); 2566 } else if (this._elements.input.value.length < this._properties.minLength) { 2567 toggleShow(this._elements.clear, true); 2568 } else { 2569 this._updateResults(); 2570 toggleShow(this._elements.clear, true); 2571 } 2572 }; 2573 2574 Search.prototype._onScroll = function(event) { 2575 // fetch new results when the results to be scrolled down are less than the visible results 2576 if (this._elements.results.scrollTop + 2 * this._elements.results.clientHeight >= this._elements.results.scrollHeight) { 2577 this._resultsOffset += this._properties.resultsSize; 2578 this._displayResults(); 2579 } 2580 }; 2581 2582 Search.prototype._onInput = function(event) { 2583 var self = this; 2584 self._cancelResults(); 2585 // start searching when the search term reaches the minimum length 2586 this._timeout = setTimeout(function() { 2587 self._displayResults(); 2588 }, DELAY); 2589 }; 2590 2591 Search.prototype._onKeydown = function(event) { 2592 var self = this; 2593 2594 switch (event.keyCode) { 2595 case keyCodes.TAB: 2596 if (self._resultsOpen()) { 2597 toggleShow(self._elements.results, false); 2598 self._elements.input.setAttribute("aria-expanded", "false"); 2599 this._hideSearchResultsStatusMessage(); 2600 } 2601 break;
2602 case keyCodes.ENTER: 2603 event.preventDefault(); 2604 if (self._resultsOpen()) { 2605 var focused = self._elements.results.querySelector(selectors.item.focused); 2606 if (focused) { 2607 focused.click(); 2608 } 2609 } 2610 break; 2611 case keyCodes.ESCAPE: 2612 self._cancelResults(); 2613 break; 2614 case keyCodes.ARROW_UP: 2615 if (self._resultsOpen()) { 2616 event.preventDefault(); 2617 self._stepResultFocus(true); 2618 } 2619 break; 2620 case keyCodes.ARROW_DOWN: 2621 if (self._resultsOpen()) { 2622 event.preventDefault(); 2623 self._stepResultFocus(); 2624 } else { 2625 // test the input and if necessary fetch and display the results 2626 self._onInput(); 2627 } 2628 break; 2629 default: 2630 return; 2631 } 2632 }; 2633 2634 Search.prototype._onClearClick = function(event) { 2635 event.preventDefault(); 2636 this._elements.input.value = ""; 2637 toggleShow(this._elements.clear, false); 2638 toggleShow(this._elements.results, false); 2639 this._elements.input.setAttribute("aria-expanded", "false"); 2640 this._hideSearchResultsStatusMessage(); 2641 }; 2642 2643 Search.prototype._onDocumentClick = function(event) { 2644 var inputContainsTarget = this._elements.input.contains(event.target); 2645 var resultsContainTarget = this._elements.results.contains(event.target); 2646 2647 if (!(inputContainsTarget || resultsContainTarget)) { 2648 toggleShow(this._elements.results, false); 2649 this._elements.input.setAttribute("aria-expanded", "false"); 2650 this._hideSearchResultsStatusMessage(); 2651 } 2652 }; 2653 2654 Search.prototype._resultsOpen = function() { 2655 return this._elements.results.style.display !== "none"; 2656 }; 2657 2658 Search.prototype._makeAccessible = function() { 2659 var id = NS + "-search-results-" + idCount; 2660 this._elements.input.setAttribute("aria-owns", id); 2661 this._elements.results.id = id; 2662 idCount++; 2663 }; 2664 2665 Search.prototype._generateItems = function(data, results) { 2666 var self = this; 2667 2668 data.forEach(function(item) { 2669 var el = document.createElement("span"); 2670 el.innerHTML = self._elements.itemTemplate.innerHTML; 2671 el.querySelectorAll(selectors.item.title)[0].appendChild(document.createTextNode(item.title)); 2672 el.querySelectorAll(selectors.item.self)[0].setAttribute("href", self._safeHref(item.url)); 2673 results.innerHTML += el.innerHTML; 2674 }); 2675 }; 2676 2677 Search.prototype._safeHref = function(href) { 2678 var a = document.createElement("a"); 2679 a.href = href; 2680 return a.pathname; 2681 }; 2682 2683 Search.prototype._markResults = function() { 2684 var nodeList = this._elements.results.querySelectorAll(selectors.item.self); 2685 var escapedTerm = this._elements.input.value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 2686 var regex = new RegExp("(" + escapedTerm + ")", "gi"); 2687 2688 for (var i = this._resultsOffset - 1; i < nodeList.length; ++i) { 2689 var result = nodeList[i]; 2690 mark(result, regex); 2691 } 2692 }; 2693 2694 Search.prototype._stepResultFocus = function(reverse) { 2695 var results = this._elements.results.querySelectorAll(selectors.item.self); 2696 var focused = this._elements.results.querySelector(selectors.item.focused); 2697 var newFocused; 2698 var index = Array.prototype.indexOf.call(results, focused); 2699 var focusedCssClass = NS + "-search__item--is-focused"; 2700 2701 if (results.length > 0) { 2702 2703 if (!reverse) { 2704 // highlight the next result 2705 if (index < 0) { 2706 results[0].classList.add(focusedCssClass); 2707 results[0].setAttribute("aria-selected", "true"); 2708 } else if (index + 1 < results.length) { 2709 results[index].classList.remove(focusedCssClass); 2710 results[index].setAttribute("aria-selected", "false"); 2711 results[index + 1].classList.add(focusedCssClass); 2712 results[index + 1].setAttribute("aria-selected", "true"); 2713 } 2714 2715 // if the last visible result is partially hidden, scroll up until it's completely visible 2716 newFocused = this._elements.results.querySelector(selectors.item.focused); 2717 if (newFocused) { 2718 var bottomHiddenHeight = newFocused.offsetTop + newFocused.offsetHeight - this._elements.results.scrollTop - this._elements.results.clientHeight; 2719 if (bottomHiddenHeight > 0) { 2720 this._elements.results.scrollTop += bottomHiddenHeight; 2721 } else { 2722 this._onScroll(); 2723 } 2724 } 2725 2726 } else { 2727 // highlight the previous result 2728 if (index >= 1) { 2729 results[index].classList.remove(focusedCssClass); 2730 results[index].setAttribute("aria-selected", "false"); 2731 results[index - 1].classList.add(focusedCssClass); 2732 results[index - 1].setAttribute("aria-selected", "true"); 2733 } 2734 2735 // if the first visible result is partially hidden, scroll down until it's completely visible 2736 newFocused = this._elements.results.querySelector(selectors.item.focused); 2737 if (newFocused) { 2738 var topHiddenHeight = this._elements.results.scrollTop - newFocused.offsetTop; 2739 if (topHiddenHeight > 0) { 2740 this._elements.results.scrollTop -= topHiddenHeight; 2741 } 2742 } 2743 } 2744 } 2745 }; 2746 2747 Search.prototype._updateResults = function() { 2748 var self = this; 2749 if (self._hasMoreResults) { 2750 var request = new XMLHttpRequest(); 2751 var url = self._action + "?" + serialize(self._elements.form) + "&" + PARAM_RESULTS_OFFSET + "=" + self._resultsOffset; 2752 2753 request.open("GET", url, true); 2754 request.onload = function() { 2755 // when the results are loaded: hide the loading indicator and display the search icon after a minimum period 2756 setTimeout(function() { 2757 toggleShow(self._elements.loadingIndicator, false); 2758 toggleShow(self._elements.icon, true); 2759 }, LOADING_DISPLAY_DELAY); 2760 if (request.status >= 200 && request.status < 400) { 2761 // success status 2762 var data = JSON.parse(request.responseText); 2763 updateSearchResultsStatusMessageElement(self._elements.self.id, data.length); 2764 if (data.length > 0) { 2765 self._generateItems(data, self._elements.results); 2766 self._markResults(); 2767 toggleShow(self._elements.results, true); 2768 self._elements.input.setAttribute("aria-expanded", "true"); 2769 } else { 2770 self._hasMoreResults = false;
2771 } 2772 // the total number of results is not a multiple of the fetched results: 2773 // -> we reached the end of the query 2774 if (self._elements.results.querySelectorAll(selectors.item.self).length % self._properties.resultsSize > 0) { 2775 self._hasMoreResults = false; 2776 } 2777 } else { 2778 // error status 2779 } 2780 }; 2781 // when the results are loading: display the loading indicator and hide the search icon 2782 toggleShow(self._elements.loadingIndicator, true); 2783 toggleShow(self._elements.icon, false); 2784 request.send(); 2785 } 2786 }; 2787 2788 Search.prototype._cancelResults = function() { 2789 clearTimeout(this._timeout); 2790 this._elements.results.scrollTop = 0; 2791 this._resultsOffset = 0; 2792 this._hasMoreResults = true; 2793 this._elements.results.innerHTML = ""; 2794 this._elements.input.setAttribute("aria-expanded", "false"); 2795 this._hideSearchResultsStatusMessage(); 2796 }; 2797 2798 Search.prototype._hideSearchResultsStatusMessage = function() { 2799 var searchResultsStatusMessage = document.querySelector("#" + this._elements.self.id + "> .cmp_search__info"); 2800 searchResultsStatusMessage.style.visibility = "hidden"; 2801 }; 2802 2803 Search.prototype._cacheElements = function(wrapper) { 2804 this._elements = {}; 2805 this._elements.self = wrapper; 2806 var hooks = this._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS + "]"); 2807 2808 for (var i = 0; i < hooks.length; i++) { 2809 var hook = hooks[i]; 2810 var capitalized = IS; 2811 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 2812 var key = hook.dataset[NS + "Hook" + capitalized]; 2813 this._elements[key] = hook; 2814 } 2815 }; 2816 2817 Search.prototype._setupProperties = function(options) { 2818 this._properties = {}; 2819 2820 for (var key in properties) { 2821 if (Object.prototype.hasOwnProperty.call(properties, key)) { 2822 var property = properties[key]; 2823 if (options && options[key] != null) { 2824 if (property && typeof property.transform === "function") { 2825 this._properties[key] = property.transform(options[key]); 2826 } else { 2827 this._properties[key] = options[key]; 2828 } 2829 } else { 2830 this._properties[key] = properties[key]["default"]; 2831 } 2832 } 2833 } 2834 }; 2835 2836 function onDocumentReady() { 2837 var elements = document.querySelectorAll(selectors.self); 2838 for (var i = 0; i < elements.length; i++) { 2839 new Search({ element: elements[i], options: readData(elements[i]) }); 2840 } 2841 2842 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; 2843 var body = document.querySelector("body"); 2844 var observer = new MutationObserver(function(mutations) { 2845 mutations.forEach(function(mutation) { 2846 // needed for IE 2847 var nodesArray = [].slice.call(mutation.addedNodes); 2848 if (nodesArray.length > 0) { 2849 nodesArray.forEach(function(addedNode) { 2850 if (addedNode.querySelectorAll) { 2851 var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self)); 2852 elementsArray.forEach(function(element) { 2853 new Search({ element: element, options: readData(element) }); 2854 }); 2855 } 2856 }); 2857 } 2858 }); 2859 }); 2860 2861 observer.observe(body, { 2862 subtree: true, 2863 childList: true, 2864 characterData: true 2865 }); 2866 } 2867 2868 if (document.readyState !== "loading") { 2869 onDocumentReady(); 2870 } else { 2871 document.addEventListener("DOMContentLoaded", onDocumentReady); 2872 } 2873 2874})(); 2875 2876 2877/******************************************************************************* 2878 * Copyright 2016 Adobe 2879 * 2880 * Licensed under the Apache License, Version 2.0 (the "License");
2881 * you may not use this file except in compliance with the License. 2882 * You may obtain a copy of the License at 2883 * 2884 * http://www.apache.org/licenses/LICENSE-2.0 2885 * 2886 * Unless required by applicable law or agreed to in writing, software 2887 * distributed under the License is distributed on an "AS IS" BASIS, 2888 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2889 * See the License for the specific language governing permissions and 2890 * limitations under the License. 2891 ******************************************************************************/ 2892(function() { 2893 "use strict"; 2894 2895 var NS = "cmp"; 2896 var IS = "formText"; 2897 var IS_DASH = "form-text"; 2898 var SELF_SELECTOR = "[data-" + NS + '-is="' + IS + '"]'; 2899 2900 var selectors = { 2901 self: SELF_SELECTOR, 2902 validationMessage: SELF_SELECTOR + " .cmp-form-text__validation-message" 2903 }; 2904 2905 var displayValidationMessage = false; 2906 2907 var properties = { 2908 /** 2909 * A validation message to display if there is a type mismatch between the user input and expected input. 2910 * 2911 * @type {String} 2912 */ 2913 constraintMessage: "", 2914 /** 2915 * A validation message to display if no input is supplied, but input is expected for the field. 2916 * 2917 * @type {String} 2918 */ 2919 requiredMessage: "" 2920 }; 2921 2922 function readData(element) { 2923 var data = element.dataset; 2924 var options = []; 2925 var capitalized = IS; 2926 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 2927 var reserved = ["is", "hook" + capitalized]; 2928 2929 for (var key in data) { 2930 if (Object.prototype.hasOwnProperty.call(data, key)) { 2931 var value = data[key]; 2932 2933 if (key.indexOf(NS) === 0) { 2934 key = key.slice(NS.length); 2935 key = key.charAt(0).toLowerCase() + key.substring(1); 2936 2937 if (reserved.indexOf(key) === -1) { 2938 options[key] = value; 2939 } 2940 } 2941 } 2942 } 2943 2944 return options; 2945 } 2946 2947 function FormText(config) { 2948 if (config.element) { 2949 // prevents multiple initialization 2950 config.element.removeAttribute("data-" + NS + "-is"); 2951 } 2952 2953 this._cacheElements(config.element); 2954 this._setupProperties(config.options); 2955 2956 this._elements.input.addEventListener("invalid", this._onInvalid.bind(this)); 2957 this._elements.input.addEventListener("input", this._onInput.bind(this)); 2958 if (displayValidationMessage) { 2959 this._elements.input.checkValidity(); 2960 } 2961 } 2962 2963 FormText.prototype._onInvalid = function(event) { 2964 event.target.setCustomValidity(""); 2965 if (event.target.validity.typeMismatch) { 2966 if (this._properties.constraintMessage) { 2967 event.target.setCustomValidity(this._properties.constraintMessage); 2968 } 2969 } else if (event.target.validity.valueMissing) { 2970 if (this._properties.requiredMessage) { 2971 event.target.setCustomValidity(this._properties.requiredMessage); 2972 } 2973 } 2974 if (displayValidationMessage) { 2975 var validationMessage = event.target.parentElement.querySelector(".cmp-form-text__validation-message"); 2976 if (validationMessage) { 2977 validationMessage.innerText = event.target.validationMessage; 2978 } 2979 } 2980 }; 2981 2982 FormText.prototype._onInput = function(event) { 2983 event.target.setCustomValidity(""); 2984 if (displayValidationMessage) { 2985 event.target.checkValidity(); 2986 } 2987 }; 2988 2989 FormText.prototype._cacheElements = function(wrapper) { 2990 this._elements = {}; 2991 this._elements.self = wrapper; 2992 var hooks = this._elements.self.querySelectorAll("[data-" + NS + "-hook-" + IS_DASH + "]"); 2993 for (var i = 0; i < hooks.length; i++) { 2994 var hook = hooks[i]; 2995 var capitalized = IS; 2996 capitalized = capitalized.charAt(0).toUpperCase() + capitalized.slice(1); 2997 var key = hook.dataset[NS + "Hook" + capitalized]; 2998 this._elements[key] = hook; 2999 } 3000 }; 3001 3002 FormText.prototype._setupProperties = function(options) { 3003 this._properties = {}; 3004 3005 for (var key in properties) { 3006 if (Object.prototype.hasOwnProperty.call(properties, key)) { 3007 var property = properties[key]; 3008 if (options && options[key] != null) { 3009 if (property && typeof property.transform === "function") { 3010 this._properties[key] = property.transform(options[key]); 3011 } else { 3012 this._properties[key] = options[key]; 3013 } 3014 } else { 3015 this._properties[key] = properties[key]["default"]; 3016 } 3017 } 3018 } 3019 }; 3020 3021 function onDocumentReady() {
3022 var validationMessages = document.querySelectorAll(selectors.validationMessage); 3023 if (validationMessages && validationMessages.length > 0) { 3024 displayValidationMessage = true; 3025 } 3026 3027 var elements = document.querySelectorAll(selectors.self); 3028 for (var i = 0; i < elements.length; i++) { 3029 new FormText({ element: elements[i], options: readData(elements[i]) }); 3030 } 3031 3032 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; 3033 var body = document.querySelector("body"); 3034 var observer = new MutationObserver(function(mutations) { 3035 mutations.forEach(function(mutation) { 3036 // needed for IE 3037 var nodesArray = [].slice.call(mutation.addedNodes); 3038 if (nodesArray.length > 0) { 3039 nodesArray.forEach(function(addedNode) { 3040 if (addedNode.querySelectorAll) { 3041 var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self)); 3042 elementsArray.forEach(function(element) { 3043 new FormText({ element: element, options: readData(element) }); 3044 }); 3045 } 3046 }); 3047 } 3048 }); 3049 }); 3050 3051 observer.observe(body, { 3052 subtree: true, 3053 childList: true, 3054 characterData: true 3055 }); 3056 } 3057 3058 if (document.readyState !== "loading") { 3059 onDocumentReady(); 3060 } else { 3061 document.addEventListener("DOMContentLoaded", onDocumentReady); 3062 } 3063 3064})(); 3065 3066 3067/******************************************************************************* 3068 * Copyright 2020 Adobe 3069 * 3070 * Licensed under the Apache License, Version 2.0 (the "License"); 3071 * you may not use this file except in compliance with the License. 3072 * You may obtain a copy of the License at 3073 * 3074 * http://www.apache.org/licenses/LICENSE-2.0 3075 * 3076 * Unless required by applicable law or agreed to in writing, software 3077 * distributed under the License is distributed on an "AS IS" BASIS, 3078 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3079 * See the License for the specific language governing permissions and 3080 * limitations under the License. 3081 ******************************************************************************/ 3082(function() { 3083 "use strict"; 3084 3085 var NS = "cmp"; 3086 var IS = "pdfviewer"; 3087 var SDK_URL = "https://acrobatservices.adobe.com/view-sdk/viewer.js"; 3088 var SDK_READY_EVENT = "adobe_dc_view_sdk.ready"; 3089 3090 var selectors = { 3091 self: "[data-" + NS + '-is="' + IS + '"]', 3092 sdkScript: 'script[src="' + SDK_URL + '"]' 3093 }; 3094 3095 function initSDK() { 3096 var sdkIncluded = document.querySelectorAll(selectors.sdkScript).length > 0; 3097 if (!window.adobe_dc_view_sdk && !sdkIncluded) { 3098 var dcv = document.createElement("script"); 3099 dcv.type = "text/javascript"; 3100 dcv.src = SDK_URL; 3101 document.body.appendChild(dcv); 3102 } 3103 } 3104 3105 function previewPdf(component) { 3106 // prevents multiple initialization 3107 component.removeAttribute("data-" + NS + "-is"); 3108 3109 // add the view sdk to the page 3110 initSDK(); 3111 3112 // manage the preview 3113 if (component.dataset && component.id) { 3114 if (window.AdobeDC && window.AdobeDC.View) { 3115 dcView(component); 3116 } else { 3117 document.addEventListener(SDK_READY_EVENT, function() { 3118 dcView(component); 3119 }); 3120 } 3121 } 3122 } 3123 3124 function dcView(component) { 3125 var adobeDCView = new window.AdobeDC.View({ 3126 clientId: component.dataset.cmpClientId, 3127 divId: component.id + "-content", 3128 reportSuiteId: component.dataset.cmpReportSuiteId, 3129 locale: typeof Granite !== "undefined" && Granite && Granite.I18n ? Granite.I18n.getLocale() : navigator.language || navigator.userLanguage 3130 }); 3131 adobeDCView.previewFile({ 3132 content: { location: { url: component.dataset.cmpDocumentPath } }, 3133 metaData: { fileName: component.dataset.cmpDocumentFileName } 3134 }, JSON.parse(component.dataset.cmpViewerConfigJson)); 3135 } 3136 3137 /** 3138 * Document ready handler and DOM mutation observers. Initializes Accordion components as necessary. 3139 * 3140 * @private 3141 */ 3142 function onDocumentReady() { 3143 var elements = document.querySelectorAll(selectors.self); 3144 for (var i = 0; i < elements.length; i++) { 3145 previewPdf(elements[i]); 3146 } 3147 3148 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; 3149 var body = document.querySelector("body"); 3150 var observer = new MutationObserver(function(mutations) { 3151 mutations.forEach(function(mutation) { 3152 // needed for IE 3153 var nodesArray = [].slice.call(mutation.addedNodes); 3154 if (nodesArray.length > 0) { 3155 nodesArray.forEach(function(addedNode) { 3156 if (addedNode.querySelectorAll) { 3157 var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self)); 3158 elementsArray.forEach(function(element) { 3159 previewPdf(element); 3160 }); 3161 } 3162 }); 3163 } 3164 }); 3165 }); 3166 3167 observer.observe(body, { 3168 subtree: true, 3169 childList: true, 3170 characterData: true 3171 }); 3172 3173 } 3174 3175 if (document.readyState !== "loading") { 3176 onDocumentReady(); 3177 } else { 3178 document.addEventListener("DOMContentLoaded", onDocumentReady); 3179 } 3180}()); 3181 3182 3183 3184/******************************************************************************* 3185 * Copyright 2022 Adobe 3186 * 3187 * Licensed under the Apache License, Version 2.0 (the "License");
3188 * you may not use this file except in compliance with the License. 3189 * You may obtain a copy of the License at 3190 * 3191 * http://www.apache.org/licenses/LICENSE-2.0 3192 * 3193 * Unless required by applicable law or agreed to in writing, software 3194 * distributed under the License is distributed on an "AS IS" BASIS, 3195 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3196 * See the License for the specific language governing permissions and 3197 * limitations under the License. 3198 ******************************************************************************/ 3199(function() { 3200 "use strict"; 3201 3202 var dataLayerEnabled; 3203 var dataLayer; 3204 var dataLayerName; 3205 3206 /** 3207 * Adds Click Event Listener to the main <div> of the Text Components 3208 */ 3209 function addClickEventListenerToTextComponents() { 3210 var componentMainDivs = document.getElementsByClassName("cmp-text"); 3211 3212 for (var i = 0; i < componentMainDivs.length; i++) { 3213 componentMainDivs[i].addEventListener("click", addClickedLinkToDataLayer); 3214 } 3215 } 3216 3217 /** 3218 * Adds clicked link contained by the Text Component to Data Layer 3219 * 3220 * @private 3221 * @param {Object} event The Click event 3222 */ 3223 function addClickedLinkToDataLayer(event) { 3224 var element = event.currentTarget; 3225 var componentId = getDataLayerId(element); 3226 3227 if (event.target.tagName === "A") { 3228 dataLayer.push({ 3229 event: "cmp:click", 3230 eventInfo: { 3231 path: "component." + componentId, 3232 link: event.target.getAttribute("href") 3233 } 3234 }); 3235 } 3236 } 3237 3238 /** 3239 * Parses the dataLayer string and returns the ID 3240 * 3241 * @private 3242 * @param {HTMLElement} item, the Text item 3243 * @returns {String} dataLayerId or undefined 3244 */ 3245 function getDataLayerId(item) { 3246 if (item) { 3247 if (item.dataset.cmpDataLayer) { 3248 return Object.keys(JSON.parse(item.dataset.cmpDataLayer))[0]; 3249 } else { 3250 return item.id; 3251 } 3252 } 3253 return null; 3254 } 3255 3256 function onDocumentReady() { 3257 dataLayerEnabled = document.body.hasAttribute("data-cmp-data-layer-enabled"); 3258 if (dataLayerEnabled) { 3259 dataLayerName = document.body.getAttribute("data-cmp-data-layer-name") || "adobeDataLayer"; 3260 dataLayer = window[dataLayerName] = window[dataLayerName] || []; 3261 3262 addClickEventListenerToTextComponents(); 3263 } 3264 } 3265 3266 if (document.readyState !== "loading") { 3267 onDocumentReady(); 3268 } else { 3269 document.addEventListener("DOMContentLoaded", onDocumentReady); 3270 } 3271})(); 3272 3273 3274/******************************************************************************* 3275 * Copyright 2022 Adobe 3276 * 3277 * Licensed under the Apache License, Version 2.0 (the "License"); 3278 * you may not use this file except in compliance with the License. 3279 * You may obtain a copy of the License at 3280 * 3281 * http://www.apache.org/licenses/LICENSE-2.0 3282 * 3283 * Unless required by applicable law or agreed to in writing, software 3284 * distributed under the License is distributed on an "AS IS" BASIS, 3285 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3286 * See the License for the specific language governing permissions and 3287 * limitations under the License. 3288 ******************************************************************************/ 3289(function() { 3290 "use strict"; 3291 3292 var linkAccessibilityClass = "cmp-link__screen-reader-only"; 3293 var selectors = { 3294 linkAccessibility: "." + linkAccessibilityClass, 3295 linkAccessibilityEnabled: "[data-cmp-link-accessibility-enabled]", 3296 linkAccessibilityText: "[data-cmp-link-accessibility-text]" 3297 }; 3298 3299 function getLinkAccessibilityText() { 3300 var linkAccessibilityEnabled = document.querySelectorAll(selectors.linkAccessibilityEnabled); 3301 if (!linkAccessibilityEnabled[0]) { 3302 return; 3303 } 3304 var linkAccessibilityTextElements = document.querySelectorAll(selectors.linkAccessibilityText); 3305 if (!linkAccessibilityTextElements[0]) { 3306 return; 3307 } 3308 return linkAccessibilityTextElements[0].dataset.cmpLinkAccessibilityText; 3309 } 3310 3311 function onDocumentReady() { 3312 var linkAccessibilityText = getLinkAccessibilityText(); 3313 if (linkAccessibilityText) { 3314 document.querySelectorAll("a[target='_blank']").forEach(function(link) { 3315 if (!link.querySelector(selectors.linkAccessibility)) { 3316 var linkAccessibilityElement = document.createElement("span"); 3317 linkAccessibilityElement.classList.add(linkAccessibilityClass); 3318 linkAccessibilityElement.innerText = linkAccessibilityText; 3319 link.insertAdjacentElement("beforeend", linkAccessibilityElement); 3320 } 3321 }); 3322 } 3323 } 3324 3325 if (document.readyState !== "loading") { 3326 onDocumentReady(); 3327 } else { 3328 document.addEventListener("DOMContentLoaded", onDocumentReady); 3329 } 3330 3331}()); 3332
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.