1/** NCE **/ 2jQuery(document).ready(function ($) { 3 (window.misocmd || (window.misocmd = [])).push(async () => { 4 var brandCode = miso_script_vars.brandCode; 5 const TYPES = [ 6 { 7 type: 'News', 8 alternatives: ['Breaking', 'Digital edition', 'Event', 'Exclude from homepages', 'ICE', 'ICE What\'s on', 'Multimedia', 'NCE100', 'News', 'Webinar'] 9 }, 10 { 11 type: 'Comment', 12 alternatives: ['Comment', 'Company profile', 'Industry View', 'Listicle', 'Your View'] 13 }, 14 { 15 type: 'In Depth', 16 alternatives: ['Analysis', 'Explainer', 'Feature', 'Inside Track'] 17 }, 18 { 19 type: 'Interview', 20 alternatives: ['Interview', 'Podcast'] 21 }, 22 { 23 type: 'Sponsored', 24 alternatives: ['Tech Showcase'] 25 } 26 ]; 27 28 const TYPE_INDEX = TYPES.reduce((map, { type, alternatives }) => { 29 map.set(type.toLowerCase(), type); 30 for (const alt of alternatives || []) { 31 map.set(alt.toLowerCase(), type); 32 } 33 return map; 34 }, new Map()); 35 function getType(source) { 36 37 const custom_attributes = source.custom_attributes || {}; 38 //console.log(custom_attributes); 39 //const type = custom_attributes.type && custom_attributes.type[0] || undefined; 40 let typeValue = custom_attributes.type; 41 // Normalize typeValue to array 42 const typeArray = Array.isArray(typeValue) ? typeValue : (typeof typeValue === 'string' ? [typeValue] : []); 43 const type = typeArray[0]; // Use the first value, if available 44 return type && TYPE_INDEX.get(type.toLowerCase()) || 'Other'; 45 } 46 function onCitationLink({ setAttribute, setTooltipHtml, escapeHtml }, { source, index }) { 47 if (source) { 48 // determine source article type by some logic 49 const type = getType(source); 50 if (type) { 51 setAttribute('data-type', type); 52 setTooltipHtml(`<span class="title">${escapeHtml(source.title)}</span><span class="type">${type}</span>`); 53 } else { 54 setTooltipHtml(`<span class="title">${escapeHtml(source.title)}</span>`); 55 } 56 } 57 } 58 function escapeHtml(text) { 59 text = `${text}`; 60 return text && text 61 .replaceAll('&', '&') 62 .replaceAll('<', '<') 63 .replaceAll('>', '>') 64 .replaceAll('"', '"') 65 .replaceAll("'", '''); 66 } 67 function renderArticleInfoBlock(layout, article) { 68 const { className } = layout; 69 const { title, snippet, description, created_at, updated_at, published_at } = article; 70 const date = published_at || created_at || updated_at; 71 const type = getType(article); 72 let content = ''; 73 if (title) { 74 content += `<div class="${className}__item-title">${escapeHtml(title)}</div>`; 75 } 76 if (snippet) { 77 content += `<div class="${className}__item-snippet">${snippet}</div>`; 78 } else if (description) { 79 content += `<div class="${className}__item-desc">${escapeHtml(description)}</div>`; 80 } 81 content += `<div class="${className}__item-date-type">`; 82 if (date) { 83 content += `<div class="${className}__item-date">${new Date(date).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}</div>`; 84 } 85 if (type) { 86 content += `<div class="${className}__item-type" data-type="${type}">${type}</div>`; 87 } 88 content += `</div>`; 89 return `<div class="${className}__item-info-container">${content}</div>`; 90 } 91 function renderArticleIndexBlock(layout, article, { index }) { 92 const { className } = layout; 93 const i = index + 1; 94 const type = getType(article); 95 return `<div class="${className}__item-index-container"><span class="${className}__item-index miso-citation-index" data-index="${i}" data-type="${type}"></span></div>`; 96 } 97 function getElements(workflow) { 98 const parentQuestionId = workflow.parentQuestionId; 99 const comboElement = document.querySelector('miso-ask-combo'); 100 const containerSelector = parentQuestionId ? `miso-ask[parent-question-id="${parentQuestionId}"]` : `miso-ask`; 101 // insert type definitions block if not already present 102 if (!comboElement.querySelector(`${containerSelector} .miso-type-defs`)) { 103 const questionElement = comboElement.querySelector(`${containerSelector} miso-question`); 104 questionElement.insertAdjacentHTML('afterend', `<div class="miso-type-defs"><ul></ul></div>`); 105 } 106 const typeDefsListElement = comboElement.querySelector(`${containerSelector} .miso-type-defs ul`); 107 return { comboElement, typeDefsListElement }; 108 } 109 MisoClient.on('create', (client) => { 110 const context = client.ui.asks; 111 context.useLayouts({
112 answer: { 113 onCitationLink, 114 }, 115 sources: ['list', { 116 templates: { 117 infoBlock: renderArticleInfoBlock, 118 indexBlock: renderArticleIndexBlock, 119 }, 120 }], 121 }); 122 context.useApi({ 123 source_fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at', 'custom_attributes.type'], 124 "_pipeline_config": { 125 "version": "v3", 126 }, 127 }); 128 context.on('loading', (event) => { 129 const { typeDefsListElement } = getElements(event.workflow); 130 // clean up type definitions block 131 typeDefsListElement.innerHTML = ''; 132 }); 133 context.on('data', (event) => { 134 const data = event.value; 135 if (!data) { 136 return; 137 } 138 const sources = data.sources; 139 if (!sources || sources.length === 0) { 140 return; 141 } 142 const { typeDefsListElement } = getElements(event.workflow); 143 // insert type definitions block if not already present 144 for (const type of sources.map(getType)) { 145 // add to <ul> if not already present 146 if (!type || typeDefsListElement.querySelector(`li[data-type="${type}"]`)) { 147 continue; 148 } 149 typeDefsListElement.insertAdjacentHTML('beforeend', `<li data-type="${type}">${type}</li>`); 150 } 151 }); 152 context.on('ready', (event) => { 153 const workflow = event.workflow; 154 const parentQuestionId = workflow.parentQuestionId; 155 const comboElement = document.querySelector('miso-ask-combo'); 156 const containerSelector = parentQuestionId ? `miso-ask[parent-question-id="${parentQuestionId}"]` : `miso-ask`; 157 if (comboElement.querySelector(`${containerSelector} .miso-notice`)) { 158 return; 159 } 160 const feedbackElement = comboElement.querySelector(`${containerSelector} miso-feedback`); 161 feedbackElement.insertAdjacentHTML('afterend', ` 162 <hr> 163 <div class="miso-notice"> 164 <div class="miso-notice-icon"><i>!</i></div> 165 <div class="miso-notice-content">As a note of caution, while the Artificial Intelligence engine is constantly learning, we cannot guarantee these Ask NCE AI-generated answers will always be without error. Please use your own judgment on the suitability of the answer and review the articles the answer is based on. Any action taken based on the answer is at your own risk.</div> 166 </div> 167 `); 168 }); 169 }); 170 171 // --- START: MutationObserver to remove reasoning/thought boxes --- 172 const observer = new MutationObserver((mutations) => { 173 mutations.forEach(() => { 174 document.querySelectorAll('.miso-ask-combo__reasoning-container') 175 .forEach(el => el.remove()); 176 }); 177 }); 178 observer.observe(document.body, { childList: true, subtree: true }); 179 // --- END: MutationObserver --- 180 181 var brand_name = miso_script_vars.brandName; 182 var miso_user_id = miso_script_vars.miso_user_id; 183 var miso_user_hash = miso_script_vars.user_hash; 184 let b_code = brandCode.toLowerCase(); 185 var fq_text = 'brand:"' + brand_name + '"'; 186 187 if (miso_script_vars.hasOwnProperty('ex_categories') && miso_script_vars.ex_categories != "") { 188 var str_cats = miso_script_vars.ex_categories; 189 var c_arr = str_cats.split(','); 190 var str = ''; 191 for (var i = 0; i < c_arr.length; i++) { 192 str += '"' + c_arr[i] + '"'; 193 if (i < c_arr.length - 1) { 194 str += ' or '; 195 } 196 } 197 fq_text += ' AND (NOT(categories:(' + str + ')))'; 198 } 199 200 if (miso_script_vars.hasOwnProperty('custom_attributes') && miso_script_vars.custom_attributes != "") { 201 var custom_attributes = formatKeyValuePairs(miso_script_vars, 'custom_attributes'); 202 fq_text += ' AND (NOT(' + custom_attributes + '))'; 203 } 204 205 206 // Function to get key-value pairs from an object 207 function formatKeyValuePairs(data, groupKey) { 208 // Get the specific group object based on the provided key 209 const groupObj = data[groupKey]; 210 211 if (!groupObj || typeof groupObj !== 'object') { 212 return ''; 213 } 214 215 let formattedPairs = []; 216 217 for (const [key, value] of Object.entries(groupObj)) { 218 formattedPairs.push(`${groupKey}.${key}:(${value.map(v => `"${v}"`).join(' or ')})`); 219 } 220 221 return formattedPairs.join(' or '); 222 } 223 224 const combo = MisoClient.ui.combo.ask; 225 combo.config({ 226 api: { 227 fq: fq_text, 228 user_id: miso_user_id, 229 user_hash: miso_user_hash, 230 source_fl: ['cover_image', 'url', 'created_at', 'updated_at', 'published_at', 'custom_attributes.type'], 231 }, 232 }); 233 combo.start(); 234 });
vendor: 3 bytes, line 235
235});
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.