1jQuery(document).ready(function ($) { 2 3 // ===== INSTAGRAM PREMIUM LIGHTBOX (Matches Preview) ===== 4 function init_insta_Modal() { 5 if (window._socialfeeds_modal_inited) return; 6 window._socialfeeds_modal_inited = true; 7 8 $(document).on('click', '.socialfeeds-open-modal-media', function (e) { 9 let $feedContainer = $(this).closest('.socialfeeds-instagram-feed, .socialfeeds-facebook-feed'); 10 let isFacebook = $feedContainer.hasClass('socialfeeds-facebook-feed'); 11 let playMode = $feedContainer.attr('data-play-mode') || 'newtab'; 12 13 // Only intercept if play mode is lightbox 14 if (playMode !== 'lightbox') return; 15 16 e.preventDefault(); 17 18 let mediaUrl = $(this).attr('data-media') || $(this).find('img').attr('src') || ''; 19 let type = $(this).attr('data-type') || 'IMAGE'; 20 let permalink = $(this).attr('data-permalink') || '#'; 21 22 if (!mediaUrl) return; 23 24 let media_content = ''; 25 if (type === 'VIDEO') { 26 media_content = ` 27 <video src="${mediaUrl}" controls autoplay 28 style="max-width: 90vw; max-height: 80vh; border-radius: 12px; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); border: 2px solid rgba(255,255,255,0.1);"> 29 Your browser does not support the video tag. 30 </video>`; 31 } else { 32 media_content = ` 33 <img src="${mediaUrl}" 34 style="max-width: 90vw; max-height: 80vh; border-radius: 12px; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); object-fit: contain; border: 2px solid rgba(255,255,255,0.1);">`; 35 } 36 37 const $lightbox = $(` 38 <div class="socialfeeds-premium-lightbox" style="position: fixed; inset: 0; z-index: 999999; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(8px); background: rgba(0, 0, 0, 0.85); opacity: 0; transition: opacity 0.3s ease;"> 39 <button class="close-btn" style="position: absolute; top: 30px; right: 30px; width: 44px; height: 44px; background: rgba(255,255,255,0.1); border: none; border-radius: 50%; color: white; font-size: 24px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s;">×</button> 40 <div class="lightbox-content-wrap" style="transform: scale(0.9); transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);"> 41 ${media_content} 42 ${permalink && permalink !== '#' ? `<div style="text-align: center; margin-top: 15px;"><a href="${permalink}" target="_blank" style="color: white; text-decoration: none; background: rgba(255,255,255,0.1); padding: 8px 16px; border-radius: 20px; font-size: 13px; font-weight: 500;">View on ${isFacebook ? 'Facebook' : 'Instagram'}</a></div>` : ''} 43 </div> 44 </div> 45 `); 46 47 $('body').append($lightbox); 48 49 // Animate In 50 setTimeout(() => { 51 $lightbox.css('opacity', '1'); 52 $lightbox.find('.lightbox-content-wrap').css('transform', 'scale(1)'); 53 }, 10); 54 55 const close_lightbox = () => { 56 $lightbox.css('opacity', '0'); 57 $lightbox.find('.lightbox-content-wrap').css('transform', 'scale(0.9)'); 58 setTimeout(() => $lightbox.remove(), 300); 59 }; 60 61 $lightbox.on('click', function (evt) { 62 if (evt.target === this || $(evt.target).hasClass('close-btn')) { 63 close_lightbox(); 64 } 65 }); 66 67 $(document).on('keydown.sf_lightbox_frontend', function (evt) { 68 if (evt.key === 'Escape') { 69 close_lightbox(); 70 $(document).off('keydown.sf_lightbox_frontend'); 71 } 72 }); 73 }); 74 } 75 76 $('.socialfeeds-load-more-btn').on('click', function (e) { 77 e.preventDefault(); 78 79 let $btn = $(this); 80 81 let feedType = $btn.data('feed-type'); 82 83 // ===== INSTAGRAM LOAD MORE ===== 84 if (feedType === 'instagram') { 85 let nextUrl = $btn.attr('data-next-url'); 86 let loaded = parseInt($btn.data('loaded'), 10) || 0; 87 let $feed = $btn.closest('.socialfeeds-instagram-feed'); 88 let $itemsContainer = $feed.find('.socialfeeds-instagram-inner'); 89 90 if (!nextUrl) { 91 $btn.hide(); 92 return; 93 } 94 95 let original_text = $btn.text(); 96 $btn.prop('disabled', true).addClass('socialfeeds-loading'); 97 98 $.ajax({ 99 url: socialfeeds_ajax.ajax_url, 100 type: 'POST', 101 dataType: 'json', 102 data: { 103 action: 'socialfeeds_load_more_instagram', 104 next_url: nextUrl, 105 load_count: parseInt($btn.data('load-count')) || 12, 106 show_reels: $btn.data('show-reels'), 107 show_feed_posts: $btn.data('show-feed-posts'), 108 layout: $btn.data('layout'), 109 padding: $btn.data('padding'), 110 cols: $btn.data('cols'), 111 aspect_ratio: $btn.data('aspect-ratio'), 112 caption_enabled: $btn.data('caption-enabled'),
113 likes_enabled: $btn.data('likes-enabled'), 114 comments_enabled: $btn.data('comments-enabled'), 115 views_enabled: $btn.data('views-enabled'), 116 hover_state: $btn.data('hover-state'), 117 play_mode: $btn.data('play-mode'), 118 show_play_icon: $btn.data('show-play-icon'), 119 nonce: socialfeeds_ajax.nonce 120 }, 121 success: function (response) { 122 123 if (!response.success || !response.data) { 124 $btn.text('No More Posts').hide(); 125 return; 126 } 127 128 if (response.data.html) { 129 $itemsContainer.append(response.data.html); 130 // If carousel, trigger resize to update dots/nav 131 if ($itemsContainer.hasClass('layout-carousel')) { 132 $(window).trigger('resize'); 133 } 134 } 135 136 let new_loaded = loaded + response.data.count; 137 $btn.data('loaded', new_loaded).attr('data-loaded', new_loaded); 138 139 if (response.data.next) { 140 $btn.attr('data-next-url', response.data.next); 141 $btn.prop('disabled', false) 142 .removeClass('socialfeeds-loading') 143 .text(original_text); 144 } else { 145 $btn.text('No More Posts').hide(); 146 } 147 }, 148 error: function () { 149 console.error('SocialFeeds Instagram AJAX Error'); 150 $btn.prop('disabled', false) 151 .removeClass('socialfeeds-loading') 152 .text('Error loading posts'); 153 } 154 }); 155 } 156 // ===== FACEBOOK LOAD MORE ===== 157 else if (feedType === 'facebook') { 158 let original_text = $btn.text(); 159 let $feed = $btn.closest('.socialfeeds-facebook-feed'); 160 let $itemsContainer = $feed.find('.socialfeeds-facebook-inner'); 161 let feedId = $btn.data('feed-id'); 162 let loadCount = parseInt($btn.data('load-count')) || 9; 163 // Use Facebook cursor-based pagination â numeric offset causes duplicate posts
164 let afterCursor = $btn.attr('data-after-cursor') || ''; 165 166 if (!afterCursor) { 167 $btn.text('No More Posts').hide(); 168 return; 169 } 170 171 // Collect IDs of posts already on the page so the server can filter duplicates 172 let existingIds = []; 173 $itemsContainer.find('.socialfeeds-facebook-item[data-post-id]').each(function () { 174 let pid = $(this).attr('data-post-id'); 175 if (pid) existingIds.push(pid); 176 }); 177 178 $btn.prop('disabled', true).text('Loading...'); 179 180 $.ajax({ 181 url: socialfeeds_ajax.ajax_url, 182 type: 'POST', 183 dataType: 'json', 184 data: { 185 action: 'socialfeeds_load_more_facebook', 186 feed_id: feedId, 187 load_count: loadCount, 188 after_cursor: afterCursor, 189 existing_ids: existingIds, 190 nonce: socialfeeds_ajax.nonce 191 }, 192 success: function (response) { 193 if (!response.success || !response.data || !response.data.html) { 194 $btn.text('No More Posts').hide(); 195 return; 196 } 197 198 $itemsContainer.append(response.data.html); 199 200 // If carousel, trigger resize 201 if ($itemsContainer.hasClass('layout-carousel')) { 202 $(window).trigger('resize'); 203 } 204 205 // Store the next cursor so the following click fetches the correct page 206 let nextCursor = response.data.next_cursor || ''; 207 $btn.attr('data-after-cursor', nextCursor); 208 209 if (response.data.has_more && nextCursor) { 210 $btn.prop('disabled', false).text(original_text); 211 } else { 212 $btn.text('No More Posts').hide(); 213 } 214 }, 215 error: function () { 216 $btn.prop('disabled', false).text('Error loading posts'); 217 } 218 }); 219 } 220 }); 221 222 init_insta_Modal(); 223 224 // ===== INSTAGRAM CAROUSEL ===== 225 function init_insta_Carousel() { 226 $('.socialfeeds-instagram-inner.layout-carousel, .socialfeeds-facebook-inner.layout-carousel').each(function () { 227 let $carousel = $(this); 228 let $feedWrapper = $carousel.closest('.socialfeeds-instagram-feed'); 229 230 // Prevent double init 231 if ($feedWrapper.find('.socialfeeds-carousel-stage').length) return; 232 233 $carousel.wrap('<div class="socialfeeds-carousel-stage" style="position:relative; width:100%;"></div>'); 234 let $stage = $carousel.parent(); 235 236 let $nav = $('<div class="socialfeeds-carousel-nav">' + 237 '<button class="socialfeeds-carousel-btn prev" aria-label="Previous" type="button"><span class="dashicons dashicons-arrow-left-alt2"></span></button>' + 238 '<button class="socialfeeds-carousel-btn next" aria-label="Next" type="button"><span class="dashicons dashicons-arrow-right-alt2"></span></button>' + 239 '</div>'); 240 241 $stage.append($nav); 242 243 let $dots = $('<div class="socialfeeds-carousel-dots"></div>'); 244 $stage.after($dots); 245 246 function updateDots() { 247 $dots.empty(); 248 let scrollW = $carousel[0].scrollWidth; 249 let clientW = $carousel[0].clientWidth; 250 if (clientW >= scrollW) { 251 $dots.hide(); 252 $nav.hide(); 253 return; 254 } 255 256 $dots.show(); 257 $nav.show(); 258 259 let pageCount = Math.ceil(scrollW / clientW); 260 for (let i = 0; i < pageCount; i++) { 261 let $d = $('<span class="socialfeeds-dot"></span>'); 262 if (i === 0) $d.addClass('active'); 263 $d.data('page', i); 264 $dots.append($d); 265 } 266 } 267 268 setTimeout(updateDots, 300); 269 $(window).on('resize', updateDots); 270 271 // Scroll Events 272 $nav.find('.prev').on('click', function (e) { 273 e.preventDefault(); 274 $carousel.animate({ scrollLeft: $carousel.scrollLeft() - $carousel.width() }, 300, update_active_dot); 275 }); 276 277 $nav.find('.next').on('click', function (e) { 278 e.preventDefault(); 279 $carousel.animate({ scrollLeft: $carousel.scrollLeft() + $carousel.width() }, 300, update_active_dot); 280 }); 281 282 $dots.on('click', '.socialfeeds-dot', function () { 283 let page = $(this).data('page'); 284 $carousel.animate({ scrollLeft: page * $carousel.width() }, 400, update_active_dot); 285 }); 286 287 function update_active_dot() { 288 let scroll_left = $carousel.scrollLeft(); 289 let clientW = $carousel[0].clientWidth; 290 let page = Math.round(scroll_left / (clientW || 1)); 291 $dots.find('.socialfeeds-dot').removeClass('active').eq(page).addClass('active'); 292 } 293 294 $carousel.on('scroll', function () { 295 clearTimeout($carousel.data('scrollTimer')); 296 $carousel.data('scrollTimer', setTimeout(update_active_dot, 100)); 297 }); 298 }); 299 } 300 301 init_insta_Carousel(); 302});
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.