1/* global Swiper */ 2/** 3 * Card Grid â Swiper initialisation 4 * 5 * Frontend works already. This update ensures the editor (iframe) also loads 6 * Swiper CSS/JS inside the iframe and initialises the carousels there, which 7 * prevents huge slide heights and accidental link wrapping in the editor. 8 */ 9(() => { 10 const SWIPER_CDN = 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js'; 11 const SWIPER_CSS_CDN = 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css'; 12 13 /** 14 * Ensure Swiper CSS is loaded into a specific document (defaults to window.document). 15 * @param {Document} targetDoc 16 */ 17 const ensureSwiperCSS = (targetDoc = document) => { 18 if (!targetDoc.querySelector(`link[href="${SWIPER_CSS_CDN}"]`)) { 19 const link = targetDoc.createElement('link'); 20 link.rel = 'stylesheet'; 21 link.href = SWIPER_CSS_CDN; 22 (targetDoc.head || targetDoc.documentElement).appendChild(link); 23 } 24 }; 25 26 /** 27 * Dynamically load Swiper in a specific document context (frontend or editor iframe). 28 * Resolves when Swiper is available on that document's defaultView. 29 * @param {Document} targetDoc 30 */ 31 const loadSwiperInDocument = (targetDoc = document) => 32 new Promise((resolve, reject) => { 33 const win = targetDoc.defaultView || window; 34 if (win.Swiper) { 35 resolve(); 36 return; 37 } 38 39 const script = targetDoc.createElement('script'); 40 script.src = SWIPER_CDN; 41 script.async = true; 42 script.onload = () => resolve(); 43 script.onerror = () => reject(new Error('Failed to load Swiper library')); 44 (targetDoc.head || targetDoc.documentElement).appendChild(script); 45 }); 46 47 /** 48 * Initialise all card-grid swipers within a provided document. 49 * @param {Document} rootDoc 50 */ 51 const initCardGridInDocument = (rootDoc = document) => { 52 const win = rootDoc.defaultView || window; 53 const swipers = rootDoc.querySelectorAll('.card-grid-swiper'); 54 55 swipers.forEach((el) => { 56 // Avoid double init 57 if (el.swiper) return; 58 59 const swiperContainer = el; // .card-grid-swiper 60 const parentContainer = swiperContainer.closest('.card-grid'); 61 if (!parentContainer) return; 62 63 const nextButton = parentContainer.querySelector('.card-grid__nav--next'); 64 const prevButton = parentContainer.querySelector('.card-grid__nav--prev'); 65 const pagination = parentContainer.querySelector('.card-grid-pagination'); 66 67 // eslint-disable-next-line no-undef 68 new win.Swiper(el, { 69 slidesPerView: 1, 70 spaceBetween: 28, 71 loop: false, 72 breakpoints: { 73 // Below 769px: 1 slide per view 74 480: { 75 slidesPerView: 2, 76 spaceBetween: 28, 77 }, 78 // 1280px and above: 3 slides per view 79 1280: { 80 slidesPerView: 3, 81 spaceBetween: 28, 82 }, 83 }, 84 navigation: { 85 nextEl: nextButton, 86 prevEl: prevButton, 87 }, 88 pagination: { 89 el: pagination, 90 clickable: false, 91 bulletClass: 'swiper-pagination-bullet', 92 bulletActiveClass: 'swiper-pagination-bullet-active', 93 }, 94 }); 95 }); 96 }; 97 98 /** 99 * Try to get the block editor iframe document (WP 6.x names/classes). 100 * @returns {Document|null} 101 */ 102 const getEditorIframeDocument = () => { 103 const iframe = document.querySelector('.block-editor-iframe__iframe') || document.querySelector('iframe[name="editor-canvas"]'); 104 if (iframe && iframe.contentDocument) return iframe.contentDocument; 105 return null; 106 }; 107 108 /** 109 * Boot in a given document: ensure CSS/JS, then init. 110 * @param {Document} doc 111 */ 112 const bootForDocument = (doc) => { 113 ensureSwiperCSS(doc); 114 return loadSwiperInDocument(doc) 115 .then(() => { 116 initCardGridInDocument(doc); 117 }) 118 .catch(() => {}); 119 }; 120 121 // Frontend (Swiper + CSS enqueued via WordPress) 122 initCardGridInDocument(document); 123 124 // Editor: run immediately if iframe is ready; also attach listeners for when it (re)loads 125 const maybeBootEditor = () => { 126 const editorDoc = getEditorIframeDocument(); 127 if (editorDoc) { 128 bootForDocument(editorDoc); 129 } 130 }; 131 132 // Try now and after DOM ready 133 if (document.readyState === 'complete' || document.readyState === 'interactive') { 134 maybeBootEditor(); 135 } else { 136 document.addEventListener('DOMContentLoaded', maybeBootEditor, { once: true });
137 } 138 139 // When the iframe reloads due to template/style changes 140 const observeEditorIframe = () => { 141 const iframe = document.querySelector('.block-editor-iframe__iframe') || document.querySelector('iframe[name="editor-canvas"]'); 142 if (!iframe) return; 143 iframe.addEventListener('load', () => { 144 const doc = iframe.contentDocument; 145 if (doc) bootForDocument(doc); 146 }); 147 }; 148 observeEditorIframe(); 149 150 // Fallback: when blocks render/update in editor 151 if (window.wp && window.wp.hooks) { 152 window.wp.hooks.addAction('dom_ready', 'mh/card-grid-swiper', () => { 153 // Frontend fallback (Swiper loaded by WP) 154 initCardGridInDocument(document); 155 maybeBootEditor(); 156 }); 157 } 158})();
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.