載入中...
from js import document, window from pyodide.ffi import create_proxy import random from time import time main_container = document.getElementById('main-container') loading_div = document.getElementById('loading') footer_info = document.getElementById('footer-info') author_name = document.getElementById('author-name') # 切換連結顯示 def toggle_links(event): if footer_info.classList.contains('show'): footer_info.classList.remove('show') else: footer_info.classList.add('show') author_name.addEventListener('click', create_proxy(toggle_links)) # ========== 韓文資料定義 ========== RAW_TEXTS = { 4: "ㄱ ㄲ ㄴ ㄷ ㄸ ㄹ ㅁ ㅂ ㅃ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ", 5: "ㅏ ㅑ ㅓ ㅕ ㅗ ㅛ ㅜ ㅠ ㅡ ㅣ ㅐ ㅒ ㅔ ㅖ ㅘ ㅙ ㅚ ㅝ ㅞ ㅟ ㅢ", 6: "가 거 고 구 그 기 갸 겨 교 규 게 계 나 너 노 누 느 니 냐 녀 뇨 뉴 네 녜 다 더 도 두 드 디 댜 더 됴 듀 데 뎨 라 러 로 루 르 리 랴 려 료 류 레 례 마 머 모 무 므 미 먀 먀 묘 뮤 메 몌 바 버 보 부 브 비 뱌 벼 뵤 뷰 베 볘 사 서 소 수 스 시 샤 셔 쇼 슈 세 셰 아 어 오 우 으 이 야 여 요 유 예 예 자 저 조 주 즈 지 쟈 져 죠 쥬 제 졔 차 처 초 추 츠 치 챠 처 쵸 츄 체 쳬 카 커 코 쿠 크 키 캬 커 쿄 큐 케 켸 타 터 토 투 트 티 탸 터 툐 튜 테 톄 파 퍼 포 푸 프 피 퍄 퍼 표 퓨 페 폐 하 허 호 후 흐 히 햐 혀 효 휴 헤 혜", 7: "개 걔 괘 괴 궤 귀 긔 내 냬 놰 뇌 눼 뉘 늬 대 댸 돼 되 뒈 뒈 듸 래 럐 뢔 뢰 뤠 뤼 릐 매 먜 뫠 뫼 뭬 뮈 믜 배 뱨 봬 뵈 붸 뷔 븨 새 쌔 쇄 쇠 쉐 쉬 싀 애 얘 왜 외 웨 위 의 재 쨰 좨 죄 줴 쥐 즤 채 쳬 쵀 최 췌 츄 츼 캐 컈 쾌 쾨 퀘 퀴 킈 태 턔 퇘 퇴 퉤 튀 틔 패 퍠 퐤 푀 풰 퓌 픠 해 햬 홰 회 훼 휘 희" } BASE_CHINESE = { 'ㄱ': 'k', 'ㄲ': 'g', 'ㄴ': 'n', 'ㄷ': 't', 'ㄸ': 'd', 'ㄹ': 'r', 'ㅁ': 'm', 'ㅂ': 'p', 'ㅃ': 'b', 'ㅅ': 's', 'ㅆ': 's', 'ㅈ': 'chi', 'ㅉ': 'j', 'ㅊ': 'chi', 'ㅋ': 'k', 'ㅌ': 't', 'ㅍ': 'p', 'ㅎ': 'h', 'ㅏ': '阿', 'ㅑ': '呀', 'ㅓ': '喔', 'ㅕ': '呦', 'ㅗ': '偶', 'ㅛ': '呦', 'ㅜ': '嗚', 'ㅠ': 'yu', 'ㅡ': '痾', 'ㅣ': '一', 'ㅐ': '欸', 'ㅒ': '耶', 'ㅔ': '欸', 'ㅖ': '耶', 'ㅘ': '哇', 'ㅙ': '威', 'ㅚ': '威', 'ㅝ': '窩', 'ㅞ': '威', 'ㅟ': 'wi', 'ㅢ': 'ui', '가': 'ga', '거': 'geo', '고': 'go', '구': 'gu', '그': 'geu', '기': 'gi', '갸': 'gya', '겨': 'gyeo', '교': 'gyo', '규': 'gyu', '게': 'ge', '계': 'gye', '나': 'na', '너': 'neo', '노': 'no', '누': 'nu', '느': 'neu', '니': 'ni', '냐': 'nya', '녀': 'nyeo', '뇨': 'nyo', '뉴': 'nyu', '네': 'ne', '녜': 'nye', '다': 'da', '더': 'deo', '도': 'do', '두': 'du', '드': 'deu', '디': 'di', '댜': 'dya', '됴': 'dyo', '듀': 'dyu', '데': 'de', '뎨': 'dye', '라': 'ra', '러': 'reo', '로': 'ro', '루': 'ru', '르': 'reu', '리': 'ri', '랴': 'rya', '려': 'ryeo', '료': 'ryo', '류': 'ryu', '레': 're', '례': 'rye', '마': 'ma', '머': 'meo', '모': 'mo', '무': 'mu', '므': 'meu', '미': 'mi', '먀': 'mya', '묘': 'myo', '뮤': 'myu', '메': 'me', '몌': 'mye', '바': 'ba', '버': 'beo', '보': 'bo', '부': 'bu', '브': 'beu', '비': 'bi', '뱌': 'bya', '벼': 'byeo', '뵤': 'byo', '뷰': 'byu', '베': 'be', '볘': 'bye', '사': 'sa', '서': 'seo', '소': 'so', '수': 'su', '스': 'seu', '시': 'si', '샤': 'sya', '셔': 'syeo', '쇼': 'syo', '슈': 'syu', '세': 'se', '셰': 'sye', '아': 'a', '어': 'eo', '오': 'o', '우': 'u', '으': 'eu', '이': 'i', '야': 'ya', '여': 'yeo', '요': 'yo', '유': 'yu', '예': 'ye', '자': 'ja', '저': 'jeo', '조': 'jo', '주': 'ju', '즈': 'jeu', '지': 'ji', '쟈': 'jya', '져': 'jyeo', '죠': 'jyo', '쥬': 'jyu', '제': 'je', '졔': 'jye', '차': 'cha', '처': 'cheo', '초': 'cho', '추': 'chu', '츠': 'cheu', '치': 'chi', '챠': 'chya', '쳐': 'chyeo', '쵸': 'chyo', '츄': 'chyu', '체': 'che', '쳬': 'chye', '카': 'ka', '커': 'keo', '코': 'ko', '쿠': 'ku', '크': 'keu', '키': 'ki', '캬': 'kya', '쿄': 'kyo', '큐': 'kyu', '케': 'ke', '켸': 'kye', '타': 'ta', '터': 'teo', '토': 'to', '투': 'tu', '트': 'teu', '티': 'ti', '탸': 'tya', '툐': 'tyo', '튜': 'tyu', '테': 'te', '톄': 'tye', '파': 'pa', '퍼': 'peo', '포': 'po', '푸': 'pu', '프': 'peu', '피': 'pi', '퍄': 'pya', '표': 'pyo', '퓨': 'pyu', '페': 'pe', '폐': 'pye', '하': 'ha', '허': 'heo', '호': 'ho', '후': 'hu', '흐': 'heu', '히': 'hi', '햐': 'hya', '혀': 'hyeo', '효': 'hyo', '휴': 'hyu', '헤': 'he', '혜': 'hye', '개': 'gae', '걔': 'gyae', '괘': 'gwae', '괴': 'goe', '궤': 'gwe', '귀': 'gwi', '긔': 'gui', '내': 'nae', '냬': 'nyae', '놰': 'nwae', '뇌': 'noe', '눼': 'nwe', '뉘': 'nwi', '늬': 'nui', '대': 'dae', '댸': 'dyae', '돼': 'dwae', '되': 'doe', '뒈': 'dwe', '듸': 'dui', '래': 'rae', '럐': 'ryae', '뢔': 'rwae', '뢰': 'roe', '뤠': 'rwe', '뤼': 'rwi', '릐': 'rui', '매': 'mae', '먜': 'myae', '뫠': 'mwae', '뫼': 'moe', '뭬': 'mwe', '뮈': 'mwi', '믜': 'mui', '배': 'bae', '뱨': 'byae', '봬': 'bwae', '뵈': 'boe', '붸': 'bwe', '뷔': 'bwi', '븨': 'bui', '새': 'sae', '쌔': 'syae', '쇄': 'swae', '쇠': 'soe', '쉐': 'swe', '쉬': 'swi', '싀': 'sui', '애': 'ae', '얘': 'yae', '왜': 'wae', '외': 'oe', '웨': 'we', '위': 'wi', '의': 'ui', '재': 'jae', '쨰': 'jyae', '좨': 'jwae', '죄': 'joe', '줴': 'jwe', '쥐': 'jwi', '즤': 'jui', '채': 'chae', '쳬': 'chyae', '쵀': 'chwae', '최': 'choe', '췌': 'chwe', '츼': 'chwi', '캐': 'kae', '컈': 'kyae', '쾌': 'kwae', '쾨': 'koe', '퀘': 'kwe', '퀴': 'kwi', '킈': 'kui', '태': 'tae', '턔': 'tyae', '퇘': 'twae', '퇴': 'toe', '퉤': 'twe', '튀': 'twi', '틔': 'tui', '패': 'pae', '퍠': 'pyae', '퐤': 'pwae', '푀': 'poe', '풰': 'pwe', '퓌': 'pwi', '픠': 'pui', '해': 'hae', '햬': 'hyae', '홰': 'hwae', '회': 'hoe', '훼': 'hwe', '휘': 'hwi', '희': 'hui' } BASE_ROMAJI = { 'ㄱ': 'k', 'ㄲ': 'kk', 'ㄴ': 'n', 'ㄷ': 't', 'ㄸ': 'tt', 'ㄹ': 'r', 'ㅁ': 'm', 'ㅂ': 'p', 'ㅃ': 'pp', 'ㅅ': 's', 'ㅆ': 'ss', 'ㅇ': '', 'ㅈ': 'j', 'ㅉ': 'jj', 'ㅊ': 'ch', 'ㅋ': 'kh', 'ㅌ': 'th', 'ㅍ': 'ph', 'ㅎ': 'h', 'ㅏ': 'a', 'ㅑ': 'ya', 'ㅓ': 'eo', 'ㅕ': 'yeo', 'ㅗ': 'o', 'ㅛ': 'yo', 'ㅜ': 'u', 'ㅠ': 'yu', 'ㅡ': 'eu', 'ㅣ': 'i', 'ㅐ': 'ae', 'ㅒ': 'yae', 'ㅔ': 'e', 'ㅖ': 'ye', 'ㅘ': 'wa', 'ㅙ': 'wae', 'ㅚ': 'oe', 'ㅝ': 'wo', 'ㅞ': 'we', 'ㅟ': 'wi', 'ㅢ': 'ui', '가': 'ga', '거': 'geo', '고': 'go', '구': 'gu', '그': 'geu', '기': 'gi', '갸': 'gya', '겨': 'gyeo', '교': 'gyo', '규': 'gyu', '게': 'ge', '계': 'gye', '나': 'na', '너': 'neo', '노': 'no', '누': 'nu', '느': 'neu', '니': 'ni', '냐': 'nya', '녀': 'nyeo', '뇨': 'nyo', '뉴': 'nyu', '네': 'ne', '녜': 'nye', '다': 'da', '더': 'deo', '도': 'do', '두': 'du', '드': 'deu', '디': 'di', '댜': 'dya', '됴': 'dyo', '듀': 'dyu', '데': 'de', '뎨': 'dye', '라': 'ra', '러': 'reo', '로': 'ro', '루': 'ru', '르': 'reu', '리': 'ri', '랴': 'rya', '려': 'ryeo', '료': 'ryo', '류': 'ryu', '레': 're', '례': 'rye', '마': 'ma', '머': 'meo', '모': 'mo', '무': 'mu', '므': 'meu', '미': 'mi', '먀': 'mya', '묘': 'myo', '뮤': 'myu', '메': 'me', '몌': 'mye', '바': 'ba', '버': 'beo', '보': 'bo', '부': 'bu', '브': 'beu', '비': 'bi', '뱌': 'bya', '벼': 'byeo', '뵤': 'byo', '뷰': 'byu', '베': 'be', '볘': 'bye', '사': 'sa', '서': 'seo', '소': 'so', '수': 'su', '스': 'seu', '시': 'si', '샤': 'sya', '셔': 'syeo', '쇼': 'syo', '슈': 'syu', '세': 'se', '셰': 'sye', '아': 'a', '어': 'eo', '오': 'o', '우': 'u', '으': 'eu', '이': 'i', '야': 'ya', '여': 'yeo', '요': 'yo', '유': 'yu', '예': 'ye', '자': 'ja', '저': 'jeo', '조': 'jo', '주': 'ju', '즈': 'jeu', '지': 'ji', '쟈': 'jya', '져': 'jyeo', '죠': 'jyo', '쥬': 'jyu', '제': 'je', '졔': 'jye', '차': 'cha', '처': 'cheo', '초': 'cho', '추': 'chu', '츠': 'cheu', '치': 'chi', '챠': 'chya', '쳐': 'chyeo', '쵸': 'chyo', '츄': 'chyu', '체': 'che', '쳬': 'chye', '카': 'ka', '커': 'keo', '코': 'ko', '쿠': 'ku', '크': 'keu', '키': 'ki', '캬': 'kya', '쿄': 'kyo', '큐': 'kyu', '케': 'ke', '켸': 'kye', '타': 'ta', '터': 'teo', '토': 'to', '투': 'tu', '트': 'teu', '티': 'ti', '탸': 'tya', '툐': 'tyo', '튜': 'tyu', '테': 'te', '톄': 'tye', '파': 'pa', '퍼': 'peo', '포': 'po', '푸': 'pu', '프': 'peu', '피': 'pi', '퍄': 'pya', '표': 'pyo', '퓨': 'pyu', '페': 'pe', '폐': 'pye', '하': 'ha', '허': 'heo', '호': 'ho', '후': 'hu', '흐': 'heu', '히': 'hi', '햐': 'hya', '혀': 'hyeo', '효': 'hyo', '휴': 'hyu', '헤': 'he', '혜': 'hye', '개': 'gae', '걔': 'gyae', '괘': 'gwae', '괴': 'goe', '궤': 'gwe', '귀': 'gwi', '긔': 'gui', '내': 'nae', '냬': 'nyae', '놰': 'nwae', '뇌': 'noe', '눼': 'nwe', '뉘': 'nwi', '늬': 'nui', '대': 'dae', '댸': 'dyae', '돼': 'dwae', '되': 'doe', '뒈': 'dwe', '듸': 'dui', '래': 'rae', '럐': 'ryae', '뢔': 'rwae', '뢰': 'roe', '뤠': 'rwe', '뤼': 'rwi', '릐': 'rui', '매': 'mae', '먜': 'myae', '뫠': 'mwae', '뫼': 'moe', '뭬': 'mwe', '뮈': 'mwi', '믜': 'mui', '배': 'bae', '뱨': 'byae', '봬': 'bwae', '뵈': 'boe', '붸': 'bwe', '뷔': 'bwi', '븨': 'bui', '새': 'sae', '쌔': 'syae', '쇄': 'swae', '쇠': 'soe', '쉐': 'swe', '쉬': 'swi', '싀': 'sui', '애': 'ae', '얘': 'yae', '왜': 'wae', '외': 'oe', '웨': 'we', '위': 'wi', '의': 'ui', '재': 'jae', '쨰': 'jyae', '좨': 'jwae', '죄': 'joe', '줴': 'jwe', '쥐': 'jwi', '즤': 'jui', '채': 'chae', '쳬': 'chyae', '쵀': 'chwae', '최': 'choe', '췌': 'chwe', '츼': 'chwi', '캐': 'kae', '컈': 'kyae', '쾌': 'kwae', '쾨': 'koe', '퀘': 'kwe', '퀴': 'kwi', '킈': 'kui', '태': 'tae', '턔': 'tyae', '퇘': 'twae', '퇴': 'toe', '퉤': 'twe', '튀': 'twi', '틔': 'tui', '패': 'pae', '퍠': 'pyae', '퐤': 'pwae', '푀': 'poe', '풰': 'pwe', '퓌': 'pwi', '픠': 'pui', '해': 'hae', '햬': 'hyae', '홰': 'hwae', '회': 'hoe', '훼': 'hwe', '휘': 'hwi', '희': 'hui', } library_words = {} for key in RAW_TEXTS: library_words[key] = RAW_TEXTS[key].split() def ensure_all_words_covered(): all_words = set() for words in library_words.values(): all_words.update(words) for word in all_words: if word not in BASE_CHINESE: BASE_CHINESE[word] = word if word not in BASE_ROMAJI: BASE_ROMAJI[word] = word ensure_all_words_covered() library_status = {4: True, 5: False, 6: False, 7: False} pronunciation_status = {'chinese': False, 'romaji': False} pronunciation_locked = False last_click_time = 0 current_word = '가' dialogs = {} contents = {} desktop_config = { 1: {'left': '38%', 'top': '10%', 'width': '36%', 'height': '54%', 'font_size': '230px'}, 2: {'left': '75%', 'top': '20%', 'width': '23%', 'height': '18%', 'font_size': '76px'}, 3: {'left': '75%', 'top': '40%', 'width': '23%', 'height': '18%', 'font_size': '76px'}, 4: {'left': '1%', 'top': '83%', 'width': '8%', 'height': '14%', 'icon': '子音', 'icon_size': '24px'}, 5: {'left': '10%', 'top': '83%', 'width': '8%', 'height': '14%', 'icon': '母音', 'icon_size': '24px'}, 6: {'left': '19%', 'top': '83%', 'width': '8%', 'height': '14%', 'icon': '基本字', 'icon_size': '24px'}, 7: {'left': '28%', 'top': '83%', 'width': '8%', 'height': '14%', 'icon': '複合字', 'icon_size': '24px'}, 8: {'left': '59%', 'top': '70%', 'width': '15%', 'height': '27%', 'font_size': '40px'}, 9: {'left': '75%', 'top': '70%', 'width': '15%', 'height': '27%', 'font_size': '40px'} } mobile_config = { 1: {'font_size': '90px', 'icon': None}, 2: {'font_size': '30px', 'icon': None}, 3: {'font_size': '30px', 'icon': None}, 4: {'icon': '子音', 'icon_size': '14px'}, 5: {'icon': '母音', 'icon_size': '14px'}, 6: {'icon': '基本字', 'icon_size': '14px'}, 7: {'icon': '複合字', 'icon_size': '14px'}, 8: {'font_size': '18px'}, 9: {'font_size': '18px'} } def create_dialog(dialog_id, base_config): config = base_config.copy() width = window.innerWidth is_mobile = width <= 567 if is_mobile: if dialog_id in mobile_config: mobile_settings = mobile_config[dialog_id] if 'font_size' in mobile_settings: config['font_size'] = mobile_settings['font_size'] if 'icon' in mobile_settings: config['icon'] = mobile_settings['icon'] if 'icon_size' in mobile_settings: config['icon_size'] = mobile_settings['icon_size'] else: if dialog_id in desktop_config: config.update(desktop_config[dialog_id]) dialog = document.createElement('div') dialog.id = f'dialog-{dialog_id}' dialog.className = f'dialog {config["class"]}' if not is_mobile: if 'left' in config: dialog.style.left = config['left'] if 'top' in config: dialog.style.top = config['top'] if 'width' in config: dialog.style.width = config['width'] if 'height' in config: dialog.style.height = config['height'] if config.get('is_clickable', False): dialog.className += ' clickable' content_div = document.createElement('div') content_div.id = f'content-{dialog_id}' content_div.className = 'display-content' if 'font_size' in config: content_div.style.fontSize = config['font_size'] dialog.appendChild(content_div) if dialog_id in [4, 5, 6, 7]: icon_div = document.createElement('div') icon_div.className = 'library-icon' icon_div.textContent = config['icon'] if 'icon_size' in config: icon_div.style.fontSize = config['icon_size'] content_div.appendChild(icon_div) elif dialog_id == 8: content_div.textContent = '讀音' elif dialog_id == 9: content_div.textContent = '下一個' if dialog_id == 1: speaker_btn = document.createElement('div') speaker_btn.id = 'speaker-btn-red' speaker_btn.className = 'speaker-btn-red' speaker_btn.textContent = '🔊' speaker_btn.title = '朗讀目前顯示的文字' dialog.appendChild(speaker_btn) if is_mobile: create_mobile_layout(dialog, dialog_id) else: main_container.appendChild(dialog) return dialog, content_div def create_mobile_layout(dialog, dialog_id): if dialog_id == 1: main_container.innerHTML = '' main_container.appendChild(dialog) elif dialog_id == 2: if not document.getElementById('pronunciation-row'): row = document.createElement('div') row.id = 'pronunciation-row' row.className = 'pronunciation-row' main_container.appendChild(row) row = document.getElementById('pronunciation-row') row.appendChild(dialog) elif dialog_id == 3: row = document.getElementById('pronunciation-row') if row: row.appendChild(dialog) elif dialog_id in [4, 5, 6, 7]: if not document.getElementById('library-grid'): grid = document.createElement('div') grid.id = 'library-grid' grid.className = 'library-grid' main_container.appendChild(grid) grid = document.getElementById('library-grid') grid.appendChild(dialog) elif dialog_id in [8, 9]: if not document.getElementById('action-row'): row = document.createElement('div') row.id = 'action-row' row.className = 'action-row' main_container.appendChild(row) row = document.getElementById('action-row') row.appendChild(dialog) base_config = { 1: {'class': 'dialog-1', 'is_clickable': False}, 2: {'class': 'dialog-2', 'is_clickable': False}, 3: {'class': 'dialog-3', 'is_clickable': False}, 4: {'class': 'dialog-4', 'is_clickable': True, 'icon': '子音'}, 5: {'class': 'dialog-5', 'is_clickable': True, 'icon': '母音'}, 6: {'class': 'dialog-6', 'is_clickable': True, 'icon': '基本字'}, 7: {'class': 'dialog-7', 'is_clickable': True, 'icon': '複合字'}, 8: {'class': 'dialog-8', 'is_clickable': True}, 9: {'class': 'dialog-9', 'is_clickable': True} } for dialog_id in range(1, 10): dialog, content = create_dialog(dialog_id, base_config[dialog_id]) dialogs[dialog_id] = dialog contents[dialog_id] = content if loading_div: loading_div.style.display = 'none' # ========== 朗讀功能 ========== from js import speechSynthesis, SpeechSynthesisUtterance best_ko_voice = None voices_loaded = False speaker_red_btn = None def init_voices(): global best_ko_voice, voices_loaded voices = speechSynthesis.getVoices() for voice in voices: if 'ko' in voice.lang and not best_ko_voice: best_ko_voice = voice voices_loaded = True if best_ko_voice: print(f"韓文語音: {best_ko_voice.name}") def speak_text(event): global best_ko_voice if not voices_loaded: init_voices() current_word_element = document.getElementById('content-1') if not current_word_element: return text_to_speak = current_word_element.textContent if not text_to_speak: return if speaker_red_btn: speaker_red_btn.classList.add('speaking') utterance = SpeechSynthesisUtterance.new(text_to_speak) utterance.lang = 'ko-KR' utterance.rate = 0.85 utterance.pitch = 1.05 if best_ko_voice: utterance.voice = best_ko_voice def on_end(e): if speaker_red_btn: speaker_red_btn.classList.remove('speaking') def on_error(e): if speaker_red_btn: speaker_red_btn.classList.remove('speaking') utterance.onend = create_proxy(on_end) utterance.onerror = create_proxy(on_error) speechSynthesis.cancel() speechSynthesis.speak(utterance) def on_voices_changed(event=None): init_voices() speechSynthesis.onvoiceschanged = create_proxy(on_voices_changed) init_voices() def bind_speaker_button(): global speaker_red_btn speaker_red_btn = document.getElementById('speaker-btn-red') if speaker_red_btn: speaker_red_btn.addEventListener('click', create_proxy(speak_text)) else: window.setTimeout(create_proxy(bind_speaker_button), 500) bind_speaker_button() def update_library_appearance(): for lib_id in [4, 5, 6, 7]: dialog = dialogs[lib_id] if library_status[lib_id]: dialog.classList.remove('library-off') else: dialog.classList.add('library-off') def update_displays(): contents[1].textContent = current_word show_pronunciation = pronunciation_locked or pronunciation_status['chinese'] or pronunciation_status['romaji'] if show_pronunciation and current_word in BASE_CHINESE: contents[2].textContent = BASE_CHINESE[current_word] else: contents[2].textContent = '' if show_pronunciation and current_word in BASE_ROMAJI: contents[3].textContent = BASE_ROMAJI[current_word] else: contents[3].textContent = '' update_library_appearance() if pronunciation_locked: contents[8].textContent = '鎖定' dialogs[8].classList.add('locked') else: if pronunciation_status['chinese'] or pronunciation_status['romaji']: contents[8].textContent = '隱藏' else: contents[8].textContent = '讀音' dialogs[8].classList.remove('locked') def update_current_word(force=False): global current_word available_words = [] for lib_id in [4, 5, 6, 7]: if library_status[lib_id]: available_words.extend(library_words[lib_id]) if not available_words: current_word = '' return if not force and current_word in available_words: return else: current_word = random.choice(available_words) def on_library_click(lib_id): def handler(event): global library_status library_status[lib_id] = not library_status[lib_id] update_current_word(force=False) update_displays() return handler def on_pronunciation_click(event): global last_click_time, pronunciation_locked, pronunciation_status current_time = time() * 1000 time_diff = current_time - last_click_time if time_diff < 300: pronunciation_locked = not pronunciation_locked if pronunciation_locked: pronunciation_status['chinese'] = True pronunciation_status['romaji'] = True else: pronunciation_status['chinese'] = False pronunciation_status['romaji'] = False else: if not pronunciation_locked: if pronunciation_status['chinese'] or pronunciation_status['romaji']: pronunciation_status['chinese'] = False pronunciation_status['romaji'] = False else: pronunciation_status['chinese'] = True pronunciation_status['romaji'] = True last_click_time = current_time update_displays() def on_next_click(event): global pronunciation_status, pronunciation_locked if not pronunciation_locked: pronunciation_status['chinese'] = False pronunciation_status['romaji'] = False update_current_word(force=True) update_displays() dialogs[4].addEventListener('click', create_proxy(on_library_click(4))) dialogs[5].addEventListener('click', create_proxy(on_library_click(5))) dialogs[6].addEventListener('click', create_proxy(on_library_click(6))) dialogs[7].addEventListener('click', create_proxy(on_library_click(7))) dialogs[8].addEventListener('click', create_proxy(on_pronunciation_click)) dialogs[9].addEventListener('click', create_proxy(on_next_click)) current_word = random.choice(library_words[4]) update_library_appearance() update_displays() print('=' * 50) print('王又贏學韓文四十音 1.6.0 - 音量按鈕在紅色框右下角') print('=' * 50)