style: run format first time
This commit is contained in:
@@ -12,22 +12,22 @@ let feedbackElement = null;
|
||||
* @param { Function} onSelectModule - Callback when a module is selected
|
||||
*/
|
||||
export function renderModuleList(container, modules, onSelectModule) {
|
||||
// Clear the container
|
||||
container.innerHTML = '<h3>Modules</h3>';
|
||||
// Clear the container
|
||||
container.innerHTML = "<h3>Modules</h3>";
|
||||
|
||||
// Create list items for each module
|
||||
modules.forEach(module => {
|
||||
const moduleItem = document.createElement('div');
|
||||
moduleItem.classList.add('module-list-item');
|
||||
moduleItem.dataset.moduleId = module.id;
|
||||
moduleItem.textContent = module.title;
|
||||
// Create list items for each module
|
||||
modules.forEach((module) => {
|
||||
const moduleItem = document.createElement("div");
|
||||
moduleItem.classList.add("module-list-item");
|
||||
moduleItem.dataset.moduleId = module.id;
|
||||
moduleItem.textContent = module.title;
|
||||
|
||||
moduleItem.addEventListener('click', () => {
|
||||
onSelectModule(module.id);
|
||||
});
|
||||
moduleItem.addEventListener("click", () => {
|
||||
onSelectModule(module.id);
|
||||
});
|
||||
|
||||
container.appendChild(moduleItem);
|
||||
});
|
||||
container.appendChild(moduleItem);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,33 +41,24 @@ export function renderModuleList(container, modules, onSelectModule) {
|
||||
* @param {HTMLElement} suffixEl - The code editor suffix element
|
||||
* @param {Object} lesson - The lesson object
|
||||
*/
|
||||
export function renderLesson(
|
||||
titleEl,
|
||||
descriptionEl,
|
||||
taskEl,
|
||||
previewEl,
|
||||
prefixEl,
|
||||
inputEl,
|
||||
suffixEl,
|
||||
lesson
|
||||
) {
|
||||
// Set lesson title and description
|
||||
titleEl.textContent = lesson.title || 'Untitled Lesson';
|
||||
descriptionEl.innerHTML = lesson.description || '';
|
||||
export function renderLesson(titleEl, descriptionEl, taskEl, previewEl, prefixEl, inputEl, suffixEl, lesson) {
|
||||
// Set lesson title and description
|
||||
titleEl.textContent = lesson.title || "Untitled Lesson";
|
||||
descriptionEl.innerHTML = lesson.description || "";
|
||||
|
||||
// Set task instructions
|
||||
taskEl.innerHTML = lesson.task || '';
|
||||
// Set task instructions
|
||||
taskEl.innerHTML = lesson.task || "";
|
||||
|
||||
// Set code editor contents
|
||||
prefixEl.textContent = lesson.codePrefix || '';
|
||||
inputEl.value = lesson.initialCode || '';
|
||||
suffixEl.textContent = lesson.codeSuffix || '';
|
||||
// Set code editor contents
|
||||
prefixEl.textContent = lesson.codePrefix || "";
|
||||
inputEl.value = lesson.initialCode || "";
|
||||
suffixEl.textContent = lesson.codeSuffix || "";
|
||||
|
||||
// Clear any existing feedback
|
||||
clearFeedback();
|
||||
// Clear any existing feedback
|
||||
clearFeedback();
|
||||
|
||||
// Initial preview render with empty user code
|
||||
// The LessonEngine will handle this when it's first set
|
||||
// Initial preview render with empty user code
|
||||
// The LessonEngine will handle this when it's first set
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +68,7 @@ export function renderLesson(
|
||||
* @param {number} total - The total number of levels
|
||||
*/
|
||||
export function renderLevelIndicator(element, current, total) {
|
||||
element.textContent = `Lesson ${current} of ${total}`;
|
||||
element.textContent = `Lesson ${current} of ${total}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,34 +77,34 @@ export function renderLevelIndicator(element, current, total) {
|
||||
* @param {string} message - The feedback message
|
||||
*/
|
||||
export function showFeedback(isSuccess, message) {
|
||||
// Clear any existing feedback
|
||||
clearFeedback();
|
||||
// Clear any existing feedback
|
||||
clearFeedback();
|
||||
|
||||
// Create feedback element
|
||||
feedbackElement = document.createElement('div');
|
||||
feedbackElement.classList.add(isSuccess ? 'feedback-success' : 'feedback-error');
|
||||
feedbackElement.textContent = message;
|
||||
// Create feedback element
|
||||
feedbackElement = document.createElement("div");
|
||||
feedbackElement.classList.add(isSuccess ? "feedback-success" : "feedback-error");
|
||||
feedbackElement.textContent = message;
|
||||
|
||||
// Find where to insert the feedback
|
||||
const insertAfter = document.querySelector('.code-editor');
|
||||
if (insertAfter && insertAfter.parentNode) {
|
||||
insertAfter.parentNode.insertBefore(feedbackElement, insertAfter.nextSibling);
|
||||
}
|
||||
// Find where to insert the feedback
|
||||
const insertAfter = document.querySelector(".code-editor");
|
||||
if (insertAfter && insertAfter.parentNode) {
|
||||
insertAfter.parentNode.insertBefore(feedbackElement, insertAfter.nextSibling);
|
||||
}
|
||||
|
||||
// Auto-remove feedback after some time if successful
|
||||
if (isSuccess) {
|
||||
setTimeout(() => {
|
||||
clearFeedback();
|
||||
}, 5000);
|
||||
}
|
||||
// Auto-remove feedback after some time if successful
|
||||
if (isSuccess) {
|
||||
setTimeout(() => {
|
||||
clearFeedback();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any existing feedback
|
||||
*/
|
||||
export function clearFeedback() {
|
||||
if (feedbackElement && feedbackElement.parentNode) {
|
||||
feedbackElement.parentNode.removeChild(feedbackElement);
|
||||
}
|
||||
feedbackElement = null;
|
||||
}
|
||||
if (feedbackElement && feedbackElement.parentNode) {
|
||||
feedbackElement.parentNode.removeChild(feedbackElement);
|
||||
}
|
||||
feedbackElement = null;
|
||||
}
|
||||
|
||||
@@ -9,73 +9,73 @@
|
||||
* @returns {Object} Validation result with isValid and message properties
|
||||
*/
|
||||
export function validateUserCode(userCode, lesson) {
|
||||
if (!lesson || !lesson.validations) {
|
||||
return { isValid: true, message: 'No validations specified for this lesson.' };
|
||||
}
|
||||
if (!lesson || !lesson.validations) {
|
||||
return { isValid: true, message: "No validations specified for this lesson." };
|
||||
}
|
||||
|
||||
// Get the validations array from the lesson
|
||||
const validations = lesson.validations;
|
||||
// Get the validations array from the lesson
|
||||
const validations = lesson.validations;
|
||||
|
||||
// Default validation result
|
||||
let result = {
|
||||
isValid: true,
|
||||
message: 'Your code looks good!'
|
||||
};
|
||||
// Default validation result
|
||||
let result = {
|
||||
isValid: true,
|
||||
message: "Your code looks good!"
|
||||
};
|
||||
|
||||
// Process each validation rule
|
||||
for (const validation of validations) {
|
||||
const { type, value, message, options } = validation;
|
||||
// Process each validation rule
|
||||
for (const validation of validations) {
|
||||
const { type, value, message, options } = validation;
|
||||
|
||||
switch (type) {
|
||||
case 'contains':
|
||||
if (!containsValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `Your code should include "${value}".` };
|
||||
}
|
||||
break;
|
||||
switch (type) {
|
||||
case "contains":
|
||||
if (!containsValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `Your code should include "${value}".` };
|
||||
}
|
||||
break;
|
||||
|
||||
case 'not_contains':
|
||||
if (containsValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `Your code should not include "${value}".` };
|
||||
}
|
||||
break;
|
||||
case "not_contains":
|
||||
if (containsValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `Your code should not include "${value}".` };
|
||||
}
|
||||
break;
|
||||
|
||||
case 'regex':
|
||||
if (!regexValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || 'Your code does not match the expected pattern.' };
|
||||
}
|
||||
break;
|
||||
case "regex":
|
||||
if (!regexValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || "Your code does not match the expected pattern." };
|
||||
}
|
||||
break;
|
||||
|
||||
case 'property_value':
|
||||
if (!propertyValueValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `The "${value.property}" property should be set to "${value.expected}".` };
|
||||
}
|
||||
break;
|
||||
case "property_value":
|
||||
if (!propertyValueValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `The "${value.property}" property should be set to "${value.expected}".` };
|
||||
}
|
||||
break;
|
||||
|
||||
case 'syntax':
|
||||
const syntaxResult = syntaxValidation(userCode);
|
||||
if (!syntaxResult.isValid) {
|
||||
return { isValid: false, message: message || `CSS syntax error: ${syntaxResult.error}` };
|
||||
}
|
||||
break;
|
||||
case "syntax":
|
||||
const syntaxResult = syntaxValidation(userCode);
|
||||
if (!syntaxResult.isValid) {
|
||||
return { isValid: false, message: message || `CSS syntax error: ${syntaxResult.error}` };
|
||||
}
|
||||
break;
|
||||
|
||||
case 'custom':
|
||||
if (validation.validator && typeof validation.validator === 'function') {
|
||||
const customResult = validation.validator(userCode);
|
||||
if (!customResult.isValid) {
|
||||
return { isValid: false, message: customResult.message || message || 'Your code does not meet the requirements.' };
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "custom":
|
||||
if (validation.validator && typeof validation.validator === "function") {
|
||||
const customResult = validation.validator(userCode);
|
||||
if (!customResult.isValid) {
|
||||
return { isValid: false, message: customResult.message || message || "Your code does not meet the requirements." };
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Add more validation types as needed
|
||||
// Add more validation types as needed
|
||||
|
||||
default:
|
||||
console.warn(`Unknown validation type: ${type}`);
|
||||
}
|
||||
}
|
||||
default:
|
||||
console.warn(`Unknown validation type: ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've passed all validations, return success
|
||||
return result;
|
||||
// If we've passed all validations, return success
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,19 +86,19 @@ export function validateUserCode(userCode, lesson) {
|
||||
* @returns {boolean} Whether the validation passes
|
||||
*/
|
||||
function containsValidation(code, value, options = {}) {
|
||||
const { caseSensitive = true, wholeWord = false } = options;
|
||||
const { caseSensitive = true, wholeWord = false } = options;
|
||||
|
||||
if (!caseSensitive) {
|
||||
code = code.toLowerCase();
|
||||
value = value.toLowerCase();
|
||||
}
|
||||
if (!caseSensitive) {
|
||||
code = code.toLowerCase();
|
||||
value = value.toLowerCase();
|
||||
}
|
||||
|
||||
if (wholeWord) {
|
||||
const regex = new RegExp(`\\b${escapeRegExp(value)}\\b`, caseSensitive ? '' : 'i');
|
||||
return regex.test(code);
|
||||
}
|
||||
if (wholeWord) {
|
||||
const regex = new RegExp(`\\b${escapeRegExp(value)}\\b`, caseSensitive ? "" : "i");
|
||||
return regex.test(code);
|
||||
}
|
||||
|
||||
return code.includes(value);
|
||||
return code.includes(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,19 +109,19 @@ function containsValidation(code, value, options = {}) {
|
||||
* @returns {boolean} Whether the validation passes
|
||||
*/
|
||||
function regexValidation(code, pattern, options = {}) {
|
||||
const { caseSensitive = true, multiline = true } = options;
|
||||
const { caseSensitive = true, multiline = true } = options;
|
||||
|
||||
let flags = '';
|
||||
if (!caseSensitive) flags += 'i';
|
||||
if (multiline) flags += 'm';
|
||||
let flags = "";
|
||||
if (!caseSensitive) flags += "i";
|
||||
if (multiline) flags += "m";
|
||||
|
||||
try {
|
||||
const regex = new RegExp(pattern, flags);
|
||||
return regex.test(code);
|
||||
} catch (e) {
|
||||
console.error('Invalid regex in validation:', e);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const regex = new RegExp(pattern, flags);
|
||||
return regex.test(code);
|
||||
} catch (e) {
|
||||
console.error("Invalid regex in validation:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,27 +132,27 @@ function regexValidation(code, pattern, options = {}) {
|
||||
* @returns {boolean} Whether the validation passes
|
||||
*/
|
||||
function propertyValueValidation(code, value, options = {}) {
|
||||
const { property, expected } = value;
|
||||
const { exact = false } = options;
|
||||
const { property, expected } = value;
|
||||
const { exact = false } = options;
|
||||
|
||||
// Create a regex to extract the property value
|
||||
// This is a simplified version and might not handle all CSS syntax nuances
|
||||
const propertyRegex = new RegExp(`${escapeRegExp(property)}\\s*:\\s*([^;\\}]+)`, 'i');
|
||||
const match = code.match(propertyRegex);
|
||||
// Create a regex to extract the property value
|
||||
// This is a simplified version and might not handle all CSS syntax nuances
|
||||
const propertyRegex = new RegExp(`${escapeRegExp(property)}\\s*:\\s*([^;\\}]+)`, "i");
|
||||
const match = code.match(propertyRegex);
|
||||
|
||||
if (!match) {
|
||||
// Property not found
|
||||
return false;
|
||||
}
|
||||
if (!match) {
|
||||
// Property not found
|
||||
return false;
|
||||
}
|
||||
|
||||
const actualValue = match[1].trim();
|
||||
const actualValue = match[1].trim();
|
||||
|
||||
if (exact) {
|
||||
return actualValue === expected;
|
||||
} else {
|
||||
// Allow for flexible matching
|
||||
return actualValue.toLowerCase().includes(expected.toLowerCase());
|
||||
}
|
||||
if (exact) {
|
||||
return actualValue === expected;
|
||||
} else {
|
||||
// Allow for flexible matching
|
||||
return actualValue.toLowerCase().includes(expected.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,16 +161,16 @@ function propertyValueValidation(code, value, options = {}) {
|
||||
* @returns {Object} Validation result
|
||||
*/
|
||||
function syntaxValidation(code) {
|
||||
try {
|
||||
// Create a hidden style element to test the CSS
|
||||
const style = document.createElement('style');
|
||||
style.textContent = code;
|
||||
document.head.appendChild(style);
|
||||
document.head.removeChild(style);
|
||||
return { isValid: true };
|
||||
} catch (e) {
|
||||
return { isValid: false, error: e.message };
|
||||
}
|
||||
try {
|
||||
// Create a hidden style element to test the CSS
|
||||
const style = document.createElement("style");
|
||||
style.textContent = code;
|
||||
document.head.appendChild(style);
|
||||
document.head.removeChild(style);
|
||||
return { isValid: true };
|
||||
} catch (e) {
|
||||
return { isValid: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,5 +179,5 @@ function syntaxValidation(code) {
|
||||
* @returns {string} Escaped string
|
||||
*/
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user