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.