Lashtw commited on
Commit
66cf72a
·
verified ·
1 Parent(s): 3c32aab

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +162 -85
index.html CHANGED
@@ -96,12 +96,20 @@
96
  <p id="highscore-display" class="text-lg font-bold text-amber-500">0</p>
97
  </div>
98
  </div>
99
- <!-- 單字範圍選擇 -->
100
- <div class="my-4 p-4 bg-gray-100 rounded-2xl flex items-center justify-center gap-4">
101
- <label for="start-range" class="font-semibold text-gray-700">單字範圍:</label>
102
- <input type="number" id="start-range" min="1" class="w-20 p-2 border border-gray-300 rounded-lg text-center" placeholder="從">
103
- <span class="font-semibold text-gray-700">-</span>
104
- <input type="number" id="end-range" min="1" class="w-20 p-2 border border-gray-300 rounded-lg text-center" placeholder="到">
 
 
 
 
 
 
 
 
105
  </div>
106
 
107
  <!-- 主要功能 -->
@@ -134,7 +142,7 @@
134
  </div>
135
 
136
  <!-- 教師工具 -->
137
- <div class="mt-8 pt-4 border-t flex justify-end items-center gap-3">
138
  <button id="share-game-btn" class="text-white font-semibold py-2 px-5 rounded-full transition-transform hover:scale-105 bg-violet-500 hover:bg-violet-600 text-sm">🔗 分享</button>
139
  <button id="manage-words-btn" class="text-white font-semibold py-2 px-5 rounded-full transition-transform hover:scale-105 bg-gray-500 hover:bg-gray-600 text-sm">⚙️ 管理</button>
140
  </div>
@@ -327,18 +335,34 @@
327
  <div id="share-modal" class="hidden fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center z-50 p-4">
328
  <div class="bg-white p-8 rounded-2xl shadow-xl w-full max-w-lg">
329
  <h3 class="text-2xl font-bold mb-4 text-gray-800">分享遊戲連結</h3>
330
- <p class="text-gray-600 mb-6">將此連結傳送給學生,他們將使用您目前的單字列表和範圍設定。</p>
331
- <div class="flex items-center gap-2">
332
- <input id="share-link-input" type="text" readonly class="w-full p-3 border border-gray-300 rounded-lg bg-gray-100">
333
- <button id="copy-link-btn" class="px-6 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 shrink-0">複製</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  </div>
335
- <p id="copy-feedback" class="text-green-600 text-sm h-5 mt-2 text-center font-semibold"></p>
336
  <div class="flex justify-end mt-4">
337
  <button type="button" id="close-share-modal-btn" class="px-6 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors">關閉</button>
338
  </div>
339
  </div>
340
  </div>
341
 
 
342
  <!-- 清除確認 Modal -->
343
  <div id="confirm-clear-modal" class="hidden fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center z-50 p-4">
344
  <div class="bg-white p-8 rounded-2xl shadow-xl w-full max-w-sm">
@@ -363,6 +387,9 @@
363
  </div>
364
  </div>
365
 
 
 
 
366
  <footer class="fixed bottom-4 right-4 text-xs text-gray-500 text-right z-50">
367
  <p>遊戲設計者:新竹縣精華國中藍星宇</p>
368
  <p>FB教育社群:<a href="https://www.facebook.com/groups/1554372228718393" target="_blank" rel="noopener noreferrer" class="text-blue-500 hover:underline">萬物皆數</a></p>
@@ -376,10 +403,14 @@
376
  const learningMode = document.getElementById('learning-mode');
377
  const modeSelectionSection = document.getElementById('mode-selection-section');
378
  const highscoreDisplay = document.getElementById('highscore-display');
 
379
  const startRangeInput = document.getElementById('start-range');
380
  const endRangeInput = document.getElementById('end-range');
 
381
  const manageWordsBtn = document.getElementById('manage-words-btn');
382
  const shareGameBtn = document.getElementById('share-game-btn');
 
 
383
 
384
  // 單字管理介面
385
  const wordManagementView = document.getElementById('word-management-view');
@@ -449,6 +480,9 @@
449
  const saveEditBtn = document.getElementById('save-edit-btn');
450
  const cancelEditBtn = document.getElementById('cancel-edit-btn');
451
  const shareModal = document.getElementById('share-modal');
 
 
 
452
  const shareLinkInput = document.getElementById('share-link-input');
453
  const copyLinkBtn = document.getElementById('copy-link-btn');
454
  const copyFeedback = document.getElementById('copy-feedback');
@@ -461,6 +495,10 @@
461
  const cancelDeleteBtn = document.getElementById('cancel-delete-btn');
462
  const confirmDeleteBtn = document.getElementById('confirm-delete-btn');
463
 
 
 
 
 
464
 
465
  // 應用程式狀態
466
  let words = [];
@@ -706,15 +744,32 @@
706
  const start = parseInt(startRangeInput.value, 10);
707
  const end = parseInt(endRangeInput.value, 10);
708
 
709
- // 學習時,使用新增順序
 
710
  if (!isNaN(start) && !isNaN(end) && start > 0 && end >= start && end <= words.length) {
711
- wordsForCurrentMode = words.slice(start - 1, end);
712
  } else {
713
- wordsForCurrentMode = [...words];
714
  }
715
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
  if (wordsForCurrentMode.length === 0) {
717
- alert("選定的範圍內沒有單字,請重新選擇。");
718
  return;
719
  }
720
 
@@ -753,75 +808,32 @@
753
 
754
  const wordIndex = words.findIndex(w => w.english === card.english && w.chinese === card.chinese);
755
 
756
- // --- Start: New, separated logic for Review Mode ---
 
 
757
  if (isReview) {
758
  const isFlipped = flashcardContainer.classList.contains('flipped');
759
- let correctAnswer, answerLang;
760
-
761
- if(isFlipped) { // Chinese side is showing, user should type English
762
  correctAnswer = card.english.split('(')[0].trim();
763
  answerLang = 'en';
764
- } else { // English side is showing, user should type Chinese
765
  correctAnswer = card.chinese.split('(')[0].trim();
766
  answerLang = 'zh';
767
  }
768
-
769
- let isCorrect;
770
- if (answerLang === 'en') {
771
- const normalize = (str) => str.toLowerCase().replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "").trim();
772
- isCorrect = normalize(userAnswer) === normalize(correctAnswer);
773
- } else if (answerLang === 'zh') {
774
- const possibleAnswers = correctAnswer.split(/[;;]/).map(a => a.trim());
775
- isCorrect = possibleAnswers.some(ans => userAnswer.trim().includes(ans) && ans !== '');
776
- }
777
-
778
- if (isCorrect) {
779
- feedbackDisplay.textContent = '答對了!';
780
- feedbackDisplay.classList.remove('text-red-500');
781
- feedbackDisplay.classList.add('text-green-600');
782
- triggerConfetti();
783
- } else {
784
- feedbackDisplay.textContent = '答錯了!';
785
- feedbackDisplay.classList.add('text-red-500');
786
- answerInput.classList.add('shake');
787
- setTimeout(() => { answerInput.classList.remove('shake'); }, 820);
788
- }
789
-
790
- // Flip the card to show the answer, regardless of correctness
791
- if (isFlipped) {
792
- flashcardContainer.classList.remove('flipped');
793
- } else {
794
- flashcardContainer.classList.add('flipped');
795
  }
796
-
797
- // Update placeholder for the new card face and clear input
798
- setTimeout(() => {
799
- const currentlyFlipped = flashcardContainer.classList.contains('flipped');
800
- answerInput.placeholder = currentlyFlipped ? "請輸入英文答案..." : "請輸入中文答案...";
801
- answerInput.value = '';
802
- answerInput.focus();
803
- }, 600); // Wait for flip to finish
804
-
805
- return; // End execution for review mode
806
- }
807
- // --- End: Logic for Review Mode ---
808
-
809
- // --- Logic for all other quiz modes ---
810
- let correctAnswer;
811
- let answerLang;
812
- let effectiveMode = currentMode;
813
- if (isSpeed) effectiveMode = currentSpeedQuestionType;
814
- if (currentMode === 'hard') effectiveMode = 'zh-en';
815
-
816
- switch (effectiveMode) {
817
- case 'zh-en': case 'listen':
818
- correctAnswer = card.english.split('(')[0].trim();
819
- answerLang = 'en';
820
- break;
821
- case 'en-zh':
822
- correctAnswer = card.chinese.split('(')[0].trim();
823
- answerLang = 'zh';
824
- break;
825
  }
826
 
827
  let isCorrect;
@@ -834,13 +846,17 @@
834
  }
835
 
836
  if (isCorrect) {
 
837
  answerInput.disabled = true;
838
  submitBtn.disabled = true;
839
  feedbackDisplay.textContent = '答對了!';
840
  feedbackDisplay.classList.remove('text-red-500');
841
  feedbackDisplay.classList.add('text-green-600');
842
  triggerConfetti();
843
- flashcardContainer.classList.add('flipped');
 
 
 
844
 
845
  if (wordIndex !== -1) words[wordIndex].proficiency = (words[wordIndex].proficiency || 0) + 1;
846
 
@@ -848,7 +864,7 @@
848
  currentScore++;
849
  scoreDisplay.textContent = `得分: ${currentScore}`;
850
  setTimeout(startSpeedCard, 500);
851
- } else {
852
  quizQueue.shift();
853
 
854
  setTimeout(() => {
@@ -866,7 +882,13 @@
866
  }
867
  }, 1500);
868
  }
869
- } else { // Incorrect answer for quiz modes
 
 
 
 
 
 
870
  quizIncorrectCount++;
871
  updateHintButtonVisibility();
872
  answerInput.classList.add('shake');
@@ -880,6 +902,13 @@
880
  feedbackDisplay.textContent = '答錯了,再試一次!';
881
  feedbackDisplay.classList.add('text-red-500');
882
  setTimeout(() => { answerInput.classList.remove('shake'); feedbackDisplay.textContent = ''; }, 1000);
 
 
 
 
 
 
 
883
  } else { // Other quiz modes
884
  answerInput.disabled = true;
885
  submitBtn.disabled = true;
@@ -1065,14 +1094,21 @@
1065
  alert('冊次和課次不能為空!');
1066
  }
1067
  });
1068
-
1069
  shareGameBtn.addEventListener('click', () => {
1070
- try {
 
 
 
 
 
 
 
1071
  const wordsString = JSON.stringify(words);
1072
- // 處理 UTF-8 字元以避免 btoa 錯誤
1073
  const base64Words = btoa(unescape(encodeURIComponent(wordsString)));
1074
  const start = startRangeInput.value || '';
1075
  const end = endRangeInput.value || '';
 
1076
 
1077
  const baseUrl = window.location.href.split('?')[0];
1078
  const url = new URL(baseUrl);
@@ -1080,10 +1116,16 @@
1080
  url.searchParams.set('data', base64Words);
1081
  if (start) url.searchParams.set('start', start);
1082
  if (end) url.searchParams.set('end', end);
 
 
 
 
 
 
 
1083
 
1084
  shareLinkInput.value = url.toString();
1085
- copyFeedback.textContent = '';
1086
- shareModal.classList.remove('hidden');
1087
 
1088
  } catch (error) {
1089
  console.error("產生分享連結時發生錯誤:", error);
@@ -1091,6 +1133,7 @@
1091
  }
1092
  });
1093
 
 
1094
  copyLinkBtn.addEventListener('click', () => {
1095
  shareLinkInput.select();
1096
  shareLinkInput.setSelectionRange(0, 99999); // 兼容移動設備
@@ -1152,6 +1195,26 @@
1152
  confirmDeleteModal.classList.add('hidden');
1153
  indexToDelete = -1;
1154
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1155
 
1156
 
1157
  // --- 應用程式初始化 ---
@@ -1203,8 +1266,21 @@
1203
 
1204
  const sharedStart = urlParams.get('start');
1205
  const sharedEnd = urlParams.get('end');
 
 
 
1206
  if (sharedStart) startRangeInput.value = sharedStart;
1207
  if (sharedEnd) endRangeInput.value = sharedEnd;
 
 
 
 
 
 
 
 
 
 
1208
 
1209
  if (urlParams.has('data')) {
1210
  history.replaceState(null, '', window.location.pathname);
@@ -1213,6 +1289,7 @@
1213
  startRangeInput.max = words.length;
1214
  endRangeInput.max = words.length;
1215
  endRangeInput.placeholder = `到 ${words.length}`;
 
1216
  showView('menu');
1217
  };
1218
  initializeApp();
 
96
  <p id="highscore-display" class="text-lg font-bold text-amber-500">0</p>
97
  </div>
98
  </div>
99
+ <!-- 單字範圍與隨機出題選擇 -->
100
+ <div id="settings-container" class="my-4 p-4 bg-gray-100 rounded-2xl space-y-3">
101
+ <div class="flex items-center justify-center gap-4">
102
+ <label for="start-range" class="font-semibold text-gray-700">單字範圍:</label>
103
+ <input type="number" id="start-range" min="1" class="w-20 p-2 border border-gray-300 rounded-lg text-center" placeholder="從">
104
+ <span class="font-semibold text-gray-700">-</span>
105
+ <input type="number" id="end-range" min="1" class="w-20 p-2 border border-gray-300 rounded-lg text-center" placeholder="到">
106
+ </div>
107
+ <div class="flex items-center justify-center gap-4">
108
+ <input type="checkbox" id="random-questions-checkbox" class="h-5 w-5 rounded text-indigo-600 focus:ring-indigo-500 border-gray-300">
109
+ <label for="random-questions-checkbox" class="font-semibold text-gray-700">隨機出題:</label>
110
+ <input type="number" id="random-questions-count" min="1" class="w-20 p-2 border border-gray-300 rounded-lg text-center disabled:bg-gray-200 disabled:cursor-not-allowed" placeholder="題數" disabled>
111
+ <span class="font-semibold text-gray-700">題</span>
112
+ </div>
113
  </div>
114
 
115
  <!-- 主要功能 -->
 
142
  </div>
143
 
144
  <!-- 教師工具 -->
145
+ <div id="teacher-tools" class="mt-8 pt-4 border-t flex justify-end items-center gap-3">
146
  <button id="share-game-btn" class="text-white font-semibold py-2 px-5 rounded-full transition-transform hover:scale-105 bg-violet-500 hover:bg-violet-600 text-sm">🔗 分享</button>
147
  <button id="manage-words-btn" class="text-white font-semibold py-2 px-5 rounded-full transition-transform hover:scale-105 bg-gray-500 hover:bg-gray-600 text-sm">⚙️ 管理</button>
148
  </div>
 
335
  <div id="share-modal" class="hidden fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center z-50 p-4">
336
  <div class="bg-white p-8 rounded-2xl shadow-xl w-full max-w-lg">
337
  <h3 class="text-2xl font-bold mb-4 text-gray-800">分享遊戲連結</h3>
338
+ <p class="text-gray-600 mb-6">請選擇分享選項,然後產生連結。</p>
339
+ <div class="my-4">
340
+ <label class="flex items-center text-lg">
341
+ <input type="checkbox" id="lock-settings-checkbox" class="h-5 w-5 rounded text-indigo-600 focus:ring-indigo-500 border-gray-300">
342
+ <span class="ml-3 text-gray-700">禁止學生變更設定 (鎖定範圍與題數)</span>
343
+ </label>
344
+ </div>
345
+
346
+ <button id="generate-link-btn" class="w-full mb-4 bg-indigo-600 text-white font-bold py-3 rounded-lg hover:bg-indigo-700 transition-colors">
347
+ 產生分享連結
348
+ </button>
349
+
350
+ <div id="share-result-container" class="hidden">
351
+ <p class="text-sm text-gray-500 mb-2">連結已產生:</p>
352
+ <div class="flex items-center gap-2">
353
+ <input id="share-link-input" type="text" readonly class="w-full p-3 border border-gray-300 rounded-lg bg-gray-100">
354
+ <button id="copy-link-btn" class="px-6 py-3 bg-violet-600 text-white rounded-lg hover:bg-violet-700 shrink-0">複製</button>
355
+ </div>
356
+ <p id="copy-feedback" class="text-green-600 text-sm h-5 mt-2 text-center font-semibold"></p>
357
  </div>
358
+
359
  <div class="flex justify-end mt-4">
360
  <button type="button" id="close-share-modal-btn" class="px-6 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors">關閉</button>
361
  </div>
362
  </div>
363
  </div>
364
 
365
+
366
  <!-- 清除確認 Modal -->
367
  <div id="confirm-clear-modal" class="hidden fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center z-50 p-4">
368
  <div class="bg-white p-8 rounded-2xl shadow-xl w-full max-w-sm">
 
387
  </div>
388
  </div>
389
 
390
+ <audio id="correct-sound" src="correct.mp3" preload="auto"></audio>
391
+ <audio id="wrong-sound" src="wrong.mp3" preload="auto"></audio>
392
+
393
  <footer class="fixed bottom-4 right-4 text-xs text-gray-500 text-right z-50">
394
  <p>遊戲設計者:新竹縣精華國中藍星宇</p>
395
  <p>FB教育社群:<a href="https://www.facebook.com/groups/1554372228718393" target="_blank" rel="noopener noreferrer" class="text-blue-500 hover:underline">萬物皆數</a></p>
 
403
  const learningMode = document.getElementById('learning-mode');
404
  const modeSelectionSection = document.getElementById('mode-selection-section');
405
  const highscoreDisplay = document.getElementById('highscore-display');
406
+ const settingsContainer = document.getElementById('settings-container');
407
  const startRangeInput = document.getElementById('start-range');
408
  const endRangeInput = document.getElementById('end-range');
409
+ const teacherTools = document.getElementById('teacher-tools');
410
  const manageWordsBtn = document.getElementById('manage-words-btn');
411
  const shareGameBtn = document.getElementById('share-game-btn');
412
+ const randomQuestionsCheckbox = document.getElementById('random-questions-checkbox');
413
+ const randomQuestionsCountInput = document.getElementById('random-questions-count');
414
 
415
  // 單字管理介面
416
  const wordManagementView = document.getElementById('word-management-view');
 
480
  const saveEditBtn = document.getElementById('save-edit-btn');
481
  const cancelEditBtn = document.getElementById('cancel-edit-btn');
482
  const shareModal = document.getElementById('share-modal');
483
+ const lockSettingsCheckbox = document.getElementById('lock-settings-checkbox');
484
+ const generateLinkBtn = document.getElementById('generate-link-btn');
485
+ const shareResultContainer = document.getElementById('share-result-container');
486
  const shareLinkInput = document.getElementById('share-link-input');
487
  const copyLinkBtn = document.getElementById('copy-link-btn');
488
  const copyFeedback = document.getElementById('copy-feedback');
 
495
  const cancelDeleteBtn = document.getElementById('cancel-delete-btn');
496
  const confirmDeleteBtn = document.getElementById('confirm-delete-btn');
497
 
498
+ // 音效元素
499
+ const correctSound = document.getElementById('correct-sound');
500
+ const wrongSound = document.getElementById('wrong-sound');
501
+
502
 
503
  // 應用程式狀態
504
  let words = [];
 
744
  const start = parseInt(startRangeInput.value, 10);
745
  const end = parseInt(endRangeInput.value, 10);
746
 
747
+ let wordPool;
748
+ // 1. First, determine the pool of words based on the range.
749
  if (!isNaN(start) && !isNaN(end) && start > 0 && end >= start && end <= words.length) {
750
+ wordPool = words.slice(start - 1, end);
751
  } else {
752
+ wordPool = [...words];
753
  }
754
 
755
+ // 2. Then, check if random selection is enabled.
756
+ if (randomQuestionsCheckbox.checked) {
757
+ const randomCount = parseInt(randomQuestionsCountInput.value, 10);
758
+ if (!isNaN(randomCount) && randomCount > 0 && randomCount <= wordPool.length) {
759
+ // Shuffle the pool and take the specified number of words.
760
+ wordsForCurrentMode = shuffleArray([...wordPool]).slice(0, randomCount);
761
+ } else {
762
+ alert('請輸入有效的隨機題數。題數不能為零或超過所選範圍的單字總數。');
763
+ return;
764
+ }
765
+ } else {
766
+ // If not random, use the entire pool.
767
+ wordsForCurrentMode = wordPool;
768
+ }
769
+
770
+
771
  if (wordsForCurrentMode.length === 0) {
772
+ alert("選定的範圍內沒有單字,請重新選擇或���增。");
773
  return;
774
  }
775
 
 
808
 
809
  const wordIndex = words.findIndex(w => w.english === card.english && w.chinese === card.chinese);
810
 
811
+ let correctAnswer;
812
+ let answerLang;
813
+
814
  if (isReview) {
815
  const isFlipped = flashcardContainer.classList.contains('flipped');
816
+ if(isFlipped) { // Chinese side showing, user should type English
 
 
817
  correctAnswer = card.english.split('(')[0].trim();
818
  answerLang = 'en';
819
+ } else { // English side showing, user should type Chinese
820
  correctAnswer = card.chinese.split('(')[0].trim();
821
  answerLang = 'zh';
822
  }
823
+ } else {
824
+ let effectiveMode = currentMode;
825
+ if (isSpeed) effectiveMode = currentSpeedQuestionType;
826
+ if (currentMode === 'hard') effectiveMode = 'zh-en';
827
+ switch (effectiveMode) {
828
+ case 'zh-en': case 'listen':
829
+ correctAnswer = card.english.split('(')[0].trim();
830
+ answerLang = 'en';
831
+ break;
832
+ case 'en-zh':
833
+ correctAnswer = card.chinese.split('(')[0].trim();
834
+ answerLang = 'zh';
835
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
836
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
  }
838
 
839
  let isCorrect;
 
846
  }
847
 
848
  if (isCorrect) {
849
+ correctSound.play();
850
  answerInput.disabled = true;
851
  submitBtn.disabled = true;
852
  feedbackDisplay.textContent = '答對了!';
853
  feedbackDisplay.classList.remove('text-red-500');
854
  feedbackDisplay.classList.add('text-green-600');
855
  triggerConfetti();
856
+
857
+ if (!flashcardContainer.classList.contains('flipped')) {
858
+ flashcardContainer.classList.add('flipped');
859
+ }
860
 
861
  if (wordIndex !== -1) words[wordIndex].proficiency = (words[wordIndex].proficiency || 0) + 1;
862
 
 
864
  currentScore++;
865
  scoreDisplay.textContent = `得分: ${currentScore}`;
866
  setTimeout(startSpeedCard, 500);
867
+ } else if (!isReview) {
868
  quizQueue.shift();
869
 
870
  setTimeout(() => {
 
882
  }
883
  }, 1500);
884
  }
885
+
886
+ if (isReview) {
887
+ answerInput.disabled = false;
888
+ submitBtn.disabled = false;
889
+ }
890
+ } else { // Incorrect answer
891
+ wrongSound.play();
892
  quizIncorrectCount++;
893
  updateHintButtonVisibility();
894
  answerInput.classList.add('shake');
 
902
  feedbackDisplay.textContent = '答錯了,再試一次!';
903
  feedbackDisplay.classList.add('text-red-500');
904
  setTimeout(() => { answerInput.classList.remove('shake'); feedbackDisplay.textContent = ''; }, 1000);
905
+ } else if (isReview) {
906
+ feedbackDisplay.textContent = '答錯了!';
907
+ feedbackDisplay.classList.add('text-red-500');
908
+ if (!flashcardContainer.classList.contains('flipped')) {
909
+ flashcardContainer.classList.add('flipped');
910
+ }
911
+ setTimeout(() => { answerInput.classList.remove('shake'); }, 820);
912
  } else { // Other quiz modes
913
  answerInput.disabled = true;
914
  submitBtn.disabled = true;
 
1094
  alert('冊次和課次不能為空!');
1095
  }
1096
  });
1097
+
1098
  shareGameBtn.addEventListener('click', () => {
1099
+ shareResultContainer.classList.add('hidden');
1100
+ shareLinkInput.value = '';
1101
+ copyFeedback.textContent = '';
1102
+ shareModal.classList.remove('hidden');
1103
+ });
1104
+
1105
+ generateLinkBtn.addEventListener('click', () => {
1106
+ try {
1107
  const wordsString = JSON.stringify(words);
 
1108
  const base64Words = btoa(unescape(encodeURIComponent(wordsString)));
1109
  const start = startRangeInput.value || '';
1110
  const end = endRangeInput.value || '';
1111
+ const isLocked = lockSettingsCheckbox.checked;
1112
 
1113
  const baseUrl = window.location.href.split('?')[0];
1114
  const url = new URL(baseUrl);
 
1116
  url.searchParams.set('data', base64Words);
1117
  if (start) url.searchParams.set('start', start);
1118
  if (end) url.searchParams.set('end', end);
1119
+ if (isLocked) {
1120
+ url.searchParams.set('lock', 'true');
1121
+ if(randomQuestionsCheckbox.checked) {
1122
+ const randomCount = randomQuestionsCountInput.value;
1123
+ if(randomCount) url.searchParams.set('random', randomCount);
1124
+ }
1125
+ }
1126
 
1127
  shareLinkInput.value = url.toString();
1128
+ shareResultContainer.classList.remove('hidden');
 
1129
 
1130
  } catch (error) {
1131
  console.error("產生分享連結時發生錯誤:", error);
 
1133
  }
1134
  });
1135
 
1136
+
1137
  copyLinkBtn.addEventListener('click', () => {
1138
  shareLinkInput.select();
1139
  shareLinkInput.setSelectionRange(0, 99999); // 兼容移動設備
 
1195
  confirmDeleteModal.classList.add('hidden');
1196
  indexToDelete = -1;
1197
  });
1198
+
1199
+ randomQuestionsCheckbox.addEventListener('change', () => {
1200
+ randomQuestionsCountInput.disabled = !randomQuestionsCheckbox.checked;
1201
+ if (!randomQuestionsCheckbox.checked) {
1202
+ randomQuestionsCountInput.value = '';
1203
+ } else {
1204
+ randomQuestionsCountInput.focus();
1205
+ }
1206
+ });
1207
+
1208
+ const updateRandomCountMax = () => {
1209
+ const start = parseInt(startRangeInput.value, 10) || 1;
1210
+ const end = parseInt(endRangeInput.value, 10) || words.length;
1211
+ if (end >= start) {
1212
+ const rangeSize = end - start + 1;
1213
+ randomQuestionsCountInput.max = rangeSize;
1214
+ }
1215
+ };
1216
+ startRangeInput.addEventListener('input', updateRandomCountMax);
1217
+ endRangeInput.addEventListener('input', updateRandomCountMax);
1218
 
1219
 
1220
  // --- 應用程式初始化 ---
 
1266
 
1267
  const sharedStart = urlParams.get('start');
1268
  const sharedEnd = urlParams.get('end');
1269
+ const sharedRandom = urlParams.get('random');
1270
+ const isLocked = urlParams.get('lock') === 'true';
1271
+
1272
  if (sharedStart) startRangeInput.value = sharedStart;
1273
  if (sharedEnd) endRangeInput.value = sharedEnd;
1274
+ if (sharedRandom) {
1275
+ randomQuestionsCheckbox.checked = true;
1276
+ randomQuestionsCountInput.value = sharedRandom;
1277
+ randomQuestionsCountInput.disabled = false;
1278
+ }
1279
+
1280
+ if (isLocked) {
1281
+ settingsContainer.classList.add('opacity-50', 'pointer-events-none');
1282
+ teacherTools.classList.add('hidden');
1283
+ }
1284
 
1285
  if (urlParams.has('data')) {
1286
  history.replaceState(null, '', window.location.pathname);
 
1289
  startRangeInput.max = words.length;
1290
  endRangeInput.max = words.length;
1291
  endRangeInput.placeholder = `到 ${words.length}`;
1292
+ updateRandomCountMax();
1293
  showView('menu');
1294
  };
1295
  initializeApp();