PageSourceSearch

https://mundoestrellagalicia.es/etc.clientlibs/catalog/clientlibs/…-32234edce85a4d718d74a93fc3db5b05-lc.min.js

js mundoestrellagalicia.es collected 2026-10-03 20:23:49 UTC 42,423 bytes, 1,120 lines download raw bytes

1class CardList {
2  constructor (component) {
3    this.lang = document.querySelector('html').getAttribute('lang')
4    this.component = component
5    this.resourcePath = this.component.getAttribute('data-component-path')
6    this.cardLinkText = this.component.getAttribute('data-cardLinkText')
7    this.cardType = this.component.getAttribute('data-cardType')
8    this.linkTextShare = this.component.getAttribute('data-linkTextShare')
9    this.isMobile = () => !window.matchMedia('(min-width: 768px)').matches
10    this.isEdit = this.component.getAttribute('is-edit-mode') === 'true'
11    this.loader = this.component.querySelector('.list-component__cards__loader')
12    this.arrow = this.component.querySelector('.list-component__arrow')
13    this.listLayout = this.component.getAttribute('data-listLayout')
14    this.cardsPerRowClass = this.component.getAttribute('data-cardsPerRow')
15    this.cardsPerRow = this.listLayout === 'default' ? this.cardsPerRowClass : ''
16    this.isNotFound = this.component.querySelector(
17      '.list-component__not-found'
18    )
19    this.inputSearch = this.component.querySelector(
20      '#list-component-input-search'
21    )
22    this.inputSearchAdvancedFilter = this.component.querySelector(
23      '#list-component__advanced-filters-input-search'
24    )
25    this.listComponentCards = this.component.querySelector(
26      '.list-component__cards'
27    )
28    this.buttonFilters = this.component.querySelector(
29      '.list-component__advanced-filters__heading__button'
30    )
31    this.filters = this.component.querySelector(
32      '.list-component__advanced-filters__filter-by'
33    )
34    this.buttonCloseFilters = this.filters?.querySelector(
35      '.list-component__advanced-filters__filter-by__icon'
36    )
37    this.buttonResetFilters = this.filters?.querySelector(
38      '.list-component__advanced-filters__filter-by__delete'
39    )
40    this.currentFilter = 'all'
41    this.cardsPageClass = `list-component__cards__cards-page ${this.listLayout} ${this.cardsPerRow}`.trim()
42    this.maxItems = this.component.getAttribute('data-maxItems')
43
44    const isLayout2 = this.listLayout === 'layout2' || (this.listLayout === 'default' && this.cardsPerRowClass === 'items-4')
45    const desktopMax = isLayout2 ? 12 : 9
46
47    if (this.isMobile()) {
48      if (this.maxItems) {
49        this.limitDisplayedCards = this.maxItems < 4 ? this.maxItems : 4
50      } else {
51        this.limitDisplayedCards = 4
52      }
53    } else {
54      if (this.maxItems) {
55        this.limitDisplayedCards =
56          this.maxItems < desktopMax ? this.maxItems : desktopMax
57      } else {
58        this.limitDisplayedCards = desktopMax
59      }
60    }
61    this.displayedCards = {
62      all: this.limitDisplayedCards
63    }
64    this.dateFormat = this.component.getAttribute('data-dateFormat')
65  }
66
67  async init () {
68    this.loader.classList.remove('list-component__cards__loader--hidden')
69    this.data = await this.getData()
70    if (this.data) {
71      this.filteredData = this.data.slice()
72      this.showNotFound(this.filteredData.length)
73      const firstGroup = this.filteredData.slice(0, this.limitDisplayedCards)
74      this.resultHTML = `<div class="${this.cardsPageClass}">${this.generateHTML(firstGroup)}</div>`
75      if (this.filteredData.length > this.limitDisplayedCards) {
76        this.arrow.classList.remove('list-component__arrow--hidden')
77      } else {
78        this.arrow.classList.add('list-component__arrow--hidden')
79      }
80      this.listComponentCards.innerHTML = this.resultHTML
81      this.loader.classList.add('list-component__cards__loader--hidden')
82      this.filterByTag()
83      this.scroll()
84      if (this.inputSearch) {
85        this.inputSearch.addEventListener('input', this.searchPost)
86      } else {
87        this.inputSearchAdvancedFilter?.addEventListener('input', this.searchPost)
88      }
89    } else {
90      this.loader.classList.add('list-component__cards__loader--hidden')
91      this.arrow.classList.add('list-component__arrow--hidden')
92      this.showNotFound(0)
93    }
94    this.buttonFilters?.addEventListener('click', () => {
95      this.showFilters()
96    })
97    HdR.share()
98
99    this.buttonResetFilters?.addEventListener('click', () => {
100      this.resetFilters()
101    })
102
103    // Escuchar el evento personalizado para filtrar las tarjetas
104    document.addEventListener('customSelectChange', this.customSelectChange.bind(this))
105  }
106
107  customSelectChange (e) {
108    // Asegura de que el evento afecte solo a este componente
109    if (!this.component.contains(document.getElementById(e.detail.selectId))) {
110      return
111    }
112
113    const selectElement = document.getElementById(e.detail.selectId);
114    if (selectElement) {
115      const ariaLabel = selectElement.getAttribute('aria-label');
116      if (e.detail.selectId.includes('location')) {
117        this.resetFilters(ariaLabel)
118        this.filterByLocation(e.detail.value)
119      } else if (e.detail.selectId.includes('dates')) {
120        this.resetFilters(ariaLabel)
121        this.filterCardsByDate(e.detail.value)
122      }
123    }
124  }
125
126  resetFilters (launchingFilter = null) {
127    if (this.filters !== null) {
128      // Clear selects
129      const selects = this.filters.querySelectorAll('.hdr-custom-select')
130      if (selects) {
131        selects.forEach((select) => {
132          const selectId = select.id.replace('-div', '')
133          const originalSelect = document.getElementById(selectId)
134          const placeholder = originalSelect.getAttribute('aria-label')
135          if (placeholder !== launchingFilter) {
136            select.textContent = placeholder
137            select.classList.remove('has-selection')
138            originalSelect.selectedIndex = -1
139          }
140        })
141      }
142    }
143    // Clear search field
144    if (this.inputSearchAdvancedFilter && launchingFilter !== 'Input') {
145      this.inputSearchAdvancedFilter.value = ''
146    }
147    // Clear tags
148    if (launchingFilter !== 'Tag') {
149      this.updateActiveTab()
150      this.currentFilter = 'all'
151    }
152
153    // Filtered
154    this.displayedCards = { all: this.limitDisplayedCards }
155    this.filteredData = this.data.slice()
156    this.showNotFound(this.filteredData.length)
157    HdR.share()
158    const firstGroup = this.filteredData.slice(0, this.limitDisplayedCards)
159    this.resultHTML = `<div class="${this.cardsPageClass}">${this.generateHTML(firstGroup)}</div>`
160    this.listComponentCards.innerHTML = this.resultHTML
161    if (this.filteredData.length > this.limitDisplayedCards) {
162      this.arrow.classList.remove('list-component__arrow--hidden')
163    } else {
164      this.arrow.classList.add('list-component__arrow--hidden')
165    }
166  }
167
168  showFilters = () => {
169    this.filters.style.display = 'flex'
170    this.buttonCloseFilters?.addEventListener('click', () => {
171      this.hideFilters()
172    })
173  }
174
175  hideFilters = () => {
176    this.filters.style.display = 'none'
177  }
178
179  showNotFound = (dataLength) => {
180    if (dataLength === 0) {
181      this.isNotFound.classList.remove('list-component__not-found--hidden')
182      this.listComponentCards.classList.add('list-component__cards--hidden')
183      this.loader.style.display = 'none'
184    } else {
185      this.listComponentCards.classList.remove('list-component__cards--hidden')
186      this.isNotFound.classList.add('list-component__not-found--hidden')
187    }
188  }
189
190  updateActiveTab = (index = 0) => {
191    const tags = this.component.querySelectorAll('.tag-item')
192    tags.forEach((tag) => tag.classList.remove('active'))
193    if (tags.length > 0) tags[index].classList.add('active')
194  }
195
196  searchPost = (e) => {
197    this.resetFilters('Input')
198    this.updateActiveTab()
199    this.currentFilter = 'all'
200    this.displayedCards = {
201      all: this.limitDisplayedCards
202    }
203
204    const value = e.srcElement.value.toLowerCase()
205    const normalizedValue = value
206      .normalize('NFD')
207      .replace(/[\u0300-\u0302\u0304-\u036f]/g, '') // Quita acentos
208    const filteredData = this.data.filter((item) => {
209      const normalizedTitle = item.title
210        ? item.title.toLowerCase().normalize('NFD').replace(/[\u0300-\u0302\u0304-\u036f]/g, '')
211        : ''
212      const normalizedDescription = item.description
213        ? item.description.toLowerCase().normalize('NFD').replace(/[\u0300-\u0302\u0304-\u036f]/g, '')
214        : ''
215      return (
216        normalizedTitle.includes(normalizedValue) ||
217        normalizedDescription.includes(normalizedValue)
218      )
219    })
220    const displayedData = filteredData.slice(
221      0,
222      this.displayedCards[this.currentFilter]
223    )
224    this.listComponentCards.innerHTML = `<div class="${this.cardsPageClass}">${this.generateHTML(displayedData)}</div>`
225    HdR.share()
226    this.showNotFound(filteredData.length)
227    this.filteredData = filteredData
228    if (filteredData.length > this.displayedCards[this.currentFilter]) {
229      this.arrow.classList.remove('list-component__arrow--hidden')
230    } else {
231      this.arrow.classList.add('list-component__arrow--hidden')
232    }
233  }
234
235  scroll () {
236    this.arrow.addEventListener('click', () => {
237      const totalCards = this.filteredData.length
238      const remainingCards =
239        totalCards - this.displayedCards[this.currentFilter]
240      const nextCards = this.filteredData.slice(
241        this.displayedCards[this.currentFilter],
242        this.displayedCards[this.currentFilter] +
243          Math.min(this.limitDisplayedCards, remainingCards)
244      )
245
246      if (nextCards.length > 0) {
247        const nextHTML = this.generateHTML(nextCards)
248        const pageDiv = document.createElement('div')
249        pageDiv.className = this.cardsPageClass
250        pageDiv.innerHTML = nextHTML
251        this.listComponentCards.appendChild(pageDiv)
252
253        this.displayedCards[this.currentFilter] += nextCards.length
254
255        if (this.displayedCards[this.currentFilter] >= totalCards) {
256          this.arrow.classList.add('list-component__arrow--hidden')
257        }
258      } else {
259        this.arrow.classList.remove('list-component__arrow--hidden')
260      }
261      HdR.share()
262    })
263  }
264
265  filterCards (tagId, displayedCards) {
266    let filteredData = this.data
267    if (tagId !== 'all') {
268      filteredData = this.data.filter(
269        (item) => item.tags && item.tags.includes(tagId)
270      )
271    }
272
273    const displayedData = filteredData.slice(0, displayedCards[tagId]) 
274    this.listComponentCards.innerHTML = `<div class="${this.cardsPageClass}">${this.generateHTML(displayedData)}</div>`
275    HdR.share()
276    this.showNotFound(filteredData.length)
277
278    if (filteredData.length > displayedCards[tagId]) {
279      this.arrow.classList.remove('list-component__arrow--hidden')
280    } else {
281      this.arrow.classList.add('list-component__arrow--hidden')
282    }
283
284    this.currentFilter = tagId
285    this.filteredData = filteredData
286  }
287
288  filterByLocation (tagId) {
289    this.updateActiveTab()
290    this.currentFilter = 'all'
291    const displayedCards = {
292      all: this.limitDisplayedCards
293    }
294
295    this.displayedCards[tagId] = this.limitDisplayedCards
296    displayedCards[tagId] = this.limitDisplayedCards
297
298    this.filterCards(tagId, displayedCards)
299  }
300
301  filterCardsByDate (month) {
302    this.updateActiveTab()
303    this.currentFilter = 'all'
304    this.displayedCards = {
305      all: this.limitDisplayedCards
306    }
307
308    const monthNames = [
309      'january', 'february', 'march', 'april', 'may', 'june',
310      'july', 'august', 'september', 'october', 'november', 'december'
311    ]
312    const monthIndex = monthNames.indexOf(month.toLowerCase())
313
314    if (monthIndex === -1) {
315      console.error('Invalid month')
316      return []
317    }
318
319    const filteredData = this.data.filter((item) => {
320      const itemDate = new Date(item.dateStory)
321      return itemDate.getMonth() === monthIndex
322    })
323
324    const displayedData = filteredData.slice(
325      0,
326      this.displayedCards[this.currentFilter]
327    )
328    const pageClass = `list-component__cards__cards-page ${this.listLayout} ${this.cardsPerRow}`.trim()
329    const pageHTML = `<div class="${pageClass}">${this.generateHTML(displayedData)}</div>`
330    this.listComponentCards.innerHTML = pageHTML
331    HdR.share()
332    this.showNotFound(filteredData.length)
333    this.filteredData = filteredData
334    if (filteredData.length > this.displayedCards[this.currentFilter]) {
335      this.arrow.classList.remove('list-component__arrow--hidden')
336    } else {
337      this.arrow.classList.add('list-component__arrow--hidden')
338    }
339  }
340
341  filterByTag () {
342    const tagsContainer = this.component.querySelector('.list-component__tags-list')
343    const tags = tagsContainer?.querySelectorAll('.tag-item')
344    const displayedCards = {
345      all: this.limitDisplayedCards
346    }
347
348    if (tags) {
349      if (tags.length > 0) tags[0].classList.add('active')
350
351      tags.forEach((tag, index) => {
352        tag.addEventListener('click', () => {
353          this.resetFilters('Tag')
354          this.updateActiveTab(index)
355          const tagId = tag.dataset.id
356          this.displayedCards[tagId] = this.limitDisplayedCards
357          displayedCards[tagId] = this.limitDisplayedCards
358          this.filterCards(tagId, displayedCards)
359        })
360      })
361    }
362
363    this.displayedCards = displayedCards
364  }
365
366  generateHTML (data) {
367    let html = ''
368    data.forEach((item) => {
369      let date = ''
370      if (item.dateStory) {
371        const day = new Date(item.dateStory).getDate()
372        const month = new Date(item.dateStory).getMonth() + 1
373        const year = new Date(item.dateStory).getFullYear()
374        const formatDate = HdR.formatDate({ day, month, year, formatType: this.dateFormat })
375        date = `<span class="list-component__card__date list-component__card__date--mobile">${formatDate}</span>`
376      }
377
378      if (this.cardType === 'podcast') {
379        html += window.cardTemplates.solidPodcast(item, date, this.linkTextShare)
380      } else if (this.cardType === 'imageCard') {
381        html += window.cardTemplates.imageCard(item, date, this.cardLinkText)
382      } else if (this.cardType === 'productCard') {
383        html += window.cardTemplates.productCard(item, date, this.cardLinkText)
384      } else if (this.cardType === 'poster') {
385        html += window.cardTemplates.poster(item, date, this.cardLinkText)
386      } else {
387        html += window.cardTemplates.solid(item, date, this.cardLinkText)
388      }
389    })
390    return html
391  }
392
393  async getData () {
394    try {
395      const response = await fetch(`${this.resourcePath}.model.json`)
396      const jsonResponse = await response.json()
397      return jsonResponse.contentCardsJsonList
398    } catch (error) {
399      console.log('Error', error.message)
400      return null
401    }
402  }
403}
404
405document.addEventListener('DOMContentLoaded', () => {
406  const listComponent = [...document.querySelectorAll('.list-component')]
407
408  if (listComponent.length) {
409    listComponent.forEach(cmp => {
410      const cardList = new CardList(cmp)
411      cardList.init()
412    })
413  }
414})
415
416window.cardTemplates = {
417  // Content Card Solid View
418  solid: (item, date, cardLinkText) => `
419    <div class="list-component__card-wrapper">
420        <article class="list-component__card list-component__card--solid" ${
421            item.tagsList
422                ? item.tagsList.map((tag) => `data-id=${tag.id}`).join('')
423                : ''
424        }>
425            ${item.pagePath 
426                ? `<a class="list-component__card__container" href="${item.pagePath}">`
427                : `<div class="list-component__card__container">`
428                }
429                ${
430                item.featured && item.featured === 'true'
431                    ? '<span class="list-component__card__corner"><span class="icon-hdr-other-star-6"></span></span>'
432                    : ''
433                }
434                <div class="list-component__card__img-container">
435                    <picture class="list-component__card__img">
436                            <source srcset="${item.imagePath}?width=1440&preferwebp=true" media="(min-width: 1440px)" />
437                            <source srcset="${item.imagePath}?width=1280&preferwebp=true" media="(min-width: 1280px)" />
438                            <source srcset="${item.imagePath}?width=720&preferwebp=true" media="(min-width: 720px)" />
439                            <source srcset="${item.imagePath}?width=400&preferwebp=true" media="(min-width: 400px)" />
440                            <img loading="lazy" class="list-component__card__img" src="${item.imagePath}?width=400&preferwebp=true" alt="${item.imageAlt}" />
441                    </picture>
442                </div>
443                <div class="list-component__card__content">
444                    ${
445                    item.tagsList && item.tagsList.length
446                        ? `<div class="tag-list list-component__card__container-tags">${item.tagsList
447                            .map(
448                            (tag) =>
449                                `<span class="tag-item list-component__card__tag" data-id=${tag.id}>${tag.title}</span>`
450                            )
451                            .join(' ')}
452                        </div>`
453                        : ''
454                    }
455                    ${item.preTitle ? `<span class="list-component__card__pretitle">${item.preTitle}</span>` : ''}
456                    <span class="list-component__card__title">${
457                    item.title
458                    }</span>
459                    ${item.description ? `<div class="list-component__card__description">${item.description}</div>` : ''}
460                    <div class="list-component__card__post-button">
461                        ${date}
462                        ${ item.pagePath 
463                            ? `<span class="cta-link list-component__card__text">
464                                ${item.linkText ? item.linkText : cardLinkText}
465                                <span class="icon-hdr-arrows-arrow-right"></span>
466                            </span>`
467                            : ''
468                        }
469                    </div>
470                </div>
471            ${item.pagePath ? `</a>` : `</div>`}
472        </article>
473    </div>
474    `,
475
476  // Content Card Image View
477  imageCard: (item, date, cardLinkText) => `
478    <article class="list-component__card list-component__card--image" ${
479        item.tagsList
480        ? item.tagsList.map((tag) => `data-id=${tag.id}`).join('')
481        : ''
482    }>
483        ${item.pagePath 
484            ? `<a href="${item.pagePath}">`
485            : `<div>`
486            }
487            ${
488            item.tagsList && item.tagsList.length
489                ? `<div class="tag-list list-component__card__tag-list">${item.tagsList
490                    .map(
491                    (tag) =>
492                        `<span class="tag-item list-component__card__tag" data-id=${tag.id}>${tag.title}</span>`
493                    )
494                    .join(' ')}
495                </div>`
496                : ''
497            }
498            ${
499                item.featured && item.featured === 'true'
500                ? '<span class="list-component__card__corner"><span class="icon-hdr-other-star-6"></span></span>'
501                : ''
502            }
503            <div class="list-component__card__img-container">
504                <picture class="list-component__card__img">
505                        <source srcset="${item.imagePath}?width=1440&preferwebp=true" media="(min-width: 1440px)" />
506                        <source srcset="${item.imagePath}?width=1280&preferwebp=true" media="(min-width: 1280px)" />
507                        <source srcset="${item.imagePath}?width=720&preferwebp=true" media="(min-width: 720px)" />
508                        <source srcset="${item.imagePath}?width=400&preferwebp=true" media="(min-width: 400px)" />
509                        <img loading="lazy" class="list-component__card__img" src="${item.imagePath}?width=400&preferwebp=true" alt="${item.imageAlt}" />
510                </picture>
511            </div>
512            <div class="list-component__card__content">
513                <div class="list-component__card__post-button">
514                    ${date}
515                </div>
516                ${item.preTitle ? `<span class="list-component__card__pretitle">${item.preTitle}</span>` : ''}
517                <span class="list-component__card__title">${
518                    item.title
519                }</span>
520                ${item.description ? `<div class="list-component__card__description">${item.description}</div>` : ''}
521            </div>
522        ${item.pagePath ? `</a>` : `</div>`}
523    </article>
524    `,
525    
526  // Content Card Podcast View
527  solidPodcast: (item, date, linkTextShare) => `
528    <article class="list-component__card list-component__card--solidPodcast">
529        <a href="${item.pagePath}">
530        <div class="list-component__card__img-container" data-sly-test="${item.imagePath}">
531            <picture class="list-component__card__img">
532            <source srcset="${item.imagePath}?width=1440&preferwebp=true" media="(min-width: 1440px)" />
533            <source srcset="${item.imagePath}?width=1280&preferwebp=true" media="(min-width: 1280px)" />
534            <source srcset="${item.imagePath}?width=720&preferwebp=true" media="(min-width: 720px)" />
535            <source srcset="${item.imagePath}?width=400&preferwebp=true" media="(min-width: 400px)" />
536            <img loading="lazy" class="list-component__card__img"
537                src="${item.imagePath}?width=400&preferwebp=true"
538                alt="${item.imageAlt}" />
539            </picture>
540        </div>
541
542        <div class="list-component__card__content">
543            <a href="${item.pagePath}">
544            <span class="btn-play btn-play--primary btn-play--small">
545                <span class="btn-play__button">
546                <span class="btn-play__icon"></span>
547                </span>
548            </span>
549            </a>
550
551            ${item.preTitle ? `<span class="list-component__card__pretitle">${item.preTitle}</span>` : ''}
552            <span class="list-component__card__title">${item.title}</span>
553            ${item.description
554            ? `<div class="list-component__card__description">${item.description}</div>`
555            : ''}
556
557            <div class="list-component__card__post-button">
558            ${date}
559            <button type="button" class="btn-share list-component__card__share js-shareButton">
560                <span class="icon icon-hdr-basic-share"></span>
561                <span class="visually-hidden">${linkTextShare}</span>
562            </button>
563            </div>
564        </div>
565        </a>
566    </article>
567    `,
568
569  // Content Card Poster View
570  poster: (item, date, cardLinkText) => `
571    <article class="list-component__card list-component__card--poster" ${
572        item.tagsList
573            ? item.tagsList.map((tag) => `data-id=${tag.id}`).join('')
574            : ''
575    }>
576        ${item.pagePath 
577            ? `<a class="list-component__card__container" href="${item.pagePath}">`
578            : `<div class="list-component__card__container">`
579            }
580            ${
581            item.featured && item.featured === 'true'
582                ? '<span class="list-component__card__corner"><span class="icon-hdr-other-star-6"></span></span>'
583                : ''
584            }
585            <div class="list-component__card__img-container">
586                <picture class="list-component__card__img">
587                        <source srcset="${item.imagePath}?width=1440&preferwebp=true" media="(min-width: 1440px)" />
588                        <source srcset="${item.imagePath}?width=1280&preferwebp=true" media="(min-width: 1280px)" />
589                        <source srcset="${item.imagePath}?width=720&preferwebp=true" media="(min-width: 720px)" />
590                        <source srcset="${item.imagePath}?width=400&preferwebp=true" media="(min-width: 400px)" />
591                        <img loading="lazy" class="list-component__card__img" src="${item.imagePath}?width=400&preferwebp=true" alt="${item.imageAlt}" />
592                </picture>
593            </div>
594            <div class="list-component__card__content">
595                ${
596                item.tagsList && item.tagsList.length
597                    ? `<div class="tag-list list-component__card__container-tags">${item.tagsList
598                        .map(
599                        (tag) =>
600                            `<span class="tag-item list-component__card__tag" data-id=${tag.id}>${tag.title}</span>`
601                        )
602                        .join(' ')}
603                    </div>`
604                    : ''
605                }
606                ${item.preTitle ? `<span class="list-component__card__pretitle">${item.preTitle}</span>` : ''}
607                <span class="list-component__card__title">${
608                item.title
609                }</span>
610                ${item.description ? `<div class="list-component__card__description">${item.description}</div>` : ''}
611                <div class="list-component__card__post-button">
612                    ${date}
613                    ${ item.pagePath 
614                        ? `<span class="list-component__card__text">
615                            ${item.linkText ? item.linkText : cardLinkText}
616                            <span class="icon-hdr-arrows-arrow-right"></span>
617                        </span>`
618                        : ''
619                    }
620                </div>
621            </div>
622        ${item.pagePath ? `</a>` : `</div>`}
623    </article>
624    `,
625
626  // Content Card Image View
627  productCard: (item, date, cardLinkText) => `
628    <article class="list-component__card list-component__card--product" ${
629        item.tagsList
630        ? item.tagsList.map((tag) => `data-id=${tag.id}`).join('')
631        : ''
632    }>
633        ${item.pagePath
634            ? `<a href="${item.pagePath}">`
635            : `<div>`
636            }
637            <div class="list-component__card__img-container">
638                <picture class="list-component__card__img">
639                        <source srcset="${item.imagePath}?width=1440&preferwebp=true" media="(min-width: 1440px)" />
640                        <source srcset="${item.imagePath}?width=1280&preferwebp=true" media="(min-width: 1280px)" />
641                        <source srcset="${item.imagePath}?width=720&preferwebp=true" media="(min-width: 720px)" />
642                        <source srcset="${item.imagePath}?width=400&preferwebp=true" media="(min-width: 400px)" />
643                        <img loading="lazy" class="list-component__card__img" src="${item.imagePath}?width=400&preferwebp=true" alt="${item.imageAlt}" />
644                </picture>
645            </div>
646            <div class="list-component__card__content">
647                <span class="list-component__card__title">${
648                    item.title
649                }</span>
650                ${item.description ? `<div class="list-component__card__description">${item.description}</div>` : ''}
651            </div>
652        ${item.pagePath ? `</a>` : `</div>`}
653    </article>
654    `
655}
656
657// document.addEventListener('DOMContentLoaded', function() {
658//     document.querySelectorAll('.list-component__tags').forEach(function(container) {
659//         initTagsComponent(container);
660//     });
661// });
662
663// function initTagsComponent(container) {
664//     const wrapper = container.querySelector('.tag-list');
665//     const btn = container.querySelector('#btnViewMoreTags');
666//     const verMenosMas = document.querySelector('.tag-view-title');
667//     const inicializar = verMenosMas.dataset.inicializar;
668
669//     console.log("inicializar", inicializar, verMenosMas.textContent);
670    
671//     if (!wrapper || !btn) return;
672    
673//     const allTags = wrapper.querySelectorAll('.tag-item:not(.tag-item__btn-tags)');
674    
675//     if (allTags.length === 0) {
676//         btn.style.display = 'none';
677//         return;
678//     }
679    
680//     const TAGS_PER_LINE = 7;
681//     let isFullyExpanded = false;
682//     let isAnimating = false;
683//     let isDesktop = window.innerWidth >= 1024;
684    
685//     function checkIsDesktop() {
686//         return window.innerWidth >= 1024;
687//     }
688    
689//     function updateButtonNumber(count) {
690//         const numberSpan = btn.querySelector('.tag-view-number');
691//         if (numberSpan) {
692//             numberSpan.textContent = count;
693//             const digits = count.toString().length;
694//             numberSpan.setAttribute('data-digits', digits);
695//         }
696//     }
697    
698//     function updateButtonText(text, count) {
699//         const spanText = btn.querySelector('.tag-view-title');
700//         if (spanText) {
701//             spanText.textContent = text;
702//         }
703//         updateButtonNumber(count);
704//     }
705    
706//     function setMobileMode() {
707//         // Mostrar todos los tags en mobile
708//         allTags.forEach(function(tag) {
709//             tag.classList.remove('hidden');
710//             tag.style.display = '';
711//             tag.style.opacity = '';
712//             tag.style.transform = '';
713//             tag.style.maxWidth = '';
714//             tag.style.padding = '';
715//             tag.style.margin = '';
716//             tag.style.border = '';
717//             tag.style.transition = '';
718//         });
719        
720//         btn.style.display = 'none';
721//         btn.classList.remove('ver-menos');
722//         isFullyExpanded = true;
723//     }
724    
725//     function initDesktop() {
726//         allTags.forEach(function(tag, index) {
727//             tag.style.transition = 'none';
728            
729//             if (index < TAGS_PER_LINE) {
730//                 tag.classList.remove('hidden');
731//                 tag.style.display = '';
732//                 tag.style.opacity = '';
733//                 tag.style.transform = '';
734//                 tag.style.maxWidth = '';
735//                 tag.style.padding = '';
736//                 tag.style.margin = '';
737//                 tag.style.border = '';
738//             } else {
739//                 tag.classList.add('hidden');
740//                 tag.style.display = 'none';
741//                 tag.style.opacity = '0';
742//                 tag.style.transform = 'scale(0.8)';
743//                 tag.style.maxWidth = '0';
744//                 tag.style.padding = '0';
745//                 tag.style.margin = '0';
746//                 tag.style.border = 'none';
747//             }
748//         });
749        
750//         void wrapper.offsetHeight;
751        
752//         setTimeout(function() {
753//             allTags.forEach(function(tag) {
754//                 tag.style.transition = '';
755//             });
756//         }, 50);
757        
758//         const hiddenCount = allTags.length - TAGS_PER_LINE;
759//         if (hiddenCount > 0) {
760//             btn.style.display = '';
761//             btn.classList.remove('ver-menos');
762//             updateButtonText(verMenosMas.textContent, hiddenCount);
763//             isFullyExpanded = false;
764//         } else {
765//             btn.style.display = 'none';
766//             isFullyExpanded = true;
767//         }
768//     }
769    
770//     function showNextLine() {
771//         if (isAnimating) return;
772//         isAnimating = true;
773        
774//         const hiddenTags = wrapper.querySelectorAll('.tag-item.hidden');
775//         const currentVisible = allTags.length - hiddenTags.length;
776//         const nextMax = Math.min(currentVisible + TAGS_PER_LINE, allTags.length);
777        
778//         allTags.forEach(function(tag, index) {
779//             if (index < nextMax && tag.classList.contains('hidden')) {
780//                 tag.classList.remove('hidden');
781//                 tag.style.display = '';
782//                 tag.style.transition = 'all 0.8s cubic-bezier(0.4, 0, 0.2, 1)';
783//                 tag.style.opacity = '1';
784//                 tag.style.transform = 'scale(1)';
785//                 tag.style.maxWidth = '';
786//                 tag.style.padding = '';
787//                 tag.style.margin = '';
788//                 tag.style.border = '';
789//             }
790//         });
791        
792//         const remainingHidden = allTags.length - nextMax;
793        
794//         setTimeout(function() {
795//             if (remainingHidden > 0) {
796//                 btn.classList.remove('ver-menos');
797//                 updateButtonText(verMenosMas.textContent, remainingHidden);
798//                 isFullyExpanded = false;
799//             } else {
800//                 btn.classList.add('ver-menos');
801//                 updateButtonText(inicializar, 0);
802//                 isFullyExpanded = true;
803//             }
804//             isAnimating = false;
805//         }, 800);
806//     }
807    
808//     function resetToFirstLine() {
809//         if (isAnimating) return;
810//         isAnimating = true;
811        
812//         allTags.forEach(function(tag, index) {
813//             if (index < TAGS_PER_LINE) {
814//                 tag.classList.remove('hidden');
815//                 tag.style.transition = 'all 0.3s ease';
816//                 tag.style.display = '';
817//                 tag.style.opacity = '1';
818//                 tag.style.transform = 'scale(1)';
819//                 tag.style.maxWidth = '';
820//                 tag.style.padding = '';
821//                 tag.style.margin = '';
822//                 tag.style.border = '';
823//             } else {
824//                 tag.style.transition = 'all 0.3s ease';
825//                 tag.style.opacity = '0';
826//                 tag.style.transform = 'scale(0.8)';
827//                 tag.style.maxWidth = '0';
828//                 tag.style.padding = '0';
829//                 tag.style.margin = '0';
830//                 tag.style.border = 'none';
831//                 setTimeout(function() {
832//                     tag.classList.add('hidden');
833//                     tag.style.display = 'none';
834//                 }, 300);
835//             }
836//         });
837        
838//         setTimeout(function() {
839//             const hiddenCount = allTags.length - TAGS_PER_LINE;
840//             if (hiddenCount > 0) {
841//                 btn.classList.remove('ver-menos');
842//                 updateButtonText(verMenosMas.textContent, hiddenCount);
843//                 isFullyExpanded = false;
844//             }
845//             isAnimating = false;
846//         }, 400);
847//     }
848    
849//     function init() {
850//         isDesktop = checkIsDesktop();
851//         if (isDesktop) {
852//             initDesktop();
853//         } else {
854//             setMobileMode();
855//         }
856//     }
857    
858//     btn.addEventListener('click', function(e) {
859//         e.preventDefault();
860//         if (isAnimating) return;
861//         if (!checkIsDesktop()) return;
862        
863//         if (isFullyExpanded) {
864//             resetToFirstLine();
865//         } else {
866//             showNextLine();
867//         }
868//     });
869    
870//     let resizeTimeout;
871//     window.addEventListener('resize', function() {
872//         clearTimeout(resizeTimeout);
873//         resizeTimeout = setTimeout(function() {
874//             const wasDesktop = isDesktop;
875//             isDesktop = checkIsDesktop();
876            
877//             if (wasDesktop !== isDesktop) {
878//                 if (isDesktop) {
879//                     initDesktop();
880//                 } else {
881//                     setMobileMode();
882//                 }
883//             }
884//         }, 250);
885//     });
886    
887//     init();
888// }
889
890document.addEventListener('DOMContentLoaded', function() {
891    document.querySelectorAll('.list-component__tags').forEach(function(container) {
892        initTagsComponent(container);
893    });
894});
895
896function initTagsComponent(container) {
897    const wrapper = container.querySelector('.tag-list');
898    const btn = container.querySelector('#btnViewMoreTags');
899    const verMenosMas = document.querySelector('.tag-view-title');
900    
901    const verMasText = verMenosMas ? verMenosMas.textContent.trim() : 'Ver más';
902    const verMenosText = verMenosMas ? verMenosMas.dataset.inicializar : 'Ver menos';
903    
904    if (!wrapper || !btn) return;
905    
906    const allTags = wrapper.querySelectorAll('.tag-item:not(.tag-item__btn-tags)');
907    
908    if (allTags.length === 0) {
909        btn.style.display = 'none';
910        return;
911    }
912    
913    const TAGS_PER_LINE = 7;
914    let isFullyExpanded = false;
915    let isAnimating = false;
916    let isDesktop = window.innerWidth >= 1024;
917    
918    function checkIsDesktop() {
919        return window.innerWidth >= 1024;
920    }
921    
922    function updateButtonNumber(count) {
923        const numberSpan = btn.querySelector('.tag-view-number');
924        if (numberSpan) {
925            numberSpan.textContent = count;
926            const digits = count.toString().length;
927            numberSpan.setAttribute('data-digits', digits);
928        }
929    }
930    
931    function updateButtonText(text, count) {
932        const spanText = btn.querySelector('.tag-view-title');
933        if (spanText) {
934            spanText.textContent = text;
935        }
936        updateButtonNumber(count);
937    }
938    
939    function setMobileMode() {
940        allTags.forEach(function(tag) {
941            tag.classList.remove('hidden');
942            tag.style.display = '';
943            tag.style.opacity = '';
944            tag.style.transform = '';
945            tag.style.maxWidth = '';
946            tag.style.padding = '';
947            tag.style.margin = '';
948            tag.style.border = '';
949            tag.style.transition = '';
950        });
951        
952        btn.style.display = 'none';
953        btn.classList.remove('ver-menos');
954        isFullyExpanded = true;
955    }
956    
957    function initDesktop() {
958        allTags.forEach(function(tag, index) {
959            tag.style.transition = 'none';
960            
961            if (index < TAGS_PER_LINE) {
962                tag.classList.remove('hidden');
963                tag.style.display = '';
964                tag.style.opacity = '';
965                tag.style.transform = '';
966                tag.style.maxWidth = '';
967                tag.style.padding = '';
968                tag.style.margin = '';
969                tag.style.border = '';
970            } else {
971                tag.classList.add('hidden');
972                tag.style.display = 'none';
973                tag.style.opacity = '0';
974                tag.style.transform = 'scale(0.8)';
975                tag.style.maxWidth = '0';
976                tag.style.padding = '0';
977                tag.style.margin = '0';
978                tag.style.border = 'none';
979            }
980        });
981        
982        void wrapper.offsetHeight;
983        
984        setTimeout(function() {
985            allTags.forEach(function(tag) {
986                tag.style.transition = '';
987            });
988        }, 50);
989        
990        const hiddenCount = allTags.length - TAGS_PER_LINE;
991        if (hiddenCount > 0) {
992            btn.style.display = '';
993            btn.classList.remove('ver-menos');
994            updateButtonText(verMasText, hiddenCount);
995            isFullyExpanded = false;
996        } else {
997            btn.style.display = 'none';
998            isFullyExpanded = true;
999        }
1000    }
1001    
1002    function showNextLine() {
1003        if (isAnimating) return;
1004        isAnimating = true;
1005        
1006        const hiddenTags = wrapper.querySelectorAll('.tag-item.hidden');
1007        const currentVisible = allTags.length - hiddenTags.length;
1008        const nextMax = Math.min(currentVisible + TAGS_PER_LINE, allTags.length);
1009        
1010        allTags.forEach(function(tag, index) {
1011            if (index < nextMax && tag.classList.contains('hidden')) {
1012                tag.classList.remove('hidden');
1013                tag.style.display = '';
1014                tag.style.transition = 'all 0.8s cubic-bezier(0.4, 0, 0.2, 1)';
1015                tag.style.opacity = '1';
1016                tag.style.transform = 'scale(1)';
1017                tag.style.maxWidth = '';
1018                tag.style.padding = '';
1019                tag.style.margin = '';
1020                tag.style.border = '';
1021            }
1022        });
1023        
1024        const remainingHidden = allTags.length - nextMax;
1025        
1026        setTimeout(function() {
1027            if (remainingHidden > 0) {
1028                btn.classList.remove('ver-menos');
1029                updateButtonText(verMasText, remainingHidden);
1030                isFullyExpanded = false;
1031            } else {
1032                btn.classList.add('ver-menos');
1033                updateButtonText(verMenosText, 0);
1034                isFullyExpanded = true;
1035            }
1036            isAnimating = false;
1037        }, 800);
1038    }
1039    
1040    function resetToFirstLine() {
1041        if (isAnimating) return;
1042        isAnimating = true;
1043        
1044        allTags.forEach(function(tag, index) {
1045            if (index < TAGS_PER_LINE) {
1046                tag.classList.remove('hidden');
1047                tag.style.transition = 'all 0.3s ease';
1048                tag.style.display = '';
1049                tag.style.opacity = '1';
1050                tag.style.transform = 'scale(1)';
1051                tag.style.maxWidth = '';
1052                tag.style.padding = '';
1053                tag.style.margin = '';
1054                tag.style.border = '';
1055            } else {
1056                tag.style.transition = 'all 0.3s ease';
1057                tag.style.opacity = '0';
1058                tag.style.transform = 'scale(0.8)';
1059                tag.style.maxWidth = '0';
1060                tag.style.padding = '0';
1061                tag.style.margin = '0';
1062                tag.style.border = 'none';
1063                setTimeout(function() {
1064                    tag.classList.add('hidden');
1065                    tag.style.display = 'none';
1066                }, 300);
1067            }
1068        });
1069        
1070        setTimeout(function() {
1071            const hiddenCount = allTags.length - TAGS_PER_LINE;
1072            if (hiddenCount > 0) {
1073                btn.classList.remove('ver-menos');
1074                updateButtonText(verMasText, hiddenCount);
1075                isFullyExpanded = false;
1076            }
1077            isAnimating = false;
1078        }, 400);
1079    }
1080    
1081    function init() {
1082        isDesktop = checkIsDesktop();
1083        if (isDesktop) {
1084            initDesktop();
1085        } else {
1086            setMobileMode();
1087        }
1088    }
1089    
1090    btn.addEventListener('click', function(e) {
1091        e.preventDefault();
1092        if (isAnimating) return;
1093        if (!checkIsDesktop()) return;
1094        
1095        if (isFullyExpanded) {
1096            resetToFirstLine();
1097        } else {
1098            showNextLine();
1099        }
1100    });
1101    
1102    let resizeTimeout;
1103    window.addEventListener('resize', function() {
1104        clearTimeout(resizeTimeout);
1105        resizeTimeout = setTimeout(function() {
1106            const wasDesktop = isDesktop;
1107            isDesktop = checkIsDesktop();
1108            
1109            if (wasDesktop !== isDesktop) {
1110                if (isDesktop) {
1111                    initDesktop();
1112                } else {
1113                    setMobileMode();
1114                }
1115            }
1116        }, 250);
1117    });
1118    
1119    init();
1120}

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.