1(function () { 2 'use strict'; 3 4 var placeholderHash = '376c362ac05909bf7e992d71ee25519c7218b46d810b5c24e49710f061eee485'; 5 var checks = new Map(); 6 var hiddenImages = new WeakMap(); 7 8 function hideFailedDescriptionImage(img) { 9 if (img && img.tagName === 'IMG' && img.complete && img.naturalWidth === 0 && 10 img.closest('[data-description-pictures]')) { 11 img.hidden = true; 12 img.style.setProperty('display', 'none', 'important'); 13 } 14 } 15 16 function createInitials(img) { 17 if (!img.hasAttribute('data-listing-initials')) { return null; } 18 var name = (img.alt || '').trim(); 19 var initials = name.split(/\s+/).map(function (word) { 20 var first = word.match(/[\p{L}\p{N}]/u); 21 return first ? first[0].toUpperCase() : ''; 22 }).filter(Boolean).slice(0, 3).join(''); 23 if (!initials || !img.parentNode) { return null; } 24 var placeholder = document.createElement('span'); 25 var size = img.offsetWidth || img.width || 100; 26 var layout = window.getComputedStyle(img); 27 placeholder.className = 'listing-picture-initials'; 28 placeholder.textContent = initials; 29 placeholder.setAttribute('role', 'img'); 30 placeholder.setAttribute('aria-label', name); 31 placeholder.style.width = size + 'px'; 32 placeholder.style.height = size + 'px'; 33 placeholder.style.fontSize = Math.max(10, size * 0.28) + 'px';
34 // Older cards position and space their pictures with img-only rules. 35 // Preserve that outer layout and the template's image corner shape. 36 ['cssFloat', 'clear', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft', 37 'verticalAlign', 'alignSelf', 'order', 'borderTopLeftRadius', 38 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'].forEach(function (property) { 39 placeholder.style[property] = layout[property]; 40 }); 41 placeholder.style.display = layout.display === 'block' ? 'flex' : 'inline-flex'; 42 img.parentNode.insertBefore(placeholder, img); 43 return placeholder; 44 } 45 46 function checkImage(img) { 47 if (!img || img.tagName !== 'IMG') { 48 return; 49 } 50 var source = img.currentSrc || img.src; 51 var previous = hiddenImages.get(img); 52 // Account uploads reuse the image element. Undo only our own hiding. 53 if (previous && previous.source !== source) { 54 if (previous.placeholder) { previous.placeholder.remove(); } 55 img.hidden = previous.hidden; 56 if (previous.display) { 57 img.style.setProperty('display', previous.display, previous.priority); 58 } else { 59 img.style.removeProperty('display'); 60 } 61 hiddenImages.delete(img); 62 } 63 if (img.naturalWidth !== 198 || img.naturalHeight !== 126) { 64 return; 65 } 66 if (!/^https:\/\/graph\.facebook\.com\/(?:v\d+\.\d+\/)?\d+\/picture(?:\?|$)/i.test(source)) { 67 return; 68 } 69 if (!window.fetch || !window.crypto || !window.crypto.subtle || !window.AbortController) { 70 return; 71 } 72 73 // Share verification between repeated pictures on the same page. 74 if (!checks.has(source)) { 75 var controller = new AbortController(); 76 var timeout = window.setTimeout(function () { controller.abort(); }, 5000); 77 var check = window.fetch(source, { credentials: 'omit', signal: controller.signal }) 78 .then(function (response) { 79 if (!response.ok) { return null; } 80 return response.blob(); 81 }) 82 .then(function (blob) { 83 if (!blob || blob.size !== 2380) { return null; } 84 return blob.arrayBuffer(); 85 }) 86 .then(function (bytes) { 87 if (!bytes) { return null; } 88 return window.crypto.subtle.digest('SHA-256', bytes); 89 }) 90 .then(function (hash) { 91 if (!hash) { return false; } 92 return Array.prototype.map.call(new Uint8Array(hash), function (byte) { 93 return ('0' + byte.toString(16)).slice(-2); 94 }).join('') === placeholderHash; 95 }) 96 .catch(function () { return false; }) 97 .then(function (matches) { 98 window.clearTimeout(timeout); 99 return matches; 100 }); 101 checks.set(source, check); 102 } 103 checks.get(source).then(function (matches) { 104 // Do not hide a replacement uploaded while verification was pending. 105 if (matches && (img.currentSrc || img.src) === source) { 106 if (!hiddenImages.has(img)) { 107 hiddenImages.set(img, { 108 source: source, 109 hidden: img.hidden, 110 display: img.style.getPropertyValue('display'), 111 priority: img.style.getPropertyPriority('display'), 112 placeholder: img.hidden ? null : createInitials(img) 113 }); 114 } 115 img.hidden = true; 116 img.style.setProperty('display', 'none', 'important'); 117 } 118 }); 119 } 120 121 // Capture image loads, including lazy-loaded pictures added later. 122 document.addEventListener('load', function (event) { checkImage(event.target); }, true); 123 document.addEventListener('error', function (event) { hideFailedDescriptionImage(event.target); }, true); 124 // The deferred script may run after cached images have already loaded. 125 Array.prototype.forEach.call(document.images, function (img) { 126 if (img.complete) { 127 hideFailedDescriptionImage(img); 128 checkImage(img); 129 } 130 }); 131}());
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.