Understand news context by viewing multiple perspectives and source comparisons
Size
16.1 KB
Version
1.1.1
Created
Jan 5, 2026
Updated
29 days ago
1// ==UserScript==
2// @name ContextCheck - News Context Analyzer
3// @description Understand news context by viewing multiple perspectives and source comparisons
4// @version 1.1.1
5// @match https://*.cnn.com/*
6// @match https://*.bbc.com/*
7// @match https://*.nytimes.com/*
8// @match https://*.theguardian.com/*
9// @match https://*.washingtonpost.com/*
10// @match https://*.reuters.com/*
11// @match https://*.apnews.com/*
12// @match https://*.nbcnews.com/*
13// @match https://*.foxnews.com/*
14// @match https://*.aljazeera.com/*
15// @match https://*.bloomberg.com/*
16// @match https://*.wsj.com/*
17// @match https://*.usatoday.com/*
18// @match https://*.cbsnews.com/*
19// @match https://*.abcnews.go.com/*
20// @icon https://robomonkey.io/favicon.ico
21// ==/UserScript==
22(function() {
23 'use strict';
24
25 console.log('ContextCheck: Extension loaded');
26
27 // Mock data generator for realistic demo results
28 function generateMockResults(headline, url) {
29 const mockScenarios = [
30 {
31 sourcesFound: 12,
32 agreements: [
33 "Event occurred on the reported date and location",
34 "Key figures and organizations involved are confirmed across sources",
35 "Timeline of major events is consistent"
36 ],
37 differences: [
38 "Estimated numbers vary between 5,000-8,000 depending on source",
39 "Some outlets emphasize economic impact, others focus on social aspects",
40 "Attribution of responsibility differs between regional and international sources"
41 ],
42 confidence: "High"
43 },
44 {
45 sourcesFound: 8,
46 agreements: [
47 "Core facts about the announcement are verified",
48 "Official statements match across multiple outlets"
49 ],
50 differences: [
51 "Interpretation of long-term implications varies significantly",
52 "Expert opinions quoted differ based on political leaning",
53 "Regional sources provide additional context not found in international coverage"
54 ],
55 confidence: "Medium"
56 },
57 {
58 sourcesFound: 5,
59 agreements: [
60 "Basic details of the incident are consistent",
61 "Primary source quotes match across reports"
62 ],
63 differences: [
64 "Limited independent verification available",
65 "Conflicting reports about specific details and timing",
66 "Some sources cite anonymous officials while others use named sources"
67 ],
68 confidence: "Low"
69 }
70 ];
71
72 // Randomly select a scenario for demo purposes
73 const scenario = mockScenarios[Math.floor(Math.random() * mockScenarios.length)];
74 return scenario;
75 }
76
77 // Add styles
78 function addStyles() {
79 const styles = `
80 #contextcheck-button {
81 position: fixed;
82 bottom: 30px;
83 right: 30px;
84 z-index: 999999;
85 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
86 color: white;
87 border: none;
88 border-radius: 50px;
89 padding: 16px 28px;
90 font-size: 15px;
91 font-weight: 600;
92 cursor: pointer;
93 box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
94 transition: all 0.3s ease;
95 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
96 display: flex;
97 align-items: center;
98 gap: 8px;
99 }
100
101 #contextcheck-button:hover {
102 transform: translateY(-2px);
103 box-shadow: 0 12px 32px rgba(102, 126, 234, 0.5);
104 }
105
106 #contextcheck-button:active {
107 transform: translateY(0);
108 }
109
110 #contextcheck-button::before {
111 content: "🔍";
112 font-size: 18px;
113 }
114
115 #contextcheck-popup {
116 position: fixed;
117 top: 50%;
118 left: 50%;
119 transform: translate(-50%, -50%);
120 z-index: 1000000;
121 background: white;
122 border-radius: 16px;
123 box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
124 width: 90%;
125 max-width: 600px;
126 max-height: 80vh;
127 overflow-y: auto;
128 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
129 animation: slideIn 0.3s ease;
130 }
131
132 @keyframes slideIn {
133 from {
134 opacity: 0;
135 transform: translate(-50%, -45%);
136 }
137 to {
138 opacity: 1;
139 transform: translate(-50%, -50%);
140 }
141 }
142
143 #contextcheck-overlay {
144 position: fixed;
145 top: 0;
146 left: 0;
147 right: 0;
148 bottom: 0;
149 background: rgba(0, 0, 0, 0.5);
150 z-index: 999999;
151 animation: fadeIn 0.3s ease;
152 }
153
154 @keyframes fadeIn {
155 from { opacity: 0; }
156 to { opacity: 1; }
157 }
158
159 .contextcheck-header {
160 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
161 color: white;
162 padding: 24px;
163 border-radius: 16px 16px 0 0;
164 display: flex;
165 justify-content: space-between;
166 align-items: center;
167 }
168
169 .contextcheck-header h2 {
170 margin: 0;
171 font-size: 22px;
172 font-weight: 700;
173 }
174
175 .contextcheck-close {
176 background: rgba(255, 255, 255, 0.2);
177 border: none;
178 color: white;
179 font-size: 24px;
180 width: 36px;
181 height: 36px;
182 border-radius: 50%;
183 cursor: pointer;
184 display: flex;
185 align-items: center;
186 justify-content: center;
187 transition: background 0.2s;
188 }
189
190 .contextcheck-close:hover {
191 background: rgba(255, 255, 255, 0.3);
192 }
193
194 .contextcheck-content {
195 padding: 32px;
196 }
197
198 .contextcheck-loading {
199 text-align: center;
200 padding: 40px 20px;
201 }
202
203 .contextcheck-spinner {
204 width: 50px;
205 height: 50px;
206 border: 4px solid #f3f3f3;
207 border-top: 4px solid #667eea;
208 border-radius: 50%;
209 animation: spin 1s linear infinite;
210 margin: 0 auto 20px;
211 }
212
213 @keyframes spin {
214 0% { transform: rotate(0deg); }
215 100% { transform: rotate(360deg); }
216 }
217
218 .contextcheck-loading-text {
219 font-size: 18px;
220 color: #333;
221 font-weight: 500;
222 }
223
224 .contextcheck-results {
225 animation: fadeIn 0.5s ease;
226 }
227
228 .contextcheck-stat {
229 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
230 color: white;
231 padding: 16px;
232 border-radius: 12px;
233 text-align: center;
234 margin-bottom: 24px;
235 font-size: 18px;
236 font-weight: 600;
237 }
238
239 .contextcheck-section {
240 margin-bottom: 28px;
241 }
242
243 .contextcheck-section h3 {
244 font-size: 16px;
245 font-weight: 700;
246 color: #333;
247 margin: 0 0 12px 0;
248 display: flex;
249 align-items: center;
250 gap: 8px;
251 }
252
253 .contextcheck-section h3::before {
254 font-size: 20px;
255 }
256
257 .contextcheck-section.agreements h3::before {
258 content: "✓";
259 color: #10b981;
260 }
261
262 .contextcheck-section.differences h3::before {
263 content: "⚡";
264 color: #f59e0b;
265 }
266
267 .contextcheck-section ul {
268 margin: 0;
269 padding-left: 20px;
270 list-style: none;
271 }
272
273 .contextcheck-section li {
274 margin-bottom: 10px;
275 padding-left: 24px;
276 position: relative;
277 color: #555;
278 line-height: 1.6;
279 font-size: 14px;
280 }
281
282 .contextcheck-section.agreements li::before {
283 content: "•";
284 position: absolute;
285 left: 0;
286 color: #10b981;
287 font-weight: bold;
288 font-size: 20px;
289 }
290
291 .contextcheck-section.differences li::before {
292 content: "•";
293 position: absolute;
294 left: 0;
295 color: #f59e0b;
296 font-weight: bold;
297 font-size: 20px;
298 }
299
300 .contextcheck-confidence {
301 background: #f9fafb;
302 border-radius: 12px;
303 padding: 16px;
304 display: flex;
305 justify-content: space-between;
306 align-items: center;
307 margin-bottom: 20px;
308 }
309
310 .contextcheck-confidence-label {
311 font-weight: 600;
312 color: #333;
313 font-size: 15px;
314 }
315
316 .contextcheck-confidence-value {
317 padding: 6px 16px;
318 border-radius: 20px;
319 font-weight: 700;
320 font-size: 14px;
321 }
322
323 .contextcheck-confidence-value.high {
324 background: #d1fae5;
325 color: #065f46;
326 }
327
328 .contextcheck-confidence-value.medium {
329 background: #fef3c7;
330 color: #92400e;
331 }
332
333 .contextcheck-confidence-value.low {
334 background: #fee2e2;
335 color: #991b1b;
336 }
337
338 .contextcheck-disclaimer {
339 background: #fef3c7;
340 border-left: 4px solid #f59e0b;
341 padding: 14px;
342 border-radius: 8px;
343 font-size: 13px;
344 color: #78350f;
345 line-height: 1.5;
346 }
347
348 .contextcheck-disclaimer::before {
349 content: "ℹ️ ";
350 margin-right: 6px;
351 }
352 `;
353
354 const styleElement = document.createElement('style');
355 styleElement.textContent = styles;
356 document.head.appendChild(styleElement);
357 console.log('ContextCheck: Styles added');
358 }
359
360 // Create floating button
361 function createButton() {
362 const button = document.createElement('button');
363 button.id = 'contextcheck-button';
364 button.textContent = 'Check Context';
365 button.addEventListener('click', openPopup);
366 document.body.appendChild(button);
367 console.log('ContextCheck: Button created');
368 }
369
370 // Create popup
371 function createPopup() {
372 // Create overlay
373 const overlay = document.createElement('div');
374 overlay.id = 'contextcheck-overlay';
375 overlay.addEventListener('click', closePopup);
376
377 // Create popup
378 const popup = document.createElement('div');
379 popup.id = 'contextcheck-popup';
380
381 popup.innerHTML = `
382 <div class="contextcheck-header">
383 <h2>🔍 ContextCheck</h2>
384 <button class="contextcheck-close" onclick="document.getElementById('contextcheck-overlay').click()">×</button>
385 </div>
386 <div class="contextcheck-content">
387 <div class="contextcheck-loading">
388 <div class="contextcheck-spinner"></div>
389 <div class="contextcheck-loading-text">Analyzing sources…</div>
390 </div>
391 </div>
392 `;
393
394 document.body.appendChild(overlay);
395 document.body.appendChild(popup);
396 console.log('ContextCheck: Popup created');
397
398 // Simulate loading and show results
399 setTimeout(() => {
400 showResults();
401 }, 2000);
402 }
403
404 // Show results
405 function showResults() {
406 const headline = document.title;
407 const url = window.location.href;
408 const results = generateMockResults(headline, url);
409
410 const content = document.querySelector('.contextcheck-content');
411
412 const agreementsList = results.agreements.map(item => `<li>${item}</li>`).join('');
413 const differencesList = results.differences.map(item => `<li>${item}</li>`).join('');
414 const confidenceClass = results.confidence.toLowerCase();
415
416 content.innerHTML = `
417 <div class="contextcheck-results">
418 <div class="contextcheck-stat">
419 📰 Sources found: ${results.sourcesFound}
420 </div>
421
422 <div class="contextcheck-section agreements">
423 <h3>What most sources agree on</h3>
424 <ul>
425 ${agreementsList}
426 </ul>
427 </div>
428
429 <div class="contextcheck-section differences">
430 <h3>Where reports differ</h3>
431 <ul>
432 ${differencesList}
433 </ul>
434 </div>
435
436 <div class="contextcheck-confidence">
437 <span class="contextcheck-confidence-label">Confidence Level:</span>
438 <span class="contextcheck-confidence-value ${confidenceClass}">${results.confidence}</span>
439 </div>
440
441 <div class="contextcheck-disclaimer">
442 This provides context, not factual verification.
443 </div>
444 </div>
445 `;
446
447 console.log('ContextCheck: Results displayed', results);
448 }
449
450 // Open popup
451 function openPopup() {
452 console.log('ContextCheck: Opening popup');
453 createPopup();
454 }
455
456 // Close popup
457 function closePopup() {
458 console.log('ContextCheck: Closing popup');
459 const overlay = document.getElementById('contextcheck-overlay');
460 const popup = document.getElementById('contextcheck-popup');
461 if (overlay) overlay.remove();
462 if (popup) popup.remove();
463 }
464
465 // Initialize
466 function init() {
467 console.log('ContextCheck: Initializing...');
468 addStyles();
469
470 // Wait for body to be ready
471 if (document.body) {
472 createButton();
473 } else {
474 const observer = new MutationObserver(() => {
475 if (document.body) {
476 createButton();
477 observer.disconnect();
478 }
479 });
480 observer.observe(document.documentElement, { childList: true });
481 }
482 }
483
484 // Start when DOM is ready
485 if (document.readyState === 'loading') {
486 document.addEventListener('DOMContentLoaded', init);
487 } else {
488 init();
489 }
490
491})();