1/** 2 * Rewards Points â standalone module 3 * 4 * Fetches rewards points for the current product and injects a static 5 * (non-accordion) rewards card above the sidepanel accordions. 6 * 7 * Depends on: universal_product (platform global), #seconddetails being 8 * present in the DOM (injected by pdp-sidepanel.js). 9 */ 10(function () { 11 'use strict'; 12 13 // --------------------------------------------------------------------------- 14 // Configuration â update per deployment 15 // --------------------------------------------------------------------------- 16 var CONFIG = { 17 endpoint: '/wsCitrusStore.asmx/RewardsPointsFull', 18 19 // Selector for the element the rewards card is inserted before 20 insertBefore: '#ModularLongDescription', 21 22 // Only show the card when the rewards percentage meets or exceeds this value 23 threshold: 5, 24 25 text: { 26 // {name} â product name, {value} â £ value string, {pct} â percentage integer 27 earnLabel: 'Earn {pct}% Back in Reward Points', 28 intro: 'Purchase this {name} and receive <strong>{value}</strong> ({pct}%) of its value back in Reward Points, ready to use on a future purchase. You must be signed in to your account at the time of purchase to receive points. Points will not be added if you checkout as a guest.', 29 // Set to '' to hide 30 note: '', 31 linkUrl: '/ssl/customerrewardslogin.aspx', 32 linkText: 'Learn more', 33 } 34 }; 35 36 // --------------------------------------------------------------------------- 37 // Icon 38 // --------------------------------------------------------------------------- 39 var ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11.245 4.174c.317-.938 1.593-.938 1.91 0l1.387 4.104a1 1 0 0 0 .95.692h4.31c.985 0 1.394 1.26.598 1.838l-3.486 2.536a1 1 0 0 0-.364 1.117l1.387 4.103c.317.938-.77 1.716-1.567 1.117l-3.487-2.537a1 1 0 0 0-1.182 0l-3.487 2.537c-.797.599-1.884-.18-1.567-1.117l1.387-4.103a1 1 0 0 0-.364-1.117L3.6 10.808c-.796-.578-.387-1.838.598-1.838h4.31a1 1 0 0 0 .95-.692z"/></svg>'; 40 41 // --------------------------------------------------------------------------- 42 // Percentage calculation 43 // --------------------------------------------------------------------------- 44 function calcRewards(rewardsPounds, salePrice) { 45 var pct = salePrice > 0 ? Math.round((rewardsPounds / salePrice) * 100) : 0; 46 var cssClass = ''; 47 48 if (pct >= 3) { 49 cssClass = 'pdpRewards--triple'; 50 } else if (pct === 2) { 51 cssClass = 'pdpRewards--double'; 52 } 53 54 return { pct: pct, cssClass: cssClass }; 55 } 56 57 // --------------------------------------------------------------------------- 58 // Render 59 // --------------------------------------------------------------------------- 60 function render(productName, valueStr, pct, cssClass) { 61 var target = document.querySelector(CONFIG.insertBefore); 62 if (!target) return; 63 64 var earnLabel = CONFIG.text.earnLabel.replace('{pct}', pct); 65 66 var introText = CONFIG.text.intro 67 .replace('{name}', productName) 68 .replace('{value}', valueStr) 69 .replace('{pct}', pct); 70 71 var noteHtml = CONFIG.text.note 72 ? '<p class="pdpRewards-note">' + CONFIG.text.note + '</p>' 73 : ''; 74 75 var linkHtml = CONFIG.text.linkUrl 76 ? '<a href="' + CONFIG.text.linkUrl + '" class="pdpRewards-link" target="_blank" rel="noopener">' + CONFIG.text.linkText + '</a>' 77 : ''; 78 79 var html = 80 '<div id="pdpRewards" class="pdpRewards ' + cssClass + '">' + 81 '<div class="pdpRewards-header">' + 82 '<span class="pdpRewards-icon">' + ICON + '</span>' + 83 '<span class="pdpRewards-earn">' + earnLabel + '</span>' + 84 linkHtml + 85 '</div>' + 86 '<div class="pdpRewards-body">' + 87 '<p class="pdpRewards-intro">' + introText + '</p>' + 88 noteHtml + 89 '</div>' + 90 '</div>'; 91 92 target.insertAdjacentHTML('beforebegin', html); 93 } 94 95 // --------------------------------------------------------------------------- 96 // Fetch 97 // ---------------------------------------------------------------------------
98 function init() { 99 if (typeof universal_product === 'undefined' || !universal_product) return; 100 101 var productName = universal_product.name || ''; 102 var salePrice = universal_product.unit_sale_price || 0; 103 104 var xhr = new XMLHttpRequest(); 105 xhr.open('POST', CONFIG.endpoint, true); 106 xhr.setRequestHeader('Content-Type', 'application/json'); 107 108 xhr.onreadystatechange = function () { 109 if (xhr.readyState !== 4) return; 110 111 if (xhr.status >= 200 && xhr.status < 300) { 112 try { 113 var result = JSON.parse(xhr.responseText); 114 var valueStr = (result && result.d) ? result.d : null; 115 116 if (!valueStr) return; 117 118 var valuePounds = parseFloat(valueStr.replace('\u00A3', '').trim()) || 0; 119 var calc = calcRewards(valuePounds, salePrice); 120 121 if (calc.pct < CONFIG.threshold) return; 122 123 render(productName, valueStr, calc.pct, calc.cssClass); 124 } catch (e) { 125 console.error('PDP Rewards: failed to parse response', e); 126 } 127 } else { 128 console.error('PDP Rewards: request failed with status ' + xhr.status); 129 } 130 }; 131 132 xhr.send(JSON.stringify({ productId: universal_product.id, attributeDetailId: null })); 133 } 134 135 // --------------------------------------------------------------------------- 136 // Boot 137 // --------------------------------------------------------------------------- 138 if (document.readyState === 'loading') { 139 document.addEventListener('DOMContentLoaded', init); 140 } else { 141 init(); 142 } 143 144}());
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.