您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Immersive fullscreen interactive panels for algebra learning.
// ==UserScript== // @name Interactive Algebra on Drawaria // @namespace http://tampermonkey.net/ // @version 4.0 // @description Immersive fullscreen interactive panels for algebra learning. // @author YouTubeDrawaria // @match https://drawaria.online/* // @grant GM_registerMenuCommand // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online // ==/UserScript== (function() { 'use strict'; // --- Estilos Globales para un Tema Claro, Grande y Moderno --- const lightThemeStyles = { windowBackground: '#ffffff', // Blanco puro windowColor: '#333333', // Texto oscuro headerBackground: '#f8f9fa', // Gris muy claro para encabezados headerColor: '#8400ff', // Púrpura vibrante para el título buttonPrimary: '#8400ff', // Púrpura primario buttonSecondary: '#6c757d', // Gris oscuro buttonHover: '#6a00cc', // Púrpura oscuro en hover borderColor: '#e9ecef', // Borde gris sutil quizCorrect: '#28a745', // Verde quizIncorrect: '#dc3545', // Rojo }; // Función para crear elementos con atributos y estilos function createElem(type, props = {}, styles = {}, parent = null) { const el = document.createElement(type); Object.entries(props).forEach(([k, v]) => el[k] = v); Object.entries(styles).forEach(([k, v]) => el.style[k] = v); if (parent) parent.appendChild(el); return el; } // Draggable mejorado function makeDraggable(el, handle) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; // La mejora: el drag solo inicia si no estamos haciendo clic en un botón de control handle.onmousedown = dragMouseDown; // DENTRO DE function makeDraggable(el, handle) { ... } let initialX, initialY, initialTop, initialLeft; // Nuevas variables para almacenar el estado handle.onmousedown = dragMouseDown; function dragMouseDown(e) { // Permitir el drag solo si el click no es en el botón de cerrar if (e.target.tagName === 'BUTTON') return; e.preventDefault(); // Almacenar la posición inicial del ratón (e.clientX, e.clientY) initialX = e.clientX; initialY = e.clientY; // Almacenar la posición inicial del panel (el.offsetLeft, el.offsetTop) initialLeft = el.offsetLeft; initialTop = el.offsetTop; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e.preventDefault(); // Calcula cuánto se ha movido el ratón desde el clic inicial const dx = e.clientX - initialX; const dy = e.clientY - initialY; // Calcula la nueva posición del panel, sumando el movimiento del ratón a la posición inicial del panel let newTop = initialTop + dy; let newLeft = initialLeft + dx; // --- Restricción de límites de la pantalla --- // Esto asegura que el panel no desaparezca, pero permite el movimiento fluido. newTop = Math.max(0, Math.min(window.innerHeight - el.offsetHeight, newTop)); newLeft = Math.max(0, Math.min(window.innerWidth - el.offsetWidth, newLeft)); // -------------------------------------------- el.style.top = newTop + 'px'; el.style.left = newLeft + 'px'; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } // --- Panel Principal (Menú) --- const menu = createElem('div', { id: 'algebraMainMenu' }, { position: 'fixed', top: '100px', left: '50px', width: '320px', background: lightThemeStyles.windowBackground, color: lightThemeStyles.windowColor, fontFamily: 'Roboto, sans-serif', fontSize: '18px', borderRadius: '16px', boxShadow: '0 8px 30px rgba(0,0,0,0.2)', padding: '20px', zIndex: 99999, userSelect: 'none', cursor: 'default', border: '2px solid ' + lightThemeStyles.headerColor, transition: 'all 0.3s ease' }, document.body); // Drag handle & Close button const dragHandle = createElem('div', { id: 'dragHandle', innerText: '🟣 Álgebra Interactivo Pro' }, { cursor: 'move', background: lightThemeStyles.headerBackground, color: lightThemeStyles.headerColor, padding: '12px 20px', marginBottom: '20px', fontWeight: '900', fontSize: '1.4em', borderRadius: '12px', borderBottom: '3px solid ' + lightThemeStyles.headerColor, userSelect: 'none', position: 'relative', textAlign: 'center' }, menu); const closeMenuBtn = createElem('button', { innerText: '✖' }, { position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)', background: lightThemeStyles.buttonSecondary, border: 'none', borderRadius: '50%', color: 'white', cursor: 'pointer', fontWeight: 'bold', width: '35px', height: '35px', lineHeight: '1', padding: '0', fontSize: '1em', transition: 'background 0.2s' }, dragHandle); closeMenuBtn.onmouseover = () => closeMenuBtn.style.background = lightThemeStyles.quizIncorrect; closeMenuBtn.onmouseout = () => closeMenuBtn.style.background = lightThemeStyles.buttonSecondary; closeMenuBtn.onclick = () => menu.style.display = 'none'; makeDraggable(menu, dragHandle); // El área de drag es todo el dragHandle // Botones para abrir ventanas independientes const buttonsInfo = [ { id: 'btnConcepts', text: '📘 Conceptos Fundamentales' }, { id: 'btnExercises', text: '💪 Taller de Ejercicios' }, { id: 'btnExamples', text: '✨ Casos Prácticos Resueltos' }, { id: 'btnQuizzes', text: '🏆 Desafío de Quizzes' } ]; let windows = {}; buttonsInfo.forEach(({ id, text }) => { const btn = createElem('button', { id, innerText: text }, { width: '100%', marginBottom: '15px', padding: '15px', borderRadius: '10px', fontWeight: 'bold', border: 'none', background: lightThemeStyles.buttonPrimary, color: 'white', cursor: 'pointer', userSelect: 'none', fontSize: '1.1em', boxShadow: '0 4px 6px rgba(0,0,0,0.1)', transition: 'background 0.2s, transform 0.1s' }, menu); btn.onmouseover = () => btn.style.background = lightThemeStyles.buttonHover; btn.onmouseout = () => btn.style.background = lightThemeStyles.buttonPrimary; btn.onmousedown = () => btn.style.transform = 'scale(0.98)'; btn.onmouseup = () => btn.style.transform = 'scale(1)'; btn.onclick = () => openWindow(id.replace('btn', '').toLowerCase()); }); // --- Contenido Extendido (Mismo que v3.0) --- const contentData = { concepts: ` <h2 style="color:${lightThemeStyles.headerColor};margin-top:0;font-size:1.5em;">📘 Conceptos Clave del Álgebra</h2> <p style="font-size:1.1em;line-height:1.7;">El álgebra es la generalización de la aritmética. Usamos letras para representar cantidades desconocidas. ¡Es la clave para resolver problemas de la vida real!</p> <hr style="border-color:${lightThemeStyles.borderColor};margin:15px 0;"> <h3>Principios Fundamentales:</h3> <ul style="list-style-type:square;padding-left:25px;line-height:1.8;font-size:1em;"> <li>**Variable ($x, y$):** Un valor que puede cambiar. Es tu "número misterioso".</li> <li>**Coeficiente:** El número que multiplica a la variable (ej: el **3** en $3x$).</li> <li>**Término:** Una variable, un número, o el producto/cociente de ambos (ej: $5, 2x, x/3$).</li> <li>**Ecuación Lineal:** Aquella donde la variable tiene exponente 1 (ej: $4x - 1 = 15$).</li> </ul> <h3 style="color:${lightThemeStyles.buttonPrimary};">La Regla de la Balanza:</h3> <p style="font-style:italic;">Lo que haces a un lado de la ecuación, **debes** hacerlo al otro. ¡Así mantienes la igualdad!</p> <table style="width:100%; border-collapse:collapse; margin-top:10px; font-size:0.95em;"> <tr style="background:${lightThemeStyles.headerBackground};"><th>Operación a Eliminar</th><th>Operación Inversa (Aplicar)</th></tr> <tr><td style="border:1px solid ${lightThemeStyles.borderColor}; padding:8px;">Suma (+)</td><td style="border:1px solid ${lightThemeStyles.borderColor}; padding:8px;">Resta (-)</td></tr> <tr><td style="border:1px solid ${lightThemeStyles.borderColor}; padding:8px;">Resta (-)</td><td style="border:1px solid ${lightThemeStyles.borderColor}; padding:8px;">Suma (+)</td></tr> <tr><td style="border:1px solid ${lightThemeStyles.borderColor}; padding:8px;">Multiplicación (*)</td><td style="border:1px solid ${lightThemeStyles.borderColor}; padding:8px;">División (/)</td></tr> <tr><td style="border:1px solid ${lightThemeStyles.borderColor}; padding:8px;">División (/)</td><td style="border:1px solid ${lightThemeStyles.borderColor}; padding:8px;">Multiplicación (*)</td></tr> </table> `, exercises: ` <h2 style="color:${lightThemeStyles.headerColor};margin-top:0;font-size:1.5em;">💪 Taller de Ecuaciones (Escribe y Comprueba)</h2> <p style="font-size:1.1em;">Resuelve cada ecuación de UN o DOS pasos. Escribe tu respuesta y haz clic en "Comprobar".</p> <hr style="border-color:${lightThemeStyles.borderColor};margin:15px 0;"> <div id="exerciseFormContainer"> </div> `, examples: ` <h2 style="color:${lightThemeStyles.headerColor};margin-top:0;font-size:1.5em;">✨ Casos Prácticos Resueltos (Método de Dos Pasos)</h2> <p style="font-size:1.1em;">La clave es deshacer la suma/resta **primero**, y luego la multiplicación/división.</p> <div style="margin-bottom:20px;padding:15px;border:2px solid ${lightThemeStyles.buttonPrimary};border-radius:10px;background:#f9f5ff;"> <h4>Ejemplo 1: Aislar la variable ($x$)</h4> <p style="font-weight:bold;">Ecuación: $4x + 10 = 30$</p> <ol style="padding-left:20px;"> <li>**Paso 1 (Restar):** Deshacer el $+10$ restando 10 a ambos lados: $4x = 30 - 10 \implies 4x = 20$.</li> <li>**Paso 2 (Dividir):** Deshacer el $4\cdot$ dividiendo entre 4 a ambos lados: $x = 20 / 4 \implies x = 5$.</li> <li>**Comprobación:** $4(5) + 10 = 20 + 10 = 30$. ¡Correcto!</li> </ol> </div> <div style="margin-bottom:20px;padding:15px;border:2px solid ${lightThemeStyles.buttonPrimary};border-radius:10px;background:#f9f5ff;"> <h4>Ejemplo 2: Variable Negativa ($a$)</h4> <p style="font-weight:bold;">Ecuación: $25 - 3a = 4$</p> <ol style="padding-left:20px;"> <li>**Paso 1 (Restar):** Deshacer el $+25$ restando 25: $-3a = 4 - 25 \implies -3a = -21$.</li> <li>**Paso 2 (Dividir):** Dividir entre $-3$: $a = -21 / -3 \implies a = 7$. (Recuerda: menos entre menos es más).</li> <li>**Comprobación:** $25 - 3(7) = 25 - 21 = 4$. ¡Correcto!</li> </ol> </div> `, quizzes: ` <h2 style="color:${lightThemeStyles.headerColor};margin-top:0;font-size:1.5em;">🏆 Desafío de Quizzes (Selección Múltiple)</h2> <p style="font-size:1.1em;">Responde estas preguntas de opción múltiple para probar tus conocimientos.</p> <hr style="border-color:${lightThemeStyles.borderColor};margin:15px 0;"> <div id="quizContainer"> </div> <button id="checkAllQuizzesBtn" style="width:100%; padding:12px; margin-top:15px; background:${lightThemeStyles.buttonPrimary}; color:white; border:none; border-radius:8px; font-weight:bold; cursor:pointer; font-size:1.1em; transition:background 0.2s;"> Comprobar Todas las Respuestas </button> <p id="quizGlobalFeedback" style="text-align:center;font-weight:bold;margin-top:15px;font-size:1.2em;"></p> ` }; // --- Data de Ejercicios y Quizzes --- const exerciseQuestions = [ { q: "$x + 9 = 24$", variable: 'x', ans: 15, hint: "Resta 9 a ambos lados." }, { q: "$5y = 45$", variable: 'y', ans: 9, hint: "Divide por 5." }, { q: "$2z - 5 = 15$", variable: 'z', ans: 10, hint: "Suma 5, luego divide por 2." }, { q: "$x/3 + 1 = 6$", variable: 'x', ans: 15, hint: "Resta 1, luego multiplica por 3." } ]; const quizQuestions = [ { q: "¿Qué operación inversa se usa para deshacer $x/5$?", ans: 'Multiplicación', options: ['Suma', 'Resta', 'División', 'Multiplicación'] }, { q: "Si $3x = 18$, ¿cuánto vale $x$?", ans: '6', options: ['3', '6', '9', '15'] }, // Corregido: convertir a string para coincidir con radio value { q: "¿Cuál es el coeficiente en la expresión $7y - 2$?", ans: '7', options: ['7', 'y', '-2', '2'] }, { q: "Si $a - 4 = 12$, ¿cuál es el valor de $a$?", ans: '16', options: ['8', '16', '3', '48'] } ]; // --- Lógica del Taller de Ejercicios (Interactiva) --- function setupExercises(content) { const container = content.querySelector('#exerciseFormContainer'); container.innerHTML = ''; // Limpiar exerciseQuestions.forEach((qData, index) => { const qDiv = createElem('div', {}, { marginBottom: '20px', padding: '15px', borderRadius: '10px', border: '1px solid ' + lightThemeStyles.borderColor, background: lightThemeStyles.headerBackground }, container); createElem('p', { innerHTML: `**Ejercicio ${index + 1}:** Resuelve ${qData.q}` }, { fontWeight: 'bold', marginBottom: '10px', fontSize: '1.1em' }, qDiv); const inputGroup = createElem('div', {}, { display: 'flex', gap: '10px', alignItems: 'center' }, qDiv); createElem('span', { innerText: `${qData.variable} = ` }, { fontWeight: 'bold', fontSize: '1.1em' }, inputGroup); const input = createElem('input', { type: 'number', placeholder: 'Tu respuesta' }, { padding: '8px', borderRadius: '5px', border: `1px solid ${lightThemeStyles.borderColor}`, flexGrow: 1, fontSize: '1em' }, inputGroup); const checkBtn = createElem('button', { innerText: 'Comprobar' }, { padding: '8px 12px', borderRadius: '5px', border: 'none', background: lightThemeStyles.buttonPrimary, color: 'white', cursor: 'pointer', transition: 'background 0.2s' }, inputGroup); checkBtn.onmouseover = () => checkBtn.style.background = lightThemeStyles.buttonHover; checkBtn.onmouseout = () => checkBtn.style.background = lightThemeStyles.buttonPrimary; const feedback = createElem('p', {}, { marginTop: '10px', fontWeight: 'bold', minHeight: '1.2em' }, qDiv); checkBtn.onclick = () => { const userAnswer = parseFloat(input.value); // Usamos el 'ans' del objeto, que es un número if (isNaN(userAnswer)) { feedback.innerText = '⚠️ Ingresa un número.'; feedback.style.color = lightThemeStyles.buttonSecondary; return; } if (userAnswer === qData.ans) { feedback.innerHTML = `✅ **¡Correcto!** $${qData.variable} = ${qData.ans}$. ¡Bien hecho!`; feedback.style.color = lightThemeStyles.quizCorrect; input.style.border = `2px solid ${lightThemeStyles.quizCorrect}`; } else { feedback.innerHTML = `❌ **Incorrecto.** Intenta de nuevo. Pista: ${qData.hint}`; feedback.style.color = lightThemeStyles.quizIncorrect; input.style.border = `2px solid ${lightThemeStyles.quizIncorrect}`; } }; }); } // --- Lógica de Quizzes Interactivos (Opción Múltiple) --- // Fix: Se usa el ID del div para el querySelector, asegurando que se inicialice. function setupQuiz(content) { const container = content.querySelector('#quizContainer'); const checkBtn = content.querySelector('#checkAllQuizzesBtn'); const globalFeedback = content.querySelector('#quizGlobalFeedback'); // Verifica si el container existe antes de continuar (DEBUG) if (!container) { console.error("Error: #quizContainer no se encontró."); return; } container.innerHTML = ''; globalFeedback.innerText = ''; checkBtn.style.display = 'block'; checkBtn.disabled = false; checkBtn.innerText = 'Comprobar Todas las Respuestas'; checkBtn.style.background = lightThemeStyles.buttonPrimary; quizQuestions.forEach((qData, index) => { const qDiv = createElem('div', { id: `quizItem_${index}` }, { marginBottom: '20px', padding: '15px', borderRadius: '10px', border: '1px solid ' + lightThemeStyles.borderColor, background: '#f5f5f5' }, container); createElem('p', { innerHTML: `**Pregunta ${index + 1}:** ${qData.q}` }, { fontWeight: 'bold', marginBottom: '12px', fontSize: '1.1em' }, qDiv); const optionsDiv = createElem('div', { className: 'options-group' }, { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }, qDiv); qData.options.forEach(option => { const label = createElem('label', {}, { padding: '10px', borderRadius: '6px', border: `1px solid ${lightThemeStyles.borderColor}`, cursor: 'pointer', background: '#ffffff', transition: 'all 0.2s', textAlign: 'center', display: 'block' }, optionsDiv); const radio = createElem('input', { type: 'radio', name: `q_${index}`, value: option // El valor es el texto de la opción }, { display: 'none' }, label); createElem('span', { innerText: option }, { fontWeight: 'normal' }, label); // Efecto visual al seleccionar radio.onchange = () => { optionsDiv.querySelectorAll('label').forEach(l => l.style.border = `1px solid ${lightThemeStyles.borderColor}`); if (radio.checked) { label.style.border = `2px solid ${lightThemeStyles.buttonPrimary}`; } }; }); }); checkBtn.onclick = () => { let correctCount = 0; quizQuestions.forEach((qData, index) => { const qDiv = content.querySelector(`#quizItem_${index}`); const selectedRadio = qDiv.querySelector(`input[name="q_${index}"]:checked`); const options = qDiv.querySelectorAll('.options-group label'); options.forEach(label => { label.style.border = `1px solid ${lightThemeStyles.borderColor}`; const radio = label.querySelector('input'); if (radio.value == qData.ans) { // Resalta la correcta en verde label.style.border = `3px solid ${lightThemeStyles.quizCorrect}`; label.style.background = '#e6ffec'; } else if (selectedRadio && radio === selectedRadio) { // Si es incorrecta y fue la seleccionada, resáltala en rojo label.style.border = `3px solid ${lightThemeStyles.quizIncorrect}`; label.style.background = '#ffe6e6'; } else { label.style.background = '#ffffff'; } radio.disabled = true; label.style.cursor = 'default'; }); if (selectedRadio && selectedRadio.value == qData.ans) { correctCount++; } }); globalFeedback.innerText = `Resultado Final: ${correctCount} de ${quizQuestions.length} correctas.`; globalFeedback.style.color = correctCount === quizQuestions.length ? lightThemeStyles.quizCorrect : lightThemeStyles.quizIncorrect; checkBtn.disabled = true; checkBtn.innerText = 'Quiz Resuelto'; checkBtn.style.background = lightThemeStyles.buttonSecondary; }; } // Función para abrir ventana nueva o mostrar si ya existe function openWindow(type) { if (windows[type]) { windows[type].style.display = 'block'; windows[type].style.opacity = '1'; // Vuelve a aplicar la lógica interactiva por si se cerró y volvió a abrir if (type === 'exercises') setupExercises(windows[type].querySelector('.content-area')); if (type === 'quizzes') setupQuiz(windows[type].querySelector('.content-area')); return; } // --- Ventana a Pantalla Completa (Full Screen) --- const win = createElem('div', {}, { position: 'fixed', top: '10px', left: '10px', width: 'calc(100% - 20px)', height: 'calc(100% - 20px)', // PANTALLA COMPLETA background: lightThemeStyles.windowBackground, color: lightThemeStyles.windowColor, borderRadius: '16px', boxShadow: '0 12px 40px rgba(0,0,0,0.3)', padding: '0', overflow: 'hidden', zIndex: 99998, userSelect: 'none', fontFamily: 'Roboto, sans-serif', cursor: 'default', border: '2px solid ' + lightThemeStyles.headerColor, transition: 'all 0.3s ease', opacity: '0' }, document.body); // Desactivamos el drag para las ventanas a pantalla completa, ya que no tiene sentido. // makeDraggable(win, header); // << Se omite intencionalmente // Header const header = createElem('div', {}, { cursor: 'default', background: lightThemeStyles.headerBackground, color: lightThemeStyles.headerColor, padding: '15px 20px', fontWeight: '900', fontSize: '1.4em', userSelect: 'none', position: 'relative', borderBottom: '2px solid ' + lightThemeStyles.borderColor, textAlign: 'center' }, win); header.textContent = { concepts: '📘 Conceptos Fundamentales', exercises: '💪 Taller de Ejercicios', examples: '✨ Casos Prácticos Resueltos', quizzes: '🏆 Desafío de Quizzes' }[type] || 'Ventana de Contenido'; const closeBtn = createElem('button', { innerText: 'X CERRAR' }, { position: 'absolute', right: '15px', top: '50%', transform: 'translateY(-50%)', border: 'none', background: lightThemeStyles.buttonSecondary, color: 'white', cursor: 'pointer', fontWeight: 'bold', borderRadius: '8px', padding: '8px 15px', fontSize: '1em', transition: 'background 0.2s' }, header); closeBtn.onmouseover = () => closeBtn.style.background = lightThemeStyles.quizIncorrect; closeBtn.onmouseout = () => closeBtn.style.background = lightThemeStyles.buttonSecondary; closeBtn.onclick = () => { win.style.opacity = '0'; setTimeout(() => win.style.display = 'none', 300); }; // Content area // El área de contenido ocupa el resto del espacio vertical. const content = createElem('div', { className: 'content-area' }, { fontSize: '1em', lineHeight: '1.6', padding: '20px', overflowY: 'auto', height: 'calc(100% - 59px)' // Altura restante (aprox 59px de header) }, win); // Insert content HTML content.innerHTML = contentData[type] || '<p>Sin contenido</p>'; // Lógicas interactivas (FIX: La lógica ahora se aplica al 'content' creado) if (type === 'exercises') { setupExercises(content); } if (type === 'quizzes') { setupQuiz(content); // ¡FIX aplicado aquí! } windows[type] = win; // Animación de entrada setTimeout(() => win.style.opacity = '1', 10); } // Abrir el menú principal con contenido inicial menu.style.display = 'block'; menu.style.opacity = '1'; // Registrar comandos para abrir/cerrar menú GM_registerMenuCommand('Abrir Menú Álgebra (v4.0)', () => { menu.style.display = 'block'; menu.style.opacity = '1'; }); GM_registerMenuCommand('Cerrar Menú Álgebra', () => { menu.style.display = 'none'; menu.style.opacity = '0'; }); })();