https://www.paulmitchell.com/cdn/shop/t/424/assets/component-adapt…?v=108722536241987996331783361503
1import{LitElement,classMap,createRef,css,html,nothing,ref,when,unsafeHTML}from"@lit";import*as paths from"@nova/paths";import styles from"@nova/styles";import{viewportObserver}from"@nova/utils";import"@vendor/media-chrome";import"@vendor/media-chrome-vimeo";import"@vendor/media-chrome-youtube";class NovaAdaptiveVideo extends LitElement{static properties={sectionId:{type:String,attribute:"section-id"},srcDesktop:{type:String,attribute:"src-desktop"},aspectRatioDesktop:{type:String,attribute:"aspect-ratio-desktop"},srcMobile:{type:String,attribute:"src-mobile"},aspectRatioMobile:{type:String,attribute:"aspect-ratio-mobile"},srcType:{type:String,attribute:"src-type"},posterDesktop:{type:String,attribute:"poster-desktop"},posterMobile:{type:String,attribute:"poster-mobile"},descriptionDesktop:{type:String,attribute:"description-desktop"},descriptionMobile:{type:String,attribute:"description-mobile"},loadingType:{type:String,attribute:"loading"},viewport:{type:String,state:!0},autoplay:{type:Boolean},loop:{type:Boolean},sound:{type:Boolean},controls:{type:Boolean},captionsFile:{type:String,attribute:"captions-file"},captionsColor:{type:String,attribute:"captions-color"},trackProgress:{type:Boolean,attribute:"track-progress"},background:{type:Boolean,attribute:"background"},adaptContainerHeight:{type:Boolean,attribute:"adapt-container-height"},adaptContainerAspectRatio:{type:Boolean,attribute:"adapt-container-aspect-ratio"},videoContent:{type:String,attribute:"video-content"},videoDisclaimer:{type:String,attribute:"video-disclaimer"},borderRadius:{type:String,attribute:"border-radius"}};get activeSrc(){return this.srcMobile?["mobile","tablet"].includes(this.viewport)?this.srcMobile:this.srcDesktop:this.srcDesktop}get activeAspectRatio(){return this.aspectRatioMobile?["mobile","tablet"].includes(this.viewport)?this.aspectRatioMobile:this.aspectRatioDesktop:this.aspectRatioDesktop}get activeDesc(){return this.activeSrc===this.srcMobile&&this.descriptionMobile!=="false"?this.descriptionMobile:this.activeSrc===this.srcDesktop&&this.descriptionDesktop!=="false"?this.descriptionDesktop:""}get activePoster(){return this.activeSrc===this.srcMobile&&this.posterMobile!=="false"?this.posterMobile:this.activeSrc===this.srcDesktop&&this.posterDesktop!=="false"?this.posterDesktop:!1}constructor(){super(),this.viewport=viewportObserver.currentViewport,viewportObserver.addListener(this._handleViewportChange),this.srcMobile=!1,this.srcDesktop=!1,this.aspectRatioMobile=!1,this.aspectRatioDesktop="16/9",this.videoRef=createRef(),this.loadingType="lazy",this.background=!1,this.adaptContainerHeight=!1,this.adaptContainerAspectRatio=!1,this.borderRadius="0",this.captionsFile="",this.captionsColor="",this.currentCaption=""}connectedCallback(){super.connectedCallback(),this.autoplay&&(this.autoplaying=!0),this.percentageCompleted=0,this.videoTwentyFive=!1,this.videoFifty=!1,this.videoSeventyFive=!1,this.videoComplete=!1,this.videoInView=!1}disconnectedCallback(){super.disconnectedCallback(),this.$video&&this._handleCaptionUpdate&&this.$video.removeEventListener("timeupdate",this._handleCaptionUpdate)}firstUpdated(){this.$video=this.videoRef.value,this.captionsFile&&this.loadCaptions(),this.setupCaptionListener()}setupCaptionListener(){this.captionsFile&&this.$video.addEventListener("timeupdate",this._handleCaptionUpdate)}async loadCaptions(){try{const response=await fetch(this.captionsFile);if(!response.ok)throw new Error(`HTTP ${response.status}: ${response.statusText}`);const vttText=await response.text();this.parseVTT(vttText)}catch(error){console.error("[Video Captions] Failed to load captions:",error)}}parseVTT(vttText){const cues=[],lines=vttText.split(` 2`);let currentCue=null;for(let i=0;i<lines.length;i++){const line=lines[i].trim();if(!(line==="WEBVTT"||line===""||line.startsWith("NOTE")))if(line.includes("-->")){const[start,end]=line.split("-->").map(time=>this.parseVTTTime(time.trim()));currentCue&&cues.push({start:currentCue.start,end:currentCue.end,text:currentCue.text.trim()}),currentCue={start,end,text:""}}
2else currentCue&&line&&(currentCue.text+=(currentCue.text?" ":"")+line)}currentCue&&cues.push({start:currentCue.start,end:currentCue.end,text:currentCue.text.trim()}),this.captionCues=cues}parseVTTTime(timeStr){const parts=timeStr.split(":"),secondsParts=parts[2].split("."),hours=Number.parseInt(parts[0],10)||0,minutes=Number.parseInt(parts[1],10)||0,seconds=Number.parseInt(secondsParts[0],10)||0,milliseconds=Number.parseInt(secondsParts[1],10)||0;return hours*3600+minutes*60+seconds+milliseconds/1e3}_handleCaptionUpdate=()=>{if(!this.$video||!this.captionCues)return;let currentTime=0;if(this.$video.currentTime!==void 0?currentTime=this.$video.currentTime:this.$video.getCurrentTime?currentTime=this.$video.getCurrentTime():this.$video.shadowRoot&&this.$video.shadowRoot.querySelector("iframe")?.contentWindow&&console.error("[Video Captions] YouTube/Vimeo video detected, time tracking may require special handling"),!currentTime&&this.captionCues.length>0)return;const activeCue=this.captionCues.find(cue=>currentTime>=cue.start&¤tTime<=cue.end);activeCue&&activeCue.text!==this.currentCaption?(this.currentCaption=activeCue.text,this.requestUpdate()):!activeCue&&this.currentCaption&&(this.currentCaption="",this.requestUpdate())};_handleViewportChange=newViewport=>{this.viewport=newViewport};_handlePlay=()=>{this.autoplaying};_handlePause=()=>{this.autoplaying=!1};_handleProgress=()=>{if(this.$video?.readyState>=2&&this.trackProgress){const progressUpdate=()=>{this.percentageCompleted=this.$video.currentTime/this.$video.duration*100,!this.videoTwentyFive&&this.percentageCompleted>25&&(this.videoTwentyFive=!0,this._handleTrackProgress("25%")),!this.videoFifty&&this.percentageCompleted>50&&(this.videoFifty=!0,this._handleTrackProgress("50%")),!this.videoSeventyFive&&this.percentageCompleted>75&&(this.videoSeventyFive=!0,this._handleTrackProgress("75%")),!this.videoComplete&&this.percentageCompleted>99&&(this.videoComplete=!0,this._handleTrackProgress("100%"),this.$video.removeEventListener("timeupdate",progressUpdate))};this.$video.addEventListener("timeupdate",progressUpdate)}};_handleTrackProgress=progress=>{};renderYoutubeVideo=()=>html` 3 <youtube-video 4 ${ref(this.videoRef)} 5 part="video-element" 6 id="video-element" 7 slot="media" 8 src=${this.activeSrc} 9 loading=${this.loadingType} 10 muted 11 playsinline 12 aria-label=${this.activeDesc} 13 ?autoplay=${this.autoplay} 14 ?loop=${this.loop} 15 @loadeddata=${this._handleProgress} 16 @pause=${this._handlePause} 17 @play=${this._handlePlay} 18 > 19 ${this.renderCaptions()} 20 </youtube-video> 21 `;renderVimeoVideo=()=>html` 22 <vimeo-video 23 ${ref(this.videoRef)} 24 part="video-element" 25 id="video-element" 26 slot="media" 27 src=${this.activeSrc} 28 loading=${this.loadingType} 29 muted 30 playsinline 31 aria-label=${this.activeDesc} 32 ?autoplay=${this.autoplay} 33 ?loop=${this.loop} 34 @loadeddata=${this._handleProgress} 35 @pause=${this._handlePause} 36 @play=${this._handlePlay} 37 > 38 ${this.renderCaptions()} 39 </vimeo-video> 40 `;renderUploadedVideo=()=>html` 41 <video 42 ${ref(this.videoRef)} 43 part="video-element" 44 id="video-element" 45 slot="media" 46 src=${this.activeSrc} 47 loading=${this.loadingType} 48 muted 49 playsinline 50 aria-label=${this.activeDesc} 51 ?autoplay=${this.autoplay} 52 ?loop=${this.loop} 53 @loadeddata=${this._handleProgress} 54 @pause=${this._handlePause} 55 @play=${this._handlePlay} 56 > 57 ${this.renderCaptions()} 58 </video> 59 `;renderCaptions=()=>{if(this.captionsFile)return html` 60 <track 61 label="English" 62 kind="subtitles" 63 srclang="en" 64 src="${this.captionsFile}" 65 />`};renderControls=()=>html` 66 <media-control-bar part="controls"> 67 ${when(this.videoContent,()=>html` 68 <div class="hidden block--md block--lg video-player__content-wrapper"> 69 ${unsafeHTML(this.videoContent)} 70 </div> 71 `,()=>nothing)} 72 <media-play-button>
73 <span slot="play">${paths.play}</span> 74 <span slot="pause">${paths.pause}</span> 75 </media-play-button> 76 ${when(this.sound,()=>html` 77 <media-mute-button> 78 <span slot="high">${paths.audioOn}</span> 79 <span slot="off">${paths.audioOff}</span> 80 </media-mute-button> 81 `,()=>nothing)} 82 ${when(this.captionsFile,()=>html` 83 <media-captions-button> 84 <span slot="on">${paths.captionsOn}</span> 85 <span slot="off">${paths.captionsOff}</span> 86 </media-captions-button> 87 `,()=>nothing)} 88 ${when(this.videoDisclaimer,()=>html` 89 <aside aria-label="Legal disclaimer" role="note" class="hidden block--md block--lg video-player__disclaimer"> 90 ${unsafeHTML(this.videoDisclaimer)} 91 </aside> 92 `,()=>nothing)} 93 </media-control-bar> 94 `;play(){this.$video.play()}render(){const stateClasses={"background-video":this.background,"adapter-height":this.adaptContainerHeight,"adapter-aspect-ratio":this.adaptContainerAspectRatio};return html` 95 <media-controller class="${classMap(stateClasses)}" part="media-controller" style="--active-aspect-ratio: ${this.activeAspectRatio}; --border-radius: ${this.borderRadius}"> 96 ${when(this.srcType==="youtube",this.renderYoutubeVideo,()=>nothing)} 97 ${when(this.srcType==="vimeo",this.renderVimeoVideo,()=>nothing)} 98 ${when(this.srcType==="upload",this.renderUploadedVideo,()=>nothing)} 99 ${when(this.activePoster,()=>html` 100 <media-poster-image 101 slot="poster" 102 src=${this.activePoster}> 103 </media-poster-image>`,()=>nothing)} 104 ${when(this.controls,this.renderControls,()=>nothing)} 105 </media-controller> 106 ${when(this.captionsFile,()=>html` 107 <div 108 class="video-captions" 109 part="captions" 110 style="--captions-color: ${this.captionsColor||"var(--nova-color-text)"}" 111 > 112 ${this.currentCaption||""} 113 </div> 114 `,()=>nothing)} 115 `}static styles=[styles.a11y,styles.variables,css` 116 :host { 117 display: block; 118 height: 100%; 119 width: 100%; 120 position: relative; 121 } 122 #video-element { 123 height: 100%; 124 object-fit: contain; 125 width: 100%; 126 border-radius: var(--border-radius); 127 } 128 media-controller { 129 width: 100%; 130 height: auto; 131 aspect-ratio: var(--active-aspect-ratio); 132 display: block; 133 border-radius: var(--border-radius); 134 } 135 .adapter-height { 136 width: auto; 137 height: 100%; 138 left: 50%; 139 transform: translateX(-50%); 140 } 141 .adapter-aspect-ratio { 142 width: 100%; 143 height: 100%; 144 } 145 .adapter-aspect-ratio #video-element { 146 object-fit: cover; 147 } 148 media-control-bar { 149 position: absolute; 150 top: 50%; 151 left: 50%; 152 transform: translate(-50%, -50%); 153 display: flex; 154 column-gap: 15px; 155 row-gap: 30px; 156 flex-wrap: wrap; 157 justify-content: center; 158 } 159 media-play-button, 160 media-mute-button { 161 display: flex; 162 width: 50px; 163 height: 50px; 164 align-items: center; 165 justify-content: center; 166 background: var(--media-bg-color, #fff); 167 color: var(--media-text-reverse-color, #272726); 168 border-radius: 9999px; 169 } 170 media-play-button > *, 171 media-mute-button > * { 172 display: flex; 173 } 174 @media screen and (max-width: 768px) { 175 .background-video { 176 height: 100%; 177 width: auto; 178 left: 50%; 179 transform: translateX(-50%); 180 } 181 } 182 183 .video-player__content-wrapper { 184 width: 100%; 185 } 186 187 .video-player__content { 188 display: flex; 189 flex-direction: column; 190 align-items: center; 191 text-align: center; 192 word-wrap: break-word; 193 194 & ol, 195 & ul { 196 padding-left: 1.5em; 197 text-align: left; 198 } 199 200 & h2 { 201 max-width: 37.5rem; 202 margin-block-start: 0; 203 margin-block-end: 0; 204 205 &.h1 { 206 font: var(--nova-h1-font); 207 letter-spacing: var(--nova-h1-letter-spacing); 208 text-transform: var(--nova-h1-text-transform); 209 } 210 &.h2 { 211 font: var(--nova-h2-font); 212 letter-spacing: var(--nova-h2-letter-spacing); 213 text-transform: var(--nova-h2-text-transform); 214 } 215 &.h3 { 216 font: var(--nova-h3-font); 217 letter-spacing: var(--nova-h3-letter-spacing); 218 text-transform: var(--nova-h3-text-transform); 219 } 220 &.h4 { 221 font: var(--nova-h4-font); 222 letter-spacing: var(--nova-h4-letter-spacing); 223 text-transform: var(--nova-h4-text-transform); 224 } 225 } 226 227 & em { 228 font-family: var(--nova-font-italic-primary); 229 } 230 231 & p, 232 & li { 233 font: var(--nova-body-font); 234 letter-spacing: var(--nova-body-letter-spacing); 235 max-width: 445px; 236 margin: auto; 237 } 238 } 239 .video-player__content--logo { 240 margin-bottom: 30px; 241 242 & img { 243 max-width: 150px; 244 height: auto; 245 } 246 } 247 248 .video-player__disclaimer { 249 text-align: center; 250 word-wrap: break-word; 251 font: var(--nova-body-caption); 252 letter-spacing: var(--nova-caption-letter-spacing); 253 width: 100%; 254 255 & a { 256 color: inherit; 257 text-underline-offset: 2px; 258 } 259 260 & strong { 261 font-family: var(--nova-font-sans-bold); 262 } 263 } 264 265 @media screen and (min-width: 750px) and (max-width: 1023px) { 266 .hidden--md { 267 display: none !important; 268 } 269 .block--md { 270 display: block !important; 271 } 272 .flex--md { 273 display: flex !important; 274 } 275 } 276 277 @media screen and (min-width: 1024px) { 278 .hidden--lg { 279 display: none !important; 280 } 281 .block--lg { 282 display: block !important; 283 } 284 .flex--lg { 285 display: flex !important; 286 } 287 } 288 289 media-poster-image::after { 290 content: ''; 291 position: absolute; 292 inset: 0; 293 background: var(--background); 294 } 295 296 /* HACK: For main font family to correctly position the ® symbol */ 297 .superscript, 298 .video-player__disclaimer em .superscript { 299 font-size: 50% !important; 300 vertical-align: super; 301 } 302 303 em .superscript { 304 font-size: inherit !important; 305 vertical-align: baseline; 306 font-style: italic; 307 } 308 309 .video-captions { 310 position: absolute; 311 bottom: var(--nova-space-md); 312 left: var(--nova-space-md); 313 right: var(--nova-space-md); 314 text-align: center; 315 font: var(--nova-body-font); 316 color: var(--captions-color, var(--nova-color-text)); 317 display: flex; 318 align-items: center; 319 justify-content: center; 320 } 321 `]}customElements.define("nova-adaptive-video",NovaAdaptiveVideo); 322//# sourceMappingURL=/cdn/shop/t/424/assets/component-adaptive-video.js.map?v=108722536241987996331783361503
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.