PageSourceSearch

https://www.paulmitchell.com/cdn/shop/t/424/assets/component-adapt…?v=108722536241987996331783361503

js paulmitchell.com collected 2026-09-24 09:49:07 UTC 15,769 bytes, 322 lines download raw bytes

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&&currentTime<=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.