1/** 2 * @file 3 * GLightbox JS. 4 */ 5 6(function (Drupal, drupalSettings, once) { 7 'use strict'; 8 9 // Create glightbox namespace if it doesn't exist. 10 if (!Drupal.hasOwnProperty('glightbox')) { 11 Drupal.glightbox = {}; 12 } 13 14 /** 15 * Global function to allow sanitizing captions and control strings. 16 * 17 * @param markup 18 * String containing potential markup. 19 * @return {string} 20 * Sanitized string with potentially dangerous markup removed. 21 */ 22 Drupal.glightbox.sanitizeMarkup = function(markup) { 23 if (typeof DOMPurify !== 'undefined') { 24 var purifyConfig = { 25 ALLOWED_TAGS: [ 26 'a', 'b', 'strong', 'i', 'em', 'u', 'cite', 'code', 'br', 'p', 'div', 'h1', 'h2', 'h3', 'h4', 'span' 27 ], 28 ALLOWED_ATTR: [ 29 'href', 'hreflang', 'title', 'target', 30 ] 31 }; 32 if (typeof drupalSettings !== 'undefined' && drupalSettings.hasOwnProperty('dompurify_custom_config')) { 33 purifyConfig = drupalSettings.dompurify_custom_config; 34 } 35 return DOMPurify.sanitize(markup, purifyConfig); 36 } 37 else if (typeof Drupal.checkPlain === 'function') { 38 return Drupal.checkPlain(markup); 39 } 40 else { 41 // Fallback: basic escaping. 42 return String(markup).replace(/[&<>"]/g, function (c) { 43 return {'&':'&','<':'<','>':'>','"':'"'}[c]; 44 }); 45 } 46 }; 47 48 /** 49 * Sanitise a user-supplied href. 50 * - Allows absolute http/https/ftp/ftps only (per SAFE_PROTOCOL_RE). 51 * - Drops *all* relative or protocol-relative URLs (â/fooâ, â//cdnâ¦â, â../barâ, â¦). 52 * - Returns the cleaned, canonicalised URL string, or an empty string when disallowed. 53 */ 54 Drupal.glightbox.sanitizeHref = function(rawHref) { 55 if (typeof rawHref !== 'string') return ''; 56 57 // Strip obvious obfuscation. 58 const clean = rawHref 59 // Null bytes. 60 .replace(/\u0000/g, '') 61 // Control chars. 62 .replace(/[\t\n\r]/g, '') 63 .trim(); 64 65 const SAFE_PROTOCOL_RE = /^(?:(?:(?:f|ht)tps?):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; 66 // Fast-fail if protocol isnât in the allow-list. 67 if (!SAFE_PROTOCOL_RE.test(clean)) return ''; 68 69 // Require the string to *explicitly* contain a protocol. 70 // Skip relative / protocol-relative URLs. 71 if (!/^[a-z][a-z0-9+.\-]*:/.test(clean)) return ''; 72 73 // Let the URL constructor canonicalise & validate. 74 try { 75 // Absolute URL required. 76 const url = new URL(clean); 77 return url.href; 78 } catch { 79 // Malformed, unreachable, etc. 80 return ''; 81 } 82 } 83 84 Drupal.behaviors.initGLightbox = { 85 attach: function (context, settings) { 86 // Sanitize data-title and data-description attributes on all .glightbox elements before GLightbox is initialized. 87 document.querySelectorAll('.glightbox').forEach(el => { 88 el.getAttributeNames() 89 .filter(name => name.startsWith('data-')) 90 .forEach(name => { 91 const rawValue = el.getAttribute(name); 92 let clean = Drupal.glightbox.sanitizeMarkup(rawValue); 93 if (name === 'data-href') { 94 clean = Drupal.glightbox.sanitizeHref(clean); 95 } 96 el.setAttribute(name, clean); 97 }); 98 }); 99 const options = settings.glightbox || {}; 100 const lightbox = GLightbox(options); 101 102 lightbox.on('slide_changed', ({ prev, current }) => { 103 const { slideIndex, slideNode, slideConfig, player } = current; 104
105 // Video autoplay if it's possible. 106 if (player) { 107 if (!player.ready) { 108 // If player is not ready. 109 player.on('ready', (event) => { 110 // Do something when video is ready. 111 }); 112 } 113 114 player.on('play', (event) => { 115 // console.log('Started play'); 116 }); 117 118 player.on('volumechange', (event) => { 119 // console.log('Volume change'); 120 }); 121 122 player.on('ended', (event) => { 123 // console.log('Video ended'); 124 }); 125 } 126 }); 127 }, 128 }; 129})(Drupal, drupalSettings, once);
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.