Embed & Initialize
To correctly load, the SDK needs to be initialized with a configuration that will be specific to each partner hotel.
- See Theming via the Theming API documentation for more details.
- Initialization setup via the Configuration API to allow the widget to be launched via HTML elements on your web page. See the Configuration API documentation for more details.
Including the SDK
To include the SDK on a page in a customer's website, add the following tags to the head of the web page:
<script>
tag containing inline JavaScript. This is responsible for initializing the Skipper booking engine.<link/>
tags - these are enhance load time performance of the Skipper Booking Engine by preloading the comprising assets
Copy the following code within the comments and paste within the of each web page to implement. Note: these will need to be included on every web page on which a partner wants to see Skipper-driven behavior.
<html>
<head>
<!-- ... Copy tags below -->
<script>
"use strict";var Y=Object.defineProperty;var z=(e,n,i)=>n in e?Y(e,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[n]=i;var d=(e,n,i)=>z(e,typeof n!="symbol"?n+"":n,i);class H{constructor(n=!0){d(this,"_eventMap");d(this,"_recalculateExport");d(this,"_exports");d(this,"_addTimestamps");this._eventMap=new Map,this._recalculateExport=!0,this._exports=[],this._addTimestamps=n}track(n,i){this._addTimestamps&&(i.eventTimestamp=new Date().toISOString()),this._eventMap.set(n,i),this._recalculateExport=!0}getEvent(n){return this._eventMap.get(n)}export(){if(this._recalculateExport){this._exports=[];const n=this._eventMap[Symbol.iterator]();for(const[i,a]of n)this._exports.push({eventName:i,payload:a})}return this._recalculateExport=!1,this._exports}clear(){this._recalculateExport=!0,this._exports=[],this._eventMap.clear()}}var m=(e=>(e.OVERLAY="overlay",e.SIDERAIL="sideRail",e))(m||{});const W={WIDGET_LOAD_TIME:"Widget Load Time",INITIAL_INTERACTION_TIME:"Initial Interaction Time",SCRIPT_INSERTION_TIME:"Script Insertion Time"},q=3e4,O=700,G=1200,Q=1e3,y=new H,F=Date.now();let N,_,R,E,k=!0,$=!1,u=!1,l=null,v=null,x=!1,p=null;window.initSkipper=et;function Z(e,n,i){const{primaryColor:a,primaryFont:h,secondaryFont:o}=n,w=i===m.OVERLAY?1e3:500,T=i===m.OVERLAY?"margin: auto !important; left: 0 !important; bottom: 0 !important; max-height: 740px !important;":"",I='<div id="skipper-interstitial-container"><svg id="interstitial-animation" width="48" height="16" viewBox="0 0 32 16" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="cw-circle-container"><circle id="circle-fill" cx="24" cy="8" r="8"/><circle id="circle-stroke" cx="8" cy="8" r="7" stroke-width="2"/></g></svg><p id="message">Loading...</p></div>',g=`@media only screen and (min-width: 768px) {#skipper-interstitial-container {width: ${w}px !important; ${T}}} #circle-stroke{animation: ltor 1.2s ease-in-out infinite;animation-direction: alternate;stroke: ${a||"#2563EB"}}#circle-fill{animation: rtol 1.2s ease-in-out infinite;animation-direction: alternate;fill: ${a||"#2563EB"}}@keyframes ltor{20%{transform: translateX(-4px)}50%{transform: translateX(20px)}80%{transform: translateX(-4px)}}@keyframes rtol{20%{transform: translateX(4px)}50%{transform: translateX(-20px)}80%{transform: translateX(4px)}}.cash-widget-overlay-container:before{content: "";width: 100%;height: 100%;background-color: #00000080;position: fixed;bottom: 0;left: 0;right: 0;top: 0;z-index: 99}`;document.getElementById(e).insertAdjacentHTML("afterend",I),l=document.getElementById("skipper-interstitial-container"),l.style=`position: fixed; height: 100vh; background: #FFFFFF; z-index: 2147483647; overflow: auto; display: initial; width: 100vw; width: -webkit-fill-available; width: -moz-available; width: stretch; top: 0px; left: initial; right: 0px; display: flex; flex-direction: column; align-items: center; justify-content: center; font-family: ${h}, ${o}, 'Verdana'; text-align: center;`;const c=document.createElement("style");c.textContent=g,document.head.append(c),document.body.classList.add("cash-widget-overlay-container")}function J(){const e=new URL(window.location.href);return e.searchParams.get("sbe_openBE")==="true"||e.searchParams.get("openBE")==="true"}function K({hotelId:e,groupId:n,brandId:i}){const a=()=>{const o=[];return n&&o.push(`group-${n}`),i&&o.push(`brand-${i}`),e&&o.push(`hotel-${e}`),o.join("--")};return localStorage.getItem(`${a}_display`)}const tt=(e,n={},i)=>()=>{u||(_=Date.now(),y.track(W.INITIAL_INTERACTION_TIME,{interactionTime:_-F}),Z(e,n,i)),u=!0,x=!0};async function P(){var e;u&&window.openWidget&&$&&k&&(await window.openWidget(),(e=l==null?void 0:l.remove)==null||e.call(l))}function et(e){const{target:n,style:i,fallbackUrl:a,isDynamic:h=!1,dynamicQueryInterval:o=Q,renderAs:w,selfHosted:T}=e,{targetClasses:I=[],targetIds:g=[]}=e,c=new URL(window.location.href).searchParams.get("sbe_renderAs"),L=c||null||(T?m.OVERLAY:w||m.OVERLAY);e.renderAs=L;const A=tt(n,i,L);(J()||K({...e,type:"display"})==="collapsed")&&(setTimeout(()=>{const t=new CustomEvent("openBE");document.dispatchEvent(t)}),setTimeout(()=>{E?u=!0:A()},O));const U=()=>{R=Date.now();const t=document.createElement("script"),s="production";{let r;switch(s){case"production":r="widget.skipperhospitality.com";break;case"stage":r="widget-stage.skipperhospitality.com";break;case"uat":r="widget-uat.skipperhospitality.com";break;default:r="widget-dev.skipperhospitality.com";break}t.src=`https://${r}/cash-sdk.umd.js`}t.type="module",t.addEventListener("load",()=>{E=Date.now();const r=new CustomEvent("skipper-hydrated",{bubbles:!0});t.dispatchEvent(r)}),setTimeout(()=>{E||(k=!1,setTimeout(async()=>{k=!0,await P()},G))},O),setTimeout(()=>{u&&x&&a&&window.open(a,"_blank")},q),document.head.appendChild(t)};document.addEventListener("DOMContentLoaded",()=>{N=Date.now()});const b=new AbortController,V=t=>{document.addEventListener(t,()=>{b.abort(),U()},{once:!0,signal:b.signal})};["mousedown","scroll","keydown","touchstart","mousemove","openBE"].forEach(t=>V(t)),document.addEventListener("skipper-hydrated",async()=>{window._initSkipper&&await window._initSkipper(e,v)},{once:!0}),document.addEventListener("skipper-initialized",async()=>{p&&clearInterval(p),f.forEach(t=>{t.removeEventListener("click",S)}),x=!1,$=!0,window.primeTracking(y.export(),{pageLoadTime:F,initialUserInteractionTime:_,skipperScriptInsertionTime:R,skipperScriptLoadedTime:E,DOMContentLoadedTime:N}),await P(),y.clear()},{once:!0});function S(t){v=t.currentTarget?t.currentTarget:v,p&&clearInterval(p),t.preventDefault(),A()}const C=(t,s)=>{let r=[];return typeof t=="string"&&(t=[t]),t.length&&(r=Array.from(document.querySelectorAll(t.map(j=>`${s==="class"?".":"#"}${j}`).join(",")))),r},X=["[data-skipper-internal-link]","[href*=sbe_internalLink]"],M=()=>{var t;return new Set([...C(I,"class"),...C(g,"id"),...((t=document.querySelectorAll(X.join(",")))==null?void 0:t.values())||[]])},D=t=>t.addEventListener("click",S),f=M();for(const t of f)D(t);h&&typeof o=="number"&&(p=setInterval(()=>{const t=M();for(const s of f)t.delete(s);for(const s of t)f.add(s),D(s)},o))}
</script>
<link
rel="preload"
href="https://widget.skipperhospitality.com/cash-sdk.umd.js"
as="script"
crossorigin
/>
<link
rel="preload"
href="https://widget.skipperhospitality.com/style.css"
as="style"
crossorigin
/>
<link
rel="preload"
href="https://widget.skipperhospitality.com/site-base.css"
as="style"
crossorigin
/>
<link rel="preconnect" href="https://cash-api.skipperhospitality.com" />
<!-- ... End copy -->
</head>
</html>
Options and Notes
- The
<link rel="preload" ... />
tags are not strictly necessary, but will help to make the booking engine is loaded and available as soon as possible - The
<link rel="preconnect" ...>
tag is not strictly necessary, but can provide a slight performance benefit in connecting with the Skipper API - Instead of using the inline
<script>...</script>
, you can use<script src="https://widget.skipperhospitality.com/cash-sdk-load.umd.js"></script>
. This will, however incur a performance cost. - Use this
UMD load script
version of our inline script if you encounter WordPress plugin compatibility issues:<script>
(function(a){typeof define=="function"&&define.amd?define(a):a()})(function(){"use strict";var ee=Object.defineProperty;var te=(a,r,p)=>r in a?ee(a,r,{enumerable:!0,configurable:!0,writable:!0,value:p}):a[r]=p;var h=(a,r,p)=>te(a,typeof r!="symbol"?r+"":r,p);class a{constructor(n=!0){h(this,"_eventMap");h(this,"_recalculateExport");h(this,"_exports");h(this,"_addTimestamps");this._eventMap=new Map,this._recalculateExport=!0,this._exports=[],this._addTimestamps=n}track(n,i){this._addTimestamps&&(i.eventTimestamp=new Date().toISOString()),this._eventMap.set(n,i),this._recalculateExport=!0}getEvent(n){return this._eventMap.get(n)}export(){if(this._recalculateExport){this._exports=[];const n=this._eventMap[Symbol.iterator]();for(const[i,s]of n)this._exports.push({eventName:i,payload:s})}return this._recalculateExport=!1,this._exports}clear(){this._recalculateExport=!0,this._exports=[],this._eventMap.clear()}}var r=(t=>(t.OVERLAY="overlay",t.SIDERAIL="sideRail",t))(r||{});const p={WIDGET_LOAD_TIME:"Widget Load Time",INITIAL_INTERACTION_TIME:"Initial Interaction Time",SCRIPT_INSERTION_TIME:"Script Insertion Time"},V=3e4,b=700,X=1200,j=1e3,I=new a,S=Date.now();let C,g,M,E,v=!0,D=!1,m=!1,d=null,y=null,_=!1,u=null;window.initSkipper=q;function Y(t,n,i){const{primaryColor:s,primaryFont:w,secondaryFont:l}=n,k=i===r.OVERLAY?1e3:500,x=i===r.OVERLAY?"margin: auto !important; left: 0 !important; bottom: 0 !important; max-height: 740px !important;":"",L='<div id="skipper-interstitial-container"><svg id="interstitial-animation" width="48" height="16" viewBox="0 0 32 16" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="cw-circle-container"><circle id="circle-fill" cx="24" cy="8" r="8"/><circle id="circle-stroke" cx="8" cy="8" r="7" stroke-width="2"/></g></svg><p id="message">Loading...</p></div>',A=`@media only screen and (min-width: 768px) {#skipper-interstitial-container {width: ${k}px !important; ${x}}} #circle-stroke{animation: ltor 1.2s ease-in-out infinite;animation-direction: alternate;stroke: ${s||"#2563EB"}}#circle-fill{animation: rtol 1.2s ease-in-out infinite;animation-direction: alternate;fill: ${s||"#2563EB"}}@keyframes ltor{20%{transform: translateX(-4px)}50%{transform: translateX(20px)}80%{transform: translateX(-4px)}}@keyframes rtol{20%{transform: translateX(4px)}50%{transform: translateX(-20px)}80%{transform: translateX(4px)}}.cash-widget-overlay-container:before{content: "";width: 100%;height: 100%;background-color: #00000080;position: fixed;bottom: 0;left: 0;right: 0;top: 0;z-index: 99}`;document.getElementById(t).insertAdjacentHTML("afterend",L),d=document.getElementById("skipper-interstitial-container"),d.style=`position: fixed; height: 100vh; background: #FFFFFF; z-index: 2147483647; overflow: auto; display: initial; width: 100vw; width: -webkit-fill-available; width: -moz-available; width: stretch; top: 0px; left: initial; right: 0px; display: flex; flex-direction: column; align-items: center; justify-content: center; font-family: ${w}, ${l}, 'Verdana'; text-align: center;`;const f=document.createElement("style");f.textContent=A,document.head.append(f),document.body.classList.add("cash-widget-overlay-container")}function z(){const t=new URL(window.location.href);return t.searchParams.get("sbe_openBE")==="true"||t.searchParams.get("openBE")==="true"}function H({hotelId:t,groupId:n,brandId:i}){const s=()=>{const l=[];return n&&l.push(`group-${n}`),i&&l.push(`brand-${i}`),t&&l.push(`hotel-${t}`),l.join("--")};return localStorage.getItem(`${s}_display`)}const W=(t,n={},i)=>()=>{m||(g=Date.now(),I.track(p.INITIAL_INTERACTION_TIME,{interactionTime:g-S}),Y(t,n,i)),m=!0,_=!0};async function O(){var t;m&&window.openWidget&&D&&v&&(await window.openWidget(),(t=d==null?void 0:d.remove)==null||t.call(d))}function q(t){const{target:n,style:i,fallbackUrl:s,isDynamic:w=!1,dynamicQueryInterval:l=j,renderAs:k,selfHosted:x}=t,{targetClasses:L=[],targetIds:A=[]}=t,f=new URL(window.location.href).searchParams.get("sbe_renderAs"),N=f||null||(x?r.OVERLAY:r.OVERLAY||k);t.renderAs=N;const R=W(n,i,N);(z()||H({...t,type:"display"})==="collapsed")&&(setTimeout(()=>{const e=new CustomEvent("openBE");document.dispatchEvent(e)}),setTimeout(()=>{E?m=!0:R()},b));const Q=()=>{M=Date.now();const e=document.createElement("script"),c="production";{let o;switch(c){case"production":o="widget.skipperhospitality.com";break;case"stage":o="widget-stage.skipperhospitality.com";break;case"uat":o="widget-uat.skipperhospitality.com";break;default:o="widget-dev.skipperhospitality.com";break}e.src=`https://${o}/cash-sdk.umd.js`}e.type="module",e.addEventListener("load",()=>{E=Date.now();const o=new CustomEvent("skipper-hydrated",{bubbles:!0});e.dispatchEvent(o)}),setTimeout(()=>{E||(v=!1,setTimeout(async()=>{v=!0,await O()},X))},b),setTimeout(()=>{m&&_&&s&&window.open(s,"_blank")},V),document.head.appendChild(e)};document.addEventListener("DOMContentLoaded",()=>{C=Date.now()});const P=new AbortController,Z=e=>{document.addEventListener(e,()=>{P.abort(),Q()},{once:!0,signal:P.signal})};["mousedown","scroll","keydown","touchstart","mousemove","openBE"].forEach(e=>Z(e)),document.addEventListener("skipper-hydrated",async()=>{window._initSkipper&&await window._initSkipper(t,y)},{once:!0}),document.addEventListener("skipper-initialized",async()=>{u&&clearInterval(u),T.forEach(e=>{e.removeEventListener("click",F)}),_=!1,D=!0,window.primeTracking(I.export(),{pageLoadTime:S,initialUserInteractionTime:g,skipperScriptInsertionTime:M,skipperScriptLoadedTime:E,DOMContentLoadedTime:C}),await O(),I.clear()},{once:!0});function F(e){y=e.currentTarget?e.currentTarget:y,u&&clearInterval(u),e.preventDefault(),R()}const $=(e,c)=>{let o=[];return typeof e=="string"&&(e=[e]),e.length&&(o=Array.from(document.querySelectorAll(e.map(K=>`${c==="class"?".":"#"}${K}`).join(",")))),o},J=["[data-skipper-internal-link]","[href*=sbe_internalLink]"],B=()=>{var e;return new Set([...$(L,"class"),...$(A,"id"),...((e=document.querySelectorAll(J.join(",")))==null?void 0:e.values())||[]])},U=e=>e.addEventListener("click",F),T=B();for(const e of T)U(e);w&&typeof l=="number"&&(u=setInterval(()=>{const e=B();for(const c of T)e.delete(c);for(const c of e)T.add(c),U(c)},l))}});
</script>
Initializing the Booking Engine
Once the customization and configuration has been decided and the SDK has been included on the proper web pages, insert the following code snippet at the end of their web page(s) body tag.
The contents of the configuration object {}
passed to the initSkipper
function should match options in the Configuration API as decided in the
previous Customize & Configure step.
<body>
<!-- ... -->
<!-- These tags must be direct children of the <body> tag -->
<div id="skipper-target"></div>
<script type="module" id="skipper-init-script">
// initializing Skipper widget with configuration
if (window.initSkipper) {
window.initSkipper({
target: "skipper-target",
targetIds: ["main-reserve-button"],
targetClasses: ["other-reserve-buttons"],
hotelId: "HOTELID_FROMSKIPPER", // not needed if groupId or brandId is provided
brandId: "BRANDID_FROMSKIPPER", // not needed if hotelId or groupId is provided
groupId: "GROUPID_FROMSKIPPER", // not needed if hotelId or brandId is provided
style: {
primaryColor: "#1B3A5C",
accentColor: "#7F9580",
primaryFont: "Comic Sans MS",
},
fallbackUrl: "https://<enter-fallback-CRS-or-PMS-site-here>",
});
}
</script>
</body>
Options and Notes
<div id="skipper-target"></div>
can be inserted anywhere as a child of the body tag. The value of itsid
attribute will be the value oftarget
in theinitSkipper
call.Adding
skipper-init-script
as theid
value for thescript
tag allows Revinate integrations to look for Skipper scripts.Both the inline
<script>
and the<div id="skipper-target"></div>
must be direct children of thebody
tag. They should not be children of other DOM elements that themselves are descendants of thebody
tag.Either put the
<script>
before the closing</body>
tag or use thetype=module
attribute on the<script>
tag to ensure that the script is executed after the DOM has been fully loaded. Otherwise the widget might no work as expected.If you prefer, you can omit the
target
parameter and instead use the custom element<skipper-booking-engine></skipper-booking-engine>
:Adding
type="module"
inside the openingscript tag
and using theUMD load script
resolves compatibility issues with newer versions of SquareSpace websites.
<body>
<!-- ... -->
<skipper-booking-engine></skipper-booking-engine>
<script type="module" id="skipper-init-script">
// initializing Skipper widget with configuration
if (window.initSkipper) {
window.initSkipper({
targetIds: ["main-reserve-button"],
targetClasses: ["other-reserve-buttons"],
hotelId: "HOTELID_FROMSKIPPER",
style: {
primaryColor: "#1B3A5C",
accentColor: "#7F9580",
primaryFont: "Comic Sans MS",
},
fallbackUrl: "https://<enter-fallback-CRS-or-PMS-site-here>",
});
}
</script>
</body>