@@ -533,7 +533,7 @@ <h2>查碼練習</h2>
533533 // 英文模式下,長按 Shift 顯示的對應字元(全數改為半形)
534534 const keyname_map_english_shifted = {
535535 '1' : '!' , '2' : '@' , '3' : '#' , '4' : '$' , '5' : '%' , '6' : '^' , '7' : '&' , '8' : '*' , '9' : '(' , '0' : ')' ,
536- 'q' : 'Q' , 'w' : 'W' , 'e' : 'E ' , 'r' : 'R' , 't' : 'T' , 'y' : 'Y' , 'u' : 'U' , 'i' : 'I' , 'o' : 'O' , 'p' : 'P' ,
536+ 'q' : 'Q' , 'w' : 'W' , 'e' : 'W ' , 'r' : 'R' , 't' : 'T' , 'y' : 'Y' , 'u' : 'U' , 'i' : 'I' , 'o' : 'O' , 'p' : 'P' ,
537537 'a' : 'A' , 's' : 'S' , 'd' : 'D' , 'f' : 'F' , 'g' : 'G' , 'h' : 'H' , 'j' : 'J' , 'k' : 'K' , 'l' : 'L' ,
538538 ';' : ':' , "'" : '"' ,
539539 'z' : 'Z' , 'x' : 'X' , 'c' : 'C' , 'v' : 'V' , 'b' : 'B' , 'n' : 'N' , 'm' : 'M' ,
@@ -548,7 +548,6 @@ <h2>查碼練習</h2>
548548 // 頁面元素
549549 const practiceDisplayArea = document . getElementById ( 'practice-display-area' ) ;
550550 const mainInput = document . getElementById ( 'main-input' ) ;
551- const candidateDisplay = document . getElementById ( 'candidate-display' ) ;
552551 const phraseInput = document . getElementById ( 'phrase-input' ) ;
553552 const encodingResults = document . getElementById ( 'encoding-results' ) ;
554553 const keyboardContainer = document . querySelector ( '.keyboard-container' ) ;
@@ -559,9 +558,6 @@ <h2>查碼練習</h2>
559558 const SHIFT_HOLD_THRESHOLD = 300 ; // 300毫秒
560559
561560 let currentCode = '' ;
562- let currentCandidates = [ ] ;
563- let currentPage = 0 ;
564- const candidatesPerPage = 10 ;
565561 let isEnglishMode = false ;
566562 let isShiftDown = false ;
567563 let shiftTimer = null ;
@@ -570,7 +566,7 @@ <h2>查碼練習</h2>
570566
571567 // 練習模式專用變數
572568 let practiceChars = [ ] ;
573- let completedChars = [ ] ;
569+ let completedCharsCount = 0 ; // 已完成的字元計數
574570
575571 // 函式:格式化碼表資料為物件
576572 function parseCinData ( data ) {
@@ -653,42 +649,6 @@ <h2>查碼練習</h2>
653649 } ) ;
654650 } ) ;
655651
656- // 函式:在游標位置插入文字
657- function insertTextAtCursor ( element , text ) {
658- const start = element . selectionStart ;
659- const end = element . selectionEnd ;
660- element . value = element . value . substring ( 0 , start ) + text + element . value . substring ( end ) ;
661- element . selectionStart = element . selectionEnd = start + text . length ;
662- element . focus ( ) ;
663- }
664-
665- // 函式:在游標位置刪除文字
666- function deleteTextAtCursor ( element ) {
667- const start = element . selectionStart ;
668- if ( start > 0 ) {
669- const end = element . selectionEnd ;
670- let text = element . value ;
671- element . value = text . substring ( 0 , start - 1 ) + text . substring ( end ) ;
672- element . selectionStart = element . selectionEnd = start - 1 ;
673- element . focus ( ) ;
674- }
675- }
676-
677- // --- 處理長按 Shift 虛擬鍵盤點擊的輔助函數 ---
678- function handleShiftedVirtualInput ( code ) {
679- let outputChar = '' ;
680- if ( isEnglishMode ) {
681- outputChar = keyname_map_english_shifted [ code ] ;
682- } else {
683- outputChar = keyname_map_chinese_shifted [ code ] ;
684- }
685- if ( outputChar ) {
686- // 在練習模式下,不處理直接插入,只處理輸入碼
687- return false ;
688- }
689- return false ;
690- }
691-
692652 // --- 虛擬鍵盤點擊事件 ---
693653 keyboardContainer . addEventListener ( 'click' , ( event ) => {
694654 const key = event . target . closest ( '.key' ) ;
@@ -707,137 +667,71 @@ <h2>查碼練習</h2>
707667 }
708668 return ;
709669 }
710-
711- if ( isLongPressActive ) {
712- if ( handleShiftedVirtualInput ( code ) ) {
713- return ;
714- }
715- }
716-
717- if ( code === '<' ) {
718- handlePageChange ( 'previous' ) ;
719- } else if ( code === '>' ) {
720- handlePageChange ( 'next' ) ;
721- } else if ( code === 'backspace' ) {
722- if ( currentCode . length > 0 ) {
723- currentCode = currentCode . slice ( 0 , - 1 ) ;
724- mainInput . value = formatCode ( currentCode ) ;
725- }
726- return ;
727- } else if ( code === 'enter' ) {
728- return ;
729- }
730-
670+
731671 if ( isEnglishMode ) {
732- // 英文模式下,不處理練習區,直接在 mainInput 輸入
733- // 因為這個網頁的主要功能是中文輸入法練習,此處不做額外處理
734- return ;
735- } else {
736- handleChineseInput ( code ) ;
737- }
738- } ) ;
739-
740- // --- 處理中文模式輸入的核心函數 ---
741- function handleChineseInput ( code ) {
742- // 處理數字鍵選字(在練習模式下無用,但保留)
743- if ( code . match ( / ^ [ 1 - 9 0 ] $ / ) ) {
672+ // 英文模式下,不處理練習區
744673 return ;
745674 }
746-
675+
676+ // 處理功能鍵
747677 if ( code === 'backspace' ) {
748678 if ( currentCode . length > 0 ) {
749679 currentCode = currentCode . slice ( 0 , - 1 ) ;
680+ mainInput . value = formatCode ( currentCode ) ;
750681 }
751- mainInput . value = formatCode ( currentCode ) ;
752- return ;
753- }
754- if ( code === 'enter' ) {
755682 return ;
756- }
757- if ( code === 'escape' ) {
758- resetInputState ( ) ;
683+ } else if ( code === 'enter' ) {
684+ checkAndFillPracticeChar ( ) ;
759685 return ;
760- }
761-
762- if ( code === ' ' ) {
686+ } else if ( code === 'escape' || code === ' ' || code === '<' || code === '>' ) {
687+ // 練習模式下,這些鍵無作用
763688 return ;
764689 }
765690
691+ // 處理編碼輸入
766692 const inputChar = code . toLowerCase ( ) ;
767- if ( inputChar . match ( / ^ [ a - z ; ' . / , ] $ / ) ) {
693+ if ( inputChar . match ( / ^ [ a - z ; ' . / , 0 - 9 ] $ / ) ) {
768694 currentCode += inputChar ;
769695 mainInput . value = formatCode ( currentCode ) ;
770696
771- // 檢查是否達到三碼
697+ // 當輸入達到最大長度時自動檢查
772698 if ( currentCode . length === MAX_CODE_LENGTH ) {
773699 checkAndFillPracticeChar ( ) ;
774700 }
775701 }
776- }
777-
702+ } ) ;
703+
778704 // 檢查並填入練習區的字
779705 function checkAndFillPracticeChar ( ) {
780- const currentPracticeIndex = completedChars . length ;
781- if ( currentPracticeIndex >= practiceChars . length ) {
782- // 練習已完成
706+ if ( completedCharsCount >= practiceChars . length ) {
707+ // 練習已完成,重設狀態
783708 resetInputState ( ) ;
784709 return ;
785710 }
786-
787- const targetChar = practiceChars [ currentPracticeIndex ] ;
711+
712+ const targetChar = practiceChars [ completedCharsCount ] ;
788713 const correctCodes = currentReverseCodemap [ targetChar ] ;
789714
715+ let isCorrect = false ;
790716 if ( correctCodes && correctCodes . some ( item => item . code === currentCode ) ) {
791- // 找到正確編碼,填入字元
792- const charBox = practiceDisplayArea . children [ currentPracticeIndex ] ;
717+ isCorrect = true ;
718+ }
719+
720+ if ( isCorrect ) {
721+ const charBox = practiceDisplayArea . children [ completedCharsCount ] ;
793722 charBox . textContent = targetChar ;
794723 charBox . classList . add ( 'filled' ) ;
795- completedChars . push ( targetChar ) ;
796- } else {
797- // 輸入錯誤
798- // 可在此處加入錯誤提示,例如閃爍或變色
799- // console.log("編碼錯誤!");
724+ completedCharsCount ++ ;
800725 }
801726
802727 // 無論正確與否,都清空輸入碼
803728 resetInputState ( ) ;
804729 }
805730
806- // --- 處理英文模式輸入的新函數 ---
807- function handleEnglishInput ( char ) {
808- // 在練習模式下,英文輸入不應改變練習區
809- return ;
810- }
811-
812- // --- 獨立的翻頁處理函數 ---
813- function handlePageChange ( direction ) {
814- // 在練習模式下,翻頁功能不再處理候選字
815- return ;
816- }
817-
818- // --- 核心狀態更新與顯示函數 (中文模式專用) ---
819- function updateStateAndDisplay ( ) {
820- // 這個函數已不再需要,邏輯已移至 handleChineseInput
821- }
822-
823- // --- 顯示候選字為數字列表 ---
824- function showCandidates ( ) {
825- // 在練習模式下,這個函數已無作用
826- candidateDisplay . innerHTML = '' ;
827- }
828-
829- // --- 選擇候選字並清空輸入 ---
830- function selectCandidate ( index ) {
831- // 在練習模式下,這個函數已無作用
832- }
833-
834731 // --- 清空所有輸入狀態 ---
835732 function resetInputState ( ) {
836733 currentCode = '' ;
837734 mainInput . value = '' ;
838- currentCandidates = [ ] ;
839- currentPage = 0 ;
840- showCandidates ( ) ;
841735 }
842736
843737 // --- 格式化編碼顯示 ---
@@ -921,17 +815,6 @@ <h2>查碼練習</h2>
921815 }
922816 return ;
923817 }
924-
925- // 在中文模式下,處理長按 Shift 後的符號鍵
926- if ( ! isEnglishMode && event . shiftKey ) {
927- const code = getKeyCode ( event ) ;
928- const outputChar = keyname_map_chinese_shifted [ code ] ;
929- if ( outputChar ) {
930- event . preventDefault ( ) ;
931- // 在練習模式下,不處理符號輸入
932- return ;
933- }
934- }
935818
936819 // 處理實體鍵盤的刪除鍵
937820 if ( event . key === 'Backspace' ) {
@@ -950,8 +833,15 @@ <h2>查碼練習</h2>
950833 if ( isEnglishMode ) {
951834 // 英文模式下不處理練習
952835 return ;
836+ }
837+ if ( code === 'enter' ) {
838+ checkAndFillPracticeChar ( ) ;
953839 } else {
954- handleChineseInput ( code ) ;
840+ currentCode += code . toLowerCase ( ) ;
841+ mainInput . value = formatCode ( currentCode ) ;
842+ if ( currentCode . length === MAX_CODE_LENGTH ) {
843+ checkAndFillPracticeChar ( ) ;
844+ }
955845 }
956846 }
957847 } ) ;
@@ -989,10 +879,13 @@ <h2>查碼練習</h2>
989879 const textToSearch = phraseInput . value . trim ( ) ;
990880 const characters = textToSearch . split ( '' ) ;
991881
882+ // 過濾掉碼表中不存在的字元
883+ const filteredChars = characters . filter ( char => currentReverseCodemap [ char ] !== undefined ) ;
884+
992885 // 更新練習區
993886 practiceDisplayArea . innerHTML = '' ;
994- practiceChars = characters . slice ( 0 , 10 ) ;
995- completedChars = [ ] ; // 重置已完成的字元
887+ practiceChars = filteredChars . slice ( 0 , 10 ) ;
888+ completedCharsCount = 0 ; // 重置已完成的字元
996889 for ( let i = 0 ; i < practiceChars . length ; i ++ ) {
997890 const charBox = document . createElement ( 'div' ) ;
998891 charBox . className = 'practice-char-box' ;
@@ -1004,7 +897,8 @@ <h2>查碼練習</h2>
1004897 charBox . className = 'practice-char-box' ;
1005898 practiceDisplayArea . appendChild ( charBox ) ;
1006899 }
1007-
900+ resetInputState ( ) ;
901+
1008902 // 更新查碼區
1009903 encodingResults . innerHTML = '' ;
1010904 for ( let i = 0 ; i < 10 ; i ++ ) {
@@ -1020,8 +914,8 @@ <h2>查碼練習</h2>
1020914 encodingContainer . style . width = '100%' ;
1021915
1022916 const codesToDisplay = [ ] ;
1023- if ( i < characters . length ) {
1024- const char = characters [ i ] ;
917+ if ( i < filteredChars . length ) {
918+ const char = filteredChars [ i ] ;
1025919 charLabel . textContent = char ;
1026920 const results = currentReverseCodemap [ char ] ;
1027921 if ( results && results . length > 0 ) {
@@ -1059,7 +953,7 @@ <h2>查碼練習</h2>
1059953 encodingContainer . appendChild ( codeLabel ) ;
1060954 }
1061955
1062- if ( i >= characters . length ) {
956+ if ( i >= filteredChars . length ) {
1063957 charLabel . textContent = '' ;
1064958 }
1065959
0 commit comments