PageSourceSearch

https://www.cinegy.com/modules/modal/modal.js

js cinegy.com collected 2026-10-02 03:13:39 UTC 23,410 bytes, 822 lines download raw bytes

1// modal module
2
3
4export function modal(images, isGallery = false, initialIndex = 0, type) {
5    const container = document.getElementById('modal');
6    const main_header = document.getElementById('main_header');
7 
8    if (!container) return;
9  
10    container.classList.add('active');
11    main_header.classList.add('none');
12
13
14    document.body.classList.add('modal_on');
15    locked();
16
17
18    gsap.to(container, { opacity: 1, duration: 0.35, ease: 'power2.out' });
19  
20    if (type === 'image') {
21      renderImageModal(images, isGallery, initialIndex);
22    } else {
23        renderVideoModal(images, initialIndex); 
24    }
25
26  }
27
28
29
30
31
32
33
34// ===============================
35// IMAGE
36// ===============================
37
38
39
40
41
42
43
44  
45  // function renderImageModal(images, isGallery = false, initialIndex = 0) {
46  //   const container = document.getElementById('modal');
47  //   const modalImg = container.querySelector('.main-image img');
48  //   const thumbnails = container.querySelector('.thumbnails');
49  //   const altBox = container.querySelector('.main-image .alt-text');
50  //   const countBox = thumbnails.querySelector('.thumb-count');
51
52  //   const expandedView = container.querySelector('.expanded-view');
53  //   const expandedImg = container.querySelector('.expanded-view img');
54
55  //   const closeExpanded = container.querySelector('.close-expanded');
56  //   const expandBtn = container.querySelector('.expand-image');    
57  //   const closeBtn = container.querySelector('.close-modal');     
58  //   const downBtn = container.querySelector('.download-image');     
59
60  
61  //   if (countBox) {
62  //     thumbnails.innerHTML = '';
63  //     thumbnails.appendChild(countBox);
64  //   }
65  
66  //   if (thumbnails) {
67  //     thumbnails.innerHTML = '';
68  //     if (countBox) thumbnails.appendChild(countBox);
69  //   }    
70
71  //   container.classList.add('image-modal');
72  //   container.classList.toggle('gallery', isGallery);
73
74  //   let currentIndex = initialIndex;
75  
76  //   updateImage({
77  //     images,
78  //     index: currentIndex,
79  //     modalImg,
80  //     altBox,
81  //     expandedImg,
82  //     thumbnails,
83  //     countBox
84  //   });
85  
86  //   if (isGallery) {
87  //     renderThumbnails(images, currentIndex, thumbnails, newIndex => {
88  //       currentIndex = newIndex;
89  //       updateImage({
90  //         images,
91  //         index: currentIndex,
92  //         modalImg,
93  //         altBox,
94  //         expandedImg,
95  //         thumbnails,
96  //         countBox
97  //       });
98  //     });
99  
100  //     setupNavigation(container, images, () => {
101  //       currentIndex = (currentIndex + 1) % images.length;
102  //       updateImage({
103  //         images,
104  //         index: currentIndex,
105  //         modalImg,
106  //         altBox,
107  //         expandedImg,
108  //         thumbnails,
109  //         countBox
110  //       });
111  //     }, () => {
112  //       currentIndex = (currentIndex - 1 + images.length) % images.length;
113  //       updateImage({
114  //         images,
115  //         index: currentIndex,
116  //         modalImg,
117  //         altBox,
118  //         expandedImg,
119  //         thumbnails,
120  //         countBox
121  //       });
122  //     });
123  //   }
124  
125  //   expandBtn.onclick = () => {
126  //     expandedImg.src = modalImg.src;
127  //     expandedImg.alt = modalImg.alt;
128  
129  //     gsap.set(expandedView, { opacity: 0 });
130  //     expandedView.classList.add('active');
131  
132  //     gsap.to(expandedView, {
133  //       opacity: 1,
134  //       duration: 0.35,
135  //       ease: 'power2.out'
136  //     });
137  //   };
138  
139  //   closeExpanded.onclick = () => {
140  //     gsap.to(expandedView, {
141  //       opacity: 0,
142  //       duration: 0.3,
143  //       ease: 'power2.inOut',
144  //       onComplete: () => {
145  //         expandedView.classList.remove('active');
146  //       }
147  //     });
148  //   };
149  
150  //   closeBtn.onclick = () => closeModal('image');
151  
152  //   gsap.to('.main-image', { opacity: 1, duration: 0.3, ease: 'power1.out' });
153  //   if (isGallery) {
154  //     gsap.to('.thumbnails', { opacity: 1, duration: 0.3, ease: 'power1.out' });
155  //   }
156  // }
157
158
159
160
161  
162  // function renderImageModal(images, isGallery = false, initialIndex = 0) {
163  //   const container = document.getElementById('modal');
164  //   const modalImg = container.querySelector('.main-image img');
165  //   const thumbnails = container.querySelector('.thumbnails');
166  //   const altBox = container.querySelector('.main-image .alt-text');
167  //   const countBox = thumbnails.querySelector('.thumb-count');
168
169  //   const expandedView = container.querySelector('.expanded-view');
170  //   const expandedImg = container.querySelector('.expanded-view img');
171
172  //   const closeExpanded = container.querySelector('.close-expanded');
173  //   const expandBtn = container.querySelector('.expand-image');    
174  //   const closeBtn = container.querySelector('.close-modal');     
175  //   const downBtn = container.querySelector('.download-image');     
176
177  
178  //   if (countBox) {
179  //     thumbnails.innerHTML = '';
180  //     thumbnails.appendChild(countBox);
181  //   }
182  
183  //   if (thumbnails) {
184  //     thumbnails.innerHTML = '';
185  //     if (countBox) thumbnails.appendChild(countBox);
186  //   }    
187
188  //   container.classList.add('image-modal');
189  //   container.classList.toggle('gallery', isGallery);
190
191  //   let currentIndex = initialIndex;
192  
193  //   updateImage({
194  //     images,
195  //     index: currentIndex,
196  //     modalImg,
197  //     altBox,
198  //     expandedImg,
199  //     thumbnails,
200  //     countBox
201  //   });
202  
203  //   if (isGallery) {
204  //     renderThumbnails(images, currentIndex, thumbnails, newIndex => {
205  //       currentIndex = newIndex;
206  //       updateImage({
207  //         images,
208  //         index: currentIndex,
209  //         modalImg,
210  //         altBox,
211  //         expandedImg,
212  //         thumbnails,
213  //         countBox
214  //       });
215  //     });
216  
217  //     setupNavigation(container, images, () => {
218  //       currentIndex = (currentIndex + 1) % images.length;
219  //       updateImage({
220  //         images,
221  //         index: currentIndex,
222  //         modalImg,
223  //         altBox,
224  //         expandedImg,
225  //         thumbnails,
226  //         countBox
227  //       });
228  //     }, () => {
229  //       currentIndex = (currentIndex - 1 + images.length) % images.length;
230  //       updateImage({
231  //         images,
232  //         index: currentIndex,
233  //         modalImg,
234  //         altBox,
235  //         expandedImg,
236  //         thumbnails,
237  //         countBox
238  //       });
239  //     });
240  //   }
241  
242  //   expandBtn.onclick = () => {
243  //     expandedImg.src = modalImg.src;
244  //     expandedImg.alt = modalImg.alt;
245  
246  //     gsap.set(expandedView, { opacity: 0 });
247  //     expandedView.classList.add('active');
248  
249  //     gsap.to(expandedView, {
250  //       opacity: 1,
251  //       duration: 0.35,
252  //       ease: 'power2.out'
253  //     });
254  //   };
255  
256  //   closeExpanded.onclick = () => {
257  //     gsap.to(expandedView, {
258  //       opacity: 0,
259  //       duration: 0.3,
260  //       ease: 'power2.inOut',
261  //       onComplete: () => {
262  //         expandedView.classList.remove('active');
263  //       }
264  //     });
265  //   };
266  
267  //   closeBtn.onclick = () => closeModal('image');
268  
269  //   gsap.to('.main-image', { opacity: 1, duration: 0.3, ease: 'power1.out' });
270  //   if (isGallery) {
271  //     gsap.to('.thumbnails', { opacity: 1, duration: 0.3, ease: 'power1.out' });
272  //   }
273  // }
274
275
276
277  function renderImageModal(images, isGallery = false, initialIndex = 0) {
278    // 1) Deduplicate images by URL
279    const seen = new Set();
280    const uniqueImages = images.filter(img => {
281      if (seen.has(img.display)) return false;
282      seen.add(img.display);
283      return true;
284    });
285  
286    // 2) Remap initialIndex into the deduped array
287    const requestedURL = images[initialIndex]?.display;
288    let currentIndex = uniqueImages.findIndex(img => img.display === requestedURL);
289    if (currentIndex < 0) currentIndex = 0;
290  
291    // 3) Grab DOM references
292    const container   = document.getElementById('modal');
293    const modalImg    = container.querySelector('.main-image img');
294    const thumbnails  = container.querySelector('.thumbnails');
295    const altBox      = container.querySelector('.main-image .alt-text');
296    const titleBox      = container.querySelector('.main-image .title-text');    
297    const countBox    = thumbnails.querySelector('.thumb-count');
298    const expandedView= container.querySelector('.expanded-view');
299    const expandedImg = expandedView.querySelector('img');
300    const closeExpanded = container.querySelector('.close-expanded');
301    const expandBtn   = container.querySelector('.expand-image');
302    const closeBtn    = container.querySelector('.close-modal');
303  
304    // 4) Clear out any old thumbnails
305    if (thumbnails) {
306      thumbnails.innerHTML = '';
307      if (countBox) thumbnails.appendChild(countBox);
308    }
309  
310    container.classList.add('image-modal');
311    container.classList.toggle('gallery', isGallery);
312  
313    // 5) Initial render
314    updateImage({
315      images: uniqueImages,
316      index: currentIndex,
317      modalImg,
318      altBox,
319      titleBox,           // ← pass it in
320      expandedImg,
321      thumbnails,
322      countBox
323    });
324  
325    // 6) If gallery, build thumbs & nav
326    if (isGallery) {
327      renderThumbnails(uniqueImages, currentIndex, thumbnails, newIndex => {
328        currentIndex = newIndex;
329        updateImage({
330          images: uniqueImages,
331          index: currentIndex,
332          modalImg,
333          altBox,
334          titleBox,
335          expandedImg,
336          thumbnails,
337          countBox
338        });
339      });
340  
341      setupNavigation(container, uniqueImages,
342        // next
343        () => {
344          currentIndex = (currentIndex + 1) % uniqueImages.length;
345          updateImage({
346            images: uniqueImages,
347            index: currentIndex,
348            modalImg,
349            altBox,
350            expandedImg,
351            thumbnails,
352            countBox
353          });
354        },
355        // prev
356        () => {
357          currentIndex = (currentIndex - 1 + uniqueImages.length) % uniqueImages.length;
358          updateImage({
359            images: uniqueImages,
360            index: currentIndex,
361            modalImg,
362            altBox,
363            expandedImg,
364            thumbnails,
365            countBox
366          });
367        }
368      );
369    }
370  
371    // 7) Expanded‐view handlers
372    expandBtn.onclick = () => {
373      expandedImg.src = modalImg.src;
374      expandedImg.alt = modalImg.alt;
375      gsap.set(expandedView, { opacity: 0 });
376      expandedView.classList.add('active');
377      gsap.to(expandedView, { opacity: 1, duration: 0.35, ease: 'power2.out' });
378    };
379    closeExpanded.onclick = () => {
380      gsap.to(expandedView, {
381        opacity: 0,
382        duration: 0.3,
383        ease: 'power2.inOut',
384        onComplete: () => {
385          expandedView.classList.remove('active');
386        }
387      });
388    };
389  
390    // 8) Close modal
391    closeBtn.onclick = () => closeModal('image');
392  
393    // 9) Fade in
394    gsap.to('.main-image', { opacity: 1, duration: 0.3, ease: 'power1.out' });
395    if (isGallery) {
396      gsap.to('.thumbnails', { opacity: 1, duration: 0.3, ease: 'power1.out' });
397    }
398  }
399  
400
401
402
403  
404  // function updateImage({ images, index, modalImg, altBox, titleBox, expandedImg, thumbnails, countBox }) {
405  //   const imgObj = images[index];
406  
407  //   // ——— NEW: title support ———
408  //   if (titleBox) {
409  //     titleBox.textContent = imgObj.title || '';
410  //     titleBox.classList.toggle('on', !!imgObj.title);
411  //   }
412  
413  //   // fade out/in main image
414  //   gsap.to(modalImg, {
415  //     opacity: 0,
416  //     duration: 0.2,
417  //     ease:    'power1.out',
418  //     onComplete: () => {
419  //       modalImg.src = imgObj.display;
420  //       modalImg.alt = imgObj.alt || '';
421  //       gsap.to(modalImg, { opacity: 1, duration: 0.3, ease: 'power2.out' });
422  //     }
423  //   });
424  
425  //   // alt text as before
426  //   altBox.textContent           = imgObj.alt || '';
427  //   altBox.classList.toggle('on', !!imgObj.alt);
428  
429  //   // expanded view
430  //   expandedImg.src = imgObj.display;
431  //   expandedImg.alt = imgObj.alt || '';
432  
433  //   // thumbnails highlighting (unchanged)
434  //   if (thumbnails) {
435  //     const thumbs = thumbnails.querySelectorAll('img');
436  //     thumbs.forEach(thumb => {
437  //       const thumbIndex = +thumb.dataset.index;
438  //       const isActive   = thumbIndex === index;
439  //       gsap.to(thumb, {
440  //         borderColor: isActive ? '#fff' : 'transparent',
441  //         opacity:     isActive ? 1    : 0.6,
442  //         duration:    0.3,
443  //         ease:        isActive ? 'power2.out' : 'power2.inOut'
444  //       });
445  //       thumb.classList.toggle('active', isActive);
446  //     });
447  //   }
448  
449  //   // count
450  //   if (countBox) {
451  //     countBox.textContent = `${index + 1} / ${images.length}`;
452  //   }
453  // }
454
455
456  
457  function updateImage({ images, index, modalImg, altBox, expandedImg, thumbnails, countBox }) {
458    const imgObj = images[index];
459  
460    // 1) Build the alt‑text HTML
461    let html = '';
462    if (imgObj.title) {
463      html += `<h3>${imgObj.title}</h3>`;
464    }
465    html += `<p>${imgObj.alt || ''}</p>`;
466  
467    // 2) Write it and toggle the .on class
468    altBox.innerHTML = html;
469    altBox.classList.toggle('on', !!(imgObj.title || imgObj.alt));
470  
471    // 3) Main image fade‑out/in
472    gsap.to(modalImg, {
473      opacity: 0,
474      duration: 0.2,
475      ease:    'power1.out',
476      onComplete: () => {
477        modalImg.src = imgObj.display;
478        modalImg.alt = imgObj.alt;
479        gsap.to(modalImg, { opacity: 1, duration: 0.3, ease: 'power2.out' });
480      }
481    });
482  
483    // 4) Expanded view
484    expandedImg.src = imgObj.display;
485    expandedImg.alt = imgObj.alt;
486  
487    // 5) Highlight thumbnails
488    if (thumbnails) {
489      thumbnails.querySelectorAll('img').forEach(thumb => {
490        const isActive = +thumb.dataset.index === index;
491        gsap.to(thumb, {
492          borderColor: isActive ? '#fff'       : 'transparent',
493          opacity:     isActive ? 1            : 0.6,
494          duration:    0.3,
495          ease:        isActive ? 'power2.out' : 'power2.inOut'
496        });
497        thumb.classList.toggle('active', isActive);
498      });
499    }
500  
501    // 6) Update count
502    if (countBox) {
503      countBox.textContent = `${index + 1} / ${images.length}`;
504    }
505  }
506  
507
508// ===============================
509// Carousel-Based Thumbnail Module (Balanced Wrapping)
510// ===============================
511
512function renderThumbnails(images, currentIndex, thumbnails, onThumbClick) {
513  // remove old carousel
514  const old = thumbnails.querySelector('.thumb-carousel');
515  if (old) thumbnails.removeChild(old);
516
517  // create new
518  const inner = document.createElement('div');
519  inner.className = 'thumb-carousel';
520  thumbnails.appendChild(inner);
521
522  // calculate the “around” indexes
523  const total = images.length;
524  const half  = Math.floor(total / 2);
525  const order = [];
526  for (let i = -half; i <= half; i++) {
527    order.push((currentIndex + i + total) % total);
528  }
529
530  // build them, skipping any URL we’ve already emitted
531  const seenUrls = new Set();
532  order.forEach(idx => {
533    const obj = images[idx];
534    const url = obj.display;
535    if (seenUrls.has(url)) {
536      console.log(`Skipping duplicate URL at index ${idx}:`, url);
537      return;
538    }
539    seenUrls.add(url);
540
541    const thumb = document.createElement('img');
542    thumb.src           = url;
543    thumb.alt           = obj.alt || '';
544    thumb.dataset.index = idx;
545    if (idx === currentIndex) thumb.classList.add('active');
546    thumb.onclick = () => onThumbClick(idx);
547    inner.appendChild(thumb);
548  });
549
550  // (you can still keep your small-by-index dedupe if you really need both guards)
551}
552
553
554  
555 
556  function shiftThumbnails(wrapper, direction = 'next') {
557    const thumbs = wrapper.querySelectorAll('img');
558    if (thumbs.length === 0) return;
559  
560    if (direction === 'next') {
561      const first = thumbs[0];
562      wrapper.removeChild(first);
563      wrapper.appendChild(first);
564    } else {
565      const last = thumbs[thumbs.length - 1];
566      wrapper.removeChild(last);
567      wrapper.insertBefore(last, thumbs[0]);
568    }
569  
570    const active = wrapper.querySelector('img.active');
571    if (!active) return;
572    const newIndex = Array.from(wrapper.children).indexOf(active);
573    //centerActiveThumbnail(wrapper, newIndex);
574  }
575  
576  function setupNavigation(container, images, onNext, onPrev) {
577    const nextBtn = container.querySelector('.nav.next');
578    const prevBtn = container.querySelector('.nav.prev');
579    const wrapper = container.querySelector('.thumb-carousel');
580  
581    if (nextBtn) nextBtn.onclick = () => {
582      shiftThumbnails(wrapper, 'next');
583      onNext();
584    };
585  
586    if (prevBtn) prevBtn.onclick = () => {
587      shiftThumbnails(wrapper, 'prev');
588      onPrev();
589    };
590  }
591  
592  
593
594
595
596// ===============================
597// VIDEO
598// ===============================
599
600
601
602
603
604
605export function renderVideoModal(videos = [], initialIndex = 0) {
606    const container = document.getElementById('modal');
607    const videoWrap = container.querySelector('.type_video');
608    const nativeVideo = videoWrap.querySelector('video');
609    const iframe = videoWrap.querySelector('iframe');
610    const closeBtn = container.querySelector('.close-modal');
611  
612    // Reset styles and hide both video types
613    nativeVideo.style.display = 'none';
614    iframe.style.display = 'none';
615  
616    const data = videos[initialIndex];
617    console.log('[VIDEO MODAL]', data);
618    const isYouTube = !!data.youtube;
619  
620    if (isYouTube) {
621      const baseSrc = data.youtube.split('?')[0];
622      const yid = baseSrc.split('/').pop();
623      iframe.src = `${baseSrc}?autoplay=1&loop=1&rel=0&modestbranding=1&disablekb=1&playsinline=1&playlist=${yid}`;
624      iframe.style.display = 'block';
625    } else {
626      nativeVideo.poster = data.poster || '';
627      nativeVideo.preload = data.preload || 'auto';
628  
629      const sources = nativeVideo.querySelectorAll('source');
630      sources[0].src = data.source1 || '';
631      sources[0].type = data.mime1 || 'video/webm';
632  
633      if (sources[1]) {
634        sources[1].src = data.source2 || '';
635        sources[1].type = data.mime2 || 'video/mp4';
636      }
637  
638      nativeVideo.load();
639      nativeVideo.style.display = 'block';
640    }
641  
642    // Set visibility for the video section
643    container.classList.add('video-modal');
644    container.classList.remove('image-modal');
645
646  
647    // Bind close button
648    closeBtn.onclick = () => closeModal('video');
649  
650    // Fade in the video wrapper (optional)
651    gsap.to(videoWrap, { opacity: 1, duration: 0.3, ease: 'power1.out' });
652  }
653  
654
655
656
657
658
659  
660
661
662// ===============================
663// CLOSE
664// ===============================
665  
666
667//pause_product_carousel()
668
669function closeModal(type) {
670    const container = document.getElementById('modal');
671    const modalInner = container.querySelector('.modal-inner');
672    const main_header = document.getElementById('main_header');
673    const closeBtn = container.querySelector('.close-modal');    
674
675    
676    console.log('$stop_carousel value:', $stop_carousel);
677    if ($stop_carousel) {
678      console.log('→ Carousel is stopped');
679      pause_product_carousel();
680    } else {
681      console.log('→ Carousel is running');
682    }
683
684
685    if (!container || !modalInner) return;
686  
687
688    if (type === 'image') {
689
690
691    // Start opacity fade on .modal-inner
692    gsap.to(modalInner, {
693        opacity: 0,
694        duration: 0.2,
695        ease: 'power2.inOut',
696        onComplete: () => {
697  
698          const expandedView = container.querySelector('.expanded-view');
699          // Remove event handlers
700
701          const nextBtn = container.querySelector('.nav.next');
702          const prevBtn = container.querySelector('.nav.prev');
703      
704          if (closeBtn) closeBtn.onclick = null;
705          if (nextBtn) nextBtn.onclick = null;
706          if (prevBtn) prevBtn.onclick = null;
707  
708    
709          // Clear thumbnails and preserve countBox
710          const thumbs = container.querySelector('.thumbnails');
711          if (thumbs) {
712          const countBox = thumbs.querySelector('.thumb-count');
713          thumbs.innerHTML = '';
714          if (countBox) thumbs.appendChild(countBox);
715          }
716      
717          // Clear modal image
718          const modalImg = container.querySelector('.main-image img');
719          if (modalImg) {
720          modalImg.src = '';
721          modalImg.alt = '';
722          }
723  
724          // Clear alt text
725          const altBox = container.querySelector('.main-image .alt-text');
726          if (altBox) {
727              altBox.textContent = '';
728              altBox.classList.remove('on');
729          }    
730  
731          main_header.classList.remove('none');
732  
733          // Then fade the container
734          gsap.to(container, {
735            opacity: 0,
736            duration: 0.3,
737            ease: 'power2.inOut',
738            onComplete: () => {
739              container.classList.remove('active', 'gallery', 'image-modal');
740    
741              // Remove all inline styles inside the modal
742              const allDescendants = container.querySelectorAll('*');
743              allDescendants.forEach(el => el.removeAttribute('style'));
744              container.removeAttribute('style');
745    
746              // Cleanup expanded view
747              if (expandedView) {
748                expandedView.classList.remove('active');
749                expandedView.removeAttribute('style');
750              }
751
752
753              document.body.classList.remove('modal_on');
754              unlock();
755            }
756          });
757        }
758      });
759
760
761    } else {
762
763
764        const videoWrap = container.querySelector('.type_video');
765        const nativeVideo = videoWrap.querySelector('video');
766        const iframe = videoWrap.querySelector('iframe');
767  
768        // Fade out the video wrapper first
769        gsap.to(videoWrap, {
770          opacity: 0,
771          duration: 0.2,
772          ease: 'power2.inOut',
773          onComplete: () => {
774  
775            // Stop and reset native video
776            if (nativeVideo) {
777              nativeVideo.pause();
778              nativeVideo.removeAttribute('poster');
779              nativeVideo.removeAttribute('preload');
780              const sources = nativeVideo.querySelectorAll('source');
781              sources.forEach(source => {
782                source.removeAttribute('src');
783                source.removeAttribute('type');
784              });
785              nativeVideo.load();
786            }
787  
788            // Clear iframe src
789            if (iframe) {
790              iframe.removeAttribute('src');
791            }
792  
793            // Remove inline styles and reset
794            const allDescendants = container.querySelectorAll('*');
795            allDescendants.forEach(el => el.removeAttribute('style'));
796            container.removeAttribute('style');
797  
798            // Restore main header
799            main_header.classList.remove('none');
800  
801            // Fade out and fully clean container
802            gsap.to(container, {
803              opacity: 0,
804              duration: 0.3,
805              ease: 'power2.inOut',
806              onComplete: () => {
807
808                document.body.classList.remove('modal_on');
809                unlock();
810
811                container.classList.remove('active', 'video-modal');
812                if (closeBtn) closeBtn.onclick = null;
813              }
814            });
815          }
816        });
817
818
819    }    
820  }
821  
822  

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.