/** * AlaskahQR - QR Code Generator * Pure JavaScript implementation with custom canvas drawing */ const QRState = { text: 'https://qr.alaskah.com', dotStyle: 'square', eyeStyle: 'square', fgColor: '#000000', bgColor: '#ffffff', gradientType: 'none', gradientColor1: '#4F46E5', gradientColor2: '#7C3AED', logoImage: null, logoSize: 20, logoPadding: 8, logoRadius: 10, errorLevel: 'M', margin: 20, downloadSize: 512, }; let renderTimer = null; const QR_UI_I18N = { ko: { empty: '내용을 입력하면 QR코드가 생성됩니다', failed: '생성 실패: 내용을 확인해주세요', infoModules: '모듈 수', infoError: '오류 수정', infoChars: '문자 수', copyOk: '클립보드에 복사되었습니다!', copyFail: '복사 실패: 브라우저가 지원하지 않습니다', uiOptions: '⚙️ 옵션 설정', uiTabInput: '내용', uiTabShape: '모양', uiTabColor: '색상', uiTabLogo: '로고', uiContentType: '콘텐츠 유형', uiTypeUrl: 'URL / 웹사이트', uiTypeText: '텍스트', uiTypeEmail: '이메일', uiTypePhone: '전화번호', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: '연락처 (vCard)', uiUrlAddress: 'URL 주소', uiTextContent: '텍스트 내용', uiTextPlaceholder: 'QR코드에 담을 내용을 입력하세요', uiEmailAddress: '이메일 주소', uiSubjectOptional: '제목 (선택)', uiBodyOptional: '본문 (선택)', uiEmailSubjectPlaceholder: '메일 제목', uiEmailBodyPlaceholder: '메일 내용', uiPhoneNumber: '전화번호', uiMessageContent: '메시지 내용', uiSmsPlaceholder: '전송할 메시지', uiWifiSsid: '네트워크 이름 (SSID)', uiWifiNamePlaceholder: 'Wi-Fi 이름', uiPassword: '비밀번호', uiWifiPasswordPlaceholder: 'Wi-Fi 비밀번호', uiSecurityType: '보안 유형', uiSecurityNone: '없음', uiName: '이름', uiCompany: '회사', uiWebsite: '웹사이트', uiErrorLevel: '오류 수정 수준', uiErrorLevelNote: '로고 추가 시 자동으로 H로 설정됩니다', uiEcL: 'L - 낮음 (7%)', uiEcM: 'M - 중간 (15%)', uiEcQ: 'Q - 높음 (25%)', uiEcH: 'H - 최고 (30%)', uiPreview: '👁️ 미리보기', uiDownloadSize: '다운로드 크기', uiDownloadPng: '⬇️ PNG 다운로드', uiCopy: '📋 클립보드 복사', uiAd: '광고 영역 (AdSense)', uiHowtoTitle: 'QR 코드 활용 방법', uiHow1Title: '웹사이트 링크', uiHow1Desc: '명함, 전단지, 포스터에 QR코드를 삽입해 웹사이트로 쉽게 연결하세요.', uiHow2Title: 'Wi-Fi 공유', uiHow2Desc: '카페나 사무실에서 Wi-Fi 비밀번호를 QR코드로 간편하게 공유하세요.', uiHow3Title: '연락처 공유', uiHow3Desc: '명함 대신 QR코드로 연락처를 공유하세요. 스캔 한 번으로 저장됩니다.', uiHow4Title: '제품 정보', uiHow4Desc: '제품 패키지에 QR코드를 추가해 상세 정보나 사용 설명서로 연결하세요.', uiDotStyle: '데이터 점 모양', uiEyeStyle: '눈(Eye) 모양', uiMarginSize: '여백 크기', uiDotSquare: '사각형', uiDotRounded: '둥근', uiDotExtraRounded: '더 둥근', uiDotCircle: '원형', uiDotDiamond: '다이아몬드', uiEyeSquare: '사각형', uiEyeRounded: '둥근', uiEyeCircle: '원형', uiFgColorLabel: '전경색 (QR 색상)', uiFgColorHelp: 'QR 점 색상', uiBgColorLabel: '배경색', uiBgColorHelp: '배경 색상', uiGradientLabel: '그라데이션', uiGradNone: '없음', uiGradLinear: '선형', uiGradRadial: '방사형', uiGradStart: '시작 색상', uiGradEnd: '끝 색상', uiColorPresetLabel: '빠른 색상 프리셋', uiPresetClassic: '클래식', uiPresetBlue: '블루', uiPresetGreen: '그린', uiPresetAmber: '에임버', uiPresetPurple: '퍼플', uiPresetRose: '로즈', uiLogoLabel: '가운데 로고/아이콘', uiLogoAutoNote: '로고 추가 시 오류 수정이 H로 자동 설정됩니다', uiUploadClick: '클릭하여 이미지 선택', uiUploadFormats: 'PNG, JPG, SVG, WebP 지원', uiUploadDone: '✓ 로고가 업로드되었습니다', uiRemove: '제거', uiLogoSizeLabel: '로고 크기', uiLogoPaddingLabel: '로고 배경 여백', uiLogoRadiusLabel: '로고 배경 모서리', uiTipStrong: '💡', uiTipLabel: '팁:', uiTipDesc: '로고 추가 시 QR코드의 오류 수정 수준이 자동으로 H(30%)로 설정됩니다. 로고가 가운데 약 20% 영역을 가리더라도 QR코드가 정상 인식됩니다.', uiSupportTitle: '마음에 드셨다면 후원으로 응원해주세요', uiSupportDesc: '무료 운영에 도움이 됩니다. 카카오페이와 토스로 간편하게 후원할 수 있습니다.', uiSupportButton: '☕ 후원하기', }, en: { empty: 'Enter content to generate your QR code', failed: 'Generation failed: please check your content', infoModules: 'Modules', infoError: 'Error Correction', infoChars: 'Characters', copyOk: 'Copied to clipboard!', copyFail: 'Copy failed: browser not supported', uiOptions: '⚙️ Options', uiTabInput: 'Content', uiTabShape: 'Shape', uiTabColor: 'Color', uiTabLogo: 'Logo', uiContentType: 'Content Type', uiTypeUrl: 'URL / Website', uiTypeText: 'Text', uiTypeEmail: 'Email', uiTypePhone: 'Phone Number', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: 'Contact (vCard)', uiUrlAddress: 'URL Address', uiTextContent: 'Text Content', uiTextPlaceholder: 'Enter the text to include in your QR code', uiEmailAddress: 'Email Address', uiSubjectOptional: 'Subject (Optional)', uiBodyOptional: 'Body (Optional)', uiEmailSubjectPlaceholder: 'Email subject', uiEmailBodyPlaceholder: 'Email body', uiPhoneNumber: 'Phone Number', uiMessageContent: 'Message', uiSmsPlaceholder: 'Message to send', uiWifiSsid: 'Network Name (SSID)', uiWifiNamePlaceholder: 'Wi-Fi name', uiPassword: 'Password', uiWifiPasswordPlaceholder: 'Wi-Fi password', uiSecurityType: 'Security Type', uiSecurityNone: 'None', uiName: 'Name', uiCompany: 'Company', uiWebsite: 'Website', uiErrorLevel: 'Error Correction Level', uiErrorLevelNote: 'Automatically set to H when a logo is added', uiEcL: 'L - Low (7%)', uiEcM: 'M - Medium (15%)', uiEcQ: 'Q - High (25%)', uiEcH: 'H - Best (30%)', uiPreview: '👁️ Preview', uiDownloadSize: 'Download Size', uiDownloadPng: '⬇️ Download PNG', uiCopy: '📋 Copy to Clipboard', uiAd: 'Ad Area (AdSense)', uiHowtoTitle: 'How to Use QR Codes', uiHow1Title: 'Website Link', uiHow1Desc: 'Insert QR codes into business cards, flyers, and posters to link to your website easily.', uiHow2Title: 'Wi-Fi Sharing', uiHow2Desc: 'Share Wi-Fi passwords quickly in cafes or offices using a QR code.', uiHow3Title: 'Contact Sharing', uiHow3Desc: 'Share contacts with a QR code instead of business cards. Save with one scan.', uiHow4Title: 'Product Information', uiHow4Desc: 'Add QR codes to product packaging to link to details or user guides.', uiDotStyle: 'Dot Style', uiEyeStyle: 'Eye Style', uiMarginSize: 'Margin Size', uiDotSquare: 'Square', uiDotRounded: 'Rounded', uiDotExtraRounded: 'Extra Rounded', uiDotCircle: 'Circle', uiDotDiamond: 'Diamond', uiEyeSquare: 'Square', uiEyeRounded: 'Rounded', uiEyeCircle: 'Circle', uiFgColorLabel: 'Foreground Color (QR Color)', uiFgColorHelp: 'QR dot color', uiBgColorLabel: 'Background Color', uiBgColorHelp: 'Background color', uiGradientLabel: 'Gradient', uiGradNone: 'None', uiGradLinear: 'Linear', uiGradRadial: 'Radial', uiGradStart: 'Start Color', uiGradEnd: 'End Color', uiColorPresetLabel: 'Quick Color Presets', uiPresetClassic: 'Classic', uiPresetBlue: 'Blue', uiPresetGreen: 'Green', uiPresetAmber: 'Amber', uiPresetPurple: 'Purple', uiPresetRose: 'Rose', uiLogoLabel: 'Center Logo/Icon', uiLogoAutoNote: 'Error correction is automatically set to H when a logo is added', uiUploadClick: 'Click to select an image', uiUploadFormats: 'PNG, JPG, SVG, WebP supported', uiUploadDone: '✓ Logo uploaded', uiRemove: 'Remove', uiLogoSizeLabel: 'Logo Size', uiLogoPaddingLabel: 'Logo Background Padding', uiLogoRadiusLabel: 'Logo Background Radius', uiTipStrong: '💡', uiTipLabel: 'Tip:', uiTipDesc: 'When a logo is added, error correction is automatically set to H (30%). Even if the center area is covered by about 20%, the QR code can still be scanned.', uiSupportTitle: 'If this helped, consider supporting the project', uiSupportDesc: 'Your support helps keep AlaskahQR free. You can donate easily with KakaoPay or Toss.', uiSupportButton: '☕ Support', }, ja: { empty: '内容を入力するとQRコードが生成されます', failed: '生成に失敗しました: 内容を確認してください', infoModules: 'モジュール数', infoError: '誤り訂正', infoChars: '文字数', copyOk: 'クリップボードにコピーしました', copyFail: 'コピーに失敗しました: ブラウザ未対応', uiOptions: '⚙️ オプション', uiTabInput: '内容', uiTabShape: '形', uiTabColor: '色', uiTabLogo: 'ロゴ', uiContentType: 'コンテンツタイプ', uiTypeUrl: 'URL / ウェブサイト', uiTypeText: 'テキスト', uiTypeEmail: 'メール', uiTypePhone: '電話番号', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: '連絡先 (vCard)', uiUrlAddress: 'URLアドレス', uiTextContent: 'テキスト内容', uiTextPlaceholder: 'QRコードに入れる内容を入力してください', uiEmailAddress: 'メールアドレス', uiSubjectOptional: '件名 (任意)', uiBodyOptional: '本文 (任意)', uiEmailSubjectPlaceholder: 'メール件名', uiEmailBodyPlaceholder: 'メール本文', uiPhoneNumber: '電話番号', uiMessageContent: 'メッセージ内容', uiSmsPlaceholder: '送信するメッセージ', uiWifiSsid: 'ネットワーク名 (SSID)', uiWifiNamePlaceholder: 'Wi-Fi名', uiPassword: 'パスワード', uiWifiPasswordPlaceholder: 'Wi-Fiパスワード', uiSecurityType: 'セキュリティ種類', uiSecurityNone: 'なし', uiName: '名前', uiCompany: '会社', uiWebsite: 'ウェブサイト', uiErrorLevel: '誤り訂正レベル', uiErrorLevelNote: 'ロゴ追加時は自動でHに設定されます', uiEcL: 'L - 低 (7%)', uiEcM: 'M - 中 (15%)', uiEcQ: 'Q - 高 (25%)', uiEcH: 'H - 最高 (30%)', uiPreview: '👁️ プレビュー', uiDownloadSize: 'ダウンロードサイズ', uiDownloadPng: '⬇️ PNGをダウンロード', uiCopy: '📋 クリップボードにコピー', uiAd: '広告エリア (AdSense)', uiHowtoTitle: 'QRコードの活用方法', uiHow1Title: 'ウェブサイトリンク', uiHow1Desc: '名刺、チラシ、ポスターにQRコードを載せて、サイトへ簡単に誘導できます。', uiHow2Title: 'Wi-Fi共有', uiHow2Desc: 'カフェやオフィスでWi-FiパスワードをQRコードで手軽に共有できます。', uiHow3Title: '連絡先共有', uiHow3Desc: '名刺の代わりにQRコードで連絡先を共有できます。1回のスキャンで保存できます。', uiHow4Title: '製品情報', uiHow4Desc: '製品パッケージにQRコードを追加して、詳細情報やガイドに接続できます。', uiDotStyle: 'ドットスタイル', uiEyeStyle: 'アイスタイル', uiMarginSize: '余白サイズ', uiDotSquare: '四角', uiDotRounded: '角丸', uiDotExtraRounded: 'より丸い', uiDotCircle: '円', uiDotDiamond: 'ダイヤモンド', uiEyeSquare: '四角', uiEyeRounded: '角丸', uiEyeCircle: '円', uiFgColorLabel: '前景色 (QR色)', uiFgColorHelp: 'QRドット色', uiBgColorLabel: '背景色', uiBgColorHelp: '背景色', uiGradientLabel: 'グラデーション', uiGradNone: 'なし', uiGradLinear: '線形', uiGradRadial: '放射状', uiGradStart: '開始色', uiGradEnd: '終了色', uiColorPresetLabel: 'クイックカラープリセット', uiPresetClassic: 'クラシック', uiPresetBlue: 'ブルー', uiPresetGreen: 'グリーン', uiPresetAmber: 'アンバー', uiPresetPurple: 'パープル', uiPresetRose: 'ローズ', uiLogoLabel: '中央ロゴ/アイコン', uiLogoAutoNote: 'ロゴ追加時、誤り訂正は自動でHに設定されます', uiUploadClick: 'クリックして画像を選択', uiUploadFormats: 'PNG, JPG, SVG, WebP 対応', uiUploadDone: '✓ ロゴがアップロードされました', uiRemove: '削除', uiLogoSizeLabel: 'ロゴサイズ', uiLogoPaddingLabel: 'ロゴ背景余白', uiLogoRadiusLabel: 'ロゴ背景角丸', uiTipStrong: '💡', uiTipLabel: 'ヒント:', uiTipDesc: 'ロゴを追加すると誤り訂正レベルは自動的にH(30%)になります。中央の約20%が隠れても読み取り可能です。', }, zh: { empty: '输入内容后将自动生成二维码', failed: '生成失败: 请检查输入内容', infoModules: '模块数', infoError: '纠错级别', infoChars: '字符数', copyOk: '已复制到剪贴板', copyFail: '复制失败: 浏览器不支持', uiOptions: '⚙️ 选项设置', uiTabInput: '内容', uiTabShape: '形状', uiTabColor: '颜色', uiTabLogo: '标志', uiContentType: '内容类型', uiTypeUrl: 'URL / 网站', uiTypeText: '文本', uiTypeEmail: '电子邮箱', uiTypePhone: '电话号码', uiTypeSms: '短信', uiTypeWifi: 'Wi-Fi', uiTypeVcard: '联系人 (vCard)', uiUrlAddress: 'URL地址', uiTextContent: '文本内容', uiTextPlaceholder: '请输入要写入二维码的内容', uiEmailAddress: '电子邮箱地址', uiSubjectOptional: '主题 (可选)', uiBodyOptional: '正文 (可选)', uiEmailSubjectPlaceholder: '邮件主题', uiEmailBodyPlaceholder: '邮件正文', uiPhoneNumber: '电话号码', uiMessageContent: '消息内容', uiSmsPlaceholder: '要发送的消息', uiWifiSsid: '网络名称 (SSID)', uiWifiNamePlaceholder: 'Wi-Fi 名称', uiPassword: '密码', uiWifiPasswordPlaceholder: 'Wi-Fi 密码', uiSecurityType: '安全类型', uiSecurityNone: '无', uiName: '姓名', uiCompany: '公司', uiWebsite: '网站', uiErrorLevel: '纠错级别', uiErrorLevelNote: '添加Logo时将自动设置为H', uiEcL: 'L - 低 (7%)', uiEcM: 'M - 中 (15%)', uiEcQ: 'Q - 高 (25%)', uiEcH: 'H - 最高 (30%)', uiPreview: '👁️ 预览', uiDownloadSize: '下载尺寸', uiDownloadPng: '⬇️ 下载 PNG', uiCopy: '📋 复制到剪贴板', uiAd: '广告区域 (AdSense)', uiHowtoTitle: '二维码使用方式', uiHow1Title: '网站链接', uiHow1Desc: '将二维码放在名片、传单和海报上,轻松跳转到网站。', uiHow2Title: 'Wi-Fi 共享', uiHow2Desc: '在咖啡店或办公室用二维码快速共享 Wi-Fi 密码。', uiHow3Title: '联系人共享', uiHow3Desc: '用二维码代替名片共享联系人,扫码即可保存。', uiHow4Title: '产品信息', uiHow4Desc: '在产品包装上添加二维码,连接到详细信息或使用说明。', uiDotStyle: '点样式', uiEyeStyle: '定位点样式', uiMarginSize: '留白大小', uiDotSquare: '方形', uiDotRounded: '圆角', uiDotExtraRounded: '更圆', uiDotCircle: '圆形', uiDotDiamond: '菱形', uiEyeSquare: '方形', uiEyeRounded: '圆角', uiEyeCircle: '圆形', uiFgColorLabel: '前景色 (二维码颜色)', uiFgColorHelp: '二维码点颜色', uiBgColorLabel: '背景色', uiBgColorHelp: '背景颜色', uiGradientLabel: '渐变', uiGradNone: '无', uiGradLinear: '线性', uiGradRadial: '径向', uiGradStart: '起始颜色', uiGradEnd: '结束颜色', uiColorPresetLabel: '快速颜色预设', uiPresetClassic: '经典', uiPresetBlue: '蓝色', uiPresetGreen: '绿色', uiPresetAmber: '琥珀', uiPresetPurple: '紫色', uiPresetRose: '玫瑰', uiLogoLabel: '中间标志/图标', uiLogoAutoNote: '添加标志后纠错级别会自动设为 H', uiUploadClick: '点击选择图片', uiUploadFormats: '支持 PNG, JPG, SVG, WebP', uiUploadDone: '✓ 标志已上传', uiRemove: '移除', uiLogoSizeLabel: '标志大小', uiLogoPaddingLabel: '标志背景内边距', uiLogoRadiusLabel: '标志背景圆角', uiTipStrong: '💡', uiTipLabel: '提示:', uiTipDesc: '添加标志后,纠错级别会自动设置为 H(30%)。即使中心约20%被遮挡,二维码通常仍可识别。', }, es: { empty: 'Introduce contenido para generar tu codigo QR', failed: 'Error al generar: revisa el contenido', infoModules: 'Modulos', infoError: 'Correccion de errores', infoChars: 'Caracteres', copyOk: 'Copiado al portapapeles', copyFail: 'Error al copiar: navegador no compatible', uiOptions: '⚙️ Opciones', uiTabInput: 'Contenido', uiTabShape: 'Forma', uiTabColor: 'Color', uiTabLogo: 'Logo', uiContentType: 'Tipo de contenido', uiTypeUrl: 'URL / Sitio web', uiTypeText: 'Texto', uiTypeEmail: 'Correo', uiTypePhone: 'Numero de telefono', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: 'Contacto (vCard)', uiUrlAddress: 'Direccion URL', uiTextContent: 'Contenido de texto', uiTextPlaceholder: 'Ingresa el contenido para el codigo QR', uiEmailAddress: 'Correo electronico', uiSubjectOptional: 'Asunto (Opcional)', uiBodyOptional: 'Cuerpo (Opcional)', uiEmailSubjectPlaceholder: 'Asunto del correo', uiEmailBodyPlaceholder: 'Contenido del correo', uiPhoneNumber: 'Numero de telefono', uiMessageContent: 'Mensaje', uiSmsPlaceholder: 'Mensaje a enviar', uiWifiSsid: 'Nombre de red (SSID)', uiWifiNamePlaceholder: 'Nombre de Wi-Fi', uiPassword: 'Contrasena', uiWifiPasswordPlaceholder: 'Contrasena de Wi-Fi', uiSecurityType: 'Tipo de seguridad', uiSecurityNone: 'Ninguno', uiName: 'Nombre', uiCompany: 'Empresa', uiWebsite: 'Sitio web', uiErrorLevel: 'Nivel de correccion de errores', uiErrorLevelNote: 'Se establece automaticamente en H al agregar un logo', uiEcL: 'L - Bajo (7%)', uiEcM: 'M - Medio (15%)', uiEcQ: 'Q - Alto (25%)', uiEcH: 'H - Maximo (30%)', uiPreview: '👁️ Vista previa', uiDownloadSize: 'Tamano de descarga', uiDownloadPng: '⬇️ Descargar PNG', uiCopy: '📋 Copiar al portapapeles', uiAd: 'Area de anuncios (AdSense)', uiHowtoTitle: 'Como usar codigos QR', uiHow1Title: 'Enlace web', uiHow1Desc: 'Inserta codigos QR en tarjetas, folletos y posters para dirigir facilmente a tu sitio web.', uiHow2Title: 'Compartir Wi-Fi', uiHow2Desc: 'Comparte rapidamente la contrasena Wi-Fi en cafeterias u oficinas con un codigo QR.', uiHow3Title: 'Compartir contacto', uiHow3Desc: 'Comparte contactos con un codigo QR en lugar de tarjetas. Se guarda con un escaneo.', uiHow4Title: 'Informacion del producto', uiHow4Desc: 'Agrega codigos QR al empaque para enlazar detalles o guias de uso.', uiDotStyle: 'Estilo de punto', uiEyeStyle: 'Estilo de ojo', uiMarginSize: 'Tamano de margen', uiDotSquare: 'Cuadrado', uiDotRounded: 'Redondeado', uiDotExtraRounded: 'Muy redondeado', uiDotCircle: 'Circulo', uiDotDiamond: 'Diamante', uiEyeSquare: 'Cuadrado', uiEyeRounded: 'Redondeado', uiEyeCircle: 'Circulo', uiFgColorLabel: 'Color frontal (QR)', uiFgColorHelp: 'Color de los puntos QR', uiBgColorLabel: 'Color de fondo', uiBgColorHelp: 'Color de fondo', uiGradientLabel: 'Degradado', uiGradNone: 'Ninguno', uiGradLinear: 'Lineal', uiGradRadial: 'Radial', uiGradStart: 'Color inicial', uiGradEnd: 'Color final', uiColorPresetLabel: 'Preajustes rapidos de color', uiPresetClassic: 'Clasico', uiPresetBlue: 'Azul', uiPresetGreen: 'Verde', uiPresetAmber: 'Ambar', uiPresetPurple: 'Purpura', uiPresetRose: 'Rosa', uiLogoLabel: 'Logo/Icono central', uiLogoAutoNote: 'La correccion se establece automaticamente en H al agregar un logo', uiUploadClick: 'Haz clic para seleccionar una imagen', uiUploadFormats: 'Compatible con PNG, JPG, SVG, WebP', uiUploadDone: '✓ Logo subido', uiRemove: 'Eliminar', uiLogoSizeLabel: 'Tamano del logo', uiLogoPaddingLabel: 'Relleno del fondo del logo', uiLogoRadiusLabel: 'Radio del fondo del logo', uiTipStrong: '💡', uiTipLabel: 'Consejo:', uiTipDesc: 'Cuando agregas un logo, la correccion de errores se ajusta automaticamente a H(30%). Incluso si se cubre cerca del 20% del centro, el QR suele seguir siendo legible.', }, fr: { empty: 'Saisissez du contenu pour generer votre code QR', failed: 'Echec de generation : verifiez le contenu', infoModules: 'Modules', infoError: 'Correction d erreurs', infoChars: 'Caracteres', copyOk: 'Copie dans le presse-papiers', copyFail: 'Echec de copie : navigateur non pris en charge', uiOptions: '⚙️ Options', uiTabInput: 'Contenu', uiTabShape: 'Forme', uiTabColor: 'Couleur', uiTabLogo: 'Logo', uiContentType: 'Type de contenu', uiTypeUrl: 'URL / Site web', uiTypeText: 'Texte', uiTypeEmail: 'E-mail', uiTypePhone: 'Numero de telephone', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: 'Contact (vCard)', uiUrlAddress: 'Adresse URL', uiTextContent: 'Contenu texte', uiTextPlaceholder: 'Saisissez le contenu du code QR', uiEmailAddress: 'Adresse e-mail', uiSubjectOptional: 'Objet (Optionnel)', uiBodyOptional: 'Message (Optionnel)', uiEmailSubjectPlaceholder: 'Objet de l e-mail', uiEmailBodyPlaceholder: 'Contenu de l e-mail', uiPhoneNumber: 'Numero de telephone', uiMessageContent: 'Message', uiSmsPlaceholder: 'Message a envoyer', uiWifiSsid: 'Nom du reseau (SSID)', uiWifiNamePlaceholder: 'Nom du Wi-Fi', uiPassword: 'Mot de passe', uiWifiPasswordPlaceholder: 'Mot de passe Wi-Fi', uiSecurityType: 'Type de securite', uiSecurityNone: 'Aucun', uiName: 'Nom', uiCompany: 'Entreprise', uiWebsite: 'Site web', uiErrorLevel: 'Niveau de correction d erreurs', uiErrorLevelNote: 'Defini automatiquement sur H lors de l ajout d un logo', uiEcL: 'L - Faible (7%)', uiEcM: 'M - Moyen (15%)', uiEcQ: 'Q - Eleve (25%)', uiEcH: 'H - Maximum (30%)', uiPreview: '👁️ Apercu', uiDownloadSize: 'Taille de telechargement', uiDownloadPng: '⬇️ Telecharger PNG', uiCopy: '📋 Copier dans le presse-papiers', uiAd: 'Zone publicitaire (AdSense)', uiHowtoTitle: 'Comment utiliser les QR codes', uiHow1Title: 'Lien vers un site web', uiHow1Desc: 'Ajoutez des QR codes sur cartes, flyers et affiches pour diriger facilement vers votre site.', uiHow2Title: 'Partage Wi-Fi', uiHow2Desc: 'Partagez rapidement le mot de passe Wi-Fi dans un cafe ou un bureau via un QR code.', uiHow3Title: 'Partage de contact', uiHow3Desc: 'Partagez vos contacts avec un QR code au lieu d une carte de visite. Un scan suffit.', uiHow4Title: 'Informations produit', uiHow4Desc: 'Ajoutez un QR code sur l emballage pour ouvrir les details ou le guide d utilisation.', uiDotStyle: 'Style des points', uiEyeStyle: 'Style des yeux', uiMarginSize: 'Taille de marge', uiDotSquare: 'Carre', uiDotRounded: 'Arrondi', uiDotExtraRounded: 'Tres arrondi', uiDotCircle: 'Cercle', uiDotDiamond: 'Diamant', uiEyeSquare: 'Carre', uiEyeRounded: 'Arrondi', uiEyeCircle: 'Cercle', uiFgColorLabel: 'Couleur du premier plan (QR)', uiFgColorHelp: 'Couleur des points QR', uiBgColorLabel: 'Couleur d arriere-plan', uiBgColorHelp: 'Couleur de fond', uiGradientLabel: 'Degrade', uiGradNone: 'Aucun', uiGradLinear: 'Lineaire', uiGradRadial: 'Radial', uiGradStart: 'Couleur de debut', uiGradEnd: 'Couleur de fin', uiColorPresetLabel: 'Preselections de couleurs rapides', uiPresetClassic: 'Classique', uiPresetBlue: 'Bleu', uiPresetGreen: 'Vert', uiPresetAmber: 'Ambre', uiPresetPurple: 'Violet', uiPresetRose: 'Rose', uiLogoLabel: 'Logo/Icone central(e)', uiLogoAutoNote: 'La correction est automatiquement reglee sur H lors de l ajout d un logo', uiUploadClick: 'Cliquez pour choisir une image', uiUploadFormats: 'PNG, JPG, SVG, WebP pris en charge', uiUploadDone: '✓ Logo televerse', uiRemove: 'Supprimer', uiLogoSizeLabel: 'Taille du logo', uiLogoPaddingLabel: 'Marge de fond du logo', uiLogoRadiusLabel: 'Rayon du fond du logo', uiTipStrong: '💡', uiTipLabel: 'Conseil:', uiTipDesc: 'Quand vous ajoutez un logo, la correction d erreurs passe automatiquement a H(30%). Meme si environ 20% de la zone centrale est couverte, le QR reste en general lisible.', }, pt: { empty: 'Insira o conteudo para gerar seu codigo QR', failed: 'Falha na geracao: verifique o conteudo', infoModules: 'Modulos', infoError: 'Correcao de erros', infoChars: 'Caracteres', copyOk: 'Copiado para a area de transferencia', copyFail: 'Falha ao copiar: navegador nao suportado', uiOptions: '⚙️ Opcoes', uiTabInput: 'Conteudo', uiTabShape: 'Forma', uiTabColor: 'Cor', uiTabLogo: 'Logo', uiContentType: 'Tipo de conteudo', uiTypeUrl: 'URL / Website', uiTypeText: 'Texto', uiTypeEmail: 'Email', uiTypePhone: 'Numero de telefone', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: 'Contato (vCard)', uiUrlAddress: 'Endereco URL', uiTextContent: 'Conteudo de texto', uiTextPlaceholder: 'Digite o conteudo para o codigo QR', uiEmailAddress: 'Endereco de email', uiSubjectOptional: 'Assunto (Opcional)', uiBodyOptional: 'Corpo (Opcional)', uiEmailSubjectPlaceholder: 'Assunto do email', uiEmailBodyPlaceholder: 'Corpo do email', uiPhoneNumber: 'Numero de telefone', uiMessageContent: 'Mensagem', uiSmsPlaceholder: 'Mensagem para enviar', uiWifiSsid: 'Nome da rede (SSID)', uiWifiNamePlaceholder: 'Nome do Wi-Fi', uiPassword: 'Senha', uiWifiPasswordPlaceholder: 'Senha do Wi-Fi', uiSecurityType: 'Tipo de seguranca', uiSecurityNone: 'Nenhum', uiName: 'Nome', uiCompany: 'Empresa', uiWebsite: 'Website', uiErrorLevel: 'Nivel de correcao de erros', uiErrorLevelNote: 'Definido automaticamente para H ao adicionar logo', uiEcL: 'L - Baixo (7%)', uiEcM: 'M - Medio (15%)', uiEcQ: 'Q - Alto (25%)', uiEcH: 'H - Maximo (30%)', uiPreview: '👁️ Pre-visualizacao', uiDownloadSize: 'Tamanho de download', uiDownloadPng: '⬇️ Baixar PNG', uiCopy: '📋 Copiar para a area de transferencia', uiAd: 'Area de anuncios (AdSense)', uiHowtoTitle: 'Como usar codigos QR', uiHow1Title: 'Link de site', uiHow1Desc: 'Insira codigos QR em cartoes, panfletos e posters para levar ao seu site com facilidade.', uiHow2Title: 'Compartilhar Wi-Fi', uiHow2Desc: 'Compartilhe a senha do Wi-Fi em cafeterias ou escritorios com um QR code.', uiHow3Title: 'Compartilhar contato', uiHow3Desc: 'Compartilhe contatos com QR code em vez de cartao de visita. Salva com um scan.', uiHow4Title: 'Informacoes do produto', uiHow4Desc: 'Adicione QR codes na embalagem para abrir detalhes ou guias de uso.', uiDotStyle: 'Estilo do ponto', uiEyeStyle: 'Estilo dos olhos', uiMarginSize: 'Tamanho da margem', uiDotSquare: 'Quadrado', uiDotRounded: 'Arredondado', uiDotExtraRounded: 'Mais arredondado', uiDotCircle: 'Circulo', uiDotDiamond: 'Diamante', uiEyeSquare: 'Quadrado', uiEyeRounded: 'Arredondado', uiEyeCircle: 'Circulo', uiFgColorLabel: 'Cor de primeiro plano (QR)', uiFgColorHelp: 'Cor dos pontos do QR', uiBgColorLabel: 'Cor de fundo', uiBgColorHelp: 'Cor de fundo', uiGradientLabel: 'Gradiente', uiGradNone: 'Nenhum', uiGradLinear: 'Linear', uiGradRadial: 'Radial', uiGradStart: 'Cor inicial', uiGradEnd: 'Cor final', uiColorPresetLabel: 'Predefinicoes rapidas de cor', uiPresetClassic: 'Classico', uiPresetBlue: 'Azul', uiPresetGreen: 'Verde', uiPresetAmber: 'Ambar', uiPresetPurple: 'Roxo', uiPresetRose: 'Rosa', uiLogoLabel: 'Logo/Icone central', uiLogoAutoNote: 'A correcao de erros e ajustada automaticamente para H ao adicionar logo', uiUploadClick: 'Clique para selecionar uma imagem', uiUploadFormats: 'Suporta PNG, JPG, SVG, WebP', uiUploadDone: '✓ Logo enviado', uiRemove: 'Remover', uiLogoSizeLabel: 'Tamanho do logo', uiLogoPaddingLabel: 'Espacamento do fundo do logo', uiLogoRadiusLabel: 'Raio do fundo do logo', uiTipStrong: '💡', uiTipLabel: 'Dica:', uiTipDesc: 'Ao adicionar um logo, a correcao de erros e definida automaticamente para H(30%). Mesmo com cerca de 20% da area central coberta, o QR geralmente continua legivel.', }, vi: { empty: 'Nhap noi dung de tao ma QR', failed: 'Tao that bai: vui long kiem tra noi dung', infoModules: 'So module', infoError: 'Muc sua loi', infoChars: 'So ky tu', copyOk: 'Da sao chep vao bo nho tam', copyFail: 'Sao chep that bai: trinh duyet khong ho tro', uiOptions: '⚙️ Tuy chon', uiTabInput: 'Noi dung', uiTabShape: 'Hinh dang', uiTabColor: 'Mau sac', uiTabLogo: 'Logo', uiContentType: 'Loai noi dung', uiTypeUrl: 'URL / Trang web', uiTypeText: 'Van ban', uiTypeEmail: 'Email', uiTypePhone: 'So dien thoai', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: 'Lien he (vCard)', uiUrlAddress: 'Dia chi URL', uiTextContent: 'Noi dung van ban', uiTextPlaceholder: 'Nhap noi dung cho ma QR', uiEmailAddress: 'Dia chi email', uiSubjectOptional: 'Tieu de (Tuy chon)', uiBodyOptional: 'Noi dung (Tuy chon)', uiEmailSubjectPlaceholder: 'Tieu de email', uiEmailBodyPlaceholder: 'Noi dung email', uiPhoneNumber: 'So dien thoai', uiMessageContent: 'Noi dung tin nhan', uiSmsPlaceholder: 'Tin nhan se gui', uiWifiSsid: 'Ten mang (SSID)', uiWifiNamePlaceholder: 'Ten Wi-Fi', uiPassword: 'Mat khau', uiWifiPasswordPlaceholder: 'Mat khau Wi-Fi', uiSecurityType: 'Kieu bao mat', uiSecurityNone: 'Khong', uiName: 'Ten', uiCompany: 'Cong ty', uiWebsite: 'Trang web', uiErrorLevel: 'Muc sua loi', uiErrorLevelNote: 'Tu dong dat H khi them logo', uiEcL: 'L - Thap (7%)', uiEcM: 'M - Trung binh (15%)', uiEcQ: 'Q - Cao (25%)', uiEcH: 'H - Cao nhat (30%)', uiPreview: '👁️ Xem truoc', uiDownloadSize: 'Kich thuoc tai xuong', uiDownloadPng: '⬇️ Tai PNG', uiCopy: '📋 Sao chep vao bo nho tam', uiAd: 'Khu vuc quang cao (AdSense)', uiHowtoTitle: 'Cach su dung ma QR', uiHow1Title: 'Lien ket website', uiHow1Desc: 'Chen ma QR vao danh thiep, to roi, poster de dieu huong den website de dang.', uiHow2Title: 'Chia se Wi-Fi', uiHow2Desc: 'Chia se mat khau Wi-Fi nhanh trong quan ca phe hoac van phong bang ma QR.', uiHow3Title: 'Chia se lien he', uiHow3Desc: 'Chia se lien he bang ma QR thay vi danh thiep. Quet mot lan la luu duoc.', uiHow4Title: 'Thong tin san pham', uiHow4Desc: 'Them ma QR len bao bi de lien ket den thong tin chi tiet hoac huong dan su dung.', uiDotStyle: 'Kieu cham', uiEyeStyle: 'Kieu mat', uiMarginSize: 'Kich thuoc le', uiDotSquare: 'Vuong', uiDotRounded: 'Bo goc', uiDotExtraRounded: 'Bo goc nhieu', uiDotCircle: 'Tron', uiDotDiamond: 'Kim cuong', uiEyeSquare: 'Vuong', uiEyeRounded: 'Bo goc', uiEyeCircle: 'Tron', uiFgColorLabel: 'Mau tien canh (mau QR)', uiFgColorHelp: 'Mau cham QR', uiBgColorLabel: 'Mau nen', uiBgColorHelp: 'Mau nen', uiGradientLabel: 'Chuyen mau', uiGradNone: 'Khong', uiGradLinear: 'Tuyen tinh', uiGradRadial: 'Huong tam', uiGradStart: 'Mau bat dau', uiGradEnd: 'Mau ket thuc', uiColorPresetLabel: 'Mau san co nhanh', uiPresetClassic: 'Co dien', uiPresetBlue: 'Xanh duong', uiPresetGreen: 'Xanh la', uiPresetAmber: 'Ho phach', uiPresetPurple: 'Tim', uiPresetRose: 'Hong', uiLogoLabel: 'Logo/Bieu tuong o giua', uiLogoAutoNote: 'Muc sua loi duoc tu dong dat H khi them logo', uiUploadClick: 'Nhan de chon anh', uiUploadFormats: 'Ho tro PNG, JPG, SVG, WebP', uiUploadDone: '✓ Da tai logo len', uiRemove: 'Xoa', uiLogoSizeLabel: 'Kich thuoc logo', uiLogoPaddingLabel: 'Le nen logo', uiLogoRadiusLabel: 'Bo goc nen logo', uiTipStrong: '💡', uiTipLabel: 'Meo:', uiTipDesc: 'Khi them logo, muc sua loi se tu dong dat H(30%). Ngay ca khi logo che khoang 20% o giua, ma QR thuong van quet duoc.', }, th: { empty: 'ป้อนเนื้อหาเพื่อสร้างคิวอาร์โค้ด', failed: 'สร้างไม่สำเร็จ: โปรดตรวจสอบเนื้อหา', infoModules: 'จำนวนโมดูล', infoError: 'ระดับแก้ไขข้อผิดพลาด', infoChars: 'จำนวนอักขระ', copyOk: 'คัดลอกไปยังคลิปบอร์ดแล้ว', copyFail: 'คัดลอกไม่สำเร็จ: เบราว์เซอร์ไม่รองรับ', uiOptions: '⚙️ ตัวเลือก', uiTabInput: 'เนื้อหา', uiTabShape: 'รูปร่าง', uiTabColor: 'สี', uiTabLogo: 'โลโก้', uiContentType: 'ประเภทเนื้อหา', uiTypeUrl: 'URL / เว็บไซต์', uiTypeText: 'ข้อความ', uiTypeEmail: 'อีเมล', uiTypePhone: 'หมายเลขโทรศัพท์', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: 'รายชื่อผู้ติดต่อ (vCard)', uiUrlAddress: 'ที่อยู่ URL', uiTextContent: 'เนื้อหาข้อความ', uiTextPlaceholder: 'ป้อนเนื้อหาสำหรับคิวอาร์โค้ด', uiEmailAddress: 'ที่อยู่อีเมล', uiSubjectOptional: 'หัวข้อ (ไม่บังคับ)', uiBodyOptional: 'เนื้อความ (ไม่บังคับ)', uiEmailSubjectPlaceholder: 'หัวข้ออีเมล', uiEmailBodyPlaceholder: 'เนื้อหาอีเมล', uiPhoneNumber: 'หมายเลขโทรศัพท์', uiMessageContent: 'เนื้อหาข้อความ', uiSmsPlaceholder: 'ข้อความที่จะส่ง', uiWifiSsid: 'ชื่อเครือข่าย (SSID)', uiWifiNamePlaceholder: 'ชื่อ Wi-Fi', uiPassword: 'รหัสผ่าน', uiWifiPasswordPlaceholder: 'รหัสผ่าน Wi-Fi', uiSecurityType: 'ประเภทความปลอดภัย', uiSecurityNone: 'ไม่มี', uiName: 'ชื่อ', uiCompany: 'บริษัท', uiWebsite: 'เว็บไซต์', uiErrorLevel: 'ระดับการแก้ไขข้อผิดพลาด', uiErrorLevelNote: 'เมื่อลงโลโก้จะตั้งเป็น H อัตโนมัติ', uiEcL: 'L - ต่ำ (7%)', uiEcM: 'M - ปานกลาง (15%)', uiEcQ: 'Q - สูง (25%)', uiEcH: 'H - สูงสุด (30%)', uiPreview: '👁️ ตัวอย่าง', uiDownloadSize: 'ขนาดดาวน์โหลด', uiDownloadPng: '⬇️ ดาวน์โหลด PNG', uiCopy: '📋 คัดลอกไปยังคลิปบอร์ด', uiAd: 'พื้นที่โฆษณา (AdSense)', uiHowtoTitle: 'วิธีใช้งานคิวอาร์โค้ด', uiHow1Title: 'ลิงก์เว็บไซต์', uiHow1Desc: 'ใส่คิวอาร์โค้ดบนนามบัตร ใบปลิว และโปสเตอร์ เพื่อพาไปยังเว็บไซต์ได้ง่าย', uiHow2Title: 'แชร์ Wi-Fi', uiHow2Desc: 'แชร์รหัสผ่าน Wi-Fi ในคาเฟ่หรือออฟฟิศได้อย่างรวดเร็วด้วยคิวอาร์โค้ด', uiHow3Title: 'แชร์ข้อมูลติดต่อ', uiHow3Desc: 'แชร์ข้อมูลติดต่อด้วยคิวอาร์โค้ดแทนนามบัตร สแกนครั้งเดียวบันทึกได้', uiHow4Title: 'ข้อมูลสินค้า', uiHow4Desc: 'เพิ่มคิวอาร์โค้ดบนบรรจุภัณฑ์เพื่อเชื่อมไปยังรายละเอียดหรือคู่มือการใช้งาน', uiDotStyle: 'สไตล์จุด', uiEyeStyle: 'สไตล์ตา', uiMarginSize: 'ขนาดขอบ', uiDotSquare: 'สี่เหลี่ยม', uiDotRounded: 'ขอบมน', uiDotExtraRounded: 'มนมาก', uiDotCircle: 'วงกลม', uiDotDiamond: 'เพชร', uiEyeSquare: 'สี่เหลี่ยม', uiEyeRounded: 'ขอบมน', uiEyeCircle: 'วงกลม', uiFgColorLabel: 'สีด้านหน้า (สี QR)', uiFgColorHelp: 'สีจุด QR', uiBgColorLabel: 'สีพื้นหลัง', uiBgColorHelp: 'สีพื้นหลัง', uiGradientLabel: 'ไล่สี', uiGradNone: 'ไม่มี', uiGradLinear: 'เชิงเส้น', uiGradRadial: 'แบบรัศมี', uiGradStart: 'สีเริ่มต้น', uiGradEnd: 'สีสิ้นสุด', uiColorPresetLabel: 'พรีเซ็ตสีด่วน', uiPresetClassic: 'คลาสสิก', uiPresetBlue: 'น้ำเงิน', uiPresetGreen: 'เขียว', uiPresetAmber: 'อำพัน', uiPresetPurple: 'ม่วง', uiPresetRose: 'กุหลาบ', uiLogoLabel: 'โลโก้/ไอคอนตรงกลาง', uiLogoAutoNote: 'เมื่อเพิ่มโลโก้ ระบบจะตั้งค่าการแก้ไขข้อผิดพลาดเป็น H อัตโนมัติ', uiUploadClick: 'คลิกเพื่อเลือกภาพ', uiUploadFormats: 'รองรับ PNG, JPG, SVG, WebP', uiUploadDone: '✓ อัปโหลดโลโก้แล้ว', uiRemove: 'ลบ', uiLogoSizeLabel: 'ขนาดโลโก้', uiLogoPaddingLabel: 'ระยะห่างพื้นหลังโลโก้', uiLogoRadiusLabel: 'ความโค้งมุมพื้นหลังโลโก้', uiTipStrong: '💡', uiTipLabel: 'เคล็ดลับ:', uiTipDesc: 'เมื่อเพิ่มโลโก้ ระดับการแก้ไขข้อผิดพลาดจะถูกตั้งเป็น H(30%) อัตโนมัติ แม้บริเวณกลางภาพถูกบังประมาณ 20% โดยทั่วไปก็ยังสแกนได้', }, }; QR_UI_I18N.de = { ...QR_UI_I18N.en, empty: 'Geben Sie Inhalte ein, um Ihr QR code zu generieren', failed: 'Generierung fehlgeschlagen: Bitte überprüfen Sie Ihren Inhalt', infoModules: 'Module', infoError: 'Fehlerkorrektur', infoChars: 'Charaktere', copyOk: 'In die Zwischenablage kopiert!', copyFail: 'Kopieren fehlgeschlagen: Browser wird nicht unterstützt', uiOptions: '⚙️ Optionen', uiTabInput: 'Inhalt', uiTabShape: 'Form', uiTabColor: 'Farbe', uiTabLogo: 'Logo', uiContentType: 'Inhaltstyp', uiTypeUrl: 'URL / Website', uiTypeText: 'Text', uiTypeEmail: 'E-Mail', uiTypePhone: 'Telefonnummer', uiTypeSms: 'SMS', uiTypeWifi: 'Wi-Fi', uiTypeVcard: 'Kontakt (vCard)', uiUrlAddress: 'URL Adresse', uiTextContent: 'Textinhalt', uiTextPlaceholder: 'Geben Sie den Text ein, der in Ihr QR code aufgenommen werden soll.', uiEmailAddress: 'E-Mail-Adresse', uiSubjectOptional: 'Betreff (optional)', uiBodyOptional: 'Körper (optional)', uiEmailSubjectPlaceholder: 'E-Mail-Betreff', uiEmailBodyPlaceholder: 'E-Mail-Text', uiPhoneNumber: 'Telefonnummer', uiMessageContent: 'Nachricht', uiSmsPlaceholder: 'Nachricht zum Senden', uiWifiSsid: 'Netzwerkname (SSID)', uiWifiNamePlaceholder: 'Wi-Fi Name', uiPassword: 'Passwort', uiWifiPasswordPlaceholder: 'Wi-Fi Passwort', uiSecurityType: 'Sicherheitstyp', uiSecurityNone: 'Keiner', uiName: 'Name', uiCompany: 'Unternehmen', uiWebsite: 'Webseite', uiErrorLevel: 'Fehlerkorrekturstufe', uiErrorLevelNote: 'Wird automatisch auf H gesetzt, wenn ein Logo hinzugefügt wird', uiEcL: 'L – Niedrig (7 %)', uiEcM: 'M – Mittel (15 %)', uiEcQ: 'Q – Hoch (25 %)', uiEcH: 'H – Am besten (30 %)', uiPreview: '👁️ Vorschau', uiDownloadSize: 'Downloadgröße', uiDownloadPng: '⬇️ Laden Sie PNG herunter', uiCopy: '📋 In die Zwischenablage kopieren', uiAd: 'Werbefläche (AdSense)', uiHowtoTitle: 'So verwenden Sie QR Codes', uiHow1Title: 'Website-Link', uiHow1Desc: 'Fügen Sie QR codes in Visitenkarten, Flyer und Poster ein, um ganz einfach eine Verknüpfung zu Ihrer Website herzustellen.', uiHow2Title: 'Wi-Fi Teilen', uiHow2Desc: 'Teilen Sie Wi-Fi Passwörter schnell in Cafés oder Büros mit einem QR code.', uiHow3Title: 'Kontaktfreigabe', uiHow3Desc: 'Teilen Sie Kontakte mit einem QR code anstelle von Visitenkarten. Sparen Sie mit einem Scan.', uiHow4Title: 'Produktinformationen', uiHow4Desc: 'Fügen Sie QR codes zur Produktverpackung hinzu, um auf Details oder Benutzerhandbücher zu verweisen.', uiDotStyle: 'Punktstil', uiEyeStyle: 'Augenstil', uiMarginSize: 'Randgröße', uiDotSquare: 'Quadrat', uiDotRounded: 'Gerundet', uiDotExtraRounded: 'Extra abgerundet', uiDotCircle: 'Kreis', uiDotDiamond: 'Diamant', uiEyeSquare: 'Quadrat', uiEyeRounded: 'Gerundet', uiEyeCircle: 'Kreis', uiFgColorLabel: 'Vordergrundfarbe (QR Farbe)', uiFgColorHelp: 'QR Punktfarbe', uiBgColorLabel: 'Hintergrundfarbe', uiBgColorHelp: 'Hintergrundfarbe', uiGradientLabel: 'Gradient', uiGradNone: 'Keiner', uiGradLinear: 'Linear', uiGradRadial: 'Radial', uiGradStart: 'Starten Sie Farbe', uiGradEnd: 'Endfarbe', uiColorPresetLabel: 'Schnelle Farbvoreinstellungen', uiPresetClassic: 'Klassiker', uiPresetBlue: 'Blau', uiPresetGreen: 'Grün', uiPresetAmber: 'Bernstein', uiPresetPurple: 'Lila', uiPresetRose: 'Rose', uiLogoLabel: 'Logo/Symbol in der Mitte', uiLogoAutoNote: 'Beim Hinzufügen eines Logos wird die Fehlerkorrektur automatisch auf H gesetzt', uiUploadClick: 'Klicken Sie, um ein Bild auszuwählen', uiUploadFormats: 'PNG, JPG, SVG, WebP unterstützt', uiUploadDone: '✓ Logo hochgeladen', uiRemove: 'Entfernen', uiLogoSizeLabel: 'Logogröße', uiLogoPaddingLabel: 'Logo-Hintergrundpolsterung', uiLogoRadiusLabel: 'Logo-Hintergrundradius', uiTipStrong: '💡', uiTipLabel: 'Tipp:', uiTipDesc: 'Wenn ein Logo hinzugefügt wird, wird die Fehlerkorrektur automatisch auf H (30 %) eingestellt. Selbst wenn der mittlere Bereich zu etwa 20 % abgedeckt ist, kann der QR code noch gescannt werden.', }; function getCurrentLangForQR() { const supported = ['ko', 'en', 'ja', 'zh', 'es', 'fr', 'pt', 'de', 'vi', 'th']; const saved = localStorage.getItem('alaskahqr_lang'); if (saved && supported.includes(saved)) return saved; const docLang = (document.documentElement.lang || '').slice(0, 2); if (supported.includes(docLang)) return docLang; const browserLang = (navigator.language || '').slice(0, 2); return supported.includes(browserLang) ? browserLang : 'ko'; } function qrt(key) { const lang = getCurrentLangForQR(); return QR_UI_I18N[lang]?.[key] || QR_UI_I18N.en[key] || QR_UI_I18N.ko[key] || key; } function applyQRStaticTexts() { const panelTitle = document.querySelector('.options-col .panel .panel-title'); if (panelTitle) panelTitle.textContent = qrt('uiOptions'); const tabInput = document.querySelector('.tab-btn[data-tab="tab-input"]'); if (tabInput) tabInput.textContent = qrt('uiTabInput'); const tabShape = document.querySelector('.tab-btn[data-tab="tab-shape"]'); if (tabShape) tabShape.textContent = qrt('uiTabShape'); const tabColor = document.querySelector('.tab-btn[data-tab="tab-color"]'); if (tabColor) tabColor.textContent = qrt('uiTabColor'); const tabLogo = document.querySelector('.tab-btn[data-tab="tab-logo"]'); if (tabLogo) tabLogo.textContent = qrt('uiTabLogo'); const contentTypeLabel = document.querySelector('#tab-input .form-group .form-label'); if (contentTypeLabel) contentTypeLabel.textContent = qrt('uiContentType'); const urlOpt = document.querySelector('#content-type option[value="url"]'); if (urlOpt) urlOpt.textContent = qrt('uiTypeUrl'); const textOpt = document.querySelector('#content-type option[value="text"]'); if (textOpt) textOpt.textContent = qrt('uiTypeText'); const emailOpt = document.querySelector('#content-type option[value="email"]'); if (emailOpt) emailOpt.textContent = qrt('uiTypeEmail'); const phoneOpt = document.querySelector('#content-type option[value="phone"]'); if (phoneOpt) phoneOpt.textContent = qrt('uiTypePhone'); const smsOpt = document.querySelector('#content-type option[value="sms"]'); if (smsOpt) smsOpt.textContent = qrt('uiTypeSms'); const wifiOpt = document.querySelector('#content-type option[value="wifi"]'); if (wifiOpt) wifiOpt.textContent = qrt('uiTypeWifi'); const vcardOpt = document.querySelector('#content-type option[value="vcard"]'); if (vcardOpt) vcardOpt.textContent = qrt('uiTypeVcard'); const urlAddressLabel = document.querySelector('#field-url .form-label'); if (urlAddressLabel) urlAddressLabel.textContent = qrt('uiUrlAddress'); const urlInput = document.getElementById('input-url'); if (urlInput) urlInput.placeholder = 'https://example.com'; const textLabel = document.querySelector('#field-text .form-label'); if (textLabel) textLabel.textContent = qrt('uiTextContent'); const textInput = document.getElementById('input-text'); if (textInput) textInput.placeholder = qrt('uiTextPlaceholder'); const emailLabel = document.querySelector('#field-email #input-email')?.closest('.form-group')?.querySelector('.form-label'); if (emailLabel) emailLabel.textContent = qrt('uiEmailAddress'); const emailInput = document.getElementById('input-email'); if (emailInput) emailInput.placeholder = 'example@email.com'; const emailSubjLabel = document.querySelector('#field-email #input-email-subject')?.closest('.form-group')?.querySelector('.form-label'); if (emailSubjLabel) emailSubjLabel.textContent = qrt('uiSubjectOptional'); const emailSubjInput = document.getElementById('input-email-subject'); if (emailSubjInput) emailSubjInput.placeholder = qrt('uiEmailSubjectPlaceholder'); const emailBodyLabel = document.querySelector('#field-email #input-email-body')?.closest('.form-group')?.querySelector('.form-label'); if (emailBodyLabel) emailBodyLabel.textContent = qrt('uiBodyOptional'); const emailBodyInput = document.getElementById('input-email-body'); if (emailBodyInput) emailBodyInput.placeholder = qrt('uiEmailBodyPlaceholder'); const phoneLabel = document.querySelector('#field-phone .form-label'); if (phoneLabel) phoneLabel.textContent = qrt('uiPhoneNumber'); const phoneInput = document.getElementById('input-phone'); if (phoneInput) phoneInput.placeholder = '010-1234-5678'; const smsPhoneLabel = document.querySelector('#field-sms #input-sms-phone')?.closest('.form-group')?.querySelector('.form-label'); if (smsPhoneLabel) smsPhoneLabel.textContent = qrt('uiPhoneNumber'); const smsInput = document.getElementById('input-sms-msg'); const smsLabel = document.querySelector('#field-sms #input-sms-msg')?.closest('.form-group')?.querySelector('.form-label'); if (smsLabel) smsLabel.textContent = qrt('uiMessageContent'); if (smsInput) smsInput.placeholder = qrt('uiSmsPlaceholder'); const wifiSsidLabel = document.querySelector('#field-wifi #input-wifi-ssid')?.closest('.form-group')?.querySelector('.form-label'); if (wifiSsidLabel) wifiSsidLabel.textContent = qrt('uiWifiSsid'); const wifiSsidInput = document.getElementById('input-wifi-ssid'); if (wifiSsidInput) wifiSsidInput.placeholder = qrt('uiWifiNamePlaceholder'); const wifiPassLabel = document.querySelector('#field-wifi #input-wifi-pass')?.closest('.form-group')?.querySelector('.form-label'); if (wifiPassLabel) wifiPassLabel.textContent = qrt('uiPassword'); const wifiPassInput = document.getElementById('input-wifi-pass'); if (wifiPassInput) wifiPassInput.placeholder = qrt('uiWifiPasswordPlaceholder'); const wifiEncLabel = document.querySelector('#field-wifi #input-wifi-enc')?.closest('.form-group')?.querySelector('.form-label'); if (wifiEncLabel) wifiEncLabel.textContent = qrt('uiSecurityType'); const wifiEncNone = document.querySelector('#input-wifi-enc option[value="nopass"]'); if (wifiEncNone) wifiEncNone.textContent = qrt('uiSecurityNone'); const vcardNameLabel = document.querySelector('#field-vcard #input-vcard-name')?.closest('.form-group')?.querySelector('.form-label'); if (vcardNameLabel) vcardNameLabel.textContent = qrt('uiName'); const vcardPhoneLabel = document.querySelector('#field-vcard #input-vcard-phone')?.closest('.form-group')?.querySelector('.form-label'); if (vcardPhoneLabel) vcardPhoneLabel.textContent = qrt('uiPhoneNumber'); const vcardEmailLabel = document.querySelector('#field-vcard #input-vcard-email')?.closest('.form-group')?.querySelector('.form-label'); if (vcardEmailLabel) vcardEmailLabel.textContent = qrt('uiEmailAddress'); const vcardOrgLabel = document.querySelector('#field-vcard #input-vcard-org')?.closest('.form-group')?.querySelector('.form-label'); if (vcardOrgLabel) vcardOrgLabel.textContent = qrt('uiCompany'); const vcardUrlLabel = document.querySelector('#field-vcard #input-vcard-url')?.closest('.form-group')?.querySelector('.form-label'); if (vcardUrlLabel) vcardUrlLabel.textContent = qrt('uiWebsite'); const errorLevelSelect = document.getElementById('error-level'); const errorLevelLabel = errorLevelSelect ? errorLevelSelect.closest('.form-group')?.querySelector('.form-label') : null; if (errorLevelLabel && errorLevelLabel.querySelector('small')) { const small = errorLevelLabel.querySelector('small'); errorLevelLabel.childNodes[0].textContent = qrt('uiErrorLevel') + ' '; small.textContent = qrt('uiErrorLevelNote'); } const optL = document.querySelector('#error-level option[value="L"]'); if (optL) optL.textContent = qrt('uiEcL'); const optM = document.querySelector('#error-level option[value="M"]'); if (optM) optM.textContent = qrt('uiEcM'); const optQ = document.querySelector('#error-level option[value="Q"]'); if (optQ) optQ.textContent = qrt('uiEcQ'); const optH = document.querySelector('#error-level option[value="H"]'); if (optH) optH.textContent = qrt('uiEcH'); const dotLabel = document.querySelector('#tab-shape .form-group .form-label'); if (dotLabel) dotLabel.textContent = qrt('uiDotStyle'); const dotSquare = document.querySelector('#dot-style-grid .style-opt[data-style="square"] span'); if (dotSquare) dotSquare.textContent = qrt('uiDotSquare'); const dotRounded = document.querySelector('#dot-style-grid .style-opt[data-style="rounded"] span'); if (dotRounded) dotRounded.textContent = qrt('uiDotRounded'); const dotExtraRounded = document.querySelector('#dot-style-grid .style-opt[data-style="extra-rounded"] span'); if (dotExtraRounded) dotExtraRounded.textContent = qrt('uiDotExtraRounded'); const dotCircle = document.querySelector('#dot-style-grid .style-opt[data-style="dot"] span'); if (dotCircle) dotCircle.textContent = qrt('uiDotCircle'); const dotDiamond = document.querySelector('#dot-style-grid .style-opt[data-style="diamond"] span'); if (dotDiamond) dotDiamond.textContent = qrt('uiDotDiamond'); const eyeLabel = document.querySelector('#eye-style-grid')?.closest('.form-group')?.querySelector('.form-label'); if (eyeLabel) eyeLabel.textContent = qrt('uiEyeStyle'); const eyeSquare = document.querySelector('#eye-style-grid .style-opt[data-style="square"] span'); if (eyeSquare) eyeSquare.textContent = qrt('uiEyeSquare'); const eyeRounded = document.querySelector('#eye-style-grid .style-opt[data-style="rounded"] span'); if (eyeRounded) eyeRounded.textContent = qrt('uiEyeRounded'); const eyeCircle = document.querySelector('#eye-style-grid .style-opt[data-style="circle"] span'); if (eyeCircle) eyeCircle.textContent = qrt('uiEyeCircle'); const marginLabel = document.getElementById('margin-slider')?.closest('.form-group')?.querySelector('.form-label'); if (marginLabel) marginLabel.textContent = qrt('uiMarginSize'); const fgLabel = document.getElementById('fg-color')?.closest('.form-group')?.querySelector('.form-label'); if (fgLabel) fgLabel.textContent = qrt('uiFgColorLabel'); const fgHelp = document.getElementById('fg-hex')?.parentElement?.querySelector('span'); if (fgHelp) fgHelp.textContent = qrt('uiFgColorHelp'); const bgLabel = document.getElementById('bg-color')?.closest('.form-group')?.querySelector('.form-label'); if (bgLabel) bgLabel.textContent = qrt('uiBgColorLabel'); const bgHelp = document.getElementById('bg-hex')?.parentElement?.querySelector('span'); if (bgHelp) bgHelp.textContent = qrt('uiBgColorHelp'); const gradLabel = document.querySelector('#tab-color .gradient-grid')?.closest('.form-group')?.querySelector('.form-label'); if (gradLabel) gradLabel.textContent = qrt('uiGradientLabel'); const gradNone = document.querySelector('.gradient-opt[data-grad="none"]'); if (gradNone) gradNone.textContent = qrt('uiGradNone'); const gradLinear = document.querySelector('.gradient-opt[data-grad="linear"]'); if (gradLinear) gradLinear.textContent = qrt('uiGradLinear'); const gradRadial = document.querySelector('.gradient-opt[data-grad="radial"]'); if (gradRadial) gradRadial.textContent = qrt('uiGradRadial'); const gradStart = document.getElementById('grad-hex1')?.parentElement?.querySelector('span'); if (gradStart) gradStart.textContent = qrt('uiGradStart'); const gradEnd = document.getElementById('grad-hex2')?.parentElement?.querySelector('span'); if (gradEnd) gradEnd.textContent = qrt('uiGradEnd'); const presetLabel = document.querySelector('#tab-color .color-preset')?.closest('.form-group')?.querySelector('.form-label'); if (presetLabel) presetLabel.textContent = qrt('uiColorPresetLabel'); const presets = document.querySelectorAll('#tab-color .color-preset'); if (presets[0]) presets[0].title = qrt('uiPresetClassic'); if (presets[1]) presets[1].title = qrt('uiPresetBlue'); if (presets[2]) presets[2].title = qrt('uiPresetGreen'); if (presets[3]) presets[3].title = qrt('uiPresetAmber'); if (presets[4]) presets[4].title = qrt('uiPresetPurple'); if (presets[5]) presets[5].title = qrt('uiPresetRose'); const logoLabel = document.getElementById('logo-upload-area')?.closest('.form-group')?.querySelector('.form-label'); if (logoLabel && logoLabel.querySelector('small')) { const small = logoLabel.querySelector('small'); logoLabel.childNodes[0].textContent = qrt('uiLogoLabel') + ' '; small.textContent = qrt('uiLogoAutoNote'); } const uploadText = document.querySelector('#upload-placeholder .upload-text'); if (uploadText) { uploadText.innerHTML = `${qrt('uiUploadClick')}
${qrt('uiUploadFormats')}`; } const uploaded = document.querySelector('#upload-preview-wrap > div:last-child'); if (uploaded) uploaded.textContent = qrt('uiUploadDone'); const removeBtn = document.querySelector('.upload-remove'); if (removeBtn) removeBtn.title = qrt('uiRemove'); const logoSizeLabel = document.getElementById('logo-size')?.closest('.form-group')?.querySelector('.form-label'); if (logoSizeLabel) logoSizeLabel.textContent = qrt('uiLogoSizeLabel'); const logoPaddingLabel = document.getElementById('logo-padding')?.closest('.form-group')?.querySelector('.form-label'); if (logoPaddingLabel) logoPaddingLabel.textContent = qrt('uiLogoPaddingLabel'); const logoRadiusLabel = document.getElementById('logo-radius')?.closest('.form-group')?.querySelector('.form-label'); if (logoRadiusLabel) logoRadiusLabel.textContent = qrt('uiLogoRadiusLabel'); const logoTipBox = document.querySelector('#tab-logo > div[style*="line-height:1.7"]'); if (logoTipBox) { logoTipBox.innerHTML = `${qrt('uiTipStrong')} ${qrt('uiTipLabel')} ${qrt('uiTipDesc')}`; } const previewTitle = document.querySelector('.preview-panel .panel-title'); if (previewTitle) previewTitle.textContent = qrt('uiPreview'); const dlSizeLabel = document.querySelector('.preview-panel .preview-area > div[style*="width:100%"] > div[style*="font-size:.8rem"]'); if (dlSizeLabel) dlSizeLabel.textContent = qrt('uiDownloadSize'); const pngBtn = document.querySelector('.download-group .btn-dl-primary'); if (pngBtn) pngBtn.textContent = qrt('uiDownloadPng'); const copyBtn = document.querySelector('.download-group .btn-dl-secondary'); if (copyBtn) copyBtn.textContent = qrt('uiCopy'); const supportTitle = document.getElementById('qr-support-title'); if (supportTitle) supportTitle.textContent = qrt('uiSupportTitle'); const supportDesc = document.getElementById('qr-support-desc'); if (supportDesc) supportDesc.textContent = qrt('uiSupportDesc'); const supportLink = document.getElementById('qr-support-link'); if (supportLink) supportLink.textContent = qrt('uiSupportButton'); const adWrap = document.querySelector('.ad-wrap'); if (adWrap) adWrap.textContent = qrt('uiAd'); const howTitle = document.querySelector('.features-bg .section-title'); if (howTitle) howTitle.textContent = qrt('uiHowtoTitle'); const featureItems = document.querySelectorAll('.features-bg .feature-item'); if (featureItems[0]) { const h4 = featureItems[0].querySelector('h4'); const p = featureItems[0].querySelector('p'); if (h4) h4.textContent = qrt('uiHow1Title'); if (p) p.textContent = qrt('uiHow1Desc'); } if (featureItems[1]) { const h4 = featureItems[1].querySelector('h4'); const p = featureItems[1].querySelector('p'); if (h4) h4.textContent = qrt('uiHow2Title'); if (p) p.textContent = qrt('uiHow2Desc'); } if (featureItems[2]) { const h4 = featureItems[2].querySelector('h4'); const p = featureItems[2].querySelector('p'); if (h4) h4.textContent = qrt('uiHow3Title'); if (p) p.textContent = qrt('uiHow3Desc'); } if (featureItems[3]) { const h4 = featureItems[3].querySelector('h4'); const p = featureItems[3].querySelector('p'); if (h4) h4.textContent = qrt('uiHow4Title'); if (p) p.textContent = qrt('uiHow4Desc'); } } /* ========== HELPERS ========== */ function roundRectPath(ctx, x, y, w, h, r) { r = Math.min(r, Math.min(w, h) / 2); ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.quadraticCurveTo(x + w, y, x + w, y + r); ctx.lineTo(x + w, y + h - r); ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); ctx.lineTo(x + r, y + h); ctx.quadraticCurveTo(x, y + h, x, y + h - r); ctx.lineTo(x, y + r); ctx.quadraticCurveTo(x, y, x + r, y); ctx.closePath(); } function drawModule(ctx, x, y, size, style) { const pad = size * 0.08; const s = size - pad * 2; const cx = x + size / 2; const cy = y + size / 2; switch (style) { case 'rounded': roundRectPath(ctx, x + pad, y + pad, s, s, s * 0.3); ctx.fill(); break; case 'extra-rounded': roundRectPath(ctx, x + pad, y + pad, s, s, s * 0.5); ctx.fill(); break; case 'dot': ctx.beginPath(); ctx.arc(cx, cy, s / 2, 0, Math.PI * 2); ctx.fill(); break; case 'diamond': ctx.beginPath(); ctx.moveTo(cx, y + pad); ctx.lineTo(x + size - pad, cy); ctx.lineTo(cx, y + size - pad); ctx.lineTo(x + pad, cy); ctx.closePath(); ctx.fill(); break; default: // square ctx.fillRect(x + pad, y + pad, s, s); } } function drawEye(ctx, x, y, ms, style, bgColor) { const total = ms * 7; const cx = x + total / 2; const cy = y + total / 2; switch (style) { case 'rounded': { // Outer frame (rounded) roundRectPath(ctx, x, y, total, total, ms * 1.8); ctx.fill(); const savedFill = ctx.fillStyle; ctx.fillStyle = bgColor; roundRectPath(ctx, x + ms, y + ms, ms * 5, ms * 5, ms * 1.0); ctx.fill(); ctx.fillStyle = savedFill; // Inner dot (rounded) roundRectPath(ctx, x + ms * 2, y + ms * 2, ms * 3, ms * 3, ms * 0.7); ctx.fill(); break; } case 'circle': { // Outer ring ctx.beginPath(); ctx.arc(cx, cy, total / 2, 0, Math.PI * 2); ctx.fill(); const savedFill2 = ctx.fillStyle; ctx.fillStyle = bgColor; ctx.beginPath(); ctx.arc(cx, cy, total / 2 - ms, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = savedFill2; // Inner circle ctx.beginPath(); ctx.arc(cx, cy, ms * 1.5, 0, Math.PI * 2); ctx.fill(); break; } default: { // square ctx.fillRect(x, y, total, total); const savedFill3 = ctx.fillStyle; ctx.fillStyle = bgColor; ctx.fillRect(x + ms, y + ms, ms * 5, ms * 5); ctx.fillStyle = savedFill3; ctx.fillRect(x + ms * 2, y + ms * 2, ms * 3, ms * 3); } } } /* ========== MAIN RENDER ========== */ function renderQR(canvas, overrideSize) { const size = overrideSize || 512; canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); const text = QRState.text; // Empty state if (!text || text.trim() === '') { ctx.fillStyle = '#f3f4f6'; ctx.fillRect(0, 0, size, size); ctx.fillStyle = '#9ca3af'; ctx.font = `${size * 0.035}px sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(qrt('empty'), size / 2, size / 2); toggleQRSupportPrompt(false); return false; } // Choose error correction (H when logo present) const ec = QRState.logoImage ? 'H' : QRState.errorLevel; try { // qrcode-generator uses type number 0 = auto const qr = qrcode(0, ec); qr.addData(text); qr.make(); const count = qr.getModuleCount(); const margin = QRState.margin * (size / 512); const moduleSize = (size - margin * 2) / count; // Background ctx.fillStyle = QRState.bgColor; ctx.fillRect(0, 0, size, size); // Foreground fill (solid or gradient) let fgFill; if (QRState.gradientType === 'none') { fgFill = QRState.fgColor; } else if (QRState.gradientType === 'linear') { const g = ctx.createLinearGradient(margin, margin, size - margin, size - margin); g.addColorStop(0, QRState.gradientColor1); g.addColorStop(1, QRState.gradientColor2); fgFill = g; } else { const g = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, (size - margin * 2) / 2); g.addColorStop(0, QRState.gradientColor1); g.addColorStop(1, QRState.gradientColor2); fgFill = g; } // Eye zones (top-left, top-right, bottom-left) const eyes = [ { row: 0, col: 0 }, { row: 0, col: count - 7 }, { row: count - 7, col: 0 }, ]; function isEye(r, c) { for (const e of eyes) { if (r >= e.row && r < e.row + 7 && c >= e.col && c < e.col + 7) return true; } return false; } // Draw data modules ctx.fillStyle = fgFill; for (let row = 0; row < count; row++) { for (let col = 0; col < count; col++) { if (!qr.isDark(row, col) || isEye(row, col)) continue; const x = margin + col * moduleSize; const y = margin + row * moduleSize; drawModule(ctx, x, y, moduleSize, QRState.dotStyle); } } // Draw eyes ctx.fillStyle = fgFill; for (const e of eyes) { const x = margin + e.col * moduleSize; const y = margin + e.row * moduleSize; drawEye(ctx, x, y, moduleSize, QRState.eyeStyle, QRState.bgColor); } // Draw logo if (QRState.logoImage) { const logoFraction = QRState.logoSize / 100; const lw = size * logoFraction; const lh = size * logoFraction; const lx = (size - lw) / 2; const ly = (size - lh) / 2; const pad = QRState.logoPadding * (size / 512); const r = (lw + pad * 2) * (QRState.logoRadius / 100); ctx.fillStyle = QRState.bgColor; roundRectPath(ctx, lx - pad, ly - pad, lw + pad * 2, lh + pad * 2, r); ctx.fill(); ctx.save(); roundRectPath(ctx, lx - pad, ly - pad, lw + pad * 2, lh + pad * 2, r); ctx.clip(); ctx.drawImage(QRState.logoImage, lx, ly, lw, lh); ctx.restore(); } // Update info updateQRInfo(count, ec, text.length); toggleQRSupportPrompt(true); return true; } catch (err) { ctx.fillStyle = '#FEF2F2'; ctx.fillRect(0, 0, size, size); ctx.fillStyle = '#EF4444'; ctx.font = `${size * 0.03}px sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(qrt('failed'), size / 2, size / 2); toggleQRSupportPrompt(false); return false; } } function toggleQRSupportPrompt(show) { const prompt = document.getElementById('qr-support-prompt'); if (!prompt) return; prompt.hidden = !show; } function updateQRInfo(modules, ec, chars) { const el = document.getElementById('qr-info'); if (el) { el.textContent = `${qrt('infoModules')}: ${modules}×${modules} | ${qrt('infoError')}: ${ec} | ${qrt('infoChars')}: ${chars}`; } } function scheduleRender() { clearTimeout(renderTimer); renderTimer = setTimeout(() => { const canvas = document.getElementById('qr-canvas'); if (canvas) renderQR(canvas); }, 120); } /* ========== CONTENT BUILDER ========== */ function buildQRText() { const type = document.getElementById('content-type').value; switch (type) { case 'url': { let url = document.getElementById('input-url').value.trim(); if (url && !url.match(/^https?:\/\//i)) url = 'https://' + url; return url; } case 'text': return document.getElementById('input-text').value; case 'email': { const mail = document.getElementById('input-email').value.trim(); const subj = document.getElementById('input-email-subject').value.trim(); const body = document.getElementById('input-email-body').value.trim(); let str = `mailto:${mail}`; const params = []; if (subj) params.push('subject=' + encodeURIComponent(subj)); if (body) params.push('body=' + encodeURIComponent(body)); if (params.length) str += '?' + params.join('&'); return str; } case 'phone': return 'tel:' + document.getElementById('input-phone').value.trim(); case 'sms': { const ph = document.getElementById('input-sms-phone').value.trim(); const msg = document.getElementById('input-sms-msg').value.trim(); return `sms:${ph}${msg ? '?body=' + encodeURIComponent(msg) : ''}`; } case 'wifi': { const ssid = document.getElementById('input-wifi-ssid').value.trim(); const pass = document.getElementById('input-wifi-pass').value; const enc = document.getElementById('input-wifi-enc').value; return `WIFI:T:${enc};S:${ssid};P:${pass};;`; } case 'vcard': { const name = document.getElementById('input-vcard-name').value.trim(); const phone = document.getElementById('input-vcard-phone').value.trim(); const email = document.getElementById('input-vcard-email').value.trim(); const org = document.getElementById('input-vcard-org').value.trim(); const url = document.getElementById('input-vcard-url').value.trim(); return [ 'BEGIN:VCARD', 'VERSION:3.0', name ? `FN:${name}` : '', phone ? `TEL:${phone}` : '', email ? `EMAIL:${email}` : '', org ? `ORG:${org}` : '', url ? `URL:${url}` : '', 'END:VCARD' ].filter(Boolean).join('\n'); } default: return ''; } } function showContentFields(type) { document.querySelectorAll('.content-fields').forEach(el => el.style.display = 'none'); const target = document.getElementById('field-' + type); if (target) target.style.display = 'block'; } /* ========== DOWNLOAD ========== */ function downloadQR(format) { const size = QRState.downloadSize; const tempCanvas = document.createElement('canvas'); const ok = renderQR(tempCanvas, size); if (!ok) return; const link = document.createElement('a'); link.download = `quickqr-${Date.now()}.png`; link.href = tempCanvas.toDataURL('image/png'); link.click(); } function copyQRToClipboard() { const canvas = document.getElementById('qr-canvas'); canvas.toBlob(blob => { try { navigator.clipboard.write([ new ClipboardItem({ 'image/png': blob }) ]).then(() => { showToast(qrt('copyOk')); }).catch(() => { showToast(qrt('copyFail')); }); } catch (e) { showToast(qrt('copyFail')); } }); } function showToast(msg) { let toast = document.getElementById('qr-toast'); if (!toast) { toast = document.createElement('div'); toast.id = 'qr-toast'; toast.style.cssText = 'position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#1F2937;color:white;padding:12px 24px;border-radius:8px;font-size:.9rem;font-weight:600;z-index:9999;transition:opacity .3s'; document.body.appendChild(toast); } toast.textContent = msg; toast.style.opacity = '1'; setTimeout(() => { toast.style.opacity = '0'; }, 2500); } /* ========== EVENT LISTENERS ========== */ document.addEventListener('DOMContentLoaded', () => { // Initial render QRState.text = buildQRText(); applyQRStaticTexts(); scheduleRender(); const htmlEl = document.documentElement; if (htmlEl) { const observer = new MutationObserver(mutations => { for (const m of mutations) { if (m.type === 'attributes' && m.attributeName === 'lang') { applyQRStaticTexts(); scheduleRender(); break; } } }); observer.observe(htmlEl, { attributes: true, attributeFilter: ['lang'] }); } // Content type switcher document.getElementById('content-type').addEventListener('change', e => { showContentFields(e.target.value); QRState.text = buildQRText(); scheduleRender(); }); // All text inputs trigger regeneration document.querySelectorAll('.form-input').forEach(el => { el.addEventListener('input', () => { QRState.text = buildQRText(); scheduleRender(); }); }); // Error level document.getElementById('error-level').addEventListener('change', e => { QRState.errorLevel = e.target.value; scheduleRender(); }); // Dot style document.querySelectorAll('#dot-style-grid .style-opt').forEach(opt => { opt.addEventListener('click', () => { document.querySelectorAll('#dot-style-grid .style-opt').forEach(o => o.classList.remove('active')); opt.classList.add('active'); QRState.dotStyle = opt.dataset.style; scheduleRender(); }); }); // Eye style document.querySelectorAll('#eye-style-grid .style-opt').forEach(opt => { opt.addEventListener('click', () => { document.querySelectorAll('#eye-style-grid .style-opt').forEach(o => o.classList.remove('active')); opt.classList.add('active'); QRState.eyeStyle = opt.dataset.style; scheduleRender(); }); }); // Margin slider const marginSlider = document.getElementById('margin-slider'); marginSlider.addEventListener('input', e => { QRState.margin = parseInt(e.target.value); document.getElementById('margin-val').textContent = e.target.value + 'px'; scheduleRender(); }); // FG Color const fgColorInput = document.getElementById('fg-color'); const fgHexInput = document.getElementById('fg-hex'); fgColorInput.addEventListener('input', e => { QRState.fgColor = e.target.value; fgHexInput.value = e.target.value; scheduleRender(); }); fgHexInput.addEventListener('input', e => { if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { QRState.fgColor = e.target.value; fgColorInput.value = e.target.value; scheduleRender(); } }); // BG Color const bgColorInput = document.getElementById('bg-color'); const bgHexInput = document.getElementById('bg-hex'); bgColorInput.addEventListener('input', e => { QRState.bgColor = e.target.value; bgHexInput.value = e.target.value; scheduleRender(); }); bgHexInput.addEventListener('input', e => { if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { QRState.bgColor = e.target.value; bgColorInput.value = e.target.value; scheduleRender(); } }); // Gradient document.querySelectorAll('.gradient-opt').forEach(opt => { opt.addEventListener('click', () => { document.querySelectorAll('.gradient-opt').forEach(o => o.classList.remove('active')); opt.classList.add('active'); QRState.gradientType = opt.dataset.grad; const gc = document.getElementById('gradient-colors'); if (QRState.gradientType === 'none') { gc.classList.add('hidden'); } else { gc.classList.remove('hidden'); } scheduleRender(); }); }); // Gradient colors ['1','2'].forEach(n => { const col = document.getElementById(`grad-color${n}`); const hex = document.getElementById(`grad-hex${n}`); col.addEventListener('input', e => { QRState[`gradientColor${n}`] = e.target.value; hex.value = e.target.value; scheduleRender(); }); hex.addEventListener('input', e => { if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) { QRState[`gradientColor${n}`] = e.target.value; col.value = e.target.value; scheduleRender(); } }); }); // Color presets document.querySelectorAll('.color-preset').forEach(preset => { preset.addEventListener('click', () => { const fg = preset.dataset.fg; const bg = preset.dataset.bg; QRState.fgColor = fg; QRState.bgColor = bg; document.getElementById('fg-color').value = fg; document.getElementById('fg-hex').value = fg; document.getElementById('bg-color').value = bg; document.getElementById('bg-hex').value = bg; // Reset gradient QRState.gradientType = 'none'; document.querySelectorAll('.gradient-opt').forEach(o => o.classList.remove('active')); document.querySelector('.gradient-opt[data-grad="none"]').classList.add('active'); document.getElementById('gradient-colors').classList.add('hidden'); scheduleRender(); }); }); // Logo upload document.getElementById('logo-file').addEventListener('change', e => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = ev => { const img = new Image(); img.onload = () => { QRState.logoImage = img; document.getElementById('upload-placeholder').style.display = 'none'; document.getElementById('upload-preview-wrap').style.display = 'block'; document.getElementById('logo-preview-img').src = ev.target.result; document.getElementById('logo-upload-area').classList.add('has-file'); document.getElementById('logo-size-group').style.display = 'block'; document.getElementById('logo-padding-group').style.display = 'block'; document.getElementById('logo-radius-group').style.display = 'block'; scheduleRender(); }; img.src = ev.target.result; }; reader.readAsDataURL(file); }); // Logo size slider document.getElementById('logo-size').addEventListener('input', e => { QRState.logoSize = parseInt(e.target.value); document.getElementById('logo-size-val').textContent = e.target.value + '%'; scheduleRender(); }); // Logo padding slider document.getElementById('logo-padding').addEventListener('input', e => { QRState.logoPadding = parseInt(e.target.value); document.getElementById('logo-padding-val').textContent = e.target.value + 'px'; scheduleRender(); }); // Logo radius slider document.getElementById('logo-radius').addEventListener('input', e => { QRState.logoRadius = parseInt(e.target.value); document.getElementById('logo-radius-val').textContent = e.target.value + '%'; scheduleRender(); }); // Download size buttons document.querySelectorAll('.size-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.size-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); QRState.downloadSize = parseInt(btn.dataset.size); }); }); // Tab switching document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active')); btn.classList.add('active'); document.getElementById(btn.dataset.tab).classList.add('active'); }); }); }); // Remove logo function removeLogo(e) { e.stopPropagation(); QRState.logoImage = null; document.getElementById('upload-placeholder').style.display = 'block'; document.getElementById('upload-preview-wrap').style.display = 'none'; document.getElementById('logo-upload-area').classList.remove('has-file'); document.getElementById('logo-size-group').style.display = 'none'; document.getElementById('logo-padding-group').style.display = 'none'; document.getElementById('logo-radius-group').style.display = 'none'; document.getElementById('logo-file').value = ''; scheduleRender(); }