Files
linux-practice/index_new.html
likingcode 5686831d9a feat: Linux练习平台
- Web界面Linux命令练习
- Python后端 + sandbox安全沙箱
- 课程和任务管理
2026-03-07 05:43:51 +00:00

927 lines
32 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Linux 命令学习平台 - 从入门到精通</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--primary: #4CAF50;
--primary-dark: #388E3C;
--secondary: #2196F3;
--accent: #FF9800;
--bg: #f5f7fa;
--card-bg: #ffffff;
--text: #333333;
--text-light: #666666;
--border: #e0e0e0;
--success: #4CAF50;
--error: #f44336;
--warning: #ff9800;
}
[data-theme="dark"] {
--bg: #1a1a2e;
--card-bg: #16213e;
--text: #eaeaea;
--text-light: #a0a0a0;
--border: #0f3460;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
}
/* Header */
.header {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
color: white;
padding: 1rem 2rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header-content {
max-width: 1400px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.5rem;
font-weight: bold;
}
.header-actions {
display: flex;
gap: 1rem;
align-items: center;
}
.mode-toggle {
background: rgba(255,255,255,0.2);
border: none;
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
}
.mode-toggle:hover {
background: rgba(255,255,255,0.3);
}
.mode-toggle.active {
background: white;
color: var(--primary);
}
/* Main Layout */
.container {
max-width: 1400px;
margin: 0 auto;
display: grid;
grid-template-columns: 280px 1fr 350px;
gap: 1.5rem;
padding: 1.5rem;
min-height: calc(100vh - 70px);
}
/* Sidebar */
.sidebar {
background: var(--card-bg);
border-radius: 12px;
padding: 1rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
height: fit-content;
}
.sidebar-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--primary);
}
.level-section {
margin-bottom: 1rem;
}
.level-header {
font-weight: 600;
color: var(--primary);
padding: 0.5rem;
background: rgba(76,175,80,0.1);
border-radius: 6px;
margin-bottom: 0.5rem;
cursor: pointer;
}
.task-list {
list-style: none;
}
.task-item {
padding: 0.6rem 0.8rem;
margin: 0.3rem 0;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
transition: all 0.2s;
font-size: 0.9rem;
}
.task-item:hover {
background: var(--bg);
}
.task-item.active {
background: var(--primary);
color: white;
}
.task-item.completed {
opacity: 0.7;
}
.task-status {
font-size: 1rem;
}
/* Main Content */
.main-content {
background: var(--card-bg);
border-radius: 12px;
padding: 2rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.welcome-panel {
text-align: center;
padding: 3rem 2rem;
}
.welcome-title {
font-size: 2rem;
margin-bottom: 1rem;
background: linear-gradient(135deg, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.welcome-desc {
color: var(--text-light);
font-size: 1.1rem;
margin-bottom: 2rem;
}
.start-btn {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
color: white;
border: none;
padding: 1rem 3rem;
font-size: 1.1rem;
border-radius: 30px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.start-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(76,175,80,0.4);
}
/* Task Panel */
.task-panel {
display: none;
}
.task-header {
margin-bottom: 1.5rem;
}
.task-title {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.task-meta {
display: flex;
gap: 1rem;
color: var(--text-light);
font-size: 0.9rem;
}
.task-description {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
padding: 1.5rem;
border-radius: 10px;
margin-bottom: 1.5rem;
border-left: 4px solid var(--primary);
}
.task-description h3 {
color: var(--primary);
margin-bottom: 0.8rem;
}
.task-description code {
background: rgba(76,175,80,0.1);
padding: 0.2rem 0.5rem;
border-radius: 4px;
color: var(--primary-dark);
font-family: 'Consolas', monospace;
}
/* Command Input Area */
.command-area {
background: #1e1e1e;
border-radius: 10px;
padding: 1rem;
margin-bottom: 1rem;
}
.command-input-wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.prompt {
color: #4CAF50;
font-family: 'Consolas', monospace;
font-weight: bold;
}
.command-input {
flex: 1;
background: transparent;
border: none;
color: #fff;
font-family: 'Consolas', monospace;
font-size: 1rem;
outline: none;
}
.command-output {
background: #2d2d2d;
padding: 1rem;
border-radius: 6px;
min-height: 100px;
max-height: 300px;
overflow-y: auto;
font-family: 'Consolas', monospace;
color: #ddd;
white-space: pre-wrap;
margin-top: 0.5rem;
}
.action-buttons {
display: flex;
gap: 1rem;
margin-top: 1rem;
}
.btn {
padding: 0.8rem 1.5rem;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
transition: all 0.2s;
}
.btn-primary {
background: var(--primary);
color: white;
}
.btn-primary:hover {
background: var(--primary-dark);
}
.btn-secondary {
background: var(--border);
color: var(--text);
}
.btn-hint {
background: var(--accent);
color: white;
}
/* Learning Panel (Right Sidebar) */
.learning-panel {
background: var(--card-bg);
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
height: fit-content;
}
.panel-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.panel-title::before {
content: "📚";
}
.command-detail {
display: none;
}
.command-detail.active {
display: block;
}
.cmd-name {
font-size: 1.3rem;
font-weight: bold;
color: var(--primary);
margin-bottom: 0.5rem;
font-family: 'Consolas', monospace;
}
.cmd-desc {
color: var(--text-light);
margin-bottom: 1rem;
line-height: 1.8;
}
.cmd-section {
margin-bottom: 1.2rem;
}
.cmd-section-title {
font-weight: 600;
color: var(--secondary);
margin-bottom: 0.5rem;
font-size: 0.95rem;
}
.param-list {
list-style: none;
}
.param-item {
padding: 0.5rem;
background: var(--bg);
border-radius: 6px;
margin-bottom: 0.5rem;
font-size: 0.9rem;
}
.param-name {
font-family: 'Consolas', monospace;
color: var(--accent);
font-weight: 600;
}
.example-box {
background: #f8f9fa;
border-left: 3px solid var(--secondary);
padding: 0.8rem;
border-radius: 0 6px 6px 0;
margin: 0.5rem 0;
}
.example-box code {
font-family: 'Consolas', monospace;
color: var(--primary-dark);
}
/* Success Panel */
.success-panel {
display: none;
background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
border: 1px solid var(--success);
border-radius: 10px;
padding: 1.5rem;
margin-top: 1rem;
}
.success-panel.show {
display: block;
}
.success-title {
color: var(--success);
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.deep-learning-btn {
background: var(--secondary);
color: white;
border: none;
padding: 0.8rem 1.5rem;
border-radius: 6px;
cursor: pointer;
margin-top: 1rem;
}
/* Progress Bar */
.progress-section {
margin-bottom: 1.5rem;
}
.progress-bar {
height: 8px;
background: var(--border);
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--primary), var(--secondary));
transition: width 0.3s;
}
.progress-text {
text-align: center;
margin-top: 0.5rem;
color: var(--text-light);
font-size: 0.9rem;
}
/* Responsive */
@media (max-width: 1200px) {
.container {
grid-template-columns: 260px 1fr;
}
.learning-panel {
display: none;
}
}
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
}
.sidebar {
display: none;
}
}
</style>
</head>
<body>
<header class="header">
<div class="header-content">
<div class="logo">🐧 Linux 学习平台</div>
<div class="header-actions">
<button class="mode-toggle active" id="learnMode" onclick="setMode('learn')">📖 学习模式</button>
<button class="mode-toggle" id="practiceMode" onclick="setMode('practice')">✏️ 练习模式</button>
<button class="mode-toggle" onclick="toggleTheme()">🌓</button>
</div>
</div>
</header>
<div class="container">
<!-- Left Sidebar: Course Navigation -->
<aside class="sidebar">
<div class="sidebar-title">📚 课程目录</div>
<div class="progress-section">
<div class="progress-bar">
<div class="progress-fill" id="progressFill" style="width: 0%"></div>
</div>
<div class="progress-text" id="progressText">0 / 95 完成</div>
</div>
<div id="courseNav"></div>
</aside>
<!-- Main Content: Task Area -->
<main class="main-content">
<!-- Welcome Panel -->
<div class="welcome-panel" id="welcomePanel">
<h1 class="welcome-title">🚀 开启 Linux 学习之旅</h1>
<p class="welcome-desc">
从入门到精通,系统学习 Linux 运维技能<br>
<strong>12 个级别 · 95 道题目 · 循序渐进</strong>
</p>
<button class="start-btn" onclick="startLearning()">开始学习</button>
</div>
<!-- Task Panel -->
<div class="task-panel" id="taskPanel">
<div class="task-header">
<h2 class="task-title" id="taskTitle">任务标题</h2>
<div class="task-meta">
<span id="taskLevel">Level 1</span>
<span>·</span>
<span id="taskNumber">1 / 95</span>
</div>
</div>
<div class="task-description" id="taskDescription">
<!-- 任务描述 -->
</div>
<!-- Command Input -->
<div class="command-area">
<div class="command-input-wrapper">
<span class="prompt">$</span>
<input type="text" class="command-input" id="cmdInput"
placeholder="输入 Linux 命令..."
onkeypress="if(event.key==='Enter')executeCommand()">
</div>
<div class="command-output" id="cmdOutput">输出将显示在这里...</div>
</div>
<div class="action-buttons">
<button class="btn btn-primary" onclick="executeCommand()">▶ 执行命令</button>
<button class="btn btn-hint" onclick="showHint()">💡 提示</button>
<button class="btn btn-secondary" onclick="showAnswer()">👀 查看答案</button>
</div>
<!-- Success Panel -->
<div class="success-panel" id="successPanel">
<div class="success-title">🎉 回答正确!</div>
<p id="successMessage">恭喜你完成了这个任务!</p>
<button class="deep-learning-btn" onclick="showDeepLearning()">
📚 查看深入学习资料
</button>
<button class="btn btn-primary" onclick="nextTask()" style="margin-left: 1rem;">
下一题 →
</button>
</div>
</div>
</main>
<!-- Right Sidebar: Learning Panel -->
<aside class="learning-panel" id="learningPanel">
<div class="panel-title">命令详解</div>
<div id="commandDetail">
<p style="color: var(--text-light); text-align: center; padding: 2rem 0;">
选择一个任务开始学习<br>
每个命令都有详细讲解
</p>
</div>
</aside>
</div>
<script>
// ====================
// 全局状态
// ====================
let COURSE_DATA = null;
let currentMode = 'learn'; // 'learn' 或 'practice'
let currentTask = null;
let completedTasks = JSON.parse(localStorage.getItem('linux_completed') || '[]');
let currentTheme = localStorage.getItem('linux_theme') || 'light';
// ====================
// 命令知识库
// ====================
const COMMAND_KNOWLEDGE = {
'pwd': {
name: 'pwd',
fullName: 'Print Working Directory',
description: '显示当前工作目录的完整路径。这是你在文件系统中的"当前位置"。',
commonParams: [
{ param: '-L', desc: '显示逻辑路径(包括符号链接)' },
{ param: '-P', desc: '显示物理路径(解析所有符号链接)' }
],
examples: [
{ cmd: 'pwd', desc: '显示当前目录' },
{ cmd: 'pwd -P', desc: '显示物理路径' }
],
tips: '当你不确定自己在哪个目录时,随时使用 pwd 确认位置。',
related: ['cd', 'ls']
},
'ls': {
name: 'ls',
fullName: 'List Directory Contents',
description: '列出目录中的文件和子目录。这是最常用的文件查看命令。',
commonParams: [
{ param: '-l', desc: '长格式显示,包含权限、所有者、大小等详细信息' },
{ param: '-a', desc: '显示所有文件,包括以点开头的隐藏文件' },
{ param: '-h', desc: '人类可读格式,文件大小显示为 K、M、G' },
{ param: '-t', desc: '按修改时间排序' },
{ param: '-r', desc: '反向排序' },
{ param: '-S', desc: '按文件大小排序' }
],
examples: [
{ cmd: 'ls', desc: '列出当前目录文件' },
{ cmd: 'ls -la', desc: '详细列出所有文件' },
{ cmd: 'ls -lh', desc: '详细列出,人类可读大小' },
{ cmd: 'ls -lt', desc: '按时间排序列出' }
],
tips: 'ls -la 是查看目录内容最常用的组合命令。',
related: ['pwd', 'cd', 'll']
},
'cd': {
name: 'cd',
fullName: 'Change Directory',
description: '切换当前工作目录。这是文件系统导航的基础命令。',
commonParams: [
{ param: '..', desc: '切换到上级目录' },
{ param: '~', desc: '切换到用户主目录' },
{ param: '-', desc: '切换到刚才所在的目录' }
],
examples: [
{ cmd: 'cd /home', desc: '切换到 /home 目录' },
{ cmd: 'cd ..', desc: '切换到上级目录' },
{ cmd: 'cd ~', desc: '切换到主目录' },
{ cmd: 'cd -', desc: '返回刚才的目录' }
],
tips: 'cd - 可以快速在两个目录之间切换。',
related: ['pwd', 'ls']
},
'cat': {
name: 'cat',
fullName: 'Concatenate',
description: '连接文件并输出内容。常用于查看小文件的内容。',
commonParams: [
{ param: '-n', desc: '显示行号' },
{ param: '-b', desc: '显示行号,但空行不编号' },
{ param: '-s', desc: '压缩多个空行为一个' }
],
examples: [
{ cmd: 'cat file.txt', desc: '显示文件内容' },
{ cmd: 'cat -n file.txt', desc: '显示文件内容并带行号' },
{ cmd: 'cat file1 file2', desc: '连接多个文件' }
],
tips: '对于大文件,建议使用 less 或 more 而不是 cat。',
related: ['less', 'more', 'head', 'tail']
}
};
// ====================
// 初始化
// ====================
document.addEventListener('DOMContentLoaded', async () => {
applyTheme(currentTheme);
await loadCourseData();
renderCourseNav();
updateProgress();
});
async function loadCourseData() {
try {
const res = await fetch('/api/tasks');
if (res.ok) {
COURSE_DATA = await res.json();
console.log('✅ 课程数据加载成功');
}
} catch (e) {
console.error('❌ 加载课程数据失败:', e);
}
}
// ====================
// 模式切换
// ====================
function setMode(mode) {
currentMode = mode;
document.getElementById('learnMode').classList.toggle('active', mode === 'learn');
document.getElementById('practiceMode').classList.toggle('active', mode === 'practice');
if (currentTask) {
renderTask(currentTask);
}
}
function toggleTheme() {
currentTheme = currentTheme === 'light' ? 'dark' : 'light';
applyTheme(currentTheme);
localStorage.setItem('linux_theme', currentTheme);
}
function applyTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
}
// ====================
// 课程导航
// ====================
function renderCourseNav() {
if (!COURSE_DATA) return;
const nav = document.getElementById('courseNav');
let html = '';
COURSE_DATA.levels.forEach((level, levelIdx) => {
html += `
<div class="level-section">
<div class="level-header" onclick="toggleLevel(${levelIdx})">
${level.title}
</div>
<ul class="task-list" id="level-${levelIdx}">
`;
level.challenges.forEach((task, taskIdx) => {
const isCompleted = completedTasks.includes(task.id);
const isActive = currentTask && currentTask.id === task.id;
html += `
<li class="task-item ${isActive ? 'active' : ''} ${isCompleted ? 'completed' : ''}"
onclick="selectTask('${level.id}', '${task.id}')">
<span class="task-status">${isCompleted ? '✅' : '○'}</span>
<span>${taskIdx + 1}. ${task.title}</span>
</li>
`;
});
html += '</ul></div>';
});
nav.innerHTML = html;
}
function toggleLevel(levelIdx) {
const list = document.getElementById(`level-${levelIdx}`);
list.style.display = list.style.display === 'none' ? 'block' : 'none';
}
function selectTask(levelId, taskId) {
if (!COURSE_DATA) return;
const level = COURSE_DATA.levels.find(l => l.id === levelId);
const task = level.challenges.find(t => t.id === taskId);
currentTask = { ...task, level: level.title, levelNum: COURSE_DATA.levels.indexOf(level) + 1 };
renderTask(currentTask);
}
// ====================
// 任务渲染
// ====================
function renderTask(task) {
document.getElementById('welcomePanel').style.display = 'none';
document.getElementById('taskPanel').style.display = 'block';
document.getElementById('successPanel').classList.remove('show');
// 基本信息
document.getElementById('taskTitle').textContent = task.title;
document.getElementById('taskLevel').textContent = task.level;
document.getElementById('taskNumber').textContent = `${task.levelNum} / 95`;
// 任务描述
let descHtml = `<h3>📝 任务描述</h3><p>${task.description}</p>`;
// 学习模式:显示更多学习资料
if (currentMode === 'learn') {
descHtml += `
<div style="margin-top: 1rem; padding: 1rem; background: rgba(33,150,243,0.1); border-radius: 8px;">
<strong>💡 学习提示:</strong> ${task.hint}
</div>
`;
}
document.getElementById('taskDescription').innerHTML = descHtml;
// 清空输出
document.getElementById('cmdOutput').textContent = '输出将显示在这里...';
document.getElementById('cmdInput').value = '';
// 更新学习面板
updateLearningPanel(task);
// 更新导航高亮
renderCourseNav();
}
function updateLearningPanel(task) {
const panel = document.getElementById('commandDetail');
// 从任务描述中提取命令
const cmdMatch = task.description.match(/<code>(\w+)<\/code>/);
const cmdName = cmdMatch ? cmdMatch[1] : null;
if (cmdName && COMMAND_KNOWLEDGE[cmdName]) {
const cmd = COMMAND_KNOWLEDGE[cmdName];
let html = `
<div class="command-detail active">
<div class="cmd-name">${cmd.name}</div>
<div style="color: var(--text-light); font-size: 0.9rem; margin-bottom: 1rem;">
${cmd.fullName}
</div>
<div class="cmd-desc">${cmd.description}</div>
<div class="cmd-section">
<div class="cmd-section-title">🔧 常用参数</div>
<ul class="param-list">
`;
cmd.commonParams.forEach(param => {
html += `
<li class="param-item">
<span class="param-name">${param.param}</span>
<span style="color: var(--text-light);"> - ${param.desc}</span>
</li>
`;
});
html += `</ul></div>
<div class="cmd-section">
<div class="cmd-section-title">📝 使用示例</div>
`;
cmd.examples.forEach(ex => {
html += `
<div class="example-box">
<code>${ex.cmd}</code>
<div style="color: var(--text-light); margin-top: 0.3rem; font-size: 0.85rem;">
${ex.desc}
</div>
</div>
`;
});
html += `
</div>
<div class="cmd-section">
<div class="cmd-section-title">💡 小贴士</div>
<p style="font-size: 0.9rem; color: var(--text-light);">${cmd.tips}</p>
</div>
</div>
`;
panel.innerHTML = html;
} else {
panel.innerHTML = `
<div class="command-detail active">
<p style="color: var(--text-light);">
本任务涉及多个命令组合使用<br>
完成任务后可查看详细解析
</p>
</div>
`;
}
}
// ====================
// 命令执行
// ====================
async function executeCommand() {
const cmd = document.getElementById('cmdInput').value.trim();
if (!cmd) return;
const outputEl = document.getElementById('cmdOutput');
outputEl.textContent = `执行: ${cmd}\n正在运行...`;
try {
const res = await fetch('/api/run?cmd=' + encodeURIComponent(cmd));
const data = await res.json();
outputEl.textContent = data.output || data.message || '(无输出)';
// 检查答案
checkAnswer(cmd, data.output || '');
} catch (e) {
outputEl.textContent = `❌ 错误: ${e.message}`;
}
}
function checkAnswer(cmd, output) {
if (!currentTask) return;
// 简化检查:命令是否包含在 solution 中
const isCorrect = currentTask.solution && currentTask.solution.some(s =>
cmd.toLowerCase().includes(s.toLowerCase())
);
if (isCorrect) {
showSuccess();
}
}
function showSuccess() {
if (!completedTasks.includes(currentTask.id)) {
completedTasks.push(currentTask.id);
localStorage.setItem('linux_completed', JSON.stringify(completedTasks));
updateProgress();
}
document.getElementById('successPanel').classList.add('show');
document.getElementById('successMessage').textContent =
currentTask.success_msg || '恭喜你完成了这个任务!';
renderCourseNav();
}
function showDeepLearning() {
// 显示深入学习资料
alert('📚 深入学习资料功能开发中...\n\n这里将显示\n- 命令原理解析\n- 实际应用场景\n- 常见错误分析\n- 相关命令对比');
}
// ====================
// 辅助功能
// ====================
function showHint() {
if (!currentTask) return;
alert('💡 提示:\n\n' + currentTask.hint);
}
function showAnswer() {
if (!currentTask) return;
const answer = currentTask.solution ? currentTask.solution[0] : '暂无答案';
if (confirm('查看答案将标记此题为"已学习",确定要继续吗?')) {
document.getElementById('cmdInput').value = answer;
showSuccess();
}
}
function nextTask() {
if (!COURSE_DATA || !currentTask) return;
// 找到当前任务的下一个
let found = false;
for (const level of COURSE_DATA.levels) {
for (const task of level.challenges) {
if (found) {
selectTask(level.id, task.id);
return;
}
if (task.id === currentTask.id) found = true;
}
}
alert('🎉 恭喜!你已完成所有课程!');
}
function startLearning() {
if (!COURSE_DATA) {
alert('课程数据加载中,请稍候...');
return;
}
selectTask(COURSE_DATA.levels[0].id, COURSE_DATA.levels[0].challenges[0].id);
}
function updateProgress() {
const total = 95;
const completed = completedTasks.length;
const percent = Math.round((completed / total) * 100);
document.getElementById('progressFill').style.width = percent + '%';
document.getElementById('progressText').textContent = `${completed} / ${total} 完成 (${percent}%)`;
}
// ====================
// 全局函数暴露
// ====================
window.setMode = setMode;
window.toggleTheme = toggleTheme;
window.toggleLevel = toggleLevel;
window.selectTask = selectTask;
window.executeCommand = executeCommand;
window.showHint = showHint;
window.showAnswer = showAnswer;
window.showDeepLearning = showDeepLearning;
window.nextTask = nextTask;
window.startLearning = startLearning;
</script>
</body>
</html>