1let __hamalightId__ = 0; 2 3class HamaLightGallery { 4 constructor(element, options = {}) { 5 this.element = typeof element === "string" ? document.querySelector(element) : element; 6 this.options = { 7 selector: "", 8 container: "", 9 transitionDuration: 300, 10 showDownloadButton: true, 11 showCloseButton: true, 12 enableKeyboardNavigation: true, 13 ...options 14 }; 15 16 this.items = []; 17 this.media = []; 18 this.previousIndexes = []; 19 this.currentIndex = 0; 20 this.currentMedia = null; 21 this.isOpen = false; 22 this.isBusy = false; 23 this.focusableElements = []; 24 this.lastFocusedElement = null; 25 26 this.$container = null; 27 this.$backdrop = null; 28 this.$inner = null; 29 this.$toolbar = null; 30 this.$list = null; 31 this.$slide = null; 32 this.$description = null; 33 34 this.$close = null; 35 this.$prev = null; 36 this.$next = null; 37 this.$counter = null; 38 this.$download = null; 39 40 // å ç¶å®ä¸æ¬¡ï¼removeEventListener æè½ç§»é¤åä¸åå½å¼åè 41 this.handleKeyDown = this.handleKeyDown.bind(this); 42 43 this.initSettings(); 44 this.init(); 45 46 __hamalightId__++; 47 } 48 49 initSettings() { 50 if (!this.options.container) { 51 this.options.container = document.body; 52 } 53 else { 54 if (typeof this.options.container === "string") { 55 this.options.container = document.querySelector(this.options.container); 56 } 57 } 58 } 59 60 init() { 61 if (!this.element) { 62 console.error("HamaLightGallery: Element not found"); 63 return; 64 } 65 66 this.items = this.getItems(); 67 this.media = this.getMediaItems(this.items); 68 this.bindEvents(this.items); 69 this.buildStructure(); 70 } 71 72 buildStructure() { 73 if (this.$container) return; 74 75 const labels = this._getLabels(); 76 const closeButton = this.options.showCloseButton ? `<a href="#" class="halg-close" id="halg-close-${__hamalightId__}" role="button" aria-label="${labels.close}" tabindex="0"></a>` : ""; 77 const downloadButton = this.options.showDownloadButton ? `<a class="halg-download" id="halg-download-${__hamalightId__}" target="_blank" rel="noopener" title ="ãå¦éæ°è¦çªã" download aria-label="${labels.downloadLink}" tabindex="0"></a>` : ""; 78 const counter = ` 79 <div class="halg-count" id="halg-count-${__hamalightId__}"> 80 <span class="halg-count-current" id="halg-count-current-${__hamalightId__}"></span> / <span class="halg-count-total">${this.media.length}</span> 81 </div> 82 `; 83 const template = ` 84 <div class="halg-lightgallery" id="halg-container-${__hamalightId__}" aria-modal="false" role="dialog" tabindex="-1" hidden=""> 85 <div class="halg-backdrop" id="halg-backdrop-${__hamalightId__}"></div> 86 <div class="halg-inner" id="halg-inner-${__hamalightId__}"> 87 <span class="halg-focus-sentinel" id="halg-sentinel-start-${__hamalightId__}" tabindex="0"></span> 88 <div class="halg-toolgroup" id="halg-toolgroup-${__hamalightId__}"> 89 ${counter} 90 <div class="halg-toolbtn"> 91 ${downloadButton} 92 ${closeButton} 93 </div> 94 </div> 95 <div class="halg-list" id="halg-list-${__hamalightId__}"> 96 <ul id="halg-slide-${__hamalightId__}"></ul> 97 <div class="halg-actions"> 98 <a href="#" class="halg-prev" id="halg-prev-${__hamalightId__}" role="button" aria-label="${labels.prev}" tabindex="0"></a> 99 <a href="#" class="halg-next" id="halg-next-${__hamalightId__}" role="button" aria-label="${labels.next}" tabindex="0"></a> 100 </div> 101 </div> 102 <div class="halg-description" id="halg-description-${__hamalightId__}"></div>
103 <span class="halg-focus-sentinel" id="halg-sentinel-end-${__hamalightId__}" tabindex="0"></span> 104 </div> 105 </div> 106 `; 107 108 this.options.container.insertAdjacentHTML("beforeend", template); 109 110 this.$container = document.querySelector(`#halg-container-${__hamalightId__}`); 111 this.$backdrop = document.querySelector(`#halg-backdrop-${__hamalightId__}`); 112 this.$inner = document.querySelector(`#halg-inner-${__hamalightId__}`); 113 this.$toolbar = document.querySelector(`#halg-toolgroup-${__hamalightId__}`); 114 this.$list = document.querySelector(`#halg-list-${__hamalightId__}`); 115 this.$slide = document.querySelector(`#halg-slide-${__hamalightId__}`); 116 this.$description = document.querySelector(`#halg-description-${__hamalightId__}`); 117 118 this.$close = this.options.showCloseButton ? document.querySelector(`#halg-close-${__hamalightId__}`) : null; 119 this.$prev = document.querySelector(`#halg-prev-${__hamalightId__}`); 120 this.$next = document.querySelector(`#halg-next-${__hamalightId__}`); 121 this.$counter = document.querySelector(`#halg-count-current-${__hamalightId__}`); 122 this.$download = document.querySelector(`#halg-download-${__hamalightId__}`); 123 this.$sentinelStart = document.querySelector(`#halg-sentinel-start-${__hamalightId__}`); 124 this.$sentinelEnd = document.querySelector(`#halg-sentinel-end-${__hamalightId__}`); 125 126 // ç¦é»è½å¨å¨å µä¸æè½éå°é /å°¾çç實å ç´ ï¼å½¢æç¦é»å¾ªç°ã 127 // ä¸ææª Tab keydownï¼ç¦é»æè½èªç¶èµ°é² video æ§å¶åï¼shadow DOMï¼çå §é¨æéã 128 this.$sentinelStart.addEventListener("focus", () => { 129 this.updateFocusableElements(); 130 const els = this.focusableElements; 131 if (els.length) els[els.length - 1].focus(); 132 }); 133 134 this.$sentinelEnd.addEventListener("focus", () => { 135 this.updateFocusableElements(); 136 if (this.focusableElements.length) this.focusableElements[0].focus(); 137 }); 138 139 if (this.$close) { 140 this.$close.addEventListener("click", (e) => { 141 e.preventDefault(); 142 this.close(); 143 }); 144 145 this.$close.addEventListener("keydown", (e) => { 146 if (e.key === "Enter" || e.key === " ") { 147 e.preventDefault(); 148 this.close(); 149 } 150 }); 151 } 152 153 if (this.$download) { 154 this.$download.addEventListener("keydown", (e) => { 155 if (e.key === "Enter" || e.key === " ") { 156 // ç©ºæ ¼éµä¹è§¸ç¼ä¸è¼ï¼ä½ä¿çé»èªè¡çº 157 if (e.key === " ") { 158 e.preventDefault(); 159 this.$download.click(); 160 } 161 } 162 }); 163 } 164 165 if (this.media.length <= 1) { 166 this.$prev.style.display = "none"; 167 this.$next.style.display = "none"; 168 } else { 169 this.$prev.addEventListener("click", (e) => { 170 e.preventDefault(); 171 this.prev(); 172 }); 173 174 this.$prev.addEventListener("keydown", (e) => { 175 if (e.key === "Enter" || e.key === " ") { 176 e.preventDefault(); 177 this.prev(); 178 } 179 }); 180 181 this.$next.addEventListener("click", (e) => { 182 e.preventDefault(); 183 this.next(); 184 }); 185 186 this.$next.addEventListener("keydown", (e) => { 187 if (e.key === "Enter" || e.key === " ") { 188 e.preventDefault(); 189 this.next(); 190 } 191 }); 192 } 193 } 194 195 bindEvents(items) { 196 items.forEach((item, index) => { 197 // æ¾å°å¯èç¦çé£çµå ç´ 198 const tag = item.tagName.toLowerCase(); 199 // åªå ä½¿ç¨ <a> æ¨ç±¤ï¼å¦ææ²æåå試å¨é ç®å §æ¥æ¾ <a> 200 const link = tag === "a" ? item : item.querySelector("a"); 201 202 // 妿æ¾ä¸å°é£çµï¼å使ç¨é ç®æ¬èº« 203 const focusableElement = link || item; 204 205 // 確ä¿å ä»¶å¯èç¦ 206 if (!focusableElement.hasAttribute('tabindex')) { 207 focusableElement.setAttribute('tabindex', '0'); 208 } 209 210 item.addEventListener("click", (e) => { 211 e.preventDefault(); 212 // è¨é實éçé£çµå ç´ ï¼ä»¥ä¾¿ééæå¯ä»¥éæ°èç¦ 213 this.lastFocusedElement = focusableElement; 214 this.open(index); 215 }); 216 217 // æ¯æ´éµç¤æä¸ Enter æç©ºæ ¼ææéç«å» 218 focusableElement.addEventListener("keydown", (e) => { 219 if (e.key === "Enter" || e.key === " ") { 220 e.preventDefault(); 221 // 忍£è¨ééµç¤æä½çå ä»¶ 222 this.lastFocusedElement = focusableElement; 223 this.open(index); 224 } 225 }); 226 227 // è¨ç½®æ¸¸æ¨æ¨£å¼ 228 item.style.cursor = "pointer"; 229 }); 230 } 231 232 getItems() { 233 return this.options.selector 234 ? [...this.element.querySelectorAll(this.options.selector)] 235 : [...this.element.children]; 236 } 237 238 getMediaItems(items) { 239 return Array.from(items) 240 .map((el, index) => { 241 const tag = el.tagName.toLowerCase(); 242 const img = tag === "img" ? el : el.querySelector("img"); 243 const a = tag === "a" ? el : el.querySelector("a"); 244 245 const src = el.dataset.src || a.href; 246 const videoOption = this._isVideo(el); 247 248 if (videoOption) { 249 return { 250 src: src, 251 alt: img.alt || `Video ${index + 1}`, 252 type: "video", 253 el: this._createVideo(src, videoOption, a && a.dataset.title), 254 thumbnailEl: img, 255 ...videoOption 256 }; 257 } 258 else if (img) { 259 return { 260 src: src, 261 alt: img.alt || `Image ${index + 1}`, 262 type: "image", 263 el: null, 264 thumbnailEl: img, 265 }; 266 } 267 else { 268 console.warn(`HamaLightGallery: Unsupported element in gallery item #${index}`); 269 return null; 270 } 271 }) 272 .filter(Boolean); 273 } 274 275 open(index) { 276 if (!this.media.length) return; 277 278 this.currentIndex = index; 279 this.isOpen = true; 280 281 this.toggleState(); 282 this.updateSlide(index, null, null); 283 284 // 鲿¢èæ¯æ»¾å 285 this.options.container.style.overflow = "hidden"; 286 } 287 288 close() { 289 this.isOpen = false; 290 291 this.toggleState(); 292 this.$slide.innerHTML = ""; 293 294 this.options.container.style.overflow = ""; 295 296 this.previousIndexes = []; 297 this.isBusy = false; 298 299 // ééç«å»æå°ç¦é»è¿å忬黿çå ä»¶ 300 if (this.lastFocusedElement) { 301 setTimeout(() => { 302 this.lastFocusedElement.focus(); 303 }, 10); 304 } 305 } 306 307 prev() { 308 if (this.isBusy) return; 309 310 const newIndex = this._wrapIndex(this.currentIndex - 1); 311 this.updateSlide(newIndex, this.currentIndex, "prev"); 312 this.currentIndex = newIndex; 313 } 314 315 next() { 316 if (this.isBusy) return; 317 318 const newIndex = this._wrapIndex(this.currentIndex + 1); 319 this.updateSlide(newIndex, this.currentIndex, "next"); 320 this.currentIndex = newIndex; 321 } 322 323 updateSlide(to, from, direction) { 324 if (this.isBusy) return; 325 326 this.isBusy = true; 327 328 // æ´æ°å §å®¹ 329 this.updateSlideContent(to, from, direction); 330 331 // æ´æ°è¨æ¸å¨ 332 this.updateCounter(to); 333 334 // æ´æ°ä¸è¼æé 335 this.updateDownloadLink(to); 336 337 // æ´æ°æªå 338 this.updateComponents(to); 339 } 340 341 updateSlideContent(to, from, direction) { 342 const maxCount = Math.min(3, this.media.length); 343 const indexes = Array(maxCount).fill(0).map((_, i) => this._wrapIndex(to + (i - maxCount + (maxCount - 1)))); 344 const removedIndexes = this._diffIndexes(indexes, this.previousIndexes); 345 346 // ç§»é¤èå ç´
347 removedIndexes.forEach((item) => { 348 if (this.previousIndexes.includes(item)) { 349 const $slideItem = this.$slide.querySelector(`.halg-item-${item}`); 350 $slideItem.remove(); 351 } 352 }); 353 354 // æ°å¢å ç´ 355 const renderSlideContent = (media, i) => { 356 if (!this.isOpen) return; 357 358 const $slideItem = this.$slide.querySelector(`.halg-item-${i}`); 359 360 if ($slideItem.children.length === 0) { 361 $slideItem.append(media); 362 } 363 364 if (i === to) { 365 $slideItem.classList.add("halg-item-current"); 366 367 setTimeout(() => { 368 this.currentMedia = media; 369 this.isBusy = false; 370 this.previousIndexes = indexes; 371 this._controlVideo(to, from); 372 }, this.options.transitionDuration); 373 } 374 }; 375 376 // æ´æ°å ç´ 377 new Promise((resolve) => { 378 const fragment = document.createDocumentFragment(); 379 indexes.forEach((item) => { 380 let $slideItem = this.$slide.querySelector(`.halg-item-${item}`); 381 382 if (removedIndexes.includes(item) || $slideItem == null) { 383 $slideItem = this._createSlide(item); 384 fragment.append($slideItem); 385 } 386 387 $slideItem.classList.remove("halg-item-current", "halg-item-prev", "halg-item-next"); 388 389 if (direction === "next") { 390 setTimeout(() => { 391 if (item === from) { 392 $slideItem.classList.add("halg-item-prev"); 393 } 394 else { 395 $slideItem.classList.add("halg-item-next"); 396 } 397 }, this.options.transitionDuration / 2); 398 } 399 400 if (direction === "prev") { 401 setTimeout(() => { 402 if (item === from) { 403 $slideItem.classList.add("halg-item-next"); 404 } 405 else { 406 $slideItem.classList.add("halg-item-prev"); 407 } 408 }, this.options.transitionDuration / 2); 409 } 410 }); 411 this.$slide.append(fragment); 412 413 setTimeout(() => { 414 resolve(); 415 }, this.options.transitionDuration); 416 }).then(() => { 417 indexes.forEach((i) => { 418 const item = this.media[i]; 419 420 if (item.el) { 421 if (i === to) renderSlideContent(item.el, i); 422 } 423 else { 424 if (item.type === "image") { 425 const img = new Image(); 426 427 img.src = item.src; 428 img.alt = item.alt || ""; 429 img.classList.add("halg-img-loading"); 430 431 img.onload = () => { 432 img.classList.add("halg-img-loaded"); 433 item.el = img; 434 if (i === to) renderSlideContent(img, i); 435 }; 436 437 img.onerror = () => { 438 item.el = item.thumbnailEl; 439 if (i === to) renderSlideContent(item.thumbnailEl, i); 440 }; 441 } 442 } 443 }); 444 }); 445 } 446 447 updateCounter(index) { 448 this.$counter.textContent = index + 1; 449 } 450 451 updateDownloadLink(index) { 452 if (this.options.showDownloadButton) { 453 const currentItem = this.media[index]; 454 this.$download.href = currentItem.src; 455 this.$download.setAttribute("download", true); 456 } 457 } 458 459 updateComponents(index) { 460 const currentItem = this.media[index]; 461 this.$description.textContent = currentItem.alt; 462 } 463 464 toggleState() { 465 const open = this.isOpen; 466 this.$container.classList.toggle("is-active", open); 467 468 if (open) { 469 this.$container.setAttribute("aria-modal", "true"); 470 this.$container.removeAttribute("hidden", ""); 471 this.initKeyboardNavigation(); 472 } else { 473 this.$container.setAttribute("aria-modal", "false"); 474 this.$container.setAttribute("hidden", ""); 475 this.removeKeyboardNavigation(); 476 } 477 } 478 479 _createSlide(i) { 480 const div = document.createElement("li"); 481 div.dataset.index = i; 482 div.className = `halg-item halg-item-${i}`; 483 return div; 484 } 485 486 initKeyboardNavigation() { 487 if (!this.options.enableKeyboardNavigation) return; 488 489 // æ·»å éµç¤äºä»¶ç£è½ 490 document.addEventListener('keydown', this.handleKeyDown); 491 492 // æ¶éå¯èç¦å ç´ ï¼åå§ç¦é»è¨å¨ç¬¬ä¸åå ç´ ï¼ä¸è¼æéï¼ï¼Tab é åºç±ä¸èä¸ï¼ä¸è¼ â éé â å½±ç 493 this.updateFocusableElements(); 494 495 if (this.focusableElements.length > 0) { 496 this.focusableElements[0].focus(); 497 } else { 498 this.$container.focus({ preventScroll: true });
499 } 500 } 501 502 removeKeyboardNavigation() { 503 if (!this.options.enableKeyboardNavigation) return; 504 505 document.removeEventListener('keydown', this.handleKeyDown); 506 } 507 508 updateFocusableElements() { 509 // ç²åææå¯èç¦çå ç´ ï¼å«å½±çææ¾å¨è YouTube iframeï¼æé¤å¨å µï¼ 510 this.focusableElements = Array.from( 511 this.$container.querySelectorAll( 512 'a[href], button, input, textarea, select, video[controls], iframe, [tabindex]:not([tabindex="-1"])' 513 ) 514 ).filter(el => 515 !el.classList.contains('halg-focus-sentinel') && 516 !el.hasAttribute('disabled') && 517 el.offsetParent !== null 518 ); 519 } 520 521 handleKeyDown(e) { 522 if (!this.isOpen) return; 523 524 // ç¦é»å¨å½±çææ¾å¨ä¸æï¼æ¹åéµäº¤çµ¦ææ¾å¨æ¬èº«ï¼å¿«è½/åè½ï¼ï¼ä¸åæä¸ä¸å¼µ 525 const target = e.target; 526 const inMedia = target && (target.tagName === 'VIDEO' || target.tagName === 'IFRAME'); 527 528 switch (e.key) { 529 case 'Escape': 530 this.close(); 531 // close() æ¹æ³å·²ç¶å å«äºè¿åç¦é»çèç 532 break; 533 case 'ArrowLeft': 534 if (!inMedia) this.prev(); 535 break; 536 case 'ArrowRight': 537 if (!inMedia) this.next(); 538 break; 539 } 540 } 541 542 _isVideo(el) { 543 const src = el.dataset.src; 544 const html = el.dataset.html; 545 if (!src && html) { 546 return { 547 video: true 548 }; 549 } 550 551 const youtube = src.match(/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/); 552 if (youtube) { 553 return { 554 youtube: youtube[1] 555 } 556 } 557 558 return null; 559 } 560 561 _createVideo(src, option, title) { 562 const labels = this._getLabels(); 563 564 if (option.video) { 565 const video = document.createElement("video"); 566 const source = document.createElement("source"); 567 568 source.src = src; 569 source.type = "video/mp4"; 570 571 video.setAttribute("controls", true); 572 video.setAttribute("playsinline", true); 573 video.setAttribute("loop", true); 574 video.classList.add("halg-video-loaded"); 575 video.append(source); 576 577 return video; 578 } 579 else if (option.youtube) { 580 const iframe = document.createElement("iframe"); 581 const params = new URLSearchParams({ 582 wmode: "opaque", 583 autoplay: 0, 584 mute: 0, 585 enablejsapi: 1, 586 }); 587 588 iframe.src = `https://www.youtube.com/embed/${option.youtube}?${params}`; 589 // å¸¶å ¥åå§ youtube é£çµç data-title 屬æ§ï¼è¥ç¡å使ç¨é è¨å¼ã 590 // æ¤èä½¿ç¨ DOM å±¬æ§æå®ï¼property assignmentï¼ï¼çè¦½å¨æå°å¼è¦çºç´åä¸²ï¼ 591 // ä¸æè§£ææ HTMLï¼å æ¤å¯é¿å XSS æ»æã 592 iframe.title = (typeof title === "string" && title.trim() !== "") 593 ? title 594 : labels.youtubePlayer; 595 iframe.width = "80%"; 596 iframe.height = "100%"; 597 iframe.frameBorder = "0"; 598 iframe.allow = "accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"; 599 iframe.referrerpolicy = "strict-origin-when-cross-origin"; 600 601 iframe.setAttribute("allowfullscreen", true); 602 603 return iframe 604 } 605 } 606 607 _getLabels() { 608 const labels = { 609 1: { 610 youtubePlayer: "YouTube å½±çææ¾å¨", 611 prev: "ä¸ä¸å¼µ", 612 next: "ä¸ä¸å¼µ", 613 downloadLink: "ä¸è¼é£çµ", 614 close: "éé" 615 }, 616 2: { 617 youtubePlayer: "YouTube video player", 618 prev: "Previous image", 619 next: "Next image", 620 downloadLink: "Download link", 621 close: "Close" 622 }, 623 3: { 624 youtubePlayer: "YouTube åç»ãã¬ã¼ã¤ã¼", 625 prev: "åã®ç»å", 626 next: "次ã®ç»å", 627 downloadLink: "ãã¦ã³ãã¼ããªã³ã¯", 628 close: "éãã" 629 }, 630 4: { 631 youtubePlayer: "Trình phát video YouTube", 632 prev: "Ảnh trưá»c", 633 next: "Ảnh tiếp theo", 634 downloadLink: "Liên kết tải xuá»ng", 635 close: "Äóng" 636 }, 637 5: { 638 youtubePlayer: "Pemutar video YouTube", 639 prev: "Gambar sebelumnya", 640 next: "Gambar berikutnya", 641 downloadLink: "Tautan unduhan", 642 close: "Tutup" 643 }, 644 6: { 645 youtubePlayer: "à¹à¸à¸£à¸·à¹à¸à¸à¹à¸¥à¹à¸à¸§à¸´à¸à¸µà¹à¸ YouTube", 646 prev: "à¸à¹à¸à¸à¸«à¸à¹à¸²", 647 next: "à¸à¸±à¸à¹à¸", 648 downloadLink: "ลิà¸à¸à¹à¸à¸²à¸§à¸à¹à¹à¸«à¸¥à¸", 649 close: "à¸à¸´à¸" 650 }, 651 7: { 652 youtubePlayer: "Player ng video sa YouTube", 653 prev: "Nakaraang larawan", 654 next: "Susunod na larawan",
655 downloadLink: "Link sa pag-download", 656 close: "Isara" 657 }, 658 8: { 659 youtubePlayer: "áááááá·áá¸á á¶áááá¸ááá¢á¼ YouTube", 660 prev: "áá¼ááá¶ááá»á", 661 next: "áá¼ááá¶áááááá¶áá", 662 downloadLink: "ááááááá¶áááá¶ááá", 663 close: "áá·á" 664 }, 665 9: { 666 youtubePlayer: "YouTube áá®áá®ááá¯áá½áá·áºá ááº", 667 prev: "ááááºáá¯á¶", 668 next: "áá±á¬ááºáá¯á¶", 669 downloadLink: "á±áá«áá¹á¸áá¯áá¹ááá¹á", 670 close: "ááááº" 671 }, 672 10: { 673 youtubePlayer: "YouTube ëìì íë ì´ì´", 674 prev: "ì´ì ì´ë¯¸ì§", 675 next: "ë¤ì ì´ë¯¸ì§", 676 downloadLink: "ë¤ì´ë¡ë ë§í¬", 677 close: "ë«ê¸°" 678 } 679 }; 680 const languageSN = Number(window.CCMS_LanguageSN); 681 682 return labels[languageSN] || labels[1]; 683 } 684 685 _controlVideo(to, from) { 686 const toVideo = this.media[to] ?? {}; 687 const fromVideo = this.media[from] ?? {}; 688 689 if (toVideo.type === "video") { 690 if (toVideo.video) { 691 toVideo.el.play(); 692 } 693 694 if (toVideo.youtube) { 695 try { 696 const message = JSON.stringify({ 697 event: "command", 698 func: "playVideo", 699 args: [] 700 }); 701 702 toVideo.el.contentWindow.postMessage(message, "*"); 703 704 toVideo.el.onload = () => { 705 toVideo.el.contentWindow.postMessage(message, "*"); 706 }; 707 } catch (error) { 708 console.error("HamaLightGallery: Error sending postMessage:", error); 709 } 710 } 711 } 712 713 if (fromVideo.type === "video") { 714 if (fromVideo.video) { 715 fromVideo.el.pause(); 716 } 717 718 if (fromVideo.youtube) { 719 try { 720 const message = JSON.stringify({ 721 event: "command", 722 func: "pauseVideo", 723 args: [] 724 }); 725 726 fromVideo.el.contentWindow.postMessage(message, "*"); 727 728 fromVideo.el.onload = () => { 729 fromVideo.el.contentWindow.postMessage(message, "*"); 730 }; 731 } catch (error) { 732 console.error("HamaLightGallery: Error sending postMessage:", error); 733 } 734 } 735 } 736 } 737 738 _wrapIndex(i) { 739 return (i + this.media.length) % this.media.length; 740 } 741 742 _diffIndexes(arr1, arr2) { 743 const set1 = new Set(arr1); 744 const set2 = new Set(arr2); 745 746 const diff1 = arr1.filter(item => !set2.has(item)); 747 const diff2 = arr2.filter(item => !set1.has(item)); 748 749 return [...new Set([...diff1, ...diff2])]; 750 } 751} 752 753function hamalightGallery(el, options) { 754 return new HamaLightGallery(el, options); 755}
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.