class FormRenderer { constructor(options) { this.container = typeof options.container === 'string' ? document.querySelector(options.container) : options.container; this.formDefinition = options.formDefinition; this.onSubmit = options.onSubmit; this.onChange = options.onChange; this.values = {}; // form değerlerini tutacak obje // reCAPTCHA v3 ayarları this.recaptcha = { siteKey: options.recaptchaSiteKey || '', // reCAPTCHA site key enabled: options.enableRecaptcha !== false, // varsayılan olarak aktif action: options.recaptchaAction || 'form_submit' // varsayılan action }; this.init(); } init() { // Container kontrolü 31.03.2026-11:06:1 ıüğişçöIÜĞİŞÇÖ if (!this.container) throw new Error('Form container bulunamadı'); // reCAPTCHA script'ini yükle if (this.recaptcha.enabled && this.recaptcha.siteKey) { this.loadRecaptchaScript(); } // Form oluştur this.form = document.createElement('form'); this.form.className = 'needs-validation'; this.form.noValidate = true; this.form.method = 'POST'; this.container.appendChild(this.form); // Form elemanlarını render et this.renderFormFields(); // Submit butonu ekle this.addSubmitButton(); // Form events this.bindEvents(); } // reCAPTCHA script'ini dinamik olarak yükle loadRecaptchaScript() { if (document.querySelector('script[src*="recaptcha"]')) { return; // Zaten yüklenmişse tekrar yükleme } const script = document.createElement('script'); script.src = `https://www.google.com/recaptcha/api.js?render=${this.recaptcha.siteKey}`; script.async = true; script.defer = true; document.head.appendChild(script); } // reCAPTCHA token'ı al async getRecaptchaToken(action = null) { if (!this.recaptcha.enabled || !this.recaptcha.siteKey) { return null; } try { // grecaptcha'nın yüklenmesini bekle await this.waitForRecaptcha(); const token = await grecaptcha.execute(this.recaptcha.siteKey, { action: action || this.recaptcha.action }); return token; } catch (error) { console.error('reCAPTCHA token alınırken hata:', error); return null; } } // grecaptcha'nın yüklenmesini bekle waitForRecaptcha(timeout = 10000) { return new Promise((resolve, reject) => { const startTime = Date.now(); const checkRecaptcha = () => { if (typeof grecaptcha !== 'undefined' && grecaptcha.execute) { resolve(); } else if (Date.now() - startTime > timeout) { reject(new Error('reCAPTCHA yüklenemedi')); } else { setTimeout(checkRecaptcha, 100); } }; checkRecaptcha(); }); } renderFormFields() { const gridContainer = document.createElement('div'); gridContainer.className = 'row'; this.form.appendChild(gridContainer); this.formDefinition.fields.forEach(field => { const fieldContainer = document.createElement('div'); fieldContainer.className = (field.containerClass || 'col-12') + ' mb-4' + ' form-item-box'; // Field HTML'ini oluştur const fieldHtml = this.renderField(field); if (fieldHtml) { fieldContainer.innerHTML = fieldHtml; gridContainer.appendChild(fieldContainer); // HTML eklendikten sonra davranışları uygula this.applyFieldBehaviors(field, fieldContainer); } }); } renderField(field) { const labelPosition = field.labelPosition || 'top'; const labelHtml = field.label && labelPosition !== 'none' ? `` : ''; let inputHtml = ''; switch (field.type) { case 'text': let inputType = 'text'; if (field.maskType === 'email') inputType = 'email'; if (field.maskType === 'number') inputType = 'text'; // IMask ile text daha iyi çalışır inputHtml = ` `; break; case 'textarea': inputHtml = ` `; break; case 'select': const options = field.options.split('\n').map(line => { const [text, value] = line.split('|'); return ``; }).join(''); inputHtml = ` `; break; case 'checkboxGroup': inputHtml = `
${this.escapeHtml(field.text)}
`; case 'fileUpload': inputHtml = ` `; break; case 'hierarchicalSelect': // Hierarchical select için özel konteyner inputHtml = ``; break; } if (labelPosition === 'left') { return `