1+ ( function ( ) {
2+ const container = document . getElementById ( 'tool-base64' ) ;
3+ if ( ! container ) return ;
4+
5+ const lang = container . getAttribute ( 'data-lang' ) || 'zh-cn' ;
6+
7+ const i18n = {
8+ 'zh-cn' : {
9+ labelInput : '输入内容' ,
10+ labelOutput : '转换结果' ,
11+ placeholderInput : '在此输入需要编码或解码的文本...' ,
12+ btnEncode : '编码 (Encode)' ,
13+ btnDecode : '解码 (Decode)' ,
14+ btnClear : '清空内容' ,
15+ errorInvalid : '错误:无效的 Base64 编码' ,
16+ copyBtn : '复制结果' ,
17+ copied : '已复制'
18+ } ,
19+ 'en' : {
20+ labelInput : 'Input Content' ,
21+ labelOutput : 'Result' ,
22+ placeholderInput : 'Enter text to encode or Base64 to decode...' ,
23+ btnEncode : 'Encode' ,
24+ btnDecode : 'Decode' ,
25+ btnClear : 'Clear' ,
26+ errorInvalid : 'Error: Invalid Base64 string' ,
27+ copyBtn : 'Copy Result' ,
28+ copied : 'Copied'
29+ }
30+ } ;
31+
32+ const t = i18n [ lang ] || i18n [ 'en' ] ;
33+
34+ container . innerHTML = `
35+ <style>
36+ #tool-base64 .tool-container { max-width: 100%; }
37+ #tool-base64 .input-group { margin-bottom: 1.5rem; }
38+ #tool-base64 label { font-weight: bold; font-size: 1.8rem; color: var(--card-text-color-main); display: block; margin-bottom: 0.5rem; }
39+ #tool-base64 textarea {
40+ width: 100%;
41+ padding: 1.2rem;
42+ border: 1px solid var(--border-color);
43+ border-radius: 8px;
44+ background: var(--body-background);
45+ color: var(--card-text-color-main);
46+ font-family: 'Fira Code', monospace;
47+ font-size: 1.4rem;
48+ line-height: 1.6;
49+ resize: vertical;
50+ outline: none;
51+ transition: border-color 0.2s;
52+ }
53+ #tool-base64 textarea:focus { border-color: var(--accent-color); }
54+ #tool-base64 .button-group { display: flex; gap: 1rem; flex-wrap: wrap; margin: 1.5rem 0 2rem 0; }
55+ #tool-base64 .btn {
56+ padding: 1rem 2rem;
57+ border: none;
58+ border-radius: 6px;
59+ cursor: pointer;
60+ font-weight: bold;
61+ font-size: 1.4rem;
62+ transition: all 0.2s;
63+ }
64+ #tool-base64 .btn-primary { background: var(--accent-color); color: #fff; }
65+ #tool-base64 .btn-primary:hover { opacity: 0.9; transform: translateY(-1px); }
66+ #tool-base64 .btn-secondary { background: var(--body-background); border: 1px solid var(--border-color); color: var(--card-text-color-main); }
67+ #tool-base64 .btn-secondary:hover { border-color: var(--accent-color); color: var(--accent-color); }
68+ #tool-base64 .result-group { position: relative; margin-top: 2rem; }
69+ #tool-base64 .btn-copy {
70+ position: absolute;
71+ right: 1rem;
72+ top: 3.5rem;
73+ padding: 0.4rem 1rem;
74+ font-size: 1.2rem;
75+ background: var(--accent-color);
76+ color: #fff;
77+ border-radius: 4px;
78+ border: none;
79+ cursor: pointer;
80+ }
81+ </style>
82+ <div class="tool-container">
83+ <div class="input-group">
84+ <label>${ t . labelInput } </label>
85+ <textarea id="base64-input" rows="8" placeholder="${ t . placeholderInput } "></textarea>
86+ </div>
87+ <div class="button-group">
88+ <button class="btn btn-primary" id="base64-encode">${ t . btnEncode } </button>
89+ <button class="btn btn-primary" id="base64-decode">${ t . btnDecode } </button>
90+ <button class="btn btn-secondary" id="base64-clear">${ t . btnClear } </button>
91+ </div>
92+ <div class="input-group result-group" id="base64-result-group" style="display: none;">
93+ <label>${ t . labelOutput } </label>
94+ <textarea id="base64-output" rows="8" readonly></textarea>
95+ <button class="btn-copy" id="base64-copy">${ t . copyBtn } </button>
96+ </div>
97+ </div>
98+ ` ;
99+
100+ const input = document . getElementById ( 'base64-input' ) ;
101+ const output = document . getElementById ( 'base64-output' ) ;
102+ const resultGroup = document . getElementById ( 'base64-result-group' ) ;
103+ const btnEncode = document . getElementById ( 'base64-encode' ) ;
104+ const btnDecode = document . getElementById ( 'base64-decode' ) ;
105+ const btnClear = document . getElementById ( 'base64-clear' ) ;
106+ const btnCopy = document . getElementById ( 'base64-copy' ) ;
107+
108+ const showResult = ( val ) => {
109+ output . value = val ;
110+ resultGroup . style . display = 'block' ;
111+ setTimeout ( ( ) => {
112+ output . scrollIntoView ( { behavior : 'smooth' , block : 'nearest' } ) ;
113+ } , 50 ) ;
114+ } ;
115+
116+ btnEncode . onclick = ( ) => {
117+ const str = input . value ;
118+ if ( ! str ) return ;
119+ try {
120+ // Support Unicode strings
121+ const encoded = btoa ( encodeURIComponent ( str ) . replace ( / % ( [ 0 - 9 A - F ] { 2 } ) / g, ( match , p1 ) => {
122+ return String . fromCharCode ( '0x' + p1 ) ;
123+ } ) ) ;
124+ showResult ( encoded ) ;
125+ } catch ( e ) {
126+ console . error ( e ) ;
127+ }
128+ } ;
129+
130+ btnDecode . onclick = ( ) => {
131+ const str = input . value . trim ( ) ;
132+ if ( ! str ) return ;
133+ try {
134+ // Support Unicode strings
135+ const decoded = decodeURIComponent ( atob ( str ) . split ( '' ) . map ( ( c ) => {
136+ return '%' + ( '00' + c . charCodeAt ( 0 ) . toString ( 16 ) ) . slice ( - 2 ) ;
137+ } ) . join ( '' ) ) ;
138+ showResult ( decoded ) ;
139+ } catch ( e ) {
140+ alert ( t . errorInvalid ) ;
141+ }
142+ } ;
143+
144+ btnClear . onclick = ( ) => {
145+ input . value = '' ;
146+ output . value = '' ;
147+ resultGroup . style . display = 'none' ;
148+ input . focus ( ) ;
149+ } ;
150+
151+ btnCopy . onclick = ( ) => {
152+ output . select ( ) ;
153+ document . execCommand ( 'copy' ) ;
154+ const originalText = btnCopy . innerText ;
155+ btnCopy . innerText = t . copied ;
156+ setTimeout ( ( ) => { btnCopy . innerText = originalText ; } , 2000 ) ;
157+ } ;
158+ } ) ( ) ;
0 commit comments