NoRedInk Answer Revealer

Shows the correct answer on NoRedInk quizzes at the press of a button

Size

10.1 KB

Version

1.0.1

Created

Oct 22, 2025

Updated

1 day ago

1// ==UserScript==
2// @name		NoRedInk Answer Revealer
3// @description		Shows the correct answer on NoRedInk quizzes at the press of a button
4// @version		1.0.1
5// @match		https://*.noredink.com/*
6// @icon		https://www.noredink.com/assets/favicon/favicon-8ac1ca529bfa7f8c60a434623b0fb3341b014ab5e4d6fe83693cfb8f74df5fe1.ico
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('NoRedInk Answer Revealer loaded');
12
13    // Debounce function to prevent excessive calls
14    function debounce(func, wait) {
15        let timeout;
16        return function executedFunction(...args) {
17            const later = () => {
18                clearTimeout(timeout);
19                func(...args);
20            };
21            clearTimeout(timeout);
22            timeout = setTimeout(later, wait);
23        };
24    }
25
26    // Create and inject the "Show Answer" button
27    function createShowAnswerButton() {
28        // Check if button already exists
29        if (document.getElementById('show-answer-btn')) {
30            return;
31        }
32
33        const button = document.createElement('button');
34        button.id = 'show-answer-btn';
35        button.textContent = '🔍 Show Answer';
36        button.style.cssText = `
37            position: fixed;
38            top: 20px;
39            right: 20px;
40            z-index: 10000;
41            padding: 12px 24px;
42            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
43            color: white;
44            border: none;
45            border-radius: 8px;
46            font-size: 16px;
47            font-weight: bold;
48            cursor: pointer;
49            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
50            transition: all 0.3s ease;
51        `;
52
53        button.addEventListener('mouseenter', () => {
54            button.style.transform = 'translateY(-2px)';
55            button.style.boxShadow = '0 6px 20px rgba(0, 0, 0, 0.3)';
56        });
57
58        button.addEventListener('mouseleave', () => {
59            button.style.transform = 'translateY(0)';
60            button.style.boxShadow = '0 4px 15px rgba(0, 0, 0, 0.2)';
61        });
62
63        button.addEventListener('click', showAnswer);
64        document.body.appendChild(button);
65        console.log('Show Answer button created');
66    }
67
68    // Show loading state
69    function showLoading() {
70        const button = document.getElementById('show-answer-btn');
71        if (button) {
72            button.textContent = '⏳ Analyzing...';
73            button.disabled = true;
74            button.style.opacity = '0.7';
75            button.style.cursor = 'not-allowed';
76        }
77    }
78
79    // Hide loading state
80    function hideLoading() {
81        const button = document.getElementById('show-answer-btn');
82        if (button) {
83            button.textContent = '🔍 Show Answer';
84            button.disabled = false;
85            button.style.opacity = '1';
86            button.style.cursor = 'pointer';
87        }
88    }
89
90    // Extract question data from the page
91    function extractQuestionData() {
92        const questionData = {
93            instructions: '',
94            claim: '',
95            evidence: '',
96            options: []
97        };
98
99        // Get instructions
100        const instructionsEl = document.querySelector('#instructions-with-optional-highlights');
101        if (instructionsEl) {
102            questionData.instructions = instructionsEl.textContent.trim();
103        }
104
105        // Get claim
106        const claimEl = document.querySelector('[data-testid="claim-text"]');
107        if (claimEl) {
108            questionData.claim = claimEl.textContent.trim();
109        }
110
111        // Get evidence
112        const evidenceEl = document.querySelector('[data-testid="evidence-text"]');
113        if (evidenceEl) {
114            questionData.evidence = evidenceEl.textContent.trim();
115        }
116
117        // Get all answer options
118        const optionButtons = document.querySelectorAll('.page-question-outline-multiple-choice-Option button[data-nri-description="Nri-Ui-Button-V10-customButton"]');
119        optionButtons.forEach((button, index) => {
120            const optionText = button.textContent.trim();
121            if (optionText) {
122                questionData.options.push({
123                    index: index,
124                    text: optionText,
125                    element: button
126                });
127            }
128        });
129
130        console.log('Extracted question data:', questionData);
131        return questionData;
132    }
133
134    // Use AI to determine the correct answer
135    async function showAnswer() {
136        console.log('Show Answer button clicked');
137        showLoading();
138
139        try {
140            const questionData = extractQuestionData();
141
142            if (questionData.options.length === 0) {
143                alert('No answer options found on this page.');
144                hideLoading();
145                return;
146            }
147
148            // Build prompt for AI
149            const prompt = `You are analyzing a NoRedInk quiz question. Based on the following information, determine which answer is correct.
150
151Instructions: ${questionData.instructions}
152Claim: ${questionData.claim}
153Evidence: ${questionData.evidence}
154
155Answer Options:
156${questionData.options.map((opt, i) => `${i + 1}. ${opt.text}`).join('\n')}
157
158Analyze the evidence in relation to the claim and determine which option is correct. Consider:
159- Does the evidence strongly support the claim?
160- Is the evidence factual and credible?
161- Does the evidence come from a reliable source?
162
163Return the correct answer.`;
164
165            console.log('Sending prompt to AI:', prompt);
166
167            const response = await RM.aiCall(prompt, {
168                type: "json_schema",
169                json_schema: {
170                    name: "answer_analysis",
171                    schema: {
172                        type: "object",
173                        properties: {
174                            correctAnswerIndex: {
175                                type: "number",
176                                description: "The index (0-based) of the correct answer"
177                            },
178                            correctAnswerText: {
179                                type: "string",
180                                description: "The text of the correct answer"
181                            },
182                            explanation: {
183                                type: "string",
184                                description: "Brief explanation of why this is the correct answer"
185                            }
186                        },
187                        required: ["correctAnswerIndex", "correctAnswerText", "explanation"]
188                    }
189                }
190            });
191
192            console.log('AI response:', response);
193
194            // Highlight the correct answer
195            highlightCorrectAnswer(questionData.options, response.correctAnswerIndex, response.explanation);
196
197        } catch (error) {
198            console.error('Error showing answer:', error);
199            alert('Failed to determine the answer. Please try again.');
200        } finally {
201            hideLoading();
202        }
203    }
204
205    // Highlight the correct answer on the page
206    function highlightCorrectAnswer(options, correctIndex, explanation) {
207        // Remove any existing highlights
208        document.querySelectorAll('.answer-highlight').forEach(el => el.remove());
209
210        if (correctIndex >= 0 && correctIndex < options.length) {
211            const correctOption = options[correctIndex];
212            const button = correctOption.element;
213            const container = button.closest('.page-question-outline-multiple-choice-Option');
214
215            if (container) {
216                // Add highlight styling
217                container.style.position = 'relative';
218                container.style.border = '3px solid #10b981';
219                container.style.borderRadius = '8px';
220                container.style.backgroundColor = '#d1fae5';
221                container.style.padding = '8px';
222                container.style.transition = 'all 0.3s ease';
223
224                // Add checkmark indicator
225                const indicator = document.createElement('div');
226                indicator.className = 'answer-highlight';
227                indicator.textContent = '✓ Correct Answer';
228                indicator.style.cssText = `
229                    position: absolute;
230                    top: -12px;
231                    right: 10px;
232                    background: #10b981;
233                    color: white;
234                    padding: 4px 12px;
235                    border-radius: 12px;
236                    font-size: 12px;
237                    font-weight: bold;
238                    box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
239                `;
240                container.appendChild(indicator);
241
242                // Show explanation
243                const explanationBox = document.createElement('div');
244                explanationBox.className = 'answer-highlight';
245                explanationBox.textContent = explanation;
246                explanationBox.style.cssText = `
247                    margin-top: 12px;
248                    padding: 12px;
249                    background: #f0fdf4;
250                    border-left: 4px solid #10b981;
251                    border-radius: 4px;
252                    font-size: 14px;
253                    color: #065f46;
254                    line-height: 1.5;
255                `;
256                container.appendChild(explanationBox);
257
258                // Scroll to the correct answer
259                container.scrollIntoView({ behavior: 'smooth', block: 'center' });
260
261                console.log('Highlighted correct answer:', correctOption.text);
262            }
263        }
264    }
265
266    // Initialize the extension
267    function init() {
268        console.log('Initializing NoRedInk Answer Revealer');
269
270        // Wait for the page to load
271        if (document.readyState === 'loading') {
272            document.addEventListener('DOMContentLoaded', createShowAnswerButton);
273        } else {
274            createShowAnswerButton();
275        }
276
277        // Watch for navigation changes (NoRedInk is a single-page app)
278        const observer = new MutationObserver(debounce(() => {
279            createShowAnswerButton();
280        }, 500));
281
282        observer.observe(document.body, {
283            childList: true,
284            subtree: true
285        });
286    }
287
288    // Start the extension
289    init();
290})();
NoRedInk Answer Revealer | Robomonkey