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 alignment: $btn.data('alignment') || 'left', 118 play_mode: $btn.data('play-mode'), 119 show_play_icon: $btn.data('show-play-icon'), 120 nonce: socialfeeds_ajax.nonce 121 }, 122 success: function (response) { 123 124 if (!response.success || !response.data) { 125 $btn.text('No More Posts').hide(); 126 return; 127 } 128 129 if (response.data.html) { 130 $itemsContainer.append(response.data.html); 131 // If carousel, trigger resize to update dots/nav 132 if ($itemsContainer.hasClass('layout-carousel')) { 133 $(window).trigger('resize'); 134 } 135 } 136 137 let new_loaded = loaded + response.data.count; 138 $btn.data('loaded', new_loaded).attr('data-loaded', new_loaded); 139 140 if (response.data.next) { 141 $btn.attr('data-next-url', response.data.next); 142 $btn.prop('disabled', false) 143 .removeClass('socialfeeds-loading') 144 .text(original_text); 145 } else { 146 $btn.text('No More Posts').hide(); 147 } 148 }, 149 error: function () { 150 console.error('SocialFeeds Instagram AJAX Error'); 151 $btn.prop('disabled', false) 152 .removeClass('socialfeeds-loading') 153 .text('Error loading posts'); 154 } 155 }); 156 } 157 // ===== FACEBOOK LOAD MORE ===== 158 else if (feedType === 'facebook') { 159 let original_text = $btn.text(); 160 let $feed = $btn.closest('.socialfeeds-facebook-feed'); 161 let $itemsContainer = $feed.find('.socialfeeds-facebook-inner'); 162 let feedId = $btn.data('feed-id'); 163 let loadCount = parseInt($btn.data('load-count')) || 9; 164 // Use Facebook cursor-based pagination â numeric offset causes duplicate posts
165 let afterCursor = $btn.attr('data-after-cursor') || ''; 166 167 if (!afterCursor) { 168 $btn.text('No More Posts').hide(); 169 return; 170 } 171 172 // Collect IDs of posts already on the page so the server can filter duplicates 173 let existingIds = []; 174 $itemsContainer.find('.socialfeeds-facebook-item[data-post-id]').each(function () { 175 let pid = $(this).attr('data-post-id'); 176 if (pid) existingIds.push(pid); 177 }); 178 179 $btn.prop('disabled', true).text('Loading...'); 180 181 $.ajax({ 182 url: socialfeeds_ajax.ajax_url, 183 type: 'POST', 184 dataType: 'json', 185 data: { 186 action: 'socialfeeds_load_more_facebook', 187 feed_id: feedId, 188 load_count: loadCount, 189 after_cursor: afterCursor, 190 existing_ids: existingIds, 191 nonce: socialfeeds_ajax.nonce 192 }, 193 success: function (response) { 194 if (!response.success || !response.data || !response.data.html) { 195 $btn.text('No More Posts').hide(); 196 return; 197 } 198 199 $itemsContainer.append(response.data.html); 200 201 // If carousel, trigger resize 202 if ($itemsContainer.hasClass('layout-carousel')) { 203 $(window).trigger('resize'); 204 } 205 206 // Store the next cursor so the following click fetches the correct page 207 let nextCursor = response.data.next_cursor || ''; 208 $btn.attr('data-after-cursor', nextCursor); 209 210 if (response.data.has_more && nextCursor) { 211 $btn.prop('disabled', false).text(original_text); 212 } else { 213 $btn.text('No More Posts').hide(); 214 } 215 }, 216 error: function () { 217 $btn.prop('disabled', false).text('Error loading posts'); 218 } 219 }); 220 } 221 }); 222 223 init_insta_Modal(); 224 225 // ===== INSTAGRAM CAROUSEL ===== 226 function init_insta_Carousel() { 227 $('.socialfeeds-instagram-inner.layout-carousel, .socialfeeds-facebook-inner.layout-carousel').each(function () { 228 let $carousel = $(this); 229 let $feedWrapper = $carousel.closest('.socialfeeds-instagram-feed'); 230 231 // Prevent double init 232 if ($feedWrapper.find('.socialfeeds-carousel-stage').length) return; 233 234 $carousel.wrap('<div class="socialfeeds-carousel-stage" style="position:relative; width:100%;"></div>'); 235 let $stage = $carousel.parent(); 236 237 let $nav = $('<div class="socialfeeds-carousel-nav">' + 238 '<button class="socialfeeds-carousel-btn prev" aria-label="Previous" type="button"><span class="dashicons dashicons-arrow-left-alt2"></span></button>' + 239 '<button class="socialfeeds-carousel-btn next" aria-label="Next" type="button"><span class="dashicons dashicons-arrow-right-alt2"></span></button>' + 240 '</div>'); 241 242 $stage.append($nav); 243 244 let $dots = $('<div class="socialfeeds-carousel-dots"></div>'); 245 $stage.after($dots); 246 247 function updateDots() { 248 $dots.empty(); 249 let scrollW = $carousel[0].scrollWidth; 250 let clientW = $carousel[0].clientWidth; 251 if (clientW >= scrollW) { 252 $dots.hide(); 253 $nav.hide(); 254 return; 255 } 256 257 $dots.show(); 258 $nav.show(); 259 260 let pageCount = Math.ceil(scrollW / clientW); 261 for (let i = 0; i < pageCount; i++) { 262 let $d = $('<span class="socialfeeds-dot"></span>'); 263 if (i === 0) $d.addClass('active'); 264 $d.data('page', i); 265 $dots.append($d); 266 } 267 } 268 269 setTimeout(updateDots, 300); 270 $(window).on('resize', updateDots); 271 272 // Scroll Events 273 $nav.find('.prev').on('click', function (e) { 274 e.preventDefault(); 275 $carousel.animate({ scrollLeft: $carousel.scrollLeft() - $carousel.width() }, 300, update_active_dot); 276 }); 277 278 $nav.find('.next').on('click', function (e) { 279 e.preventDefault(); 280 $carousel.animate({ scrollLeft: $carousel.scrollLeft() + $carousel.width() }, 300, update_active_dot); 281 }); 282 283 $dots.on('click', '.socialfeeds-dot', function () { 284 let page = $(this).data('page'); 285 $carousel.animate({ scrollLeft: page * $carousel.width() }, 400, update_active_dot); 286 }); 287 288 function update_active_dot() { 289 let scroll_left = $carousel.scrollLeft(); 290 let clientW = $carousel[0].clientWidth; 291 let page = Math.round(scroll_left / (clientW || 1)); 292 $dots.find('.socialfeeds-dot').removeClass('active').eq(page).addClass('active'); 293 } 294 295 $carousel.on('scroll', function () { 296 clearTimeout($carousel.data('scrollTimer')); 297 $carousel.data('scrollTimer', setTimeout(update_active_dot, 100)); 298 }); 299 }); 300 } 301 302 init_insta_Carousel(); 303});
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.