@getmicdrop/venue-calendar 3.6.0 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +661 -661
  2. package/dist/api/api.cjs +1 -1
  3. package/dist/api/api.cjs.map +1 -1
  4. package/dist/api/api.mjs +84 -81
  5. package/dist/api/api.mjs.map +1 -1
  6. package/dist/api/client.d.ts +0 -1
  7. package/dist/api/orders.d.ts +1 -2
  8. package/dist/api/types.d.ts +349 -349
  9. package/dist/seo/seo.cjs +1 -1
  10. package/dist/seo/seo.cjs.map +1 -1
  11. package/dist/seo/seo.mjs +40 -52
  12. package/dist/seo/seo.mjs.map +1 -1
  13. package/dist/seo/types.d.ts +135 -136
  14. package/dist/types/index.d.ts +387 -387
  15. package/dist/venue-calendar.css +1 -1
  16. package/dist/venue-calendar.es.js +33632 -37
  17. package/dist/venue-calendar.es.js.map +1 -1
  18. package/dist/venue-calendar.iife.js +29 -45
  19. package/dist/venue-calendar.iife.js.map +1 -1
  20. package/dist/venue-calendar.umd.js +29 -45
  21. package/dist/venue-calendar.umd.js.map +1 -1
  22. package/package.json +118 -121
  23. package/src/lib/theme.js +217 -213
  24. package/dist/CarouselView.legacy-DeWfW7JT.js +0 -64
  25. package/dist/CarouselView.legacy-DeWfW7JT.js.map +0 -1
  26. package/dist/Checkout.legacy-CJPThT4I.js +0 -1218
  27. package/dist/Checkout.legacy-CJPThT4I.js.map +0 -1
  28. package/dist/CollectionView.legacy-BG3g7XbI.js +0 -375
  29. package/dist/CollectionView.legacy-BG3g7XbI.js.map +0 -1
  30. package/dist/FeaturedView.legacy-CALzP8EW.js +0 -128
  31. package/dist/FeaturedView.legacy-CALzP8EW.js.map +0 -1
  32. package/dist/GalleryView.legacy-DIhYvOQU.js +0 -51
  33. package/dist/GalleryView.legacy-DIhYvOQU.js.map +0 -1
  34. package/dist/GroupedListView.legacy-C9dQ_v_d.js +0 -144
  35. package/dist/GroupedListView.legacy-C9dQ_v_d.js.map +0 -1
  36. package/dist/SeriesPage.legacy-hSI5Sm8W.js +0 -187
  37. package/dist/SeriesPage.legacy-hSI5Sm8W.js.map +0 -1
  38. package/dist/Success.legacy-DEjngxhO.js +0 -191
  39. package/dist/Success.legacy-DEjngxhO.js.map +0 -1
  40. package/dist/VenueCalendar-DvT1UOq-.js +0 -37371
  41. package/dist/VenueCalendar-DvT1UOq-.js.map +0 -1
  42. package/dist/colors-BZoMuXdh.js +0 -62
  43. package/dist/colors-BZoMuXdh.js.map +0 -1
package/dist/seo/seo.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function p(s,e){if(!s)return null;const t=new Date(s);if(isNaN(t.getTime()))return null;const r=new Intl.DateTimeFormat("en-US",{timeZone:e,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1,timeZoneName:"shortOffset"}).formatToParts(t),o=h=>r.find(b=>b.type===h)?.value||"",l=o("year"),c=o("month"),i=o("day"),a=o("hour"),u=o("minute"),d=o("second"),f=o("timeZoneName");let m="+00:00";if(f){const h=f.match(/GMT([+-])(\d+)(?::(\d+))?/);if(h){const b=h[1],L=h[2].padStart(2,"0"),O=h[3]||"00";m=`${b}${L}:${O}`}}return`${l}-${c}-${i}T${a}:${u}:${d}${m}`}function y(s){if(!s)return null;const e=s.split(",").map(r=>r.trim());if(e.length<2)return{"@type":"PostalAddress",streetAddress:s,addressCountry:"US"};const n=e[e.length-1].match(/^([A-Z]{2})\s+(\d{5}(?:-\d{4})?)$/);return n&&e.length>=3?{"@type":"PostalAddress",streetAddress:e.slice(0,-2).join(", "),addressLocality:e[e.length-2],addressRegion:n[1],postalCode:n[2],addressCountry:"US"}:{"@type":"PostalAddress",streetAddress:e[0],addressLocality:e.slice(1).join(", "),addressCountry:"US"}}function _(s){if(!s)return"https://schema.org/EventScheduled";const e=s.toLowerCase();return e==="cancelled"||e==="canceled"?"https://schema.org/EventCancelled":e==="postponed"?"https://schema.org/EventPostponed":e==="rescheduled"?"https://schema.org/EventRescheduled":"https://schema.org/EventScheduled"}function S(s){if((s.remainingCapacity??s.quantityRemaining??s.quantity)===0||s.soldOut)return"https://schema.org/SoldOut";const t=new Date,n=s.salesBegin||s.salesStart||s.saleBegin||s.onSaleStart,r=s.salesEnd||s.saleEnd||s.onSaleEnd;return n&&new Date(n)>t?"https://schema.org/PreOrder":r&&new Date(r)<t?"https://schema.org/SoldOut":"https://schema.org/InStock"}function T(s,e){if(!s||s.length===0)return null;const t=s.filter(c=>{const i=c.visibility??0,a=i===0||i===1,u=c.salesChannel!==2;return a&&u});if(t.length===0)return null;const n=t.map(c=>parseFloat(String(c.price??0))).filter(c=>!isNaN(c));if(n.length===0)return null;const r=Math.min(...n),o=Math.max(...n);if(r!==o)return{"@type":"AggregateOffer",lowPrice:r,highPrice:o,priceCurrency:"USD",availability:S(t[0]),url:e||void 0,validFrom:t[0]?.salesBegin||t[0]?.salesStart||void 0};const l=t[0];return{"@type":"Offer",price:r,priceCurrency:"USD",availability:S(l),url:e||void 0,validFrom:l?.salesBegin||l?.salesStart||void 0}}function D(s){if(!s||s.length===0)return null;const e=s.filter(t=>{const n=t.acceptedState===2,r=!t.shouldBeHidden,o=t.RosterPerformer?.User?.performerProfile,l=o?.firstName||o?.lastName||o?.displayName;return n&&r&&l});return e.length===0?null:e.map(t=>{const n=t.RosterPerformer?.User?.performerProfile;return{"@type":"PerformingGroup",name:n?.displayName||[n?.firstName,n?.lastName].filter(Boolean).join(" ")}})}function E(s){const{event:e,venue:t,venueTimeZone:n="America/Los_Angeles",eventUrl:r,organizer:o}=s;if(!e)return null;const l=t?.address||t?.googleLocationNameCache||"",c=p(e.startDateTime,n);if(!e.name||!c||!t?.name)return null;const i={"@context":"https://schema.org","@type":"Event",name:e.name,startDate:c,eventStatus:_(e.status),eventAttendanceMode:"https://schema.org/OfflineEventAttendanceMode",location:{"@type":"Place",name:t.name,address:y(l)}},a=p(e.endDateTime,n);a&&(i.endDate=a);const u=e.description||e.eventSummary;u&&(i.description=u);const d=e.image||e.coverImage||e.ShowImage;d&&(i.image=[d]);const f=T(e.availableTickets,r);f&&(i.offers=f);const m=D(e.performers);if(m&&m.length>0&&(i.performer=m),o?.name){const h={"@type":"Organization",name:o.name};o.url&&(h.url=o.url),i.organizer=h}return r&&(i.url=r),i}function N(s){switch(s){case"available":return"https://schema.org/InStock";case"sold_out":return"https://schema.org/SoldOut";case"coming_soon":return"https://schema.org/PreOrder";case"ended":return"https://schema.org/SoldOut";default:return"https://schema.org/InStock"}}function U(s,e){if(!s||s.length===0)return null;const t=s.filter(l=>l.status!=="sold_out"&&l.status!=="ended");if(t.length===0){const l=s.map(a=>a.price).filter(a=>!isNaN(a));if(l.length===0)return null;const c=Math.min(...l),i=Math.max(...l);return c!==i?{"@type":"AggregateOffer",lowPrice:c,highPrice:i,priceCurrency:"USD",availability:"https://schema.org/SoldOut"}:{"@type":"Offer",price:c,priceCurrency:"USD",availability:"https://schema.org/SoldOut"}}const n=t.map(l=>l.price).filter(l=>!isNaN(l));if(n.length===0)return null;const r=Math.min(...n),o=Math.max(...n);return r!==o?{"@type":"AggregateOffer",lowPrice:r,highPrice:o,priceCurrency:"USD",availability:N(t[0].status)}:{"@type":"Offer",price:r,priceCurrency:"USD",availability:N(t[0].status)}}function P(s){const{series:e,baseEventUrl:t}=s;if(!e.title||!e.occurrences||e.occurrences.length===0||!e.venue?.name)return null;const n=y(e.venue.googleLocationNameCache||""),r={"@type":"Place",name:e.venue.name};n&&(r.address=n);const o={"@context":"https://schema.org","@type":"EventSeries",name:e.title,location:r},l=e.description||e.eventSummary;l&&(o.description=l),e.image&&(o.image=[e.image]);const c=e.performers?.filter(a=>a.displayName);c&&c.length>0&&(o.performer=c.map(a=>({"@type":"PerformingGroup",name:a.displayName})));const i=[];for(const a of e.occurrences){const u=p(a.startDateTime,e.timeZone);if(!u)continue;const d={"@type":"Event",name:a.title,startDate:u,eventStatus:_(a.status),location:r};t&&(d["@id"]=`${t}/${a.id}-${a.slug}`);const f=p(a.endDateTime,e.timeZone);f&&(d.endDate=f);const m=U(a.tickets,a.status);m&&(d.offers=m),i.push(d)}return i.length===0?null:(o.subEvent=i,o)}function I(s){const{collection:e,baseEventUrl:t,defaultTimeZone:n="America/Los_Angeles"}=s;if(!e.collectionTitle||!e.events||e.events.length===0)return null;const r={"@context":"https://schema.org","@type":"Festival",name:e.collectionTitle},o=e.description||e.summary;if(o&&(r.description=o),e.coverImage&&(r.image=[e.coverImage]),e.dateRange){const i=p(e.dateRange.startDate,n),a=p(e.dateRange.endDate,n);i&&(r.startDate=i),a&&(r.endDate=a)}if(new Set(e.events.map(i=>i.venueId).filter(i=>i!==void 0&&i!==0)).size===1){const i=e.events.find(a=>a.venueId&&a.venueId!==0&&a.venueName);i?.venueName&&(r.location={"@type":"Place",name:i.venueName})}const c=[];for(const i of e.events){const a=p(i.startDateTime,n);if(!a)continue;const u={"@type":"Event",name:i.title,startDate:a,eventStatus:_(i.status)};if(i.venueName){const f={"@type":"Place",name:i.venueName};if(i.venueAddress){const m=y(i.venueAddress);m&&(f.address=m)}u.location=f}t&&i.slug&&(u["@id"]=`${t}/${i.id}-${i.slug}`);const d=p(i.endDateTime,n);d&&(u.endDate=d),i.image&&(u.image=[i.image]),c.push(u)}return c.length===0?null:(r.subEvent=c,r)}function v(s){return s?s.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/\s+/g,"-").replace(/--+/g,"-").replace(/^-+|-+$/g,""):""}const g=class g{constructor(e){this._state="idle",this._originals=null,this._originalsSaved=!1,this._events=[],!(typeof document>"u")&&(g._activeInstance&&g._activeInstance!==this&&g._activeInstance.destroy(),this._venueName=e.venueName,this._venueAddress=e.venueAddress,this._venueTimeZone=e.venueTimeZone||"America/Los_Angeles",this._baseUrl=e.baseUrl||window.location.origin+window.location.pathname,this._saveOriginals(),g._activeInstance=this)}_saveOriginals(){if(this._originalsSaved)return;const e=document.querySelector('link[rel="canonical"]'),t=document.querySelector('meta[property="og:title"]'),n=document.querySelector('meta[property="og:description"]'),r=document.querySelector('meta[property="og:image"]'),o=document.querySelector('meta[property="og:url"]'),l=document.querySelector('meta[property="og:type"]'),c=document.querySelector('meta[name="description"]');this._originals={title:document.title,canonicalHref:e?.getAttribute("href")??null,ogTitle:t?.getAttribute("content")??null,ogDescription:n?.getAttribute("content")??null,ogImage:r?.getAttribute("content")??null,ogUrl:o?.getAttribute("content")??null,ogType:l?.getAttribute("content")??null,metaDescription:c?.getAttribute("content")??null},this._originalsSaved=!0}_injectJsonLd(e){if(typeof document>"u")return;document.querySelectorAll('script[data-micdrop="jsonld"]').forEach(n=>n.remove());const t=document.createElement("script");t.setAttribute("type","application/ld+json"),t.setAttribute("data-micdrop","jsonld"),t.textContent=JSON.stringify(e),document.head.appendChild(t)}_setMeta(e,t){if(!(typeof document>"u"))if(e.startsWith("og:")){let n=document.querySelector(`meta[property="${e}"]`);n?n.setAttribute("content",t):(n=document.createElement("meta"),n.setAttribute("property",e),n.setAttribute("content",t),n.setAttribute("data-micdrop","og"),document.head.appendChild(n))}else{let n=document.querySelector(`meta[name="${e}"]`);n?n.setAttribute("content",t):(n=document.createElement("meta"),n.setAttribute("name",e),n.setAttribute("content",t),n.setAttribute("data-micdrop","meta"),document.head.appendChild(n))}}_setCanonical(e){if(typeof document>"u")return;let t=document.querySelector('link[rel="canonical"]');t?t.setAttribute("href",e):(t=document.createElement("link"),t.setAttribute("rel","canonical"),t.setAttribute("href",e),t.setAttribute("data-micdrop","canonical"),document.head.appendChild(t))}_removeAllInjected(){typeof document>"u"||document.querySelectorAll("[data-micdrop]").forEach(e=>e.remove())}_restoreOriginals(){if(!(typeof document>"u"||!this._originals)){if(document.title=this._originals.title,this._originals.canonicalHref!==null){const e=document.querySelector('link[rel="canonical"]');e&&e.setAttribute("href",this._originals.canonicalHref)}this._restoreMetaProperty("og:title",this._originals.ogTitle),this._restoreMetaProperty("og:description",this._originals.ogDescription),this._restoreMetaProperty("og:image",this._originals.ogImage),this._restoreMetaProperty("og:url",this._originals.ogUrl),this._restoreMetaProperty("og:type",this._originals.ogType),this._restoreMetaName("description",this._originals.metaDescription),this._originalsSaved=!1}}_restoreMetaProperty(e,t){const n=document.querySelector(`meta[property="${e}"]`);t!==null&&n?n.setAttribute("content",t):t===null&&n?.hasAttribute("data-micdrop")&&n.remove()}_restoreMetaName(e,t){const n=document.querySelector(`meta[name="${e}"]`);t!==null&&n?n.setAttribute("content",t):t===null&&n?.hasAttribute("data-micdrop")&&n.remove()}_buildItemList(e){return{"@context":"https://schema.org","@type":"ItemList",itemListElement:e.map((t,n)=>{const r=t.slug||v(t.name);return{"@type":"ListItem",position:n+1,url:`${this._baseUrl}?e=${t.id}-${r}`}})}}_buildEntityUrl(e,t,n){const r=e==="event"?"e":e==="series"?"s":"c";return`${this._baseUrl}?${r}=${t}-${n}`}onEventsLoaded(e,t){if(typeof document>"u"||this._state==="destroyed")return;t&&(this._venueName=t.name||this._venueName,this._venueAddress=t.address||t.googleLocationNameCache||this._venueAddress,t.timeZone&&(this._venueTimeZone=t.timeZone)),this._events=e;const n=this._buildItemList(e);this._injectJsonLd(n),this._state="listing"}onEntitySelected(e,t,n){if(typeof document>"u"||this._state==="destroyed")return;this._saveOriginals(),n&&(this._venueName=n.name||this._venueName,this._venueAddress=n.address||n.googleLocationNameCache||this._venueAddress,n.timeZone&&(this._venueTimeZone=n.timeZone));let r=null,o="",l="",c="",i="";if(e==="event"){const a=t.slug||v(t.name||"");i=this._buildEntityUrl("event",t.id||t.eventID||"",a),r=E({event:t,venue:{name:this._venueName,googleLocationNameCache:this._venueAddress},venueTimeZone:this._venueTimeZone,eventUrl:i}),o=t.name||"",l=t.description||t.eventSummary||"",c=t.image||t.coverImage||t.ShowImage||""}else if(e==="series"){const a=t.slug||v(t.title||"");i=this._buildEntityUrl("series",t.id||"",a),r=P({series:{...t,venue:{name:this._venueName,googleLocationNameCache:this._venueAddress},timeZone:this._venueTimeZone},baseEventUrl:i}),o=t.title||"",l=t.description||t.eventSummary||"",c=t.image||""}else if(e==="collection"){const a=t.slug||v(t.collectionTitle||"");i=this._buildEntityUrl("collection",t.id||"",a),r=I({collection:t,baseEventUrl:i,defaultTimeZone:this._venueTimeZone}),o=t.collectionTitle||"",l=t.description||t.summary||"",c=t.coverImage||""}r&&this._injectJsonLd(r),i&&this._setCanonical(i),o&&this._setMeta("og:title",o),l&&this._setMeta("og:description",l),c&&this._setMeta("og:image",c),i&&this._setMeta("og:url",i),this._setMeta("og:type","website"),this._setMeta("twitter:card","summary_large_image"),l&&this._setMeta("description",l),o&&(document.title=`${o} | ${this._venueName}`),this._state="entity"}onBackToListing(){if(!(typeof document>"u")&&this._state!=="destroyed"){if(this._restoreOriginals(),this._removeAllInjected(),this._events.length>0){const e=this._buildItemList(this._events);this._injectJsonLd(e)}this._saveOriginals(),this._state="listing"}}destroy(){typeof document>"u"||this._state!=="destroyed"&&(this._removeAllInjected(),this._restoreOriginals(),g._activeInstance===this&&(g._activeInstance=null),this._state="destroyed")}};g._activeInstance=null;let A=g;exports.HostSeoController=A;exports.buildCollectionJsonLd=I;exports.buildEventJsonLd=E;exports.buildOffers=T;exports.buildPerformers=D;exports.buildSeriesJsonLd=P;exports.formatDateTimeWithOffset=p;exports.getEventStatus=_;exports.getTicketAvailability=S;exports.parseAddressToPostal=y;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function g(s,e){if(!s)return null;const t=new Date(s);if(isNaN(t.getTime()))return null;const r=new Intl.DateTimeFormat("en-US",{timeZone:e,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1,timeZoneName:"shortOffset"}).formatToParts(t),o=f=>r.find(_=>_.type===f)?.value||"",l=o("year"),c=o("month"),i=o("day"),a=o("hour"),u=o("minute"),d=o("second"),p=o("timeZoneName");let m="+00:00";if(p){const f=p.match(/GMT([+-])(\d+)(?::(\d+))?/);if(f){const _=f[1],L=f[2].padStart(2,"0"),O=f[3]||"00";m=`${_}${L}:${O}`}}return`${l}-${c}-${i}T${a}:${u}:${d}${m}`}function A(s){if(!s)return null;const e=s.split(",").map(r=>r.trim());if(e.length<2)return{"@type":"PostalAddress",streetAddress:s,addressCountry:"US"};const n=e[e.length-1].match(/^([A-Z]{2})\s+(\d{5}(?:-\d{4})?)$/);return n&&e.length>=3?{"@type":"PostalAddress",streetAddress:e.slice(0,-2).join(", "),addressLocality:e[e.length-2],addressRegion:n[1],postalCode:n[2],addressCountry:"US"}:{"@type":"PostalAddress",streetAddress:e[0],addressLocality:e.slice(1).join(", "),addressCountry:"US"}}function y(s){if(!s)return"https://schema.org/EventScheduled";const e=s.toLowerCase();return e==="cancelled"||e==="canceled"?"https://schema.org/EventCancelled":e==="postponed"?"https://schema.org/EventPostponed":e==="rescheduled"?"https://schema.org/EventRescheduled":"https://schema.org/EventScheduled"}function b(s){if((s.remainingCapacity??s.quantityRemaining??s.quantity)===0||s.soldOut)return"https://schema.org/SoldOut";const t=new Date,n=s.salesBegin||s.salesStart||s.saleBegin||s.onSaleStart,r=s.salesEnd||s.saleEnd||s.onSaleEnd;return n&&new Date(n)>t?"https://schema.org/PreOrder":r&&new Date(r)<t?"https://schema.org/SoldOut":"https://schema.org/InStock"}function D(s,e){if(!s||s.length===0)return null;const t=s.filter(c=>{const i=c.visibility??0,a=i===0||i===1,u=c.salesChannel!==2;return a&&u});if(t.length===0)return null;const n=t.map(c=>parseFloat(String(c.price??0))).filter(c=>!isNaN(c));if(n.length===0)return null;const r=Math.min(...n),o=Math.max(...n);if(r!==o)return{"@type":"AggregateOffer",lowPrice:r,highPrice:o,priceCurrency:"USD",availability:b(t[0]),url:e||void 0,validFrom:t[0]?.salesBegin||t[0]?.salesStart||void 0};const l=t[0];return{"@type":"Offer",price:r,priceCurrency:"USD",availability:b(l),url:e||void 0,validFrom:l?.salesBegin||l?.salesStart||void 0}}function N(s){if(!s||s.length===0)return null;const e=s.filter(t=>{const n=t.acceptedState===2,r=!t.shouldBeHidden,o=t.RosterPerformer?.User?.performerProfile,l=o?.firstName||o?.lastName||o?.displayName;return n&&r&&l});return e.length===0?null:e.map(t=>{const n=t.RosterPerformer?.User?.performerProfile;return{"@type":"PerformingGroup",name:n?.displayName||[n?.firstName,n?.lastName].filter(Boolean).join(" ")}})}function E(s){const{event:e,venue:t,venueTimeZone:n="America/Los_Angeles",eventUrl:r,organizer:o}=s;if(!e)return null;const l=t?.address||t?.googleLocationNameCache||"",c=g(e.startDateTime,n);if(!e.name||!c||!t?.name)return null;const i={"@context":"https://schema.org","@type":"Event",name:e.name,startDate:c,eventStatus:y(e.status),location:{"@type":"Place",name:t.name,address:A(l)}},a=g(e.endDateTime,n);a&&(i.endDate=a);const u=e.description||e.eventSummary;u&&(i.description=u);const d=e.image||e.coverImage||e.ShowImage;d&&(i.image=[d]);const p=D(e.availableTickets,r);p&&(i.offers=p);const m=N(e.performers);if(m&&m.length>0&&(i.performer=m),o?.name){const f={"@type":"Organization",name:o.name};o.url&&(f.url=o.url),i.organizer=f}return r&&(i.url=r),i}function T(s){switch(s){case"available":return"https://schema.org/InStock";case"sold_out":return"https://schema.org/SoldOut";case"coming_soon":return"https://schema.org/PreOrder";case"ended":return"https://schema.org/SoldOut";default:return"https://schema.org/InStock"}}function U(s,e){if(!s||s.length===0)return null;const t=s.filter(l=>l.status!=="sold_out"&&l.status!=="ended");if(t.length===0){const l=s.map(a=>a.price).filter(a=>!isNaN(a));if(l.length===0)return null;const c=Math.min(...l),i=Math.max(...l);return c!==i?{"@type":"AggregateOffer",lowPrice:c,highPrice:i,priceCurrency:"USD",availability:"https://schema.org/SoldOut"}:{"@type":"Offer",price:c,priceCurrency:"USD",availability:"https://schema.org/SoldOut"}}const n=t.map(l=>l.price).filter(l=>!isNaN(l));if(n.length===0)return null;const r=Math.min(...n),o=Math.max(...n);return r!==o?{"@type":"AggregateOffer",lowPrice:r,highPrice:o,priceCurrency:"USD",availability:T(t[0].status)}:{"@type":"Offer",price:r,priceCurrency:"USD",availability:T(t[0].status)}}function P(s){const{series:e,baseEventUrl:t}=s;if(!e.title||!e.occurrences||e.occurrences.length===0||!e.venue?.name)return null;const n=A(e.venue.googleLocationNameCache||""),r={"@type":"Place",name:e.venue.name};n&&(r.address=n);const o={"@context":"https://schema.org","@type":"EventSeries",name:e.title,location:r},l=e.description||e.eventSummary;l&&(o.description=l),e.image&&(o.image=[e.image]);const c=e.performers?.filter(a=>a.displayName);c&&c.length>0&&(o.performer=c.map(a=>({"@type":"PerformingGroup",name:a.displayName})));const i=[];for(const a of e.occurrences){const u=g(a.startDateTime,e.timeZone);if(!u)continue;const d={"@type":"Event",name:a.title,startDate:u,eventStatus:y(a.status),location:r};t&&(d["@id"]=`${t}/${a.id}-${a.slug}`);const p=g(a.endDateTime,e.timeZone);p&&(d.endDate=p);const m=U(a.tickets,a.status);m&&(d.offers=m),i.push(d)}return i.length===0?null:(o.subEvent=i,o)}function I(s){const{collection:e,baseEventUrl:t,defaultTimeZone:n="America/Los_Angeles"}=s;if(!e.collectionTitle||!e.events||e.events.length===0)return null;const r={"@context":"https://schema.org","@type":"Festival",name:e.collectionTitle},o=e.description||e.summary;if(o&&(r.description=o),e.coverImage&&(r.image=[e.coverImage]),e.dateRange){const i=g(e.dateRange.startDate,n),a=g(e.dateRange.endDate,n);i&&(r.startDate=i),a&&(r.endDate=a)}if(new Set(e.events.map(i=>i.venueId).filter(i=>i!==void 0&&i!==0)).size===1){const i=e.events.find(a=>a.venueId&&a.venueId!==0&&a.venueName);i?.venueName&&(r.location={"@type":"Place",name:i.venueName})}const c=[];for(const i of e.events){const a=g(i.startDateTime,n);if(!a)continue;const u={"@type":"Event",name:i.title,startDate:a,eventStatus:y(i.status)};t&&i.slug&&(u["@id"]=`${t}/${i.id}-${i.slug}`);const d=g(i.endDateTime,n);d&&(u.endDate=d),i.image&&(u.image=[i.image]),c.push(u)}return c.length===0?null:(r.subEvent=c,r)}function v(s){return s?s.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/\s+/g,"-").replace(/--+/g,"-").replace(/^-+|-+$/g,""):""}const h=class h{constructor(e){this._state="idle",this._originals=null,this._originalsSaved=!1,this._events=[],!(typeof document>"u")&&(h._activeInstance&&h._activeInstance!==this&&h._activeInstance.destroy(),this._venueName=e.venueName,this._venueAddress=e.venueAddress,this._venueTimeZone=e.venueTimeZone||"America/Los_Angeles",this._baseUrl=e.baseUrl||window.location.origin+window.location.pathname,this._saveOriginals(),h._activeInstance=this)}_saveOriginals(){if(this._originalsSaved)return;const e=document.querySelector('link[rel="canonical"]'),t=document.querySelector('meta[property="og:title"]'),n=document.querySelector('meta[property="og:description"]'),r=document.querySelector('meta[property="og:image"]'),o=document.querySelector('meta[property="og:url"]'),l=document.querySelector('meta[property="og:type"]'),c=document.querySelector('meta[name="description"]');this._originals={title:document.title,canonicalHref:e?.getAttribute("href")??null,ogTitle:t?.getAttribute("content")??null,ogDescription:n?.getAttribute("content")??null,ogImage:r?.getAttribute("content")??null,ogUrl:o?.getAttribute("content")??null,ogType:l?.getAttribute("content")??null,metaDescription:c?.getAttribute("content")??null},this._originalsSaved=!0}_injectJsonLd(e){if(typeof document>"u")return;document.querySelectorAll('script[data-micdrop="jsonld"]').forEach(n=>n.remove());const t=document.createElement("script");t.setAttribute("type","application/ld+json"),t.setAttribute("data-micdrop","jsonld"),t.textContent=JSON.stringify(e),document.head.appendChild(t)}_setMeta(e,t){if(!(typeof document>"u"))if(e.startsWith("og:")){let n=document.querySelector(`meta[property="${e}"]`);n?n.setAttribute("content",t):(n=document.createElement("meta"),n.setAttribute("property",e),n.setAttribute("content",t),n.setAttribute("data-micdrop","og"),document.head.appendChild(n))}else{let n=document.querySelector(`meta[name="${e}"]`);n?n.setAttribute("content",t):(n=document.createElement("meta"),n.setAttribute("name",e),n.setAttribute("content",t),n.setAttribute("data-micdrop","meta"),document.head.appendChild(n))}}_setCanonical(e){if(typeof document>"u")return;let t=document.querySelector('link[rel="canonical"]');t?t.setAttribute("href",e):(t=document.createElement("link"),t.setAttribute("rel","canonical"),t.setAttribute("href",e),t.setAttribute("data-micdrop","canonical"),document.head.appendChild(t))}_removeAllInjected(){typeof document>"u"||document.querySelectorAll("[data-micdrop]").forEach(e=>e.remove())}_restoreOriginals(){if(!(typeof document>"u"||!this._originals)){if(document.title=this._originals.title,this._originals.canonicalHref!==null){const e=document.querySelector('link[rel="canonical"]');e&&e.setAttribute("href",this._originals.canonicalHref)}this._restoreMetaProperty("og:title",this._originals.ogTitle),this._restoreMetaProperty("og:description",this._originals.ogDescription),this._restoreMetaProperty("og:image",this._originals.ogImage),this._restoreMetaProperty("og:url",this._originals.ogUrl),this._restoreMetaProperty("og:type",this._originals.ogType),this._restoreMetaName("description",this._originals.metaDescription),this._originalsSaved=!1}}_restoreMetaProperty(e,t){const n=document.querySelector(`meta[property="${e}"]`);t!==null&&n?n.setAttribute("content",t):t===null&&n?.hasAttribute("data-micdrop")&&n.remove()}_restoreMetaName(e,t){const n=document.querySelector(`meta[name="${e}"]`);t!==null&&n?n.setAttribute("content",t):t===null&&n?.hasAttribute("data-micdrop")&&n.remove()}_buildItemList(e){return{"@context":"https://schema.org","@type":"ItemList",itemListElement:e.map((t,n)=>{const r=t.slug||v(t.name);return{"@type":"ListItem",position:n+1,url:`${this._baseUrl}?e=${t.id}-${r}`}})}}_buildEntityUrl(e,t,n){const r=e==="event"?"e":e==="series"?"s":"c";return`${this._baseUrl}?${r}=${t}-${n}`}onEventsLoaded(e,t){if(typeof document>"u"||this._state==="destroyed")return;t&&(this._venueName=t.name||this._venueName,this._venueAddress=t.address||t.googleLocationNameCache||this._venueAddress,t.timeZone&&(this._venueTimeZone=t.timeZone)),this._events=e;const n=this._buildItemList(e);this._injectJsonLd(n),this._state="listing"}onEntitySelected(e,t,n){if(typeof document>"u"||this._state==="destroyed")return;this._saveOriginals(),n&&(this._venueName=n.name||this._venueName,this._venueAddress=n.address||n.googleLocationNameCache||this._venueAddress,n.timeZone&&(this._venueTimeZone=n.timeZone));let r=null,o="",l="",c="",i="";if(e==="event"){const a=t.slug||v(t.name||"");i=this._buildEntityUrl("event",t.id||t.eventID||"",a),r=E({event:t,venue:{name:this._venueName,googleLocationNameCache:this._venueAddress},venueTimeZone:this._venueTimeZone,eventUrl:i}),o=t.name||"",l=t.description||t.eventSummary||"",c=t.image||t.coverImage||t.ShowImage||""}else if(e==="series"){const a=t.slug||v(t.title||"");i=this._buildEntityUrl("series",t.id||"",a),r=P({series:{...t,venue:{name:this._venueName,googleLocationNameCache:this._venueAddress},timeZone:this._venueTimeZone},baseEventUrl:i}),o=t.title||"",l=t.description||t.eventSummary||"",c=t.image||""}else if(e==="collection"){const a=t.slug||v(t.collectionTitle||"");i=this._buildEntityUrl("collection",t.id||"",a),r=I({collection:t,baseEventUrl:i,defaultTimeZone:this._venueTimeZone}),o=t.collectionTitle||"",l=t.description||t.summary||"",c=t.coverImage||""}r&&this._injectJsonLd(r),i&&this._setCanonical(i),o&&this._setMeta("og:title",o),l&&this._setMeta("og:description",l),c&&this._setMeta("og:image",c),i&&this._setMeta("og:url",i),this._setMeta("og:type","website"),l&&this._setMeta("description",l),o&&(document.title=`${o} | ${this._venueName}`),this._state="entity"}onBackToListing(){if(!(typeof document>"u")&&this._state!=="destroyed"){if(this._restoreOriginals(),this._removeAllInjected(),this._events.length>0){const e=this._buildItemList(this._events);this._injectJsonLd(e)}this._saveOriginals(),this._state="listing"}}destroy(){typeof document>"u"||this._state!=="destroyed"&&(this._removeAllInjected(),this._restoreOriginals(),h._activeInstance===this&&(h._activeInstance=null),this._state="destroyed")}};h._activeInstance=null;let S=h;exports.HostSeoController=S;exports.buildCollectionJsonLd=I;exports.buildEventJsonLd=E;exports.buildOffers=D;exports.buildPerformers=N;exports.buildSeriesJsonLd=P;exports.formatDateTimeWithOffset=g;exports.getEventStatus=y;exports.getTicketAvailability=b;exports.parseAddressToPostal=A;
2
2
  //# sourceMappingURL=seo.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"seo.cjs","sources":["../../src/lib/seo/helpers.ts","../../src/lib/seo/buildEventJsonLd.ts","../../src/lib/seo/buildSeriesJsonLd.ts","../../src/lib/seo/buildCollectionJsonLd.ts","../../src/lib/utils/textUtils.ts","../../src/lib/seo/HostSeoController.ts"],"sourcesContent":["/**\n * Shared helper utilities for JSON-LD builders.\n *\n * All functions are pure (no DOM, no Svelte, no side effects).\n * Extracted verbatim from EventStructuredData.svelte.\n */\n\nimport type { EventTicketInput, EventPerformerInput } from './types';\n\n/**\n * Format datetime to ISO-8601 with timezone offset.\n * Google requires: \"2025-07-21T19:00:00-05:00\"\n *\n * Extracted from EventStructuredData.svelte lines 25-68.\n */\nexport function formatDateTimeWithOffset(\n isoString: string | null | undefined,\n tz: string,\n): string | null {\n if (!isoString) return null;\n\n const date = new Date(isoString);\n if (isNaN(date.getTime())) return null;\n\n // Get the offset in the target timezone\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n timeZoneName: 'shortOffset',\n });\n\n const parts = formatter.formatToParts(date);\n const get = (type: string) => parts.find((p) => p.type === type)?.value || '';\n\n const year = get('year');\n const month = get('month');\n const day = get('day');\n const hour = get('hour');\n const minute = get('minute');\n const second = get('second');\n const tzOffset = get('timeZoneName'); // e.g., \"GMT-5\" or \"GMT+2\"\n\n // Convert \"GMT-5\" to \"-05:00\" format\n let offset = '+00:00';\n if (tzOffset) {\n const match = tzOffset.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\n if (match) {\n const sign = match[1];\n const hours = match[2].padStart(2, '0');\n const mins = match[3] || '00';\n offset = `${sign}${hours}:${mins}`;\n }\n }\n\n return `${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`;\n}\n\n/**\n * Parse address string into PostalAddress components.\n * Input: \"7702 Santa Monica Blvd, West Hollywood, CA 90046\"\n * Output: { @type, streetAddress, addressLocality, addressRegion, postalCode, addressCountry }\n *\n * Extracted from EventStructuredData.svelte lines 75-111.\n */\nexport function parseAddressToPostal(\n addressStr: string | null | undefined,\n): Record<string, string> | null {\n if (!addressStr) return null;\n\n const parts = addressStr.split(',').map((p) => p.trim());\n\n if (parts.length < 2) {\n return {\n '@type': 'PostalAddress',\n streetAddress: addressStr,\n addressCountry: 'US',\n };\n }\n\n // Check if last part is \"State ZIP\" (e.g., \"CA 90046\")\n const lastPart = parts[parts.length - 1];\n const stateZipMatch = lastPart.match(/^([A-Z]{2})\\s+(\\d{5}(?:-\\d{4})?)$/);\n\n if (stateZipMatch && parts.length >= 3) {\n // Standard US address format\n return {\n '@type': 'PostalAddress',\n streetAddress: parts.slice(0, -2).join(', '),\n addressLocality: parts[parts.length - 2],\n addressRegion: stateZipMatch[1],\n postalCode: stateZipMatch[2],\n addressCountry: 'US',\n };\n }\n\n // Fallback for non-standard formats\n return {\n '@type': 'PostalAddress',\n streetAddress: parts[0],\n addressLocality: parts.slice(1).join(', '),\n addressCountry: 'US',\n };\n}\n\n/**\n * Map event status to schema.org EventStatusType.\n *\n * Extracted from EventStructuredData.svelte lines 116-132.\n */\nexport function getEventStatus(rawStatus: string | null | undefined): string {\n if (!rawStatus) return 'https://schema.org/EventScheduled';\n\n const status = rawStatus.toLowerCase();\n\n if (status === 'cancelled' || status === 'canceled') {\n return 'https://schema.org/EventCancelled';\n }\n if (status === 'postponed') {\n return 'https://schema.org/EventPostponed';\n }\n if (status === 'rescheduled') {\n return 'https://schema.org/EventRescheduled';\n }\n\n return 'https://schema.org/EventScheduled';\n}\n\n/**\n * Map ticket availability to schema.org ItemAvailability.\n *\n * Extracted from EventStructuredData.svelte lines 137-157.\n */\nexport function getTicketAvailability(ticket: EventTicketInput): string {\n const remaining =\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\n\n if (remaining === 0 || ticket.soldOut) {\n return 'https://schema.org/SoldOut';\n }\n\n const now = new Date();\n const salesBegin =\n ticket.salesBegin || ticket.salesStart || ticket.saleBegin || ticket.onSaleStart;\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\n\n if (salesBegin && new Date(salesBegin) > now) {\n return 'https://schema.org/PreOrder';\n }\n\n if (salesEnd && new Date(salesEnd) < now) {\n return 'https://schema.org/SoldOut';\n }\n\n return 'https://schema.org/InStock';\n}\n\n/**\n * Build offers from available tickets.\n *\n * Extracted from EventStructuredData.svelte lines 162-208.\n */\nexport function buildOffers(\n tickets: EventTicketInput[] | null | undefined,\n url?: string,\n): Record<string, unknown> | null {\n if (!tickets || tickets.length === 0) return null;\n\n // Filter to public tickets only (exclude door-only and hidden)\n const publicTickets = tickets.filter((t) => {\n const visibility = t.visibility ?? 0;\n const isVisible = visibility === 0 || visibility === 1;\n const isPublic = t.salesChannel !== 2;\n return isVisible && isPublic;\n });\n\n if (publicTickets.length === 0) return null;\n\n // Find lowest and highest price\n const prices = publicTickets\n .map((t) => parseFloat(String(t.price ?? 0)))\n .filter((p) => !isNaN(p));\n\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n // Use AggregateOffer when there are multiple price points\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice: lowPrice,\n highPrice: highPrice,\n priceCurrency: 'USD',\n availability: getTicketAvailability(publicTickets[0]),\n url: url || undefined,\n validFrom:\n publicTickets[0]?.salesBegin || publicTickets[0]?.salesStart || undefined,\n };\n }\n\n // Single price point - use Offer\n const ticket = publicTickets[0];\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: getTicketAvailability(ticket),\n url: url || undefined,\n validFrom: ticket?.salesBegin || ticket?.salesStart || undefined,\n };\n}\n\n/**\n * Build performer array from event performers.\n *\n * Extracted from EventStructuredData.svelte lines 213-237.\n */\nexport function buildPerformers(\n performers: EventPerformerInput[] | null | undefined,\n): Array<Record<string, string>> | null {\n if (!performers || performers.length === 0) return null;\n\n // Filter to confirmed, visible performers\n const confirmed = performers.filter((p) => {\n const isConfirmed = p.acceptedState === 2;\n const isNotHidden = !p.shouldBeHidden;\n const profile = p.RosterPerformer?.User?.performerProfile;\n const hasName = profile?.firstName || profile?.lastName || profile?.displayName;\n return isConfirmed && isNotHidden && hasName;\n });\n\n if (confirmed.length === 0) return null;\n\n return confirmed.map((p) => {\n const profile = p.RosterPerformer?.User?.performerProfile;\n const name =\n profile?.displayName ||\n [profile?.firstName, profile?.lastName].filter(Boolean).join(' ');\n\n return {\n '@type': 'PerformingGroup',\n name: name,\n };\n });\n}\n","/**\n * Pure function that builds Event JSON-LD structured data for Google Events rich results.\n *\n * Extracted from EventStructuredData.svelte (lines 242-313).\n * Produces identical output to the original $derived.by block.\n */\n\nimport type { EventJsonLdInput, VenueInput, OrganizerInput } from './types';\nimport {\n formatDateTimeWithOffset,\n parseAddressToPostal,\n getEventStatus,\n buildOffers,\n buildPerformers,\n} from './helpers';\n\nexport interface BuildEventJsonLdOptions {\n event: EventJsonLdInput;\n venue: VenueInput;\n venueTimeZone?: string;\n eventUrl?: string;\n organizer?: OrganizerInput;\n}\n\nexport function buildEventJsonLd(\n options: BuildEventJsonLdOptions,\n): Record<string, unknown> | null {\n const {\n event,\n venue,\n venueTimeZone = 'America/Los_Angeles',\n eventUrl,\n organizer,\n } = options;\n\n if (!event) return null;\n\n const address = venue?.address || venue?.googleLocationNameCache || '';\n const startDate = formatDateTimeWithOffset(event.startDateTime, venueTimeZone);\n\n // Skip if we don't have required fields\n if (!event.name || !startDate || !venue?.name) {\n return null;\n }\n\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Event',\n name: event.name,\n startDate: startDate,\n eventStatus: getEventStatus(event.status),\n eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',\n location: {\n '@type': 'Place',\n name: venue.name,\n address: parseAddressToPostal(address),\n },\n };\n\n // Add endDate if available\n const endDate = formatDateTimeWithOffset(event.endDateTime, venueTimeZone);\n if (endDate) {\n data.endDate = endDate;\n }\n\n // Add description\n const description = event.description || event.eventSummary;\n if (description) {\n data.description = description;\n }\n\n // Add image - Google recommends multiple aspect ratios\n const imageUrl = event.image || event.coverImage || event.ShowImage;\n if (imageUrl) {\n data.image = [imageUrl];\n }\n\n // Add offers from tickets\n const offers = buildOffers(event.availableTickets, eventUrl);\n if (offers) {\n data.offers = offers;\n }\n\n // Add performers\n const performers = buildPerformers(event.performers);\n if (performers && performers.length > 0) {\n data.performer = performers;\n }\n\n // Add organizer if provided\n if (organizer?.name) {\n const org: Record<string, string> = {\n '@type': 'Organization',\n name: organizer.name,\n };\n if (organizer.url) {\n org.url = organizer.url;\n }\n data.organizer = org;\n }\n\n // Add event URL if provided\n if (eventUrl) {\n data.url = eventUrl;\n }\n\n return data;\n}\n","/**\n * Pure function that builds EventSeries JSON-LD structured data.\n *\n * Series use schema.org EventSeries type with subEvent array containing\n * individual Event blocks for each occurrence. Each subEvent has its own\n * offers built from the simplified series ticket DTOs (different shape\n * from the event builder's tickets).\n *\n * Google Rich Results Test does NOT support EventSeries directly, but\n * the individual subEvent Event blocks can qualify for rich results.\n */\n\nimport type { SeriesJsonLdInput } from './types';\nimport { formatDateTimeWithOffset, parseAddressToPostal, getEventStatus } from './helpers';\n\nexport interface BuildSeriesJsonLdOptions {\n series: SeriesJsonLdInput;\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\" -- used to build subEvent @id URLs\n}\n\n/**\n * Map a series ticket status string to schema.org ItemAvailability.\n *\n * Series tickets use a simplified { name, price, status } shape from the\n * backend (SeriesTicketDTO), unlike event tickets which have visibility,\n * salesChannel, remainingCapacity, etc.\n */\nfunction mapSeriesTicketAvailability(status: string): string {\n switch (status) {\n case 'available':\n return 'https://schema.org/InStock';\n case 'sold_out':\n return 'https://schema.org/SoldOut';\n case 'coming_soon':\n return 'https://schema.org/PreOrder';\n case 'ended':\n return 'https://schema.org/SoldOut';\n default:\n return 'https://schema.org/InStock';\n }\n}\n\n/**\n * Build offers from series occurrence tickets.\n *\n * Series tickets have a simpler shape than event tickets: { name, price, status }.\n * Uses AggregateOffer for multiple price points, Offer for single price.\n */\nfunction buildSeriesOccurrenceOffers(\n tickets: Array<{ name: string; price: number; status: string }> | undefined,\n occurrenceStatus: string | undefined,\n): Record<string, unknown> | null {\n if (!tickets || tickets.length === 0) return null;\n\n // Filter out sold_out and ended tickets for offer building\n const activeTickets = tickets.filter(\n (t) => t.status !== 'sold_out' && t.status !== 'ended',\n );\n\n // If all tickets are sold out / ended, report SoldOut with the price range\n if (activeTickets.length === 0) {\n const prices = tickets.map((t) => t.price).filter((p) => !isNaN(p));\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice,\n highPrice,\n priceCurrency: 'USD',\n availability: 'https://schema.org/SoldOut',\n };\n }\n\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: 'https://schema.org/SoldOut',\n };\n }\n\n const prices = activeTickets.map((t) => t.price).filter((p) => !isNaN(p));\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice,\n highPrice,\n priceCurrency: 'USD',\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\n };\n }\n\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\n };\n}\n\nexport function buildSeriesJsonLd(\n options: BuildSeriesJsonLdOptions,\n): Record<string, unknown> | null {\n const { series, baseEventUrl } = options;\n\n // Required fields\n if (!series.title) return null;\n if (!series.occurrences || series.occurrences.length === 0) return null;\n if (!series.venue?.name) return null;\n\n // Build location\n const address = parseAddressToPostal(series.venue.googleLocationNameCache || '');\n const location: Record<string, unknown> = {\n '@type': 'Place',\n name: series.venue.name,\n };\n if (address) {\n location.address = address;\n }\n\n // Build parent EventSeries\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'EventSeries',\n name: series.title,\n location,\n };\n\n // Description: prefer description, fall back to eventSummary\n const description = series.description || series.eventSummary;\n if (description) {\n data.description = description;\n }\n\n // Image\n if (series.image) {\n data.image = [series.image];\n }\n\n // Performers: flat displayName array (different from event builder's nested shape)\n const performers = series.performers?.filter((p) => p.displayName);\n if (performers && performers.length > 0) {\n data.performer = performers.map((p) => ({\n '@type': 'PerformingGroup',\n name: p.displayName,\n }));\n }\n\n // SubEvents: build Event blocks for each occurrence\n const subEvents: Record<string, unknown>[] = [];\n\n for (const occ of series.occurrences) {\n const startDate = formatDateTimeWithOffset(occ.startDateTime, series.timeZone);\n if (!startDate) continue; // Skip occurrences without valid startDate\n\n const subEvent: Record<string, unknown> = {\n '@type': 'Event',\n name: occ.title,\n startDate,\n eventStatus: getEventStatus(occ.status),\n location,\n };\n\n // @id from baseEventUrl\n if (baseEventUrl) {\n subEvent['@id'] = `${baseEventUrl}/${occ.id}-${occ.slug}`;\n }\n\n // endDate\n const endDate = formatDateTimeWithOffset(occ.endDateTime, series.timeZone);\n if (endDate) {\n subEvent.endDate = endDate;\n }\n\n // Offers from simplified ticket DTOs\n const offers = buildSeriesOccurrenceOffers(occ.tickets, occ.status);\n if (offers) {\n subEvent.offers = offers;\n }\n\n subEvents.push(subEvent);\n }\n\n // If all occurrences were skipped, return null\n if (subEvents.length === 0) return null;\n\n data.subEvent = subEvents;\n\n return data;\n}\n","/**\n * Pure function that builds Festival JSON-LD structured data for event collections.\n *\n * Collections use schema.org Festival type with subEvent array containing\n * individual Event blocks. Location is included only when all events share\n * the same venue. Date range is included only when dateRange is not null.\n *\n * Google Rich Results Test does NOT support Festival directly, but\n * the individual subEvent Event blocks can qualify for rich results.\n */\n\nimport type { CollectionJsonLdInput } from './types';\nimport { formatDateTimeWithOffset, parseAddressToPostal, getEventStatus } from './helpers';\n\nexport interface BuildCollectionJsonLdOptions {\n collection: CollectionJsonLdInput;\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\"\n defaultTimeZone?: string; // fallback timezone, defaults to 'America/Los_Angeles'\n}\n\nexport function buildCollectionJsonLd(\n options: BuildCollectionJsonLdOptions,\n): Record<string, unknown> | null {\n const {\n collection,\n baseEventUrl,\n defaultTimeZone = 'America/Los_Angeles',\n } = options;\n\n // Required fields\n if (!collection.collectionTitle) return null;\n if (!collection.events || collection.events.length === 0) return null;\n\n // Build parent Festival\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Festival',\n name: collection.collectionTitle,\n };\n\n // Description: prefer description, fall back to summary\n const description = collection.description || collection.summary;\n if (description) {\n data.description = description;\n }\n\n // Cover image\n if (collection.coverImage) {\n data.image = [collection.coverImage];\n }\n\n // Date range: include only when dateRange is not null and has values\n if (collection.dateRange) {\n const startDate = formatDateTimeWithOffset(\n collection.dateRange.startDate,\n defaultTimeZone,\n );\n const endDate = formatDateTimeWithOffset(\n collection.dateRange.endDate,\n defaultTimeZone,\n );\n if (startDate) {\n data.startDate = startDate;\n }\n if (endDate) {\n data.endDate = endDate;\n }\n }\n\n // Location: include only when all events share the same venue\n const uniqueVenueIds = new Set(\n collection.events\n .map((e) => e.venueId)\n .filter((id): id is number => id !== undefined && id !== 0),\n );\n\n if (uniqueVenueIds.size === 1) {\n // All events share the same venue; use the first event's venueName\n const venueEvent = collection.events.find(\n (e) => e.venueId && e.venueId !== 0 && e.venueName,\n );\n if (venueEvent?.venueName) {\n data.location = {\n '@type': 'Place',\n name: venueEvent.venueName,\n };\n }\n }\n // If multiple venueIds or zero venueIds: OMIT location entirely\n\n // SubEvents: build Event blocks for each collection event\n const subEvents: Record<string, unknown>[] = [];\n\n for (const event of collection.events) {\n const startDate = formatDateTimeWithOffset(event.startDateTime, defaultTimeZone);\n if (!startDate) continue; // Skip events without valid startDate\n\n const subEvent: Record<string, unknown> = {\n '@type': 'Event',\n name: event.title,\n startDate,\n eventStatus: getEventStatus(event.status),\n };\n\n // Location: include venue name and address if available\n if (event.venueName) {\n const subLocation: Record<string, unknown> = {\n '@type': 'Place',\n name: event.venueName,\n };\n if (event.venueAddress) {\n const address = parseAddressToPostal(event.venueAddress);\n if (address) {\n subLocation.address = address;\n }\n }\n subEvent.location = subLocation;\n }\n\n // @id from baseEventUrl\n if (baseEventUrl && event.slug) {\n subEvent['@id'] = `${baseEventUrl}/${event.id}-${event.slug}`;\n }\n\n // endDate\n const endDate = formatDateTimeWithOffset(event.endDateTime, defaultTimeZone);\n if (endDate) {\n subEvent.endDate = endDate;\n }\n\n // Image\n if (event.image) {\n subEvent.image = [event.image];\n }\n\n subEvents.push(subEvent);\n }\n\n // If all events were skipped, return null\n if (subEvents.length === 0) return null;\n\n data.subEvent = subEvents;\n\n return data;\n}\n","/**\n * Text utility functions: classNames, truncateTitle, generateSlug, validateNegativeNumber\n */\n\n/**\n * Combine class names, filtering out falsy values\n */\nexport function classNames(...classes: (string | false | null | undefined)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n\n/**\n * Truncate a title to a maximum length, appending \"...\" if truncated\n */\nexport function truncateTitle(title: string, maxLength: number): string {\n if (title.length > maxLength) {\n return title.slice(0, maxLength) + \"...\";\n }\n return title;\n}\n\n/**\n * Generate a URL-friendly slug from a string\n */\nexport function generateSlug(text: string): string {\n if (!text) return \"\";\n\n return text\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/--+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n/**\n * Validate that a value is a positive number\n * Returns an error message string, or empty string if valid\n */\nexport function validateNegativeNumber(value: string | number | null | undefined): string {\n if (value === null || value === undefined || value === \"\") {\n return \"Must be greater than zero.\";\n }\n\n const stringValue = String(value).trim();\n\n if (\n stringValue.includes(\"--\") ||\n stringValue.includes(\"++\") ||\n stringValue.match(/^-+$/) ||\n stringValue.match(/^\\++$/)\n ) {\n return \"Please enter a valid number\";\n }\n\n if (stringValue.match(/^-?\\d+-$/)) {\n return \"Please enter a valid number\";\n }\n\n if ((stringValue.match(/\\./g) || []).length > 1) {\n return \"Please enter a valid number\";\n }\n\n const numValue = Number(value);\n if (isNaN(numValue)) {\n return \"Please enter a valid number\";\n }\n\n if (numValue < 0 || numValue === 0) {\n return \"Price must be greater than zero.\";\n }\n\n return \"\";\n}\n","/**\n * HostSeoController -- manages JSON-LD structured data, canonical URLs,\n * OG tags, meta descriptions, and page title injection into venue pages'\n * <head> when the Micdrop calendar widget is embedded.\n *\n * Follows a save-replace-restore pattern to be a \"good citizen\" on venue pages:\n * idle -> listing -> entity -> listing -> ... -> destroyed\n *\n * Uses textContent (never innerHTML) for JSON-LD script tags to prevent XSS.\n */\n\nimport type { HostSeoControllerOptions } from './types';\nimport { buildEventJsonLd } from './buildEventJsonLd';\nimport { buildSeriesJsonLd } from './buildSeriesJsonLd';\nimport { buildCollectionJsonLd } from './buildCollectionJsonLd';\nimport { generateSlug } from '$lib/utils/textUtils';\n\ntype ControllerState = 'idle' | 'listing' | 'entity' | 'destroyed';\n\ninterface SavedOriginals {\n title: string;\n canonicalHref: string | null;\n ogTitle: string | null;\n ogDescription: string | null;\n ogImage: string | null;\n ogUrl: string | null;\n ogType: string | null;\n metaDescription: string | null;\n}\n\n// Minimal event shape for listing mode\ninterface ListingEvent {\n id: number | string;\n name: string;\n slug?: string;\n startDateTime?: string;\n image?: string;\n}\n\n// Venue data passed at runtime\ninterface VenueData {\n name: string;\n address?: string;\n googleLocationNameCache?: string;\n timeZone?: string;\n}\n\nexport class HostSeoController {\n /** Static guard: only one instance active at a time */\n static _activeInstance: HostSeoController | null = null;\n\n private _state: ControllerState = 'idle';\n private _venueName: string;\n private _venueAddress: string | undefined;\n private _venueTimeZone: string;\n private _baseUrl: string;\n private _originals: SavedOriginals | null = null;\n private _originalsSaved = false;\n private _events: ListingEvent[] = [];\n\n constructor(options: HostSeoControllerOptions) {\n // SSR guard\n if (typeof document === 'undefined') return;\n\n // If another instance exists, destroy it first\n if (HostSeoController._activeInstance && HostSeoController._activeInstance !== this) {\n HostSeoController._activeInstance.destroy();\n }\n\n this._venueName = options.venueName;\n this._venueAddress = options.venueAddress;\n this._venueTimeZone = options.venueTimeZone || 'America/Los_Angeles';\n this._baseUrl = options.baseUrl || (window.location.origin + window.location.pathname);\n\n // Save originals immediately\n this._saveOriginals();\n\n HostSeoController._activeInstance = this;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private _saveOriginals(): void {\n if (this._originalsSaved) return;\n\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\n const ogTitle = document.querySelector('meta[property=\"og:title\"]') as HTMLMetaElement | null;\n const ogDescription = document.querySelector('meta[property=\"og:description\"]') as HTMLMetaElement | null;\n const ogImage = document.querySelector('meta[property=\"og:image\"]') as HTMLMetaElement | null;\n const ogUrl = document.querySelector('meta[property=\"og:url\"]') as HTMLMetaElement | null;\n const ogType = document.querySelector('meta[property=\"og:type\"]') as HTMLMetaElement | null;\n const metaDescription = document.querySelector('meta[name=\"description\"]') as HTMLMetaElement | null;\n\n this._originals = {\n title: document.title,\n canonicalHref: canonical?.getAttribute('href') ?? null,\n ogTitle: ogTitle?.getAttribute('content') ?? null,\n ogDescription: ogDescription?.getAttribute('content') ?? null,\n ogImage: ogImage?.getAttribute('content') ?? null,\n ogUrl: ogUrl?.getAttribute('content') ?? null,\n ogType: ogType?.getAttribute('content') ?? null,\n metaDescription: metaDescription?.getAttribute('content') ?? null,\n };\n\n this._originalsSaved = true;\n }\n\n private _injectJsonLd(data: Record<string, unknown>): void {\n if (typeof document === 'undefined') return;\n\n // Remove any existing micdrop JSON-LD\n document.querySelectorAll('script[data-micdrop=\"jsonld\"]').forEach((el) => el.remove());\n\n const script = document.createElement('script');\n script.setAttribute('type', 'application/ld+json');\n script.setAttribute('data-micdrop', 'jsonld');\n // Use textContent (NOT innerHTML) to prevent XSS\n script.textContent = JSON.stringify(data);\n document.head.appendChild(script);\n }\n\n private _setMeta(property: string, content: string): void {\n if (typeof document === 'undefined') return;\n\n if (property.startsWith('og:')) {\n // OG tags use property attribute\n let el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\n if (el) {\n el.setAttribute('content', content);\n } else {\n el = document.createElement('meta');\n el.setAttribute('property', property);\n el.setAttribute('content', content);\n el.setAttribute('data-micdrop', 'og');\n document.head.appendChild(el);\n }\n } else {\n // Standard meta tags use name attribute (e.g., description)\n let el = document.querySelector(`meta[name=\"${property}\"]`) as HTMLMetaElement | null;\n if (el) {\n el.setAttribute('content', content);\n } else {\n el = document.createElement('meta');\n el.setAttribute('name', property);\n el.setAttribute('content', content);\n el.setAttribute('data-micdrop', 'meta');\n document.head.appendChild(el);\n }\n }\n }\n\n private _setCanonical(url: string): void {\n if (typeof document === 'undefined') return;\n\n let el = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\n if (el) {\n el.setAttribute('href', url);\n } else {\n el = document.createElement('link');\n el.setAttribute('rel', 'canonical');\n el.setAttribute('href', url);\n el.setAttribute('data-micdrop', 'canonical');\n document.head.appendChild(el);\n }\n }\n\n private _removeAllInjected(): void {\n if (typeof document === 'undefined') return;\n document.querySelectorAll('[data-micdrop]').forEach((el) => el.remove());\n }\n\n private _restoreOriginals(): void {\n if (typeof document === 'undefined' || !this._originals) return;\n\n // Restore title\n document.title = this._originals.title;\n\n // Restore canonical\n if (this._originals.canonicalHref !== null) {\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\n if (canonical) {\n canonical.setAttribute('href', this._originals.canonicalHref);\n }\n }\n\n // Restore OG tags\n this._restoreMetaProperty('og:title', this._originals.ogTitle);\n this._restoreMetaProperty('og:description', this._originals.ogDescription);\n this._restoreMetaProperty('og:image', this._originals.ogImage);\n this._restoreMetaProperty('og:url', this._originals.ogUrl);\n this._restoreMetaProperty('og:type', this._originals.ogType);\n\n // Restore meta description\n this._restoreMetaName('description', this._originals.metaDescription);\n\n this._originalsSaved = false;\n }\n\n private _restoreMetaProperty(property: string, originalContent: string | null): void {\n const el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\n if (originalContent !== null && el) {\n // Had original value -- restore it\n el.setAttribute('content', originalContent);\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\n // We created it -- remove it\n el.remove();\n }\n }\n\n private _restoreMetaName(name: string, originalContent: string | null): void {\n const el = document.querySelector(`meta[name=\"${name}\"]`) as HTMLMetaElement | null;\n if (originalContent !== null && el) {\n el.setAttribute('content', originalContent);\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\n el.remove();\n }\n }\n\n private _buildItemList(events: ListingEvent[]): Record<string, unknown> {\n return {\n '@context': 'https://schema.org',\n '@type': 'ItemList',\n itemListElement: events.map((event, i) => {\n const slug = event.slug || generateSlug(event.name);\n return {\n '@type': 'ListItem',\n position: i + 1,\n url: `${this._baseUrl}?e=${event.id}-${slug}`,\n };\n }),\n };\n }\n\n private _buildEntityUrl(type: string, id: string | number, slug: string): string {\n const prefix = type === 'event' ? 'e' : type === 'series' ? 's' : 'c';\n return `${this._baseUrl}?${prefix}=${id}-${slug}`;\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n /**\n * Called when events finish loading from the API.\n * Injects an ItemList JSON-LD for the listing. Does not modify OG/canonical/title.\n */\n onEventsLoaded(\n events: ListingEvent[],\n venue?: VenueData,\n ): void {\n if (typeof document === 'undefined') return;\n if (this._state === 'destroyed') return;\n\n // Update venue info from parameter if provided\n if (venue) {\n this._venueName = venue.name || this._venueName;\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\n if (venue.timeZone) {\n this._venueTimeZone = venue.timeZone;\n }\n }\n\n // Store events for later use (re-injection on back to listing)\n this._events = events;\n\n // Build and inject ItemList JSON-LD\n const itemList = this._buildItemList(events);\n this._injectJsonLd(itemList);\n\n this._state = 'listing';\n }\n\n /**\n * Called when the user selects an event, series, or collection.\n * Replaces ItemList JSON-LD with entity-specific JSON-LD and updates\n * all head tags (canonical, OG, meta description, title).\n */\n onEntitySelected(\n type: 'event' | 'series' | 'collection',\n data: any,\n venue?: VenueData,\n ): void {\n if (typeof document === 'undefined') return;\n if (this._state === 'destroyed') return;\n\n // Save originals if not already saved\n this._saveOriginals();\n\n // Update venue info from parameter if provided\n if (venue) {\n this._venueName = venue.name || this._venueName;\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\n if (venue.timeZone) {\n this._venueTimeZone = venue.timeZone;\n }\n }\n\n let jsonLd: Record<string, unknown> | null = null;\n let entityName = '';\n let entityDescription = '';\n let entityImage = '';\n let entityUrl = '';\n\n if (type === 'event') {\n const slug = data.slug || generateSlug(data.name || '');\n entityUrl = this._buildEntityUrl('event', data.id || data.eventID || '', slug);\n\n jsonLd = buildEventJsonLd({\n event: data,\n venue: {\n name: this._venueName,\n googleLocationNameCache: this._venueAddress,\n },\n venueTimeZone: this._venueTimeZone,\n eventUrl: entityUrl,\n });\n\n entityName = data.name || '';\n entityDescription = data.description || data.eventSummary || '';\n entityImage = data.image || data.coverImage || data.ShowImage || '';\n } else if (type === 'series') {\n const slug = data.slug || generateSlug(data.title || '');\n entityUrl = this._buildEntityUrl('series', data.id || '', slug);\n\n jsonLd = buildSeriesJsonLd({\n series: {\n ...data,\n venue: {\n name: this._venueName,\n googleLocationNameCache: this._venueAddress,\n },\n timeZone: this._venueTimeZone,\n },\n baseEventUrl: entityUrl,\n });\n\n entityName = data.title || '';\n entityDescription = data.description || data.eventSummary || '';\n entityImage = data.image || '';\n } else if (type === 'collection') {\n const slug = data.slug || generateSlug(data.collectionTitle || '');\n entityUrl = this._buildEntityUrl('collection', data.id || '', slug);\n\n jsonLd = buildCollectionJsonLd({\n collection: data,\n baseEventUrl: entityUrl,\n defaultTimeZone: this._venueTimeZone,\n });\n\n entityName = data.collectionTitle || '';\n entityDescription = data.description || data.summary || '';\n entityImage = data.coverImage || '';\n }\n\n // Inject JSON-LD (replaces existing micdrop JSON-LD)\n if (jsonLd) {\n this._injectJsonLd(jsonLd);\n }\n\n // Update canonical URL\n if (entityUrl) {\n this._setCanonical(entityUrl);\n }\n\n // Update OG tags\n if (entityName) {\n this._setMeta('og:title', entityName);\n }\n if (entityDescription) {\n this._setMeta('og:description', entityDescription);\n }\n if (entityImage) {\n this._setMeta('og:image', entityImage);\n }\n if (entityUrl) {\n this._setMeta('og:url', entityUrl);\n }\n this._setMeta('og:type', 'website');\n\n // Twitter Card: large image preview\n this._setMeta('twitter:card', 'summary_large_image');\n\n // Update meta description\n if (entityDescription) {\n this._setMeta('description', entityDescription);\n }\n\n // Update page title\n if (entityName) {\n document.title = `${entityName} | ${this._venueName}`;\n }\n\n this._state = 'entity';\n }\n\n /**\n * Called when the user navigates back to the event listing.\n * Restores original head elements and re-injects ItemList JSON-LD.\n */\n onBackToListing(): void {\n if (typeof document === 'undefined') return;\n if (this._state === 'destroyed') return;\n\n // Restore all originals\n this._restoreOriginals();\n\n // Remove any remaining injected elements\n this._removeAllInjected();\n\n // Re-inject ItemList JSON-LD if events are available\n if (this._events.length > 0) {\n const itemList = this._buildItemList(this._events);\n this._injectJsonLd(itemList);\n }\n\n // Re-save originals since we just restored them\n this._saveOriginals();\n\n this._state = 'listing';\n }\n\n /**\n * Called when the widget is destroyed. Removes all injected elements\n * and restores originals. Clears the static active instance.\n */\n destroy(): void {\n if (typeof document === 'undefined') return;\n if (this._state === 'destroyed') return;\n\n this._removeAllInjected();\n this._restoreOriginals();\n\n if (HostSeoController._activeInstance === this) {\n HostSeoController._activeInstance = null;\n }\n\n this._state = 'destroyed';\n }\n}\n"],"names":["formatDateTimeWithOffset","isoString","tz","date","parts","get","type","p","year","month","day","hour","minute","second","tzOffset","offset","match","sign","hours","mins","parseAddressToPostal","addressStr","stateZipMatch","getEventStatus","rawStatus","status","getTicketAvailability","ticket","now","salesBegin","salesEnd","buildOffers","tickets","url","publicTickets","t","visibility","isVisible","isPublic","prices","lowPrice","highPrice","buildPerformers","performers","confirmed","isConfirmed","isNotHidden","profile","hasName","buildEventJsonLd","options","event","venue","venueTimeZone","eventUrl","organizer","address","startDate","data","endDate","description","imageUrl","offers","org","mapSeriesTicketAvailability","buildSeriesOccurrenceOffers","occurrenceStatus","activeTickets","buildSeriesJsonLd","series","baseEventUrl","location","subEvents","occ","subEvent","buildCollectionJsonLd","collection","defaultTimeZone","e","id","venueEvent","subLocation","generateSlug","text","_HostSeoController","canonical","ogTitle","ogDescription","ogImage","ogUrl","ogType","metaDescription","el","script","property","content","originalContent","name","events","i","slug","prefix","itemList","jsonLd","entityName","entityDescription","entityImage","entityUrl","HostSeoController"],"mappings":"gFAeO,SAASA,EACdC,EACAC,EACe,CACf,GAAI,CAACD,EAAW,OAAO,KAEvB,MAAME,EAAO,IAAI,KAAKF,CAAS,EAC/B,GAAI,MAAME,EAAK,QAAA,CAAS,EAAG,OAAO,KAelC,MAAMC,EAZY,IAAI,KAAK,eAAe,QAAS,CACjD,SAAUF,EACV,KAAM,UACN,MAAO,UACP,IAAK,UACL,KAAM,UACN,OAAQ,UACR,OAAQ,UACR,OAAQ,GACR,aAAc,aAAA,CACf,EAEuB,cAAcC,CAAI,EACpCE,EAAOC,GAAiBF,EAAM,KAAMG,GAAMA,EAAE,OAASD,CAAI,GAAG,OAAS,GAErEE,EAAOH,EAAI,MAAM,EACjBI,EAAQJ,EAAI,OAAO,EACnBK,EAAML,EAAI,KAAK,EACfM,EAAON,EAAI,MAAM,EACjBO,EAASP,EAAI,QAAQ,EACrBQ,EAASR,EAAI,QAAQ,EACrBS,EAAWT,EAAI,cAAc,EAGnC,IAAIU,EAAS,SACb,GAAID,EAAU,CACZ,MAAME,EAAQF,EAAS,MAAM,2BAA2B,EACxD,GAAIE,EAAO,CACT,MAAMC,EAAOD,EAAM,CAAC,EACdE,EAAQF,EAAM,CAAC,EAAE,SAAS,EAAG,GAAG,EAChCG,EAAOH,EAAM,CAAC,GAAK,KACzBD,EAAS,GAAGE,CAAI,GAAGC,CAAK,IAAIC,CAAI,EAClC,CACF,CAEA,MAAO,GAAGX,CAAI,IAAIC,CAAK,IAAIC,CAAG,IAAIC,CAAI,IAAIC,CAAM,IAAIC,CAAM,GAAGE,CAAM,EACrE,CASO,SAASK,EACdC,EAC+B,CAC/B,GAAI,CAACA,EAAY,OAAO,KAExB,MAAMjB,EAAQiB,EAAW,MAAM,GAAG,EAAE,IAAKd,GAAMA,EAAE,MAAM,EAEvD,GAAIH,EAAM,OAAS,EACjB,MAAO,CACL,QAAS,gBACT,cAAeiB,EACf,eAAgB,IAAA,EAMpB,MAAMC,EADWlB,EAAMA,EAAM,OAAS,CAAC,EACR,MAAM,mCAAmC,EAExE,OAAIkB,GAAiBlB,EAAM,QAAU,EAE5B,CACL,QAAS,gBACT,cAAeA,EAAM,MAAM,EAAG,EAAE,EAAE,KAAK,IAAI,EAC3C,gBAAiBA,EAAMA,EAAM,OAAS,CAAC,EACvC,cAAekB,EAAc,CAAC,EAC9B,WAAYA,EAAc,CAAC,EAC3B,eAAgB,IAAA,EAKb,CACL,QAAS,gBACT,cAAelB,EAAM,CAAC,EACtB,gBAAiBA,EAAM,MAAM,CAAC,EAAE,KAAK,IAAI,EACzC,eAAgB,IAAA,CAEpB,CAOO,SAASmB,EAAeC,EAA8C,CAC3E,GAAI,CAACA,EAAW,MAAO,oCAEvB,MAAMC,EAASD,EAAU,YAAA,EAEzB,OAAIC,IAAW,aAAeA,IAAW,WAChC,oCAELA,IAAW,YACN,oCAELA,IAAW,cACN,sCAGF,mCACT,CAOO,SAASC,EAAsBC,EAAkC,CAItE,IAFEA,EAAO,mBAAqBA,EAAO,mBAAqBA,EAAO,YAE/C,GAAKA,EAAO,QAC5B,MAAO,6BAGT,MAAMC,MAAU,KACVC,EACJF,EAAO,YAAcA,EAAO,YAAcA,EAAO,WAAaA,EAAO,YACjEG,EAAWH,EAAO,UAAYA,EAAO,SAAWA,EAAO,UAE7D,OAAIE,GAAc,IAAI,KAAKA,CAAU,EAAID,EAChC,8BAGLE,GAAY,IAAI,KAAKA,CAAQ,EAAIF,EAC5B,6BAGF,4BACT,CAOO,SAASG,EACdC,EACAC,EACgC,CAChC,GAAI,CAACD,GAAWA,EAAQ,SAAW,EAAG,OAAO,KAG7C,MAAME,EAAgBF,EAAQ,OAAQG,GAAM,CAC1C,MAAMC,EAAaD,EAAE,YAAc,EAC7BE,EAAYD,IAAe,GAAKA,IAAe,EAC/CE,EAAWH,EAAE,eAAiB,EACpC,OAAOE,GAAaC,CACtB,CAAC,EAED,GAAIJ,EAAc,SAAW,EAAG,OAAO,KAGvC,MAAMK,EAASL,EACZ,IAAKC,GAAM,WAAW,OAAOA,EAAE,OAAS,CAAC,CAAC,CAAC,EAC3C,OAAQ5B,GAAM,CAAC,MAAMA,CAAC,CAAC,EAE1B,GAAIgC,EAAO,SAAW,EAAG,OAAO,KAEhC,MAAMC,EAAW,KAAK,IAAI,GAAGD,CAAM,EAC7BE,EAAY,KAAK,IAAI,GAAGF,CAAM,EAGpC,GAAIC,IAAaC,EACf,MAAO,CACL,QAAS,iBACT,SAAAD,EACA,UAAAC,EACA,cAAe,MACf,aAAcf,EAAsBQ,EAAc,CAAC,CAAC,EACpD,IAAKD,GAAO,OACZ,UACEC,EAAc,CAAC,GAAG,YAAcA,EAAc,CAAC,GAAG,YAAc,MAAA,EAKtE,MAAMP,EAASO,EAAc,CAAC,EAC9B,MAAO,CACL,QAAS,QACT,MAAOM,EACP,cAAe,MACf,aAAcd,EAAsBC,CAAM,EAC1C,IAAKM,GAAO,OACZ,UAAWN,GAAQ,YAAcA,GAAQ,YAAc,MAAA,CAE3D,CAOO,SAASe,EACdC,EACsC,CACtC,GAAI,CAACA,GAAcA,EAAW,SAAW,EAAG,OAAO,KAGnD,MAAMC,EAAYD,EAAW,OAAQpC,GAAM,CACzC,MAAMsC,EAActC,EAAE,gBAAkB,EAClCuC,EAAc,CAACvC,EAAE,eACjBwC,EAAUxC,EAAE,iBAAiB,MAAM,iBACnCyC,EAAUD,GAAS,WAAaA,GAAS,UAAYA,GAAS,YACpE,OAAOF,GAAeC,GAAeE,CACvC,CAAC,EAED,OAAIJ,EAAU,SAAW,EAAU,KAE5BA,EAAU,IAAKrC,GAAM,CAC1B,MAAMwC,EAAUxC,EAAE,iBAAiB,MAAM,iBAKzC,MAAO,CACL,QAAS,kBACT,KALAwC,GAAS,aACT,CAACA,GAAS,UAAWA,GAAS,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,CAIhE,CAEJ,CAAC,CACH,CCnOO,SAASE,EACdC,EACgC,CAChC,KAAM,CACJ,MAAAC,EACA,MAAAC,EACA,cAAAC,EAAgB,sBAChB,SAAAC,EACA,UAAAC,CAAA,EACEL,EAEJ,GAAI,CAACC,EAAO,OAAO,KAEnB,MAAMK,EAAUJ,GAAO,SAAWA,GAAO,yBAA2B,GAC9DK,EAAYzD,EAAyBmD,EAAM,cAAeE,CAAa,EAG7E,GAAI,CAACF,EAAM,MAAQ,CAACM,GAAa,CAACL,GAAO,KACvC,OAAO,KAGT,MAAMM,EAAgC,CACpC,WAAY,qBACZ,QAAS,QACT,KAAMP,EAAM,KACZ,UAAAM,EACA,YAAalC,EAAe4B,EAAM,MAAM,EACxC,oBAAqB,gDACrB,SAAU,CACR,QAAS,QACT,KAAMC,EAAM,KACZ,QAAShC,EAAqBoC,CAAO,CAAA,CACvC,EAIIG,EAAU3D,EAAyBmD,EAAM,YAAaE,CAAa,EACrEM,IACFD,EAAK,QAAUC,GAIjB,MAAMC,EAAcT,EAAM,aAAeA,EAAM,aAC3CS,IACFF,EAAK,YAAcE,GAIrB,MAAMC,EAAWV,EAAM,OAASA,EAAM,YAAcA,EAAM,UACtDU,IACFH,EAAK,MAAQ,CAACG,CAAQ,GAIxB,MAAMC,EAAS/B,EAAYoB,EAAM,iBAAkBG,CAAQ,EACvDQ,IACFJ,EAAK,OAASI,GAIhB,MAAMnB,EAAaD,EAAgBS,EAAM,UAAU,EAMnD,GALIR,GAAcA,EAAW,OAAS,IACpCe,EAAK,UAAYf,GAIfY,GAAW,KAAM,CACnB,MAAMQ,EAA8B,CAClC,QAAS,eACT,KAAMR,EAAU,IAAA,EAEdA,EAAU,MACZQ,EAAI,IAAMR,EAAU,KAEtBG,EAAK,UAAYK,CACnB,CAGA,OAAIT,IACFI,EAAK,IAAMJ,GAGNI,CACT,CChFA,SAASM,EAA4BvC,EAAwB,CAC3D,OAAQA,EAAA,CACN,IAAK,YACH,MAAO,6BACT,IAAK,WACH,MAAO,6BACT,IAAK,cACH,MAAO,8BACT,IAAK,QACH,MAAO,6BACT,QACE,MAAO,4BAAA,CAEb,CAQA,SAASwC,EACPjC,EACAkC,EACgC,CAChC,GAAI,CAAClC,GAAWA,EAAQ,SAAW,EAAG,OAAO,KAG7C,MAAMmC,EAAgBnC,EAAQ,OAC3BG,GAAMA,EAAE,SAAW,YAAcA,EAAE,SAAW,OAAA,EAIjD,GAAIgC,EAAc,SAAW,EAAG,CAC9B,MAAM5B,EAASP,EAAQ,IAAKG,GAAMA,EAAE,KAAK,EAAE,OAAQ5B,GAAM,CAAC,MAAMA,CAAC,CAAC,EAClE,GAAIgC,EAAO,SAAW,EAAG,OAAO,KAEhC,MAAMC,EAAW,KAAK,IAAI,GAAGD,CAAM,EAC7BE,EAAY,KAAK,IAAI,GAAGF,CAAM,EAEpC,OAAIC,IAAaC,EACR,CACL,QAAS,iBACT,SAAAD,EACA,UAAAC,EACA,cAAe,MACf,aAAc,4BAAA,EAIX,CACL,QAAS,QACT,MAAOD,EACP,cAAe,MACf,aAAc,4BAAA,CAElB,CAEA,MAAMD,EAAS4B,EAAc,IAAKhC,GAAMA,EAAE,KAAK,EAAE,OAAQ5B,GAAM,CAAC,MAAMA,CAAC,CAAC,EACxE,GAAIgC,EAAO,SAAW,EAAG,OAAO,KAEhC,MAAMC,EAAW,KAAK,IAAI,GAAGD,CAAM,EAC7BE,EAAY,KAAK,IAAI,GAAGF,CAAM,EAEpC,OAAIC,IAAaC,EACR,CACL,QAAS,iBACT,SAAAD,EACA,UAAAC,EACA,cAAe,MACf,aAAcuB,EAA4BG,EAAc,CAAC,EAAE,MAAM,CAAA,EAI9D,CACL,QAAS,QACT,MAAO3B,EACP,cAAe,MACf,aAAcwB,EAA4BG,EAAc,CAAC,EAAE,MAAM,CAAA,CAErE,CAEO,SAASC,EACdlB,EACgC,CAChC,KAAM,CAAE,OAAAmB,EAAQ,aAAAC,CAAA,EAAiBpB,EAKjC,GAFI,CAACmB,EAAO,OACR,CAACA,EAAO,aAAeA,EAAO,YAAY,SAAW,GACrD,CAACA,EAAO,OAAO,KAAM,OAAO,KAGhC,MAAMb,EAAUpC,EAAqBiD,EAAO,MAAM,yBAA2B,EAAE,EACzEE,EAAoC,CACxC,QAAS,QACT,KAAMF,EAAO,MAAM,IAAA,EAEjBb,IACFe,EAAS,QAAUf,GAIrB,MAAME,EAAgC,CACpC,WAAY,qBACZ,QAAS,cACT,KAAMW,EAAO,MACb,SAAAE,CAAA,EAIIX,EAAcS,EAAO,aAAeA,EAAO,aAC7CT,IACFF,EAAK,YAAcE,GAIjBS,EAAO,QACTX,EAAK,MAAQ,CAACW,EAAO,KAAK,GAI5B,MAAM1B,EAAa0B,EAAO,YAAY,OAAQ9D,GAAMA,EAAE,WAAW,EAC7DoC,GAAcA,EAAW,OAAS,IACpCe,EAAK,UAAYf,EAAW,IAAKpC,IAAO,CACtC,QAAS,kBACT,KAAMA,EAAE,WAAA,EACR,GAIJ,MAAMiE,EAAuC,CAAA,EAE7C,UAAWC,KAAOJ,EAAO,YAAa,CACpC,MAAMZ,EAAYzD,EAAyByE,EAAI,cAAeJ,EAAO,QAAQ,EAC7E,GAAI,CAACZ,EAAW,SAEhB,MAAMiB,EAAoC,CACxC,QAAS,QACT,KAAMD,EAAI,MACV,UAAAhB,EACA,YAAalC,EAAekD,EAAI,MAAM,EACtC,SAAAF,CAAA,EAIED,IACFI,EAAS,KAAK,EAAI,GAAGJ,CAAY,IAAIG,EAAI,EAAE,IAAIA,EAAI,IAAI,IAIzD,MAAMd,EAAU3D,EAAyByE,EAAI,YAAaJ,EAAO,QAAQ,EACrEV,IACFe,EAAS,QAAUf,GAIrB,MAAMG,EAASG,EAA4BQ,EAAI,QAASA,EAAI,MAAM,EAC9DX,IACFY,EAAS,OAASZ,GAGpBU,EAAU,KAAKE,CAAQ,CACzB,CAGA,OAAIF,EAAU,SAAW,EAAU,MAEnCd,EAAK,SAAWc,EAETd,EACT,CClLO,SAASiB,EACdzB,EACgC,CAChC,KAAM,CACJ,WAAA0B,EACA,aAAAN,EACA,gBAAAO,EAAkB,qBAAA,EAChB3B,EAIJ,GADI,CAAC0B,EAAW,iBACZ,CAACA,EAAW,QAAUA,EAAW,OAAO,SAAW,EAAG,OAAO,KAGjE,MAAMlB,EAAgC,CACpC,WAAY,qBACZ,QAAS,WACT,KAAMkB,EAAW,eAAA,EAIbhB,EAAcgB,EAAW,aAAeA,EAAW,QAWzD,GAVIhB,IACFF,EAAK,YAAcE,GAIjBgB,EAAW,aACblB,EAAK,MAAQ,CAACkB,EAAW,UAAU,GAIjCA,EAAW,UAAW,CACxB,MAAMnB,EAAYzD,EAChB4E,EAAW,UAAU,UACrBC,CAAA,EAEIlB,EAAU3D,EACd4E,EAAW,UAAU,QACrBC,CAAA,EAEEpB,IACFC,EAAK,UAAYD,GAEfE,IACFD,EAAK,QAAUC,EAEnB,CASA,GANuB,IAAI,IACzBiB,EAAW,OACR,IAAKE,GAAMA,EAAE,OAAO,EACpB,OAAQC,GAAqBA,IAAO,QAAaA,IAAO,CAAC,CAAA,EAG3C,OAAS,EAAG,CAE7B,MAAMC,EAAaJ,EAAW,OAAO,KAClCE,GAAMA,EAAE,SAAWA,EAAE,UAAY,GAAKA,EAAE,SAAA,EAEvCE,GAAY,YACdtB,EAAK,SAAW,CACd,QAAS,QACT,KAAMsB,EAAW,SAAA,EAGvB,CAIA,MAAMR,EAAuC,CAAA,EAE7C,UAAWrB,KAASyB,EAAW,OAAQ,CACrC,MAAMnB,EAAYzD,EAAyBmD,EAAM,cAAe0B,CAAe,EAC/E,GAAI,CAACpB,EAAW,SAEhB,MAAMiB,EAAoC,CACxC,QAAS,QACT,KAAMvB,EAAM,MACZ,UAAAM,EACA,YAAalC,EAAe4B,EAAM,MAAM,CAAA,EAI1C,GAAIA,EAAM,UAAW,CACnB,MAAM8B,EAAuC,CAC3C,QAAS,QACT,KAAM9B,EAAM,SAAA,EAEd,GAAIA,EAAM,aAAc,CACtB,MAAMK,EAAUpC,EAAqB+B,EAAM,YAAY,EACnDK,IACFyB,EAAY,QAAUzB,EAE1B,CACAkB,EAAS,SAAWO,CACtB,CAGIX,GAAgBnB,EAAM,OACxBuB,EAAS,KAAK,EAAI,GAAGJ,CAAY,IAAInB,EAAM,EAAE,IAAIA,EAAM,IAAI,IAI7D,MAAMQ,EAAU3D,EAAyBmD,EAAM,YAAa0B,CAAe,EACvElB,IACFe,EAAS,QAAUf,GAIjBR,EAAM,QACRuB,EAAS,MAAQ,CAACvB,EAAM,KAAK,GAG/BqB,EAAU,KAAKE,CAAQ,CACzB,CAGA,OAAIF,EAAU,SAAW,EAAU,MAEnCd,EAAK,SAAWc,EAETd,EACT,CCxHO,SAASwB,EAAaC,EAAsB,CACjD,OAAKA,EAEEA,EACJ,cACA,OACA,QAAQ,YAAa,EAAE,EACvB,QAAQ,OAAQ,GAAG,EACnB,QAAQ,OAAQ,GAAG,EACnB,QAAQ,WAAY,EAAE,EARP,EASpB,CCaO,MAAMC,EAAN,MAAMA,CAAkB,CAa7B,YAAYlC,EAAmC,CAT/C,KAAQ,OAA0B,OAKlC,KAAQ,WAAoC,KAC5C,KAAQ,gBAAkB,GAC1B,KAAQ,QAA0B,CAAA,EAI5B,SAAO,SAAa,OAGpBkC,EAAkB,iBAAmBA,EAAkB,kBAAoB,MAC7EA,EAAkB,gBAAgB,QAAA,EAGpC,KAAK,WAAalC,EAAQ,UAC1B,KAAK,cAAgBA,EAAQ,aAC7B,KAAK,eAAiBA,EAAQ,eAAiB,sBAC/C,KAAK,SAAWA,EAAQ,SAAY,OAAO,SAAS,OAAS,OAAO,SAAS,SAG7E,KAAK,eAAA,EAELkC,EAAkB,gBAAkB,KACtC,CAMQ,gBAAuB,CAC7B,GAAI,KAAK,gBAAiB,OAE1B,MAAMC,EAAY,SAAS,cAAc,uBAAuB,EAC1DC,EAAU,SAAS,cAAc,2BAA2B,EAC5DC,EAAgB,SAAS,cAAc,iCAAiC,EACxEC,EAAU,SAAS,cAAc,2BAA2B,EAC5DC,EAAQ,SAAS,cAAc,yBAAyB,EACxDC,EAAS,SAAS,cAAc,0BAA0B,EAC1DC,EAAkB,SAAS,cAAc,0BAA0B,EAEzE,KAAK,WAAa,CAChB,MAAO,SAAS,MAChB,cAAeN,GAAW,aAAa,MAAM,GAAK,KAClD,QAASC,GAAS,aAAa,SAAS,GAAK,KAC7C,cAAeC,GAAe,aAAa,SAAS,GAAK,KACzD,QAASC,GAAS,aAAa,SAAS,GAAK,KAC7C,MAAOC,GAAO,aAAa,SAAS,GAAK,KACzC,OAAQC,GAAQ,aAAa,SAAS,GAAK,KAC3C,gBAAiBC,GAAiB,aAAa,SAAS,GAAK,IAAA,EAG/D,KAAK,gBAAkB,EACzB,CAEQ,cAAcjC,EAAqC,CACzD,GAAI,OAAO,SAAa,IAAa,OAGrC,SAAS,iBAAiB,+BAA+B,EAAE,QAASkC,GAAOA,EAAG,QAAQ,EAEtF,MAAMC,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,aAAa,OAAQ,qBAAqB,EACjDA,EAAO,aAAa,eAAgB,QAAQ,EAE5CA,EAAO,YAAc,KAAK,UAAUnC,CAAI,EACxC,SAAS,KAAK,YAAYmC,CAAM,CAClC,CAEQ,SAASC,EAAkBC,EAAuB,CACxD,GAAI,SAAO,SAAa,KAExB,GAAID,EAAS,WAAW,KAAK,EAAG,CAE9B,IAAIF,EAAK,SAAS,cAAc,kBAAkBE,CAAQ,IAAI,EAC1DF,EACFA,EAAG,aAAa,UAAWG,CAAO,GAElCH,EAAK,SAAS,cAAc,MAAM,EAClCA,EAAG,aAAa,WAAYE,CAAQ,EACpCF,EAAG,aAAa,UAAWG,CAAO,EAClCH,EAAG,aAAa,eAAgB,IAAI,EACpC,SAAS,KAAK,YAAYA,CAAE,EAEhC,KAAO,CAEL,IAAIA,EAAK,SAAS,cAAc,cAAcE,CAAQ,IAAI,EACtDF,EACFA,EAAG,aAAa,UAAWG,CAAO,GAElCH,EAAK,SAAS,cAAc,MAAM,EAClCA,EAAG,aAAa,OAAQE,CAAQ,EAChCF,EAAG,aAAa,UAAWG,CAAO,EAClCH,EAAG,aAAa,eAAgB,MAAM,EACtC,SAAS,KAAK,YAAYA,CAAE,EAEhC,CACF,CAEQ,cAAc3D,EAAmB,CACvC,GAAI,OAAO,SAAa,IAAa,OAErC,IAAI2D,EAAK,SAAS,cAAc,uBAAuB,EACnDA,EACFA,EAAG,aAAa,OAAQ3D,CAAG,GAE3B2D,EAAK,SAAS,cAAc,MAAM,EAClCA,EAAG,aAAa,MAAO,WAAW,EAClCA,EAAG,aAAa,OAAQ3D,CAAG,EAC3B2D,EAAG,aAAa,eAAgB,WAAW,EAC3C,SAAS,KAAK,YAAYA,CAAE,EAEhC,CAEQ,oBAA2B,CAC7B,OAAO,SAAa,KACxB,SAAS,iBAAiB,gBAAgB,EAAE,QAASA,GAAOA,EAAG,QAAQ,CACzE,CAEQ,mBAA0B,CAChC,GAAI,SAAO,SAAa,KAAe,CAAC,KAAK,YAM7C,IAHA,SAAS,MAAQ,KAAK,WAAW,MAG7B,KAAK,WAAW,gBAAkB,KAAM,CAC1C,MAAMP,EAAY,SAAS,cAAc,uBAAuB,EAC5DA,GACFA,EAAU,aAAa,OAAQ,KAAK,WAAW,aAAa,CAEhE,CAGA,KAAK,qBAAqB,WAAY,KAAK,WAAW,OAAO,EAC7D,KAAK,qBAAqB,iBAAkB,KAAK,WAAW,aAAa,EACzE,KAAK,qBAAqB,WAAY,KAAK,WAAW,OAAO,EAC7D,KAAK,qBAAqB,SAAU,KAAK,WAAW,KAAK,EACzD,KAAK,qBAAqB,UAAW,KAAK,WAAW,MAAM,EAG3D,KAAK,iBAAiB,cAAe,KAAK,WAAW,eAAe,EAEpE,KAAK,gBAAkB,GACzB,CAEQ,qBAAqBS,EAAkBE,EAAsC,CACnF,MAAMJ,EAAK,SAAS,cAAc,kBAAkBE,CAAQ,IAAI,EAC5DE,IAAoB,MAAQJ,EAE9BA,EAAG,aAAa,UAAWI,CAAe,EACjCA,IAAoB,MAAQJ,GAAI,aAAa,cAAc,GAEpEA,EAAG,OAAA,CAEP,CAEQ,iBAAiBK,EAAcD,EAAsC,CAC3E,MAAMJ,EAAK,SAAS,cAAc,cAAcK,CAAI,IAAI,EACpDD,IAAoB,MAAQJ,EAC9BA,EAAG,aAAa,UAAWI,CAAe,EACjCA,IAAoB,MAAQJ,GAAI,aAAa,cAAc,GACpEA,EAAG,OAAA,CAEP,CAEQ,eAAeM,EAAiD,CACtE,MAAO,CACL,WAAY,qBACZ,QAAS,WACT,gBAAiBA,EAAO,IAAI,CAAC/C,EAAOgD,IAAM,CACxC,MAAMC,EAAOjD,EAAM,MAAQ+B,EAAa/B,EAAM,IAAI,EAClD,MAAO,CACL,QAAS,WACT,SAAUgD,EAAI,EACd,IAAK,GAAG,KAAK,QAAQ,MAAMhD,EAAM,EAAE,IAAIiD,CAAI,EAAA,CAE/C,CAAC,CAAA,CAEL,CAEQ,gBAAgB9F,EAAcyE,EAAqBqB,EAAsB,CAC/E,MAAMC,EAAS/F,IAAS,QAAU,IAAMA,IAAS,SAAW,IAAM,IAClE,MAAO,GAAG,KAAK,QAAQ,IAAI+F,CAAM,IAAItB,CAAE,IAAIqB,CAAI,EACjD,CAUA,eACEF,EACA9C,EACM,CAEN,GADI,OAAO,SAAa,KACpB,KAAK,SAAW,YAAa,OAG7BA,IACF,KAAK,WAAaA,EAAM,MAAQ,KAAK,WACrC,KAAK,cAAgBA,EAAM,SAAWA,EAAM,yBAA2B,KAAK,cACxEA,EAAM,WACR,KAAK,eAAiBA,EAAM,WAKhC,KAAK,QAAU8C,EAGf,MAAMI,EAAW,KAAK,eAAeJ,CAAM,EAC3C,KAAK,cAAcI,CAAQ,EAE3B,KAAK,OAAS,SAChB,CAOA,iBACEhG,EACAoD,EACAN,EACM,CAEN,GADI,OAAO,SAAa,KACpB,KAAK,SAAW,YAAa,OAGjC,KAAK,eAAA,EAGDA,IACF,KAAK,WAAaA,EAAM,MAAQ,KAAK,WACrC,KAAK,cAAgBA,EAAM,SAAWA,EAAM,yBAA2B,KAAK,cACxEA,EAAM,WACR,KAAK,eAAiBA,EAAM,WAIhC,IAAImD,EAAyC,KACzCC,EAAa,GACbC,EAAoB,GACpBC,EAAc,GACdC,EAAY,GAEhB,GAAIrG,IAAS,QAAS,CACpB,MAAM8F,EAAO1C,EAAK,MAAQwB,EAAaxB,EAAK,MAAQ,EAAE,EACtDiD,EAAY,KAAK,gBAAgB,QAASjD,EAAK,IAAMA,EAAK,SAAW,GAAI0C,CAAI,EAE7EG,EAAStD,EAAiB,CACxB,MAAOS,EACP,MAAO,CACL,KAAM,KAAK,WACX,wBAAyB,KAAK,aAAA,EAEhC,cAAe,KAAK,eACpB,SAAUiD,CAAA,CACX,EAEDH,EAAa9C,EAAK,MAAQ,GAC1B+C,EAAoB/C,EAAK,aAAeA,EAAK,cAAgB,GAC7DgD,EAAchD,EAAK,OAASA,EAAK,YAAcA,EAAK,WAAa,EACnE,SAAWpD,IAAS,SAAU,CAC5B,MAAM8F,EAAO1C,EAAK,MAAQwB,EAAaxB,EAAK,OAAS,EAAE,EACvDiD,EAAY,KAAK,gBAAgB,SAAUjD,EAAK,IAAM,GAAI0C,CAAI,EAE9DG,EAASnC,EAAkB,CACzB,OAAQ,CACN,GAAGV,EACH,MAAO,CACL,KAAM,KAAK,WACX,wBAAyB,KAAK,aAAA,EAEhC,SAAU,KAAK,cAAA,EAEjB,aAAciD,CAAA,CACf,EAEDH,EAAa9C,EAAK,OAAS,GAC3B+C,EAAoB/C,EAAK,aAAeA,EAAK,cAAgB,GAC7DgD,EAAchD,EAAK,OAAS,EAC9B,SAAWpD,IAAS,aAAc,CAChC,MAAM8F,EAAO1C,EAAK,MAAQwB,EAAaxB,EAAK,iBAAmB,EAAE,EACjEiD,EAAY,KAAK,gBAAgB,aAAcjD,EAAK,IAAM,GAAI0C,CAAI,EAElEG,EAAS5B,EAAsB,CAC7B,WAAYjB,EACZ,aAAciD,EACd,gBAAiB,KAAK,cAAA,CACvB,EAEDH,EAAa9C,EAAK,iBAAmB,GACrC+C,EAAoB/C,EAAK,aAAeA,EAAK,SAAW,GACxDgD,EAAchD,EAAK,YAAc,EACnC,CAGI6C,GACF,KAAK,cAAcA,CAAM,EAIvBI,GACF,KAAK,cAAcA,CAAS,EAI1BH,GACF,KAAK,SAAS,WAAYA,CAAU,EAElCC,GACF,KAAK,SAAS,iBAAkBA,CAAiB,EAE/CC,GACF,KAAK,SAAS,WAAYA,CAAW,EAEnCC,GACF,KAAK,SAAS,SAAUA,CAAS,EAEnC,KAAK,SAAS,UAAW,SAAS,EAGlC,KAAK,SAAS,eAAgB,qBAAqB,EAG/CF,GACF,KAAK,SAAS,cAAeA,CAAiB,EAI5CD,IACF,SAAS,MAAQ,GAAGA,CAAU,MAAM,KAAK,UAAU,IAGrD,KAAK,OAAS,QAChB,CAMA,iBAAwB,CACtB,GAAI,SAAO,SAAa,MACpB,KAAK,SAAW,YASpB,IANA,KAAK,kBAAA,EAGL,KAAK,mBAAA,EAGD,KAAK,QAAQ,OAAS,EAAG,CAC3B,MAAMF,EAAW,KAAK,eAAe,KAAK,OAAO,EACjD,KAAK,cAAcA,CAAQ,CAC7B,CAGA,KAAK,eAAA,EAEL,KAAK,OAAS,UAChB,CAMA,SAAgB,CACV,OAAO,SAAa,KACpB,KAAK,SAAW,cAEpB,KAAK,mBAAA,EACL,KAAK,kBAAA,EAEDlB,EAAkB,kBAAoB,OACxCA,EAAkB,gBAAkB,MAGtC,KAAK,OAAS,YAChB,CACF,EAvYEA,EAAO,gBAA4C,KAF9C,IAAMwB,EAANxB"}
1
+ {"version":3,"file":"seo.cjs","sources":["../../src/lib/seo/helpers.ts","../../src/lib/seo/buildEventJsonLd.ts","../../src/lib/seo/buildSeriesJsonLd.ts","../../src/lib/seo/buildCollectionJsonLd.ts","../../src/lib/utils/textUtils.ts","../../src/lib/seo/HostSeoController.ts"],"sourcesContent":["/**\r\n * Shared helper utilities for JSON-LD builders.\r\n *\r\n * All functions are pure (no DOM, no Svelte, no side effects).\r\n * Extracted verbatim from EventStructuredData.svelte.\r\n */\r\n\r\nimport type { EventTicketInput, EventPerformerInput } from './types';\r\n\r\n/**\r\n * Format datetime to ISO-8601 with timezone offset.\r\n * Google requires: \"2025-07-21T19:00:00-05:00\"\r\n *\r\n * Extracted from EventStructuredData.svelte lines 25-68.\r\n */\r\nexport function formatDateTimeWithOffset(\r\n isoString: string | null | undefined,\r\n tz: string,\r\n): string | null {\r\n if (!isoString) return null;\r\n\r\n const date = new Date(isoString);\r\n if (isNaN(date.getTime())) return null;\r\n\r\n // Get the offset in the target timezone\r\n const formatter = new Intl.DateTimeFormat('en-US', {\r\n timeZone: tz,\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n hour12: false,\r\n timeZoneName: 'shortOffset',\r\n });\r\n\r\n const parts = formatter.formatToParts(date);\r\n const get = (type: string) => parts.find((p) => p.type === type)?.value || '';\r\n\r\n const year = get('year');\r\n const month = get('month');\r\n const day = get('day');\r\n const hour = get('hour');\r\n const minute = get('minute');\r\n const second = get('second');\r\n const tzOffset = get('timeZoneName'); // e.g., \"GMT-5\" or \"GMT+2\"\r\n\r\n // Convert \"GMT-5\" to \"-05:00\" format\r\n let offset = '+00:00';\r\n if (tzOffset) {\r\n const match = tzOffset.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\r\n if (match) {\r\n const sign = match[1];\r\n const hours = match[2].padStart(2, '0');\r\n const mins = match[3] || '00';\r\n offset = `${sign}${hours}:${mins}`;\r\n }\r\n }\r\n\r\n return `${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`;\r\n}\r\n\r\n/**\r\n * Parse address string into PostalAddress components.\r\n * Input: \"7702 Santa Monica Blvd, West Hollywood, CA 90046\"\r\n * Output: { @type, streetAddress, addressLocality, addressRegion, postalCode, addressCountry }\r\n *\r\n * Extracted from EventStructuredData.svelte lines 75-111.\r\n */\r\nexport function parseAddressToPostal(\r\n addressStr: string | null | undefined,\r\n): Record<string, string> | null {\r\n if (!addressStr) return null;\r\n\r\n const parts = addressStr.split(',').map((p) => p.trim());\r\n\r\n if (parts.length < 2) {\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: addressStr,\r\n addressCountry: 'US',\r\n };\r\n }\r\n\r\n // Check if last part is \"State ZIP\" (e.g., \"CA 90046\")\r\n const lastPart = parts[parts.length - 1];\r\n const stateZipMatch = lastPart.match(/^([A-Z]{2})\\s+(\\d{5}(?:-\\d{4})?)$/);\r\n\r\n if (stateZipMatch && parts.length >= 3) {\r\n // Standard US address format\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: parts.slice(0, -2).join(', '),\r\n addressLocality: parts[parts.length - 2],\r\n addressRegion: stateZipMatch[1],\r\n postalCode: stateZipMatch[2],\r\n addressCountry: 'US',\r\n };\r\n }\r\n\r\n // Fallback for non-standard formats\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: parts[0],\r\n addressLocality: parts.slice(1).join(', '),\r\n addressCountry: 'US',\r\n };\r\n}\r\n\r\n/**\r\n * Map event status to schema.org EventStatusType.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 116-132.\r\n */\r\nexport function getEventStatus(rawStatus: string | null | undefined): string {\r\n if (!rawStatus) return 'https://schema.org/EventScheduled';\r\n\r\n const status = rawStatus.toLowerCase();\r\n\r\n if (status === 'cancelled' || status === 'canceled') {\r\n return 'https://schema.org/EventCancelled';\r\n }\r\n if (status === 'postponed') {\r\n return 'https://schema.org/EventPostponed';\r\n }\r\n if (status === 'rescheduled') {\r\n return 'https://schema.org/EventRescheduled';\r\n }\r\n\r\n return 'https://schema.org/EventScheduled';\r\n}\r\n\r\n/**\r\n * Map ticket availability to schema.org ItemAvailability.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 137-157.\r\n */\r\nexport function getTicketAvailability(ticket: EventTicketInput): string {\r\n const remaining =\r\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\r\n\r\n if (remaining === 0 || ticket.soldOut) {\r\n return 'https://schema.org/SoldOut';\r\n }\r\n\r\n const now = new Date();\r\n const salesBegin =\r\n ticket.salesBegin || ticket.salesStart || ticket.saleBegin || ticket.onSaleStart;\r\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\r\n\r\n if (salesBegin && new Date(salesBegin) > now) {\r\n return 'https://schema.org/PreOrder';\r\n }\r\n\r\n if (salesEnd && new Date(salesEnd) < now) {\r\n return 'https://schema.org/SoldOut';\r\n }\r\n\r\n return 'https://schema.org/InStock';\r\n}\r\n\r\n/**\r\n * Build offers from available tickets.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 162-208.\r\n */\r\nexport function buildOffers(\r\n tickets: EventTicketInput[] | null | undefined,\r\n url?: string,\r\n): Record<string, unknown> | null {\r\n if (!tickets || tickets.length === 0) return null;\r\n\r\n // Filter to public tickets only (exclude door-only and hidden)\r\n const publicTickets = tickets.filter((t) => {\r\n const visibility = t.visibility ?? 0;\r\n const isVisible = visibility === 0 || visibility === 1;\r\n const isPublic = t.salesChannel !== 2;\r\n return isVisible && isPublic;\r\n });\r\n\r\n if (publicTickets.length === 0) return null;\r\n\r\n // Find lowest and highest price\r\n const prices = publicTickets\r\n .map((t) => parseFloat(String(t.price ?? 0)))\r\n .filter((p) => !isNaN(p));\r\n\r\n if (prices.length === 0) return null;\r\n\r\n const lowPrice = Math.min(...prices);\r\n const highPrice = Math.max(...prices);\r\n\r\n // Use AggregateOffer when there are multiple price points\r\n if (lowPrice !== highPrice) {\r\n return {\r\n '@type': 'AggregateOffer',\r\n lowPrice: lowPrice,\r\n highPrice: highPrice,\r\n priceCurrency: 'USD',\r\n availability: getTicketAvailability(publicTickets[0]),\r\n url: url || undefined,\r\n validFrom:\r\n publicTickets[0]?.salesBegin || publicTickets[0]?.salesStart || undefined,\r\n };\r\n }\r\n\r\n // Single price point - use Offer\r\n const ticket = publicTickets[0];\r\n return {\r\n '@type': 'Offer',\r\n price: lowPrice,\r\n priceCurrency: 'USD',\r\n availability: getTicketAvailability(ticket),\r\n url: url || undefined,\r\n validFrom: ticket?.salesBegin || ticket?.salesStart || undefined,\r\n };\r\n}\r\n\r\n/**\r\n * Build performer array from event performers.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 213-237.\r\n */\r\nexport function buildPerformers(\r\n performers: EventPerformerInput[] | null | undefined,\r\n): Array<Record<string, string>> | null {\r\n if (!performers || performers.length === 0) return null;\r\n\r\n // Filter to confirmed, visible performers\r\n const confirmed = performers.filter((p) => {\r\n const isConfirmed = p.acceptedState === 2;\r\n const isNotHidden = !p.shouldBeHidden;\r\n const profile = p.RosterPerformer?.User?.performerProfile;\r\n const hasName = profile?.firstName || profile?.lastName || profile?.displayName;\r\n return isConfirmed && isNotHidden && hasName;\r\n });\r\n\r\n if (confirmed.length === 0) return null;\r\n\r\n return confirmed.map((p) => {\r\n const profile = p.RosterPerformer?.User?.performerProfile;\r\n const name =\r\n profile?.displayName ||\r\n [profile?.firstName, profile?.lastName].filter(Boolean).join(' ');\r\n\r\n return {\r\n '@type': 'PerformingGroup',\r\n name: name,\r\n };\r\n });\r\n}\r\n","/**\r\n * Pure function that builds Event JSON-LD structured data for Google Events rich results.\r\n *\r\n * Extracted from EventStructuredData.svelte (lines 242-313).\r\n * Produces identical output to the original $derived.by block.\r\n */\r\n\r\nimport type { EventJsonLdInput, VenueInput, OrganizerInput } from './types';\r\nimport {\r\n formatDateTimeWithOffset,\r\n parseAddressToPostal,\r\n getEventStatus,\r\n buildOffers,\r\n buildPerformers,\r\n} from './helpers';\r\n\r\nexport interface BuildEventJsonLdOptions {\r\n event: EventJsonLdInput;\r\n venue: VenueInput;\r\n venueTimeZone?: string;\r\n eventUrl?: string;\r\n organizer?: OrganizerInput;\r\n}\r\n\r\nexport function buildEventJsonLd(\r\n options: BuildEventJsonLdOptions,\r\n): Record<string, unknown> | null {\r\n const {\r\n event,\r\n venue,\r\n venueTimeZone = 'America/Los_Angeles',\r\n eventUrl,\r\n organizer,\r\n } = options;\r\n\r\n if (!event) return null;\r\n\r\n const address = venue?.address || venue?.googleLocationNameCache || '';\r\n const startDate = formatDateTimeWithOffset(event.startDateTime, venueTimeZone);\r\n\r\n // Skip if we don't have required fields\r\n if (!event.name || !startDate || !venue?.name) {\r\n return null;\r\n }\r\n\r\n const data: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': 'Event',\r\n name: event.name,\r\n startDate: startDate,\r\n eventStatus: getEventStatus(event.status),\r\n location: {\r\n '@type': 'Place',\r\n name: venue.name,\r\n address: parseAddressToPostal(address),\r\n },\r\n };\r\n\r\n // Add endDate if available\r\n const endDate = formatDateTimeWithOffset(event.endDateTime, venueTimeZone);\r\n if (endDate) {\r\n data.endDate = endDate;\r\n }\r\n\r\n // Add description\r\n const description = event.description || event.eventSummary;\r\n if (description) {\r\n data.description = description;\r\n }\r\n\r\n // Add image - Google recommends multiple aspect ratios\r\n const imageUrl = event.image || event.coverImage || event.ShowImage;\r\n if (imageUrl) {\r\n data.image = [imageUrl];\r\n }\r\n\r\n // Add offers from tickets\r\n const offers = buildOffers(event.availableTickets, eventUrl);\r\n if (offers) {\r\n data.offers = offers;\r\n }\r\n\r\n // Add performers\r\n const performers = buildPerformers(event.performers);\r\n if (performers && performers.length > 0) {\r\n data.performer = performers;\r\n }\r\n\r\n // Add organizer if provided\r\n if (organizer?.name) {\r\n const org: Record<string, string> = {\r\n '@type': 'Organization',\r\n name: organizer.name,\r\n };\r\n if (organizer.url) {\r\n org.url = organizer.url;\r\n }\r\n data.organizer = org;\r\n }\r\n\r\n // Add event URL if provided\r\n if (eventUrl) {\r\n data.url = eventUrl;\r\n }\r\n\r\n return data;\r\n}\r\n","/**\r\n * Pure function that builds EventSeries JSON-LD structured data.\r\n *\r\n * Series use schema.org EventSeries type with subEvent array containing\r\n * individual Event blocks for each occurrence. Each subEvent has its own\r\n * offers built from the simplified series ticket DTOs (different shape\r\n * from the event builder's tickets).\r\n *\r\n * Google Rich Results Test does NOT support EventSeries directly, but\r\n * the individual subEvent Event blocks can qualify for rich results.\r\n */\r\n\r\nimport type { SeriesJsonLdInput } from './types';\r\nimport { formatDateTimeWithOffset, parseAddressToPostal, getEventStatus } from './helpers';\r\n\r\nexport interface BuildSeriesJsonLdOptions {\r\n series: SeriesJsonLdInput;\r\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\" -- used to build subEvent @id URLs\r\n}\r\n\r\n/**\r\n * Map a series ticket status string to schema.org ItemAvailability.\r\n *\r\n * Series tickets use a simplified { name, price, status } shape from the\r\n * backend (SeriesTicketDTO), unlike event tickets which have visibility,\r\n * salesChannel, remainingCapacity, etc.\r\n */\r\nfunction mapSeriesTicketAvailability(status: string): string {\r\n switch (status) {\r\n case 'available':\r\n return 'https://schema.org/InStock';\r\n case 'sold_out':\r\n return 'https://schema.org/SoldOut';\r\n case 'coming_soon':\r\n return 'https://schema.org/PreOrder';\r\n case 'ended':\r\n return 'https://schema.org/SoldOut';\r\n default:\r\n return 'https://schema.org/InStock';\r\n }\r\n}\r\n\r\n/**\r\n * Build offers from series occurrence tickets.\r\n *\r\n * Series tickets have a simpler shape than event tickets: { name, price, status }.\r\n * Uses AggregateOffer for multiple price points, Offer for single price.\r\n */\r\nfunction buildSeriesOccurrenceOffers(\r\n tickets: Array<{ name: string; price: number; status: string }> | undefined,\r\n occurrenceStatus: string | undefined,\r\n): Record<string, unknown> | null {\r\n if (!tickets || tickets.length === 0) return null;\r\n\r\n // Filter out sold_out and ended tickets for offer building\r\n const activeTickets = tickets.filter(\r\n (t) => t.status !== 'sold_out' && t.status !== 'ended',\r\n );\r\n\r\n // If all tickets are sold out / ended, report SoldOut with the price range\r\n if (activeTickets.length === 0) {\r\n const prices = tickets.map((t) => t.price).filter((p) => !isNaN(p));\r\n if (prices.length === 0) return null;\r\n\r\n const lowPrice = Math.min(...prices);\r\n const highPrice = Math.max(...prices);\r\n\r\n if (lowPrice !== highPrice) {\r\n return {\r\n '@type': 'AggregateOffer',\r\n lowPrice,\r\n highPrice,\r\n priceCurrency: 'USD',\r\n availability: 'https://schema.org/SoldOut',\r\n };\r\n }\r\n\r\n return {\r\n '@type': 'Offer',\r\n price: lowPrice,\r\n priceCurrency: 'USD',\r\n availability: 'https://schema.org/SoldOut',\r\n };\r\n }\r\n\r\n const prices = activeTickets.map((t) => t.price).filter((p) => !isNaN(p));\r\n if (prices.length === 0) return null;\r\n\r\n const lowPrice = Math.min(...prices);\r\n const highPrice = Math.max(...prices);\r\n\r\n if (lowPrice !== highPrice) {\r\n return {\r\n '@type': 'AggregateOffer',\r\n lowPrice,\r\n highPrice,\r\n priceCurrency: 'USD',\r\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\r\n };\r\n }\r\n\r\n return {\r\n '@type': 'Offer',\r\n price: lowPrice,\r\n priceCurrency: 'USD',\r\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\r\n };\r\n}\r\n\r\nexport function buildSeriesJsonLd(\r\n options: BuildSeriesJsonLdOptions,\r\n): Record<string, unknown> | null {\r\n const { series, baseEventUrl } = options;\r\n\r\n // Required fields\r\n if (!series.title) return null;\r\n if (!series.occurrences || series.occurrences.length === 0) return null;\r\n if (!series.venue?.name) return null;\r\n\r\n // Build location\r\n const address = parseAddressToPostal(series.venue.googleLocationNameCache || '');\r\n const location: Record<string, unknown> = {\r\n '@type': 'Place',\r\n name: series.venue.name,\r\n };\r\n if (address) {\r\n location.address = address;\r\n }\r\n\r\n // Build parent EventSeries\r\n const data: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': 'EventSeries',\r\n name: series.title,\r\n location,\r\n };\r\n\r\n // Description: prefer description, fall back to eventSummary\r\n const description = series.description || series.eventSummary;\r\n if (description) {\r\n data.description = description;\r\n }\r\n\r\n // Image\r\n if (series.image) {\r\n data.image = [series.image];\r\n }\r\n\r\n // Performers: flat displayName array (different from event builder's nested shape)\r\n const performers = series.performers?.filter((p) => p.displayName);\r\n if (performers && performers.length > 0) {\r\n data.performer = performers.map((p) => ({\r\n '@type': 'PerformingGroup',\r\n name: p.displayName,\r\n }));\r\n }\r\n\r\n // SubEvents: build Event blocks for each occurrence\r\n const subEvents: Record<string, unknown>[] = [];\r\n\r\n for (const occ of series.occurrences) {\r\n const startDate = formatDateTimeWithOffset(occ.startDateTime, series.timeZone);\r\n if (!startDate) continue; // Skip occurrences without valid startDate\r\n\r\n const subEvent: Record<string, unknown> = {\r\n '@type': 'Event',\r\n name: occ.title,\r\n startDate,\r\n eventStatus: getEventStatus(occ.status),\r\n location,\r\n };\r\n\r\n // @id from baseEventUrl\r\n if (baseEventUrl) {\r\n subEvent['@id'] = `${baseEventUrl}/${occ.id}-${occ.slug}`;\r\n }\r\n\r\n // endDate\r\n const endDate = formatDateTimeWithOffset(occ.endDateTime, series.timeZone);\r\n if (endDate) {\r\n subEvent.endDate = endDate;\r\n }\r\n\r\n // Offers from simplified ticket DTOs\r\n const offers = buildSeriesOccurrenceOffers(occ.tickets, occ.status);\r\n if (offers) {\r\n subEvent.offers = offers;\r\n }\r\n\r\n subEvents.push(subEvent);\r\n }\r\n\r\n // If all occurrences were skipped, return null\r\n if (subEvents.length === 0) return null;\r\n\r\n data.subEvent = subEvents;\r\n\r\n return data;\r\n}\r\n","/**\r\n * Pure function that builds Festival JSON-LD structured data for event collections.\r\n *\r\n * Collections use schema.org Festival type with subEvent array containing\r\n * individual Event blocks. Location is included only when all events share\r\n * the same venue. Date range is included only when dateRange is not null.\r\n *\r\n * Google Rich Results Test does NOT support Festival directly, but\r\n * the individual subEvent Event blocks can qualify for rich results.\r\n */\r\n\r\nimport type { CollectionJsonLdInput } from './types';\r\nimport { formatDateTimeWithOffset, getEventStatus } from './helpers';\r\n\r\nexport interface BuildCollectionJsonLdOptions {\r\n collection: CollectionJsonLdInput;\r\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\"\r\n defaultTimeZone?: string; // fallback timezone, defaults to 'America/Los_Angeles'\r\n}\r\n\r\nexport function buildCollectionJsonLd(\r\n options: BuildCollectionJsonLdOptions,\r\n): Record<string, unknown> | null {\r\n const {\r\n collection,\r\n baseEventUrl,\r\n defaultTimeZone = 'America/Los_Angeles',\r\n } = options;\r\n\r\n // Required fields\r\n if (!collection.collectionTitle) return null;\r\n if (!collection.events || collection.events.length === 0) return null;\r\n\r\n // Build parent Festival\r\n const data: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': 'Festival',\r\n name: collection.collectionTitle,\r\n };\r\n\r\n // Description: prefer description, fall back to summary\r\n const description = collection.description || collection.summary;\r\n if (description) {\r\n data.description = description;\r\n }\r\n\r\n // Cover image\r\n if (collection.coverImage) {\r\n data.image = [collection.coverImage];\r\n }\r\n\r\n // Date range: include only when dateRange is not null and has values\r\n if (collection.dateRange) {\r\n const startDate = formatDateTimeWithOffset(\r\n collection.dateRange.startDate,\r\n defaultTimeZone,\r\n );\r\n const endDate = formatDateTimeWithOffset(\r\n collection.dateRange.endDate,\r\n defaultTimeZone,\r\n );\r\n if (startDate) {\r\n data.startDate = startDate;\r\n }\r\n if (endDate) {\r\n data.endDate = endDate;\r\n }\r\n }\r\n\r\n // Location: include only when all events share the same venue\r\n const uniqueVenueIds = new Set(\r\n collection.events\r\n .map((e) => e.venueId)\r\n .filter((id): id is number => id !== undefined && id !== 0),\r\n );\r\n\r\n if (uniqueVenueIds.size === 1) {\r\n // All events share the same venue; use the first event's venueName\r\n const venueEvent = collection.events.find(\r\n (e) => e.venueId && e.venueId !== 0 && e.venueName,\r\n );\r\n if (venueEvent?.venueName) {\r\n data.location = {\r\n '@type': 'Place',\r\n name: venueEvent.venueName,\r\n };\r\n }\r\n }\r\n // If multiple venueIds or zero venueIds: OMIT location entirely\r\n\r\n // SubEvents: build Event blocks for each collection event\r\n const subEvents: Record<string, unknown>[] = [];\r\n\r\n for (const event of collection.events) {\r\n const startDate = formatDateTimeWithOffset(event.startDateTime, defaultTimeZone);\r\n if (!startDate) continue; // Skip events without valid startDate\r\n\r\n const subEvent: Record<string, unknown> = {\r\n '@type': 'Event',\r\n name: event.title,\r\n startDate,\r\n eventStatus: getEventStatus(event.status),\r\n };\r\n\r\n // @id from baseEventUrl\r\n if (baseEventUrl && event.slug) {\r\n subEvent['@id'] = `${baseEventUrl}/${event.id}-${event.slug}`;\r\n }\r\n\r\n // endDate\r\n const endDate = formatDateTimeWithOffset(event.endDateTime, defaultTimeZone);\r\n if (endDate) {\r\n subEvent.endDate = endDate;\r\n }\r\n\r\n // Image\r\n if (event.image) {\r\n subEvent.image = [event.image];\r\n }\r\n\r\n subEvents.push(subEvent);\r\n }\r\n\r\n // If all events were skipped, return null\r\n if (subEvents.length === 0) return null;\r\n\r\n data.subEvent = subEvents;\r\n\r\n return data;\r\n}\r\n","/**\r\n * Text utility functions: classNames, truncateTitle, generateSlug, validateNegativeNumber\r\n */\r\n\r\n/**\r\n * Combine class names, filtering out falsy values\r\n */\r\nexport function classNames(...classes: (string | false | null | undefined)[]): string {\r\n return classes.filter(Boolean).join(\" \");\r\n}\r\n\r\n/**\r\n * Truncate a title to a maximum length, appending \"...\" if truncated\r\n */\r\nexport function truncateTitle(title: string, maxLength: number): string {\r\n if (title.length > maxLength) {\r\n return title.slice(0, maxLength) + \"...\";\r\n }\r\n return title;\r\n}\r\n\r\n/**\r\n * Generate a URL-friendly slug from a string\r\n */\r\nexport function generateSlug(text: string): string {\r\n if (!text) return \"\";\r\n\r\n return text\r\n .toLowerCase()\r\n .trim()\r\n .replace(/[^\\w\\s-]/g, \"\")\r\n .replace(/\\s+/g, \"-\")\r\n .replace(/--+/g, \"-\")\r\n .replace(/^-+|-+$/g, \"\");\r\n}\r\n\r\n/**\r\n * Validate that a value is a positive number\r\n * Returns an error message string, or empty string if valid\r\n */\r\nexport function validateNegativeNumber(value: string | number | null | undefined): string {\r\n if (value === null || value === undefined || value === \"\") {\r\n return \"Must be greater than zero.\";\r\n }\r\n\r\n const stringValue = String(value).trim();\r\n\r\n if (\r\n stringValue.includes(\"--\") ||\r\n stringValue.includes(\"++\") ||\r\n stringValue.match(/^-+$/) ||\r\n stringValue.match(/^\\++$/)\r\n ) {\r\n return \"Please enter a valid number\";\r\n }\r\n\r\n if (stringValue.match(/^-?\\d+-$/)) {\r\n return \"Please enter a valid number\";\r\n }\r\n\r\n if ((stringValue.match(/\\./g) || []).length > 1) {\r\n return \"Please enter a valid number\";\r\n }\r\n\r\n const numValue = Number(value);\r\n if (isNaN(numValue)) {\r\n return \"Please enter a valid number\";\r\n }\r\n\r\n if (numValue < 0 || numValue === 0) {\r\n return \"Price must be greater than zero.\";\r\n }\r\n\r\n return \"\";\r\n}\r\n","/**\r\n * HostSeoController -- manages JSON-LD structured data, canonical URLs,\r\n * OG tags, meta descriptions, and page title injection into venue pages'\r\n * <head> when the Micdrop calendar widget is embedded.\r\n *\r\n * Follows a save-replace-restore pattern to be a \"good citizen\" on venue pages:\r\n * idle -> listing -> entity -> listing -> ... -> destroyed\r\n *\r\n * Uses textContent (never innerHTML) for JSON-LD script tags to prevent XSS.\r\n */\r\n\r\nimport type { HostSeoControllerOptions } from './types';\r\nimport { buildEventJsonLd } from './buildEventJsonLd';\r\nimport { buildSeriesJsonLd } from './buildSeriesJsonLd';\r\nimport { buildCollectionJsonLd } from './buildCollectionJsonLd';\r\nimport { generateSlug } from '$lib/utils/textUtils';\r\n\r\ntype ControllerState = 'idle' | 'listing' | 'entity' | 'destroyed';\r\n\r\ninterface SavedOriginals {\r\n title: string;\r\n canonicalHref: string | null;\r\n ogTitle: string | null;\r\n ogDescription: string | null;\r\n ogImage: string | null;\r\n ogUrl: string | null;\r\n ogType: string | null;\r\n metaDescription: string | null;\r\n}\r\n\r\n// Minimal event shape for listing mode\r\ninterface ListingEvent {\r\n id: number | string;\r\n name: string;\r\n slug?: string;\r\n startDateTime?: string;\r\n image?: string;\r\n}\r\n\r\n// Venue data passed at runtime\r\ninterface VenueData {\r\n name: string;\r\n address?: string;\r\n googleLocationNameCache?: string;\r\n timeZone?: string;\r\n}\r\n\r\nexport class HostSeoController {\r\n /** Static guard: only one instance active at a time */\r\n static _activeInstance: HostSeoController | null = null;\r\n\r\n private _state: ControllerState = 'idle';\r\n private _venueName: string;\r\n private _venueAddress: string | undefined;\r\n private _venueTimeZone: string;\r\n private _baseUrl: string;\r\n private _originals: SavedOriginals | null = null;\r\n private _originalsSaved = false;\r\n private _events: ListingEvent[] = [];\r\n\r\n constructor(options: HostSeoControllerOptions) {\r\n // SSR guard\r\n if (typeof document === 'undefined') return;\r\n\r\n // If another instance exists, destroy it first\r\n if (HostSeoController._activeInstance && HostSeoController._activeInstance !== this) {\r\n HostSeoController._activeInstance.destroy();\r\n }\r\n\r\n this._venueName = options.venueName;\r\n this._venueAddress = options.venueAddress;\r\n this._venueTimeZone = options.venueTimeZone || 'America/Los_Angeles';\r\n this._baseUrl = options.baseUrl || (window.location.origin + window.location.pathname);\r\n\r\n // Save originals immediately\r\n this._saveOriginals();\r\n\r\n HostSeoController._activeInstance = this;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Private helpers\r\n // ---------------------------------------------------------------------------\r\n\r\n private _saveOriginals(): void {\r\n if (this._originalsSaved) return;\r\n\r\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n const ogTitle = document.querySelector('meta[property=\"og:title\"]') as HTMLMetaElement | null;\r\n const ogDescription = document.querySelector('meta[property=\"og:description\"]') as HTMLMetaElement | null;\r\n const ogImage = document.querySelector('meta[property=\"og:image\"]') as HTMLMetaElement | null;\r\n const ogUrl = document.querySelector('meta[property=\"og:url\"]') as HTMLMetaElement | null;\r\n const ogType = document.querySelector('meta[property=\"og:type\"]') as HTMLMetaElement | null;\r\n const metaDescription = document.querySelector('meta[name=\"description\"]') as HTMLMetaElement | null;\r\n\r\n this._originals = {\r\n title: document.title,\r\n canonicalHref: canonical?.getAttribute('href') ?? null,\r\n ogTitle: ogTitle?.getAttribute('content') ?? null,\r\n ogDescription: ogDescription?.getAttribute('content') ?? null,\r\n ogImage: ogImage?.getAttribute('content') ?? null,\r\n ogUrl: ogUrl?.getAttribute('content') ?? null,\r\n ogType: ogType?.getAttribute('content') ?? null,\r\n metaDescription: metaDescription?.getAttribute('content') ?? null,\r\n };\r\n\r\n this._originalsSaved = true;\r\n }\r\n\r\n private _injectJsonLd(data: Record<string, unknown>): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n // Remove any existing micdrop JSON-LD\r\n document.querySelectorAll('script[data-micdrop=\"jsonld\"]').forEach((el) => el.remove());\r\n\r\n const script = document.createElement('script');\r\n script.setAttribute('type', 'application/ld+json');\r\n script.setAttribute('data-micdrop', 'jsonld');\r\n // Use textContent (NOT innerHTML) to prevent XSS\r\n script.textContent = JSON.stringify(data);\r\n document.head.appendChild(script);\r\n }\r\n\r\n private _setMeta(property: string, content: string): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n if (property.startsWith('og:')) {\r\n // OG tags use property attribute\r\n let el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (el) {\r\n el.setAttribute('content', content);\r\n } else {\r\n el = document.createElement('meta');\r\n el.setAttribute('property', property);\r\n el.setAttribute('content', content);\r\n el.setAttribute('data-micdrop', 'og');\r\n document.head.appendChild(el);\r\n }\r\n } else {\r\n // Standard meta tags use name attribute (e.g., description)\r\n let el = document.querySelector(`meta[name=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (el) {\r\n el.setAttribute('content', content);\r\n } else {\r\n el = document.createElement('meta');\r\n el.setAttribute('name', property);\r\n el.setAttribute('content', content);\r\n el.setAttribute('data-micdrop', 'meta');\r\n document.head.appendChild(el);\r\n }\r\n }\r\n }\r\n\r\n private _setCanonical(url: string): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n let el = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n if (el) {\r\n el.setAttribute('href', url);\r\n } else {\r\n el = document.createElement('link');\r\n el.setAttribute('rel', 'canonical');\r\n el.setAttribute('href', url);\r\n el.setAttribute('data-micdrop', 'canonical');\r\n document.head.appendChild(el);\r\n }\r\n }\r\n\r\n private _removeAllInjected(): void {\r\n if (typeof document === 'undefined') return;\r\n document.querySelectorAll('[data-micdrop]').forEach((el) => el.remove());\r\n }\r\n\r\n private _restoreOriginals(): void {\r\n if (typeof document === 'undefined' || !this._originals) return;\r\n\r\n // Restore title\r\n document.title = this._originals.title;\r\n\r\n // Restore canonical\r\n if (this._originals.canonicalHref !== null) {\r\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n if (canonical) {\r\n canonical.setAttribute('href', this._originals.canonicalHref);\r\n }\r\n }\r\n\r\n // Restore OG tags\r\n this._restoreMetaProperty('og:title', this._originals.ogTitle);\r\n this._restoreMetaProperty('og:description', this._originals.ogDescription);\r\n this._restoreMetaProperty('og:image', this._originals.ogImage);\r\n this._restoreMetaProperty('og:url', this._originals.ogUrl);\r\n this._restoreMetaProperty('og:type', this._originals.ogType);\r\n\r\n // Restore meta description\r\n this._restoreMetaName('description', this._originals.metaDescription);\r\n\r\n this._originalsSaved = false;\r\n }\r\n\r\n private _restoreMetaProperty(property: string, originalContent: string | null): void {\r\n const el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (originalContent !== null && el) {\r\n // Had original value -- restore it\r\n el.setAttribute('content', originalContent);\r\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\r\n // We created it -- remove it\r\n el.remove();\r\n }\r\n }\r\n\r\n private _restoreMetaName(name: string, originalContent: string | null): void {\r\n const el = document.querySelector(`meta[name=\"${name}\"]`) as HTMLMetaElement | null;\r\n if (originalContent !== null && el) {\r\n el.setAttribute('content', originalContent);\r\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\r\n el.remove();\r\n }\r\n }\r\n\r\n private _buildItemList(events: ListingEvent[]): Record<string, unknown> {\r\n return {\r\n '@context': 'https://schema.org',\r\n '@type': 'ItemList',\r\n itemListElement: events.map((event, i) => {\r\n const slug = event.slug || generateSlug(event.name);\r\n return {\r\n '@type': 'ListItem',\r\n position: i + 1,\r\n url: `${this._baseUrl}?e=${event.id}-${slug}`,\r\n };\r\n }),\r\n };\r\n }\r\n\r\n private _buildEntityUrl(type: string, id: string | number, slug: string): string {\r\n const prefix = type === 'event' ? 'e' : type === 'series' ? 's' : 'c';\r\n return `${this._baseUrl}?${prefix}=${id}-${slug}`;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Public API\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Called when events finish loading from the API.\r\n * Injects an ItemList JSON-LD for the listing. Does not modify OG/canonical/title.\r\n */\r\n onEventsLoaded(\r\n events: ListingEvent[],\r\n venue?: VenueData,\r\n ): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Update venue info from parameter if provided\r\n if (venue) {\r\n this._venueName = venue.name || this._venueName;\r\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\r\n if (venue.timeZone) {\r\n this._venueTimeZone = venue.timeZone;\r\n }\r\n }\r\n\r\n // Store events for later use (re-injection on back to listing)\r\n this._events = events;\r\n\r\n // Build and inject ItemList JSON-LD\r\n const itemList = this._buildItemList(events);\r\n this._injectJsonLd(itemList);\r\n\r\n this._state = 'listing';\r\n }\r\n\r\n /**\r\n * Called when the user selects an event, series, or collection.\r\n * Replaces ItemList JSON-LD with entity-specific JSON-LD and updates\r\n * all head tags (canonical, OG, meta description, title).\r\n */\r\n onEntitySelected(\r\n type: 'event' | 'series' | 'collection',\r\n data: any,\r\n venue?: VenueData,\r\n ): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Save originals if not already saved\r\n this._saveOriginals();\r\n\r\n // Update venue info from parameter if provided\r\n if (venue) {\r\n this._venueName = venue.name || this._venueName;\r\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\r\n if (venue.timeZone) {\r\n this._venueTimeZone = venue.timeZone;\r\n }\r\n }\r\n\r\n let jsonLd: Record<string, unknown> | null = null;\r\n let entityName = '';\r\n let entityDescription = '';\r\n let entityImage = '';\r\n let entityUrl = '';\r\n\r\n if (type === 'event') {\r\n const slug = data.slug || generateSlug(data.name || '');\r\n entityUrl = this._buildEntityUrl('event', data.id || data.eventID || '', slug);\r\n\r\n jsonLd = buildEventJsonLd({\r\n event: data,\r\n venue: {\r\n name: this._venueName,\r\n googleLocationNameCache: this._venueAddress,\r\n },\r\n venueTimeZone: this._venueTimeZone,\r\n eventUrl: entityUrl,\r\n });\r\n\r\n entityName = data.name || '';\r\n entityDescription = data.description || data.eventSummary || '';\r\n entityImage = data.image || data.coverImage || data.ShowImage || '';\r\n } else if (type === 'series') {\r\n const slug = data.slug || generateSlug(data.title || '');\r\n entityUrl = this._buildEntityUrl('series', data.id || '', slug);\r\n\r\n jsonLd = buildSeriesJsonLd({\r\n series: {\r\n ...data,\r\n venue: {\r\n name: this._venueName,\r\n googleLocationNameCache: this._venueAddress,\r\n },\r\n timeZone: this._venueTimeZone,\r\n },\r\n baseEventUrl: entityUrl,\r\n });\r\n\r\n entityName = data.title || '';\r\n entityDescription = data.description || data.eventSummary || '';\r\n entityImage = data.image || '';\r\n } else if (type === 'collection') {\r\n const slug = data.slug || generateSlug(data.collectionTitle || '');\r\n entityUrl = this._buildEntityUrl('collection', data.id || '', slug);\r\n\r\n jsonLd = buildCollectionJsonLd({\r\n collection: data,\r\n baseEventUrl: entityUrl,\r\n defaultTimeZone: this._venueTimeZone,\r\n });\r\n\r\n entityName = data.collectionTitle || '';\r\n entityDescription = data.description || data.summary || '';\r\n entityImage = data.coverImage || '';\r\n }\r\n\r\n // Inject JSON-LD (replaces existing micdrop JSON-LD)\r\n if (jsonLd) {\r\n this._injectJsonLd(jsonLd);\r\n }\r\n\r\n // Update canonical URL\r\n if (entityUrl) {\r\n this._setCanonical(entityUrl);\r\n }\r\n\r\n // Update OG tags\r\n if (entityName) {\r\n this._setMeta('og:title', entityName);\r\n }\r\n if (entityDescription) {\r\n this._setMeta('og:description', entityDescription);\r\n }\r\n if (entityImage) {\r\n this._setMeta('og:image', entityImage);\r\n }\r\n if (entityUrl) {\r\n this._setMeta('og:url', entityUrl);\r\n }\r\n this._setMeta('og:type', 'website');\r\n\r\n // Update meta description\r\n if (entityDescription) {\r\n this._setMeta('description', entityDescription);\r\n }\r\n\r\n // Update page title\r\n if (entityName) {\r\n document.title = `${entityName} | ${this._venueName}`;\r\n }\r\n\r\n this._state = 'entity';\r\n }\r\n\r\n /**\r\n * Called when the user navigates back to the event listing.\r\n * Restores original head elements and re-injects ItemList JSON-LD.\r\n */\r\n onBackToListing(): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Restore all originals\r\n this._restoreOriginals();\r\n\r\n // Remove any remaining injected elements\r\n this._removeAllInjected();\r\n\r\n // Re-inject ItemList JSON-LD if events are available\r\n if (this._events.length > 0) {\r\n const itemList = this._buildItemList(this._events);\r\n this._injectJsonLd(itemList);\r\n }\r\n\r\n // Re-save originals since we just restored them\r\n this._saveOriginals();\r\n\r\n this._state = 'listing';\r\n }\r\n\r\n /**\r\n * Called when the widget is destroyed. Removes all injected elements\r\n * and restores originals. Clears the static active instance.\r\n */\r\n destroy(): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n this._removeAllInjected();\r\n this._restoreOriginals();\r\n\r\n if (HostSeoController._activeInstance === this) {\r\n HostSeoController._activeInstance = null;\r\n }\r\n\r\n this._state = 'destroyed';\r\n }\r\n}\r\n"],"names":["formatDateTimeWithOffset","isoString","tz","date","parts","get","type","p","year","month","day","hour","minute","second","tzOffset","offset","match","sign","hours","mins","parseAddressToPostal","addressStr","stateZipMatch","getEventStatus","rawStatus","status","getTicketAvailability","ticket","now","salesBegin","salesEnd","buildOffers","tickets","url","publicTickets","t","visibility","isVisible","isPublic","prices","lowPrice","highPrice","buildPerformers","performers","confirmed","isConfirmed","isNotHidden","profile","hasName","buildEventJsonLd","options","event","venue","venueTimeZone","eventUrl","organizer","address","startDate","data","endDate","description","imageUrl","offers","org","mapSeriesTicketAvailability","buildSeriesOccurrenceOffers","occurrenceStatus","activeTickets","buildSeriesJsonLd","series","baseEventUrl","location","subEvents","occ","subEvent","buildCollectionJsonLd","collection","defaultTimeZone","e","id","venueEvent","generateSlug","text","_HostSeoController","canonical","ogTitle","ogDescription","ogImage","ogUrl","ogType","metaDescription","el","script","property","content","originalContent","name","events","i","slug","prefix","itemList","jsonLd","entityName","entityDescription","entityImage","entityUrl","HostSeoController"],"mappings":"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"}
package/dist/seo/seo.mjs CHANGED
@@ -12,18 +12,18 @@ function p(s, e) {
12
12
  second: "2-digit",
13
13
  hour12: !1,
14
14
  timeZoneName: "shortOffset"
15
- }).formatToParts(t), o = (h) => r.find((y) => y.type === h)?.value || "", c = o("year"), l = o("month"), i = o("day"), a = o("hour"), u = o("minute"), m = o("second"), f = o("timeZoneName");
15
+ }).formatToParts(t), o = (f) => r.find((y) => y.type === f)?.value || "", c = o("year"), l = o("month"), i = o("day"), a = o("hour"), u = o("minute"), m = o("second"), g = o("timeZoneName");
16
16
  let d = "+00:00";
17
- if (f) {
18
- const h = f.match(/GMT([+-])(\d+)(?::(\d+))?/);
19
- if (h) {
20
- const y = h[1], D = h[2].padStart(2, "0"), E = h[3] || "00";
21
- d = `${y}${D}:${E}`;
17
+ if (g) {
18
+ const f = g.match(/GMT([+-])(\d+)(?::(\d+))?/);
19
+ if (f) {
20
+ const y = f[1], D = f[2].padStart(2, "0"), T = f[3] || "00";
21
+ d = `${y}${D}:${T}`;
22
22
  }
23
23
  }
24
24
  return `${c}-${l}-${i}T${a}:${u}:${m}${d}`;
25
25
  }
26
- function _(s) {
26
+ function N(s) {
27
27
  if (!s) return null;
28
28
  const e = s.split(",").map((r) => r.trim());
29
29
  if (e.length < 2)
@@ -47,18 +47,18 @@ function _(s) {
47
47
  addressCountry: "US"
48
48
  };
49
49
  }
50
- function b(s) {
50
+ function _(s) {
51
51
  if (!s) return "https://schema.org/EventScheduled";
52
52
  const e = s.toLowerCase();
53
53
  return e === "cancelled" || e === "canceled" ? "https://schema.org/EventCancelled" : e === "postponed" ? "https://schema.org/EventPostponed" : e === "rescheduled" ? "https://schema.org/EventRescheduled" : "https://schema.org/EventScheduled";
54
54
  }
55
- function A(s) {
55
+ function b(s) {
56
56
  if ((s.remainingCapacity ?? s.quantityRemaining ?? s.quantity) === 0 || s.soldOut)
57
57
  return "https://schema.org/SoldOut";
58
58
  const t = /* @__PURE__ */ new Date(), n = s.salesBegin || s.salesStart || s.saleBegin || s.onSaleStart, r = s.salesEnd || s.saleEnd || s.onSaleEnd;
59
59
  return n && new Date(n) > t ? "https://schema.org/PreOrder" : r && new Date(r) < t ? "https://schema.org/SoldOut" : "https://schema.org/InStock";
60
60
  }
61
- function T(s, e) {
61
+ function E(s, e) {
62
62
  if (!s || s.length === 0) return null;
63
63
  const t = s.filter((l) => {
64
64
  const i = l.visibility ?? 0, a = i === 0 || i === 1, u = l.salesChannel !== 2;
@@ -74,7 +74,7 @@ function T(s, e) {
74
74
  lowPrice: r,
75
75
  highPrice: o,
76
76
  priceCurrency: "USD",
77
- availability: A(t[0]),
77
+ availability: b(t[0]),
78
78
  url: e || void 0,
79
79
  validFrom: t[0]?.salesBegin || t[0]?.salesStart || void 0
80
80
  };
@@ -83,7 +83,7 @@ function T(s, e) {
83
83
  "@type": "Offer",
84
84
  price: r,
85
85
  priceCurrency: "USD",
86
- availability: A(c),
86
+ availability: b(c),
87
87
  url: e || void 0,
88
88
  validFrom: c?.salesBegin || c?.salesStart || void 0
89
89
  };
@@ -119,12 +119,11 @@ function P(s) {
119
119
  "@type": "Event",
120
120
  name: e.name,
121
121
  startDate: l,
122
- eventStatus: b(e.status),
123
- eventAttendanceMode: "https://schema.org/OfflineEventAttendanceMode",
122
+ eventStatus: _(e.status),
124
123
  location: {
125
124
  "@type": "Place",
126
125
  name: t.name,
127
- address: _(c)
126
+ address: N(c)
128
127
  }
129
128
  }, a = p(e.endDateTime, n);
130
129
  a && (i.endDate = a);
@@ -132,15 +131,15 @@ function P(s) {
132
131
  u && (i.description = u);
133
132
  const m = e.image || e.coverImage || e.ShowImage;
134
133
  m && (i.image = [m]);
135
- const f = T(e.availableTickets, r);
136
- f && (i.offers = f);
134
+ const g = E(e.availableTickets, r);
135
+ g && (i.offers = g);
137
136
  const d = I(e.performers);
138
137
  if (d && d.length > 0 && (i.performer = d), o?.name) {
139
- const h = {
138
+ const f = {
140
139
  "@type": "Organization",
141
140
  name: o.name
142
141
  };
143
- o.url && (h.url = o.url), i.organizer = h;
142
+ o.url && (f.url = o.url), i.organizer = f;
144
143
  }
145
144
  return r && (i.url = r), i;
146
145
  }
@@ -196,10 +195,10 @@ function L(s, e) {
196
195
  availability: S(t[0].status)
197
196
  };
198
197
  }
199
- function O(s) {
198
+ function U(s) {
200
199
  const { series: e, baseEventUrl: t } = s;
201
200
  if (!e.title || !e.occurrences || e.occurrences.length === 0 || !e.venue?.name) return null;
202
- const n = _(e.venue.googleLocationNameCache || ""), r = {
201
+ const n = N(e.venue.googleLocationNameCache || ""), r = {
203
202
  "@type": "Place",
204
203
  name: e.venue.name
205
204
  };
@@ -224,18 +223,18 @@ function O(s) {
224
223
  "@type": "Event",
225
224
  name: a.title,
226
225
  startDate: u,
227
- eventStatus: b(a.status),
226
+ eventStatus: _(a.status),
228
227
  location: r
229
228
  };
230
229
  t && (m["@id"] = `${t}/${a.id}-${a.slug}`);
231
- const f = p(a.endDateTime, e.timeZone);
232
- f && (m.endDate = f);
230
+ const g = p(a.endDateTime, e.timeZone);
231
+ g && (m.endDate = g);
233
232
  const d = L(a.tickets, a.status);
234
233
  d && (m.offers = d), i.push(m);
235
234
  }
236
235
  return i.length === 0 ? null : (o.subEvent = i, o);
237
236
  }
238
- function U(s) {
237
+ function $(s) {
239
238
  const {
240
239
  collection: e,
241
240
  baseEventUrl: t,
@@ -276,19 +275,8 @@ function U(s) {
276
275
  "@type": "Event",
277
276
  name: i.title,
278
277
  startDate: a,
279
- eventStatus: b(i.status)
278
+ eventStatus: _(i.status)
280
279
  };
281
- if (i.venueName) {
282
- const f = {
283
- "@type": "Place",
284
- name: i.venueName
285
- };
286
- if (i.venueAddress) {
287
- const d = _(i.venueAddress);
288
- d && (f.address = d);
289
- }
290
- u.location = f;
291
- }
292
280
  t && i.slug && (u["@id"] = `${t}/${i.id}-${i.slug}`);
293
281
  const m = p(i.endDateTime, n);
294
282
  m && (u.endDate = m), i.image && (u.image = [i.image]), l.push(u);
@@ -298,9 +286,9 @@ function U(s) {
298
286
  function v(s) {
299
287
  return s ? s.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/--+/g, "-").replace(/^-+|-+$/g, "") : "";
300
288
  }
301
- const g = class g {
289
+ const h = class h {
302
290
  constructor(e) {
303
- this._state = "idle", this._originals = null, this._originalsSaved = !1, this._events = [], !(typeof document > "u") && (g._activeInstance && g._activeInstance !== this && g._activeInstance.destroy(), this._venueName = e.venueName, this._venueAddress = e.venueAddress, this._venueTimeZone = e.venueTimeZone || "America/Los_Angeles", this._baseUrl = e.baseUrl || window.location.origin + window.location.pathname, this._saveOriginals(), g._activeInstance = this);
291
+ this._state = "idle", this._originals = null, this._originalsSaved = !1, this._events = [], !(typeof document > "u") && (h._activeInstance && h._activeInstance !== this && h._activeInstance.destroy(), this._venueName = e.venueName, this._venueAddress = e.venueAddress, this._venueTimeZone = e.venueTimeZone || "America/Los_Angeles", this._baseUrl = e.baseUrl || window.location.origin + window.location.pathname, this._saveOriginals(), h._activeInstance = this);
304
292
  }
305
293
  // ---------------------------------------------------------------------------
306
294
  // Private helpers
@@ -413,7 +401,7 @@ const g = class g {
413
401
  }), o = t.name || "", c = t.description || t.eventSummary || "", l = t.image || t.coverImage || t.ShowImage || "";
414
402
  } else if (e === "series") {
415
403
  const a = t.slug || v(t.title || "");
416
- i = this._buildEntityUrl("series", t.id || "", a), r = O({
404
+ i = this._buildEntityUrl("series", t.id || "", a), r = U({
417
405
  series: {
418
406
  ...t,
419
407
  venue: {
@@ -426,13 +414,13 @@ const g = class g {
426
414
  }), o = t.title || "", c = t.description || t.eventSummary || "", l = t.image || "";
427
415
  } else if (e === "collection") {
428
416
  const a = t.slug || v(t.collectionTitle || "");
429
- i = this._buildEntityUrl("collection", t.id || "", a), r = U({
417
+ i = this._buildEntityUrl("collection", t.id || "", a), r = $({
430
418
  collection: t,
431
419
  baseEventUrl: i,
432
420
  defaultTimeZone: this._venueTimeZone
433
421
  }), o = t.collectionTitle || "", c = t.description || t.summary || "", l = t.coverImage || "";
434
422
  }
435
- r && this._injectJsonLd(r), i && this._setCanonical(i), o && this._setMeta("og:title", o), c && this._setMeta("og:description", c), l && this._setMeta("og:image", l), i && this._setMeta("og:url", i), this._setMeta("og:type", "website"), this._setMeta("twitter:card", "summary_large_image"), c && this._setMeta("description", c), o && (document.title = `${o} | ${this._venueName}`), this._state = "entity";
423
+ r && this._injectJsonLd(r), i && this._setCanonical(i), o && this._setMeta("og:title", o), c && this._setMeta("og:description", c), l && this._setMeta("og:image", l), i && this._setMeta("og:url", i), this._setMeta("og:type", "website"), c && this._setMeta("description", c), o && (document.title = `${o} | ${this._venueName}`), this._state = "entity";
436
424
  }
437
425
  /**
438
426
  * Called when the user navigates back to the event listing.
@@ -452,21 +440,21 @@ const g = class g {
452
440
  * and restores originals. Clears the static active instance.
453
441
  */
454
442
  destroy() {
455
- typeof document > "u" || this._state !== "destroyed" && (this._removeAllInjected(), this._restoreOriginals(), g._activeInstance === this && (g._activeInstance = null), this._state = "destroyed");
443
+ typeof document > "u" || this._state !== "destroyed" && (this._removeAllInjected(), this._restoreOriginals(), h._activeInstance === this && (h._activeInstance = null), this._state = "destroyed");
456
444
  }
457
445
  };
458
- g._activeInstance = null;
459
- let N = g;
446
+ h._activeInstance = null;
447
+ let A = h;
460
448
  export {
461
- N as HostSeoController,
462
- U as buildCollectionJsonLd,
449
+ A as HostSeoController,
450
+ $ as buildCollectionJsonLd,
463
451
  P as buildEventJsonLd,
464
- T as buildOffers,
452
+ E as buildOffers,
465
453
  I as buildPerformers,
466
- O as buildSeriesJsonLd,
454
+ U as buildSeriesJsonLd,
467
455
  p as formatDateTimeWithOffset,
468
- b as getEventStatus,
469
- A as getTicketAvailability,
470
- _ as parseAddressToPostal
456
+ _ as getEventStatus,
457
+ b as getTicketAvailability,
458
+ N as parseAddressToPostal
471
459
  };
472
460
  //# sourceMappingURL=seo.mjs.map