Research Proposal Generator

Research Proposal Generator

Research Proposal Generator

Structure and generate your research proposal.

Generated Proposal

Generate a proposal from the 'Proposal Builder' tab.

Proposal Builder

Fill in the sections below to build your research proposal. Use the 'Configuration' tab to add or remove custom sections.

Configuration

Manage the sections included in your proposal template. Standard sections (like Title, Abstract) cannot be removed.

Manage Proposal Sections

Generate a proposal from the 'Proposal Builder' tab.

`; pdfDownloadBtn.style.display = 'none'; copyBtn.style.display = 'none'; return; } let html = `

${escapeHTML(db.content["Title"] || "Untitled Proposal")}

`; db.sections.forEach(section => { if (section !== "Title" && db.content[section]?.trim()) { html += `

${escapeHTML(section)}

`; // Basic formatting attempt: replace double newlines with paragraph breaks const paragraphs = escapeHTML(db.content[section]).split(/\n\s*\n/); html += paragraphs.map(p => `

${p.replace(/\n/g, '
')}

`).join(''); } }); pdfContent.innerHTML = html; pdfDownloadBtn.style.display = 'inline-block'; copyBtn.style.display = 'inline-block'; } // --- Utility Functions --- function showMessageModal(message) { let modal = document.getElementById('rpg-modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'rpg-modal'; modal.className = 'rpg-pdf-hide fixed inset-0 bg-gray-800 bg-opacity-50 flex items-center justify-center p-4'; modal.style.zIndex = '1000'; modal.innerHTML = `

Notification

`; document.body.appendChild(modal); modal.querySelector('#rpg-modal-close').addEventListener('click', () => modal.style.display = 'none'); } modal.querySelector('#rpg-modal-message').textContent = message; modal.style.display = 'flex'; } async function downloadPDF() { // Per user request, ensuring this is robust pdfTarget.classList.add('rpg-pdf-view'); try { // Use scale: 1 for text-heavy documents to avoid blurriness const canvas = await html2canvas(pdfTarget, { scale: 1, logging: false, useCORS: true }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF('p', 'mm', 'a4'); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const pageMargin = 15; const imgProps = pdf.getImageProperties(imgData); const imgWidth = pdfWidth - (pageMargin * 2); const imgHeight = (imgProps.height * imgWidth) / imgProps.width; let heightLeft = imgHeight; let position = pageMargin; // Initial top margin pdf.addImage(imgData, 'PNG', pageMargin, position, imgWidth, imgHeight); heightLeft -= (pdfHeight - (pageMargin * 2)); while (heightLeft > 0) { position = heightLeft - imgHeight + pageMargin; // Adjust position for next page pdf.addPage(); pdf.addImage(imgData, 'PNG', pageMargin, position, imgWidth, imgHeight); heightLeft -= (pdfHeight - (pageMargin * 2)); } pdf.save(`${(db.content["Title"] || 'Research_Proposal').replace(/\s+/g, '_')}.pdf`); } catch (error) { console.error("Error generating PDF:", error); showMessageModal("An error occurred while generating the PDF. Please try again."); } finally { pdfTarget.classList.remove('rpg-pdf-view'); } } function copyToClipboard() { // Use document.execCommand for iframe compatibility const el = document.createElement('textarea'); let text = `${db.content["Title"] || "Untitled Proposal"}\n\n`; db.sections.forEach(section => { if (section !== "Title" && db.content[section]?.trim()) { text += `--- ${section} ---\n`; text += `${db.content[section]}\n\n`; } }); el.value = text; document.body.appendChild(el); el.select(); try { document.execCommand('copy'); showMessageModal('Proposal text copied to clipboard!'); } catch (err) { showMessageModal('Failed to copy text. Please try again.'); } document.body.removeChild(el); } function renderAll() { renderConfig(); renderBuilderForm(); // Re-render form to reflect section changes } function escapeHTML(str) { if (typeof str !== 'string') return ''; return str.replace(/[&<>"']/g, m => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[m])); } // --- 4. EVENT BINDING & INITIALIZATION --- navTabs.forEach((tab, index) => { tab.addEventListener('click', () => showTab(index)); }); navPrev.addEventListener('click', () => showTab(currentTab - 1)); navNext.addEventListener('click', () => showTab(currentTab + 1)); addSectionForm.addEventListener('submit', handleAddSection); generateBtn.addEventListener('click', handleGenerateProposal); pdfDownloadBtn.addEventListener('click', downloadPDF); copyBtn.addEventListener('click', copyToClipboard); // Delegate delete for sections sectionsList.addEventListener('click', (e) => { if (e.target.classList.contains('rpg-section-delete')) { const index = parseInt(e.target.dataset.index, 10); const sectionName = db.sections[index]; db.sections.splice(index, 1); delete db.content[sectionName]; // Remove content when section is deleted renderAll(); } }); // Initial Load renderAll(); showTab(1); // Start on the builder tab });
Scroll to Top