1/* eslint-env browser */ 2/* global svgSettings, ForceInlineSVGActive, cssTarget, frontSanitizationEnabled, DOMPurify */ 3 4document.addEventListener("DOMContentLoaded", function(event) { 5 6 let bodhisvgsReplacements = 0; 7 let target; 8 9 // Function to replace the img tag with the SVG 10 function bodhisvgsReplace(img) { 11 // Ensure it's an image 12 if (img.nodeName !== 'IMG') { 13 return; 14 } 15 16 const hasTargetClass = img.classList.contains(target); 17 const parentHasTargetClass = img.parentElement.classList.contains(target); 18 const insideTargetContainer = img.closest('.' + target) !== null; 19 20 // First check if we should process at all 21 if (ForceInlineSVGActive !== 'true' && !hasTargetClass && !insideTargetContainer) { 22 return; 23 } 24 25 // If skip nested is enabled, only skip if: 26 // 1. Image doesn't have target class AND 27 // 2. Image's parent is not the target container but is inside one 28 if (svgSettings.skipNested && !hasTargetClass && !parentHasTargetClass && insideTargetContainer) { 29 return; 30 } 31 32 var imgID = img.id; 33 var imgClass = img.className; 34 var imgURL = img.src; 35 36 // Ensure the URL ends with .svg before proceeding 37 if (!imgURL.endsWith('svg')) { 38 return; 39 } 40 41 var xmlHttp = new XMLHttpRequest(); 42 xmlHttp.onreadystatechange = function() { 43 if (xmlHttp.readyState === 4 && xmlHttp.status === 200) { 44 var data = xmlHttp.responseText; 45 46 // Parse the returned data to extract the SVG 47 let parser = new DOMParser(); 48 const doc = parser.parseFromString(data, 'text/html'); 49 50 // Get the SVG tag from the parsed data 51 var svg = doc.getElementsByTagName('svg')[0]; 52 53 var svgID = svg.id; 54 55 // Add replaced image's ID to the new SVG if necessary 56 if (typeof imgID === 'undefined' || imgID === '') { 57 if (typeof svgID === 'undefined' || svgID === '') { 58 imgID = 'svg-replaced-' + bodhisvgsReplacements; 59 svg.setAttribute('id', imgID); 60 } else { 61 imgID = svgID; 62 } 63 } else { 64 svg.setAttribute('id', imgID); 65 } 66 67 // Add replaced image's classes to the new SVG 68 if (typeof imgClass !== 'undefined' && imgClass !== '') { 69 svg.setAttribute('class', imgClass + ' replaced-svg svg-replaced-' + bodhisvgsReplacements); 70 } 71 72 // Remove any invalid XML tags as per http://validator.w3.org 73 svg.removeAttribute('xmlns:a'); 74 75 // If sanitization is enabled, sanitize the SVG code 76 if (frontSanitizationEnabled === 'on' && svg.outerHTML !== "") { 77 var sanitizedSVG = DOMPurify.sanitize(svg.outerHTML); 78 img.outerHTML = sanitizedSVG; 79 } else { 80 img.replaceWith(svg); 81 } 82 83 bodhisvgsReplacements++; 84 85 } else if (xmlHttp.readyState === 4 && xmlHttp.status !== 200) { 86 // Silently fail 87 } 88 }; 89 90 xmlHttp.open("GET", imgURL, true); 91 xmlHttp.send(null); 92 } 93 94 // Function to iterate over nodes and replace images 95 function bodhisvgsIterator(node) { 96 if (node.childNodes.length > 0) { 97 for (var i = 0; i < node.childNodes.length; i++) { 98 if (node.childNodes[i].nodeName === 'IMG') { 99 var img = node.childNodes[i]; 100 bodhisvgsReplace(img); 101 } else { 102 bodhisvgsIterator(node.childNodes[i]); 103 } 104 } 105 } 106 } 107 108 // Wrap in IIFE so that it can be called again later as bodhisvgsInlineSupport(); 109 (bodhisvgsInlineSupport = function() { 110 // If force inline SVG option is active then add class 111 if (ForceInlineSVGActive === 'true') { 112 var allImages = document.getElementsByTagName('img'); 113 for (var i = 0; i < allImages.length; i++) { 114 if (typeof allImages[i].src !== 'undefined') { 115 if (allImages[i].src.match(/\.(svg)/)) { 116 if (!allImages[i].classList.contains(cssTarget.ForceInlineSVG)) { 117 allImages[i].classList.add(cssTarget.ForceInlineSVG); 118 } 119 } 120 } 121 } 122 } 123
124 // Polyfill to support all older browsers 125 if (!String.prototype.endsWith) { 126 String.prototype.endsWith = function(searchString, position) { 127 var subjectString = this.toString(); 128 if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { 129 position = subjectString.length; 130 } 131 position -= searchString.length; 132 var lastIndex = subjectString.lastIndexOf(searchString, position); 133 return lastIndex !== -1 && lastIndex === position; 134 }; 135 } 136 137 // Another snippet to support IE11 138 String.prototype.endsWith = function(pattern) { 139 var d = this.length - pattern.length; 140 return d >= 0 && this.lastIndexOf(pattern) === d; 141 }; 142 143 // Set target before we use it 144 if (ForceInlineSVGActive === 'true') { 145 target = cssTarget.Bodhi !== 'img.' ? cssTarget.ForceInlineSVG : 'style-svg'; 146 } else { 147 target = cssTarget.Bodhi !== 'img.' ? cssTarget.Bodhi : 'style-svg'; 148 } 149 150 // Ensure target is a string before applying replace method 151 if (typeof target === 'string') { 152 target = target.replace("img.", ""); 153 } else { 154 return; 155 } 156 157 // Replace images with SVGs based on the target class 158 document.querySelectorAll('.' + target).forEach(function(element) { 159 if (element.nodeName === 'IMG') { 160 bodhisvgsReplace(element); 161 } else { 162 bodhisvgsIterator(element); 163 } 164 }); 165 166 })(); // Execute immediately 167}); 168
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.