PageSourceSearch

https://www.englishchess.org.uk/wp-content/plugins/nextgen-gallery…/nextgen_simple_lightbox_init.js?ver=4.5.1

js englishchess.org.uk collected 2026-09-24 08:42:10 UTC 10,296 bytes, 334 lines download raw bytes

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, "&lt;")
83      .replace(/>/g, "&gt;")
84      .replace(/"/g, "&quot;");
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.