PageSourceSearch

https://www.goodwaytechs.com/assets/js/unit/content-detail.js

js goodwaytechs.com collected 2026-10-04 04:04:16 UTC 20,017 bytes, 470 lines download raw bytes

1class ScriptContentDetail extends Script {
2  constructor(params) {
3    super(params)
4    this.$dom = document.querySelector(`#${this.attrId}`)
5    this.videoUrl = this.$dom.querySelector('[video-src]')?.getAttribute('video-src') || ''
6    this.config = JSON.parse(this.utilts.htmlDecode(params.config))
7    this.isMp4 = /\.(mp4|m3u8)(\?|$)/i.test(this.videoUrl)
8    this.isIframe = /^\s*<iframe[^>]*>[\s\S]*?<\/iframe>\s*$/.test(this.videoUrl)
9
10    let methodMap = {
11      default: this.config.method,
12      md: this.config.method_md,
13      xl: this.config.method_xl
14    }
15    this.method = methodMap[this.utilts.getScreen()]
16    this.relate_product = JSON.parse(this.utilts.htmlDecode(params.relate_product))
17    this.modal = null
18    this.inquiryDrawer = null
19    // 是否展开导购产品
20    this.isExpand = true
21    this.initStatus = 0
22    this.xgPlayer = null
23  }
24  async init() {
25    if (this.initStatus === 0) {
26      this.initStatus++
27      this.initStickyEvent()
28      // 绑定视频相关事件
29      this.initNodeEvent()
30      // 其他相关事件
31      this.initSwiper()
32      this.initModal()
33      this.initDrawer()
34      this.initMoreTextEvent()
35    }
36  }
37  initStickyEvent() {
38    const videoInner = this.$dom.querySelector('.video-inner')
39    if (this.utilts.getScreen() === 'default' && !this.utilts.checkDesign() && !videoInner.destroyFn) {
40      let offsetTop = 0
41      let headerBlock = document.querySelector(`[package-block-type="header"]`)
42      if (['2', '3'].includes(headerBlock?.getAttribute('position'))) {
43        offsetTop = headerBlock.offsetHeight
44      }
45      this.utilts.stickyByViewport(videoInner, offsetTop, 999, { width: '100vw', clearLeft: true })
46    }
47  }
48  /** 绑定播放事件 */
49  initNodeEvent() {
50    let config = JSON.parse(JSON.stringify(this.config))
51    let $el = this.$dom.querySelector('[video-src]')
52    if (!$el) return
53    let url = this.videoUrl
54    const videothumbDom = $el.querySelector('.base-video__thumb img')
55    const videothumb = videothumbDom.getAttribute('src') || videothumbDom.getAttribute('lazy-src')
56    const isIframe = this.isIframe
57    const showMask = config.show_mask * 1
58    const opacity = showMask?`filter:brightness(${config.opacity});`:''
59    const showControls = config.controls && config.controls.toString() === '1' || false
60    if (url) {
61      // 重播
62      const replayWrap =
63        `<div class="base-video__wrap--replay">
64          <div class="replay-icon">
65            <svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%">
66              <path class="ytp-svg-fill" d="M 18,11 V 7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z" id="ytp-id-125"></path>
67            </svg>
68          </div>
69        </div>`
70
71      const videoList = [
72        `<div class="base-video__wrap">
73          ${showControls && this.method=== '2' ? '<div class="base-video__wrap--close">×</div>': ''}
74          ${showControls && this.method=== '2' ? replayWrap : replayWrap}
75          <video
76            style="height:auto;display:block;margin:0 auto;max-width:100%;${opacity}"
77            autoplay
78            controlsList="nodownload"
79            webkit-playsinline="true"
80            playsinline="true"
81            x5-video-player-type="h5-page"
82            preload="metadata"
83            src="${url}"
84            ${config.loop && config.loop.toString() === '1' ? ' loop' : ''}
85            ${config.muted && config.muted.toString() === '1' ? ' muted' : ''}
86            ${showControls ? ' controls' : '' }
87            poster="${videothumb}"
88          ></video>
89        </div>`,
90        '<iframe '+ opacity +' width="100%" frameborder="0" allow="autoplay" allowfullscreen="true" webkitallowfullscreen="true" mozallowfullscreen="true" src="' + url + '"></iframe>'
91      ]
92      let videoHtml;
93      let iframeUrl = []
94      if (isIframe) {
95        iframeUrl = globalThis.Server.iframeAutoplay(url).split('<iframe')
96        videoHtml = `<iframe style="filter:brightness(${showMask?config.opacity:'1'})"`+iframeUrl[1]
97      } else if (this.isMp4) {
98        this.xgPlayer = new VideoPlayer($el.querySelector('.video-container'), url, {
99          ...config,
100          playerConfig: {
101            poster: videothumb,
102          }
103        })
104        const relateProductEle = this.$dom.querySelector('.relate-product-inner')
105        this.xgPlayer.onControls('show', () => { 
106          if (this.utilts.getScreen() === 'xl') {
107            relateProductEle.classList.add('show-video-controls')
108          }
109        })
110        this.xgPlayer.onControls('hide', () => { 
111          if (this.utilts.getScreen() === 'xl') {
112            relateProductEle.classList.remove('show-video-controls')
113          }
114        })
115        return
116      } else {
117        videoHtml = globalThis.Server.iframeAutoplay(videoList[1])
118      }
119      // 当前播放
120      const playVideo = $el.playVideo = () => {
121        const videothumb = $el.querySelector('.base-video__thumb img')
122        const videotDom = $el.querySelector('.base-video__preview video')
123        const videoPoster = videothumb && (videothumb.currentSrc || videothumb.getAttribute('src') || videothumb.getAttribute('lazy-src'))
124        const $preview = $el.querySelector('.base-video__preview')
125        const $videos = $preview.querySelectorAll('video, iframe')
126        $preview.classList.add('play')
127        // $el.getAttribute('videoscale') && $el.classList.add('base-video--scale')
128        $el.classList.add('play')
129        if ($preview && !$videos.length) {
130          const template = document.createElement('template')
131          template.innerHTML = videoHtml
132          const videos = Array.from(template.content.children)
133          videos.forEach(video => {
134            $preview.insertAdjacentElement('beforeend', video)
135          })
136        }
137        if (this.$carouselEl.swiper) {
138          this.$carouselEl.swiper.autoplay.stop()
139        }
140        if (videothumb && videotDom) videotDom.poster = videothumb.getAttribute('src') || videothumb.getAttribute('lazy-src')
141        $el.querySelector('.base-video__preview').classList.add('play')
142        // $el.getAttribute('videoscale') && $el.classList.add('base-video--scale')
143        $el.classList.add('play')
144        const closeBtn = $el.querySelector('.base-video__preview').querySelector('.base-video__wrap--close')
145        const replayBtn = $el.querySelector('.base-video__preview').querySelector('.replay-icon')
146        if (showControls && closeBtn) {
147          const closeVideo = (event) => {
148            event.stopPropagation()
149            event.preventDefault()
150            const videoWrapDom =  $el.querySelector('.base-video__preview').querySelector('.base-video__wrap')
151            if (videoWrapDom) {
152              videoWrapDom?.remove()
153              $el.querySelector('.base-video__preview')?.classList.remove('play')
154              $el.classList.remove('play')
155            }
156          }
157          closeBtn.addEventListener('click', closeVideo, { once: true })
158        }
159        // if (showControls && replayBtn && this.config.auto_reset !== '1') {
160        if (showControls && replayBtn ) {
161          const videoDom = $el.querySelector('.base-video__wrap video')
162          const replayWrapDom = $el.querySelector('.base-video__preview').querySelector('.base-video__wrap--replay')
163          const showReplayBtn = (event) => {
164            event.stopPropagation()
165            event.preventDefault()
166            if(replayWrapDom) {
167              replayWrapDom.style.display = 'flex'
168              videoDom.controls = false
169            }
170          }
171          videoDom?.addEventListener('ended', showReplayBtn)
172          const replay = (event) => {
173            event.stopPropagation()
174            event.preventDefault()
175            videoDom.currentTime = 0
176            videoDom.play()
177            replayWrapDom.style.display = 'none'
178            videoDom.controls = true
179          }
180          replayBtn?.addEventListener('click', replay)
181        }
182        let myVideo = $el.querySelectorAll('video')
183        myVideo.forEach(elVideo => {
184          if (videoPoster) {
185            elVideo.poster = videoPoster
186          }
187        })
188      }
189      const clickPlayVideo = (e) => {
190        e.stopPropagation()
191        e.stopImmediatePropagation()
192        if (!$el.querySelector('.base-video__preview').classList.contains('play')) {
193          // 有视频
194          // e.preventDefault()
195          playVideo()
196        }
197
198        // if (/\.mp4(\?|$)/.test(url) && window.app.utilts.checkScreenMobile()) {
199        // 	// 手机端
200        // 	$el.querySelector('video').requestFullscreen()
201        // }
202        return false
203      }
204      if (config.autoplay.toString() === '1' && this.isMp4 && $el.querySelector('.base-video__preview').classList.value.indexOf('play') === -1) {
205        // 自动播放
206        playVideo()
207        if (this.config.auto_reset > 0) {
208          $el.addEventListener('click', clickPlayVideo, {useCapture: false})
209        }
210      } else {
211        // 手动播放
212        if (isIframe || !this.isMp4) {
213          videoHtml = this.config.default_src ? videoHtml : globalThis.Server.iframeAutoplayClear(videoHtml, config.muted && config.muted.toString() === '1')
214        }
215        $el.addEventListener('click', clickPlayVideo, {useCapture: false})
216      }
217    }
218  }
219  /**检查视频链接是否有效 */
220  _checkVideoUrl(url, callback, errBack) {
221    let fragment = document.createDocumentFragment()
222    let video = document.createElement('video')
223    video.src = url
224    fragment.append(video)
225    video.onloadedmetadata = () => {
226      if (callback) callback()
227    }
228    video.onerror = () => {
229      if (errBack) errBack()
230    }
231  }
232  initSwiper() {
233    const loadSwiper = () => {
234      let relateProSwiper = null
235      if (this.config.show_product && this.relate_product.length) {
236        relateProSwiper = new Swiper(`#${this.attrId} .relate-product-container`, {
237          centeredSlides: true,
238          autoplay: {
239            delay: 5000,
240            disableOnInteraction: false,
241            pauseOnMouseEnter: true
242          },
243          pagination: {
244            el: ".swiper-pagination",
245            clickable: true
246          }
247        });
248        relateProSwiper.el.onmouseover = function(){
249          relateProSwiper.autoplay.stop();
250        }
251        relateProSwiper.el.onmouseout = function(){
252          relateProSwiper.autoplay.start();
253        }
254        if (this.utilts.getScreen() === 'default') {
255          this.$dom.querySelector('.content-body').style.paddingTop = '0px'
256          this.$dom.querySelector('.relate-product-container').style.paddingTop = '15px'
257          this.$dom.querySelector('.relate-product-container').style.paddingBottom = '15px'
258        }
259      }
260      const allSlide = this.$dom.querySelectorAll('.relate-product-container .swiper-slide')
261      if (allSlide.length) {
262        allSlide.forEach((val, index) => {
263          val.addEventListener('click', (e) => {
264            // console.log(e);
265            if (relateProSwiper && (e.target.classList.contains('inquire-img') || e.target.classList.contains('inquire-iconfont'))) {
266              relateProSwiper.slideTo(0, 0, false)
267            } else {
268              if (index === 0 && this.config.show_inquire) {
269                if (this.utilts.getScreen() === 'default') {
270                  // this.inquiryDrawer.open()
271                  this.showInquire()
272                } else {
273                  this.modal.open()
274                }
275              } else {
276                const link = val.getAttribute('data-link')
277                window.open(globalThis.Server.getRinseHref(link, window.app.info.site))
278              }
279            }
280          })
281        })
282      }
283      const moreProSlide = this.$dom.querySelectorAll('.more-product-list .swiper-slide')
284      if (moreProSlide.length) {
285        moreProSlide.forEach((val, index) => {
286          val.addEventListener('click', (e) => {
287            // console.log(e);
288            if (index === 0 && this.config.show_inquire) {
289              this.modal.open()
290            } else {
291              const link = val.getAttribute('data-link')
292              window.open(globalThis.Server.getRinseHref(link, window.app.info.site))
293            }
294          })
295        })
296      }
297    }
298    loadSwiper()
299    if (this.utilts.getScreen() === 'xl' && this.xgPlayer) {
300      const relateProductEle = this.$dom.querySelector('.relate-product-inner')
301      relateProductEle.addEventListener('mouseenter', () => {
302        this.xgPlayer.checkHideControls(true)
303      })
304    }
305    const relateProductInner = this.$dom.querySelector('.relate-product-inner .content-body')
306    const relateProductContainer = this.$dom.querySelector('.relate-product-container')
307    const closeIcon = this.$dom.querySelector('.close-icon')
308    const expandIcon = this.$dom.querySelector('.relate-product-icon')
309    const moreIcon = this.$dom.querySelector('.more-icon')
310    const moreProduct = this.$dom.querySelector('.more-relate-product')
311    const moreProductClose = this.$dom.querySelector('.more-product-header__close')
312    if (this.utilts.getScreen() !== 'default') closeIcon?.classList.remove('d-none')
313    closeIcon?.addEventListener('click', () => {
314      this.isExpand = false
315      closeIcon.classList.add('d-none')
316      relateProductContainer.classList.add('d-none')
317      expandIcon.classList.remove('d-none')
318      relateProductInner.classList.add('set-width')
319      if (moreIcon) moreIcon.classList.add('d-none')
320      if (moreProduct) moreProduct.classList.add('d-none')
321    })
322    relateProductInner?.addEventListener('click', (e) => {
323      if (!e.target.classList.contains('icon-action-close-2') && !this.isExpand) {
324        this.isExpand = true
325        closeIcon?.classList.remove('d-none')
326        relateProductContainer.classList.remove('d-none')
327        expandIcon.classList.add('d-none')
328        relateProductInner.classList.remove('set-width')
329        if (moreIcon) moreIcon.classList.remove('d-none')
330      }
331    })
332    if (this.utilts.getScreen() === 'default') return
333    if (moreIcon) {
334      moreIcon.addEventListener('click', () => {
335        moreProduct.classList.remove('d-none')
336      })
337    }
338    if (moreProductClose) {
339      moreProductClose.addEventListener('click', () => {
340        moreProduct.classList.add('d-none')
341      })
342    }
343  }
344  initModal() {
345    if (this.utilts.getScreen() === 'default') return
346    this.modal = new Modal()
347    const $modal = this.$dom.querySelector('.inquiry-modal-inner')
348    if (!$modal) return
349    this.modal.init({
350      body: $modal,
351      title_style: 1,
352      title: this.utilts.$t('Send Inquiry'),
353      size: 'lg',
354      className: 'inquiry-modal content-detail-inquiry',
355      onSubmit: () => {
356        $modal.querySelector(`.btn`).click()
357      }
358    })
359  }
360  initDrawer() {
361    if (this.utilts.getScreen() === 'default') {
362      const moreIcon = this.$dom.querySelector('.more-icon')
363      moreIcon?.addEventListener('click', () => {
364        // const videoInner = this.$dom.querySelector('.video-inner')
365        // const videoRect = videoInner.getBoundingClientRect()
366        let drawer = new Drawer()
367        drawer.setOptions({
368          title: this.$dom.querySelector('.more-relate-product .more-product-header__inner').innerHTML,
369          className: 'modal-more-product__info unit-content-detail',
370          content: this.$dom.querySelector('.more-relate-product .more-product-list__inner').innerHTML,
371          hiddentMask: true
372          // bottomVal: videoRect.bottom
373        })
374        drawer.open()
375        const moreProSlide = document.querySelectorAll('.modal-more-product__info .swiper-slide')
376        if (moreProSlide.length) {
377          moreProSlide.forEach((val, index) => {
378            val.addEventListener('click', () => {
379              if (index === 0) {
380                this.showInquire()
381              } else {
382                const link = val.getAttribute('data-link')
383                window.open(globalThis.Server.getRinseHref(link, window.app.info.site))
384              }
385            })
386          })
387        }
388      })
389    }
390  }
391  showInquire() {
392    const inquireEle = this.$dom.querySelector('.unit-detail-button-inquiry')
393    const modalMorePro = document.querySelector('.modal-more-product__info')
394    const copyrightEle = document.querySelector('[package-block-type="copyright"]')
395    inquireEle.style.left =  '0px'
396    inquireEle.classList.add('inquiry-drawer-container', 'active')
397    inquireEle.children[0].classList.remove('d-none')
398    if (this.utilts.getScreen() === 'default') {
399      // const videoInner = this.$dom.querySelector('.video-inner')
400      // const videoRect = videoInner.getBoundingClientRect()
401      // inquireEle.style.height = `calc(100vh - ${videoRect.bottom}px - var(--SAIB))`
402    }
403    // this.$dom.querySelector('.inquiry-mask').classList.add('active')
404    document.body.style.overflow = 'hidden'
405    copyrightEle.style.opacity = 0
406    copyrightEle.style['pointer-events'] = 'none'
407    const parents = this.utilts.getParentsByAttr(this.$dom, 'package-item', 'block')
408    parents[0].style.position = 'relative'
409    const cellDom = this.utilts.getParentsByAttr(this.$dom, 'package-item', 'cell')
410    cellDom[0].style.zIndex = 99
411    document.querySelector('[package-group="block"]').style.zIndex = 111
412    if (modalMorePro?.classList.contains('active')) {
413      document.querySelector('#app').style.position = 'relative'
414      document.querySelector('#app').style.zIndex = 1000
415    }
416    const closeInquire = inquireEle.querySelector('.drawer-close-btn')
417    closeInquire.addEventListener('click', () => {
418      inquireEle.classList.remove('active')
419      // this.$dom.querySelector('.inquiry-mask').classList.remove('active')
420      inquireEle.children[0].classList.add('d-none')
421      if (!document.querySelector('.modal-more-product__info.active')) document.body.style.overflow = ''
422      copyrightEle.style.removeProperty('opacity')
423      copyrightEle.style.removeProperty('pointer-events')
424      parents[0].style.removeProperty('position')
425      cellDom[0].style.removeProperty('zIndex')
426      document.querySelector('[package-group="block"]').style.removeProperty('zIndex')
427      if (modalMorePro?.classList.contains('active')) {
428        document.querySelector('#app').style.removeProperty('position')
429        document.querySelector('#app').style.removeProperty('zIndex')
430      }
431    })
432  }
433  initMoreTextEvent() {
434    setTimeout(() => {
435      const content = this.$dom.querySelector('.unit-content-detail__summary .text-inner');
436      const moreBtn = this.$dom.querySelector('.unit-content-detail__summary .more-text-btn')
437      if (!content) return
438      console.log('content.scrollHeight', content.scrollHeight, 'clientHeight', content.clientHeight);
439      if (content.scrollHeight > content.clientHeight) {
440        const tempSpan = document.createElement('span');
441        tempSpan.textContent = content.textContent;
442        tempSpan.style.visibility = 'hidden';
443        tempSpan.style.whiteSpace = 'nowrap';
444        const range = document.createRange();
445        range.selectNodeContents(content);
446        const rects = range.getClientRects();
447        const secondLineEnd = rects[1]; // 第二行的矩形信息
448        moreBtn.classList.remove('d-none')
449      }
450      moreBtn.addEventListener('click', () => {
451        if (content.classList.contains('rich-text')) {
452          content.classList.remove('rich-text')
453          moreBtn.innerText = this.utilts.$t('Put Away')
454        } else {
455          content.classList.add('rich-text')
456          moreBtn.innerText = this.utilts.$t('More')
457        }
458        // if (content.classList.contains('d-none')) {
459        //   content.classList.remove('d-none')
460        //   moreBtn.innerText = 'Put Away'
461        //   moreBtn.style.marginTop = '8px'
462        // } else {
463        //   content.classList.add('d-none')
464        //   moreBtn.innerText = 'Check Description'
465        //   moreBtn.style.marginTop = '0px'
466        // }
467      })
468    }, 100)
469  }
470}

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.