1jQuery(function ($) { 2 var selector = null; 3 var lightbox = null; 4 5 // Whitelist of allowed HTML elements and their allowed attributes. 6 var allowedTags = { 7 a: ["href", "title", "target", "rel"], 8 b: [], 9 i: [], 10 u: [], 11 em: [], 12 strong: [], 13 p: [], 14 br: [], 15 span: ["class", "id", "style"], 16 img: ["src", "alt", "title"], 17 h1: [], 18 h2: [], 19 h3: [], 20 h4: [], 21 h5: [], 22 h6: [], 23 ul: [], 24 ol: [], 25 li: [], 26 }; 27 28 // Function to sanitize HTML, allowing only certain tags and attributes. 29 var sanitizeHTML = function (str) { 30 // Create a temporary DOM element to parse the HTML string. 31 var tempDiv = document.createElement("div"); 32 tempDiv.innerHTML = str; 33 34 // Iterate through all elements. 35 var elements = tempDiv.querySelectorAll("*"); 36 elements.forEach(function (el) { 37 var tagName = el.tagName.toLowerCase(); 38 39 // If the tag is not allowed, replace the element with its content. 40 if (!allowedTags.hasOwnProperty(tagName)) { 41 el.replaceWith(el.innerHTML); 42 return; 43 } 44 45 // If the tag is allowed, check attributes. 46 var allowedAttributes = allowedTags[tagName]; 47 48 // Loop through each attribute of the element. 49 for (var i = el.attributes.length - 1; i >= 0; i--) { 50 var attrName = el.attributes[i].name; 51 var attrValue = el.attributes[i].value; 52 53 // Remove attributes that are not allowed for this tag. 54 if (!allowedAttributes.includes(attrName)) { 55 el.removeAttribute(attrName); 56 } 57 58 // Additional checks to sanitize certain attributes like href, src. 59 if ( 60 ["href", "src"].includes(attrName) && 61 attrValue.startsWith("javascript:") 62 ) { 63 el.removeAttribute(attrName); // Remove dangerous URLs. 64 } 65 66 // Sanitize the title attribute (if allowed). 67 if (attrName === "title") { 68 el.setAttribute("title", sanitizeTitle(attrValue)); // Sanitize the title value. 69 } 70 } 71 }); 72 73 // Return the sanitized HTML as a string. 74 var sanitizedText = tempDiv.innerHTML; 75 return sanitizedText.replace(/\\/g, ""); 76 }; 77 78 // Helper function to sanitize the content of the title attribute. 79 var sanitizeTitle = function (title) { 80 // Replace potential XSS characters in the title. 81 return title 82 .replace(/</g, "<") 83 .replace(/>/g, ">") 84 .replace(/"/g, """); 85 }; 86 87 // Function to sanitize captions in the DOM elements. 88 var sanitizeCaptions = function () { 89 $(".ngg-simplelightbox").each(function () { 90 var caption = $(this).attr("title"); 91 if (caption) { 92 // Sanitize the caption and update the element attribute. 93 var sanitizedCaption = sanitizeHTML(caption); 94 $(this).attr("title", sanitizedCaption); 95 } 96 }); 97 }; 98 99 var handleTikTokContent = function (element, imageContainer) { 100 var playUrl = element.getAttribute("data-tiktok-play-url"); 101 var shareUrl = element.getAttribute("data-tiktok-share-url"); 102 103 if (!playUrl && !shareUrl) { 104 imageContainer.classList.remove("sl-tiktok-mode"); 105 return false; 106 } 107 108 imageContainer.classList.add("sl-tiktok-mode"); 109 110 var existingContainer = imageContainer.querySelector(".ngg-tiktok-container"); 111 if (existingContainer) { 112 existingContainer.remove(); 113 } 114 var existingError = imageContainer.querySelector(".ngg-tiktok-error"); 115 if (existingError) { 116 existingError.remove(); 117 } 118 119 var $img = $(imageContainer).find("img"); 120 121 NextGEN_TikTok.handle_content({ 122 playUrl: playUrl, 123 shareUrl: shareUrl, 124 container: imageContainer, 125 width: $img.width(), 126 height: $img.height(), 127 onBeforeAppend: function () { 128 // img is already hidden via CSS .sl-tiktok-mode, but we can be explicit 129 $img.hide(); 130 }, 131 }); 132 133 return true; 134 }; 135 136 var cleanupTikTokContent = function (imageContainer) { 137 imageContainer.classList.remove("sl-tiktok-mode"); 138 139 var tiktokContainer = imageContainer.querySelector(".ngg-tiktok-container"); 140 if (tiktokContainer) { 141 tiktokContainer.remove(); 142 } 143 var errorContainer = imageContainer.querySelector(".ngg-tiktok-error"); 144 if (errorContainer) { 145 errorContainer.remove(); 146 } 147 }; 148 149 // Get video settings for a gallery 150 var getVideoSettings = function(galleryId) { 151 if (!window.ngg_video_gallery_settings) { 152 return { 153 show_video_controls: true, 154 show_play_pause_controls: true,
155 autoplay_videos: false 156 }; 157 } 158 159 var settings = window.ngg_video_gallery_settings; 160 var galleryIdStr = galleryId ? String(galleryId) : null; 161 162 if (galleryIdStr && settings['gallery_' + galleryIdStr]) { 163 return settings['gallery_' + galleryIdStr]; 164 } 165 166 return settings.default || { 167 show_video_controls: true, 168 show_play_pause_controls: true, 169 autoplay_videos: false 170 }; 171 }; 172 173 var handleVideoContent = function (element, imageContainer) { 174 const videoContainer = document.querySelector(".sl-wrapper") 175 176 var videoUrl = element.getAttribute("data-video-url") || element.getAttribute("href"); 177 178 if (!videoUrl) { 179 videoContainer.classList.remove("sl-video-mode"); 180 return false; 181 } 182 183 if (!window.NextGEN_Video || !window.NextGEN_Video.detect_platform(videoUrl)) { 184 videoContainer.classList.remove("sl-video-mode"); 185 return false; 186 } 187 188 videoContainer.classList.add("sl-video-mode"); 189 190 var existingContainer = imageContainer.querySelector(".ngg-video-container"); 191 if (existingContainer) { 192 existingContainer.remove(); 193 } 194 var existingError = imageContainer.querySelector(".ngg-video-error"); 195 if (existingError) { 196 existingError.remove(); 197 } 198 199 var galleryId = null; 200 var $galleryContainer = $(element).closest('[data-gallery-id]'); 201 if ($galleryContainer.length) { 202 galleryId = $galleryContainer.attr('data-gallery-id') || $galleryContainer.data('gallery-id'); 203 } 204 var videoSettings = getVideoSettings(galleryId); 205 206 var $img = $(imageContainer).find("img"); 207 208 if (window.NextGEN_Video && window.NextGEN_Video.handle_content) { 209 window.NextGEN_Video.handle_content({ 210 videoUrl: videoUrl, 211 container: imageContainer, 212 settings: videoSettings, 213 containerClass: "ngg-video-container", 214 videoClass: "ngg-video-player", 215 errorClass: "ngg-video-error", 216 onBeforeAppend: function () { 217 // img is already hidden via CSS .sl-video-mode, but we can be explicit 218 $img.hide(); 219 }, 220 }); 221 } 222 223 return true; 224 }; 225 226 var cleanupVideoContent = function (imageContainer) { 227 imageContainer.classList.remove("sl-video-mode"); 228 229 var videoContainer = imageContainer.querySelector(".ngg-video-container"); 230 if (videoContainer) { 231 videoContainer.remove(); 232 } 233 var errorContainer = imageContainer.querySelector(".ngg-video-error"); 234 if (errorContainer) { 235 errorContainer.remove(); 236 } 237 }; 238 239 // Factory function to create lightbox content handlers 240 // Reduces code duplication between TikTok and video handlers 241 var createLightboxHandlers = function (options) { 242 var handleContent = options.handleContent; 243 var cleanupContent = options.cleanupContent; 244 var eventName = options.eventName || "simplelightbox"; 245 246 return function (elements) { 247 elements.each(function () { 248 var el = this; 249 250 // Handle shown event - content is displayed (fires after lightbox opens) 251 // Small delay (150ms) ensures the image is fully rendered before processing. 252 // This delay accounts for CSS transitions and DOM updates in SimpleLightbox. 253 el.addEventListener("shown." + eventName, function () { 254 setTimeout(function () { 255 var imageContainer = document.querySelector(".sl-image"); 256 if (imageContainer) { 257 handleContent(el, imageContainer); 258 } 259 }, 150); 260 }); 261 262 // Handle changed event - navigated to new image 263 // Same 150ms delay for consistency with shown event 264 el.addEventListener("changed." + eventName, function () { 265 setTimeout(function () { 266 var imageContainer = document.querySelector(".sl-image"); 267 if (imageContainer) { 268 cleanupContent(imageContainer); 269 handleContent(el, imageContainer); 270 } 271 }, 150); 272 }); 273 274 // Handle close event - clean up 275 el.addEventListener("close." + eventName, function () { 276 var imageContainer = document.querySelector(".sl-image"); 277 if (imageContainer) { 278 cleanupContent(imageContainer); 279 } 280 }); 281 }); 282 }; 283 }; 284 285 // Note: SimpleLightbox uses native dispatchEvent with event names like 'shown.simplelightbox' 286 // jQuery's .on() interprets dots as namespace separators, so we must use native addEventListener 287 var attachTikTokHandlers = createLightboxHandlers({ 288 handleContent: handleTikTokContent, 289 cleanupContent: cleanupTikTokContent, 290 }); 291 292 // Attach video handlers to lightbox elements 293 var attachVideoHandlers = createLightboxHandlers({ 294 handleContent: handleVideoContent, 295 cleanupContent: cleanupVideoContent, 296 }); 297 298 var nextgen_simplebox_options = { 299 history: false, 300 animationSlide: false, 301 animationSpeed: 100, 302 captionSelector: "self", 303 }; 304 305 var nextgen_simplelightbox_init = function () { 306 sanitizeCaptions(); 307 308 selector = nextgen_lightbox_filter_selector($, $(".ngg-simplelightbox")); 309 if (selector.length > 0) { 310 lightbox = selector.simpleLightbox(nextgen_simplebox_options); 311 attachTikTokHandlers(selector); 312 // Attach video handlers 313 attachVideoHandlers(selector); 314 } 315 }; 316 317 nextgen_simplelightbox_init(); 318 319 $(window).on("refreshed", function () { 320 if (lightbox) { 321 lightbox.destroy(); 322 } 323 324 sanitizeCaptions(); 325 326 selector = nextgen_lightbox_filter_selector($, $(".ngg-simplelightbox")); 327 if (selector.length > 0) { 328 lightbox = selector.simpleLightbox(nextgen_simplebox_options); 329 attachTikTokHandlers(selector); 330 // Attach video handlers 331 attachVideoHandlers(selector); 332 } 333 }); 334});
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.