`;
durationInputsContainer.insertAdjacentHTML('beforeend', inputHtml);
});
};
const calculateTimeline = () => {
const startDate = filingDateInput.value;
if (!startDate) {
calculatedTimeline = [];
return;
}
const filingType = filingTypeSelect.value;
let currentStages = JSON.parse(JSON.stringify(timelineStages[filingType])); // Deep copy
// Update durations from inputs
document.querySelectorAll('.duration-input').forEach(input => {
const key = input.dataset.key;
const stage = currentStages.find(s => s.key === key);
if(stage) stage.duration = parseInt(input.value, 10);
});
if (includePctCheckbox.checked && filingType === 'provisional') {
const pctStages = JSON.parse(JSON.stringify(timelineStages.pct));
document.querySelectorAll('.duration-input').forEach(input => {
const key = input.dataset.key;
const stage = pctStages.find(s => s.key === key);
if(stage) stage.duration = parseInt(input.value, 10);
});
currentStages.push(...pctStages);
}
const dates = {};
const result = [];
currentStages.sort((a, b) => { // Simple sort to process dependencies
if (a.dependsOn === null) return -1;
if (b.dependsOn === null) return 1;
return 0;
});
currentStages.forEach(stage => {
let baseDate;
if(stage.dependsOn === null) {
baseDate = new Date(startDate + 'T00:00:00'); // Fix timezone issue
} else {
baseDate = dates[stage.dependsOn];
}
if(baseDate){
dates[stage.key] = addMonths(baseDate, stage.duration);
result.push({ ...stage, date: dates[stage.key] });
}
});
result.sort((a,b) => a.date - b.date);
calculatedTimeline = result;
};
const renderTimeline = () => {
timelineContainer.innerHTML = '';
if (calculatedTimeline.length === 0) {
timelineContainer.innerHTML = `
`;
timelineContainer.insertAdjacentHTML('beforeend', itemHtml);
});
};
// --- TAB NAVIGATION LOGIC ---
window.switchTab = (tabName) => {
currentTab = tabName;
Object.keys(tabs).forEach(key => {
tabs[key].classList.toggle('active', key === tabName);
tabs[key].classList.toggle('inactive', key !== tabName);
});
Object.keys(contents).forEach(key => {
contents[key].classList.toggle('hidden', key !== tabName);
});
updateNavButtons();
};
window.navigateTabs = (direction) => {
const tabKeys = Object.keys(tabs);
const currentIndex = tabKeys.indexOf(currentTab);
let newIndex = (direction === 'next') ? currentIndex + 1 : currentIndex - 1;
if (newIndex >= 0 && newIndex < tabKeys.length) {
if (tabKeys[newIndex] === 'dashboard') {
calculateTimeline();
renderTimeline();
}
switchTab(tabKeys[newIndex]);
}
};
const updateNavButtons = () => {
const tabKeys = Object.keys(tabs);
const currentIndex = tabKeys.indexOf(currentTab);
navButtons.prev.disabled = currentIndex === 0;
navButtons.next.disabled = currentIndex === tabKeys.length - 1;
};
// --- PDF GENERATION ---
const generatePdf = () => {
if(calculatedTimeline.length === 0) return;
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.setFontSize(18);
doc.text('Patent Filing Timeline Report', 14, 22);
doc.setFontSize(11);
doc.text(`Based on initial filing date: ${formatDate(new Date(filingDateInput.value + 'T00:00:00'))}`, 14, 30);
const tableColumn = ["Stage", "Description", "Projected Date"];
const tableRows = calculatedTimeline.map(item => [
item.name,
item.description || '-',
formatDate(item.date)
]);
doc.autoTable({
head: [tableColumn],
body: tableRows,
startY: 40,
theme: 'grid',
headStyles: { fillColor: [79, 70, 229] },
});
doc.save('Patent_Filing_Timeline.pdf');
};
// --- INITIALIZATION & EVENT LISTENERS ---
const initialize = () => {
// Set default filing date to today for USA context
const today = new Date();
const offset = today.getTimezoneOffset();
const todayLocal = new Date(today.getTime() - (offset*60*1000));
filingDateInput.value = todayLocal.toISOString().split('T')[0];
renderDurationInputs();
calculateTimeline();
renderTimeline();
updateNavButtons();
};
filingTypeSelect.addEventListener('change', renderDurationInputs);
includePctCheckbox.addEventListener('change', renderDurationInputs);
updateTimelineBtn.addEventListener('click', () => {
calculateTimeline();
renderTimeline();
switchTab('dashboard');
});
downloadPdfBtn.addEventListener('click', generatePdf);
initialize();
});
Please set an initial filing date in the Configuration tab to generate a timeline.
`; return; } const today = new Date(); calculatedTimeline.forEach(item => { const isCompleted = item.date < today; const itemHtml = `${item.name}
${item.description || ''}
